copy from devtools/server/main.js
copy to devtools/server/actor-registry.js
--- a/devtools/server/main.js
+++ b/devtools/server/actor-registry.js
@@ -1,220 +1,58 @@
/* 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";
-/**
- * Toolkit glue for the remote debugging protocol, loaded into the
- * debugging global.
- */
-var { Ci, Cc } = require("chrome");
var Services = require("Services");
-var { ActorPool, OriginalLocation } = require("devtools/server/actors/common");
-var { LocalDebuggerTransport, ChildDebuggerTransport, WorkerDebuggerTransport } =
- require("devtools/shared/transport/transport");
-var DevToolsUtils = require("devtools/shared/DevToolsUtils");
-var { dumpn } = DevToolsUtils;
-
-DevToolsUtils.defineLazyGetter(this, "DebuggerSocket", () => {
- // eslint-disable-next-line no-shadow
- const { DebuggerSocket } = require("devtools/shared/security/socket");
- return DebuggerSocket;
-});
-DevToolsUtils.defineLazyGetter(this, "Authentication", () => {
- return require("devtools/shared/security/auth");
-});
-DevToolsUtils.defineLazyGetter(this, "generateUUID", () => {
- // eslint-disable-next-line no-shadow
- const { generateUUID } = Cc["@mozilla.org/uuid-generator;1"]
- .getService(Ci.nsIUUIDGenerator);
- return generateUUID;
-});
-
-// Overload `Components` to prevent DevTools loader exception on Components
-// object usage
-// eslint-disable-next-line no-unused-vars
-Object.defineProperty(this, "Components", {
- get() {
- return require("chrome").components;
- }
-});
-
-const CONTENT_PROCESS_SERVER_STARTUP_SCRIPT =
- "resource://devtools/server/startup/content-process.js";
-
-function loadSubScript(url) {
- try {
- Services.scriptloader.loadSubScript(url, this);
- } catch (e) {
- const errorStr = "Error loading: " + url + ":\n" +
- (e.fileName ? "at " + e.fileName + " : " + e.lineNumber + "\n" : "") +
- e + " - " + e.stack + "\n";
- dump(errorStr);
- reportError(errorStr);
- throw e;
- }
-}
-
-loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
-
+var { Ci } = require("chrome");
var gRegisteredModules = Object.create(null);
-/**
- * Public API
- */
-var DebuggerServer = {
- _listeners: [],
- _initialized: false,
- // Flag to check if the content process server startup script was already loaded.
- _contentProcessServerStartupScriptLoaded: false,
- // Map of global actor names to actor constructors.
- globalActorFactories: {},
- // Map of target-scoped actor names to actor constructors.
- targetScopedActorFactories: {},
-
- LONG_STRING_LENGTH: 10000,
- LONG_STRING_INITIAL_LENGTH: 1000,
- LONG_STRING_READ_LENGTH: 65 * 1024,
-
- /**
- * The windowtype of the chrome window to use for actors that use the global
- * window (i.e the global style editor). Set this to your main window type,
- * for example "navigator:browser".
- */
- chromeWindowType: "navigator:browser",
-
- /**
- * Allow debugging chrome of (parent or child) processes.
- */
- allowChromeProcess: false,
-
- /**
- * We run a special server in child process whose main actor is an instance
- * of FrameTargetActor, but that isn't a root actor. Instead there is no root
- * actor registered on DebuggerServer.
- */
- get rootlessServer() {
- return !this.createRootActor;
- },
-
- /**
- * Initialize the debugger server.
- */
- init() {
- if (this.initialized) {
- return;
- }
-
- this._connections = {};
- this._nextConnID = 0;
-
- this._initialized = true;
- },
-
- get protocol() {
- return require("devtools/shared/protocol");
- },
-
- get initialized() {
- return this._initialized;
- },
-
- /**
- * Performs cleanup tasks before shutting down the debugger server. Such tasks
- * include clearing any actor constructors added at runtime. This method
- * should be called whenever a debugger server is no longer useful, to avoid
- * memory leaks. After this method returns, the debugger server must be
- * initialized again before use.
- */
- destroy() {
- if (!this._initialized) {
- return;
- }
+// const BrowserActorList = [
+// {
+// id: "devtools/server/actors/preference",
+// typeName: "preference",
+// name: "PreferenceActor"
+// },
+// {
+// id: "devtools/server/actors/actor-registry",
+// typeName: "actorRegistry",
+// name: "ActorRegistryActor",
+// },
+// {
+// id: "devtools/server/actors/addon/addons",
+// typeName: "addons",
+// name: "AddonsActor",
+// },
+// {
+// id: "devtools/server/actors/device",
+// typeName: "device",
+// name: "DeviceActor",
+// },
+// {
+// id: "devtools/server/actors/heap-snapshot-file",
+// typeName: "heapSnapshotFile",
+// name: "HeapSnapshotFileActor",
+// },
+// // Always register this as a global module, even while there is a pref turning
+// // on and off the other performance actor. This actor shouldn't conflict with
+// // the other one. These are also lazily loaded so there shouldn't be a performance
+// // impact.
+// {
+// id: "devtools/server/actors/perf",
+// typeName: "perf",
+// name: "PerfActor",
+// }
+// ];
- for (const connID of Object.getOwnPropertyNames(this._connections)) {
- this._connections[connID].close();
- }
-
- for (const id of Object.getOwnPropertyNames(gRegisteredModules)) {
- this.unregisterModule(id);
- }
- gRegisteredModules = Object.create(null);
-
- this.closeAllListeners();
- this.globalActorFactories = {};
- this.targetScopedActorFactories = {};
- this._initialized = false;
-
- dumpn("Debugger server is shut down.");
- },
-
- /**
- * Raises an exception if the server has not been properly initialized.
- */
- _checkInit() {
- if (!this._initialized) {
- throw new Error("DebuggerServer has not been initialized.");
- }
-
- if (!this.rootlessServer && !this.createRootActor) {
- throw new Error("Use DebuggerServer.addActors() to add a root actor " +
- "implementation.");
- }
- },
-
- /**
- * Register different type of actors. Only register the one that are not already
- * registered.
- *
- * @param root boolean
- * Registers the root actor from webbrowser module, which is used to
- * connect to and fetch any other actor.
- * @param browser boolean
- * Registers all the parent process actors useful for debugging the
- * runtime itself, like preferences and addons actors.
- * @param target boolean
- * Registers all the target-scoped actors like console, script, etc.
- * for debugging a target context.
- */
- registerActors({ root, browser, target }) {
- if (browser) {
- this._addBrowserActors();
- }
-
- if (root) {
- const { createRootActor } = require("devtools/server/actors/webbrowser");
- this.setRootActor(createRootActor);
- }
-
- if (target) {
- this._addTargetScopedActors();
- }
- },
-
- /**
- * Register all possible actors for this DebuggerServer.
- */
- registerAllActors() {
- this.registerActors({ root: true, browser: true, target: true });
- },
-
- /**
- * Load a subscript into the debugging global.
- *
- * @param url string A url that will be loaded as a subscript into the
- * debugging global. The user must load at least one script
- * that implements a createRootActor() function to create the
- * server's root actor.
- */
- addActors(url) {
- loadSubScript.call(this, url);
- },
+const LazyActorRegistry = {
+ globalActorFactories: {},
+ targetScopedActorFactories: {},
/**
* Register a CommonJS module with the debugger server.
* @param id string
* The ID of a CommonJS module.
* The actor is going to be registered immediately, but loaded only
* when a client starts sending packets to an actor with the same id.
*
@@ -278,17 +116,17 @@ var DebuggerServer = {
if (mod.globalActor) {
this.addGlobalActor(mod, name);
}
},
/**
* Returns true if a module id has been registered.
*/
- isModuleRegistered(id) {
+ isActorRegistered(id) {
return (id in gRegisteredModules);
},
/**
* Unregister a previously-loaded CommonJS module from the debugger server.
*/
unregisterModule(id) {
const mod = gRegisteredModules[id];
@@ -460,695 +298,16 @@ var DebuggerServer = {
this.registerModule("devtools/server/actors/accessibility", {
prefix: "accessibility",
constructor: "AccessibilityActor",
type: { target: true }
});
},
/**
- * Passes a set of options to the AddonTargetActors for the given ID.
- *
- * @param id string
- * The ID of the add-on to pass the options to
- * @param options object
- * The options.
- * @return a promise that will be resolved when complete.
- */
- setAddonOptions(id, options) {
- if (!this._initialized) {
- return Promise.resolve();
- }
-
- const promises = [];
-
- // Pass to all connections
- for (const connID of Object.getOwnPropertyNames(this._connections)) {
- promises.push(this._connections[connID].setAddonOptions(id, options));
- }
-
- return Promise.all(promises);
- },
-
- get listeningSockets() {
- return this._listeners.length;
- },
-
- /**
- * Creates a socket listener for remote debugger connections.
- *
- * After calling this, set some socket options, such as the port / path to
- * listen on, and then call |open| on the listener.
- *
- * See SocketListener in devtools/shared/security/socket.js for available
- * options.
- *
- * @return SocketListener
- * A SocketListener instance that is waiting to be configured and
- * opened is returned. This single listener can be closed at any
- * later time by calling |close| on the SocketListener. If remote
- * connections are disabled, an error is thrown.
- */
- createListener() {
- if (!Services.prefs.getBoolPref("devtools.debugger.remote-enabled")) {
- throw new Error("Can't create listener, remote debugging disabled");
- }
- this._checkInit();
- return DebuggerSocket.createListener();
- },
-
- /**
- * Add a SocketListener instance to the server's set of active
- * SocketListeners. This is called by a SocketListener after it is opened.
- */
- _addListener(listener) {
- this._listeners.push(listener);
- },
-
- /**
- * Remove a SocketListener instance from the server's set of active
- * SocketListeners. This is called by a SocketListener after it is closed.
- */
- _removeListener(listener) {
- this._listeners = this._listeners.filter(l => l !== listener);
- },
-
- /**
- * Closes and forgets all previously opened listeners.
- *
- * @return boolean
- * Whether any listeners were actually closed.
- */
- closeAllListeners() {
- if (!this.listeningSockets) {
- return false;
- }
-
- for (const listener of this._listeners) {
- listener.close();
- }
-
- return true;
- },
-
- /**
- * Creates a new connection to the local debugger speaking over a fake
- * transport. This connection results in straightforward calls to the onPacket
- * handlers of each side.
- *
- * @param prefix string [optional]
- * If given, all actors in this connection will have names starting
- * with |prefix + '/'|.
- * @returns a client-side DebuggerTransport for communicating with
- * the newly-created connection.
- */
- connectPipe(prefix) {
- this._checkInit();
-
- const serverTransport = new LocalDebuggerTransport();
- const clientTransport = new LocalDebuggerTransport(serverTransport);
- serverTransport.other = clientTransport;
- const connection = this._onConnection(serverTransport, prefix);
-
- // I'm putting this here because I trust you.
- //
- // There are times, when using a local connection, when you're going
- // to be tempted to just get direct access to the server. Resist that
- // temptation! If you succumb to that temptation, you will make the
- // fine developers that work on Fennec and Firefox OS sad. They're
- // professionals, they'll try to act like they understand, but deep
- // down you'll know that you hurt them.
- //
- // This reference allows you to give in to that temptation. There are
- // times this makes sense: tests, for example, and while porting a
- // previously local-only codebase to the remote protocol.
- //
- // But every time you use this, you will feel the shame of having
- // used a property that starts with a '_'.
- clientTransport._serverConnection = connection;
-
- return clientTransport;
- },
-
- /**
- * In a content child process, create a new connection that exchanges
- * nsIMessageSender messages with our parent process.
- *
- * @param prefix
- * The prefix we should use in our nsIMessageSender message names and
- * actor names. This connection will use messages named
- * "debug:<prefix>:packet", and all its actors will have names
- * beginning with "<prefix>/".
- */
- connectToParent(prefix, scopeOrManager) {
- this._checkInit();
-
- const transport = isWorker ?
- new WorkerDebuggerTransport(scopeOrManager, prefix) :
- new ChildDebuggerTransport(scopeOrManager, prefix);
-
- return this._onConnection(transport, prefix, true);
- },
-
- /**
- * Start a DevTools server in a content process (representing the entire process, not
- * just a single frame) and add it as a child server for an active connection.
- */
- connectToContentProcess(connection, mm, onDestroy) {
- return new Promise(resolve => {
- const prefix = connection.allocID("content-process");
- let actor, childTransport;
-
- mm.addMessageListener("debug:content-process-actor", function listener(msg) {
- // Arbitrarily choose the first content process to reply
- // XXX: This code needs to be updated if we use more than one content process
- mm.removeMessageListener("debug:content-process-actor", listener);
-
- // Pipe Debugger message from/to parent/child via the message manager
- childTransport = new ChildDebuggerTransport(mm, prefix);
- childTransport.hooks = {
- onPacket: connection.send.bind(connection),
- onClosed() {}
- };
- childTransport.ready();
-
- connection.setForwarding(prefix, childTransport);
-
- dumpn(`Start forwarding for process with prefix ${prefix}`);
-
- actor = msg.json.actor;
-
- resolve(actor);
- });
-
- // Load the content process server startup script only once.
- if (!this._contentProcessServerStartupScriptLoaded) {
- // Load the process script that will receive the debug:init-content-server message
- Services.ppmm.loadProcessScript(CONTENT_PROCESS_SERVER_STARTUP_SCRIPT, true);
- this._contentProcessServerStartupScriptLoaded = true;
- }
-
- // Send a message to the content process server startup script to forward it the
- // prefix.
- mm.sendAsyncMessage("debug:init-content-server", {
- prefix: prefix
- });
-
- function onClose() {
- Services.obs.removeObserver(onMessageManagerClose, "message-manager-close");
- EventEmitter.off(connection, "closed", onClose);
- if (childTransport) {
- // If we have a child transport, the actor has already
- // been created. We need to stop using this message manager.
- childTransport.close();
- childTransport = null;
- connection.cancelForwarding(prefix);
-
- // ... and notify the child process to clean the target-scoped actors.
- try {
- mm.sendAsyncMessage("debug:content-process-destroy");
- } catch (e) {
- // Nothing to do
- }
- }
-
- if (onDestroy) {
- onDestroy(mm);
- }
- }
-
- const onMessageManagerClose =
- DevToolsUtils.makeInfallible((subject, topic, data) => {
- if (subject == mm) {
- onClose();
- connection.send({ from: actor.actor, type: "tabDetached" });
- }
- });
- Services.obs.addObserver(onMessageManagerClose,
- "message-manager-close");
-
- EventEmitter.on(connection, "closed", onClose);
- });
- },
-
- /**
- * Start a DevTools server in a worker and add it as a child server for an active
- * connection.
- */
- connectToWorker(connection, dbg, id, options) {
- return new Promise((resolve, reject) => {
- // Step 1: Ensure the worker debugger is initialized.
- if (!dbg.isInitialized) {
- dbg.initialize("resource://devtools/server/startup/worker.js");
-
- // Create a listener for rpc requests from the worker debugger. Only do
- // this once, when the worker debugger is first initialized, rather than
- // for each connection.
- const listener = {
- onClose: () => {
- dbg.removeListener(listener);
- },
-
- onMessage: (message) => {
- message = JSON.parse(message);
- if (message.type !== "rpc") {
- return;
- }
-
- Promise.resolve().then(() => {
- const method = {
- "fetch": DevToolsUtils.fetch,
- }[message.method];
- if (!method) {
- throw Error("Unknown method: " + message.method);
- }
-
- return method.apply(undefined, message.params);
- }).then((value) => {
- dbg.postMessage(JSON.stringify({
- type: "rpc",
- result: value,
- error: null,
- id: message.id
- }));
- }, (reason) => {
- dbg.postMessage(JSON.stringify({
- type: "rpc",
- result: null,
- error: reason,
- id: message.id
- }));
- });
- }
- };
-
- dbg.addListener(listener);
- }
-
- // Step 2: Send a connect request to the worker debugger.
- dbg.postMessage(JSON.stringify({
- type: "connect",
- id,
- options,
- }));
-
- // Steps 3-5 are performed on the worker thread (see worker.js).
-
- // Step 6: Wait for a connection response from the worker debugger.
- const listener = {
- onClose: () => {
- dbg.removeListener(listener);
-
- reject("closed");
- },
-
- onMessage: (message) => {
- message = JSON.parse(message);
- if (message.type !== "connected" || message.id !== id) {
- return;
- }
-
- // The initial connection message has been received, don't
- // need to listen any longer
- dbg.removeListener(listener);
-
- // Step 7: Create a transport for the connection to the worker.
- const transport = new WorkerDebuggerTransport(dbg, id);
- transport.ready();
- transport.hooks = {
- onClosed: () => {
- if (!dbg.isClosed) {
- // If the worker happens to be shutting down while we are trying
- // to close the connection, there is a small interval during
- // which no more runnables can be dispatched to the worker, but
- // the worker debugger has not yet been closed. In that case,
- // the call to postMessage below will fail. The onClosed hook on
- // DebuggerTransport is not supposed to throw exceptions, so we
- // need to make sure to catch these early.
- try {
- dbg.postMessage(JSON.stringify({
- type: "disconnect",
- id,
- }));
- } catch (e) {
- // We can safely ignore these exceptions. The only time the
- // call to postMessage can fail is if the worker is either
- // shutting down, or has finished shutting down. In both
- // cases, there is nothing to clean up, so we don't care
- // whether this message arrives or not.
- }
- }
-
- connection.cancelForwarding(id);
- },
-
- onPacket: (packet) => {
- // Ensure that any packets received from the server on the worker
- // thread are forwarded to the client on the main thread, as if
- // they had been sent by the server on the main thread.
- connection.send(packet);
- }
- };
-
- // Ensure that any packets received from the client on the main thread
- // to actors on the worker thread are forwarded to the server on the
- // worker thread.
- connection.setForwarding(id, transport);
-
- resolve({
- threadActor: message.threadActor,
- consoleActor: message.consoleActor,
- transport: transport
- });
- }
- };
- dbg.addListener(listener);
- });
- },
-
- /**
- * Check if the server is running in the child process.
- */
- get isInChildProcess() {
- return Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
- },
-
- /**
- * 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}) { ... })
- * @param waitForEval (optional)
- * If true, the returned promise only resolves once code in child
- * is evaluated
- */
- setupInChild({ module, setupChild, args, waitForEval }) {
- if (this._childMessageManagers.size == 0) {
- return Promise.resolve();
- }
- return new Promise(done => {
- // If waitForEval is set, pass a unique id and expect child.js to send
- // a message back once the code in child is evaluated.
- if (typeof (waitForEval) != "boolean") {
- waitForEval = false;
- }
- let count = this._childMessageManagers.size;
- const id = waitForEval ? generateUUID().toString() : null;
-
- this._childMessageManagers.forEach(mm => {
- if (waitForEval) {
- // Listen for the end of each child execution
- const evalListener = msg => {
- if (msg.data.id !== id) {
- return;
- }
- mm.removeMessageListener("debug:setup-in-child-response", evalListener);
- if (--count === 0) {
- done();
- }
- };
- mm.addMessageListener("debug:setup-in-child-response", evalListener);
- }
- mm.sendAsyncMessage("debug:setup-in-child", {
- module: module,
- setupChild: setupChild,
- args: args,
- id: id,
- });
- });
-
- if (!waitForEval) {
- done();
- }
- });
- },
-
- /**
- * Live list of all currenctly attached child's message managers.
- */
- _childMessageManagers: new Set(),
-
- /**
- * Start a DevTools server in a remote frame's process and add it as a child server for
- * an active connection.
- *
- * @param object connection
- * The debugger server connection to use.
- * @param Element frame
- * The frame element with remote content to connect to.
- * @param function [onDestroy]
- * Optional function to invoke when the child process closes or the connection
- * shuts down. (Need to forget about the related target actor.)
- * @return object
- * A promise object that is resolved once the connection is established.
- */
- connectToFrame(connection, frame, onDestroy, {addonId} = {}) {
- return new Promise(resolve => {
- // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
- // or else fallback to asking the frameLoader itself.
- let mm = frame.messageManager || frame.frameLoader.messageManager;
- mm.loadFrameScript("resource://devtools/server/startup/frame.js", false);
-
- const trackMessageManager = () => {
- frame.addEventListener("DevTools:BrowserSwap", onBrowserSwap);
- mm.addMessageListener("debug:setup-in-parent", onSetupInParent);
- if (!actor) {
- mm.addMessageListener("debug:actor", onActorCreated);
- }
- DebuggerServer._childMessageManagers.add(mm);
- };
-
- const untrackMessageManager = () => {
- frame.removeEventListener("DevTools:BrowserSwap", onBrowserSwap);
- mm.removeMessageListener("debug:setup-in-parent", onSetupInParent);
- if (!actor) {
- mm.removeMessageListener("debug:actor", onActorCreated);
- }
- DebuggerServer._childMessageManagers.delete(mm);
- };
-
- let actor, childTransport;
- const prefix = connection.allocID("child");
- // Compute the same prefix that's used by DebuggerServerConnection
- const connPrefix = prefix + "/";
-
- // provides hook to actor modules that need to exchange messages
- // between e10s parent and child processes
- const parentModules = [];
- const onSetupInParent = function(msg) {
- // We may have multiple connectToFrame instance running for the same frame and
- // need to filter the messages.
- if (msg.json.prefix != connPrefix) {
- return false;
- }
-
- const { module, setupParent } = msg.json;
- let m;
-
- try {
- m = require(module);
-
- if (!(setupParent in m)) {
- dumpn(`ERROR: module '${module}' does not export '${setupParent}'`);
- return false;
- }
-
- parentModules.push(m[setupParent]({ mm, prefix: connPrefix }));
-
- return true;
- } catch (e) {
- const errorMessage =
- "Exception during actor module setup running in the parent process: ";
- DevToolsUtils.reportException(errorMessage + e);
- dumpn(`ERROR: ${errorMessage}\n\t module: '${module}'\n\t ` +
- `setupParent: '${setupParent}'\n${DevToolsUtils.safeErrorString(e)}`);
- return false;
- }
- };
-
- const onActorCreated = DevToolsUtils.makeInfallible(function(msg) {
- if (msg.json.prefix != prefix) {
- return;
- }
- mm.removeMessageListener("debug:actor", onActorCreated);
-
- // Pipe Debugger message from/to parent/child via the message manager
- childTransport = new ChildDebuggerTransport(mm, prefix);
- childTransport.hooks = {
- onPacket: connection.send.bind(connection),
- onClosed() {}
- };
- childTransport.ready();
-
- connection.setForwarding(prefix, childTransport);
-
- dumpn(`Start forwarding for frame with prefix ${prefix}`);
-
- actor = msg.json.actor;
- resolve(actor);
- }).bind(this);
-
- // Listen for browser frame swap
- const onBrowserSwap = ({ detail: newFrame }) => {
- // Remove listeners from old frame and mm
- untrackMessageManager();
- // Update frame and mm to point to the new browser frame
- frame = newFrame;
- // Get messageManager from XUL browser (which might be a specialized tunnel for
- // RDM) or else fallback to asking the frameLoader itself.
- mm = frame.messageManager || frame.frameLoader.messageManager;
- // Add listeners to new frame and mm
- trackMessageManager();
-
- // provides hook to actor modules that need to exchange messages
- // between e10s parent and child processes
- parentModules.forEach(mod => {
- if (mod.onBrowserSwap) {
- mod.onBrowserSwap(mm);
- }
- });
-
- if (childTransport) {
- childTransport.swapBrowser(mm);
- }
- };
-
- const destroy = DevToolsUtils.makeInfallible(function() {
- EventEmitter.off(connection, "closed", destroy);
- Services.obs.removeObserver(onMessageManagerClose, "message-manager-close");
-
- // provides hook to actor modules that need to exchange messages
- // between e10s parent and child processes
- parentModules.forEach(mod => {
- if (mod.onDisconnected) {
- mod.onDisconnected();
- }
- });
- // TODO: Remove this deprecated path once it's no longer needed by add-ons.
- DebuggerServer.emit("disconnected-from-child:" + connPrefix,
- { mm, prefix: connPrefix });
-
- if (childTransport) {
- // If we have a child transport, the actor has already
- // been created. We need to stop using this message manager.
- childTransport.close();
- childTransport = null;
- connection.cancelForwarding(prefix);
-
- // ... and notify the child process to clean the target-scoped actors.
- try {
- // Bug 1169643: Ignore any exception as the child process
- // may already be destroyed by now.
- mm.sendAsyncMessage("debug:disconnect", { prefix });
- } catch (e) {
- // Nothing to do
- }
- } else {
- // Otherwise, the frame has been closed before the actor
- // had a chance to be created, so we are not able to create
- // the actor.
- resolve(null);
- }
- if (actor) {
- // The FrameTargetActor within the child process doesn't necessary
- // have time to uninitialize itself when the frame is closed/killed.
- // So ensure telling the client that the related actor is detached.
- connection.send({ from: actor.actor, type: "tabDetached" });
- actor = null;
- }
-
- if (onDestroy) {
- onDestroy(mm);
- }
-
- // Cleanup all listeners
- untrackMessageManager();
- });
-
- // Listen for various messages and frame events
- trackMessageManager();
-
- // Listen for app process exit
- const onMessageManagerClose = function(subject, topic, data) {
- if (subject == mm) {
- destroy();
- }
- };
- Services.obs.addObserver(onMessageManagerClose,
- "message-manager-close");
-
- // Listen for connection close to cleanup things
- // when user unplug the device or we lose the connection somehow.
- EventEmitter.on(connection, "closed", destroy);
-
- mm.sendAsyncMessage("debug:connect", { prefix, addonId });
- });
- },
-
- /**
- * Create a new debugger connection for the given transport. Called after
- * connectPipe(), from connectToParent, or from an incoming socket
- * connection handler.
- *
- * If present, |forwardingPrefix| is a forwarding prefix that a parent
- * server is using to recognizes messages intended for this server. Ensure
- * that all our actors have names beginning with |forwardingPrefix + '/'|.
- * In particular, the root actor's name will be |forwardingPrefix + '/root'|.
- */
- _onConnection(transport, forwardingPrefix, noRootActor = false) {
- let connID;
- if (forwardingPrefix) {
- connID = forwardingPrefix + "/";
- } else {
- // Multiple servers can be started at the same time, and when that's the
- // case, they are loaded in separate devtools loaders.
- // So, use the current loader ID to prefix the connection ID and make it
- // unique.
- connID = "server" + loader.id + ".conn" + this._nextConnID++ + ".";
- }
-
- const conn = new DebuggerServerConnection(connID, transport);
- this._connections[connID] = conn;
-
- // Create a root actor for the connection and send the hello packet.
- if (!noRootActor) {
- conn.rootActor = this.createRootActor(conn);
- if (forwardingPrefix) {
- conn.rootActor.actorID = forwardingPrefix + "/root";
- } else {
- conn.rootActor.actorID = "root";
- }
- conn.addActor(conn.rootActor);
- transport.send(conn.rootActor.sayHello());
- }
- transport.ready();
-
- this.emit("connectionchange", "opened", conn);
- return conn;
- },
-
- /**
- * Remove the connection from the debugging server.
- */
- _connectionClosed(connection) {
- delete this._connections[connection.prefix];
- this.emit("connectionchange", "closed", connection);
- },
-
- // DebuggerServer extension API.
-
- setRootActor(actorFactory) {
- this.createRootActor = actorFactory;
- },
-
- /**
* Registers handlers for new target-scoped request types defined dynamically.
*
* Note that the name or actorPrefix of the request type is not allowed to clash with
* existing protocol packet properties, like 'title', 'url' or 'actor', since that would
* break the protocol.
*
* @param options object
* - constructorName: (required)
@@ -1163,20 +322,20 @@ var DebuggerServer = {
*/
addTargetScopedActor(options, name) {
if (!name) {
throw Error("addTargetScopedActor requires the `name` argument");
}
if (["title", "url", "actor"].includes(name)) {
throw Error(name + " is not allowed");
}
- if (DebuggerServer.targetScopedActorFactories.hasOwnProperty(name)) {
+ if (this.targetScopedActorFactories.hasOwnProperty(name)) {
throw Error(name + " already exists");
}
- DebuggerServer.targetScopedActorFactories[name] = { options, name };
+ this.targetScopedActorFactories[name] = { options, name };
},
/**
* Unregisters the handler for the specified target-scoped request type.
*
* When unregistering an existing target-scoped actor, we remove the actor factory as
* well as all existing instances of the actor.
*
@@ -1187,29 +346,29 @@ var DebuggerServer = {
* The `name` string being given to related addTargetScopedActor call.
*/
removeTargetScopedActor(actorOrName) {
let name;
if (typeof actorOrName == "string") {
name = actorOrName;
} else {
const actor = actorOrName;
- for (const factoryName in DebuggerServer.targetScopedActorFactories) {
- const handler = DebuggerServer.targetScopedActorFactories[factoryName];
+ for (const factoryName in this.targetScopedActorFactories) {
+ const handler = this.targetScopedActorFactories[factoryName];
if ((handler.options.constructorName == actor.name) ||
(handler.options.id == actor.id)) {
name = factoryName;
break;
}
}
}
if (!name) {
return;
}
- delete DebuggerServer.targetScopedActorFactories[name];
+ delete this.targetScopedActorFactories[name];
for (const connID of Object.getOwnPropertyNames(this._connections)) {
// DebuggerServerConnection in child process don't have rootActor
if (this._connections[connID].rootActor) {
this._connections[connID].rootActor.removeActorByName(name);
}
}
},
@@ -1233,20 +392,20 @@ var DebuggerServer = {
*/
addGlobalActor(options, name) {
if (!name) {
throw Error("addGlobalActor requires the `name` argument");
}
if (["from", "tabs", "selected"].includes(name)) {
throw Error(name + " is not allowed");
}
- if (DebuggerServer.globalActorFactories.hasOwnProperty(name)) {
+ if (this.globalActorFactories.hasOwnProperty(name)) {
throw Error(name + " already exists");
}
- DebuggerServer.globalActorFactories[name] = { options, name };
+ this.globalActorFactories[name] = { options, name };
},
/**
* Unregisters the handler for the specified browser-scoped request type.
*
* When unregistering an existing global actor, we remove the actor factory as well as
* all existing instances of the actor.
*
@@ -1257,607 +416,41 @@ var DebuggerServer = {
* The `name` string being given to related addGlobalActor call.
*/
removeGlobalActor(actorOrName) {
let name;
if (typeof actorOrName == "string") {
name = actorOrName;
} else {
const actor = actorOrName;
- for (const factoryName in DebuggerServer.globalActorFactories) {
- const handler = DebuggerServer.globalActorFactories[factoryName];
+ for (const factoryName in this.globalActorFactories) {
+ const handler = this.globalActorFactories[factoryName];
if ((handler.options.constructorName == actor.name) ||
(handler.options.id == actor.id)) {
name = factoryName;
break;
}
}
}
if (!name) {
return;
}
- delete DebuggerServer.globalActorFactories[name];
+ delete this.globalActorFactories[name];
for (const connID of Object.getOwnPropertyNames(this._connections)) {
// DebuggerServerConnection in child process don't have rootActor
if (this._connections[connID].rootActor) {
this._connections[connID].rootActor.removeActorByName(name);
}
}
},
- /**
- * Called when DevTools are unloaded to remove the contend process server startup script
- * for the list of scripts loaded for each new content process. Will also remove message
- * listeners from already loaded scripts.
- */
- removeContentServerScript() {
- Services.ppmm.removeDelayedProcessScript(CONTENT_PROCESS_SERVER_STARTUP_SCRIPT);
- try {
- Services.ppmm.broadcastAsyncMessage("debug:close-content-server");
- } catch (e) {
- // Nothing to do
+ destroy() {
+ for (const id of Object.getOwnPropertyNames(gRegisteredModules)) {
+ this.unregisterModule(id);
}
- },
+ gRegisteredModules = Object.create(null);
- /**
- * Searches all active connections for an actor matching an ID.
- *
- * âš TO BE USED ONLY FROM SERVER CODE OR TESTING ONLY! âš `
- *
- * This is helpful for some tests which depend on reaching into the server to check some
- * properties of an actor, and it is also used by the actors related to the
- * DevTools WebExtensions API to be able to interact with the actors created for the
- * panels natively provided by the DevTools Toolbox.
- */
- searchAllConnectionsForActor(actorID) {
- // NOTE: the actor IDs are generated with the following format:
- //
- // `server${loaderID}.conn${ConnectionID}${ActorPrefix}${ActorID}`
- //
- // as an optimization we can come up with a regexp to query only
- // the right connection via its id.
- for (const connID of Object.getOwnPropertyNames(this._connections)) {
- const actor = this._connections[connID].getActor(actorID);
- if (actor) {
- return actor;
- }
- }
- return null;
+ this.globalActorFactories = {};
+ this.targetScopedActorFactories = {};
},
};
-// Expose these to save callers the trouble of importing DebuggerSocket
-DevToolsUtils.defineLazyGetter(DebuggerServer, "Authenticators", () => {
- return Authentication.Authenticators;
-});
-DevToolsUtils.defineLazyGetter(DebuggerServer, "AuthenticationResult", () => {
- return Authentication.AuthenticationResult;
-});
-
-EventEmitter.decorate(DebuggerServer);
-
-if (this.exports) {
- exports.DebuggerServer = DebuggerServer;
- exports.ActorPool = ActorPool;
- exports.OriginalLocation = OriginalLocation;
-}
-
-// Needed on B2G (See header note)
-this.DebuggerServer = DebuggerServer;
-this.ActorPool = ActorPool;
-this.OriginalLocation = OriginalLocation;
-
-// When using DebuggerServer.addActors, some symbols are expected to be in
-// the scope of the added actor even before the corresponding modules are
-// loaded, so let's explicitly bind the expected symbols here.
-var includes = ["Components", "Ci", "Cu", "require", "Services", "DebuggerServer",
- "ActorPool", "DevToolsUtils"];
-includes.forEach(name => {
- DebuggerServer[name] = this[name];
-});
-
-/**
- * Creates a DebuggerServerConnection.
- *
- * Represents a connection to this debugging global from a client.
- * Manages a set of actors and actor pools, allocates actor ids, and
- * handles incoming requests.
- *
- * @param prefix string
- * All actor IDs created by this connection should be prefixed
- * with prefix.
- * @param transport transport
- * Packet transport for the debugging protocol.
- */
-function DebuggerServerConnection(prefix, transport) {
- this._prefix = prefix;
- this._transport = transport;
- this._transport.hooks = this;
- this._nextID = 1;
-
- this._actorPool = new ActorPool(this);
- this._extraPools = [this._actorPool];
-
- // Responses to a given actor must be returned the the client
- // in the same order as the requests that they're replying to, but
- // Implementations might finish serving requests in a different
- // order. To keep things in order we generate a promise for each
- // request, chained to the promise for the request before it.
- // This map stores the latest request promise in the chain, keyed
- // by an actor ID string.
- this._actorResponses = new Map();
-
- /*
- * We can forward packets to other servers, if the actors on that server
- * all use a distinct prefix on their names. This is a map from prefixes
- * to transports: it maps a prefix P to a transport T if T conveys
- * packets to the server whose actors' names all begin with P + "/".
- */
- this._forwardingPrefixes = new Map();
-}
-
-DebuggerServerConnection.prototype = {
- _prefix: null,
- get prefix() {
- return this._prefix;
- },
-
- _transport: null,
- get transport() {
- return this._transport;
- },
-
- /**
- * Message manager used to communicate with the parent process,
- * set by child.js. Is only defined for connections instantiated
- * within a child process.
- */
- parentMessageManager: null,
-
- close() {
- if (this._transport) {
- this._transport.close();
- }
- },
-
- send(packet) {
- this.transport.send(packet);
- },
-
- /**
- * Used when sending a bulk reply from an actor.
- * @see DebuggerTransport.prototype.startBulkSend
- */
- startBulkSend(header) {
- return this.transport.startBulkSend(header);
- },
-
- allocID(prefix) {
- return this.prefix + (prefix || "") + this._nextID++;
- },
-
- /**
- * Add a map of actor IDs to the connection.
- */
- addActorPool(actorPool) {
- this._extraPools.push(actorPool);
- },
-
- /**
- * Remove a previously-added pool of actors to the connection.
- *
- * @param ActorPool actorPool
- * The ActorPool instance you want to remove.
- * @param boolean noCleanup [optional]
- * True if you don't want to destroy each actor from the pool, false
- * otherwise.
- */
- removeActorPool(actorPool, noCleanup) {
- // When a connection is closed, it removes each of its actor pools. When an
- // actor pool is removed, it calls the destroy method on each of its
- // actors. Some actors, such as ThreadActor, manage their own actor pools.
- // When the destroy method is called on these actors, they manually
- // remove their actor pools. Consequently, this method is reentrant.
- //
- // In addition, some actors, such as ThreadActor, perform asynchronous work
- // (in the case of ThreadActor, because they need to resume), before they
- // remove each of their actor pools. Since we don't wait for this work to
- // be completed, we can end up in this function recursively after the
- // connection already set this._extraPools to null.
- //
- // This is a bug: if the destroy method can perform asynchronous work,
- // then we should wait for that work to be completed before setting this.
- // _extraPools to null. As a temporary solution, it should be acceptable
- // to just return early (if this._extraPools has been set to null, all
- // actors pools for this connection should already have been removed).
- if (this._extraPools === null) {
- return;
- }
- const index = this._extraPools.lastIndexOf(actorPool);
- if (index > -1) {
- const pool = this._extraPools.splice(index, 1);
- if (!noCleanup) {
- pool.forEach(p => p.destroy());
- }
- }
- },
-
- /**
- * Add an actor to the default actor pool for this connection.
- */
- addActor(actor) {
- this._actorPool.addActor(actor);
- },
-
- /**
- * Remove an actor to the default actor pool for this connection.
- */
- removeActor(actor) {
- this._actorPool.removeActor(actor);
- },
-
- /**
- * Match the api expected by the protocol library.
- */
- unmanage(actor) {
- return this.removeActor(actor);
- },
-
- /**
- * Look up an actor implementation for an actorID. Will search
- * all the actor pools registered with the connection.
- *
- * @param actorID string
- * Actor ID to look up.
- */
- getActor(actorID) {
- const pool = this.poolFor(actorID);
- if (pool) {
- return pool.get(actorID);
- }
-
- if (actorID === "root") {
- return this.rootActor;
- }
-
- return null;
- },
-
- _getOrCreateActor(actorID) {
- try {
- const actor = this.getActor(actorID);
- if (!actor) {
- this.transport.send({ from: actorID ? actorID : "root",
- error: "noSuchActor",
- message: "No such actor for ID: " + actorID });
- return null;
- }
-
- if (typeof (actor) !== "object") {
- // ActorPools should now contain only actor instances (i.e. objects)
- // or ObservedActorFactory instances.
- throw new Error("Unexpected actor constructor/function in ActorPool " +
- "for actorID=" + actorID + ".");
- }
-
- return actor;
- } catch (error) {
- const prefix = `Error occurred while creating actor' ${actorID}`;
- this.transport.send(this._unknownError(actorID, prefix, error));
- }
- return null;
- },
-
- poolFor(actorID) {
- for (const pool of this._extraPools) {
- if (pool.has(actorID)) {
- return pool;
- }
- }
- return null;
- },
-
- _unknownError(from, prefix, error) {
- const errorString = prefix + ": " + DevToolsUtils.safeErrorString(error);
- reportError(errorString);
- dumpn(errorString);
- return {
- from,
- error: "unknownError",
- message: errorString
- };
- },
-
- _queueResponse: function(from, type, responseOrPromise) {
- const pendingResponse = this._actorResponses.get(from) || Promise.resolve(null);
- const responsePromise = pendingResponse.then(() => {
- return responseOrPromise;
- }).then(response => {
- if (!this.transport) {
- throw new Error(`Connection closed, pending response from ${from}, ` +
- `type ${type} failed`);
- }
- if (!response.from) {
- response.from = from;
- }
- this.transport.send(response);
- }).catch((error) => {
- if (!this.transport) {
- throw new Error(`Connection closed, pending error from ${from}, ` +
- `type ${type} failed`);
- }
-
- const prefix = "error occurred while processing '" + type;
- this.transport.send(this._unknownError(from, prefix, error));
- });
-
- this._actorResponses.set(from, responsePromise);
- },
-
- /**
- * Passes a set of options to the AddonTargetActors for the given ID.
- *
- * @param id string
- * The ID of the add-on to pass the options to
- * @param options object
- * The options.
- * @return a promise that will be resolved when complete.
- */
- setAddonOptions(id, options) {
- const addonList = this.rootActor._parameters.addonList;
- if (!addonList) {
- return Promise.resolve();
- }
- return addonList.getList().then((addonTargetActors) => {
- for (const actor of addonTargetActors) {
- if (actor.id != id) {
- continue;
- }
- actor.setOptions(options);
- return;
- }
- });
- },
-
- /* Forwarding packets to other transports based on actor name prefixes. */
-
- /*
- * Arrange to forward packets to another server. This is how we
- * forward debugging connections to child processes.
- *
- * If we receive a packet for an actor whose name begins with |prefix|
- * followed by '/', then we will forward that packet to |transport|.
- *
- * This overrides any prior forwarding for |prefix|.
- *
- * @param prefix string
- * The actor name prefix, not including the '/'.
- * @param transport object
- * A packet transport to which we should forward packets to actors
- * whose names begin with |(prefix + '/').|
- */
- setForwarding(prefix, transport) {
- this._forwardingPrefixes.set(prefix, transport);
- },
-
- /*
- * Stop forwarding messages to actors whose names begin with
- * |prefix+'/'|. Such messages will now elicit 'noSuchActor' errors.
- */
- cancelForwarding(prefix) {
- this._forwardingPrefixes.delete(prefix);
-
- // Notify the client that forwarding in now cancelled for this prefix.
- // There could be requests in progress that the client should abort rather leaving
- // handing indefinitely.
- if (this.rootActor) {
- this.send(this.rootActor.forwardingCancelled(prefix));
- }
- },
-
- sendActorEvent(actorID, eventName, event = {}) {
- event.from = actorID;
- event.type = eventName;
- this.send(event);
- },
-
- // Transport hooks.
-
- /**
- * Called by DebuggerTransport to dispatch incoming packets as appropriate.
- *
- * @param packet object
- * The incoming packet.
- */
- onPacket(packet) {
- // If the actor's name begins with a prefix we've been asked to
- // forward, do so.
- //
- // Note that the presence of a prefix alone doesn't indicate that
- // forwarding is needed: in DebuggerServerConnection instances in child
- // processes, every actor has a prefixed name.
- if (this._forwardingPrefixes.size > 0) {
- let to = packet.to;
- let separator = to.lastIndexOf("/");
- while (separator >= 0) {
- to = to.substring(0, separator);
- const forwardTo = this._forwardingPrefixes.get(packet.to.substring(0, separator));
- if (forwardTo) {
- forwardTo.send(packet);
- return;
- }
- separator = to.lastIndexOf("/");
- }
- }
-
- const actor = this._getOrCreateActor(packet.to);
- if (!actor) {
- return;
- }
-
- let ret = null;
-
- // handle "requestTypes" RDP request.
- if (packet.type == "requestTypes") {
- ret = { from: actor.actorID, requestTypes: Object.keys(actor.requestTypes) };
- } else if (actor.requestTypes && actor.requestTypes[packet.type]) {
- // Dispatch the request to the actor.
- try {
- this.currentPacket = packet;
- ret = actor.requestTypes[packet.type].bind(actor)(packet, this);
- } catch (error) {
- const prefix = "error occurred while processing '" + packet.type;
- this.transport.send(this._unknownError(actor.actorID, prefix, error));
- } finally {
- this.currentPacket = undefined;
- }
- } else {
- ret = { error: "unrecognizedPacketType",
- message: ("Actor " + actor.actorID +
- " does not recognize the packet type " +
- packet.type) };
- }
-
- // There will not be a return value if a bulk reply is sent.
- if (ret) {
- this._queueResponse(packet.to, packet.type, ret);
- }
- },
-
- /**
- * Called by the DebuggerTransport to dispatch incoming bulk packets as
- * appropriate.
- *
- * @param packet object
- * The incoming packet, which contains:
- * * actor: Name of actor that will receive the packet
- * * type: Name of actor's method that should be called on receipt
- * * length: Size of the data to be read
- * * stream: This input stream should only be used directly if you can
- * ensure that you will read exactly |length| bytes and will
- * not close the stream when reading is complete
- * * done: If you use the stream directly (instead of |copyTo|
- * below), you must signal completion by resolving /
- * rejecting this deferred. If it's rejected, the transport
- * will be closed. If an Error is supplied as a rejection
- * value, it will be logged via |dumpn|. If you do use
- * |copyTo|, resolving is taken care of for you when copying
- * completes.
- * * copyTo: A helper function for getting your data out of the stream
- * that meets the stream handling requirements above, and has
- * the following signature:
- * @param output nsIAsyncOutputStream
- * The stream to copy to.
- * @return Promise
- * The promise is resolved when copying completes or rejected
- * if any (unexpected) errors occur.
- * This object also emits "progress" events for each chunk
- * that is copied. See stream-utils.js.
- */
- onBulkPacket(packet) {
- const { actor: actorKey, type } = packet;
-
- const actor = this._getOrCreateActor(actorKey);
- if (!actor) {
- return;
- }
-
- // Dispatch the request to the actor.
- let ret;
- if (actor.requestTypes && actor.requestTypes[type]) {
- try {
- ret = actor.requestTypes[type].call(actor, packet);
- } catch (error) {
- const prefix = "error occurred while processing bulk packet '" + type;
- this.transport.send(this._unknownError(actorKey, prefix, error));
- packet.done.reject(error);
- }
- } else {
- const message = "Actor " + actorKey +
- " does not recognize the bulk packet type " + type;
- ret = { error: "unrecognizedPacketType",
- message: message };
- packet.done.reject(new Error(message));
- }
-
- // If there is a JSON response, queue it for sending back to the client.
- if (ret) {
- this._queueResponse(actorKey, type, ret);
- }
- },
-
- /**
- * Called by DebuggerTransport when the underlying stream is closed.
- *
- * @param status nsresult
- * The status code that corresponds to the reason for closing
- * the stream.
- */
- onClosed(status) {
- dumpn("Cleaning up connection.");
- if (!this._actorPool) {
- // Ignore this call if the connection is already closed.
- return;
- }
- this._actorPool = null;
-
- EventEmitter.emit(this, "closed", status);
-
- this._extraPools.forEach(p => p.destroy());
- this._extraPools = null;
-
- this.rootActor = null;
- this._transport = null;
- DebuggerServer._connectionClosed(this);
- },
-
- /*
- * Debugging helper for inspecting the state of the actor pools.
- */
- _dumpPools() {
- dumpn("/-------------------- dumping pools:");
- if (this._actorPool) {
- dumpn("--------------------- actorPool actors: " +
- uneval(Object.keys(this._actorPool._actors)));
- }
- for (const pool of this._extraPools) {
- if (pool !== this._actorPool) {
- dumpn("--------------------- extraPool actors: " +
- uneval(Object.keys(pool._actors)));
- }
- }
- },
-
- /*
- * Debugging helper for inspecting the state of an actor pool.
- */
- _dumpPool(pool) {
- dumpn("/-------------------- dumping pool:");
- dumpn("--------------------- actorPool actors: " +
- uneval(Object.keys(pool._actors)));
- },
-
- /**
- * In a content child process, ask the DebuggerServer in the parent process
- * to execute a given module setup helper.
- *
- * @param module
- * The module to be required
- * @param setupParent
- * The name of the setup helper exported by the above module
- * (setup helper signature: function ({mm}) { ... })
- * @return boolean
- * true if the setup helper returned successfully
- */
- setupInParent({ module, setupParent }) {
- if (!this.parentMessageManager) {
- return false;
- }
-
- const { sendSyncMessage } = this.parentMessageManager;
-
- return sendSyncMessage("debug:setup-in-parent", {
- prefix: this.prefix,
- module: module,
- setupParent: setupParent
- });
- },
-};
+exports.actorRegistry = LazyActorRegistry;
--- a/devtools/server/actors/targets/browsing-context.js
+++ b/devtools/server/actors/targets/browsing-context.js
@@ -19,17 +19,17 @@
* process. For example, it shouldn't be evaluated in the parent process until we try to
* debug a document living in the parent process.
*/
var { Ci, Cu, Cr, Cc } = require("chrome");
var Services = require("Services");
const ChromeUtils = require("ChromeUtils");
var { ActorPool, appendExtraActors } = require("devtools/server/actors/common");
-var { DebuggerServer } = require("devtools/server/main");
+var { actorRegistry } = require("devtools/server/actor-registry");
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
var { assert } = DevToolsUtils;
var { TabSources } = require("devtools/server/actors/utils/TabSources");
var makeDebugger = require("devtools/server/actors/utils/make-debugger");
const InspectorUtils = require("InspectorUtils");
const EXTENSION_CONTENT_JSM = "resource://gre/modules/ExtensionContent.jsm";
@@ -101,17 +101,17 @@ const browsingContextTargetPrototype = {
/**
* BrowsingContextTargetActor is an abstract class used by target actors that
* hold documents, such as frames, chrome windows, etc. The term "browsing
* context" is defined in the HTML spec as "an environment in which `Document`
* objects are presented to the user". In Gecko, this means a browsing context
* is a `docShell`.
*
* The main goal of this class is to expose the target-scoped actors being registered
- * via `DebuggerServer.registerModule` and manage their lifetimes. In addition, this
+ * via `actorRegistry.registerModule` and manage their lifetimes. In addition, this
* class also tracks the lifetime of the targeted browsing context.
*
* ### Main requests:
*
* `attach`/`detach` requests:
* - start/stop document watching:
* Starts watching for new documents and emits `tabNavigated` and
* `frameUpdate` over RDP.
@@ -482,17 +482,17 @@ const browsingContextTargetPrototype = {
// (created in createExtraActors) are not lost.
if (!this._targetScopedActorPool) {
this._targetScopedActorPool = new Pool(this.conn);
}
// Walk over target-scoped actor factories and make sure they are all
// instantiated and added into the ActorPool.
createExtraActors(
- DebuggerServer.targetScopedActorFactories,
+ actorRegistry.targetScopedActorFactories,
this._targetScopedActorPool,
this
);
this._appendExtraActors(response);
return response;
},
@@ -561,17 +561,17 @@ const browsingContextTargetPrototype = {
&& metadata["inner-window-id"]
&& metadata["inner-window-id"] == id) {
return true;
}
return false;
},
- /* Support for DebuggerServer.addTargetScopedActor. */
+ /* Support for actorRegistry.addTargetScopedActor. */
_appendExtraActors: appendExtraActors,
/**
* Does the actual work of attaching to a browsing context.
*/
_attach() {
if (this._attached) {
return;
--- a/devtools/server/actors/utils/actor-registry-utils.js
+++ b/devtools/server/actors/utils/actor-registry-utils.js
@@ -2,16 +2,17 @@
* 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";
const { Cu, CC } = require("chrome");
const { DebuggerServer } = require("devtools/server/main");
+const { actorRegistry } = require("devtools/server/actor-registry");
/**
* 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
@@ -33,25 +34,25 @@ exports.registerActorInCurrentProcess =
const sandbox = Cu.Sandbox(principal);
sandbox.exports = {};
sandbox.require = require;
Cu.evalInSandbox(sourceText, sandbox, "1.8", fileName, 1);
const { prefix, constructor, type } = options;
- if (type.global && !DebuggerServer.globalActorFactories.hasOwnProperty(prefix)) {
- DebuggerServer.addGlobalActor({
+ if (type.global && !actorRegistry.globalActorFactories.hasOwnProperty(prefix)) {
+ actorRegistry.addGlobalActor({
constructorName: constructor,
constructorFun: sandbox[constructor]
}, prefix);
}
- if (type.target && !DebuggerServer.targetScopedActorFactories.hasOwnProperty(prefix)) {
- DebuggerServer.addTargetScopedActor({
+ if (type.target && !actorRegistry.targetScopedActorFactories.hasOwnProperty(prefix)) {
+ actorRegistry.addTargetScopedActor({
constructorName: constructor,
constructorFun: sandbox[constructor]
}, prefix);
}
};
exports.unregisterActor = function(options) {
// Unregister in the current process
@@ -61,15 +62,15 @@ exports.unregisterActor = function(optio
module: "devtools/server/actors/utils/actor-registry-utils",
setupChild: "unregisterActorInCurrentProcess",
args: [options]
});
};
exports.unregisterActorInCurrentProcess = function(options) {
if (options.target) {
- DebuggerServer.removeTargetScopedActor(options);
+ actorRegistry.removeTargetScopedActor(options);
}
if (options.global) {
- DebuggerServer.removeGlobalActor(options);
+ actorRegistry.removeGlobalActor(options);
}
};
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -4,16 +4,17 @@
* 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";
var { Ci } = require("chrome");
var Services = require("Services");
var { DebuggerServer } = require("devtools/server/main");
+var { actorRegistry } = require("devtools/server/actor-registry");
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
loader.lazyRequireGetter(this, "RootActor", "devtools/server/actors/root", true);
loader.lazyRequireGetter(this, "FrameTargetActorProxy", "devtools/server/actors/targets/frame-proxy", true);
loader.lazyRequireGetter(this, "AddonTargetActor", "devtools/server/actors/targets/addon", true);
loader.lazyRequireGetter(this, "WebExtensionActor", "devtools/server/actors/addon/webextension", true);
loader.lazyRequireGetter(this, "WorkerTargetActorList", "devtools/server/actors/worker/worker-list", true);
loader.lazyRequireGetter(this, "ServiceWorkerRegistrationActorList", "devtools/server/actors/worker/worker-list", true);
@@ -73,17 +74,17 @@ exports.sendShutdownEvent = sendShutdown
exports.createRootActor = function createRootActor(connection) {
return new RootActor(connection, {
tabList: new BrowserTabList(connection),
addonList: new BrowserAddonList(connection),
workerList: new WorkerTargetActorList(connection, {}),
serviceWorkerRegistrationList:
new ServiceWorkerRegistrationActorList(connection),
processList: new ProcessActorList(),
- globalActorFactories: DebuggerServer.globalActorFactories,
+ globalActorFactories: actorRegistry.globalActorFactories,
onShutdown: sendShutdownEvent
});
};
/**
* A live list of FrameTargetActorProxys representing the current browser tabs,
* to be provided to the root actor to answer 'listTabs' requests.
*
--- a/devtools/server/main.js
+++ b/devtools/server/main.js
@@ -6,16 +6,17 @@
/**
* Toolkit glue for the remote debugging protocol, loaded into the
* debugging global.
*/
var { Ci, Cc } = require("chrome");
var Services = require("Services");
var { ActorPool, OriginalLocation } = require("devtools/server/actors/common");
+var { actorRegistry } = require("devtools/server/actor-registry");
var { LocalDebuggerTransport, ChildDebuggerTransport, WorkerDebuggerTransport } =
require("devtools/shared/transport/transport");
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
var { dumpn } = DevToolsUtils;
DevToolsUtils.defineLazyGetter(this, "DebuggerSocket", () => {
// eslint-disable-next-line no-shadow
const { DebuggerSocket } = require("devtools/shared/security/socket");
@@ -53,18 +54,16 @@ function loadSubScript(url) {
dump(errorStr);
reportError(errorStr);
throw e;
}
}
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
-var gRegisteredModules = Object.create(null);
-
/**
* Public API
*/
var DebuggerServer = {
_listeners: [],
_initialized: false,
// Flag to check if the content process server startup script was already loaded.
_contentProcessServerStartupScriptLoaded: false,
@@ -102,16 +101,17 @@ var DebuggerServer = {
* Initialize the debugger server.
*/
init() {
if (this.initialized) {
return;
}
this._connections = {};
+ actorRegistry._connections = this._connections;
this._nextConnID = 0;
this._initialized = true;
},
get protocol() {
return require("devtools/shared/protocol");
},
@@ -131,24 +131,19 @@ var DebuggerServer = {
if (!this._initialized) {
return;
}
for (const connID of Object.getOwnPropertyNames(this._connections)) {
this._connections[connID].close();
}
- for (const id of Object.getOwnPropertyNames(gRegisteredModules)) {
- this.unregisterModule(id);
- }
- gRegisteredModules = Object.create(null);
+ actorRegistry.destroy();
this.closeAllListeners();
- this.globalActorFactories = {};
- this.targetScopedActorFactories = {};
this._initialized = false;
dumpn("Debugger server is shut down.");
},
/**
* Raises an exception if the server has not been properly initialized.
*/
@@ -174,26 +169,26 @@ var DebuggerServer = {
* Registers all the parent process actors useful for debugging the
* runtime itself, like preferences and addons actors.
* @param target boolean
* Registers all the target-scoped actors like console, script, etc.
* for debugging a target context.
*/
registerActors({ root, browser, target }) {
if (browser) {
- this._addBrowserActors();
+ actorRegistry._addBrowserActors();
}
if (root) {
const { createRootActor } = require("devtools/server/actors/webbrowser");
this.setRootActor(createRootActor);
}
if (target) {
- this._addTargetScopedActors();
+ actorRegistry._addTargetScopedActors();
}
},
/**
* Register all possible actors for this DebuggerServer.
*/
registerAllActors() {
this.registerActors({ root: true, browser: true, target: true });
@@ -207,269 +202,16 @@ var DebuggerServer = {
* that implements a createRootActor() function to create the
* server's root actor.
*/
addActors(url) {
loadSubScript.call(this, url);
},
/**
- * Register a CommonJS module with the debugger server.
- * @param id string
- * The ID of a CommonJS module.
- * The actor is going to be registered immediately, but loaded only
- * when a client starts sending packets to an actor with the same id.
- *
- * @param options object
- * An object with 3 mandatory attributes:
- * - prefix (string):
- * The prefix of an actor is used to compute:
- * - the `actorID` of each new actor instance (ex: prefix1).
- * (See ActorPool.addActor)
- * - the actor name in the listTabs request. Sending a listTabs
- * request to the root actor returns actor IDs. IDs are in
- * dictionaries, with actor names as keys and actor IDs as values.
- * The actor name is the prefix to which the "Actor" string is
- * appended. So for an actor with the `console` prefix, the actor
- * name will be `consoleActor`.
- * - constructor (string):
- * the name of the exported symbol to be used as the actor
- * constructor.
- * - type (a dictionary of booleans with following attribute names):
- * - "global"
- * registers a global actor instance, if true.
- * A global actor has the root actor as its parent.
- * - "target"
- * registers a target-scoped actor instance, if true.
- * A new actor will be created for each target, such as a tab.
- */
- registerModule(id, options) {
- if (id in gRegisteredModules) {
- return;
- }
-
- if (!options) {
- throw new Error("DebuggerServer.registerModule requires an options argument");
- }
- const {prefix, constructor, type} = options;
- if (typeof (prefix) !== "string") {
- throw new Error(`Lazy actor definition for '${id}' requires a string ` +
- `'prefix' option.`);
- }
- if (typeof (constructor) !== "string") {
- throw new Error(`Lazy actor definition for '${id}' requires a string ` +
- `'constructor' option.`);
- }
- if (!("global" in type) && !("target" in type)) {
- throw new Error(`Lazy actor definition for '${id}' requires a dictionary ` +
- `'type' option whose attributes can be 'global' or 'target'.`);
- }
- const name = prefix + "Actor";
- const mod = {
- id,
- prefix,
- constructorName: constructor,
- type,
- globalActor: type.global,
- targetScopedActor: type.target
- };
- gRegisteredModules[id] = mod;
- if (mod.targetScopedActor) {
- this.addTargetScopedActor(mod, name);
- }
- if (mod.globalActor) {
- this.addGlobalActor(mod, name);
- }
- },
-
- /**
- * Returns true if a module id has been registered.
- */
- isModuleRegistered(id) {
- return (id in gRegisteredModules);
- },
-
- /**
- * Unregister a previously-loaded CommonJS module from the debugger server.
- */
- unregisterModule(id) {
- const mod = gRegisteredModules[id];
- if (!mod) {
- throw new Error("Tried to unregister a module that was not previously registered.");
- }
-
- // Lazy actors
- if (mod.targetScopedActor) {
- this.removeTargetScopedActor(mod);
- }
- if (mod.globalActor) {
- this.removeGlobalActor(mod);
- }
-
- delete gRegisteredModules[id];
- },
-
- /**
- * Install Firefox-specific actors.
- *
- * /!\ Be careful when adding a new actor, especially global actors.
- * Any new global actor will be exposed and returned by the root actor.
- */
- _addBrowserActors() {
- this.registerModule("devtools/server/actors/preference", {
- prefix: "preference",
- constructor: "PreferenceActor",
- type: { global: true }
- });
- this.registerModule("devtools/server/actors/actor-registry", {
- prefix: "actorRegistry",
- constructor: "ActorRegistryActor",
- type: { global: true }
- });
- this.registerModule("devtools/server/actors/addon/addons", {
- prefix: "addons",
- constructor: "AddonsActor",
- type: { global: true }
- });
- this.registerModule("devtools/server/actors/device", {
- prefix: "device",
- constructor: "DeviceActor",
- type: { global: true }
- });
- this.registerModule("devtools/server/actors/heap-snapshot-file", {
- prefix: "heapSnapshotFile",
- constructor: "HeapSnapshotFileActor",
- type: { global: true }
- });
- // Always register this as a global module, even while there is a pref turning
- // on and off the other performance actor. This actor shouldn't conflict with
- // the other one. These are also lazily loaded so there shouldn't be a performance
- // impact.
- this.registerModule("devtools/server/actors/perf", {
- prefix: "perf",
- constructor: "PerfActor",
- type: { global: true }
- });
- },
-
- /**
- * Install target-scoped actors.
- */
- _addTargetScopedActors() {
- this.registerModule("devtools/server/actors/webconsole", {
- prefix: "console",
- constructor: "WebConsoleActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/inspector/inspector", {
- prefix: "inspector",
- constructor: "InspectorActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/call-watcher", {
- prefix: "callWatcher",
- constructor: "CallWatcherActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/canvas", {
- prefix: "canvas",
- constructor: "CanvasActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/webgl", {
- prefix: "webgl",
- constructor: "WebGLActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/webaudio", {
- prefix: "webaudio",
- constructor: "WebAudioActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/stylesheets", {
- prefix: "styleSheets",
- constructor: "StyleSheetsActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/storage", {
- prefix: "storage",
- constructor: "StorageActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/gcli", {
- prefix: "gcli",
- constructor: "GcliActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/memory", {
- prefix: "memory",
- constructor: "MemoryActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/framerate", {
- prefix: "framerate",
- constructor: "FramerateActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/reflow", {
- prefix: "reflow",
- constructor: "ReflowActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/css-properties", {
- prefix: "cssProperties",
- constructor: "CssPropertiesActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/csscoverage", {
- prefix: "cssUsage",
- constructor: "CSSUsageActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/timeline", {
- prefix: "timeline",
- constructor: "TimelineActor",
- type: { target: true }
- });
- if ("nsIProfiler" in Ci &&
- !Services.prefs.getBoolPref("devtools.performance.new-panel-enabled", false)) {
- this.registerModule("devtools/server/actors/performance", {
- prefix: "performance",
- constructor: "PerformanceActor",
- type: { target: true }
- });
- }
- this.registerModule("devtools/server/actors/animation", {
- prefix: "animations",
- constructor: "AnimationsActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/promises", {
- prefix: "promises",
- constructor: "PromisesActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/emulation", {
- prefix: "emulation",
- constructor: "EmulationActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/addon/webextension-inspected-window", {
- prefix: "webExtensionInspectedWindow",
- constructor: "WebExtensionInspectedWindowActor",
- type: { target: true }
- });
- this.registerModule("devtools/server/actors/accessibility", {
- prefix: "accessibility",
- constructor: "AccessibilityActor",
- type: { target: true }
- });
- },
-
- /**
* Passes a set of options to the AddonTargetActors for the given ID.
*
* @param id string
* The ID of the add-on to pass the options to
* @param options object
* The options.
* @return a promise that will be resolved when complete.
*/
@@ -1139,156 +881,16 @@ var DebuggerServer = {
// DebuggerServer extension API.
setRootActor(actorFactory) {
this.createRootActor = actorFactory;
},
/**
- * Registers handlers for new target-scoped request types defined dynamically.
- *
- * Note that the name or actorPrefix of the request type is not allowed to clash with
- * existing protocol packet properties, like 'title', 'url' or 'actor', since that would
- * break the protocol.
- *
- * @param options object
- * - constructorName: (required)
- * name of actor constructor, which is also used when removing the actor.
- * One of the following:
- * - id:
- * module ID that contains the actor
- * - constructorFun:
- * a function to construct the actor
- * @param name string
- * The name of the new request type.
- */
- addTargetScopedActor(options, name) {
- if (!name) {
- throw Error("addTargetScopedActor requires the `name` argument");
- }
- if (["title", "url", "actor"].includes(name)) {
- throw Error(name + " is not allowed");
- }
- if (DebuggerServer.targetScopedActorFactories.hasOwnProperty(name)) {
- throw Error(name + " already exists");
- }
- DebuggerServer.targetScopedActorFactories[name] = { options, name };
- },
-
- /**
- * Unregisters the handler for the specified target-scoped request type.
- *
- * When unregistering an existing target-scoped actor, we remove the actor factory as
- * well as all existing instances of the actor.
- *
- * @param actor object, string
- * In case of object:
- * The `actor` object being given to related addTargetScopedActor call.
- * In case of string:
- * The `name` string being given to related addTargetScopedActor call.
- */
- removeTargetScopedActor(actorOrName) {
- let name;
- if (typeof actorOrName == "string") {
- name = actorOrName;
- } else {
- const actor = actorOrName;
- for (const factoryName in DebuggerServer.targetScopedActorFactories) {
- const handler = DebuggerServer.targetScopedActorFactories[factoryName];
- if ((handler.options.constructorName == actor.name) ||
- (handler.options.id == actor.id)) {
- name = factoryName;
- break;
- }
- }
- }
- if (!name) {
- return;
- }
- delete DebuggerServer.targetScopedActorFactories[name];
- for (const connID of Object.getOwnPropertyNames(this._connections)) {
- // DebuggerServerConnection in child process don't have rootActor
- if (this._connections[connID].rootActor) {
- this._connections[connID].rootActor.removeActorByName(name);
- }
- }
- },
-
- /**
- * Registers handlers for new browser-scoped request types defined dynamically.
- *
- * Note that the name or actorPrefix of the request type is not allowed to clash with
- * existing protocol packet properties, like 'from', 'tabs' or 'selected', since that
- * would break the protocol.
- *
- * @param options object
- * - constructorName: (required)
- * name of actor constructor, which is also used when removing the actor.
- * One of the following:
- * - id:
- * module ID that contains the actor
- * - constructorFun:
- * a function to construct the actor
- * @param name string
- * The name of the new request type.
- */
- addGlobalActor(options, name) {
- if (!name) {
- throw Error("addGlobalActor requires the `name` argument");
- }
- if (["from", "tabs", "selected"].includes(name)) {
- throw Error(name + " is not allowed");
- }
- if (DebuggerServer.globalActorFactories.hasOwnProperty(name)) {
- throw Error(name + " already exists");
- }
- DebuggerServer.globalActorFactories[name] = { options, name };
- },
-
- /**
- * Unregisters the handler for the specified browser-scoped request type.
- *
- * When unregistering an existing global actor, we remove the actor factory as well as
- * all existing instances of the actor.
- *
- * @param actor object, string
- * In case of object:
- * The `actor` object being given to related addGlobalActor call.
- * In case of string:
- * The `name` string being given to related addGlobalActor call.
- */
- removeGlobalActor(actorOrName) {
- let name;
- if (typeof actorOrName == "string") {
- name = actorOrName;
- } else {
- const actor = actorOrName;
- for (const factoryName in DebuggerServer.globalActorFactories) {
- const handler = DebuggerServer.globalActorFactories[factoryName];
- if ((handler.options.constructorName == actor.name) ||
- (handler.options.id == actor.id)) {
- name = factoryName;
- break;
- }
- }
- }
- if (!name) {
- return;
- }
- delete DebuggerServer.globalActorFactories[name];
- for (const connID of Object.getOwnPropertyNames(this._connections)) {
- // DebuggerServerConnection in child process don't have rootActor
- if (this._connections[connID].rootActor) {
- this._connections[connID].rootActor.removeActorByName(name);
- }
- }
- },
-
- /**
* Called when DevTools are unloaded to remove the contend process server startup script
* for the list of scripts loaded for each new content process. Will also remove message
* listeners from already loaded scripts.
*/
removeContentServerScript() {
Services.ppmm.removeDelayedProcessScript(CONTENT_PROCESS_SERVER_STARTUP_SCRIPT);
try {
Services.ppmm.broadcastAsyncMessage("debug:close-content-server");
--- a/devtools/server/moz.build
+++ b/devtools/server/moz.build
@@ -13,13 +13,14 @@ DIRS += [
'startup',
]
BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
DevToolsModules(
+ 'actor-registry.js',
'main.js',
)
with Files('**'):
BUG_COMPONENT = ('DevTools', 'General')
--- a/devtools/server/tests/unit/head_dbg.js
+++ b/devtools/server/tests/unit/head_dbg.js
@@ -28,16 +28,17 @@ const Services = require("Services");
// the output away anyway, unless you give it the --verbose flag.
Services.prefs.setBoolPref("devtools.debugger.log", true);
// Enable remote debugging for the relevant tests.
Services.prefs.setBoolPref("devtools.debugger.remote-enabled", true);
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { DebuggerServer } = require("devtools/server/main");
const { DebuggerServer: WorkerDebuggerServer } = worker.require("devtools/server/main");
+const { actorRegistry } = require("devtools/server/actor-registry");
const { DebuggerClient } = require("devtools/shared/client/debugger-client");
const ObjectClient = require("devtools/shared/client/object-client");
const { MemoryFront } = require("devtools/shared/fronts/memory");
const { addDebuggerToGlobal } = ChromeUtils.import("resource://gre/modules/jsdebugger.jsm", {});
const systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
.createInstance(Ci.nsIPrincipal);
@@ -71,17 +72,17 @@ function startupAddonsManager() {
* @returns `run_test` function
*/
function makeMemoryActorTest(testGeneratorFunction) {
const TEST_GLOBAL_NAME = "test_MemoryActor";
return function run_test() {
do_test_pending();
startTestDebuggerServer(TEST_GLOBAL_NAME).then(client => {
- DebuggerServer.registerModule("devtools/server/actors/heap-snapshot-file", {
+ actorRegistry.registerModule("devtools/server/actors/heap-snapshot-file", {
prefix: "heapSnapshotFile",
constructor: "HeapSnapshotFileActor",
type: { global: true }
});
getTestTab(client, TEST_GLOBAL_NAME, function(tabForm, rootForm) {
if (!tabForm || !rootForm) {
ok(false, "Could not attach to test tab: " + TEST_GLOBAL_NAME);
@@ -109,17 +110,17 @@ function makeMemoryActorTest(testGenerat
/**
* Save as makeMemoryActorTest but attaches the MemoryFront to the MemoryActor
* scoped to the full runtime rather than to a tab.
*/
function makeFullRuntimeMemoryActorTest(testGeneratorFunction) {
return function run_test() {
do_test_pending();
startTestDebuggerServer("test_MemoryActor").then(client => {
- DebuggerServer.registerModule("devtools/server/actors/heap-snapshot-file", {
+ actorRegistry.registerModule("devtools/server/actors/heap-snapshot-file", {
prefix: "heapSnapshotFile",
constructor: "HeapSnapshotFileActor",
type: { global: true }
});
getParentProcessActors(client).then(function(form) {
if (!form) {
ok(false, "Could not attach to chrome actors");
--- a/devtools/server/tests/unit/post_init_global_actors.js
+++ b/devtools/server/tests/unit/post_init_global_actors.js
@@ -14,12 +14,12 @@ PostInitGlobalActor.prototype = {
return { message: "pong" };
},
};
PostInitGlobalActor.prototype.requestTypes = {
"ping": PostInitGlobalActor.prototype.onPing,
};
-DebuggerServer.addGlobalActor({
+actorRegistry.addGlobalActor({
constructorName: "PostInitGlobalActor",
constructorFun: PostInitGlobalActor,
}, "postInitGlobalActor");
--- a/devtools/server/tests/unit/post_init_target_scoped_actors.js
+++ b/devtools/server/tests/unit/post_init_target_scoped_actors.js
@@ -14,12 +14,12 @@ PostInitTargetScopedActor.prototype = {
return { message: "pong" };
},
};
PostInitTargetScopedActor.prototype.requestTypes = {
"ping": PostInitTargetScopedActor.prototype.onPing,
};
-DebuggerServer.addTargetScopedActor({
+actorRegistry.addTargetScopedActor({
constructorName: "PostInitTargetScopedActor",
constructorFun: PostInitTargetScopedActor,
}, "postInitTargetScopedActor");
--- a/devtools/server/tests/unit/pre_init_global_actors.js
+++ b/devtools/server/tests/unit/pre_init_global_actors.js
@@ -1,25 +1,27 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
+const { actorRegistry } = require("devtools/server/actor-registry");
+
// Uses the same scope as test_add_actors.js
/* import-globals-from head_dbg.js */
function PreInitGlobalActor(connection) {}
PreInitGlobalActor.prototype = {
actorPrefix: "preInitGlobal",
onPing(request) {
return { message: "pong" };
},
};
PreInitGlobalActor.prototype.requestTypes = {
"ping": PreInitGlobalActor.prototype.onPing,
};
-DebuggerServer.addGlobalActor({
+actorRegistry.addGlobalActor({
constructorName: "PreInitGlobalActor",
constructorFun: PreInitGlobalActor,
}, "preInitGlobalActor");
--- a/devtools/server/tests/unit/pre_init_target_scoped_actors.js
+++ b/devtools/server/tests/unit/pre_init_target_scoped_actors.js
@@ -14,12 +14,12 @@ PreInitTargetScopedActor.prototype = {
return { message: "pong" };
},
};
PreInitTargetScopedActor.prototype.requestTypes = {
"ping": PreInitTargetScopedActor.prototype.onPing,
};
-DebuggerServer.addTargetScopedActor({
+actorRegistry.addTargetScopedActor({
constructorName: "PreInitTargetScopedActor",
constructorFun: PreInitTargetScopedActor,
}, "preInitTargetScopedActor");
--- a/devtools/server/tests/unit/test_MemoryActor_saveHeapSnapshot_03.js
+++ b/devtools/server/tests/unit/test_MemoryActor_saveHeapSnapshot_03.js
@@ -4,15 +4,17 @@
"use strict";
// Test that we can save full runtime heap snapshots when attached to the
// ParentProcessTargetActor or a ContentProcessTargetActor.
const { OS } = require("resource://gre/modules/osfile.jsm");
const run_test = makeFullRuntimeMemoryActorTest(async function(client, memoryFront) {
+ dump(`\nHIHIHH\n`);
const snapshotFilePath = await memoryFront.saveHeapSnapshot();
+ dump(`\nHIHIHH\n`);
ok(!!(await OS.File.stat(snapshotFilePath)),
"Should have the heap snapshot file");
const snapshot = ChromeUtils.readHeapSnapshot(snapshotFilePath);
ok(snapshot instanceof HeapSnapshot,
"And we should be able to read a HeapSnapshot instance from the file");
});
--- a/devtools/server/tests/unit/test_register_actor.js
+++ b/devtools/server/tests/unit/test_register_actor.js
@@ -1,44 +1,46 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
+const { actorRegistry } = require("devtools/server/actors/webconsole/actor-registry.js");
+
function run_test() {
// Allow incoming connections.
DebuggerServer.init();
DebuggerServer.registerAllActors();
add_test(test_lazy_api);
add_test(manual_remove);
add_test(cleanup);
run_next_test();
}
-// Bug 988237: Test the new lazy actor loading
+// Bug 988237: Test the new lazy actor actor-register
function test_lazy_api() {
let isActorLoaded = false;
let isActorInstantiated = false;
function onActorEvent(subject, topic, data) {
if (data == "loaded") {
isActorLoaded = true;
} else if (data == "instantiated") {
isActorInstantiated = true;
}
}
Services.obs.addObserver(onActorEvent, "actor");
- DebuggerServer.registerModule("xpcshell-test/registertestactors-lazy", {
+ actorRegistry.registerModule("xpcshell-test/registertestactors-lazy", {
prefix: "lazy",
constructor: "LazyActor",
type: { global: true, target: true }
});
// The actor is immediatly registered, but not loaded
- Assert.ok(DebuggerServer.targetScopedActorFactories.hasOwnProperty("lazyActor"));
- Assert.ok(DebuggerServer.globalActorFactories.hasOwnProperty("lazyActor"));
+ Assert.ok(actorRegistry.targetScopedActorFactories.hasOwnProperty("lazyActor"));
+ Assert.ok(actorRegistry.globalActorFactories.hasOwnProperty("lazyActor"));
Assert.ok(!isActorLoaded);
Assert.ok(!isActorInstantiated);
const client = new DebuggerClient(DebuggerServer.connectPipe());
client.connect().then(function onConnect() {
client.listTabs().then(onListTabs);
});
function onListTabs(response) {
@@ -60,25 +62,25 @@ function test_lazy_api() {
Assert.ok(isActorInstantiated);
Services.obs.removeObserver(onActorEvent, "actor");
client.close().then(() => run_next_test());
}
}
function manual_remove() {
- Assert.ok(DebuggerServer.globalActorFactories.hasOwnProperty("lazyActor"));
- DebuggerServer.removeGlobalActor("lazyActor");
- Assert.ok(!DebuggerServer.globalActorFactories.hasOwnProperty("lazyActor"));
+ Assert.ok(actorRegistry.globalActorFactories.hasOwnProperty("lazyActor"));
+ actorRegistry.removeGlobalActor("lazyActor");
+ Assert.ok(!actorRegistry.globalActorFactories.hasOwnProperty("lazyActor"));
run_next_test();
}
function cleanup() {
DebuggerServer.destroy();
// Check that all actors are unregistered on server destruction
- Assert.ok(!DebuggerServer.targetScopedActorFactories.hasOwnProperty("lazyActor"));
- Assert.ok(!DebuggerServer.globalActorFactories.hasOwnProperty("lazyActor"));
+ Assert.ok(!actorRegistry.targetScopedActorFactories.hasOwnProperty("lazyActor"));
+ Assert.ok(!actorRegistry.globalActorFactories.hasOwnProperty("lazyActor"));
run_next_test();
}
--- a/devtools/server/tests/unit/testactors.js
+++ b/devtools/server/tests/unit/testactors.js
@@ -3,16 +3,17 @@
"use strict";
const { appendExtraActors } = require("devtools/server/actors/common");
const { Pool, createExtraActors } = require("devtools/shared/protocol.js");
const { RootActor } = require("devtools/server/actors/root");
const { ThreadActor } = require("devtools/server/actors/thread");
const { DebuggerServer } = require("devtools/server/main");
+const { actorRegistry } = require("devtools/server/actor-registry");
const { TabSources } = require("devtools/server/actors/utils/TabSources");
const makeDebugger = require("devtools/server/actors/utils/make-debugger");
var gTestGlobals = [];
DebuggerServer.addTestGlobal = function(global) {
gTestGlobals.push(global);
};
@@ -32,17 +33,17 @@ DebuggerServer.getTestGlobal = function(
//
// As implemented now, we consult gTestGlobals when we're constructed, not
// when we're iterated over, so tests have to add their globals before the
// root actor is created.
function TestTabList(connection) {
this.conn = connection;
// An array of actors for each global added with
- // DebuggerServer.addTestGlobal.
+ // actorRegistry.addTestGlobal.
this._targetActors = [];
// A pool mapping those actors' names to the actors.
this._targetActorPool = new Pool(connection);
for (const global of gTestGlobals) {
const actor = new TestTargetActor(connection, global);
actor.selected = false;
@@ -59,17 +60,17 @@ TestTabList.prototype = {
getList: function() {
return Promise.resolve([...this._targetActors]);
}
};
exports.createRootActor = function createRootActor(connection) {
const root = new RootActor(connection, {
tabList: new TestTabList(connection),
- globalActorFactories: DebuggerServer.globalActorFactories,
+ globalActorFactories: actorRegistry.globalActorFactories,
});
root.applicationType = "xpcshell-tests";
return root;
};
function TestTargetActor(connection, global) {
this.conn = connection;
@@ -107,17 +108,17 @@ TestTargetActor.prototype = {
return this._sources;
},
form: function() {
const response = { actor: this.actorID, title: this._global.__name };
// Walk over target-scoped actors and add them to a new Pool.
const actorPool = new Pool(this.conn);
- createExtraActors(DebuggerServer.targetScopedActorFactories, actorPool, this);
+ createExtraActors(actorRegistry.targetScopedActorFactories, actorPool, this);
if (!actorPool.isEmpty()) {
this._targetActorPool = actorPool;
}
this._appendExtraActors(response);
return response;
},
@@ -148,17 +149,17 @@ TestTargetActor.prototype = {
removeActorByName: function(name) {
const actor = this._extraActors[name];
if (this._targetActorPool) {
this._targetActorPool.removeActor(actor);
}
delete this._extraActors[name];
},
- /* Support for DebuggerServer.addTargetScopedActor. */
+ /* Support for actorRegistry.addTargetScopedActor. */
_appendExtraActors: appendExtraActors
};
TestTargetActor.prototype.requestTypes = {
"attach": TestTargetActor.prototype.onAttach,
"detach": TestTargetActor.prototype.onDetach,
"reload": TestTargetActor.prototype.onReload
};
--- a/devtools/shared/security/tests/unit/testactors.js
+++ b/devtools/shared/security/tests/unit/testactors.js
@@ -2,16 +2,17 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { appendExtraActors } = require("devtools/server/actors/common");
const { Pool, createExtraActors } = require("devtools/shared/protocol.js");
const { RootActor } = require("devtools/server/actors/root");
const { ThreadActor } = require("devtools/server/actors/thread");
const { DebuggerServer } = require("devtools/server/main");
+const { actorRegistry } = require("devtools/server/actor-registry");
const promise = require("promise");
var gTestGlobals = [];
DebuggerServer.addTestGlobal = function(global) {
gTestGlobals.push(global);
};
// A mock tab list, for use by tests. This simply presents each global in
@@ -20,17 +21,17 @@ DebuggerServer.addTestGlobal = function(
//
// As implemented now, we consult gTestGlobals when we're constructed, not
// when we're iterated over, so tests have to add their globals before the
// root actor is created.
function TestTabList(connection) {
this.conn = connection;
// An array of actors for each global added with
- // DebuggerServer.addTestGlobal.
+ // actorRegistry.addTestGlobal.
this._targetActors = [];
// A pool mapping those actors' names to the actors.
this._targetActorPool = new Pool(connection);
for (const global of gTestGlobals) {
const actor = new TestTargetActor(connection, global);
actor.selected = false;
@@ -47,17 +48,17 @@ TestTabList.prototype = {
getList: function() {
return promise.resolve([...this._targetActors]);
}
};
exports.createRootActor = function createRootActor(connection) {
const root = new RootActor(connection, {
tabList: new TestTabList(connection),
- globalActorFactories: DebuggerServer.globalActorFactories
+ globalActorFactories: actorRegistry.globalActorFactories
});
root.applicationType = "xpcshell-tests";
return root;
};
function TestTargetActor(connection, global) {
this.conn = connection;
this._global = global;
@@ -79,17 +80,17 @@ TestTargetActor.prototype = {
return this._global.__name;
},
form: function() {
const response = { actor: this.actorID, title: this._global.__name };
// Walk over target-scoped actors and add them to a new Pool.
const actorPool = new Pool(this.conn);
- createExtraActors(DebuggerServer.targetScopedActorFactories, actorPool);
+ createExtraActors(actorRegistry.targetScopedActorFactories, actorPool);
if (!actorPool.isEmpty()) {
this._targetActorPool = actorPool;
}
this._appendExtraActors(response);
return response;
},
@@ -105,16 +106,16 @@ TestTargetActor.prototype = {
onDetach: function(request) {
if (!this._attached) {
return { "error": "wrongState" };
}
return { type: "detached" };
},
- /* Support for DebuggerServer.addTargetScopedActor. */
+ /* Support for actorRegistry.addTargetScopedActor. */
_appendExtraActors: appendExtraActors
};
TestTargetActor.prototype.requestTypes = {
"attach": TestTargetActor.prototype.onAttach,
"detach": TestTargetActor.prototype.onDetach
};
--- a/devtools/shared/transport/tests/unit/testactors-no-bulk.js
+++ b/devtools/shared/transport/tests/unit/testactors-no-bulk.js
@@ -1,20 +1,20 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { RootActor } = require("devtools/server/actors/root");
-const { DebuggerServer } = require("devtools/server/main");
+const { actorRegistry } = require("devtools/server/actor-registry");
/**
* Root actor that doesn't have the bulk trait.
*/
exports.createRootActor = function createRootActor(connection) {
const root = new RootActor(connection, {
- globalActorFactories: DebuggerServer.globalActorFactories
+ globalActorFactories: actorRegistry.globalActorFactories
});
root.applicationType = "xpcshell-tests";
root.traits = {
bulk: false
};
return root;
};
--- a/devtools/shared/transport/tests/unit/testactors.js
+++ b/devtools/shared/transport/tests/unit/testactors.js
@@ -2,16 +2,17 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { appendExtraActors } = require("devtools/server/actors/common");
const { Pool, createExtraActors } = require("devtools/shared/protocol.js");
const { RootActor } = require("devtools/server/actors/root");
const { ThreadActor } = require("devtools/server/actors/thread");
const { DebuggerServer } = require("devtools/server/main");
+const { actorRegistry } = require("devtools/server/actor-registry");
const promise = require("promise");
var gTestGlobals = [];
DebuggerServer.addTestGlobal = function(global) {
gTestGlobals.push(global);
};
// A mock tab list, for use by tests. This simply presents each global in
@@ -20,17 +21,17 @@ DebuggerServer.addTestGlobal = function(
//
// As implemented now, we consult gTestGlobals when we're constructed, not
// when we're iterated over, so tests have to add their globals before the
// root actor is created.
function TestTabList(connection) {
this.conn = connection;
// An array of actors for each global added with
- // DebuggerServer.addTestGlobal.
+ // actorRegistry.addTestGlobal.
this._targetActors = [];
// A pool mapping those actors' names to the actors.
this._targetActorPool = new Pool(connection);
for (const global of gTestGlobals) {
const actor = new TestTargetActor(connection, global);
actor.selected = false;
@@ -47,17 +48,17 @@ TestTabList.prototype = {
getList: function() {
return promise.resolve([...this._targetActors]);
}
};
exports.createRootActor = function createRootActor(connection) {
const root = new RootActor(connection, {
tabList: new TestTabList(connection),
- globalActorFactories: DebuggerServer.globalActorFactories
+ globalActorFactories: actorRegistry.globalActorFactories
});
root.applicationType = "xpcshell-tests";
return root;
};
function TestTargetActor(connection, global) {
this.conn = connection;
this._global = global;
@@ -79,17 +80,17 @@ TestTargetActor.prototype = {
return this._global.__name;
},
form: function() {
const response = { actor: this.actorID, title: this._global.__name };
// Walk over target-scoped actors and add them to a new Pool.
const actorPool = new Pool(this.conn);
- createExtraActors(DebuggerServer.targetScopedActorFactories, actorPool);
+ createExtraActors(actorRegistry.targetScopedActorFactories, actorPool);
if (!actorPool.isEmpty()) {
this._targetActorPool = actorPool;
}
this._appendExtraActors(response);
return response;
},
@@ -105,16 +106,16 @@ TestTargetActor.prototype = {
onDetach: function(request) {
if (!this._attached) {
return { "error": "wrongState" };
}
return { type: "detached" };
},
- /* Support for DebuggerServer.addTargetScopedActor. */
+ /* Support for actorRegistry.addTargetScopedActor. */
_appendExtraActors: appendExtraActors
};
TestTargetActor.prototype.requestTypes = {
"attach": TestTargetActor.prototype.onAttach,
"detach": TestTargetActor.prototype.onDetach
};
--- a/mobile/android/modules/dbg-browser-actors.js
+++ b/mobile/android/modules/dbg-browser-actors.js
@@ -6,36 +6,36 @@
/* eslint-env commonjs */
"use strict";
/**
* Fennec-specific actors.
*/
const { RootActor } = require("devtools/server/actors/root");
-const { DebuggerServer } = require("devtools/server/main");
+const { actorRegistry } = require("devtools/server/actor-registry");
const { BrowserTabList, BrowserAddonList, sendShutdownEvent } =
require("devtools/server/actors/webbrowser");
/**
* Construct a root actor appropriate for use in a server running in a
* browser on Android. The returned root actor:
- * - respects the factories registered with DebuggerServer.addGlobalActor,
+ * - respects the factories registered with actorRegistry.addGlobalActor,
* - uses a MobileTabList to supply tab actors,
* - sends all navigator:browser window documents a Debugger:Shutdown event
* when it exits.
*
* * @param aConnection DebuggerServerConnection
* The conection to the client.
*/
exports.createRootActor = function createRootActor(aConnection) {
let parameters = {
tabList: new MobileTabList(aConnection),
addonList: new BrowserAddonList(aConnection),
- globalActorFactories: DebuggerServer.globalActorFactories,
+ globalActorFactories: actorRegistry.globalActorFactories,
onShutdown: sendShutdownEvent
};
return new RootActor(aConnection, parameters);
};
/**
* A live list of BrowserTabActors representing the current browser tabs,
* to be provided to the root actor to answer 'listTabs' requests.
--- a/testing/xpcshell/dbg-actors.js
+++ b/testing/xpcshell/dbg-actors.js
@@ -8,31 +8,32 @@
/* eslint "mozilla/use-chromeutils-import": ["error", {allowCu: true}] */
// eslint-disable-next-line mozilla/no-define-cc-etc
const Cu = Components.utils; // eslint-disable-line mozilla/use-cc-etc
const { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
const { devtools } = Cu.import("resource://devtools/shared/Loader.jsm", {});
const { RootActor } = devtools.require("devtools/server/actors/root");
+const { actorRegistry } = devtools.require("devtools/server/actor-registry");
const { BrowserTabList } = devtools.require("devtools/server/actors/webbrowser");
/**
* xpcshell-test (XPCST) specific actors.
*
*/
/**
* Construct a root actor appropriate for use in a server running xpcshell
* tests. <snip boilerplate> :)
*/
function createRootActor(connection) {
let parameters = {
tabList: new XPCSTTabList(connection),
- globalActorFactories: DebuggerServer.globalActorFactories,
+ globalActorFactories: actorRegistry.globalActorFactories,
onShutdown() {
// If the user never switches to the "debugger" tab we might get a
// shutdown before we've attached.
Services.obs.notifyObservers(null, "xpcshell-test-devtools-shutdown");
}
};
return new RootActor(connection, parameters);
}