mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
184 lines
6.5 KiB
JavaScript
184 lines
6.5 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
const Cu = Components.utils;
|
|
const Ci = Components.interfaces;
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "Prefetcher",
|
|
"resource://gre/modules/Prefetcher.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "RemoteAddonsParent",
|
|
"resource://gre/modules/RemoteAddonsParent.jsm");
|
|
|
|
/**
|
|
* This service overlays the API that the browser exposes to
|
|
* add-ons. The overlay tries to make a multiprocess browser appear as
|
|
* much as possible like a single process browser. An overlay can
|
|
* replace methods, getters, and setters of arbitrary browser objects.
|
|
*
|
|
* Most of the actual replacement code is implemented in
|
|
* RemoteAddonsParent. The code in this service simply decides how to
|
|
* replace code. For a given type of object (say, an
|
|
* nsIObserverService) the code in RemoteAddonsParent can register a
|
|
* set of replacement methods. This set is called an
|
|
* "interposition". The service keeps track of all the different
|
|
* interpositions. Whenever a method is called on some part of the
|
|
* browser API, this service gets a chance to replace it. To do so, it
|
|
* consults its map based on the type of object. If an interposition
|
|
* is found, the given method is looked up on it and called
|
|
* instead. If no method (or no interposition) is found, then the
|
|
* original target method is called as normal.
|
|
*
|
|
* For each method call, we need to determine the type of the target
|
|
* object. If the object is an old-style XPConnect wrapped native,
|
|
* then the type is simply the interface that the method was called on
|
|
* (Ci.nsIObserverService, say). For all other objects (WebIDL
|
|
* objects, CPOWs, and normal JS objects), the type is determined by
|
|
* calling getObjectTag.
|
|
*
|
|
* The interpositions defined in RemoteAddonsParent have three
|
|
* properties: methods, getters, and setters. When accessing a
|
|
* property, we first consult methods. If nothing is found, then we
|
|
* consult getters or setters, depending on whether the access is a
|
|
* get or a set.
|
|
*
|
|
* The methods in |methods| are functions that will be called whenever
|
|
* the given method is called on the target object. They are passed
|
|
* the same parameters as the original function except for two
|
|
* additional ones at the beginning: the add-on ID and the original
|
|
* target object that the method was called on. Additionally, the
|
|
* value of |this| is set to the original target object.
|
|
*
|
|
* The values in |getters| and |setters| should also be
|
|
* functions. They are called immediately when the given property is
|
|
* accessed. The functions in |getters| take two parameters: the
|
|
* add-on ID and the original target object. The functions in
|
|
* |setters| take those arguments plus the value that the property is
|
|
* being set to.
|
|
*/
|
|
|
|
function AddonInterpositionService()
|
|
{
|
|
Prefetcher.init();
|
|
RemoteAddonsParent.init();
|
|
|
|
// These maps keep track of the interpositions for all different
|
|
// kinds of objects.
|
|
this._interfaceInterpositions = RemoteAddonsParent.getInterfaceInterpositions();
|
|
this._taggedInterpositions = RemoteAddonsParent.getTaggedInterpositions();
|
|
|
|
let wl = [];
|
|
for (let v in this._interfaceInterpositions) {
|
|
let interp = this._interfaceInterpositions[v];
|
|
wl.push(...Object.getOwnPropertyNames(interp.methods));
|
|
wl.push(...Object.getOwnPropertyNames(interp.getters));
|
|
wl.push(...Object.getOwnPropertyNames(interp.setters));
|
|
}
|
|
|
|
for (let v in this._taggedInterpositions) {
|
|
let interp = this._taggedInterpositions[v];
|
|
wl.push(...Object.getOwnPropertyNames(interp.methods));
|
|
wl.push(...Object.getOwnPropertyNames(interp.getters));
|
|
wl.push(...Object.getOwnPropertyNames(interp.setters));
|
|
}
|
|
|
|
let nameSet = new Set();
|
|
wl = wl.filter(function(item) {
|
|
if (nameSet.has(item))
|
|
return true;
|
|
|
|
nameSet.add(item);
|
|
return true;
|
|
});
|
|
|
|
this._whitelist = wl;
|
|
}
|
|
|
|
AddonInterpositionService.prototype = {
|
|
classID: Components.ID("{1363d5f0-d95e-11e3-9c1a-0800200c9a66}"),
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonInterposition, Ci.nsISupportsWeakReference]),
|
|
|
|
getWhitelist: function() {
|
|
return this._whitelist;
|
|
},
|
|
|
|
// When the interface is not known for a method call, this code
|
|
// determines the type of the target object.
|
|
getObjectTag: function(target) {
|
|
if (Cu.isCrossProcessWrapper(target)) {
|
|
return Cu.getCrossProcessWrapperTag(target);
|
|
}
|
|
|
|
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
|
if (target instanceof Ci.nsIDOMXULElement) {
|
|
if (target.localName == "browser" && target.isRemoteBrowser) {
|
|
return "RemoteBrowserElement";
|
|
}
|
|
|
|
if (target.localName == "tabbrowser") {
|
|
return "TabBrowserElement";
|
|
}
|
|
}
|
|
|
|
if (target instanceof Ci.nsIDOMChromeWindow && target.gMultiProcessBrowser) {
|
|
return "ChromeWindow";
|
|
}
|
|
|
|
if (target instanceof Ci.nsIDOMEventTarget) {
|
|
return "EventTarget";
|
|
}
|
|
|
|
return "generic";
|
|
},
|
|
|
|
interposeProperty: function(addon, target, iid, prop) {
|
|
let interp;
|
|
if (iid) {
|
|
interp = this._interfaceInterpositions[iid];
|
|
} else {
|
|
try {
|
|
interp = this._taggedInterpositions[this.getObjectTag(target)];
|
|
}
|
|
catch (e) {
|
|
Cu.reportError(new Components.Exception("Failed to interpose object", e.result, Components.stack.caller));
|
|
}
|
|
}
|
|
|
|
if (!interp) {
|
|
return Prefetcher.lookupInCache(addon, target, prop);
|
|
}
|
|
|
|
let desc = { configurable: false, enumerable: true };
|
|
|
|
if ("methods" in interp && prop in interp.methods) {
|
|
desc.writable = false;
|
|
desc.value = function(...args) {
|
|
return interp.methods[prop](addon, target, ...args);
|
|
}
|
|
|
|
return desc;
|
|
} else if ("getters" in interp && prop in interp.getters) {
|
|
desc.get = function() { return interp.getters[prop](addon, target); };
|
|
|
|
if ("setters" in interp && prop in interp.setters) {
|
|
desc.set = function(v) { return interp.setters[prop](addon, target, v); };
|
|
}
|
|
|
|
return desc;
|
|
}
|
|
|
|
return Prefetcher.lookupInCache(addon, target, prop);
|
|
},
|
|
|
|
interposeCall: function(addonId, originalFunc, originalThis, args) {
|
|
args.splice(0, 0, addonId);
|
|
return originalFunc.apply(originalThis, args);
|
|
},
|
|
};
|
|
|
|
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AddonInterpositionService]);
|