Bug 1315044 - Prevent loading multiple loaders and debugger servers when creating multiple ContentActors. r=jryans draft
authorAlexandre Poirot <poirot.alex@gmail.com>
Fri, 04 Nov 2016 08:04:21 -0700
changeset 439042 622dfeeb720cde802a0fff228b87f5ee15478447
parent 439041 a55dc87844cb16d86a1cab7a4b2c738fee2d4fbc
child 537060 99658998cfdebc445533aac4cb367eebcd0d82af
push id35889
push userbmo:poirot.alex@gmail.com
push dateTue, 15 Nov 2016 10:25:33 +0000
reviewersjryans
bugs1315044
milestone52.0a1
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>