--- 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);
@@ -482,26 +481,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);
@@ -566,17 +567,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;
@@ -895,17 +895,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;
}
@@ -1424,31 +1424,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
@@ -17,16 +17,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
};