☠☠ backed out by 98b5ff9533ee ☠ ☠ | |
author | yulia <ystartsev@mozilla.com> |
Wed, 11 Jul 2018 18:34:22 +0200 | |
changeset 490587 | 7acc52a7f81f9a69eaa768ce13768cd7fb75b4da |
parent 490586 | 4e1e283b347e15910c87610577ffeba931de3eeb |
child 490588 | ce86ea60a31cc1944347306e28aaff1fbff38f59 |
push id | 1815 |
push user | ffxbld-merge |
push date | Mon, 15 Oct 2018 10:40:45 +0000 |
treeherder | mozilla-release@18d4c09e9378 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | ochameau |
bugs | 1473513 |
milestone | 63.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/devtools/server/actors/common.js +++ b/devtools/server/actors/common.js @@ -4,171 +4,16 @@ * 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 { method } = require("devtools/shared/protocol"); /** - * Creates "registered" actors factory meant for creating another kind of - * factories, ObservedActorFactory, during the call to listTabs. - * These factories live in DebuggerServer.{tab|global}ActorFactories. - * - * These actors only exposes: - * - `name` string attribute used to match actors by constructor name - * in DebuggerServer.remove{Global,Tab}Actor. - * - `createObservedActorFactory` function to create "observed" actors factory - * - * @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 - */ -function RegisteredActorFactory(options, prefix) { - // By default the actor name will also be used for the actorID prefix. - this._prefix = prefix; - if (options.constructorFun) { - // Actor definition registered by ActorRegistryActor or testing helpers - this._getConstructor = () => options.constructorFun; - } else { - // Lazy actor definition, where options contains all the information - // required to load the actor lazily. - this._getConstructor = function() { - // Load the module - let mod; - try { - mod = require(options.id); - } catch (e) { - throw new Error("Unable to load actor module '" + options.id + "'.\n" + - e.message + "\n" + e.stack + "\n"); - } - // Fetch the actor constructor - const c = mod[options.constructorName]; - if (!c) { - throw new Error("Unable to find actor constructor named '" + - options.constructorName + "'. (Is it exported?)"); - } - return c; - }; - } - // Exposes `name` attribute in order to allow removeXXXActor to match - // the actor by its actor constructor name. - this.name = options.constructorName; -} -RegisteredActorFactory.prototype.createObservedActorFactory = function(conn, - parentActor) { - return new ObservedActorFactory(this._getConstructor, this._prefix, conn, parentActor); -}; -exports.RegisteredActorFactory = RegisteredActorFactory; - -/** - * Creates "observed" actors factory meant for creating real actor instances. - * These factories lives in actor pools and fake various actor attributes. - * They will be replaced in actor pools by final actor instances during - * the first request for the same actorID from DebuggerServer._getOrCreateActor. - * - * ObservedActorFactory fakes the following actors attributes: - * actorPrefix (string) Used by ActorPool.addActor to compute the actor id - * actorID (string) Set by ActorPool.addActor just after being instantiated - * registeredPool (object) Set by ActorPool.addActor just after being - * instantiated - * And exposes the following method: - * createActor (function) Instantiate an actor that is going to replace - * this factory in the actor pool. - */ -function ObservedActorFactory(getConstructor, prefix, conn, parentActor) { - this._getConstructor = getConstructor; - this._conn = conn; - this._parentActor = parentActor; - - this.actorPrefix = prefix; - - this.actorID = null; - this.registeredPool = null; -} -ObservedActorFactory.prototype.createActor = function() { - // Fetch the actor constructor - const C = this._getConstructor(); - // Instantiate a new actor instance - const instance = new C(this._conn, this._parentActor); - instance.conn = this._conn; - instance.parentID = this._parentActor.actorID; - // We want the newly-constructed actor to completely replace the factory - // actor. Reusing the existing actor ID will make sure ActorPool.addActor - // does the right thing. - instance.actorID = this.actorID; - this.registeredPool.addActor(instance); - return instance; -}; -exports.ObservedActorFactory = ObservedActorFactory; - -/* - * Methods shared between RootActor and BrowsingContextTargetActor. - */ - -/** - * Populate |this._extraActors| as specified by |factories|, reusing whatever - * actors are already there. Add all actors in the final extra actors table to - * |pool|. - * - * The root actor and the target actor use this to instantiate actors that other - * parts of the browser have specified with DebuggerServer.addTargetScopedActor and - * DebuggerServer.addGlobalActor. - * - * @param factories - * An object whose own property names are the names of properties to add to - * some reply packet (say, a target actor grip or the "listTabs" response - * form), and whose own property values are actor constructor functions, as - * documented for addTargetScopedActor and addGlobalActor. - * - * @param this - * The RootActor or BrowsingContextTargetActor with which the new actors - * will be associated. It should support whatever API the |factories| - * constructor functions might be interested in, as it is passed to them. - * For the sake of CommonCreateExtraActors itself, it should have at least - * the following properties: - * - * - _extraActors - * An object whose own property names are factory table (and packet) - * property names, and whose values are no-argument actor constructors, - * of the sort that one can add to an ActorPool. - * - * - conn - * The DebuggerServerConnection in which the new actors will participate. - * - * - actorID - * The actor's name, for use as the new actors' parentID. - */ -exports.createExtraActors = function createExtraActors(factories, pool) { - // Walk over global actors added by extensions. - for (const name in factories) { - let actor = this._extraActors[name]; - if (!actor) { - // Register another factory, but this time specific to this connection. - // It creates a fake actor that looks like an regular actor in the pool, - // but without actually instantiating the actor. - // It will only be instantiated on the first request made to the actor. - actor = factories[name].createObservedActorFactory(this.conn, this); - this._extraActors[name] = actor; - } - - // If the actor already exists in the pool, it may have been instantiated, - // so make sure not to overwrite it by a non-instantiated version. - if (!pool.has(actor.actorID)) { - pool.addActor(actor); - } - } -}; - -/** * Append the extra actors in |this._extraActors|, constructed by a prior call * to CommonCreateExtraActors, to |object|. * * @param object * The object to which the extra actors should be added, under the * property names given in the |factories| table passed to * CommonCreateExtraActors. *
--- a/devtools/server/actors/root.js +++ b/devtools/server/actors/root.js @@ -3,18 +3,19 @@ /* 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"; const { Cu } = require("chrome"); const Services = require("Services"); -const { ActorPool, appendExtraActors, createExtraActors } = require("devtools/server/actors/common"); +const { ActorPool, appendExtraActors } = require("devtools/server/actors/common"); const { Pool } = require("devtools/shared/protocol"); +const { LazyPool, createExtraActors } = require("devtools/shared/protocol/lazy-pool"); const { DebuggerServer } = require("devtools/server/main"); loader.lazyRequireGetter(this, "ChromeWindowTargetActor", "devtools/server/actors/targets/chrome-window", true); /* Root actor for the remote debugging protocol. */ /** @@ -95,18 +96,17 @@ function RootActor(connection, parameter this._onTabListChanged = this.onTabListChanged.bind(this); this._onAddonListChanged = this.onAddonListChanged.bind(this); this._onWorkerListChanged = this.onWorkerListChanged.bind(this); this._onServiceWorkerRegistrationListChanged = this.onServiceWorkerRegistrationListChanged.bind(this); this._onProcessListChanged = this.onProcessListChanged.bind(this); this._extraActors = {}; - this._globalActorPool = new ActorPool(this.conn); - this.conn.addActorPool(this._globalActorPool); + this._globalActorPool = new LazyPool(this.conn); this._parentProcessTargetActor = null; this._processActors = new Map(); } RootActor.prototype = { constructor: RootActor, applicationType: "browser", @@ -235,20 +235,19 @@ RootActor.prototype = { */ onGetRoot: function() { const reply = { from: this.actorID, }; // Create global actors if (!this._globalActorPool) { - this._globalActorPool = new ActorPool(this.conn); - this.conn.addActorPool(this._globalActorPool); + this._globalActorPool = new LazyPool(this.conn); } - this._createExtraActors(this._parameters.globalActorFactories, this._globalActorPool); + createExtraActors(this._parameters.globalActorFactories, this._globalActorPool, this); // List the global actors this._appendExtraActors(reply); return reply; }, /* The 'listTabs' request and the 'tabListChanged' notification. */ @@ -514,25 +513,25 @@ RootActor.prototype = { return { error: "wrongParameter", message: "getProcess requires a valid `id` attribute." }; } // If the request doesn't contains id parameter or id is 0 // (id == 0, based on onListProcesses implementation) if ((!("id" in request)) || request.id === 0) { if (this._parentProcessTargetActor && (!this._parentProcessTargetActor.docShell || this._parentProcessTargetActor.docShell.isBeingDestroyed)) { - this._globalActorPool.removeActor(this._parentProcessTargetActor); + this._parentProcessTargetActor.destroy(); this._parentProcessTargetActor = null; } if (!this._parentProcessTargetActor) { // Create a ParentProcessTargetActor for the parent process const { ParentProcessTargetActor } = require("devtools/server/actors/targets/parent-process"); this._parentProcessTargetActor = new ParentProcessTargetActor(this.conn); - this._globalActorPool.addActor(this._parentProcessTargetActor); + this._globalActorPool.manage(this._parentProcessTargetActor); } return { form: this._parentProcessTargetActor.form() }; } const { id } = request; const mm = Services.ppmm.getChildAt(id); if (!mm) { @@ -560,28 +559,27 @@ RootActor.prototype = { return Cu.cloneInto(request, {}); }, onProtocolDescription: function() { return require("devtools/shared/protocol").dumpProtocolSpec(); }, /* Support for DebuggerServer.addGlobalActor. */ - _createExtraActors: createExtraActors, _appendExtraActors: appendExtraActors, /** * Remove the extra actor (added by DebuggerServer.addGlobalActor or * DebuggerServer.addTargetScopedActor) name |name|. */ removeActorByName: function(name) { if (name in this._extraActors) { const actor = this._extraActors[name]; - if (this._globalActorPool.has(actor)) { - this._globalActorPool.removeActor(actor); + if (this._globalActorPool.has(actor.actorID)) { + actor.destroy(); } if (this._tabTargetActorPool) { // Iterate over BrowsingContextTargetActor instances to also remove target-scoped // actors created during listTabs for each document. this._tabTargetActorPool.forEach(tab => { tab.removeActorByName(name); }); }
--- a/devtools/server/actors/targets/browsing-context.js +++ b/devtools/server/actors/targets/browsing-context.js @@ -18,35 +18,34 @@ * For performance matters, this file should only be loaded in the targeted context's * 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, createExtraActors, appendExtraActors -} = require("devtools/server/actors/common"); +var { appendExtraActors } = require("devtools/server/actors/common"); var { DebuggerServer } = require("devtools/server/main"); 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 Debugger = require("Debugger"); const ReplayDebugger = require("devtools/server/actors/replay/debugger"); const InspectorUtils = require("InspectorUtils"); const EXTENSION_CONTENT_JSM = "resource://gre/modules/ExtensionContent.jsm"; const { LocalizationHelper } = require("devtools/shared/l10n"); const STRINGS_URI = "devtools/shared/locales/browsing-context.properties"; const L10N = new LocalizationHelper(STRINGS_URI); const { ActorClassWithSpec, Actor, Pool } = require("devtools/shared/protocol"); +const { LazyPool, createExtraActors } = require("devtools/shared/protocol/lazy-pool"); const { browsingContextTargetSpec } = require("devtools/shared/specs/targets/browsing-context"); loader.lazyRequireGetter(this, "ThreadActor", "devtools/server/actors/thread", true); loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/thread", true); loader.lazyRequireGetter(this, "WorkerTargetActorList", "devtools/server/actors/worker/worker-list", true); loader.lazyImporter(this, "ExtensionContent", EXTENSION_CONTENT_JSM); loader.lazyRequireGetter(this, "StyleSheetActor", "devtools/server/actors/stylesheets", true); @@ -475,26 +474,28 @@ const browsingContextTargetPrototype = { response.title = this.title; response.url = this.url; response.outerWindowID = this.outerWindowID; } // Always use the same ActorPool, so existing actor instances // (created in createExtraActors) are not lost. if (!this._targetScopedActorPool) { - this._targetScopedActorPool = new ActorPool(this.conn); - this.conn.addActorPool(this._targetScopedActorPool); + this._targetScopedActorPool = new LazyPool(this.conn); } // Walk over target-scoped actor factories and make sure they are all // instantiated and added into the ActorPool. - this._createExtraActors(DebuggerServer.targetScopedActorFactories, - this._targetScopedActorPool); + const addedActors = createExtraActors( + DebuggerServer.targetScopedActorFactories, + this._targetScopedActorPool, + this + ); - this._appendExtraActors(response); + Object.assign(response, addedActors); return response; }, /** * Called when the actor is removed from the connection. */ destroy() { Actor.prototype.destroy.call(this); @@ -559,17 +560,16 @@ const browsingContextTargetPrototype = { && metadata["inner-window-id"] == id) { return true; } return false; }, /* Support for DebuggerServer.addTargetScopedActor. */ - _createExtraActors: createExtraActors, _appendExtraActors: appendExtraActors, /** * Does the actual work of attaching to a browsing context. */ _attach() { if (this._attached) { return; @@ -888,17 +888,17 @@ const browsingContextTargetPrototype = { Services.obs.removeObserver(this, "webnavigation-destroy"); } this._destroyThreadActor(); // Shut down actors that belong to this target's pool. this._styleSheetActors.clear(); if (this._targetScopedActorPool) { - this.conn.removeActorPool(this._targetScopedActorPool); + this._targetScopedActorPool.destroy(); this._targetScopedActorPool = null; } // Make sure that no more workerListChanged notifications are sent. if (this._workerTargetActorList !== null) { this._workerTargetActorList.onListChanged = null; this._workerTargetActorList = null; } @@ -1415,31 +1415,31 @@ const browsingContextTargetPrototype = { createStyleSheetActor(styleSheet) { assert(!this.exited, "Target must not be exited to create a sheet actor."); if (this._styleSheetActors.has(styleSheet)) { return this._styleSheetActors.get(styleSheet); } const actor = new StyleSheetActor(styleSheet, this); this._styleSheetActors.set(styleSheet, actor); - this._targetScopedActorPool.addActor(actor); + this._targetScopedActorPool.manage(actor); this.emit("stylesheet-added", actor); return actor; }, removeActorByName(name) { if (name in this._extraActors) { const actor = this._extraActors[name]; if (this._targetScopedActorPool.has(actor)) { this._targetScopedActorPool.removeActor(actor); } delete this._extraActors[name]; } - }, + } }; exports.browsingContextTargetPrototype = browsingContextTargetPrototype; exports.BrowsingContextTargetActor = ActorClassWithSpec(browsingContextTargetSpec, browsingContextTargetPrototype); /** * The DebuggerProgressListener object is an nsIWebProgressListener which
--- a/devtools/server/main.js +++ b/devtools/server/main.js @@ -5,18 +5,17 @@ "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, RegisteredActorFactory, - ObservedActorFactory } = require("devtools/server/actors/common"); +var { ActorPool } = require("devtools/server/actors/common"); var DevToolsUtils = require("devtools/shared/DevToolsUtils"); var { dumpn } = DevToolsUtils; loader.lazyRequireGetter(this, "DebuggerSocket", "devtools/shared/security/socket", true); loader.lazyRequireGetter(this, "Authentication", "devtools/shared/security/auth"); loader.lazyRequireGetter(this, "LocalDebuggerTransport", "devtools/shared/transport/local-transport", true); loader.lazyRequireGetter(this, "ChildDebuggerTransport", "devtools/shared/transport/child-transport", true); loader.lazyRequireGetter(this, "WorkerThreadWorkerDebuggerTransport", "devtools/shared/transport/worker-transport", true); @@ -1160,39 +1159,38 @@ var DebuggerServer = { /** * 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 actor object + * @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(actor, name) { + 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] = - new RegisteredActorFactory(actor, name); + 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. * @@ -1205,18 +1203,18 @@ var DebuggerServer = { 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.name && handler.name == actor.name) || - (handler.id && handler.id == actor.id)) { + if ((handler.options.constructorName == actor.name) || + (handler.options.id == actor.id)) { name = factoryName; break; } } } if (!name) { return; } @@ -1231,38 +1229,38 @@ var DebuggerServer = { /** * 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 actor object + * @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(actor, name) { + 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] = new RegisteredActorFactory(actor, name); + 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. * @@ -1275,18 +1273,18 @@ var DebuggerServer = { 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.name && handler.name == actor.name) || - (handler.id && handler.id == actor.id)) { + if ((handler.options.constructorName == actor.name) || + (handler.options.id == actor.id)) { name = factoryName; break; } } } if (!name) { return; } @@ -1516,40 +1514,37 @@ DebuggerServerConnection.prototype = { if (actorID === "root") { return this.rootActor; } return null; }, _getOrCreateActor(actorID) { - let actor = this.getActor(actorID); - if (!actor) { - this.transport.send({ from: actorID ? actorID : "root", - error: "noSuchActor", - message: "No such actor for ID: " + actorID }); - return null; - } + 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; + } - // Dynamically-loaded actors have to be created lazily. - if (actor instanceof ObservedActorFactory) { - try { - actor = actor.createActor(); - } catch (error) { - const prefix = "Error occurred while creating actor '" + actor.name; - this.transport.send(this._unknownError(actorID, prefix, error)); + if (typeof (actor) !== "object") { + // ActorPools should now contain only actor instances (i.e. objects) + throw new Error("Unexpected actor constructor/function in ActorPool " + + "for actorID=" + actorID + "."); } - } else 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 actor; + return null; }, poolFor(actorID) { for (const pool of this._extraPools) { if (pool.has(actorID)) { return pool; } }
--- a/devtools/server/tests/unit/test_promises_actor_attach.js +++ b/devtools/server/tests/unit/test_promises_actor_attach.js @@ -17,19 +17,18 @@ add_task(async function() { // We have to attach the chrome target actor before playing with the PromiseActor await attachTab(client, parentProcessActors); await testAttach(client, parentProcessActors); const response = await listTabs(client); const targetTab = findTab(response.tabs, "promises-actor-test"); ok(targetTab, "Found our target tab."); - const [ tabResponse ] = await attachTab(client, targetTab); - - await testAttach(client, tabResponse); + await attachTab(client, targetTab); + await testAttach(client, targetTab); await close(client); }); async function testAttach(client, parent) { const promises = PromisesFront(client, parent); try {
--- a/devtools/server/tests/unit/test_promises_actor_exist.js +++ b/devtools/server/tests/unit/test_promises_actor_exist.js @@ -19,18 +19,16 @@ add_task(async function() { // Attach to the BrowsingContextTargetActor and check the response await new Promise(resolve => { client.request({ to: targetTab.actor, type: "attach" }, response => { Assert.ok(!("error" in response), "Expect no error in response."); Assert.equal(response.from, targetTab.actor, "Expect the target BrowsingContextTargetActor in response form field."); Assert.equal(response.type, "tabAttached", "Expect tabAttached in the response type."); - Assert.ok(typeof response.promisesActor === "string", - "Should have a tab context PromisesActor."); resolve(); }); }); const parentProcessActors = await getParentProcessActors(client); Assert.ok(typeof parentProcessActors.promisesActor === "string", "Should have a chrome context PromisesActor."); });
--- a/devtools/server/tests/unit/testactors.js +++ b/devtools/server/tests/unit/testactors.js @@ -1,14 +1,15 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -const { ActorPool, appendExtraActors, createExtraActors } = require("devtools/server/actors/common"); +const { appendExtraActors } = require("devtools/server/actors/common"); +const { LazyPool, createExtraActors } = require("devtools/shared/protocol/lazy-pool"); const { RootActor } = require("devtools/server/actors/root"); const { ThreadActor } = require("devtools/server/actors/thread"); const { DebuggerServer } = require("devtools/server/main"); const { TabSources } = require("devtools/server/actors/utils/TabSources"); const makeDebugger = require("devtools/server/actors/utils/make-debugger"); var gTestGlobals = []; DebuggerServer.addTestGlobal = function(global) { @@ -35,29 +36,27 @@ DebuggerServer.getTestGlobal = function( function TestTabList(connection) { this.conn = connection; // An array of actors for each global added with // DebuggerServer.addTestGlobal. this._targetActors = []; // A pool mapping those actors' names to the actors. - this._targetActorPool = new ActorPool(connection); + this._targetActorPool = new LazyPool(connection); for (const global of gTestGlobals) { const actor = new TestTargetActor(connection, global); actor.selected = false; this._targetActors.push(actor); - this._targetActorPool.addActor(actor); + this._targetActorPool.manage(actor); } if (this._targetActors.length > 0) { this._targetActors[0].selected = true; } - - connection.addActorPool(this._targetActorPool); } TestTabList.prototype = { constructor: TestTabList, getList: function() { return Promise.resolve([...this._targetActors]); } }; @@ -106,36 +105,35 @@ TestTargetActor.prototype = { this._sources = new TabSources(this.threadActor); } 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 ActorPool. - const actorPool = new ActorPool(this.conn); - this._createExtraActors(DebuggerServer.targetScopedActorFactories, actorPool); + // Walk over target-scoped actors and add them to a new LazyPool. + const actorPool = new LazyPool(this.conn); + const actors = createExtraActors( + DebuggerServer.targetScopedActorFactories, + actorPool, + this + ); if (!actorPool.isEmpty()) { this._targetActorPool = actorPool; this.conn.addActorPool(this._targetActorPool); } - this._appendExtraActors(response); - - return response; + return { ...response, ...actors }; }, onAttach: function(request) { this._attached = true; - const response = { type: "tabAttached", threadActor: this.threadActor.actorID }; - this._appendExtraActors(response); - - return response; + return { type: "tabAttached", threadActor: this.threadActor.actorID }; }, onDetach: function(request) { if (!this._attached) { return { "error": "wrongState" }; } return { type: "detached" }; }, @@ -151,17 +149,16 @@ TestTargetActor.prototype = { const actor = this._extraActors[name]; if (this._targetActorPool) { this._targetActorPool.removeActor(actor); } delete this._extraActors[name]; }, /* Support for DebuggerServer.addTargetScopedActor. */ - _createExtraActors: createExtraActors, _appendExtraActors: appendExtraActors }; TestTargetActor.prototype.requestTypes = { "attach": TestTargetActor.prototype.onAttach, "detach": TestTargetActor.prototype.onDetach, "reload": TestTargetActor.prototype.onReload };
--- a/devtools/shared/moz.build +++ b/devtools/shared/moz.build @@ -18,16 +18,17 @@ DIRS += [ 'heapsnapshot', 'inspector', 'jsbeautify', 'layout', 'locales', 'node-properties', 'performance', 'platform', + 'protocol', 'pretty-fast', 'qrcode', 'security', 'sourcemap', 'sprintfjs', 'specs', 'transport', 'webconsole',
--- a/devtools/shared/protocol.js +++ b/devtools/shared/protocol.js @@ -821,16 +821,20 @@ var Pool = function(conn) { Pool.prototype = extend(EventEmitter.prototype, { /** * Return the parent pool for this client. */ parent: function() { return this.conn.poolFor(this.actorID); }, + poolFor: function(actorID) { + return this.conn.poolFor(actorID); + }, + /** * Override this if you want actors returned by this actor * to belong to a different actor by default. */ marshallPool: function() { return this; }, @@ -853,17 +857,19 @@ Pool.prototype = extend(EventEmitter.pro * Add an actor as a child of this pool. */ manage: function(actor) { if (!actor.actorID) { actor.actorID = this.conn.allocID(actor.actorPrefix || actor.typeName); } // If the actor is already in a pool, remove it without destroying it. - const parent = actor.parent(); + // TODO: not all actors have been moved to protocol.js, so they do not all have + // a parent field. Remove the check for the parent once the conversion is finished + const parent = this.poolFor(actor.actorID); if (parent) { parent.unmanage(actor); } this._poolMap.set(actor.actorID, actor); return actor; }, @@ -876,23 +882,29 @@ Pool.prototype = extend(EventEmitter.pro // true if the given actor ID exists in the pool. has: function(actorID) { return this.__poolMap && this._poolMap.has(actorID); }, // The actor for a given actor id stored in this pool actor: function(actorID) { - return this.__poolMap ? this._poolMap.get(actorID) : null; + if (this.__poolMap) { + return this._poolMap.get(actorID); + } + return null; }, // Same as actor, should update debugger connection to use 'actor' // and then remove this. get: function(actorID) { - return this.__poolMap ? this._poolMap.get(actorID) : null; + if (this.__poolMap) { + return this._poolMap.get(actorID); + } + return null; }, // True if this pool has no children. isEmpty: function() { return !this.__poolMap || this._poolMap.size == 0; }, // Generator that yields each non-self child of the pool.
copy from devtools/server/actors/common.js copy to devtools/shared/protocol/lazy-pool.js --- a/devtools/server/actors/common.js +++ b/devtools/shared/protocol/lazy-pool.js @@ -1,505 +1,231 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; -const { method } = require("devtools/shared/protocol"); +const { extend } = require("devtools/shared/extend"); +const { Pool } = require("devtools/shared/protocol"); /** - * Creates "registered" actors factory meant for creating another kind of - * factories, ObservedActorFactory, during the call to listTabs. - * These factories live in DebuggerServer.{tab|global}ActorFactories. - * - * These actors only exposes: - * - `name` string attribute used to match actors by constructor name - * in DebuggerServer.remove{Global,Tab}Actor. - * - `createObservedActorFactory` function to create "observed" actors factory + * A Special Pool for RootActor and BrowsingContextTargetActor, which allows lazy loaded + * actors to be added to the pool. * - * @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 + * Like the Pool, this is a protocol object that can manage the lifetime of other protocol + * objects. Pools are used on both sides of the connection to help coordinate lifetimes. + * + * @param conn + * Is a DebuggerServerConnection. Must have + * addActorPool, removeActorPool, and poolFor. + * @constructor */ -function RegisteredActorFactory(options, prefix) { - // By default the actor name will also be used for the actorID prefix. - this._prefix = prefix; - if (options.constructorFun) { - // Actor definition registered by ActorRegistryActor or testing helpers - this._getConstructor = () => options.constructorFun; - } else { - // Lazy actor definition, where options contains all the information - // required to load the actor lazily. - this._getConstructor = function() { - // Load the module - let mod; - try { - mod = require(options.id); - } catch (e) { - throw new Error("Unable to load actor module '" + options.id + "'.\n" + - e.message + "\n" + e.stack + "\n"); +function LazyPool(conn) { + this.conn = conn; +} + +LazyPool.prototype = extend(Pool.prototype, { + // The actor for a given actor id stored in this pool + actor: function(actorID) { + if (this.__poolMap) { + const entry = this._poolMap.get(actorID); + if (entry instanceof LazyActor) { + return entry.createActor(); } - // Fetch the actor constructor - const c = mod[options.constructorName]; - if (!c) { - throw new Error("Unable to find actor constructor named '" + - options.constructorName + "'. (Is it exported?)"); - } - return c; - }; - } - // Exposes `name` attribute in order to allow removeXXXActor to match - // the actor by its actor constructor name. - this.name = options.constructorName; -} -RegisteredActorFactory.prototype.createObservedActorFactory = function(conn, - parentActor) { - return new ObservedActorFactory(this._getConstructor, this._prefix, conn, parentActor); -}; -exports.RegisteredActorFactory = RegisteredActorFactory; + return entry; + } + return null; + }, + + // Same as actor, should update debugger connection to use 'actor' + // and then remove this. + get: function(actorID) { + return this.actor(actorID); + }, +}); + +exports.LazyPool = LazyPool; /** - * Creates "observed" actors factory meant for creating real actor instances. - * These factories lives in actor pools and fake various actor attributes. - * They will be replaced in actor pools by final actor instances during - * the first request for the same actorID from DebuggerServer._getOrCreateActor. + * Populate |parent._extraActors| as specified by |registeredActors|, reusing whatever + * actors are already there. Add all actors in the final extra actors table to + * |pool|. _extraActors is treated as a cache for lazy actors * - * ObservedActorFactory fakes the following actors attributes: - * actorPrefix (string) Used by ActorPool.addActor to compute the actor id - * actorID (string) Set by ActorPool.addActor just after being instantiated - * registeredPool (object) Set by ActorPool.addActor just after being - * instantiated - * And exposes the following method: - * createActor (function) Instantiate an actor that is going to replace - * this factory in the actor pool. - */ -function ObservedActorFactory(getConstructor, prefix, conn, parentActor) { - this._getConstructor = getConstructor; - this._conn = conn; - this._parentActor = parentActor; - - this.actorPrefix = prefix; - - this.actorID = null; - this.registeredPool = null; -} -ObservedActorFactory.prototype.createActor = function() { - // Fetch the actor constructor - const C = this._getConstructor(); - // Instantiate a new actor instance - const instance = new C(this._conn, this._parentActor); - instance.conn = this._conn; - instance.parentID = this._parentActor.actorID; - // We want the newly-constructed actor to completely replace the factory - // actor. Reusing the existing actor ID will make sure ActorPool.addActor - // does the right thing. - instance.actorID = this.actorID; - this.registeredPool.addActor(instance); - return instance; -}; -exports.ObservedActorFactory = ObservedActorFactory; - -/* - * Methods shared between RootActor and BrowsingContextTargetActor. - */ - -/** - * Populate |this._extraActors| as specified by |factories|, reusing whatever - * actors are already there. Add all actors in the final extra actors table to - * |pool|. - * - * The root actor and the target actor use this to instantiate actors that other - * parts of the browser have specified with DebuggerServer.addTargetScopedActor and - * DebuggerServer.addGlobalActor. + * The target actor uses this to instantiate actors that other + * parts of the browser have specified with DebuggerServer.addTargetScopedActor * * @param factories * An object whose own property names are the names of properties to add to * some reply packet (say, a target actor grip or the "listTabs" response * form), and whose own property values are actor constructor functions, as - * documented for addTargetScopedActor and addGlobalActor. + * documented for addTargetScopedActor * - * @param this - * The RootActor or BrowsingContextTargetActor with which the new actors + * @param parent + * The parent TargetActor with which the new actors * will be associated. It should support whatever API the |factories| * constructor functions might be interested in, as it is passed to them. * For the sake of CommonCreateExtraActors itself, it should have at least * the following properties: * * - _extraActors * An object whose own property names are factory table (and packet) * property names, and whose values are no-argument actor constructors, * of the sort that one can add to an ActorPool. * * - conn * The DebuggerServerConnection in which the new actors will participate. * * - actorID * The actor's name, for use as the new actors' parentID. + * @param pool + * An object which implements the protocol.js Pool interface, and has the + * following properties + * + * - manage + * a function which adds a given actor to an actor pool */ -exports.createExtraActors = function createExtraActors(factories, pool) { +function createExtraActors(registeredActors, pool, parent) { // Walk over global actors added by extensions. - for (const name in factories) { - let actor = this._extraActors[name]; + const nameMap = {}; + for (const name in registeredActors) { + let actor = parent._extraActors[name]; if (!actor) { // Register another factory, but this time specific to this connection. // It creates a fake actor that looks like an regular actor in the pool, // but without actually instantiating the actor. // It will only be instantiated on the first request made to the actor. - actor = factories[name].createObservedActorFactory(this.conn, this); - this._extraActors[name] = actor; + actor = new LazyActor(registeredActors[name], parent, pool); + parent._extraActors[name] = actor; } // If the actor already exists in the pool, it may have been instantiated, // so make sure not to overwrite it by a non-instantiated version. if (!pool.has(actor.actorID)) { - pool.addActor(actor); + pool.manage(actor); } + nameMap[name] = actor.actorID; } -}; + return nameMap; +} + +exports.createExtraActors = createExtraActors; /** - * Append the extra actors in |this._extraActors|, constructed by a prior call - * to CommonCreateExtraActors, to |object|. + * Creates an "actor-like" object which responds in the same way as an ordinary actor + * but has fewer capabilities (ie, does not manage lifetimes or have it's own pool). + * + * + * @param factories + * An object whose own property names are the names of properties to add to + * some reply packet (say, a target actor grip or the "listTabs" response + * form), and whose own property values are actor constructor functions, as + * documented for addTargetScopedActor + * + * @param parent + * The parent TargetActor with which the new actors + * will be associated. It should support whatever API the |factories| + * constructor functions might be interested in, as it is passed to them. + * For the sake of CommonCreateExtraActors itself, it should have at least + * the following properties: * - * @param object - * The object to which the extra actors should be added, under the - * property names given in the |factories| table passed to - * CommonCreateExtraActors. + * - _extraActors + * An object whose own property names are factory table (and packet) + * property names, and whose values are no-argument actor constructors, + * of the sort that one can add to an ActorPool. + * + * - conn + * The DebuggerServerConnection in which the new actors will participate. * - * @param this - * The RootActor or BrowsingContextTargetActor whose |_extraActors| table we - * should use; see above. + * - actorID + * The actor's name, for use as the new actors' parentID. + * @param pool + * An object which implements the protocol.js Pool interface, and has the + * following properties + * + * - manage + * a function which adds a given actor to an actor pool */ -exports.appendExtraActors = function appendExtraActors(object) { - for (const name in this._extraActors) { - const actor = this._extraActors[name]; - object[name] = actor.actorID; - } -}; -/** - * Construct an ActorPool. - * - * ActorPools are actorID -> actor mapping and storage. These are - * used to accumulate and quickly dispose of groups of actors that - * share a lifetime. - */ -function ActorPool(connection) { - this.conn = connection; - this._actors = {}; +function LazyActor(factory, parent, pool) { + this._options = factory.options; + this._parentActor = parent; + this._name = factory.name; + this._pool = pool; + + // needed for taking a place in a pool + this.typeName = factory.name; } -ActorPool.prototype = { - /** - * Destroy the pool. This will remove all actors from the pool. - */ - destroy: function APDestroy() { - for (const id in this._actors) { - this.removeActor(this._actors[id]); +LazyActor.prototype = { + loadModule(id) { + const options = this._options; + try { + return require(id); + // Fetch the actor constructor + } catch (e) { + throw new Error( + `Unable to load actor module '${options.id}'\n${e.message}\n${e.stack}\n` + ); } }, - /** - * Add an actor to the pool. If the actor doesn't have an ID, allocate one - * from the connection. - * - * @param Object actor - * The actor to be added to the pool. - */ - addActor: function APAddActor(actor) { - actor.conn = this.conn; - if (!actor.actorID) { - // Older style actors use actorPrefix, while protocol.js-based actors use typeName - const prefix = actor.actorPrefix || actor.typeName; - if (!prefix) { - throw new Error("Actor should precify either `actorPrefix` or `typeName` " + - "attribute"); - } - actor.actorID = this.conn.allocID(prefix || undefined); - } - - // If the actor is already in a pool, remove it without destroying it. - if (actor.registeredPool) { - delete actor.registeredPool._actors[actor.actorID]; + getConstructor() { + const options = this._options; + if (options.constructorFun) { + // Actor definition registered by ActorRegistryActor or testing helpers + return options.constructorFun; } - actor.registeredPool = this; - - this._actors[actor.actorID] = actor; - }, - - /** - * Remove an actor from the pool. If the actor has a destroy method, call it. - */ - removeActor(actor) { - delete this._actors[actor.actorID]; - if (actor.destroy) { - actor.destroy(); - return; + // Lazy actor definition, where options contains all the information + // required to load the actor lazily. + // Exposes `name` attribute in order to allow removeXXXActor to match + // the actor by its actor constructor name. + this.name = options.constructorName; + const module = this.loadModule(options.id); + const constructor = module[options.constructorName]; + if (!constructor) { + throw new Error( + `Unable to find actor constructor named '${this.name}'. (Is it exported?)` + ); } - // Obsolete destruction method name (might still be used by custom actors) - if (actor.disconnect) { - actor.disconnect(); - } - }, - - get: function APGet(actorID) { - return this._actors[actorID] || undefined; - }, - - has: function APHas(actorID) { - return actorID in this._actors; - }, - - /** - * Returns true if the pool is empty. - */ - isEmpty: function APIsEmpty() { - return Object.keys(this._actors).length == 0; + return constructor; }, /** - * Match the api expected by the protocol library. + * Return the parent pool for this lazy actor. */ - unmanage: function(actor) { - return this.removeActor(actor); + parent: function() { + return this.conn && this.conn.poolFor(this.actorID); }, - forEach: function(callback) { - for (const name in this._actors) { - callback(this._actors[name]); + /** + * This will only happen if the actor is destroyed before it is created + * We do not want to use the Pool destruction method, because this actor + * has no pool. However, it might have a parent that should unmange this + * actor + */ + destroy() { + const parent = this.parent(); + if (parent) { + parent.unmanage(this); } }, -}; - -exports.ActorPool = ActorPool; - -/** - * An OriginalLocation represents a location in an original source. - * - * @param SourceActor actor - * A SourceActor representing an original source. - * @param Number line - * A line within the given source. - * @param Number column - * A column within the given line. - * @param String name - * The name of the symbol corresponding to this OriginalLocation. - */ -function OriginalLocation(actor, line, column, name) { - this._connection = actor ? actor.conn : null; - this._actorID = actor ? actor.actorID : undefined; - this._line = line; - this._column = column; - this._name = name; -} - -OriginalLocation.fromGeneratedLocation = function(generatedLocation) { - return new OriginalLocation( - generatedLocation.generatedSourceActor, - generatedLocation.generatedLine, - generatedLocation.generatedColumn - ); -}; - -OriginalLocation.prototype = { - get originalSourceActor() { - return this._connection ? this._connection.getActor(this._actorID) : null; - }, - get originalUrl() { - const actor = this.originalSourceActor; - const source = actor.source; - return source ? source.url : actor._originalUrl; - }, - - get originalLine() { - return this._line; - }, - - get originalColumn() { - return this._column; - }, - - get originalName() { - return this._name; - }, - - get generatedSourceActor() { - throw new Error("Shouldn't access generatedSourceActor from an OriginalLocation"); - }, + createActor() { + // Fetch the actor constructor + const Constructor = this.getConstructor(); + // Instantiate a new actor instance + const conn = this._parentActor.conn; + // this should be taken care of once all actors are moved to protocol.js + const instance = new Constructor(conn, this._parentActor); + instance.conn = conn; - get generatedLine() { - throw new Error("Shouldn't access generatedLine from an OriginalLocation"); - }, - - get generatedColumn() { - throw new Error("Shouldn't access generatedColumn from an Originallocation"); - }, + // We want the newly-constructed actor to completely replace the factory + // actor. Reusing the existing actor ID will make sure Pool.manage + // replaces the old actor with the new actor. + instance.actorID = this.actorID; - equals: function(other) { - return this.originalSourceActor.url == other.originalSourceActor.url && - this.originalLine === other.originalLine && - (this.originalColumn === undefined || - other.originalColumn === undefined || - this.originalColumn === other.originalColumn); - }, + this._pool.manage(instance); - toJSON: function() { - return { - source: this.originalSourceActor.form(), - line: this.originalLine, - column: this.originalColumn - }; + return instance; } }; -exports.OriginalLocation = OriginalLocation; - -/** - * A GeneratedLocation represents a location in a generated source. - * - * @param SourceActor actor - * A SourceActor representing a generated source. - * @param Number line - * A line within the given source. - * @param Number column - * A column within the given line. - */ -function GeneratedLocation(actor, line, column, lastColumn) { - this._connection = actor ? actor.conn : null; - this._actorID = actor ? actor.actorID : undefined; - this._line = line; - this._column = column; - this._lastColumn = (lastColumn !== undefined) ? lastColumn : column + 1; -} - -GeneratedLocation.fromOriginalLocation = function(originalLocation) { - return new GeneratedLocation( - originalLocation.originalSourceActor, - originalLocation.originalLine, - originalLocation.originalColumn - ); -}; - -GeneratedLocation.prototype = { - get originalSourceActor() { - throw new Error(); - }, - - get originalUrl() { - throw new Error("Shouldn't access originalUrl from a GeneratedLocation"); - }, - - get originalLine() { - throw new Error("Shouldn't access originalLine from a GeneratedLocation"); - }, - - get originalColumn() { - throw new Error("Shouldn't access originalColumn from a GeneratedLocation"); - }, - - get originalName() { - throw new Error("Shouldn't access originalName from a GeneratedLocation"); - }, - - get generatedSourceActor() { - return this._connection ? this._connection.getActor(this._actorID) : null; - }, - - get generatedLine() { - return this._line; - }, - - get generatedColumn() { - return this._column; - }, - - get generatedLastColumn() { - return this._lastColumn; - }, - - equals: function(other) { - return this.generatedSourceActor.url == other.generatedSourceActor.url && - this.generatedLine === other.generatedLine && - (this.generatedColumn === undefined || - other.generatedColumn === undefined || - this.generatedColumn === other.generatedColumn); - }, - - toJSON: function() { - return { - source: this.generatedSourceActor.form(), - line: this.generatedLine, - column: this.generatedColumn, - lastColumn: this.generatedLastColumn - }; - } -}; - -exports.GeneratedLocation = GeneratedLocation; - -/** - * A method decorator that ensures the actor is in the expected state before - * proceeding. If the actor is not in the expected state, the decorated method - * returns a rejected promise. - * - * The actor's state must be at this.state property. - * - * @param String expectedState - * The expected state. - * @param String activity - * Additional info about what's going on. - * @param Function methodFunc - * The actor method to proceed with when the actor is in the expected - * state. - * - * @returns Function - * The decorated method. - */ -function expectState(expectedState, methodFunc, activity) { - return function(...args) { - if (this.state !== expectedState) { - const msg = `Wrong state while ${activity}:` + - `Expected '${expectedState}', ` + - `but current state is '${this.state}'.`; - return Promise.reject(new Error(msg)); - } - - return methodFunc.apply(this, args); - }; -} - -exports.expectState = expectState; - -/** - * Proxies a call from an actor to an underlying module, stored - * as `bridge` on the actor. This allows a module to be defined in one - * place, usable by other modules/actors on the server, but a separate - * module defining the actor/RDP definition. - * - * @see Framerate implementation: devtools/server/performance/framerate.js - * @see Framerate actor definition: devtools/server/actors/framerate.js - */ -function actorBridge(methodName, definition = {}) { - return method(function() { - return this.bridge[methodName].apply(this.bridge, arguments); - }, definition); -} -exports.actorBridge = actorBridge; - -/** - * Like `actorBridge`, but without a spec definition, for when the actor is - * created with `ActorClassWithSpec` rather than vanilla `ActorClass`. - */ -function actorBridgeWithSpec(methodName) { - return method(function() { - return this.bridge[methodName].apply(this.bridge, arguments); - }); -} -exports.actorBridgeWithSpec = actorBridgeWithSpec;
new file mode 100644 --- /dev/null +++ b/devtools/shared/protocol/moz.build @@ -0,0 +1,9 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +DevToolsModules( + 'lazy-pool.js', +)
--- a/devtools/shared/security/tests/unit/testactors.js +++ b/devtools/shared/security/tests/unit/testactors.js @@ -1,15 +1,15 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -const { ActorPool, appendExtraActors, createExtraActors } = - require("devtools/server/actors/common"); +const { appendExtraActors } = require("devtools/server/actors/common"); +const { LazyPool, createExtraActors } = require("devtools/shared/protocol/lazy-pool"); const { RootActor } = require("devtools/server/actors/root"); const { ThreadActor } = require("devtools/server/actors/thread"); const { DebuggerServer } = require("devtools/server/main"); const promise = require("promise"); var gTestGlobals = []; DebuggerServer.addTestGlobal = function(global) { gTestGlobals.push(global); @@ -25,29 +25,27 @@ DebuggerServer.addTestGlobal = function( function TestTabList(connection) { this.conn = connection; // An array of actors for each global added with // DebuggerServer.addTestGlobal. this._targetActors = []; // A pool mapping those actors' names to the actors. - this._targetActorPool = new ActorPool(connection); + this._targetActorPool = new LazyPool(connection); for (const global of gTestGlobals) { const actor = new TestTargetActor(connection, global); actor.selected = false; this._targetActors.push(actor); - this._targetActorPool.addActor(actor); + this._targetActorPool.manage(actor); } if (this._targetActors.length > 0) { this._targetActors[0].selected = true; } - - connection.addActorPool(this._targetActorPool); } TestTabList.prototype = { constructor: TestTabList, getList: function() { return promise.resolve([...this._targetActors]); } }; @@ -80,46 +78,44 @@ TestTargetActor.prototype = { get url() { 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 ActorPool. - const actorPool = new ActorPool(this.conn); - this._createExtraActors(DebuggerServer.targetScopedActorFactories, actorPool); + // Walk over target-scoped actors and add them to a new LazyPool. + const actorPool = new LazyPool(this.conn); + const actors = createExtraActors( + DebuggerServer.targetScopedActorFactories, + actorPool, + this + ); if (!actorPool.isEmpty()) { this._targetActorPool = actorPool; this.conn.addActorPool(this._targetActorPool); } - this._appendExtraActors(response); - - return response; + return { ...response, ...actors }; }, onAttach: function(request) { this._attached = true; - const response = { type: "tabAttached", threadActor: this._threadActor.actorID }; - this._appendExtraActors(response); - - return response; + return { type: "tabAttached", threadActor: this._threadActor.actorID }; }, onDetach: function(request) { if (!this._attached) { return { "error": "wrongState" }; } return { type: "detached" }; }, /* Support for DebuggerServer.addTargetScopedActor. */ - _createExtraActors: createExtraActors, _appendExtraActors: appendExtraActors }; TestTargetActor.prototype.requestTypes = { "attach": TestTargetActor.prototype.onAttach, "detach": TestTargetActor.prototype.onDetach };
--- a/devtools/shared/transport/tests/unit/testactors.js +++ b/devtools/shared/transport/tests/unit/testactors.js @@ -1,14 +1,14 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; -const { ActorPool, appendExtraActors, createExtraActors } = - require("devtools/server/actors/common"); +const { appendExtraActors } = require("devtools/server/actors/common"); +const { LazyPool, createExtraActors } = require("devtools/shared/protocol/lazy-pool"); const { RootActor } = require("devtools/server/actors/root"); const { ThreadActor } = require("devtools/server/actors/thread"); const { DebuggerServer } = require("devtools/server/main"); const promise = require("promise"); var gTestGlobals = []; DebuggerServer.addTestGlobal = function(global) { gTestGlobals.push(global); @@ -24,29 +24,27 @@ DebuggerServer.addTestGlobal = function( function TestTabList(connection) { this.conn = connection; // An array of actors for each global added with // DebuggerServer.addTestGlobal. this._targetActors = []; // A pool mapping those actors' names to the actors. - this._targetActorPool = new ActorPool(connection); + this._targetActorPool = new LazyPool(connection); for (const global of gTestGlobals) { const actor = new TestTargetActor(connection, global); actor.selected = false; this._targetActors.push(actor); - this._targetActorPool.addActor(actor); + this._targetActorPool.manage(actor); } if (this._targetActors.length > 0) { this._targetActors[0].selected = true; } - - connection.addActorPool(this._targetActorPool); } TestTabList.prototype = { constructor: TestTabList, getList: function() { return promise.resolve([...this._targetActors]); } }; @@ -79,46 +77,44 @@ TestTargetActor.prototype = { get url() { 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 ActorPool. - const actorPool = new ActorPool(this.conn); - this._createExtraActors(DebuggerServer.targetScopedActorFactories, actorPool); + // Walk over target-scoped actors and add them to a new LazyPool. + const actorPool = new LazyPool(this.conn); + const actors = createExtraActors( + DebuggerServer.targetScopedActorFactories, + actorPool, + this + ); if (!actorPool.isEmpty()) { this._targetActorPool = actorPool; this.conn.addActorPool(this._targetActorPool); } - this._appendExtraActors(response); - - return response; + return { ...response, ...actors }; }, onAttach: function(request) { this._attached = true; - const response = { type: "tabAttached", threadActor: this._threadActor.actorID }; - this._appendExtraActors(response); - - return response; + return { type: "tabAttached", threadActor: this._threadActor.actorID }; }, onDetach: function(request) { if (!this._attached) { return { "error": "wrongState" }; } return { type: "detached" }; }, /* Support for DebuggerServer.addTargetScopedActor. */ - _createExtraActors: createExtraActors, _appendExtraActors: appendExtraActors }; TestTargetActor.prototype.requestTypes = { "attach": TestTargetActor.prototype.onAttach, "detach": TestTargetActor.prototype.onDetach };