Bug 1309906 - part1: adapt webext-oop internals for devtools contexts and APIs. r=kmag

MozReview-Commit-ID: E0gxV271N31

--HG--
extra : rebase_source : 62e0ef8ac8f7060ff438126982b0d66ec7bfa17b
This commit is contained in:
Luca Greco 2016-11-14 21:38:20 +01:00
Родитель ef2eba34ab
Коммит 54efa5c5bb
4 изменённых файлов: 155 добавлений и 22 удалений

Просмотреть файл

@ -32,6 +32,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
"resource://gre/modules/Schemas.jsm");
const CATEGORY_EXTENSION_SCRIPTS_ADDON = "webextension-scripts-addon";
const CATEGORY_EXTENSION_SCRIPTS_DEVTOOLS = "webextension-scripts-devtools";
Cu.import("resource://gre/modules/ExtensionCommon.jsm");
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
@ -463,6 +464,29 @@ var apiManager = new class extends SchemaAPIManager {
}
}();
var devtoolsAPIManager = new class extends SchemaAPIManager {
constructor() {
super("devtools");
this.initialized = false;
}
generateAPIs(...args) {
if (!this.initialized) {
this.initialized = true;
for (let [/* name */, value] of XPCOMUtils.enumerateCategoryEntries(CATEGORY_EXTENSION_SCRIPTS_DEVTOOLS)) {
this.loadScript(value);
}
}
return super.generateAPIs(...args);
}
registerSchemaAPI(namespace, envType, getAPI) {
if (envType == "devtools_child") {
super.registerSchemaAPI(namespace, envType, getAPI);
}
}
}();
/**
* An object that runs an remote implementation of an API.
*/
@ -686,6 +710,19 @@ class ChildAPIManager {
if (allowedContexts.includes("addon_parent_only")) {
return false;
}
// Do not generate devtools APIs, unless explicitly allowed.
if (this.context.envType === "devtools_child" &&
!allowedContexts.includes("devtools")) {
return false;
}
// Do not generate devtools APIs, unless explicitly allowed.
if (this.context.envType !== "devtools_child" &&
allowedContexts.includes("devtools_only")) {
return false;
}
return true;
}
@ -711,32 +748,32 @@ class ChildAPIManager {
}
}
class ExtensionPageContextChild extends BaseContext {
class ExtensionBaseContextChild extends BaseContext {
/**
* This ExtensionPageContextChild represents a privileged addon
* execution environment that has full access to the WebExtensions
* APIs (provided that the correct permissions have been requested).
*
* This is the child side of the ExtensionPageContextParent class
* defined in ExtensionParent.jsm.
* This ExtensionBaseContextChild represents an addon execution environment
* that is running in an addon or devtools child process.
*
* @param {BrowserExtensionContent} extension This context's owner.
* @param {object} params
* @param {string} params.envType One of "addon_child" or "devtools_child".
* @param {nsIDOMWindow} params.contentWindow The window where the addon runs.
* @param {string} params.viewType One of "background", "popup" or "tab".
* "background" and "tab" are used by `browser.extension.getViews`.
* "popup" is only used internally to identify page action and browser
* action popups and options_ui pages.
* @param {string} params.viewType One of "background", "popup", "tab",
* "devtools_page" or "devtools_panel".
* @param {number} [params.tabId] This tab's ID, used if viewType is "tab".
*/
constructor(extension, params) {
super("addon_child", extension);
if (!params.envType) {
throw new Error("Missing envType");
}
if (Services.appinfo.processType != Services.appinfo.PROCESS_TYPE_DEFAULT) {
// This check is temporary. It should be removed once the proxy creation
// is asynchronous.
throw new Error("ExtensionPageContextChild cannot be created in child processes");
throw new Error("ExtensionContext cannot be created in child processes");
}
super(params.envType, extension);
let {viewType, uri, contentWindow, tabId} = params;
this.viewType = viewType;
this.uri = uri || extension.baseURI;
@ -769,8 +806,6 @@ class ExtensionPageContextChild extends BaseContext {
Schemas.inject(chromeObj, chromeApiWrapper);
return chromeObj;
});
this.extension.views.add(this);
}
get cloneScope() {
@ -808,11 +843,10 @@ class ExtensionPageContextChild extends BaseContext {
}
super.unload();
this.extension.views.delete(this);
}
}
defineLazyGetter(ExtensionPageContextChild.prototype, "messenger", function() {
defineLazyGetter(ExtensionBaseContextChild.prototype, "messenger", function() {
let filter = {extensionId: this.extension.id};
let optionalFilter = {};
// Addon-generated messages (not necessarily from the same process as the
@ -823,16 +857,89 @@ defineLazyGetter(ExtensionPageContextChild.prototype, "messenger", function() {
filter, optionalFilter);
});
class ExtensionPageContextChild extends ExtensionBaseContextChild {
/**
* This ExtensionPageContextChild represents a privileged addon
* execution environment that has full access to the WebExtensions
* APIs (provided that the correct permissions have been requested).
*
* This is the child side of the ExtensionPageContextParent class
* defined in ExtensionParent.jsm.
*
* @param {BrowserExtensionContent} extension This context's owner.
* @param {object} params
* @param {nsIDOMWindow} params.contentWindow The window where the addon runs.
* @param {string} params.viewType One of "background", "popup" or "tab".
* "background" and "tab" are used by `browser.extension.getViews`.
* "popup" is only used internally to identify page action and browser
* action popups and options_ui pages.
* @param {number} [params.tabId] This tab's ID, used if viewType is "tab".
*/
constructor(extension, params) {
super(extension, Object.assign(params, {envType: "addon_child"}));
this.extension.views.add(this);
}
unload() {
super.unload();
this.extension.views.delete(this);
}
}
defineLazyGetter(ExtensionPageContextChild.prototype, "childManager", function() {
let localApis = {};
apiManager.generateAPIs(this, localApis);
let childManager = new ChildAPIManager(this, this.messageManager, localApis, {
envType: "addon_parent",
viewType: this.viewType,
url: this.uri.spec,
incognito: this.incognito,
});
this.callOnClose(childManager);
if (this.viewType == "background") {
apiManager.global.initializeBackgroundPage(this.contentWindow);
}
return childManager;
});
class DevtoolsContextChild extends ExtensionBaseContextChild {
/**
* This DevtoolsContextChild represents a devtools-related addon execution
* environment that has access to the devtools API namespace and to the same subset
* of APIs available in a content script execution environment.
*
* @param {BrowserExtensionContent} extension This context's owner.
* @param {object} params
* @param {nsIDOMWindow} params.contentWindow The window where the addon runs.
* @param {string} params.viewType One of "devtools_page" or "devtools_panel".
* @param {object} [params.devtoolsToolboxInfo] This devtools toolbox's information,
* used if viewType is "devtools_page" or "devtools_panel".
*/
constructor(extension, params) {
super(extension, Object.assign(params, {envType: "devtools_child"}));
this.devtoolsToolboxInfo = params.devtoolsToolboxInfo;
this.extension.devtoolsViews.add(this);
}
unload() {
super.unload();
this.extension.devtoolsViews.delete(this);
}
}
defineLazyGetter(DevtoolsContextChild.prototype, "childManager", function() {
let localApis = {};
devtoolsAPIManager.generateAPIs(this, localApis);
let childManager = new ChildAPIManager(this, this.messageManager, localApis, {
envType: "addon_parent",
envType: "devtools_parent",
viewType: this.viewType,
url: this.uri.spec,
incognito: this.incognito,
@ -891,6 +998,11 @@ class ContentGlobal {
this.global.removeMessageListener("Extension:InitExtensionView", this);
let {viewType, url} = data;
this.viewType = viewType;
if (data.devtoolsToolboxInfo) {
this.devtoolsToolboxInfo = data.devtoolsToolboxInfo;
}
this.global.addEventListener("DOMContentLoaded", this);
if (url) {
// TODO(robwu): Remove this check. It is only here because the popup
@ -991,11 +1103,21 @@ ExtensionChild = {
.getInterface(Ci.nsIDocShell)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
let {viewType, tabId} = this.contentGlobals.get(mm).ensureInitialized();
let {
viewType, tabId,
devtoolsToolboxInfo,
} = this.contentGlobals.get(mm).ensureInitialized();
let uri = contentWindow.document.documentURIObject;
context = new ExtensionPageContextChild(extension, {viewType, contentWindow, uri, tabId});
if (devtoolsToolboxInfo) {
context = new DevtoolsContextChild(extension, {
viewType, contentWindow, uri, tabId, devtoolsToolboxInfo,
});
} else {
context = new ExtensionPageContextChild(extension, {viewType, contentWindow, uri, tabId});
}
this.extensionContexts.set(windowId, context);
},

Просмотреть файл

@ -542,6 +542,7 @@ class SchemaAPIManager extends EventEmitter {
* "main" - The main, one and only chrome browser process.
* "addon" - An addon process.
* "content" - A content process.
* "devtools" - A devtools process.
*/
constructor(processType) {
super();
@ -553,6 +554,8 @@ class SchemaAPIManager extends EventEmitter {
addon_child: [],
content_parent: [],
content_child: [],
devtools_parent: [],
devtools_child: [],
};
}
@ -608,6 +611,8 @@ class SchemaAPIManager extends EventEmitter {
* - "addon_child" - addon APIs that runs in an addon process.
* - "content_parent" - content script APIs that runs in the main process.
* - "content_child" - content script APIs that runs in a content process.
* - "devtools_parent" - devtools APIs that runs in the main process.
* - "devtools_child" - devtools APIs that runs in a devtools process.
* @param {function(BaseContext)} getAPI A function that returns an object
* that will be merged with |chrome| and |browser|. The next example adds
* the create, update and remove methods to the tabs API.

Просмотреть файл

@ -797,6 +797,9 @@ class BrowserExtensionContent extends EventEmitter {
// Only used in addon processes.
this.views = new Set();
// Only used for devtools views.
this.devtoolsViews = new Set();
let uri = Services.io.newURI(data.resourceURL, null, null);
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {

Просмотреть файл

@ -95,7 +95,8 @@ let apiManager = new class extends SchemaAPIManager {
}
registerSchemaAPI(namespace, envType, getAPI) {
if (envType == "addon_parent" || envType == "content_parent") {
if (envType == "addon_parent" || envType == "content_parent" ||
envType == "devtools_parent") {
super.registerSchemaAPI(namespace, envType, getAPI);
}
}
@ -321,6 +322,8 @@ class ExtensionPageContextParent extends ProxyContextParent {
super(envType, extension, params, xulBrowser, extension.principal);
this.viewType = params.viewType;
extension.emit("extension-proxy-context-load", this);
}
// The window that contains this context. This may change due to moving tabs.
@ -425,7 +428,7 @@ ParentAPIManager = {
}
let context;
if (envType == "addon_parent") {
if (envType == "addon_parent" || envType == "devtools_parent") {
// Privileged addon contexts can only be loaded in documents whose main
// frame is also the same addon.
if (principal.URI.prePath !== extension.baseURI.prePath ||