--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -1139,19 +1139,21 @@ xpc::CreateSandboxObject(JSContext* cx,
// Try to figure out any addon this sandbox should be associated with.
// The addon could have been passed in directly, as part of the metadata,
// or by being constructed from an addon's code.
JSAddonId* addonId = nullptr;
if (options.addonId) {
addonId = JS::NewAddonId(cx, options.addonId);
NS_ENSURE_TRUE(addonId, NS_ERROR_FAILURE);
- } else if (JSObject* obj = JS::CurrentGlobalOrNull(cx)) {
- if (JSAddonId* id = JS::AddonIdOfObject(obj))
- addonId = id;
+ } else if (principal == nsXPConnect::SystemPrincipal()) {
+ if (JSObject* obj = JS::CurrentGlobalOrNull(cx)) {
+ if (JSAddonId* id = JS::AddonIdOfObject(obj))
+ addonId = id;
+ }
}
creationOptions.setAddonId(addonId);
compartmentOptions.behaviors().setDiscardSource(options.discardSource);
const js::Class* clasp = options.writeToGlobalPrototype
? &SandboxWriteToProtoClass
deleted file mode 100644
--- a/toolkit/components/addoncompat/CompatWarning.jsm
+++ /dev/null
@@ -1,99 +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/.
-
-var EXPORTED_SYMBOLS = ["CompatWarning"];
-
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.import("resource://gre/modules/Preferences.jsm");
-
-function section(number, url) {
- const baseURL = "https://developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox/Limitations_of_chrome_scripts";
- return { number, url: baseURL + url };
-}
-
-var CompatWarning = {
- // Sometimes we want to generate a warning, but put off issuing it
- // until later. For example, if someone registers a listener, we
- // might only want to warn about it if the listener actually
- // fires. However, we want the warning to show a stack for the
- // registration site.
- delayedWarning(msg, addon, warning) {
- function isShimLayer(filename) {
- return filename.includes("CompatWarning.jsm") ||
- filename.includes("RemoteAddonsParent.jsm") ||
- filename.includes("RemoteAddonsChild.jsm") ||
- filename.includes("multiprocessShims.js");
- }
-
- let stack = Components.stack;
- while (stack && isShimLayer(stack.filename))
- stack = stack.caller;
-
- let alreadyWarned = false;
-
- return function() {
- if (alreadyWarned) {
- return;
- }
- alreadyWarned = true;
-
- if (addon) {
- let histogram = Services.telemetry.getKeyedHistogramById("ADDON_SHIM_USAGE");
- histogram.add(addon, warning ? warning.number : 0);
- }
-
- if (!Preferences.get("dom.ipc.shims.enabledWarnings", false))
- return;
-
- let error = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError);
- if (!error || !Services.console) {
- // Too late during shutdown to use the nsIConsole
- return;
- }
-
- let message = `Warning: ${msg}`;
- if (warning)
- message += `\nMore info at: ${warning.url}`;
-
- error.init(
- /* message*/ message,
- /* sourceName*/ stack ? stack.filename : "",
- /* sourceLine*/ stack ? stack.sourceLine : "",
- /* lineNumber*/ stack ? stack.lineNumber : 0,
- /* columnNumber*/ 0,
- /* flags*/ Ci.nsIScriptError.warningFlag,
- /* category*/ "chrome javascript");
- Services.console.logMessage(error);
-
- if (Preferences.get("dom.ipc.shims.dumpWarnings", false)) {
- dump(message + "\n");
- while (stack) {
- dump(stack + "\n");
- stack = stack.caller;
- }
- dump("\n");
- }
- };
- },
-
- warn(msg, addon, warning) {
- let delayed = this.delayedWarning(msg, addon, warning);
- delayed();
- },
-
- warnings: {
- content: section(1, "#gBrowser.contentWindow.2C_window.content..."),
- limitations_of_CPOWs: section(2, "#Limitations_of_CPOWs"),
- nsIContentPolicy: section(3, "#nsIContentPolicy"),
- nsIWebProgressListener: section(4, "#nsIWebProgressListener"),
- observers: section(5, "#Observers_in_the_chrome_process"),
- DOM_events: section(6, "#DOM_Events"),
- sandboxes: section(7, "#Sandboxes"),
- JSMs: section(8, "#JavaScript_code_modules_(JSMs)"),
- nsIAboutModule: section(9, "#nsIAboutModule"),
- // If more than 14 values appear here, you need to change the
- // ADDON_SHIM_USAGE histogram definition in Histograms.json.
- },
-};
--- a/toolkit/components/addoncompat/RemoteAddonsChild.jsm
+++ b/toolkit/components/addoncompat/RemoteAddonsChild.jsm
@@ -1,32 +1,16 @@
// 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/.
var EXPORTED_SYMBOLS = ["RemoteAddonsChild"];
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.defineModuleGetter(this, "BrowserUtils",
- "resource://gre/modules/BrowserUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "Prefetcher",
- "resource://gre/modules/Prefetcher.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "SystemPrincipal",
- "@mozilla.org/systemprincipal;1", "nsIPrincipal");
-
-XPCOMUtils.defineLazyServiceGetter(this, "contentSecManager",
- "@mozilla.org/contentsecuritymanager;1",
- "nsIContentSecurityManager");
-
-const TELEMETRY_SHOULD_LOAD_LOADING_KEY = "ADDON_CONTENT_POLICY_SHIM_BLOCKING_LOADING_MS";
-const TELEMETRY_SHOULD_LOAD_LOADED_KEY = "ADDON_CONTENT_POLICY_SHIM_BLOCKING_LOADED_MS";
-
// Similar to Python. Returns dict[key] if it exists. Otherwise,
// sets dict[key] to default_ and returns default_.
function setDefault(dict, key, default_) {
if (key in dict) {
return dict[key];
}
dict[key] = default_;
return default_;
@@ -139,502 +123,37 @@ var NotificationTracker = {
this._registered.delete(watcher);
},
getCount(component1) {
return this.findPaths([component1]).length;
},
};
-// This code registers an nsIContentPolicy in the child process. When
-// it runs, it notifies the parent that it needs to run its own
-// nsIContentPolicy list. If any policy in the parent rejects a
-// resource load, that answer is returned to the child.
-var ContentPolicyChild = {
- _classDescription: "Addon shim content policy",
- _classID: Components.ID("6e869130-635c-11e2-bcfd-0800200c9a66"),
- _contractID: "@mozilla.org/addon-child/policy;1",
-
- // A weak map of time spent blocked in hooks for a given document.
- // WeakMap[document -> Map[addonId -> timeInMS]]
- timings: new WeakMap(),
-
- init() {
- let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
- registrar.registerFactory(this._classID, this._classDescription, this._contractID, this);
-
- this.loadingHistogram = Services.telemetry.getKeyedHistogramById(TELEMETRY_SHOULD_LOAD_LOADING_KEY);
- this.loadedHistogram = Services.telemetry.getKeyedHistogramById(TELEMETRY_SHOULD_LOAD_LOADED_KEY);
-
- NotificationTracker.watch("content-policy", this);
- },
-
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPolicy, Ci.nsIObserver,
- Ci.nsIChannelEventSink, Ci.nsIFactory,
- Ci.nsISupportsWeakReference]),
-
- track(path, register) {
- let catMan = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
- if (register) {
- catMan.addCategoryEntry("content-policy", this._contractID, this._contractID, false, true);
- } else {
- catMan.deleteCategoryEntry("content-policy", this._contractID, false);
- }
- },
-
- // Returns a map of cumulative time spent in shouldLoad hooks for a
- // given add-on in the given node's document. May return null if
- // telemetry recording is disabled, or the given context does not
- // point to a document.
- getTimings(context) {
- if (!Services.telemetry.canRecordExtended) {
- return null;
- }
-
- let doc;
- if (context instanceof Ci.nsIDOMNode) {
- doc = context.ownerDocument;
- } else if (context instanceof Ci.nsIDOMDocument) {
- doc = context;
- } else if (context instanceof Ci.nsIDOMWindow) {
- doc = context.document;
- }
-
- if (!doc) {
- return null;
- }
-
- let map = this.timings.get(doc);
- if (!map) {
- // No timing object exists for this document yet. Create one, and
- // set up a listener to record the final values at the right time.
- map = new Map();
- this.timings.set(doc, map);
-
- // If the document is still loading, record aggregate pre-load
- // timings when the load event fires. If it's already loaded,
- // record aggregate post-load timings when the page is hidden.
- let eventName = doc.readyState == "complete" ? "pagehide" : "load";
-
- let listener = event => {
- if (event.target == doc) {
- event.currentTarget.removeEventListener(eventName, listener, true);
- this.logTelemetry(doc, eventName);
- }
- };
- doc.defaultView.addEventListener(eventName, listener, true);
- }
- return map;
- },
-
- // Logs the accumulated telemetry for the given document, into the
- // appropriate telemetry histogram based on the DOM event name that
- // triggered it.
- logTelemetry(doc, eventName) {
- let map = this.timings.get(doc);
- this.timings.delete(doc);
-
- let histogram = eventName == "load" ? this.loadingHistogram : this.loadedHistogram;
-
- for (let [addon, time] of map.entries()) {
- histogram.add(addon, time);
- }
- },
-
- shouldLoad(contentType, contentLocation, requestOrigin,
- node, mimeTypeGuess, extra, requestPrincipal) {
- let startTime = Cu.now();
-
- let addons = NotificationTracker.findSuffixes(["content-policy"]);
- let [prefetched, cpows] = Prefetcher.prefetch("ContentPolicy.shouldLoad",
- addons, {InitNode: node});
- cpows.node = node;
-
- let cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
- .getService(Ci.nsISyncMessageSender);
- let rval = cpmm.sendRpcMessage("Addons:ContentPolicy:Run", {
- contentType,
- contentLocation: contentLocation.spec,
- requestOrigin: requestOrigin ? requestOrigin.spec : null,
- mimeTypeGuess,
- requestPrincipal,
- prefetched,
- }, cpows);
-
- let timings = this.getTimings(node);
- if (timings) {
- let delta = Cu.now() - startTime;
-
- for (let addon of addons) {
- let old = timings.get(addon) || 0;
- timings.set(addon, old + delta);
- }
- }
-
- if (rval.length != 1) {
- return Ci.nsIContentPolicy.ACCEPT;
- }
-
- return rval[0];
- },
-
- shouldProcess(contentType, contentLocation, requestOrigin, insecNode, mimeType, extra) {
- return Ci.nsIContentPolicy.ACCEPT;
- },
-
- createInstance(outer, iid) {
- if (outer) {
- throw Cr.NS_ERROR_NO_AGGREGATION;
- }
- return this.QueryInterface(iid);
- },
-};
-
-// This is a shim channel whose only purpose is to return some string
-// data from an about: protocol handler.
-function AboutProtocolChannel(uri, contractID, loadInfo) {
- this.URI = uri;
- this.originalURI = uri;
- this._contractID = contractID;
- this._loadingPrincipal = loadInfo.loadingPrincipal;
- this._securityFlags = loadInfo.securityFlags;
- this._contentPolicyType = loadInfo.externalContentPolicyType;
-}
-
-AboutProtocolChannel.prototype = {
- contentCharset: "utf-8",
- contentLength: 0,
- owner: SystemPrincipal,
- securityInfo: null,
- notificationCallbacks: null,
- loadFlags: 0,
- loadGroup: null,
- name: null,
- status: Cr.NS_OK,
-
- asyncOpen(listener, context) {
- // Ask the parent to synchronously read all the data from the channel.
- let cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
- .getService(Ci.nsISyncMessageSender);
- let rval = cpmm.sendRpcMessage("Addons:AboutProtocol:OpenChannel", {
- uri: this.URI.spec,
- contractID: this._contractID,
- loadingPrincipal: this._loadingPrincipal,
- securityFlags: this._securityFlags,
- contentPolicyType: this._contentPolicyType
- }, {
- notificationCallbacks: this.notificationCallbacks,
- loadGroupNotificationCallbacks: this.loadGroup ? this.loadGroup.notificationCallbacks : null,
- });
-
- if (rval.length != 1) {
- throw Cr.NS_ERROR_FAILURE;
- }
-
- let {data, contentType} = rval[0];
- this.contentType = contentType;
-
- // Return the data via an nsIStringInputStream.
- let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
- stream.setData(data, data.length);
-
- let runnable = {
- run: () => {
- try {
- listener.onStartRequest(this, context);
- } catch (e) {}
- try {
- listener.onDataAvailable(this, context, stream, 0, stream.available());
- } catch (e) {}
- try {
- listener.onStopRequest(this, context, Cr.NS_OK);
- } catch (e) {}
- }
- };
- Services.tm.dispatchToMainThread(runnable);
- },
-
- asyncOpen2(listener) {
- // throws an error if security checks fail
- var outListener = contentSecManager.performSecurityCheck(this, listener);
- this.asyncOpen(outListener, null);
- },
-
- open() {
- throw Cr.NS_ERROR_NOT_IMPLEMENTED;
- },
-
- open2() {
- throw Cr.NS_ERROR_NOT_IMPLEMENTED;
- },
-
- isPending() {
- return false;
- },
-
- cancel() {
- throw Cr.NS_ERROR_NOT_IMPLEMENTED;
- },
-
- suspend() {
- throw Cr.NS_ERROR_NOT_IMPLEMENTED;
- },
-
- resume() {
- throw Cr.NS_ERROR_NOT_IMPLEMENTED;
- },
-
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel, Ci.nsIRequest])
-};
-
-// This shim protocol handler is used when content fetches an about: URL.
-function AboutProtocolInstance(contractID) {
- this._contractID = contractID;
- this._uriFlags = undefined;
-}
-
-AboutProtocolInstance.prototype = {
- createInstance(outer, iid) {
- if (outer != null) {
- throw Cr.NS_ERROR_NO_AGGREGATION;
- }
-
- return this.QueryInterface(iid);
- },
-
- getURIFlags(uri) {
- // Cache the result to avoid the extra IPC.
- if (this._uriFlags !== undefined) {
- return this._uriFlags;
- }
-
- let cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
- .getService(Ci.nsISyncMessageSender);
-
- let rval = cpmm.sendRpcMessage("Addons:AboutProtocol:GetURIFlags", {
- uri: uri.spec,
- contractID: this._contractID
- });
-
- if (rval.length != 1) {
- throw Cr.NS_ERROR_FAILURE;
- }
-
- this._uriFlags = rval[0];
- return this._uriFlags;
- },
-
- // We take some shortcuts here. Ideally, we would return a CPOW that
- // wraps the add-on's nsIChannel. However, many of the methods
- // related to nsIChannel are marked [noscript], so they're not
- // available to CPOWs. Consequently, we return a shim channel that,
- // when opened, asks the parent to open the channel and read out all
- // the data.
- newChannel(uri, loadInfo) {
- return new AboutProtocolChannel(uri, this._contractID, loadInfo);
- },
-
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory, Ci.nsIAboutModule])
-};
-
-var AboutProtocolChild = {
- _classDescription: "Addon shim about: protocol handler",
-
- init() {
- // Maps contractIDs to instances
- this._instances = new Map();
- // Maps contractIDs to classIDs
- this._classIDs = new Map();
- NotificationTracker.watch("about-protocol", this);
- },
-
- track(path, register) {
- let contractID = path[1];
- let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
- if (register) {
- let instance = new AboutProtocolInstance(contractID);
- let classID = Cc["@mozilla.org/uuid-generator;1"]
- .getService(Ci.nsIUUIDGenerator)
- .generateUUID();
-
- this._instances.set(contractID, instance);
- this._classIDs.set(contractID, classID);
- registrar.registerFactory(classID, this._classDescription, contractID, instance);
- } else {
- let instance = this._instances.get(contractID);
- let classID = this._classIDs.get(contractID);
- registrar.unregisterFactory(classID, instance);
- this._instances.delete(contractID);
- this._classIDs.delete(contractID);
- }
- },
-};
-
-// This code registers observers in the child whenever an add-on in
-// the parent asks for notifications on the given topic.
-var ObserverChild = {
- init() {
- NotificationTracker.watch("observer", this);
- },
-
- track(path, register) {
- let topic = path[1];
- if (register) {
- Services.obs.addObserver(this, topic);
- } else {
- Services.obs.removeObserver(this, topic);
- }
- },
-
- observe(subject, topic, data) {
- let cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
- .getService(Ci.nsISyncMessageSender);
- cpmm.sendRpcMessage("Addons:Observer:Run", {}, {
- topic,
- subject,
- data
- });
- }
-};
-
-// There is one of these objects per browser tab in the child. When an
-// add-on in the parent listens for an event, this child object
-// listens for that event in the child.
-function EventTargetChild(childGlobal) {
- this._childGlobal = childGlobal;
- this.capturingHandler = (event) => this.handleEvent(true, event);
- this.nonCapturingHandler = (event) => this.handleEvent(false, event);
- NotificationTracker.watch("event", this);
-}
-
-EventTargetChild.prototype = {
- uninit() {
- NotificationTracker.unwatch("event", this);
- },
-
- track(path, register) {
- let eventType = path[1];
- let useCapture = path[2];
- let listener = useCapture ? this.capturingHandler : this.nonCapturingHandler;
- if (register) {
- this._childGlobal.addEventListener(eventType, listener, useCapture, true);
- } else {
- this._childGlobal.removeEventListener(eventType, listener, useCapture);
- }
- },
-
- handleEvent(capturing, event) {
- let addons = NotificationTracker.findSuffixes(["event", event.type, capturing]);
- let [prefetched, cpows] = Prefetcher.prefetch("EventTarget.handleEvent",
- addons,
- {Event: event,
- Window: this._childGlobal.content});
- cpows.event = event;
- cpows.eventTarget = event.target;
-
- this._childGlobal.sendRpcMessage("Addons:Event:Run",
- {type: event.type,
- capturing,
- isTrusted: event.isTrusted,
- prefetched},
- cpows);
- }
-};
-
-// The parent can create a sandbox to run code in the child
-// process. We actually create the sandbox in the child so that the
-// code runs there. However, managing the lifetime of these sandboxes
-// can be tricky. The parent references these sandboxes using CPOWs,
-// which only keep weak references. So we need to create a strong
-// reference in the child. For simplicity, we kill off these strong
-// references whenever we navigate away from the page for which the
-// sandbox was created.
-function SandboxChild(chromeGlobal) {
- this.chromeGlobal = chromeGlobal;
- this.sandboxes = [];
-}
-
-SandboxChild.prototype = {
- uninit() {
- this.clearSandboxes();
- },
-
- addListener() {
- let webProgress = this.chromeGlobal.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebProgress);
- webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
- },
-
- removeListener() {
- let webProgress = this.chromeGlobal.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebProgress);
- webProgress.removeProgressListener(this);
- },
-
- onLocationChange(webProgress, request, location, flags) {
- this.clearSandboxes();
- },
-
- addSandbox(sandbox) {
- if (this.sandboxes.length == 0) {
- this.addListener();
- }
- this.sandboxes.push(sandbox);
- },
-
- clearSandboxes() {
- if (this.sandboxes.length) {
- this.removeListener();
- }
- this.sandboxes = [];
- },
-
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
- Ci.nsISupportsWeakReference])
-};
var RemoteAddonsChild = {
_ready: false,
makeReady() {
- let shims = [
- Prefetcher,
- NotificationTracker,
- ContentPolicyChild,
- AboutProtocolChild,
- ObserverChild,
- ];
-
- for (let shim of shims) {
- try {
- shim.init();
- } catch (e) {
- Cu.reportError(e);
- }
- }
},
init(global) {
if (!this._ready) {
if (!Services.cpmm.initialProcessData.remoteAddonsParentInitted) {
return null;
}
this.makeReady();
this._ready = true;
}
global.sendAsyncMessage("Addons:RegisterGlobal", {}, {global});
- let sandboxChild = new SandboxChild(global);
- global.addSandbox = sandboxChild.addSandbox.bind(sandboxChild);
-
- // Return this so it gets rooted in the content script.
- return [new EventTargetChild(global), sandboxChild];
+ return [];
},
uninit(perTabShims) {
for (let shim of perTabShims) {
try {
shim.uninit();
} catch (e) {
Cu.reportError(e);
--- a/toolkit/components/addoncompat/RemoteAddonsParent.jsm
+++ b/toolkit/components/addoncompat/RemoteAddonsParent.jsm
@@ -1,25 +1,17 @@
// 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/.
var EXPORTED_SYMBOLS = ["RemoteAddonsParent"];
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/RemoteWebProgress.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.defineModuleGetter(this, "NetUtil",
- "resource://gre/modules/NetUtil.jsm");
-ChromeUtils.defineModuleGetter(this, "Prefetcher",
- "resource://gre/modules/Prefetcher.jsm");
-ChromeUtils.defineModuleGetter(this, "CompatWarning",
- "resource://gre/modules/CompatWarning.jsm");
-
Cu.permitCPOWsInScope(this);
// Similar to Python. Returns dict[key] if it exists. Otherwise,
// sets dict[key] to default_ and returns default_.
function setDefault(dict, key, default_) {
if (key in dict) {
return dict[key];
}
@@ -88,878 +80,17 @@ function Interposition(name, base) {
this.setters = Object.create(base.setters);
} else {
this.methods = Object.create(null);
this.getters = Object.create(null);
this.setters = Object.create(null);
}
}
-// This object is responsible for notifying the child when a new
-// content policy is added or removed. It also runs all the registered
-// add-on content policies when the child asks it to do so.
-var ContentPolicyParent = {
- init() {
- let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
- .getService(Ci.nsIMessageBroadcaster);
- ppmm.addMessageListener("Addons:ContentPolicy:Run", this);
-
- this._policies = new Map();
- },
-
- addContentPolicy(addon, name, cid) {
- this._policies.set(name, cid);
- NotificationTracker.add(["content-policy", addon]);
- },
-
- removeContentPolicy(addon, name) {
- this._policies.delete(name);
- NotificationTracker.remove(["content-policy", addon]);
- },
-
- receiveMessage(aMessage) {
- switch (aMessage.name) {
- case "Addons:ContentPolicy:Run":
- return this.shouldLoad(aMessage.data, aMessage.objects);
- }
- return undefined;
- },
-
- shouldLoad(aData, aObjects) {
- for (let policyCID of this._policies.values()) {
- let policy;
- try {
- policy = Cc[policyCID].getService(Ci.nsIContentPolicy);
- } catch (e) {
- // Current Gecko behavior is to ignore entries that don't QI.
- continue;
- }
- try {
- let contentLocation = Services.io.newURI(aData.contentLocation);
- let requestOrigin = aData.requestOrigin ? Services.io.newURI(aData.requestOrigin) : null;
-
- let result = Prefetcher.withPrefetching(aData.prefetched, aObjects, () => {
- return policy.shouldLoad(aData.contentType,
- contentLocation,
- requestOrigin,
- aObjects.node,
- aData.mimeTypeGuess,
- null,
- aData.requestPrincipal);
- });
- if (result != Ci.nsIContentPolicy.ACCEPT && result != 0)
- return result;
- } catch (e) {
- Cu.reportError(e);
- }
- }
-
- return Ci.nsIContentPolicy.ACCEPT;
- },
-};
-ContentPolicyParent.init();
-
-// This interposition intercepts calls to add or remove new content
-// policies and forwards these requests to ContentPolicyParent.
-var CategoryManagerInterposition = new Interposition("CategoryManagerInterposition");
-
-CategoryManagerInterposition.methods.addCategoryEntry =
- function(addon, target, category, entry, value, persist, replace) {
- if (category == "content-policy") {
- CompatWarning.warn("content-policy should be added from the child process only.",
- addon, CompatWarning.warnings.nsIContentPolicy);
- ContentPolicyParent.addContentPolicy(addon, entry, value);
- }
-
- target.addCategoryEntry(category, entry, value, persist, replace);
- };
-
-CategoryManagerInterposition.methods.deleteCategoryEntry =
- function(addon, target, category, entry, persist) {
- if (category == "content-policy") {
- CompatWarning.warn("content-policy should be removed from the child process only.",
- addon, CompatWarning.warnings.nsIContentPolicy);
- ContentPolicyParent.removeContentPolicy(addon, entry);
- }
-
- target.deleteCategoryEntry(category, entry, persist);
- };
-
-// This shim handles the case where an add-on registers an about:
-// protocol handler in the parent and we want the child to be able to
-// use it. This code is pretty specific to Adblock's usage.
-var AboutProtocolParent = {
- init() {
- let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
- .getService(Ci.nsIMessageBroadcaster);
- ppmm.addMessageListener("Addons:AboutProtocol:GetURIFlags", this);
- ppmm.addMessageListener("Addons:AboutProtocol:OpenChannel", this);
- this._protocols = [];
- },
-
- registerFactory(addon, class_, className, contractID, factory) {
- this._protocols.push({contractID, factory});
- NotificationTracker.add(["about-protocol", contractID, addon]);
- },
-
- unregisterFactory(addon, class_, factory) {
- for (let i = 0; i < this._protocols.length; i++) {
- if (this._protocols[i].factory == factory) {
- NotificationTracker.remove(["about-protocol", this._protocols[i].contractID, addon]);
- this._protocols.splice(i, 1);
- break;
- }
- }
- },
-
- receiveMessage(msg) {
- switch (msg.name) {
- case "Addons:AboutProtocol:GetURIFlags":
- return this.getURIFlags(msg);
- case "Addons:AboutProtocol:OpenChannel":
- return this.openChannel(msg);
- }
- return undefined;
- },
-
- getURIFlags(msg) {
- let uri = Services.io.newURI(msg.data.uri);
- let contractID = msg.data.contractID;
- let module = Cc[contractID].getService(Ci.nsIAboutModule);
- try {
- return module.getURIFlags(uri);
- } catch (e) {
- Cu.reportError(e);
- return undefined;
- }
- },
-
- // We immediately read all the data out of the channel here and
- // return it to the child.
- openChannel(msg) {
- function wrapGetInterface(cpow) {
- return {
- getInterface(intf) { return cpow.getInterface(intf); }
- };
- }
-
- let uri = Services.io.newURI(msg.data.uri);
- let channelParams;
- if (msg.data.contentPolicyType === Ci.nsIContentPolicy.TYPE_DOCUMENT) {
- // For TYPE_DOCUMENT loads, we cannot recreate the loadinfo here in the
- // parent. In that case, treat this as a chrome (addon)-requested
- // subload. When we use the data in the child, we'll load it into the
- // correctly-principaled document.
- channelParams = {
- uri,
- contractID: msg.data.contractID,
- loadingPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
- securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
- contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER
- };
- } else {
- // We can recreate the loadinfo here in the parent for non TYPE_DOCUMENT
- // loads.
- channelParams = {
- uri,
- contractID: msg.data.contractID,
- loadingPrincipal: msg.data.loadingPrincipal,
- securityFlags: msg.data.securityFlags,
- contentPolicyType: msg.data.contentPolicyType
- };
- }
-
- try {
- let channel = NetUtil.newChannel(channelParams);
-
- // We're not allowed to set channel.notificationCallbacks to a
- // CPOW, since the setter for notificationCallbacks is in C++,
- // which can't tolerate CPOWs. Instead we just use a JS object
- // that wraps the CPOW.
- channel.notificationCallbacks = wrapGetInterface(msg.objects.notificationCallbacks);
- if (msg.objects.loadGroupNotificationCallbacks) {
- channel.loadGroup = {notificationCallbacks: msg.objects.loadGroupNotificationCallbacks};
- } else {
- channel.loadGroup = null;
- }
- let stream = channel.open2();
- let data = NetUtil.readInputStreamToString(stream, stream.available(), {});
- return {
- data,
- contentType: channel.contentType
- };
- } catch (e) {
- Cu.reportError(e);
- return undefined;
- }
- },
-};
-AboutProtocolParent.init();
-
-var ComponentRegistrarInterposition = new Interposition("ComponentRegistrarInterposition");
-
-ComponentRegistrarInterposition.methods.registerFactory =
- function(addon, target, class_, className, contractID, factory) {
- if (contractID && contractID.startsWith("@mozilla.org/network/protocol/about;1?")) {
- CompatWarning.warn("nsIAboutModule should be registered in the content process" +
- " as well as the chrome process. (If you do that already, ignore" +
- " this warning.)",
- addon, CompatWarning.warnings.nsIAboutModule);
- AboutProtocolParent.registerFactory(addon, class_, className, contractID, factory);
- }
-
- target.registerFactory(class_, className, contractID, factory);
- };
-
-ComponentRegistrarInterposition.methods.unregisterFactory =
- function(addon, target, class_, factory) {
- AboutProtocolParent.unregisterFactory(addon, class_, factory);
- target.unregisterFactory(class_, factory);
- };
-
-// This object manages add-on observers that might fire in the child
-// process. Rather than managing the observers itself, it uses the
-// parent's observer service. When an add-on listens on topic T,
-// ObserverParent asks the child process to listen on T. It also adds
-// an observer in the parent for the topic e10s-T. When the T observer
-// fires in the child, the parent fires all the e10s-T observers,
-// passing them CPOWs for the subject and data. We don't want to use T
-// in the parent because there might be non-add-on T observers that
-// won't expect to get notified in this case.
-var ObserverParent = {
- init() {
- let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
- .getService(Ci.nsIMessageBroadcaster);
- ppmm.addMessageListener("Addons:Observer:Run", this);
- },
-
- addObserver(addon, observer, topic, ownsWeak) {
- Services.obs.addObserver(observer, "e10s-" + topic, ownsWeak);
- NotificationTracker.add(["observer", topic, addon]);
- },
-
- removeObserver(addon, observer, topic) {
- Services.obs.removeObserver(observer, "e10s-" + topic);
- NotificationTracker.remove(["observer", topic, addon]);
- },
-
- receiveMessage(msg) {
- switch (msg.name) {
- case "Addons:Observer:Run":
- this.notify(msg.objects.subject, msg.objects.topic, msg.objects.data);
- break;
- }
- },
-
- notify(subject, topic, data) {
- let e = Services.obs.enumerateObservers("e10s-" + topic);
- while (e.hasMoreElements()) {
- let obs = e.getNext().QueryInterface(Ci.nsIObserver);
- try {
- obs.observe(subject, topic, data);
- } catch (e) {
- Cu.reportError(e);
- }
- }
- }
-};
-ObserverParent.init();
-
-// We only forward observers for these topics.
-var TOPIC_WHITELIST = [
- "content-document-global-created",
- "document-element-inserted",
- "dom-window-destroyed",
- "inner-window-destroyed",
- "outer-window-destroyed",
- "csp-on-violate-policy",
-];
-
-// This interposition listens for
-// nsIObserverService.{add,remove}Observer.
-var ObserverInterposition = new Interposition("ObserverInterposition");
-
-ObserverInterposition.methods.addObserver =
- function(addon, target, observer, topic, ownsWeak) {
- if (TOPIC_WHITELIST.includes(topic)) {
- CompatWarning.warn(`${topic} observer should be added from the child process only.`,
- addon, CompatWarning.warnings.observers);
-
- ObserverParent.addObserver(addon, observer, topic);
- }
-
- target.addObserver(observer, topic, ownsWeak);
- };
-
-ObserverInterposition.methods.removeObserver =
- function(addon, target, observer, topic) {
- if (TOPIC_WHITELIST.includes(topic)) {
- ObserverParent.removeObserver(addon, observer, topic);
- }
-
- target.removeObserver(observer, topic);
- };
-
-// This object is responsible for forwarding events from the child to
-// the parent.
-var EventTargetParent = {
- init() {
- // The _listeners map goes from targets (either <browser> elements
- // or windows) to a dictionary from event types to listeners.
- this._listeners = new WeakMap();
-
- let mm = Cc["@mozilla.org/globalmessagemanager;1"].
- getService(Ci.nsIMessageListenerManager);
- mm.addMessageListener("Addons:Event:Run", this);
- },
-
- // If target is not on the path from a <browser> element to the
- // window root, then we return null here to ignore the
- // target. Otherwise, if the target is a browser-specific element
- // (the <browser> or <tab> elements), then we return the
- // <browser>. If it's some generic element, then we return the
- // window itself.
- redirectEventTarget(target) {
- if (Cu.isCrossProcessWrapper(target)) {
- return null;
- }
-
- if (target instanceof Ci.nsIDOMChromeWindow) {
- return target;
- }
-
- if (target instanceof Ci.nsIDOMXULElement) {
- if (target.localName == "browser") {
- return target;
- } else if (target.localName == "tab") {
- return target.linkedBrowser;
- }
-
- // Check if |target| is somewhere on the path from the
- // tabbrowser tabbox.
- let window = target.ownerGlobal;
-
- // Some non-browser windows define gBrowser globals which are not elements
- // and can't be passed to target.contains().
- if (window &&
- window.gBrowser &&
- window.gBrowser.tabbox &&
- target.contains(window.gBrowser.tabbox)) {
- return window;
- }
- }
-
- if (target.ownerGlobal && target === target.ownerGlobal.gBrowser) {
- return target.ownerGlobal;
- }
-
- return null;
- },
-
- // When a given event fires in the child, we fire it on the
- // <browser> element and the window since those are the two possible
- // results of redirectEventTarget.
- getTargets(browser) {
- let window = browser.ownerGlobal;
- return [browser, window];
- },
-
- addEventListener(addon, target, type, listener, useCapture, wantsUntrusted, delayedWarning) {
- let newTarget = this.redirectEventTarget(target);
- if (!newTarget) {
- return;
- }
-
- useCapture = useCapture || false;
- wantsUntrusted = wantsUntrusted || false;
-
- NotificationTracker.add(["event", type, useCapture, addon]);
-
- let listeners = this._listeners.get(newTarget);
- if (!listeners) {
- listeners = {};
- this._listeners.set(newTarget, listeners);
- }
- let forType = setDefault(listeners, type, []);
-
- // If there's already an identical listener, don't do anything.
- for (let i = 0; i < forType.length; i++) {
- if (forType[i].listener === listener &&
- forType[i].target === target &&
- forType[i].useCapture === useCapture &&
- forType[i].wantsUntrusted === wantsUntrusted) {
- return;
- }
- }
-
- forType.push({listener,
- target,
- wantsUntrusted,
- useCapture,
- delayedWarning});
- },
-
- removeEventListener(addon, target, type, listener, useCapture) {
- let newTarget = this.redirectEventTarget(target);
- if (!newTarget) {
- return;
- }
-
- useCapture = useCapture || false;
-
- let listeners = this._listeners.get(newTarget);
- if (!listeners) {
- return;
- }
- let forType = setDefault(listeners, type, []);
-
- for (let i = 0; i < forType.length; i++) {
- if (forType[i].listener === listener &&
- forType[i].target === target &&
- forType[i].useCapture === useCapture) {
- forType.splice(i, 1);
- NotificationTracker.remove(["event", type, useCapture, addon]);
- break;
- }
- }
- },
-
- receiveMessage(msg) {
- switch (msg.name) {
- case "Addons:Event:Run":
- this.dispatch(msg.target, msg.data.type, msg.data.capturing,
- msg.data.isTrusted, msg.data.prefetched, msg.objects);
- break;
- }
- },
-
- dispatch(browser, type, capturing, isTrusted, prefetched, cpows) {
- let event = cpows.event;
- let eventTarget = cpows.eventTarget;
- let targets = this.getTargets(browser);
- for (let target of targets) {
- let listeners = this._listeners.get(target);
- if (!listeners) {
- continue;
- }
- let forType = setDefault(listeners, type, []);
-
- // Make a copy in case they call removeEventListener in the listener.
- let handlers = [];
- for (let {listener, target, wantsUntrusted, useCapture, delayedWarning} of forType) {
- if ((wantsUntrusted || isTrusted) && useCapture == capturing) {
- // Issue a warning for this listener.
- delayedWarning();
-
- handlers.push([listener, target]);
- }
- }
-
- for (let [handler, target] of handlers) {
- let EventProxy = {
- get(knownProps, name) {
- if (knownProps.hasOwnProperty(name))
- return knownProps[name];
- return event[name];
- }
- };
- let proxyEvent = new Proxy({
- currentTarget: target,
- target: eventTarget,
- type,
- QueryInterface(iid) {
- if (iid.equals(Ci.nsISupports) ||
- iid.equals(Ci.nsIDOMEventTarget))
- return proxyEvent;
- // If event deson't support the interface this will throw. If it
- // does we want to return the proxy
- event.QueryInterface(iid);
- return proxyEvent;
- }
- }, EventProxy);
-
- try {
- Prefetcher.withPrefetching(prefetched, cpows, () => {
- if ("handleEvent" in handler) {
- handler.handleEvent(proxyEvent);
- } else {
- handler.call(eventTarget, proxyEvent);
- }
- });
- } catch (e) {
- Cu.reportError(e);
- }
- }
- }
- }
-};
-EventTargetParent.init();
-
-// This function returns a listener that will remove itself the first time
-// it is fired.
-var selfRemovingListeners = new WeakMap();
-function makeSelfRemovingListener(addon, target, type, listener, useCapture) {
- if (selfRemovingListeners.has(listener)) {
- return selfRemovingListeners.get(listener);
- }
-
- function selfRemovingListener(event) {
- EventTargetInterposition.methods.removeEventListener(addon, target, type,
- listener, useCapture);
- if ("handleEvent" in listener) {
- listener.handleEvent(event);
- } else {
- listener.call(event.target, event);
- }
- }
- selfRemovingListeners.set(listener, selfRemovingListener);
-
- return selfRemovingListener;
-}
-
-// This function returns a listener that will not fire on events where
-// the target is a remote xul:browser element itself. We'd rather let
-// the child process handle the event and pass it up via
-// EventTargetParent.
-var filteringListeners = new WeakMap();
-function makeFilteringListener(eventType, listener) {
- // Some events are actually targeted at the <browser> element
- // itself, so we only handle the ones where know that won't happen.
- let eventTypes = ["mousedown", "mouseup", "click"];
- if (!eventTypes.includes(eventType) || !listener ||
- (typeof listener != "object" && typeof listener != "function")) {
- return listener;
- }
-
- if (filteringListeners.has(listener)) {
- return filteringListeners.get(listener);
- }
-
- function filter(event) {
- let target = event.originalTarget;
- if (target instanceof Ci.nsIDOMXULElement &&
- target.localName == "browser" &&
- target.isRemoteBrowser) {
- return;
- }
-
- if ("handleEvent" in listener) {
- listener.handleEvent(event);
- } else {
- listener.call(event.target, event);
- }
- }
- filteringListeners.set(listener, filter);
- return filter;
-}
-
-// This interposition redirects addEventListener and
-// removeEventListener to EventTargetParent.
-var EventTargetInterposition = new Interposition("EventTargetInterposition");
-
-EventTargetInterposition.methods.addEventListener =
- function(addon, target, type, listener, options, wantsUntrusted) {
- let delayed = CompatWarning.delayedWarning(
- `Registering a ${type} event listener on content DOM nodes` +
- " needs to happen in the content process.",
- addon, CompatWarning.warnings.DOM_events);
-
- let useCapture =
- options === true || (typeof options == "object" && options.capture) || false;
- if (typeof options == "object" && options.once) {
- listener = makeSelfRemovingListener(addon, target, type, listener, useCapture);
- }
-
- EventTargetParent.addEventListener(addon, target, type, listener,
- useCapture, wantsUntrusted, delayed);
- target.addEventListener(type, makeFilteringListener(type, listener),
- useCapture, wantsUntrusted);
- };
-
-EventTargetInterposition.methods.removeEventListener =
- function(addon, target, type, listener, options) {
- let useCapture =
- options === true || (typeof options == "object" && options.capture) || false;
-
- if (selfRemovingListeners.has(listener)) {
- listener = selfRemovingListeners.get(listener);
- }
-
- EventTargetParent.removeEventListener(addon, target, type, listener, useCapture);
- target.removeEventListener(type, makeFilteringListener(type, listener), useCapture);
- };
-
-// This interposition intercepts accesses to |rootTreeItem| on a child
-// process docshell. In the child, each docshell is its own
-// root. However, add-ons expect the root to be the chrome docshell,
-// so we make that happen here.
-var ContentDocShellTreeItemInterposition = new Interposition("ContentDocShellTreeItemInterposition");
-
-ContentDocShellTreeItemInterposition.getters.rootTreeItem =
- function(addon, target) {
- // The chrome global in the child.
- let chromeGlobal = target.rootTreeItem
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIContentFrameMessageManager);
-
- // Map it to a <browser> element and window.
- let browser = RemoteAddonsParent.globalToBrowser.get(chromeGlobal);
- if (!browser) {
- // Somehow we have a CPOW from the child, but it hasn't sent us
- // its global yet. That shouldn't happen, but return null just
- // in case.
- return null;
- }
-
- let chromeWin = browser.ownerGlobal;
-
- // Return that window's docshell.
- return chromeWin.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShellTreeItem);
- };
-
-function chromeGlobalForContentWindow(window) {
- return window
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShellTreeItem)
- .rootTreeItem
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIContentFrameMessageManager);
-}
-
-// This object manages sandboxes created with content principals in
-// the parent. We actually create these sandboxes in the child process
-// so that the code loaded into them runs there. The resulting sandbox
-// object is a CPOW. This is primarly useful for Greasemonkey.
-var SandboxParent = {
- componentsMap: new WeakMap(),
-
- makeContentSandbox(addon, chromeGlobal, principals, ...rest) {
- CompatWarning.warn("This sandbox should be created from the child process.",
- addon, CompatWarning.warnings.sandboxes);
- if (rest.length) {
- // Do a shallow copy of the options object into the child
- // process. This way we don't have to access it through a Chrome
- // object wrapper, which would not let us access any properties.
- //
- // The only object property here is sandboxPrototype. We assume
- // it's a child process object (since that's what Greasemonkey
- // does) and leave it alone.
- let options = rest[0];
- let optionsCopy = new chromeGlobal.Object();
- for (let prop in options) {
- optionsCopy[prop] = options[prop];
- }
- rest[0] = optionsCopy;
- }
-
- // Make a sandbox in the child.
- let cu = chromeGlobal.Components.utils;
- let sandbox = cu.Sandbox(principals, ...rest);
-
- // We need to save the sandbox in the child so it won't get
- // GCed. The child will drop this reference at the next
- // navigation.
- chromeGlobal.addSandbox(sandbox);
-
- // The sandbox CPOW will be kept alive by whomever we return it
- // to. Its lifetime is unrelated to that of the sandbox object in
- // the child.
- this.componentsMap.set(sandbox, cu);
- return sandbox;
- },
-
- evalInSandbox(code, sandbox, ...rest) {
- let cu = this.componentsMap.get(sandbox);
- return cu.evalInSandbox(code, sandbox, ...rest);
- }
-};
-
-// This interposition redirects calls to Cu.Sandbox and
-// Cu.evalInSandbox to SandboxParent if the principals are content
-// principals.
-var ComponentsUtilsInterposition = new Interposition("ComponentsUtilsInterposition");
-
-ComponentsUtilsInterposition.methods.Sandbox =
- function(addon, target, principals, ...rest) {
- // principals can be a window object, a list of window objects, or
- // something else (a string, for example).
- if (principals &&
- typeof(principals) == "object" &&
- Cu.isCrossProcessWrapper(principals) &&
- principals instanceof Ci.nsIDOMWindow) {
- let chromeGlobal = chromeGlobalForContentWindow(principals);
- return SandboxParent.makeContentSandbox(addon, chromeGlobal, principals, ...rest);
- } else if (principals &&
- typeof(principals) == "object" &&
- "every" in principals &&
- principals.length &&
- principals.every(e => e instanceof Ci.nsIDOMWindow && Cu.isCrossProcessWrapper(e))) {
- let chromeGlobal = chromeGlobalForContentWindow(principals[0]);
-
- // The principals we pass to the content process must use an
- // Array object from the content process.
- let array = new chromeGlobal.Array();
- for (let i = 0; i < principals.length; i++) {
- array[i] = principals[i];
- }
- return SandboxParent.makeContentSandbox(addon, chromeGlobal, array, ...rest);
- }
- return Cu.Sandbox(principals, ...rest);
- };
-
-ComponentsUtilsInterposition.methods.evalInSandbox =
- function(addon, target, code, sandbox, ...rest) {
- if (sandbox && Cu.isCrossProcessWrapper(sandbox)) {
- return SandboxParent.evalInSandbox(code, sandbox, ...rest);
- }
- return Cu.evalInSandbox(code, sandbox, ...rest);
- };
-
-// This interposition handles cases where an add-on tries to import a
-// chrome XUL node into a content document. It doesn't actually do the
-// import, which we can't support. It just avoids throwing an
-// exception.
-var ContentDocumentInterposition = new Interposition("ContentDocumentInterposition");
-
-ContentDocumentInterposition.methods.importNode =
- function(addon, target, node, deep) {
- if (!Cu.isCrossProcessWrapper(node)) {
- // Trying to import a node from the parent process into the
- // child process. We don't support this now. Video Download
- // Helper does this in domhook-service.js to add a XUL
- // popupmenu to content.
- Cu.reportError("Calling contentDocument.importNode on a XUL node is not allowed.");
- return node;
- }
-
- return target.importNode(node, deep);
- };
-
-// This interposition ensures that calling browser.docShell from an
-// add-on returns a CPOW around the docshell.
-var RemoteBrowserElementInterposition = new Interposition("RemoteBrowserElementInterposition",
- EventTargetInterposition);
-
-RemoteBrowserElementInterposition.getters.docShell = function(addon, target) {
- CompatWarning.warn("Direct access to content docshell will no longer work in the chrome process.",
- addon, CompatWarning.warnings.content);
- let remoteChromeGlobal = RemoteAddonsParent.browserToGlobal.get(target);
- if (!remoteChromeGlobal) {
- // We may not have any messages from this tab yet.
- return null;
- }
- return remoteChromeGlobal.docShell;
-};
-
-RemoteBrowserElementInterposition.getters.sessionHistory = function(addon, target) {
- CompatWarning.warn("Direct access to browser.sessionHistory will no longer " +
- "work in the chrome process.",
- addon, CompatWarning.warnings.content);
-
- return getSessionHistory(target);
-};
-
-// We use this in place of the real browser.contentWindow if we
-// haven't yet received a CPOW for the child process's window. This
-// happens if the tab has just started loading.
-function makeDummyContentWindow(browser) {
- let dummyContentWindow = {
- set location(url) {
- browser.loadURI(url, null, null);
- },
- document: {
- readyState: "loading",
- location: { href: "about:blank" }
- },
- frames: [],
- };
- dummyContentWindow.top = dummyContentWindow;
- dummyContentWindow.document.defaultView = dummyContentWindow;
- browser._contentWindow = dummyContentWindow;
- return dummyContentWindow;
-}
-
-RemoteBrowserElementInterposition.getters.contentWindow = function(addon, target) {
- CompatWarning.warn("Direct access to browser.contentWindow will no longer work in the chrome process.",
- addon, CompatWarning.warnings.content);
-
- // If we don't have a CPOW yet, just return something we can use for
- // setting the location. This is useful for tests that create a tab
- // and immediately set contentWindow.location.
- if (!target.contentWindowAsCPOW) {
- CompatWarning.warn("CPOW to the content window does not exist yet, dummy content window is created.");
- return makeDummyContentWindow(target);
- }
- return target.contentWindowAsCPOW;
-};
-
-function getContentDocument(addon, browser) {
- if (!browser.contentWindowAsCPOW) {
- return makeDummyContentWindow(browser).document;
- }
-
- let doc = Prefetcher.lookupInCache(addon, browser.contentWindowAsCPOW, "document");
- if (doc) {
- return doc;
- }
-
- return browser.contentWindowAsCPOW.document;
-}
-
-function getSessionHistory(browser) {
- let remoteChromeGlobal = RemoteAddonsParent.browserToGlobal.get(browser);
- if (!remoteChromeGlobal) {
- CompatWarning.warn("CPOW for the remote browser docShell hasn't been received yet.");
- // We may not have any messages from this tab yet.
- return null;
- }
- return remoteChromeGlobal.docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory;
-}
-
-RemoteBrowserElementInterposition.getters.contentDocument = function(addon, target) {
- CompatWarning.warn("Direct access to browser.contentDocument will no longer work in the chrome process.",
- addon, CompatWarning.warnings.content);
-
- return getContentDocument(addon, target);
-};
-
-var TabBrowserElementInterposition = new Interposition("TabBrowserElementInterposition",
- EventTargetInterposition);
-
-TabBrowserElementInterposition.getters.contentWindow = function(addon, target) {
- CompatWarning.warn("Direct access to gBrowser.contentWindow will no longer work in the chrome process.",
- addon, CompatWarning.warnings.content);
-
- if (!target.selectedBrowser.contentWindowAsCPOW) {
- return makeDummyContentWindow(target.selectedBrowser);
- }
- return target.selectedBrowser.contentWindowAsCPOW;
-};
-
-TabBrowserElementInterposition.getters.contentDocument = function(addon, target) {
- CompatWarning.warn("Direct access to gBrowser.contentDocument will no longer work in the chrome process.",
- addon, CompatWarning.warnings.content);
-
- let browser = target.selectedBrowser;
- return getContentDocument(addon, browser);
-};
-
-TabBrowserElementInterposition.getters.sessionHistory = function(addon, target) {
- CompatWarning.warn("Direct access to gBrowser.sessionHistory will no " +
- "longer work in the chrome process.",
- addon, CompatWarning.warnings.content);
- let browser = target.selectedBrowser;
- if (!browser.isRemoteBrowser) {
- return browser.sessionHistory;
- }
- return getSessionHistory(browser);
-};
+var TabBrowserElementInterposition = new Interposition("TabBrowserElementInterposition");
// This function returns a wrapper around an
// nsIWebProgressListener. When the wrapper is invoked, it calls the
// real listener but passes CPOWs for the nsIWebProgress and
// nsIRequest arguments.
var progressListeners = {global: new WeakMap(), tabs: new WeakMap()};
function wrapProgressListener(kind, listener) {
if (progressListeners[kind].has(listener)) {
@@ -996,117 +127,39 @@ TabBrowserElementInterposition.methods.r
if (!target.ownerGlobal.gMultiProcessBrowser) {
return target.removeProgressListener(listener);
}
NotificationTracker.remove(["web-progress", addon]);
return target.removeProgressListener(wrapProgressListener("global", listener));
};
-TabBrowserElementInterposition.methods.addTabsProgressListener = function(addon, target, listener) {
- if (!target.ownerGlobal.gMultiProcessBrowser) {
- return target.addTabsProgressListener(listener);
- }
-
- NotificationTracker.add(["web-progress", addon]);
- return target.addTabsProgressListener(wrapProgressListener("tabs", listener));
-};
-
-TabBrowserElementInterposition.methods.removeTabsProgressListener = function(addon, target, listener) {
- if (!target.ownerGlobal.gMultiProcessBrowser) {
- return target.removeTabsProgressListener(listener);
- }
-
- NotificationTracker.remove(["web-progress", addon]);
- return target.removeTabsProgressListener(wrapProgressListener("tabs", listener));
-};
-
-var ChromeWindowInterposition = new Interposition("ChromeWindowInterposition",
- EventTargetInterposition);
-
-ChromeWindowInterposition.getters.content = function(addon, target) {
- CompatWarning.warn("Direct access to chromeWindow.content will no longer work in the chrome process.",
- addon, CompatWarning.warnings.content);
-
- let browser = target.gBrowser.selectedBrowser;
- if (!browser.contentWindowAsCPOW) {
- return makeDummyContentWindow(browser);
- }
- return browser.contentWindowAsCPOW;
-};
-
-var RemoteWebNavigationInterposition = new Interposition("RemoteWebNavigation");
-
-RemoteWebNavigationInterposition.getters.sessionHistory = function(addon, target) {
- CompatWarning.warn("Direct access to webNavigation.sessionHistory will no longer " +
- "work in the chrome process.",
- addon, CompatWarning.warnings.content);
-
- if (target instanceof Ci.nsIDocShell) {
- // We must have a non-remote browser, so we can go ahead
- // and just return the real sessionHistory.
- return target.sessionHistory;
- }
-
- let impl = target.wrappedJSObject;
- if (!impl) {
- return null;
- }
-
- let browser = impl._browser;
-
- return getSessionHistory(browser);
-};
-
var RemoteAddonsParent = {
init() {
let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
mm.addMessageListener("Addons:RegisterGlobal", this);
Services.ppmm.initialProcessData.remoteAddonsParentInitted = true;
Services.ppmm.loadProcessScript("data:,new " + function() {
ChromeUtils.import("resource://gre/modules/RemoteAddonsChild.jsm");
}, true);
this.globalToBrowser = new WeakMap();
this.browserToGlobal = new WeakMap();
},
getInterfaceInterpositions() {
- let result = {};
-
- function register(intf, interp) {
- result[intf.number] = interp;
- }
-
- register(Ci.nsICategoryManager, CategoryManagerInterposition);
- register(Ci.nsIComponentRegistrar, ComponentRegistrarInterposition);
- register(Ci.nsIObserverService, ObserverInterposition);
- register(Ci.nsIXPCComponents_Utils, ComponentsUtilsInterposition);
- register(Ci.nsIWebNavigation, RemoteWebNavigationInterposition);
-
- return result;
+ return {};
},
getTaggedInterpositions() {
- let result = {};
-
- function register(tag, interp) {
- result[tag] = interp;
- }
-
- register("EventTarget", EventTargetInterposition);
- register("ContentDocShellTreeItem", ContentDocShellTreeItemInterposition);
- register("ContentDocument", ContentDocumentInterposition);
- register("RemoteBrowserElement", RemoteBrowserElementInterposition);
- register("TabBrowserElement", TabBrowserElementInterposition);
- register("ChromeWindow", ChromeWindowInterposition);
-
- return result;
+ return {
+ TabBrowserElement: TabBrowserElementInterposition,
+ };
},
receiveMessage(msg) {
switch (msg.name) {
case "Addons:RegisterGlobal":
this.browserToGlobal.set(msg.target, msg.objects.global);
this.globalToBrowser.set(msg.objects.global, msg.target);
break;
--- a/toolkit/components/addoncompat/moz.build
+++ b/toolkit/components/addoncompat/moz.build
@@ -2,22 +2,19 @@
# vim: set filetype=python:
# 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 = ('Firefox', 'Extension Compatibility')
-TEST_DIRS += ['tests']
-
EXTRA_COMPONENTS += [
'addoncompat.manifest',
'defaultShims.js',
'multiprocessShims.js',
]
EXTRA_JS_MODULES += [
- 'CompatWarning.jsm',
'Prefetcher.jsm',
'RemoteAddonsChild.jsm',
'RemoteAddonsParent.jsm',
]
--- a/toolkit/components/addoncompat/multiprocessShims.js
+++ b/toolkit/components/addoncompat/multiprocessShims.js
@@ -104,34 +104,20 @@ AddonInterpositionService.prototype = {
// When the interface is not known for a method call, this code
// determines the type of the target object.
getObjectTag(target) {
if (Cu.isCrossProcessWrapper(target)) {
return Cu.getCrossProcessWrapperTag(target);
}
- if (target instanceof Ci.nsIDOMXULElement) {
- if (target.localName == "browser" && target.isRemoteBrowser) {
- return "RemoteBrowserElement";
- }
- }
-
if (target.ownerGlobal && target === target.ownerGlobal.gBrowser) {
return "TabBrowserElement";
}
- if (target instanceof Ci.nsIDOMChromeWindow && target.gMultiProcessBrowser) {
- return "ChromeWindow";
- }
-
- if (target instanceof Ci.nsIDOMEventTarget) {
- return "EventTarget";
- }
-
return "generic";
},
interposeProperty(addon, target, iid, prop) {
let interp;
if (iid) {
interp = this._interfaceInterpositions[iid];
} else {
deleted file mode 100644
--- a/toolkit/components/addoncompat/tests/addon/bootstrap.js
+++ /dev/null
@@ -1,628 +0,0 @@
-// This file also defines a frame script.
-/* eslint-env mozilla/frame-script */
-
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-const baseURL = "http://mochi.test:8888/browser/" +
- "toolkit/components/addoncompat/tests/browser/";
-
-var contentSecManager = Cc["@mozilla.org/contentsecuritymanager;1"]
- .getService(Ci.nsIContentSecurityManager);
-
-function forEachWindow(f) {
- let wins = Services.wm.getEnumerator("navigator:browser");
- while (wins.hasMoreElements()) {
- let win = wins.getNext();
- f(win);
- }
-}
-
-function addLoadListener(target, listener) {
- target.addEventListener("load", function(event) {
- return listener(event);
- }, {capture: true, once: true});
-}
-
-var gWin;
-var gBrowser;
-var ok, is, info;
-
-function removeTab(tab, done) {
- // Remove the tab in a different turn of the event loop. This way
- // the nested event loop in removeTab doesn't conflict with the
- // event listener shims.
- gWin.setTimeout(() => {
- gBrowser.removeTab(tab);
- done();
- }, 0);
-}
-
-// Make sure that the shims for window.content, browser.contentWindow,
-// and browser.contentDocument are working.
-function testContentWindow() {
- return new Promise(function(resolve, reject) {
- const url = baseURL + "browser_addonShims_testpage.html";
- let tab = BrowserTestUtils.addTab(gBrowser, url); // eslint-disable-line no-undef
- gBrowser.selectedTab = tab;
- let browser = tab.linkedBrowser;
- addLoadListener(browser, function handler() {
- ok(gWin.content, "content is defined on chrome window");
- ok(browser.contentWindow, "contentWindow is defined");
- ok(browser.contentDocument, "contentWindow is defined");
- is(gWin.content, browser.contentWindow, "content === contentWindow");
- ok(browser.webNavigation.sessionHistory, "sessionHistory is defined");
-
- ok(browser.contentDocument.getElementById("link"), "link present in document");
-
- // FIXME: Waiting on bug 1073631.
- // is(browser.contentWindow.wrappedJSObject.global, 3, "global available on document");
-
- removeTab(tab, resolve);
- });
- });
-}
-
-// Test for bug 1060046 and bug 1072607. We want to make sure that
-// adding and removing listeners works as expected.
-function testListeners() {
- return new Promise(function(resolve, reject) {
- const url1 = baseURL + "browser_addonShims_testpage.html";
- const url2 = baseURL + "browser_addonShims_testpage2.html";
-
- let tab = BrowserTestUtils.addTab(gBrowser, url2); // eslint-disable-line no-undef
- let browser = tab.linkedBrowser;
- addLoadListener(browser, function handler() {
- function dummyHandler() {}
-
- // Test that a removed listener stays removed (bug
- // 1072607). We're looking to make sure that adding and removing
- // a listener here doesn't cause later listeners to fire more
- // than once.
- for (let i = 0; i < 5; i++) {
- gBrowser.addEventListener("load", dummyHandler, true);
- gBrowser.removeEventListener("load", dummyHandler, true);
- }
-
- // We also want to make sure that this listener doesn't fire
- // after it's removed.
- let loadWithRemoveCount = 0;
- addLoadListener(browser, function handler1(event) {
- loadWithRemoveCount++;
- is(event.target.documentURI, url1, "only fire for first url");
- });
-
- // Load url1 and then url2. We want to check that:
- // 1. handler1 only fires for url1.
- // 2. handler2 only fires once for url1 (so the second time it
- // fires should be for url2).
- let loadCount = 0;
- browser.addEventListener("load", function handler2(event) {
- loadCount++;
- if (loadCount == 1) {
- is(event.target.documentURI, url1, "first load is for first page loaded");
- browser.loadURI(url2);
- } else {
- gBrowser.removeEventListener("load", handler2, true);
-
- is(event.target.documentURI, url2, "second load is for second page loaded");
- is(loadWithRemoveCount, 1, "load handler is only called once");
-
- removeTab(tab, resolve);
- }
- }, true);
-
- browser.loadURI(url1);
- });
- });
-}
-
-// Test for bug 1059207. We want to make sure that adding a capturing
-// listener and a non-capturing listener to the same element works as
-// expected.
-function testCapturing() {
- return new Promise(function(resolve, reject) {
- let capturingCount = 0;
- let nonCapturingCount = 0;
-
- function capturingHandler(event) {
- is(capturingCount, 0, "capturing handler called once");
- is(nonCapturingCount, 0, "capturing handler called before bubbling handler");
- capturingCount++;
- }
-
- function nonCapturingHandler(event) {
- is(capturingCount, 1, "bubbling handler called after capturing handler");
- is(nonCapturingCount, 0, "bubbling handler called once");
- nonCapturingCount++;
- }
-
- gBrowser.addEventListener("mousedown", capturingHandler, true);
- gBrowser.addEventListener("mousedown", nonCapturingHandler);
-
- const url = baseURL + "browser_addonShims_testpage.html";
- let tab = BrowserTestUtils.addTab(gBrowser, url); // eslint-disable-line no-undef
- let browser = tab.linkedBrowser;
- addLoadListener(browser, function handler() {
- let win = browser.contentWindow;
- let event = win.document.createEvent("MouseEvents");
- event.initMouseEvent("mousedown", true, false, win, 1,
- 1, 0, 0, 0, // screenX, screenY, clientX, clientY
- false, false, false, false, // ctrlKey, altKey, shiftKey, metaKey
- 0, null); // buttonCode, relatedTarget
-
- let element = win.document.getElementById("output");
- element.dispatchEvent(event);
-
- is(capturingCount, 1, "capturing handler fired");
- is(nonCapturingCount, 1, "bubbling handler fired");
-
- gBrowser.removeEventListener("mousedown", capturingHandler, true);
- gBrowser.removeEventListener("mousedown", nonCapturingHandler);
-
- removeTab(tab, resolve);
- });
- });
-}
-
-// Make sure we get observer notifications that normally fire in the
-// child.
-function testObserver() {
- return new Promise(function(resolve, reject) {
- let observerFired = 0;
-
- function observer(subject, topic, data) {
- Services.obs.removeObserver(observer, "document-element-inserted");
- observerFired++;
- }
- Services.obs.addObserver(observer, "document-element-inserted");
-
- let count = 0;
- const url = baseURL + "browser_addonShims_testpage.html";
- let tab = BrowserTestUtils.addTab(gBrowser, url); // eslint-disable-line no-undef
- let browser = tab.linkedBrowser;
- browser.addEventListener("load", function handler() {
- count++;
- if (count == 1) {
- browser.reload();
- } else {
- browser.removeEventListener("load", handler);
-
- is(observerFired, 1, "got observer notification");
-
- removeTab(tab, resolve);
- }
- }, true);
- });
-}
-
-// Test for bug 1072472. Make sure that creating a sandbox to run code
-// in the content window works. This is essentially a test for
-// Greasemonkey.
-function testSandbox() {
- return new Promise(function(resolve, reject) {
- const url = baseURL + "browser_addonShims_testpage.html";
- let tab = BrowserTestUtils.addTab(gBrowser, url); // eslint-disable-line no-undef
- let browser = tab.linkedBrowser;
- browser.addEventListener("load", function() {
- let sandbox = Cu.Sandbox(browser.contentWindow,
- {sandboxPrototype: browser.contentWindow,
- wantXrays: false});
- Cu.evalInSandbox("const unsafeWindow = window;", sandbox);
- Cu.evalInSandbox("document.getElementById('output').innerHTML = 'hello';", sandbox);
-
- is(browser.contentDocument.getElementById("output").innerHTML, "hello",
- "sandbox code ran successfully");
-
- // Now try a sandbox with expanded principals.
- sandbox = Cu.Sandbox([browser.contentWindow],
- {sandboxPrototype: browser.contentWindow,
- wantXrays: false});
- Cu.evalInSandbox("const unsafeWindow = window;", sandbox);
- Cu.evalInSandbox("document.getElementById('output').innerHTML = 'hello2';", sandbox);
-
- is(browser.contentDocument.getElementById("output").innerHTML, "hello2",
- "EP sandbox code ran successfully");
-
- removeTab(tab, resolve);
- }, {capture: true, once: true});
- });
-}
-
-// Test for bug 1095305. We just want to make sure that loading some
-// unprivileged content from an add-on package doesn't crash.
-function testAddonContent() {
- let chromeRegistry = Cc["@mozilla.org/chrome/chrome-registry;1"]
- .getService(Ci.nsIChromeRegistry);
- let base = chromeRegistry.convertChromeURL(Services.io.newURI("chrome://addonshim1/content/"));
-
- let res = Services.io.getProtocolHandler("resource")
- .QueryInterface(Ci.nsIResProtocolHandler);
- res.setSubstitution("addonshim1", base);
-
- return new Promise(function(resolve, reject) {
- const url = "resource://addonshim1/page.html";
- let tab = BrowserTestUtils.addTab(gBrowser, url); // eslint-disable-line no-undef
- let browser = tab.linkedBrowser;
- addLoadListener(browser, function handler() {
- res.setSubstitution("addonshim1", null);
- removeTab(tab, resolve);
- });
- });
-}
-
-
-// Test for bug 1102410. We check that multiple nsIAboutModule's can be
-// registered in the parent, and that the child can browse to each of
-// the registered about: pages.
-function testAboutModuleRegistration() {
- let Registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
-
- let modulesToUnregister = new Map();
-
- function TestChannel(uri, aLoadInfo, aboutName) {
- this.aboutName = aboutName;
- this.loadInfo = aLoadInfo;
- this.URI = this.originalURI = uri;
- }
-
- TestChannel.prototype = {
- asyncOpen(listener, context) {
- let stream = this.open();
- let runnable = {
- run: () => {
- try {
- listener.onStartRequest(this, context);
- } catch (e) {}
- try {
- listener.onDataAvailable(this, context, stream, 0, stream.available());
- } catch (e) {}
- try {
- listener.onStopRequest(this, context, Cr.NS_OK);
- } catch (e) {}
- }
- };
- Services.tm.dispatchToMainThread(runnable);
- },
-
- asyncOpen2(listener) {
- // throws an error if security checks fail
- var outListener = contentSecManager.performSecurityCheck(this, listener);
- return this.asyncOpen(outListener, null);
- },
-
- open() {
- function getWindow(channel) {
- try {
- if (channel.notificationCallbacks)
- return channel.notificationCallbacks.getInterface(Ci.nsILoadContext).associatedWindow;
- } catch (e) {}
-
- try {
- if (channel.loadGroup && channel.loadGroup.notificationCallbacks)
- return channel.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext).associatedWindow;
- } catch (e) {}
-
- return null;
- }
-
- let data = `<html><h1>${this.aboutName}</h1></html>`;
- let wnd = getWindow(this);
- if (!wnd)
- throw Cr.NS_ERROR_UNEXPECTED;
-
- let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
- stream.setData(data, data.length);
- return stream;
- },
-
- open2() {
- // throws an error if security checks fail
- contentSecManager.performSecurityCheck(this, null);
- return this.open();
- },
-
- isPending() {
- return false;
- },
- cancel() {
- throw Cr.NS_ERROR_NOT_IMPLEMENTED;
- },
- suspend() {
- throw Cr.NS_ERROR_NOT_IMPLEMENTED;
- },
- resume() {
- throw Cr.NS_ERROR_NOT_IMPLEMENTED;
- },
-
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel, Ci.nsIRequest])
- };
-
- /**
- * This function creates a new nsIAboutModule and registers it. Callers
- * should also call unregisterModules after using this function to clean
- * up the nsIAboutModules at the end of this test.
- *
- * @param aboutName
- * This will be the string after about: used to refer to this module.
- * For example, if aboutName is foo, you can refer to this module by
- * browsing to about:foo.
- *
- * @param uuid
- * A unique identifer string for this module. For example,
- * "5f3a921b-250f-4ac5-a61c-8f79372e6063"
- */
- let createAndRegisterAboutModule = function(aboutName, uuid) {
-
- let AboutModule = function() {};
-
- AboutModule.prototype = {
- classID: Components.ID(uuid),
- classDescription: `Testing About Module for about:${aboutName}`,
- contractID: `@mozilla.org/network/protocol/about;1?what=${aboutName}`,
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
-
- newChannel: (aURI, aLoadInfo) => {
- return new TestChannel(aURI, aLoadInfo, aboutName);
- },
-
- getURIFlags: (aURI) => {
- return Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT |
- Ci.nsIAboutModule.ALLOW_SCRIPT;
- },
- };
-
- let factory = {
- createInstance(outer, iid) {
- if (outer) {
- throw Cr.NS_ERROR_NO_AGGREGATION;
- }
- return new AboutModule();
- },
- };
-
- Registrar.registerFactory(AboutModule.prototype.classID,
- AboutModule.prototype.classDescription,
- AboutModule.prototype.contractID,
- factory);
-
- modulesToUnregister.set(AboutModule.prototype.classID,
- factory);
- };
-
- /**
- * Unregisters any nsIAboutModules registered with
- * createAndRegisterAboutModule.
- */
- let unregisterModules = () => {
- for (let [classID, factory] of modulesToUnregister) {
- Registrar.unregisterFactory(classID, factory);
- }
- };
-
- /**
- * Takes a browser, and sends it a framescript to attempt to
- * load some about: pages. The frame script will send a test:result
- * message on completion, passing back a data object with:
- *
- * {
- * pass: true
- * }
- *
- * on success, and:
- *
- * {
- * pass: false,
- * errorMsg: message,
- * }
- *
- * on failure.
- *
- * @param browser
- * The browser to send the framescript to.
- */
- let testAboutModulesWork = (browser) => {
- let testConnection = () => {
- // This section is loaded into a frame script.
- /* global content:false */
- let request = new content.XMLHttpRequest();
- try {
- request.open("GET", "about:test1", false);
- request.send(null);
- if (request.status != 200) {
- throw (`about:test1 response had status ${request.status} - expected 200`);
- }
- if (!request.responseText.includes("test1")) {
- throw (`about:test1 response had result ${request.responseText}`);
- }
-
- request = new content.XMLHttpRequest();
- request.open("GET", "about:test2", false);
- request.send(null);
-
- if (request.status != 200) {
- throw (`about:test2 response had status ${request.status} - expected 200`);
- }
- if (!request.responseText.includes("test2")) {
- throw (`about:test2 response had result ${request.responseText}`);
- }
-
- sendAsyncMessage("test:result", {
- pass: true,
- });
- } catch (e) {
- sendAsyncMessage("test:result", {
- pass: false,
- errorMsg: e.toString(),
- });
- }
- };
-
- return new Promise((resolve, reject) => {
- let mm = browser.messageManager;
- mm.addMessageListener("test:result", function onTestResult(message) {
- mm.removeMessageListener("test:result", onTestResult);
- if (message.data.pass) {
- ok(true, "Connections to about: pages were successful");
- } else {
- ok(false, message.data.errorMsg);
- }
- resolve();
- });
- mm.loadFrameScript("data:,(" + testConnection.toString() + ")();", false);
- });
- };
-
- // Here's where the actual test is performed.
- return new Promise((resolve, reject) => {
- createAndRegisterAboutModule("test1", "5f3a921b-250f-4ac5-a61c-8f79372e6063");
- createAndRegisterAboutModule("test2", "d7ec0389-1d49-40fa-b55c-a1fc3a6dbf6f");
-
- // This needs to be a chrome-privileged page that loads in the
- // content process. It needs chrome privs because otherwise the
- // XHRs for about:test[12] will fail with a privilege error
- // despite the presence of URI_SAFE_FOR_UNTRUSTED_CONTENT.
- let newTab = BrowserTestUtils.addTab(gBrowser, "chrome://addonshim1/content/page.html"); // eslint-disable-line no-undef
- gBrowser.selectedTab = newTab;
- let browser = newTab.linkedBrowser;
-
- addLoadListener(browser, function() {
- testAboutModulesWork(browser).then(() => {
- unregisterModules();
- removeTab(newTab, resolve);
- });
- });
- });
-}
-
-function testProgressListener() {
- const url = baseURL + "browser_addonShims_testpage.html";
-
- let sawGlobalLocChange = false;
- let sawTabsLocChange = false;
-
- let globalListener = {
- onLocationChange(webProgress, request, uri) {
- if (uri.spec == url) {
- sawGlobalLocChange = true;
- ok(request instanceof Ci.nsIHttpChannel, "Global listener channel is an HTTP channel");
- }
- },
- };
-
- let tabsListener = {
- onLocationChange(browser, webProgress, request, uri) {
- if (uri.spec == url) {
- sawTabsLocChange = true;
- ok(request instanceof Ci.nsIHttpChannel, "Tab listener channel is an HTTP channel");
- }
- },
- };
-
- gBrowser.addProgressListener(globalListener);
- gBrowser.addTabsProgressListener(tabsListener);
- info("Added progress listeners");
-
- return new Promise(function(resolve, reject) {
- let tab = BrowserTestUtils.addTab(gBrowser, url); // eslint-disable-line no-undef
- gBrowser.selectedTab = tab;
- addLoadListener(tab.linkedBrowser, function handler() {
- ok(sawGlobalLocChange, "Saw global onLocationChange");
- ok(sawTabsLocChange, "Saw tabs onLocationChange");
-
- gBrowser.removeProgressListener(globalListener);
- gBrowser.removeTabsProgressListener(tabsListener);
- removeTab(tab, resolve);
- });
- });
-}
-
-function testRootTreeItem() {
- return new Promise(function(resolve, reject) {
- const url = baseURL + "browser_addonShims_testpage.html";
- let tab = BrowserTestUtils.addTab(gBrowser, url); // eslint-disable-line no-undef
- gBrowser.selectedTab = tab;
- let browser = tab.linkedBrowser;
- addLoadListener(browser, function handler() {
- let win = browser.contentWindow;
-
- // Add-ons love this crap.
- let root = win.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShellTreeItem)
- .rootTreeItem
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindow);
- is(root, gWin, "got correct chrome window");
-
- removeTab(tab, resolve);
- });
- });
-}
-
-function testImportNode() {
- return new Promise(function(resolve, reject) {
- const url = baseURL + "browser_addonShims_testpage.html";
- let tab = BrowserTestUtils.addTab(gBrowser, url); // eslint-disable-line no-undef
- gBrowser.selectedTab = tab;
- let browser = tab.linkedBrowser;
- addLoadListener(browser, function handler() {
- let node = gWin.document.createElement("div");
- let doc = browser.contentDocument;
- let result;
- try {
- result = doc.importNode(node, false);
- } catch (e) {
- ok(false, "importing threw an exception");
- }
- if (browser.isRemoteBrowser) {
- is(result, node, "got expected import result");
- }
-
- removeTab(tab, resolve);
- });
- });
-}
-
-function runTests(win, funcs) {
- ok = funcs.ok;
- is = funcs.is;
- info = funcs.info;
-
- gWin = win;
- gBrowser = win.gBrowser;
-
- return testContentWindow().
- then(testListeners).
- then(testCapturing).
- then(testObserver).
- then(testSandbox).
- then(testAddonContent).
- then(testAboutModuleRegistration).
- then(testProgressListener).
- then(testRootTreeItem).
- then(testImportNode).
- then(Promise.resolve());
-}
-
-/*
- bootstrap.js API
-*/
-
-function startup(aData, aReason) {
- forEachWindow(win => {
- win.runAddonShimTests = (funcs) => runTests(win, funcs);
- });
-}
-
-function shutdown(aData, aReason) {
- forEachWindow(win => {
- delete win.runAddonShimTests;
- });
-}
-
-function install(aData, aReason) {
-}
-
-function uninstall(aData, aReason) {
-}
deleted file mode 100644
--- a/toolkit/components/addoncompat/tests/addon/chrome.manifest
+++ /dev/null
@@ -1,1 +0,0 @@
-content addonshim1 content/
deleted file mode 100644
--- a/toolkit/components/addoncompat/tests/addon/content/page.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<html>
-</html>
deleted file mode 100644
--- a/toolkit/components/addoncompat/tests/addon/install.rdf
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
- <Description about="urn:mozilla:install-manifest">
- <em:id>test-addon-shim-1@tests.mozilla.org</em:id>
- <em:version>1</em:version>
- <em:type>2</em:type>
- <em:bootstrap>true</em:bootstrap>
-
- <!-- Front End MetaData -->
- <em:name>Test addon shim 1</em:name>
- <em:description>Test an add-on that needs multiprocess shims.</em:description>
- <em:multiprocessCompatible>false</em:multiprocessCompatible>
-
- <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
- <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
- <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
-
- <em:targetApplication>
- <Description>
- <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
- <em:minVersion>0.3</em:minVersion>
- <em:maxVersion>*</em:maxVersion>
- </Description>
- </em:targetApplication>
-
- <em:targetApplication>
- <Description>
- <em:id>toolkit@mozilla.org</em:id>
- <em:minVersion>10.0</em:minVersion>
- <em:maxVersion>*</em:maxVersion>
- </Description>
- </em:targetApplication>
- </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/components/addoncompat/tests/browser/.eslintrc.js
+++ /dev/null
@@ -1,7 +0,0 @@
-"use strict";
-
-module.exports = {
- "extends": [
- "plugin:mozilla/browser-test"
- ]
-};
deleted file mode 100644
index e6392fb40a1c917dcd967cd426555613a11e1bd9..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/toolkit/components/addoncompat/tests/browser/browser.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[DEFAULT]
-tags = addons
-support-files =
- addon.xpi
- browser_addonShims_testpage.html
- browser_addonShims_testpage2.html
- compat-addon.xpi
-
-[browser_addonShims.js]
deleted file mode 100644
--- a/toolkit/components/addoncompat/tests/browser/browser_addonShims.js
+++ /dev/null
@@ -1,62 +0,0 @@
-var {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm", {});
-var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
-
-const ADDON_URL = "http://example.com/browser/toolkit/components/addoncompat/tests/browser/addon.xpi";
-const COMPAT_ADDON_URL = "http://example.com/browser/toolkit/components/addoncompat/tests/browser/compat-addon.xpi";
-
-// Install a test add-on that will exercise e10s shims.
-// url: Location of the add-on.
-function addAddon(url) {
- info("Installing add-on: " + url);
-
- return new Promise(function(resolve, reject) {
- AddonManager.getInstallForURL(url, installer => {
- installer.install();
- let listener = {
- onInstallEnded(addon, addonInstall) {
- installer.removeListener(listener);
-
- // Wait for add-on's startup scripts to execute. See bug 997408
- executeSoon(function() {
- resolve(addonInstall);
- });
- }
- };
- installer.addListener(listener);
- }, "application/x-xpinstall");
- });
-}
-
-// Uninstall a test add-on.
-// addon: The addon reference returned from addAddon.
-function removeAddon(addon) {
- info("Removing addon.");
-
- return new Promise(function(resolve, reject) {
- let listener = {
- onUninstalled(uninstalledAddon) {
- if (uninstalledAddon != addon) {
- return;
- }
- AddonManager.removeAddonListener(listener);
- resolve();
- }
- };
- AddonManager.addAddonListener(listener);
- addon.uninstall();
- });
-}
-
-add_task(async function test_addon_shims() {
- await SpecialPowers.pushPrefEnv({set: [["dom.ipc.shims.enabledWarnings", true]]});
-
- let addon = await addAddon(ADDON_URL);
- await window.runAddonShimTests({ok, is, info});
- await removeAddon(addon);
-
- if (Services.appinfo.browserTabsRemoteAutostart) {
- addon = await addAddon(COMPAT_ADDON_URL);
- await window.runAddonTests({ok, is, info});
- await removeAddon(addon);
- }
-});
deleted file mode 100644
--- a/toolkit/components/addoncompat/tests/browser/browser_addonShims_testpage.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<html>
-<head>
- <meta charset="utf-8">
- <title>shim test</title>
-</head>
-
-<body>
-Hello!
-
-<a href="browser_addonShims_testpage2.html" id="link">Link</a>
-<div id="output"></div>
-
-<script type="text/javascript">
-var global = 3;
-</script>
-</body>
-</html>
deleted file mode 100644
--- a/toolkit/components/addoncompat/tests/browser/browser_addonShims_testpage2.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<html>
-<head>
- <meta charset="utf-8">
- <title>shim test</title>
-</head>
-
-<body>
-Hello!
-
-<a href="browser_addonShims_testpage.html" id="link">Link</a>
-
-<script type="text/javascript">
-var global = 5;
-</script>
-</body>
-</html>
deleted file mode 100644
index c7ca32cdc69a519c9258931b6ad982d911b657e6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/toolkit/components/addoncompat/tests/compat-addon/bootstrap.js
+++ /dev/null
@@ -1,88 +0,0 @@
-// This file defines a frame script.
-/* eslint-env mozilla/frame-script */
-
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-const baseURL = "http://mochi.test:8888/browser/" +
- "toolkit/components/addoncompat/tests/browser/";
-
-function forEachWindow(f) {
- let wins = Services.wm.getEnumerator("navigator:browser");
- while (wins.hasMoreElements()) {
- let win = wins.getNext();
- f(win);
- }
-}
-
-function addLoadListener(target, listener) {
- function frameScript() {
- addEventListener("load", function handler(event) {
- removeEventListener("load", handler, true);
- sendAsyncMessage("compat-test:loaded");
- }, true);
- }
- target.messageManager.loadFrameScript("data:,(" + frameScript.toString() + ")()", false);
- target.messageManager.addMessageListener("compat-test:loaded", function handler() {
- target.messageManager.removeMessageListener("compat-test:loaded", handler);
- listener();
- });
-}
-
-var gWin;
-var gBrowser;
-var ok, is, info;
-
-// Make sure that the shims for window.content, browser.contentWindow,
-// and browser.contentDocument are working.
-function testContentWindow() {
- return new Promise(function(resolve, reject) {
- const url = baseURL + "browser_addonShims_testpage.html";
- let tab = BrowserTestUtils.addTab(gBrowser, "about:blank"); // eslint-disable-line no-undef
- gBrowser.selectedTab = tab;
- let browser = tab.linkedBrowser;
- addLoadListener(browser, function handler() {
- ok(!gWin.content, "content is defined on chrome window");
- ok(!browser.contentWindow, "contentWindow is defined");
- ok(!browser.contentDocument, "contentWindow is defined");
-
- gBrowser.removeTab(tab);
- resolve();
- });
- browser.loadURI(url);
- });
-}
-
-function runTests(win, funcs) {
- ok = funcs.ok;
- is = funcs.is;
- info = funcs.info;
-
- gWin = win;
- gBrowser = win.gBrowser;
-
- return testContentWindow();
-}
-
-/*
- bootstrap.js API
-*/
-
-function startup(aData, aReason) {
- forEachWindow(win => {
- win.runAddonTests = (funcs) => runTests(win, funcs);
- });
-}
-
-function shutdown(aData, aReason) {
- forEachWindow(win => {
- delete win.runAddonTests;
- });
-}
-
-function install(aData, aReason) {
-}
-
-function uninstall(aData, aReason) {
-}
deleted file mode 100644
--- a/toolkit/components/addoncompat/tests/compat-addon/install.rdf
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
- <Description about="urn:mozilla:install-manifest">
- <em:id>test-addon-shim-2@tests.mozilla.org</em:id>
- <em:version>1</em:version>
- <em:type>2</em:type>
- <em:bootstrap>true</em:bootstrap>
-
- <!-- Front End MetaData -->
- <em:name>Test addon shims 2</em:name>
- <em:description>Test an add-on that doesn't need multiprocess shims.</em:description>
- <em:multiprocessCompatible>true</em:multiprocessCompatible>
-
- <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
- <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
- <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
-
- <em:targetApplication>
- <Description>
- <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
- <em:minVersion>0.3</em:minVersion>
- <em:maxVersion>*</em:maxVersion>
- </Description>
- </em:targetApplication>
-
- <em:targetApplication>
- <Description>
- <em:id>toolkit@mozilla.org</em:id>
- <em:minVersion>10.0</em:minVersion>
- <em:maxVersion>*</em:maxVersion>
- </Description>
- </em:targetApplication>
- </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/components/addoncompat/tests/moz.build
+++ /dev/null
@@ -1,7 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-BROWSER_CHROME_MANIFESTS += ['browser/browser.ini']