mirror of
https://github.com/Feodor2/Mypal68.git
synced 2025-06-18 06:45:44 -04:00
68.14.8 - toolkit
This commit is contained in:
parent
0841e6d37b
commit
047e832886
@ -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;
|
||||
|
@ -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"/>
|
||||
|
@ -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;
|
||||
|
@ -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"/>
|
||||
|
@ -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'))
|
||||
|
@ -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. -->
|
||||
|
@ -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);
|
@ -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);
|
@ -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 -->
|
@ -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);
|
@ -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"/>
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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") {
|
||||
|
@ -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;
|
||||
|
@ -63,7 +63,7 @@ AppPicker.prototype = {
|
||||
.setAttribute("value", description);
|
||||
document
|
||||
.getElementById("suggested-filename")
|
||||
.setAttribute("value", filename);
|
||||
.setAttribute("value", filename || "");
|
||||
document
|
||||
.getElementById("content-icon")
|
||||
.setAttribute(
|
||||
|
@ -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>
|
@ -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)
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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 ||
|
||||
|
@ -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
|
||||
);
|
||||
},
|
||||
};
|
@ -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',
|
||||
]
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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"
|
||||
);
|
||||
});
|
@ -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.
|
@ -5,5 +5,5 @@ support-files =
|
||||
ctypes_worker.js
|
||||
../unit/test_jsctypes.js
|
||||
|
||||
[test_ctypes.xul]
|
||||
[test_ctypes.xhtml]
|
||||
skip-if = verify
|
||||
|
@ -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>
|
||||
|
@ -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)");
|
||||
|
@ -743,7 +743,6 @@ this.DownloadHistoryList.prototype = {
|
||||
},
|
||||
|
||||
// nsINavHistoryResultObserver
|
||||
nodeAnnotationChanged() {},
|
||||
nodeIconChanged() {},
|
||||
nodeTitleChanged() {},
|
||||
nodeKeywordChanged() {},
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -208,7 +208,8 @@ ExtensionManager = {
|
||||
allowedOrigins: extension.whiteListedHosts,
|
||||
webAccessibleResources: extension.webAccessibleResources,
|
||||
|
||||
contentSecurityPolicy: extension.contentSecurityPolicy,
|
||||
extensionPageCSP: extension.extensionPageCSP,
|
||||
contentScriptCSP: extension.contentScriptCSP,
|
||||
|
||||
localizeCallback,
|
||||
|
||||
|
@ -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 };
|
||||
});
|
||||
}
|
||||
|
@ -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);
|
||||
},
|
||||
});
|
@ -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;
|
||||
},
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
});
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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: {
|
||||
|
@ -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");
|
@ -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)
|
||||
|
@ -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',
|
||||
|
6
toolkit/components/extensions/onExtensionBrowser.js
Normal file
6
toolkit/components/extensions/onExtensionBrowser.js
Normal 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");
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ this.permissions = class extends ExtensionAPI {
|
||||
)
|
||||
);
|
||||
|
||||
if (permissions.length == 0 && origins.length == 0) {
|
||||
if (!permissions.length && !origins.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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": {
|
||||
|
@ -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",
|
||||
|
@ -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."},
|
||||
|
@ -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 },
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -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();
|
||||
|
@ -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(", ")})`,
|
||||
|
@ -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(", ")})`,
|
||||
|
@ -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)
|
||||
|
@ -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");
|
||||
|
@ -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();
|
||||
});
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user