Browse Source

More devtools backports

Ian Neal 10 months ago
parent
commit
9f9da7f630
35 changed files with 13829 additions and 0 deletions
  1. 68 0
      comm-release/patches/1363061-cc-60a1.patch
  2. 29 0
      comm-release/patches/1430501-61a1.patch
  3. 32 0
      comm-release/patches/1868034-registerAllActors-25319.patch
  4. 3 0
      comm-release/patches/series
  5. 66 0
      mozilla-release/patches/1151156-59a1.patch
  6. 647 0
      mozilla-release/patches/1272774-1-60a1.patch
  7. 450 0
      mozilla-release/patches/1272774-2-60a1.patch
  8. 56 0
      mozilla-release/patches/1272774-3-60a1.patch
  9. 32 0
      mozilla-release/patches/1272774-4-60a1.patch
  10. 102 0
      mozilla-release/patches/1272774-5-60a1.patch
  11. 411 0
      mozilla-release/patches/1363061-60a1.patch
  12. 722 0
      mozilla-release/patches/1386613-1-59a1.patch
  13. 533 0
      mozilla-release/patches/1386613-2-59a1.patch
  14. 268 0
      mozilla-release/patches/1386613-3-59a1.patch
  15. 49 0
      mozilla-release/patches/1394235-57a1.patch
  16. 72 0
      mozilla-release/patches/1401343-58a1.patch
  17. 137 0
      mozilla-release/patches/1405008-59a1.patch
  18. 68 0
      mozilla-release/patches/1407426-1-58a1.patch
  19. 177 0
      mozilla-release/patches/1407426-2-58a1.patch
  20. 301 0
      mozilla-release/patches/1411199-58a1.patch
  21. 839 0
      mozilla-release/patches/1411565-59a1.patch
  22. 1598 0
      mozilla-release/patches/1411920-58a1.patch
  23. 65 0
      mozilla-release/patches/1416105-58a1.patch
  24. 83 0
      mozilla-release/patches/1416711-1-59a1.patch
  25. 1718 0
      mozilla-release/patches/1416711-2-59a1.patch
  26. 35 0
      mozilla-release/patches/1416711-3-59a1.patch
  27. 1339 0
      mozilla-release/patches/1416711-4-59a1.patch
  28. 44 0
      mozilla-release/patches/1416711-5-59a1.patch
  29. 1777 0
      mozilla-release/patches/1416711-6-59a1.patch
  30. 87 0
      mozilla-release/patches/1416928-1-59a1.patch
  31. 157 0
      mozilla-release/patches/1416928-2-59a1.patch
  32. 374 0
      mozilla-release/patches/1417444-59a1.patch
  33. 62 0
      mozilla-release/patches/1430799-59a1.patch
  34. 1398 0
      mozilla-release/patches/1435187-60a1.patch
  35. 30 0
      mozilla-release/patches/series

+ 68 - 0
comm-release/patches/1363061-cc-60a1.patch

@@ -0,0 +1,68 @@
+# HG changeset patch
+# User Jorg K <jorgk@jorgk.com>
+# Date 1520496718 -3600
+# Node ID 47b4430bdfb677b17ee358288b3500ee1be85e30
+# Parent  c29edb768365168bee661d34a49e078b71c5dcb2
+Port bug 1363061 to TB/IB/SM: rename jsdownloads.xpt to downloads.xpt. rs=bustage-fix
+
+diff --git a/mail/installer/package-manifest.in b/mail/installer/package-manifest.in
+--- a/mail/installer/package-manifest.in
++++ b/mail/installer/package-manifest.in
+@@ -351,17 +351,17 @@
+ @RESPATH@/components/mozldap.xpt
+ @RESPATH@/components/nsAbLDAPAutoCompleteSearch.js
+ @RESPATH@/components/nsLDAPProtocolHandler.js
+ @RESPATH@/components/ldapComponents.manifest
+ @BINPATH@/@DLL_PREFIX@ldap60@DLL_SUFFIX@
+ @BINPATH@/@DLL_PREFIX@ldif60@DLL_SUFFIX@
+ @BINPATH@/@DLL_PREFIX@prldap60@DLL_SUFFIX@
+ 
+-; download progress for jsdownloads
++; download progress for downloads
+ @RESPATH@/components/DownloadsStartup.js
+ 
+ ; Protocol/Content handling
+ @RESPATH@/components/nsSMTPProtocolHandler.js
+ @RESPATH@/components/nsSMTPProtocolHandler.manifest
+ 
+ ; misson control, autoconfig
+ #ifdef MOZ_PREF_EXTENSIONS
+@@ -462,17 +462,16 @@
+ ; layout
+ @RESPATH@/components/html5.xpt
+ @RESPATH@/components/htmlparser.xpt
+ @RESPATH@/components/imglib2.xpt
+ @RESPATH@/components/inspector.xpt
+ @RESPATH@/components/intl.xpt
+ @RESPATH@/components/jar.xpt
+ @RESPATH@/components/jsdebugger.xpt
+-@RESPATH@/components/jsdownloads.xpt
+ @RESPATH@/components/jsinspector.xpt
+ @RESPATH@/components/layout_base.xpt
+ #ifdef NS_PRINTING
+ @RESPATH@/components/layout_printing.xpt
+ #endif
+ @RESPATH@/components/layout_xul_tree.xpt
+ @RESPATH@/components/layout_xul.xpt
+ @RESPATH@/components/locale.xpt
+diff --git a/suite/installer/package-manifest.in b/suite/installer/package-manifest.in
+--- a/suite/installer/package-manifest.in
++++ b/suite/installer/package-manifest.in
+@@ -230,17 +230,16 @@
+ @RESPATH@/components/gfx.xpt
+ @RESPATH@/components/html5.xpt
+ @RESPATH@/components/htmlparser.xpt
+ @RESPATH@/components/imglib2.xpt
+ @RESPATH@/components/inspector.xpt
+ @RESPATH@/components/intl.xpt
+ @RESPATH@/components/jar.xpt
+ @RESPATH@/components/jsdebugger.xpt
+-@RESPATH@/components/jsdownloads.xpt
+ @RESPATH@/components/jsinspector.xpt
+ @RESPATH@/components/layout_base.xpt
+ #ifdef NS_PRINTING
+ @RESPATH@/components/layout_printing.xpt
+ #endif
+ @RESPATH@/components/layout_xul_tree.xpt
+ @RESPATH@/components/layout_xul.xpt
+ @RESPATH@/components/locale.xpt

+ 29 - 0
comm-release/patches/1430501-61a1.patch

@@ -0,0 +1,29 @@
+# HG changeset patch
+# User Jorg K <jorgk@jorgk.com>
+# Date 1520888055 -3600
+# Node ID 526b5080e36a1147416b8aa8530ef7a1c14e3bf4
+# Parent  cdfcd1bd45048e880ddb4b3e80277370acf0716a
+Bug 1430501 - Port bug 1416711 to TB: Migrate addBrowser to registerActors. r=philipp
+
+diff --git a/mail/components/devtools/devtools-loader.js b/mail/components/devtools/devtools-loader.js
+--- a/mail/components/devtools/devtools-loader.js
++++ b/mail/components/devtools/devtools-loader.js
+@@ -61,17 +61,17 @@ DevToolsStartup.prototype = {
+     // loaded instance. Thunderbird now also provides the Browser Toolbox for
+     // chrome debugging, which uses its own separate loader instance.
+     DevToolsLoader.prototype.invisibleToDebugger = false;
+     devtools.invisibleToDebugger = false;
+ 
+     if (!DebuggerServer.initialized) {
+       // Initialize and load the toolkit/browser actors
+       DebuggerServer.init();
+-      DebuggerServer.addBrowserActors("mail:3pane");
++      DebuggerServer.registerActors({ browser: true, root: true, tab: true, windowType: "mail:3pane" });
+     }
+ 
+     if (!DebuggerServer.createRootActor.isMailRootActor) {
+       // Register the Thunderbird root actor
+       DebuggerServer.registerModule("resource:///modules/tb-root-actor.js");
+     }
+   }
+ };

+ 32 - 0
comm-release/patches/1868034-registerAllActors-25319.patch

@@ -0,0 +1,32 @@
+# HG changeset patch
+# User Ian Neal <iann_cvs@blueyonder.co.uk>
+# Date 1701613497 0
+# Parent  908755d3b68d2c0052e2ad443f6df9fdaa7cdb1b
+Bug 1868034 - Migrate addBrowserActors to registerAllActors. r=frg a=frg
+
+diff --git a/suite/components/nsSuiteGlue.js b/suite/components/nsSuiteGlue.js
+--- a/suite/components/nsSuiteGlue.js
++++ b/suite/components/nsSuiteGlue.js
+@@ -1286,20 +1286,18 @@ SuiteGlue.prototype = {
+ 
+   dbgStart: function()
+   {
+     var port = Services.prefs.getIntPref(DEBUGGER_REMOTE_PORT);
+ 
+     // Make sure chrome debugging is enabled, no sense in starting otherwise.
+     DebuggerServer.allowChromeProcess = true;
+ 
+-    if (!DebuggerServer.initialized) {
+-      DebuggerServer.init();
+-      DebuggerServer.addBrowserActors();
+-    }
++    DebuggerServer.init();
++    DebuggerServer.registerAllActors();
+     try {
+       let listener = DebuggerServer.createListener();
+       listener.portOrPath = port;
+ 
+       // Expose this listener via wifi discovery, if enabled.
+       if (Services.prefs.getBoolPref(DEBUGGER_WIFI_VISIBLE) &&
+           !Services.prefs.getBoolPref(DEBUGGER_FORCE_LOCAL)) {
+         listener.discoverable = true;

+ 3 - 0
comm-release/patches/series

@@ -2139,3 +2139,6 @@ WIP-9999999-lintglobals.patch
 1453403-2-61a1.patch
 1611010-DOMEventListener-25319.patch
 9999999-czclean-25319.patch
+1363061-cc-60a1.patch
+1430501-61a1.patch
+1868034-registerAllActors-25319.patch

+ 66 - 0
mozilla-release/patches/1151156-59a1.patch

@@ -0,0 +1,66 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1513209374 21600
+# Node ID 4186b4a812fc4ef0cc126be45dcd8b3530465f80
+# Parent  dc6e9394f8e1fd7f96cefcec72ff3f46ac232262
+Bug 1151156 - remove unused b2g code in debugger-client onPacket;r=jryans
+
+MozReview-Commit-ID: IWyGUCKCFAM
+
+diff --git a/devtools/server/actors/root.js b/devtools/server/actors/root.js
+--- a/devtools/server/actors/root.js
++++ b/devtools/server/actors/root.js
+@@ -163,19 +163,16 @@ RootActor.prototype = {
+     noBlackBoxing: false,
+     noPrettyPrinting: false,
+     // Whether the page style actor implements the getUsedFontFaces method
+     // that returns the font faces used on a node
+     getUsedFontFaces: true,
+     // Trait added in Gecko 38, indicating that all features necessary for
+     // grabbing allocations from the MemoryActor are available for the performance tool
+     memoryActorAllocations: true,
+-    // Added in Gecko 40, indicating that the backend isn't stupid about
+-    // sending resumption packets on tab navigation.
+-    noNeedToFakeResumptionOnNavigation: true,
+     // Added in Firefox 40. Indicates that the backend supports registering custom
+     // commands through the WebConsoleCommands API.
+     webConsoleCommands: true,
+     // Whether root actor exposes tab actors and access to any window.
+     // If allowChromeProcess is true, you can:
+     // * get a ChromeActor instance to debug chrome and any non-content
+     //   resource via getProcess requests
+     // * get a WindowActor instance to debug windows which could be chrome,
+diff --git a/devtools/shared/client/debugger-client.js b/devtools/shared/client/debugger-client.js
+--- a/devtools/shared/client/debugger-client.js
++++ b/devtools/shared/client/debugger-client.js
+@@ -904,30 +904,16 @@ DebuggerClient.prototype = {
+ 
+     // Packets that indicate thread state changes get special treatment.
+     if (packet.type in ThreadStateTypes &&
+         this._clients.has(packet.from) &&
+         typeof this._clients.get(packet.from)._onThreadState == "function") {
+       this._clients.get(packet.from)._onThreadState(packet);
+     }
+ 
+-    // TODO: Bug 1151156 - Remove once Gecko 40 is on b2g-stable.
+-    if (!this.traits.noNeedToFakeResumptionOnNavigation) {
+-      // On navigation the server resumes, so the client must resume as well.
+-      // We achieve that by generating a fake resumption packet that triggers
+-      // the client's thread state change listeners.
+-      if (packet.type == UnsolicitedNotifications.tabNavigated &&
+-          this._clients.has(packet.from) &&
+-          this._clients.get(packet.from).thread) {
+-        let thread = this._clients.get(packet.from).thread;
+-        let resumption = { from: thread._actor, type: "resumed" };
+-        thread._onThreadState(resumption);
+-      }
+-    }
+-
+     // Only try to notify listeners on events, not responses to requests
+     // that lack a packet type.
+     if (packet.type) {
+       this.emit(packet.type, packet);
+     }
+ 
+     if (activeRequest) {
+       let emitReply = () => activeRequest.emit("json-reply", packet);

+ 647 - 0
mozilla-release/patches/1272774-1-60a1.patch

@@ -0,0 +1,647 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1516037914 -3600
+# Node ID ab564531dda980a4873e2cb33969c30c9509b6c1
+# Parent  1676550ce2fb8e6f3c52b94071de9760c740a033
+Bug 1272774 - migrate all listTabs() callers to use promise;r=ochameau
+
+MozReview-Commit-ID: 9PtRZHG5GuF
+
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js b/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
+@@ -18,17 +18,17 @@ function test() {
+   DebuggerServer.addActors(ACTORS_URL);
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+-    gClient.listTabs(aResponse => {
++    gClient.listTabs().then(aResponse => {
+       let globalActor = aResponse.testGlobalActor1;
+       ok(globalActor, "Found the test tab actor.");
+       ok(globalActor.includes("test_one"),
+         "testGlobalActor1's actorPrefix should be used.");
+ 
+       gClient.request({ to: globalActor, type: "ping" }, aResponse => {
+         is(aResponse.pong, "pong", "Actor should respond to requests.");
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js b/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
+@@ -40,17 +40,17 @@ function test() {
+ }
+ 
+ function testFirstTab(aTab) {
+   let deferred = promise.defer();
+ 
+   gNewTab = aTab;
+   ok(!!gNewTab, "Second tab created.");
+ 
+-  gClient.listTabs(aResponse => {
++  gClient.listTabs().then(aResponse => {
+     let tabActor = aResponse.tabs.filter(aGrip => aGrip.url == TAB1_URL).pop();
+     ok(tabActor,
+       "Should find a tab actor for the first tab.");
+ 
+     is(aResponse.selected, 1,
+       "The first tab is selected.");
+ 
+     deferred.resolve();
+@@ -70,17 +70,17 @@ function testNewWindow(aWindow) {
+   let topWindow = Services.wm.getMostRecentWindow("navigator:browser");
+   is(topWindow, gNewWindow,
+     "The second window is on top.");
+ 
+   let isActive = promise.defer();
+   let isLoaded = promise.defer();
+ 
+   promise.all([isActive.promise, isLoaded.promise]).then(() => {
+-    gClient.listTabs(aResponse => {
++    gClient.listTabs().then(aResponse => {
+       is(aResponse.selected, 2,
+         "The second tab is selected.");
+ 
+       deferred.resolve();
+     });
+   });
+ 
+   if (Services.focus.activeWindow != gNewWindow) {
+@@ -110,17 +110,17 @@ function testNewWindow(aWindow) {
+ 
+   return deferred.promise;
+ }
+ 
+ function testFocusFirst() {
+   let deferred = promise.defer();
+ 
+   once(window.content, "focus").then(() => {
+-    gClient.listTabs(aResponse => {
++    gClient.listTabs().then(aResponse => {
+       is(aResponse.selected, 1,
+         "The first tab is selected after focusing on it.");
+ 
+       deferred.resolve();
+     });
+   });
+ 
+   window.content.focus();
+@@ -137,17 +137,17 @@ function testRemoveTab() {
+   executeSoon(function () { continue_remove_tab(deferred); });
+   return deferred.promise;
+ }
+ 
+ function continue_remove_tab(deferred)
+ {
+   removeTab(gNewTab);
+ 
+-  gClient.listTabs(aResponse => {
++  gClient.listTabs().then(aResponse => {
+     // Verify that tabs are no longer included in listTabs.
+     let foundTab1 = aResponse.tabs.some(aGrip => aGrip.url == TAB1_URL);
+     let foundTab2 = aResponse.tabs.some(aGrip => aGrip.url == TAB2_URL);
+     ok(!foundTab1, "Tab1 should be gone.");
+     ok(!foundTab2, "Tab2 should be gone.");
+ 
+     is(aResponse.selected, 0,
+       "The original tab is selected.");
+diff --git a/devtools/client/debugger/test/mochitest/head.js b/devtools/client/debugger/test/mochitest/head.js
+--- a/devtools/client/debugger/test/mochitest/head.js
++++ b/devtools/client/debugger/test/mochitest/head.js
+@@ -156,17 +156,17 @@ function removeAddon(aAddon) {
+   aAddon.uninstall();
+ 
+   return deferred.promise;
+ }
+ 
+ function getTabActorForUrl(aClient, aUrl) {
+   let deferred = promise.defer();
+ 
+-  aClient.listTabs(aResponse => {
++  aClient.listTabs().then(aResponse => {
+     let tabActor = aResponse.tabs.filter(aGrip => aGrip.url == aUrl).pop();
+     deferred.resolve(tabActor);
+   });
+ 
+   return deferred.promise;
+ }
+ 
+ function getAddonActorForId(aClient, aAddonId) {
+diff --git a/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js b/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
+--- a/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
++++ b/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
+@@ -70,17 +70,17 @@ function getClient() {
+   let client = new DebuggerClient(transport);
+ 
+   return client.connect().then(() => client);
+ }
+ 
+ function getTarget(client) {
+   let deferred = defer();
+ 
+-  client.listTabs(tabList => {
++  client.listTabs().then(tabList => {
+     let target = TargetFactory.forRemoteTab({
+       client: client,
+       form: tabList.tabs[tabList.selected],
+       chrome: false
+     });
+     deferred.resolve(target);
+   });
+ 
+diff --git a/devtools/client/shared/test/test-actor-registry.js b/devtools/client/shared/test/test-actor-registry.js
+--- a/devtools/client/shared/test/test-actor-registry.js
++++ b/devtools/client/shared/test/test-actor-registry.js
+@@ -3,28 +3,25 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ "use strict";
+ 
+ (function (exports) {
+   const CC = Components.Constructor;
+ 
+   const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+   const { fetch } = require("devtools/shared/DevToolsUtils");
+-  const defer = require("devtools/shared/defer");
+   const { Task } = require("devtools/shared/task");
+ 
+   const TEST_URL_ROOT = "http://example.com/browser/devtools/client/shared/test/";
+   const ACTOR_URL = TEST_URL_ROOT + "test-actor.js";
+ 
+   // Register a test actor that can operate on the remote document
+   exports.registerTestActor = Task.async(function* (client) {
+     // First, instanciate ActorRegistryFront to be able to dynamically register an actor
+-    let deferred = defer();
+-    client.listTabs(deferred.resolve);
+-    let response = yield deferred.promise;
++    let response = yield client.listTabs();
+     let { ActorRegistryFront } = require("devtools/shared/fronts/actor-registry");
+     let registryFront = ActorRegistryFront(client, response);
+ 
+     // Then ask to register our test-actor to retrieve its front
+     let options = {
+       type: { tab: true },
+       constructor: "TestActor",
+       prefix: "testActor"
+diff --git a/devtools/docs/backend/client-api.md b/devtools/docs/backend/client-api.md
+--- a/devtools/docs/backend/client-api.md
++++ b/devtools/docs/backend/client-api.md
+@@ -75,17 +75,17 @@ function shutdown() {
+ 
+ ## Attaching to a browser tab
+ 
+ Attaching to a browser tab requires enumerating the available tabs and attaching to one:
+ 
+ ```javascript
+ function attachToTab() {
+   // Get the list of tabs to find the one to attach to.
+-  client.listTabs((response) => {
++  client.listTabs().then((response) => {
+     // Find the active tab.
+     let tab = response.tabs[response.selected];
+ 
+     // Attach to the tab.
+     client.attachTab(tab.actor, (response, tabClient) => {
+       if (!tabClient) {
+         return;
+       }
+@@ -186,17 +186,17 @@ function shutdownDebugger() {
+   client.close();
+ }
+ 
+ /**
+  * Start debugging the current tab.
+  */
+ function debugTab() {
+   // Get the list of tabs to find the one to attach to.
+-  client.listTabs(response => {
++  client.listTabs().then(response => {
+     // Find the active tab.
+     let tab = response.tabs[response.selected];
+     // Attach to the tab.
+     client.attachTab(tab.actor, (response, tabClient) => {
+       if (!tabClient) {
+         return;
+       }
+ 
+diff --git a/devtools/server/tests/browser/browser_register_actor.js b/devtools/server/tests/browser/browser_register_actor.js
+--- a/devtools/server/tests/browser/browser_register_actor.js
++++ b/devtools/server/tests/browser/browser_register_actor.js
+@@ -17,17 +17,17 @@ function test() {
+       let options = {
+         prefix: "helloActor",
+         constructor: "HelloActor",
+         type: { tab: true }
+       };
+ 
+       let registry = ActorRegistryFront(gClient, response);
+       registry.registerActor(actorURL, options).then(actorFront => {
+-        gClient.listTabs(res => {
++        gClient.listTabs().then(res => {
+           let tab = res.tabs[res.selected];
+           ok(!!tab.helloActor, "Hello actor must exist");
+ 
+           // Make sure actor's state is maintained across listTabs requests.
+           checkActorState(tab.helloActor, cleanupActor.bind(this, actorFront));
+         });
+       });
+     });
+diff --git a/devtools/server/tests/browser/head.js.1272774-1.later b/devtools/server/tests/browser/head.js.1272774-1.later
+new file mode 100644
+--- /dev/null
++++ b/devtools/server/tests/browser/head.js.1272774-1.later
+@@ -0,0 +1,25 @@
++--- head.js
+++++ head.js
++@@ -116,21 +116,17 @@ async function initPerfFront() {
++ }
++ 
++ /**
++  * Gets the RootActor form from a DebuggerClient.
++  * @param {DebuggerClient} client
++  * @return {RootActor} Resolves when connected.
++  */
++ function getRootForm(client) {
++-  return new Promise(resolve => {
++-    client.listTabs(rootForm => {
++-      resolve(rootForm);
++-    });
++-  });
+++  return client.listTabs();
++ }
++ 
++ /**
++  * Wait until a DebuggerClient is connected.
++  * @param {DebuggerClient} client
++  * @return {Promise} Resolves when connected.
++  */
++ function waitUntilClientConnected(client) {
+diff --git a/devtools/server/tests/mochitest/inspector-helpers.js b/devtools/server/tests/mochitest/inspector-helpers.js
+--- a/devtools/server/tests/mochitest/inspector-helpers.js
++++ b/devtools/server/tests/mochitest/inspector-helpers.js
+@@ -59,17 +59,17 @@ function attachURL(url, callback) {
+     }
+   };
+   gAttachCleanups.push(cleanup);
+ 
+   window.addEventListener("message", function loadListener(event) {
+     if (event.data === "ready") {
+       client = new DebuggerClient(DebuggerServer.connectPipe());
+       client.connect().then(([applicationType, traits]) => {
+-        client.listTabs(response => {
++        client.listTabs().then(response => {
+           for (let tab of response.tabs) {
+             if (tab.url === url) {
+               window.removeEventListener("message", loadListener);
+               // eslint-disable-next-line max-nested-callbacks
+               client.attachTab(tab.actor, function (_response, _tabClient) {
+                 try {
+                   callback(null, client, tab, win.document);
+                 } catch (ex) {
+diff --git a/devtools/server/tests/mochitest/test_device.html b/devtools/server/tests/mochitest/test_device.html
+--- a/devtools/server/tests/mochitest/test_device.html
++++ b/devtools/server/tests/mochitest/test_device.html
+@@ -24,17 +24,17 @@ window.onload = function () {
+ 
+   const {getDeviceFront} = require("devtools/shared/fronts/device");
+ 
+   DebuggerServer.init();
+   DebuggerServer.registerAllActors();
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+-    client.listTabs(function onListTabs(response) {
++    client.listTabs().then(function onListTabs(response) {
+       let d = getDeviceFront(client, response);
+ 
+       let desc;
+       let appInfo = Services.appinfo;
+       let utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIDOMWindowUtils);
+ 
+       let localDesc = {
+diff --git a/devtools/server/tests/mochitest/test_framerate_01.html b/devtools/server/tests/mochitest/test_framerate_01.html
+--- a/devtools/server/tests/mochitest/test_framerate_01.html
++++ b/devtools/server/tests/mochitest/test_framerate_01.html
+@@ -65,17 +65,17 @@ window.onload = function () {
+     return timeline;
+   }
+ 
+   DebuggerServer.init();
+   DebuggerServer.registerAllActors();
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+-    client.listTabs(function onListTabs(response) {
++    client.listTabs().then(function onListTabs(response) {
+       let form = response.tabs[response.selected];
+       let front = FramerateFront(client, form);
+ 
+       window.setTimeout(() => {
+         /* eslint-disable max-nested-callbacks */
+         front.startRecording().then(() => {
+           window.setTimeout(() => {
+             front.stopRecording().then(rawData => {
+diff --git a/devtools/server/tests/mochitest/test_framerate_02.html b/devtools/server/tests/mochitest/test_framerate_02.html
+--- a/devtools/server/tests/mochitest/test_framerate_02.html
++++ b/devtools/server/tests/mochitest/test_framerate_02.html
+@@ -65,17 +65,17 @@ window.onload = function () {
+     return timeline;
+   }
+ 
+   DebuggerServer.init();
+   DebuggerServer.registerAllActors();
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+-    client.listTabs(function onListTabs(response) {
++    client.listTabs().then(function onListTabs(response) {
+       let form = response.tabs[response.selected];
+       let front = FramerateFront(client, form);
+ 
+       front.stopRecording().then(rawData => {
+         ok(rawData, "There should be a recording available.");
+         is(rawData.length, 0, "...but it should be empty.");
+ 
+         let timeline = plotFPS(rawData);
+diff --git a/devtools/server/tests/mochitest/test_framerate_03.html b/devtools/server/tests/mochitest/test_framerate_03.html
+--- a/devtools/server/tests/mochitest/test_framerate_03.html
++++ b/devtools/server/tests/mochitest/test_framerate_03.html
+@@ -33,17 +33,17 @@ window.onload = function () {
+   const STOP_TICK = 3000;
+   const TOTAL_TIME = 5000;
+ 
+   DebuggerServer.init();
+   DebuggerServer.registerAllActors();
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+-    client.listTabs(function onListTabs(response) {
++    client.listTabs().then(function onListTabs(response) {
+       let form = response.tabs[response.selected];
+       let front = FramerateFront(client, form);
+ 
+       front.startRecording().then(() => {
+         /* eslint-disable max-nested-callbacks */
+         window.setTimeout(() => {
+           front.stopRecording(START_TICK, STOP_TICK).then(rawData => {
+             onRecordingStopped(front, rawData);
+diff --git a/devtools/server/tests/mochitest/test_framerate_05.html b/devtools/server/tests/mochitest/test_framerate_05.html
+--- a/devtools/server/tests/mochitest/test_framerate_05.html
++++ b/devtools/server/tests/mochitest/test_framerate_05.html
+@@ -30,17 +30,17 @@ window.onload = function () {
+ 
+   const {FramerateFront} = require("devtools/shared/fronts/framerate");
+ 
+   DebuggerServer.init();
+   DebuggerServer.registerAllActors();
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+-    client.listTabs(function onListTabs(response) {
++    client.listTabs().then(function onListTabs(response) {
+       let form = response.tabs[response.selected];
+       let front = FramerateFront(client, form);
+ 
+       front.startRecording().then(() => {
+         /* eslint-disable max-nested-callbacks */
+         window.setTimeout(() => {
+           front.cancelRecording().then(() => {
+             window.setTimeout(() => {
+diff --git a/devtools/server/tests/mochitest/test_preference.html b/devtools/server/tests/mochitest/test_preference.html
+--- a/devtools/server/tests/mochitest/test_preference.html
++++ b/devtools/server/tests/mochitest/test_preference.html
+@@ -24,17 +24,17 @@ function runTests() {
+ 
+   let {getPreferenceFront} = require("devtools/shared/fronts/preference");
+ 
+   DebuggerServer.init();
+   DebuggerServer.registerAllActors();
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+-    client.listTabs(function onListTabs(response) {
++    client.listTabs().then(function onListTabs(response) {
+       let p = getPreferenceFront(client, response);
+ 
+       let prefs = {};
+ 
+       let localPref = {
+         boolPref: true,
+         intPref: 0x1234,
+         charPref: "Hello World",
+diff --git a/devtools/server/tests/unit/head_dbg.js b/devtools/server/tests/unit/head_dbg.js
+--- a/devtools/server/tests/unit/head_dbg.js
++++ b/devtools/server/tests/unit/head_dbg.js
+@@ -344,17 +344,17 @@ function addTestGlobal(name, server = De
+   let global = testGlobal(name);
+   server.addTestGlobal(global);
+   return global;
+ }
+ 
+ // List the DebuggerClient |client|'s tabs, look for one whose title is
+ // |title|, and apply |callback| to the packet's entry for that tab.
+ function getTestTab(client, title, callback) {
+-  client.listTabs(function (response) {
++  client.listTabs().then(function (response) {
+     for (let tab of response.tabs) {
+       if (tab.title === title) {
+         callback(tab, response);
+         return;
+       }
+     }
+     callback(null);
+   });
+diff --git a/devtools/server/tests/unit/test_actor-registry-actor.js b/devtools/server/tests/unit/test_actor-registry-actor.js
+--- a/devtools/server/tests/unit/test_actor-registry-actor.js
++++ b/devtools/server/tests/unit/test_actor-registry-actor.js
+@@ -20,17 +20,17 @@ function run_test() {
+   initTestDebuggerServer();
+   DebuggerServer.registerAllActors();
+   gClient = new DebuggerClient(DebuggerServer.connectPipe());
+   gClient.connect().then(getRegistry);
+   do_test_pending();
+ }
+ 
+ function getRegistry() {
+-  gClient.listTabs((response) => {
++  gClient.listTabs().then((response) => {
+     gRegistryFront = ActorRegistryFront(gClient, response);
+     registerNewActor();
+   });
+ }
+ 
+ function registerNewActor() {
+   let options = {
+     prefix: "helloActor",
+@@ -44,17 +44,17 @@ function registerNewActor() {
+     .then(talkToNewActor)
+     .catch(e => {
+       DevToolsUtils.reportException("registerNewActor", e);
+       Assert.ok(false);
+     });
+ }
+ 
+ function talkToNewActor() {
+-  gClient.listTabs(({ helloActor }) => {
++  gClient.listTabs().then(({ helloActor }) => {
+     Assert.ok(!!helloActor);
+     gClient.request({
+       to: helloActor,
+       type: "hello"
+     }, response => {
+       Assert.ok(!response.error);
+       unregisterNewActor();
+     });
+@@ -67,15 +67,15 @@ function unregisterNewActor() {
+     .then(testActorIsUnregistered)
+     .catch(e => {
+       DevToolsUtils.reportException("unregisterNewActor", e);
+       Assert.ok(false);
+     });
+ }
+ 
+ function testActorIsUnregistered() {
+-  gClient.listTabs(({ helloActor }) => {
++  gClient.listTabs().then(({ helloActor }) => {
+     Assert.ok(!helloActor);
+ 
+     Services.prefs.setBoolPref("devtools.debugger.forbid-certified-apps", gOldPref);
+     finishClient(gClient);
+   });
+ }
+diff --git a/devtools/server/tests/unit/test_add_actors.js b/devtools/server/tests/unit/test_add_actors.js
+--- a/devtools/server/tests/unit/test_add_actors.js
++++ b/devtools/server/tests/unit/test_add_actors.js
+@@ -85,17 +85,17 @@ function getActorInstance(connID, actorI
+ }
+ 
+ function test_stable_global_actor_instances() {
+   // Consider that there is only one connection,
+   // and the first one is ours
+   let connID = Object.keys(DebuggerServer._connections)[0];
+   let postInitGlobalActor = getActorInstance(connID, gActors.postInitGlobalActor);
+   let preInitGlobalActor = getActorInstance(connID, gActors.preInitGlobalActor);
+-  gClient.listTabs(function onListTabs(response) {
++  gClient.listTabs().then(function onListTabs(response) {
+     Assert.equal(postInitGlobalActor,
+                  getActorInstance(connID, response.postInitGlobalActor));
+     Assert.equal(preInitGlobalActor,
+                  getActorInstance(connID, response.preInitGlobalActor));
+     run_next_test();
+   });
+ }
+ 
+diff --git a/devtools/server/tests/unit/test_dbgactor.js b/devtools/server/tests/unit/test_dbgactor.js
+--- a/devtools/server/tests/unit/test_dbgactor.js
++++ b/devtools/server/tests/unit/test_dbgactor.js
+@@ -11,17 +11,17 @@ const xpcInspector = Cc["@mozilla.org/js
+ function run_test() {
+   initTestDebuggerServer();
+   gDebuggee = testGlobal("test-1");
+   DebuggerServer.addTestGlobal(gDebuggee);
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.addListener("connected", function (event, type, traits) {
+-    gClient.listTabs((response) => {
++    gClient.listTabs().then((response) => {
+       Assert.ok("tabs" in response);
+       for (let tab of response.tabs) {
+         if (tab.title == "test-1") {
+           test_attach_tab(tab.actor);
+           return false;
+         }
+       }
+       // We should have found our tab in the list.
+diff --git a/devtools/server/tests/unit/test_register_actor.js b/devtools/server/tests/unit/test_register_actor.js
+--- a/devtools/server/tests/unit/test_register_actor.js
++++ b/devtools/server/tests/unit/test_register_actor.js
+@@ -68,17 +68,17 @@ function test_lazy_api() {
+   // The actor is immediatly registered, but not loaded
+   Assert.ok(DebuggerServer.tabActorFactories.hasOwnProperty("lazyActor"));
+   Assert.ok(DebuggerServer.globalActorFactories.hasOwnProperty("lazyActor"));
+   Assert.ok(!isActorLoaded);
+   Assert.ok(!isActorInstanciated);
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+-    client.listTabs(onListTabs);
++    client.listTabs().then(onListTabs);
+   });
+   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(!isActorInstanciated);
+     Assert.ok("lazyActor" in response);
+ 
+diff --git a/devtools/shared/security/tests/unit/test_encryption.js b/devtools/shared/security/tests/unit/test_encryption.js
+--- a/devtools/shared/security/tests/unit/test_encryption.js
++++ b/devtools/shared/security/tests/unit/test_encryption.js
+@@ -8,21 +8,17 @@ function run_test() {
+   // Need profile dir to store the key / cert
+   do_get_profile();
+   // Ensure PSM is initialized
+   Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
+   run_next_test();
+ }
+ 
+ function connectClient(client) {
+-  let deferred = defer();
+-  client.connect(() => {
+-    client.listTabs(deferred.resolve);
+-  });
+-  return deferred.promise;
++  return client.connect(() => client.listTabs());
+ }
+ 
+ add_task(function* () {
+   initTestDebuggerServer();
+ });
+ 
+ // Client w/ encryption connects successfully to server w/ encryption
+ add_task(function* () {
+diff --git a/devtools/shared/transport/tests/unit/test_client_server_bulk.js b/devtools/shared/transport/tests/unit/test_client_server_bulk.js
+--- a/devtools/shared/transport/tests/unit/test_client_server_bulk.js
++++ b/devtools/shared/transport/tests/unit/test_client_server_bulk.js
+@@ -142,17 +142,17 @@ var test_bulk_request_cs = Task.async(fu
+   let serverDeferred = defer();
+   let bulkCopyDeferred = defer();
+ 
+   let transport = yield transportFactory();
+ 
+   let client = new DebuggerClient(transport);
+   client.connect().then(([app, traits]) => {
+     Assert.equal(traits.bulk, true);
+-    client.listTabs(clientDeferred.resolve);
++    client.listTabs().then(clientDeferred.resolve);
+   });
+ 
+   function bulkSendReadyCallback({copyFrom}) {
+     NetUtil.asyncFetch({
+       uri: NetUtil.newURI(getTestTempFile("bulk-input")),
+       loadUsingSystemPrincipal: true
+     }, input => {
+       copyFrom(input).then(() => {
+@@ -200,17 +200,17 @@ var test_json_request_cs = Task.async(fu
+   let clientDeferred = defer();
+   let serverDeferred = defer();
+ 
+   let transport = yield transportFactory();
+ 
+   let client = new DebuggerClient(transport);
+   client.connect((app, traits) => {
+     Assert.equal(traits.bulk, true);
+-    client.listTabs(clientDeferred.resolve);
++    client.listTabs().then(clientDeferred.resolve);
+   });
+ 
+   clientDeferred.promise.then(response => {
+     let request = client.request({
+       to: response.testBulk,
+       type: actorType
+     });
+ 

+ 450 - 0
mozilla-release/patches/1272774-2-60a1.patch

@@ -0,0 +1,450 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1515083145 -3600
+# Node ID 357cf743b5821101c7458431563917d7db509be7
+# Parent  2755d43a3c81c3d0fc6e4f0ff47d94d678b7919b
+Bug 1272774 - allow listTabs to return favicon data from PlacesUtils;r=ochameau
+
+MozReview-Commit-ID: 8bkn3mG6YkL
+
+diff --git a/devtools/client/aboutdebugging/components/tabs/Panel.js b/devtools/client/aboutdebugging/components/tabs/Panel.js
+--- a/devtools/client/aboutdebugging/components/tabs/Panel.js
++++ b/devtools/client/aboutdebugging/components/tabs/Panel.js
+@@ -46,39 +46,32 @@ class TabsPanel extends Component {
+     this.update();
+   }
+ 
+   componentWillUnmount() {
+     let { client } = this.props;
+     client.removeListener("tabListChanged", this.update);
+   }
+ 
+-  update() {
+-    this.props.client.mainRoot.listTabs().then(({ tabs }) => {
+-      // Filter out closed tabs (represented as `null`).
+-      tabs = tabs.filter(tab => !!tab);
+-      tabs.forEach(tab => {
+-        // FIXME Also try to fetch low-res favicon. But we should use actor
+-        // support for this to get the high-res one (bug 1061654).
+-        let url = new URL(tab.url);
+-        if (url.protocol.startsWith("http")) {
+-          let prePath = url.origin;
+-          let idx = url.pathname.lastIndexOf("/");
+-          if (idx === -1) {
+-            prePath += url.pathname;
+-          } else {
+-            prePath += url.pathname.substr(0, idx);
+-          }
+-          tab.icon = prePath + "/favicon.ico";
+-        } else {
+-          tab.icon = "chrome://devtools/skin/images/globe.svg";
+-        }
+-      });
+-      this.setState({ tabs });
+-    });
++  async update() {
++    let { tabs } = await this.props.client.mainRoot.listTabs({ favicons: true });
++
++    // Filter out closed tabs (represented as `null`).
++    tabs = tabs.filter(tab => !!tab);
++
++    for (let tab of tabs) {
++      if (tab.favicon) {
++        let base64Favicon = btoa(String.fromCharCode.apply(String, tab.favicon));
++        tab.icon = "data:image/png;base64," + base64Favicon;
++      } else {
++        tab.icon = "chrome://devtools/skin/images/globe.svg";
++      }
++    }
++
++    this.setState({ tabs });
+   }
+ 
+   render() {
+     let { client, connect, id } = this.props;
+     let { tabs } = this.state;
+ 
+     return dom.div({
+       id: id + "-panel",
+diff --git a/devtools/client/aboutdebugging/test/browser_tabs.js b/devtools/client/aboutdebugging/test/browser_tabs.js
+--- a/devtools/client/aboutdebugging/test/browser_tabs.js
++++ b/devtools/client/aboutdebugging/test/browser_tabs.js
+@@ -24,16 +24,33 @@ add_task(function* () {
+   info("Wait for the tab to appear in the list with the correct name");
+   let container = yield waitUntilTabContainer("foo", document);
+ 
+   info("Wait until the title to update");
+   yield waitUntil(() => {
+     return container.querySelector(".target-name").title === TAB_URL;
+   }, 100);
+ 
++  let icon = container.querySelector(".target-icon");
++  ok(icon && icon.src, "Tab icon found and src attribute is not empty");
++
++  info("Check if the tab icon is a valid image");
++  yield new Promise(r => {
++    let image = new Image();
++    image.onload = () => {
++      ok(true, "Favicon is not a broken image");
++      r();
++    };
++    image.onerror = () => {
++      ok(false, "Favicon is a broken image");
++      r();
++    };
++    image.src = icon.src;
++  });
++
+   // Finally, close the tab
+   yield removeTab(newTab);
+ 
+   info("Wait until the tab container is removed");
+   yield waitUntil(() => !getTabContainer("foo", document), 100);
+ 
+   // Check that the tab disappeared from the UI
+   names = [...tabsElement.querySelectorAll("#tabs .target-name")];
+diff --git a/devtools/server/actors/root.js b/devtools/server/actors/root.js
+--- a/devtools/server/actors/root.js
++++ b/devtools/server/actors/root.js
+@@ -281,17 +281,17 @@ RootActor.prototype = {
+    * the next listTabs request.
+    *
+    * ⚠ WARNING ⚠ This can be a very expensive operation, especially if there are many
+    * open tabs.  It will cause us to visit every tab, load a frame script, start a
+    * debugger server, and read some data.  With lazy tab support (bug 906076), this
+    * would trigger any lazy tabs to be loaded, greatly increasing resource usage.  Avoid
+    * this method whenever possible.
+    */
+-  onListTabs: async function () {
++  onListTabs: async function (request) {
+     let tabList = this._parameters.tabList;
+     if (!tabList) {
+       return { from: this.actorID, error: "noTabs",
+                message: "This root actor has no browser tabs." };
+     }
+ 
+     // Now that a client has requested the list of tabs, we reattach the onListChanged
+     // listener in order to be notified if the list of tabs changes again in the future.
+@@ -300,17 +300,18 @@ RootActor.prototype = {
+     // Walk the tab list, accumulating the array of tab actors for the reply, and moving
+     // all the actors to a new ActorPool. We'll replace the old tab actor pool with the
+     // one we build here, thus retiring any actors that didn't get listed again, and
+     // preparing any new actors to receive packets.
+     let newActorPool = new ActorPool(this.conn);
+     let tabActorList = [];
+     let selected;
+ 
+-    let tabActors = await tabList.getList();
++    let options = request.options || {};
++    let tabActors = await tabList.getList(options);
+     for (let tabActor of tabActors) {
+       if (tabActor.exited) {
+         // Tab actor may have exited while we were gathering the list.
+         continue;
+       }
+       if (tabActor.selected) {
+         selected = tabActorList.length;
+       }
+diff --git a/devtools/server/actors/webbrowser.js b/devtools/server/actors/webbrowser.js
+--- a/devtools/server/actors/webbrowser.js
++++ b/devtools/server/actors/webbrowser.js
+@@ -15,16 +15,17 @@ var DevToolsUtils = require("devtools/sh
+ 
+ loader.lazyRequireGetter(this, "RootActor", "devtools/server/actors/root", true);
+ loader.lazyRequireGetter(this, "BrowserAddonActor", "devtools/server/actors/addon", true);
+ loader.lazyRequireGetter(this, "WebExtensionParentActor", "devtools/server/actors/webextension-parent", true);
+ loader.lazyRequireGetter(this, "WorkerActorList", "devtools/server/actors/worker-list", true);
+ loader.lazyRequireGetter(this, "ServiceWorkerRegistrationActorList", "devtools/server/actors/worker-list", true);
+ loader.lazyRequireGetter(this, "ProcessActorList", "devtools/server/actors/process", true);
+ loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
++loader.lazyImporter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm");
+ 
+ /**
+  * Browser-specific actors.
+  */
+ 
+ /**
+  * Yield all windows of type |windowType|, from the oldest window to the
+  * youngest, using nsIWindowMediator::getEnumerator. We're usually
+@@ -251,17 +252,17 @@ BrowserTabList.prototype._getChildren = 
+   return gBrowser.browsers.filter(browser => {
+     // Filter tabs that are closing. listTabs calls made right after TabClose
+     // events still list tabs in process of being closed.
+     let tab = gBrowser.getTabForBrowser(browser);
+     return !tab.closing;
+   });
+ };
+ 
+-BrowserTabList.prototype.getList = function () {
++BrowserTabList.prototype.getList = function (browserActorOptions) {
+   let topXULWindow = Services.wm.getMostRecentWindow(
+     DebuggerServer.chromeWindowType);
+   let selectedBrowser = null;
+   if (topXULWindow) {
+     selectedBrowser = this._getSelectedBrowser(topXULWindow);
+   }
+ 
+   // As a sanity check, make sure all the actors presently in our map get
+@@ -274,17 +275,17 @@ BrowserTabList.prototype.getList = funct
+   // the actors. Thus, the sequence yielded is always a snapshot of the
+   // actors that were live when we began the iteration.
+ 
+   let actorPromises = [];
+ 
+   for (let browser of this._getBrowsers()) {
+     let selected = browser === selectedBrowser;
+     actorPromises.push(
+-      this._getActorForBrowser(browser)
++      this._getActorForBrowser(browser, browserActorOptions)
+           .then(actor => {
+             // Set the 'selected' properties on all actors correctly.
+             actor.selected = selected;
+             return actor;
+           }, e => {
+             if (e.error === "tabDestroyed") {
+               // Return null if a tab was destroyed while retrieving the tab list.
+               return null;
+@@ -304,25 +305,28 @@ BrowserTabList.prototype.getList = funct
+   this._checkListening();
+ 
+   return promise.all(actorPromises).then(values => {
+     // Filter out null values if we received a tabDestroyed error.
+     return values.filter(value => value != null);
+   });
+ };
+ 
+-BrowserTabList.prototype._getActorForBrowser = function (browser) {
++/**
++ * @param browserActorOptions see options argument of BrowserTabActor constructor.
++ */
++BrowserTabList.prototype._getActorForBrowser = function (browser, browserActorOptions) {
+   // Do we have an existing actor for this browser? If not, create one.
+   let actor = this._actorByBrowser.get(browser);
+   if (actor) {
+     this._foundCount++;
+-    return actor.update();
++    return actor.update(browserActorOptions);
+   }
+ 
+-  actor = new BrowserTabActor(this._connection, browser);
++  actor = new BrowserTabActor(this._connection, browser, browserActorOptions);
+   this._actorByBrowser.set(browser, actor);
+   this._checkListening();
+   return actor.connect();
+ };
+ 
+ BrowserTabList.prototype.getTab = function ({ outerWindowID, tabId }) {
+   if (typeof outerWindowID == "number") {
+     // First look for in-process frames with this ID
+@@ -690,78 +694,110 @@ exports.BrowserTabList = BrowserTabList;
+ /**
+  * Creates a tab actor for handling requests to a single browser frame.
+  * Both <xul:browser> and <iframe mozbrowser> are supported.
+  * This actor is a shim that connects to a ContentActor in a remote browser process.
+  * All RDP packets get forwarded using the message manager.
+  *
+  * @param connection The main RDP connection.
+  * @param browser <xul:browser> or <iframe mozbrowser> element to connect to.
++ * @param options
++ *        - {Boolean} favicons: true if the form should include the favicon for the tab.
+  */
+-function BrowserTabActor(connection, browser) {
++function BrowserTabActor(connection, browser, options = {}) {
+   this._conn = connection;
+   this._browser = browser;
+   this._form = null;
+   this.exited = false;
++  this.options = options;
+ }
+ 
+ BrowserTabActor.prototype = {
+-  connect() {
++  async connect() {
+     let onDestroy = () => {
+       if (this._deferredUpdate) {
+         // Reject the update promise if the tab was destroyed while requesting an update
+         this._deferredUpdate.reject({
+           error: "tabDestroyed",
+           message: "Tab destroyed while performing a BrowserTabActor update"
+         });
+       }
+       this.exit();
+     };
+     let connect = DebuggerServer.connectToChild(this._conn, this._browser, onDestroy);
+-    return connect.then(form => {
+-      this._form = form;
+-      return this;
+-    });
++    let form = await connect;
++
++    this._form = form;
++    if (this.options.favicons) {
++      this._form.favicon = await this.getFaviconData();
++    }
++
++    return this;
+   },
+ 
+   get _tabbrowser() {
+     if (this._browser && typeof this._browser.getTabBrowser == "function") {
+       return this._browser.getTabBrowser();
+     }
+     return null;
+   },
+ 
+   get _mm() {
+     // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
+     // or else fallback to asking the frameLoader itself.
+     return this._browser.messageManager ||
+            this._browser.frameLoader.messageManager;
+   },
+ 
+-  update() {
++  async getFaviconData() {
++    try {
++      let { data } = await PlacesUtils.promiseFaviconData(this._form.url);
++      return data;
++    } catch (e) {
++      // Favicon unavailable for this url.
++      return null;
++    }
++  },
++
++  /**
++   * @param {Object} options
++   *        See BrowserTabActor constructor.
++   */
++  async update(options = {}) {
++    // Update the BrowserTabActor options.
++    this.options = options;
++
+     // If the child happens to be crashed/close/detach, it won't have _form set,
+     // so only request form update if some code is still listening on the other
+     // side.
+-    if (!this.exited) {
+-      this._deferredUpdate = defer();
++    if (this.exited) {
++      return this.connect();
++    }
++
++    let form = await new Promise(resolve => {
+       let onFormUpdate = msg => {
+         // There may be more than just one childtab.js up and running
+         if (this._form.actor != msg.json.actor) {
+           return;
+         }
+         this._mm.removeMessageListener("debug:form", onFormUpdate);
+-        this._form = msg.json;
+-        this._deferredUpdate.resolve(this);
++
++        resolve(msg.json);
+       };
++
+       this._mm.addMessageListener("debug:form", onFormUpdate);
+       this._mm.sendAsyncMessage("debug:form");
+-      return this._deferredUpdate.promise;
++    });
++
++    this._form = form;
++    if (this.options.favicons) {
++      this._form.favicon = await this.getFaviconData();
+     }
+ 
+-    return this.connect();
++    return this;
+   },
+ 
+   /**
+    * If we don't have a title from the content side because it's a zombie tab, try to find
+    * it on the chrome side.
+    */
+   get title() {
+     // On Fennec, we can check the session store data for zombie tabs
+@@ -804,16 +840,17 @@ BrowserTabActor.prototype = {
+     // restored) are a good example.  In such cases, try to look up values for these
+     // fields using other data in the parent process.
+     if (!form.title) {
+       form.title = this.title;
+     }
+     if (!form.url) {
+       form.url = this.url;
+     }
++
+     return form;
+   },
+ 
+   exit() {
+     this._browser = null;
+     this._form = null;
+     this.exited = true;
+   },
+diff --git a/devtools/shared/client/debugger-client.js b/devtools/shared/client/debugger-client.js
+--- a/devtools/shared/client/debugger-client.js
++++ b/devtools/shared/client/debugger-client.js
+@@ -310,18 +310,18 @@ DebuggerClient.prototype = {
+ 
+     return deferred.promise;
+   },
+ 
+   /*
+    * This function exists only to preserve DebuggerClient's interface;
+    * new code should say 'client.mainRoot.listTabs()'.
+    */
+-  listTabs: function (onResponse) {
+-    return this.mainRoot.listTabs(onResponse);
++  listTabs: function (options, onResponse) {
++    return this.mainRoot.listTabs(options, onResponse);
+   },
+ 
+   /*
+    * This function exists only to preserve DebuggerClient's interface;
+    * new code should say 'client.mainRoot.listAddons()'.
+    */
+   listAddons: function (onResponse) {
+     return this.mainRoot.listAddons(onResponse);
+diff --git a/devtools/shared/client/root-client.js b/devtools/shared/client/root-client.js
+--- a/devtools/shared/client/root-client.js
++++ b/devtools/shared/client/root-client.js
+@@ -1,16 +1,16 @@
+ /* 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 { Ci } = require("chrome");
+-const {DebuggerClient} = require("devtools/shared/client/debugger-client");
++const { arg, DebuggerClient } = require("devtools/shared/client/debugger-client");
+ 
+ /**
+  * A RootClient object represents a root actor on the server. Each
+  * DebuggerClient keeps a RootClient instance representing the root actor
+  * for the initial connection; DebuggerClient's 'listTabs' and
+  * 'listChildProcesses' methods forward to that root actor.
+  *
+  * @param client object
+@@ -43,20 +43,23 @@ RootClient.prototype = {
+    * browser.  This can replace usages of `listTabs` that only wanted the global actors
+    * and didn't actually care about tabs.
+    */
+   getRoot: DebuggerClient.requester({ type: "getRoot" }),
+ 
+    /**
+    * List the open tabs.
+    *
++   * @param object options
++   *        Optional flags for listTabs:
++   *        - boolean favicons: return favicon data
+    * @param function onResponse
+    *        Called with the response packet.
+    */
+-  listTabs: DebuggerClient.requester({ type: "listTabs" }),
++  listTabs: DebuggerClient.requester({ type: "listTabs", options: arg(0) }),
+ 
+   /**
+    * List the installed addons.
+    *
+    * @param function onResponse
+    *        Called with the response packet.
+    */
+   listAddons: DebuggerClient.requester({ type: "listAddons" }),

+ 56 - 0
mozilla-release/patches/1272774-3-60a1.patch

@@ -0,0 +1,56 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1515142623 -3600
+# Node ID 22254eef5a3acd5e4ef61e5dff1a500572b358c6
+# Parent  5c0113d9a21769950ea2a4813c00583107257470
+Bug 1272774 - remove unused update() method on tab actor;r=ochameau
+
+MozReview-Commit-ID: FdrEAsIBsvj
+
+diff --git a/devtools/server/actors/tab.js b/devtools/server/actors/tab.js
+--- a/devtools/server/actors/tab.js
++++ b/devtools/server/actors/tab.js
+@@ -10,17 +10,16 @@
+ 
+ // For performance matters, this file should only be loaded in the targeted
+ // document 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");
+ var { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
+-var promise = require("promise");
+ var {
+   ActorPool, createExtraActors, appendExtraActors
+ } = require("devtools/server/actors/common");
+ var { DebuggerServer } = require("devtools/server/main");
+ var DevToolsUtils = require("devtools/shared/DevToolsUtils");
+ var { assert } = DevToolsUtils;
+ var { TabSources } = require("./utils/TabSources");
+ var makeDebugger = require("./utils/make-debugger");
+@@ -438,25 +437,16 @@ TabActor.prototype = {
+ 
+   get sources() {
+     if (!this._sources) {
+       this._sources = new TabSources(this.threadActor, this._allowSource);
+     }
+     return this._sources;
+   },
+ 
+-  /**
+-   * This is called by BrowserTabList.getList for existing tab actors prior to
+-   * calling |form| below.  It can be used to do any async work that may be
+-   * needed to assemble the form.
+-   */
+-  update() {
+-    return promise.resolve(this);
+-  },
+-
+   form() {
+     assert(!this.exited,
+                "form() shouldn't be called on exited browser actor.");
+     assert(this.actorID,
+                "tab should have an actorID.");
+ 
+     let response = {
+       actor: this.actorID

+ 32 - 0
mozilla-release/patches/1272774-4-60a1.patch

@@ -0,0 +1,32 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1515143218 -3600
+# Node ID 93aa8ac595221243c4463323f696ec74e49922ee
+# Parent  5f27c9f99ce84164e34509234516a525442e821d
+Bug 1272774 - remove irrelevant cleanup comment;r=ochameau
+
+MozReview-Commit-ID: KsULTBH9bGL
+
+diff --git a/devtools/server/actors/tab.js b/devtools/server/actors/tab.js
+--- a/devtools/server/actors/tab.js
++++ b/devtools/server/actors/tab.js
+@@ -232,19 +232,16 @@ function TabActor(connection) {
+     logErrorInPage: true,
+   };
+ 
+   this._workerActorList = null;
+   this._workerActorPool = null;
+   this._onWorkerActorListChanged = this._onWorkerActorListChanged.bind(this);
+ }
+ 
+-// XXX (bug 710213): TabActor attach/detach/exit/destroy is a
+-// *complete* mess, needs to be rethought asap.
+-
+ TabActor.prototype = {
+   traits: null,
+ 
+   // Optional console API listener options (e.g. used by the WebExtensionActor to
+   // filter console messages by addonID), set to an empty (no options) object by default.
+   consoleAPIListenerOptions: {},
+ 
+   // Optional TabSources filter function (e.g. used by the WebExtensionActor to filter

+ 102 - 0
mozilla-release/patches/1272774-5-60a1.patch

@@ -0,0 +1,102 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1515143464 -3600
+# Node ID ec6bd22c4d0034a2ee2dc12781406f2d74202295
+# Parent  0a8e61aa34f16c20ebbebbe7ba5013c42e7be2a1
+Bug 1272774 - rename actor/childtab.js to actor/content.js to match ContentActor;r=ochameau
+
+MozReview-Commit-ID: BJ8zjFeiZPD
+
+diff --git a/devtools/docs/backend/actor-hierarchy.md b/devtools/docs/backend/actor-hierarchy.md
+--- a/devtools/docs/backend/actor-hierarchy.md
++++ b/devtools/docs/backend/actor-hierarchy.md
+@@ -43,17 +43,17 @@ RootActor (root.js)
+    |
+    |-- BrowserTabActor (webbrowser.js)
+    |   Targets tabs living in the parent or child process. Note that this is
+    |   just a proxy for ContentActor, which is loaded via the tab's message
+    |   manager as a frame script in the process containing the tab. This proxy
+    |   via message manager is always used, even when e10s is disabled.
+    |   Returned by "listTabs" or "getTab" requests.
+    |   |
+-   |   \-> ContentActor (childtab.js)
++   |   \-> ContentActor (content.js)
+    |       The "real" actor for a tab, which runs in whichever process holds the
+    |       content.  BrowserTabActor communicates with this via the tab's
+    |       message manager.
+    |       Returned by "connect" on BrowserTabActor.
+    |
+    |-- WorkerActor (worker.js)
+    |   Targets a worker (applies to various kinds like web worker, service
+    |   worker, etc.).
+diff --git a/devtools/server/actors/childtab.js b/devtools/server/actors/content.js
+rename from devtools/server/actors/childtab.js
+rename to devtools/server/actors/content.js
+diff --git a/devtools/server/actors/moz.build b/devtools/server/actors/moz.build
+--- a/devtools/server/actors/moz.build
++++ b/devtools/server/actors/moz.build
+@@ -15,19 +15,19 @@ DevToolsModules(
+     'actor-registry.js',
+     'addon.js',
+     'addons.js',
+     'animation.js',
+     'breakpoint.js',
+     'call-watcher.js',
+     'canvas.js',
+     'child-process.js',
+-    'childtab.js',
+     'chrome.js',
+     'common.js',
++    'content.js',
+     'css-properties.js',
+     'csscoverage.js',
+     'device.js',
+     'emulation.js',
+     'environment.js',
+     'errordocs.js',
+     'frame.js',
+     'framerate.js',
+diff --git a/devtools/server/actors/webbrowser.js b/devtools/server/actors/webbrowser.js
+--- a/devtools/server/actors/webbrowser.js
++++ b/devtools/server/actors/webbrowser.js
+@@ -769,17 +769,17 @@ BrowserTabActor.prototype = {
+     // so only request form update if some code is still listening on the other
+     // side.
+     if (this.exited) {
+       return this.connect();
+     }
+ 
+     let form = await new Promise(resolve => {
+       let onFormUpdate = msg => {
+-        // There may be more than just one childtab.js up and running
++        // There may be more than just one content.js (ContentActor) up and running
+         if (this._form.actor != msg.json.actor) {
+           return;
+         }
+         this._mm.removeMessageListener("debug:form", onFormUpdate);
+ 
+         resolve(msg.json);
+       };
+ 
+diff --git a/devtools/server/child.js b/devtools/server/child.js
+--- a/devtools/server/child.js
++++ b/devtools/server/child.js
+@@ -37,17 +37,17 @@ try {
+       connections.set(prefix, conn);
+ 
+       let actor;
+ 
+       if (addonId) {
+         const { WebExtensionChildActor } = require("devtools/server/actors/webextension");
+         actor = new WebExtensionChildActor(conn, chromeGlobal, prefix, addonId);
+       } else {
+-        const { ContentActor } = require("devtools/server/actors/childtab");
++        const { ContentActor } = require("devtools/server/actors/content");
+         actor = new ContentActor(conn, chromeGlobal, prefix);
+       }
+ 
+       let actorPool = new ActorPool(conn);
+       actorPool.addActor(actor);
+       conn.addActorPool(actorPool);
+ 
+       sendAsyncMessage("debug:actor", {actor: actor.form(), prefix: prefix});

+ 411 - 0
mozilla-release/patches/1363061-60a1.patch

@@ -0,0 +1,411 @@
+# HG changeset patch
+# User Paolo Amadini <paolo.mozmail@amadzone.org>
+# Date 1520358059 0
+# Node ID af5fa4cbcf26422e4bb0693920c13db600b0ae84
+# Parent  0213baa9a533daa668bef8a3920adad040101a33
+Bug 1363061 - Rename toolkit/components/jsdownloads to toolkit/components/downloads. r=mak
+
+MozReview-Commit-ID: GuWeax0ubv3
+
+diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in
+--- a/browser/installer/package-manifest.in
++++ b/browser/installer/package-manifest.in
+@@ -241,17 +241,16 @@
+ @RESPATH@/components/gfx.xpt
+ @RESPATH@/components/html5.xpt
+ @RESPATH@/components/htmlparser.xpt
+ @RESPATH@/components/imglib2.xpt
+ @RESPATH@/components/inspector.xpt
+ @RESPATH@/components/intl.xpt
+ @RESPATH@/components/jar.xpt
+ @RESPATH@/components/jsdebugger.xpt
+-@RESPATH@/components/jsdownloads.xpt
+ @RESPATH@/browser/components/jsinspector.xpt
+ @RESPATH@/components/layout_base.xpt
+ #ifdef NS_PRINTING
+ @RESPATH@/components/layout_printing.xpt
+ #endif
+ @RESPATH@/components/layout_xul_tree.xpt
+ @RESPATH@/components/layout_xul.xpt
+ @RESPATH@/components/locale.xpt
+diff --git a/build/dumbmake-dependencies b/build/dumbmake-dependencies
+--- a/build/dumbmake-dependencies
++++ b/build/dumbmake-dependencies
+@@ -55,17 +55,17 @@ browser/app
+   browser/base
+   browser/components
+   devtools/client
+   browser/locales
+   browser/modules
+   browser/themes
+   toolkit
+   toolkit/components
+-  toolkit/components/jsdownloads
++  toolkit/components/downloads
+   toolkit/content
+   toolkit/crashreporter
+   toolkit/forgetaboutsite
+   toolkit/identity
+   toolkit/modules
+   toolkit/mozapps/extensions
+   toolkit/profile
+   toolkit/themes
+diff --git a/testing/runtimes/mochitest-browser-chrome.runtimes.json.1363061.later b/testing/runtimes/mochitest-browser-chrome.runtimes.json.1363061.later
+new file mode 100644
+--- /dev/null
++++ b/testing/runtimes/mochitest-browser-chrome.runtimes.json.1363061.later
+@@ -0,0 +1,21 @@
++--- mochitest-browser-chrome.runtimes.json
+++++ mochitest-browser-chrome.runtimes.json
++@@ -800,17 +800,17 @@
++     "security/manager/ssl/tests/mochitest/browser/browser_certViewer.js": 3220,
++     "security/manager/ssl/tests/mochitest/browser/browser_clientAuth_connection.js": 2066,
++     "services/fxaccounts/tests/browser/browser_device_connected.js": 3094,
++     "services/fxaccounts/tests/browser/browser_verify_login.js": 1763,
++     "toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js": 6246,
++     "toolkit/components/addoncompat/tests/browser/browser_addonShims.js": 3823,
++     "toolkit/components/extensions/test/browser/browser_ext_management_themes.js": 2922,
++     "toolkit/components/extensions/test/browser/browser_ext_themes_persistence.js": 1948,
++-    "toolkit/components/jsdownloads/test/browser/browser_DownloadPDFSaver.js": 4102,
+++    "toolkit/components/downloads/test/browser/browser_DownloadPDFSaver.js": 4102,
++     "toolkit/components/narrate/test/browser_narrate.js": 2700,
++     "toolkit/components/narrate/test/browser_voiceselect.js": 2254,
++     "toolkit/components/narrate/test/browser_word_highlight.js": 2215,
++     "toolkit/components/passwordmgr/test/browser/browser_capture_doorhanger.js": 15909,
++     "toolkit/components/passwordmgr/test/browser/browser_capture_doorhanger_httpsUpgrade.js": 3296,
++     "toolkit/components/passwordmgr/test/browser/browser_capture_doorhanger_window_open.js": 4673,
++     "toolkit/components/passwordmgr/test/browser/browser_context_menu.js": 12686,
++     "toolkit/components/passwordmgr/test/browser/browser_context_menu_autocomplete_interaction.js": 3297,
+diff --git a/toolkit/components/build/moz.build b/toolkit/components/build/moz.build
+--- a/toolkit/components/build/moz.build
++++ b/toolkit/components/build/moz.build
+@@ -18,17 +18,16 @@ SOURCES += [
+ FINAL_LIBRARY = 'xul'
+ 
+ LOCAL_INCLUDES += [
+     '../../xre',
+     '../alerts',
+     '../downloads',
+     '../feeds',
+     '../find',
+-    '../jsdownloads/src',
+     '../perfmonitoring',
+     '../protobuf',
+     '../reputationservice',
+     '../startup',
+     '../statusfilter',
+     '../typeaheadfind',
+     '../url-classifier',
+ ]
+diff --git a/toolkit/components/jsdownloads/src/DownloadCore.jsm b/toolkit/components/downloads/DownloadCore.jsm
+rename from toolkit/components/jsdownloads/src/DownloadCore.jsm
+rename to toolkit/components/downloads/DownloadCore.jsm
+diff --git a/toolkit/components/jsdownloads/src/DownloadHistory.jsm b/toolkit/components/downloads/DownloadHistory.jsm
+rename from toolkit/components/jsdownloads/src/DownloadHistory.jsm
+rename to toolkit/components/downloads/DownloadHistory.jsm
+diff --git a/toolkit/components/jsdownloads/src/DownloadImport.jsm b/toolkit/components/downloads/DownloadImport.jsm
+rename from toolkit/components/jsdownloads/src/DownloadImport.jsm
+rename to toolkit/components/downloads/DownloadImport.jsm
+diff --git a/toolkit/components/jsdownloads/src/DownloadIntegration.jsm b/toolkit/components/downloads/DownloadIntegration.jsm
+rename from toolkit/components/jsdownloads/src/DownloadIntegration.jsm
+rename to toolkit/components/downloads/DownloadIntegration.jsm
+diff --git a/toolkit/components/jsdownloads/src/DownloadLegacy.js b/toolkit/components/downloads/DownloadLegacy.js
+rename from toolkit/components/jsdownloads/src/DownloadLegacy.js
+rename to toolkit/components/downloads/DownloadLegacy.js
+diff --git a/toolkit/components/jsdownloads/src/DownloadList.jsm b/toolkit/components/downloads/DownloadList.jsm
+rename from toolkit/components/jsdownloads/src/DownloadList.jsm
+rename to toolkit/components/downloads/DownloadList.jsm
+diff --git a/toolkit/components/jsdownloads/src/DownloadPaths.jsm b/toolkit/components/downloads/DownloadPaths.jsm
+rename from toolkit/components/jsdownloads/src/DownloadPaths.jsm
+rename to toolkit/components/downloads/DownloadPaths.jsm
+diff --git a/toolkit/components/jsdownloads/src/DownloadPlatform.cpp b/toolkit/components/downloads/DownloadPlatform.cpp
+rename from toolkit/components/jsdownloads/src/DownloadPlatform.cpp
+rename to toolkit/components/downloads/DownloadPlatform.cpp
+--- a/toolkit/components/jsdownloads/src/DownloadPlatform.cpp
++++ b/toolkit/components/downloads/DownloadPlatform.cpp
+@@ -22,17 +22,17 @@
+ #ifdef XP_WIN
+ #include <shlobj.h>
+ #include <urlmon.h>
+ #include "nsILocalFileWin.h"
+ #endif
+ 
+ #ifdef XP_MACOSX
+ #include <CoreFoundation/CoreFoundation.h>
+-#include "../../../../xpcom/io/CocoaFileUtils.h"
++#include "../../../xpcom/io/CocoaFileUtils.h"
+ #endif
+ 
+ #ifdef MOZ_WIDGET_GTK
+ #include <gtk/gtk.h>
+ #endif
+ 
+ using namespace mozilla;
+ 
+diff --git a/toolkit/components/jsdownloads/src/DownloadPlatform.h b/toolkit/components/downloads/DownloadPlatform.h
+rename from toolkit/components/jsdownloads/src/DownloadPlatform.h
+rename to toolkit/components/downloads/DownloadPlatform.h
+diff --git a/toolkit/components/jsdownloads/src/DownloadStore.jsm b/toolkit/components/downloads/DownloadStore.jsm
+rename from toolkit/components/jsdownloads/src/DownloadStore.jsm
+rename to toolkit/components/downloads/DownloadStore.jsm
+diff --git a/toolkit/components/jsdownloads/src/DownloadUIHelper.jsm b/toolkit/components/downloads/DownloadUIHelper.jsm
+rename from toolkit/components/jsdownloads/src/DownloadUIHelper.jsm
+rename to toolkit/components/downloads/DownloadUIHelper.jsm
+diff --git a/toolkit/components/jsdownloads/src/Downloads.jsm b/toolkit/components/downloads/Downloads.jsm
+rename from toolkit/components/jsdownloads/src/Downloads.jsm
+rename to toolkit/components/downloads/Downloads.jsm
+diff --git a/toolkit/components/jsdownloads/src/Downloads.manifest b/toolkit/components/downloads/Downloads.manifest
+rename from toolkit/components/jsdownloads/src/Downloads.manifest
+rename to toolkit/components/downloads/Downloads.manifest
+diff --git a/toolkit/components/downloads/moz.build b/toolkit/components/downloads/moz.build
+--- a/toolkit/components/downloads/moz.build
++++ b/toolkit/components/downloads/moz.build
+@@ -1,25 +1,59 @@
+ # -*- 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/.
+ 
+-with Files('*'):
+-    BUG_COMPONENT = ('Toolkit', 'Download Manager')
++XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
++BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
++
++TEST_HARNESS_FILES.xpcshell.toolkit.components.downloads.test.data += [
++    'test/data/empty.txt',
++    'test/data/source.txt',
++]
+ 
+ XPIDL_SOURCES += [
++    'mozIDownloadPlatform.idl',
+     'nsIDownload.idl',
+     'nsIDownloadManager.idl',
+     'nsIDownloadManagerUI.idl',
+     'nsIDownloadProgressListener.idl',
+ ]
+ 
+ XPIDL_MODULE = 'downloads'
+ 
++CXXFLAGS += CONFIG['TK_CFLAGS']
++
+ UNIFIED_SOURCES += [
+-    'nsDownloadManager.cpp'
++    'nsDownloadManager.cpp',
++]
++
++SOURCES += [
++    'DownloadPlatform.cpp',
++]
++
++EXTRA_COMPONENTS += [
++    'DownloadLegacy.js',
++    'Downloads.manifest',
+ ]
+ 
++EXTRA_JS_MODULES += [
++    'DownloadCore.jsm',
++    'DownloadImport.jsm',
++    'DownloadIntegration.jsm',
++    'DownloadList.jsm',
++    'DownloadPaths.jsm',
++    'Downloads.jsm',
++    'DownloadStore.jsm',
++    'DownloadUIHelper.jsm',
++]
++
++if CONFIG['MOZ_PLACES']:
++    EXTRA_JS_MODULES += [
++        'DownloadHistory.jsm',
++    ]
++
+ FINAL_LIBRARY = 'xul'
+ 
+-CXXFLAGS += CONFIG['TK_CFLAGS']
++with Files('*'):
++    BUG_COMPONENT = ('Toolkit', 'Download Manager')
+diff --git a/toolkit/components/jsdownloads/public/mozIDownloadPlatform.idl b/toolkit/components/downloads/mozIDownloadPlatform.idl
+rename from toolkit/components/jsdownloads/public/mozIDownloadPlatform.idl
+rename to toolkit/components/downloads/mozIDownloadPlatform.idl
+diff --git a/toolkit/components/jsdownloads/test/browser/.eslintrc.js b/toolkit/components/downloads/test/browser/.eslintrc.js
+rename from toolkit/components/jsdownloads/test/browser/.eslintrc.js
+rename to toolkit/components/downloads/test/browser/.eslintrc.js
+diff --git a/toolkit/components/jsdownloads/test/browser/browser.ini b/toolkit/components/downloads/test/browser/browser.ini
+rename from toolkit/components/jsdownloads/test/browser/browser.ini
+rename to toolkit/components/downloads/test/browser/browser.ini
+diff --git a/toolkit/components/jsdownloads/test/browser/browser_DownloadPDFSaver.js b/toolkit/components/downloads/test/browser/browser_DownloadPDFSaver.js
+rename from toolkit/components/jsdownloads/test/browser/browser_DownloadPDFSaver.js
+rename to toolkit/components/downloads/test/browser/browser_DownloadPDFSaver.js
+diff --git a/toolkit/components/jsdownloads/test/browser/head.js b/toolkit/components/downloads/test/browser/head.js
+rename from toolkit/components/jsdownloads/test/browser/head.js
+rename to toolkit/components/downloads/test/browser/head.js
+diff --git a/toolkit/components/jsdownloads/test/browser/testFile.html b/toolkit/components/downloads/test/browser/testFile.html
+rename from toolkit/components/jsdownloads/test/browser/testFile.html
+rename to toolkit/components/downloads/test/browser/testFile.html
+diff --git a/toolkit/components/jsdownloads/test/data/.eslintrc.js b/toolkit/components/downloads/test/data/.eslintrc.js
+rename from toolkit/components/jsdownloads/test/data/.eslintrc.js
+rename to toolkit/components/downloads/test/data/.eslintrc.js
+diff --git a/toolkit/components/jsdownloads/test/data/empty.txt b/toolkit/components/downloads/test/data/empty.txt
+rename from toolkit/components/jsdownloads/test/data/empty.txt
+rename to toolkit/components/downloads/test/data/empty.txt
+diff --git a/toolkit/components/jsdownloads/test/data/source.txt b/toolkit/components/downloads/test/data/source.txt
+rename from toolkit/components/jsdownloads/test/data/source.txt
+rename to toolkit/components/downloads/test/data/source.txt
+diff --git a/toolkit/components/jsdownloads/test/unit/.eslintrc.js b/toolkit/components/downloads/test/unit/.eslintrc.js
+rename from toolkit/components/jsdownloads/test/unit/.eslintrc.js
+rename to toolkit/components/downloads/test/unit/.eslintrc.js
+diff --git a/toolkit/components/jsdownloads/test/unit/common_test_Download.js b/toolkit/components/downloads/test/unit/common_test_Download.js
+rename from toolkit/components/jsdownloads/test/unit/common_test_Download.js
+rename to toolkit/components/downloads/test/unit/common_test_Download.js
+diff --git a/toolkit/components/jsdownloads/test/unit/head.js b/toolkit/components/downloads/test/unit/head.js
+rename from toolkit/components/jsdownloads/test/unit/head.js
+rename to toolkit/components/downloads/test/unit/head.js
+diff --git a/toolkit/components/jsdownloads/test/unit/test_DownloadCore.js b/toolkit/components/downloads/test/unit/test_DownloadCore.js
+rename from toolkit/components/jsdownloads/test/unit/test_DownloadCore.js
+rename to toolkit/components/downloads/test/unit/test_DownloadCore.js
+diff --git a/toolkit/components/jsdownloads/test/unit/test_DownloadHistory.js b/toolkit/components/downloads/test/unit/test_DownloadHistory.js
+rename from toolkit/components/jsdownloads/test/unit/test_DownloadHistory.js
+rename to toolkit/components/downloads/test/unit/test_DownloadHistory.js
+diff --git a/toolkit/components/jsdownloads/test/unit/test_DownloadIntegration.js b/toolkit/components/downloads/test/unit/test_DownloadIntegration.js
+rename from toolkit/components/jsdownloads/test/unit/test_DownloadIntegration.js
+rename to toolkit/components/downloads/test/unit/test_DownloadIntegration.js
+diff --git a/toolkit/components/jsdownloads/test/unit/test_DownloadLegacy.js b/toolkit/components/downloads/test/unit/test_DownloadLegacy.js
+rename from toolkit/components/jsdownloads/test/unit/test_DownloadLegacy.js
+rename to toolkit/components/downloads/test/unit/test_DownloadLegacy.js
+diff --git a/toolkit/components/jsdownloads/test/unit/test_DownloadList.js b/toolkit/components/downloads/test/unit/test_DownloadList.js
+rename from toolkit/components/jsdownloads/test/unit/test_DownloadList.js
+rename to toolkit/components/downloads/test/unit/test_DownloadList.js
+diff --git a/toolkit/components/jsdownloads/test/unit/test_DownloadPaths.js b/toolkit/components/downloads/test/unit/test_DownloadPaths.js
+rename from toolkit/components/jsdownloads/test/unit/test_DownloadPaths.js
+rename to toolkit/components/downloads/test/unit/test_DownloadPaths.js
+diff --git a/toolkit/components/jsdownloads/test/unit/test_DownloadStore.js b/toolkit/components/downloads/test/unit/test_DownloadStore.js
+rename from toolkit/components/jsdownloads/test/unit/test_DownloadStore.js
+rename to toolkit/components/downloads/test/unit/test_DownloadStore.js
+diff --git a/toolkit/components/jsdownloads/test/unit/test_Downloads.js b/toolkit/components/downloads/test/unit/test_Downloads.js
+rename from toolkit/components/jsdownloads/test/unit/test_Downloads.js
+rename to toolkit/components/downloads/test/unit/test_Downloads.js
+diff --git a/toolkit/components/jsdownloads/test/unit/test_PrivateTemp.js b/toolkit/components/downloads/test/unit/test_PrivateTemp.js
+rename from toolkit/components/jsdownloads/test/unit/test_PrivateTemp.js
+rename to toolkit/components/downloads/test/unit/test_PrivateTemp.js
+diff --git a/toolkit/components/jsdownloads/test/unit/xpcshell.ini b/toolkit/components/downloads/test/unit/xpcshell.ini
+rename from toolkit/components/jsdownloads/test/unit/xpcshell.ini
+rename to toolkit/components/downloads/test/unit/xpcshell.ini
+diff --git a/toolkit/components/jsdownloads/moz.build b/toolkit/components/jsdownloads/moz.build
+deleted file mode 100644
+--- a/toolkit/components/jsdownloads/moz.build
++++ /dev/null
+@@ -1,18 +0,0 @@
+-# -*- 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/.
+-
+-with Files('*'):
+-    BUG_COMPONENT = ('Toolkit', 'Download Manager')
+-
+-DIRS += ['public', 'src']
+-
+-XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
+-BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
+-
+-TEST_HARNESS_FILES.xpcshell.toolkit.components.jsdownloads.test.data += [
+-    'test/data/empty.txt',
+-    'test/data/source.txt',
+-]
+diff --git a/toolkit/components/jsdownloads/public/moz.build b/toolkit/components/jsdownloads/public/moz.build
+deleted file mode 100644
+--- a/toolkit/components/jsdownloads/public/moz.build
++++ /dev/null
+@@ -1,9 +0,0 @@
+-# 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/.
+-
+-XPIDL_MODULE = 'jsdownloads'
+-
+-XPIDL_SOURCES += [
+-    'mozIDownloadPlatform.idl',
+-]
+diff --git a/toolkit/components/jsdownloads/src/moz.build b/toolkit/components/jsdownloads/src/moz.build
+deleted file mode 100644
+--- a/toolkit/components/jsdownloads/src/moz.build
++++ /dev/null
+@@ -1,34 +0,0 @@
+-# -*- 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/.
+-
+-SOURCES += [
+-    'DownloadPlatform.cpp',
+-]
+-
+-EXTRA_COMPONENTS += [
+-    'DownloadLegacy.js',
+-    'Downloads.manifest',
+-]
+-
+-EXTRA_JS_MODULES += [
+-    'DownloadCore.jsm',
+-    'DownloadImport.jsm',
+-    'DownloadIntegration.jsm',
+-    'DownloadList.jsm',
+-    'DownloadPaths.jsm',
+-    'Downloads.jsm',
+-    'DownloadStore.jsm',
+-    'DownloadUIHelper.jsm',
+-]
+-
+-if CONFIG['MOZ_PLACES']:
+-    EXTRA_JS_MODULES += [
+-        'DownloadHistory.jsm',
+-    ]
+-
+-FINAL_LIBRARY = 'xul'
+-
+-CXXFLAGS += CONFIG['TK_CFLAGS']
+diff --git a/toolkit/components/moz.build b/toolkit/components/moz.build
+--- a/toolkit/components/moz.build
++++ b/toolkit/components/moz.build
+@@ -28,17 +28,16 @@ DIRS += [
+     'crashes',
+     'crashmonitor',
+     'downloads',
+     'extensions',
+     'exthelper',
+     'filewatcher',
+     'finalizationwitness',
+     'find',
+-    'jsdownloads',
+     'jsoncpp/src/lib_json',
+     'lz4',
+     'mediasniffer',
+     'microformats',
+     'mozintl',
+     'mozprotocol',
+     'osfile',
+     'parentalcontrols',
+diff --git a/toolkit/content/contentAreaUtils.js b/toolkit/content/contentAreaUtils.js
+--- a/toolkit/content/contentAreaUtils.js
++++ b/toolkit/content/contentAreaUtils.js
+@@ -761,17 +761,17 @@ function uniqueFile(aLocalFile) {
+       // replace the last (n) in the filename with (n+1)
+       aLocalFile.leafName = aLocalFile.leafName.replace(/^(.*\()\d+\)/, "$1" + (collisionCount + 1) + ")");
+     }
+   }
+   return aLocalFile;
+ }
+ 
+ /**
+- * Download a URL using the new jsdownloads API.
++ * Download a URL using the Downloads API.
+  *
+  * @param aURL
+  *        the url to download
+  * @param [optional] aFileName
+  *        the destination file name, if omitted will be obtained from the url.
+  * @param aInitiatingDocument
+  *        The document from which the download was initiated.
+  */

+ 722 - 0
mozilla-release/patches/1386613-1-59a1.patch

@@ -0,0 +1,722 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1512492034 -3600
+# Node ID 5cbdcea46ed0c0aa93eb79b20e56e2fc260ce8a4
+# Parent  2d6f41ad884aed9c1ea74dc1d22bfad8fb9c194f
+Bug 1386613 - stop using waitForMutations in aboutdebugging tests (serviceworkers);r=ochameau
+
+MozReview-Commit-ID: 3JMEZUx8rTt
+
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers.js b/devtools/client/aboutdebugging/test/browser_service_workers.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers.js
+@@ -12,32 +12,32 @@ add_task(function* () {
+   yield enableServiceWorkerDebugging();
+ 
+   let { tab, document } = yield openAboutDebugging("workers");
+ 
+   let swTab = yield addTab(TAB_URL);
+ 
+   let serviceWorkersElement = getServiceWorkerList(document);
+ 
+-  yield waitForMutation(serviceWorkersElement, { childList: true });
+-
+-  // Check that the service worker appears in the UI
+-  let names = [...document.querySelectorAll("#service-workers .target-name")];
+-  names = names.map(element => element.textContent);
+-  ok(names.includes(SERVICE_WORKER),
+-    "The service worker url appears in the list: " + names);
++  yield waitUntil(() => {
++    // Check that the service worker appears in the UI
++    let names = [...document.querySelectorAll("#service-workers .target-name")];
++    names = names.map(element => element.textContent);
++    return names.includes(SERVICE_WORKER);
++  });
++  info("The service worker url appears in the list");
+ 
+   try {
+     yield unregisterServiceWorker(swTab, serviceWorkersElement);
+     ok(true, "Service worker registration unregistered");
+   } catch (e) {
+     ok(false, "SW not unregistered; " + e);
+   }
+ 
+   // Check that the service worker disappeared from the UI
+-  names = [...document.querySelectorAll("#service-workers .target-name")];
++  let names = [...document.querySelectorAll("#service-workers .target-name")];
+   names = names.map(element => element.textContent);
+   ok(!names.includes(SERVICE_WORKER),
+     "The service worker url is no longer in the list: " + names);
+ 
+   yield removeTab(swTab);
+   yield closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_fetch_flag.js b/devtools/client/aboutdebugging/test/browser_service_workers_fetch_flag.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers_fetch_flag.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers_fetch_flag.js
+@@ -10,22 +10,26 @@ const FETCH_SW_TAB_URL = URL_ROOT + "ser
+ 
+ function* testBody(url, expecting) {
+   yield enableServiceWorkerDebugging();
+   let { tab, document } = yield openAboutDebugging("workers");
+ 
+   let swTab = yield addTab(url);
+ 
+   let serviceWorkersElement = getServiceWorkerList(document);
+-  yield waitForMutation(serviceWorkersElement, { childList: true });
+ 
+-  let fetchFlags =
+-    [...document.querySelectorAll("#service-workers .service-worker-fetch-flag")];
+-  fetchFlags = fetchFlags.map(element => element.textContent);
+-  ok(fetchFlags.includes(expecting), "Found correct fetch flag.");
++  info("Wait for fetch flag.");
++  yield waitUntil(() => {
++    let fetchFlags =
++      [...document.querySelectorAll("#service-workers .service-worker-fetch-flag")];
++    fetchFlags = fetchFlags.map(element => element.textContent);
++    return fetchFlags.includes(expecting);
++  }, 100);
++
++  info("Found correct fetch flag.");
+ 
+   try {
+     yield unregisterServiceWorker(swTab, serviceWorkersElement);
+     ok(true, "Service worker registration unregistered");
+   } catch (e) {
+     ok(false, "SW not unregistered; " + e);
+   }
+ 
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_multi_content_process.js b/devtools/client/aboutdebugging/test/browser_service_workers_multi_content_process.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers_multi_content_process.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers_multi_content_process.js
+@@ -17,50 +17,57 @@ add_task(function* () {
+ 
+   let { tab, document } = yield openAboutDebugging("workers");
+ 
+   let warningSection = document.querySelector(".service-worker-multi-process");
+   let img = warningSection.querySelector(".warning");
+   ok(img, "warning message is rendered");
+ 
+   let serviceWorkersElement = getServiceWorkerList(document);
+-  let onMutation = waitForMutation(serviceWorkersElement, { childList: true });
+ 
+   let swTab = yield addTab(TAB_URL, { background: true });
+ 
+   info("Wait for service worker to appear in the list");
+-  yield onMutation;
++  // Check that the service worker appears in the UI
++  let serviceWorkerContainer =
++    yield waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
+ 
+   info("Check that service worker buttons are disabled.");
+-  // Check that the service worker appears in the UI
+-  let serviceWorkerContainer = getServiceWorkerContainer(SERVICE_WORKER, document);
+-  let debugButton = serviceWorkerContainer.querySelector(".debug-button");
++  let debugButton = getDebugButton(serviceWorkerContainer);
+   ok(debugButton.disabled, "Start/Debug button is disabled");
+ 
+   info("Update the preference to 1");
+   let onWarningCleared = waitUntil(() => {
+     let hasWarning = document.querySelector(".service-worker-multi-process");
+     return !hasWarning && !debugButton.disabled;
+-  });
++  }, 100);
+   yield pushPref("dom.ipc.processCount", 1);
+   yield onWarningCleared;
+   ok(!debugButton.disabled, "Debug button is enabled.");
+ 
+   info("Update the preference back to 2");
+   let onWarningRestored = waitUntil(() => {
+     let hasWarning = document.querySelector(".service-worker-multi-process");
+-    return hasWarning && debugButton.disabled;
+-  });
++    return hasWarning && getDebugButton(serviceWorkerContainer).disabled;
++  }, 100);
+   yield pushPref("dom.ipc.processCount", 2);
+   yield onWarningRestored;
++
++  // Update the reference to the debugButton, as the previous DOM element might have been
++  // deleted.
++  debugButton = getDebugButton(serviceWorkerContainer);
+   ok(debugButton.disabled, "Debug button is disabled again.");
+ 
+   info("Unregister service worker");
+   try {
+     yield unregisterServiceWorker(swTab, serviceWorkersElement);
+     ok(true, "Service worker registration unregistered");
+   } catch (e) {
+     ok(false, "SW not unregistered; " + e);
+   }
+ 
+   yield removeTab(swTab);
+   yield closeAboutDebugging(tab);
+ });
++
++function getDebugButton(serviceWorkerContainer) {
++  return serviceWorkerContainer.querySelector(".debug-button");
++}
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_push.js b/devtools/client/aboutdebugging/test/browser_service_workers_push.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers_push.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers_push.js
+@@ -15,44 +15,34 @@ const SERVICE_WORKER = URL_ROOT + "servi
+ const TAB_URL = URL_ROOT + "service-workers/push-sw.html";
+ 
+ add_task(function* () {
+   yield enableServiceWorkerDebugging();
+   let { tab, document } = yield openAboutDebugging("workers");
+ 
+   // Listen for mutations in the service-workers list.
+   let serviceWorkersElement = getServiceWorkerList(document);
+-  let onMutation = waitForMutation(serviceWorkersElement, { childList: true });
+ 
+   // Open a tab that registers a push service worker.
+   let swTab = yield addTab(TAB_URL);
+ 
+   info("Make the test page notify us when the service worker sends a message.");
+ 
+   yield ContentTask.spawn(swTab.linkedBrowser, {}, function () {
+     let win = content.wrappedJSObject;
+     win.navigator.serviceWorker.addEventListener("message", function (event) {
+       sendAsyncMessage(event.data);
+     });
+   });
+ 
+   // Expect the service worker to claim the test window when activating.
+-  let mm = swTab.linkedBrowser.messageManager;
+-  let onClaimed = new Promise(done => {
+-    mm.addMessageListener("sw-claimed", function listener() {
+-      mm.removeMessageListener("sw-claimed", listener);
+-      done();
+-    });
+-  });
++  let onClaimed = onTabMessage(swTab, "sw-claimed");
+ 
+-  // Wait for the service-workers list to update.
+-  yield onMutation;
+-
+-  // Check that the service worker appears in the UI.
+-  assertHasTarget(true, document, "service-workers", SERVICE_WORKER);
++  info("Wait until the service worker appears in the UI");
++  yield waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
+ 
+   info("Ensure that the registration resolved before trying to interact with " +
+     "the service worker.");
+   yield waitForServiceWorkerRegistered(swTab);
+   ok(true, "Service worker registration resolved");
+ 
+   yield waitForServiceWorkerActivation(SERVICE_WORKER, document);
+ 
+@@ -61,35 +51,43 @@ add_task(function* () {
+   let name = names.filter(element => element.textContent === SERVICE_WORKER)[0];
+   ok(name, "Found the service worker in the list");
+ 
+   let targetElement = name.parentNode.parentNode;
+ 
+   let pushBtn = targetElement.querySelector(".push-button");
+   ok(pushBtn, "Found its push button");
+ 
+-  info("Wait for the service worker to claim the test window before " +
+-    "proceeding.");
++  info("Wait for the service worker to claim the test window before proceeding.");
+   yield onClaimed;
+ 
+   info("Click on the Push button and wait for the service worker to receive " +
+     "a push notification");
+-  let onPushNotification = new Promise(done => {
+-    mm.addMessageListener("sw-pushed", function listener() {
+-      mm.removeMessageListener("sw-pushed", listener);
+-      done();
+-    });
+-  });
++  let onPushNotification = onTabMessage(swTab, "sw-pushed");
++
+   pushBtn.click();
+   yield onPushNotification;
+   ok(true, "Service worker received a push notification");
+ 
+   // Finally, unregister the service worker itself.
+   try {
+     yield unregisterServiceWorker(swTab, serviceWorkersElement);
+     ok(true, "Service worker registration unregistered");
+   } catch (e) {
+     ok(false, "SW not unregistered; " + e);
+   }
+ 
+   yield removeTab(swTab);
+   yield closeAboutDebugging(tab);
+ });
++
++/**
++ * Helper to listen once on a message sent using postMessage from the provided tab.
++ */
++function onTabMessage(tab, message) {
++  let mm = tab.linkedBrowser.messageManager;
++  return new Promise(done => {
++    mm.addMessageListener(message, function listener() {
++      mm.removeMessageListener(message, listener);
++      done();
++    });
++  });
++}
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_push_service.js b/devtools/client/aboutdebugging/test/browser_service_workers_push_service.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers_push_service.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers_push_service.js
+@@ -51,57 +51,49 @@ add_task(function* () {
+       return Promise.resolve(deleted);
+     },
+   };
+ 
+   let { tab, document } = yield openAboutDebugging("workers");
+ 
+   // Listen for mutations in the service-workers list.
+   let serviceWorkersElement = document.getElementById("service-workers");
+-  let onMutation = waitForMutation(serviceWorkersElement, { childList: true });
+ 
+   // Open a tab that registers a push service worker.
+   let swTab = yield addTab(TAB_URL);
+ 
+-  // Wait for the service-workers list to update.
+-  yield onMutation;
+-
+-  // Check that the service worker appears in the UI.
+-  assertHasTarget(true, document, "service-workers", SERVICE_WORKER);
++  info("Wait until the service worker appears in about:debugging");
++  yield waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
+ 
+   yield waitForServiceWorkerActivation(SERVICE_WORKER, document);
+ 
+   // Wait for the service worker details to update.
+   let names = [...document.querySelectorAll("#service-workers .target-name")];
+   let name = names.filter(element => element.textContent === SERVICE_WORKER)[0];
+   ok(name, "Found the service worker in the list");
+ 
+-  let targetContainer = name.parentNode.parentNode;
+-  let targetDetailsElement = targetContainer.querySelector(".target-details");
++  let targetContainer = name.closest(".target-container");
+ 
+   // Retrieve the push subscription endpoint URL, and verify it looks good.
+-  let pushURL = targetContainer.querySelector(".service-worker-push-url");
+-  if (!pushURL) {
+-    yield waitForMutation(targetDetailsElement, { childList: true });
+-    pushURL = targetContainer.querySelector(".service-worker-push-url");
+-  }
++  info("Wait for the push URL");
++  let pushURL = yield waitUntilElement(".service-worker-push-url", targetContainer);
+ 
+-  ok(pushURL, "Found the push service URL in the service worker details");
++  info("Found the push service URL in the service worker details");
+   is(pushURL.textContent, FAKE_ENDPOINT, "The push service URL looks correct");
+ 
+   // Unsubscribe from the push service.
+   ContentTask.spawn(swTab.linkedBrowser, {}, function () {
+     let win = content.wrappedJSObject;
+     return win.sub.unsubscribe();
+   });
+ 
+-  // Wait for the service worker details to update again.
+-  yield waitForMutation(targetDetailsElement, { childList: true });
+-  ok(!targetContainer.querySelector(".service-worker-push-url"),
+-    "The push service URL should be removed");
++  // Wait for the service worker details to update again
++  info("Wait until the push URL is removed from the UI");
++  yield waitUntil(() => !targetContainer.querySelector(".service-worker-push-url"), 100);
++  info("The push service URL should be removed");
+ 
+   // Finally, unregister the service worker itself.
+   try {
+     yield unregisterServiceWorker(swTab, serviceWorkersElement);
+     ok(true, "Service worker registration unregistered");
+   } catch (e) {
+     ok(false, "SW not unregistered; " + e);
+   }
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_start.js b/devtools/client/aboutdebugging/test/browser_service_workers_start.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers_start.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers_start.js
+@@ -18,65 +18,57 @@ add_task(function* () {
+   yield enableServiceWorkerDebugging();
+   yield pushPref("dom.serviceWorkers.idle_timeout", SW_TIMEOUT);
+   yield pushPref("dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT);
+ 
+   let { tab, document } = yield openAboutDebugging("workers");
+ 
+   // Listen for mutations in the service-workers list.
+   let serviceWorkersElement = getServiceWorkerList(document);
+-  let onMutation = waitForMutation(serviceWorkersElement, { childList: true });
+ 
+   // Open a tab that registers an empty service worker.
+   let swTab = yield addTab(TAB_URL);
+ 
+   // Wait for the service-workers list to update.
+-  yield onMutation;
+-
+-  // Check that the service worker appears in the UI.
+-  assertHasTarget(true, document, "service-workers", SERVICE_WORKER);
++  info("Wait until the service worker appears in about:debugging");
++  yield waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
+ 
+   info("Ensure that the registration resolved before trying to interact with " +
+     "the service worker.");
+   yield waitForServiceWorkerRegistered(swTab);
+   ok(true, "Service worker registration resolved");
+ 
+   yield waitForServiceWorkerActivation(SERVICE_WORKER, document);
+ 
+   // Retrieve the Target element corresponding to the service worker.
+   let names = [...document.querySelectorAll("#service-workers .target-name")];
+   let name = names.filter(element => element.textContent === SERVICE_WORKER)[0];
+   ok(name, "Found the service worker in the list");
+   let targetElement = name.parentNode.parentNode;
+ 
+-  // The service worker may already be killed with the low 1s timeout
+-  if (!targetElement.querySelector(".start-button")) {
+-    // Check that there is a Debug button but not a Start button.
+-    ok(targetElement.querySelector(".debug-button"), "Found its debug button");
+-
+-    // Wait for the service worker to be killed due to inactivity.
+-    yield waitForMutation(targetElement, { childList: true });
+-  } else {
+-    // Check that there is no Debug button when the SW is already shut down.
+-    ok(!targetElement.querySelector(".debug-button"), "No debug button when " +
+-      "the worker is already killed");
+-  }
++  // The service worker may already be killed with the low 1s timeout.
++  // At this stage, either the SW is started and the Debug button is visible or was
++  // already stopped and the start button is visible. Wait until the service worker is
++  // stopped.
++  info("Wait until the start button is visible");
++  yield waitUntilElement(".start-button", targetElement);
+ 
+   // We should now have a Start button but no Debug button.
+   let startBtn = targetElement.querySelector(".start-button");
+   ok(startBtn, "Found its start button");
+   ok(!targetElement.querySelector(".debug-button"), "No debug button");
+ 
+   // Click on the Start button and wait for the service worker to be back.
+-  let onStarted = waitForMutation(targetElement, { childList: true });
+   startBtn.click();
+-  yield onStarted;
++
++  info("Wait until the service worker starts and the debug button appears");
++  yield waitUntilElement(".debug-button", targetElement);
++  info("Found the debug button");
+ 
+   // Check that we have a Debug button but not a Start button again.
+-  ok(targetElement.querySelector(".debug-button"), "Found its debug button");
+   ok(!targetElement.querySelector(".start-button"), "No start button");
+ 
+   // Finally, unregister the service worker itself.
+   try {
+     yield unregisterServiceWorker(swTab, serviceWorkersElement);
+     ok(true, "Service worker registration unregistered");
+   } catch (e) {
+     ok(false, "SW not unregistered; " + e);
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_status.js b/devtools/client/aboutdebugging/test/browser_service_workers_status.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers_status.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers_status.js
+@@ -15,48 +15,38 @@ add_task(function* () {
+   yield enableServiceWorkerDebugging();
+   yield pushPref("dom.serviceWorkers.idle_timeout", SW_TIMEOUT);
+   yield pushPref("dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT);
+ 
+   let { tab, document } = yield openAboutDebugging("workers");
+ 
+   // Listen for mutations in the service-workers list.
+   let serviceWorkersElement = getServiceWorkerList(document);
+-  let onMutation = waitForMutation(serviceWorkersElement, { childList: true });
+ 
+   let swTab = yield addTab(TAB_URL);
+ 
+-  info("Make the test page notify us when the service worker sends a message.");
+-
+-  // Wait for the service-workers list to update.
+-  yield onMutation;
+-
+-  // Check that the service worker appears in the UI
+-  let names = [...document.querySelectorAll("#service-workers .target-name")];
+-  let name = names.filter(element => element.textContent === SERVICE_WORKER)[0];
+-  ok(name, "Found the service worker in the list");
+-
+-  let targetElement = name.parentNode.parentNode;
+-  let status = targetElement.querySelector(".target-status");
++  info("Wait until the service worker appears in about:debugging");
++  let container = yield waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
+ 
+   // We should ideally check that the service worker registration goes through the
+   // "registering" and "running" steps, but it is difficult to workaround race conditions
+   // for a test running on a wide variety of platforms. Due to intermittent failures, we
+   // simply check that the registration transitions to "stopped".
++  let status = container.querySelector(".target-status");
+   yield waitUntil(() => status.textContent == "Stopped", 100);
+   is(status.textContent, "Stopped", "Service worker is currently stopped");
+ 
+   try {
+     yield unregisterServiceWorker(swTab, serviceWorkersElement);
+     ok(true, "Service worker unregistered");
+   } catch (e) {
+     ok(false, "Service worker not unregistered; " + e);
+   }
+ 
+   // Check that the service worker disappeared from the UI
+-  names = [...document.querySelectorAll("#service-workers .target-name")];
++  let names = [...document.querySelectorAll("#service-workers .target-name")];
+   names = names.map(element => element.textContent);
+   ok(!names.includes(SERVICE_WORKER),
+     "The service worker url is no longer in the list: " + names);
+ 
+   yield removeTab(swTab);
+   yield closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js b/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js
+@@ -13,22 +13,22 @@ const SW_TIMEOUT = 1000;
+ 
+ add_task(function* () {
+   yield enableServiceWorkerDebugging();
+   yield pushPref("dom.serviceWorkers.idle_timeout", SW_TIMEOUT);
+   yield pushPref("dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT);
+ 
+   let { tab, document } = yield openAboutDebugging("workers");
+ 
++  let serviceWorkersElement = getServiceWorkerList(document);
++
+   let swTab = yield addTab(TAB_URL);
+ 
+-  let serviceWorkersElement = getServiceWorkerList(document);
+-  yield waitForMutation(serviceWorkersElement, { childList: true });
+-
+-  assertHasTarget(true, document, "service-workers", SERVICE_WORKER);
++  info("Wait until the service worker appears in about:debugging");
++  yield waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
+ 
+   // Ensure that the registration resolved before trying to connect to the sw
+   yield waitForServiceWorkerRegistered(swTab);
+   ok(true, "Service worker registration resolved");
+ 
+   // Retrieve the DEBUG button for the worker
+   let names = [...document.querySelectorAll("#service-workers .target-name")];
+   let name = names.filter(element => element.textContent === SERVICE_WORKER)[0];
+@@ -59,19 +59,20 @@ add_task(function* () {
+     "The debug button is still there");
+ 
+   yield toolbox.destroy();
+   toolbox = null;
+ 
+   // Now ensure that the worker is correctly destroyed
+   // after we destroy the toolbox.
+   // The DEBUG button should disappear once the worker is destroyed.
+-  yield waitForMutation(targetElement, { childList: true });
+-  ok(!targetElement.querySelector(".debug-button"),
+-    "The debug button was removed when the worker was killed");
++  info("Wait until the debug button disappears");
++  yield waitUntil(() => {
++    return !targetElement.querySelector(".debug-button");
++  });
+ 
+   // Finally, unregister the service worker itself.
+   try {
+     yield unregisterServiceWorker(swTab, serviceWorkersElement);
+     ok(true, "Service worker registration unregistered");
+   } catch (e) {
+     ok(false, "SW not unregistered; " + e);
+   }
+diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_unregister.js b/devtools/client/aboutdebugging/test/browser_service_workers_unregister.js
+--- a/devtools/client/aboutdebugging/test/browser_service_workers_unregister.js
++++ b/devtools/client/aboutdebugging/test/browser_service_workers_unregister.js
+@@ -14,28 +14,21 @@ const SCOPE = URL_ROOT + "service-worker
+ const SERVICE_WORKER = SCOPE + "empty-sw.js";
+ const TAB_URL = SCOPE + "empty-sw.html";
+ 
+ add_task(function* () {
+   yield enableServiceWorkerDebugging();
+ 
+   let { tab, document } = yield openAboutDebugging("workers");
+ 
+-  // Listen for mutations in the service-workers list.
+-  let serviceWorkersElement = getServiceWorkerList(document);
+-  let onMutation = waitForMutation(serviceWorkersElement, { childList: true });
+-
+   // Open a tab that registers an empty service worker.
+   let swTab = yield addTab(TAB_URL);
+ 
+-  // Wait for the service workers-list to update.
+-  yield onMutation;
+-
+-  // Check that the service worker appears in the UI.
+-  assertHasTarget(true, document, "service-workers", SERVICE_WORKER);
++  info("Wait until the service worker appears in about:debugging");
++  yield waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
+ 
+   yield waitForServiceWorkerActivation(SERVICE_WORKER, document);
+ 
+   info("Ensure that the registration resolved before trying to interact with " +
+     "the service worker.");
+   yield waitForServiceWorkerRegistered(swTab);
+   ok(true, "Service worker registration resolved");
+ 
+@@ -50,18 +43,18 @@ add_task(function* () {
+   let scope = target.querySelector(".service-worker-scope");
+   is(scope.textContent, SCOPE,
+     "The expected scope is displayed in the service worker info.");
+ 
+   info("Unregister the service worker via the unregister link.");
+   let unregisterLink = target.querySelector(".unregister-link");
+   ok(unregisterLink, "Found the unregister link");
+ 
+-  onMutation = waitForMutation(serviceWorkersElement, { childList: true });
+   unregisterLink.click();
+-  yield onMutation;
+ 
+-  is(document.querySelector("#service-workers .target"), null,
+-   "No service worker displayed anymore.");
++  info("Wait until the service worker disappears");
++  yield waitUntil(() => {
++    return !document.querySelector("#service-workers .target");
++  });
+ 
+   yield removeTab(swTab);
+   yield closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/head.js b/devtools/client/aboutdebugging/test/head.js
+--- a/devtools/client/aboutdebugging/test/head.js
++++ b/devtools/client/aboutdebugging/test/head.js
+@@ -142,16 +142,51 @@ function getServiceWorkerContainer(name,
+   if (nameElement) {
+     return nameElement.closest(".target-container");
+   }
+ 
+   return null;
+ }
+ 
+ /**
++ * Wait until a service worker "container" element is found with a specific service worker
++ * name, in the provided document.
++ * Returns a promise that resolves the service worker container element.
++ *
++ * @param  {String} name
++ *         expected service worker name
++ * @param  {DOMDocument} document
++ *         #service-workers section container document
++ * @return {Promise} promise that resolves the service worker container element.
++ */
++function* waitUntilServiceWorkerContainer(name, document) {
++  yield waitUntil(() => {
++    return getServiceWorkerContainer(name, document);
++  }, 100);
++  return getServiceWorkerContainer(name, document);
++}
++
++/**
++ * Wait until a selector matches an element in a given parent node.
++ * Returns a promise that resolves the matched element.
++ *
++ * @param {String} selector
++ *        CSS selector to match.
++ * @param {DOMNode} parent
++ *        Parent that should contain the element.
++ * @return {Promise} promise that resolves the matched DOMNode.
++ */
++function* waitUntilElement(selector, parent) {
++  yield waitUntil(() => {
++    return parent.querySelector(selector);
++  }, 100);
++  return parent.querySelector(selector);
++}
++
++/**
+  * Depending on whether there are tabs opened, return either a
+  * target list element or its container.
+  * @param  {DOMDocument}  document   #tabs section container document
+  * @return {DOMNode}                 target list or container element
+  */
+ function getTabList(document) {
+   return document.querySelector("#tabs .target-list") ||
+     document.querySelector("#tabs.targets");
+@@ -436,20 +471,19 @@ function* setupTestAboutDebuggingWebExte
+  */
+ function* waitForServiceWorkerActivation(swUrl, document) {
+   let serviceWorkersElement = getServiceWorkerList(document);
+   let names = serviceWorkersElement.querySelectorAll(".target-name");
+   let name = [...names].filter(element => element.textContent === swUrl)[0];
+ 
+   let targetElement = name.parentNode.parentNode;
+   let targetStatus = targetElement.querySelector(".target-status");
+-  while (targetStatus.textContent === "Registering") {
+-    // Wait for the status to leave the "registering" stage.
+-    yield waitForMutation(serviceWorkersElement, { childList: true, subtree: true });
+-  }
++  yield waitUntil(() => {
++    return targetStatus.textContent !== "Registering";
++  }, 100);
+ }
+ 
+ /**
+  * Set all preferences needed to enable service worker debugging and testing.
+  */
+ function enableServiceWorkerDebugging() {
+   return new Promise(done => {
+     let options = { "set": [
+diff --git a/devtools/client/aboutdebugging/test/service-workers/push-sw.html b/devtools/client/aboutdebugging/test/service-workers/push-sw.html
+--- a/devtools/client/aboutdebugging/test/service-workers/push-sw.html
++++ b/devtools/client/aboutdebugging/test/service-workers/push-sw.html
+@@ -1,32 +1,43 @@
+ <!DOCTYPE HTML>
+ <html>
+ <head>
+   <meta charset="UTF-8">
+   <title>Service worker push test</title>
+ </head>
+ <body>
+ <script type="text/javascript">
++/* exported sw */
++
+ "use strict";
+-SpecialPowers.addPermission("desktop-notification", true, document);
+-var sw = navigator.serviceWorker.register("push-sw.js");
++// The subscription is expected as a global by browser_service_workers_push_service.js
+ var sub = null;
+-sw.then(
+-  function (registration) {
++
++// The registration promise is expected as a global by head.js's unregisterServiceWorker.
++var sw = (async function () {
++  await new Promise(resolve => {
++    let perm = { type: "desktop-notification", allow: true, context: document };
++    SpecialPowers.pushPermissions([perm], resolve);
++  });
++
++  let registrationPromise = navigator.serviceWorker.register("push-sw.js");
++
++  try {
++    let registration = await registrationPromise;
+     dump("SW registered\n");
+-    registration.pushManager.subscribe().then(
+-      function (subscription) {
+-        sub = subscription;
+-        dump("SW subscribed to push: " + sub.endpoint + "\n");
+-      },
+-      function (error) {
+-        dump("SW not subscribed to push: " + error + "\n");
+-      }
+-    );
+-  },
+-  function (error) {
+-    dump("SW not registered: " + error + "\n");
++
++    try {
++      sub = await registration.pushManager.subscribe();
++      dump("SW subscribed to push: " + sub.endpoint + "\n");
++    } catch (e) {
++      dump("SW not subscribed to push: " + e + "\n");
++    }
++  } catch (e) {
++    dump("SW not registered: " + e + "\n");
+   }
+-);
++
++  return registrationPromise;
++})();
++
+ </script>
+ </body>
+ </html>

+ 533 - 0
mozilla-release/patches/1386613-2-59a1.patch

@@ -0,0 +1,533 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1512508635 -3600
+# Node ID c9c877119f3fed3c81e30e2a65a638604db7de0d
+# Parent  4fb18be0ff69885a43bcb594e7c90e4412d4d99c
+Bug 1386613 - stop using waitForMutations in aboutdebugging tests (addons);r=ochameau
+
+MozReview-Commit-ID: Ii32r9Dhjje
+
+diff --git a/devtools/client/aboutdebugging/test/browser_addons_debug_info.js b/devtools/client/aboutdebugging/test/browser_addons_debug_info.js
+--- a/devtools/client/aboutdebugging/test/browser_addons_debug_info.js
++++ b/devtools/client/aboutdebugging/test/browser_addons_debug_info.js
+@@ -96,19 +96,21 @@ add_task(function* testUnknownManifestPr
+   yield waitForInitialAddonList(document);
+   yield installAddon({
+     document,
+     path: "addons/test-devtools-webextension-unknown-prop/manifest.json",
+     name: addonName,
+     isWebExtension: true
+   });
+ 
+-  let container = document.querySelector(`[data-addon-id="${addonId}"]`);
++  info("Wait until the addon appears in about:debugging");
++  let container = yield waitUntilAddonContainer(addonName, document);
+ 
+-  yield waitForInstallMessages(container);
++  info("Wait until the installation message appears for the new addon");
++  yield waitUntilElement(".addon-target-messages", container);
+ 
+   let messages = container.querySelectorAll(".addon-target-message");
+   ok(messages.length === 1, "there is one message");
+   ok(messages[0].textContent.match(/Error processing browser_actions/),
+      "the message is helpful");
+   ok(messages[0].classList.contains("addon-target-warning-message"),
+      "the message is a warning");
+ 
+diff --git a/devtools/client/aboutdebugging/test/browser_addons_install.js b/devtools/client/aboutdebugging/test/browser_addons_install.js
+--- a/devtools/client/aboutdebugging/test/browser_addons_install.js
++++ b/devtools/client/aboutdebugging/test/browser_addons_install.js
+@@ -50,32 +50,23 @@ add_task(function* testLegacyInstallSucc
+ 
+   yield closeAboutDebugging(tab);
+ });
+ 
+ add_task(function* testWebextensionInstallError() {
+   let { tab, document, window } = yield openAboutDebugging("addons");
+   yield waitForInitialAddonList(document);
+ 
+-  // Start an observer that looks for the install error before
+-  // actually doing the install
+-  let top = document.querySelector(".addons-top");
+-  let promise = waitForMutation(top, { childList: true });
+-
++  // Trigger the file picker by clicking on the button
+   mockFilePicker(window, getSupportsFile("addons/bad/manifest.json").file);
+-
+-  // Trigger the file picker by clicking on the button
+   document.getElementById("load-addon-from-file").click();
+ 
+-  // Now wait for the install error to appear.
+-  yield promise;
+-
+-  // And check that it really is there.
+-  let err = document.querySelector(".addons-install-error");
+-  isnot(err, null, "Addon install error message appeared");
++  info("wait for the install error to appear");
++  let top = document.querySelector(".addons-top");
++  yield waitUntilElement(".addons-install-error", top);
+ 
+   yield closeAboutDebugging(tab);
+ });
+ 
+ add_task(function* testWebextensionInstallErrorRetry() {
+   let { tab, document, window } = yield openAboutDebugging("addons");
+   yield waitForInitialAddonList(document);
+ 
+@@ -91,61 +82,47 @@ add_task(function* testWebextensionInsta
+     applications: { gecko: { id: addonId } },
+     // These should all be wrapped in arrays.
+     // eslint-disable-next-line camelcase
+     content_scripts: { matches: "http://*/", js: "foo.js" },
+   };
+ 
+   yield promiseWriteWebManifestForExtension(manifest, tempdir);
+ 
+-  // Start an observer that looks for the install error before
+-  // actually doing the install.
+-  let top = document.querySelector(".addons-top");
+-  let contentUpdated = waitForMutation(top, { childList: true });
+-
+   // Mock the file picker to select a test addon.
+   let manifestFile = tempdir.clone();
+   manifestFile.append(addonId, "manifest.json");
+   mockFilePicker(window, manifestFile);
+ 
+   // Trigger the file picker by clicking on the button.
+   document.getElementById("load-addon-from-file").click();
+ 
+-  // Now wait for the install error to appear.
+-  yield contentUpdated;
++  info("wait for the install error to appear");
++  let top = document.querySelector(".addons-top");
++  yield waitUntilElement(".addons-install-error", top);
+ 
+-  // Check that the error is shown.
+-  let err = document.querySelector(".addons-install-error");
+-  isnot(err, null, "Addon install error message appeared");
+   let retryButton = document.querySelector("button.addons-install-retry");
+   is(retryButton.textContent, "Retry", "Retry button has a good label");
+ 
+   // Fix the manifest so the add-on will install.
+   // eslint-disable-next-line camelcase
+   manifest.content_scripts = [{
+     matches: ["http://*/"],
+     js: ["foo.js"],
+   }];
+   yield promiseWriteWebManifestForExtension(manifest, tempdir);
+ 
+-  let getAddonEl = () => document.querySelector(`[data-addon-id="${addonId}"]`);
+-
++  let addonEl = document.querySelector(`[data-addon-id="${addonId}"]`);
+   // Verify this add-on isn't installed yet.
+-  ok(!getAddonEl(), "Addon is not installed yet");
+-
+-  // Prepare to wait for the add-on to be added to the temporary list.
+-  let addonAdded = waitForMutation(
+-    getTemporaryAddonList(document), { childList: true });
++  ok(!addonEl, "Addon is not installed yet");
+ 
+   // Retry the install.
+   retryButton.click();
+ 
+-  // Wait for the add-on to be shown.
+-  yield addonAdded;
+-
+-  // Verify the add-on is installed.
+-  ok(getAddonEl(), "Addon is installed");
++  info("Wait for the add-on to be shown");
++  yield waitUntilElement(`[data-addon-id="${addonId}"]`, document);
++  info("Addon is installed");
+ 
+   // Install the add-on, and verify that it disappears in the about:debugging UI
+   yield uninstallAddon({document, id: addonId, name: addonName});
+ 
+   yield closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_addons_reload.js b/devtools/client/aboutdebugging/test/browser_addons_reload.js
+--- a/devtools/client/aboutdebugging/test/browser_addons_reload.js
++++ b/devtools/client/aboutdebugging/test/browser_addons_reload.js
+@@ -1,15 +1,18 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ 
+ const ADDON_ID = "test-devtools@mozilla.org";
+ const ADDON_NAME = "test-devtools";
+ 
++const PACKAGED_ADDON_ID = "bug1273184@tests";
++const PACKAGED_ADDON_NAME = "bug 1273184";
++
+ function getReloadButton(document, addonName) {
+   const names = getInstalledAddonNames(document);
+   const name = names.filter(element => element.textContent === addonName)[0];
+   ok(name, `Found ${addonName} add-on in the list`);
+   const targetElement = name.parentNode.parentNode;
+   const reloadButton = targetElement.querySelector(".reload-button");
+   info(`Found reload button for ${addonName}`);
+   return reloadButton;
+@@ -102,54 +105,54 @@ add_task(function* reloadButtonRefreshes
+         "id": ADDON_ID
+       }
+     }
+   };
+ 
+   const tempExt = new TempWebExt(ADDON_ID);
+   tempExt.writeManifest(manifestBase);
+ 
+-  const onAddonListUpdated = waitForMutation(getTemporaryAddonList(document),
+-                                             { childList: true });
+   const onInstalled = promiseAddonEvent("onInstalled");
+   yield AddonManager.installTemporaryAddon(tempExt.sourceDir);
+-  const [addon] = yield onInstalled;
+-  info(`addon installed: ${addon.id}`);
+-  yield onAddonListUpdated;
++
++  info("Wait until addon onInstalled event is received");
++  yield onInstalled;
++
++  info("Wait until addon appears in about:debugging#addons");
++  yield waitUntilAddonContainer("Temporary web extension", document);
+ 
+   const newName = "Temporary web extension (updated)";
+   tempExt.writeManifest(Object.assign({}, manifestBase, {name: newName}));
+ 
+   // Wait for the add-on list to be updated with the reloaded name.
+   const onReInstall = promiseAddonEvent("onInstalled");
+-  const onAddonReloaded = waitForContentMutation(getTemporaryAddonList(document));
+-
+   const reloadButton = getReloadButton(document, manifestBase.name);
+   reloadButton.click();
+ 
+-  yield onAddonReloaded;
++  info("Wait until addon onInstalled event is received again");
+   const [reloadedAddon] = yield onReInstall;
+-  // Make sure the name was updated correctly.
+-  const allAddons = getInstalledAddonNames(document)
+-    .map(element => element.textContent);
+-  const nameWasUpdated = allAddons.some(name => name === newName);
+-  ok(nameWasUpdated, `New name appeared in reloaded add-ons: ${allAddons}`);
++
++  info("Wait until addon name is updated in about:debugging#addons");
++  yield waitUntilAddonContainer(newName, document);
+ 
+   yield tearDownAddon(reloadedAddon);
+   tempExt.remove();
+   yield closeAboutDebugging(tab);
+ });
+ 
+ add_task(function* onlyTempInstalledAddonsCanBeReloaded() {
+   const { tab, document } = yield openAboutDebugging("addons");
+   yield waitForInitialAddonList(document);
+-  const onAddonListUpdated = waitForMutation(getAddonList(document),
+-                                             { childList: true });
+   yield installAddonWithManager(getSupportsFile("addons/bug1273184.xpi").file);
+-  yield onAddonListUpdated;
+-  const addon = yield getAddonByID("bug1273184@tests");
++
++  info("Wait until addon appears in about:debugging#addons");
++  yield waitUntilAddonContainer(PACKAGED_ADDON_NAME, document);
++
++  info("Retrieved the installed addon from the addon manager");
++  const addon = yield getAddonByID(PACKAGED_ADDON_ID);
++  is(addon.name, PACKAGED_ADDON_NAME, "Addon name is correct");
+ 
+   const reloadButton = getReloadButton(document, addon.name);
+   ok(!reloadButton, "There should not be a reload button");
+ 
+   yield tearDownAddon(addon);
+   yield closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_addons_remove.js b/devtools/client/aboutdebugging/test/browser_addons_remove.js
+--- a/devtools/client/aboutdebugging/test/browser_addons_remove.js
++++ b/devtools/client/aboutdebugging/test/browser_addons_remove.js
+@@ -1,12 +1,14 @@
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ "use strict";
+ 
++const PACKAGED_ADDON_NAME = "bug 1273184";
++
+ function getTargetEl(document, id) {
+   return document.querySelector(`[data-addon-id="${id}"]`);
+ }
+ 
+ function getRemoveButton(document, id) {
+   return document.querySelector(`[data-addon-id="${id}"] .uninstall-button`);
+ }
+ 
+@@ -21,24 +23,21 @@ add_task(function* removeLegacyExtension
+   yield installAddon({
+     document,
+     path: "addons/unpacked/install.rdf",
+     name: addonName,
+   });
+ 
+   ok(getTargetEl(document, addonID), "add-on is shown");
+ 
+-  // Click the remove button and wait for the DOM to change.
+-  const addonListMutation = waitForMutation(
+-    getTemporaryAddonList(document).parentNode,
+-    { childList: true, subtree: true });
++  info("Click on the remove button and wait until the addon container is removed");
+   getRemoveButton(document, addonID).click();
+-  yield addonListMutation;
++  yield waitUntil(() => !getTargetEl(document, addonID), 100);
+ 
+-  ok(!getTargetEl(document, addonID), "add-on is not shown");
++  info("add-on is not shown");
+ 
+   yield closeAboutDebugging(tab);
+ });
+ 
+ add_task(function* removeWebextension() {
+   const addonID = "test-devtools-webextension@mozilla.org";
+   const addonName = "test-devtools-webextension";
+ 
+@@ -50,35 +49,33 @@ add_task(function* removeWebextension() 
+     document,
+     path: "addons/test-devtools-webextension/manifest.json",
+     name: addonName,
+     isWebExtension: true,
+   });
+ 
+   ok(getTargetEl(document, addonID), "add-on is shown");
+ 
+-  // Click the remove button and wait for the DOM to change.
+-  const addonListMutation = waitForMutation(
+-    getTemporaryAddonList(document).parentNode,
+-    { childList: true, subtree: true });
++  info("Click on the remove button and wait until the addon container is removed");
+   getRemoveButton(document, addonID).click();
+-  yield addonListMutation;
++  yield waitUntil(() => !getTargetEl(document, addonID), 100);
+ 
+-  ok(!getTargetEl(document, addonID), "add-on is not shown");
++  info("add-on is not shown");
+ 
+   yield closeAboutDebugging(tab);
+ });
+ 
+ add_task(function* onlyTempInstalledAddonsCanBeRemoved() {
+   const { tab, document } = yield openAboutDebugging("addons");
+   yield waitForInitialAddonList(document);
+-  const onAddonListUpdated = waitForMutation(getAddonList(document),
+-                                             { childList: true });
++
+   yield installAddonWithManager(getSupportsFile("addons/bug1273184.xpi").file);
+-  yield onAddonListUpdated;
+   const addon = yield getAddonByID("bug1273184@tests");
+ 
++  info("Wait until addon appears in about:debugging#addons");
++  yield waitUntilAddonContainer(PACKAGED_ADDON_NAME, document);
++
+   const removeButton = getRemoveButton(document, addon.id);
+   ok(!removeButton, "remove button is not shown");
+ 
+   yield tearDownAddon(addon);
+   yield closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_addons_toggle_debug.js b/devtools/client/aboutdebugging/test/browser_addons_toggle_debug.js
+--- a/devtools/client/aboutdebugging/test/browser_addons_toggle_debug.js
++++ b/devtools/client/aboutdebugging/test/browser_addons_toggle_debug.js
+@@ -32,34 +32,38 @@ add_task(function* () {
+   let addonDebugCheckbox = document.querySelector("#enable-addon-debugging");
+   ok(!addonDebugCheckbox.checked, "Addons debugging should be disabled.");
+ 
+   info("Check all debug buttons are disabled.");
+   let debugButtons = [...document.querySelectorAll("#addons .debug-button")];
+   ok(debugButtons.every(b => b.disabled), "Debug buttons should be disabled");
+ 
+   info("Click on 'Enable addons debugging' checkbox.");
+-  let addonsContainer = document.getElementById("addons");
+-  let onAddonsMutation = waitForMutation(addonsContainer,
+-    { subtree: true, attributes: true });
+   addonDebugCheckbox.click();
+-  yield onAddonsMutation;
+ 
+-  info("Check all debug buttons are enabled.");
+-  ok(addonDebugCheckbox.checked, "Addons debugging should be enabled.");
+-  debugButtons = [...document.querySelectorAll("#addons .debug-button")];
+-  ok(debugButtons.every(b => !b.disabled), "Debug buttons should be enabled");
++  info("Wait until all debug buttons are enabled.");
++  waitUntil(() => addonDebugCheckbox.checked && areDebugButtonsEnabled(document), 100);
++  info("Addons debugging should be enabled and debug buttons are enabled");
+ 
+   info("Click again on 'Enable addons debugging' checkbox.");
+-  onAddonsMutation = waitForMutation(addonsContainer,
+-    { subtree: true, attributes: true });
+   addonDebugCheckbox.click();
+-  yield onAddonsMutation;
+ 
+-  info("Check all debug buttons are disabled again.");
+-  debugButtons = [...document.querySelectorAll("#addons .debug-button")];
+-  ok(debugButtons.every(b => b.disabled), "Debug buttons should be disabled");
++  info("Wait until all debug buttons are enabled.");
++  waitUntil(() => areDebugButtonsDisabled(document), 100);
++  info("All debug buttons are disabled again.");
+ 
+   info("Uninstall addon installed earlier.");
+   yield uninstallAddon({document, id: ADDON_ID, name: ADDON_NAME});
+ 
+   yield closeAboutDebugging(tab);
+ });
++
++function getDebugButtons(document) {
++  return [...document.querySelectorAll("#addons .debug-button")];
++}
++
++function areDebugButtonsEnabled(document) {
++  return getDebugButtons(document).every(b => !b.disabled);
++}
++
++function areDebugButtonsDisabled(document) {
++  return getDebugButtons(document).every(b => b.disabled);
++}
+diff --git a/devtools/client/aboutdebugging/test/head.js b/devtools/client/aboutdebugging/test/head.js
+--- a/devtools/client/aboutdebugging/test/head.js
++++ b/devtools/client/aboutdebugging/test/head.js
+@@ -194,19 +194,16 @@ function getTabList(document) {
+ 
+ function* installAddon({document, path, name, isWebExtension}) {
+   // Mock the file picker to select a test addon
+   let MockFilePicker = SpecialPowers.MockFilePicker;
+   MockFilePicker.init(window);
+   let file = getSupportsFile(path);
+   MockFilePicker.setFiles([file.file]);
+ 
+-  let addonList = getTemporaryAddonList(document);
+-  let addonListMutation = waitForMutation(addonList, { childList: true });
+-
+   let onAddonInstalled;
+ 
+   if (isWebExtension) {
+     onAddonInstalled = new Promise(done => {
+       Management.on("startup", function listener(event, extension) {
+         if (extension.name != name) {
+           return;
+         }
+@@ -226,29 +223,21 @@ function* installAddon({document, path, 
+     });
+   }
+   // Trigger the file picker by clicking on the button
+   document.getElementById("load-addon-from-file").click();
+ 
+   yield onAddonInstalled;
+   ok(true, "Addon installed and running its bootstrap.js file");
+ 
+-  // Check that the addon appears in the UI
+-  yield addonListMutation;
+-  let names = [...addonList.querySelectorAll(".target-name")];
+-  names = names.map(element => element.textContent);
+-  ok(names.includes(name),
+-    "The addon name appears in the list of addons: " + names);
++  info("Wait for the addon to appear in the UI");
++  yield waitUntilAddonContainer(name, document);
+ }
+ 
+ function* uninstallAddon({document, id, name}) {
+-  let addonList = getAddonListWithAddon(document, id);
+-  let addonListMutation = waitForMutation(addonList.parentNode,
+-                                          { childList: true, subtree: true });
+-
+   // Now uninstall this addon
+   yield new Promise(done => {
+     AddonManager.getAddonByID(id, addon => {
+       let listener = {
+         onUninstalled: function (uninstalledAddon) {
+           if (uninstalledAddon != addon) {
+             return;
+           }
+@@ -257,69 +246,52 @@ function* uninstallAddon({document, id, 
+           done();
+         }
+       };
+       AddonManager.addAddonListener(listener);
+       addon.uninstall();
+     });
+   });
+ 
+-  yield addonListMutation;
++  info("Wait until the addon is removed from about:debugging");
++  yield waitUntil(() => !getAddonContainer(name, document), 100);
++}
+ 
+-  // If parentNode is none, that means the entire addonList was removed from the
+-  // document. This happens when the addon we are removing is the last one.
+-  if (addonList.parentNode !== null) {
+-    // Ensure that the UI removes the addon from the list
+-    let names = [...addonList.querySelectorAll(".target-name")];
+-    names = names.map(element => element.textContent);
+-    ok(!names.includes(name),
+-      "After uninstall, the addon name disappears from the list of addons: "
+-      + names);
+-  }
++function getAddonCount(document) {
++  const addonListContainer = getAddonList(document);
++  let addonElements = addonListContainer.querySelectorAll(".target");
++  return addonElements.length;
+ }
+ 
+ /**
+  * Returns a promise that will resolve when the add-on list has been updated.
+  *
+  * @param {Node} document
+  * @return {Promise}
+  */
+ function waitForInitialAddonList(document) {
+-  const addonListContainer = getAddonList(document);
+-  let addonCount = addonListContainer.querySelectorAll(".target");
+-  addonCount = addonCount ? [...addonCount].length : -1;
+-  info("Waiting for add-ons to load. Current add-on count: " + addonCount);
+-
+-  // This relies on the network speed of the actor responding to the
+-  // listAddons() request and also the speed of openAboutDebugging().
+-  let result;
+-  if (addonCount > 0) {
+-    info("Actually, the add-ons have already loaded");
+-    result = Promise.resolve();
+-  } else {
+-    result = waitForMutation(addonListContainer, { childList: true });
+-  }
+-  return result;
++  info("Waiting for add-ons to load. Current add-on count: " + getAddonCount(document));
++  return waitUntil(() => getAddonCount(document) > 0, 100);
+ }
+ 
+-function waitForInstallMessages(target) {
+-  return new Promise(resolve => {
+-    let observer = new MutationObserver((mutations) => {
+-      const messageAdded = mutations.some((mutation) => {
+-        return [...mutation.addedNodes].some((node) => {
+-          return node.classList.contains("addon-target-messages");
+-        });
+-      });
+-      if (messageAdded) {
+-        observer.disconnect();
+-        resolve();
+-      }
+-    });
+-    observer.observe(target, { childList: true });
++function getAddonContainer(name, document) {
++  let nameElements = [...document.querySelectorAll("#addons-panel .target-name")];
++  let nameElement = nameElements.filter(element => element.textContent === name)[0];
++  if (nameElement) {
++    return nameElement.closest(".addon-target-container");
++  }
++
++  return null;
++}
++
++function* waitUntilAddonContainer(name, document) {
++  yield waitUntil(() => {
++    return getAddonContainer(name, document);
+   });
++  return getAddonContainer(name, document);
+ }
+ 
+ /**
+  * Returns a promise that will resolve after receiving a mutation matching the
+  * provided mutation options on the provided target.
+  * @param {Node} target
+  * @param {Object} mutationOptions
+  * @return {Promise}

+ 268 - 0
mozilla-release/patches/1386613-3-59a1.patch

@@ -0,0 +1,268 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1512510258 -3600
+# Node ID 24bd9eb8d545b989c95c66662dbf1572b7cf747c
+# Parent  4179d92463897a1f59f84ad201e2ebf3e88e9ec7
+Bug 1386613 - stop using waitForMutations in aboutdebugging tests (remaining tests);r=ochameau
+
+MozReview-Commit-ID: HDNzIFwp4Ur
+
+diff --git a/devtools/client/aboutdebugging/test/.eslintrc.js b/devtools/client/aboutdebugging/test/.eslintrc.js
+--- a/devtools/client/aboutdebugging/test/.eslintrc.js
++++ b/devtools/client/aboutdebugging/test/.eslintrc.js
+@@ -4,23 +4,20 @@ module.exports = {
+   // Extend from the shared list of defined globals for mochitests.
+   "extends": "../../../.eslintrc.mochitests.js",
+   // All globals made available in aboutdebugging head.js file.
+   "globals": {
+     "AddonManager": true,
+     "addTab": true,
+     "assertHasTarget": true,
+     "CHROME_ROOT": true,
+-    "changeAboutDebuggingHash": true,
+     "closeAboutDebugging": true,
+     "getServiceWorkerList": true,
+     "getSupportsFile": true,
+     "installAddon": true,
+     "openAboutDebugging": true,
+-    "openPanel": true,
+     "removeTab": true,
+     "uninstallAddon": true,
+     "unregisterServiceWorker": true,
+     "waitForInitialAddonList": true,
+-    "waitForMutation": true,
+     "waitForServiceWorkerRegistered": true
+   }
+ };
+diff --git a/devtools/client/aboutdebugging/test/browser_page_not_found.js b/devtools/client/aboutdebugging/test/browser_page_not_found.js
+--- a/devtools/client/aboutdebugging/test/browser_page_not_found.js
++++ b/devtools/client/aboutdebugging/test/browser_page_not_found.js
+@@ -6,32 +6,36 @@
+ // error page.
+ // Every url navigating including #invalid-hash should be kept in history and
+ // navigate back as expected.
+ add_task(function* () {
+   let { tab, document } = yield openAboutDebugging("invalid-hash");
+   let element = document.querySelector(".header-name");
+   is(element.textContent, "Page not found", "Show error page");
+ 
+-  yield openPanel(document, "addons-panel");
++  info("Opening addons-panel panel");
++  document.querySelector("[aria-controls='addons-panel']").click();
++  yield waitUntilElement("#addons-panel", document);
++
+   yield waitForInitialAddonList(document);
+   element = document.querySelector(".header-name");
+   is(element.textContent, "Add-ons", "Show Addons");
+ 
+-  yield changeAboutDebuggingHash(document, "invalid-hash");
++  info("Opening about:debugging#invalid-hash");
++  window.openUILinkIn("about:debugging#invalid-hash", "current");
++  yield waitUntilElement(".error-page", document);
++
+   element = document.querySelector(".header-name");
+   is(element.textContent, "Page not found", "Show error page");
+ 
+   gBrowser.goBack();
+-  yield waitForMutation(
+-    document.querySelector(".main-content"), {childList: true});
++  yield waitUntilElement("#addons-panel", document);
+   yield waitForInitialAddonList(document);
+   element = document.querySelector(".header-name");
+   is(element.textContent, "Add-ons", "Show Addons");
+ 
+   gBrowser.goBack();
+-  yield waitForMutation(
+-    document.querySelector(".main-content"), {childList: true});
++  yield waitUntilElement(".error-page", document);
+   element = document.querySelector(".header-name");
+   is(element.textContent, "Page not found", "Show error page");
+ 
+   yield closeAboutDebugging(tab);
+ });
+diff --git a/devtools/client/aboutdebugging/test/browser_tabs.js b/devtools/client/aboutdebugging/test/browser_tabs.js
+--- a/devtools/client/aboutdebugging/test/browser_tabs.js
++++ b/devtools/client/aboutdebugging/test/browser_tabs.js
+@@ -5,53 +5,56 @@
+ 
+ const TAB_URL = "data:text/html,<title>foo</title>";
+ 
+ add_task(function* () {
+   let { tab, document } = yield openAboutDebugging("tabs");
+ 
+   // Wait for initial tabs list which may be empty
+   let tabsElement = getTabList(document);
+-  if (tabsElement.querySelectorAll(".target-name").length == 0) {
+-    yield waitForMutation(tabsElement, { childList: true });
+-  }
++  yield waitUntilElement(".target-name", tabsElement);
++
+   // Refresh tabsElement to get the .target-list element
+   tabsElement = getTabList(document);
+ 
+   let names = [...tabsElement.querySelectorAll(".target-name")];
+   let initialTabCount = names.length;
+ 
+-  // Open a new tab in background and wait for its addition in the UI
+-  let onNewTab = waitForMutation(tabsElement, { childList: true });
++  info("Open a new background tab");
+   let newTab = yield addTab(TAB_URL, { background: true });
+-  yield onNewTab;
+-
+-  // Check that the new tab appears in the UI, but with an empty name
+-  let newNames = [...tabsElement.querySelectorAll(".target-name")];
+-  newNames = newNames.filter(node => !names.includes(node));
+-  is(newNames.length, 1, "A new tab appeared in the list");
+-  let newTabTarget = newNames[0];
+ 
+-  // Then wait for title update, but on slow test runner, the title may already
+-  // be set to the expected value
+-  if (newTabTarget.textContent != "foo") {
+-    yield waitForContentMutation(newTabTarget);
+-  }
++  info("Wait for the tab to appear in the list with the correct name");
++  let container = yield waitUntilTabContainer("foo", document);
+ 
+-  // Then wait for title update, but on slow test runner, the title may already
+-  // be set to the expected value
+-  yield waitUntil(() => newTabTarget.title === TAB_URL);
+-
+-  // Check that the new tab appears in the UI
+-  is(newTabTarget.textContent, "foo", "The tab title got updated");
+-  is(newTabTarget.title, TAB_URL, "The tab tooltip is the url");
++  info("Wait until the title to update");
++  yield waitUntil(() => {
++    return container.querySelector(".target-name").title === TAB_URL;
++  }, 100);
+ 
+   // Finally, close the tab
+-  let onTabsUpdate = waitForMutation(tabsElement, { childList: true });
+   yield removeTab(newTab);
+-  yield onTabsUpdate;
++
++  info("Wait until the tab container is removed");
++  yield waitUntil(() => !getTabContainer("foo", document), 100);
+ 
+   // Check that the tab disappeared from the UI
+   names = [...tabsElement.querySelectorAll("#tabs .target-name")];
+   is(names.length, initialTabCount, "The tab disappeared from the UI");
+ 
+   yield closeAboutDebugging(tab);
+ });
++
++function getTabContainer(name, document) {
++  let nameElements = [...document.querySelectorAll("#tabs .target-name")];
++  let nameElement = nameElements.filter(element => element.textContent === name)[0];
++  if (nameElement) {
++    return nameElement.closest(".target-container");
++  }
++
++  return null;
++}
++
++function* waitUntilTabContainer(name, document) {
++  yield waitUntil(() => {
++    return getTabContainer(name, document);
++  });
++  return getTabContainer(name, document);
++}
+diff --git a/devtools/client/aboutdebugging/test/head.js b/devtools/client/aboutdebugging/test/head.js
+--- a/devtools/client/aboutdebugging/test/head.js
++++ b/devtools/client/aboutdebugging/test/head.js
+@@ -27,44 +27,22 @@ function* openAboutDebugging(page, win) 
+     url += "#" + page;
+   }
+ 
+   let tab = yield addTab(url, { window: win });
+   let browser = tab.linkedBrowser;
+   let document = browser.contentDocument;
+   let window = browser.contentWindow;
+ 
+-  if (!document.querySelector(".app")) {
+-    yield waitForMutation(document.body, { childList: true });
+-  }
++  info("Wait until the main about debugging container is available");
++  yield waitUntilElement(".app", document);
+ 
+   return { tab, document, window };
+ }
+ 
+-/**
+- * Change url hash for current about:debugging tab, return a promise after
+- * new content is loaded.
+- * @param  {DOMDocument}  document   container document from current tab
+- * @param  {String}       hash       hash for about:debugging
+- * @return {Promise}
+- */
+-function changeAboutDebuggingHash(document, hash) {
+-  info(`Opening about:debugging#${hash}`);
+-  window.openUILinkIn(`about:debugging#${hash}`, "current");
+-  return waitForMutation(
+-    document.querySelector(".main-content"), {childList: true});
+-}
+-
+-function openPanel(document, panelId) {
+-  info(`Opening ${panelId} panel`);
+-  document.querySelector(`[aria-controls="${panelId}"]`).click();
+-  return waitForMutation(
+-    document.querySelector(".main-content"), {childList: true});
+-}
+-
+ function closeAboutDebugging(tab) {
+   info("Closing about:debugging");
+   return removeTab(tab);
+ }
+ 
+ function getSupportsFile(path) {
+   let cr = Cc["@mozilla.org/chrome/chrome-registry;1"]
+     .getService(Ci.nsIChromeRegistry);
+@@ -285,49 +263,16 @@ function getAddonContainer(name, documen
+ function* waitUntilAddonContainer(name, document) {
+   yield waitUntil(() => {
+     return getAddonContainer(name, document);
+   });
+   return getAddonContainer(name, document);
+ }
+ 
+ /**
+- * Returns a promise that will resolve after receiving a mutation matching the
+- * provided mutation options on the provided target.
+- * @param {Node} target
+- * @param {Object} mutationOptions
+- * @return {Promise}
+- */
+-function waitForMutation(target, mutationOptions) {
+-  return new Promise(resolve => {
+-    let observer = new MutationObserver(() => {
+-      observer.disconnect();
+-      resolve();
+-    });
+-    observer.observe(target, mutationOptions);
+-  });
+-}
+-
+-/**
+- * Returns a promise that will resolve after receiving a mutation in the subtree of the
+- * provided target. Depending on the current React implementation, a text change might be
+- * observable as a childList mutation or a characterData mutation.
+- *
+- * @param {Node} target
+- * @return {Promise}
+- */
+-function waitForContentMutation(target) {
+-  return waitForMutation(target, {
+-    characterData: true,
+-    childList: true,
+-    subtree: true
+-  });
+-}
+-
+-/**
+  * Checks if an about:debugging TargetList element contains a Target element
+  * corresponding to the specified name.
+  * @param {Boolean} expected
+  * @param {Document} document
+  * @param {String} type
+  * @param {String} name
+  */
+ function assertHasTarget(expected, document, type, name) {

+ 49 - 0
mozilla-release/patches/1394235-57a1.patch

@@ -0,0 +1,49 @@
+# HG changeset patch
+# User Florian Quèze <florian@queze.net>
+# Date 1504219364 -7200
+# Node ID 9e59ca9dd30d73c57dad6a7e6220b159dd1e6db5
+# Parent  45b6d0d482c25dbb9b8afb0b0a0e7a980e20e96e
+Bug 1394235 - If a browser action icon is rectangular, the height of the icon needs to be set, r=kmag.
+
+diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css
+--- a/browser/base/content/browser.css
++++ b/browser/base/content/browser.css
+@@ -349,16 +349,17 @@ toolbarpaletteitem:-moz-any([place="pale
+   display: -moz-box;
+ }
+ 
+ toolbarpaletteitem > toolbaritem[sdkstylewidget="true"][cui-areatype="toolbar"] > .toolbarbutton-text {
+   display: -moz-box;
+ }
+ 
+ .webextension-browser-action > .toolbarbutton-badge-stack > .toolbarbutton-icon {
++  height: 16px;
+   width: 16px;
+ }
+ 
+ @media not all and (min-resolution: 1.1dppx) {
+   .webextension-browser-action {
+     list-style-image: var(--webextension-toolbar-image, inherit);
+   }
+ 
+diff --git a/devtools/client/aboutdebugging/aboutdebugging.css b/devtools/client/aboutdebugging/aboutdebugging.css
+--- a/devtools/client/aboutdebugging/aboutdebugging.css
++++ b/devtools/client/aboutdebugging/aboutdebugging.css
+@@ -58,16 +58,17 @@ button {
+   min-height: 34px;
+   display: flex;
+   flex-direction: row;
+   align-items: start;
+ }
+ 
+ .target-icon {
+   height: 24px;
++  width: 24px;
+   margin-inline-end: 5px;
+ }
+ 
+ .target-icon:not([src]) {
+   display: none;
+ }
+ 
+ .inverted-icons .target-icon {

+ 72 - 0
mozilla-release/patches/1401343-58a1.patch

@@ -0,0 +1,72 @@
+# HG changeset patch
+# User Matthew Noorenberghe <mozilla@noorenberghe.ca>
+# Date 1506623306 25200
+# Node ID f1b4bde4dadd0bc2f4867334c051c915bfe814b9
+# Parent  16677f7fa26d987b2c2c10196a19b68d2b9093ab
+Bug 1401343 - Expose a 'tabs' getter in the child-process actor. r=jryans,ochameau
+
+MozReview-Commit-ID: U5xB35kBv8
+
+diff --git a/devtools/server/actors/child-process.js b/devtools/server/actors/child-process.js
+--- a/devtools/server/actors/child-process.js
++++ b/devtools/server/actors/child-process.js
+@@ -1,15 +1,16 @@
+ /* 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 { Cc, Ci, Cu } = require("chrome");
++const Services = require("Services");
+ 
+ const { ChromeDebuggerActor } = require("devtools/server/actors/script");
+ const { WebConsoleActor } = require("devtools/server/actors/webconsole");
+ const makeDebugger = require("devtools/server/actors/utils/make-debugger");
+ const { ActorPool } = require("devtools/server/main");
+ const { assert } = require("devtools/shared/DevToolsUtils");
+ const { TabSources } = require("./utils/TabSources");
+ 
+@@ -22,21 +23,40 @@ function ChildProcessActor(connection) {
+   this.threadActor = null;
+ 
+   // Use a see-everything debugger
+   this.makeDebugger = makeDebugger.bind(null, {
+     findDebuggees: dbg => dbg.findAllGlobals(),
+     shouldAddNewGlobalAsDebuggee: global => true
+   });
+ 
++  let sandboxPrototype = {
++    get tabs() {
++      let tabs = [];
++      let windowEnumerator = Services.ww.getWindowEnumerator();
++      while (windowEnumerator.hasMoreElements()) {
++        let window = windowEnumerator.getNext().QueryInterface(Ci.nsIDOMWindow);
++        let tabChildGlobal = window.QueryInterface(Ci.nsIInterfaceRequestor)
++                                   .getInterface(Ci.nsIDocShell)
++                                   .sameTypeRootTreeItem
++                                   .QueryInterface(Ci.nsIInterfaceRequestor)
++                                   .getInterface(Ci.nsIContentFrameMessageManager);
++        tabs.push(tabChildGlobal);
++      }
++      return tabs;
++    },
++  };
++
+   // Scope into which the webconsole executes:
+-  // An empty sandbox with chrome privileges
++  // A sandbox with chrome privileges with a `tabs` getter.
+   let systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
+     .createInstance(Ci.nsIPrincipal);
+-  let sandbox = Cu.Sandbox(systemPrincipal);
++  let sandbox = Cu.Sandbox(systemPrincipal, {
++    sandboxPrototype,
++  });
+   this._consoleScope = sandbox;
+ 
+   this._workerList = null;
+   this._workerActorPool = null;
+   this._onWorkerListChanged = this._onWorkerListChanged.bind(this);
+ }
+ exports.ChildProcessActor = ChildProcessActor;
+ 

+ 137 - 0
mozilla-release/patches/1405008-59a1.patch

@@ -0,0 +1,137 @@
+# HG changeset patch
+# User Alexandre Poirot <poirot.alex@gmail.com>
+# Date 1506976832 -7200
+# Node ID ae73805527947837137ac0ce89a011fb6ae178a8
+# Parent  1b8259e52b39795cebe6d3fa0ca6139f7f0011f7
+Bug 1405008 - Make WebIDE warn when connecting to old runtimes. r=jdescottes
+
+MozReview-Commit-ID: KQc2b1ohksA
+
+diff --git a/devtools/shared/client/debugger-client.js b/devtools/shared/client/debugger-client.js
+--- a/devtools/shared/client/debugger-client.js
++++ b/devtools/shared/client/debugger-client.js
+@@ -1,38 +1,47 @@
+ /* 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 Services = require("Services");
+ const promise = require("devtools/shared/deprecated-sync-thenables");
+ 
+ const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+ const { getStack, callFunctionWithAsyncStack } = require("devtools/shared/platform/stack");
+ const eventSource = require("devtools/shared/client/event-source");
+ const {
+   ThreadStateTypes,
+   UnsolicitedNotifications,
+   UnsolicitedPauses,
+ } = require("./constants");
+ 
+ loader.lazyRequireGetter(this, "Authentication", "devtools/shared/security/auth");
+ loader.lazyRequireGetter(this, "DebuggerSocket", "devtools/shared/security/socket", true);
+ loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
++loader.lazyRequireGetter(this, "getDeviceFront", "devtools/shared/fronts/device", true);
++
+ loader.lazyRequireGetter(this, "WebConsoleClient", "devtools/shared/webconsole/client", true);
+ loader.lazyRequireGetter(this, "AddonClient", "devtools/shared/client/addon-client");
+ loader.lazyRequireGetter(this, "RootClient", "devtools/shared/client/root-client");
+ loader.lazyRequireGetter(this, "TabClient", "devtools/shared/client/tab-client");
+ loader.lazyRequireGetter(this, "ThreadClient", "devtools/shared/client/thread-client");
+ loader.lazyRequireGetter(this, "TraceClient", "devtools/shared/client/trace-client");
+ loader.lazyRequireGetter(this, "WorkerClient", "devtools/shared/client/worker-client");
+ 
+ const noop = () => {};
+ 
++// Define the minimum officially supported version of Firefox when connecting to a remote
++// runtime. (Use ".0a1" to support the very first nightly version)
++// This is usually the current ESR version.
++const MIN_SUPPORTED_PLATFORM_VERSION = "52.0a1";
++const MS_PER_DAY = 86400000;
++
+ /**
+  * Creates a client for the remote debugging protocol server. This client
+  * provides the means to communicate with the server and exchange the messages
+  * required by the protocol in a traditional JavaScript API.
+  */
+ function DebuggerClient(transport) {
+   this._transport = transport;
+   this._transport.hooks = this;
+@@ -178,16 +187,76 @@ DebuggerClient.prototype = {
+       deferred.resolve([applicationType, traits]);
+     });
+ 
+     this._transport.ready();
+     return deferred.promise;
+   },
+ 
+   /**
++   * Tells if the remote device is using a supported version of Firefox.
++   *
++   * @return Object with the following attributes:
++   *   * String incompatible
++   *            null if the runtime is compatible,
++   *            "too-recent" if the runtime uses a too recent version,
++   *            "too-old" if the runtime uses a too old version.
++   *   * String minVersion
++   *            The minimum supported version.
++   *   * String runtimeVersion
++   *            The remote runtime version.
++   *   * String localID
++   *            Build ID of local runtime. A date with like this: YYYYMMDD.
++   *   * String deviceID
++   *            Build ID of remote runtime. A date with like this: YYYYMMDD.
++   */
++  async checkRuntimeVersion(listTabsForm) {
++    let incompatible = null;
++
++    // Instead of requiring to pass `listTabsForm` here,
++    // we can call getRoot() instead, but only once Firefox ESR59 is released
++    let deviceFront = await getDeviceFront(this, listTabsForm);
++    let desc = await deviceFront.getDescription();
++
++    // 1) Check for Firefox too recent on device.
++    // Compare device and firefox build IDs
++    // and only compare by day (strip hours/minutes) to prevent
++    // warning against builds of the same day.
++    let runtimeID = desc.appbuildid.substr(0, 8);
++    let localID = Services.appinfo.appBuildID.substr(0, 8);
++    function buildIDToDate(buildID) {
++      let fields = buildID.match(/(\d{4})(\d{2})(\d{2})/);
++      // Date expects 0 - 11 for months
++      return new Date(fields[1], Number.parseInt(fields[2], 10) - 1, fields[3]);
++    }
++    let runtimeDate = buildIDToDate(runtimeID);
++    let localDate = buildIDToDate(localID);
++    // Allow device to be newer by up to a week.  This accommodates those with
++    // local device builds, since their devices will almost always be newer
++    // than the client.
++    if (runtimeDate - localDate > 7 * MS_PER_DAY) {
++      incompatible = "too-recent";
++    }
++
++    // 2) Check for too old Firefox on device
++    let platformversion = desc.platformversion;
++    if (Services.vc.compare(platformversion, MIN_SUPPORTED_PLATFORM_VERSION) < 0) {
++      incompatible = "too-old";
++    }
++
++    return {
++      incompatible,
++      minVersion: MIN_SUPPORTED_PLATFORM_VERSION,
++      runtimeVersion: platformversion,
++      localID,
++      runtimeID,
++    };
++  },
++
++  /**
+    * Shut down communication with the debugging server.
+    *
+    * @param onClosed function
+    *        If specified, will be called when the debugging connection
+    *        has been closed. This parameter is deprecated - please use
+    *        the returned Promise.
+    * @return Promise
+    *         Resolves after the underlying transport is closed.

+ 68 - 0
mozilla-release/patches/1407426-1-58a1.patch

@@ -0,0 +1,68 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1507670480 -7200
+# Node ID 0f216d29ac806bc6168da2eb516f21b3ff07a0ba
+# Parent  fad4bea403121845bef857bc47de06b8137abea5
+Bug 1407426 - return the toolbox created when opening BrowserContentToolbox;r=jlast
+
+We need to retrieve the toolbox object created in openContentProcessToolbox in order
+to use it in tests;
+
+MozReview-Commit-ID: BC8bWaiYAnS
+
+diff --git a/devtools/client/framework/devtools-browser.js b/devtools/client/framework/devtools-browser.js
+--- a/devtools/client/framework/devtools-browser.js
++++ b/devtools/client/framework/devtools-browser.js
+@@ -324,40 +324,47 @@ var gDevToolsBrowser = exports.gDevTools
+               });
+               deferred.resolve(target);
+             });
+     });
+ 
+     return deferred.promise;
+   },
+ 
+-   // Used by menus.js
++  /**
++   * Open the Browser Content Toolbox for the provided gBrowser instance.
++   * Returns a promise that resolves with a toolbox instance. If no content process is
++   * available, the promise will be rejected and a message will be displayed to the user.
++   *
++   * Used by menus.js
++  */
+   openContentProcessToolbox(gBrowser) {
+     let { childCount } = Services.ppmm;
+     // Get the process message manager for the current tab
+     let mm = gBrowser.selectedBrowser.messageManager.processMessageManager;
+     let processId = null;
+     for (let i = 1; i < childCount; i++) {
+       let child = Services.ppmm.getChildAt(i);
+       if (child == mm) {
+         processId = i;
+         break;
+       }
+     }
+     if (processId) {
+-      this._getContentProcessTarget(processId)
++      return this._getContentProcessTarget(processId)
+           .then(target => {
+             // Display a new toolbox, in a new window, with debugger by default
+             return gDevTools.showToolbox(target, "jsdebugger",
+                                          Toolbox.HostType.WINDOW);
+           });
+-    } else {
+-      let msg = L10N.getStr("toolbox.noContentProcessForTab.message");
+-      Services.prompt.alert(null, "", msg);
+     }
++
++    let msg = L10N.getStr("toolbox.noContentProcessForTab.message");
++    Services.prompt.alert(null, "", msg);
++    return Promise.reject(msg);
+   },
+ 
+   /**
+    * Add the devtools-browser stylesheet to browser window's document. Returns a promise.
+    *
+    * @param  {Window} win
+    *         The window on which the stylesheet should be added.
+    * @return {Promise} promise that resolves when the stylesheet is loaded (or rejects

+ 177 - 0
mozilla-release/patches/1407426-2-58a1.patch

@@ -0,0 +1,177 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1507625012 -7200
+# Node ID caef338370a531902aea56a36f3c8f9b6e41c501
+# Parent  f0c355254059dd1389c15433bae74b6760856542
+Bug 1407426 - add smoke test for debugger in browser content toolbox;r=jlast
+
+This test is a simpler version of the breakpoints test, but runs from the
+browser content toolbox. We don't particularly assess that the debugger can
+see more sources than the web content sources, but at least this ensures that
+the debugger is correctly initialized.
+
+MozReview-Commit-ID: 5rSb7z3HP4F
+
+diff --git a/devtools/client/debugger/new/test/mochitest/browser.ini b/devtools/client/debugger/new/test/mochitest/browser.ini
+--- a/devtools/client/debugger/new/test/mochitest/browser.ini
++++ b/devtools/client/debugger/new/test/mochitest/browser.ini
+@@ -58,16 +58,18 @@ support-files =
+ [browser_dbg-async-stepping.js]
+ [browser_dbg-breaking.js]
+ [browser_dbg-breaking-from-console.js]
+ [browser_dbg-breakpoints.js]
+ [browser_dbg-breakpoints-toggle.js]
+ [browser_dbg-breakpoints-reloading.js]
+ skip-if = true # Bug 1383576
+ [browser_dbg-breakpoints-cond.js]
++[browser_dbg-browser-content-toolbox.js]
++skip-if = !e10s # This test is only valid in e10s
+ [browser_dbg-call-stack.js]
+ [browser_dbg-expressions.js]
+ [browser_dbg-scopes.js]
+ [browser_dbg-chrome-create.js]
+ [browser_dbg-chrome-debugging.js]
+ skip-if = debug # bug 1374187
+ [browser_dbg-console.js]
+ [browser_dbg-debugger-buttons.js]
+@@ -97,9 +99,9 @@ skip-if = true # Bug 1393121, 1393299
+ [browser_dbg-sourcemaps.js]
+ [browser_dbg-sourcemaps-reloading.js]
+ [browser_dbg-sourcemaps2.js]
+ [browser_dbg-sourcemaps-bogus.js]
+ [browser_dbg-sources.js]
+ [browser_dbg-tabs.js]
+ [browser_dbg-toggling-tools.js]
+ [browser_dbg-wasm-sourcemaps.js]
+-skip-if = true 
++skip-if = true
+diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-browser-content-toolbox.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-browser-content-toolbox.js
+new file mode 100644
+--- /dev/null
++++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-browser-content-toolbox.js
+@@ -0,0 +1,73 @@
++/* -*- 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/ */
++
++"use strict";
++
++/**
++ * Tests that the debugger is succesfully loaded in the Browser Content Toolbox.
++ */
++
++const {gDevToolsBrowser} = require("devtools/client/framework/devtools-browser");
++
++function toggleBreakpoint(dbg, index) {
++  const bp = findElement(dbg, "breakpointItem", index);
++  const input = bp.querySelector("input");
++  input.click();
++}
++
++async function disableBreakpoint(dbg, index) {
++  const disabled = waitForDispatch(dbg, "DISABLE_BREAKPOINT");
++  toggleBreakpoint(dbg, index);
++  await disabled;
++}
++
++async function enableBreakpoint(dbg, index) {
++  const enabled = waitForDispatch(dbg, "ENABLE_BREAKPOINT");
++  toggleBreakpoint(dbg, index);
++  await enabled;
++}
++
++function findBreakpoint(dbg, url, line) {
++  const { selectors: { getBreakpoint }, getState } = dbg;
++  const source = findSource(dbg, url);
++  return getBreakpoint(getState(), { sourceId: source.id, line });
++}
++
++add_task(async function() {
++  clearDebuggerPreferences();
++
++  info("Open a tab pointing to doc-scripts.html");
++  await addTab(EXAMPLE_URL + "doc-scripts.html");
++
++  info("Open the Browser Content Toolbox");
++  let toolbox = await gDevToolsBrowser.openContentProcessToolbox(gBrowser);
++
++  info("Wait for the debugger to be ready");
++  await toolbox.getPanelWhenReady("jsdebugger");
++
++  let dbg = createDebuggerContext(toolbox);
++  ok(dbg, "Debugger context is available");
++
++  info("Create a breakpoint");
++  await selectSource(dbg, "simple2");
++  await addBreakpoint(dbg, "simple2", 3);
++
++  info("Disable the breakpoint");
++  await disableBreakpoint(dbg, 1);
++  let bp = findBreakpoint(dbg, "simple2", 3);
++  is(bp.disabled, true, "breakpoint is disabled");
++
++  info("Enable the breakpoint");
++  await enableBreakpoint(dbg, 1);
++  bp = findBreakpoint(dbg, "simple2", 3);
++  is(bp.disabled, false, "breakpoint is enabled");
++
++  info("Close the browser toolbox window");
++  let onToolboxDestroyed = toolbox.once("destroyed");
++  toolbox.win.top.close();
++  await onToolboxDestroyed;
++
++  info("Toolbox is destroyed");
++});
+diff --git a/devtools/client/debugger/new/test/mochitest/head.js b/devtools/client/debugger/new/test/mochitest/head.js
+--- a/devtools/client/debugger/new/test/mochitest/head.js
++++ b/devtools/client/debugger/new/test/mochitest/head.js
+@@ -361,33 +361,40 @@ function createDebuggerContext(toolbox) 
+     getState: store.getState,
+     store: store,
+     client: client,
+     toolbox: toolbox,
+     win: win
+   };
+ }
+ 
++
++/**
++ * Clear all the debugger related preferences.
++ */
++function clearDebuggerPreferences() {
++  Services.prefs.clearUserPref("devtools.debugger.pause-on-exceptions");
++  Services.prefs.clearUserPref("devtools.debugger.ignore-caught-exceptions");
++  Services.prefs.clearUserPref("devtools.debugger.tabs");
++  Services.prefs.clearUserPref("devtools.debugger.pending-selected-location");
++  Services.prefs.clearUserPref("devtools.debugger.pending-breakpoints");
++  Services.prefs.clearUserPref("devtools.debugger.expressions");
++}
++
+ /**
+  * Intilializes the debugger.
+  *
+  * @memberof mochitest
+  * @param {String} url
+- * @param {Array} sources
+  * @return {Promise} dbg
+  * @static
+  */
+-function initDebugger(url, ...sources) {
++function initDebugger(url) {
+   return Task.spawn(function*() {
+-    Services.prefs.clearUserPref("devtools.debugger.pause-on-exceptions");
+-    Services.prefs.clearUserPref("devtools.debugger.ignore-caught-exceptions");
+-    Services.prefs.clearUserPref("devtools.debugger.tabs");
+-    Services.prefs.clearUserPref("devtools.debugger.pending-selected-location");
+-    Services.prefs.clearUserPref("devtools.debugger.pending-breakpoints");
+-    Services.prefs.clearUserPref("devtools.debugger.expressions");
++    clearDebuggerPreferences();
+     const toolbox = yield openNewTabAndToolbox(EXAMPLE_URL + url, "jsdebugger");
+     return createDebuggerContext(toolbox);
+   });
+ }
+ 
+ window.resumeTest = undefined;
+ /**
+  * Pause the test and let you interact with the debugger.

+ 301 - 0
mozilla-release/patches/1411199-58a1.patch

@@ -0,0 +1,301 @@
+# HG changeset patch
+# User Maxime Vaillancourt <maxvaillancourt1@gmail.com>
+# Date 1508900361 14400
+# Node ID a8526281637dec379cd45d6c766ddb99296da9c4
+# Parent  7a20f825dfa610ee80c18d3a35a18fc2425ac464
+Bug 1411199 - CamelCase all aboutdebugging component files. r=jdescottes
+
+diff --git a/devtools/client/aboutdebugging/components/aboutdebugging.js b/devtools/client/aboutdebugging/components/Aboutdebugging.js
+rename from devtools/client/aboutdebugging/components/aboutdebugging.js
+rename to devtools/client/aboutdebugging/components/Aboutdebugging.js
+--- a/devtools/client/aboutdebugging/components/aboutdebugging.js
++++ b/devtools/client/aboutdebugging/components/Aboutdebugging.js
+@@ -5,24 +5,24 @@
+ /* eslint-env browser */
+ 
+ "use strict";
+ 
+ const { createFactory, createClass, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ const Services = require("Services");
+ 
+-const PanelMenu = createFactory(require("./panel-menu"));
++const PanelMenu = createFactory(require("./PanelMenu"));
+ 
+ loader.lazyGetter(this, "AddonsPanel",
+-  () => createFactory(require("./addons/panel")));
++  () => createFactory(require("./addons/Panel")));
+ loader.lazyGetter(this, "TabsPanel",
+-  () => createFactory(require("./tabs/panel")));
++  () => createFactory(require("./tabs/Panel")));
+ loader.lazyGetter(this, "WorkersPanel",
+-  () => createFactory(require("./workers/panel")));
++  () => createFactory(require("./workers/Panel")));
+ 
+ loader.lazyRequireGetter(this, "DebuggerClient",
+   "devtools/shared/client/debugger-client", true);
+ loader.lazyRequireGetter(this, "Telemetry",
+   "devtools/client/shared/telemetry");
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+diff --git a/devtools/client/aboutdebugging/components/panel-header.js b/devtools/client/aboutdebugging/components/PanelHeader.js
+rename from devtools/client/aboutdebugging/components/panel-header.js
+rename to devtools/client/aboutdebugging/components/PanelHeader.js
+diff --git a/devtools/client/aboutdebugging/components/panel-menu.js b/devtools/client/aboutdebugging/components/PanelMenu.js
+rename from devtools/client/aboutdebugging/components/panel-menu.js
+rename to devtools/client/aboutdebugging/components/PanelMenu.js
+--- a/devtools/client/aboutdebugging/components/panel-menu.js
++++ b/devtools/client/aboutdebugging/components/PanelMenu.js
+@@ -1,17 +1,17 @@
+ /* 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 { createClass, createFactory, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+-const PanelMenuEntry = createFactory(require("./panel-menu-entry"));
++const PanelMenuEntry = createFactory(require("./PanelMenuEntry"));
+ 
+ module.exports = createClass({
+   displayName: "PanelMenu",
+ 
+   propTypes: {
+     panels: PropTypes.arrayOf(PropTypes.shape({
+       id: PropTypes.string.isRequired,
+       name: PropTypes.string.isRequired,
+diff --git a/devtools/client/aboutdebugging/components/panel-menu-entry.js b/devtools/client/aboutdebugging/components/PanelMenuEntry.js
+rename from devtools/client/aboutdebugging/components/panel-menu-entry.js
+rename to devtools/client/aboutdebugging/components/PanelMenuEntry.js
+diff --git a/devtools/client/aboutdebugging/components/target-list.js b/devtools/client/aboutdebugging/components/TargetList.js
+rename from devtools/client/aboutdebugging/components/target-list.js
+rename to devtools/client/aboutdebugging/components/TargetList.js
+diff --git a/devtools/client/aboutdebugging/components/addons/controls.js b/devtools/client/aboutdebugging/components/addons/Controls.js
+rename from devtools/client/aboutdebugging/components/addons/controls.js
+rename to devtools/client/aboutdebugging/components/addons/Controls.js
+--- a/devtools/client/aboutdebugging/components/addons/controls.js
++++ b/devtools/client/aboutdebugging/components/addons/Controls.js
+@@ -9,17 +9,17 @@
+ 
+ loader.lazyImporter(this, "AddonManager",
+   "resource://gre/modules/AddonManager.jsm");
+ 
+ const { Cc, Ci } = require("chrome");
+ const { createFactory, createClass, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ const Services = require("Services");
+-const AddonsInstallError = createFactory(require("./install-error"));
++const AddonsInstallError = createFactory(require("./InstallError"));
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+ const MORE_INFO_URL = "https://developer.mozilla.org/docs/Tools" +
+                       "/about:debugging#Enabling_add-on_debugging";
+ 
+ module.exports = createClass({
+diff --git a/devtools/client/aboutdebugging/components/addons/install-error.js b/devtools/client/aboutdebugging/components/addons/InstallError.js
+rename from devtools/client/aboutdebugging/components/addons/install-error.js
+rename to devtools/client/aboutdebugging/components/addons/InstallError.js
+diff --git a/devtools/client/aboutdebugging/components/addons/panel.js b/devtools/client/aboutdebugging/components/addons/Panel.js
+rename from devtools/client/aboutdebugging/components/addons/panel.js
+rename to devtools/client/aboutdebugging/components/addons/Panel.js
+--- a/devtools/client/aboutdebugging/components/addons/panel.js
++++ b/devtools/client/aboutdebugging/components/addons/Panel.js
+@@ -5,20 +5,20 @@
+ "use strict";
+ 
+ const { AddonManager } = require("resource://gre/modules/AddonManager.jsm");
+ const { Management } = require("resource://gre/modules/Extension.jsm");
+ const { createFactory, createClass, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ const Services = require("Services");
+ 
+-const AddonsControls = createFactory(require("./controls"));
+-const AddonTarget = createFactory(require("./target"));
+-const PanelHeader = createFactory(require("../panel-header"));
+-const TargetList = createFactory(require("../target-list"));
++const AddonsControls = createFactory(require("./Controls"));
++const AddonTarget = createFactory(require("./Target"));
++const PanelHeader = createFactory(require("../PanelHeader"));
++const TargetList = createFactory(require("../TargetList"));
+ 
+ loader.lazyRequireGetter(this, "DebuggerClient",
+   "devtools/shared/client/debugger-client", true);
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+ const ExtensionIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
+diff --git a/devtools/client/aboutdebugging/components/addons/target.js b/devtools/client/aboutdebugging/components/addons/Target.js
+rename from devtools/client/aboutdebugging/components/addons/target.js
+rename to devtools/client/aboutdebugging/components/addons/Target.js
+diff --git a/devtools/client/aboutdebugging/components/addons/moz.build b/devtools/client/aboutdebugging/components/addons/moz.build
+--- a/devtools/client/aboutdebugging/components/addons/moz.build
++++ b/devtools/client/aboutdebugging/components/addons/moz.build
+@@ -1,10 +1,10 @@
+ # 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(
+-    'controls.js',
+-    'install-error.js',
+-    'panel.js',
+-    'target.js',
++    'Controls.js',
++    'InstallError.js',
++    'Panel.js',
++    'Target.js',
+ )
+diff --git a/devtools/client/aboutdebugging/components/moz.build b/devtools/client/aboutdebugging/components/moz.build
+--- a/devtools/client/aboutdebugging/components/moz.build
++++ b/devtools/client/aboutdebugging/components/moz.build
+@@ -4,14 +4,14 @@
+ 
+ DIRS += [
+     'addons',
+     'tabs',
+     'workers',
+ ]
+ 
+ DevToolsModules(
+-    'aboutdebugging.js',
+-    'panel-header.js',
+-    'panel-menu-entry.js',
+-    'panel-menu.js',
+-    'target-list.js',
++    'Aboutdebugging.js',
++    'PanelHeader.js',
++    'PanelMenu.js',
++    'PanelMenuEntry.js',
++    'TargetList.js',
+ )
+diff --git a/devtools/client/aboutdebugging/components/tabs/panel.js b/devtools/client/aboutdebugging/components/tabs/Panel.js
+rename from devtools/client/aboutdebugging/components/tabs/panel.js
+rename to devtools/client/aboutdebugging/components/tabs/Panel.js
+--- a/devtools/client/aboutdebugging/components/tabs/panel.js
++++ b/devtools/client/aboutdebugging/components/tabs/Panel.js
+@@ -5,19 +5,19 @@
+ /* eslint-env browser */
+ 
+ "use strict";
+ 
+ const { createClass, createFactory, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ const Services = require("Services");
+ 
+-const PanelHeader = createFactory(require("../panel-header"));
+-const TargetList = createFactory(require("../target-list"));
+-const TabTarget = createFactory(require("./target"));
++const PanelHeader = createFactory(require("../PanelHeader"));
++const TargetList = createFactory(require("../TargetList"));
++const TabTarget = createFactory(require("./Target"));
+ 
+ loader.lazyRequireGetter(this, "DebuggerClient",
+   "devtools/shared/client/debugger-client", true);
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+ module.exports = createClass({
+diff --git a/devtools/client/aboutdebugging/components/tabs/target.js b/devtools/client/aboutdebugging/components/tabs/Target.js
+rename from devtools/client/aboutdebugging/components/tabs/target.js
+rename to devtools/client/aboutdebugging/components/tabs/Target.js
+diff --git a/devtools/client/aboutdebugging/components/tabs/moz.build b/devtools/client/aboutdebugging/components/tabs/moz.build
+--- a/devtools/client/aboutdebugging/components/tabs/moz.build
++++ b/devtools/client/aboutdebugging/components/tabs/moz.build
+@@ -1,8 +1,8 @@
+ # 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(
+-    'panel.js',
+-    'target.js',
++    'Panel.js',
++    'Target.js',
+ )
+diff --git a/devtools/client/aboutdebugging/components/workers/multi-e10s-warning.js b/devtools/client/aboutdebugging/components/workers/MultiE10sWarning.js
+rename from devtools/client/aboutdebugging/components/workers/multi-e10s-warning.js
+rename to devtools/client/aboutdebugging/components/workers/MultiE10sWarning.js
+diff --git a/devtools/client/aboutdebugging/components/workers/panel.js b/devtools/client/aboutdebugging/components/workers/Panel.js
+rename from devtools/client/aboutdebugging/components/workers/panel.js
+rename to devtools/client/aboutdebugging/components/workers/Panel.js
+--- a/devtools/client/aboutdebugging/components/workers/panel.js
++++ b/devtools/client/aboutdebugging/components/workers/Panel.js
+@@ -8,21 +8,21 @@ loader.lazyImporter(this, "PrivateBrowsi
+   "resource://gre/modules/PrivateBrowsingUtils.jsm");
+ 
+ const { Ci } = require("chrome");
+ const { createClass, createFactory, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ const { getWorkerForms } = require("../../modules/worker");
+ const Services = require("Services");
+ 
+-const PanelHeader = createFactory(require("../panel-header"));
+-const TargetList = createFactory(require("../target-list"));
+-const WorkerTarget = createFactory(require("./target"));
+-const MultiE10SWarning = createFactory(require("./multi-e10s-warning"));
+-const ServiceWorkerTarget = createFactory(require("./service-worker-target"));
++const PanelHeader = createFactory(require("../PanelHeader"));
++const TargetList = createFactory(require("../TargetList"));
++const WorkerTarget = createFactory(require("./Target"));
++const MultiE10SWarning = createFactory(require("./MultiE10sWarning"));
++const ServiceWorkerTarget = createFactory(require("./ServiceWorkerTarget"));
+ 
+ loader.lazyImporter(this, "PrivateBrowsingUtils",
+   "resource://gre/modules/PrivateBrowsingUtils.jsm");
+ 
+ loader.lazyRequireGetter(this, "DebuggerClient",
+   "devtools/shared/client/debugger-client", true);
+ 
+ const Strings = Services.strings.createBundle(
+diff --git a/devtools/client/aboutdebugging/components/workers/service-worker-target.js b/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js
+rename from devtools/client/aboutdebugging/components/workers/service-worker-target.js
+rename to devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js
+diff --git a/devtools/client/aboutdebugging/components/workers/target.js b/devtools/client/aboutdebugging/components/workers/Target.js
+rename from devtools/client/aboutdebugging/components/workers/target.js
+rename to devtools/client/aboutdebugging/components/workers/Target.js
+diff --git a/devtools/client/aboutdebugging/components/workers/moz.build b/devtools/client/aboutdebugging/components/workers/moz.build
+--- a/devtools/client/aboutdebugging/components/workers/moz.build
++++ b/devtools/client/aboutdebugging/components/workers/moz.build
+@@ -1,10 +1,10 @@
+ # 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(
+-    'multi-e10s-warning.js',
+-    'panel.js',
+-    'service-worker-target.js',
+-    'target.js',
++    'MultiE10sWarning.js',
++    'Panel.js',
++    'ServiceWorkerTarget.js',
++    'Target.js',
+ )
+diff --git a/devtools/client/aboutdebugging/initializer.js b/devtools/client/aboutdebugging/initializer.js
+--- a/devtools/client/aboutdebugging/initializer.js
++++ b/devtools/client/aboutdebugging/initializer.js
+@@ -24,17 +24,17 @@ loader.lazyRequireGetter(this, "Telemetr
+ const { require } = BrowserLoader({
+   baseURI: "resource://devtools/client/aboutdebugging/",
+   window
+ });
+ 
+ const { createFactory } = require("devtools/client/shared/vendor/react");
+ const { render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
+ 
+-const AboutDebuggingApp = createFactory(require("./components/aboutdebugging"));
++const AboutDebuggingApp = createFactory(require("./components/Aboutdebugging"));
+ 
+ var AboutDebugging = {
+   init() {
+     if (!Services.prefs.getBoolPref("devtools.enabled", true)) {
+       // If DevTools are disabled, navigate to about:devtools.
+       window.location = "about:devtools?reason=AboutDebugging";
+       return;
+     }

+ 839 - 0
mozilla-release/patches/1411565-59a1.patch

@@ -0,0 +1,839 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1508746540 -7200
+# Node ID 95b95506cb1bf92940cb5e516852d8f98390afdf
+# Parent  fb0e57e5404b83e56806800346122e62e75f35c5
+Bug 1411565 - about:debugging connect to remote runtime using url parameters;r=ochameau
+
+This changeset adds basic remote connection functionality to about:debugging.
+About:debugging can target a remote firefox instance if the host and port
+parameters are passed as URL search params.
+
+The feature is not explicitly exposed at the moment and there is no UI to
+connect an instance, and no UI feedback when connected to a remote instance.
+
+When connected, about:debugging should correctly list tabs, workers and addons
+for the target instance of Firefox. Debugging features work for all supported
+targets.
+
+Known limitations:
+- preferences are read from the local Firefox instance (multiprocess, addon
+  debugging etc...). At the moment the remote instance must be manually
+  correctly configured
+
+MozReview-Commit-ID: DOekSCb96XC
+
+diff --git a/devtools/client/aboutdebugging/components/Aboutdebugging.js b/devtools/client/aboutdebugging/components/Aboutdebugging.js
+--- a/devtools/client/aboutdebugging/components/Aboutdebugging.js
++++ b/devtools/client/aboutdebugging/components/Aboutdebugging.js
+@@ -46,16 +46,17 @@ const panels = [{
+ }];
+ 
+ const defaultPanelId = "addons";
+ 
+ class AboutDebuggingApp extends Component {
+   static get propTypes() {
+     return {
+       client: PropTypes.instanceOf(DebuggerClient).isRequired,
++      connect: PropTypes.object.isRequired,
+       telemetry: PropTypes.instanceOf(Telemetry).isRequired
+     };
+   }
+ 
+   constructor(props) {
+     super(props);
+ 
+     this.state = {
+@@ -84,24 +85,24 @@ class AboutDebuggingApp extends Componen
+     });
+   }
+ 
+   selectPanel(panelId) {
+     window.location.hash = "#" + panelId;
+   }
+ 
+   render() {
+-    let { client } = this.props;
++    let { client, connect } = this.props;
+     let { selectedPanelId } = this.state;
+     let selectPanel = this.selectPanel;
+     let selectedPanel = panels.find(p => p.id == selectedPanelId);
+     let panel;
+ 
+     if (selectedPanel) {
+-      panel = selectedPanel.component({ client, id: selectedPanel.id });
++      panel = selectedPanel.component({ client, connect, id: selectedPanel.id });
+     } else {
+       panel = (
+         dom.div({ className: "error-page" },
+           dom.h1({ className: "header-name" },
+             Strings.GetStringFromName("pageNotFound")
+           ),
+           dom.h4({ className: "error-page-details" },
+             Strings.formatStringFromName("doesNotExist", [selectedPanelId], 1))
+diff --git a/devtools/client/aboutdebugging/components/TargetList.js b/devtools/client/aboutdebugging/components/TargetList.js
+--- a/devtools/client/aboutdebugging/components/TargetList.js
++++ b/devtools/client/aboutdebugging/components/TargetList.js
+@@ -18,33 +18,43 @@ const Strings = Services.strings.createB
+ const LocaleCompare = (a, b) => {
+   return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
+ };
+ 
+ class TargetList extends Component {
+   static get propTypes() {
+     return {
+       client: PropTypes.instanceOf(DebuggerClient).isRequired,
++      connect: PropTypes.object,
+       debugDisabled: PropTypes.bool,
+       error: PropTypes.node,
+       id: PropTypes.string.isRequired,
+       name: PropTypes.string,
+       sort: PropTypes.bool,
+       targetClass: PropTypes.func.isRequired,
+       targets: PropTypes.arrayOf(PropTypes.object).isRequired
+     };
+   }
+ 
+   render() {
+-    let { client, debugDisabled, error, targetClass, targets, sort } = this.props;
++    let {
++      client,
++      connect,
++      debugDisabled,
++      error,
++      targetClass,
++      targets,
++      sort
++    } = this.props;
++
+     if (sort) {
+       targets = targets.sort(LocaleCompare);
+     }
+     targets = targets.map(target => {
+-      return targetClass({ client, target, debugDisabled });
++      return targetClass({ client, connect, target, debugDisabled });
+     });
+ 
+     let content = "";
+     if (error) {
+       content = error;
+     } else if (targets.length > 0) {
+       content = dom.ul({ className: "target-list" }, targets);
+     } else {
+diff --git a/devtools/client/aboutdebugging/components/addons/Panel.js b/devtools/client/aboutdebugging/components/addons/Panel.js
+--- a/devtools/client/aboutdebugging/components/addons/Panel.js
++++ b/devtools/client/aboutdebugging/components/addons/Panel.js
+@@ -27,16 +27,17 @@ const CHROME_ENABLED_PREF = "devtools.ch
+ const REMOTE_ENABLED_PREF = "devtools.debugger.remote-enabled";
+ const WEB_EXT_URL = "https://developer.mozilla.org/Add-ons" +
+                     "/WebExtensions/Getting_started_with_web-ext";
+ 
+ class AddonsPanel extends Component {
+   static get propTypes() {
+     return {
+       client: PropTypes.instanceOf(DebuggerClient).isRequired,
++      connect: PropTypes.object,
+       id: PropTypes.string.isRequired
+     };
+   }
+ 
+   constructor(props) {
+     super(props);
+ 
+     this.state = {
+@@ -85,23 +86,25 @@ class AddonsPanel extends Component {
+     this.setState({ debugDisabled });
+   }
+ 
+   updateAddonsList() {
+     this.props.client.listAddons()
+       .then(({addons}) => {
+         let extensions = addons.filter(addon => addon.debuggable).map(addon => {
+           return {
+-            name: addon.name,
++            addonActor: addon.actor,
++            addonID: addon.id,
++            // Forward the whole addon actor form for potential remote debugging.
++            form: addon,
+             icon: addon.iconURL || ExtensionIcon,
+-            addonID: addon.id,
+-            addonActor: addon.actor,
++            manifestURL: addon.manifestURL,
++            name: addon.name,
+             temporarilyInstalled: addon.temporarilyInstalled,
+             url: addon.url,
+-            manifestURL: addon.manifestURL,
+             warnings: addon.warnings,
+           };
+         });
+ 
+         this.setState({ extensions });
+       }, error => {
+         throw new Error("Client error while listing addons: " + error);
+       });
+@@ -131,17 +134,17 @@ class AddonsPanel extends Component {
+   /**
+    * Mandatory callback as AddonManager listener.
+    */
+   onDisabled() {
+     this.updateAddonsList();
+   }
+ 
+   render() {
+-    let { client, id } = this.props;
++    let { client, connect, id } = this.props;
+     let { debugDisabled, extensions: targets } = this.state;
+     let installedName = Strings.GetStringFromName("extensions");
+     let temporaryName = Strings.GetStringFromName("temporaryExtensions");
+     let targetClass = AddonTarget;
+ 
+     const installedTargets = targets.filter((target) => !target.temporarilyInstalled);
+     const temporaryTargets = targets.filter((target) => target.temporarilyInstalled);
+ 
+@@ -157,16 +160,17 @@ class AddonsPanel extends Component {
+     }),
+     AddonsControls({ debugDisabled }),
+     dom.div({ id: "temporary-addons" },
+       TargetList({
+         id: "temporary-extensions",
+         name: temporaryName,
+         targets: temporaryTargets,
+         client,
++        connect,
+         debugDisabled,
+         targetClass,
+         sort: true
+       }),
+       dom.div({ className: "addons-tip"},
+         dom.span({
+           className: "addons-web-ext-tip",
+         }, Strings.GetStringFromName("webExtTip")),
+@@ -176,16 +180,17 @@ class AddonsPanel extends Component {
+       )
+     ),
+     dom.div({ id: "addons" },
+       TargetList({
+         id: "extensions",
+         name: installedName,
+         targets: installedTargets,
+         client,
++        connect,
+         debugDisabled,
+         targetClass,
+         sort: true
+       })
+     ));
+   }
+ }
+ 
+diff --git a/devtools/client/aboutdebugging/components/addons/Target.js b/devtools/client/aboutdebugging/components/addons/Target.js
+--- a/devtools/client/aboutdebugging/components/addons/Target.js
++++ b/devtools/client/aboutdebugging/components/addons/Target.js
+@@ -4,17 +4,17 @@
+ 
+ /* eslint-env browser */
+ 
+ "use strict";
+ 
+ const { Component } = require("devtools/client/shared/vendor/react");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+-const { debugAddon, isTemporaryID, parseFileUri, uninstallAddon } =
++const { debugLocalAddon, debugRemoteAddon, isTemporaryID, parseFileUri, uninstallAddon } =
+   require("../../modules/addon");
+ const Services = require("Services");
+ 
+ loader.lazyImporter(this, "BrowserToolboxProcess",
+   "resource://devtools/client/framework/ToolboxProcess.jsm");
+ 
+ loader.lazyRequireGetter(this, "DebuggerClient",
+   "devtools/shared/client/debugger-client", true);
+@@ -122,20 +122,22 @@ function warningMessages(warnings = []) 
+       warning);
+   });
+ }
+ 
+ class AddonTarget extends Component {
+   static get propTypes() {
+     return {
+       client: PropTypes.instanceOf(DebuggerClient).isRequired,
++      connect: PropTypes.object,
+       debugDisabled: PropTypes.bool,
+       target: PropTypes.shape({
+         addonActor: PropTypes.string.isRequired,
+         addonID: PropTypes.string.isRequired,
++        form: PropTypes.object.isRequired,
+         icon: PropTypes.string,
+         name: PropTypes.string.isRequired,
+         temporarilyInstalled: PropTypes.bool,
+         url: PropTypes.string,
+         warnings: PropTypes.array,
+       }).isRequired
+     };
+   }
+@@ -143,18 +145,23 @@ class AddonTarget extends Component {
+   constructor(props) {
+     super(props);
+     this.debug = this.debug.bind(this);
+     this.uninstall = this.uninstall.bind(this);
+     this.reload = this.reload.bind(this);
+   }
+ 
+   debug() {
+-    let { target } = this.props;
+-    debugAddon(target.addonID);
++    let { client, connect, target } = this.props;
++
++    if (connect.type === "REMOTE") {
++      debugRemoteAddon(target.form, client);
++    } else if (connect.type === "LOCAL") {
++      debugLocalAddon(target.addonID);
++    }
+   }
+ 
+   uninstall() {
+     let { target } = this.props;
+     uninstallAddon(target.addonID);
+   }
+ 
+   reload() {
+diff --git a/devtools/client/aboutdebugging/components/tabs/Panel.js b/devtools/client/aboutdebugging/components/tabs/Panel.js
+--- a/devtools/client/aboutdebugging/components/tabs/Panel.js
++++ b/devtools/client/aboutdebugging/components/tabs/Panel.js
+@@ -20,16 +20,17 @@ loader.lazyRequireGetter(this, "Debugger
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+ class TabsPanel extends Component {
+   static get propTypes() {
+     return {
+       client: PropTypes.instanceOf(DebuggerClient).isRequired,
++      connect: PropTypes.object,
+       id: PropTypes.string.isRequired
+     };
+   }
+ 
+   constructor(props) {
+     super(props);
+ 
+     this.state = {
+@@ -71,32 +72,33 @@ class TabsPanel extends Component {
+           tab.icon = "chrome://devtools/skin/images/globe.svg";
+         }
+       });
+       this.setState({ tabs });
+     });
+   }
+ 
+   render() {
+-    let { client, id } = this.props;
++    let { client, connect, id } = this.props;
+     let { tabs } = this.state;
+ 
+     return dom.div({
+       id: id + "-panel",
+       className: "panel",
+       role: "tabpanel",
+       "aria-labelledby": id + "-header"
+     },
+     PanelHeader({
+       id: id + "-header",
+       name: Strings.GetStringFromName("tabs")
+     }),
+     dom.div({},
+       TargetList({
+         client,
++        connect,
+         id: "tabs",
+         name: Strings.GetStringFromName("tabs"),
+         sort: false,
+         targetClass: TabTarget,
+         targets: tabs
+       })
+     ));
+   }
+diff --git a/devtools/client/aboutdebugging/components/tabs/Target.js b/devtools/client/aboutdebugging/components/tabs/Target.js
+--- a/devtools/client/aboutdebugging/components/tabs/Target.js
++++ b/devtools/client/aboutdebugging/components/tabs/Target.js
+@@ -13,33 +13,39 @@ const dom = require("devtools/client/sha
+ const Services = require("Services");
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+ class TabTarget extends Component {
+   static get propTypes() {
+     return {
++      connect: PropTypes.object,
+       target: PropTypes.shape({
+         icon: PropTypes.string,
+         outerWindowID: PropTypes.number.isRequired,
+         title: PropTypes.string,
+         url: PropTypes.string.isRequired
+       }).isRequired
+     };
+   }
+ 
+   constructor(props) {
+     super(props);
+     this.debug = this.debug.bind(this);
+   }
+ 
+   debug() {
+-    let { target } = this.props;
+-    window.open("about:devtools-toolbox?type=tab&id=" + target.outerWindowID);
++    let { target, connect } = this.props;
++    let url = "about:devtools-toolbox?type=tab&id=" + target.outerWindowID;
++    if (connect.type == "REMOTE") {
++      let {host, port} = connect.params;
++      url += `&host=${encodeURIComponent(host)}&port=${encodeURIComponent(port)}`;
++    }
++    window.open(url);
+   }
+ 
+   render() {
+     let { target } = this.props;
+ 
+     return dom.div({ className: "target-container" },
+       dom.img({
+         className: "target-icon",
+diff --git a/devtools/client/aboutdebugging/initializer.js b/devtools/client/aboutdebugging/initializer.js
+--- a/devtools/client/aboutdebugging/initializer.js
++++ b/devtools/client/aboutdebugging/initializer.js
+@@ -1,64 +1,55 @@
+ /* 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/. */
+ 
+ /* eslint-env browser */
+-/* globals DebuggerClient, DebuggerServer, Telemetry */
++/* globals Telemetry */
+ 
+ "use strict";
+ 
+ const { loader } = Cu.import(
+   "resource://devtools/shared/Loader.jsm", {});
+ const { BrowserLoader } = Cu.import(
+   "resource://devtools/client/shared/browser-loader.js", {});
+ const { Services } = Cu.import(
+   "resource://gre/modules/Services.jsm", {});
+ 
+-loader.lazyRequireGetter(this, "DebuggerClient",
+-  "devtools/shared/client/debugger-client", true);
+-loader.lazyRequireGetter(this, "DebuggerServer",
+-  "devtools/server/main", true);
+ loader.lazyRequireGetter(this, "Telemetry",
+   "devtools/client/shared/telemetry");
+ 
+ const { require } = BrowserLoader({
+   baseURI: "resource://devtools/client/aboutdebugging/",
+   window
+ });
+ 
+ const { createFactory } = require("devtools/client/shared/vendor/react");
+ const { render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
+ 
+ const AboutDebuggingApp = createFactory(require("./components/Aboutdebugging"));
++const { createClient } = require("./modules/connect");
+ 
+ var AboutDebugging = {
+-  init() {
++  async init() {
+     if (!Services.prefs.getBoolPref("devtools.enabled", true)) {
+       // If DevTools are disabled, navigate to about:devtools.
+       window.location = "about:devtools?reason=AboutDebugging";
+       return;
+     }
+ 
+-    DebuggerServer.init();
+-    DebuggerServer.allowChromeProcess = true;
+-    // We want a full featured server for about:debugging. Especially the
+-    // "browser actors" like addons.
+-    DebuggerServer.registerAllActors();
++    let {connect, client} = await createClient();
+ 
+-    this.client = new DebuggerClient(DebuggerServer.connectPipe());
++    this.client = client;
++    await this.client.connect();
+ 
+-    this.client.connect().then(() => {
+-      let client = this.client;
+-      let telemetry = new Telemetry();
++    let telemetry = new Telemetry();
+ 
+-      render(AboutDebuggingApp({ client, telemetry }),
+-        document.querySelector("#body"));
+-    });
++    render(AboutDebuggingApp({ client, connect, telemetry }),
++      document.querySelector("#body"));
+   },
+ 
+   destroy() {
+     unmountComponentAtNode(document.querySelector("#body"));
+ 
+     if (this.client) {
+       this.client.close();
+       this.client = null;
+diff --git a/devtools/client/aboutdebugging/modules/addon.js b/devtools/client/aboutdebugging/modules/addon.js
+--- a/devtools/client/aboutdebugging/modules/addon.js
++++ b/devtools/client/aboutdebugging/modules/addon.js
+@@ -4,31 +4,79 @@
+ 
+ "use strict";
+ 
+ loader.lazyImporter(this, "BrowserToolboxProcess",
+   "resource://devtools/client/framework/ToolboxProcess.jsm");
+ loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
+ loader.lazyImporter(this, "AddonManagerPrivate", "resource://gre/modules/AddonManager.jsm");
+ 
+-let toolbox = null;
++var {TargetFactory} = require("devtools/client/framework/target");
++var {Toolbox} = require("devtools/client/framework/toolbox");
++
++var {gDevTools} = require("devtools/client/framework/devtools");
+ 
+-exports.debugAddon = function (addonID) {
+-  if (toolbox) {
+-    toolbox.close();
++let browserToolboxProcess = null;
++let remoteAddonToolbox = null;
++function closeToolbox() {
++  if (browserToolboxProcess) {
++    browserToolboxProcess.close();
+   }
+ 
+-  toolbox = BrowserToolboxProcess.init({
++  if (remoteAddonToolbox) {
++    remoteAddonToolbox.destroy();
++  }
++}
++
++/**
++ * Start debugging an addon in the current instance of Firefox.
++ *
++ * @param {String} addonID
++ *        String id of the addon to debug.
++ */
++exports.debugLocalAddon = async function (addonID) {
++  // Close previous addon debugging toolbox.
++  closeToolbox();
++
++  browserToolboxProcess = BrowserToolboxProcess.init({
+     addonID,
+     onClose: () => {
+-      toolbox = null;
++      browserToolboxProcess = null;
+     }
+   });
+ };
+ 
++/**
++ * Start debugging an addon in a remote instance of Firefox.
++ *
++ * @param {Object} addonForm
++ *        Necessary to create an addon debugging target.
++ * @param {DebuggerClient} client
++ *        Required for remote debugging.
++ */
++exports.debugRemoteAddon = async function (addonForm, client) {
++  // Close previous addon debugging toolbox.
++  closeToolbox();
++
++  let options = {
++    form: addonForm,
++    chrome: true,
++    client,
++    isTabActor: addonForm.isWebExtension
++  };
++
++  let target = await TargetFactory.forRemoteTab(options);
++
++  let hostType = Toolbox.HostType.WINDOW;
++  remoteAddonToolbox = await gDevTools.showToolbox(target, null, hostType);
++  remoteAddonToolbox.once("destroy", () => {
++    remoteAddonToolbox = null;
++  });
++};
++
+ exports.uninstallAddon = async function (addonID) {
+   let addon = await AddonManager.getAddonByID(addonID);
+   return addon && addon.uninstall();
+ };
+ 
+ exports.isTemporaryID = function (addonID) {
+   return AddonManagerPrivate.isTemporaryInstallID(addonID);
+ };
+diff --git a/devtools/client/aboutdebugging/modules/connect.js b/devtools/client/aboutdebugging/modules/connect.js
+new file mode 100644
+--- /dev/null
++++ b/devtools/client/aboutdebugging/modules/connect.js
+@@ -0,0 +1,75 @@
++/* 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/. */
++
++/* eslint-env browser */
++
++"use strict";
++
++/**
++ * The connect module creates a connection to a debugger server based on the current
++ * context (e.g. URL parameters).
++ */
++
++const { clientFromURL } = require("devtools/client/framework/target-from-url");
++const { DebuggerServer } = require("devtools/server/main");
++
++// Supported connection types
++const TYPE = {
++  // Default, connected to the current instance of Firefox
++  LOCAL: "LOCAL",
++  // Connected to a remote instance of Firefox via host&port settings.
++  REMOTE: "REMOTE",
++};
++
++/**
++ * Create a plain object containing the connection information relevant to aboutdebugging
++ * components.
++ *
++ * @returns {Object}
++ *          - type: {String} from TYPE ("LOCAL", "REMOTE")
++ *          - params: {Object} additional metadata depending on the type.
++ *            - if type === "LOCAL", empty object
++ *            - if type === "REMOTE", {host: {String}, port: {String}}
++ */
++function createDescriptorFromURL(url) {
++  let params = url.searchParams;
++
++  let host = params.get("host");
++  let port = params.get("port");
++
++  let descriptor;
++  if (host && port) {
++    descriptor = {
++      type: TYPE.REMOTE,
++      params: {host, port}
++    };
++  } else {
++    descriptor = {
++      type: TYPE.LOCAL,
++      params: {}
++    };
++  }
++
++  return descriptor;
++}
++
++/**
++ * Returns a promise that resolves after creating a debugger client corresponding to the
++ * provided options.
++ *
++ * @returns Promise that resolves an object with the following properties:
++ *          - client: a DebuggerClient instance
++ *          - connect: a connection descriptor, see doc for createDescriptorFromURL(url).
++ */
++exports.createClient = async function () {
++  let href = window.location.href;
++  let url = new window.URL(href.replace("about:", "http://"));
++
++  let connect = createDescriptorFromURL(url);
++  let client = await clientFromURL(url);
++
++  DebuggerServer.allowChromeProcess = true;
++
++  return {client, connect};
++};
+diff --git a/devtools/client/aboutdebugging/modules/moz.build b/devtools/client/aboutdebugging/modules/moz.build
+--- a/devtools/client/aboutdebugging/modules/moz.build
++++ b/devtools/client/aboutdebugging/modules/moz.build
+@@ -1,8 +1,9 @@
+ # 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(
+     'addon.js',
++    'connect.js',
+     'worker.js',
+ )
+diff --git a/devtools/client/framework/target-from-url.js b/devtools/client/framework/target-from-url.js
+--- a/devtools/client/framework/target-from-url.js
++++ b/devtools/client/framework/target-from-url.js
+@@ -2,93 +2,85 @@
+  * 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 { TargetFactory } = require("devtools/client/framework/target");
+ const { DebuggerServer } = require("devtools/server/main");
+ const { DebuggerClient } = require("devtools/shared/client/debugger-client");
+-const { Task } = require("devtools/shared/task");
+ 
+ /**
+  * Construct a Target for a given URL object having various query parameters:
+  *
+- * host:
+- *    {String} The hostname or IP address to connect to.
+- * port:
+- *    {Number} The TCP port to connect to, to use with `host` argument.
+- * ws:
+- *    {Boolean} If true, connect via websocket instread of regular TCP connection.
++ * - host, port & ws: See the documentation for clientFromURL
+  *
+- * type: tab, process, window
+- *    {String} The type of target to connect to.
++ * - type: tab, process, window
++ *      {String} The type of target to connect to.
+  *
+  * If type == "tab":
+- * id:
+- *    {Number} the tab outerWindowID
+- * chrome: Optional
+- *    {Boolean} Force the creation of a chrome target. Gives more privileges to
+- *    the tab actor. Allows chrome execution in the webconsole and see chrome
+- *    files in the debugger. (handy when contributing to firefox)
++ * - id:
++ *      {Number} the tab outerWindowID
++ * - chrome: Optional
++ *      {Boolean} Force the creation of a chrome target. Gives more privileges to
++ *      the tab actor. Allows chrome execution in the webconsole and see chrome
++ *      files in the debugger. (handy when contributing to firefox)
+  *
+  * If type == "process":
+- * id:
+- *    {Number} the process id to debug. Default to 0, which is the parent
+- *    process.
++ * - id:
++ *      {Number} the process id to debug. Default to 0, which is the parent process.
+  *
+  * If type == "window":
+- * id:
+- *    {Number} the window outerWindowID
++ * - id:
++ *      {Number} the window outerWindowID
+  *
+  * @param {URL} url
+  *        The url to fetch query params from.
+  *
+  * @return A target object
+  */
+-exports.targetFromURL = Task.async(function* (url) {
++exports.targetFromURL = async function targetFromURL(url) {
++  let client = await clientFromURL(url);
++  await client.connect();
++
+   let params = url.searchParams;
+   let type = params.get("type");
+   if (!type) {
+     throw new Error("targetFromURL, missing type parameter");
+   }
+   let id = params.get("id");
+   // Allows to spawn a chrome enabled target for any context
+   // (handy to debug chrome stuff in a child process)
+   let chrome = params.has("chrome");
+ 
+-  let client = yield createClient(params);
+-
+-  yield client.connect();
+-
+   let form, isTabActor;
+   if (type === "tab") {
+     // Fetch target for a remote tab
+     id = parseInt(id, 10);
+     if (isNaN(id)) {
+       throw new Error(`targetFromURL, wrong tab id '${id}', should be a number`);
+     }
+     try {
+-      let response = yield client.getTab({ outerWindowID: id });
++      let response = await client.getTab({ outerWindowID: id });
+       form = response.tab;
+     } catch (ex) {
+       if (ex.error == "noTab") {
+         throw new Error(`targetFromURL, tab with outerWindowID '${id}' doesn't exist`);
+       }
+       throw ex;
+     }
+   } else if (type == "process") {
+     // Fetch target for a remote chrome actor
+     DebuggerServer.allowChromeProcess = true;
+     try {
+       id = parseInt(id, 10);
+       if (isNaN(id)) {
+         id = 0;
+       }
+-      let response = yield client.getProcess(id);
++      let response = await client.getProcess(id);
+       form = response.form;
+       chrome = true;
+       if (id != 0) {
+         // Child process are not exposing tab actors and only support debugger+console
+         isTabActor = false;
+       }
+     } catch (ex) {
+       if (ex.error == "noProcess") {
+@@ -99,42 +91,59 @@ exports.targetFromURL = Task.async(funct
+   } else if (type == "window") {
+     // Fetch target for a remote window actor
+     DebuggerServer.allowChromeProcess = true;
+     try {
+       id = parseInt(id, 10);
+       if (isNaN(id)) {
+         throw new Error("targetFromURL, window requires id parameter");
+       }
+-      let response = yield client.mainRoot.getWindow({
++      let response = await client.mainRoot.getWindow({
+         outerWindowID: id,
+       });
+       form = response.window;
+       chrome = true;
+     } catch (ex) {
+       if (ex.error == "notFound") {
+         throw new Error(`targetFromURL, window with id '${id}' doesn't exist`);
+       }
+       throw ex;
+     }
+   } else {
+     throw new Error(`targetFromURL, unsupported type '${type}' parameter`);
+   }
+ 
+   return TargetFactory.forRemoteTab({ client, form, chrome, isTabActor });
+-});
++};
+ 
+-function* createClient(params) {
++/**
++ * Create a DebuggerClient for a given URL object having various query parameters:
++ *
++ * host:
++ *    {String} The hostname or IP address to connect to.
++ * port:
++ *    {Number} The TCP port to connect to, to use with `host` argument.
++ * ws:
++ *    {Boolean} If true, connect via websocket instead of regular TCP connection.
++ *
++ * @param {URL} url
++ *        The url to fetch query params from.
++ * @return a promise that resolves a DebuggerClient object
++ */
++async function clientFromURL(url) {
++  let params = url.searchParams;
+   let host = params.get("host");
+   let port = params.get("port");
+   let webSocket = !!params.get("ws");
+ 
+   let transport;
+   if (port) {
+-    transport = yield DebuggerClient.socketConnect({ host, port, webSocket });
++    transport = await DebuggerClient.socketConnect({ host, port, webSocket });
+   } else {
+     // Setup a server if we don't have one already running
+     DebuggerServer.init();
+     DebuggerServer.registerAllActors();
+     transport = DebuggerServer.connectPipe();
+   }
+   return new DebuggerClient(transport);
+ }
++
++exports.clientFromURL = clientFromURL;

+ 1598 - 0
mozilla-release/patches/1411920-58a1.patch

@@ -0,0 +1,1598 @@
+# HG changeset patch
+# User Michael Ratcliffe <mratcliffe@mozilla.com>
+# Date 1509020422 -3600
+# Node ID f2326b69f91ecea44005b9be55ed6ad6f515f253
+# Parent  3f12dd16e3e72eb1de08fcc07e539dfa11c9d0d8
+Bug 1411920 - about:debugging to ES6 classes r=jdescottes
+
+MozReview-Commit-ID: Js4NQoGfgtI
+
+diff --git a/devtools/client/aboutdebugging/components/Aboutdebugging.js b/devtools/client/aboutdebugging/components/Aboutdebugging.js
+--- a/devtools/client/aboutdebugging/components/Aboutdebugging.js
++++ b/devtools/client/aboutdebugging/components/Aboutdebugging.js
+@@ -1,17 +1,17 @@
+ /* 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/. */
+ 
+ /* eslint-env browser */
+ 
+ "use strict";
+ 
+-const { createFactory, createClass, DOM: dom, PropTypes } =
++const { createFactory, Component, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ const Services = require("Services");
+ 
+ const PanelMenu = createFactory(require("./PanelMenu"));
+ 
+ loader.lazyGetter(this, "AddonsPanel",
+   () => createFactory(require("./addons/Panel")));
+ loader.lazyGetter(this, "TabsPanel",
+@@ -41,51 +41,56 @@ const panels = [{
+   id: "workers",
+   name: Strings.GetStringFromName("workers"),
+   icon: "chrome://devtools/skin/images/debugging-workers.svg",
+   component: WorkersPanel
+ }];
+ 
+ const defaultPanelId = "addons";
+ 
+-module.exports = createClass({
+-  displayName: "AboutDebuggingApp",
++class AboutDebuggingApp extends Component {
++  static get propTypes() {
++    return {
++      client: PropTypes.instanceOf(DebuggerClient).isRequired,
++      telemetry: PropTypes.instanceOf(Telemetry).isRequired
++    };
++  }
+ 
+-  propTypes: {
+-    client: PropTypes.instanceOf(DebuggerClient).isRequired,
+-    telemetry: PropTypes.instanceOf(Telemetry).isRequired
+-  },
++  constructor(props) {
++    super(props);
+ 
+-  getInitialState() {
+-    return {
++    this.state = {
+       selectedPanelId: defaultPanelId
+     };
+-  },
++
++    this.onHashChange = this.onHashChange.bind(this);
++    this.selectPanel = this.selectPanel.bind(this);
++  }
+ 
+   componentDidMount() {
+     window.addEventListener("hashchange", this.onHashChange);
+     this.onHashChange();
+     this.props.telemetry.toolOpened("aboutdebugging");
+-  },
++  }
+ 
+   componentWillUnmount() {
+     window.removeEventListener("hashchange", this.onHashChange);
+     this.props.telemetry.toolClosed("aboutdebugging");
+     this.props.telemetry.destroy();
+-  },
++  }
+ 
+   onHashChange() {
+     this.setState({
+       selectedPanelId: window.location.hash.substr(1) || defaultPanelId
+     });
+-  },
++  }
+ 
+   selectPanel(panelId) {
+     window.location.hash = "#" + panelId;
+-  },
++  }
+ 
+   render() {
+     let { client } = this.props;
+     let { selectedPanelId } = this.state;
+     let selectPanel = this.selectPanel;
+     let selectedPanel = panels.find(p => p.id == selectedPanelId);
+     let panel;
+ 
+@@ -103,9 +108,11 @@ module.exports = createClass({
+       );
+     }
+ 
+     return dom.div({ className: "app" },
+       PanelMenu({ panels, selectedPanelId, selectPanel }),
+       dom.div({ className: "main-content" }, panel)
+     );
+   }
+-});
++}
++
++module.exports = AboutDebuggingApp;
+diff --git a/devtools/client/aboutdebugging/components/PanelHeader.js b/devtools/client/aboutdebugging/components/PanelHeader.js
+--- a/devtools/client/aboutdebugging/components/PanelHeader.js
++++ b/devtools/client/aboutdebugging/components/PanelHeader.js
+@@ -1,24 +1,26 @@
+ /* 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 { createClass, DOM: dom, PropTypes } =
++const { Component, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ 
+-module.exports = createClass({
+-  displayName: "PanelHeader",
+-
+-  propTypes: {
+-    id: PropTypes.string.isRequired,
+-    name: PropTypes.string.isRequired
+-  },
++class PanelHeader extends Component {
++  static get propTypes() {
++    return {
++      id: PropTypes.string.isRequired,
++      name: PropTypes.string.isRequired
++    };
++  }
+ 
+   render() {
+     let { name, id } = this.props;
+ 
+     return dom.div({ className: "header" },
+       dom.h1({ id, className: "header-name" }, name));
+-  },
+-});
++  }
++}
++
++module.exports = PanelHeader;
+diff --git a/devtools/client/aboutdebugging/components/PanelMenu.js b/devtools/client/aboutdebugging/components/PanelMenu.js
+--- a/devtools/client/aboutdebugging/components/PanelMenu.js
++++ b/devtools/client/aboutdebugging/components/PanelMenu.js
+@@ -1,41 +1,43 @@
+ /* 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 { createClass, createFactory, DOM: dom, PropTypes } =
++const { Component, createFactory, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ const PanelMenuEntry = createFactory(require("./PanelMenuEntry"));
+ 
+-module.exports = createClass({
+-  displayName: "PanelMenu",
+-
+-  propTypes: {
+-    panels: PropTypes.arrayOf(PropTypes.shape({
+-      id: PropTypes.string.isRequired,
+-      name: PropTypes.string.isRequired,
+-      icon: PropTypes.string.isRequired,
+-      component: PropTypes.func.isRequired
+-    })).isRequired,
+-    selectPanel: PropTypes.func.isRequired,
+-    selectedPanelId: PropTypes.string
+-  },
++class PanelMenu extends Component {
++  static get propTypes() {
++    return {
++      panels: PropTypes.arrayOf(PropTypes.shape({
++        id: PropTypes.string.isRequired,
++        name: PropTypes.string.isRequired,
++        icon: PropTypes.string.isRequired,
++        component: PropTypes.func.isRequired
++      })).isRequired,
++      selectPanel: PropTypes.func.isRequired,
++      selectedPanelId: PropTypes.string
++    };
++  }
+ 
+   render() {
+     let { panels, selectedPanelId, selectPanel } = this.props;
+     let panelLinks = panels.map(({ id, name, icon }) => {
+       let selected = id == selectedPanelId;
+       return PanelMenuEntry({
+         id,
+         name,
+         icon,
+         selected,
+         selectPanel
+       });
+     });
+ 
+     // "categories" id used for styling purposes
+     return dom.div({ id: "categories", role: "tablist" }, panelLinks);
+-  },
+-});
++  }
++}
++
++module.exports = PanelMenu;
+diff --git a/devtools/client/aboutdebugging/components/PanelMenuEntry.js b/devtools/client/aboutdebugging/components/PanelMenuEntry.js
+--- a/devtools/client/aboutdebugging/components/PanelMenuEntry.js
++++ b/devtools/client/aboutdebugging/components/PanelMenuEntry.js
+@@ -1,37 +1,42 @@
+ /* 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 { createClass, DOM: dom, PropTypes } =
++const { Component, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ 
+-module.exports = createClass({
+-  displayName: "PanelMenuEntry",
++class PanelMenuEntry extends Component {
++  static get propTypes() {
++    return {
++      icon: PropTypes.string.isRequired,
++      id: PropTypes.string.isRequired,
++      name: PropTypes.string.isRequired,
++      selected: PropTypes.bool,
++      selectPanel: PropTypes.func.isRequired
++    };
++  }
+ 
+-  propTypes: {
+-    icon: PropTypes.string.isRequired,
+-    id: PropTypes.string.isRequired,
+-    name: PropTypes.string.isRequired,
+-    selected: PropTypes.bool,
+-    selectPanel: PropTypes.func.isRequired
+-  },
++  constructor(props) {
++    super(props);
++    this.onClick = this.onClick.bind(this);
++  }
+ 
+   onClick() {
+     this.props.selectPanel(this.props.id);
+-  },
++  }
+ 
+   onKeyDown(event) {
+     if ([" ", "Enter"].includes(event.key)) {
+       this.props.selectPanel(this.props.id);
+     }
+-  },
++  }
+ 
+   render() {
+     let { id, name, icon, selected } = this.props;
+ 
+     // Here .category, .category-icon, .category-name classnames are used to
+     // apply common styles defined.
+     let className = "category" + (selected ? " selected" : "");
+     return dom.div({
+@@ -40,9 +45,11 @@ module.exports = createClass({
+       className,
+       onClick: this.onClick,
+       onKeyDown: this.onKeyDown,
+       tabIndex: "0",
+       role: "tab" },
+     dom.img({ className: "category-icon", src: icon, role: "presentation" }),
+     dom.div({ className: "category-name" }, name));
+   }
+-});
++}
++
++module.exports = PanelMenuEntry;
+diff --git a/devtools/client/aboutdebugging/components/TargetList.js b/devtools/client/aboutdebugging/components/TargetList.js
+--- a/devtools/client/aboutdebugging/components/TargetList.js
++++ b/devtools/client/aboutdebugging/components/TargetList.js
+@@ -1,41 +1,41 @@
+ /* 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 { createClass, DOM: dom, PropTypes } =
++const { Component, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ const Services = require("Services");
+ 
+ loader.lazyRequireGetter(this, "DebuggerClient",
+   "devtools/shared/client/debugger-client", true);
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+ const LocaleCompare = (a, b) => {
+   return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
+ };
+ 
+-module.exports = createClass({
+-  displayName: "TargetList",
+-
+-  propTypes: {
+-    client: PropTypes.instanceOf(DebuggerClient).isRequired,
+-    debugDisabled: PropTypes.bool,
+-    error: PropTypes.node,
+-    id: PropTypes.string.isRequired,
+-    name: PropTypes.string,
+-    sort: PropTypes.bool,
+-    targetClass: PropTypes.func.isRequired,
+-    targets: PropTypes.arrayOf(PropTypes.object).isRequired
+-  },
++class TargetList extends Component {
++  static get propTypes() {
++    return {
++      client: PropTypes.instanceOf(DebuggerClient).isRequired,
++      debugDisabled: PropTypes.bool,
++      error: PropTypes.node,
++      id: PropTypes.string.isRequired,
++      name: PropTypes.string,
++      sort: PropTypes.bool,
++      targetClass: PropTypes.func.isRequired,
++      targets: PropTypes.arrayOf(PropTypes.object).isRequired
++    };
++  }
+ 
+   render() {
+     let { client, debugDisabled, error, targetClass, targets, sort } = this.props;
+     if (sort) {
+       targets = targets.sort(LocaleCompare);
+     }
+     targets = targets.map(target => {
+       return targetClass({ client, target, debugDisabled });
+@@ -47,10 +47,12 @@ module.exports = createClass({
+     } else if (targets.length > 0) {
+       content = dom.ul({ className: "target-list" }, targets);
+     } else {
+       content = dom.p(null, Strings.GetStringFromName("nothing"));
+     }
+ 
+     return dom.div({ id: this.props.id, className: "targets" },
+       dom.h2(null, this.props.name), content);
+-  },
+-});
++  }
++}
++
++module.exports = TargetList;
+diff --git a/devtools/client/aboutdebugging/components/addons/Controls.js b/devtools/client/aboutdebugging/components/addons/Controls.js
+--- a/devtools/client/aboutdebugging/components/addons/Controls.js
++++ b/devtools/client/aboutdebugging/components/addons/Controls.js
+@@ -6,45 +6,52 @@
+ /* globals AddonManager */
+ 
+ "use strict";
+ 
+ loader.lazyImporter(this, "AddonManager",
+   "resource://gre/modules/AddonManager.jsm");
+ 
+ const { Cc, Ci } = require("chrome");
+-const { createFactory, createClass, DOM: dom, PropTypes } =
++const { createFactory, Component, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ const Services = require("Services");
+ const AddonsInstallError = createFactory(require("./InstallError"));
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+ const MORE_INFO_URL = "https://developer.mozilla.org/docs/Tools" +
+                       "/about:debugging#Enabling_add-on_debugging";
+ 
+-module.exports = createClass({
+-  displayName: "AddonsControls",
++class AddonsControls extends Component {
++  static get propTypes() {
++    return {
++      debugDisabled: PropTypes.bool
++    };
++  }
+ 
+-  propTypes: {
+-    debugDisabled: PropTypes.bool
+-  },
++  constructor(props) {
++    super(props);
+ 
+-  getInitialState() {
+-    return {
++    this.state = {
+       installError: null,
+     };
+-  },
++
++    this.onEnableAddonDebuggingChange = this.onEnableAddonDebuggingChange.bind(this);
++    this.loadAddonFromFile = this.loadAddonFromFile.bind(this);
++    this.retryInstall = this.retryInstall.bind(this);
++    this.installAddon = this.installAddon.bind(this);
++  }
+ 
+   onEnableAddonDebuggingChange(event) {
+     let enabled = event.target.checked;
+     Services.prefs.setBoolPref("devtools.chrome.enabled", enabled);
+     Services.prefs.setBoolPref("devtools.debugger.remote-enabled", enabled);
+-  },
++  }
+ 
+   loadAddonFromFile() {
+     this.setState({ installError: null });
+     let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+     fp.init(window,
+       Strings.GetStringFromName("selectAddonFromFile2"),
+       Ci.nsIFilePicker.modeOpen);
+     fp.open(res => {
+@@ -55,33 +62,33 @@ module.exports = createClass({
+       // AddonManager.installTemporaryAddon accepts either
+       // addon directory or final xpi file.
+       if (!file.isDirectory() && !file.leafName.endsWith(".xpi")) {
+         file = file.parent;
+       }
+ 
+       this.installAddon(file);
+     });
+-  },
++  }
+ 
+   retryInstall() {
+     this.setState({ installError: null });
+     this.installAddon(this.state.lastInstallErrorFile);
+-  },
++  }
+ 
+   installAddon(file) {
+     AddonManager.installTemporaryAddon(file)
+       .then(() => {
+         this.setState({ lastInstallErrorFile: null });
+       })
+       .catch(e => {
+         console.error(e);
+         this.setState({ installError: e.message, lastInstallErrorFile: file });
+       });
+-  },
++  }
+ 
+   render() {
+     let { debugDisabled } = this.props;
+ 
+     return dom.div({ className: "addons-top" },
+       dom.div({ className: "addons-controls" },
+         dom.div({ className: "addons-options toggle-container-with-text" },
+           dom.input({
+@@ -105,9 +112,11 @@ module.exports = createClass({
+           onClick: this.loadAddonFromFile,
+         }, Strings.GetStringFromName("loadTemporaryAddon"))
+       ),
+       AddonsInstallError({
+         error: this.state.installError,
+         retryInstall: this.retryInstall,
+       }));
+   }
+-});
++}
++
++module.exports = AddonsControls;
+diff --git a/devtools/client/aboutdebugging/components/addons/InstallError.js b/devtools/client/aboutdebugging/components/addons/InstallError.js
+--- a/devtools/client/aboutdebugging/components/addons/InstallError.js
++++ b/devtools/client/aboutdebugging/components/addons/InstallError.js
+@@ -1,29 +1,29 @@
+ /* 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/. */
+ 
+ /* eslint-env browser */
+ "use strict";
+ 
+-const { createClass, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react");
++const { Component, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react");
+ 
+ const Services = require("Services");
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+-module.exports = createClass({
+-  displayName: "AddonsInstallError",
+-
+-  propTypes: {
+-    error: PropTypes.string,
+-    retryInstall: PropTypes.func,
+-  },
++class AddonsInstallError extends Component {
++  static get propTypes() {
++    return {
++      error: PropTypes.string,
++      retryInstall: PropTypes.func,
++    };
++  }
+ 
+   render() {
+     if (!this.props.error) {
+       return null;
+     }
+     let text = `There was an error during installation: ${this.props.error}`;
+     return dom.div(
+       { className: "addons-install-error" },
+@@ -31,9 +31,11 @@ module.exports = createClass({
+         {},
+         dom.div({ className: "warning" }),
+         dom.span({}, text),
+       ),
+       dom.button(
+         { className: "addons-install-retry", onClick: this.props.retryInstall },
+         Strings.GetStringFromName("retryTemporaryInstall")));
+   }
+-});
++}
++
++module.exports = AddonsInstallError;
+diff --git a/devtools/client/aboutdebugging/components/addons/Panel.js b/devtools/client/aboutdebugging/components/addons/Panel.js
+--- a/devtools/client/aboutdebugging/components/addons/Panel.js
++++ b/devtools/client/aboutdebugging/components/addons/Panel.js
+@@ -1,17 +1,17 @@
+ /* 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 { AddonManager } = require("resource://gre/modules/AddonManager.jsm");
+ const { Management } = require("resource://gre/modules/Extension.jsm");
+-const { createFactory, createClass, DOM: dom, PropTypes } =
++const { createFactory, Component, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ const Services = require("Services");
+ 
+ const AddonsControls = createFactory(require("./Controls"));
+ const AddonTarget = createFactory(require("./Target"));
+ const PanelHeader = createFactory(require("../PanelHeader"));
+ const TargetList = createFactory(require("../TargetList"));
+ 
+@@ -22,63 +22,72 @@ const Strings = Services.strings.createB
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+ const ExtensionIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
+ const CHROME_ENABLED_PREF = "devtools.chrome.enabled";
+ const REMOTE_ENABLED_PREF = "devtools.debugger.remote-enabled";
+ const WEB_EXT_URL = "https://developer.mozilla.org/Add-ons" +
+                     "/WebExtensions/Getting_started_with_web-ext";
+ 
+-module.exports = createClass({
+-  displayName: "AddonsPanel",
++class AddonsPanel extends Component {
++  static get propTypes() {
++    return {
++      client: PropTypes.instanceOf(DebuggerClient).isRequired,
++      id: PropTypes.string.isRequired
++    };
++  }
+ 
+-  propTypes: {
+-    client: PropTypes.instanceOf(DebuggerClient).isRequired,
+-    id: PropTypes.string.isRequired
+-  },
++  constructor(props) {
++    super(props);
+ 
+-  getInitialState() {
+-    return {
++    this.state = {
+       extensions: [],
+       debugDisabled: false,
+     };
+-  },
++
++    this.updateDebugStatus = this.updateDebugStatus.bind(this);
++    this.updateAddonsList = this.updateAddonsList.bind(this);
++    this.onInstalled = this.onInstalled.bind(this);
++    this.onUninstalled = this.onUninstalled.bind(this);
++    this.onEnabled = this.onEnabled.bind(this);
++    this.onDisabled = this.onDisabled.bind(this);
++  }
+ 
+   componentDidMount() {
+     AddonManager.addAddonListener(this);
+     // Listen to startup since that's when errors and warnings
+     // get populated on the extension.
+     Management.on("startup", this.updateAddonsList);
+ 
+     Services.prefs.addObserver(CHROME_ENABLED_PREF,
+       this.updateDebugStatus);
+     Services.prefs.addObserver(REMOTE_ENABLED_PREF,
+       this.updateDebugStatus);
+ 
+     this.updateDebugStatus();
+     this.updateAddonsList();
+-  },
++  }
+ 
+   componentWillUnmount() {
+     AddonManager.removeAddonListener(this);
+     Management.off("startup", this.updateAddonsList);
+ 
+     Services.prefs.removeObserver(CHROME_ENABLED_PREF,
+       this.updateDebugStatus);
+     Services.prefs.removeObserver(REMOTE_ENABLED_PREF,
+       this.updateDebugStatus);
+-  },
++  }
+ 
+   updateDebugStatus() {
+     let debugDisabled =
+       !Services.prefs.getBoolPref(CHROME_ENABLED_PREF) ||
+       !Services.prefs.getBoolPref(REMOTE_ENABLED_PREF);
+ 
+     this.setState({ debugDisabled });
+-  },
++  }
+ 
+   updateAddonsList() {
+     this.props.client.listAddons()
+       .then(({addons}) => {
+         let extensions = addons.filter(addon => addon.debuggable).map(addon => {
+           return {
+             name: addon.name,
+             icon: addon.iconURL || ExtensionIcon,
+@@ -90,45 +99,45 @@ module.exports = createClass({
+             warnings: addon.warnings,
+           };
+         });
+ 
+         this.setState({ extensions });
+       }, error => {
+         throw new Error("Client error while listing addons: " + error);
+       });
+-  },
++  }
+ 
+   /**
+    * Mandatory callback as AddonManager listener.
+    */
+   onInstalled() {
+     this.updateAddonsList();
+-  },
++  }
+ 
+   /**
+    * Mandatory callback as AddonManager listener.
+    */
+   onUninstalled() {
+     this.updateAddonsList();
+-  },
++  }
+ 
+   /**
+    * Mandatory callback as AddonManager listener.
+    */
+   onEnabled() {
+     this.updateAddonsList();
+-  },
++  }
+ 
+   /**
+    * Mandatory callback as AddonManager listener.
+    */
+   onDisabled() {
+     this.updateAddonsList();
+-  },
++  }
+ 
+   render() {
+     let { client, id } = this.props;
+     let { debugDisabled, extensions: targets } = this.state;
+     let installedName = Strings.GetStringFromName("extensions");
+     let temporaryName = Strings.GetStringFromName("temporaryExtensions");
+     let targetClass = AddonTarget;
+ 
+@@ -172,9 +181,11 @@ module.exports = createClass({
+         targets: installedTargets,
+         client,
+         debugDisabled,
+         targetClass,
+         sort: true
+       })
+     ));
+   }
+-});
++}
++
++module.exports = AddonsPanel;
+diff --git a/devtools/client/aboutdebugging/components/addons/Target.js b/devtools/client/aboutdebugging/components/addons/Target.js
+--- a/devtools/client/aboutdebugging/components/addons/Target.js
++++ b/devtools/client/aboutdebugging/components/addons/Target.js
+@@ -1,17 +1,17 @@
+ /* 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/. */
+ 
+ /* eslint-env browser */
+ 
+ "use strict";
+ 
+-const { createClass, DOM: dom, PropTypes } =
++const { Component, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ const { debugAddon, isTemporaryID, parseFileUri, uninstallAddon } =
+   require("../../modules/addon");
+ const Services = require("Services");
+ 
+ loader.lazyImporter(this, "BrowserToolboxProcess",
+   "resource://devtools/client/framework/ToolboxProcess.jsm");
+ 
+@@ -117,55 +117,62 @@ function infoMessages(target) {
+ function warningMessages(warnings = []) {
+   return warnings.map((warning) => {
+     return dom.li(
+       { className: "addon-target-warning-message addon-target-message" },
+       warning);
+   });
+ }
+ 
+-module.exports = createClass({
+-  displayName: "AddonTarget",
++class AddonTarget extends Component {
++  static get propTypes() {
++    return {
++      client: PropTypes.instanceOf(DebuggerClient).isRequired,
++      debugDisabled: PropTypes.bool,
++      target: PropTypes.shape({
++        addonActor: PropTypes.string.isRequired,
++        addonID: PropTypes.string.isRequired,
++        icon: PropTypes.string,
++        name: PropTypes.string.isRequired,
++        temporarilyInstalled: PropTypes.bool,
++        url: PropTypes.string,
++        warnings: PropTypes.array,
++      }).isRequired
++    };
++  }
+ 
+-  propTypes: {
+-    client: PropTypes.instanceOf(DebuggerClient).isRequired,
+-    debugDisabled: PropTypes.bool,
+-    target: PropTypes.shape({
+-      addonActor: PropTypes.string.isRequired,
+-      addonID: PropTypes.string.isRequired,
+-      icon: PropTypes.string,
+-      name: PropTypes.string.isRequired,
+-      temporarilyInstalled: PropTypes.bool,
+-      url: PropTypes.string,
+-      warnings: PropTypes.array,
+-    }).isRequired
+-  },
++  constructor(props) {
++    super(props);
++    this.debug = this.debug.bind(this);
++    this.uninstall = this.uninstall.bind(this);
++    this.reload = this.reload.bind(this);
++  }
+ 
+   debug() {
+     let { target } = this.props;
+     debugAddon(target.addonID);
+-  },
++  }
+ 
+   uninstall() {
+     let { target } = this.props;
+     uninstallAddon(target.addonID);
+-  },
++  }
+ 
+   reload() {
+     let { client, target } = this.props;
+     // This function sometimes returns a partial promise that only
+     // implements then().
+     client.request({
+       to: target.addonActor,
+       type: "reload"
+     }).then(() => {}, error => {
+       throw new Error(
+         "Error reloading addon " + target.addonID + ": " + error);
+     });
+-  },
++  }
+ 
+   render() {
+     let { target, debugDisabled } = this.props;
+ 
+     return dom.li(
+       { className: "addon-target-container", "data-addon-id": target.addonID },
+       dom.div({ className: "target" },
+         dom.img({
+@@ -200,9 +207,11 @@ module.exports = createClass({
+           ? dom.button({
+             className: "uninstall-button addon-target-button",
+             onClick: this.uninstall,
+           }, Strings.GetStringFromName("remove"))
+           : null,
+       ),
+     );
+   }
+-});
++}
++
++module.exports = AddonTarget;
+diff --git a/devtools/client/aboutdebugging/components/tabs/Panel.js b/devtools/client/aboutdebugging/components/tabs/Panel.js
+--- a/devtools/client/aboutdebugging/components/tabs/Panel.js
++++ b/devtools/client/aboutdebugging/components/tabs/Panel.js
+@@ -1,54 +1,58 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ /* eslint-env browser */
+ 
+ "use strict";
+ 
+-const { createClass, createFactory, DOM: dom, PropTypes } =
++const { Component, createFactory, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ const Services = require("Services");
+ 
+ const PanelHeader = createFactory(require("../PanelHeader"));
+ const TargetList = createFactory(require("../TargetList"));
+ const TabTarget = createFactory(require("./Target"));
+ 
+ loader.lazyRequireGetter(this, "DebuggerClient",
+   "devtools/shared/client/debugger-client", true);
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+-module.exports = createClass({
+-  displayName: "TabsPanel",
++class TabsPanel extends Component {
++  static get propTypes() {
++    return {
++      client: PropTypes.instanceOf(DebuggerClient).isRequired,
++      id: PropTypes.string.isRequired
++    };
++  }
+ 
+-  propTypes: {
+-    client: PropTypes.instanceOf(DebuggerClient).isRequired,
+-    id: PropTypes.string.isRequired
+-  },
++  constructor(props) {
++    super(props);
+ 
+-  getInitialState() {
+-    return {
++    this.state = {
+       tabs: []
+     };
+-  },
++
++    this.update = this.update.bind(this);
++  }
+ 
+   componentDidMount() {
+     let { client } = this.props;
+     client.addListener("tabListChanged", this.update);
+     this.update();
+-  },
++  }
+ 
+   componentWillUnmount() {
+     let { client } = this.props;
+     client.removeListener("tabListChanged", this.update);
+-  },
++  }
+ 
+   update() {
+     this.props.client.mainRoot.listTabs().then(({ tabs }) => {
+       // Filter out closed tabs (represented as `null`).
+       tabs = tabs.filter(tab => !!tab);
+       tabs.forEach(tab => {
+         // FIXME Also try to fetch low-res favicon. But we should use actor
+         // support for this to get the high-res one (bug 1061654).
+@@ -63,17 +67,17 @@ module.exports = createClass({
+           }
+           tab.icon = prePath + "/favicon.ico";
+         } else {
+           tab.icon = "chrome://devtools/skin/images/globe.svg";
+         }
+       });
+       this.setState({ tabs });
+     });
+-  },
++  }
+ 
+   render() {
+     let { client, id } = this.props;
+     let { tabs } = this.state;
+ 
+     return dom.div({
+       id: id + "-panel",
+       className: "panel",
+@@ -90,9 +94,11 @@ module.exports = createClass({
+         id: "tabs",
+         name: Strings.GetStringFromName("tabs"),
+         sort: false,
+         targetClass: TabTarget,
+         targets: tabs
+       })
+     ));
+   }
+-});
++}
++
++module.exports = TabsPanel;
+diff --git a/devtools/client/aboutdebugging/components/tabs/Target.js b/devtools/client/aboutdebugging/components/tabs/Target.js
+--- a/devtools/client/aboutdebugging/components/tabs/Target.js
++++ b/devtools/client/aboutdebugging/components/tabs/Target.js
+@@ -1,39 +1,44 @@
+ /* 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/. */
+ 
+ /* eslint-env browser */
+ 
+ "use strict";
+ 
+-const { createClass, DOM: dom, PropTypes } =
++const { Component, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ const Services = require("Services");
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+-module.exports = createClass({
+-  displayName: "TabTarget",
++class TabTarget extends Component {
++  static get propTypes() {
++    return {
++      target: PropTypes.shape({
++        icon: PropTypes.string,
++        outerWindowID: PropTypes.number.isRequired,
++        title: PropTypes.string,
++        url: PropTypes.string.isRequired
++      }).isRequired
++    };
++  }
+ 
+-  propTypes: {
+-    target: PropTypes.shape({
+-      icon: PropTypes.string,
+-      outerWindowID: PropTypes.number.isRequired,
+-      title: PropTypes.string,
+-      url: PropTypes.string.isRequired
+-    }).isRequired
+-  },
++  constructor(props) {
++    super(props);
++    this.debug = this.debug.bind(this);
++  }
+ 
+   debug() {
+     let { target } = this.props;
+     window.open("about:devtools-toolbox?type=tab&id=" + target.outerWindowID);
+-  },
++  }
+ 
+   render() {
+     let { target } = this.props;
+ 
+     return dom.div({ className: "target-container" },
+       dom.img({
+         className: "target-icon",
+         role: "presentation",
+@@ -45,9 +50,11 @@ module.exports = createClass({
+           target.title || target.url)
+       ),
+       dom.button({
+         className: "debug-button",
+         onClick: this.debug,
+       }, Strings.GetStringFromName("debug"))
+     );
+   }
+-});
++}
++
++module.exports = TabTarget;
+diff --git a/devtools/client/aboutdebugging/components/workers/MultiE10sWarning.js b/devtools/client/aboutdebugging/components/workers/MultiE10sWarning.js
+--- a/devtools/client/aboutdebugging/components/workers/MultiE10sWarning.js
++++ b/devtools/client/aboutdebugging/components/workers/MultiE10sWarning.js
+@@ -3,43 +3,46 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ /* eslint-env browser */
+ 
+ "use strict";
+ 
+ loader.lazyImporter(this, "PrivateBrowsingUtils",
+   "resource://gre/modules/PrivateBrowsingUtils.jsm");
+-const { createClass, DOM: dom } =
++const { Component, DOM: dom } =
+   require("devtools/client/shared/vendor/react");
+ const Services = require("Services");
+ const { Ci } = require("chrome");
+ 
+ loader.lazyImporter(this, "PrivateBrowsingUtils",
+   "resource://gre/modules/PrivateBrowsingUtils.jsm");
+ 
+ loader.lazyRequireGetter(this, "DebuggerClient",
+   "devtools/shared/client/debugger-client", true);
+ 
+ const Strings = Services.strings.createBundle("chrome://devtools/locale/aboutdebugging.properties");
+ const MULTI_OPT_OUT_PREF = "dom.ipc.multiOptOut";
+ 
+-module.exports = createClass({
+-  displayName: "multiE10SWarning",
++class multiE10SWarning extends Component {
++  constructor(props) {
++    super(props);
++    this.onUpdatePreferenceClick = this.onUpdatePreferenceClick.bind(this);
++  }
+ 
+   onUpdatePreferenceClick() {
+     let message = Strings.GetStringFromName("multiProcessWarningConfirmUpdate2");
+     if (window.confirm(message)) {
+       // Disable multi until at least the next experiment.
+       Services.prefs.setIntPref(MULTI_OPT_OUT_PREF,
+                                 Services.appinfo.E10S_MULTI_EXPERIMENT);
+       // Restart the browser.
+       Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
+     }
+-  },
++  }
+ 
+   render() {
+     return dom.div(
+       {
+         className: "service-worker-multi-process"
+       },
+       dom.div(
+         {},
+@@ -53,10 +56,12 @@ module.exports = createClass({
+       dom.button(
+         {
+           className: "update-button",
+           onClick: this.onUpdatePreferenceClick,
+         },
+         Strings.GetStringFromName("multiProcessWarningUpdateLink2")
+       )
+     );
+-  },
+-});
++  }
++}
++
++module.exports = multiE10SWarning;
+diff --git a/devtools/client/aboutdebugging/components/workers/Panel.js b/devtools/client/aboutdebugging/components/workers/Panel.js
+--- a/devtools/client/aboutdebugging/components/workers/Panel.js
++++ b/devtools/client/aboutdebugging/components/workers/Panel.js
+@@ -3,17 +3,17 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ /* globals window */
+ "use strict";
+ 
+ loader.lazyImporter(this, "PrivateBrowsingUtils",
+   "resource://gre/modules/PrivateBrowsingUtils.jsm");
+ 
+ const { Ci } = require("chrome");
+-const { createClass, createFactory, DOM: dom, PropTypes } =
++const { Component, createFactory, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ const { getWorkerForms } = require("../../modules/worker");
+ const Services = require("Services");
+ 
+ const PanelHeader = createFactory(require("../PanelHeader"));
+ const TargetList = createFactory(require("../TargetList"));
+ const WorkerTarget = createFactory(require("./Target"));
+ const MultiE10SWarning = createFactory(require("./MultiE10sWarning"));
+@@ -29,34 +29,35 @@ const Strings = Services.strings.createB
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+ const WorkerIcon = "chrome://devtools/skin/images/debugging-workers.svg";
+ const MORE_INFO_URL = "https://developer.mozilla.org/en-US/docs/Tools/about%3Adebugging" +
+                       "#Service_workers_not_compatible";
+ const PROCESS_COUNT_PREF = "dom.ipc.processCount";
+ const MULTI_OPTOUT_PREF = "dom.ipc.multiOptOut";
+ 
+-module.exports = createClass({
+-  displayName: "WorkersPanel",
+-
+-  propTypes: {
+-    client: PropTypes.instanceOf(DebuggerClient).isRequired,
+-    id: PropTypes.string.isRequired
+-  },
++class WorkersPanel extends Component {
++  static get propTypes() {
++    return {
++      client: PropTypes.instanceOf(DebuggerClient).isRequired,
++      id: PropTypes.string.isRequired
++    };
++  }
+ 
+-  getInitialState() {
+-    return {
+-      workers: {
+-        service: [],
+-        shared: [],
+-        other: []
+-      },
+-      processCount: 1,
+-    };
+-  },
++  constructor(props) {
++    super(props);
++
++    this.updateMultiE10S = this.updateMultiE10S.bind(this);
++    this.updateWorkers = this.updateWorkers.bind(this);
++    this.getRegistrationForWorker = this.getRegistrationForWorker.bind(this);
++    this.isE10S = this.isE10S.bind(this);
++    this.renderServiceWorkersError = this.renderServiceWorkersError.bind(this);
++
++    this.state = this.initialState;
++  }
+ 
+   componentDidMount() {
+     let client = this.props.client;
+     client.addListener("workerListChanged", this.updateWorkers);
+     client.addListener("serviceWorkerRegistrationListChanged", this.updateWorkers);
+     client.addListener("processListChanged", this.updateWorkers);
+     client.addListener("registration-changed", this.updateWorkers);
+ 
+@@ -72,38 +73,49 @@ module.exports = createClass({
+     // - In all cases, we don't have to manually check which pref changed to
+     //   what. The platform code in nsIXULRuntime.maxWebProcessCount does all
+     //   of that for us.
+     Services.prefs.addObserver(PROCESS_COUNT_PREF, this.updateMultiE10S);
+     Services.prefs.addObserver(MULTI_OPTOUT_PREF, this.updateMultiE10S);
+ 
+     this.updateMultiE10S();
+     this.updateWorkers();
+-  },
++  }
+ 
+   componentWillUnmount() {
+     let client = this.props.client;
+     client.removeListener("processListChanged", this.updateWorkers);
+     client.removeListener("serviceWorkerRegistrationListChanged", this.updateWorkers);
+     client.removeListener("workerListChanged", this.updateWorkers);
+     client.removeListener("registration-changed", this.updateWorkers);
+ 
+     Services.prefs.removeObserver(PROCESS_COUNT_PREF, this.updateMultiE10S);
+     Services.prefs.removeObserver(MULTI_OPTOUT_PREF, this.updateMultiE10S);
+-  },
++  }
++
++  get initialState() {
++    return {
++      workers: {
++        service: [],
++        shared: [],
++        other: []
++      },
++      processCount: 1,
++    };
++  }
+ 
+   updateMultiE10S() {
+     // We watch the pref but set the state based on
+     // nsIXULRuntime.maxWebProcessCount.
+     let processCount = Services.appinfo.maxWebProcessCount;
+     this.setState({ processCount });
+-  },
++  }
+ 
+   updateWorkers() {
+-    let workers = this.getInitialState().workers;
++    let workers = this.initialState.workers;
+ 
+     getWorkerForms(this.props.client).then(forms => {
+       forms.registrations.forEach(form => {
+         workers.service.push({
+           icon: WorkerIcon,
+           name: form.url,
+           url: form.url,
+           scope: form.scope,
+@@ -151,30 +163,30 @@ module.exports = createClass({
+       });
+ 
+       // XXX: Filter out the service worker registrations for which we couldn't
+       // find the scriptSpec.
+       workers.service = workers.service.filter(reg => !!reg.url);
+ 
+       this.setState({ workers });
+     });
+-  },
++  }
+ 
+   getRegistrationForWorker(form, registrations) {
+     for (let registration of registrations) {
+       if (registration.scope === form.scope) {
+         return registration;
+       }
+     }
+     return null;
+-  },
++  }
+ 
+   isE10S() {
+     return Services.appinfo.browserTabsRemoteAutostart;
+-  },
++  }
+ 
+   renderServiceWorkersError() {
+     let isWindowPrivate = PrivateBrowsingUtils.isContentWindowPrivate(window);
+     let isPrivateBrowsingMode = PrivateBrowsingUtils.permanentPrivateBrowsing;
+     let isServiceWorkerDisabled = !Services.prefs
+                                     .getBoolPref("dom.serviceWorkers.enabled");
+ 
+     let isDisabled = isWindowPrivate || isPrivateBrowsingMode || isServiceWorkerDisabled;
+@@ -195,17 +207,17 @@ module.exports = createClass({
+       dom.a(
+         {
+           href: MORE_INFO_URL,
+           target: "_blank"
+         },
+         Strings.GetStringFromName("configurationIsNotCompatible.learnMore")
+       ),
+     );
+-  },
++  }
+ 
+   render() {
+     let { client, id } = this.props;
+     let { workers, processCount } = this.state;
+ 
+     let isE10S = Services.appinfo.browserTabsRemoteAutostart;
+     let isMultiE10S = isE10S && processCount > 1;
+ 
+@@ -250,9 +262,11 @@ module.exports = createClass({
+           name: Strings.GetStringFromName("otherWorkers"),
+           sort: true,
+           targetClass: WorkerTarget,
+           targets: workers.other
+         })
+       )
+     );
+   }
+-});
++}
++
++module.exports = WorkersPanel;
+diff --git a/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js b/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js
+--- a/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js
++++ b/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js
+@@ -1,161 +1,175 @@
+ /* 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/. */
+ 
+ /* eslint-env browser */
+ 
+ "use strict";
+ 
+-const { createClass, DOM: dom, PropTypes } =
++const { Component, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ const { debugWorker } = require("../../modules/worker");
+ const Services = require("Services");
+ 
+ loader.lazyRequireGetter(this, "DebuggerClient",
+   "devtools/shared/client/debugger-client", true);
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+-module.exports = createClass({
+-  displayName: "ServiceWorkerTarget",
++class ServiceWorkerTarget extends Component {
++  static get propTypes() {
++    return {
++      client: PropTypes.instanceOf(DebuggerClient).isRequired,
++      debugDisabled: PropTypes.bool,
++      target: PropTypes.shape({
++        active: PropTypes.bool,
++        fetch: PropTypes.bool.isRequired,
++        icon: PropTypes.string,
++        name: PropTypes.string.isRequired,
++        url: PropTypes.string,
++        scope: PropTypes.string.isRequired,
++        // registrationActor can be missing in e10s.
++        registrationActor: PropTypes.string,
++        workerActor: PropTypes.string
++      }).isRequired
++    };
++  }
+ 
+-  propTypes: {
+-    client: PropTypes.instanceOf(DebuggerClient).isRequired,
+-    debugDisabled: PropTypes.bool,
+-    target: PropTypes.shape({
+-      active: PropTypes.bool,
+-      fetch: PropTypes.bool.isRequired,
+-      icon: PropTypes.string,
+-      name: PropTypes.string.isRequired,
+-      url: PropTypes.string,
+-      scope: PropTypes.string.isRequired,
+-      // registrationActor can be missing in e10s.
+-      registrationActor: PropTypes.string,
+-      workerActor: PropTypes.string
+-    }).isRequired
+-  },
++  constructor(props) {
++    super(props);
+ 
+-  getInitialState() {
+-    return {
++    this.state = {
+       pushSubscription: null
+     };
+-  },
++
++    this.debug = this.debug.bind(this);
++    this.push = this.push.bind(this);
++    this.start = this.start.bind(this);
++    this.unregister = this.unregister.bind(this);
++    this.onPushSubscriptionModified = this.onPushSubscriptionModified.bind(this);
++    this.updatePushSubscription = this.updatePushSubscription.bind(this);
++    this.isRunning = this.isRunning.bind(this);
++    this.isActive = this.isActive.bind(this);
++    this.getServiceWorkerStatus = this.getServiceWorkerStatus.bind(this);
++    this.renderButtons = this.renderButtons.bind(this);
++    this.renderUnregisterLink = this.renderUnregisterLink.bind(this);
++  }
+ 
+   componentDidMount() {
+     let { client } = this.props;
+     client.addListener("push-subscription-modified", this.onPushSubscriptionModified);
+     this.updatePushSubscription();
+-  },
++  }
+ 
+   componentDidUpdate(oldProps, oldState) {
+     let wasActive = oldProps.target.active;
+     if (!wasActive && this.isActive()) {
+       // While the service worker isn't active, any calls to `updatePushSubscription`
+       // won't succeed. If we just became active, make sure we didn't miss a push
+       // subscription change by updating it now.
+       this.updatePushSubscription();
+     }
+-  },
++  }
+ 
+   componentWillUnmount() {
+     let { client } = this.props;
+     client.removeListener("push-subscription-modified", this.onPushSubscriptionModified);
+-  },
++  }
+ 
+   debug() {
+     if (!this.isRunning()) {
+       // If the worker is not running, we can't debug it.
+       return;
+     }
+ 
+     let { client, target } = this.props;
+     debugWorker(client, target.workerActor);
+-  },
++  }
+ 
+   push() {
+     if (!this.isActive() || !this.isRunning()) {
+       // If the worker is not running, we can't push to it.
+       // If the worker is not active, the registration might be unavailable and the
+       // push will not succeed.
+       return;
+     }
+ 
+     let { client, target } = this.props;
+     client.request({
+       to: target.workerActor,
+       type: "push"
+     });
+-  },
++  }
+ 
+   start() {
+     if (!this.isActive() || this.isRunning()) {
+       // If the worker is not active or if it is already running, we can't start it.
+       return;
+     }
+ 
+     let { client, target } = this.props;
+     client.request({
+       to: target.registrationActor,
+       type: "start"
+     });
+-  },
++  }
+ 
+   unregister() {
+     let { client, target } = this.props;
+     client.request({
+       to: target.registrationActor,
+       type: "unregister"
+     });
+-  },
++  }
+ 
+   onPushSubscriptionModified(type, data) {
+     let { target } = this.props;
+     if (data.from === target.registrationActor) {
+       this.updatePushSubscription();
+     }
+-  },
++  }
+ 
+   updatePushSubscription() {
+     if (!this.props.target.registrationActor) {
+       // A valid registrationActor is needed to retrieve the push subscription.
+       return;
+     }
+ 
+     let { client, target } = this.props;
+     client.request({
+       to: target.registrationActor,
+       type: "getPushSubscription"
+     }, ({ subscription }) => {
+       this.setState({ pushSubscription: subscription });
+     });
+-  },
++  }
+ 
+   isRunning() {
+     // We know the target is running if it has a worker actor.
+     return !!this.props.target.workerActor;
+-  },
++  }
+ 
+   isActive() {
+     return this.props.target.active;
+-  },
++  }
+ 
+   getServiceWorkerStatus() {
+     if (this.isActive() && this.isRunning()) {
+       return "running";
+     } else if (this.isActive()) {
+       return "stopped";
+     }
+     // We cannot get service worker registrations unless the registration is in
+     // ACTIVE state. Unable to know the actual state ("installing", "waiting"), we
+     // display a custom state "registering" for now. See Bug 1153292.
+     return "registering";
+-  },
++  }
+ 
+   renderButtons() {
+     let pushButton = dom.button({
+       className: "push-button",
+       onClick: this.push,
+       disabled: this.props.debugDisabled
+     }, Strings.GetStringFromName("push"));
+ 
+@@ -174,29 +188,29 @@ module.exports = createClass({
+     if (this.isRunning()) {
+       if (this.isActive()) {
+         return [pushButton, debugButton];
+       }
+       // Only debug button is available if the service worker is not active.
+       return debugButton;
+     }
+     return startButton;
+-  },
++  }
+ 
+   renderUnregisterLink() {
+     if (!this.isActive()) {
+       // If not active, there might be no registrationActor available.
+       return null;
+     }
+ 
+     return dom.a({
+       onClick: this.unregister,
+       className: "unregister-link",
+     }, Strings.GetStringFromName("unregister"));
+-  },
++  }
+ 
+   render() {
+     let { target } = this.props;
+     let { pushSubscription } = this.state;
+     let status = this.getServiceWorkerStatus();
+ 
+     let fetch = target.fetch ? Strings.GetStringFromName("listeningForFetchEvents") :
+       Strings.GetStringFromName("notListeningForFetchEvents");
+@@ -235,9 +249,11 @@ module.exports = createClass({
+             }, target.scope),
+             this.renderUnregisterLink()
+           )
+         )
+       ),
+       this.renderButtons()
+     );
+   }
+-});
++}
++
++module.exports = ServiceWorkerTarget;
+diff --git a/devtools/client/aboutdebugging/components/workers/Target.js b/devtools/client/aboutdebugging/components/workers/Target.js
+--- a/devtools/client/aboutdebugging/components/workers/Target.js
++++ b/devtools/client/aboutdebugging/components/workers/Target.js
+@@ -1,44 +1,49 @@
+ /* 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/. */
+ 
+ /* eslint-env browser */
+ 
+ "use strict";
+ 
+-const { createClass, DOM: dom, PropTypes } =
++const { Component, DOM: dom, PropTypes } =
+   require("devtools/client/shared/vendor/react");
+ const { debugWorker } = require("../../modules/worker");
+ const Services = require("Services");
+ 
+ loader.lazyRequireGetter(this, "DebuggerClient",
+   "devtools/shared/client/debugger-client", true);
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+-module.exports = createClass({
+-  displayName: "WorkerTarget",
++class WorkerTarget extends Component {
++  static get propTypes() {
++    return {
++      client: PropTypes.instanceOf(DebuggerClient).isRequired,
++      debugDisabled: PropTypes.bool,
++      target: PropTypes.shape({
++        icon: PropTypes.string,
++        name: PropTypes.string.isRequired,
++        workerActor: PropTypes.string
++      }).isRequired
++    };
++  }
+ 
+-  propTypes: {
+-    client: PropTypes.instanceOf(DebuggerClient).isRequired,
+-    debugDisabled: PropTypes.bool,
+-    target: PropTypes.shape({
+-      icon: PropTypes.string,
+-      name: PropTypes.string.isRequired,
+-      workerActor: PropTypes.string
+-    }).isRequired
+-  },
++  constructor(props) {
++    super(props);
++    this.debug = this.debug.bind(this);
++  }
+ 
+   debug() {
+     let { client, target } = this.props;
+     debugWorker(client, target.workerActor);
+-  },
++  }
+ 
+   render() {
+     let { target, debugDisabled } = this.props;
+ 
+     return dom.li({ className: "target-container" },
+       dom.img({
+         className: "target-icon",
+         role: "presentation",
+@@ -49,9 +54,11 @@ module.exports = createClass({
+       ),
+       dom.button({
+         className: "debug-button",
+         onClick: this.debug,
+         disabled: debugDisabled
+       }, Strings.GetStringFromName("debug"))
+     );
+   }
+-});
++}
++
++module.exports = WorkerTarget;

+ 65 - 0
mozilla-release/patches/1416105-58a1.patch

@@ -0,0 +1,65 @@
+# HG changeset patch
+# User Luca Greco <lgreco@mozilla.com>
+# Date 1510500016 -3600
+# Node ID bb29d72aa071d8e736c3b5c7767aa4962a6e6157
+# Parent  a9334a0be006dd681ea41c104725d48ec285f6e7
+Bug 1416105 - devtools/server/child.js should call DebuggerServer.addBrowserActorsadd when initializes a DebuggerServer running in the main process. r=ochameau
+
+When web-ext (the nodejs CLI tool which is often used to develop a WebExtension)
+starts Firefox with the --start-debugger-server CLI parameter,
+devtools/shims/devtools-startup.js creates a DebuggerServer instance in an
+isolated separate module loader, marked invisible to the debugger so that
+we can use it to debug the regular DebuggerServer instance (loaded internally
+when the user is going to open one of the developer tools for the first time).
+
+Right after this "debugger invisible" DebuggerServer instance is started,
+web-ext connects to the debugger server and interacts with the RDP actors
+(e.g. to list the addons actors, install the extensions temporarily, reload
+the extensions etc.), and as a side-effect of connecting to the WebExtensions
+RDP actor, the "devtools/server/child.js" module is going to be loaded,
+and it will be loaded in the main process if the webextensions are not
+configured to run in a separate extension child process
+(on the contrary it would be loaded in a child process when the
+the extensions are running in oop mode).
+
+If the regular developer tools have been already opened by the user (e.g.
+when --jsconsole has been passed to the Firefox command line, which
+is what happens when web-ext is executed with the --bc command line option),
+the DebuggerServer is initialized and the browser actors added to it,
+on the contrary when the "devtools/server/child.js" module initializes
+the DebuggerServer module for the first time, it doesn't add the browser actors
+to it and so the DebuggerServer instance is going initialized but it will be
+missing the createRootActor attribute (which is expected if the DebuggerServer
+instance is initialized in a child process, but it is unexpected when the DebuggerServer
+instance is the one that serves the main process).
+
+MozReview-Commit-ID: GE51X14HfHJ
+
+diff --git a/devtools/client/webconsole/hudservice.js b/devtools/client/webconsole/hudservice.js
+--- a/devtools/client/webconsole/hudservice.js
++++ b/devtools/client/webconsole/hudservice.js
+@@ -185,18 +185,23 @@ HUD_SERVICE.prototype =
+     this._browserConsoleDefer = defer();
+ 
+     function connect()
+     {
+       let deferred = defer();
+ 
+       if (!DebuggerServer.initialized) {
+         DebuggerServer.init();
+-        DebuggerServer.addBrowserActors();
+       }
++
++      // Ensure that the root actor and the tab actors have been registered on the DebuggerServer,
++      // so that the Browser Console can retrieve the console actors.
++      // (See Bug 1416105 for rationale).
++      DebuggerServer.registerActors({ root: true, browser: false, tab: true });
++
+       DebuggerServer.allowChromeProcess = true;
+ 
+       let client = new DebuggerClient(DebuggerServer.connectPipe());
+       return client.connect()
+         .then(() => client.getProcess())
+         .then(aResponse => {
+           // Use a TabActor in order to ensure calling `attach` to the ChromeActor
+           return { form: aResponse.form, client, chrome: true, isTabActor: true };

+ 83 - 0
mozilla-release/patches/1416711-1-59a1.patch

@@ -0,0 +1,83 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1510587683 -3600
+# Node ID b9b7d5086d370c5f98fb60284cda7f274712ec30
+# Parent  396ac9b6434d1647c8cf4a8bc40a4da25c242692
+Bug 1416711 - Remove unused restrictPrivileges argument in addBrowserActors;r=ochameau
+
+restrictPrivileges was used for FirefoxOS and can be removed now.
+
+MozReview-Commit-ID: J5SYQ0OPTSk
+
+diff --git a/devtools/server/main.js b/devtools/server/main.js
+--- a/devtools/server/main.js
++++ b/devtools/server/main.js
+@@ -398,50 +398,50 @@ var DebuggerServer = {
+     delete gRegisteredModules[id];
+   },
+ 
+   /**
+    * Install Firefox-specific actors.
+    *
+    * /!\ Be careful when adding a new actor, especially global actors.
+    * Any new global actor will be exposed and returned by the root actor.
+-   *
+-   * That's the reason why tab actors aren't loaded on demand via
+-   * restrictPrivileges=true, to prevent exposing them on b2g parent process's
+-   * root actor.
+    */
+-  addBrowserActors(windowType = null, restrictPrivileges = false) {
++  addBrowserActors(windowType = null) {
+     if (windowType) {
+       this.chromeWindowType = windowType;
+     }
++
+     this.registerModule("devtools/server/actors/webbrowser");
+ 
+-    if (!restrictPrivileges) {
+-      this.addTabActors();
+-      this.registerModule("devtools/server/actors/preference", {
+-        prefix: "preference",
+-        constructor: "PreferenceActor",
+-        type: { global: true }
+-      });
+-      this.registerModule("devtools/server/actors/actor-registry", {
+-        prefix: "actorRegistry",
+-        constructor: "ActorRegistryActor",
+-        type: { global: true }
+-      });
+-    }
++    this.addTabActors();
++
++    this.registerModule("devtools/server/actors/preference", {
++      prefix: "preference",
++      constructor: "PreferenceActor",
++      type: { global: true }
++    });
++
++    this.registerModule("devtools/server/actors/actor-registry", {
++      prefix: "actorRegistry",
++      constructor: "ActorRegistryActor",
++      type: { global: true }
++    });
++
+     this.registerModule("devtools/server/actors/addons", {
+       prefix: "addons",
+       constructor: "AddonsActor",
+       type: { global: true }
+     });
++
+     this.registerModule("devtools/server/actors/device", {
+       prefix: "device",
+       constructor: "DeviceActor",
+       type: { global: true }
+     });
++
+     this.registerModule("devtools/server/actors/heap-snapshot-file", {
+       prefix: "heapSnapshotFile",
+       constructor: "HeapSnapshotFileActor",
+       type: { global: true }
+     });
+   },
+ 
+   /**

+ 1718 - 0
mozilla-release/patches/1416711-2-59a1.patch

@@ -0,0 +1,1718 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1510601395 -3600
+# Node ID e2993f4377faf706eb2cf158721c484a4607422b
+# Parent  a0198d0819abcaab80f2f2aaa94d7c74ac82a247
+Bug 1416711 - Migrate addBrowser/TabActors to registerActors;r=ochameau
+
+DebuggerServer has old APIs addBrowserActors & addTabActors that can be
+replaced by calls to registerActors.
+
+MozReview-Commit-ID: KpYJpbSHM8I
+
+diff --git a/devtools/client/canvasdebugger/test/head.js b/devtools/client/canvasdebugger/test/head.js
+--- a/devtools/client/canvasdebugger/test/head.js
++++ b/devtools/client/canvasdebugger/test/head.js
+@@ -186,17 +186,17 @@ function navigate(aTarget, aUrl, aWaitFo
+ function reload(aTarget, aWaitForTargetEvent = "navigate") {
+   executeSoon(() => aTarget.activeTab.reload());
+   return once(aTarget, aWaitForTargetEvent);
+ }
+ 
+ function initServer() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ }
+ 
+ function initCallWatcherBackend(aUrl) {
+   info("Initializing a call watcher front.");
+   initServer();
+ 
+   return Task.spawn(function* () {
+diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js
+--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js
++++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js
+@@ -15,17 +15,17 @@ var { DevToolsLoader } = ChromeUtils.imp
+ var customLoader = new DevToolsLoader();
+ customLoader.invisibleToDebugger = true;
+ var { DebuggerServer } = customLoader.require("devtools/server/main");
+ var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+ 
+ function initDebuggerClient() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+   DebuggerServer.allowChromeProcess = true;
+ 
+   let transport = DebuggerServer.connectPipe();
+   return new DebuggerClient(transport);
+ }
+ 
+ function attachThread(client, actor) {
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attach.js b/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attach.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attach.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attach.js
+@@ -6,17 +6,17 @@ var WORKER1_URL = "code_WorkerActor.atta
+ var WORKER2_URL = "code_WorkerActor.attach-worker2.js";
+ 
+ function test() {
+   Task.spawn(function* () {
+     let oldMaxTotalViewers = SpecialPowers.getIntPref(MAX_TOTAL_VIEWERS);
+     SpecialPowers.setIntPref(MAX_TOTAL_VIEWERS, 10);
+ 
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+     yield connect(client);
+ 
+     let tab = yield addTab(TAB1_URL);
+     let { tabs } = yield listTabs(client);
+     let [, tabClient] = yield attachTab(client, findTab(tabs, TAB1_URL));
+     yield listWorkers(tabClient);
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attachThread.js b/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attachThread.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attachThread.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attachThread.js
+@@ -1,15 +1,15 @@
+ var TAB_URL = EXAMPLE_URL + "doc_WorkerActor.attachThread-tab.html";
+ var WORKER_URL = "code_WorkerActor.attachThread-worker.js";
+ 
+ function test() {
+   Task.spawn(function* () {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+     let client1 = new DebuggerClient(DebuggerServer.connectPipe());
+     yield connect(client1);
+     let client2 = new DebuggerClient(DebuggerServer.connectPipe());
+     yield connect(client2);
+ 
+     let tab = yield addTab(TAB_URL);
+     let { tabs: tabs1 } = yield listTabs(client1);
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-01.js b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-01.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-01.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-01.js
+@@ -9,17 +9,17 @@
+ 
+ const TAB_URL = EXAMPLE_URL + "doc_event-listeners-01.html";
+ 
+ var gClient, gThreadClient, gInput, gButton;
+ 
+ function test() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-02.js b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-02.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-02.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-02.js
+@@ -10,17 +10,17 @@
+ 
+ const TAB_URL = EXAMPLE_URL + "doc_event-listeners-03.html";
+ 
+ var gClient, gThreadClient;
+ 
+ function test() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js b/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js
+@@ -17,17 +17,17 @@ var gNewChromeSource = promise.defer();
+ var { DevToolsLoader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ var customLoader = new DevToolsLoader();
+ customLoader.invisibleToDebugger = true;
+ var { DebuggerServer } = customLoader.require("devtools/server/main");
+ 
+ function test() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+   DebuggerServer.allowChromeProcess = true;
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_debugger-statement.js b/devtools/client/debugger/test/mochitest/browser_dbg_debugger-statement.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_debugger-statement.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_debugger-statement.js
+@@ -10,17 +10,17 @@
+ const TAB_URL = EXAMPLE_URL + "doc_inline-debugger-statement.html";
+ 
+ var gClient;
+ var gTab;
+ 
+ function test() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js
+@@ -10,17 +10,17 @@
+ const TAB_URL = EXAMPLE_URL + "doc_event-listeners-01.html";
+ 
+ var gClient;
+ var gTab;
+ 
+ function test() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js
+@@ -11,17 +11,17 @@
+ const TAB_URL = EXAMPLE_URL + "doc_event-listeners-03.html";
+ 
+ var gClient;
+ var gTab;
+ 
+ function test() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-03.js b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-03.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-03.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-03.js
+@@ -11,17 +11,17 @@
+ const TAB_URL = EXAMPLE_URL + "doc_native-event-handler.html";
+ 
+ var gClient;
+ var gTab;
+ 
+ function test() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js b/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
+@@ -9,17 +9,17 @@
+ 
+ const ACTORS_URL = CHROME_URL + "testactors.js";
+ 
+ function test() {
+   let gClient;
+ 
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   DebuggerServer.addActors(ACTORS_URL);
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js b/devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
+@@ -11,17 +11,17 @@ const ADDON1_PATH = "addon1.xpi";
+ const ADDON2_ID = "jid1-qjtzNGV8xw5h2A@jetpack";
+ const ADDON2_PATH = "addon2.xpi";
+ 
+ var gAddon1, gAddon1Actor, gAddon2, gAddon2Actor, gClient;
+ 
+ function test() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
+@@ -10,17 +10,17 @@
+ const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
+ const TAB2_URL = EXAMPLE_URL + "doc_empty-tab-02.html";
+ 
+ var gTab1, gTab1Actor, gTab2, gTab2Actor, gClient;
+ 
+ function test() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
+@@ -22,17 +22,17 @@ var gNewWindow;
+ var onListChangedCount = 0;
+ function onListChangedHandler() {
+   onListChangedCount++;
+ }
+ 
+ function test() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   gTabList = new BrowserTabList("fake DebuggerServerConnection");
+   gTabList._testing = true;
+   gTabList.onListChanged = onListChangedHandler;
+ 
+   checkSingleTab()
+     .then(addTabA)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
+@@ -9,17 +9,17 @@
+ 
+ const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
+ 
+ var gTab1, gTab1Actor, gTab2, gTab2Actor, gClient;
+ 
+ function test() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(Task.async(function* ([aType, aTraits]) {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+     let tab = yield addTab(TAB1_URL);
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_listworkers.js b/devtools/client/debugger/test/mochitest/browser_dbg_listworkers.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_listworkers.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_listworkers.js
+@@ -1,16 +1,16 @@
+ var TAB_URL = EXAMPLE_URL + "doc_listworkers-tab.html";
+ var WORKER1_URL = "code_listworkers-worker1.js";
+ var WORKER2_URL = "code_listworkers-worker2.js";
+ 
+ function test() {
+   Task.spawn(function* () {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+     yield connect(client);
+ 
+     let tab = yield addTab(TAB_URL);
+     let { tabs } = yield listTabs(client);
+     let [, tabClient] = yield attachTab(client, findTab(tabs, TAB_URL));
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js b/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
+@@ -12,17 +12,17 @@ const TAB1_URL = EXAMPLE_URL + "doc_scri
+ const TAB2_URL = EXAMPLE_URL + "doc_script-switching-02.html";
+ 
+ var gNewTab, gNewWindow;
+ var gClient;
+ 
+ function test() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_navigation.js b/devtools/client/debugger/test/mochitest/browser_dbg_navigation.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_navigation.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_navigation.js
+@@ -10,17 +10,17 @@
+ const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
+ const TAB2_URL = EXAMPLE_URL + "doc_empty-tab-02.html";
+ 
+ var gClient;
+ 
+ function test() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_promises-allocation-stack.js b/devtools/client/debugger/test/mochitest/browser_dbg_promises-allocation-stack.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-allocation-stack.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-allocation-stack.js
+@@ -11,17 +11,17 @@
+ 
+ const TAB_URL = EXAMPLE_URL + "doc_promise-get-allocation-stack.html";
+ const { PromisesFront } = require("devtools/shared/fronts/promises");
+ var EventEmitter = require("devtools/shared/event-emitter");
+ 
+ function test() {
+   Task.spawn(function* () {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+     let options = {
+       source: TAB_URL,
+       line: 1
+     };
+     const [ tab,, panel ] = yield initDebugger(TAB_URL, options);
+ 
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js b/devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js
+@@ -19,17 +19,17 @@ const STACK_DATA = [
+   { functionDisplayName: "testGetAllocationStack" },
+ ];
+ 
+ function test() {
+   Task.spawn(function* () {
+     requestLongerTimeout(10);
+ 
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+     DebuggerServer.allowChromeProcess = true;
+ 
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+     yield connect(client);
+     let chrome = yield client.getProcess();
+     let [, tabClient] = yield attachTab(client, chrome.form);
+     yield tabClient.attachThread();
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_promises-fulfillment-stack.js b/devtools/client/debugger/test/mochitest/browser_dbg_promises-fulfillment-stack.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-fulfillment-stack.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-fulfillment-stack.js
+@@ -29,17 +29,17 @@ const TEST_DATA = [
+     line: 14,
+     column: 15
+   },
+ ];
+ 
+ function test() {
+   Task.spawn(function* () {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+     let options = {
+       source: TAB_URL,
+       line: 1
+     };
+     const [ tab,, panel ] = yield initDebugger(TAB_URL, options);
+ 
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js b/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js
+@@ -36,17 +36,17 @@ const TEST_DATA = [
+     line: 14,
+     column: 15
+   },
+ ];
+ 
+ function test() {
+   Task.spawn(function* () {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+     let options = {
+       source: TAB_URL,
+       line: 1
+     };
+     const [ tab,, panel ] = yield initDebugger(TAB_URL, options);
+ 
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-01.js b/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-01.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-01.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-01.js
+@@ -10,17 +10,17 @@
+ const ACTORS_URL = CHROME_URL + "testactors.js";
+ const TAB_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
+ 
+ var gClient;
+ 
+ function test() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   DebuggerServer.addActors(ACTORS_URL);
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-02.js b/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-02.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-02.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-02.js
+@@ -10,17 +10,17 @@
+ const ACTORS_URL = CHROME_URL + "testactors.js";
+ const TAB_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
+ 
+ var gClient;
+ 
+ function test() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   DebuggerServer.addActors(ACTORS_URL);
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js b/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js
+@@ -10,17 +10,17 @@ PromiseTestUtils.expectUncaughtRejection
+ 
+ var TAB_URL = EXAMPLE_URL + "doc_WorkerActor.attachThread-tab.html";
+ var WORKER_URL = "code_WorkerActor.attachThread-worker.js";
+ 
+ add_task(function* () {
+   yield pushPrefs(["devtools.scratchpad.enabled", true]);
+ 
+   DebuggerServer.init();
+-  DebuggerServer.addBrowserActors();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   yield connect(client);
+ 
+   let tab = yield addTab(TAB_URL);
+   let { tabs } = yield listTabs(client);
+   let [, tabClient] = yield attachTab(client, findTab(tabs, TAB_URL));
+ 
+diff --git a/devtools/client/debugger/test/mochitest/head.js b/devtools/client/debugger/test/mochitest/head.js
+--- a/devtools/client/debugger/test/mochitest/head.js
++++ b/devtools/client/debugger/test/mochitest/head.js
+@@ -624,17 +624,17 @@ function AddonDebugger() {
+ }
+ 
+ AddonDebugger.prototype = {
+   init: Task.async(function* (aAddonId) {
+     info("Initializing an addon debugger panel.");
+ 
+     if (!DebuggerServer.initialized) {
+       DebuggerServer.init();
+-      DebuggerServer.addBrowserActors();
++      DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+     }
+     DebuggerServer.allowChromeProcess = true;
+ 
+     this.frame = document.createElement("iframe");
+     this.frame.setAttribute("height", 400);
+     document.documentElement.appendChild(this.frame);
+     window.addEventListener("message", this._onMessage);
+ 
+@@ -1321,17 +1321,17 @@ function waitForDispatch(panel, type, ev
+       info(type + " dispatched " + count + " time(s)");
+     }
+   });
+ }
+ 
+ function* initWorkerDebugger(TAB_URL, WORKER_URL) {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   yield connect(client);
+ 
+   let tab = yield addTab(TAB_URL);
+   let { tabs } = yield listTabs(client);
+   let [, tabClient] = yield attachTab(client, findTab(tabs, TAB_URL));
+diff --git a/devtools/client/framework/devtools-browser.js b/devtools/client/framework/devtools-browser.js
+--- a/devtools/client/framework/devtools-browser.js
++++ b/devtools/client/framework/devtools-browser.js
+@@ -291,17 +291,17 @@ var gDevToolsBrowser = exports.gDevTools
+   openConnectScreen(gBrowser) {
+     gBrowser.selectedTab = gBrowser.addTab("chrome://devtools/content/framework/connect/connect.xhtml");
+   },
+ 
+   _getContentProcessTarget(processId) {
+     // Create a DebuggerServer in order to connect locally to it
+     if (!DebuggerServer.initialized) {
+       DebuggerServer.init();
+-      DebuggerServer.addBrowserActors();
++      DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+     }
+     DebuggerServer.allowChromeProcess = true;
+ 
+     let transport = DebuggerServer.connectPipe();
+     let client = new DebuggerClient(transport);
+ 
+     let deferred = defer();
+     client.connect().then(() => {
+diff --git a/devtools/client/framework/target-from-url.js b/devtools/client/framework/target-from-url.js
+--- a/devtools/client/framework/target-from-url.js
++++ b/devtools/client/framework/target-from-url.js
+@@ -129,14 +129,14 @@ function* createClient(params) {
+ 
+   let transport;
+   if (port) {
+     transport = yield DebuggerClient.socketConnect({ host, port, webSocket });
+   } else {
+     // Setup a server if we don't have one already running
+     if (!DebuggerServer.initialized) {
+       DebuggerServer.init();
+-      DebuggerServer.addBrowserActors();
++      DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+     }
+     transport = DebuggerServer.connectPipe();
+   }
+   return new DebuggerClient(transport);
+ }
+diff --git a/devtools/client/framework/test/browser_target_from_url.js b/devtools/client/framework/test/browser_target_from_url.js
+--- a/devtools/client/framework/test/browser_target_from_url.js
++++ b/devtools/client/framework/test/browser_target_from_url.js
+@@ -76,17 +76,17 @@ add_task(function* () {
+ });
+ 
+ function* setupDebuggerServer(websocket) {
+   info("Create a separate loader instance for the DebuggerServer.");
+   let loader = new DevToolsLoader();
+   let { DebuggerServer } = loader.require("devtools/server/main");
+ 
+   DebuggerServer.init();
+-  DebuggerServer.addBrowserActors();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   DebuggerServer.allowChromeProcess = true;
+ 
+   let listener = DebuggerServer.createListener();
+   ok(listener, "Socket listener created");
+   // Pass -1 to automatically choose an available port
+   listener.portOrPath = -1;
+   listener.webSocket = websocket;
+   yield listener.open();
+diff --git a/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js b/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
+--- a/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
++++ b/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
+@@ -60,17 +60,17 @@ function runTools(target) {
+   });
+ }
+ 
+ function getClient() {
+   let deferred = defer();
+ 
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let transport = DebuggerServer.connectPipe();
+   let client = new DebuggerClient(transport);
+ 
+   return client.connect().then(() => client);
+ }
+ 
+diff --git a/devtools/client/framework/test/browser_two_tabs.js b/devtools/client/framework/test/browser_two_tabs.js
+--- a/devtools/client/framework/test/browser_two_tabs.js
++++ b/devtools/client/framework/test/browser_two_tabs.js
+@@ -17,17 +17,17 @@ var gClient;
+ var gTab1, gTab2;
+ var gTabActor1, gTabActor2;
+ 
+ function test() {
+   waitForExplicitFinish();
+ 
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   openTabs();
+ }
+ 
+ function openTabs() {
+   // Open two tabs, select the second
+   addTab(TAB_URL_1).then(tab1 => {
+diff --git a/devtools/client/framework/test/head.js b/devtools/client/framework/test/head.js
+--- a/devtools/client/framework/test/head.js
++++ b/devtools/client/framework/test/head.js
+@@ -25,17 +25,17 @@ function toggleAllTools(state) {
+ 
+ function getChromeActors(callback)
+ {
+   let { DebuggerServer } = require("devtools/server/main");
+   let { DebuggerClient } = require("devtools/shared/client/debugger-client");
+ 
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+   DebuggerServer.allowChromeProcess = true;
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect()
+     .then(() => client.getProcess())
+     .then(response => {
+       callback(client, response.form);
+diff --git a/devtools/client/framework/toolbox-init.js b/devtools/client/framework/toolbox-init.js
+--- a/devtools/client/framework/toolbox-init.js
++++ b/devtools/client/framework/toolbox-init.js
+@@ -65,17 +65,17 @@ if (url.search.length > 1) {
+       iframe = XPCNativeWrapper(iframe);
+ 
+       // Fake a xul:tab object as we don't have one.
+       // linkedBrowser is the only one attribute being queried by client.getTab
+       let tab = { linkedBrowser: iframe };
+ 
+       if (!DebuggerServer.initialized) {
+         DebuggerServer.init();
+-        DebuggerServer.addBrowserActors();
++        DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+       }
+       let client = new DebuggerClient(DebuggerServer.connectPipe());
+ 
+       yield client.connect();
+       // Creates a target for a given browser iframe.
+       let response = yield client.getTab({ tab });
+       let form = response.tab;
+       target = yield TargetFactory.forRemoteTab({client, form, chrome: false});
+diff --git a/devtools/client/responsive.html/manager.js b/devtools/client/responsive.html/manager.js
+--- a/devtools/client/responsive.html/manager.js
++++ b/devtools/client/responsive.html/manager.js
+@@ -417,17 +417,17 @@ ResponsiveUI.prototype = {
+     this.destroyed = true;
+ 
+     return true;
+   }),
+ 
+   connectToServer: Task.async(function* () {
+     if (!DebuggerServer.initialized) {
+       DebuggerServer.init();
+-      DebuggerServer.addBrowserActors();
++      DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+     }
+     this.client = new DebuggerClient(DebuggerServer.connectPipe());
+     yield this.client.connect();
+     let { tab } = yield this.client.getTab();
+     this.emulationFront = EmulationFront(this.client, tab);
+   }),
+ 
+   handleEvent(event) {
+diff --git a/devtools/client/scratchpad/scratchpad.js b/devtools/client/scratchpad/scratchpad.js
+--- a/devtools/client/scratchpad/scratchpad.js
++++ b/devtools/client/scratchpad/scratchpad.js
+@@ -2179,17 +2179,17 @@ ScratchpadWindow.prototype = Heritage.ex
+    *
+    * @return Promise
+    *         The promise for the target for this window.
+    */
+   _attach: function SW__attach()
+   {
+     if (!DebuggerServer.initialized) {
+       DebuggerServer.init();
+-      DebuggerServer.addBrowserActors();
++      DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+     }
+     DebuggerServer.allowChromeProcess = true;
+ 
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+     return client.connect()
+       .then(() => client.getProcess())
+       .then(aResponse => {
+         return { form: aResponse.form, client: client };
+diff --git a/devtools/client/shadereditor/test/head.js b/devtools/client/shadereditor/test/head.js
+--- a/devtools/client/shadereditor/test/head.js
++++ b/devtools/client/shadereditor/test/head.js
+@@ -221,17 +221,17 @@ function reload(aTarget, aWaitForTargetE
+   return once(aTarget, aWaitForTargetEvent);
+ }
+ 
+ function initBackend(aUrl) {
+   info("Initializing a shader editor front.");
+ 
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   return Task.spawn(function* () {
+     let tab = yield addTab(aUrl);
+     let target = TargetFactory.forTab(tab);
+ 
+     yield target.makeRemote();
+ 
+diff --git a/devtools/client/shared/test/test-actor-registry.js b/devtools/client/shared/test/test-actor-registry.js
+--- a/devtools/client/shared/test/test-actor-registry.js
++++ b/devtools/client/shared/test/test-actor-registry.js
+@@ -62,17 +62,17 @@
+   exports.getTestActorWithoutToolbox = Task.async(function* (tab) {
+     let { DebuggerServer } = require("devtools/server/main");
+     let { DebuggerClient } = require("devtools/shared/client/debugger-client");
+ 
+     // We need to spawn a client instance,
+     // but for that we have to first ensure a server is running
+     if (!DebuggerServer.initialized) {
+       DebuggerServer.init();
+-      DebuggerServer.addBrowserActors();
++      DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+     }
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+ 
+     yield client.connect();
+ 
+     // We also need to make sure the test actor is registered on the server.
+     yield exports.registerTestActor(client);
+ 
+diff --git a/devtools/client/webaudioeditor/test/head.js b/devtools/client/webaudioeditor/test/head.js
+--- a/devtools/client/webaudioeditor/test/head.js
++++ b/devtools/client/webaudioeditor/test/head.js
+@@ -156,17 +156,17 @@ function loadFrameScripts() {
+  * Adds a new tab, and instantiate a WebAudiFront object.
+  * This requires calling removeTab before the test ends.
+  */
+ function initBackend(aUrl) {
+   info("Initializing a web audio editor front.");
+ 
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   return Task.spawn(function* () {
+     let tab = yield addTab(aUrl);
+     let target = TargetFactory.forTab(tab);
+ 
+     yield target.makeRemote();
+ 
+diff --git a/devtools/docs/backend/actor-registration.md b/devtools/docs/backend/actor-registration.md
+--- a/devtools/docs/backend/actor-registration.md
++++ b/devtools/docs/backend/actor-registration.md
+@@ -25,17 +25,17 @@ To register a global actor:
+ ```
+ DebuggerServer.registerModule("devtools/server/actors/addons", {
+   prefix: "addons",
+   constructor: "AddonsActor",
+   type: { global: true }
+ });
+ ```
+ 
+-If you are adding a new built-in devtools actor, you should be registering it using `DebuggerServer.registerModule` in `addBrowserActors` or `addTabActors` in `/devtools/server/main.js`.
++If you are adding a new built-in devtools actor, you should be registering it using `DebuggerServer.registerModule` in `_addBrowserActors` or `addTabActors` in `/devtools/server/main.js`.
+ 
+ If you are adding a new actor from an add-on, you should call `DebuggerServer.registerModule` directly from your add-on code.
+ 
+ ## A note about lazy registration
+ 
+ The `DebuggerServer` loads and creates all of the actors lazily to keep the initial memory usage down (which is extremely important on lower end devices).
+ 
+ It becomes especially important when debugging pages with e10s when there are more than one process, because that's when we need to spawn a `DebuggerServer` per process (it may not be immediately obvious that the server in the main process is mostly only here for piping messages to the actors in the child process).
+diff --git a/devtools/docs/backend/client-api.md b/devtools/docs/backend/client-api.md
+--- a/devtools/docs/backend/client-api.md
++++ b/devtools/docs/backend/client-api.md
+@@ -9,17 +9,17 @@ In order to communicate, a client and a 
+ ```javascript
+ Components.utils.import("resource://gre/modules/devtools/dbg-server.jsm");
+ Components.utils.import("resource://gre/modules/devtools/dbg-client.jsm");
+ 
+ function start() {
+   // Start the server.
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   // Listen to an nsIPipe
+   let transport = DebuggerServer.connectPipe();
+ 
+   // Start the client.
+   client = new DebuggerClient(transport);
+ 
+@@ -38,17 +38,17 @@ If a TCP socket is required, the functio
+ ```javascript
+ Components.utils.import("resource://gre/modules/devtools/dbg-server.jsm");
+ Components.utils.import("resource://gre/modules/devtools/dbg-client.jsm");
+ 
+ function startServer() {
+   // Start the server.
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   // For an nsIServerSocket we do this:
+   DebuggerServer.openListener(2929); // A connection on port 2929.
+ }
+ 
+ async function startClient() {
+   let transport = await DebuggerClient.socketConnect({ host: "localhost", port: 2929 });
+@@ -163,17 +163,17 @@ Components.utils.import("resource://gre/
+ 
+ let client;
+ let threadClient;
+ 
+ function startDebugger() {
+   // Start the server.
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+   // Listen to an nsIPipe
+   let transport = DebuggerServer.connectPipe();
+   // For an nsIServerSocket we do this:
+   // DebuggerServer.openListener(port);
+   // ...and this at the client:
+   // let transport = debuggerSocketConnect(host, port);
+ 
+diff --git a/devtools/docs/backend/protocol.js.md b/devtools/docs/backend/protocol.js.md
+--- a/devtools/docs/backend/protocol.js.md
++++ b/devtools/docs/backend/protocol.js.md
+@@ -46,17 +46,17 @@ The actor implementation would go somewh
+       sayHello: function () {
+        return "hello";
+       },
+     });
+ 
+     // You also need to export the actor class in your module for discovery.
+     exports.HelloActor = HelloActor;
+ 
+-To activate your actor, register it in the `addBrowserActors` method in `server/main.js`.
++To activate your actor, register it in the `_addBrowserActors` method in `server/main.js`.
+ The registration code would look something like this:
+ 
+     this.registerModule("devtools/server/actors/hello-world", {
+       prefix: "hello",
+       constructor: "HelloActor",
+       type: { global: true }
+     });
+ 
+diff --git a/devtools/server/main.js b/devtools/server/main.js
+--- a/devtools/server/main.js
++++ b/devtools/server/main.js
+@@ -253,25 +253,25 @@ var DebuggerServer = {
+    */
+   registerActors({ root = true, browser = true, tab = true,
+                    windowType = null }) {
+     if (windowType) {
+       this.chromeWindowType = windowType;
+     }
+ 
+     if (browser) {
+-      this.addBrowserActors(this.chromeWindowType);
++      this._addBrowserActors();
+     }
+ 
+-    if (root) {
++    if (browser || root) {
+       this.registerModule("devtools/server/actors/webbrowser");
+     }
+ 
+-    if (tab) {
+-      this.addTabActors();
++    if (browser || tab) {
++      this._addTabActors();
+     }
+   },
+ 
+   /**
+    * Load a subscript into the debugging global.
+    *
+    * @param url string A url that will be loaded as a subscript into the
+    *        debugging global.  The user must load at least one script
+@@ -399,60 +399,48 @@ var DebuggerServer = {
+   },
+ 
+   /**
+    * Install Firefox-specific actors.
+    *
+    * /!\ Be careful when adding a new actor, especially global actors.
+    * Any new global actor will be exposed and returned by the root actor.
+    */
+-  addBrowserActors(windowType = null) {
+-    if (windowType) {
+-      this.chromeWindowType = windowType;
+-    }
+-
+-    this.registerModule("devtools/server/actors/webbrowser");
+-
+-    this.addTabActors();
+-
++  _addBrowserActors() {
+     this.registerModule("devtools/server/actors/preference", {
+       prefix: "preference",
+       constructor: "PreferenceActor",
+       type: { global: true }
+     });
+-
+     this.registerModule("devtools/server/actors/actor-registry", {
+       prefix: "actorRegistry",
+       constructor: "ActorRegistryActor",
+       type: { global: true }
+     });
+-
+     this.registerModule("devtools/server/actors/addons", {
+       prefix: "addons",
+       constructor: "AddonsActor",
+       type: { global: true }
+     });
+-
+     this.registerModule("devtools/server/actors/device", {
+       prefix: "device",
+       constructor: "DeviceActor",
+       type: { global: true }
+     });
+-
+     this.registerModule("devtools/server/actors/heap-snapshot-file", {
+       prefix: "heapSnapshotFile",
+       constructor: "HeapSnapshotFileActor",
+       type: { global: true }
+     });
+   },
+ 
+   /**
+    * Install tab actors.
+    */
+-  addTabActors() {
++  _addTabActors() {
+     this.registerModule("devtools/server/actors/webconsole", {
+       prefix: "console",
+       constructor: "WebConsoleActor",
+       type: { tab: true }
+     });
+     this.registerModule("devtools/server/actors/inspector", {
+       prefix: "inspector",
+       constructor: "InspectorActor",
+diff --git a/devtools/server/tests/browser/browser_register_actor.js b/devtools/server/tests/browser/browser_register_actor.js
+--- a/devtools/server/tests/browser/browser_register_actor.js
++++ b/devtools/server/tests/browser/browser_register_actor.js
+@@ -4,17 +4,17 @@ var gClient;
+ 
+ function test() {
+   waitForExplicitFinish();
+   let {ActorRegistryFront} = require("devtools/shared/fronts/actor-registry");
+   let actorURL = "chrome://mochitests/content/chrome/devtools/server/tests/mochitest/hello-actor.js";
+ 
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   gClient = new DebuggerClient(DebuggerServer.connectPipe());
+   gClient.connect()
+     .then(() => gClient.listTabs())
+     .then(response => {
+       let options = {
+         prefix: "helloActor",
+diff --git a/devtools/server/tests/browser/head.js b/devtools/server/tests/browser/head.js
+--- a/devtools/server/tests/browser/head.js
++++ b/devtools/server/tests/browser/head.js
+@@ -96,17 +96,17 @@ function initDebuggerServer() {
+   try {
+     // Sometimes debugger server does not get destroyed correctly by previous
+     // tests.
+     DebuggerServer.destroy();
+   } catch (e) {
+     info(`DebuggerServer destroy error: ${e}\n${e.stack}`);
+   }
+   DebuggerServer.init();
+-  DebuggerServer.addBrowserActors();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ }
+ 
+ /**
+  * Connect a debugger client.
+  * @param {DebuggerClient}
+  * @return {Promise} Resolves to the selected tabActor form when the client is
+  * connected.
+  */
+diff --git a/devtools/server/tests/mochitest/inspector-helpers.js b/devtools/server/tests/mochitest/inspector-helpers.js
+--- a/devtools/server/tests/mochitest/inspector-helpers.js
++++ b/devtools/server/tests/mochitest/inspector-helpers.js
+@@ -18,17 +18,17 @@ const {_documentWalker} = require("devto
+ // Always log packets when running tests.
+ Services.prefs.setBoolPref("devtools.debugger.log", true);
+ SimpleTest.registerCleanupFunction(function () {
+   Services.prefs.clearUserPref("devtools.debugger.log");
+ });
+ 
+ if (!DebuggerServer.initialized) {
+   DebuggerServer.init();
+-  DebuggerServer.addBrowserActors();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   SimpleTest.registerCleanupFunction(function () {
+     DebuggerServer.destroy();
+   });
+ }
+ 
+ var gAttachCleanups = [];
+ 
+ SimpleTest.registerCleanupFunction(function () {
+diff --git a/devtools/server/tests/mochitest/memory-helpers.js b/devtools/server/tests/mochitest/memory-helpers.js
+--- a/devtools/server/tests/mochitest/memory-helpers.js
++++ b/devtools/server/tests/mochitest/memory-helpers.js
+@@ -16,17 +16,17 @@ var gReduceTimePrecision = Services.pref
+ Services.prefs.setBoolPref("privacy.reduceTimerPrecision", false);
+ SimpleTest.registerCleanupFunction(function () {
+   Services.prefs.clearUserPref("devtools.debugger.log");
+   Services.prefs.setBoolPref("privacy.reduceTimerPrecision", gReduceTimePrecision);
+ });
+ 
+ function startServerAndGetSelectedTabMemory() {
+   DebuggerServer.init();
+-  DebuggerServer.addBrowserActors();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+ 
+   return client.connect()
+     .then(() => client.listTabs())
+     .then(response => {
+       let form = response.tabs[response.selected];
+       let memory = MemoryFront(client, form, response);
+ 
+diff --git a/devtools/server/tests/mochitest/test_connectToChild.html b/devtools/server/tests/mochitest/test_connectToChild.html
+--- a/devtools/server/tests/mochitest/test_connectToChild.html
++++ b/devtools/server/tests/mochitest/test_connectToChild.html
+@@ -69,17 +69,17 @@ function runTests() {
+     DebuggerServer.addTabActor(TestActor, "testActor");
+   }, false);
+ 
+   // Instantiate a minimal server
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+   }
+   if (!DebuggerServer.createRootActor) {
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   function firstClient() {
+     // Fake a first connection to an iframe
+     let transport = DebuggerServer.connectPipe();
+     let conn = transport._serverConnection;
+     let client = new DebuggerClient(transport);
+     DebuggerServer.connectToChild(conn, iframe).then(actor => {
+diff --git a/devtools/server/tests/mochitest/test_connection-manager.html b/devtools/server/tests/mochitest/test_connection-manager.html
+--- a/devtools/server/tests/mochitest/test_connection-manager.html
++++ b/devtools/server/tests/mochitest/test_connection-manager.html
+@@ -18,17 +18,17 @@ window.onload = function () {
+   SimpleTest.waitForExplicitFinish();
+ 
+   const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+   const {DebuggerServer} = require("devtools/server/main");
+   const Services = require("Services");
+ 
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   const {
+     ConnectionManager,
+     Connection
+   } = require("devtools/shared/client/connection-manager");
+ 
+   let orgCount = ConnectionManager.connections.length;
+diff --git a/devtools/server/tests/mochitest/test_device.html b/devtools/server/tests/mochitest/test_device.html
+--- a/devtools/server/tests/mochitest/test_device.html
++++ b/devtools/server/tests/mochitest/test_device.html
+@@ -21,17 +21,17 @@ window.onload = function () {
+   const Services = require("Services");
+ 
+   SimpleTest.waitForExplicitFinish();
+ 
+   const {getDeviceFront} = require("devtools/shared/fronts/device");
+ 
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+     client.listTabs(function onListTabs(response) {
+       let d = getDeviceFront(client, response);
+ 
+       let desc;
+diff --git a/devtools/server/tests/mochitest/test_framerate_01.html b/devtools/server/tests/mochitest/test_framerate_01.html
+--- a/devtools/server/tests/mochitest/test_framerate_01.html
++++ b/devtools/server/tests/mochitest/test_framerate_01.html
+@@ -62,17 +62,17 @@ window.onload = function () {
+       prevTime = currTime;
+     }
+ 
+     return timeline;
+   }
+ 
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+     client.listTabs(function onListTabs(response) {
+       let form = response.tabs[response.selected];
+       let front = FramerateFront(client, form);
+ 
+diff --git a/devtools/server/tests/mochitest/test_framerate_02.html b/devtools/server/tests/mochitest/test_framerate_02.html
+--- a/devtools/server/tests/mochitest/test_framerate_02.html
++++ b/devtools/server/tests/mochitest/test_framerate_02.html
+@@ -62,17 +62,17 @@ window.onload = function () {
+       prevTime = currTime;
+     }
+ 
+     return timeline;
+   }
+ 
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+     client.listTabs(function onListTabs(response) {
+       let form = response.tabs[response.selected];
+       let front = FramerateFront(client, form);
+ 
+diff --git a/devtools/server/tests/mochitest/test_framerate_03.html b/devtools/server/tests/mochitest/test_framerate_03.html
+--- a/devtools/server/tests/mochitest/test_framerate_03.html
++++ b/devtools/server/tests/mochitest/test_framerate_03.html
+@@ -30,17 +30,17 @@ window.onload = function () {
+ 
+   const {FramerateFront} = require("devtools/shared/fronts/framerate");
+   const START_TICK = 2000;
+   const STOP_TICK = 3000;
+   const TOTAL_TIME = 5000;
+ 
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+     client.listTabs(function onListTabs(response) {
+       let form = response.tabs[response.selected];
+       let front = FramerateFront(client, form);
+ 
+diff --git a/devtools/server/tests/mochitest/test_framerate_05.html b/devtools/server/tests/mochitest/test_framerate_05.html
+--- a/devtools/server/tests/mochitest/test_framerate_05.html
++++ b/devtools/server/tests/mochitest/test_framerate_05.html
+@@ -26,17 +26,17 @@ window.onload = function () {
+     Services.prefs.clearUserPref("devtools.debugger.log");
+   });
+ 
+   SimpleTest.waitForExplicitFinish();
+ 
+   const {FramerateFront} = require("devtools/shared/fronts/framerate");
+ 
+   DebuggerServer.init();
+-  DebuggerServer.addBrowserActors();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+     client.listTabs(function onListTabs(response) {
+       let form = response.tabs[response.selected];
+       let front = FramerateFront(client, form);
+ 
+       front.startRecording().then(() => {
+diff --git a/devtools/server/tests/mochitest/test_getProcess.html b/devtools/server/tests/mochitest/test_getProcess.html
+--- a/devtools/server/tests/mochitest/test_getProcess.html
++++ b/devtools/server/tests/mochitest/test_getProcess.html
+@@ -36,17 +36,17 @@ window.onload = function () {
+ 
+ function runTests() {
+   // Instantiate a minimal server
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+   }
+   DebuggerServer.allowChromeProcess = true;
+   if (!DebuggerServer.createRootActor) {
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let client, iframe, processCount;
+ 
+   function connect() {
+     // Fake a first connection to the content process
+     let transport = DebuggerServer.connectPipe();
+     client = new DebuggerClient(transport);
+diff --git a/devtools/server/tests/mochitest/test_preference.html b/devtools/server/tests/mochitest/test_preference.html
+--- a/devtools/server/tests/mochitest/test_preference.html
++++ b/devtools/server/tests/mochitest/test_preference.html
+@@ -20,17 +20,17 @@ function runTests() {
+   let {DebuggerServer} = require("devtools/server/main");
+   let Services = require("Services");
+ 
+   SimpleTest.waitForExplicitFinish();
+ 
+   let {getPreferenceFront} = require("devtools/shared/fronts/preference");
+ 
+   DebuggerServer.init();
+-  DebuggerServer.addBrowserActors();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+     client.listTabs(function onListTabs(response) {
+       let p = getPreferenceFront(client, response);
+ 
+       let prefs = {};
+ 
+diff --git a/devtools/server/tests/mochitest/test_setupInParentChild.html b/devtools/server/tests/mochitest/test_setupInParentChild.html
+--- a/devtools/server/tests/mochitest/test_setupInParentChild.html
++++ b/devtools/server/tests/mochitest/test_setupInParentChild.html
+@@ -37,17 +37,17 @@ function runTests() {
+   iframe.mozbrowser = true;
+   document.body.appendChild(iframe);
+ 
+   // Instantiate a minimal server
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+   }
+   if (!DebuggerServer.createRootActor) {
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   // Fake a connection to an iframe
+   let transport = DebuggerServer.connectPipe();
+   let conn = transport._serverConnection;
+   let client = new DebuggerClient(transport);
+ 
+   // Wait for a response from setupInChild
+diff --git a/devtools/server/tests/mochitest/webconsole-helpers.js b/devtools/server/tests/mochitest/webconsole-helpers.js
+--- a/devtools/server/tests/mochitest/webconsole-helpers.js
++++ b/devtools/server/tests/mochitest/webconsole-helpers.js
+@@ -10,17 +10,17 @@ const Services = require("Services");
+ // Always log packets when running tests.
+ Services.prefs.setBoolPref("devtools.debugger.log", true);
+ SimpleTest.registerCleanupFunction(function () {
+   Services.prefs.clearUserPref("devtools.debugger.log");
+ });
+ 
+ if (!DebuggerServer.initialized) {
+   DebuggerServer.init();
+-  DebuggerServer.addBrowserActors();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   SimpleTest.registerCleanupFunction(function () {
+     DebuggerServer.destroy();
+   });
+ }
+ 
+ /**
+  * Open a tab, load the url, find the tab with the debugger server,
+  * and attach the console to it.
+diff --git a/devtools/server/tests/mochitest/webextension-helpers.js b/devtools/server/tests/mochitest/webextension-helpers.js
+--- a/devtools/server/tests/mochitest/webextension-helpers.js
++++ b/devtools/server/tests/mochitest/webextension-helpers.js
+@@ -16,17 +16,17 @@ const {flushJarCache} = require("resourc
+ const {Services} = require("resource://gre/modules/Services.jsm");
+ 
+ loader.lazyImporter(this, "ExtensionParent", "resource://gre/modules/ExtensionParent.jsm");
+ loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
+ 
+ // Initialize a minimal DebuggerServer and connect to the webextension addon actor.
+ if (!DebuggerServer.initialized) {
+   DebuggerServer.init();
+-  DebuggerServer.addBrowserActors();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   SimpleTest.registerCleanupFunction(function () {
+     DebuggerServer.destroy();
+   });
+ }
+ 
+ SimpleTest.registerCleanupFunction(function () {
+   const {hiddenXULWindow} = ExtensionParent.DebugUtils;
+   const debugBrowserMapSize = ExtensionParent.DebugUtils.debugBrowserPromises.size;
+diff --git a/devtools/server/tests/unit/head_dbg.js b/devtools/server/tests/unit/head_dbg.js
+--- a/devtools/server/tests/unit/head_dbg.js
++++ b/devtools/server/tests/unit/head_dbg.js
+@@ -411,17 +411,17 @@ function initTestDebuggerServer(server =
+ }
+ 
+ /**
+  * Initialize the testing debugger server with a tab whose title is |title|.
+  */
+ function startTestDebuggerServer(title, server = DebuggerServer) {
+   initTestDebuggerServer(server);
+   addTestGlobal(title);
+-  DebuggerServer.addTabActors();
++  DebuggerServer.registerActors({ browser: false, root: false, tab: true });
+ 
+   let transport = DebuggerServer.connectPipe();
+   let client = new DebuggerClient(transport);
+ 
+   return connect(client).then(() => client);
+ }
+ 
+ function finishClient(client) {
+@@ -431,17 +431,17 @@ function finishClient(client) {
+   });
+ }
+ 
+ // Create a server, connect to it and fetch tab actors for the parent process;
+ // pass |callback| the debugger client and tab actor form with all actor IDs.
+ function get_chrome_actors(callback) {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+   DebuggerServer.allowChromeProcess = true;
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect()
+     .then(() => client.getProcess())
+     .then(response => {
+       callback(client, response.form);
+diff --git a/devtools/server/tests/unit/test_actor-registry-actor.js b/devtools/server/tests/unit/test_actor-registry-actor.js
+--- a/devtools/server/tests/unit/test_actor-registry-actor.js
++++ b/devtools/server/tests/unit/test_actor-registry-actor.js
+@@ -13,17 +13,17 @@ var gActorFront;
+ var gOldPref;
+ 
+ const { ActorRegistryFront } = require("devtools/shared/fronts/actor-registry");
+ 
+ function run_test() {
+   gOldPref = Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps");
+   Services.prefs.setBoolPref("devtools.debugger.forbid-certified-apps", false);
+   initTestDebuggerServer();
+-  DebuggerServer.addBrowserActors();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   gClient = new DebuggerClient(DebuggerServer.connectPipe());
+   gClient.connect().then(getRegistry);
+   do_test_pending();
+ }
+ 
+ function getRegistry() {
+   gClient.listTabs((response) => {
+     gRegistryFront = ActorRegistryFront(gClient, response);
+diff --git a/devtools/server/tests/unit/test_add_actors.js b/devtools/server/tests/unit/test_add_actors.js
+--- a/devtools/server/tests/unit/test_add_actors.js
++++ b/devtools/server/tests/unit/test_add_actors.js
+@@ -13,17 +13,17 @@ var gActors;
+  * in order to add actors after initialization but rather can add actors anytime
+  * regardless of the object's state.
+  */
+ function run_test() {
+   DebuggerServer.addActors("resource://test/pre_init_global_actors.js");
+   DebuggerServer.addActors("resource://test/pre_init_tab_actors.js");
+ 
+   DebuggerServer.init();
+-  DebuggerServer.addBrowserActors();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   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);
+diff --git a/devtools/server/tests/unit/test_client_request.js b/devtools/server/tests/unit/test_client_request.js
+--- a/devtools/server/tests/unit/test_client_request.js
++++ b/devtools/server/tests/unit/test_client_request.js
+@@ -25,17 +25,17 @@ TestActor.prototype.requestTypes = {
+   "hello": TestActor.prototype.hello,
+   "error": TestActor.prototype.error
+ };
+ 
+ function run_test() {
+   DebuggerServer.addGlobalActor(TestActor);
+ 
+   DebuggerServer.init();
+-  DebuggerServer.addBrowserActors();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   add_test(init);
+   add_test(test_client_request_callback);
+   add_test(test_client_request_promise);
+   add_test(test_client_request_promise_error);
+   add_test(test_client_request_event_emitter);
+   add_test(test_close_client_while_sending_requests);
+   add_test(test_client_request_after_close);
+diff --git a/devtools/server/tests/unit/test_registerClient.js b/devtools/server/tests/unit/test_registerClient.js
+--- a/devtools/server/tests/unit/test_registerClient.js
++++ b/devtools/server/tests/unit/test_registerClient.js
+@@ -49,17 +49,17 @@ TestClient.prototype = {
+     onDone();
+   }
+ };
+ 
+ function run_test() {
+   DebuggerServer.addGlobalActor(TestActor);
+ 
+   DebuggerServer.init();
+-  DebuggerServer.addBrowserActors();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   add_test(init);
+   add_test(test_client_events);
+   add_test(close_client);
+   run_next_test();
+ }
+ 
+ function init() {
+diff --git a/devtools/server/tests/unit/test_register_actor.js b/devtools/server/tests/unit/test_register_actor.js
+--- a/devtools/server/tests/unit/test_register_actor.js
++++ b/devtools/server/tests/unit/test_register_actor.js
+@@ -13,17 +13,17 @@ function check_actors(expect) {
+                DebuggerServer.globalActorFactories.hasOwnProperty("registeredActor2"));
+   Assert.equal(expect,
+                DebuggerServer.globalActorFactories.hasOwnProperty("registeredActor1"));
+ }
+ 
+ function run_test() {
+   // Allow incoming connections.
+   DebuggerServer.init();
+-  DebuggerServer.addBrowserActors();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   add_test(test_deprecated_api);
+   add_test(test_lazy_api);
+   add_test(cleanup);
+   run_next_test();
+ }
+ 
+ function test_deprecated_api() {
+diff --git a/devtools/server/tests/unit/test_requestTypes.js b/devtools/server/tests/unit/test_requestTypes.js
+--- a/devtools/server/tests/unit/test_requestTypes.js
++++ b/devtools/server/tests/unit/test_requestTypes.js
+@@ -18,17 +18,17 @@ function test_requestTypes_request(clien
+     client.close().then(() => {
+       do_test_finished();
+     });
+   });
+ }
+ 
+ function run_test() {
+   DebuggerServer.init();
+-  DebuggerServer.addBrowserActors();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function () {
+     test_requestTypes_request(client);
+   });
+ 
+   do_test_pending();
+ }
+diff --git a/devtools/shared/gcli/commands/listen.js b/devtools/shared/gcli/commands/listen.js
+--- a/devtools/shared/gcli/commands/listen.js
++++ b/devtools/shared/gcli/commands/listen.js
+@@ -21,17 +21,17 @@ XPCOMUtils.defineLazyGetter(this, "debug
+   // devtools.  This allows us to safely use the tools against even the
+   // actors and DebuggingServer itself, especially since we can mark
+   // serverLoader as invisible to the debugger (unlike the usual loader
+   // settings).
+   let serverLoader = new DevToolsLoader();
+   serverLoader.invisibleToDebugger = true;
+   let { DebuggerServer: debuggerServer } = serverLoader.require("devtools/server/main");
+   debuggerServer.init();
+-  debuggerServer.addBrowserActors();
++  debuggerServer.registerActors({ browser: true, root: true, tab: true });
+   debuggerServer.allowChromeProcess = !l10n.hiddenByChromePref();
+   return debuggerServer;
+ });
+ 
+ exports.items = [
+   {
+     item: "command",
+     runAt: "client",
+diff --git a/devtools/shared/security/tests/chrome/test_websocket-transport.html b/devtools/shared/security/tests/chrome/test_websocket-transport.html
+--- a/devtools/shared/security/tests/chrome/test_websocket-transport.html
++++ b/devtools/shared/security/tests/chrome/test_websocket-transport.html
+@@ -23,17 +23,17 @@ window.onload = function () {
+   SimpleTest.registerCleanupFunction(() => {
+     Services.prefs.clearUserPref("devtools.debugger.remote-enabled");
+     Services.prefs.clearUserPref("devtools.debugger.prompt-connection");
+   });
+ 
+   add_task(function* () {
+     if (!DebuggerServer.initialized) {
+       DebuggerServer.init();
+-      DebuggerServer.addBrowserActors();
++      DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+     }
+ 
+     is(DebuggerServer.listeningSockets, 0, "0 listening sockets");
+ 
+     let listener = DebuggerServer.createListener();
+     ok(listener, "Socket listener created");
+     listener.portOrPath = -1;
+     listener.webSocket = true;
+diff --git a/devtools/shared/webconsole/test/common.js b/devtools/shared/webconsole/test/common.js
+--- a/devtools/shared/webconsole/test/common.js
++++ b/devtools/shared/webconsole/test/common.js
+@@ -21,17 +21,17 @@ const Services = require("Services");
+ 
+ function initCommon() {
+   // Services.prefs.setBoolPref("devtools.debugger.log", true);
+ }
+ 
+ function initDebuggerServer() {
+   if (!DebuggerServer.initialized) {
+     DebuggerServer.init();
+-    DebuggerServer.addBrowserActors();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+   DebuggerServer.allowChromeProcess = true;
+ }
+ 
+ function connectToDebugger() {
+   initCommon();
+   initDebuggerServer();
+ 
+diff --git a/devtools/startup/devtools-startup.js b/devtools/startup/devtools-startup.js
+--- a/devtools/startup/devtools-startup.js
++++ b/devtools/startup/devtools-startup.js
+@@ -678,17 +678,17 @@ DevToolsStartup.prototype = {
+       // actors and DebuggingServer itself, especially since we can mark
+       // serverLoader as invisible to the debugger (unlike the usual loader
+       // settings).
+       let serverLoader = new DevToolsLoader();
+       serverLoader.invisibleToDebugger = true;
+       let { DebuggerServer: debuggerServer } =
+         serverLoader.require("devtools/server/main");
+       debuggerServer.init();
+-      debuggerServer.addBrowserActors();
++      debuggerServer.registerActors({ browser: true, root: true, tab: true });
+       debuggerServer.allowChromeProcess = true;
+ 
+       let listener = debuggerServer.createListener();
+       listener.portOrPath = portOrPath;
+       listener.webSocket = webSocket;
+       listener.open();
+       dump("Started debugger server on " + portOrPath + "\n");
+     } catch (e) {
+diff --git a/testing/xpcshell/head.js b/testing/xpcshell/head.js
+--- a/testing/xpcshell/head.js
++++ b/testing/xpcshell/head.js
+@@ -380,17 +380,17 @@ function _setupDebuggerServer(breakpoint
+                     "It is possible for this to alter test behevior by " +
+                     "triggering additional browser code to run, so check " +
+                     "test behavior after making this change.\n" +
+                     "See also https://bugzil.la/1215378.")
+   }
+   let { DebuggerServer } = require("devtools/server/main");
+   let { OriginalLocation } = require("devtools/server/actors/common");
+   DebuggerServer.init();
+-  DebuggerServer.addBrowserActors();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   DebuggerServer.addActors("resource://testing-common/dbg-actors.js");
+   DebuggerServer.allowChromeProcess = true;
+ 
+   // An observer notification that tells us when we can "resume" script
+   // execution.
+   const TOPICS = ["devtools-thread-resumed", "xpcshell-test-devtools-shutdown"];
+   let observe = function(subject, topic, data) {
+     switch (topic) {

+ 35 - 0
mozilla-release/patches/1416711-3-59a1.patch

@@ -0,0 +1,35 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1510601700 -3600
+# Node ID 71cc1ff4febe42ebc14eb757e3842940b117bcdb
+# Parent  562db82dca87d80f6a35b262afcc0ee1872d9459
+Bug 1416711 - Remove default values for registerActors;r=ochameau
+
+The default values for registerActors are never used in the codebase
+and feel counter intuitive as they set all booleans to true.
+
+MozReview-Commit-ID: 25zaJss9E4R
+
+diff --git a/devtools/server/main.js b/devtools/server/main.js
+--- a/devtools/server/main.js
++++ b/devtools/server/main.js
+@@ -246,18 +246,17 @@ var DebuggerServer = {
+    *        runtime itself, like preferences and addons actors.
+    * @param tab boolean
+    *        Registers all the tab actors like console, script, ... all useful
+    *        for debugging a target context.
+    * @param windowType string
+    *        "windowtype" attribute of the main chrome windows. Used by some
+    *        actors to retrieve them.
+    */
+-  registerActors({ root = true, browser = true, tab = true,
+-                   windowType = null }) {
++  registerActors({ root, browser, tab, windowType }) {
+     if (windowType) {
+       this.chromeWindowType = windowType;
+     }
+ 
+     if (browser) {
+       this._addBrowserActors();
+     }
+ 

+ 1339 - 0
mozilla-release/patches/1416711-4-59a1.patch

@@ -0,0 +1,1339 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1510603483 -3600
+# Node ID 6e58664da16e453fe051ef33b9b123d47358270e
+# Parent  8b27c92a903a15a1a6b92013eae9568c4b8d70dc
+Bug 1416711 - Stop guarding consumer calls to DebuggerServer.init();r=ochameau
+
+DebuggerServer.init() already bails out if it was previously initialized
+so we could avoid guarding the calls to init() with it everywhere.
+
+Registering an actor module several times is also a noop as the Server
+keeps a map of all the already registered modules and will bail out if
+the module is already known.
+
+MozReview-Commit-ID: 4ONLlx9253i
+
+diff --git a/devtools/client/aboutdebugging/initializer.js b/devtools/client/aboutdebugging/initializer.js
+--- a/devtools/client/aboutdebugging/initializer.js
++++ b/devtools/client/aboutdebugging/initializer.js
+@@ -34,19 +34,17 @@ const AboutDebuggingApp = createFactory(
+ var AboutDebugging = {
+   init() {
+     if (!Services.prefs.getBoolPref("devtools.enabled", true)) {
+       // If DevTools are disabled, navigate to about:devtools.
+       window.location = "about:devtools?reason=AboutDebugging";
+       return;
+     }
+ 
+-    if (!DebuggerServer.initialized) {
+-      DebuggerServer.init();
+-    }
++    DebuggerServer.init();
+     DebuggerServer.allowChromeProcess = true;
+     // We want a full featured server for about:debugging. Especially the
+     // "browser actors" like addons.
+     DebuggerServer.registerActors({ root: true, browser: true, tab: true });
+ 
+     this.client = new DebuggerClient(DebuggerServer.connectPipe());
+ 
+     this.client.connect().then(() => {
+diff --git a/devtools/client/canvasdebugger/test/head.js b/devtools/client/canvasdebugger/test/head.js
+--- a/devtools/client/canvasdebugger/test/head.js
++++ b/devtools/client/canvasdebugger/test/head.js
+@@ -184,20 +184,18 @@ function navigate(aTarget, aUrl, aWaitFo
+ }
+ 
+ function reload(aTarget, aWaitForTargetEvent = "navigate") {
+   executeSoon(() => aTarget.activeTab.reload());
+   return once(aTarget, aWaitForTargetEvent);
+ }
+ 
+ function initServer() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ }
+ 
+ function initCallWatcherBackend(aUrl) {
+   info("Initializing a call watcher front.");
+   initServer();
+ 
+   return Task.spawn(function* () {
+     let tab = yield addTab(aUrl);
+diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js
+--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js
++++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js
+@@ -13,20 +13,18 @@ var gNewChromeSource = promise.defer();
+ 
+ var { DevToolsLoader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ var customLoader = new DevToolsLoader();
+ customLoader.invisibleToDebugger = true;
+ var { DebuggerServer } = customLoader.require("devtools/server/main");
+ var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+ 
+ function initDebuggerClient() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   DebuggerServer.allowChromeProcess = true;
+ 
+   let transport = DebuggerServer.connectPipe();
+   return new DebuggerClient(transport);
+ }
+ 
+ function attachThread(client, actor) {
+   return new Promise(resolve => {
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-01.js b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-01.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-01.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-01.js
+@@ -7,20 +7,18 @@
+  * Tests that the break-on-dom-events request works.
+  */
+ 
+ const TAB_URL = EXAMPLE_URL + "doc_event-listeners-01.html";
+ 
+ var gClient, gThreadClient, gInput, gButton;
+ 
+ function test() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     addTab(TAB_URL)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-02.js b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-02.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-02.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-02.js
+@@ -8,20 +8,18 @@
+  * listeners and handler objects with 'handleEvent' methods.
+  */
+ 
+ const TAB_URL = EXAMPLE_URL + "doc_event-listeners-03.html";
+ 
+ var gClient, gThreadClient;
+ 
+ function test() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     addTab(TAB_URL)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js b/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js
+@@ -15,20 +15,18 @@ var gNewGlobal = promise.defer();
+ var gNewChromeSource = promise.defer();
+ 
+ var { DevToolsLoader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ var customLoader = new DevToolsLoader();
+ customLoader.invisibleToDebugger = true;
+ var { DebuggerServer } = customLoader.require("devtools/server/main");
+ 
+ function test() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   DebuggerServer.allowChromeProcess = true;
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_debugger-statement.js b/devtools/client/debugger/test/mochitest/browser_dbg_debugger-statement.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_debugger-statement.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_debugger-statement.js
+@@ -8,20 +8,18 @@
+  */
+ 
+ const TAB_URL = EXAMPLE_URL + "doc_inline-debugger-statement.html";
+ 
+ var gClient;
+ var gTab;
+ 
+ function test() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     addTab(TAB_URL)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js
+@@ -8,20 +8,18 @@
+  */
+ 
+ const TAB_URL = EXAMPLE_URL + "doc_event-listeners-01.html";
+ 
+ var gClient;
+ var gTab;
+ 
+ function test() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     addTab(TAB_URL)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js
+@@ -9,20 +9,18 @@
+  */
+ 
+ const TAB_URL = EXAMPLE_URL + "doc_event-listeners-03.html";
+ 
+ var gClient;
+ var gTab;
+ 
+ function test() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     addTab(TAB_URL)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-03.js b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-03.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-03.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-03.js
+@@ -9,20 +9,18 @@
+  */
+ 
+ const TAB_URL = EXAMPLE_URL + "doc_native-event-handler.html";
+ 
+ var gClient;
+ var gTab;
+ 
+ function test() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     addTab(TAB_URL)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js b/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
+@@ -7,20 +7,18 @@
+  * Check extension-added global actor API.
+  */
+ 
+ const ACTORS_URL = CHROME_URL + "testactors.js";
+ 
+ function test() {
+   let gClient;
+ 
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   DebuggerServer.addActors(ACTORS_URL);
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js b/devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
+@@ -9,20 +9,18 @@
+ const ADDON1_ID = "jid1-oBAwBoE5rSecNg@jetpack";
+ const ADDON1_PATH = "addon1.xpi";
+ const ADDON2_ID = "jid1-qjtzNGV8xw5h2A@jetpack";
+ const ADDON2_PATH = "addon2.xpi";
+ 
+ var gAddon1, gAddon1Actor, gAddon2, gAddon2Actor, gClient;
+ 
+ function test() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     promise.resolve(null)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
+@@ -8,20 +8,18 @@
+  */
+ 
+ const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
+ const TAB2_URL = EXAMPLE_URL + "doc_empty-tab-02.html";
+ 
+ var gTab1, gTab1Actor, gTab2, gTab2Actor, gClient;
+ 
+ function test() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     promise.resolve(null)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
+@@ -20,20 +20,18 @@ var gNewWindow;
+ 
+ // Stock onListChanged handler.
+ var onListChangedCount = 0;
+ function onListChangedHandler() {
+   onListChangedCount++;
+ }
+ 
+ function test() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   gTabList = new BrowserTabList("fake DebuggerServerConnection");
+   gTabList._testing = true;
+   gTabList.onListChanged = onListChangedHandler;
+ 
+   checkSingleTab()
+     .then(addTabA)
+     .then(testTabA)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
+@@ -7,20 +7,18 @@
+  * Make sure the listTabs request works as specified.
+  */
+ 
+ const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
+ 
+ var gTab1, gTab1Actor, gTab2, gTab2Actor, gClient;
+ 
+ function test() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(Task.async(function* ([aType, aTraits]) {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+     let tab = yield addTab(TAB1_URL);
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js b/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
+@@ -10,20 +10,18 @@
+ 
+ const TAB1_URL = EXAMPLE_URL + "doc_script-switching-01.html";
+ const TAB2_URL = EXAMPLE_URL + "doc_script-switching-02.html";
+ 
+ var gNewTab, gNewWindow;
+ var gClient;
+ 
+ function test() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     promise.resolve(null)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_navigation.js b/devtools/client/debugger/test/mochitest/browser_dbg_navigation.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_navigation.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_navigation.js
+@@ -8,20 +8,18 @@
+  */
+ 
+ const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
+ const TAB2_URL = EXAMPLE_URL + "doc_empty-tab-02.html";
+ 
+ var gClient;
+ 
+ function test() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     addTab(TAB1_URL)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-01.js b/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-01.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-01.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-01.js
+@@ -8,20 +8,18 @@
+  */
+ 
+ const ACTORS_URL = CHROME_URL + "testactors.js";
+ const TAB_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
+ 
+ var gClient;
+ 
+ function test() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   DebuggerServer.addActors(ACTORS_URL);
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-02.js b/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-02.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-02.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-02.js
+@@ -8,20 +8,18 @@
+  */
+ 
+ const ACTORS_URL = CHROME_URL + "testactors.js";
+ const TAB_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
+ 
+ var gClient;
+ 
+ function test() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   DebuggerServer.addActors(ACTORS_URL);
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+diff --git a/devtools/client/debugger/test/mochitest/head.js b/devtools/client/debugger/test/mochitest/head.js
+--- a/devtools/client/debugger/test/mochitest/head.js
++++ b/devtools/client/debugger/test/mochitest/head.js
+@@ -622,20 +622,18 @@ function AddonDebugger() {
+   this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
+   EventEmitter.decorate(this);
+ }
+ 
+ AddonDebugger.prototype = {
+   init: Task.async(function* (aAddonId) {
+     info("Initializing an addon debugger panel.");
+ 
+-    if (!DebuggerServer.initialized) {
+-      DebuggerServer.init();
+-      DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-    }
++    DebuggerServer.init();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+     DebuggerServer.allowChromeProcess = true;
+ 
+     this.frame = document.createElement("iframe");
+     this.frame.setAttribute("height", 400);
+     document.documentElement.appendChild(this.frame);
+     window.addEventListener("message", this._onMessage);
+ 
+     let transport = DebuggerServer.connectPipe();
+@@ -1319,20 +1317,18 @@ function waitForDispatch(panel, type, ev
+       yield _afterDispatchDone(controller, actionType);
+       count++;
+       info(type + " dispatched " + count + " time(s)");
+     }
+   });
+ }
+ 
+ function* initWorkerDebugger(TAB_URL, WORKER_URL) {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   yield connect(client);
+ 
+   let tab = yield addTab(TAB_URL);
+   let { tabs } = yield listTabs(client);
+   let [, tabClient] = yield attachTab(client, findTab(tabs, TAB_URL));
+ 
+diff --git a/devtools/client/framework/devtools-browser.js b/devtools/client/framework/devtools-browser.js
+--- a/devtools/client/framework/devtools-browser.js
++++ b/devtools/client/framework/devtools-browser.js
+@@ -289,20 +289,18 @@ var gDevToolsBrowser = exports.gDevTools
+    */
+    // Used by browser-sets.inc, command
+   openConnectScreen(gBrowser) {
+     gBrowser.selectedTab = gBrowser.addTab("chrome://devtools/content/framework/connect/connect.xhtml");
+   },
+ 
+   _getContentProcessTarget(processId) {
+     // Create a DebuggerServer in order to connect locally to it
+-    if (!DebuggerServer.initialized) {
+-      DebuggerServer.init();
+-      DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-    }
++    DebuggerServer.init();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+     DebuggerServer.allowChromeProcess = true;
+ 
+     let transport = DebuggerServer.connectPipe();
+     let client = new DebuggerClient(transport);
+ 
+     let deferred = defer();
+     client.connect().then(() => {
+       client.getProcess(processId)
+diff --git a/devtools/client/framework/target-from-url.js b/devtools/client/framework/target-from-url.js
+--- a/devtools/client/framework/target-from-url.js
++++ b/devtools/client/framework/target-from-url.js
+@@ -127,16 +127,14 @@ function* createClient(params) {
+   let port = params.get("port");
+   let webSocket = !!params.get("ws");
+ 
+   let transport;
+   if (port) {
+     transport = yield DebuggerClient.socketConnect({ host, port, webSocket });
+   } else {
+     // Setup a server if we don't have one already running
+-    if (!DebuggerServer.initialized) {
+-      DebuggerServer.init();
+-      DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-    }
++    DebuggerServer.init();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+     transport = DebuggerServer.connectPipe();
+   }
+   return new DebuggerClient(transport);
+ }
+diff --git a/devtools/client/framework/target.js b/devtools/client/framework/target.js
+--- a/devtools/client/framework/target.js
++++ b/devtools/client/framework/target.js
+@@ -399,19 +399,18 @@ TabTarget.prototype = {
+       return this._remote.promise;
+     }
+ 
+     this._remote = defer();
+ 
+     if (this.isLocalTab) {
+       // Since a remote protocol connection will be made, let's start the
+       // DebuggerServer here, once and for all tools.
+-      if (!DebuggerServer.initialized) {
+-        DebuggerServer.init();
+-      }
++      DebuggerServer.init();
++
+       // When connecting to a local tab, we only need the root actor.
+       // Then we are going to call DebuggerServer.connectToChild and talk
+       // directly with actors living in the child process.
+       // We also need browser actors for actor registry which enabled addons
+       // to register custom actors.
+       DebuggerServer.registerActors({ root: true, browser: true, tab: false });
+ 
+       this._client = new DebuggerClient(DebuggerServer.connectPipe());
+diff --git a/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js b/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
+--- a/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
++++ b/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
+@@ -58,20 +58,18 @@ function runTools(target) {
+ 
+     yield toolbox.destroy();
+   });
+ }
+ 
+ function getClient() {
+   let deferred = defer();
+ 
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let transport = DebuggerServer.connectPipe();
+   let client = new DebuggerClient(transport);
+ 
+   return client.connect().then(() => client);
+ }
+ 
+ function getTarget(client) {
+diff --git a/devtools/client/framework/test/browser_two_tabs.js b/devtools/client/framework/test/browser_two_tabs.js
+--- a/devtools/client/framework/test/browser_two_tabs.js
++++ b/devtools/client/framework/test/browser_two_tabs.js
+@@ -15,20 +15,18 @@ const TAB_URL_2 = "data:text/html;charse
+ 
+ var gClient;
+ var gTab1, gTab2;
+ var gTabActor1, gTabActor2;
+ 
+ function test() {
+   waitForExplicitFinish();
+ 
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   openTabs();
+ }
+ 
+ function openTabs() {
+   // Open two tabs, select the second
+   addTab(TAB_URL_1).then(tab1 => {
+     gTab1 = tab1;
+diff --git a/devtools/client/framework/test/head.js b/devtools/client/framework/test/head.js
+--- a/devtools/client/framework/test/head.js
++++ b/devtools/client/framework/test/head.js
+@@ -23,20 +23,18 @@ function toggleAllTools(state) {
+   }
+ }
+ 
+ function getChromeActors(callback)
+ {
+   let { DebuggerServer } = require("devtools/server/main");
+   let { DebuggerClient } = require("devtools/shared/client/debugger-client");
+ 
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   DebuggerServer.allowChromeProcess = true;
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect()
+     .then(() => client.getProcess())
+     .then(response => {
+       callback(client, response.form);
+     });
+diff --git a/devtools/client/framework/toolbox-init.js b/devtools/client/framework/toolbox-init.js
+--- a/devtools/client/framework/toolbox-init.js
++++ b/devtools/client/framework/toolbox-init.js
+@@ -63,20 +63,18 @@ if (url.search.length > 1) {
+       // Need to use a xray to have attributes and behavior expected by
+       // devtools codebase
+       iframe = XPCNativeWrapper(iframe);
+ 
+       // Fake a xul:tab object as we don't have one.
+       // linkedBrowser is the only one attribute being queried by client.getTab
+       let tab = { linkedBrowser: iframe };
+ 
+-      if (!DebuggerServer.initialized) {
+-        DebuggerServer.init();
+-        DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-      }
++      DebuggerServer.init();
++      DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+       let client = new DebuggerClient(DebuggerServer.connectPipe());
+ 
+       yield client.connect();
+       // Creates a target for a given browser iframe.
+       let response = yield client.getTab({ tab });
+       let form = response.tab;
+       target = yield TargetFactory.forRemoteTab({client, form, chrome: false});
+     } else {
+diff --git a/devtools/client/responsive.html/manager.js b/devtools/client/responsive.html/manager.js
+--- a/devtools/client/responsive.html/manager.js
++++ b/devtools/client/responsive.html/manager.js
+@@ -415,20 +415,18 @@ ResponsiveUI.prototype = {
+     }
+ 
+     this.destroyed = true;
+ 
+     return true;
+   }),
+ 
+   connectToServer: Task.async(function* () {
+-    if (!DebuggerServer.initialized) {
+-      DebuggerServer.init();
+-      DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-    }
++    DebuggerServer.init();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+     this.client = new DebuggerClient(DebuggerServer.connectPipe());
+     yield this.client.connect();
+     let { tab } = yield this.client.getTab();
+     this.emulationFront = EmulationFront(this.client, tab);
+   }),
+ 
+   handleEvent(event) {
+     let { browserWindow, tab } = this;
+diff --git a/devtools/client/scratchpad/scratchpad.js b/devtools/client/scratchpad/scratchpad.js
+--- a/devtools/client/scratchpad/scratchpad.js
++++ b/devtools/client/scratchpad/scratchpad.js
+@@ -2177,20 +2177,18 @@ ScratchpadWindow.prototype = Heritage.ex
+   /**
+    * Attach to this window.
+    *
+    * @return Promise
+    *         The promise for the target for this window.
+    */
+   _attach: function SW__attach()
+   {
+-    if (!DebuggerServer.initialized) {
+-      DebuggerServer.init();
+-      DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-    }
++    DebuggerServer.init();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+     DebuggerServer.allowChromeProcess = true;
+ 
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+     return client.connect()
+       .then(() => client.getProcess())
+       .then(aResponse => {
+         return { form: aResponse.form, client: client };
+       });
+diff --git a/devtools/client/shadereditor/test/head.js b/devtools/client/shadereditor/test/head.js
+--- a/devtools/client/shadereditor/test/head.js
++++ b/devtools/client/shadereditor/test/head.js
+@@ -219,20 +219,18 @@ function navigate(aTarget, aUrl, aWaitFo
+ function reload(aTarget, aWaitForTargetEvent = "navigate") {
+   executeSoon(() => aTarget.activeTab.reload());
+   return once(aTarget, aWaitForTargetEvent);
+ }
+ 
+ function initBackend(aUrl) {
+   info("Initializing a shader editor front.");
+ 
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   return Task.spawn(function* () {
+     let tab = yield addTab(aUrl);
+     let target = TargetFactory.forTab(tab);
+ 
+     yield target.makeRemote();
+ 
+     let front = new WebGLFront(target.client, target.form);
+diff --git a/devtools/client/shared/test/test-actor-registry.js b/devtools/client/shared/test/test-actor-registry.js
+--- a/devtools/client/shared/test/test-actor-registry.js
++++ b/devtools/client/shared/test/test-actor-registry.js
+@@ -60,20 +60,18 @@
+   // Sometimes, we need the test actor before opening or without a toolbox then just
+   // create a front for the given `tab`
+   exports.getTestActorWithoutToolbox = Task.async(function* (tab) {
+     let { DebuggerServer } = require("devtools/server/main");
+     let { DebuggerClient } = require("devtools/shared/client/debugger-client");
+ 
+     // We need to spawn a client instance,
+     // but for that we have to first ensure a server is running
+-    if (!DebuggerServer.initialized) {
+-      DebuggerServer.init();
+-      DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-    }
++    DebuggerServer.init();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+ 
+     yield client.connect();
+ 
+     // We also need to make sure the test actor is registered on the server.
+     yield exports.registerTestActor(client);
+ 
+     return getTestActor(client, tab);
+diff --git a/devtools/client/webaudioeditor/test/head.js b/devtools/client/webaudioeditor/test/head.js
+--- a/devtools/client/webaudioeditor/test/head.js
++++ b/devtools/client/webaudioeditor/test/head.js
+@@ -154,20 +154,18 @@ function loadFrameScripts() {
+ 
+ /**
+  * Adds a new tab, and instantiate a WebAudiFront object.
+  * This requires calling removeTab before the test ends.
+  */
+ function initBackend(aUrl) {
+   info("Initializing a web audio editor front.");
+ 
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   return Task.spawn(function* () {
+     let tab = yield addTab(aUrl);
+     let target = TargetFactory.forTab(tab);
+ 
+     yield target.makeRemote();
+ 
+     let front = new WebAudioFront(target.client, target.form);
+diff --git a/devtools/client/webconsole/hudservice.js b/devtools/client/webconsole/hudservice.js
+--- a/devtools/client/webconsole/hudservice.js
++++ b/devtools/client/webconsole/hudservice.js
+@@ -183,23 +183,20 @@ HUD_SERVICE.prototype =
+     }
+ 
+     this._browserConsoleDefer = defer();
+ 
+     function connect()
+     {
+       let deferred = defer();
+ 
+-      if (!DebuggerServer.initialized) {
+-        DebuggerServer.init();
+-      }
+-
+       // Ensure that the root actor and the tab actors have been registered on the DebuggerServer,
+       // so that the Browser Console can retrieve the console actors.
+       // (See Bug 1416105 for rationale).
++      DebuggerServer.init();
+       DebuggerServer.registerActors({ root: true, browser: false, tab: true });
+ 
+       DebuggerServer.allowChromeProcess = true;
+ 
+       let client = new DebuggerClient(DebuggerServer.connectPipe());
+       return client.connect()
+         .then(() => client.getProcess())
+         .then(aResponse => {
+diff --git a/devtools/docs/backend/client-api.md b/devtools/docs/backend/client-api.md
+--- a/devtools/docs/backend/client-api.md
++++ b/devtools/docs/backend/client-api.md
+@@ -7,20 +7,18 @@ DevTools has a client module that allows
+ In order to communicate, a client and a server instance must be created and a protocol connection must be established. The connection can be either over a TCP socket or an nsIPipe. The `start` function displayed below establishes an nsIPipe-backed connection:
+ 
+ ```javascript
+ Components.utils.import("resource://gre/modules/devtools/dbg-server.jsm");
+ Components.utils.import("resource://gre/modules/devtools/dbg-client.jsm");
+ 
+ function start() {
+   // Start the server.
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   // Listen to an nsIPipe
+   let transport = DebuggerServer.connectPipe();
+ 
+   // Start the client.
+   client = new DebuggerClient(transport);
+ 
+   // Attach listeners for client events.
+@@ -36,20 +34,18 @@ function start() {
+ If a TCP socket is required, the function should be split in two parts, a server-side and a client-side, like this:
+ 
+ ```javascript
+ Components.utils.import("resource://gre/modules/devtools/dbg-server.jsm");
+ Components.utils.import("resource://gre/modules/devtools/dbg-client.jsm");
+ 
+ function startServer() {
+   // Start the server.
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   // For an nsIServerSocket we do this:
+   DebuggerServer.openListener(2929); // A connection on port 2929.
+ }
+ 
+ async function startClient() {
+   let transport = await DebuggerClient.socketConnect({ host: "localhost", port: 2929 });
+ 
+@@ -161,20 +157,18 @@ Here is the source code for a complete d
+ Components.utils.import("resource://gre/modules/devtools/dbg-server.jsm");
+ Components.utils.import("resource://gre/modules/devtools/dbg-client.jsm");
+ 
+ let client;
+ let threadClient;
+ 
+ function startDebugger() {
+   // Start the server.
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   // Listen to an nsIPipe
+   let transport = DebuggerServer.connectPipe();
+   // For an nsIServerSocket we do this:
+   // DebuggerServer.openListener(port);
+   // ...and this at the client:
+   // let transport = debuggerSocketConnect(host, port);
+ 
+   // Start the client.
+diff --git a/devtools/server/child.js b/devtools/server/child.js
+--- a/devtools/server/child.js
++++ b/devtools/server/child.js
+@@ -12,19 +12,17 @@ try {
+   // Encapsulate in its own scope to allows loading this frame script more than once.
+   (function () {
+     const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ 
+     const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+     const { dumpn } = DevToolsUtils;
+     const { DebuggerServer, ActorPool } = require("devtools/server/main");
+ 
+-    if (!DebuggerServer.initialized) {
+-      DebuggerServer.init();
+-    }
++    DebuggerServer.init();
+     // We want a special server without any root actor and only tab actors.
+     // We are going to spawn a ContentActor instance in the next few lines,
+     // it is going to act like a root actor without being one.
+     DebuggerServer.registerActors({ root: false, browser: false, tab: true });
+ 
+     let connections = new Map();
+ 
+     let onConnect = DevToolsUtils.makeInfallible(function (msg) {
+diff --git a/devtools/server/content-server.jsm b/devtools/server/content-server.jsm
+--- a/devtools/server/content-server.jsm
++++ b/devtools/server/content-server.jsm
+@@ -22,19 +22,17 @@ function setupServer(mm) {
+ 
+   // Init a custom, invisible DebuggerServer, in order to not pollute the
+   // debugger with all devtools modules, nor break the debugger itself with
+   // using it in the same process.
+   gLoader = new DevToolsLoader();
+   gLoader.invisibleToDebugger = true;
+   let { DebuggerServer } = gLoader.require("devtools/server/main");
+ 
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-  }
++  DebuggerServer.init();
+   // For browser content toolbox, we do need a regular root actor and all tab
+   // actors, but don't need all the "browser actors" that are only useful when
+   // debugging the parent process via the browser toolbox.
+   DebuggerServer.registerActors({ browser: false, root: true, tab: true });
+ 
+   // Clean up things when the client disconnects
+   mm.addMessageListener("debug:content-process-destroy", function onDestroy() {
+     mm.removeMessageListener("debug:content-process-destroy", onDestroy);
+diff --git a/devtools/server/tests/browser/browser_register_actor.js b/devtools/server/tests/browser/browser_register_actor.js
+--- a/devtools/server/tests/browser/browser_register_actor.js
++++ b/devtools/server/tests/browser/browser_register_actor.js
+@@ -2,20 +2,18 @@
+ 
+ var gClient;
+ 
+ function test() {
+   waitForExplicitFinish();
+   let {ActorRegistryFront} = require("devtools/shared/fronts/actor-registry");
+   let actorURL = "chrome://mochitests/content/chrome/devtools/server/tests/mochitest/hello-actor.js";
+ 
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   gClient = new DebuggerClient(DebuggerServer.connectPipe());
+   gClient.connect()
+     .then(() => gClient.listTabs())
+     .then(response => {
+       let options = {
+         prefix: "helloActor",
+         constructor: "HelloActor",
+diff --git a/devtools/server/tests/mochitest/test_connectToChild.html b/devtools/server/tests/mochitest/test_connectToChild.html
+--- a/devtools/server/tests/mochitest/test_connectToChild.html
++++ b/devtools/server/tests/mochitest/test_connectToChild.html
+@@ -41,19 +41,17 @@ function runTests() {
+   // Register a test actor in the child process so that we can know if and when
+   // this fake actor is destroyed.
+   mm.loadFrameScript("data:text/javascript,new " + function FrameScriptScope() {
+     /* eslint-disable no-shadow */
+     const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+     const { DebuggerServer } = require("devtools/server/main");
+     /* eslint-enable no-shadow */
+ 
+-    if (!DebuggerServer.initialized) {
+-      DebuggerServer.init();
+-    }
++    DebuggerServer.init();
+ 
+     function TestActor() {
+       dump("instanciate test actor\n");
+     }
+     TestActor.prototype = {
+       actorPrefix: "test",
+ 
+       destroy: function () {
+@@ -65,19 +63,17 @@ function runTests() {
+     };
+     TestActor.prototype.requestTypes = {
+       "hello": TestActor.prototype.hello
+     };
+     DebuggerServer.addTabActor(TestActor, "testActor");
+   }, false);
+ 
+   // Instantiate a minimal server
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-  }
++  DebuggerServer.init();
+   if (!DebuggerServer.createRootActor) {
+     DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   function firstClient() {
+     // Fake a first connection to an iframe
+     let transport = DebuggerServer.connectPipe();
+     let conn = transport._serverConnection;
+diff --git a/devtools/server/tests/mochitest/test_connection-manager.html b/devtools/server/tests/mochitest/test_connection-manager.html
+--- a/devtools/server/tests/mochitest/test_connection-manager.html
++++ b/devtools/server/tests/mochitest/test_connection-manager.html
+@@ -16,20 +16,18 @@ Bug 898485 - [app manager] Implement an 
+ 
+ window.onload = function () {
+   SimpleTest.waitForExplicitFinish();
+ 
+   const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+   const {DebuggerServer} = require("devtools/server/main");
+   const Services = require("Services");
+ 
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   const {
+     ConnectionManager,
+     Connection
+   } = require("devtools/shared/client/connection-manager");
+ 
+   let orgCount = ConnectionManager.connections.length;
+ 
+diff --git a/devtools/server/tests/mochitest/test_device.html b/devtools/server/tests/mochitest/test_device.html
+--- a/devtools/server/tests/mochitest/test_device.html
++++ b/devtools/server/tests/mochitest/test_device.html
+@@ -19,20 +19,18 @@ window.onload = function () {
+   const {DebuggerClient} = require("devtools/shared/client/debugger-client");
+   const {DebuggerServer} = require("devtools/server/main");
+   const Services = require("Services");
+ 
+   SimpleTest.waitForExplicitFinish();
+ 
+   const {getDeviceFront} = require("devtools/shared/fronts/device");
+ 
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+     client.listTabs(function onListTabs(response) {
+       let d = getDeviceFront(client, response);
+ 
+       let desc;
+       let appInfo = Services.appinfo;
+diff --git a/devtools/server/tests/mochitest/test_framerate_01.html b/devtools/server/tests/mochitest/test_framerate_01.html
+--- a/devtools/server/tests/mochitest/test_framerate_01.html
++++ b/devtools/server/tests/mochitest/test_framerate_01.html
+@@ -60,20 +60,18 @@ window.onload = function () {
+ 
+       frameCount = 0;
+       prevTime = currTime;
+     }
+ 
+     return timeline;
+   }
+ 
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+     client.listTabs(function onListTabs(response) {
+       let form = response.tabs[response.selected];
+       let front = FramerateFront(client, form);
+ 
+       window.setTimeout(() => {
+diff --git a/devtools/server/tests/mochitest/test_framerate_02.html b/devtools/server/tests/mochitest/test_framerate_02.html
+--- a/devtools/server/tests/mochitest/test_framerate_02.html
++++ b/devtools/server/tests/mochitest/test_framerate_02.html
+@@ -60,20 +60,18 @@ window.onload = function () {
+ 
+       frameCount = 0;
+       prevTime = currTime;
+     }
+ 
+     return timeline;
+   }
+ 
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+     client.listTabs(function onListTabs(response) {
+       let form = response.tabs[response.selected];
+       let front = FramerateFront(client, form);
+ 
+       front.stopRecording().then(rawData => {
+diff --git a/devtools/server/tests/mochitest/test_framerate_03.html b/devtools/server/tests/mochitest/test_framerate_03.html
+--- a/devtools/server/tests/mochitest/test_framerate_03.html
++++ b/devtools/server/tests/mochitest/test_framerate_03.html
+@@ -28,20 +28,18 @@ window.onload = function () {
+ 
+   SimpleTest.waitForExplicitFinish();
+ 
+   const {FramerateFront} = require("devtools/shared/fronts/framerate");
+   const START_TICK = 2000;
+   const STOP_TICK = 3000;
+   const TOTAL_TIME = 5000;
+ 
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+     client.listTabs(function onListTabs(response) {
+       let form = response.tabs[response.selected];
+       let front = FramerateFront(client, form);
+ 
+       front.startRecording().then(() => {
+diff --git a/devtools/server/tests/mochitest/test_getProcess.html b/devtools/server/tests/mochitest/test_getProcess.html
+--- a/devtools/server/tests/mochitest/test_getProcess.html
++++ b/devtools/server/tests/mochitest/test_getProcess.html
+@@ -31,19 +31,17 @@ window.onload = function () {
+       // Allows creating a branch new process when creation the iframe
+       ["dom.ipc.processCount", 10],
+     ]
+   }, runTests);
+ };
+ 
+ function runTests() {
+   // Instantiate a minimal server
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-  }
++  DebuggerServer.init();
+   DebuggerServer.allowChromeProcess = true;
+   if (!DebuggerServer.createRootActor) {
+     DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   let client, iframe, processCount;
+ 
+   function connect() {
+diff --git a/devtools/server/tests/mochitest/test_setupInParentChild.html b/devtools/server/tests/mochitest/test_setupInParentChild.html
+--- a/devtools/server/tests/mochitest/test_setupInParentChild.html
++++ b/devtools/server/tests/mochitest/test_setupInParentChild.html
+@@ -33,19 +33,17 @@ window.onload = function () {
+ 
+ function runTests() {
+   // Create a minimal iframe with a message manager
+   let iframe = document.createElement("iframe");
+   iframe.mozbrowser = true;
+   document.body.appendChild(iframe);
+ 
+   // Instantiate a minimal server
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-  }
++  DebuggerServer.init();
+   if (!DebuggerServer.createRootActor) {
+     DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   }
+ 
+   // Fake a connection to an iframe
+   let transport = DebuggerServer.connectPipe();
+   let conn = transport._serverConnection;
+   let client = new DebuggerClient(transport);
+diff --git a/devtools/server/tests/unit/head_dbg.js b/devtools/server/tests/unit/head_dbg.js
+--- a/devtools/server/tests/unit/head_dbg.js
++++ b/devtools/server/tests/unit/head_dbg.js
+@@ -429,20 +429,18 @@ function finishClient(client) {
+     DebuggerServer.destroy();
+     do_test_finished();
+   });
+ }
+ 
+ // Create a server, connect to it and fetch tab actors for the parent process;
+ // pass |callback| the debugger client and tab actor form with all actor IDs.
+ function get_chrome_actors(callback) {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   DebuggerServer.allowChromeProcess = true;
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect()
+     .then(() => client.getProcess())
+     .then(response => {
+       callback(client, response.form);
+     });
+diff --git a/devtools/shared/security/tests/chrome/test_websocket-transport.html b/devtools/shared/security/tests/chrome/test_websocket-transport.html
+--- a/devtools/shared/security/tests/chrome/test_websocket-transport.html
++++ b/devtools/shared/security/tests/chrome/test_websocket-transport.html
+@@ -21,20 +21,18 @@ window.onload = function () {
+   Services.prefs.setBoolPref("devtools.debugger.prompt-connection", false);
+ 
+   SimpleTest.registerCleanupFunction(() => {
+     Services.prefs.clearUserPref("devtools.debugger.remote-enabled");
+     Services.prefs.clearUserPref("devtools.debugger.prompt-connection");
+   });
+ 
+   add_task(function* () {
+-    if (!DebuggerServer.initialized) {
+-      DebuggerServer.init();
+-      DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-    }
++    DebuggerServer.init();
++    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+ 
+     is(DebuggerServer.listeningSockets, 0, "0 listening sockets");
+ 
+     let listener = DebuggerServer.createListener();
+     ok(listener, "Socket listener created");
+     listener.portOrPath = -1;
+     listener.webSocket = true;
+     yield listener.open();
+diff --git a/devtools/shared/webconsole/test/common.js b/devtools/shared/webconsole/test/common.js
+--- a/devtools/shared/webconsole/test/common.js
++++ b/devtools/shared/webconsole/test/common.js
+@@ -19,20 +19,18 @@ const {DebuggerClient} = require("devtoo
+ const ObjectClient = require("devtools/shared/client/object-client");
+ const Services = require("Services");
+ 
+ function initCommon() {
+   // Services.prefs.setBoolPref("devtools.debugger.log", true);
+ }
+ 
+ function initDebuggerServer() {
+-  if (!DebuggerServer.initialized) {
+-    DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+-  }
++  DebuggerServer.init();
++  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
+   DebuggerServer.allowChromeProcess = true;
+ }
+ 
+ function connectToDebugger() {
+   initCommon();
+   initDebuggerServer();
+ 
+   let transport = DebuggerServer.connectPipe();

+ 44 - 0
mozilla-release/patches/1416711-5-59a1.patch

@@ -0,0 +1,44 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1510603619 -3600
+# Node ID 6075d6f91ae732fea71637802f185c20808d14b0
+# Parent  ffb84364d94518dac4276a171c8ced57003e625e
+Bug 1416711 - Remove windowType argument from registerActors;r=ochameau
+
+The windowType option of registerActors has nothing to do with
+registering actors. It is only used in one spot in the codebase, so
+switch this one to DebuggerServer.chromeWindowType = "..."; and
+remove the option from registerActors.
+
+MozReview-Commit-ID: QH6GKmTbVq
+
+diff --git a/devtools/server/main.js b/devtools/server/main.js
+--- a/devtools/server/main.js
++++ b/devtools/server/main.js
+@@ -242,25 +242,18 @@ var DebuggerServer = {
+    *        Registers the root actor from webbrowser module, which is used to
+    *        connect to and fetch any other actor.
+    * @param browser boolean
+    *        Registers all the parent process actors useful for debugging the
+    *        runtime itself, like preferences and addons actors.
+    * @param tab boolean
+    *        Registers all the tab actors like console, script, ... all useful
+    *        for debugging a target context.
+-   * @param windowType string
+-   *        "windowtype" attribute of the main chrome windows. Used by some
+-   *        actors to retrieve them.
+    */
+-  registerActors({ root, browser, tab, windowType }) {
+-    if (windowType) {
+-      this.chromeWindowType = windowType;
+-    }
+-
++  registerActors({ root, browser, tab }) {
+     if (browser) {
+       this._addBrowserActors();
+     }
+ 
+     if (browser || root) {
+       this.registerModule("devtools/server/actors/webbrowser");
+     }
+ 

+ 1777 - 0
mozilla-release/patches/1416711-6-59a1.patch

@@ -0,0 +1,1777 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1510604535 -3600
+# Node ID a2b9ac473749361d0c73fc53aa7fdc6b3c99b8e9
+# Parent  036dc3ecf529d5200a5873edcbb1df9d4f156d8d
+Bug 1416711 - Add registerAllActors API;r=ochameau
+
+Most of the codebase that needs to create a debugger server
+can use a server with all actors registered.
+
+Define an additional method registerAllActors to do that.
+
+By previous implementations, all the call sites that were
+using browser: true were indirectly using tab & root: true
+as well. So all the call sites using browser: true have been
+migrated to registerAllActors and the specific behavior of
+the browser: true case has been removed. Passing browser:true
+to registerActors now only registers browser specific actors.
+
+MozReview-Commit-ID: F3sx71eGrdG
+
+diff --git a/devtools/client/aboutdebugging/initializer.js b/devtools/client/aboutdebugging/initializer.js
+--- a/devtools/client/aboutdebugging/initializer.js
++++ b/devtools/client/aboutdebugging/initializer.js
+@@ -38,17 +38,17 @@ var AboutDebugging = {
+       window.location = "about:devtools?reason=AboutDebugging";
+       return;
+     }
+ 
+     DebuggerServer.init();
+     DebuggerServer.allowChromeProcess = true;
+     // We want a full featured server for about:debugging. Especially the
+     // "browser actors" like addons.
+-    DebuggerServer.registerActors({ root: true, browser: true, tab: true });
++    DebuggerServer.registerAllActors();
+ 
+     this.client = new DebuggerClient(DebuggerServer.connectPipe());
+ 
+     this.client.connect().then(() => {
+       let client = this.client;
+       let telemetry = new Telemetry();
+ 
+       render(AboutDebuggingApp({ client, telemetry }),
+diff --git a/devtools/client/canvasdebugger/test/head.js b/devtools/client/canvasdebugger/test/head.js
+--- a/devtools/client/canvasdebugger/test/head.js
++++ b/devtools/client/canvasdebugger/test/head.js
+@@ -185,17 +185,17 @@ function navigate(aTarget, aUrl, aWaitFo
+ 
+ function reload(aTarget, aWaitForTargetEvent = "navigate") {
+   executeSoon(() => aTarget.activeTab.reload());
+   return once(aTarget, aWaitForTargetEvent);
+ }
+ 
+ function initServer() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ }
+ 
+ function initCallWatcherBackend(aUrl) {
+   info("Initializing a call watcher front.");
+   initServer();
+ 
+   return Task.spawn(function* () {
+     let tab = yield addTab(aUrl);
+diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js
+--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js
++++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js
+@@ -14,17 +14,17 @@ var gNewChromeSource = promise.defer();
+ var { DevToolsLoader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ var customLoader = new DevToolsLoader();
+ customLoader.invisibleToDebugger = true;
+ var { DebuggerServer } = customLoader.require("devtools/server/main");
+ var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+ 
+ function initDebuggerClient() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+   DebuggerServer.allowChromeProcess = true;
+ 
+   let transport = DebuggerServer.connectPipe();
+   return new DebuggerClient(transport);
+ }
+ 
+ function attachThread(client, actor) {
+   return new Promise(resolve => {
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attach.js b/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attach.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attach.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attach.js
+@@ -6,17 +6,17 @@ var WORKER1_URL = "code_WorkerActor.atta
+ var WORKER2_URL = "code_WorkerActor.attach-worker2.js";
+ 
+ function test() {
+   Task.spawn(function* () {
+     let oldMaxTotalViewers = SpecialPowers.getIntPref(MAX_TOTAL_VIEWERS);
+     SpecialPowers.setIntPref(MAX_TOTAL_VIEWERS, 10);
+ 
+     DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++    DebuggerServer.registerAllActors();
+ 
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+     yield connect(client);
+ 
+     let tab = yield addTab(TAB1_URL);
+     let { tabs } = yield listTabs(client);
+     let [, tabClient] = yield attachTab(client, findTab(tabs, TAB1_URL));
+     yield listWorkers(tabClient);
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attachThread.js b/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attachThread.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attachThread.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attachThread.js
+@@ -1,15 +1,15 @@
+ var TAB_URL = EXAMPLE_URL + "doc_WorkerActor.attachThread-tab.html";
+ var WORKER_URL = "code_WorkerActor.attachThread-worker.js";
+ 
+ function test() {
+   Task.spawn(function* () {
+     DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++    DebuggerServer.registerAllActors();
+ 
+     let client1 = new DebuggerClient(DebuggerServer.connectPipe());
+     yield connect(client1);
+     let client2 = new DebuggerClient(DebuggerServer.connectPipe());
+     yield connect(client2);
+ 
+     let tab = yield addTab(TAB_URL);
+     let { tabs: tabs1 } = yield listTabs(client1);
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-01.js b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-01.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-01.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-01.js
+@@ -8,17 +8,17 @@
+  */
+ 
+ const TAB_URL = EXAMPLE_URL + "doc_event-listeners-01.html";
+ 
+ var gClient, gThreadClient, gInput, gButton;
+ 
+ function test() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     addTab(TAB_URL)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-02.js b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-02.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-02.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-02.js
+@@ -9,17 +9,17 @@
+  */
+ 
+ const TAB_URL = EXAMPLE_URL + "doc_event-listeners-03.html";
+ 
+ var gClient, gThreadClient;
+ 
+ function test() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     addTab(TAB_URL)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js b/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js
+@@ -16,17 +16,17 @@ var gNewChromeSource = promise.defer();
+ 
+ var { DevToolsLoader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ var customLoader = new DevToolsLoader();
+ customLoader.invisibleToDebugger = true;
+ var { DebuggerServer } = customLoader.require("devtools/server/main");
+ 
+ function test() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+   DebuggerServer.allowChromeProcess = true;
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_debugger-statement.js b/devtools/client/debugger/test/mochitest/browser_dbg_debugger-statement.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_debugger-statement.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_debugger-statement.js
+@@ -9,17 +9,17 @@
+ 
+ const TAB_URL = EXAMPLE_URL + "doc_inline-debugger-statement.html";
+ 
+ var gClient;
+ var gTab;
+ 
+ function test() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     addTab(TAB_URL)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js
+@@ -9,17 +9,17 @@
+ 
+ const TAB_URL = EXAMPLE_URL + "doc_event-listeners-01.html";
+ 
+ var gClient;
+ var gTab;
+ 
+ function test() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     addTab(TAB_URL)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js
+@@ -10,17 +10,17 @@
+ 
+ const TAB_URL = EXAMPLE_URL + "doc_event-listeners-03.html";
+ 
+ var gClient;
+ var gTab;
+ 
+ function test() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     addTab(TAB_URL)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-03.js b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-03.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-03.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-03.js
+@@ -10,17 +10,17 @@
+ 
+ const TAB_URL = EXAMPLE_URL + "doc_native-event-handler.html";
+ 
+ var gClient;
+ var gTab;
+ 
+ function test() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     addTab(TAB_URL)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js b/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
+@@ -8,17 +8,17 @@
+  */
+ 
+ const ACTORS_URL = CHROME_URL + "testactors.js";
+ 
+ function test() {
+   let gClient;
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   DebuggerServer.addActors(ACTORS_URL);
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js b/devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
+@@ -10,17 +10,17 @@ const ADDON1_ID = "jid1-oBAwBoE5rSecNg@j
+ const ADDON1_PATH = "addon1.xpi";
+ const ADDON2_ID = "jid1-qjtzNGV8xw5h2A@jetpack";
+ const ADDON2_PATH = "addon2.xpi";
+ 
+ var gAddon1, gAddon1Actor, gAddon2, gAddon2Actor, gClient;
+ 
+ function test() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     promise.resolve(null)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
+@@ -9,17 +9,17 @@
+ 
+ const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
+ const TAB2_URL = EXAMPLE_URL + "doc_empty-tab-02.html";
+ 
+ var gTab1, gTab1Actor, gTab2, gTab2Actor, gClient;
+ 
+ function test() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     promise.resolve(null)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
+@@ -21,17 +21,17 @@ var gNewWindow;
+ // Stock onListChanged handler.
+ var onListChangedCount = 0;
+ function onListChangedHandler() {
+   onListChangedCount++;
+ }
+ 
+ function test() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   gTabList = new BrowserTabList("fake DebuggerServerConnection");
+   gTabList._testing = true;
+   gTabList.onListChanged = onListChangedHandler;
+ 
+   checkSingleTab()
+     .then(addTabA)
+     .then(testTabA)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
+@@ -8,17 +8,17 @@
+  */
+ 
+ const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
+ 
+ var gTab1, gTab1Actor, gTab2, gTab2Actor, gClient;
+ 
+ function test() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(Task.async(function* ([aType, aTraits]) {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+     let tab = yield addTab(TAB1_URL);
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_listworkers.js b/devtools/client/debugger/test/mochitest/browser_dbg_listworkers.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_listworkers.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_listworkers.js
+@@ -1,16 +1,16 @@
+ var TAB_URL = EXAMPLE_URL + "doc_listworkers-tab.html";
+ var WORKER1_URL = "code_listworkers-worker1.js";
+ var WORKER2_URL = "code_listworkers-worker2.js";
+ 
+ function test() {
+   Task.spawn(function* () {
+     DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++    DebuggerServer.registerAllActors();
+ 
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+     yield connect(client);
+ 
+     let tab = yield addTab(TAB_URL);
+     let { tabs } = yield listTabs(client);
+     let [, tabClient] = yield attachTab(client, findTab(tabs, TAB_URL));
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js b/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
+@@ -11,17 +11,17 @@
+ const TAB1_URL = EXAMPLE_URL + "doc_script-switching-01.html";
+ const TAB2_URL = EXAMPLE_URL + "doc_script-switching-02.html";
+ 
+ var gNewTab, gNewWindow;
+ var gClient;
+ 
+ function test() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     promise.resolve(null)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_navigation.js b/devtools/client/debugger/test/mochitest/browser_dbg_navigation.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_navigation.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_navigation.js
+@@ -9,17 +9,17 @@
+ 
+ const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
+ const TAB2_URL = EXAMPLE_URL + "doc_empty-tab-02.html";
+ 
+ var gClient;
+ 
+ function test() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+ 
+     addTab(TAB1_URL)
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_promises-allocation-stack.js b/devtools/client/debugger/test/mochitest/browser_dbg_promises-allocation-stack.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-allocation-stack.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-allocation-stack.js
+@@ -11,17 +11,17 @@
+ 
+ const TAB_URL = EXAMPLE_URL + "doc_promise-get-allocation-stack.html";
+ const { PromisesFront } = require("devtools/shared/fronts/promises");
+ var EventEmitter = require("devtools/shared/event-emitter");
+ 
+ function test() {
+   Task.spawn(function* () {
+     DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++    DebuggerServer.registerAllActors();
+ 
+     let options = {
+       source: TAB_URL,
+       line: 1
+     };
+     const [ tab,, panel ] = yield initDebugger(TAB_URL, options);
+ 
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js b/devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js
+@@ -19,17 +19,17 @@ const STACK_DATA = [
+   { functionDisplayName: "testGetAllocationStack" },
+ ];
+ 
+ function test() {
+   Task.spawn(function* () {
+     requestLongerTimeout(10);
+ 
+     DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++    DebuggerServer.registerAllActors();
+     DebuggerServer.allowChromeProcess = true;
+ 
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+     yield connect(client);
+     let chrome = yield client.getProcess();
+     let [, tabClient] = yield attachTab(client, chrome.form);
+     yield tabClient.attachThread();
+ 
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_promises-fulfillment-stack.js b/devtools/client/debugger/test/mochitest/browser_dbg_promises-fulfillment-stack.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-fulfillment-stack.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-fulfillment-stack.js
+@@ -29,17 +29,17 @@ const TEST_DATA = [
+     line: 14,
+     column: 15
+   },
+ ];
+ 
+ function test() {
+   Task.spawn(function* () {
+     DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++    DebuggerServer.registerAllActors();
+ 
+     let options = {
+       source: TAB_URL,
+       line: 1
+     };
+     const [ tab,, panel ] = yield initDebugger(TAB_URL, options);
+ 
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js b/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js
+@@ -36,17 +36,17 @@ const TEST_DATA = [
+     line: 14,
+     column: 15
+   },
+ ];
+ 
+ function test() {
+   Task.spawn(function* () {
+     DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++    DebuggerServer.registerAllActors();
+ 
+     let options = {
+       source: TAB_URL,
+       line: 1
+     };
+     const [ tab,, panel ] = yield initDebugger(TAB_URL, options);
+ 
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-01.js b/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-01.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-01.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-01.js
+@@ -9,17 +9,17 @@
+ 
+ const ACTORS_URL = CHROME_URL + "testactors.js";
+ const TAB_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
+ 
+ var gClient;
+ 
+ function test() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   DebuggerServer.addActors(ACTORS_URL);
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-02.js b/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-02.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-02.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-02.js
+@@ -9,17 +9,17 @@
+ 
+ const ACTORS_URL = CHROME_URL + "testactors.js";
+ const TAB_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
+ 
+ var gClient;
+ 
+ function test() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   DebuggerServer.addActors(ACTORS_URL);
+ 
+   let transport = DebuggerServer.connectPipe();
+   gClient = new DebuggerClient(transport);
+   gClient.connect().then(([aType, aTraits]) => {
+     is(aType, "browser",
+       "Root actor should identify itself as a browser.");
+diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js b/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js
+--- a/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js
++++ b/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js
+@@ -10,17 +10,17 @@ PromiseTestUtils.expectUncaughtRejection
+ 
+ var TAB_URL = EXAMPLE_URL + "doc_WorkerActor.attachThread-tab.html";
+ var WORKER_URL = "code_WorkerActor.attachThread-worker.js";
+ 
+ add_task(function* () {
+   yield pushPrefs(["devtools.scratchpad.enabled", true]);
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   yield connect(client);
+ 
+   let tab = yield addTab(TAB_URL);
+   let { tabs } = yield listTabs(client);
+   let [, tabClient] = yield attachTab(client, findTab(tabs, TAB_URL));
+ 
+diff --git a/devtools/client/debugger/test/mochitest/head.js b/devtools/client/debugger/test/mochitest/head.js
+--- a/devtools/client/debugger/test/mochitest/head.js
++++ b/devtools/client/debugger/test/mochitest/head.js
+@@ -623,17 +623,17 @@ function AddonDebugger() {
+   EventEmitter.decorate(this);
+ }
+ 
+ AddonDebugger.prototype = {
+   init: Task.async(function* (aAddonId) {
+     info("Initializing an addon debugger panel.");
+ 
+     DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++    DebuggerServer.registerAllActors();
+     DebuggerServer.allowChromeProcess = true;
+ 
+     this.frame = document.createElement("iframe");
+     this.frame.setAttribute("height", 400);
+     document.documentElement.appendChild(this.frame);
+     window.addEventListener("message", this._onMessage);
+ 
+     let transport = DebuggerServer.connectPipe();
+@@ -1318,17 +1318,17 @@ function waitForDispatch(panel, type, ev
+       count++;
+       info(type + " dispatched " + count + " time(s)");
+     }
+   });
+ }
+ 
+ function* initWorkerDebugger(TAB_URL, WORKER_URL) {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   yield connect(client);
+ 
+   let tab = yield addTab(TAB_URL);
+   let { tabs } = yield listTabs(client);
+   let [, tabClient] = yield attachTab(client, findTab(tabs, TAB_URL));
+ 
+diff --git a/devtools/client/framework/ToolboxProcess.jsm b/devtools/client/framework/ToolboxProcess.jsm
+--- a/devtools/client/framework/ToolboxProcess.jsm
++++ b/devtools/client/framework/ToolboxProcess.jsm
+@@ -135,17 +135,17 @@ BrowserToolboxProcess.prototype = {
+ 
+     // Forward interesting events.
+     this.debuggerServer.on("connectionchange", this._onConnectionChange);
+ 
+     this.debuggerServer.init();
+     // We mainly need a root actor and tab actors for opening a toolbox, even
+     // against chrome/content/addon. But the "no auto hide" button uses the
+     // preference actor, so also register the browser actors.
+-    this.debuggerServer.registerActors({ root: true, browser: true, tab: true });
++    this.debuggerServer.registerAllActors();
+     this.debuggerServer.allowChromeProcess = true;
+     dumpn("initialized and added the browser actors for the DebuggerServer.");
+ 
+     let chromeDebuggingWebSocket =
+       Services.prefs.getBoolPref("devtools.debugger.chrome-debugging-websocket");
+     let listener = this.debuggerServer.createListener();
+     listener.portOrPath = -1;
+     listener.webSocket = chromeDebuggingWebSocket;
+diff --git a/devtools/client/framework/devtools-browser.js b/devtools/client/framework/devtools-browser.js
+--- a/devtools/client/framework/devtools-browser.js
++++ b/devtools/client/framework/devtools-browser.js
+@@ -290,17 +290,17 @@ var gDevToolsBrowser = exports.gDevTools
+    // Used by browser-sets.inc, command
+   openConnectScreen(gBrowser) {
+     gBrowser.selectedTab = gBrowser.addTab("chrome://devtools/content/framework/connect/connect.xhtml");
+   },
+ 
+   _getContentProcessTarget(processId) {
+     // Create a DebuggerServer in order to connect locally to it
+     DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++    DebuggerServer.registerAllActors();
+     DebuggerServer.allowChromeProcess = true;
+ 
+     let transport = DebuggerServer.connectPipe();
+     let client = new DebuggerClient(transport);
+ 
+     let deferred = defer();
+     client.connect().then(() => {
+       client.getProcess(processId)
+diff --git a/devtools/client/framework/target-from-url.js b/devtools/client/framework/target-from-url.js
+--- a/devtools/client/framework/target-from-url.js
++++ b/devtools/client/framework/target-from-url.js
+@@ -128,13 +128,13 @@ function* createClient(params) {
+   let webSocket = !!params.get("ws");
+ 
+   let transport;
+   if (port) {
+     transport = yield DebuggerClient.socketConnect({ host, port, webSocket });
+   } else {
+     // Setup a server if we don't have one already running
+     DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++    DebuggerServer.registerAllActors();
+     transport = DebuggerServer.connectPipe();
+   }
+   return new DebuggerClient(transport);
+ }
+diff --git a/devtools/client/framework/target.js b/devtools/client/framework/target.js
+--- a/devtools/client/framework/target.js
++++ b/devtools/client/framework/target.js
+@@ -406,17 +406,18 @@ TabTarget.prototype = {
+       // DebuggerServer here, once and for all tools.
+       DebuggerServer.init();
+ 
+       // When connecting to a local tab, we only need the root actor.
+       // Then we are going to call DebuggerServer.connectToChild and talk
+       // directly with actors living in the child process.
+       // We also need browser actors for actor registry which enabled addons
+       // to register custom actors.
+-      DebuggerServer.registerActors({ root: true, browser: true, tab: false });
++      // TODO: the comment and implementation are out of sync here. See Bug 1420134.
++      DebuggerServer.registerAllActors();
+ 
+       this._client = new DebuggerClient(DebuggerServer.connectPipe());
+       // A local TabTarget will never perform chrome debugging.
+       this._chrome = false;
+     } else if (this._form.isWebExtension &&
+           this.client.mainRoot.traits.webExtensionAddonConnect) {
+       // The addonActor form is related to a WebExtensionParentActor instance,
+       // which isn't a tab actor on its own, it is an actor living in the parent process
+diff --git a/devtools/client/framework/test/browser_target_from_url.js b/devtools/client/framework/test/browser_target_from_url.js
+--- a/devtools/client/framework/test/browser_target_from_url.js
++++ b/devtools/client/framework/test/browser_target_from_url.js
+@@ -76,17 +76,17 @@ add_task(function* () {
+ });
+ 
+ function* setupDebuggerServer(websocket) {
+   info("Create a separate loader instance for the DebuggerServer.");
+   let loader = new DevToolsLoader();
+   let { DebuggerServer } = loader.require("devtools/server/main");
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+   DebuggerServer.allowChromeProcess = true;
+ 
+   let listener = DebuggerServer.createListener();
+   ok(listener, "Socket listener created");
+   // Pass -1 to automatically choose an available port
+   listener.portOrPath = -1;
+   listener.webSocket = websocket;
+   yield listener.open();
+diff --git a/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js b/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
+--- a/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
++++ b/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
+@@ -59,17 +59,17 @@ function runTools(target) {
+     yield toolbox.destroy();
+   });
+ }
+ 
+ function getClient() {
+   let deferred = defer();
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let transport = DebuggerServer.connectPipe();
+   let client = new DebuggerClient(transport);
+ 
+   return client.connect().then(() => client);
+ }
+ 
+ function getTarget(client) {
+diff --git a/devtools/client/framework/test/browser_two_tabs.js b/devtools/client/framework/test/browser_two_tabs.js
+--- a/devtools/client/framework/test/browser_two_tabs.js
++++ b/devtools/client/framework/test/browser_two_tabs.js
+@@ -16,17 +16,17 @@ const TAB_URL_2 = "data:text/html;charse
+ var gClient;
+ var gTab1, gTab2;
+ var gTabActor1, gTabActor2;
+ 
+ function test() {
+   waitForExplicitFinish();
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   openTabs();
+ }
+ 
+ function openTabs() {
+   // Open two tabs, select the second
+   addTab(TAB_URL_1).then(tab1 => {
+     gTab1 = tab1;
+diff --git a/devtools/client/framework/test/head.js b/devtools/client/framework/test/head.js
+--- a/devtools/client/framework/test/head.js
++++ b/devtools/client/framework/test/head.js
+@@ -24,17 +24,17 @@ function toggleAllTools(state) {
+ }
+ 
+ function getChromeActors(callback)
+ {
+   let { DebuggerServer } = require("devtools/server/main");
+   let { DebuggerClient } = require("devtools/shared/client/debugger-client");
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+   DebuggerServer.allowChromeProcess = true;
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect()
+     .then(() => client.getProcess())
+     .then(response => {
+       callback(client, response.form);
+     });
+diff --git a/devtools/client/framework/toolbox-init.js b/devtools/client/framework/toolbox-init.js
+--- a/devtools/client/framework/toolbox-init.js
++++ b/devtools/client/framework/toolbox-init.js
+@@ -64,17 +64,17 @@ if (url.search.length > 1) {
+       // devtools codebase
+       iframe = XPCNativeWrapper(iframe);
+ 
+       // Fake a xul:tab object as we don't have one.
+       // linkedBrowser is the only one attribute being queried by client.getTab
+       let tab = { linkedBrowser: iframe };
+ 
+       DebuggerServer.init();
+-      DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++      DebuggerServer.registerAllActors();
+       let client = new DebuggerClient(DebuggerServer.connectPipe());
+ 
+       yield client.connect();
+       // Creates a target for a given browser iframe.
+       let response = yield client.getTab({ tab });
+       let form = response.tab;
+       target = yield TargetFactory.forRemoteTab({client, form, chrome: false});
+     } else {
+diff --git a/devtools/client/responsive.html/manager.js b/devtools/client/responsive.html/manager.js
+--- a/devtools/client/responsive.html/manager.js
++++ b/devtools/client/responsive.html/manager.js
+@@ -416,17 +416,17 @@ ResponsiveUI.prototype = {
+ 
+     this.destroyed = true;
+ 
+     return true;
+   }),
+ 
+   connectToServer: Task.async(function* () {
+     DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++    DebuggerServer.registerAllActors();
+     this.client = new DebuggerClient(DebuggerServer.connectPipe());
+     yield this.client.connect();
+     let { tab } = yield this.client.getTab();
+     this.emulationFront = EmulationFront(this.client, tab);
+   }),
+ 
+   handleEvent(event) {
+     let { browserWindow, tab } = this;
+diff --git a/devtools/client/scratchpad/scratchpad.js b/devtools/client/scratchpad/scratchpad.js
+--- a/devtools/client/scratchpad/scratchpad.js
++++ b/devtools/client/scratchpad/scratchpad.js
+@@ -2178,17 +2178,17 @@ ScratchpadWindow.prototype = Heritage.ex
+    * Attach to this window.
+    *
+    * @return Promise
+    *         The promise for the target for this window.
+    */
+   _attach: function SW__attach()
+   {
+     DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++    DebuggerServer.registerAllActors();
+     DebuggerServer.allowChromeProcess = true;
+ 
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+     return client.connect()
+       .then(() => client.getProcess())
+       .then(aResponse => {
+         return { form: aResponse.form, client: client };
+       });
+diff --git a/devtools/client/shadereditor/test/head.js b/devtools/client/shadereditor/test/head.js
+--- a/devtools/client/shadereditor/test/head.js
++++ b/devtools/client/shadereditor/test/head.js
+@@ -220,17 +220,17 @@ function reload(aTarget, aWaitForTargetE
+   executeSoon(() => aTarget.activeTab.reload());
+   return once(aTarget, aWaitForTargetEvent);
+ }
+ 
+ function initBackend(aUrl) {
+   info("Initializing a shader editor front.");
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   return Task.spawn(function* () {
+     let tab = yield addTab(aUrl);
+     let target = TargetFactory.forTab(tab);
+ 
+     yield target.makeRemote();
+ 
+     let front = new WebGLFront(target.client, target.form);
+diff --git a/devtools/client/shared/test/test-actor-registry.js b/devtools/client/shared/test/test-actor-registry.js
+--- a/devtools/client/shared/test/test-actor-registry.js
++++ b/devtools/client/shared/test/test-actor-registry.js
+@@ -61,17 +61,17 @@
+   // create a front for the given `tab`
+   exports.getTestActorWithoutToolbox = Task.async(function* (tab) {
+     let { DebuggerServer } = require("devtools/server/main");
+     let { DebuggerClient } = require("devtools/shared/client/debugger-client");
+ 
+     // We need to spawn a client instance,
+     // but for that we have to first ensure a server is running
+     DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++    DebuggerServer.registerAllActors();
+     let client = new DebuggerClient(DebuggerServer.connectPipe());
+ 
+     yield client.connect();
+ 
+     // We also need to make sure the test actor is registered on the server.
+     yield exports.registerTestActor(client);
+ 
+     return getTestActor(client, tab);
+diff --git a/devtools/client/webaudioeditor/test/head.js b/devtools/client/webaudioeditor/test/head.js
+--- a/devtools/client/webaudioeditor/test/head.js
++++ b/devtools/client/webaudioeditor/test/head.js
+@@ -155,17 +155,17 @@ function loadFrameScripts() {
+ /**
+  * Adds a new tab, and instantiate a WebAudiFront object.
+  * This requires calling removeTab before the test ends.
+  */
+ function initBackend(aUrl) {
+   info("Initializing a web audio editor front.");
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   return Task.spawn(function* () {
+     let tab = yield addTab(aUrl);
+     let target = TargetFactory.forTab(tab);
+ 
+     yield target.makeRemote();
+ 
+     let front = new WebAudioFront(target.client, target.form);
+diff --git a/devtools/client/webconsole/hudservice.js b/devtools/client/webconsole/hudservice.js
+--- a/devtools/client/webconsole/hudservice.js
++++ b/devtools/client/webconsole/hudservice.js
+@@ -187,17 +187,17 @@ HUD_SERVICE.prototype =
+     function connect()
+     {
+       let deferred = defer();
+ 
+       // Ensure that the root actor and the tab actors have been registered on the DebuggerServer,
+       // so that the Browser Console can retrieve the console actors.
+       // (See Bug 1416105 for rationale).
+       DebuggerServer.init();
+-      DebuggerServer.registerActors({ root: true, browser: false, tab: true });
++      DebuggerServer.registerActors({ root: true, tab: true });
+ 
+       DebuggerServer.allowChromeProcess = true;
+ 
+       let client = new DebuggerClient(DebuggerServer.connectPipe());
+       return client.connect()
+         .then(() => client.getProcess())
+         .then(aResponse => {
+           // Use a TabActor in order to ensure calling `attach` to the ChromeActor
+diff --git a/devtools/docs/backend/client-api.md b/devtools/docs/backend/client-api.md
+--- a/devtools/docs/backend/client-api.md
++++ b/devtools/docs/backend/client-api.md
+@@ -8,17 +8,17 @@ In order to communicate, a client and a 
+ 
+ ```javascript
+ Components.utils.import("resource://gre/modules/devtools/dbg-server.jsm");
+ Components.utils.import("resource://gre/modules/devtools/dbg-client.jsm");
+ 
+ function start() {
+   // Start the server.
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   // Listen to an nsIPipe
+   let transport = DebuggerServer.connectPipe();
+ 
+   // Start the client.
+   client = new DebuggerClient(transport);
+ 
+   // Attach listeners for client events.
+@@ -35,17 +35,17 @@ If a TCP socket is required, the functio
+ 
+ ```javascript
+ Components.utils.import("resource://gre/modules/devtools/dbg-server.jsm");
+ Components.utils.import("resource://gre/modules/devtools/dbg-client.jsm");
+ 
+ function startServer() {
+   // Start the server.
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   // For an nsIServerSocket we do this:
+   DebuggerServer.openListener(2929); // A connection on port 2929.
+ }
+ 
+ async function startClient() {
+   let transport = await DebuggerClient.socketConnect({ host: "localhost", port: 2929 });
+ 
+@@ -158,17 +158,17 @@ Components.utils.import("resource://gre/
+ Components.utils.import("resource://gre/modules/devtools/dbg-client.jsm");
+ 
+ let client;
+ let threadClient;
+ 
+ function startDebugger() {
+   // Start the server.
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+   // Listen to an nsIPipe
+   let transport = DebuggerServer.connectPipe();
+   // For an nsIServerSocket we do this:
+   // DebuggerServer.openListener(port);
+   // ...and this at the client:
+   // let transport = debuggerSocketConnect(host, port);
+ 
+   // Start the client.
+diff --git a/devtools/server/child.js b/devtools/server/child.js
+--- a/devtools/server/child.js
++++ b/devtools/server/child.js
+@@ -16,17 +16,17 @@ try {
+     const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+     const { dumpn } = DevToolsUtils;
+     const { DebuggerServer, ActorPool } = require("devtools/server/main");
+ 
+     DebuggerServer.init();
+     // We want a special server without any root actor and only tab actors.
+     // We are going to spawn a ContentActor instance in the next few lines,
+     // it is going to act like a root actor without being one.
+-    DebuggerServer.registerActors({ root: false, browser: false, tab: true });
++    DebuggerServer.registerActors({ tab: true });
+ 
+     let connections = new Map();
+ 
+     let onConnect = DevToolsUtils.makeInfallible(function (msg) {
+       removeMessageListener("debug:connect", onConnect);
+ 
+       let mm = msg.target;
+       let prefix = msg.data.prefix;
+diff --git a/devtools/server/content-server.jsm b/devtools/server/content-server.jsm
+--- a/devtools/server/content-server.jsm
++++ b/devtools/server/content-server.jsm
+@@ -26,17 +26,17 @@ function setupServer(mm) {
+   gLoader = new DevToolsLoader();
+   gLoader.invisibleToDebugger = true;
+   let { DebuggerServer } = gLoader.require("devtools/server/main");
+ 
+   DebuggerServer.init();
+   // For browser content toolbox, we do need a regular root actor and all tab
+   // actors, but don't need all the "browser actors" that are only useful when
+   // debugging the parent process via the browser toolbox.
+-  DebuggerServer.registerActors({ browser: false, root: true, tab: true });
++  DebuggerServer.registerActors({ root: true, tab: true });
+ 
+   // Clean up things when the client disconnects
+   mm.addMessageListener("debug:content-process-destroy", function onDestroy() {
+     mm.removeMessageListener("debug:content-process-destroy", onDestroy);
+ 
+     DebuggerServer.destroy();
+     gLoader.destroy();
+     gLoader = null;
+diff --git a/devtools/server/main.js b/devtools/server/main.js
+--- a/devtools/server/main.js
++++ b/devtools/server/main.js
+@@ -230,17 +230,17 @@ var DebuggerServer = {
+ 
+     if (!this.rootlessServer && !this.createRootActor) {
+       throw new Error("Use DebuggerServer.addActors() to add a root actor " +
+                       "implementation.");
+     }
+   },
+ 
+   /**
+-   * Register all type of actors. Only register the one that are not already
++   * Register different type of actors. Only register the one that are not already
+    * registered.
+    *
+    * @param root boolean
+    *        Registers the root actor from webbrowser module, which is used to
+    *        connect to and fetch any other actor.
+    * @param browser boolean
+    *        Registers all the parent process actors useful for debugging the
+    *        runtime itself, like preferences and addons actors.
+@@ -248,26 +248,33 @@ var DebuggerServer = {
+    *        Registers all the tab actors like console, script, ... all useful
+    *        for debugging a target context.
+    */
+   registerActors({ root, browser, tab }) {
+     if (browser) {
+       this._addBrowserActors();
+     }
+ 
+-    if (browser || root) {
++    if (root) {
+       this.registerModule("devtools/server/actors/webbrowser");
+     }
+ 
+-    if (browser || tab) {
++    if (tab) {
+       this._addTabActors();
+     }
+   },
+ 
+   /**
++   * Register all possible actors for this DebuggerServer.
++   */
++  registerAllActors() {
++    this.registerActors({ root: true, browser: true, tab: true });
++  },
++
++  /**
+    * Load a subscript into the debugging global.
+    *
+    * @param url string A url that will be loaded as a subscript into the
+    *        debugging global.  The user must load at least one script
+    *        that implements a createRootActor() function to create the
+    *        server's root actor.
+    */
+   addActors(url) {
+diff --git a/devtools/server/tests/browser/browser_register_actor.js b/devtools/server/tests/browser/browser_register_actor.js
+--- a/devtools/server/tests/browser/browser_register_actor.js
++++ b/devtools/server/tests/browser/browser_register_actor.js
+@@ -3,17 +3,17 @@
+ var gClient;
+ 
+ function test() {
+   waitForExplicitFinish();
+   let {ActorRegistryFront} = require("devtools/shared/fronts/actor-registry");
+   let actorURL = "chrome://mochitests/content/chrome/devtools/server/tests/mochitest/hello-actor.js";
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   gClient = new DebuggerClient(DebuggerServer.connectPipe());
+   gClient.connect()
+     .then(() => gClient.listTabs())
+     .then(response => {
+       let options = {
+         prefix: "helloActor",
+         constructor: "HelloActor",
+diff --git a/devtools/server/tests/browser/head.js b/devtools/server/tests/browser/head.js
+--- a/devtools/server/tests/browser/head.js
++++ b/devtools/server/tests/browser/head.js
+@@ -96,17 +96,17 @@ function initDebuggerServer() {
+   try {
+     // Sometimes debugger server does not get destroyed correctly by previous
+     // tests.
+     DebuggerServer.destroy();
+   } catch (e) {
+     info(`DebuggerServer destroy error: ${e}\n${e.stack}`);
+   }
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ }
+ 
+ /**
+  * Connect a debugger client.
+  * @param {DebuggerClient}
+  * @return {Promise} Resolves to the selected tabActor form when the client is
+  * connected.
+  */
+diff --git a/devtools/server/tests/mochitest/inspector-helpers.js b/devtools/server/tests/mochitest/inspector-helpers.js
+--- a/devtools/server/tests/mochitest/inspector-helpers.js
++++ b/devtools/server/tests/mochitest/inspector-helpers.js
+@@ -18,17 +18,17 @@ const {_documentWalker} = require("devto
+ // Always log packets when running tests.
+ Services.prefs.setBoolPref("devtools.debugger.log", true);
+ SimpleTest.registerCleanupFunction(function () {
+   Services.prefs.clearUserPref("devtools.debugger.log");
+ });
+ 
+ if (!DebuggerServer.initialized) {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+   SimpleTest.registerCleanupFunction(function () {
+     DebuggerServer.destroy();
+   });
+ }
+ 
+ var gAttachCleanups = [];
+ 
+ SimpleTest.registerCleanupFunction(function () {
+diff --git a/devtools/server/tests/mochitest/memory-helpers.js b/devtools/server/tests/mochitest/memory-helpers.js
+--- a/devtools/server/tests/mochitest/memory-helpers.js
++++ b/devtools/server/tests/mochitest/memory-helpers.js
+@@ -16,17 +16,17 @@ var gReduceTimePrecision = Services.pref
+ Services.prefs.setBoolPref("privacy.reduceTimerPrecision", false);
+ SimpleTest.registerCleanupFunction(function () {
+   Services.prefs.clearUserPref("devtools.debugger.log");
+   Services.prefs.setBoolPref("privacy.reduceTimerPrecision", gReduceTimePrecision);
+ });
+ 
+ function startServerAndGetSelectedTabMemory() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+ 
+   return client.connect()
+     .then(() => client.listTabs())
+     .then(response => {
+       let form = response.tabs[response.selected];
+       let memory = MemoryFront(client, form, response);
+ 
+diff --git a/devtools/server/tests/mochitest/test_connectToChild.html b/devtools/server/tests/mochitest/test_connectToChild.html
+--- a/devtools/server/tests/mochitest/test_connectToChild.html
++++ b/devtools/server/tests/mochitest/test_connectToChild.html
+@@ -65,17 +65,17 @@ function runTests() {
+       "hello": TestActor.prototype.hello
+     };
+     DebuggerServer.addTabActor(TestActor, "testActor");
+   }, false);
+ 
+   // Instantiate a minimal server
+   DebuggerServer.init();
+   if (!DebuggerServer.createRootActor) {
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++    DebuggerServer.registerAllActors();
+   }
+ 
+   function firstClient() {
+     // Fake a first connection to an iframe
+     let transport = DebuggerServer.connectPipe();
+     let conn = transport._serverConnection;
+     let client = new DebuggerClient(transport);
+     DebuggerServer.connectToChild(conn, iframe).then(actor => {
+diff --git a/devtools/server/tests/mochitest/test_connection-manager.html b/devtools/server/tests/mochitest/test_connection-manager.html
+--- a/devtools/server/tests/mochitest/test_connection-manager.html
++++ b/devtools/server/tests/mochitest/test_connection-manager.html
+@@ -17,17 +17,17 @@ Bug 898485 - [app manager] Implement an 
+ window.onload = function () {
+   SimpleTest.waitForExplicitFinish();
+ 
+   const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+   const {DebuggerServer} = require("devtools/server/main");
+   const Services = require("Services");
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   const {
+     ConnectionManager,
+     Connection
+   } = require("devtools/shared/client/connection-manager");
+ 
+   let orgCount = ConnectionManager.connections.length;
+ 
+diff --git a/devtools/server/tests/mochitest/test_device.html b/devtools/server/tests/mochitest/test_device.html
+--- a/devtools/server/tests/mochitest/test_device.html
++++ b/devtools/server/tests/mochitest/test_device.html
+@@ -20,17 +20,17 @@ window.onload = function () {
+   const {DebuggerServer} = require("devtools/server/main");
+   const Services = require("Services");
+ 
+   SimpleTest.waitForExplicitFinish();
+ 
+   const {getDeviceFront} = require("devtools/shared/fronts/device");
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+     client.listTabs(function onListTabs(response) {
+       let d = getDeviceFront(client, response);
+ 
+       let desc;
+       let appInfo = Services.appinfo;
+diff --git a/devtools/server/tests/mochitest/test_framerate_01.html b/devtools/server/tests/mochitest/test_framerate_01.html
+--- a/devtools/server/tests/mochitest/test_framerate_01.html
++++ b/devtools/server/tests/mochitest/test_framerate_01.html
+@@ -61,17 +61,17 @@ window.onload = function () {
+       frameCount = 0;
+       prevTime = currTime;
+     }
+ 
+     return timeline;
+   }
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+     client.listTabs(function onListTabs(response) {
+       let form = response.tabs[response.selected];
+       let front = FramerateFront(client, form);
+ 
+       window.setTimeout(() => {
+diff --git a/devtools/server/tests/mochitest/test_framerate_02.html b/devtools/server/tests/mochitest/test_framerate_02.html
+--- a/devtools/server/tests/mochitest/test_framerate_02.html
++++ b/devtools/server/tests/mochitest/test_framerate_02.html
+@@ -61,17 +61,17 @@ window.onload = function () {
+       frameCount = 0;
+       prevTime = currTime;
+     }
+ 
+     return timeline;
+   }
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+     client.listTabs(function onListTabs(response) {
+       let form = response.tabs[response.selected];
+       let front = FramerateFront(client, form);
+ 
+       front.stopRecording().then(rawData => {
+diff --git a/devtools/server/tests/mochitest/test_framerate_03.html b/devtools/server/tests/mochitest/test_framerate_03.html
+--- a/devtools/server/tests/mochitest/test_framerate_03.html
++++ b/devtools/server/tests/mochitest/test_framerate_03.html
+@@ -29,17 +29,17 @@ window.onload = function () {
+   SimpleTest.waitForExplicitFinish();
+ 
+   const {FramerateFront} = require("devtools/shared/fronts/framerate");
+   const START_TICK = 2000;
+   const STOP_TICK = 3000;
+   const TOTAL_TIME = 5000;
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+     client.listTabs(function onListTabs(response) {
+       let form = response.tabs[response.selected];
+       let front = FramerateFront(client, form);
+ 
+       front.startRecording().then(() => {
+diff --git a/devtools/server/tests/mochitest/test_framerate_05.html b/devtools/server/tests/mochitest/test_framerate_05.html
+--- a/devtools/server/tests/mochitest/test_framerate_05.html
++++ b/devtools/server/tests/mochitest/test_framerate_05.html
+@@ -26,17 +26,17 @@ window.onload = function () {
+     Services.prefs.clearUserPref("devtools.debugger.log");
+   });
+ 
+   SimpleTest.waitForExplicitFinish();
+ 
+   const {FramerateFront} = require("devtools/shared/fronts/framerate");
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+     client.listTabs(function onListTabs(response) {
+       let form = response.tabs[response.selected];
+       let front = FramerateFront(client, form);
+ 
+       front.startRecording().then(() => {
+diff --git a/devtools/server/tests/mochitest/test_getProcess.html b/devtools/server/tests/mochitest/test_getProcess.html
+--- a/devtools/server/tests/mochitest/test_getProcess.html
++++ b/devtools/server/tests/mochitest/test_getProcess.html
+@@ -34,17 +34,17 @@ window.onload = function () {
+   }, runTests);
+ };
+ 
+ function runTests() {
+   // Instantiate a minimal server
+   DebuggerServer.init();
+   DebuggerServer.allowChromeProcess = true;
+   if (!DebuggerServer.createRootActor) {
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++    DebuggerServer.registerAllActors();
+   }
+ 
+   let client, iframe, processCount;
+ 
+   function connect() {
+     // Fake a first connection to the content process
+     let transport = DebuggerServer.connectPipe();
+     client = new DebuggerClient(transport);
+diff --git a/devtools/server/tests/mochitest/test_preference.html b/devtools/server/tests/mochitest/test_preference.html
+--- a/devtools/server/tests/mochitest/test_preference.html
++++ b/devtools/server/tests/mochitest/test_preference.html
+@@ -20,17 +20,17 @@ function runTests() {
+   let {DebuggerServer} = require("devtools/server/main");
+   let Services = require("Services");
+ 
+   SimpleTest.waitForExplicitFinish();
+ 
+   let {getPreferenceFront} = require("devtools/shared/fronts/preference");
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function onConnect() {
+     client.listTabs(function onListTabs(response) {
+       let p = getPreferenceFront(client, response);
+ 
+       let prefs = {};
+ 
+diff --git a/devtools/server/tests/mochitest/test_setupInParentChild.html b/devtools/server/tests/mochitest/test_setupInParentChild.html
+--- a/devtools/server/tests/mochitest/test_setupInParentChild.html
++++ b/devtools/server/tests/mochitest/test_setupInParentChild.html
+@@ -35,17 +35,17 @@ function runTests() {
+   // Create a minimal iframe with a message manager
+   let iframe = document.createElement("iframe");
+   iframe.mozbrowser = true;
+   document.body.appendChild(iframe);
+ 
+   // Instantiate a minimal server
+   DebuggerServer.init();
+   if (!DebuggerServer.createRootActor) {
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++    DebuggerServer.registerAllActors();
+   }
+ 
+   // Fake a connection to an iframe
+   let transport = DebuggerServer.connectPipe();
+   let conn = transport._serverConnection;
+   let client = new DebuggerClient(transport);
+ 
+   // Wait for a response from setupInChild
+diff --git a/devtools/server/tests/mochitest/webconsole-helpers.js b/devtools/server/tests/mochitest/webconsole-helpers.js
+--- a/devtools/server/tests/mochitest/webconsole-helpers.js
++++ b/devtools/server/tests/mochitest/webconsole-helpers.js
+@@ -10,17 +10,17 @@ const Services = require("Services");
+ // Always log packets when running tests.
+ Services.prefs.setBoolPref("devtools.debugger.log", true);
+ SimpleTest.registerCleanupFunction(function () {
+   Services.prefs.clearUserPref("devtools.debugger.log");
+ });
+ 
+ if (!DebuggerServer.initialized) {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+   SimpleTest.registerCleanupFunction(function () {
+     DebuggerServer.destroy();
+   });
+ }
+ 
+ /**
+  * Open a tab, load the url, find the tab with the debugger server,
+  * and attach the console to it.
+diff --git a/devtools/server/tests/mochitest/webextension-helpers.js b/devtools/server/tests/mochitest/webextension-helpers.js
+--- a/devtools/server/tests/mochitest/webextension-helpers.js
++++ b/devtools/server/tests/mochitest/webextension-helpers.js
+@@ -16,17 +16,17 @@ const {flushJarCache} = require("resourc
+ const {Services} = require("resource://gre/modules/Services.jsm");
+ 
+ loader.lazyImporter(this, "ExtensionParent", "resource://gre/modules/ExtensionParent.jsm");
+ loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
+ 
+ // Initialize a minimal DebuggerServer and connect to the webextension addon actor.
+ if (!DebuggerServer.initialized) {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+   SimpleTest.registerCleanupFunction(function () {
+     DebuggerServer.destroy();
+   });
+ }
+ 
+ SimpleTest.registerCleanupFunction(function () {
+   const {hiddenXULWindow} = ExtensionParent.DebugUtils;
+   const debugBrowserMapSize = ExtensionParent.DebugUtils.debugBrowserPromises.size;
+diff --git a/devtools/server/tests/unit/head_dbg.js b/devtools/server/tests/unit/head_dbg.js
+--- a/devtools/server/tests/unit/head_dbg.js
++++ b/devtools/server/tests/unit/head_dbg.js
+@@ -411,17 +411,17 @@ function initTestDebuggerServer(server =
+ }
+ 
+ /**
+  * Initialize the testing debugger server with a tab whose title is |title|.
+  */
+ function startTestDebuggerServer(title, server = DebuggerServer) {
+   initTestDebuggerServer(server);
+   addTestGlobal(title);
+-  DebuggerServer.registerActors({ browser: false, root: false, tab: true });
++  DebuggerServer.registerActors({ tab: true });
+ 
+   let transport = DebuggerServer.connectPipe();
+   let client = new DebuggerClient(transport);
+ 
+   return connect(client).then(() => client);
+ }
+ 
+ function finishClient(client) {
+@@ -430,17 +430,17 @@ function finishClient(client) {
+     do_test_finished();
+   });
+ }
+ 
+ // Create a server, connect to it and fetch tab actors for the parent process;
+ // pass |callback| the debugger client and tab actor form with all actor IDs.
+ function get_chrome_actors(callback) {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+   DebuggerServer.allowChromeProcess = true;
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect()
+     .then(() => client.getProcess())
+     .then(response => {
+       callback(client, response.form);
+     });
+diff --git a/devtools/server/tests/unit/test_actor-registry-actor.js b/devtools/server/tests/unit/test_actor-registry-actor.js
+--- a/devtools/server/tests/unit/test_actor-registry-actor.js
++++ b/devtools/server/tests/unit/test_actor-registry-actor.js
+@@ -13,17 +13,17 @@ var gActorFront;
+ var gOldPref;
+ 
+ const { ActorRegistryFront } = require("devtools/shared/fronts/actor-registry");
+ 
+ function run_test() {
+   gOldPref = Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps");
+   Services.prefs.setBoolPref("devtools.debugger.forbid-certified-apps", false);
+   initTestDebuggerServer();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+   gClient = new DebuggerClient(DebuggerServer.connectPipe());
+   gClient.connect().then(getRegistry);
+   do_test_pending();
+ }
+ 
+ function getRegistry() {
+   gClient.listTabs((response) => {
+     gRegistryFront = ActorRegistryFront(gClient, response);
+diff --git a/devtools/server/tests/unit/test_add_actors.js b/devtools/server/tests/unit/test_add_actors.js
+--- a/devtools/server/tests/unit/test_add_actors.js
++++ b/devtools/server/tests/unit/test_add_actors.js
+@@ -13,17 +13,17 @@ var gActors;
+  * in order to add actors after initialization but rather can add actors anytime
+  * regardless of the object's state.
+  */
+ function run_test() {
+   DebuggerServer.addActors("resource://test/pre_init_global_actors.js");
+   DebuggerServer.addActors("resource://test/pre_init_tab_actors.js");
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   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);
+diff --git a/devtools/server/tests/unit/test_client_request.js b/devtools/server/tests/unit/test_client_request.js
+--- a/devtools/server/tests/unit/test_client_request.js
++++ b/devtools/server/tests/unit/test_client_request.js
+@@ -25,17 +25,17 @@ TestActor.prototype.requestTypes = {
+   "hello": TestActor.prototype.hello,
+   "error": TestActor.prototype.error
+ };
+ 
+ function run_test() {
+   DebuggerServer.addGlobalActor(TestActor);
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   add_test(init);
+   add_test(test_client_request_callback);
+   add_test(test_client_request_promise);
+   add_test(test_client_request_promise_error);
+   add_test(test_client_request_event_emitter);
+   add_test(test_close_client_while_sending_requests);
+   add_test(test_client_request_after_close);
+diff --git a/devtools/server/tests/unit/test_registerClient.js b/devtools/server/tests/unit/test_registerClient.js
+--- a/devtools/server/tests/unit/test_registerClient.js
++++ b/devtools/server/tests/unit/test_registerClient.js
+@@ -49,17 +49,17 @@ TestClient.prototype = {
+     onDone();
+   }
+ };
+ 
+ function run_test() {
+   DebuggerServer.addGlobalActor(TestActor);
+ 
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   add_test(init);
+   add_test(test_client_events);
+   add_test(close_client);
+   run_next_test();
+ }
+ 
+ function init() {
+diff --git a/devtools/server/tests/unit/test_register_actor.js b/devtools/server/tests/unit/test_register_actor.js
+--- a/devtools/server/tests/unit/test_register_actor.js
++++ b/devtools/server/tests/unit/test_register_actor.js
+@@ -13,17 +13,17 @@ function check_actors(expect) {
+                DebuggerServer.globalActorFactories.hasOwnProperty("registeredActor2"));
+   Assert.equal(expect,
+                DebuggerServer.globalActorFactories.hasOwnProperty("registeredActor1"));
+ }
+ 
+ function run_test() {
+   // Allow incoming connections.
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   add_test(test_deprecated_api);
+   add_test(test_lazy_api);
+   add_test(cleanup);
+   run_next_test();
+ }
+ 
+ function test_deprecated_api() {
+diff --git a/devtools/server/tests/unit/test_requestTypes.js b/devtools/server/tests/unit/test_requestTypes.js
+--- a/devtools/server/tests/unit/test_requestTypes.js
++++ b/devtools/server/tests/unit/test_requestTypes.js
+@@ -18,17 +18,17 @@ function test_requestTypes_request(clien
+     client.close().then(() => {
+       do_test_finished();
+     });
+   });
+ }
+ 
+ function run_test() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+ 
+   let client = new DebuggerClient(DebuggerServer.connectPipe());
+   client.connect().then(function () {
+     test_requestTypes_request(client);
+   });
+ 
+   do_test_pending();
+ }
+diff --git a/devtools/shared/gcli/commands/listen.js b/devtools/shared/gcli/commands/listen.js
+--- a/devtools/shared/gcli/commands/listen.js
++++ b/devtools/shared/gcli/commands/listen.js
+@@ -21,17 +21,17 @@ XPCOMUtils.defineLazyGetter(this, "debug
+   // devtools.  This allows us to safely use the tools against even the
+   // actors and DebuggingServer itself, especially since we can mark
+   // serverLoader as invisible to the debugger (unlike the usual loader
+   // settings).
+   let serverLoader = new DevToolsLoader();
+   serverLoader.invisibleToDebugger = true;
+   let { DebuggerServer: debuggerServer } = serverLoader.require("devtools/server/main");
+   debuggerServer.init();
+-  debuggerServer.registerActors({ browser: true, root: true, tab: true });
++  debuggerServer.registerAllActors();
+   debuggerServer.allowChromeProcess = !l10n.hiddenByChromePref();
+   return debuggerServer;
+ });
+ 
+ exports.items = [
+   {
+     item: "command",
+     runAt: "client",
+diff --git a/devtools/shared/security/tests/chrome/test_websocket-transport.html b/devtools/shared/security/tests/chrome/test_websocket-transport.html
+--- a/devtools/shared/security/tests/chrome/test_websocket-transport.html
++++ b/devtools/shared/security/tests/chrome/test_websocket-transport.html
+@@ -22,17 +22,17 @@ window.onload = function () {
+ 
+   SimpleTest.registerCleanupFunction(() => {
+     Services.prefs.clearUserPref("devtools.debugger.remote-enabled");
+     Services.prefs.clearUserPref("devtools.debugger.prompt-connection");
+   });
+ 
+   add_task(function* () {
+     DebuggerServer.init();
+-    DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++    DebuggerServer.registerAllActors();
+ 
+     is(DebuggerServer.listeningSockets, 0, "0 listening sockets");
+ 
+     let listener = DebuggerServer.createListener();
+     ok(listener, "Socket listener created");
+     listener.portOrPath = -1;
+     listener.webSocket = true;
+     yield listener.open();
+diff --git a/devtools/shared/webconsole/test/common.js b/devtools/shared/webconsole/test/common.js
+--- a/devtools/shared/webconsole/test/common.js
++++ b/devtools/shared/webconsole/test/common.js
+@@ -20,17 +20,17 @@ const ObjectClient = require("devtools/s
+ const Services = require("Services");
+ 
+ function initCommon() {
+   // Services.prefs.setBoolPref("devtools.debugger.log", true);
+ }
+ 
+ function initDebuggerServer() {
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+   DebuggerServer.allowChromeProcess = true;
+ }
+ 
+ function connectToDebugger() {
+   initCommon();
+   initDebuggerServer();
+ 
+   let transport = DebuggerServer.connectPipe();
+diff --git a/devtools/startup/devtools-startup.js b/devtools/startup/devtools-startup.js
+--- a/devtools/startup/devtools-startup.js
++++ b/devtools/startup/devtools-startup.js
+@@ -678,17 +678,17 @@ DevToolsStartup.prototype = {
+       // actors and DebuggingServer itself, especially since we can mark
+       // serverLoader as invisible to the debugger (unlike the usual loader
+       // settings).
+       let serverLoader = new DevToolsLoader();
+       serverLoader.invisibleToDebugger = true;
+       let { DebuggerServer: debuggerServer } =
+         serverLoader.require("devtools/server/main");
+       debuggerServer.init();
+-      debuggerServer.registerActors({ browser: true, root: true, tab: true });
++      debuggerServer.registerAllActors();
+       debuggerServer.allowChromeProcess = true;
+ 
+       let listener = debuggerServer.createListener();
+       listener.portOrPath = portOrPath;
+       listener.webSocket = webSocket;
+       listener.open();
+       dump("Started debugger server on " + portOrPath + "\n");
+     } catch (e) {
+diff --git a/testing/xpcshell/head.js b/testing/xpcshell/head.js
+--- a/testing/xpcshell/head.js
++++ b/testing/xpcshell/head.js
+@@ -380,17 +380,17 @@ function _setupDebuggerServer(breakpoint
+                     "It is possible for this to alter test behevior by " +
+                     "triggering additional browser code to run, so check " +
+                     "test behavior after making this change.\n" +
+                     "See also https://bugzil.la/1215378.")
+   }
+   let { DebuggerServer } = require("devtools/server/main");
+   let { OriginalLocation } = require("devtools/server/actors/common");
+   DebuggerServer.init();
+-  DebuggerServer.registerActors({ browser: true, root: true, tab: true });
++  DebuggerServer.registerAllActors();
+   DebuggerServer.addActors("resource://testing-common/dbg-actors.js");
+   DebuggerServer.allowChromeProcess = true;
+ 
+   // An observer notification that tells us when we can "resume" script
+   // execution.
+   const TOPICS = ["devtools-thread-resumed", "xpcshell-test-devtools-shutdown"];
+   let observe = function(subject, topic, data) {
+     switch (topic) {

+ 87 - 0
mozilla-release/patches/1416928-1-59a1.patch

@@ -0,0 +1,87 @@
+# HG changeset patch
+# User Luca Greco <lgreco@mozilla.com>
+# Date 1510673052 -3600
+# Node ID 303b35020290c51e38912de89939b6aaa6c9080f
+# Parent  a3fb0b53944406f8a95e483db2a0a52119b60ece
+Bug 1416928 - Ensure that cached ContentScript sources are not skipped by the ThreadActor.onNewScript. r=yulia
+
+MozReview-Commit-ID: JJjcAvsI3Na
+
+diff --git a/devtools/server/actors/script.js b/devtools/server/actors/script.js
+--- a/devtools/server/actors/script.js
++++ b/devtools/server/actors/script.js
+@@ -1944,17 +1944,25 @@ const ThreadActor = ActorClassWithSpec(t
+   /**
+    * Add the provided source to the server cache.
+    *
+    * @param aSource Debugger.Source
+    *        The source that will be stored.
+    * @returns true, if the source was added; false otherwise.
+    */
+   _addSource: function (source) {
+-    if (!this.sources.allowSource(source) || this._debuggerSourcesSeen.has(source)) {
++    if (!this.sources.allowSource(source)) {
++      return false;
++    }
++
++    // Preloaded WebExtension content scripts may be cached internally by
++    // ExtensionContent.jsm and ThreadActor would ignore them on a page reload
++    // because it finds them in the _debuggerSourcesSeen WeakSet,
++    // and so we also need to be sure that there is still a source actor for the source.
++    if (this._debuggerSourcesSeen.has(source) && this.sources.hasSourceActor(source)) {
+       return false;
+     }
+ 
+     let sourceActor = this.sources.createNonSourceMappedActor(source);
+     let bpActors = [...this.breakpointActorMap.findActors()];
+ 
+     if (this._options.useSourceMaps) {
+       let promises = [];
+diff --git a/devtools/server/actors/utils/TabSources.js b/devtools/server/actors/utils/TabSources.js
+--- a/devtools/server/actors/utils/TabSources.js
++++ b/devtools/server/actors/utils/TabSources.js
+@@ -209,27 +209,41 @@ TabSources.prototype = {
+       this.fetchSourceMap(actor.source).then(map => {
+         if (!map) {
+           this.emit("newSource", actor);
+         }
+       });
+     }
+   },
+ 
+-  getSourceActor: function (source) {
++  _getSourceActor: function (source) {
+     if (source.url in this._sourceMappedSourceActors) {
+       return this._sourceMappedSourceActors[source.url];
+     }
+ 
+     if (this._sourceActors.has(source)) {
+       return this._sourceActors.get(source);
+     }
+ 
+-    throw new Error("getSource: could not find source actor for " +
+-                    (source.url || "source"));
++    return null;
++  },
++
++  hasSourceActor: function (source) {
++    return !!this._getSourceActor(source);
++  },
++
++  getSourceActor: function (source) {
++    const sourceActor = this._getSourceActor(source);
++
++    if (!sourceActor) {
++      throw new Error("getSource: could not find source actor for " +
++                      (source.url || "source"));
++    }
++
++    return sourceActor;
+   },
+ 
+   getSourceActorByURL: function (url) {
+     if (url) {
+       for (let [source, actor] of this._sourceActors) {
+         if (source.url === url) {
+           return actor;
+         }

+ 157 - 0
mozilla-release/patches/1416928-2-59a1.patch

@@ -0,0 +1,157 @@
+# HG changeset patch
+# User Luca Greco <lgreco@mozilla.com>
+# Date 1510764863 -3600
+# Node ID 3de9dc374632c47bfe05c610bfa40363fab1101c
+# Parent  7cb79209bcbc8538cc1107f8430ce2ff2687292b
+Bug 1416928 - Test content script debugging on the new debugger UI. r=jlast
+
+MozReview-Commit-ID: Dt0eaKmp777
+
+diff --git a/devtools/client/debugger/new/test/mochitest/browser.ini b/devtools/client/debugger/new/test/mochitest/browser.ini
+--- a/devtools/client/debugger/new/test/mochitest/browser.ini
++++ b/devtools/client/debugger/new/test/mochitest/browser.ini
+@@ -16,16 +16,17 @@ support-files =
+   examples/wasm-sourcemaps/average.wasm.map
+   examples/wasm-sourcemaps/average.c
+   examples/wasm-sourcemaps/utils.js
+   examples/sum/sum.js
+   examples/sum/sum.min.js
+   examples/sum/sum.min.js.map
+   examples/doc-async.html
+   examples/doc-asm.html
++  examples/doc-content-script-sources.html
+   examples/doc-scripts.html
+   examples/doc-script-mutate.html
+   examples/doc-script-switching.html
+   examples/doc-exceptions.html
+   examples/doc-iframes.html
+   examples/doc-frames.html
+   examples/doc-debugger-statements.html
+   examples/doc-minified.html
+@@ -67,16 +68,17 @@ skip-if = true # Bug 1383576
+ skip-if = !e10s # This test is only valid in e10s
+ [browser_dbg-call-stack.js]
+ [browser_dbg-expressions.js]
+ [browser_dbg-scopes.js]
+ [browser_dbg-chrome-create.js]
+ [browser_dbg-chrome-debugging.js]
+ skip-if = debug # bug 1374187
+ [browser_dbg-console.js]
++[browser_dbg-content-script-sources.js]
+ [browser_dbg-debugger-buttons.js]
+ [browser_dbg-editor-gutter.js]
+ [browser_dbg-editor-select.js]
+ [browser_dbg-editor-highlight.js]
+ [browser_dbg-iframes.js]
+ [browser_dbg_keyboard_navigation.js]
+ [browser_dbg_keyboard-shortcuts.js]
+ skip-if = os == "linux" # bug 1351952
+diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-content-script-sources.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-content-script-sources.js
+new file mode 100644
+--- /dev/null
++++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-content-script-sources.js
+@@ -0,0 +1,87 @@
++/* Any copyright is dedicated to the Public Domain.
++ * http://creativecommons.org/publicdomain/zero/1.0/ */
++
++"use strict";
++
++/* global ExtensionTestUtils, closeTab, openToolboxForTab, assertDebugLine,
++          waitForSelectedSource */
++
++// Tests that the content scripts are listed in the source tree.
++
++async function selectContentScriptSources(dbg) {
++  await waitForSources(dbg, "content_script.js");
++
++  // Select a source.
++  await selectSource(dbg, "content_script.js");
++
++  ok(
++    findElementWithSelector(dbg, ".sources-list .focused"),
++    "Source is focused"
++  );
++}
++
++async function installAndStartExtension() {
++  function contentScript() {
++    console.log("content script loads");
++
++    // This listener prevents the source from being garbage collected
++    // and be missing from the scripts returned by `dbg.findScripts()`
++    // in `ThreadActor._discoverSources`.
++    window.onload = () => {};
++  }
++
++  let extension = ExtensionTestUtils.loadExtension({
++    manifest: {
++      "content_scripts": [
++        {
++          "js": ["content_script.js"],
++          "matches": ["http://example.com/*"],
++          "run_at": "document_start",
++        },
++      ],
++    },
++    files: {
++      "content_script.js": contentScript,
++    },
++  });
++
++  await extension.startup();
++
++  return extension;
++}
++
++add_task(async function () {
++  const extension = await installAndStartExtension();
++
++  let dbg = await initDebugger("doc-content-script-sources.html");
++  await selectContentScriptSources(dbg);
++  await closeTab(dbg, "content_script.js");
++
++  // Destroy the toolbox and repeat the test in a new toolbox
++  // and ensures that the content script is still listed.
++  await dbg.toolbox.destroy();
++  const toolbox = await openToolboxForTab(gBrowser.selectedTab, "jsdebugger");
++  dbg = createDebuggerContext(toolbox);
++  await selectContentScriptSources(dbg);
++
++  await addBreakpoint(dbg, "content_script.js", 2);
++
++  for (let i = 1; i < 3; i++) {
++    info(`Reloading tab (${i} time)`);
++    gBrowser.reloadTab(gBrowser.selectedTab);
++    await waitForPaused(dbg);
++    await waitForSources(dbg, "content_script.js");
++    await waitForSelectedSource(dbg, "content_script.js");
++    ok(
++      findElementWithSelector(dbg, ".sources-list .focused"),
++      "Source is focused"
++    );
++    assertPausedLocation(dbg);
++    assertDebugLine(dbg, 2);
++    await resume(dbg);
++  }
++
++  await closeTab(dbg, "content_script.js");
++
++  await extension.unload();
++});
+diff --git a/devtools/client/debugger/new/test/mochitest/examples/doc-content-script-sources.html b/devtools/client/debugger/new/test/mochitest/examples/doc-content-script-sources.html
+new file mode 100644
+--- /dev/null
++++ b/devtools/client/debugger/new/test/mochitest/examples/doc-content-script-sources.html
+@@ -0,0 +1,12 @@
++<!-- Any copyright is dedicated to the Public Domain.
++     http://creativecommons.org/publicdomain/zero/1.0/ -->
++<!DOCTYPE html>
++<html>
++  <head>
++    <meta charset="utf-8"/>
++    <title>Debugger test page</title>
++  </head>
++
++  <body>
++  </body>
++</html>

+ 374 - 0
mozilla-release/patches/1417444-59a1.patch

@@ -0,0 +1,374 @@
+# HG changeset patch
+# User Michael Ratcliffe <mratcliffe@mozilla.com>
+# Date 1510757317 0
+# Node ID 58593f36fc8f5c899dee8ef0b0d0096119c67bc7
+# Parent  25a81dc0fa2a2b8b3bbec2bd159c64f33a726720
+Bug 1417444 - about:debugging to use prop-types and react-dom-factories r=jdescottes
+
+MozReview-Commit-ID: EQ5MSMrvdWW
+
+diff --git a/devtools/client/aboutdebugging/components/Aboutdebugging.js b/devtools/client/aboutdebugging/components/Aboutdebugging.js
+--- a/devtools/client/aboutdebugging/components/Aboutdebugging.js
++++ b/devtools/client/aboutdebugging/components/Aboutdebugging.js
+@@ -1,18 +1,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/. */
+ 
+ /* eslint-env browser */
+ 
+ "use strict";
+ 
+-const { createFactory, Component, DOM: dom, PropTypes } =
+-  require("devtools/client/shared/vendor/react");
++const { createFactory, Component } = require("devtools/client/shared/vendor/react");
++const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
++const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const Services = require("Services");
+ 
+ const PanelMenu = createFactory(require("./PanelMenu"));
+ 
+ loader.lazyGetter(this, "AddonsPanel",
+   () => createFactory(require("./addons/Panel")));
+ loader.lazyGetter(this, "TabsPanel",
+   () => createFactory(require("./tabs/Panel")));
+diff --git a/devtools/client/aboutdebugging/components/PanelHeader.js b/devtools/client/aboutdebugging/components/PanelHeader.js
+--- a/devtools/client/aboutdebugging/components/PanelHeader.js
++++ b/devtools/client/aboutdebugging/components/PanelHeader.js
+@@ -1,16 +1,17 @@
+ /* 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 { Component, DOM: dom, PropTypes } =
+-  require("devtools/client/shared/vendor/react");
++const { Component } = require("devtools/client/shared/vendor/react");
++const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
++const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ 
+ class PanelHeader extends Component {
+   static get propTypes() {
+     return {
+       id: PropTypes.string.isRequired,
+       name: PropTypes.string.isRequired
+     };
+   }
+diff --git a/devtools/client/aboutdebugging/components/PanelMenu.js b/devtools/client/aboutdebugging/components/PanelMenu.js
+--- a/devtools/client/aboutdebugging/components/PanelMenu.js
++++ b/devtools/client/aboutdebugging/components/PanelMenu.js
+@@ -1,16 +1,17 @@
+ /* 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 { Component, createFactory, DOM: dom, PropTypes } =
+-  require("devtools/client/shared/vendor/react");
++const { Component, createFactory } = require("devtools/client/shared/vendor/react");
++const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
++const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const PanelMenuEntry = createFactory(require("./PanelMenuEntry"));
+ 
+ class PanelMenu extends Component {
+   static get propTypes() {
+     return {
+       panels: PropTypes.arrayOf(PropTypes.shape({
+         id: PropTypes.string.isRequired,
+         name: PropTypes.string.isRequired,
+diff --git a/devtools/client/aboutdebugging/components/PanelMenuEntry.js b/devtools/client/aboutdebugging/components/PanelMenuEntry.js
+--- a/devtools/client/aboutdebugging/components/PanelMenuEntry.js
++++ b/devtools/client/aboutdebugging/components/PanelMenuEntry.js
+@@ -1,16 +1,17 @@
+ /* 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 { Component, DOM: dom, PropTypes } =
+-  require("devtools/client/shared/vendor/react");
++const { Component } = require("devtools/client/shared/vendor/react");
++const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
++const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ 
+ class PanelMenuEntry extends Component {
+   static get propTypes() {
+     return {
+       icon: PropTypes.string.isRequired,
+       id: PropTypes.string.isRequired,
+       name: PropTypes.string.isRequired,
+       selected: PropTypes.bool,
+diff --git a/devtools/client/aboutdebugging/components/TargetList.js b/devtools/client/aboutdebugging/components/TargetList.js
+--- a/devtools/client/aboutdebugging/components/TargetList.js
++++ b/devtools/client/aboutdebugging/components/TargetList.js
+@@ -1,16 +1,17 @@
+ /* 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 { Component, DOM: dom, PropTypes } =
+-  require("devtools/client/shared/vendor/react");
++const { Component } = require("devtools/client/shared/vendor/react");
++const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
++const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const Services = require("Services");
+ 
+ loader.lazyRequireGetter(this, "DebuggerClient",
+   "devtools/shared/client/debugger-client", true);
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+diff --git a/devtools/client/aboutdebugging/components/addons/Controls.js b/devtools/client/aboutdebugging/components/addons/Controls.js
+--- a/devtools/client/aboutdebugging/components/addons/Controls.js
++++ b/devtools/client/aboutdebugging/components/addons/Controls.js
+@@ -6,18 +6,19 @@
+ /* globals AddonManager */
+ 
+ "use strict";
+ 
+ loader.lazyImporter(this, "AddonManager",
+   "resource://gre/modules/AddonManager.jsm");
+ 
+ const { Cc, Ci } = require("chrome");
+-const { createFactory, Component, DOM: dom, PropTypes } =
+-  require("devtools/client/shared/vendor/react");
++const { createFactory, Component } = require("devtools/client/shared/vendor/react");
++const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
++const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const Services = require("Services");
+ const AddonsInstallError = createFactory(require("./InstallError"));
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+ const MORE_INFO_URL = "https://developer.mozilla.org/docs/Tools" +
+                       "/about:debugging#Enabling_add-on_debugging";
+diff --git a/devtools/client/aboutdebugging/components/addons/InstallError.js b/devtools/client/aboutdebugging/components/addons/InstallError.js
+--- a/devtools/client/aboutdebugging/components/addons/InstallError.js
++++ b/devtools/client/aboutdebugging/components/addons/InstallError.js
+@@ -1,16 +1,18 @@
+ /* 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/. */
+ 
+ /* eslint-env browser */
+ "use strict";
+ 
+-const { Component, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react");
++const { Component } = require("devtools/client/shared/vendor/react");
++const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
++const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ 
+ const Services = require("Services");
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+ class AddonsInstallError extends Component {
+   static get propTypes() {
+diff --git a/devtools/client/aboutdebugging/components/addons/Panel.js b/devtools/client/aboutdebugging/components/addons/Panel.js
+--- a/devtools/client/aboutdebugging/components/addons/Panel.js
++++ b/devtools/client/aboutdebugging/components/addons/Panel.js
+@@ -1,18 +1,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 { AddonManager } = require("resource://gre/modules/AddonManager.jsm");
+ const { Management } = require("resource://gre/modules/Extension.jsm");
+-const { createFactory, Component, DOM: dom, PropTypes } =
+-  require("devtools/client/shared/vendor/react");
++const { createFactory, Component } = require("devtools/client/shared/vendor/react");
++const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
++const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const Services = require("Services");
+ 
+ const AddonsControls = createFactory(require("./Controls"));
+ const AddonTarget = createFactory(require("./Target"));
+ const PanelHeader = createFactory(require("../PanelHeader"));
+ const TargetList = createFactory(require("../TargetList"));
+ 
+ loader.lazyRequireGetter(this, "DebuggerClient",
+diff --git a/devtools/client/aboutdebugging/components/addons/Target.js b/devtools/client/aboutdebugging/components/addons/Target.js
+--- a/devtools/client/aboutdebugging/components/addons/Target.js
++++ b/devtools/client/aboutdebugging/components/addons/Target.js
+@@ -1,18 +1,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/. */
+ 
+ /* eslint-env browser */
+ 
+ "use strict";
+ 
+-const { Component, DOM: dom, PropTypes } =
+-  require("devtools/client/shared/vendor/react");
++const { Component } = require("devtools/client/shared/vendor/react");
++const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
++const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const { debugAddon, isTemporaryID, parseFileUri, uninstallAddon } =
+   require("../../modules/addon");
+ const Services = require("Services");
+ 
+ loader.lazyImporter(this, "BrowserToolboxProcess",
+   "resource://devtools/client/framework/ToolboxProcess.jsm");
+ 
+ loader.lazyRequireGetter(this, "DebuggerClient",
+diff --git a/devtools/client/aboutdebugging/components/tabs/Panel.js b/devtools/client/aboutdebugging/components/tabs/Panel.js
+--- a/devtools/client/aboutdebugging/components/tabs/Panel.js
++++ b/devtools/client/aboutdebugging/components/tabs/Panel.js
+@@ -1,18 +1,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/. */
+ 
+ /* eslint-env browser */
+ 
+ "use strict";
+ 
+-const { Component, createFactory, DOM: dom, PropTypes } =
+-  require("devtools/client/shared/vendor/react");
++const { Component, createFactory } = require("devtools/client/shared/vendor/react");
++const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
++const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const Services = require("Services");
+ 
+ const PanelHeader = createFactory(require("../PanelHeader"));
+ const TargetList = createFactory(require("../TargetList"));
+ const TabTarget = createFactory(require("./Target"));
+ 
+ loader.lazyRequireGetter(this, "DebuggerClient",
+   "devtools/shared/client/debugger-client", true);
+diff --git a/devtools/client/aboutdebugging/components/tabs/Target.js b/devtools/client/aboutdebugging/components/tabs/Target.js
+--- a/devtools/client/aboutdebugging/components/tabs/Target.js
++++ b/devtools/client/aboutdebugging/components/tabs/Target.js
+@@ -1,18 +1,20 @@
+ /* 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/. */
+ 
+ /* eslint-env browser */
+ 
+ "use strict";
+ 
+-const { Component, DOM: dom, PropTypes } =
+-  require("devtools/client/shared/vendor/react");
++const { Component } = require("devtools/client/shared/vendor/react");
++const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
++const dom = require("devtools/client/shared/vendor/react-dom-factories");
++
+ const Services = require("Services");
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+ 
+ class TabTarget extends Component {
+   static get propTypes() {
+     return {
+diff --git a/devtools/client/aboutdebugging/components/workers/MultiE10sWarning.js b/devtools/client/aboutdebugging/components/workers/MultiE10sWarning.js
+--- a/devtools/client/aboutdebugging/components/workers/MultiE10sWarning.js
++++ b/devtools/client/aboutdebugging/components/workers/MultiE10sWarning.js
+@@ -3,18 +3,18 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ /* eslint-env browser */
+ 
+ "use strict";
+ 
+ loader.lazyImporter(this, "PrivateBrowsingUtils",
+   "resource://gre/modules/PrivateBrowsingUtils.jsm");
+-const { Component, DOM: dom } =
+-  require("devtools/client/shared/vendor/react");
++const { Component } = require("devtools/client/shared/vendor/react");
++const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const Services = require("Services");
+ const { Ci } = require("chrome");
+ 
+ loader.lazyImporter(this, "PrivateBrowsingUtils",
+   "resource://gre/modules/PrivateBrowsingUtils.jsm");
+ 
+ loader.lazyRequireGetter(this, "DebuggerClient",
+   "devtools/shared/client/debugger-client", true);
+diff --git a/devtools/client/aboutdebugging/components/workers/Panel.js b/devtools/client/aboutdebugging/components/workers/Panel.js
+--- a/devtools/client/aboutdebugging/components/workers/Panel.js
++++ b/devtools/client/aboutdebugging/components/workers/Panel.js
+@@ -3,18 +3,19 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ /* globals window */
+ "use strict";
+ 
+ loader.lazyImporter(this, "PrivateBrowsingUtils",
+   "resource://gre/modules/PrivateBrowsingUtils.jsm");
+ 
+ const { Ci } = require("chrome");
+-const { Component, createFactory, DOM: dom, PropTypes } =
+-  require("devtools/client/shared/vendor/react");
++const { Component, createFactory } = require("devtools/client/shared/vendor/react");
++const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
++const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const { getWorkerForms } = require("../../modules/worker");
+ const Services = require("Services");
+ 
+ const PanelHeader = createFactory(require("../PanelHeader"));
+ const TargetList = createFactory(require("../TargetList"));
+ const WorkerTarget = createFactory(require("./Target"));
+ const MultiE10SWarning = createFactory(require("./MultiE10sWarning"));
+ const ServiceWorkerTarget = createFactory(require("./ServiceWorkerTarget"));
+diff --git a/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js b/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js
+--- a/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js
++++ b/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js
+@@ -1,18 +1,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/. */
+ 
+ /* eslint-env browser */
+ 
+ "use strict";
+ 
+-const { Component, DOM: dom, PropTypes } =
+-  require("devtools/client/shared/vendor/react");
++const { Component } = require("devtools/client/shared/vendor/react");
++const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
++const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const { debugWorker } = require("../../modules/worker");
+ const Services = require("Services");
+ 
+ loader.lazyRequireGetter(this, "DebuggerClient",
+   "devtools/shared/client/debugger-client", true);
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");
+diff --git a/devtools/client/aboutdebugging/components/workers/Target.js b/devtools/client/aboutdebugging/components/workers/Target.js
+--- a/devtools/client/aboutdebugging/components/workers/Target.js
++++ b/devtools/client/aboutdebugging/components/workers/Target.js
+@@ -1,18 +1,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/. */
+ 
+ /* eslint-env browser */
+ 
+ "use strict";
+ 
+-const { Component, DOM: dom, PropTypes } =
+-  require("devtools/client/shared/vendor/react");
++const { Component } = require("devtools/client/shared/vendor/react");
++const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
++const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const { debugWorker } = require("../../modules/worker");
+ const Services = require("Services");
+ 
+ loader.lazyRequireGetter(this, "DebuggerClient",
+   "devtools/shared/client/debugger-client", true);
+ 
+ const Strings = Services.strings.createBundle(
+   "chrome://devtools/locale/aboutdebugging.properties");

+ 62 - 0
mozilla-release/patches/1430799-59a1.patch

@@ -0,0 +1,62 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1516119958 -3600
+# Node ID f3e1fd49ad093f343538a34f826eecb229cdd8e6
+# Parent  3b3c79e0aa3e7725b10c088cfaa727974cba8306
+Bug 1430799 - Add a createObjectClient method to the DebuggerClient;r=jlast.
+
+This is needed by the debugger in order to use the latest devtools-reps
+package (0.19.0).
+This function was already added in the devtools-connection package.
+
+MozReview-Commit-ID: 3SVxq4Jbs16
+
+diff --git a/devtools/shared/client/debugger-client.js b/devtools/shared/client/debugger-client.js
+--- a/devtools/shared/client/debugger-client.js
++++ b/devtools/shared/client/debugger-client.js
+@@ -23,16 +23,17 @@ loader.lazyRequireGetter(this, "getDevic
+ 
+ loader.lazyRequireGetter(this, "WebConsoleClient", "devtools/shared/webconsole/client", true);
+ loader.lazyRequireGetter(this, "AddonClient", "devtools/shared/client/addon-client");
+ loader.lazyRequireGetter(this, "RootClient", "devtools/shared/client/root-client");
+ loader.lazyRequireGetter(this, "TabClient", "devtools/shared/client/tab-client");
+ loader.lazyRequireGetter(this, "ThreadClient", "devtools/shared/client/thread-client");
+ loader.lazyRequireGetter(this, "TraceClient", "devtools/shared/client/trace-client");
+ loader.lazyRequireGetter(this, "WorkerClient", "devtools/shared/client/worker-client");
++loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/object-client");
+ 
+ const noop = () => {};
+ 
+ // Define the minimum officially supported version of Firefox when connecting to a remote
+ // runtime. (Use ".0a1" to support the very first nightly version)
+ // This is usually the current ESR version.
+ const MIN_SUPPORTED_PLATFORM_VERSION = "52.0a1";
+ const MS_PER_DAY = 86400000;
+@@ -1188,17 +1189,26 @@ DebuggerClient.prototype = {
+       }
+     }
+     return null;
+   },
+ 
+   /**
+    * Currently attached addon.
+    */
+-  activeAddon: null
++  activeAddon: null,
++
++  /**
++   * Creates an object client for this DebuggerClient and the grip in parameter,
++   * @param {Object} grip: The grip to create the ObjectClient for.
++   * @returns {ObjectClient}
++   */
++  createObjectClient: function (grip) {
++    return new ObjectClient(this, grip);
++  }
+ };
+ 
+ eventSource(DebuggerClient.prototype);
+ 
+ class Request extends EventEmitter {
+   constructor(request) {
+     super();
+     this.request = request;

+ 1398 - 0
mozilla-release/patches/1435187-60a1.patch

@@ -0,0 +1,1398 @@
+# HG changeset patch
+# User Jason Laster <jason.laster.11@gmail.com>
+# Date 1517561820 -7200
+# Node ID 2fdf70b7c76829533719e8d1136a0df88f110b81
+# Parent  c00c0980ac6841540a4650cb7b01df23926afc07
+Bug 1435187 - Refactor the script actor. r=jdescottes
+
+ - Extract paused scoped objects.
+ - Extract event loop stack. r=jdescottes
+ - Extract actor stores. r=jdescottes
+ - Move script.js to actor.js. r=jdescottes
+
+diff --git a/devtools/server/actors/addon.js b/devtools/server/actors/addon.js
+--- a/devtools/server/actors/addon.js
++++ b/devtools/server/actors/addon.js
+@@ -8,18 +8,18 @@ var { Ci, Cu } = require("chrome");
+ var Services = require("Services");
+ var { ActorPool } = require("devtools/server/actors/common");
+ var { TabSources } = require("./utils/TabSources");
+ var makeDebugger = require("./utils/make-debugger");
+ var { ConsoleAPIListener } = require("devtools/server/actors/webconsole/listeners");
+ var DevToolsUtils = require("devtools/shared/DevToolsUtils");
+ var { assert, update } = DevToolsUtils;
+ 
+-loader.lazyRequireGetter(this, "AddonThreadActor", "devtools/server/actors/script", true);
+-loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/script", true);
++loader.lazyRequireGetter(this, "AddonThreadActor", "devtools/server/actors/thread", true);
++loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/thread", true);
+ loader.lazyRequireGetter(this, "mapURIToAddonID", "devtools/server/actors/utils/map-uri-to-addon-id");
+ loader.lazyRequireGetter(this, "WebConsoleActor", "devtools/server/actors/webconsole", true);
+ 
+ loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
+ 
+ function BrowserAddonActor(connection, addon) {
+   this.conn = connection;
+   this._addon = addon;
+diff --git a/devtools/server/actors/child-process.js b/devtools/server/actors/child-process.js
+--- a/devtools/server/actors/child-process.js
++++ b/devtools/server/actors/child-process.js
+@@ -2,17 +2,17 @@
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ const { Cc, Ci, Cu } = require("chrome");
+ const Services = require("Services");
+ 
+-const { ChromeDebuggerActor } = require("devtools/server/actors/script");
++const { ChromeDebuggerActor } = require("devtools/server/actors/thread");
+ const { WebConsoleActor } = require("devtools/server/actors/webconsole");
+ const makeDebugger = require("devtools/server/actors/utils/make-debugger");
+ const { ActorPool } = require("devtools/server/main");
+ const { assert } = require("devtools/shared/DevToolsUtils");
+ const { TabSources } = require("./utils/TabSources");
+ 
+ loader.lazyRequireGetter(this, "WorkerActorList", "devtools/server/actors/worker-list", true);
+ 
+diff --git a/devtools/server/actors/moz.build b/devtools/server/actors/moz.build
+--- a/devtools/server/actors/moz.build
++++ b/devtools/server/actors/moz.build
+@@ -34,31 +34,32 @@ DevToolsModules(
+     'gcli.js',
+     'heap-snapshot-file.js',
+     'highlighters.css',
+     'highlighters.js',
+     'inspector.js',
+     'layout.js',
+     'memory.js',
+     'object.js',
++    'pause-scoped.js',
+     'performance-recording.js',
+     'performance.js',
+     'preference.js',
+     'pretty-print-worker.js',
+     'process.js',
+     'promises.js',
+     'reflow.js',
+     'root.js',
+-    'script.js',
+     'source.js',
+     'storage.js',
+     'string.js',
+     'styles.js',
+     'stylesheets.js',
+     'tab.js',
++    'thread.js',
+     'timeline.js',
+     'webaudio.js',
+     'webbrowser.js',
+     'webconsole.js',
+     'webextension-inspected-window.js',
+     'webextension-parent.js',
+     'webextension.js',
+     'webgl.js',
+diff --git a/devtools/server/actors/pause-scoped.js b/devtools/server/actors/pause-scoped.js
+new file mode 100644
+--- /dev/null
++++ b/devtools/server/actors/pause-scoped.js
+@@ -0,0 +1,128 @@
++/* -*- indent-tabs-mode: nil; js-indent-level: 2; 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 { ObjectActor } = require("devtools/server/actors/object");
++
++/**
++ * A base actor for any actors that should only respond receive messages in the
++ * paused state. Subclasses may expose a `threadActor` which is used to help
++ * determine when we are in a paused state. Subclasses should set their own
++ * "constructor" property if they want better error messages. You should never
++ * instantiate a PauseScopedActor directly, only through subclasses.
++ */
++function PauseScopedActor() {
++}
++
++/**
++ * A function decorator for creating methods to handle protocol messages that
++ * should only be received while in the paused state.
++ *
++ * @param method Function
++ *        The function we are decorating.
++ */
++PauseScopedActor.withPaused = function (method) {
++  return function () {
++    if (this.isPaused()) {
++      return method.apply(this, arguments);
++    }
++    return this._wrongState();
++  };
++};
++
++PauseScopedActor.prototype = {
++
++  /**
++   * Returns true if we are in the paused state.
++   */
++  isPaused: function () {
++    // When there is not a ThreadActor available (like in the webconsole) we
++    // have to be optimistic and assume that we are paused so that we can
++    // respond to requests.
++    return this.threadActor ? this.threadActor.state === "paused" : true;
++  },
++
++  /**
++   * Returns the wrongState response packet for this actor.
++   */
++  _wrongState: function () {
++    return {
++      error: "wrongState",
++      message: this.constructor.name +
++        " actors can only be accessed while the thread is paused."
++    };
++  }
++};
++
++/**
++ * Creates a pause-scoped actor for the specified object.
++ * @see ObjectActor
++ */
++function PauseScopedObjectActor(obj, hooks) {
++  ObjectActor.call(this, obj, hooks);
++  this.hooks.promote = hooks.promote;
++  this.hooks.isThreadLifetimePool = hooks.isThreadLifetimePool;
++}
++
++PauseScopedObjectActor.prototype = Object.create(PauseScopedActor.prototype);
++
++Object.assign(PauseScopedObjectActor.prototype, ObjectActor.prototype);
++
++Object.assign(PauseScopedObjectActor.prototype, {
++  constructor: PauseScopedObjectActor,
++  actorPrefix: "pausedobj",
++
++  onOwnPropertyNames:
++    PauseScopedActor.withPaused(ObjectActor.prototype.onOwnPropertyNames),
++
++  onPrototypeAndProperties:
++    PauseScopedActor.withPaused(ObjectActor.prototype.onPrototypeAndProperties),
++
++  onPrototype: PauseScopedActor.withPaused(ObjectActor.prototype.onPrototype),
++  onProperty: PauseScopedActor.withPaused(ObjectActor.prototype.onProperty),
++  onDecompile: PauseScopedActor.withPaused(ObjectActor.prototype.onDecompile),
++
++  onDisplayString:
++    PauseScopedActor.withPaused(ObjectActor.prototype.onDisplayString),
++
++  onParameterNames:
++    PauseScopedActor.withPaused(ObjectActor.prototype.onParameterNames),
++
++  /**
++   * Handle a protocol request to promote a pause-lifetime grip to a
++   * thread-lifetime grip.
++   *
++   * @param request object
++   *        The protocol request object.
++   */
++  onThreadGrip: PauseScopedActor.withPaused(function (request) {
++    this.hooks.promote();
++    return {};
++  }),
++
++  /**
++   * Handle a protocol request to release a thread-lifetime grip.
++   *
++   * @param request object
++   *        The protocol request object.
++   */
++  onRelease: PauseScopedActor.withPaused(function (request) {
++    if (this.hooks.isThreadLifetimePool()) {
++      return { error: "notReleasable",
++               message: "Only thread-lifetime actors can be released." };
++    }
++
++    this.release();
++    return {};
++  }),
++});
++
++Object.assign(PauseScopedObjectActor.prototype.requestTypes, {
++  "threadGrip": PauseScopedObjectActor.prototype.onThreadGrip,
++});
++
++exports.PauseScopedObjectActor = PauseScopedObjectActor;
+diff --git a/devtools/server/actors/tab.js b/devtools/server/actors/tab.js
+--- a/devtools/server/actors/tab.js
++++ b/devtools/server/actors/tab.js
+@@ -22,18 +22,18 @@ var { DebuggerServer } = require("devtoo
+ var DevToolsUtils = require("devtools/shared/DevToolsUtils");
+ var { assert } = DevToolsUtils;
+ var { TabSources } = require("./utils/TabSources");
+ var makeDebugger = require("./utils/make-debugger");
+ const EventEmitter = require("devtools/shared/event-emitter");
+ 
+ const EXTENSION_CONTENT_JSM = "resource://gre/modules/ExtensionContent.jsm";
+ 
+-loader.lazyRequireGetter(this, "ThreadActor", "devtools/server/actors/script", true);
+-loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/script", true);
++loader.lazyRequireGetter(this, "ThreadActor", "devtools/server/actors/thread", true);
++loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/thread", true);
+ loader.lazyRequireGetter(this, "WorkerActorList", "devtools/server/actors/worker-list", true);
+ loader.lazyImporter(this, "ExtensionContent", EXTENSION_CONTENT_JSM);
+ 
+ loader.lazyRequireGetter(this, "StyleSheetActor", "devtools/server/actors/stylesheets", true);
+ 
+ function getWindowID(window) {
+   return window.QueryInterface(Ci.nsIInterfaceRequestor)
+                .getInterface(Ci.nsIDOMWindowUtils)
+diff --git a/devtools/server/actors/script.js b/devtools/server/actors/thread.js
+rename from devtools/server/actors/script.js
+rename to devtools/server/actors/thread.js
+--- a/devtools/server/actors/script.js
++++ b/devtools/server/actors/thread.js
+@@ -3,401 +3,45 @@
+ /* 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 Services = require("Services");
+ const { Cc, Ci, Cr } = require("chrome");
+-const { ActorPool, OriginalLocation, GeneratedLocation } = require("devtools/server/actors/common");
+-const { ObjectActor, createValueGrip, longStringGrip } = require("devtools/server/actors/object");
++const { ActorPool, GeneratedLocation } = require("devtools/server/actors/common");
++const { createValueGrip, longStringGrip } = require("devtools/server/actors/object");
+ const { ActorClassWithSpec } = require("devtools/shared/protocol");
+ const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+ const flags = require("devtools/shared/flags");
+ const { assert, dumpn } = DevToolsUtils;
+ const promise = require("promise");
+-const xpcInspector = require("xpcInspector");
+ const { DevToolsWorker } = require("devtools/shared/worker/worker");
+ const { threadSpec } = require("devtools/shared/specs/script");
+ 
+ const { resolve, reject, all } = promise;
+ 
+ loader.lazyGetter(this, "Debugger", () => {
+   let Debugger = require("Debugger");
+   hackDebugger(Debugger);
+   return Debugger;
+ });
+-loader.lazyRequireGetter(this, "CssLogic", "devtools/server/css-logic", true);
+ loader.lazyRequireGetter(this, "findCssSelector", "devtools/shared/inspector/css-logic", true);
+-loader.lazyRequireGetter(this, "mapURIToAddonID", "devtools/server/actors/utils/map-uri-to-addon-id");
+ loader.lazyRequireGetter(this, "BreakpointActor", "devtools/server/actors/breakpoint", true);
+ loader.lazyRequireGetter(this, "setBreakpointAtEntryPoints", "devtools/server/actors/breakpoint", true);
+-loader.lazyRequireGetter(this, "getSourceURL", "devtools/server/actors/source", true);
+ loader.lazyRequireGetter(this, "EnvironmentActor", "devtools/server/actors/environment", true);
++loader.lazyRequireGetter(this, "SourceActorStore", "devtools/server/actors/utils/source-actor-store", true);
++loader.lazyRequireGetter(this, "BreakpointActorMap", "devtools/server/actors/utils/breakpoint-actor-map", true);
++loader.lazyRequireGetter(this, "PauseScopedObjectActor", "devtools/server/actors/pause-scoped", true);
++loader.lazyRequireGetter(this, "EventLoopStack", "devtools/server/actors/utils/event-loop", true);
+ loader.lazyRequireGetter(this, "FrameActor", "devtools/server/actors/frame", true);
+ loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
+ 
+ /**
+- * A BreakpointActorMap is a map from locations to instances of BreakpointActor.
+- */
+-function BreakpointActorMap() {
+-  this._size = 0;
+-  this._actors = {};
+-}
+-
+-BreakpointActorMap.prototype = {
+-  /**
+-   * Return the number of BreakpointActors in this BreakpointActorMap.
+-   *
+-   * @returns Number
+-   *          The number of BreakpointActor in this BreakpointActorMap.
+-   */
+-  get size() {
+-    return this._size;
+-  },
+-
+-  /**
+-   * Generate all BreakpointActors that match the given location in
+-   * this BreakpointActorMap.
+-   *
+-   * @param OriginalLocation location
+-   *        The location for which matching BreakpointActors should be generated.
+-   */
+-  findActors: function* (location = new OriginalLocation()) {
+-    // Fast shortcut for when we know we won't find any actors. Surprisingly
+-    // enough, this speeds up refreshing when there are no breakpoints set by
+-    // about 2x!
+-    if (this.size === 0) {
+-      return;
+-    }
+-
+-    function* findKeys(obj, key) {
+-      if (key !== undefined) {
+-        if (key in obj) {
+-          yield key;
+-        }
+-      } else {
+-        for (key of Object.keys(obj)) {
+-          yield key;
+-        }
+-      }
+-    }
+-
+-    let query = {
+-      sourceActorID: location.originalSourceActor
+-                     ? location.originalSourceActor.actorID
+-                     : undefined,
+-      line: location.originalLine,
+-    };
+-
+-    // If location contains a line, assume we are searching for a whole line
+-    // breakpoint, and set begin/endColumn accordingly. Otherwise, we are
+-    // searching for all breakpoints, so begin/endColumn should be left unset.
+-    if (location.originalLine) {
+-      query.beginColumn = location.originalColumn ? location.originalColumn : 0;
+-      query.endColumn = location.originalColumn ? location.originalColumn + 1 : Infinity;
+-    } else {
+-      query.beginColumn = location.originalColumn ? query.originalColumn : undefined;
+-      query.endColumn = location.originalColumn ? query.originalColumn + 1 : undefined;
+-    }
+-
+-    for (let sourceActorID of findKeys(this._actors, query.sourceActorID)) {
+-      let actor = this._actors[sourceActorID];
+-      for (let line of findKeys(actor, query.line)) {
+-        for (let beginColumn of findKeys(actor[line], query.beginColumn)) {
+-          for (let endColumn of findKeys(actor[line][beginColumn],
+-               query.endColumn)) {
+-            yield actor[line][beginColumn][endColumn];
+-          }
+-        }
+-      }
+-    }
+-  },
+-
+-  /**
+-   * Return the BreakpointActor at the given location in this
+-   * BreakpointActorMap.
+-   *
+-   * @param OriginalLocation location
+-   *        The location for which the BreakpointActor should be returned.
+-   *
+-   * @returns BreakpointActor actor
+-   *          The BreakpointActor at the given location.
+-   */
+-  getActor: function (originalLocation) {
+-    for (let actor of this.findActors(originalLocation)) {
+-      return actor;
+-    }
+-
+-    return null;
+-  },
+-
+-  /**
+-   * Set the given BreakpointActor to the given location in this
+-   * BreakpointActorMap.
+-   *
+-   * @param OriginalLocation location
+-   *        The location to which the given BreakpointActor should be set.
+-   *
+-   * @param BreakpointActor actor
+-   *        The BreakpointActor to be set to the given location.
+-   */
+-  setActor: function (location, actor) {
+-    let { originalSourceActor, originalLine, originalColumn } = location;
+-
+-    let sourceActorID = originalSourceActor.actorID;
+-    let line = originalLine;
+-    let beginColumn = originalColumn ? originalColumn : 0;
+-    let endColumn = originalColumn ? originalColumn + 1 : Infinity;
+-
+-    if (!this._actors[sourceActorID]) {
+-      this._actors[sourceActorID] = [];
+-    }
+-    if (!this._actors[sourceActorID][line]) {
+-      this._actors[sourceActorID][line] = [];
+-    }
+-    if (!this._actors[sourceActorID][line][beginColumn]) {
+-      this._actors[sourceActorID][line][beginColumn] = [];
+-    }
+-    if (!this._actors[sourceActorID][line][beginColumn][endColumn]) {
+-      ++this._size;
+-    }
+-    this._actors[sourceActorID][line][beginColumn][endColumn] = actor;
+-  },
+-
+-  /**
+-   * Delete the BreakpointActor from the given location in this
+-   * BreakpointActorMap.
+-   *
+-   * @param OriginalLocation location
+-   *        The location from which the BreakpointActor should be deleted.
+-   */
+-  deleteActor: function (location) {
+-    let { originalSourceActor, originalLine, originalColumn } = location;
+-
+-    let sourceActorID = originalSourceActor.actorID;
+-    let line = originalLine;
+-    let beginColumn = originalColumn ? originalColumn : 0;
+-    let endColumn = originalColumn ? originalColumn + 1 : Infinity;
+-
+-    if (this._actors[sourceActorID]) {
+-      if (this._actors[sourceActorID][line]) {
+-        if (this._actors[sourceActorID][line][beginColumn]) {
+-          if (this._actors[sourceActorID][line][beginColumn][endColumn]) {
+-            --this._size;
+-          }
+-          delete this._actors[sourceActorID][line][beginColumn][endColumn];
+-          if (Object.keys(this._actors[sourceActorID][line][beginColumn]).length === 0) {
+-            delete this._actors[sourceActorID][line][beginColumn];
+-          }
+-        }
+-        if (Object.keys(this._actors[sourceActorID][line]).length === 0) {
+-          delete this._actors[sourceActorID][line];
+-        }
+-      }
+-    }
+-  }
+-};
+-
+-exports.BreakpointActorMap = BreakpointActorMap;
+-
+-/**
+- * Keeps track of persistent sources across reloads and ties different
+- * source instances to the same actor id so that things like
+- * breakpoints survive reloads. ThreadSources uses this to force the
+- * same actorID on a SourceActor.
+- */
+-function SourceActorStore() {
+-  // source identifier --> actor id
+-  this._sourceActorIds = Object.create(null);
+-}
+-
+-SourceActorStore.prototype = {
+-  /**
+-   * Lookup an existing actor id that represents this source, if available.
+-   */
+-  getReusableActorId: function (source, originalUrl) {
+-    let url = this.getUniqueKey(source, originalUrl);
+-    if (url && url in this._sourceActorIds) {
+-      return this._sourceActorIds[url];
+-    }
+-    return null;
+-  },
+-
+-  /**
+-   * Update a source with an actorID.
+-   */
+-  setReusableActorId: function (source, originalUrl, actorID) {
+-    let url = this.getUniqueKey(source, originalUrl);
+-    if (url) {
+-      this._sourceActorIds[url] = actorID;
+-    }
+-  },
+-
+-  /**
+-   * Make a unique URL from a source that identifies it across reloads.
+-   */
+-  getUniqueKey: function (source, originalUrl) {
+-    if (originalUrl) {
+-      // Original source from a sourcemap.
+-      return originalUrl;
+-    }
+-
+-    return getSourceURL(source);
+-  }
+-};
+-
+-exports.SourceActorStore = SourceActorStore;
+-
+-/**
+- * Manages pushing event loops and automatically pops and exits them in the
+- * correct order as they are resolved.
+- *
+- * @param ThreadActor thread
+- *        The thread actor instance that owns this EventLoopStack.
+- * @param DebuggerServerConnection connection
+- *        The remote protocol connection associated with this event loop stack.
+- * @param Object hooks
+- *        An object with the following properties:
+- *          - url: The URL string of the debuggee we are spinning an event loop
+- *                 for.
+- *          - preNest: function called before entering a nested event loop
+- *          - postNest: function called after exiting a nested event loop
+- */
+-function EventLoopStack({ thread, connection, hooks }) {
+-  this._hooks = hooks;
+-  this._thread = thread;
+-  this._connection = connection;
+-}
+-
+-EventLoopStack.prototype = {
+-  /**
+-   * The number of nested event loops on the stack.
+-   */
+-  get size() {
+-    return xpcInspector.eventLoopNestLevel;
+-  },
+-
+-  /**
+-   * The URL of the debuggee who pushed the event loop on top of the stack.
+-   */
+-  get lastPausedUrl() {
+-    let url = null;
+-    if (this.size > 0) {
+-      try {
+-        url = xpcInspector.lastNestRequestor.url;
+-      } catch (e) {
+-        // The tab's URL getter may throw if the tab is destroyed by the time
+-        // this code runs, but we don't really care at this point.
+-        dumpn(e);
+-      }
+-    }
+-    return url;
+-  },
+-
+-  /**
+-   * The DebuggerServerConnection of the debugger who pushed the event loop on
+-   * top of the stack
+-   */
+-  get lastConnection() {
+-    return xpcInspector.lastNestRequestor._connection;
+-  },
+-
+-  /**
+-   * Push a new nested event loop onto the stack.
+-   *
+-   * @returns EventLoop
+-   */
+-  push: function () {
+-    return new EventLoop({
+-      thread: this._thread,
+-      connection: this._connection,
+-      hooks: this._hooks
+-    });
+-  }
+-};
+-
+-/**
+- * An object that represents a nested event loop. It is used as the nest
+- * requestor with nsIJSInspector instances.
+- *
+- * @param ThreadActor thread
+- *        The thread actor that is creating this nested event loop.
+- * @param DebuggerServerConnection connection
+- *        The remote protocol connection associated with this event loop.
+- * @param Object hooks
+- *        The same hooks object passed into EventLoopStack during its
+- *        initialization.
+- */
+-function EventLoop({ thread, connection, hooks }) {
+-  this._thread = thread;
+-  this._hooks = hooks;
+-  this._connection = connection;
+-
+-  this.enter = this.enter.bind(this);
+-  this.resolve = this.resolve.bind(this);
+-}
+-
+-EventLoop.prototype = {
+-  entered: false,
+-  resolved: false,
+-  get url() {
+-    return this._hooks.url;
+-  },
+-
+-  /**
+-   * Enter this nested event loop.
+-   */
+-  enter: function () {
+-    let nestData = this._hooks.preNest
+-      ? this._hooks.preNest()
+-      : null;
+-
+-    this.entered = true;
+-    xpcInspector.enterNestedEventLoop(this);
+-
+-    // Keep exiting nested event loops while the last requestor is resolved.
+-    if (xpcInspector.eventLoopNestLevel > 0) {
+-      const { resolved } = xpcInspector.lastNestRequestor;
+-      if (resolved) {
+-        xpcInspector.exitNestedEventLoop();
+-      }
+-    }
+-
+-    if (this._hooks.postNest) {
+-      this._hooks.postNest(nestData);
+-    }
+-  },
+-
+-  /**
+-   * Resolve this nested event loop.
+-   *
+-   * @returns boolean
+-   *          True if we exited this nested event loop because it was on top of
+-   *          the stack, false if there is another nested event loop above this
+-   *          one that hasn't resolved yet.
+-   */
+-  resolve: function () {
+-    if (!this.entered) {
+-      throw new Error("Can't resolve an event loop before it has been entered!");
+-    }
+-    if (this.resolved) {
+-      throw new Error("Already resolved this nested event loop!");
+-    }
+-    this.resolved = true;
+-    if (this === xpcInspector.lastNestRequestor) {
+-      xpcInspector.exitNestedEventLoop();
+-      return true;
+-    }
+-    return false;
+-  },
+-};
+-
+-/**
+  * JSD2 actors.
+  */
+ 
+ /**
+  * Creates a ThreadActor.
+  *
+  * ThreadActors manage a JSInspector object and manage execution/inspection
+  * of debuggees.
+@@ -2087,133 +1731,16 @@ exports.ThreadActor = ThreadActor;
+ function PauseActor(pool) {
+   this.pool = pool;
+ }
+ 
+ PauseActor.prototype = {
+   actorPrefix: "pause"
+ };
+ 
+-/**
+- * A base actor for any actors that should only respond receive messages in the
+- * paused state. Subclasses may expose a `threadActor` which is used to help
+- * determine when we are in a paused state. Subclasses should set their own
+- * "constructor" property if they want better error messages. You should never
+- * instantiate a PauseScopedActor directly, only through subclasses.
+- */
+-function PauseScopedActor() {
+-}
+-
+-/**
+- * A function decorator for creating methods to handle protocol messages that
+- * should only be received while in the paused state.
+- *
+- * @param method Function
+- *        The function we are decorating.
+- */
+-PauseScopedActor.withPaused = function (method) {
+-  return function () {
+-    if (this.isPaused()) {
+-      return method.apply(this, arguments);
+-    }
+-    return this._wrongState();
+-  };
+-};
+-
+-PauseScopedActor.prototype = {
+-
+-  /**
+-   * Returns true if we are in the paused state.
+-   */
+-  isPaused: function () {
+-    // When there is not a ThreadActor available (like in the webconsole) we
+-    // have to be optimistic and assume that we are paused so that we can
+-    // respond to requests.
+-    return this.threadActor ? this.threadActor.state === "paused" : true;
+-  },
+-
+-  /**
+-   * Returns the wrongState response packet for this actor.
+-   */
+-  _wrongState: function () {
+-    return {
+-      error: "wrongState",
+-      message: this.constructor.name +
+-        " actors can only be accessed while the thread is paused."
+-    };
+-  }
+-};
+-
+-/**
+- * Creates a pause-scoped actor for the specified object.
+- * @see ObjectActor
+- */
+-function PauseScopedObjectActor(obj, hooks) {
+-  ObjectActor.call(this, obj, hooks);
+-  this.hooks.promote = hooks.promote;
+-  this.hooks.isThreadLifetimePool = hooks.isThreadLifetimePool;
+-}
+-
+-PauseScopedObjectActor.prototype = Object.create(PauseScopedActor.prototype);
+-
+-Object.assign(PauseScopedObjectActor.prototype, ObjectActor.prototype);
+-
+-Object.assign(PauseScopedObjectActor.prototype, {
+-  constructor: PauseScopedObjectActor,
+-  actorPrefix: "pausedobj",
+-
+-  onOwnPropertyNames:
+-    PauseScopedActor.withPaused(ObjectActor.prototype.onOwnPropertyNames),
+-
+-  onPrototypeAndProperties:
+-    PauseScopedActor.withPaused(ObjectActor.prototype.onPrototypeAndProperties),
+-
+-  onPrototype: PauseScopedActor.withPaused(ObjectActor.prototype.onPrototype),
+-  onProperty: PauseScopedActor.withPaused(ObjectActor.prototype.onProperty),
+-  onDecompile: PauseScopedActor.withPaused(ObjectActor.prototype.onDecompile),
+-
+-  onDisplayString:
+-    PauseScopedActor.withPaused(ObjectActor.prototype.onDisplayString),
+-
+-  onParameterNames:
+-    PauseScopedActor.withPaused(ObjectActor.prototype.onParameterNames),
+-
+-  /**
+-   * Handle a protocol request to promote a pause-lifetime grip to a
+-   * thread-lifetime grip.
+-   *
+-   * @param request object
+-   *        The protocol request object.
+-   */
+-  onThreadGrip: PauseScopedActor.withPaused(function (request) {
+-    this.hooks.promote();
+-    return {};
+-  }),
+-
+-  /**
+-   * Handle a protocol request to release a thread-lifetime grip.
+-   *
+-   * @param request object
+-   *        The protocol request object.
+-   */
+-  onRelease: PauseScopedActor.withPaused(function (request) {
+-    if (this.hooks.isThreadLifetimePool()) {
+-      return { error: "notReleasable",
+-               message: "Only thread-lifetime actors can be released." };
+-    }
+-
+-    this.release();
+-    return {};
+-  }),
+-});
+-
+-Object.assign(PauseScopedObjectActor.prototype.requestTypes, {
+-  "threadGrip": PauseScopedObjectActor.prototype.onThreadGrip,
+-});
+-
+ function hackDebugger(Debugger) {
+   // TODO: Improve native code instead of hacking on top of it
+ 
+   /**
+    * Override the toString method in order to get more meaningful script output
+    * for debugging the debugger.
+    */
+   Debugger.Script.prototype.toString = function () {
+diff --git a/devtools/server/actors/utils/breakpoint-actor-map.js b/devtools/server/actors/utils/breakpoint-actor-map.js
+new file mode 100644
+--- /dev/null
++++ b/devtools/server/actors/utils/breakpoint-actor-map.js
+@@ -0,0 +1,173 @@
++/* -*- indent-tabs-mode: nil; js-indent-level: 2; 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 { OriginalLocation } = require("devtools/server/actors/common");
++
++/**
++ * A BreakpointActorMap is a map from locations to instances of BreakpointActor.
++ */
++function BreakpointActorMap() {
++  this._size = 0;
++  this._actors = {};
++}
++
++BreakpointActorMap.prototype = {
++  /**
++   * Return the number of BreakpointActors in this BreakpointActorMap.
++   *
++   * @returns Number
++   *          The number of BreakpointActor in this BreakpointActorMap.
++   */
++  get size() {
++    return this._size;
++  },
++
++  /**
++   * Generate all BreakpointActors that match the given location in
++   * this BreakpointActorMap.
++   *
++   * @param OriginalLocation location
++   *        The location for which matching BreakpointActors should be generated.
++   */
++  findActors: function* (location = new OriginalLocation()) {
++    // Fast shortcut for when we know we won't find any actors. Surprisingly
++    // enough, this speeds up refreshing when there are no breakpoints set by
++    // about 2x!
++    if (this.size === 0) {
++      return;
++    }
++
++    function* findKeys(obj, key) {
++      if (key !== undefined) {
++        if (key in obj) {
++          yield key;
++        }
++      } else {
++        for (key of Object.keys(obj)) {
++          yield key;
++        }
++      }
++    }
++
++    let query = {
++      sourceActorID: location.originalSourceActor
++                     ? location.originalSourceActor.actorID
++                     : undefined,
++      line: location.originalLine,
++    };
++
++    // If location contains a line, assume we are searching for a whole line
++    // breakpoint, and set begin/endColumn accordingly. Otherwise, we are
++    // searching for all breakpoints, so begin/endColumn should be left unset.
++    if (location.originalLine) {
++      query.beginColumn = location.originalColumn ? location.originalColumn : 0;
++      query.endColumn = location.originalColumn ? location.originalColumn + 1 : Infinity;
++    } else {
++      query.beginColumn = location.originalColumn ? query.originalColumn : undefined;
++      query.endColumn = location.originalColumn ? query.originalColumn + 1 : undefined;
++    }
++
++    for (let sourceActorID of findKeys(this._actors, query.sourceActorID)) {
++      let actor = this._actors[sourceActorID];
++      for (let line of findKeys(actor, query.line)) {
++        for (let beginColumn of findKeys(actor[line], query.beginColumn)) {
++          for (let endColumn of findKeys(actor[line][beginColumn],
++               query.endColumn)) {
++            yield actor[line][beginColumn][endColumn];
++          }
++        }
++      }
++    }
++  },
++
++  /**
++   * Return the BreakpointActor at the given location in this
++   * BreakpointActorMap.
++   *
++   * @param OriginalLocation location
++   *        The location for which the BreakpointActor should be returned.
++   *
++   * @returns BreakpointActor actor
++   *          The BreakpointActor at the given location.
++   */
++  getActor: function (originalLocation) {
++    for (let actor of this.findActors(originalLocation)) {
++      return actor;
++    }
++
++    return null;
++  },
++
++  /**
++   * Set the given BreakpointActor to the given location in this
++   * BreakpointActorMap.
++   *
++   * @param OriginalLocation location
++   *        The location to which the given BreakpointActor should be set.
++   *
++   * @param BreakpointActor actor
++   *        The BreakpointActor to be set to the given location.
++   */
++  setActor: function (location, actor) {
++    let { originalSourceActor, originalLine, originalColumn } = location;
++
++    let sourceActorID = originalSourceActor.actorID;
++    let line = originalLine;
++    let beginColumn = originalColumn ? originalColumn : 0;
++    let endColumn = originalColumn ? originalColumn + 1 : Infinity;
++
++    if (!this._actors[sourceActorID]) {
++      this._actors[sourceActorID] = [];
++    }
++    if (!this._actors[sourceActorID][line]) {
++      this._actors[sourceActorID][line] = [];
++    }
++    if (!this._actors[sourceActorID][line][beginColumn]) {
++      this._actors[sourceActorID][line][beginColumn] = [];
++    }
++    if (!this._actors[sourceActorID][line][beginColumn][endColumn]) {
++      ++this._size;
++    }
++    this._actors[sourceActorID][line][beginColumn][endColumn] = actor;
++  },
++
++  /**
++   * Delete the BreakpointActor from the given location in this
++   * BreakpointActorMap.
++   *
++   * @param OriginalLocation location
++   *        The location from which the BreakpointActor should be deleted.
++   */
++  deleteActor: function (location) {
++    let { originalSourceActor, originalLine, originalColumn } = location;
++
++    let sourceActorID = originalSourceActor.actorID;
++    let line = originalLine;
++    let beginColumn = originalColumn ? originalColumn : 0;
++    let endColumn = originalColumn ? originalColumn + 1 : Infinity;
++
++    if (this._actors[sourceActorID]) {
++      if (this._actors[sourceActorID][line]) {
++        if (this._actors[sourceActorID][line][beginColumn]) {
++          if (this._actors[sourceActorID][line][beginColumn][endColumn]) {
++            --this._size;
++          }
++          delete this._actors[sourceActorID][line][beginColumn][endColumn];
++          if (Object.keys(this._actors[sourceActorID][line][beginColumn]).length === 0) {
++            delete this._actors[sourceActorID][line][beginColumn];
++          }
++        }
++        if (Object.keys(this._actors[sourceActorID][line]).length === 0) {
++          delete this._actors[sourceActorID][line];
++        }
++      }
++    }
++  }
++};
++
++exports.BreakpointActorMap = BreakpointActorMap;
+diff --git a/devtools/server/actors/utils/event-loop.js b/devtools/server/actors/utils/event-loop.js
+new file mode 100644
+--- /dev/null
++++ b/devtools/server/actors/utils/event-loop.js
+@@ -0,0 +1,157 @@
++/* -*- indent-tabs-mode: nil; js-indent-level: 2; 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 xpcInspector = require("xpcInspector");
++const DevToolsUtils = require("devtools/shared/DevToolsUtils");
++const { dumpn } = DevToolsUtils;
++
++/**
++ * Manages pushing event loops and automatically pops and exits them in the
++ * correct order as they are resolved.
++ *
++ * @param ThreadActor thread
++ *        The thread actor instance that owns this EventLoopStack.
++ * @param DebuggerServerConnection connection
++ *        The remote protocol connection associated with this event loop stack.
++ * @param Object hooks
++ *        An object with the following properties:
++ *          - url: The URL string of the debuggee we are spinning an event loop
++ *                 for.
++ *          - preNest: function called before entering a nested event loop
++ *          - postNest: function called after exiting a nested event loop
++ */
++function EventLoopStack({ thread, connection, hooks }) {
++  this._hooks = hooks;
++  this._thread = thread;
++  this._connection = connection;
++}
++
++EventLoopStack.prototype = {
++  /**
++   * The number of nested event loops on the stack.
++   */
++  get size() {
++    return xpcInspector.eventLoopNestLevel;
++  },
++
++  /**
++   * The URL of the debuggee who pushed the event loop on top of the stack.
++   */
++  get lastPausedUrl() {
++    let url = null;
++    if (this.size > 0) {
++      try {
++        url = xpcInspector.lastNestRequestor.url;
++      } catch (e) {
++        // The tab's URL getter may throw if the tab is destroyed by the time
++        // this code runs, but we don't really care at this point.
++        dumpn(e);
++      }
++    }
++    return url;
++  },
++
++  /**
++   * The DebuggerServerConnection of the debugger who pushed the event loop on
++   * top of the stack
++   */
++  get lastConnection() {
++    return xpcInspector.lastNestRequestor._connection;
++  },
++
++  /**
++   * Push a new nested event loop onto the stack.
++   *
++   * @returns EventLoop
++   */
++  push: function () {
++    return new EventLoop({
++      thread: this._thread,
++      connection: this._connection,
++      hooks: this._hooks
++    });
++  }
++};
++
++/**
++ * An object that represents a nested event loop. It is used as the nest
++ * requestor with nsIJSInspector instances.
++ *
++ * @param ThreadActor thread
++ *        The thread actor that is creating this nested event loop.
++ * @param DebuggerServerConnection connection
++ *        The remote protocol connection associated with this event loop.
++ * @param Object hooks
++ *        The same hooks object passed into EventLoopStack during its
++ *        initialization.
++ */
++function EventLoop({ thread, connection, hooks }) {
++  this._thread = thread;
++  this._hooks = hooks;
++  this._connection = connection;
++
++  this.enter = this.enter.bind(this);
++  this.resolve = this.resolve.bind(this);
++}
++
++EventLoop.prototype = {
++  entered: false,
++  resolved: false,
++  get url() {
++    return this._hooks.url;
++  },
++
++  /**
++   * Enter this nested event loop.
++   */
++  enter: function () {
++    let nestData = this._hooks.preNest
++      ? this._hooks.preNest()
++      : null;
++
++    this.entered = true;
++    xpcInspector.enterNestedEventLoop(this);
++
++    // Keep exiting nested event loops while the last requestor is resolved.
++    if (xpcInspector.eventLoopNestLevel > 0) {
++      const { resolved } = xpcInspector.lastNestRequestor;
++      if (resolved) {
++        xpcInspector.exitNestedEventLoop();
++      }
++    }
++
++    if (this._hooks.postNest) {
++      this._hooks.postNest(nestData);
++    }
++  },
++
++  /**
++   * Resolve this nested event loop.
++   *
++   * @returns boolean
++   *          True if we exited this nested event loop because it was on top of
++   *          the stack, false if there is another nested event loop above this
++   *          one that hasn't resolved yet.
++   */
++  resolve: function () {
++    if (!this.entered) {
++      throw new Error("Can't resolve an event loop before it has been entered!");
++    }
++    if (this.resolved) {
++      throw new Error("Already resolved this nested event loop!");
++    }
++    this.resolved = true;
++    if (this === xpcInspector.lastNestRequestor) {
++      xpcInspector.exitNestedEventLoop();
++      return true;
++    }
++    return false;
++  },
++};
++
++exports.EventLoopStack = EventLoopStack;
+diff --git a/devtools/server/actors/utils/moz.build b/devtools/server/actors/utils/moz.build
+--- a/devtools/server/actors/utils/moz.build
++++ b/devtools/server/actors/utils/moz.build
+@@ -3,16 +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/.
+ 
+ DevToolsModules(
+     'actor-registry-utils.js',
+     'audionodes.json',
+     'automation-timeline.js',
++    'breakpoint-actor-map.js',
+     'css-grid-utils.js',
++    'event-loop.js',
+     'make-debugger.js',
+     'map-uri-to-addon-id.js',
+     'shapes-geometry-utils.js',
++    'source-actor-store.js',
+     'stack.js',
+     'TabSources.js',
+     'walker-search.js',
+ )
+diff --git a/devtools/server/actors/utils/source-actor-store.js b/devtools/server/actors/utils/source-actor-store.js
+new file mode 100644
+--- /dev/null
++++ b/devtools/server/actors/utils/source-actor-store.js
+@@ -0,0 +1,57 @@
++/* -*- indent-tabs-mode: nil; js-indent-level: 2; 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";
++
++loader.lazyRequireGetter(this, "getSourceURL", "devtools/server/actors/source", true);
++
++/**
++ * Keeps track of persistent sources across reloads and ties different
++ * source instances to the same actor id so that things like
++ * breakpoints survive reloads. ThreadSources uses this to force the
++ * same actorID on a SourceActor.
++ */
++function SourceActorStore() {
++  // source identifier --> actor id
++  this._sourceActorIds = Object.create(null);
++}
++
++SourceActorStore.prototype = {
++  /**
++   * Lookup an existing actor id that represents this source, if available.
++   */
++  getReusableActorId: function (source, originalUrl) {
++    let url = this.getUniqueKey(source, originalUrl);
++    if (url && url in this._sourceActorIds) {
++      return this._sourceActorIds[url];
++    }
++    return null;
++  },
++
++  /**
++   * Update a source with an actorID.
++   */
++  setReusableActorId: function (source, originalUrl, actorID) {
++    let url = this.getUniqueKey(source, originalUrl);
++    if (url) {
++      this._sourceActorIds[url] = actorID;
++    }
++  },
++
++  /**
++   * Make a unique URL from a source that identifies it across reloads.
++   */
++  getUniqueKey: function (source, originalUrl) {
++    if (originalUrl) {
++      // Original source from a sourcemap.
++      return originalUrl;
++    }
++
++    return getSourceURL(source);
++  }
++};
++
++exports.SourceActorStore = SourceActorStore;
+diff --git a/devtools/server/actors/webconsole.js b/devtools/server/actors/webconsole.js
+--- a/devtools/server/actors/webconsole.js
++++ b/devtools/server/actors/webconsole.js
+@@ -6,17 +6,17 @@
+ 
+ "use strict";
+ 
+ /* global XPCNativeWrapper */
+ 
+ const Services = require("Services");
+ const { Cc, Ci, Cu } = require("chrome");
+ const { DebuggerServer, ActorPool } = require("devtools/server/main");
+-const { ThreadActor } = require("devtools/server/actors/script");
++const { ThreadActor } = require("devtools/server/actors/thread");
+ const { ObjectActor, LongStringActor, createValueGrip, stringIsLong } = require("devtools/server/actors/object");
+ const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+ const ErrorDocs = require("devtools/server/actors/errordocs");
+ 
+ loader.lazyRequireGetter(this, "NetworkMonitor", "devtools/shared/webconsole/network-monitor", true);
+ loader.lazyRequireGetter(this, "NetworkMonitorChild", "devtools/shared/webconsole/network-monitor", true);
+ loader.lazyRequireGetter(this, "ConsoleProgressListener", "devtools/shared/webconsole/network-monitor", true);
+ loader.lazyRequireGetter(this, "StackTraceCollector", "devtools/shared/webconsole/network-monitor", true);
+diff --git a/devtools/server/actors/webextension.js b/devtools/server/actors/webextension.js
+--- a/devtools/server/actors/webextension.js
++++ b/devtools/server/actors/webextension.js
+@@ -6,17 +6,17 @@
+ 
+ const { Ci, Cu, Cc } = require("chrome");
+ const Services = require("Services");
+ 
+ const { ChromeActor } = require("./chrome");
+ const makeDebugger = require("./utils/make-debugger");
+ 
+ loader.lazyRequireGetter(this, "mapURIToAddonID", "devtools/server/actors/utils/map-uri-to-addon-id");
+-loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/script", true);
++loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/thread", true);
+ 
+ const FALLBACK_DOC_MESSAGE = "Your addon does not have any document opened yet.";
+ 
+ /**
+  * Creates a TabActor for debugging all the contexts associated to a target WebExtensions
+  * add-on running in a child extension process.
+  * Most of the implementation is inherited from ChromeActor (which inherits most of its
+  * implementation from TabActor).
+diff --git a/devtools/server/tests/unit/test_breakpoint-actor-map.js b/devtools/server/tests/unit/test_breakpoint-actor-map.js
+--- a/devtools/server/tests/unit/test_breakpoint-actor-map.js
++++ b/devtools/server/tests/unit/test_breakpoint-actor-map.js
+@@ -1,17 +1,17 @@
+ /* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
+ /* Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ // Test the functionality of the BreakpointActorMap object.
+ 
+-const { BreakpointActorMap } = require("devtools/server/actors/script");
++const { BreakpointActorMap } = require("devtools/server/actors/utils/breakpoint-actor-map");
+ 
+ function run_test() {
+   test_get_actor();
+   test_set_actor();
+   test_delete_actor();
+   test_find_actors();
+   test_duplicate_actors();
+ }
+diff --git a/devtools/server/tests/unit/testactors.js b/devtools/server/tests/unit/testactors.js
+--- a/devtools/server/tests/unit/testactors.js
++++ b/devtools/server/tests/unit/testactors.js
+@@ -1,16 +1,16 @@
+ /* 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 { RootActor } = require("devtools/server/actors/root");
+-const { ThreadActor } = require("devtools/server/actors/script");
++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) {
+   gTestGlobals.push(global);
+ };
+diff --git a/devtools/server/worker.js b/devtools/server/worker.js
+--- a/devtools/server/worker.js
++++ b/devtools/server/worker.js
+@@ -25,17 +25,17 @@ this.rpc = function (method, ...params) 
+   rpcDeferreds[id] = deferred;
+   return deferred.promise;
+ };
+ 
+ loadSubScript("resource://devtools/shared/worker/loader.js");
+ 
+ var defer = worker.require("devtools/shared/defer");
+ var { ActorPool } = worker.require("devtools/server/actors/common");
+-var { ThreadActor } = worker.require("devtools/server/actors/script");
++var { ThreadActor } = worker.require("devtools/server/actors/thread");
+ var { WebConsoleActor } = worker.require("devtools/server/actors/webconsole");
+ var { TabSources } = worker.require("devtools/server/actors/utils/TabSources");
+ var makeDebugger = worker.require("devtools/server/actors/utils/make-debugger");
+ var { DebuggerServer } = worker.require("devtools/server/main");
+ 
+ DebuggerServer.init();
+ DebuggerServer.createRootActor = function () {
+   throw new Error("Should never get here!");
+diff --git a/devtools/shared/security/tests/unit/testactors.js b/devtools/shared/security/tests/unit/testactors.js
+--- a/devtools/shared/security/tests/unit/testactors.js
++++ b/devtools/shared/security/tests/unit/testactors.js
+@@ -1,17 +1,17 @@
+ /* 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 { RootActor } = require("devtools/server/actors/root");
+-const { ThreadActor } = require("devtools/server/actors/script");
++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);
+ };
+ 
+diff --git a/devtools/shared/transport/tests/unit/head_dbg.js b/devtools/shared/transport/tests/unit/head_dbg.js
+--- a/devtools/shared/transport/tests/unit/head_dbg.js
++++ b/devtools/shared/transport/tests/unit/head_dbg.js
+@@ -89,17 +89,17 @@ var listener = {
+ var consoleService = Cc["@mozilla.org/consoleservice;1"]
+                      .getService(Ci.nsIConsoleService);
+ consoleService.registerListener(listener);
+ 
+ /**
+  * Initialize the testing debugger server.
+  */
+ function initTestDebuggerServer() {
+-  DebuggerServer.registerModule("devtools/server/actors/script", {
++  DebuggerServer.registerModule("devtools/server/actors/thread", {
+     prefix: "script",
+     constructor: "ScriptActor",
+     type: { global: true, tab: true }
+   });
+   DebuggerServer.registerModule("xpcshell-test/testactors");
+   // Allow incoming connections.
+   DebuggerServer.init();
+ }
+diff --git a/devtools/shared/transport/tests/unit/testactors.js b/devtools/shared/transport/tests/unit/testactors.js
+--- a/devtools/shared/transport/tests/unit/testactors.js
++++ b/devtools/shared/transport/tests/unit/testactors.js
+@@ -1,16 +1,16 @@
+ /* 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 { RootActor } = require("devtools/server/actors/root");
+-const { ThreadActor } = require("devtools/server/actors/script");
++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);
+ };
+ 

+ 30 - 0
mozilla-release/patches/series

@@ -5188,6 +5188,7 @@ PPPPPPP-NSSgetentropy.patch
 1854076-11505.patch
 1721612-11506.patch
 1386275-57a1.patch
+1394235-57a1.patch
 1250832-58a1.patch
 1394559-58a1.patch
 1304328-58a1.patch
@@ -5195,6 +5196,7 @@ PPPPPPP-NSSgetentropy.patch
 1403175-2-58a1.patch
 1403489-58a1.patch
 1403831-58a1.patch
+1401343-58a1.patch
 1402394-58a1.patch
 1402237-58a1.patch
 1402262-58a1.patch
@@ -5251,6 +5253,9 @@ PPPPPPP-NSSgetentropy.patch
 1408321-58a1.patch
 1408845-58a1.patch
 1406100-58a1.patch
+1407426-1-58a1.patch
+1407426-2-58a1.patch
+1416105-58a1.patch
 1408870-58a1.patch
 1407550-58a1.patch
 1408872-58a1.patch
@@ -5276,6 +5281,7 @@ PPPPPPP-NSSgetentropy.patch
 1251836-58a1.patch
 1410119-58a1.patch
 1411202-58a1.patch
+1411199-58a1.patch
 1409064-58a1.patch
 1411531-58a1.patch
 1408921-58a1.patch
@@ -5297,6 +5303,7 @@ PPPPPPP-NSSgetentropy.patch
 1412269-58a1.patch
 1360457-58a1.patch
 1412311-58a1.patch
+1411920-58a1.patch
 1413941-58a1.patch
 1413829-58a1.patch
 1407561-58a1.patch
@@ -5328,7 +5335,9 @@ PPPPPPP-NSSgetentropy.patch
 1353716-59a1.patch
 1417035-59a1.patch
 1408970-59a1.patch
+1417444-59a1.patch
 1371936-59a1.patch
+1405008-59a1.patch
 1408182-59a1.patch
 1411889-59a1.patch
 1372115-59a1.patch
@@ -5343,12 +5352,21 @@ PPPPPPP-NSSgetentropy.patch
 1416201-3-59a1.patch
 1420037-1-59a1.patch
 1420037-2-59a1.patch
+1416711-1-59a1.patch
+1416711-2-59a1.patch
+1416711-3-59a1.patch
+1416711-4-59a1.patch
+1416711-5-59a1.patch
+1416711-6-59a1.patch
+1416928-1-59a1.patch
+1416928-2-59a1.patch
 1420711-59a1.patch
 1416394-59a1.patch
 1420451-59a1.patch
 1419471-59a1.patch
 1421242-59a1.patch
 1404928-59a1.patch
+1411565-59a1.patch
 1403536-59a1.patch
 1418928-59a1.patch
 1419367-59a1.patch
@@ -5356,6 +5374,9 @@ PPPPPPP-NSSgetentropy.patch
 1419369-59a1.patch
 1220758-59a1.patch
 1423502-59a1.patch
+1386613-1-59a1.patch
+1386613-2-59a1.patch
+1386613-3-59a1.patch
 1419401-59a1.patch
 1424658-59a1.patch
 1424916-59a1.patch
@@ -5373,13 +5394,22 @@ PPPPPPP-NSSgetentropy.patch
 1427184-59a1.patch
 1424722-59a1.patch
 1426908-59a1.patch
+1151156-59a1.patch
 1396434-59a1.patch
 1427077-59a1.patch
 1428777-59a1.patch
 1419370-59a1.patch
 1429721-59a1.patch
 1429365-59a1.patch
+1430799-59a1.patch
+1272774-1-60a1.patch
+1272774-2-60a1.patch
+1272774-3-60a1.patch
+1272774-4-60a1.patch
+1272774-5-60a1.patch
+1435187-60a1.patch
 1439242-60a1.patch
+1363061-60a1.patch
 1431698-1-60a1.patch
 1431698-2-60a1.patch
 1658308-1-83a1.patch