68.14.8 - toolkit

This commit is contained in:
Fedor 2025-04-19 19:15:41 +03:00
parent 0841e6d37b
commit 047e832886
906 changed files with 18400 additions and 34034 deletions

View File

@ -8,9 +8,6 @@ var EXPORTED_SYMBOLS = ["PictureInPictureChild", "PictureInPictureToggleChild"];
const { ActorChild } = ChromeUtils.import(
"resource://gre/modules/ActorChild.jsm"
);
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
@ -23,8 +20,6 @@ ChromeUtils.defineModuleGetter(
"resource://gre/modules/Services.jsm"
);
XPCOMUtils.defineLazyGlobalGetters(this, ["InspectorUtils"]);
const TOGGLE_ENABLED_PREF =
"media.videocontrols.picture-in-picture.video-toggle.enabled";
const TOGGLE_TESTING_PREF =
@ -149,6 +144,10 @@ class PictureInPictureToggleChild extends ActorChild {
}
break;
}
case "mouseout": {
this.onMouseOut(event);
break;
}
case "mousedown":
case "pointerup":
case "mouseup":
@ -280,6 +279,9 @@ class PictureInPictureToggleChild extends ActorChild {
capture: true,
});
this.content.windowRoot.addEventListener("click", this, { capture: true });
this.content.windowRoot.addEventListener("mouseout", this, {
capture: true,
});
}
removeMouseButtonListeners() {
@ -298,6 +300,9 @@ class PictureInPictureToggleChild extends ActorChild {
this.content.windowRoot.removeEventListener("click", this, {
capture: true,
});
this.content.windowRoot.removeEventListener("mouseout", this, {
capture: true,
});
}
/**
@ -455,6 +460,28 @@ class PictureInPictureToggleChild extends ActorChild {
}
}
/**
* Called on mouseout events to determine whether or not the mouse has
* exited the window.
*
* @param {Event} event The mouseout event.
*/
onMouseOut(event) {
if (!event.relatedTarget) {
// For mouseout events, if there's no relatedTarget (which normally
// maps to the element that the mouse entered into) then this means that
// we left the window.
let state = this.docState;
let video = state.weakOverVideo && state.weakOverVideo.get();
if (!video) {
return;
}
this.onMouseLeaveVideo(video);
}
}
/**
* Called for each mousemove event when we're tracking those events to
* determine if the cursor is hovering over a <video>.
@ -570,7 +597,7 @@ class PictureInPictureToggleChild extends ActorChild {
}
state.weakOverVideo = Cu.getWeakReference(video);
InspectorUtils.addPseudoClassLock(controlsOverlay, ":hover");
controlsOverlay.classList.add("hovering");
// Now that we're hovering the video, we'll check to see if we're
// hovering the toggle too.
@ -579,18 +606,14 @@ class PictureInPictureToggleChild extends ActorChild {
/**
* Checks if a mouse event is happening over a toggle element. If it is,
* sets the :hover pseudoclass on it. Otherwise, it clears the :hover
* pseudoclass.
* sets the hovering class on it. Otherwise, it clears the hovering
* class.
*
* @param {Element} toggle The Picture-in-Picture toggle to check.
* @param {MouseEvent} event A MouseEvent to test.
*/
checkHoverToggle(toggle, event) {
if (this.isMouseOverToggle(toggle, event)) {
InspectorUtils.addPseudoClassLock(toggle, ":hover");
} else {
InspectorUtils.removePseudoClassLock(toggle, ":hover");
}
toggle.classList.toggle("hovering", this.isMouseOverToggle(toggle, event));
}
/**
@ -606,14 +629,15 @@ class PictureInPictureToggleChild extends ActorChild {
if (shadowRoot) {
let controlsOverlay = shadowRoot.querySelector(".controlsOverlay");
let toggle = shadowRoot.getElementById("pictureInPictureToggleButton");
InspectorUtils.removePseudoClassLock(controlsOverlay, ":hover");
InspectorUtils.removePseudoClassLock(toggle, ":hover");
controlsOverlay.classList.remove("hovering");
toggle.classList.remove("hovering");
}
state.weakOverVideo = null;
if (!this.toggleTesting) {
state.hideToggleDeferredTask.disarm();
state.mousemoveDeferredTask.disarm();
}
state.hideToggleDeferredTask = null;

View File

@ -6,7 +6,7 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Security-Policy" content="default-src chrome:" />
<meta http-equiv="Content-Security-Policy" content="default-src chrome:; object-src 'none'" />
<meta name="viewport" content="width=device-width"/>
<title>Checkerboard Analyzer</title>
<link rel="stylesheet" href="chrome://global/content/aboutCheckerboard.css" type="text/css"/>

View File

@ -21,6 +21,11 @@ body {
margin: auto;
}
/* The comment at the top of aboutMemory.xhtml explains this font choice. */
pre {
font-family: DejaVu Sans Mono, Liberation Mono, Fira Mono, monospace;
}
div.ancillary {
margin: 0.5em 0;
-moz-user-select: none;

View File

@ -4,9 +4,19 @@
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html xmlns="http://www.w3.org/1999/xhtml">
<!-- We explicitly set the language to English, which more-or-less guarantees
that the box-drawing characters used are the correct width. Without that,
in a Japanese or Chinese locale we might end up with box-drawing
characters that are twice the width of English characters, which messes up
the tree layout. See bug 1561153 for details. Note that about:memory is
not localized, so setting it explicitly to English should be fine.
We also set the fonts in aboutMemory.css in such a way that maximizes the
chances that the font chosen supports the box-drawing chars.
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Security-Policy" content="default-src chrome:" />
<meta http-equiv="Content-Security-Policy" content="default-src chrome:; object-src 'none'" />
<meta name="viewport" content="width=device-width"/>
<title>Memory Analyzer</title>
<link rel="stylesheet" href="chrome://global/skin/aboutMemory.css" type="text/css"/>

View File

@ -8,24 +8,24 @@ support-files =
memory-reports-diff1.json
memory-reports-diff2.json
memory-reports-good.json
remote.xul
remote.xhtml
[test_aboutmemory.xul]
[test_aboutmemory.xhtml]
tags = clipboard
[test_aboutmemory2.xul]
[test_aboutmemory2.xhtml]
tags = clipboard
[test_aboutmemory3.xul]
[test_aboutmemory3.xhtml]
tags = clipboard
[test_aboutmemory4.xul]
[test_aboutmemory4.xhtml]
tags = clipboard
[test_aboutmemory5.xul]
[test_aboutmemory5.xhtml]
tags = clipboard
skip-if = asan # Bug 1116230
[test_aboutmemory6.xul]
[test_aboutmemory7.xul]
[test_aboutmemory6.xhtml]
[test_aboutmemory7.xhtml]
tags = clipboard
[test_memoryReporters.xul]
[test_memoryReporters2.xul]
[test_sqliteMultiReporter.xul]
[test_dumpGCAndCCLogsToFile.xul]
[test_memoryReporters.xhtml]
[test_memoryReporters2.xhtml]
[test_sqliteMultiReporter.xhtml]
[test_dumpGCAndCCLogsToFile.xhtml]
skip-if = (verify && debug && (os == 'mac'))

View File

@ -7,7 +7,7 @@
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<!-- This file uses fake memory reporters to test the presentation of memory
reports in about:memory. test_memoryReporters.xul uses the real
reports in about:memory. test_memoryReporters.xhtml uses the real
memory reporters to test whether the memory reporters are producing
sensible results. -->

View File

@ -42,7 +42,7 @@
SpecialPowers.pushPrefEnv({"set": prefs}, function() {
for (let i = 0; i < numRemotes; i++) {
let w = remotes[i] = window.open("remote.xul", "", "chrome");
let w = remotes[i] = window.open("remote.xhtml", "", "chrome");
w.addEventListener("load", function loadHandler() {
w.removeEventListener("load", loadHandler);

View File

@ -24,7 +24,7 @@
SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", numRemotes]]},
function() {
for (let i = 0; i < numRemotes; i++) {
let w = remotes[i] = window.open("remote.xul", "", "chrome");
let w = remotes[i] = window.open("remote.xhtml", "", "chrome");
w.addEventListener("load", function loadHandler() {
w.removeEventListener("load", loadHandler);

View File

@ -6,7 +6,7 @@
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<!-- This file tests (in a rough fashion) whether the memory reporters are
producing sensible results. test_aboutmemory.xul tests the
producing sensible results. test_aboutmemory.xhtml tests the
presentation of memory reports in about:memory. -->
<!-- test results are displayed in the html:body -->

View File

@ -31,7 +31,7 @@
let remotes = [];
SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 3]]}, function() {
for (let i = 0; i < numToOpen; i++) {
let w = remotes[i] = window.open("remote.xul", "", "chrome");
let w = remotes[i] = window.open("remote.xhtml", "", "chrome");
w.addEventListener("load", function loadHandler() {
w.removeEventListener("load", loadHandler);

View File

@ -6,7 +6,7 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Security-Policy" content="default-src chrome:;img-src data:" />
<meta http-equiv="Content-Security-Policy" content="default-src chrome:;img-src data:; object-src 'none'" />
<title data-l10n-id="about-performance-title"/>
<link rel="icon" type="image/svg+xml" id="favicon"
href="chrome://global/skin/icons/performance.svg"/>

View File

@ -4,5 +4,5 @@
toolkit.jar:
content/global/alerts/alert.css (resources/content/alert.css)
content/global/alerts/alert.xul (resources/content/alert.xul)
content/global/alerts/alert.xhtml (resources/content/alert.xhtml)
content/global/alerts/alert.js (resources/content/alert.js)

View File

@ -5,7 +5,6 @@
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "nsXULAppAPI.h"
#include "nsAlertsService.h"
@ -290,9 +289,6 @@ NS_IMETHODIMP nsAlertsService::SetManualDoNotDisturb(bool aDoNotDisturb) {
NS_ENSURE_TRUE(alertsDND, NS_ERROR_NOT_IMPLEMENTED);
nsresult rv = alertsDND->SetManualDoNotDisturb(aDoNotDisturb);
if (NS_SUCCEEDED(rv)) {
Telemetry::Accumulate(Telemetry::ALERTS_SERVICE_DND_ENABLED, 1);
}
return rv;
#endif
}

View File

@ -17,7 +17,7 @@
using namespace mozilla;
#define ALERT_CHROME_URL "chrome://global/content/alerts/alert.xul"
#define ALERT_CHROME_URL "chrome://global/content/alerts/alert.xhtml"
namespace {
StaticRefPtr<nsXULAlerts> gXULAlerts;

View File

@ -8,7 +8,7 @@
animation-name: alert-animation;
}
#alertBox[animate]:not([clicked]):not([closing]):hover {
#alertBox[animate]:not([clicked], [closing]):hover {
animation-play-state: paused;
}

View File

@ -62,6 +62,9 @@
<!-- This method is called inline because we want to make sure we establish the width
and height of the alert before we fire the onload handler. -->
<script>prefillAlertInfo();</script>
<script>
/* eslint-disable no-undef */
prefillAlertInfo();
</script>
</window>

View File

@ -23,12 +23,11 @@ const chromeScript = SpecialPowers.loadChromeScript(_ => {
sendAsyncMessage("waitForXULAlert", false);
}, 2000);
var windowObserver = function(aSubject, aTopic, aData) {
var windowObserver = function(win, aTopic, aData) {
if (aTopic != "domwindowopened") {
return;
}
var win = aSubject.QueryInterface(Ci.nsIDOMWindow);
win.addEventListener("load", function() {
let windowType = win.document.documentElement.getAttribute("windowtype");
if (windowType == "alert:alert") {

View File

@ -26,7 +26,7 @@ const chromeScript = SpecialPowers.loadChromeScript(_ => {
sendAsyncMessage("waitedForPosition", null);
}, 2000);
var windowObserver = function(aSubject, aTopic, aData) {
var windowObserver = function(win, aTopic, aData) {
if (aTopic != "domwindowopened") {
return;
}
@ -36,7 +36,6 @@ const chromeScript = SpecialPowers.loadChromeScript(_ => {
Services.ww.unregisterNotification(windowObserver);
var win = aSubject.QueryInterface(Ci.nsIDOMWindow);
win.addEventListener("pageshow", function() {
var x = win.screenX;
var y = win.screenY;

View File

@ -63,7 +63,7 @@ AppPicker.prototype = {
.setAttribute("value", description);
document
.getElementById("suggested-filename")
.setAttribute("value", filename);
.setAttribute("value", filename || "");
document
.getElementById("content-icon")
.setAttribute(

View File

@ -7,16 +7,17 @@
<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
<?xml-stylesheet href="chrome://global/skin/appPicker.css" type="text/css"?>
<!DOCTYPE dialog SYSTEM "chrome://global/locale/appPicker.dtd" >
<!DOCTYPE window SYSTEM "chrome://global/locale/appPicker.dtd" >
<dialog id="app-picker"
<window
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="g_dialog.appPickerLoad();"
buttons="accept,cancel,extra2"
buttonlabelextra2="&BrowseButton.label;"
defaultButton="cancel"
aria-describedby="content-description suggested-filename"
persist="screenX screenY">
<dialog id="app-picker"
buttons="accept,cancel,extra2"
buttonlabelextra2="&BrowseButton.label;"
defaultButton="cancel">
<script src="chrome://global/content/appPicker.js"/>
@ -35,3 +36,4 @@
<label id="app-picker-notfound" value="&NoAppFound.label;" hidden="true"/>
</dialog>
</window>

View File

@ -3,6 +3,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
toolkit.jar:
content/global/appPicker.xul (content/appPicker.xul)
content/global/appPicker.xhtml (content/appPicker.xhtml)
content/global/appPicker.js (content/appPicker.js)

View File

@ -1283,12 +1283,13 @@ nsresult nsAutoCompleteController::EnterMatch(bool aIsPopupSelection,
}
obsSvc->NotifyObservers(input, "autocomplete-did-enter-text", nullptr);
ClosePopup();
bool cancel;
bool itemWasSelected = selectedIndex >= 0 && !value.IsEmpty();
input->OnTextEntered(aEvent, itemWasSelected, &cancel);
ClosePopup();
return NS_OK;
}

View File

@ -1068,7 +1068,7 @@ nsWebBrowser::SetParentNativeWindow(nativeWindow aParentNativeWindow) {
NS_IMETHODIMP
nsWebBrowser::GetNativeHandle(nsAString& aNativeHandle) {
// the nativeHandle should be accessed from nsIXULWindow
// the nativeHandle should be accessed from nsIAppWindow
return NS_ERROR_NOT_IMPLEMENTED;
}

View File

@ -7,7 +7,7 @@
<html>
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Security-Policy" content="default-src chrome:" />
<meta http-equiv="Content-Security-Policy" content="default-src chrome:; object-src 'none'" />
<title>about:certificate</title>
</head>
<body>

View File

@ -555,8 +555,8 @@ _ContextualIdentityService.prototype = {
}
let tabbrowser = win.gBrowser;
for (let i = tabbrowser.tabContainer.children.length - 1; i >= 0; --i) {
let tab = tabbrowser.tabContainer.children[i];
for (let i = tabbrowser.tabs.length - 1; i >= 0; --i) {
let tab = tabbrowser.tabs[i];
if (
tab.hasAttribute("usercontextid") &&
(!userContextId ||

View File

@ -1,90 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const { AppConstants } = ChromeUtils.import(
"resource://gre/modules/AppConstants.jsm"
);
const { FileUtils } = ChromeUtils.import(
"resource://gre/modules/FileUtils.jsm"
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyServiceGetters(this, {
gCertDB: ["@mozilla.org/security/x509certdb;1", "nsIX509CertDB"],
});
var EXPORTED_SYMBOLS = ["Corroborate"];
/**
* Tools for verifying internal files in Mozilla products.
*/
this.Corroborate = {
async init() {
const appOmniJar = FileUtils.getFile("XCurProcD", [
AppConstants.OMNIJAR_NAME,
]);
const greOmniJar = FileUtils.getFile("GreD", [AppConstants.OMNIJAR_NAME]);
const systemAddons = FileUtils.getFile("XCurProcD", ["features"]);
let corruptOmnijar = true;
// If an omni jar is missing, we consider that corrupt. Firefox could be running with
// an omni jar unpacked, but it would never be signed correctly in that case so there
// isn't a point checking further.
if (appOmniJar.exists() && greOmniJar.exists()) {
corruptOmnijar = !(
(await this.verifyJar(appOmniJar)) && (await this.verifyJar(greOmniJar))
);
}
// It's not necessarily a problem if all built-in system add-ons have been removed,
// more that we want to know if any unsigned add-ons are present. The Telemetry Environment
// shows which system add-ons are present, anyway.
let corruptSystemAddons = false;
for (let file of systemAddons.directoryEntries) {
if (!(await this.verifyJar(file))) {
corruptSystemAddons = true;
break;
}
}
this.reportTelemetry(corruptOmnijar, corruptSystemAddons);
},
/**
* Verify signed state of arbitrary JAR file. Currently only JAR files signed
* with Mozilla-internal keys are supported.
*
* @argument file - an nsIFile pointing to the JAR to verify.
*
* @returns {Promise} - resolves true if file exists and is valid, false otherwise.
* Never rejects.
*/
verifyJar(file) {
let root = Ci.nsIX509CertDB.AddonsPublicRoot;
let expectedOrganizationalUnit = "Mozilla Components";
return new Promise(resolve => {
gCertDB.openSignedAppFileAsync(root, file, (rv, _zipReader, cert) => {
resolve(
Components.isSuccessCode(rv) &&
cert.organizationalUnit === expectedOrganizationalUnit
);
});
});
},
reportTelemetry(corruptOmnijar, corruptSystemAddons) {
Services.telemetry.scalarSet(
"corroborate.omnijar_corrupted",
corruptOmnijar
);
Services.telemetry.scalarSet(
"corroborate.system_addons_corrupted",
corruptSystemAddons
);
},
};

View File

@ -1,15 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files('**'):
BUG_COMPONENT = ('Toolkit', 'General')
EXTRA_JS_MODULES += [
'Corroborate.jsm',
]
XPCSHELL_TESTS_MANIFESTS += [
'test/xpcshell/xpcshell.ini',
]

View File

@ -1,49 +0,0 @@
"use strict";
const { Corroborate } = ChromeUtils.import(
"resource://gre/modules/Corroborate.jsm"
);
const { FileUtils } = ChromeUtils.import(
"resource://gre/modules/FileUtils.jsm"
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
add_task(async function test_various_jars() {
let result = await Corroborate.verifyJar(do_get_file("data/unsigned.xpi"));
equal(result, false, "unsigned files do not verify");
result = await Corroborate.verifyJar(do_get_file("data/signed-amo.xpi"));
equal(result, false, "AMO signed files do not verify");
result = await Corroborate.verifyJar(
do_get_file("data/signed-privileged.xpi")
);
equal(result, false, "Privileged signed files do not verify");
let missingFile = do_get_file("data");
missingFile.append("missing.xpi");
result = await Corroborate.verifyJar(missingFile);
equal(result, false, "Missing (but expected) files do not verify");
result = await Corroborate.verifyJar(
do_get_file("data/signed-components.xpi")
);
equal(result, true, "Components signed files do verify");
});
add_task(async function test_telemetry() {
Corroborate.reportTelemetry(true, false);
const scalars = Services.telemetry.getSnapshotForScalars("main", false)
.parent;
ok(
"corroborate.omnijar_corrupted" in scalars,
"omni jar should be corrupted"
);
ok(
"corroborate.system_addons_corrupted" in scalars,
"system addons should not be corrupted"
);
});

View File

@ -1,7 +0,0 @@
[DEFAULT]
tags = corroborator
support-files =
data/**
[test_verify_jar.js]
skip-if = true # Bug 1549147 - Disable temporily until non-expired cert is available to sign test XPI.

View File

@ -5,5 +5,5 @@ support-files =
ctypes_worker.js
../unit/test_jsctypes.js
[test_ctypes.xul]
[test_ctypes.xhtml]
skip-if = verify

View File

@ -5,7 +5,8 @@
<window title="DOM Worker Threads Test"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="test();">
onload="test();"
onunload="onunload();">
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
@ -39,7 +40,7 @@
.get("CurWorkD", Ci.nsIFile);
path = location.pathname;
path = path.slice("content/".length,
-1 * "/test_ctypes.xul".length);
-1 * "/test_ctypes.xhtml".length);
let components = path.split("/");
for (let part in components) {
dir.append(components[part]);
@ -71,6 +72,8 @@
{
SimpleTest.waitForExplicitFinish();
Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
var dir = getCurrentDir(location.path);
ok(dir.exists() && dir.isDirectory(), "Chrome test dir doesn't exist?!");
setupLibs(dir);
@ -97,6 +100,11 @@
worker.postMessage({dir: dir.path, os: Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS});
}
function onunload()
{
Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
}
]]>
</script>

View File

@ -2852,7 +2852,7 @@ function run_FunctionType_tests() {
Assert.equal(f_t.size, undefined);
Assert.ok(f_t.abi === ctypes.default_abi);
Assert.ok(f_t.returnType === g_t);
Assert.ok(f_t.argTypes.length == 0);
Assert.ok(!f_t.argTypes.length);
Assert.equal(f_t.toString(), "type " + name);
Assert.equal(f_t.toSource(), "ctypes.FunctionType(ctypes.default_abi, g_t)");

View File

@ -743,7 +743,6 @@ this.DownloadHistoryList.prototype = {
},
// nsINavHistoryResultObserver
nodeAnnotationChanged() {},
nodeIconChanged() {},
nodeTitleChanged() {},
nodeKeywordChanged() {},

View File

@ -50,7 +50,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
ExtensionProcessScript: "resource://gre/modules/ExtensionProcessScript.jsm",
ExtensionStorage: "resource://gre/modules/ExtensionStorage.jsm",
ExtensionStorageIDB: "resource://gre/modules/ExtensionStorageIDB.jsm",
ExtensionTelemetry: "resource://gre/modules/ExtensionTelemetry.jsm",
FileSource: "resource://gre/modules/L10nRegistry.jsm",
L10nRegistry: "resource://gre/modules/L10nRegistry.jsm",
LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
@ -147,6 +146,7 @@ const PRIVILEGED_PERMS = new Set([
"mozillaAddons",
"geckoViewAddons",
"telemetry",
"urlbar",
]);
/**
@ -937,7 +937,7 @@ class ExtensionData {
// Check if there's a root directory `/localization` in the langpack.
// If there is one, add it with the name `toolkit` as a FileSource.
const entries = await this.readDirectory("localization");
if (entries.length > 0) {
if (entries.length) {
l10nRegistrySources.toolkit = "";
}
@ -1271,8 +1271,6 @@ class ExtensionData {
* Origin permissions requested.
* @param {array<string>} info.permissions.permissions
* Regular (non-origin) permissions requested.
* @param {boolean} info.unsigned
* True if the prompt is for installing an unsigned addon.
* @param {string} info.type
* The type of prompt being shown. May be one of "update",
* "sideload", "optional", or omitted for a regular
@ -1404,9 +1402,6 @@ class ExtensionData {
"webextPerms.header",
["<>"]
);
result.text = info.unsigned
? bundle.GetStringFromName("webextPerms.unsignedWarning")
: "";
result.listIntro = bundle.GetStringFromName("webextPerms.listIntro");
result.acceptText = bundle.GetStringFromName("webextPerms.add.label");
@ -1423,10 +1418,9 @@ class ExtensionData {
"webextPerms.sideloadHeader",
["<>"]
);
let key =
result.msgs.length == 0
? "webextPerms.sideloadTextNoPerms"
: "webextPerms.sideloadText2";
let key = !result.msgs.length
? "webextPerms.sideloadTextNoPerms"
: "webextPerms.sideloadText2";
result.text = bundle.GetStringFromName(key);
result.acceptText = bundle.GetStringFromName(
"webextPerms.sideloadEnable.label"
@ -1672,7 +1666,7 @@ class Extension extends ExtensionData {
this.permissions.add(perm);
}
if (permissions.origins.length > 0) {
if (permissions.origins.length) {
let patterns = this.whiteListedHosts.patterns.map(host => host.pattern);
this.whiteListedHosts = new MatchPatternSet(
@ -1883,8 +1877,25 @@ class Extension extends ExtensionData {
return manifest;
}
get contentSecurityPolicy() {
return this.manifest.content_security_policy;
get extensionPageCSP() {
const { content_security_policy } = this.manifest;
if (
content_security_policy &&
typeof content_security_policy === "object"
) {
return content_security_policy.extension_pages;
}
return content_security_policy;
}
get contentScriptCSP() {
let { content_security_policy } = this.manifest;
if (
content_security_policy &&
typeof content_security_policy === "object"
) {
return content_security_policy.content_scripts;
}
}
get backgroundScripts() {
@ -1911,7 +1922,8 @@ class Extension extends ExtensionData {
id: this.id,
uuid: this.uuid,
name: this.name,
contentSecurityPolicy: this.contentSecurityPolicy,
extensionPageCSP: this.extensionPageCSP,
contentScriptCSP: this.contentScriptCSP,
instanceId: this.instanceId,
resourceURL: this.resourceURL,
contentScripts: this.contentScripts,
@ -2174,7 +2186,6 @@ class Extension extends ExtensionData {
});
sharedData.set("extensions/pending", pendingExtensions);
ExtensionTelemetry.extensionStartup.stopwatchStart(this);
try {
this.state = "Startup: Loading manifest";
await this.loadManifest();
@ -2279,7 +2290,6 @@ class Extension extends ExtensionData {
throw errors;
} finally {
ExtensionTelemetry.extensionStartup.stopwatchFinish(this);
}
}
@ -2486,7 +2496,7 @@ class Langpack extends ExtensionData {
async startup(reason) {
this.chromeRegistryHandle = null;
if (this.startupData.chromeEntries.length > 0) {
if (this.startupData.chromeEntries.length) {
const manifestURI = Services.io.newURI(
"manifest.json",
null,

View File

@ -786,7 +786,7 @@ class BrowserExtensionContent extends EventEmitter {
/* eslint-disable mozilla/balanced-listeners */
this.on("add-permissions", (ignoreEvent, permissions) => {
if (permissions.permissions.length > 0) {
if (permissions.permissions.length) {
let perms = new Set(this.policy.permissions);
for (let perm of permissions.permissions) {
perms.add(perm);
@ -794,7 +794,7 @@ class BrowserExtensionContent extends EventEmitter {
this.policy.permissions = perms;
}
if (permissions.origins.length > 0) {
if (permissions.origins.length) {
let patterns = this.whiteListedHosts.patterns.map(host => host.pattern);
this.policy.allowedOrigins = new MatchPatternSet(
@ -805,7 +805,7 @@ class BrowserExtensionContent extends EventEmitter {
});
this.on("remove-permissions", (ignoreEvent, permissions) => {
if (permissions.permissions.length > 0) {
if (permissions.permissions.length) {
let perms = new Set(this.policy.permissions);
for (let perm of permissions.permissions) {
perms.delete(perm);
@ -813,7 +813,7 @@ class BrowserExtensionContent extends EventEmitter {
this.policy.permissions = perms;
}
if (permissions.origins.length > 0) {
if (permissions.origins.length) {
let origins = permissions.origins.map(
origin => new MatchPattern(origin, { ignorePath: true }).pattern
);
@ -1135,7 +1135,7 @@ class ChildAPIManager {
this.permissionsChangedCallbacks = new Set();
this.updatePermissions = null;
if (this.context.extension.optionalPermissions.length > 0) {
if (this.context.extension.optionalPermissions.length) {
this.updatePermissions = () => {
for (let callback of this.permissionsChangedCallbacks) {
try {

View File

@ -1344,7 +1344,6 @@ class SchemaAPIManager extends EventEmitter {
* "addon" - An addon process.
* "content" - A content process.
* "devtools" - A devtools process.
* "proxy" - A proxy script process.
* @param {SchemaRoot} schema
*/
constructor(processType, schema) {
@ -2152,9 +2151,6 @@ class EventManager {
this.remove = new Map();
if (this.persistent) {
if (this.context.viewType !== "background") {
this.persistent = null;
}
if (AppConstants.DEBUG) {
if (this.context.envType !== "addon_parent") {
throw new Error(
@ -2167,6 +2163,9 @@ class EventManager {
);
}
}
if (this.context.viewType !== "background") {
this.persistent = null;
}
}
}
@ -2188,10 +2187,13 @@ class EventManager {
* the object also has a `primed` property that holds the things needed
* to handle events during startup and eventually connect the listener
* with a callback registered from the extension.
*
* @param {Extension} extension
* @returns {boolean} True if the extension had any persistent listeners.
*/
static _initPersistentListeners(extension) {
if (extension.persistentListeners) {
return;
return false;
}
let listeners = new DefaultMap(() => new DefaultMap(() => new Map()));
@ -2199,9 +2201,10 @@ class EventManager {
let { persistentListeners } = extension.startupData;
if (!persistentListeners) {
return;
return false;
}
let found = false;
for (let [module, entry] of Object.entries(persistentListeners)) {
for (let [event, paramlists] of Object.entries(entry)) {
for (let paramlist of paramlists) {
@ -2210,9 +2213,11 @@ class EventManager {
.get(module)
.get(event)
.set(key, { params: paramlist });
found = true;
}
}
}
return found;
}
// Extract just the information needed at startup for all persistent
@ -2239,16 +2244,29 @@ class EventManager {
// This function is only called during browser startup, it stores details
// about all primed listeners in the extension's persistentListeners Map.
static primeListeners(extension) {
EventManager._initPersistentListeners(extension);
if (!EventManager._initPersistentListeners(extension)) {
return;
}
let bgStartupPromise = new Promise(resolve => {
function resolveBgPromise(type) {
extension.off("startup", resolveBgPromise);
extension.off("background-page-aborted", resolveBgPromise);
extension.off("shutdown", resolveBgPromise);
resolve();
}
extension.on("startup", resolveBgPromise);
extension.on("background-page-aborted", resolveBgPromise);
extension.on("shutdown", resolveBgPromise);
});
for (let [module, moduleEntry] of extension.persistentListeners) {
let api = extension.apiManager.getAPI(module, extension, "addon_parent");
for (let [event, eventEntry] of moduleEntry) {
for (let listener of eventEntry.values()) {
let primed = { pendingEvents: [], cleared: false };
let primed = { pendingEvents: [] };
listener.primed = primed;
let bgStartupPromise = new Promise(r => extension.once("startup", r));
let wakeup = () => {
extension.emit("background-page-event");
return bgStartupPromise;
@ -2256,8 +2274,8 @@ class EventManager {
let fireEvent = (...args) =>
new Promise((resolve, reject) => {
if (primed.cleared) {
reject(new Error("listener not re-registered"));
if (!listener.primed) {
reject(new Error("primed listener not re-registered"));
return;
}
primed.pendingEvents.push({ args, resolve, reject });
@ -2311,6 +2329,7 @@ class EventManager {
if (!primed) {
continue;
}
listener.primed = null;
for (let evt of primed.pendingEvents) {
evt.reject(new Error("listener not re-registered"));
@ -2320,7 +2339,6 @@ class EventManager {
EventManager.clearPersistentListener(extension, module, event, key);
}
primed.unregister();
primed.cleared = true;
}
}
}

View File

@ -12,7 +12,6 @@ const { XPCOMUtils } = ChromeUtils.import(
XPCOMUtils.defineLazyModuleGetters(this, {
ExtensionProcessScript: "resource://gre/modules/ExtensionProcessScript.jsm",
ExtensionTelemetry: "resource://gre/modules/ExtensionTelemetry.jsm",
LanguageDetector: "resource:///modules/translation/LanguageDetector.jsm",
MessageChannel: "resource://gre/modules/MessageChannel.jsm",
Schemas: "resource://gre/modules/Schemas.jsm",
@ -352,7 +351,7 @@ class Script {
}
get requiresCleanup() {
return !this.removeCSS && (this.css.length > 0 || this.cssCodeHash);
return !this.removeCSS && (!!this.css.length || this.cssCodeHash);
}
async addCSSCode(cssCode) {
@ -527,7 +526,7 @@ class Script {
// problem since there are no network loads involved, and since we cache
// the stylesheets on first load. We should fix this up if it does becomes
// a problem.
if (this.css.length > 0) {
if (this.css.length) {
context.contentWindow.document.blockParsing(cssPromise, {
blockScriptCreated: false,
});
@ -546,10 +545,6 @@ class Script {
// The evaluations below may throw, in which case the promise will be
// automatically rejected.
ExtensionTelemetry.contentScriptInjection.stopwatchStart(
extension,
context
);
try {
for (let script of scripts) {
result = script.executeInGlobal(context.cloneScope);
@ -559,14 +554,12 @@ class Script {
result = Cu.evalInSandbox(
this.matcher.jsCode,
context.cloneScope,
"latest"
"latest",
"sandbox eval code",
1
);
}
} finally {
ExtensionTelemetry.contentScriptInjection.stopwatchFinish(
extension,
context
);
}
await cssPromise;
@ -666,7 +659,6 @@ class UserScript extends Script {
// The evaluations below may throw, in which case the promise will be
// automatically rejected.
ExtensionTelemetry.userScriptInjection.stopwatchStart(extension, context);
try {
let userScriptSandbox = this.sandboxes.get(context);
@ -693,10 +685,6 @@ class UserScript extends Script {
script.executeInGlobal(userScriptSandbox);
}
} finally {
ExtensionTelemetry.userScriptInjection.stopwatchFinish(
extension,
context
);
}
}

View File

@ -132,14 +132,14 @@ let apiManager = new (class extends SchemaAPIManager {
let disabledIds = AddonManager.getStartupChanges(
AddonManager.STARTUP_CHANGE_DISABLED
);
if (disabledIds.length > 0) {
if (disabledIds.length) {
this._callHandlers(disabledIds, "disable", "onDisable");
}
let uninstalledIds = AddonManager.getStartupChanges(
AddonManager.STARTUP_CHANGE_UNINSTALLED
);
if (uninstalledIds.length > 0) {
if (uninstalledIds.length) {
this._callHandlers(uninstalledIds, "uninstall", "onUninstall");
}
}
@ -504,7 +504,7 @@ ProxyMessenger = {
apiManager.global.tabGetSender(extension, target, sender);
}
let promise1 = MessageChannel.sendMessage(receiverMM, messageName, data, {
let promise = MessageChannel.sendMessage(receiverMM, messageName, data, {
sender,
recipient,
responseType,
@ -529,7 +529,7 @@ ProxyMessenger = {
receiverMM
);
port.register();
promise1.catch(() => {
promise.catch(() => {
port.unregister();
});
}
@ -540,49 +540,7 @@ ProxyMessenger = {
}
}
if (!(recipient.toProxyScript && extension.remote)) {
return promise1;
}
// Proxy scripts run in the parent process so we need to dispatch
// the message to both the parent and extension process and merge
// the results.
// Once proxy scripts are gone (bug 1443259) we can remove this
let promise2 = MessageChannel.sendMessage(
Services.ppmm.getChildAt(0),
messageName,
data,
{
sender,
recipient,
responseType,
}
);
let result = undefined;
let failures = 0;
let tryPromise = async promise => {
try {
let res = await promise;
if (result === undefined) {
result = res;
}
} catch (e) {
if (e.result === MessageChannel.RESULT_NO_RESPONSE) {
// Ignore.
} else if (e.result === MessageChannel.RESULT_NO_HANDLER) {
failures++;
} else {
throw e;
}
}
};
await Promise.all([tryPromise(promise1), tryPromise(promise2)]);
if (failures == 2) {
return Promise.reject(noHandlerError);
}
return result;
return promise;
},
/**
@ -703,11 +661,7 @@ GlobalManager = {
_onExtensionBrowser(type, browser, additionalData = {}) {
browser.messageManager.loadFrameScript(
`data:,
Components.utils.import("resource://gre/modules/Services.jsm");
Services.obs.notifyObservers(this, "tab-content-frameloader-created", "");
`,
"resource://gre/modules/onExtensionBrowser.js",
false,
true
);
@ -846,14 +800,14 @@ class ExtensionPageContextParent extends ProxyContextParent {
}
// The window that contains this context. This may change due to moving tabs.
get xulWindow() {
get appWindow() {
let win = this.xulBrowser.ownerGlobal;
return win.docShell.rootTreeItem.domWindow;
}
get currentWindow() {
if (this.viewType !== "background") {
return this.xulWindow;
return this.appWindow;
}
}
@ -1283,7 +1237,7 @@ class HiddenXULWindow {
}
/**
* Private helper that create a XULDocument in a windowless browser.
* Private helper that create a HTMLDocument in a windowless browser.
*
* @returns {Promise<void>}
* A promise which resolves when the windowless browser is ready.
@ -1322,7 +1276,7 @@ class HiddenXULWindow {
triggeringPrincipal: system,
};
chromeShell.loadURI(
"chrome://extensions/content/dummy.xul",
"chrome://extensions/content/dummy.xhtml",
loadURIOptions
);

View File

@ -119,7 +119,7 @@ var ExtensionPermissions = {
}
}
if (added.permissions.length > 0 || added.origins.length > 0) {
if (added.permissions.length || added.origins.length) {
await this._update(extensionId, { permissions, origins });
if (emitter) {
emitter.emit("add-permissions", added);
@ -158,7 +158,7 @@ var ExtensionPermissions = {
}
}
if (removed.permissions.length > 0 || removed.origins.length > 0) {
if (removed.permissions.length || removed.origins.length) {
await this._update(extensionId, { permissions, origins });
if (emitter) {
emitter.emit("remove-permissions", removed);

View File

@ -45,11 +45,14 @@ using dom::ContentFrameMessageManager;
using dom::Document;
using dom::Promise;
#define BASE_CSP_PREF "extensions.webextensions.base-content-security-policy"
#define DEFAULT_BASE_CSP \
"script-src 'self' https://* moz-extension: blob: filesystem: " \
"'unsafe-eval' 'unsafe-inline'; " \
"object-src 'self' https://* moz-extension: blob: filesystem:;"
#define DEFAULT_CSP_PREF \
"extensions.webextensions.default-content-security-policy"
#define DEFAULT_DEFAULT_CSP "script-src 'self'; object-src 'self';"
#define OBS_TOPIC_PRELOAD_SCRIPT "web-extension-preload-content-script"
@ -97,6 +100,9 @@ ExtensionPolicyService::ExtensionPolicyService() {
Preferences::AddBoolVarCache(&sRemoteExtensions,
"extensions.webextensions.remote", false);
mBaseCSP.SetIsVoid(true);
mDefaultCSP.SetIsVoid(true);
RegisterObservers();
}
@ -178,26 +184,6 @@ bool ExtensionPolicyService::UnregisterObserver(DocumentObserver& aObserver) {
return true;
}
void ExtensionPolicyService::BaseCSP(nsAString& aBaseCSP) const {
nsresult rv;
rv = Preferences::GetString(
"extensions.webextensions.base-content-security-policy", aBaseCSP);
if (NS_FAILED(rv)) {
aBaseCSP.AssignLiteral(DEFAULT_BASE_CSP);
}
}
void ExtensionPolicyService::DefaultCSP(nsAString& aDefaultCSP) const {
nsresult rv;
rv = Preferences::GetString(
"extensions.webextensions.default-content-security-policy", aDefaultCSP);
if (NS_FAILED(rv)) {
aDefaultCSP.AssignLiteral(DEFAULT_DEFAULT_CSP);
}
}
/*****************************************************************************
* nsIMemoryReporter
*****************************************************************************/
@ -243,6 +229,9 @@ void ExtensionPolicyService::RegisterObservers() {
if (XRE_IsContentProcess()) {
mObs->AddObserver(this, "http-on-opening-request", false);
}
Preferences::AddStrongObserver(this, BASE_CSP_PREF);
Preferences::AddStrongObserver(this, DEFAULT_CSP_PREF);
}
void ExtensionPolicyService::UnregisterObservers() {
@ -251,6 +240,9 @@ void ExtensionPolicyService::UnregisterObservers() {
if (XRE_IsContentProcess()) {
mObs->RemoveObserver(this, "http-on-opening-request");
}
Preferences::RemoveObserver(this, BASE_CSP_PREF);
Preferences::RemoveObserver(this, DEFAULT_CSP_PREF);
}
nsresult ExtensionPolicyService::Observe(nsISupports* aSubject,
@ -273,6 +265,14 @@ nsresult ExtensionPolicyService::Observe(nsISupports* aSubject,
mMessageManagers.PutEntry(mm);
mm->AddSystemEventListener(NS_LITERAL_STRING("unload"), this, false, false);
} else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
const nsCString converted = NS_ConvertUTF16toUTF8(aData);
const char* pref = converted.get();
if (!strcmp(pref, BASE_CSP_PREF)) {
mBaseCSP.SetIsVoid(true);
} else if (!strcmp(pref, DEFAULT_CSP_PREF)) {
mDefaultCSP.SetIsVoid(true);
}
}
return NS_OK;
}
@ -523,19 +523,44 @@ void ExtensionPolicyService::CheckContentScripts(const DocInfo& aDocInfo,
*****************************************************************************/
nsresult ExtensionPolicyService::GetBaseCSP(nsAString& aBaseCSP) {
BaseCSP(aBaseCSP);
if (mBaseCSP.IsVoid()) {
nsresult rv = Preferences::GetString(BASE_CSP_PREF, mBaseCSP);
if (NS_FAILED(rv)) {
mBaseCSP.AssignLiteral(DEFAULT_BASE_CSP);
}
mBaseCSP.SetIsVoid(false);
}
aBaseCSP.Assign(mBaseCSP);
return NS_OK;
}
nsresult ExtensionPolicyService::GetDefaultCSP(nsAString& aDefaultCSP) {
DefaultCSP(aDefaultCSP);
if (mDefaultCSP.IsVoid()) {
nsresult rv = Preferences::GetString(DEFAULT_CSP_PREF, mDefaultCSP);
if (NS_FAILED(rv)) {
mDefaultCSP.AssignLiteral(DEFAULT_DEFAULT_CSP);
}
mDefaultCSP.SetIsVoid(false);
}
aDefaultCSP.Assign(mDefaultCSP);
return NS_OK;
}
nsresult ExtensionPolicyService::GetAddonCSP(const nsAString& aAddonId,
nsAString& aResult) {
nsresult ExtensionPolicyService::GetExtensionPageCSP(const nsAString& aAddonId,
nsAString& aResult) {
if (WebExtensionPolicy* policy = GetByID(aAddonId)) {
policy->GetContentSecurityPolicy(aResult);
policy->GetExtensionPageCSP(aResult);
return NS_OK;
}
return NS_ERROR_INVALID_ARG;
}
nsresult ExtensionPolicyService::GetContentScriptCSP(const nsAString& aAddonId,
nsAString& aResult) {
if (WebExtensionPolicy* policy = GetByID(aAddonId)) {
policy->GetContentScriptCSP(aResult);
return NS_OK;
}
return NS_ERROR_INVALID_ARG;

View File

@ -83,9 +83,6 @@ class ExtensionPolicyService final : public nsIAddonPolicyService,
bool RegisterObserver(extensions::DocumentObserver& aPolicy);
bool UnregisterObserver(extensions::DocumentObserver& aPolicy);
void BaseCSP(nsAString& aDefaultCSP) const;
void DefaultCSP(nsAString& aDefaultCSP) const;
bool UseRemoteExtensions() const;
bool IsExtensionProcess() const;
@ -126,6 +123,9 @@ class ExtensionPolicyService final : public nsIAddonPolicyService,
nsCOMPtr<nsIObserverService> mObs;
static bool sRemoteExtensions;
nsString mBaseCSP;
nsString mDefaultCSP;
};
} // namespace mozilla

View File

@ -208,7 +208,8 @@ ExtensionManager = {
allowedOrigins: extension.whiteListedHosts,
webAccessibleResources: extension.webAccessibleResources,
contentSecurityPolicy: extension.contentSecurityPolicy,
extensionPageCSP: extension.extensionPageCSP,
contentScriptCSP: extension.contentScriptCSP,
localizeCallback,

View File

@ -15,7 +15,6 @@ const { IndexedDB } = ChromeUtils.import(
XPCOMUtils.defineLazyModuleGetters(this, {
ExtensionStorage: "resource://gre/modules/ExtensionStorage.jsm",
getTrimmedString: "resource://gre/modules/ExtensionTelemetry.jsm",
Services: "resource://gre/modules/Services.jsm",
OS: "resource://gre/modules/osfile.jsm",
});
@ -665,6 +664,9 @@ this.ExtensionStorageIDB = {
promise = migrateJSONFileData(extension, storagePrincipal)
.then(() => {
extension.setSharedData("storageIDBBackend", true);
extension.setSharedData("storageIDBPrincipal", storagePrincipal);
Services.ppmm.sharedData.flush();
return {
backendEnabled: true,
storagePrincipal: serializedPrincipal,
@ -684,6 +686,9 @@ this.ExtensionStorageIDB = {
"JSONFile backend is being kept enabled by an unexpected " +
`IDBBackend failure: ${err.message}::${err.stack}`
);
extension.setSharedData("storageIDBBackend", false);
Services.ppmm.sharedData.flush();
return { backendEnabled: false };
});
}

View File

@ -1,186 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var EXPORTED_SYMBOLS = ["ExtensionTelemetry", "getTrimmedString"];
ChromeUtils.defineModuleGetter(
this,
"Services",
"resource://gre/modules/Services.jsm"
);
// Map of the base histogram ids for the metrics recorded for the extensions.
const histograms = {
extensionStartup: "WEBEXT_EXTENSION_STARTUP_MS",
backgroundPageLoad: "WEBEXT_BACKGROUND_PAGE_LOAD_MS",
browserActionPopupOpen: "WEBEXT_BROWSERACTION_POPUP_OPEN_MS",
browserActionPreloadResult: "WEBEXT_BROWSERACTION_POPUP_PRELOAD_RESULT_COUNT",
contentScriptInjection: "WEBEXT_CONTENT_SCRIPT_INJECTION_MS",
pageActionPopupOpen: "WEBEXT_PAGEACTION_POPUP_OPEN_MS",
storageLocalGetJSON: "WEBEXT_STORAGE_LOCAL_GET_MS",
storageLocalSetJSON: "WEBEXT_STORAGE_LOCAL_SET_MS",
storageLocalGetIDB: "WEBEXT_STORAGE_LOCAL_IDB_GET_MS",
storageLocalSetIDB: "WEBEXT_STORAGE_LOCAL_IDB_SET_MS",
userScriptInjection: "WEBEXT_USER_SCRIPT_INJECTION_MS",
};
/**
* Get a trimmed version of the given string if it is longer than 80 chars (used in telemetry
* when a string may be longer than allowed).
*
* @param {string} str
* The original string content.
*
* @returns {string}
* The trimmed version of the string when longer than 80 chars, or the given string
* unmodified otherwise.
*/
function getTrimmedString(str) {
if (str.length <= 80) {
return str;
}
const length = str.length;
// Trim the string to prevent a flood of warnings messages logged internally by recordEvent,
// the trimmed version is going to be composed by the first 40 chars and the last 37 and 3 dots
// that joins the two parts, to visually indicate that the string has been trimmed.
return `${str.slice(0, 40)}...${str.slice(length - 37, length)}`;
}
/**
* This is a internal helper object which contains a collection of helpers used to make it easier
* to collect extension telemetry (in both the general histogram and in the one keyed by addon id).
*
* This helper object is not exported from ExtensionUtils, it is used by the ExtensionTelemetry
* Proxy which is exported and used by the callers to record telemetry data for one of the
* supported metrics.
*/
class ExtensionTelemetryMetric {
constructor(metric) {
this.metric = metric;
}
// Stopwatch methods.
stopwatchStart(extension, obj = extension) {
this._wrappedStopwatchMethod("start", this.metric, extension, obj);
}
stopwatchFinish(extension, obj = extension) {
this._wrappedStopwatchMethod("finish", this.metric, extension, obj);
}
stopwatchCancel(extension, obj = extension) {
this._wrappedStopwatchMethod("cancel", this.metric, extension, obj);
}
// Histogram counters methods.
histogramAdd(opts) {
this._histogramAdd(this.metric, opts);
}
/**
* Wraps a call to a TelemetryStopwatch method for a given metric and extension.
*
* @param {string} method
* The stopwatch method to call ("start", "finish" or "cancel").
* @param {string} metric
* The stopwatch metric to record (used to retrieve the base histogram id from the _histogram object).
* @param {Extension | BrowserExtensionContent} extension
* The extension to record the telemetry for.
* @param {any | undefined} [obj = extension]
* An optional telemetry stopwatch object (which defaults to the extension parameter when missing).
*/
_wrappedStopwatchMethod(method, metric, extension, obj = extension) {
if (!extension) {
throw new Error(`Mandatory extension parameter is undefined`);
}
const baseId = histograms[metric];
if (!baseId) {
throw new Error(`Unknown metric ${metric}`);
}
// Record metric in the general histogram.
TelemetryStopwatch[method](baseId, obj);
// Record metric in the histogram keyed by addon id.
let extensionId = getTrimmedString(extension.id);
TelemetryStopwatch[`${method}Keyed`](
`${baseId}_BY_ADDONID`,
extensionId,
obj
);
}
/**
* Record a telemetry category and/or value for a given metric.
*
* @param {string} metric
* The metric to record (used to retrieve the base histogram id from the _histogram object).
* @param {Object} options
* @param {Extension | BrowserExtensionContent} options.extension
* The extension to record the telemetry for.
* @param {string | undefined} [options.category]
* An optional histogram category.
* @param {number | undefined} [options.value]
* An optional value to record.
*/
_histogramAdd(metric, { category, extension, value }) {
if (!extension) {
throw new Error(`Mandatory extension parameter is undefined`);
}
const baseId = histograms[metric];
if (!baseId) {
throw new Error(`Unknown metric ${metric}`);
}
const histogram = Services.telemetry.getHistogramById(baseId);
if (typeof category === "string") {
histogram.add(category, value);
} else {
histogram.add(value);
}
const keyedHistogram = Services.telemetry.getKeyedHistogramById(
`${baseId}_BY_ADDONID`
);
const extensionId = getTrimmedString(extension.id);
if (typeof category === "string") {
keyedHistogram.add(extensionId, category, value);
} else {
keyedHistogram.add(extensionId, value);
}
}
}
// Cache of the ExtensionTelemetryMetric instances that has been lazily created by the
// Extension Telemetry Proxy.
const metricsCache = new Map();
/**
* This proxy object provides the telemetry helpers for the currently supported metrics (the ones listed in
* ExtensionTelemetryHelpers._histograms), the telemetry helpers for a particular metric are lazily created
* when the related property is being accessed on this object for the first time, e.g.:
*
* ExtensionTelemetry.extensionStartup.stopwatchStart(extension);
* ExtensionTelemetry.browserActionPreloadResult.histogramAdd({category: "Shown", extension});
*/
var ExtensionTelemetry = new Proxy(metricsCache, {
get(target, prop, receiver) {
if (!(prop in histograms)) {
throw new Error(`Unknown metric ${prop}`);
}
// Lazily create and cache the metric result object.
if (!target.has(prop)) {
target.set(prop, new ExtensionTelemetryMetric(prop));
}
return target.get(prop);
},
});

View File

@ -134,6 +134,9 @@ function frameScript() {
let kungFuDeathGrip = new Set();
function promiseBrowserLoaded(browser, url, redirectUrl) {
url = url && Services.io.newURI(url);
redirectUrl = redirectUrl && Services.io.newURI(redirectUrl);
return new Promise(resolve => {
const listener = {
QueryInterface: ChromeUtils.generateQI([
@ -144,12 +147,12 @@ function promiseBrowserLoaded(browser, url, redirectUrl) {
onStateChange(webProgress, request, stateFlags, statusCode) {
request.QueryInterface(Ci.nsIChannel);
let requestUrl = request.originalURI
? request.originalURI.spec
: webProgress.DOMWindow.location.href;
let requestURI =
request.originalURI ||
webProgress.DOMWindow.document.documentURIObject;
if (
webProgress.isTopLevel &&
(requestUrl === url || requestUrl === redirectUrl) &&
(url?.equals(requestURI) || redirectUrl?.equals(requestURI)) &&
stateFlags & Ci.nsIWebProgressListener.STATE_STOP
) {
resolve();
@ -207,7 +210,7 @@ class ContentPage {
triggeringPrincipal: system,
};
chromeShell.loadURI(
"chrome://extensions/content/dummy.xul",
"chrome://extensions/content/dummy.xhtml",
loadURIOptions
);
@ -218,7 +221,7 @@ class ContentPage {
let chromeDoc = await promiseDocumentLoaded(chromeShell.document);
let browser = chromeDoc.createElement("browser");
let browser = chromeDoc.createXULElement("browser");
browser.setAttribute("type", "content");
browser.setAttribute("disableglobalhistory", "true");
if (this.userContextId) {
@ -938,6 +941,16 @@ var ExtensionTestUtils = {
return new ExternallyInstalledWrapper(this.currentScope, id);
},
failOnSchemaWarnings(warningsAsErrors = true) {
let prefName = "extensions.webextensions.warnings-as-errors";
Services.prefs.setBoolPref(prefName, warningsAsErrors);
if (!warningsAsErrors) {
this.currentScope.registerCleanupFunction(() => {
Services.prefs.setBoolPref(prefName, true);
});
}
},
get remoteContentScripts() {
return REMOTE_CONTENT_SCRIPTS;
},

View File

@ -322,15 +322,15 @@ void MatchPattern::Init(JSContext* aCx, const nsAString& aPattern,
if (host.EqualsLiteral("*")) {
mMatchSubdomain = true;
} else if (StringHead(host, 2).EqualsLiteral("*.")) {
mDomain = NS_ConvertUTF16toUTF8(Substring(host, 2));
CopyUTF16toUTF8(Substring(host, 2), mDomain);
mMatchSubdomain = true;
} else if (host.Length() > 1 && host[0] == '[' &&
host[host.Length() - 1] == ']') {
// This is an IPv6 literal, we drop the enclosing `[]` to be
// consistent with nsIURI.
mDomain = NS_ConvertUTF16toUTF8(Substring(host, 1, host.Length() - 2));
CopyUTF16toUTF8(Substring(host, 1, host.Length() - 2), mDomain);
} else {
mDomain = NS_ConvertUTF16toUTF8(host);
CopyUTF16toUTF8(host, mDomain);
}
}

View File

@ -21,7 +21,7 @@ class MatchURLFilters {
throw new TypeError("filters should be an array");
}
if (filters.length == 0) {
if (!filters.length) {
throw new Error("filters array should not be empty");
}

View File

@ -890,7 +890,7 @@ this.MessageChannel = {
// At least one handler is required for all response types but
// RESPONSE_ALL.
if (handlers.length == 0 && responseType != this.RESPONSE_ALL) {
if (!handlers.length && responseType != this.RESPONSE_ALL) {
return Promise.reject({
result: MessageChannel.RESULT_NO_HANDLER,
message: "No matching message handler",
@ -925,7 +925,7 @@ this.MessageChannel = {
switch (responseType) {
case this.RESPONSE_FIRST:
if (responses.length == 0) {
if (!responses.length) {
return Promise.reject({
result: MessageChannel.RESULT_NO_RESPONSE,
message: "No handler returned a response",

View File

@ -196,7 +196,7 @@ var NativeApp = class extends EventEmitter {
}
_startWrite() {
if (this.sendQueue.length == 0) {
if (!this.sendQueue.length) {
return;
}
@ -228,7 +228,7 @@ var NativeApp = class extends EventEmitter {
let partial = "";
while (true) {
let data = await proc.stderr.readString();
if (data.length == 0) {
if (!data.length) {
// We have hit EOF, just stop reading
if (partial) {
Services.console.logStringMessage(

View File

@ -3,36 +3,22 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var EXPORTED_SYMBOLS = ["ProxyScriptContext", "ProxyChannelFilter"];
var EXPORTED_SYMBOLS = ["ProxyChannelFilter"];
/* exported ProxyScriptContext, ProxyChannelFilter */
/* exported ProxyChannelFilter */
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const { ExtensionCommon } = ChromeUtils.import(
"resource://gre/modules/ExtensionCommon.jsm"
);
const { ExtensionUtils } = ChromeUtils.import(
"resource://gre/modules/ExtensionUtils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"ExtensionChild",
"resource://gre/modules/ExtensionChild.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"ExtensionParent",
"resource://gre/modules/ExtensionParent.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Schemas",
"resource://gre/modules/Schemas.jsm"
);
XPCOMUtils.defineLazyServiceGetter(
this,
"ProxyService",
@ -47,8 +33,6 @@ XPCOMUtils.defineLazyGetter(this, "getCookieStoreIdForOriginAttributes", () => {
return ExtensionParent.apiManager.global.getCookieStoreIdForOriginAttributes;
});
const CATEGORY_EXTENSION_SCRIPTS_CONTENT = "webextension-scripts-content";
// DNS is resolved on the SOCKS proxy server.
const { TRANSPARENT_PROXY_RESOLVES_HOST } = Ci.nsIProxyInfo;
@ -57,14 +41,6 @@ const PROXY_TIMEOUT_SEC = 10;
const { ExtensionError } = ExtensionUtils;
const {
BaseContext,
CanOfAPIs,
LocalAPIImplementation,
SchemaAPIManager,
defineLazyGetter,
} = ExtensionCommon;
const PROXY_TYPES = Object.freeze({
DIRECT: "direct",
HTTPS: "https",
@ -233,7 +209,7 @@ const ProxyInfoData = {
proxyAuthorizationHeader,
connectionIsolationKey,
} = ProxyInfoData.validate(proxyData);
if (type === PROXY_TYPES.DIRECT) {
if (type === PROXY_TYPES.DIRECT && defaultProxyInfo) {
return defaultProxyInfo;
}
let failoverProxy = this.createProxyInfoFromData(
@ -267,93 +243,6 @@ const ProxyInfoData = {
failoverProxy
);
},
/**
* Creates a new proxy info data object using the return value of FindProxyForURL.
*
* @param {Array<string>} rule A single proxy rule returned by FindProxyForURL.
* (e.g. "PROXY 1.2.3.4:8080", "SOCKS 1.1.1.1:9090" or "DIRECT")
* @returns {nsIProxyInfo} The proxy info to apply for the given URI.
*/
parseProxyInfoDataFromPAC(rule) {
if (!rule) {
throw new ExtensionError("ProxyInfoData: Missing Proxy Rule");
}
let parts = rule.toLowerCase().split(/\s+/);
if (!parts[0] || parts.length > 2) {
throw new ExtensionError(
`ProxyInfoData: Invalid arguments passed for proxy rule: "${rule}"`
);
}
let type = parts[0];
let [host, port] = parts.length > 1 ? parts[1].split(":") : [];
switch (PROXY_TYPES[type.toUpperCase()]) {
case PROXY_TYPES.HTTP:
case PROXY_TYPES.HTTPS:
case PROXY_TYPES.SOCKS:
case PROXY_TYPES.SOCKS4:
if (!host || !port) {
throw new ExtensionError(
`ProxyInfoData: Invalid host or port from proxy rule: "${rule}"`
);
}
return { type, host, port };
case PROXY_TYPES.DIRECT:
if (host || port) {
throw new ExtensionError(
`ProxyInfoData: Invalid argument for proxy type: "${type}"`
);
}
return { type };
default:
throw new ExtensionError(
`ProxyInfoData: Unrecognized proxy type: "${type}"`
);
}
},
proxyInfoFromProxyData(context, proxyData, defaultProxyInfo) {
switch (typeof proxyData) {
case "string":
let proxyRules = [];
try {
for (let result of proxyData.split(";")) {
proxyRules.push(
ProxyInfoData.parseProxyInfoDataFromPAC(result.trim())
);
}
} catch (e) {
// If we have valid proxies already, lets use them and just emit
// errors for the failovers.
if (proxyRules.length === 0) {
throw e;
}
let error = context.normalizeError(e);
context.extension.emit("proxy-error", {
message: error.message,
fileName: error.fileName,
lineNumber: error.lineNumber,
stack: error.stack,
});
}
proxyData = proxyRules;
// fall through
case "object":
if (Array.isArray(proxyData) && proxyData.length > 0) {
return ProxyInfoData.createProxyInfoFromData(
proxyData,
defaultProxyInfo
);
}
// Not an array, fall through to error.
default:
throw new ExtensionError(
"ProxyInfoData: proxyData must be a string or array of objects"
);
}
},
};
function normalizeFilter(filter) {
@ -502,195 +391,3 @@ class ProxyChannelFilter {
ProxyService.unregisterFilter(this);
}
}
class ProxyScriptContext extends BaseContext {
constructor(extension, url, contextInfo = {}) {
super("proxy_script", extension);
this.contextInfo = contextInfo;
this.extension = extension;
this.messageManager = Services.cpmm;
this.sandbox = Cu.Sandbox(this.extension.principal, {
sandboxName: `Extension Proxy Script (${
extension.policy.debugName
}): ${url}`,
metadata: { addonID: extension.id },
});
this.url = url;
this.FindProxyForURL = null;
}
/**
* Loads and validates a proxy script into the sandbox, and then
* registers a new proxy filter for the context.
*
* @returns {boolean} true if load succeeded; false otherwise.
*/
load() {
Schemas.exportLazyGetter(this.sandbox, "browser", () => this.browserObj);
try {
Services.scriptloader.loadSubScript(this.url, this.sandbox);
} catch (error) {
this.extension.emit("proxy-error", {
message: this.normalizeError(error).message,
});
return false;
}
this.FindProxyForURL = Cu.unwaiveXrays(this.sandbox.FindProxyForURL);
if (typeof this.FindProxyForURL !== "function") {
this.extension.emit("proxy-error", {
message: "The proxy script must define FindProxyForURL as a function",
});
return false;
}
ProxyService.registerChannelFilter(
this /* nsIProtocolProxyChannelFilter aFilter */,
0 /* unsigned long aPosition */
);
return true;
}
get principal() {
return this.extension.principal;
}
get cloneScope() {
return this.sandbox;
}
/**
* This method (which is required by the nsIProtocolProxyService interface)
* is called to apply proxy filter rules for the given URI and proxy object
* (or list of proxy objects).
*
* @param {nsIProtocolProxyService} service A reference to the Protocol Proxy Service.
* @param {nsIChannel} channel The channel for which these proxy settings apply.
* @param {nsIProxyInfo} defaultProxyInfo The proxy (or list of proxies) that
* would be used by default for the given URI. This may be null.
* @param {nsIProxyProtocolFilterResult} proxyFilter to call
on with the proxy info to apply for the given URI.
*/
applyFilter(service, channel, defaultProxyInfo, proxyFilter) {
let proxyInfo;
try {
let wrapper = ChannelWrapper.get(channel);
if (
this.extension.policy.privateBrowsingAllowed ||
wrapper.loadInfo.originAttributes.privateBrowsingId == 0
) {
let uri = wrapper.finalURI;
// TODO Bug 1337001 - provide path and query components to non-https URLs.
let ret = this.FindProxyForURL(uri.prePath, uri.host, this.contextInfo);
proxyInfo = ProxyInfoData.proxyInfoFromProxyData(
this,
ret,
defaultProxyInfo
);
}
} catch (e) {
let error = this.normalizeError(e);
this.extension.emit("proxy-error", {
message: error.message,
fileName: error.fileName,
lineNumber: error.lineNumber,
stack: error.stack,
});
} finally {
// FindProxyForURL may return nothing, null, or proxyInfo.
proxyFilter.onProxyFilterResult(
proxyInfo !== undefined ? proxyInfo : defaultProxyInfo
);
}
}
/**
* Unloads the proxy filter and shuts down the sandbox.
*/
unload() {
super.unload();
ProxyService.unregisterFilter(this);
Cu.nukeSandbox(this.sandbox);
this.sandbox = null;
}
}
class ProxyScriptAPIManager extends SchemaAPIManager {
constructor() {
super("proxy", Schemas);
this.initialized = false;
}
lazyInit() {
if (!this.initialized) {
this.initGlobal();
let entries = Services.catMan.enumerateCategory(
CATEGORY_EXTENSION_SCRIPTS_CONTENT
);
for (let { value } of entries) {
this.loadScript(value);
}
this.initialized = true;
}
}
}
class ProxyScriptInjectionContext {
constructor(context, apiCan) {
this.context = context;
this.localAPIs = apiCan.root;
this.apiCan = apiCan;
}
shouldInject(namespace, name, allowedContexts) {
if (this.context.envType !== "proxy_script") {
throw new Error(`Unexpected context type "${this.context.envType}"`);
}
// Do not generate proxy script APIs unless explicitly allowed.
return allowedContexts.includes("proxy");
}
getImplementation(namespace, name) {
this.apiCan.findAPIPath(`${namespace}.${name}`);
let obj = this.apiCan.findAPIPath(namespace);
if (obj && name in obj) {
return new LocalAPIImplementation(obj, name, this.context);
}
}
get cloneScope() {
return this.context.cloneScope;
}
get principal() {
return this.context.principal;
}
}
defineLazyGetter(ProxyScriptContext.prototype, "messenger", function() {
let sender = { id: this.extension.id, frameId: this.frameId, url: this.url };
let filter = { extensionId: this.extension.id, toProxyScript: true };
return new ExtensionChild.Messenger(
this,
[this.messageManager],
sender,
filter
);
});
let proxyScriptAPIManager = new ProxyScriptAPIManager();
defineLazyGetter(ProxyScriptContext.prototype, "browserObj", function() {
let localAPIs = {};
let can = new CanOfAPIs(this, proxyScriptAPIManager, localAPIs);
proxyScriptAPIManager.lazyInit();
let browserObj = Cu.createObjectIn(this.sandbox);
let injectionContext = new ProxyScriptInjectionContext(this, can);
proxyScriptAPIManager.schema.inject(browserObj, injectionContext);
return browserObj;
});

View File

@ -1047,6 +1047,10 @@ const FORMATS = {
contentSecurityPolicy(string, context) {
let error = contentPolicyService.validateAddonCSP(string);
if (error != null) {
// The SyntaxError raised below is not reported as part of the "choices" error message,
// we log the CSP validation error explicitly here to make it easier for the addon developers
// to see and fix the extension CSP.
context.logError(`Error processing ${context.currentTarget}: ${error}`);
throw new SyntaxError(error);
}
return string;
@ -3151,7 +3155,7 @@ class SchemaRoots extends Namespaces {
return results[0];
}
if (results.length > 0) {
if (results.length) {
return new Namespaces(this.root, name, name.split("."), results);
}
return null;

View File

@ -127,7 +127,8 @@ WebExtensionPolicy::WebExtensionPolicy(GlobalObject& aGlobal,
: mId(NS_AtomizeMainThread(aInit.mId)),
mHostname(aInit.mMozExtensionHostname),
mName(aInit.mName),
mContentSecurityPolicy(aInit.mContentSecurityPolicy),
mExtensionPageCSP(aInit.mExtensionPageCSP),
mContentScriptCSP(aInit.mContentScriptCSP),
mLocalizeCallback(aInit.mLocalizeCallback),
mIsPrivileged(aInit.mIsPrivileged),
mPermissions(new AtomSet(aInit.mPermissions)) {
@ -154,8 +155,12 @@ WebExtensionPolicy::WebExtensionPolicy(GlobalObject& aGlobal,
aInit.mBackgroundScripts.Value());
}
if (mContentSecurityPolicy.IsVoid()) {
EPS().DefaultCSP(mContentSecurityPolicy);
if (mExtensionPageCSP.IsVoid()) {
EPS().GetDefaultCSP(mExtensionPageCSP);
}
if (mContentScriptCSP.IsVoid()) {
EPS().GetDefaultCSP(mContentScriptCSP);
}
mContentScripts.SetCapacity(aInit.mContentScripts.Length());

View File

@ -99,12 +99,11 @@ class WebExtensionPolicy final : public nsISupports,
const nsString& Name() const { return mName; }
void GetName(nsAString& aName) const { aName = mName; }
const nsString& ContentSecurityPolicy() const {
return mContentSecurityPolicy;
}
void GetContentSecurityPolicy(nsAString& aCSP) const {
aCSP = mContentSecurityPolicy;
}
const nsString& ExtensionPageCSP() const { return mExtensionPageCSP; }
void GetExtensionPageCSP(nsAString& aCSP) const { aCSP = mExtensionPageCSP; }
const nsString& ContentScriptCSP() const { return mContentScriptCSP; }
void GetContentScriptCSP(nsAString& aCSP) const { aCSP = mContentScriptCSP; }
already_AddRefed<MatchPatternSet> AllowedOrigins() {
return do_AddRef(mHostPermissions);
@ -176,7 +175,8 @@ class WebExtensionPolicy final : public nsISupports,
nsCOMPtr<nsIURI> mBaseURI;
nsString mName;
nsString mContentSecurityPolicy;
nsString mExtensionPageCSP;
nsString mContentScriptCSP;
bool mActive = false;
bool mAllowPrivateBrowsingByDefault = true;

View File

@ -38,7 +38,6 @@ this.runtime = class extends ExtensionAPI {
}
function checkOptions(options) {
let toProxyScript = false;
if (typeof options !== "object") {
return [
false,
@ -47,21 +46,10 @@ this.runtime = class extends ExtensionAPI {
}
for (let key of Object.keys(options)) {
if (key === "toProxyScript") {
let value = options[key];
if (typeof value !== "boolean") {
return [
false,
"runtime.sendMessage's options.toProxyScript argument is invalid",
];
}
toProxyScript = value;
} else {
return [false, `Unexpected property ${key}`];
}
return [false, `Unexpected property ${key}`];
}
return [true, { toProxyScript }];
return [true, {}];
}
if (!args.length) {

View File

@ -10,57 +10,26 @@ ChromeUtils.defineModuleGetter(
"ExtensionStorageIDB",
"resource://gre/modules/ExtensionStorageIDB.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"ExtensionTelemetry",
"resource://gre/modules/ExtensionTelemetry.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Services",
"resource://gre/modules/Services.jsm"
);
// Wrap a storage operation in a TelemetryStopWatch.
async function measureOp(telemetryMetric, extension, fn) {
const stopwatchKey = {};
telemetryMetric.stopwatchStart(extension, stopwatchKey);
try {
let result = await fn();
telemetryMetric.stopwatchFinish(extension, stopwatchKey);
return result;
} catch (err) {
telemetryMetric.stopwatchCancel(extension, stopwatchKey);
throw err;
}
}
this.storage = class extends ExtensionAPI {
getLocalFileBackend(context, { deserialize, serialize }) {
return {
get(keys) {
return measureOp(
ExtensionTelemetry.storageLocalGetJSON,
context.extension,
() => {
return context.childManager
.callParentAsyncFunction("storage.local.JSONFileBackend.get", [
serialize(keys),
])
.then(deserialize);
}
);
return context.childManager
.callParentAsyncFunction("storage.local.JSONFileBackend.get", [
serialize(keys),
])
.then(deserialize);
},
set(items) {
return measureOp(
ExtensionTelemetry.storageLocalSetJSON,
context.extension,
() => {
return context.childManager.callParentAsyncFunction(
"storage.local.JSONFileBackend.set",
[serialize(items)]
);
}
return context.childManager.callParentAsyncFunction(
"storage.local.JSONFileBackend.set",
[serialize(items)]
);
},
remove(keys) {
@ -99,31 +68,19 @@ this.storage = class extends ExtensionAPI {
}
return {
get(keys) {
return measureOp(
ExtensionTelemetry.storageLocalGetIDB,
context.extension,
async () => {
const db = await getDB();
return db.get(keys);
}
);
async get(keys) {
const db = await getDB();
return db.get(keys);
},
set(items) {
return measureOp(
ExtensionTelemetry.storageLocalSetIDB,
context.extension,
async () => {
const db = await getDB();
const changes = await db.set(items, {
serialize: ExtensionStorage.serialize,
});
async set(items) {
const db = await getDB();
const changes = await db.set(items, {
serialize: ExtensionStorage.serialize,
});
if (changes) {
fireOnChanged(changes);
}
}
);
if (changes) {
fireOnChanged(changes);
}
},
async remove(keys) {
const db = await getDB();

View File

@ -35,27 +35,27 @@ extensions.registerModules({
},
extension: {
url: "chrome://extensions/content/child/ext-extension.js",
scopes: ["addon_child", "content_child", "devtools_child", "proxy_script"],
scopes: ["addon_child", "content_child", "devtools_child"],
paths: [["extension"]],
},
i18n: {
url: "chrome://extensions/content/parent/ext-i18n.js",
scopes: ["addon_child", "content_child", "devtools_child", "proxy_script"],
scopes: ["addon_child", "content_child", "devtools_child"],
paths: [["i18n"]],
},
runtime: {
url: "chrome://extensions/content/child/ext-runtime.js",
scopes: ["addon_child", "content_child", "devtools_child", "proxy_script"],
scopes: ["addon_child", "content_child", "devtools_child"],
paths: [["runtime"]],
},
storage: {
url: "chrome://extensions/content/child/ext-storage.js",
scopes: ["addon_child", "content_child", "devtools_child", "proxy_script"],
scopes: ["addon_child", "content_child", "devtools_child"],
paths: [["storage"]],
},
test: {
url: "chrome://extensions/content/child/ext-test.js",
scopes: ["addon_child", "content_child", "devtools_child", "proxy_script"],
scopes: ["addon_child", "content_child", "devtools_child"],
paths: [["test"]],
},
userScripts: {

View File

@ -0,0 +1,6 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
ChromeUtils.import("resource://gre/modules/ExtensionProcessScript.jsm");

View File

@ -4,7 +4,7 @@
toolkit.jar:
% content extensions %content/extensions/
content/extensions/dummy.xul
content/extensions/dummy.xhtml
content/extensions/ext-browser-content.js
content/extensions/ext-toolkit.json
content/extensions/parent/ext-alarms.js (parent/ext-alarms.js)

View File

@ -17,12 +17,12 @@ EXTRA_JS_MODULES += [
'ExtensionPermissions.jsm',
'ExtensionPreferencesManager.jsm',
'ExtensionProcessScript.jsm',
'extensionProcessScriptLoader.js',
'ExtensionSettingsStore.jsm',
'ExtensionShortcuts.jsm',
'ExtensionStorage.jsm',
'ExtensionStorageIDB.jsm',
'ExtensionStorageSync.jsm',
'ExtensionTelemetry.jsm',
'ExtensionUtils.jsm',
'FindContent.jsm',
'MatchURLFilters.jsm',
@ -30,8 +30,9 @@ EXTRA_JS_MODULES += [
'MessageManagerProxy.jsm',
'NativeManifests.jsm',
'NativeMessaging.jsm',
'onExtensionBrowser.js',
'PerformanceCounters.jsm',
'ProxyScriptContext.jsm',
'ProxyChannelFilter.jsm',
'Schemas.jsm',
'WebNavigation.jsm',
'WebNavigationContent.js',

View File

@ -0,0 +1,6 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
Services.obs.notifyObservers(this, "tab-content-frameloader-created");

View File

@ -5,11 +5,6 @@ var { ExtensionParent } = ChromeUtils.import(
);
var { HiddenExtensionPage, promiseExtensionViewLoaded } = ExtensionParent;
ChromeUtils.defineModuleGetter(
this,
"ExtensionTelemetry",
"resource://gre/modules/ExtensionTelemetry.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"PrivateBrowsingUtils",
@ -42,8 +37,6 @@ class BackgroundPage extends HiddenExtensionPage {
async build() {
const { extension } = this;
ExtensionTelemetry.backgroundPageLoad.stopwatchStart(extension, this);
let context;
try {
await this.createBrowserElement();
@ -65,13 +58,11 @@ class BackgroundPage extends HiddenExtensionPage {
} catch (e) {
// Extension was down before the background page has loaded.
Cu.reportError(e);
ExtensionTelemetry.backgroundPageLoad.stopwatchCancel(extension, this);
EventManager.clearPrimedListeners(this.extension, false);
extension.emit("background-page-aborted");
return;
}
ExtensionTelemetry.backgroundPageLoad.stopwatchFinish(extension, this);
if (context) {
// Wait until all event listeners registered by the script so far
// to be handled.

View File

@ -79,7 +79,7 @@ class ContentScriptParent {
return blobURL;
};
if (details.js && details.js.length > 0) {
if (details.js && details.js.length) {
options.jsPaths = details.js.map(data => {
if (data.file) {
return data.file;
@ -89,7 +89,7 @@ class ContentScriptParent {
});
}
if (details.css && details.css.length > 0) {
if (details.css && details.css.length) {
options.cssPaths = details.css.map(data => {
if (data.file) {
return data.file;

View File

@ -19,7 +19,6 @@ var { ExtensionError } = ExtensionUtils;
const CONTAINER_PREF_INSTALL_DEFAULTS = {
"privacy.userContext.enabled": true,
"privacy.userContext.longPressBehavior": 2,
"privacy.userContext.ui.enabled": true,
"privacy.usercontext.about_newtab_segregation.enabled": true,
"privacy.userContext.extension": undefined,

View File

@ -556,7 +556,7 @@ this.downloads = class extends ExtensionAPI {
}
if (filename != null) {
if (filename.length == 0) {
if (!filename.length) {
return Promise.reject({ message: "filename must not be empty" });
}
@ -1067,7 +1067,7 @@ this.downloads = class extends ExtensionAPI {
};
}
});
if (Object.keys(changes).length > 0) {
if (Object.keys(changes).length) {
changes.id = item.id;
fire.async(changes);
}

View File

@ -57,7 +57,7 @@ this.permissions = class extends ExtensionAPI {
)
);
if (permissions.length == 0 && origins.length == 0) {
if (!permissions.length && !origins.length) {
return true;
}

View File

@ -4,15 +4,10 @@
"use strict";
ChromeUtils.defineModuleGetter(
this,
"ProxyScriptContext",
"resource://gre/modules/ProxyScriptContext.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"ProxyChannelFilter",
"resource://gre/modules/ProxyScriptContext.jsm"
"resource://gre/modules/ProxyChannelFilter.jsm"
);
var { ExtensionPreferencesManager } = ChromeUtils.import(
"resource://gre/modules/ExtensionPreferencesManager.jsm"
@ -21,9 +16,6 @@ var { ExtensionPreferencesManager } = ChromeUtils.import(
var { ExtensionError } = ExtensionUtils;
var { getSettingsAPI } = ExtensionPreferencesManager;
// WeakMap[Extension -> ProxyScriptContext]
const proxyScriptContextMap = new WeakMap();
const proxySvc = Ci.nsIProtocolProxyService;
const PROXY_TYPES_MAP = new Map([
@ -134,16 +126,6 @@ function registerProxyFilterEvent(
}
this.proxy = class extends ExtensionAPI {
onShutdown() {
let { extension } = this;
let proxyScriptContext = proxyScriptContextMap.get(extension);
if (proxyScriptContext) {
proxyScriptContext.unload();
proxyScriptContextMap.delete(extension);
}
}
primeListener(extension, event, fire, params) {
if (event === "onRequest") {
return registerProxyFilterEvent(undefined, extension, fire, ...params);
@ -153,45 +135,8 @@ this.proxy = class extends ExtensionAPI {
getAPI(context) {
let { extension } = context;
// Leaving as non-persistent. By itself it's not useful since proxy-error
// is emitted from the proxy filter.
let onError = new EventManager({
context,
name: "proxy.onError",
register: fire => {
let listener = (name, error) => {
fire.async(error);
};
extension.on("proxy-error", listener);
return () => {
extension.off("proxy-error", listener);
};
},
}).api();
return {
proxy: {
register(url) {
this.unregister();
let proxyScriptContext = new ProxyScriptContext(extension, url);
if (proxyScriptContext.load()) {
proxyScriptContextMap.set(extension, proxyScriptContext);
}
},
unregister() {
// Unload the current proxy script if one is loaded.
if (proxyScriptContextMap.has(extension)) {
proxyScriptContextMap.get(extension).unload();
proxyScriptContextMap.delete(extension);
}
},
registerProxyScript(url) {
this.register(url);
},
onRequest: new EventManager({
context,
name: `proxy.onRequest`,
@ -210,10 +155,21 @@ this.proxy = class extends ExtensionAPI {
},
}).api(),
onError,
// TODO Bug 1388619 deprecate onProxyError.
onProxyError: onError,
// Leaving as non-persistent. By itself it's not useful since proxy-error
// is emitted from the proxy filter.
onError: new EventManager({
context,
name: "proxy.onError",
register: fire => {
let listener = (name, error) => {
fire.async(error);
};
extension.on("proxy-error", listener);
return () => {
extension.off("proxy-error", listener);
};
},
}).api(),
settings: Object.assign(
getSettingsAPI(
@ -301,7 +257,7 @@ this.proxy = class extends ExtensionAPI {
// Match what about:preferences does with proxy settings
// since the proxy service does not check the value
// of share_proxy_settings.
for (let prop of ["ftp", "ssl", "socks"]) {
for (let prop of ["ftp", "ssl"]) {
value[prop] = value.http;
}
}

View File

@ -841,14 +841,14 @@ class WindowBase {
}
/**
* @property {nsIXULWindow} xulWindow
* The nsIXULWindow object for this browser window.
* @property {nsIAppWindow} appWindow
* The nsIAppWindow object for this browser window.
* @readonly
*/
get xulWindow() {
get appWindow() {
return this.window.docShell.treeOwner
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIXULWindow);
.getInterface(Ci.nsIAppWindow);
}
/**
@ -874,7 +874,7 @@ class WindowBase {
* @readonly
*/
get type() {
let { chromeFlags } = this.xulWindow;
let { chromeFlags } = this.appWindow;
if (chromeFlags & Ci.nsIWebBrowserChrome.CHROME_OPENAS_DIALOG) {
return "popup";

View File

@ -1,9 +1,5 @@
"use strict";
// This file expects tabTracker to be defined in the global scope (e.g.
// by ext-utils.js).
/* global tabTracker */
ChromeUtils.defineModuleGetter(
this,
"WebRequest",
@ -21,27 +17,7 @@ function registerEvent(
remoteTab = null
) {
let listener = async data => {
let browserData = { tabId: -1, windowId: -1 };
if (data.browser) {
browserData = tabTracker.getBrowserData(data.browser);
}
if (filter.tabId != null && browserData.tabId != filter.tabId) {
return;
}
if (filter.windowId != null && browserData.windowId != filter.windowId) {
return;
}
let event = data.serialize(eventName);
event.tabId = browserData.tabId;
if (data.originAttributes) {
event.incognito = data.originAttributes.privateBrowsingId > 0;
if (extension.hasPermission("cookies")) {
event.cookieStoreId = getCookieStoreIdForOriginAttributes(
data.originAttributes
);
}
}
let event = data.serialize(eventName, extension);
if (data.registerTraceableChannel) {
// If this is a primed listener, no tabParent was passed in here,
// but the convert() callback later in this function will be called
@ -74,10 +50,10 @@ function registerEvent(
if (filter.types) {
filter2.types = filter.types;
}
if (filter.tabId) {
if (filter.tabId !== undefined) {
filter2.tabId = filter.tabId;
}
if (filter.windowId) {
if (filter.windowId !== undefined) {
filter2.windowId = filter.windowId;
}
if (filter.incognito !== undefined) {
@ -106,7 +82,7 @@ function registerEvent(
let listenerDetails = {
addonId: extension.id,
extension: extension.policy,
policy: extension.policy,
blockingAllowed,
};
WebRequest[eventName].addListener(listener, filter2, info2, listenerDetails);
@ -166,7 +142,7 @@ this.webRequest = class extends ExtensionAPI {
getSecurityInfo: function(requestId, options = {}) {
return WebRequest.getSecurityInfo({
id: requestId,
extension: context.extension.policy,
policy: context.extension.policy,
remoteTab: context.xulBrowser.frameLoader.remoteTab,
options,
});

View File

@ -178,10 +178,29 @@
},
"content_security_policy": {
"type": "string",
"optional": true,
"format": "contentSecurityPolicy",
"onError": "warn"
"onError": "warn",
"choices": [
{
"type": "string",
"format": "contentSecurityPolicy"
},
{
"type": "object",
"properties": {
"extension_pages": {
"type": "string",
"optional": true,
"format": "contentSecurityPolicy"
},
"content_scripts": {
"type": "string",
"optional": true,
"format": "contentSecurityPolicy"
}
}
}
]
},
"permissions": {

View File

@ -15,7 +15,7 @@
},
{
"namespace": "proxy",
"description": "Use the browser.proxy API to register proxy scripts in Firefox. Proxy scripts in Firefox are proxy auto-config files with extra contextual information and support for additional return types.",
"description": "Provides access to global proxy settings for Firefox and proxy event listeners to handle dynamic proxy implementations.",
"permissions": ["proxy"],
"types": [
{
@ -96,44 +96,6 @@
"description": "Configures proxy settings. This setting's value is an object of type ProxyConfig."
}
},
"functions": [
{
"name": "register",
"type": "function",
"deprecated": "proxy.register has been deprecated and will be removed in Firefox 71.",
"description": "Registers the proxy script for the extension.",
"async": true,
"parameters": [
{
"name": "url",
"type": "string",
"format": "strictRelativeUrl"
}
]
},
{
"name": "unregister",
"type": "function",
"deprecated": "proxy.unregister has been deprecated and will be removed in Firefox 71.",
"description": "Unregisters the proxy script for the extension.",
"async": true,
"parameters": []
},
{
"name": "registerProxyScript",
"type": "function",
"deprecated": "proxy.registerProxyScript has been deprecated and will be removed in Firefox 71.",
"description": "Registers the proxy script for the extension. This is an alias for proxy.register.",
"async": true,
"parameters": [
{
"name": "url",
"type": "string",
"format": "strictRelativeUrl"
}
]
}
],
"events": [
{
"name": "onRequest",

View File

@ -19,7 +19,7 @@
},
{
"namespace": "runtime",
"allowedContexts": ["content", "devtools", "proxy"],
"allowedContexts": ["content", "devtools"],
"description": "Use the <code>browser.runtime</code> API to retrieve the background page, return details about the manifest, and listen for and respond to events in the app or extension lifecycle. You can also use this API to convert the relative path of URLs to fully-qualified URLs.",
"types": [
{
@ -334,7 +334,7 @@
"name": "sendMessage",
"type": "function",
"allowAmbiguousOptionalArguments": true,
"allowedContexts": ["content", "devtools", "proxy"],
"allowedContexts": ["content", "devtools"],
"description": "Sends a single message to event listeners within your extension/app or a different extension/app. Similar to $(ref:runtime.connect) but only sends a single message, with an optional response. If sending to your extension, the $(ref:runtime.onMessage) event will be fired in each page, or $(ref:runtime.onMessageExternal), if a different extension. Note that extensions cannot send messages to content scripts using this method. To send messages to content scripts, use $(ref:tabs.sendMessage).",
"async": "responseCallback",
"parameters": [
@ -344,8 +344,7 @@
"type": "object",
"name": "options",
"properties": {
"includeTlsChannelId": { "type": "boolean", "optional": true, "unsupported": true, "description": "Whether the TLS channel ID will be passed into onMessageExternal for processes that are listening for the connection event." },
"toProxyScript": { "type": "boolean", "optional": true, "description": "If true, the message will be directed to the extension's proxy sandbox."}
"includeTlsChannelId": { "type": "boolean", "optional": true, "unsupported": true, "description": "Whether the TLS channel ID will be passed into onMessageExternal for processes that are listening for the connection event." }
},
"optional": true
},
@ -553,7 +552,7 @@
{
"name": "onMessage",
"type": "function",
"allowedContexts": ["content", "devtools", "proxy"],
"allowedContexts": ["content", "devtools"],
"description": "Fired when a message is sent from either an extension process or a content script.",
"parameters": [
{"name": "message", "type": "any", "optional": true, "description": "The message sent by the calling script."},

View File

@ -41,7 +41,6 @@
"object",
"object_subrequest",
"xmlhttprequest",
"xbl",
"xslt",
"ping",
"beacon",
@ -115,7 +114,7 @@
"type": "array",
"optional": true,
"description": "A list of request types. Requests that cannot match any of the types will be filtered out.",
"items": { "$ref": "ResourceType" },
"items": { "$ref": "ResourceType", "onError": "warn" },
"minItems": 1
},
"tabId": { "type": "integer", "optional": true },

View File

@ -41,3 +41,4 @@ skip-if = verify
[browser_ext_themes_findbar.js]
[browser_ext_themes_warnings.js]
[browser_ext_themes_highlight.js]
[browser_ext_windows_popup_title.js]

View File

@ -54,10 +54,8 @@ add_task(async function test_popup_styling(browser, accDoc) {
// Open the information arrow panel
await openIdentityPopup();
let arrowContent = document.getAnonymousElementByAttribute(
gIdentityHandler._identityPopup,
"class",
"panel-arrowcontent"
let arrowContent = gIdentityHandler._identityPopup.shadowRoot.querySelector(
".panel-arrowcontent"
);
let arrowContentComputedStyle = window.getComputedStyle(arrowContent);
// Ensure popup background color was set properly

View File

@ -101,26 +101,32 @@ add_task(async function test_popup_url() {
"Should get maxResults=" + maxResults + " results"
);
let popup = UrlbarTestUtils.getPanel(window);
let popupCS = window.getComputedStyle(popup);
let popup = gURLBar.view.panel;
Assert.equal(
popupCS.backgroundColor,
`rgb(${hexToRGB(POPUP_COLOR).join(", ")})`,
`Popup background color should be set to ${POPUP_COLOR}`
);
if (!gURLBar.megabar) {
// The urlbar popup supports these colors only with the legacy non-megabar
// design. With megabar, the popup visually extends the textbox and use its
// colors.
let popupCS = window.getComputedStyle(popup);
Assert.equal(
popupCS.borderBottomColor,
`rgb(${hexToRGB(CHROME_CONTENT_SEPARATOR_COLOR).join(", ")})`,
`Popup bottom color should be set to ${CHROME_CONTENT_SEPARATOR_COLOR}`
);
Assert.equal(
popupCS.backgroundColor,
`rgb(${hexToRGB(POPUP_COLOR).join(", ")})`,
`Popup background color should be set to ${POPUP_COLOR}`
);
Assert.equal(
popupCS.color,
`rgb(${hexToRGB(POPUP_TEXT_COLOR_DARK).join(", ")})`,
`Popup color should be set to ${POPUP_TEXT_COLOR_DARK}`
);
Assert.equal(
popupCS.borderBottomColor,
`rgb(${hexToRGB(CHROME_CONTENT_SEPARATOR_COLOR).join(", ")})`,
`Popup bottom color should be set to ${CHROME_CONTENT_SEPARATOR_COLOR}`
);
Assert.equal(
popupCS.color,
`rgb(${hexToRGB(POPUP_TEXT_COLOR_DARK).join(", ")})`,
`Popup color should be set to ${POPUP_TEXT_COLOR_DARK}`
);
}
// Set the selected attribute to true to test the highlight popup properties
UrlbarTestUtils.setSelectedIndex(window, 1);
@ -158,8 +164,8 @@ add_task(async function test_popup_url() {
let root = document.documentElement;
Assert.equal(
root.getAttribute("lwt-popup-brighttext"),
"",
root.hasAttribute("lwt-popup-brighttext"),
false,
"brighttext should not be set!"
);
Assert.equal(
@ -200,12 +206,16 @@ add_task(async function test_popup_url() {
await extension.startup();
popupCS = window.getComputedStyle(popup);
Assert.equal(
popupCS.color,
`rgb(${hexToRGB(POPUP_TEXT_COLOR_BRIGHT).join(", ")})`,
`Popup color should be set to ${POPUP_TEXT_COLOR_BRIGHT}`
);
if (!gURLBar.megabar) {
// The urlbar popup supports this color only with the legacy non-megabar
// design. With megabar, the popup visually extends the textbox and use its
// colors.
Assert.equal(
window.getComputedStyle(popup).color,
`rgb(${hexToRGB(POPUP_TEXT_COLOR_BRIGHT).join(", ")})`,
`Popup color should be set to ${POPUP_TEXT_COLOR_BRIGHT}`
);
}
Assert.equal(
window.getComputedStyle(urlResult.element.url).color,
@ -233,8 +243,8 @@ add_task(async function test_popup_url() {
"brighttext should be set to true!"
);
Assert.equal(
root.getAttribute("lwt-popup-darktext"),
"",
root.hasAttribute("lwt-popup-darktext"),
false,
"darktext should not be set!"
);
@ -243,26 +253,29 @@ add_task(async function test_popup_url() {
// Check to see if popup-brighttext and secondary color are not set after
// unload of theme
Assert.equal(
root.getAttribute("lwt-popup-brighttext"),
"",
root.hasAttribute("lwt-popup-brighttext"),
false,
"brighttext should not be set!"
);
Assert.equal(
root.getAttribute("lwt-popup-darktext"),
"",
root.hasAttribute("lwt-popup-darktext"),
false,
"darktext should not be set!"
);
// Calculate what GrayText should be. May differ between platforms.
let span = document.createXULElement("span");
span.style.color = "GrayText";
document.documentElement.appendChild(span);
let GRAY_TEXT = window.getComputedStyle(span).color;
span.remove();
// Calculate what GrayText should be. Differs between platforms.
// We don't use graytext for urlbar results on Mac as it's too faint.
if (AppConstants.platform != "macosx") {
let span = document.createXULElement("span");
span.style.color = "GrayText";
document.documentElement.appendChild(span);
let GRAY_TEXT = window.getComputedStyle(span).color;
span.remove();
Assert.equal(
window.getComputedStyle(urlResult.element.separator, ":before").color,
GRAY_TEXT,
`Urlbar popup separator color should be set to ${GRAY_TEXT}`
);
Assert.equal(
window.getComputedStyle(urlResult.element.separator, ":before").color,
GRAY_TEXT,
`Urlbar popup separator color should be set to ${GRAY_TEXT}`
);
}
});

View File

@ -29,10 +29,9 @@ add_task(async function test_support_selection() {
});
await extension.startup();
let urlBar = document.querySelector("#urlbar");
let fields = [
document.getAnonymousElementByAttribute(urlBar, "anonid", "input"),
gURLBar.inputField,
document.querySelector("#searchbar .searchbar-textbox"),
].filter(field => {
let bounds = field.getBoundingClientRect();

View File

@ -21,11 +21,7 @@ add_task(async function test_support_tab_line() {
info("Checking selected tab line color");
let selectedTab = document.querySelector(".tabbrowser-tab[selected]");
let line = document.getAnonymousElementByAttribute(
selectedTab,
"class",
"tab-line"
);
let line = selectedTab.querySelector(".tab-line");
Assert.equal(
window.getComputedStyle(line).backgroundColor,
`rgb(${hexToRGB(TAB_LINE_COLOR).join(", ")})`,

View File

@ -34,11 +34,7 @@ add_task(async function test_support_tab_loading_filling() {
selectedTab.setAttribute("busy", "true");
selectedTab.setAttribute("progress", "true");
let throbber = document.getAnonymousElementByAttribute(
selectedTab,
"class",
"tab-throbber"
);
let throbber = selectedTab.throbber;
Assert.equal(
window.getComputedStyle(throbber, "::before").fill,
`rgb(${hexToRGB(TAB_LOADING_COLOR).join(", ")})`,

View File

@ -24,21 +24,13 @@ add_task(async function test_tab_background_color_property() {
let openTab = document.querySelector(
".tabbrowser-tab[visuallyselected=true]"
);
let openTabBackground = document.getAnonymousElementByAttribute(
openTab,
"class",
"tab-background"
);
let openTabBackground = openTab.querySelector(".tab-background");
let selectedTab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"about:blank"
);
let selectedTabBackground = document.getAnonymousElementByAttribute(
selectedTab,
"class",
"tab-background"
);
let selectedTabBackground = selectedTab.querySelector(".tab-background");
let openTabGradient = window
.getComputedStyle(openTabBackground)

View File

@ -31,11 +31,7 @@ add_task(async function test_button_background_properties() {
await extension.startup();
let toolbarButton = document.querySelector("#home-button");
let toolbarButtonIcon = document.getAnonymousElementByAttribute(
toolbarButton,
"class",
"toolbarbutton-icon"
);
let toolbarButtonIcon = toolbarButton.icon;
let toolbarButtonIconCS = window.getComputedStyle(toolbarButtonIcon);
InspectorUtils.addPseudoClassLock(toolbarButton, ":hover");

View File

@ -0,0 +1,61 @@
"use strict";
// Check that extension popup windows contain the name of the extension
// as well as the title of the loaded document, but not the URL.
add_task(async function test_popup_title() {
const name = "custom_title_number_9_please";
const docTitle = "popup-test-title";
const extension = ExtensionTestUtils.loadExtension({
manifest: {
name,
permissions: ["tabs"],
},
async background() {
let popup;
// Called after the popup loads
browser.runtime.onMessage.addListener(async ({ docTitle }) => {
const { id } = await popup;
const { title } = await browser.windows.get(id);
browser.windows.remove(id);
browser.test.assertTrue(
title.includes(name),
"popup title must include extension name"
);
browser.test.assertTrue(
title.includes(docTitle),
"popup title must include extension document title"
);
browser.test.assertFalse(
title.includes("moz-extension:"),
"popup title must not include extension URL"
);
browser.test.notifyPass("popup-window-title");
});
popup = browser.windows.create({
url: "/index.html",
type: "popup",
});
},
files: {
"index.html": `<!doctype html>
<meta charset="utf-8">
<title>${docTitle}</title>,
<script src="index.js"></script>
`,
"index.js": `addEventListener(
"load",
() => browser.runtime.sendMessage({docTitle: document.title})
);`,
},
});
await extension.startup();
await extension.awaitFinish("popup-window-title");
await extension.unload();
});

View File

@ -19,6 +19,5 @@ sandbox.setAttribute("src", "file_simple_sandboxed_frame.html");
document.documentElement.appendChild(sandbox);
</script>
<img src="file_image_redirect.png"/>
<iframe src="data:text/plain,webRequestTest"/>
</body>
</html>

View File

@ -69,18 +69,7 @@ function background(events) {
// Retrieve the per-file/test expected values.
function getExpected(details) {
let url = new URL(details.url);
let filename;
if (url.protocol == "data:") {
// See bug 1471387
if (details.originUrl == "about:newtab") {
return;
}
// pathname is everything after protocol.
filename = url.pathname;
} else {
filename = url.pathname.split("/").pop();
}
let filename = url.pathname.split("/").pop();
if (ignore && ignore.includes(filename)) {
return;
}

View File

@ -17,7 +17,7 @@ add_task(async function webnav_unresolved_uri_on_expected_URI_scheme() {
let checkURLs;
browser.webNavigation.onCompleted.addListener(async msg => {
if (checkURLs.length > 0) {
if (checkURLs.length) {
let expectedURL = checkURLs.shift();
browser.test.assertEq(expectedURL, msg.url, "Got the expected URL");
await browser.tabs.remove(msg.tabId);

View File

@ -331,7 +331,7 @@ add_task(async function test_contentscript_clipboard_nocontents_read() {
// and read from it, the data transfer object contains an item of type
// text/plain and kind string, but we can't call getAsString on it to verify
// that at least it is an empty string because the callback never gets invoked.
if (dataT.items.length == 0 ||
if (!dataT.items.length ||
(dataT.items.length == 1 && dataT.items[0].type == "text/plain" &&
dataT.items[0].kind == "string")) {
browser.test.succeed("Read promise successfully resolved");

View File

@ -139,7 +139,7 @@ add_task(async function test_protocolHandler() {
let window = await BrowserTestUtils.domWindowOpened(undefined, win => {
return BrowserTestUtils.waitForEvent(win, "load", false, event => {
let win = event.target.defaultView;
return win.document.documentElement.getAttribute("id") === "handling";
return !!win.document.querySelector("dialog#handling");
});
});
let entry = window.document.getElementById("items").firstChild;

View File

@ -355,15 +355,6 @@ add_task(async function test_webRequest_tabId_browser() {
add_task(async function test_webRequest_frames() {
let expect = {
"text/plain,webRequestTest": {
type: "sub_frame",
events: ["onBeforeRequest", "onCompleted"],
},
"text/plain,webRequestTest_bad": {
type: "sub_frame",
events: ["onBeforeRequest", "onCompleted"],
cancel: "onBeforeRequest",
},
"redirection.sjs": {
status: 302,
type: "sub_frame",
@ -386,9 +377,6 @@ add_task(async function test_webRequest_frames() {
};
extension.sendMessage("set-expected", {expect, origin: location.href});
await extension.awaitMessage("continue");
addFrame("data:text/plain,webRequestTest");
addFrame("data:text/plain,webRequestTest_bad");
await extension.awaitMessage("cancelled");
addFrame("redirection.sjs");
addFrame("https://nonresolvablehostname.invalid/badrobot");
await extension.awaitMessage("done");

Some files were not shown because too many files have changed in this diff Show More