mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-10 02:08:38 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
576
toolkit/components/addoncompat/RemoteAddonsChild.jsm
Normal file
576
toolkit/components/addoncompat/RemoteAddonsChild.jsm
Normal file
|
|
@ -0,0 +1,576 @@
|
|||
// 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/.
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["RemoteAddonsChild"];
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
|
||||
"resource://gre/modules/BrowserUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(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");
|
||||
|
||||
// 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_;
|
||||
}
|
||||
|
||||
// This code keeps track of a set of paths of the form [component_1,
|
||||
// ..., component_n]. The components can be strings or booleans. The
|
||||
// child is notified whenever a path is added or removed, and new
|
||||
// children can request the current set of paths. The purpose is to
|
||||
// keep track of all the observers and events that the child should
|
||||
// monitor for the parent.
|
||||
//
|
||||
// In the child, clients can watch for changes to all paths that start
|
||||
// with a given component.
|
||||
var NotificationTracker = {
|
||||
init: function() {
|
||||
let cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
|
||||
.getService(Ci.nsISyncMessageSender);
|
||||
cpmm.addMessageListener("Addons:ChangeNotification", this);
|
||||
this._paths = cpmm.initialProcessData.remoteAddonsNotificationPaths;
|
||||
this._registered = new Map();
|
||||
this._watchers = {};
|
||||
},
|
||||
|
||||
receiveMessage: function(msg) {
|
||||
let path = msg.data.path;
|
||||
let count = msg.data.count;
|
||||
|
||||
let tracked = this._paths;
|
||||
for (let component of path) {
|
||||
tracked = setDefault(tracked, component, {});
|
||||
}
|
||||
|
||||
tracked._count = count;
|
||||
|
||||
if (this._watchers[path[0]]) {
|
||||
for (let watcher of this._watchers[path[0]]) {
|
||||
this.runCallback(watcher, path, count);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
runCallback: function(watcher, path, count) {
|
||||
let pathString = path.join("/");
|
||||
let registeredSet = this._registered.get(watcher);
|
||||
let registered = registeredSet.has(pathString);
|
||||
if (count && !registered) {
|
||||
watcher.track(path, true);
|
||||
registeredSet.add(pathString);
|
||||
} else if (!count && registered) {
|
||||
watcher.track(path, false);
|
||||
registeredSet.delete(pathString);
|
||||
}
|
||||
},
|
||||
|
||||
findPaths: function(prefix) {
|
||||
if (!this._paths) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let tracked = this._paths;
|
||||
for (let component of prefix) {
|
||||
tracked = setDefault(tracked, component, {});
|
||||
}
|
||||
|
||||
let result = [];
|
||||
let enumerate = (tracked, curPath) => {
|
||||
for (let component in tracked) {
|
||||
if (component == "_count") {
|
||||
result.push([curPath, tracked._count]);
|
||||
} else {
|
||||
let path = curPath.slice();
|
||||
if (component === "true") {
|
||||
component = true;
|
||||
} else if (component === "false") {
|
||||
component = false;
|
||||
}
|
||||
path.push(component);
|
||||
enumerate(tracked[component], path);
|
||||
}
|
||||
}
|
||||
}
|
||||
enumerate(tracked, prefix);
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
findSuffixes: function(prefix) {
|
||||
let paths = this.findPaths(prefix);
|
||||
return paths.map(([path, count]) => path[path.length - 1]);
|
||||
},
|
||||
|
||||
watch: function(component1, watcher) {
|
||||
setDefault(this._watchers, component1, []).push(watcher);
|
||||
this._registered.set(watcher, new Set());
|
||||
|
||||
let paths = this.findPaths([component1]);
|
||||
for (let [path, count] of paths) {
|
||||
this.runCallback(watcher, path, count);
|
||||
}
|
||||
},
|
||||
|
||||
unwatch: function(component1, watcher) {
|
||||
let watchers = this._watchers[component1];
|
||||
let index = watchers.lastIndexOf(watcher);
|
||||
if (index > -1) {
|
||||
watchers.splice(index, 1);
|
||||
}
|
||||
|
||||
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",
|
||||
|
||||
init: function() {
|
||||
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
registrar.registerFactory(this._classID, this._classDescription, this._contractID, this);
|
||||
|
||||
NotificationTracker.watch("content-policy", this);
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPolicy, Ci.nsIObserver,
|
||||
Ci.nsIChannelEventSink, Ci.nsIFactory,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
track: function(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);
|
||||
}
|
||||
},
|
||||
|
||||
shouldLoad: function(contentType, contentLocation, requestOrigin,
|
||||
node, mimeTypeGuess, extra, requestPrincipal) {
|
||||
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: contentType,
|
||||
contentLocation: contentLocation.spec,
|
||||
requestOrigin: requestOrigin ? requestOrigin.spec : null,
|
||||
mimeTypeGuess: mimeTypeGuess,
|
||||
requestPrincipal: requestPrincipal,
|
||||
prefetched: prefetched,
|
||||
}, cpows);
|
||||
if (rval.length != 1) {
|
||||
return Ci.nsIContentPolicy.ACCEPT;
|
||||
}
|
||||
|
||||
return rval[0];
|
||||
},
|
||||
|
||||
shouldProcess: function(contentType, contentLocation, requestOrigin, insecNode, mimeType, extra) {
|
||||
return Ci.nsIContentPolicy.ACCEPT;
|
||||
},
|
||||
|
||||
createInstance: function(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: function(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.currentThread.dispatch(runnable, Ci.nsIEventTarget.DISPATCH_NORMAL);
|
||||
},
|
||||
|
||||
asyncOpen2: function(listener) {
|
||||
// throws an error if security checks fail
|
||||
var outListener = contentSecManager.performSecurityCheck(this, listener);
|
||||
this.asyncOpen(outListener, null);
|
||||
},
|
||||
|
||||
open: function() {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
open2: function() {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
isPending: function() {
|
||||
return false;
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
suspend: function() {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
resume: function() {
|
||||
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: function(outer, iid) {
|
||||
if (outer != null) {
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
|
||||
return this.QueryInterface(iid);
|
||||
},
|
||||
|
||||
getURIFlags: function(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: function(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: function() {
|
||||
// Maps contractIDs to instances
|
||||
this._instances = new Map();
|
||||
// Maps contractIDs to classIDs
|
||||
this._classIDs = new Map();
|
||||
NotificationTracker.watch("about-protocol", this);
|
||||
},
|
||||
|
||||
track: function(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: function() {
|
||||
NotificationTracker.watch("observer", this);
|
||||
},
|
||||
|
||||
track: function(path, register) {
|
||||
let topic = path[1];
|
||||
if (register) {
|
||||
Services.obs.addObserver(this, topic, false);
|
||||
} else {
|
||||
Services.obs.removeObserver(this, topic);
|
||||
}
|
||||
},
|
||||
|
||||
observe: function(subject, topic, data) {
|
||||
let cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
|
||||
.getService(Ci.nsISyncMessageSender);
|
||||
cpmm.sendRpcMessage("Addons:Observer:Run", {}, {
|
||||
topic: topic,
|
||||
subject: subject,
|
||||
data: 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: function() {
|
||||
NotificationTracker.unwatch("event", this);
|
||||
},
|
||||
|
||||
track: function(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: function(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: capturing,
|
||||
isTrusted: event.isTrusted,
|
||||
prefetched: 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: function() {
|
||||
this.clearSandboxes();
|
||||
},
|
||||
|
||||
addListener: function() {
|
||||
let webProgress = this.chromeGlobal.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
|
||||
},
|
||||
|
||||
removeListener: function() {
|
||||
let webProgress = this.chromeGlobal.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
webProgress.removeProgressListener(this);
|
||||
},
|
||||
|
||||
onLocationChange: function(webProgress, request, location, flags) {
|
||||
this.clearSandboxes();
|
||||
},
|
||||
|
||||
addSandbox: function(sandbox) {
|
||||
if (this.sandboxes.length == 0) {
|
||||
this.addListener();
|
||||
}
|
||||
this.sandboxes.push(sandbox);
|
||||
},
|
||||
|
||||
clearSandboxes: function() {
|
||||
if (this.sandboxes.length) {
|
||||
this.removeListener();
|
||||
}
|
||||
this.sandboxes = [];
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference])
|
||||
};
|
||||
|
||||
var RemoteAddonsChild = {
|
||||
_ready: false,
|
||||
|
||||
makeReady: function() {
|
||||
let shims = [
|
||||
Prefetcher,
|
||||
NotificationTracker,
|
||||
ContentPolicyChild,
|
||||
AboutProtocolChild,
|
||||
ObserverChild,
|
||||
];
|
||||
|
||||
for (let shim of shims) {
|
||||
try {
|
||||
shim.init();
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
init: function(global) {
|
||||
|
||||
if (!this._ready) {
|
||||
if (!Services.cpmm.initialProcessData.remoteAddonsParentInitted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.makeReady();
|
||||
this._ready = true;
|
||||
}
|
||||
|
||||
global.sendAsyncMessage("Addons:RegisterGlobal", {}, {global: 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];
|
||||
},
|
||||
|
||||
uninit: function(perTabShims) {
|
||||
for (let shim of perTabShims) {
|
||||
try {
|
||||
shim.uninit();
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get useSyncWebProgress() {
|
||||
return NotificationTracker.getCount("web-progress") > 0;
|
||||
},
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue