author | Rob Wu <rob@robwu.nl> |
Thu, 18 Aug 2016 18:15:37 -0700 (2016-08-19) | |
changeset 311150 | 2427f8eb4e83add47679215a19cabbb81dcc12a1 |
parent 311149 | e4ce08beaf7474321a89ee4f45cf88e943f32618 |
child 311151 | 598895fae31dc86756be5481478d32bb177a764e |
push id | 30602 |
push user | kwierso@gmail.com |
push date | Thu, 25 Aug 2016 23:53:05 +0000 (2016-08-25) |
treeherder | mozilla-central@cd4ed9909dc9 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | billm |
bugs | 1287010, 1286712 |
milestone | 51.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/toolkit/components/extensions/ExtensionContent.jsm +++ b/toolkit/components/extensions/ExtensionContent.jsm @@ -89,64 +89,16 @@ var apiManager = new class extends Schem } } }; // This is the fairly simple API that we inject into content // scripts. var api = context => { return { - runtime: { - connect: function(extensionId, connectInfo) { - if (!connectInfo) { - connectInfo = extensionId; - extensionId = null; - } - extensionId = extensionId || context.extension.id; - let name = connectInfo && connectInfo.name || ""; - let recipient = {extensionId}; - return context.messenger.connect(context.messageManager, name, recipient); - }, - - get id() { - return context.extension.id; - }, - - get lastError() { - return context.lastError; - }, - - getManifest: function() { - return Cu.cloneInto(context.extension.manifest, context.cloneScope); - }, - - getURL: function(url) { - return context.extension.baseURI.resolve(url); - }, - - onConnect: context.messenger.onConnect("runtime.onConnect"), - - onMessage: context.messenger.onMessage("runtime.onMessage"), - - sendMessage: function(...args) { - let options; // eslint-disable-line no-unused-vars - let extensionId, message, responseCallback; - if (args.length == 1) { - message = args[0]; - } else if (args.length == 2) { - [message, responseCallback] = args; - } else { - [extensionId, message, options, responseCallback] = args; - } - extensionId = extensionId || context.extension.id; - - let recipient = {extensionId}; - return context.messenger.sendMessage(context.messageManager, message, recipient, responseCallback); - }, - }, extension: { getURL: function(url) { return context.extension.baseURI.resolve(url); }, get lastError() { return context.lastError;
new file mode 100644 --- /dev/null +++ b/toolkit/components/extensions/ext-c-runtime.js @@ -0,0 +1,82 @@ +"use strict"; + +function runtimeApiFactory(context) { + let {extension} = context; + + // TODO(robwu): Investigate which message-manager to use once we start with + // reworking Messenger and ExtensionContext to be usable in a child process + // instead of the parent process. + // For now use exactly the original behavior out of caution. + let mm = context.envType == "content_child" ? context.messageManager : Services.cpmm; + + return { + runtime: { + onConnect: context.messenger.onConnect("runtime.onConnect"), + + onMessage: context.messenger.onMessage("runtime.onMessage"), + + connect: function(extensionId, connectInfo) { + let name = connectInfo !== null && connectInfo.name || ""; + extensionId = extensionId || extension.id; + let recipient = {extensionId}; + + return context.messenger.connect(mm, name, recipient); + }, + + sendMessage: function(...args) { + let options; // eslint-disable-line no-unused-vars + let extensionId, message, responseCallback; + if (typeof args[args.length - 1] == "function") { + responseCallback = args.pop(); + } + if (!args.length) { + return Promise.reject({message: "runtime.sendMessage's message argument is missing"}); + } else if (args.length == 1) { + message = args[0]; + } else if (args.length == 2) { + if (typeof args[0] == "string" && args[0]) { + [extensionId, message] = args; + } else { + [message, options] = args; + } + } else if (args.length == 3) { + [extensionId, message, options] = args; + } else if (args.length == 4 && !responseCallback) { + return Promise.reject({message: "runtime.sendMessage's last argument is not a function"}); + } else { + return Promise.reject({message: "runtime.sendMessage received too many arguments"}); + } + + if (extensionId != null && typeof extensionId != "string") { + return Promise.reject({message: "runtime.sendMessage's extensionId argument is invalid"}); + } + if (options != null && typeof options != "object") { + return Promise.reject({message: "runtime.sendMessage's options argument is invalid"}); + } + // TODO(robwu): Validate option keys and values when we support it. + + extensionId = extensionId || extension.id; + let recipient = {extensionId}; + + return context.messenger.sendMessage(mm, message, recipient, responseCallback); + }, + + get lastError() { + return context.lastError; + }, + + getManifest() { + return Cu.cloneInto(extension.manifest, context.cloneScope); + }, + + id: extension.id, + + getURL: function(url) { + return extension.baseURI.resolve(url); + }, + }, + }; +} + +extensions.registerSchemaAPI("runtime", "addon_child", runtimeApiFactory); +extensions.registerSchemaAPI("runtime", "content_child", runtimeApiFactory);
--- a/toolkit/components/extensions/ext-runtime.js +++ b/toolkit/components/extensions/ext-runtime.js @@ -28,20 +28,16 @@ extensions.registerSchemaAPI("runtime", extension.onStartup = fire; return () => { extension.onStartup = null; }; }).api(), onInstalled: ignoreEvent(context, "runtime.onInstalled"), - onMessage: context.messenger.onMessage("runtime.onMessage"), - - onConnect: context.messenger.onConnect("runtime.onConnect"), - onUpdateAvailable: new SingletonEventManager(context, "runtime.onUpdateAvailable", fire => { let instanceID = extension.addonData.instanceID; AddonManager.addUpgradeListener(instanceID, upgrade => { extension.upgrade = upgrade; let details = { version: upgrade.version, }; context.runSafe(fire, details); @@ -58,86 +54,33 @@ extensions.registerSchemaAPI("runtime", } else { // Otherwise, reload the current extension. AddonManager.getAddonByID(extension.id, addon => { addon.reload(); }); } }, - connect: function(extensionId, connectInfo) { - let name = connectInfo !== null && connectInfo.name || ""; - extensionId = extensionId || extension.id; - let recipient = {extensionId}; - - return context.messenger.connect(Services.cpmm, name, recipient); - }, - - sendMessage: function(...args) { - let options; // eslint-disable-line no-unused-vars - let extensionId, message, responseCallback; - if (typeof args[args.length - 1] == "function") { - responseCallback = args.pop(); - } - if (!args.length) { - return Promise.reject({message: "runtime.sendMessage's message argument is missing"}); - } else if (args.length == 1) { - message = args[0]; - } else if (args.length == 2) { - if (typeof args[0] == "string" && args[0]) { - [extensionId, message] = args; - } else { - [message, options] = args; - } - } else if (args.length == 3) { - [extensionId, message, options] = args; - } else if (args.length == 4 && !responseCallback) { - return Promise.reject({message: "runtime.sendMessage's last argument is not a function"}); - } else { - return Promise.reject({message: "runtime.sendMessage received too many arguments"}); - } - - if (extensionId != null && typeof extensionId != "string") { - return Promise.reject({message: "runtime.sendMessage's extensionId argument is invalid"}); - } - if (options != null && typeof options != "object") { - return Promise.reject({message: "runtime.sendMessage's options argument is invalid"}); - } - // TODO(robwu): Validate option keys and values when we support it. - - extensionId = extensionId || extension.id; - let recipient = {extensionId}; - - return context.messenger.sendMessage(Services.cpmm, message, recipient, responseCallback); - }, - connectNative(application) { let app = new NativeApp(extension, context, application); return app.portAPI(); }, sendNativeMessage(application, message) { let app = new NativeApp(extension, context, application); return app.sendMessage(message); }, get lastError() { + // TODO(robwu): Figure out how to make sure that errors in the parent + // process are propagated to the child process. + // lastError should not be accessed from the parent. return context.lastError; }, - getManifest() { - return Cu.cloneInto(extension.manifest, context.cloneScope); - }, - - id: extension.id, - - getURL: function(url) { - return extension.baseURI.resolve(url); - }, - getPlatformInfo: function() { return Promise.resolve(ExtensionUtils.PlatformInfo); }, openOptionsPage: function() { if (!extension.manifest.options_ui) { return Promise.reject({message: "No `options_ui` declared"}); }
--- a/toolkit/components/extensions/extensions-toolkit.manifest +++ b/toolkit/components/extensions/extensions-toolkit.manifest @@ -11,16 +11,17 @@ category webextension-scripts webRequest category webextension-scripts webNavigation chrome://extensions/content/ext-webNavigation.js category webextension-scripts runtime chrome://extensions/content/ext-runtime.js category webextension-scripts extension chrome://extensions/content/ext-extension.js category webextension-scripts storage chrome://extensions/content/ext-storage.js category webextension-scripts test chrome://extensions/content/ext-test.js # scripts specific for content process. category webextension-scripts-content i18n chrome://extensions/content/ext-i18n.js +category webextension-scripts-content runtime chrome://extensions/content/ext-c-runtime.js # schemas category webextension-schemas alarms chrome://extensions/content/schemas/alarms.json category webextension-schemas cookies chrome://extensions/content/schemas/cookies.json category webextension-schemas downloads chrome://extensions/content/schemas/downloads.json category webextension-schemas events chrome://extensions/content/schemas/events.json category webextension-schemas extension chrome://extensions/content/schemas/extension.json category webextension-schemas extension_types chrome://extensions/content/schemas/extension_types.json
--- a/toolkit/components/extensions/jar.mn +++ b/toolkit/components/extensions/jar.mn @@ -13,8 +13,9 @@ toolkit.jar: content/extensions/ext-i18n.js content/extensions/ext-idle.js content/extensions/ext-webRequest.js content/extensions/ext-webNavigation.js content/extensions/ext-runtime.js content/extensions/ext-extension.js content/extensions/ext-storage.js content/extensions/ext-test.js + content/extensions/ext-c-runtime.js
--- a/toolkit/components/extensions/schemas/runtime.json +++ b/toolkit/components/extensions/schemas/runtime.json @@ -14,21 +14,23 @@ "nativeMessaging" ] }] } ] }, { "namespace": "runtime", + "restrictions": ["content"], "description": "Use the <code>browser.runtime</code> API to retrieve the background page, return details about the manifest, and listen for and respond to events in the app or extension lifecycle. You can also use this API to convert the relative path of URLs to fully-qualified URLs.", "types": [ { "id": "Port", "type": "object", + "restrictions": ["content"], "description": "An object which allows two way communication with other pages.", "properties": { "name": {"type": "string"}, "disconnect": { "type": "function" }, "onDisconnect": { "$ref": "events.Event" }, "onMessage": { "$ref": "events.Event" }, "postMessage": {"type": "function"}, "sender": { @@ -37,40 +39,44 @@ "description": "This property will <b>only</b> be present on ports passed to onConnect/onConnectExternal listeners." } }, "additionalProperties": { "type": "any"} }, { "id": "MessageSender", "type": "object", + "restrictions": ["content"], "description": "An object containing information about the script context that sent a message or request.", "properties": { "tab": {"$ref": "tabs.Tab", "optional": true, "description": "The $(ref:tabs.Tab) which opened the connection, if any. This property will <strong>only</strong> be present when the connection was opened from a tab (including content scripts), and <strong>only</strong> if the receiver is an extension, not an app."}, "frameId": {"type": "integer", "optional": true, "description": "The $(topic:frame_ids)[frame] that opened the connection. 0 for top-level frames, positive for child frames. This will only be set when <code>tab</code> is set."}, "id": {"type": "string", "optional": true, "description": "The ID of the extension or app that opened the connection, if any."}, "url": {"type": "string", "optional": true, "description": "The URL of the page or frame that opened the connection. If the sender is in an iframe, it will be iframe's URL not the URL of the page which hosts it."}, "tlsChannelId": {"unsupported": true, "type": "string", "optional": true, "description": "The TLS channel ID of the page or frame that opened the connection, if requested by the extension or app, and if available."} } }, { "id": "PlatformOs", "type": "string", + "restrictions": ["content"], "description": "The operating system the browser is running on.", "enum": ["mac", "win", "android", "cros", "linux", "openbsd"] }, { "id": "PlatformArch", "type": "string", "enum": ["arm", "x86-32", "x86-64"], + "restrictions": ["content"], "description": "The machine's processor architecture." }, { "id": "PlatformInfo", "type": "object", + "restrictions": ["content"], "description": "An object containing information about the current platform.", "properties": { "os": { "$ref": "PlatformOs", "description": "The operating system the browser is running on." }, "arch": { "$ref": "PlatformArch", @@ -82,49 +88,54 @@ "$ref": "PlatformNaclArch" } } }, { "id": "RequestUpdateCheckStatus", "type": "string", "enum": ["throttled", "no_update", "update_available"], + "restrictions": ["content"], "description": "Result of the update check." }, { "id": "OnInstalledReason", "type": "string", "enum": ["install", "update", "chrome_update", "shared_module_update"], + "restrictions": ["content"], "description": "The reason that this event is being dispatched." }, { "id": "OnRestartRequiredReason", "type": "string", + "restrictions": ["content"], "description": "The reason that the event is being dispatched. 'app_update' is used when the restart is needed because the application is updated to a newer version. 'os_update' is used when the restart is needed because the browser/OS is updated to a newer version. 'periodic' is used when the system runs for more than the permitted uptime set in the enterprise policy.", "enum": ["app_update", "os_update", "periodic"] } ], "properties": { "lastError": { "type": "object", "optional": true, + "restrictions": ["content"], "description": "This will be defined during an API method callback if there was an error", "properties": { "message": { "optional": true, "type": "string", "description": "Details about the error which occurred." } }, "additionalProperties": { "type": "any" } }, "id": { "type": "string", + "restrictions": ["content"], "description": "The ID of the extension/app." } }, "functions": [ { "name": "getBackgroundPage", "type": "function", "description": "Retrieves the JavaScript 'window' object for the background page running inside the current extension/app. If the background page is an event page, the system will ensure it is loaded before calling the callback. If there is no background page, an error is set.", @@ -155,29 +166,31 @@ "type": "function", "name": "callback", "parameters": [], "optional": true }] }, { "name": "getManifest", + "restrictions": ["content"], "description": "Returns details about the app or extension from the manifest. The object returned is a serialization of the full $(topic:manifest)[manifest file].", "type": "function", "parameters": [], "returns": { "type": "object", "properties": {}, "additionalProperties": { "type": "any" }, "description": "The manifest details." } }, { "name": "getURL", "type": "function", + "restrictions": ["content"], "description": "Converts a relative path within an app/extension install directory to a fully-qualified URL.", "parameters": [ { "type": "string", "name": "path", "description": "A path to a resource within an app/extension expressed relative to its install directory." } ], @@ -250,16 +263,17 @@ "unsupported": true, "description": "Restart the device when the app runs in kiosk mode. Otherwise, it's no-op.", "type": "function", "parameters": [] }, { "name": "connect", "type": "function", + "restrictions": ["content"], "description": "Attempts to connect to connect listeners within an extension/app (such as the background page), or other extensions/apps. This is useful for content scripts connecting to their extension processes, inter-app/extension communication, and $(topic:manifest/externally_connectable)[web messaging]. Note that this does not connect to any listeners in a content script. Extensions may connect to content scripts embedded in tabs via $(ref:tabs.connect).", "parameters": [ {"type": "string", "name": "extensionId", "optional": true, "description": "The ID of the extension or app to connect to. If omitted, a connection will be attempted with your own extension. Required if sending messages from a web page for $(topic:manifest/externally_connectable)[web messaging]."}, { "type": "object", "name": "connectInfo", "properties": { "name": { "type": "string", "optional": true, "description": "Will be passed into onConnect for processes that are listening for the connection event." }, @@ -289,16 +303,17 @@ "$ref": "Port", "description": "Port through which messages can be sent and received with the application" } }, { "name": "sendMessage", "type": "function", "allowAmbiguousOptionalArguments": true, + "restrictions": ["content"], "description": "Sends a single message to event listeners within your extension/app or a different extension/app. Similar to $(ref:runtime.connect) but only sends a single message, with an optional response. If sending to your extension, the $(ref:runtime.onMessage) event will be fired in each page, or $(ref:runtime.onMessageExternal), if a different extension. Note that extensions cannot send messages to content scripts using this method. To send messages to content scripts, use $(ref:tabs.sendMessage).", "async": "responseCallback", "parameters": [ {"type": "string", "name": "extensionId", "optional": true, "description": "The ID of the extension/app to send the message to. If omitted, the message will be sent to your own extension/app. Required if sending messages from a web page for $(topic:manifest/externally_connectable)[web messaging]."}, { "type": "any", "name": "message" }, { "type": "object", "name": "options", @@ -465,16 +480,17 @@ "type": "function", "description": "Fired when an update for the browser is available, but isn't installed immediately because a browser restart is required.", "deprecated": "Please use $(ref:runtime.onRestartRequired).", "parameters": [] }, { "name": "onConnect", "type": "function", + "restrictions": ["content"], "description": "Fired when a connection is made from either an extension process or a content script.", "parameters": [ {"$ref": "Port", "name": "port"} ] }, { "name": "onConnectExternal", "unsupported": true, @@ -482,16 +498,17 @@ "description": "Fired when a connection is made from another extension.", "parameters": [ {"$ref": "Port", "name": "port"} ] }, { "name": "onMessage", "type": "function", + "restrictions": ["content"], "description": "Fired when a message is sent from either an extension process or a content script.", "parameters": [ {"name": "message", "type": "any", "optional": true, "description": "The message sent by the calling script."}, {"name": "sender", "$ref": "MessageSender" }, {"name": "sendResponse", "type": "function", "description": "Function to call (at most once) when you have a response. The argument should be any JSON-ifiable object. If you have more than one <code>onMessage</code> listener in the same document, then only one may send a response. This function becomes invalid when the event listener returns, unless you return true from the event listener to indicate you wish to send a response asynchronously (this will keep the message channel open to the other end until <code>sendResponse</code> is called)." } ], "returns": { "type": "boolean",