Bug 1287010 - Make environment of Context explicit. r=billm

- Add `envType` to BaseContext.
 - Pass an explicit envType to all `registerSchemaAPI` invocations.
 - The factories passed to `registerSchemaAPI` will be split up later, so
   that content scripts (`content_child`) and addon pages can share
   common implementations.
 - The factories that implement the addon API will also be split up,
   to separate code running in the main process (`addon_parent`) from
   code running in a child process (`addon_child`).
 - Remove the use of a hardcoded list of `namespaces` from ProxyContext.
   Now `envType` is used to specify whether an API should be activated.

MozReview-Commit-ID: Jiff8HIwG92

--HG--
extra : rebase_source : 946a3c0009a4e3223c2d10044b3099a94c845394
This commit is contained in:
Rob Wu 2016-08-16 15:51:50 -07:00
parent 1067b066d5
commit be3652cd57
28 changed files with 72 additions and 50 deletions

View File

@ -77,7 +77,7 @@ function convert(result) {
return node;
}
extensions.registerSchemaAPI("bookmarks", context => {
extensions.registerSchemaAPI("bookmarks", "addon_parent", context => {
return {
bookmarks: {
get: function(idOrIdList) {

View File

@ -365,7 +365,7 @@ extensions.on("shutdown", (type, extension) => {
});
/* eslint-enable mozilla/balanced-listeners */
extensions.registerSchemaAPI("browserAction", context => {
extensions.registerSchemaAPI("browserAction", "addon_parent", context => {
let {extension} = context;
return {
browserAction: {

View File

@ -228,7 +228,7 @@ extensions.on("shutdown", (type, extension) => {
});
/* eslint-enable mozilla/balanced-listeners */
extensions.registerSchemaAPI("commands", context => {
extensions.registerSchemaAPI("commands", "addon_parent", context => {
let {extension} = context;
return {
commands: {

View File

@ -485,7 +485,7 @@ extensions.on("shutdown", (type, extension) => {
});
/* eslint-enable mozilla/balanced-listeners */
extensions.registerSchemaAPI("contextMenus", context => {
extensions.registerSchemaAPI("contextMenus", "addon_parent", context => {
let {extension} = context;
return {
contextMenus: {

View File

@ -130,7 +130,7 @@ function getObserver() {
return _observer;
}
extensions.registerSchemaAPI("history", context => {
extensions.registerSchemaAPI("history", "addon_parent", context => {
return {
history: {
addUrl: function(details) {

View File

@ -217,7 +217,7 @@ PageAction.for = extension => {
global.pageActionFor = PageAction.for;
extensions.registerSchemaAPI("pageAction", context => {
extensions.registerSchemaAPI("pageAction", "addon_parent", context => {
let {extension} = context;
return {
pageAction: {

View File

@ -264,7 +264,7 @@ let tabListener = {
},
};
extensions.registerSchemaAPI("tabs", context => {
extensions.registerSchemaAPI("tabs", "addon_parent", context => {
let {extension} = context;
let self = {
tabs: {

View File

@ -15,7 +15,7 @@ var {
EventManager,
} = ExtensionUtils;
extensions.registerSchemaAPI("windows", context => {
extensions.registerSchemaAPI("windows", "addon_parent", context => {
let {extension} = context;
return {
windows: {

View File

@ -117,7 +117,7 @@ extensions.on("shutdown", (type, extension) => {
});
/* eslint-enable mozilla/balanced-listeners */
extensions.registerSchemaAPI("pageAction", context => {
extensions.registerSchemaAPI("pageAction", "addon_parent", context => {
let {extension} = context;
return {
pageAction: {

View File

@ -118,7 +118,12 @@ var ExtensionContext, GlobalManager;
var Management = {
initialized: null,
scopes: [],
schemaApis: [],
schemaApis: {
addon_parent: [],
addon_child: [],
content_parent: [],
content_child: [],
},
emitter: new EventEmitter(),
// Loads all the ext-*.js scripts currently registered.
@ -152,25 +157,38 @@ var Management = {
* Called by an ext-*.js script to register an API.
*
* @param {string} namespace The API namespace.
* Used to determine whether the API should be generated when the caller
* requests a subset of the available APIs (e.g. in content scripts).
* Intended to match the namespace of the generated API, but not used at
* the moment - see bugzil.la/1295774.
* @param {string} envType Restricts the API to contexts that run in the
* given environment. Must be one of the following:
* - "addon_parent" - addon APIs that runs in the main process.
* - "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.
* @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.
*
* registerSchemaAPI("tabs", (context) => ({
* registerSchemaAPI("tabs", "addon_parent", (context) => ({
* tabs: { create, update },
* }));
* registerSchemaAPI("tabs", (context) => ({
* registerSchemaAPI("tabs", "addon_parent", (context) => ({
* tabs: { remove },
* }));
*/
registerSchemaAPI(namespace, getAPI) {
this.schemaApis.push({namespace, getAPI});
registerSchemaAPI(namespace, envType, getAPI) {
this.schemaApis[envType].push({namespace, getAPI});
if (envType === "addon_child") {
// TODO(robwu): Remove this. It is a temporary hack to ease the transition
// from ext-*.js running in the parent to APIs running in a child process.
// This can be removed once there is a dedicated ExtensionContext with type
// "addon_child".
this.schemaApis.addon_parent.push({namespace, getAPI});
}
},
// Mash together all the APIs from apis into obj.
generateAPIs(context, apis, obj, namespaces = null) {
generateAPIs(context, apis, obj) {
// Recursively copy properties from source to dest.
function copy(dest, source) {
for (let prop in source) {
@ -187,9 +205,6 @@ var Management = {
}
for (let api of apis) {
if (namespaces && !namespaces.includes(api.namespace)) {
continue;
}
if (api.permission) {
if (!context.extension.hasPermission(api.permission)) {
continue;
@ -229,7 +244,9 @@ var Management = {
// |incognito| is the content running in a private context (default: false).
ExtensionContext = class extends BaseContext {
constructor(extension, params) {
super(extension);
// TODO(robwu): This should be addon_child once all ext- files are split.
// There should be a new ProxyContext instance with the "addon_parent" type.
super("addon_parent", extension);
let {type, uri} = params;
this.type = type;
@ -305,11 +322,14 @@ class ProxyContext extends ExtensionContext {
params.uri = NetUtil.newURI(params.url);
super(extension, params);
// TODO(robwu): Get ProxyContext to inherit from BaseContext instead of
// ExtensionContext and let callers specify the environment type.
this.envType = "content_parent";
this.messageManager = messageManager;
this.principal_ = principal;
this.apiObj = {};
GlobalManager.injectInObject(this, null, this.apiObj, ["storage", "test"]);
GlobalManager.injectInObject(this, null, this.apiObj);
this.listenerProxies = new Map();
@ -609,12 +629,12 @@ GlobalManager = {
return this.extensionMap.get(extensionId);
},
injectInObject(context, defaultCallback, dest, namespaces = null) {
injectInObject(context, defaultCallback, dest) {
let apis = {
extensionTypes: {},
};
Management.generateAPIs(context, Management.schemaApis, apis, namespaces);
Management.generateAPIs(context, context.extension.apis, apis, namespaces);
Management.generateAPIs(context, Management.schemaApis[context.envType], apis);
Management.generateAPIs(context, context.extension.apis, apis);
let schemaWrapper = {
get principal() {
@ -656,9 +676,6 @@ GlobalManager = {
},
shouldInject(namespace, name) {
if (namespaces && !namespaces.includes(namespace)) {
return false;
}
return findPathInObject(apis, [namespace]) != null;
},

View File

@ -315,7 +315,7 @@ var ExtensionManager;
// frame.
class ExtensionContext extends BaseContext {
constructor(extension, contentWindow, contextOptions = {}) {
super(extension);
super("content_child", extension);
let {isExtensionPage} = contextOptions;

View File

@ -162,7 +162,8 @@ class SpreadArgs extends Array {
let gContextId = 0;
class BaseContext {
constructor(extension) {
constructor(envType, extension) {
this.envType = envType;
this.onClose = new Set();
this.checkedLastError = false;
this._lastError = null;

View File

@ -93,7 +93,7 @@ extensions.on("shutdown", (type, extension) => {
});
/* eslint-enable mozilla/balanced-listeners */
extensions.registerSchemaAPI("alarms", context => {
extensions.registerSchemaAPI("alarms", "addon_parent", context => {
let {extension} = context;
return {
alarms: {

View File

@ -144,7 +144,7 @@ extensions.on("shutdown", (type, extension) => {
});
/* eslint-enable mozilla/balanced-listeners */
extensions.registerSchemaAPI("extension", context => {
extensions.registerSchemaAPI("extension", "addon_parent", context => {
let {extension} = context;
return {
extension: {

View File

@ -238,7 +238,7 @@ function* query(detailsIn, props, extension) {
}
}
extensions.registerSchemaAPI("cookies", context => {
extensions.registerSchemaAPI("cookies", "addon_parent", context => {
let {extension} = context;
let self = {
cookies: {

View File

@ -386,7 +386,7 @@ function queryHelper(query) {
});
}
extensions.registerSchemaAPI("downloads", context => {
extensions.registerSchemaAPI("downloads", "addon_parent", context => {
let {extension} = context;
return {
downloads: {

View File

@ -1,6 +1,6 @@
"use strict";
extensions.registerSchemaAPI("extension", context => {
extensions.registerSchemaAPI("extension", "addon_parent", context => {
let {extension} = context;
return {
extension: {

View File

@ -7,7 +7,7 @@ var {
detectLanguage,
} = ExtensionUtils;
extensions.registerSchemaAPI("i18n", context => {
extensions.registerSchemaAPI("i18n", "addon_parent", context => {
let {extension} = context;
return {
i18n: {

View File

@ -1,6 +1,6 @@
"use strict";
extensions.registerSchemaAPI("idle", context => {
extensions.registerSchemaAPI("idle", "addon_parent", context => {
return {
idle: {
queryState: function(detectionIntervalInSeconds) {

View File

@ -16,7 +16,7 @@ function installType(addon) {
return "normal";
}
extensions.registerSchemaAPI("management", context => {
extensions.registerSchemaAPI("management", "addon_parent", context => {
let {extension} = context;
return {
management: {

View File

@ -92,7 +92,7 @@ extensions.on("shutdown", (type, extension) => {
var nextId = 0;
extensions.registerSchemaAPI("notifications", context => {
extensions.registerSchemaAPI("notifications", "addon_parent", context => {
let {extension} = context;
return {
notifications: {

View File

@ -20,7 +20,7 @@ var {
XPCOMUtils.defineLazyModuleGetter(this, "NativeApp",
"resource://gre/modules/NativeMessaging.jsm");
extensions.registerSchemaAPI("runtime", context => {
extensions.registerSchemaAPI("runtime", "addon_parent", context => {
let {extension} = context;
return {
runtime: {

View File

@ -10,7 +10,7 @@ var {
EventManager,
} = ExtensionUtils;
extensions.registerSchemaAPI("storage", context => {
function storageApiFactory(context) {
let {extension} = context;
return {
storage: {
@ -41,4 +41,6 @@ extensions.registerSchemaAPI("storage", context => {
}).api(),
},
};
});
}
extensions.registerSchemaAPI("storage", "addon_parent", storageApiFactory);
extensions.registerSchemaAPI("storage", "content_parent", storageApiFactory);

View File

@ -25,7 +25,7 @@ extensions.on("test-message", (type, extension, ...args) => {
});
/* eslint-enable mozilla/balanced-listeners */
extensions.registerSchemaAPI("test", context => {
function testApiFactory(context) {
let {extension} = context;
return {
test: {
@ -75,4 +75,6 @@ extensions.registerSchemaAPI("test", context => {
}).api(),
},
};
});
}
extensions.registerSchemaAPI("test", "addon_parent", testApiFactory);
extensions.registerSchemaAPI("test", "content_parent", testApiFactory);

View File

@ -158,7 +158,7 @@ function convertGetFrameResult(tabId, data) {
};
}
extensions.registerSchemaAPI("webNavigation", context => {
extensions.registerSchemaAPI("webNavigation", "addon_parent", context => {
return {
webNavigation: {
onBeforeNavigate: new WebNavigationEventManager(context, "onBeforeNavigate").api(),

View File

@ -100,7 +100,7 @@ function WebRequestEventManager(context, eventName) {
WebRequestEventManager.prototype = Object.create(SingletonEventManager.prototype);
extensions.registerSchemaAPI("webRequest", context => {
extensions.registerSchemaAPI("webRequest", "addon_parent", context => {
return {
webRequest: {
onBeforeRequest: new WebRequestEventManager(context, "onBeforeRequest").api(),

View File

@ -14,7 +14,7 @@ var {
class StubContext extends BaseContext {
constructor() {
let fakeExtension = {id: "test@web.extension"};
super(fakeExtension);
super("testEnv", fakeExtension);
this.sandbox = Cu.Sandbox(global);
}
@ -128,7 +128,7 @@ add_task(function* test_post_unload_listeners() {
class Context extends BaseContext {
constructor(principal) {
let fakeExtension = {id: "test@web.extension"};
super(fakeExtension);
super("testEnv", fakeExtension);
Object.defineProperty(this, "principal", {
value: principal,
configurable: true,

View File

@ -49,7 +49,7 @@ add_task(function* testSchemaAPIInjection() {
yield Schemas.load(url);
// Register an API that will skip the background page.
Management.registerSchemaAPI("noBackgroundAPI.testnamespace", context => {
Management.registerSchemaAPI("noBackgroundAPI.testnamespace", "addon_child", context => {
if (context.type !== "background") {
return {
noBackgroundAPI: {
@ -66,7 +66,7 @@ add_task(function* testSchemaAPIInjection() {
});
// Register an API that will skip any but the background page.
Management.registerSchemaAPI("backgroundAPI.testnamespace", context => {
Management.registerSchemaAPI("backgroundAPI.testnamespace", "addon_child", context => {
if (context.type === "background") {
return {
backgroundAPI: {