Bug 1172897 - Remove deprecated eager actor construction path. r=ochameau draft
authorJ. Ryan Stinnett <jryans@gmail.com>
Wed, 30 May 2018 19:19:11 -0500
changeset 805435 eb30aa2937344d249dd23ea01b2513c136d0a40f
parent 805434 98c98bb695ef5f1cfbb832af37fc5a0e7276dbe4
child 805436 478af4f05ad68c0d7b2633a09c5e10f0f79a2945
push id112656
push userbmo:jryans@gmail.com
push dateThu, 07 Jun 2018 20:15:26 +0000
reviewersochameau
bugs1172897
milestone62.0a1
Bug 1172897 - Remove deprecated eager actor construction path. r=ochameau MozReview-Commit-ID: ALnbhoh0Nzj
devtools/client/debugger/test/mochitest/testactors.js
devtools/server/actors/common.js
devtools/server/actors/preference.js
devtools/server/main.js
devtools/server/tests/browser/error-actor.js
devtools/server/tests/mochitest/test_connectToFrame.html
devtools/server/tests/unit/post_init_global_actors.js
devtools/server/tests/unit/post_init_tab_actors.js
devtools/server/tests/unit/pre_init_global_actors.js
devtools/server/tests/unit/pre_init_tab_actors.js
devtools/server/tests/unit/registertestactors-01.js
devtools/server/tests/unit/registertestactors-02.js
devtools/server/tests/unit/registertestactors-03.js
devtools/server/tests/unit/registertestactors-lazy.js
devtools/server/tests/unit/test_add_actors.js
devtools/server/tests/unit/test_client_request.js
devtools/server/tests/unit/test_registerClient.js
devtools/server/tests/unit/test_register_actor.js
devtools/server/tests/unit/xpcshell.ini
devtools/shared/transport/tests/unit/test_bulk_error.js
devtools/shared/transport/tests/unit/test_client_server_bulk.js
--- a/devtools/client/debugger/test/mochitest/testactors.js
+++ b/devtools/client/debugger/test/mochitest/testactors.js
@@ -1,15 +1,14 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-function TestActor1(aConnection, aTab)
-{
+"use strict";
+
+function TestActor1(aConnection, aTab) {
   this.conn = aConnection;
   this.tab = aTab;
 }
 
 TestActor1.prototype = {
   actorPrefix: "test_one",
 
   grip: function TA1_grip() {
@@ -21,13 +20,19 @@ TestActor1.prototype = {
     return { pong: "pong" };
   }
 };
 
 TestActor1.prototype.requestTypes = {
   "ping": TestActor1.prototype.onPing
 };
 
-DebuggerServer.removeTabActor(TestActor1);
-DebuggerServer.removeGlobalActor(TestActor1);
+DebuggerServer.removeTabActor("testTabActor1");
+DebuggerServer.removeGlobalActor("testGlobalActor1");
 
-DebuggerServer.addTabActor(TestActor1, "testTabActor1");
-DebuggerServer.addGlobalActor(TestActor1, "testGlobalActor1");
+DebuggerServer.addTabActor({
+  constructorName: "TestActor1",
+  constructorFun: TestActor1,
+}, "testTabActor1");
+DebuggerServer.addGlobalActor({
+  constructorName: "TestActor1",
+  constructorFun: TestActor1,
+}, "testGlobalActor1");
--- a/devtools/server/actors/common.js
+++ b/devtools/server/actors/common.js
@@ -13,74 +13,55 @@ const { method } = require("devtools/sha
  * 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, function
- *        Either an object or a function.
- *        If given an object:
- *
- *        If given a function (deprecated):
- *          Constructor function of an actor.
- *          The constructor function for this actor type.
- *          This expects to be called as a constructor (i.e. with 'new'),
- *          and passed two arguments: the DebuggerServerConnection, and
- *          the BrowserTabActor with which it will be associated.
- *          Only used for deprecated eagerly loaded actors.
- *
+ * @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 (typeof (options) != "function") {
-    // actors definition registered by actorRegistryActor
-    if (options.constructorFun) {
-      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;
+  if (options.constructorFun) {
+    // Actor definition registered by ActorRegistryActor or testing helpers
+    this._getConstructor = () => options.constructorFun;
   } else {
-    // Old actor case, where options is a function that is the actor constructor.
-    this._getConstructor = () => options;
-    // Exposes `name` attribute in order to allow removeXXXActor to match
-    // the actor by its actor constructor name.
-    this.name = options.name;
-
-    // For old actors, we allow the use of a different prefix for actorID
-    // than for listTabs actor names, by fetching a prefix on the actor prototype.
-    // (Used by ChromeDebuggerActor)
-    if (options.prototype && options.prototype.actorPrefix) {
-      this._prefix = options.prototype.actorPrefix;
-    }
+    // 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;
 
 /**
--- a/devtools/server/actors/preference.js
+++ b/devtools/server/actors/preference.js
@@ -4,32 +4,25 @@
 
 "use strict";
 
 const {Ci} = require("chrome");
 const protocol = require("devtools/shared/protocol");
 const Services = require("Services");
 const {preferenceSpec} = require("devtools/shared/specs/preference");
 
-exports.register = function(handle) {
-  handle.addGlobalActor(PreferenceActor, "preferenceActor");
-};
-
-exports.unregister = function(handle) {
-};
-
 /**
  * Normally the preferences are set using Services.prefs, but this actor allows
  * a debugger client to set preferences on the debuggee. This is particularly useful
  * when remote debugging, and the preferences should persist to the remote target
  * and not to the client. If used for a local target, it effectively behaves the same
  * as using Services.prefs.
  *
- * This actor is used as a Root actor, targeting the entire browser, not an individual
- * tab.
+ * This actor is used as a global-scoped actor, targeting the entire browser, not an
+ * individual tab.
  */
 var PreferenceActor = protocol.ActorClassWithSpec(preferenceSpec, {
 
   typeName: "preference",
 
   getBoolPref: function(name) {
     return Services.prefs.getBoolPref(name);
   },
--- a/devtools/server/main.js
+++ b/devtools/server/main.js
@@ -1220,125 +1220,148 @@ var DebuggerServer = {
 
   /**
    * Registers handlers for new tab-scoped request types defined dynamically.
    * This is used for example by add-ons to augment the functionality of the tab
    * actor. 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 function, object
-   *      In case of function:
-   *        The constructor function for this request type. This expects to be
-   *        called as a constructor (i.e. with 'new'), and passed two
-   *        arguments: the DebuggerServerConnection, and the BrowserTabActor
-   *        with which it will be associated.
-   *        Only used for deprecated eagerly loaded actors.
-   *      In case of object:
-   *        First argument of RegisteredActorFactory constructor.
-   *        See the it's definition for more info.
-   *
-   * @param name string [optional]
-   *        The name of the new request type. If this is not present, the
-   *        actorPrefix property of the constructor prototype is used.
+   * @param actor 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.
    */
-  addTabActor(actor, name = actor.prototype.actorPrefix) {
+  addTabActor(actor, name) {
+    if (!name) {
+      throw Error("addTabActor requires the `name` argument");
+    }
     if (["title", "url", "actor"].includes(name)) {
       throw Error(name + " is not allowed");
     }
     if (DebuggerServer.tabActorFactories.hasOwnProperty(name)) {
       throw Error(name + " already exists");
     }
     DebuggerServer.tabActorFactories[name] = new RegisteredActorFactory(actor, name);
   },
 
   /**
    * Unregisters the handler for the specified tab-scoped request type.
    * This may be used for example by add-ons when shutting down or upgrading.
    * When unregistering an existing tab actor remove related tab factory
    * as well as all existing instances of the actor.
    *
-   * @param actor function, object
-   *      In case of function:
-   *        The constructor function for this request type.
-   *      In case of object:
-   *        Same object being given to related addTabActor call.
+   * @param actor object, string
+   *        In case of object:
+   *          The `actor` object being given to related addTabActor call.
+   *        In case of string:
+   *          The `name` string being given to related addTabActor call.
    */
-  removeTabActor(actor) {
-    for (const name in DebuggerServer.tabActorFactories) {
-      const handler = DebuggerServer.tabActorFactories[name];
-      if ((handler.name && handler.name == actor.name) ||
-          (handler.id && handler.id == actor.id)) {
-        delete DebuggerServer.tabActorFactories[name];
-        for (const connID of Object.getOwnPropertyNames(this._connections)) {
-          // DebuggerServerConnection in child process don't have rootActor
-          if (this._connections[connID].rootActor) {
-            this._connections[connID].rootActor.removeActorByName(name);
-          }
+  removeTabActor(actorOrName) {
+    let name;
+    if (typeof actorOrName == "string") {
+      name = actorOrName;
+    } else {
+      const actor = actorOrName;
+      for (const factoryName in DebuggerServer.tabActorFactories) {
+        const handler = DebuggerServer.tabActorFactories[factoryName];
+        if ((handler.name && handler.name == actor.name) ||
+            (handler.id && handler.id == actor.id)) {
+          name = factoryName;
+          break;
         }
       }
     }
+    if (!name) {
+      return;
+    }
+    delete DebuggerServer.tabActorFactories[name];
+    for (const connID of Object.getOwnPropertyNames(this._connections)) {
+      // DebuggerServerConnection in child process don't have rootActor
+      if (this._connections[connID].rootActor) {
+        this._connections[connID].rootActor.removeActorByName(name);
+      }
+    }
   },
 
   /**
    * Registers handlers for new browser-scoped request types defined
    * dynamically. This is used for example by add-ons to augment the
    * functionality of the root actor. 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 function, object
-   *      In case of function:
-   *        The constructor function for this request type. This expects to be
-   *        called as a constructor (i.e. with 'new'), and passed two
-   *        arguments: the DebuggerServerConnection, and the BrowserRootActor
-   *        with which it will be associated.
-   *        Only used for deprecated eagerly loaded actors.
-   *      In case of object:
-   *        First argument of RegisteredActorFactory constructor.
-   *        See the it's definition for more info.
-   *
-   * @param name string [optional]
-   *        The name of the new request type. If this is not present, the
-   *        actorPrefix property of the constructor prototype is used.
+   * @param actor 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 = actor.prototype.actorPrefix) {
+  addGlobalActor(actor, 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);
   },
 
   /**
    * Unregisters the handler for the specified browser-scoped request type.
    * This may be used for example by add-ons when shutting down or upgrading.
    * When unregistering an existing global actor remove related global factory
    * as well as all existing instances of the actor.
    *
-   * @param actor function, object
-   *      In case of function:
-   *        The constructor function for this request type.
-   *      In case of object:
-   *        Same object being given to related addGlobalActor call.
+   * @param actor object, string
+   *        In case of object:
+   *          The `actor` object being given to related addGlobalActor call.
+   *        In case of string:
+   *          The `name` string being given to related addGlobalActor call.
    */
-  removeGlobalActor(actor) {
-    for (const name in DebuggerServer.globalActorFactories) {
-      const handler = DebuggerServer.globalActorFactories[name];
-      if ((handler.name && handler.name == actor.name) ||
-          (handler.id && handler.id == actor.id)) {
-        delete DebuggerServer.globalActorFactories[name];
-        for (const connID of Object.getOwnPropertyNames(this._connections)) {
-          this._connections[connID].rootActor.removeActorByName(name);
+  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)) {
+          name = factoryName;
+          break;
         }
       }
     }
+    if (!name) {
+      return;
+    }
+    delete DebuggerServer.globalActorFactories[name];
+    for (const connID of Object.getOwnPropertyNames(this._connections)) {
+      // DebuggerServerConnection in child process don't have rootActor
+      if (this._connections[connID].rootActor) {
+        this._connections[connID].rootActor.removeActorByName(name);
+      }
+    }
   },
 
   /**
    * Called when DevTools are unloaded to remove the contend process server startup script
    * for the list of scripts loaded for each new content process. Will also remove message
    * listeners from already loaded scripts.
    */
   removeContentServerScript() {
--- a/devtools/server/tests/browser/error-actor.js
+++ b/devtools/server/tests/browser/error-actor.js
@@ -23,10 +23,13 @@ ErrorActor.prototype = {
     throw new Error("error");
   }
 };
 
 ErrorActor.prototype.requestTypes = {
   "error": ErrorActor.prototype.onError
 };
 
-DebuggerServer.removeGlobalActor(ErrorActor);
-DebuggerServer.addGlobalActor(ErrorActor, "errorActor");
+DebuggerServer.removeGlobalActor("errorActor");
+DebuggerServer.addGlobalActor({
+  constructorName: "ErrorActor",
+  constructorFun: ErrorActor,
+}, "errorActor");
--- a/devtools/server/tests/mochitest/test_connectToFrame.html
+++ b/devtools/server/tests/mochitest/test_connectToFrame.html
@@ -59,17 +59,20 @@ function runTests() {
       },
       hello: function() {
         return {msg: "world"};
       }
     };
     TestActor.prototype.requestTypes = {
       "hello": TestActor.prototype.hello
     };
-    DebuggerServer.addTabActor(TestActor, "testActor");
+    DebuggerServer.addTabActor({
+      constructorName: "TestActor",
+      constructorFun: TestActor,
+    }, "testActor");
   }, false);
 
   // Instantiate a minimal server
   DebuggerServer.init();
   if (!DebuggerServer.createRootActor) {
     DebuggerServer.registerAllActors();
   }
 
--- a/devtools/server/tests/unit/post_init_global_actors.js
+++ b/devtools/server/tests/unit/post_init_global_actors.js
@@ -14,9 +14,12 @@ PostInitGlobalActor.prototype = {
     return { message: "pong" };
   },
 };
 
 PostInitGlobalActor.prototype.requestTypes = {
   "ping": PostInitGlobalActor.prototype.onPing,
 };
 
-DebuggerServer.addGlobalActor(PostInitGlobalActor, "postInitGlobalActor");
+DebuggerServer.addGlobalActor({
+  constructorName: "PostInitGlobalActor",
+  constructorFun: PostInitGlobalActor,
+}, "postInitGlobalActor");
--- a/devtools/server/tests/unit/post_init_tab_actors.js
+++ b/devtools/server/tests/unit/post_init_tab_actors.js
@@ -14,9 +14,12 @@ PostInitTabActor.prototype = {
     return { message: "pong" };
   },
 };
 
 PostInitTabActor.prototype.requestTypes = {
   "ping": PostInitTabActor.prototype.onPing,
 };
 
-DebuggerServer.addGlobalActor(PostInitTabActor, "postInitTabActor");
+DebuggerServer.addTabActor({
+  constructorName: "PostInitTabActor",
+  constructorFun: PostInitTabActor,
+}, "postInitTabActor");
--- a/devtools/server/tests/unit/pre_init_global_actors.js
+++ b/devtools/server/tests/unit/pre_init_global_actors.js
@@ -14,9 +14,12 @@ PreInitGlobalActor.prototype = {
     return { message: "pong" };
   },
 };
 
 PreInitGlobalActor.prototype.requestTypes = {
   "ping": PreInitGlobalActor.prototype.onPing,
 };
 
-DebuggerServer.addGlobalActor(PreInitGlobalActor, "preInitGlobalActor");
+DebuggerServer.addGlobalActor({
+  constructorName: "PreInitGlobalActor",
+  constructorFun: PreInitGlobalActor,
+}, "preInitGlobalActor");
--- a/devtools/server/tests/unit/pre_init_tab_actors.js
+++ b/devtools/server/tests/unit/pre_init_tab_actors.js
@@ -14,9 +14,12 @@ PreInitTabActor.prototype = {
     return { message: "pong" };
   },
 };
 
 PreInitTabActor.prototype.requestTypes = {
   "ping": PreInitTabActor.prototype.onPing,
 };
 
-DebuggerServer.addGlobalActor(PreInitTabActor, "preInitTabActor");
+DebuggerServer.addTabActor({
+  constructorName: "PreInitTabActor",
+  constructorFun: PreInitTabActor,
+}, "preInitTabActor");
deleted file mode 100644
--- a/devtools/server/tests/unit/registertestactors-01.js
+++ /dev/null
@@ -1,17 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-function Actor() {}
-
-exports.register = function(handle) {
-  handle.addTabActor(Actor, "registeredActor1");
-  handle.addGlobalActor(Actor, "registeredActor1");
-};
-
-exports.unregister = function(handle) {
-  handle.removeTabActor(Actor);
-  handle.removeGlobalActor(Actor);
-};
-
deleted file mode 100644
--- a/devtools/server/tests/unit/registertestactors-02.js
+++ /dev/null
@@ -1,17 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-function Actor() {}
-
-exports.register = function(handle) {
-  handle.addGlobalActor(Actor, "registeredActor2");
-  handle.addTabActor(Actor, "registeredActor2");
-};
-
-exports.unregister = function(handle) {
-  handle.removeTabActor(Actor);
-  handle.removeGlobalActor(Actor);
-};
-
rename from devtools/server/tests/unit/registertestactors-03.js
rename to devtools/server/tests/unit/registertestactors-lazy.js
--- a/devtools/server/tests/unit/test_add_actors.js
+++ b/devtools/server/tests/unit/test_add_actors.js
@@ -1,104 +1,65 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-var gClient;
-var gActors;
+// Get the object, from the server side, for a given actor ID
+function getActorInstance(connID, actorID) {
+  return DebuggerServer._connections[connID].getActor(actorID);
+}
 
 /**
  * The purpose of these tests is to verify that it's possible to add actors
  * both before and after the DebuggerServer has been initialized, so addons
  * that add actors don't have to poll the object for its initialization state
  * in order to add actors after initialization but rather can add actors anytime
  * regardless of the object's state.
  */
-function run_test() {
+add_task(async function() {
   DebuggerServer.addActors("resource://test/pre_init_global_actors.js");
   DebuggerServer.addActors("resource://test/pre_init_tab_actors.js");
 
-  DebuggerServer.init();
-  DebuggerServer.registerAllActors();
+  const client = await startTestDebuggerServer("example tab");
 
   DebuggerServer.addActors("resource://test/post_init_global_actors.js");
   DebuggerServer.addActors("resource://test/post_init_tab_actors.js");
 
-  add_test(init);
-  add_test(test_pre_init_global_actor);
-  add_test(test_pre_init_tab_actor);
-  add_test(test_post_init_global_actor);
-  add_test(test_post_init_tab_actor);
-  add_test(test_stable_global_actor_instances);
-  add_test(close_client);
-  run_next_test();
-}
+  let actors = await client.listTabs();
+  Assert.equal(actors.tabs.length, 1);
 
-function init() {
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect()
-    .then(() => gClient.listTabs())
-    .then(response => {
-      gActors = response;
-      run_next_test();
-    });
-}
+  let reply = await client.request({
+    to: actors.preInitGlobalActor,
+    type: "ping",
+  });
+  Assert.equal(reply.message, "pong");
 
-function test_pre_init_global_actor() {
-  gClient.request({ to: gActors.preInitGlobalActor, type: "ping" },
-    function onResponse(response) {
-      Assert.equal(response.message, "pong");
-      run_next_test();
-    }
-  );
-}
-
-function test_pre_init_tab_actor() {
-  gClient.request({ to: gActors.preInitTabActor, type: "ping" },
-    function onResponse(response) {
-      Assert.equal(response.message, "pong");
-      run_next_test();
-    }
-  );
-}
+  reply = await client.request({
+    to: actors.tabs[0].preInitTabActor,
+    type: "ping",
+  });
+  Assert.equal(reply.message, "pong");
 
-function test_post_init_global_actor() {
-  gClient.request({ to: gActors.postInitGlobalActor, type: "ping" },
-    function onResponse(response) {
-      Assert.equal(response.message, "pong");
-      run_next_test();
-    }
-  );
-}
+  reply = await client.request({
+    to: actors.postInitGlobalActor,
+    type: "ping",
+  });
+  Assert.equal(reply.message, "pong");
 
-function test_post_init_tab_actor() {
-  gClient.request({ to: gActors.postInitTabActor, type: "ping" },
-    function onResponse(response) {
-      Assert.equal(response.message, "pong");
-      run_next_test();
-    }
-  );
-}
+  reply = await client.request({
+    to: actors.tabs[0].postInitTabActor,
+    type: "ping",
+  });
+  Assert.equal(reply.message, "pong");
 
-// Get the object object, from the server side, for a given actor ID
-function getActorInstance(connID, actorID) {
-  return DebuggerServer._connections[connID].getActor(actorID);
-}
-
-function test_stable_global_actor_instances() {
-  // Consider that there is only one connection,
-  // and the first one is ours
+  // Consider that there is only one connection, and the first one is ours
   const connID = Object.keys(DebuggerServer._connections)[0];
-  const postInitGlobalActor = getActorInstance(connID, gActors.postInitGlobalActor);
-  const preInitGlobalActor = getActorInstance(connID, gActors.preInitGlobalActor);
-  gClient.listTabs().then(function onListTabs(response) {
-    Assert.equal(postInitGlobalActor,
-                 getActorInstance(connID, response.postInitGlobalActor));
-    Assert.equal(preInitGlobalActor,
-                 getActorInstance(connID, response.preInitGlobalActor));
-    run_next_test();
-  });
-}
+  const postInitGlobalActor = getActorInstance(connID, actors.postInitGlobalActor);
+  const preInitGlobalActor = getActorInstance(connID, actors.preInitGlobalActor);
+  actors = await client.listTabs();
+  Assert.equal(postInitGlobalActor,
+    getActorInstance(connID, actors.postInitGlobalActor));
+  Assert.equal(preInitGlobalActor,
+    getActorInstance(connID, actors.preInitGlobalActor));
 
-function close_client() {
-  gClient.close().then(() => run_next_test());
-}
+  await client.close();
+});
--- a/devtools/server/tests/unit/test_client_request.js
+++ b/devtools/server/tests/unit/test_client_request.js
@@ -22,17 +22,20 @@ TestActor.prototype = {
   }
 };
 TestActor.prototype.requestTypes = {
   "hello": TestActor.prototype.hello,
   "error": TestActor.prototype.error
 };
 
 function run_test() {
-  DebuggerServer.addGlobalActor(TestActor);
+  DebuggerServer.addGlobalActor({
+    constructorName: "TestActor",
+    constructorFun: TestActor,
+  }, "test");
 
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
   add_test(init);
   add_test(test_client_request_callback);
   add_test(test_client_request_promise);
   add_test(test_client_request_promise_error);
--- a/devtools/server/tests/unit/test_registerClient.js
+++ b/devtools/server/tests/unit/test_registerClient.js
@@ -46,17 +46,20 @@ TestClient.prototype = {
 
   detach: function(onDone) {
     this.detached = true;
     onDone();
   }
 };
 
 function run_test() {
-  DebuggerServer.addGlobalActor(TestActor);
+  DebuggerServer.addGlobalActor({
+    constructorName: "TestActor",
+    constructorFun: TestActor,
+  }, "test");
 
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
   add_test(init);
   add_test(test_client_events);
   add_test(close_client);
   run_next_test();
--- a/devtools/server/tests/unit/test_register_actor.js
+++ b/devtools/server/tests/unit/test_register_actor.js
@@ -1,71 +1,37 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-function check_actors(expect) {
-  Assert.equal(expect,
-               DebuggerServer.tabActorFactories.hasOwnProperty("registeredActor1"));
-  Assert.equal(expect,
-               DebuggerServer.tabActorFactories.hasOwnProperty("registeredActor2"));
-
-  Assert.equal(expect,
-               DebuggerServer.globalActorFactories.hasOwnProperty("registeredActor2"));
-  Assert.equal(expect,
-               DebuggerServer.globalActorFactories.hasOwnProperty("registeredActor1"));
-}
-
 function run_test() {
   // Allow incoming connections.
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  add_test(test_deprecated_api);
   add_test(test_lazy_api);
+  add_test(manual_remove);
   add_test(cleanup);
   run_next_test();
 }
 
-function test_deprecated_api() {
-  // The xpcshell-test/ path maps to resource://test/
-  DebuggerServer.registerModule("xpcshell-test/registertestactors-01");
-  DebuggerServer.registerModule("xpcshell-test/registertestactors-02");
-
-  check_actors(true);
-
-  // Calling registerModule again is just a no-op and doesn't throw
-  DebuggerServer.registerModule("xpcshell-test/registertestactors-01");
-  DebuggerServer.registerModule("xpcshell-test/registertestactors-02");
-
-  DebuggerServer.unregisterModule("xpcshell-test/registertestactors-01");
-  DebuggerServer.unregisterModule("xpcshell-test/registertestactors-02");
-  check_actors(false);
-
-  DebuggerServer.registerModule("xpcshell-test/registertestactors-01");
-  DebuggerServer.registerModule("xpcshell-test/registertestactors-02");
-  check_actors(true);
-
-  run_next_test();
-}
-
 // Bug 988237: Test the new lazy actor loading
 function test_lazy_api() {
   let isActorLoaded = false;
   let isActorInstantiated = false;
   function onActorEvent(subject, topic, data) {
     if (data == "loaded") {
       isActorLoaded = true;
     } else if (data == "instantiated") {
       isActorInstantiated = true;
     }
   }
   Services.obs.addObserver(onActorEvent, "actor");
-  DebuggerServer.registerModule("xpcshell-test/registertestactors-03", {
+  DebuggerServer.registerModule("xpcshell-test/registertestactors-lazy", {
     prefix: "lazy",
     constructor: "LazyActor",
     type: { global: true, tab: true }
   });
   // The actor is immediatly registered, but not loaded
   Assert.ok(DebuggerServer.tabActorFactories.hasOwnProperty("lazyActor"));
   Assert.ok(DebuggerServer.globalActorFactories.hasOwnProperty("lazyActor"));
   Assert.ok(!isActorLoaded);
@@ -77,35 +43,42 @@ function test_lazy_api() {
   });
   function onListTabs(response) {
     // On listTabs, the actor is still not loaded,
     // but we can see its name in the list of available actors
     Assert.ok(!isActorLoaded);
     Assert.ok(!isActorInstantiated);
     Assert.ok("lazyActor" in response);
 
-    const {LazyFront} = require("xpcshell-test/registertestactors-03");
+    const {LazyFront} = require("xpcshell-test/registertestactors-lazy");
     const front = LazyFront(client, response);
     front.hello().then(onRequest);
   }
   function onRequest(response) {
     Assert.equal(response, "world");
 
     // Finally, the actor is loaded on the first request being made to it
     Assert.ok(isActorLoaded);
     Assert.ok(isActorInstantiated);
 
     Services.obs.removeObserver(onActorEvent, "actor");
     client.close().then(() => run_next_test());
   }
 }
 
+function manual_remove() {
+  Assert.ok(DebuggerServer.globalActorFactories.hasOwnProperty("lazyActor"));
+  DebuggerServer.removeGlobalActor("lazyActor");
+  Assert.ok(!DebuggerServer.globalActorFactories.hasOwnProperty("lazyActor"));
+
+  run_next_test();
+}
+
 function cleanup() {
   DebuggerServer.destroy();
 
   // Check that all actors are unregistered on server destruction
-  check_actors(false);
   Assert.ok(!DebuggerServer.tabActorFactories.hasOwnProperty("lazyActor"));
   Assert.ok(!DebuggerServer.globalActorFactories.hasOwnProperty("lazyActor"));
 
   run_next_test();
 }
 
--- a/devtools/server/tests/unit/xpcshell.ini
+++ b/devtools/server/tests/unit/xpcshell.ini
@@ -7,19 +7,17 @@ skip-if = toolkit == 'android'
 support-files =
   babel_and_browserify_script_with_source_map.js
   source-map-data/sourcemapped.coffee
   source-map-data/sourcemapped.map
   post_init_global_actors.js
   post_init_tab_actors.js
   pre_init_global_actors.js
   pre_init_tab_actors.js
-  registertestactors-01.js
-  registertestactors-02.js
-  registertestactors-03.js
+  registertestactors-lazy.js
   sourcemapped.js
   testactors.js
   hello-actor.js
   setBreakpoint-on-column.js
   setBreakpoint-on-column-in-gcd-script.js
   setBreakpoint-on-column-with-no-offsets.js
   setBreakpoint-on-column-with-no-offsets-in-gcd-script.js
   setBreakpoint-on-line.js
--- a/devtools/shared/transport/tests/unit/test_bulk_error.js
+++ b/devtools/shared/transport/tests/unit/test_bulk_error.js
@@ -34,17 +34,20 @@ TestBulkActor.prototype = {
 
 };
 
 TestBulkActor.prototype.requestTypes = {
   "jsonReply": TestBulkActor.prototype.jsonReply
 };
 
 function add_test_bulk_actor() {
-  DebuggerServer.addGlobalActor(TestBulkActor);
+  DebuggerServer.addGlobalActor({
+    constructorName: "TestBulkActor",
+    constructorFun: TestBulkActor,
+  }, "testBulk");
 }
 
 /** * Tests ***/
 
 var test_string_error = async function(transportFactory, onReady) {
   const transport = await transportFactory();
 
   const client = new DebuggerClient(transport);
--- a/devtools/shared/transport/tests/unit/test_client_server_bulk.js
+++ b/devtools/shared/transport/tests/unit/test_client_server_bulk.js
@@ -88,17 +88,20 @@ TestBulkActor.prototype = {
 
 TestBulkActor.prototype.requestTypes = {
   "bulkEcho": TestBulkActor.prototype.bulkEcho,
   "bulkReply": TestBulkActor.prototype.bulkReply,
   "jsonReply": TestBulkActor.prototype.jsonReply
 };
 
 function add_test_bulk_actor() {
-  DebuggerServer.addGlobalActor(TestBulkActor);
+  DebuggerServer.addGlobalActor({
+    constructorName: "TestBulkActor",
+    constructorFun: TestBulkActor,
+  }, "testBulk");
 }
 
 /** * Reply Handlers ***/
 
 var replyHandlers = {
 
   json: function(request) {
     // Receive JSON reply from server