--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -79,19 +79,19 @@ let schemaURLs = new Set();
if (!AppConstants.RELEASE_OR_BETA) {
schemaURLs.add("chrome://extensions/content/schemas/experiments.json");
}
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
BaseContext,
EventEmitter,
- LocalAPIImplementation,
LocaleData,
SchemaAPIManager,
+ SpreadArgs,
defineLazyGetter,
flushJarCache,
instanceOf,
} = ExtensionUtils;
XPCOMUtils.defineLazyGetter(this, "console", ExtensionUtils.getConsole);
const LOGGER_ID_BASE = "addons.webextension.";
@@ -380,27 +380,32 @@ class ExtensionChildProxyContext extends
shutdown() {
Management.emit("page-shutdown", this);
super.shutdown();
}
}
function findPathInObject(obj, path, printErrors = true) {
+ let parent;
for (let elt of path.split(".")) {
if (!obj || !(elt in obj)) {
if (printErrors) {
Cu.reportError(`WebExtension API ${path} not found (it may be unimplemented by Firefox).`);
}
return null;
}
+ parent = obj;
obj = obj[elt];
}
+ if (typeof obj === "function") {
+ return obj.bind(parent);
+ }
return obj;
}
ParentAPIManager = {
proxyContexts: new Map(),
init() {
Services.obs.addObserver(this, "message-manager-close", false);
@@ -493,41 +498,51 @@ ParentAPIManager = {
},
call(data, target) {
let context = this.getContextById(data.childId);
if (context.currentMessageManager !== target.messageManager) {
Cu.reportError("WebExtension warning: Message manager unexpectedly changed");
}
- function callback(...cbArgs) {
- let lastError = context.lastError;
+ try {
+ let args = Cu.cloneInto(data.args, context.sandbox);
+ let result = findPathInObject(context.apiObj, data.path)(...args);
- context.currentMessageManager.sendAsyncMessage("API:CallResult", {
- childId: data.childId,
- callId: data.callId,
- args: cbArgs,
- lastError: lastError ? lastError.message : null,
- });
- }
+ if (data.callId) {
+ result = result || Promise.resolve();
+
+ result.then(result => {
+ result = result instanceof SpreadArgs ? [...result] : [result];
- let args = data.args;
- args = Cu.cloneInto(args, context.sandbox);
- if (data.callId) {
- args = args.concat(callback);
- }
- try {
- findPathInObject(context.apiObj, data.path)(...args);
+ context.currentMessageManager.sendAsyncMessage("API:CallResult", {
+ childId: data.childId,
+ callId: data.callId,
+ result,
+ });
+ }, error => {
+ error = context.normalizeError(error);
+ context.currentMessageManager.sendAsyncMessage("API:CallResult", {
+ childId: data.childId,
+ callId: data.callId,
+ error: {message: error.message},
+ });
+ });
+ }
} catch (e) {
- let msg = e.message || "API failed";
- context.currentMessageManager.sendAsyncMessage("API:CallResult", {
- childId: data.childId,
- callId: data.callId,
- lastError: msg,
- });
+ if (data.callId) {
+ let error = context.normalizeError(e);
+ context.currentMessageManager.sendAsyncMessage("API:CallResult", {
+ childId: data.childId,
+ callId: data.callId,
+ error: {message: error.message},
+ });
+ } else {
+ Cu.reportError(e);
+ }
}
},
addListener(data, target) {
let context = this.getContextById(data.childId);
if (context.currentMessageManager !== target.messageManager) {
Cu.reportError("WebExtension warning: Message manager unexpectedly changed");
}
@@ -707,62 +722,18 @@ GlobalManager = {
`, false);
},
getExtension(extensionId) {
return this.extensionMap.get(extensionId);
},
injectInObject(context, isChromeCompat, dest) {
- let apis = {
- extensionTypes: {},
- };
- Management.generateAPIs(context, apis);
- SchemaAPIManager.generateAPIs(context, context.extension.apis, apis);
-
- // For testing only.
- context._unwrappedAPIs = apis;
-
- let schemaWrapper = {
- isChromeCompat,
-
- get url() {
- return context.uri.spec;
- },
-
- get principal() {
- return context.principal;
- },
-
- get cloneScope() {
- return context.cloneScope;
- },
-
- hasPermission(permission) {
- return context.extension.hasPermission(permission);
- },
-
- shouldInject(namespace, name, allowedContexts) {
- // Do not generate content script APIs, unless explicitly allowed.
- if (context.envType === "content_parent" && !allowedContexts.includes("content")) {
- return false;
- }
- if (context.envType !== "addon_parent" &&
- allowedContexts.includes("addon_parent_only")) {
- return false;
- }
- return findPathInObject(apis, namespace, false) !== null;
- },
-
- getImplementation(namespace, name) {
- let pathObj = findPathInObject(apis, namespace);
- return new LocalAPIImplementation(pathObj, name, context);
- },
- };
- Schemas.inject(dest, schemaWrapper);
+ Management.generateAPIs(context, dest);
+ SchemaAPIManager.generateAPIs(context, context.extension.apis, dest);
},
};
// Represents the data contained in an extension, contained either
// in a directory or a zip file, which may or may not be installed.
// This class implements the functionality of the Extension class,
// primarily related to manifest parsing and localization, which is
// useful prior to extension installation or initialization.
--- a/toolkit/components/extensions/test/xpcshell/test_ext_api_permissions.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_api_permissions.js
@@ -27,17 +27,17 @@ add_task(function* test_storage_api_with
let contextPromise = getNextContext();
yield extension.startup();
let context = yield contextPromise;
// Force API initialization.
void context.apiObj;
- ok(!("storage" in context._unwrappedAPIs),
+ ok(!("storage" in context.apiObj),
"The storage API should not be initialized");
yield extension.unload();
});
add_task(function* test_storage_api_with_permissions() {
let extension = ExtensionTestUtils.loadExtension({
background() {
@@ -52,13 +52,13 @@ add_task(function* test_storage_api_with
let contextPromise = getNextContext();
yield extension.startup();
let context = yield contextPromise;
// Force API initialization.
void context.apiObj;
- equal(typeof context._unwrappedAPIs.storage, "object",
+ equal(typeof context.apiObj.storage, "object",
"The storage API should be initialized");
yield extension.unload();
});