mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1190680: Part 1 - [webext] Factor common extension context logic into a shared base class. r=billm
This commit is contained in:
parent
64d33abd44
commit
724b49e80c
@ -81,6 +81,7 @@ ExtensionManagement.registerSchema("chrome://extensions/content/schemas/web_requ
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
var {
|
||||
BaseContext,
|
||||
LocaleData,
|
||||
MessageBroker,
|
||||
Messenger,
|
||||
@ -218,63 +219,45 @@ var gContextId = 0;
|
||||
// |uri| is the URI of the content (optional).
|
||||
// |docShell| is the docshell the content runs in (optional).
|
||||
// |incognito| is the content running in a private context (default: false).
|
||||
ExtensionPage = function(extension, params) {
|
||||
let {type, contentWindow, uri} = params;
|
||||
this.extension = extension;
|
||||
this.type = type;
|
||||
this.contentWindow = contentWindow || null;
|
||||
this.uri = uri || extension.baseURI;
|
||||
this.incognito = params.incognito || false;
|
||||
this.onClose = new Set();
|
||||
this.contextId = gContextId++;
|
||||
this.unloaded = false;
|
||||
ExtensionPage = class extends BaseContext {
|
||||
constructor(extension, params) {
|
||||
super();
|
||||
|
||||
// This is the MessageSender property passed to extension.
|
||||
// It can be augmented by the "page-open" hook.
|
||||
let sender = {id: extension.uuid};
|
||||
if (uri) {
|
||||
sender.url = uri.spec;
|
||||
let {type, contentWindow, uri} = params;
|
||||
this.extension = extension;
|
||||
this.type = type;
|
||||
this.contentWindow = contentWindow || null;
|
||||
this.uri = uri || extension.baseURI;
|
||||
this.incognito = params.incognito || false;
|
||||
this.contextId = gContextId++;
|
||||
this.unloaded = false;
|
||||
|
||||
// This is the MessageSender property passed to extension.
|
||||
// It can be augmented by the "page-open" hook.
|
||||
let sender = {id: extension.uuid};
|
||||
if (uri) {
|
||||
sender.url = uri.spec;
|
||||
}
|
||||
let delegate = {
|
||||
getSender() {},
|
||||
};
|
||||
Management.emit("page-load", this, params, sender, delegate);
|
||||
|
||||
// Properties in |filter| must match those in the |recipient|
|
||||
// parameter of sendMessage.
|
||||
let filter = {extensionId: extension.id};
|
||||
this.messenger = new Messenger(this, globalBroker, sender, filter, delegate);
|
||||
|
||||
this.extension.views.add(this);
|
||||
}
|
||||
let delegate = {
|
||||
getSender() {},
|
||||
};
|
||||
Management.emit("page-load", this, params, sender, delegate);
|
||||
|
||||
// Properties in |filter| must match those in the |recipient|
|
||||
// parameter of sendMessage.
|
||||
let filter = {extensionId: extension.id};
|
||||
this.messenger = new Messenger(this, globalBroker, sender, filter, delegate);
|
||||
|
||||
this.extension.views.add(this);
|
||||
};
|
||||
|
||||
ExtensionPage.prototype = {
|
||||
get cloneScope() {
|
||||
return this.contentWindow;
|
||||
},
|
||||
}
|
||||
|
||||
get principal() {
|
||||
return this.contentWindow.document.nodePrincipal;
|
||||
},
|
||||
|
||||
checkLoadURL(url, options = {}) {
|
||||
let ssm = Services.scriptSecurityManager;
|
||||
|
||||
let flags = ssm.STANDARD;
|
||||
if (!options.allowScript) {
|
||||
flags |= ssm.DISALLOW_SCRIPT;
|
||||
}
|
||||
if (!options.allowInheritsPrincipal) {
|
||||
flags |= ssm.DISALLOW_INHERIT_PRINCIPAL;
|
||||
}
|
||||
|
||||
try {
|
||||
ssm.checkLoadURIStrWithPrincipal(this.principal, url, flags);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}
|
||||
|
||||
// A wrapper around MessageChannel.sendMessage which adds the extension ID
|
||||
// to the recipient object, and ensures replies are not processed after the
|
||||
@ -285,21 +268,13 @@ ExtensionPage.prototype = {
|
||||
sender.contextId = this.contextId;
|
||||
|
||||
return MessageChannel.sendMessage(target, messageName, data, recipient, sender);
|
||||
},
|
||||
|
||||
callOnClose(obj) {
|
||||
this.onClose.add(obj);
|
||||
},
|
||||
|
||||
forgetOnClose(obj) {
|
||||
this.onClose.delete(obj);
|
||||
},
|
||||
}
|
||||
|
||||
// Called when the extension shuts down.
|
||||
shutdown() {
|
||||
Management.emit("page-shutdown", this);
|
||||
this.unload();
|
||||
},
|
||||
}
|
||||
|
||||
// This method is called when an extension page navigates away or
|
||||
// its tab is closed.
|
||||
@ -322,10 +297,8 @@ ExtensionPage.prototype = {
|
||||
|
||||
this.extension.views.delete(this);
|
||||
|
||||
for (let obj of this.onClose) {
|
||||
obj.close();
|
||||
}
|
||||
},
|
||||
super.unload();
|
||||
}
|
||||
};
|
||||
|
||||
// Responsible for loading extension APIs into the right globals.
|
||||
|
@ -37,6 +37,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
|
||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
var {
|
||||
runSafeSyncWithoutClone,
|
||||
BaseContext,
|
||||
LocaleData,
|
||||
MessageBroker,
|
||||
Messenger,
|
||||
@ -227,109 +228,103 @@ var ExtensionManager;
|
||||
// Scope in which extension content script code can run. It uses
|
||||
// Cu.Sandbox to run the code. There is a separate scope for each
|
||||
// frame.
|
||||
function ExtensionContext(extensionId, contentWindow, contextOptions = {}) {
|
||||
let { isExtensionPage } = contextOptions;
|
||||
class ExtensionContext extends BaseContext {
|
||||
constructor(extensionId, contentWindow, contextOptions = {}) {
|
||||
super();
|
||||
|
||||
this.isExtensionPage = isExtensionPage;
|
||||
this.extension = ExtensionManager.get(extensionId);
|
||||
this.extensionId = extensionId;
|
||||
this.contentWindow = contentWindow;
|
||||
let { isExtensionPage } = contextOptions;
|
||||
|
||||
this.onClose = new Set();
|
||||
this.isExtensionPage = isExtensionPage;
|
||||
this.extension = ExtensionManager.get(extensionId);
|
||||
this.extensionId = extensionId;
|
||||
this.contentWindow = contentWindow;
|
||||
|
||||
let utils = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
let outerWindowId = utils.outerWindowID;
|
||||
let frameId = contentWindow == contentWindow.top ? 0 : outerWindowId;
|
||||
this.frameId = frameId;
|
||||
let utils = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
let outerWindowId = utils.outerWindowID;
|
||||
let frameId = contentWindow == contentWindow.top ? 0 : outerWindowId;
|
||||
this.frameId = frameId;
|
||||
|
||||
let mm = getWindowMessageManager(contentWindow);
|
||||
this.messageManager = mm;
|
||||
let mm = getWindowMessageManager(contentWindow);
|
||||
this.messageManager = mm;
|
||||
|
||||
let prin;
|
||||
let contentPrincipal = contentWindow.document.nodePrincipal;
|
||||
let ssm = Services.scriptSecurityManager;
|
||||
|
||||
let prin;
|
||||
let contentPrincipal = contentWindow.document.nodePrincipal;
|
||||
let ssm = Services.scriptSecurityManager;
|
||||
if (ssm.isSystemPrincipal(contentPrincipal)) {
|
||||
// Make sure we don't hand out the system principal by accident.
|
||||
prin = Cc["@mozilla.org/nullprincipal;1"].createInstance(Ci.nsIPrincipal);
|
||||
} else {
|
||||
let extensionPrincipal = ssm.createCodebasePrincipal(this.extension.baseURI, {addonId: extensionId});
|
||||
prin = [contentPrincipal, extensionPrincipal];
|
||||
}
|
||||
Object.defineProperty(this, "principal",
|
||||
{value: extensionPrincipal, enumerable: true, configurable: true});
|
||||
|
||||
if (isExtensionPage) {
|
||||
if (ExtensionManagement.getAddonIdForWindow(this.contentWindow) != extensionId) {
|
||||
throw new Error("Invalid target window for this extension context");
|
||||
if (ssm.isSystemPrincipal(contentPrincipal)) {
|
||||
// Make sure we don't hand out the system principal by accident.
|
||||
prin = Cc["@mozilla.org/nullprincipal;1"].createInstance(Ci.nsIPrincipal);
|
||||
} else {
|
||||
prin = [contentPrincipal, extensionPrincipal];
|
||||
}
|
||||
|
||||
if (isExtensionPage) {
|
||||
if (ExtensionManagement.getAddonIdForWindow(this.contentWindow) != extensionId) {
|
||||
throw new Error("Invalid target window for this extension context");
|
||||
}
|
||||
// This is an iframe with content script API enabled and its principal should be the
|
||||
// contentWindow itself. (we create a sandbox with the contentWindow as principal and with X-rays disabled
|
||||
// because it enables us to create the APIs object in this sandbox object and then copying it
|
||||
// into the iframe's window, see Bug 1214658 for rationale)
|
||||
this.sandbox = Cu.Sandbox(contentWindow, {
|
||||
sandboxPrototype: contentWindow,
|
||||
wantXrays: false,
|
||||
isWebExtensionContentScript: true,
|
||||
});
|
||||
} else {
|
||||
this.sandbox = Cu.Sandbox(prin, {
|
||||
sandboxPrototype: contentWindow,
|
||||
wantXrays: true,
|
||||
isWebExtensionContentScript: true,
|
||||
wantGlobalProperties: ["XMLHttpRequest"],
|
||||
});
|
||||
}
|
||||
|
||||
let delegate = {
|
||||
getSender(context, target, sender) {
|
||||
// Nothing to do here.
|
||||
},
|
||||
};
|
||||
|
||||
let url = contentWindow.location.href;
|
||||
let broker = ExtensionContent.getBroker(mm);
|
||||
// The |sender| parameter is passed directly to the extension.
|
||||
let sender = {id: this.extension.uuid, frameId, url};
|
||||
// Properties in |filter| must match those in the |recipient|
|
||||
// parameter of sendMessage.
|
||||
let filter = {extensionId, frameId};
|
||||
this.messenger = new Messenger(this, broker, sender, filter, delegate);
|
||||
|
||||
this.chromeObj = Cu.createObjectIn(this.sandbox, {defineAs: "browser"});
|
||||
|
||||
// Sandboxes don't get Xrays for some weird compatibility
|
||||
// reason. However, we waive here anyway in case that changes.
|
||||
Cu.waiveXrays(this.sandbox).chrome = this.chromeObj;
|
||||
|
||||
injectAPI(api(this), this.chromeObj);
|
||||
|
||||
// This is an iframe with content script API enabled. (See Bug 1214658 for rationale)
|
||||
if (isExtensionPage) {
|
||||
Cu.waiveXrays(this.contentWindow).chrome = this.chromeObj;
|
||||
Cu.waiveXrays(this.contentWindow).browser = this.chromeObj;
|
||||
}
|
||||
// This is an iframe with content script API enabled and its principal should be the
|
||||
// contentWindow itself. (we create a sandbox with the contentWindow as principal and with X-rays disabled
|
||||
// because it enables us to create the APIs object in this sandbox object and then copying it
|
||||
// into the iframe's window, see Bug 1214658 for rationale)
|
||||
this.sandbox = Cu.Sandbox(contentWindow, {
|
||||
sandboxPrototype: contentWindow,
|
||||
wantXrays: false,
|
||||
isWebExtensionContentScript: true,
|
||||
});
|
||||
} else {
|
||||
this.sandbox = Cu.Sandbox(prin, {
|
||||
sandboxPrototype: contentWindow,
|
||||
wantXrays: true,
|
||||
isWebExtensionContentScript: true,
|
||||
wantGlobalProperties: ["XMLHttpRequest"],
|
||||
});
|
||||
}
|
||||
|
||||
let delegate = {
|
||||
getSender(context, target, sender) {
|
||||
// Nothing to do here.
|
||||
},
|
||||
};
|
||||
|
||||
let url = contentWindow.location.href;
|
||||
let broker = ExtensionContent.getBroker(mm);
|
||||
// The |sender| parameter is passed directly to the extension.
|
||||
let sender = {id: this.extension.uuid, frameId, url};
|
||||
// Properties in |filter| must match those in the |recipient|
|
||||
// parameter of sendMessage.
|
||||
let filter = {extensionId, frameId};
|
||||
this.messenger = new Messenger(this, broker, sender, filter, delegate);
|
||||
|
||||
this.chromeObj = Cu.createObjectIn(this.sandbox, {defineAs: "browser"});
|
||||
|
||||
// Sandboxes don't get Xrays for some weird compatibility
|
||||
// reason. However, we waive here anyway in case that changes.
|
||||
Cu.waiveXrays(this.sandbox).chrome = this.chromeObj;
|
||||
|
||||
injectAPI(api(this), this.chromeObj);
|
||||
|
||||
// This is an iframe with content script API enabled. (See Bug 1214658 for rationale)
|
||||
if (isExtensionPage) {
|
||||
Cu.waiveXrays(this.contentWindow).chrome = this.chromeObj;
|
||||
Cu.waiveXrays(this.contentWindow).browser = this.chromeObj;
|
||||
}
|
||||
}
|
||||
|
||||
ExtensionContext.prototype = {
|
||||
get cloneScope() {
|
||||
return this.sandbox;
|
||||
},
|
||||
}
|
||||
|
||||
execute(script, shouldRun) {
|
||||
script.tryInject(this.extension, this.contentWindow, this.sandbox, shouldRun);
|
||||
},
|
||||
|
||||
callOnClose(obj) {
|
||||
this.onClose.add(obj);
|
||||
},
|
||||
|
||||
forgetOnClose(obj) {
|
||||
this.onClose.delete(obj);
|
||||
},
|
||||
}
|
||||
|
||||
close() {
|
||||
for (let obj of this.onClose) {
|
||||
obj.close();
|
||||
}
|
||||
super.unload();
|
||||
|
||||
// Overwrite the content script APIs with an empty object if the APIs objects are still
|
||||
// defined in the content window (See Bug 1214658 for rationale).
|
||||
@ -338,11 +333,10 @@ ExtensionContext.prototype = {
|
||||
Cu.createObjectIn(this.contentWindow, { defineAs: "browser" });
|
||||
Cu.createObjectIn(this.contentWindow, { defineAs: "chrome" });
|
||||
}
|
||||
|
||||
Cu.nukeSandbox(this.sandbox);
|
||||
this.sandbox = null;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function windowId(window) {
|
||||
return window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
|
@ -112,6 +112,53 @@ DefaultWeakMap.prototype = {
|
||||
},
|
||||
};
|
||||
|
||||
class BaseContext {
|
||||
constructor() {
|
||||
this.onClose = new Set();
|
||||
}
|
||||
|
||||
get cloneScope() {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
get principal() {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
checkLoadURL(url, options = {}) {
|
||||
let ssm = Services.scriptSecurityManager;
|
||||
|
||||
let flags = ssm.STANDARD;
|
||||
if (!options.allowScript) {
|
||||
flags |= ssm.DISALLOW_SCRIPT;
|
||||
}
|
||||
if (!options.allowInheritsPrincipal) {
|
||||
flags |= ssm.DISALLOW_INHERIT_PRINCIPAL;
|
||||
}
|
||||
|
||||
try {
|
||||
ssm.checkLoadURIStrWithPrincipal(this.principal, url, flags);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
callOnClose(obj) {
|
||||
this.onClose.add(obj);
|
||||
}
|
||||
|
||||
forgetOnClose(obj) {
|
||||
this.onClose.delete(obj);
|
||||
}
|
||||
|
||||
unload() {
|
||||
for (let obj of this.onClose) {
|
||||
obj.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function LocaleData(data) {
|
||||
this.defaultLocale = data.defaultLocale;
|
||||
this.selectedLocale = data.selectedLocale;
|
||||
@ -786,6 +833,7 @@ this.ExtensionUtils = {
|
||||
runSafeSyncWithoutClone,
|
||||
runSafe,
|
||||
runSafeSync,
|
||||
BaseContext,
|
||||
DefaultWeakMap,
|
||||
EventManager,
|
||||
LocaleData,
|
||||
|
Loading…
Reference in New Issue
Block a user