diff --git a/toolkit/devtools/server/actors/actor-registry.js b/toolkit/devtools/server/actors/actor-registry.js index 31c270a962d..f156ef44b1f 100644 --- a/toolkit/devtools/server/actors/actor-registry.js +++ b/toolkit/devtools/server/actors/actor-registry.js @@ -11,8 +11,10 @@ const { Cu, CC, components } = require("chrome"); const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {}); const Services = require("Services"); const { DebuggerServer } = require("devtools/server/main"); -const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); +const ActorRegistryUtils = require("devtools/server/actors/utils/actor-registry-utils"); +const { registerActor, unregisterActor } = ActorRegistryUtils; + +loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); /** * The ActorActor gives you a handle to an actor you've dynamically @@ -28,13 +30,7 @@ const ActorActor = protocol.ActorClass({ }, unregister: method(function () { - if (this.options.tab) { - DebuggerServer.removeTabActor(this.options); - } - - if (this.options.global) { - DebuggerServer.removeGlobalActor(this.options); - } + unregisterActor(this.options); }, { request: {}, response: {} @@ -61,28 +57,9 @@ const ActorRegistryActor = protocol.ActorClass({ }, registerActor: method(function (sourceText, fileName, options) { - const principal = CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")(); - const sandbox = Cu.Sandbox(principal); - const exports = sandbox.exports = {}; - sandbox.require = require; + registerActor(sourceText, fileName, options); - Cu.evalInSandbox(sourceText, sandbox, "1.8", fileName, 1); - - let { prefix, constructor, type } = options; - - if (type.global) { - DebuggerServer.addGlobalActor({ - constructorName: constructor, - constructorFun: sandbox[constructor] - }, prefix); - } - - if (type.tab) { - DebuggerServer.addTabActor({ - constructorName: constructor, - constructorFun: sandbox[constructor] - }, prefix); - } + let { constructor, type } = options; return ActorActor(this.conn, { name: constructor, diff --git a/toolkit/devtools/server/actors/utils/actor-registry-utils.js b/toolkit/devtools/server/actors/utils/actor-registry-utils.js new file mode 100644 index 00000000000..f42f30c3d3e --- /dev/null +++ b/toolkit/devtools/server/actors/utils/actor-registry-utils.js @@ -0,0 +1,74 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* 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"; + +let { Cu, CC, Ci, Cc } = require("chrome"); + +const { DebuggerServer } = require("devtools/server/main"); + +/** + * Support for actor registration. Main used by ActorRegistryActor + * for dynamic registration of new actors. + * + * @param sourceText {String} Source of the actor implementation + * @param fileName {String} URL of the actor module (for proper stack traces) + * @param options {Object} Configuration object + */ +exports.registerActor = function(sourceText, fileName, options) { + const principal = CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")(); + const sandbox = Cu.Sandbox(principal); + const exports = sandbox.exports = {}; + sandbox.require = require; + + Cu.evalInSandbox(sourceText, sandbox, "1.8", fileName, 1); + + let { prefix, constructor, type } = options; + + if (type.global && !DebuggerServer.globalActorFactories.hasOwnProperty(prefix)) { + DebuggerServer.addGlobalActor({ + constructorName: constructor, + constructorFun: sandbox[constructor] + }, prefix); + } + + if (type.tab && !DebuggerServer.tabActorFactories.hasOwnProperty(prefix)) { + DebuggerServer.addTabActor({ + constructorName: constructor, + constructorFun: sandbox[constructor] + }, prefix); + } + + // Also register in all child processes in case the current scope + // is chrome parent process. + if (!DebuggerServer.isInChildProcess) { + DebuggerServer.setupInChild({ + module: "devtools/server/actors/utils/actor-registry-utils", + setupChild: "registerActor", + args: [sourceText, fileName, options] + }); + } +} + +exports.unregisterActor = function(options) { + if (options.tab) { + DebuggerServer.removeTabActor(options); + } + + if (options.global) { + DebuggerServer.removeGlobalActor(options); + } + + // Also unregister it from all child processes in case the current + // scope is chrome parent process. + if (!DebuggerServer.isInChildProcess) { + DebuggerServer.setupInChild({ + module: "devtools/server/actors/utils/actor-registry-utils", + setupChild: "unregisterActor", + args: [options] + }); + } +} diff --git a/toolkit/devtools/server/child.js b/toolkit/devtools/server/child.js index d5bd9a6af97..2d350168d4c 100644 --- a/toolkit/devtools/server/child.js +++ b/toolkit/devtools/server/child.js @@ -14,7 +14,9 @@ let chromeGlobal = this; let Cu = Components.utils; let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils.js"); - const {DebuggerServer, ActorPool} = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {}); + const { dumpn } = DevToolsUtils; + const { DebuggerServer, ActorPool } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {}); + if (!DebuggerServer.childID) { DebuggerServer.childID = 1; } @@ -57,6 +59,36 @@ let chromeGlobal = this; addMessageListener("debug:connect", onConnect); + // Allows executing module setup helper from the parent process. + // See also: DebuggerServer.setupInChild() + let onSetupInChild = DevToolsUtils.makeInfallible(msg => { + let { module, setupChild, args } = msg.data; + let m, fn; + + try { + m = devtools.require(module); + + if (!setupChild in m) { + dumpn("ERROR: module '" + module + "' does not export '" + + setupChild + "'"); + return false; + } + + m[setupChild].apply(m, args); + + return true; + } catch(e) { + let error_msg = "exception during actor module setup running in the child process: "; + DevToolsUtils.reportException(error_msg + e); + dumpn("ERROR: " + error_msg + " \n\t module: '" + module + + "' \n\t setupChild: '" + setupChild + "'\n" + + DevToolsUtils.safeErrorString(e)); + return false; + } + }); + + addMessageListener("debug:setup-in-child", onSetupInChild); + let onDisconnect = DevToolsUtils.makeInfallible(function (msg) { removeMessageListener("debug:disconnect", onDisconnect); diff --git a/toolkit/devtools/server/main.js b/toolkit/devtools/server/main.js index 90eefb3fdcf..3d3c5915180 100644 --- a/toolkit/devtools/server/main.js +++ b/toolkit/devtools/server/main.js @@ -730,6 +730,31 @@ var DebuggerServer = { */ get isInChildProcess() !!this.parentMessageManager, + /** + * In a chrome parent process, ask all content child processes + * to execute a given module setup helper. + * + * @param module + * The module to be required + * @param setupChild + * The name of the setup helper exported by the above module + * (setup helper signature: function ({mm}) { ... }) + */ + setupInChild: function({ module, setupChild, args }) { + if (this.isInChildProcess) { + return; + } + + const gMessageManager = Cc["@mozilla.org/globalmessagemanager;1"]. + getService(Ci.nsIMessageListenerManager); + + gMessageManager.broadcastAsyncMessage("debug:setup-in-child", { + module: module, + setupChild: setupChild, + args: args, + }); + }, + /** * In a content child process, ask the DebuggerServer in the parent process * to execute a given module setup helper. @@ -829,6 +854,8 @@ var DebuggerServer = { let { NetworkMonitorManager } = require("devtools/toolkit/webconsole/network-monitor"); netMonitor = new NetworkMonitorManager(aFrame, actor.actor); + events.emit(DebuggerServer, "new-child-process", { mm: mm }); + deferred.resolve(actor); }).bind(this); mm.addMessageListener("debug:actor", onActorCreated); diff --git a/toolkit/devtools/server/moz.build b/toolkit/devtools/server/moz.build index 144ba7aee47..53077c4289c 100644 --- a/toolkit/devtools/server/moz.build +++ b/toolkit/devtools/server/moz.build @@ -70,6 +70,7 @@ EXTRA_JS_MODULES.devtools.server.actors += [ ] EXTRA_JS_MODULES.devtools.server.actors.utils += [ + 'actors/utils/actor-registry-utils.js', 'actors/utils/automation-timeline.js', 'actors/utils/make-debugger.js', 'actors/utils/map-uri-to-addon-id.js', diff --git a/toolkit/devtools/server/tests/unit/test_actor-registry-actor.js b/toolkit/devtools/server/tests/unit/test_actor-registry-actor.js index 7707d5522a3..a5bb4c2f647 100644 --- a/toolkit/devtools/server/tests/unit/test_actor-registry-actor.js +++ b/toolkit/devtools/server/tests/unit/test_actor-registry-actor.js @@ -65,7 +65,7 @@ function unregisterNewActor() { .unregister() .then(testActorIsUnregistered) .then(null, e => { - DevToolsUtils.reportException("registerNewActor", e) + DevToolsUtils.reportException("unregisterNewActor", e) do_check_true(false); }); }