Bug 1190680: Part 1 - [webext] Factor common extension context logic into a shared base class. r=billm

This commit is contained in:
Kris Maglione 2016-01-29 18:39:29 -08:00
parent 64d33abd44
commit 724b49e80c
3 changed files with 167 additions and 152 deletions

View File

@ -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,14 +219,16 @@ 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) {
ExtensionPage = class extends BaseContext {
constructor(extension, params) {
super();
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;
@ -246,35 +249,15 @@ ExtensionPage = function(extension, params) {
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.

View File

@ -37,6 +37,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
runSafeSyncWithoutClone,
BaseContext,
LocaleData,
MessageBroker,
Messenger,
@ -227,7 +228,10 @@ 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 = {}) {
class ExtensionContext extends BaseContext {
constructor(extensionId, contentWindow, contextOptions = {}) {
super();
let { isExtensionPage } = contextOptions;
this.isExtensionPage = isExtensionPage;
@ -235,8 +239,6 @@ function ExtensionContext(extensionId, contentWindow, contextOptions = {}) {
this.extensionId = extensionId;
this.contentWindow = contentWindow;
this.onClose = new Set();
let utils = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let outerWindowId = utils.outerWindowID;
@ -249,11 +251,15 @@ function ExtensionContext(extensionId, contentWindow, contextOptions = {}) {
let prin;
let contentPrincipal = contentWindow.document.nodePrincipal;
let ssm = Services.scriptSecurityManager;
let extensionPrincipal = ssm.createCodebasePrincipal(this.extension.baseURI, {addonId: extensionId});
Object.defineProperty(this, "principal",
{value: extensionPrincipal, enumerable: true, configurable: true});
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];
}
@ -309,27 +315,16 @@ function ExtensionContext(extensionId, contentWindow, contextOptions = {}) {
}
}
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)

View File

@ -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,