forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			143 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			143 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 | |
|  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| "use strict";
 | |
| 
 | |
| ChromeUtils.defineModuleGetter(
 | |
|   this,
 | |
|   "WebNavigationFrames",
 | |
|   "resource://gre/modules/WebNavigationFrames.jsm"
 | |
| );
 | |
| 
 | |
| /**
 | |
|  * With optional arguments on both ends, this case is ambiguous:
 | |
|  *     runtime.sendMessage("string", {} or nullish)
 | |
|  *
 | |
|  * Sending a message within the extension is more common than sending
 | |
|  * an empty object to another extension, so we prefer that conclusion.
 | |
|  *
 | |
|  * @param {string?}  [extensionId]
 | |
|  * @param {any}      message
 | |
|  * @param {object?}  [options]
 | |
|  * @param {function} [callback]
 | |
|  * @returns {{extensionId: string?, message: any, callback: function?}}
 | |
|  */
 | |
| function parseBonkersArgs(...args) {
 | |
|   let Error = ExtensionUtils.ExtensionError;
 | |
|   let callback = typeof args[args.length - 1] === "function" && args.pop();
 | |
| 
 | |
|   // We don't support any options anymore, so only an empty object is valid.
 | |
|   function validOptions(v) {
 | |
|     return v == null || (typeof v === "object" && !Object.keys(v).length);
 | |
|   }
 | |
| 
 | |
|   if (args.length === 1 || (args.length === 2 && validOptions(args[1]))) {
 | |
|     // Interpret as passing null for extensionId (message within extension).
 | |
|     args.unshift(null);
 | |
|   }
 | |
|   let [extensionId, message, options] = args;
 | |
| 
 | |
|   if (!args.length) {
 | |
|     throw new Error("runtime.sendMessage's message argument is missing");
 | |
|   } else if (!validOptions(options)) {
 | |
|     throw new Error("runtime.sendMessage's options argument is invalid");
 | |
|   } else if (args.length === 4 && args[3] && !callback) {
 | |
|     throw new Error("runtime.sendMessage's last argument is not a function");
 | |
|   } else if (args[3] != null || args.length > 4) {
 | |
|     throw new Error("runtime.sendMessage received too many arguments");
 | |
|   } else if (extensionId && typeof extensionId !== "string") {
 | |
|     throw new Error("runtime.sendMessage's extensionId argument is invalid");
 | |
|   }
 | |
|   return { extensionId, message, callback };
 | |
| }
 | |
| 
 | |
| this.runtime = class extends ExtensionAPI {
 | |
|   getAPI(context) {
 | |
|     let { extension } = context;
 | |
| 
 | |
|     return {
 | |
|       runtime: {
 | |
|         onConnect: context.messenger.onConnect.api(),
 | |
|         onMessage: context.messenger.onMessage.api(),
 | |
| 
 | |
|         onConnectExternal: context.messenger.onConnectEx.api(),
 | |
|         onMessageExternal: context.messenger.onMessageEx.api(),
 | |
| 
 | |
|         connect(extensionId, options) {
 | |
|           let name = options?.name ?? "";
 | |
|           return context.messenger.connect({ name, extensionId });
 | |
|         },
 | |
| 
 | |
|         sendMessage(...args) {
 | |
|           let arg = parseBonkersArgs(...args);
 | |
|           return context.messenger.sendRuntimeMessage(arg);
 | |
|         },
 | |
| 
 | |
|         connectNative(name) {
 | |
|           return context.messenger.connect({ name, native: true });
 | |
|         },
 | |
| 
 | |
|         sendNativeMessage(nativeApp, message) {
 | |
|           return context.messenger.sendNativeMessage(nativeApp, message);
 | |
|         },
 | |
| 
 | |
|         get lastError() {
 | |
|           return context.lastError;
 | |
|         },
 | |
| 
 | |
|         getManifest() {
 | |
|           return Cu.cloneInto(extension.manifest, context.cloneScope);
 | |
|         },
 | |
| 
 | |
|         id: extension.id,
 | |
| 
 | |
|         getURL(url) {
 | |
|           return extension.baseURI.resolve(url);
 | |
|         },
 | |
| 
 | |
|         getFrameId(target) {
 | |
|           let frameId = WebNavigationFrames.getFromWindow(target);
 | |
|           if (frameId >= 0) {
 | |
|             return frameId;
 | |
|           }
 | |
|           // Not a WindowProxy, perhaps an embedder element?
 | |
| 
 | |
|           let type;
 | |
|           try {
 | |
|             type = Cu.getClassName(target, true);
 | |
|           } catch (e) {
 | |
|             // Not a valid object, will throw below.
 | |
|           }
 | |
| 
 | |
|           const embedderTypes = [
 | |
|             "HTMLIFrameElement",
 | |
|             "HTMLFrameElement",
 | |
|             "HTMLEmbedElement",
 | |
|             "HTMLObjectElement",
 | |
|           ];
 | |
| 
 | |
|           if (embedderTypes.includes(type)) {
 | |
|             if (!target.browsingContext) {
 | |
|               return -1;
 | |
|             }
 | |
|             return WebNavigationFrames.getFrameId(target.browsingContext);
 | |
|           }
 | |
| 
 | |
|           throw new ExtensionUtils.ExtensionError("Invalid argument");
 | |
|         },
 | |
|       },
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   getAPIObjectForRequest(context, request) {
 | |
|     if (request.apiObjectType === "Port") {
 | |
|       const port = context.messenger.getPortById(request.apiObjectId);
 | |
|       if (!port) {
 | |
|         throw new Error(`Port API object not found: ${request}`);
 | |
|       }
 | |
|       return port.api;
 | |
|     }
 | |
| 
 | |
|     throw new Error(`Unexpected apiObjectType: ${request}`);
 | |
|   }
 | |
| };
 | 
