1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 ChromeUtils.defineModuleGetter(
9 "resource://gre/modules/WebNavigationFrames.jsm"
13 * With optional arguments on both ends, this case is ambiguous:
14 * runtime.sendMessage("string", {} or nullish)
16 * Sending a message within the extension is more common than sending
17 * an empty object to another extension, so we prefer that conclusion.
19 * @param {string?} [extensionId]
20 * @param {any} message
21 * @param {object?} [options]
22 * @param {function} [callback]
23 * @returns {{extensionId: string?, message: any, callback: function?}}
25 function parseBonkersArgs(...args) {
26 let Error = ExtensionUtils.ExtensionError;
27 let callback = typeof args[args.length - 1] === "function" && args.pop();
29 // We don't support any options anymore, so only an empty object is valid.
30 function validOptions(v) {
31 return v == null || (typeof v === "object" && !Object.keys(v).length);
34 if (args.length === 1 || (args.length === 2 && validOptions(args[1]))) {
35 // Interpret as passing null for extensionId (message within extension).
38 let [extensionId, message, options] = args;
41 throw new Error("runtime.sendMessage's message argument is missing");
42 } else if (!validOptions(options)) {
43 throw new Error("runtime.sendMessage's options argument is invalid");
44 } else if (args.length === 4 && args[3] && !callback) {
45 throw new Error("runtime.sendMessage's last argument is not a function");
46 } else if (args[3] != null || args.length > 4) {
47 throw new Error("runtime.sendMessage received too many arguments");
48 } else if (extensionId && typeof extensionId !== "string") {
49 throw new Error("runtime.sendMessage's extensionId argument is invalid");
51 return { extensionId, message, callback };
54 this.runtime = class extends ExtensionAPI {
56 let { extension } = context;
60 onConnect: context.messenger.onConnect.api(),
61 onMessage: context.messenger.onMessage.api(),
63 onConnectExternal: context.messenger.onConnectEx.api(),
64 onMessageExternal: context.messenger.onMessageEx.api(),
66 connect(extensionId, options) {
67 let name = options?.name ?? "";
68 return context.messenger.connect({ name, extensionId });
71 sendMessage(...args) {
72 let arg = parseBonkersArgs(...args);
73 return context.messenger.sendRuntimeMessage(arg);
77 return context.messenger.connect({ name, native: true });
80 sendNativeMessage(nativeApp, message) {
81 return context.messenger.sendNativeMessage(nativeApp, message);
85 return context.lastError;
89 return Cu.cloneInto(extension.manifest, context.cloneScope);
95 return extension.baseURI.resolve(url);
99 let frameId = WebNavigationFrames.getFromWindow(target);
103 // Not a WindowProxy, perhaps an embedder element?
107 type = Cu.getClassName(target, true);
109 // Not a valid object, will throw below.
112 const embedderTypes = [
119 if (embedderTypes.includes(type)) {
120 if (!target.browsingContext) {
123 return WebNavigationFrames.getFrameId(target.browsingContext);
126 throw new ExtensionUtils.ExtensionError("Invalid argument");
132 getAPIObjectForRequest(context, request) {
133 if (request.apiObjectType === "Port") {
134 const port = context.messenger.getPortById(request.apiObjectId);
136 throw new Error(`Port API object not found: ${request}`);
141 throw new Error(`Unexpected apiObjectType: ${request}`);