Bug 1315044 - Prevent loading multiple loaders and debugger servers when creating multiple ContentActors. r=jryans
authorAlexandre Poirot <poirot.alex@gmail.com>
Fri, 04 Nov 2016 08:04:21 -0700
changeset 322594 eca8f585871bbe8074fe5f4476f3157fc7848adf
parent 322593 f57c3052af5a813e8b25620ef6f343be8fe09b58
child 322595 39ac4382a2c019178604b90acd816753fe142908
push id30958
push usercbook@mozilla.com
push dateWed, 16 Nov 2016 13:04:28 +0000
treeherdermozilla-central@2598a93e2e1a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjryans
bugs1315044
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1315044 - Prevent loading multiple loaders and debugger servers when creating multiple ContentActors. r=jryans MozReview-Commit-ID: 4slVLBNdGyg
devtools/server/content-server.jsm
devtools/server/tests/mochitest/test_getProcess.html
--- a/devtools/server/content-server.jsm
+++ b/devtools/server/content-server.jsm
@@ -3,54 +3,77 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 
-const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
-const { DevToolsLoader } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-
 this.EXPORTED_SYMBOLS = ["init"];
 
-function init(msg) {
-  // 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.
-  let devtools = new DevToolsLoader();
-  devtools.invisibleToDebugger = true;
-  let { DebuggerServer, ActorPool } = devtools.require("devtools/server/main");
+let gLoader;
+
+function setupServer(mm) {
+  // Prevent spawning multiple server per process, even if the caller call us
+  // multiple times
+  if (gLoader) {
+    return gLoader;
+  }
+
+  // Lazy load Loader.jsm to prevent loading any devtools dependency too early.
+  let { DevToolsLoader } =
+    Cu.import("resource://devtools/shared/Loader.jsm", {});
+
+  // 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();
   }
 
   // In case of apps being loaded in parent process, DebuggerServer is already
   // initialized, but child specific actors are not registered.
   // Otherwise, for child process, we need to load actors the first
   // time we load child.js
   DebuggerServer.addChildActors();
 
+  // 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;
+  });
+
+  return gLoader;
+}
+
+function init(msg) {
   let mm = msg.target;
   mm.QueryInterface(Ci.nsISyncMessageSender);
   let prefix = msg.data.prefix;
 
-  // Connect both parent/child processes debugger servers RDP via message managers
+  // Setup a server if none started yet
+  let loader = setupServer(mm);
+
+  // Connect both parent/child processes debugger servers RDP via message
+  // managers
+  let { DebuggerServer } = loader.require("devtools/server/main");
   let conn = DebuggerServer.connectToParent(prefix, mm);
   conn.parentMessageManager = mm;
 
-  let { ChildProcessActor } = devtools.require("devtools/server/actors/child-process");
+  let { ChildProcessActor } =
+    loader.require("devtools/server/actors/child-process");
+  let { ActorPool } = loader.require("devtools/server/main");
   let actor = new ChildProcessActor(conn);
   let actorPool = new ActorPool(conn);
   actorPool.addActor(actor);
   conn.addActorPool(actorPool);
 
-  let response = {actor: actor.form()};
+  let response = { actor: actor.form() };
   mm.sendAsyncMessage("debug:content-process-actor", response);
-
-  mm.addMessageListener("debug:content-process-destroy", function onDestroy() {
-    mm.removeMessageListener("debug:content-process-destroy", onDestroy);
-
-    DebuggerServer.destroy();
-  });
 }
--- a/devtools/server/tests/mochitest/test_getProcess.html
+++ b/devtools/server/tests/mochitest/test_getProcess.html
@@ -15,16 +15,17 @@ Bug 1060093 - Test DebuggerServer.getPro
 
 let Cu = Components.utils;
 let Cc = Components.classes;
 let Ci = Components.interfaces;
 
 let {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 let {DebuggerClient} = require("devtools/shared/client/main");
 let {DebuggerServer} = require("devtools/server/main");
+let Services = require("Services");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
 
   SpecialPowers.pushPrefEnv({
     "set": [
       // Always log packets when running tests.
       ["devtools.debugger.log", true],
@@ -106,26 +107,52 @@ function runTests() {
 
   // Assert that calling client.getProcess against the same process id is
   // returning the same actor.
   function getProcessAgain(firstActor, id) {
     client.getProcess(id).then(response => {
       let actor = response.form;
       is(actor, firstActor,
          "Second call to getProcess with the same id returns the same form");
+      closeClient();
+    });
+  }
+
+  function processScript() {
+    let listener = function () {
+      Services.obs.removeObserver(listener, "sdk:loader:destroy", false);
+      sendAsyncMessage("test:getProcess-destroy", null);
+    };
+    Services.obs.addObserver(listener, "sdk:loader:destroy", false);
+  }
+
+  function closeClient() {
+    let onLoaderDestroyed = new Promise(done => {
+      let processListener = function () {
+        Services.ppmm.removeMessageListener("test:getProcess-destroy", processListener)
+        done();
+      };
+      Services.ppmm.addMessageListener("test:getProcess-destroy", processListener)
+    });
+    let script = "data:,(" + processScript + ")()";
+    Services.ppmm.loadProcessScript(script, true);
+    client.close();
+
+    onLoaderDestroyed.then(function () {
+      Services.ppmm.removeDelayedProcessScript(script);
+      info("Loader destroyed in the content process");
+
       cleanup();
     });
   }
 
   function cleanup() {
-    client.close().then(function () {
-      DebuggerServer.destroy();
-      iframe.remove();
-      SimpleTest.finish()
-    });
+    DebuggerServer.destroy();
+    iframe.remove();
+    SimpleTest.finish()
   }
 
   connect();
 }
 
 </script>
 </pre>
 </body>