Bug 1240912 - Convert Network Monitor to setupInParent. r=ochameau draft
authorJ. Ryan Stinnett <jryans@gmail.com>
Fri, 05 Aug 2016 16:00:36 -0500
changeset 402167 eec7e8b15d9baccc91105f08c526e1a2ed44912d
parent 402166 80142b4687b4c3f8916e62812fb8defbb7cbaa6b
child 402168 26b2f193a08e683d9dff466ad7d88db27c77a739
push id26614
push userbmo:jryans@gmail.com
push dateThu, 18 Aug 2016 01:07:09 +0000
reviewersochameau
bugs1240912
milestone51.0a1
Bug 1240912 - Convert Network Monitor to setupInParent. r=ochameau MozReview-Commit-ID: 2fqsnR7IMWO
devtools/server/actors/webconsole.js
devtools/server/main.js
devtools/shared/webconsole/network-monitor.js
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -611,17 +611,17 @@ WebConsoleActor.prototype =
 
             let processBoundary = Services.appinfo.processType !=
                                   Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
             if ((appId || messageManager) && processBoundary) {
               // Start a network monitor in the parent process to listen to
               // most requests than happen in parent
               this.networkMonitor =
                 new NetworkMonitorChild(appId, this.parentActor.outerWindowID,
-                                        messageManager, this.parentActor.actorID, this);
+                                        messageManager, this.conn, this);
               this.networkMonitor.init();
               // Spawn also one in the child to listen to service workers
               this.networkMonitorChild = new NetworkMonitor({ window }, this);
               this.networkMonitorChild.init();
             } else {
               this.networkMonitor = new NetworkMonitor({ window }, this);
               this.networkMonitor.init();
             }
--- a/devtools/server/main.js
+++ b/devtools/server/main.js
@@ -1001,40 +1001,40 @@ var DebuggerServer = {
       if (!actor) {
         mm.removeMessageListener("debug:actor", onActorCreated);
       }
       DebuggerServer._childMessageManagers.delete(mm);
     };
 
     let actor, childTransport;
     let prefix = connection.allocID("child");
-    let netMonitor = null;
+    // Compute the same prefix that's used by DebuggerServerConnection
+    let connPrefix = prefix + "/";
 
     // provides hook to actor modules that need to exchange messages
     // between e10s parent and child processes
     let onSetupInParent = function (msg) {
       // We may have multiple connectToChild instance running for the same tab
-      // and need to filter the messages. Also the DebuggerServerConnection's
-      // prefix has an additional '/' and the end, so use `includes`.
-      if (!msg.json.prefix.includes(prefix)) {
+      // and need to filter the messages.
+      if (msg.json.prefix != connPrefix) {
         return false;
       }
 
       let { module, setupParent } = msg.json;
       let m;
 
       try {
         m = require(module);
 
         if (!setupParent in m) {
           dumpn(`ERROR: module '${module}' does not export 'setupParent'`);
           return false;
         }
 
-        m[setupParent]({ mm, prefix });
+        m[setupParent]({ mm, prefix: connPrefix });
 
         return true;
       } catch (e) {
         let errorMessage =
           "Exception during actor module setup running in the parent process: ";
         DevToolsUtils.reportException(errorMessage + e);
         dumpn(`ERROR: ${errorMessage}\n\t module: '${module}'\n\t ` +
               `setupParent: '${setupParent}'\n${DevToolsUtils.safeErrorString(e)}`);
@@ -1056,20 +1056,16 @@ var DebuggerServer = {
       };
       childTransport.ready();
 
       connection.setForwarding(prefix, childTransport);
 
       dumpn("establishing forwarding for app with prefix " + prefix);
 
       actor = msg.json.actor;
-
-      let { NetworkMonitorManager } = require("devtools/shared/webconsole/network-monitor");
-      netMonitor = new NetworkMonitorManager(mm, actor.actor);
-
       deferred.resolve(actor);
     }).bind(this);
 
     // Listen for browser frame swap
     let onBrowserSwap = ({ detail: newFrame }) => {
       // Remove listeners from old frame and mm
       untrackMessageManager();
       // Update frame and mm to point to the new browser frame
@@ -1083,17 +1079,18 @@ var DebuggerServer = {
       if (childTransport) {
         childTransport.swapBrowser(mm);
       }
     };
 
     let destroy = DevToolsUtils.makeInfallible(function () {
       // provides hook to actor modules that need to exchange messages
       // between e10s parent and child processes
-      DebuggerServer.emit("disconnected-from-child:" + prefix, { mm, prefix });
+      DebuggerServer.emit("disconnected-from-child:" + connPrefix,
+                          { mm, prefix: connPrefix });
 
       if (childTransport) {
         // If we have a child transport, the actor has already
         // been created. We need to stop using this message manager.
         childTransport.close();
         childTransport = null;
         connection.cancelForwarding(prefix);
 
@@ -1114,21 +1111,16 @@ var DebuggerServer = {
       if (actor) {
         // The ContentActor within the child process doesn't necessary
         // have time to uninitialize itself when the app is closed/killed.
         // So ensure telling the client that the related actor is detached.
         connection.send({ from: actor.actor, type: "tabDetached" });
         actor = null;
       }
 
-      if (netMonitor) {
-        netMonitor.destroy();
-        netMonitor = null;
-      }
-
       if (onDestroy) {
         onDestroy(mm);
       }
 
       // Cleanup all listeners
       untrackMessageManager();
       Services.obs.removeObserver(onMessageManagerClose, "message-manager-close");
       events.off(connection, "closed", destroy);
--- a/devtools/shared/webconsole/network-monitor.js
+++ b/devtools/shared/webconsole/network-monitor.js
@@ -11,16 +11,18 @@ const Services = require("Services");
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyRequireGetter(this, "NetworkHelper",
                          "devtools/shared/webconsole/network-helper");
 loader.lazyRequireGetter(this, "DevToolsUtils",
                          "devtools/shared/DevToolsUtils");
 loader.lazyRequireGetter(this, "flags",
                          "devtools/shared/flags");
+loader.lazyRequireGetter(this, "DebuggerServer",
+                         "devtools/server/main", true);
 loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
 loader.lazyServiceGetter(this, "gActivityDistributor",
                          "@mozilla.org/network/http-activity-distributor;1",
                          "nsIHttpActivityDistributor");
 
 // /////////////////////////////////////////////////////////////////////////////
 // Network logging
 // /////////////////////////////////////////////////////////////////////////////
@@ -1383,25 +1385,25 @@ NetworkMonitor.prototype = {
  *
  * @constructor
  * @param number appId
  *        The web appId of the child process.
  * @param number outerWindowID
  *        The outerWindowID of the TabActor's main window.
  * @param nsIMessageManager messageManager
  *        The nsIMessageManager to use to communicate with the parent process.
- * @param string connID
- *        The connection ID to use for send messages to the parent process.
+ * @param object DebuggerServerConnection
+ *        The RDP connection to the client.
  * @param object owner
  *        The WebConsoleActor that is listening for the network requests.
  */
-function NetworkMonitorChild(appId, outerWindowID, messageManager, connID, owner) {
+function NetworkMonitorChild(appId, outerWindowID, messageManager, conn, owner) {
   this.appId = appId;
   this.outerWindowID = outerWindowID;
-  this.connID = connID;
+  this.conn = conn;
   this.owner = owner;
   this._messageManager = messageManager;
   this._onNewEvent = this._onNewEvent.bind(this);
   this._onUpdateEvent = this._onUpdateEvent.bind(this);
   this._netEvents = new Map();
 }
 
 exports.NetworkMonitorChild = NetworkMonitorChild;
@@ -1414,31 +1416,36 @@ NetworkMonitorChild.prototype = {
 
   get saveRequestAndResponseBodies() {
     return this._saveRequestAndResponseBodies;
   },
 
   set saveRequestAndResponseBodies(val) {
     this._saveRequestAndResponseBodies = val;
 
-    this._messageManager.sendAsyncMessage("debug:netmonitor:" + this.connID, {
+    this._messageManager.sendAsyncMessage("debug:netmonitor", {
       action: "setPreferences",
       preferences: {
         saveRequestAndResponseBodies: this._saveRequestAndResponseBodies,
       },
     });
   },
 
   init: function () {
+    this.conn.setupInParent({
+      module: "devtools/shared/webconsole/network-monitor",
+      setupParent: "setupParentProcess"
+    });
+
     let mm = this._messageManager;
-    mm.addMessageListener("debug:netmonitor:" + this.connID + ":newEvent",
+    mm.addMessageListener("debug:netmonitor:newEvent",
                           this._onNewEvent);
-    mm.addMessageListener("debug:netmonitor:" + this.connID + ":updateEvent",
+    mm.addMessageListener("debug:netmonitor:updateEvent",
                           this._onUpdateEvent);
-    mm.sendAsyncMessage("debug:netmonitor:" + this.connID, {
+    mm.sendAsyncMessage("debug:netmonitor", {
       appId: this.appId,
       outerWindowID: this.outerWindowID,
       action: "start",
     });
   },
 
   _onNewEvent: DevToolsUtils.makeInfallible(function _onNewEvent(msg) {
     let {id, event} = msg.data;
@@ -1468,30 +1475,30 @@ NetworkMonitorChild.prototype = {
       return;
     }
     actor[method].apply(actor, args);
   }),
 
   destroy: function () {
     let mm = this._messageManager;
     try {
-      mm.removeMessageListener("debug:netmonitor:" + this.connID + ":newEvent",
+      mm.removeMessageListener("debug:netmonitor:newEvent",
                                this._onNewEvent);
-      mm.removeMessageListener("debug:netmonitor:" + this.connID +
-                               ":updateEvent",
+      mm.removeMessageListener("debug:netmonitor:updateEvent",
                                this._onUpdateEvent);
     } catch (e) {
       // On b2g, when registered to a new root docshell,
       // all message manager functions throw when trying to call them during
       // message-manager-disconnect event.
       // As there is no attribute/method on message manager to know
       // if they are still usable or not, we can only catch the exception...
     }
     this._netEvents.clear();
     this._messageManager = null;
+    this.conn = null;
     this.owner = null;
   },
 };
 
 /**
  * The NetworkEventActorProxy is used to send network request information from
  * the main process to the child app process. One proxy is used per request.
  * Similarly, one NetworkEventActor in the child app process is used per
@@ -1500,31 +1507,28 @@ NetworkMonitorChild.prototype = {
  * The child process has a NetworkMonitorChild instance that is listening for
  * all network logging from the main process. The net monitor shim is used to
  * proxy the data to the WebConsoleActor instance of the child process.
  *
  * @constructor
  * @param nsIMessageManager messageManager
  *        The message manager for the child app process. This is used for
  *        communication with the NetworkMonitorChild instance of the process.
- * @param string connID
- *        The connection ID to use to send messages to the child process.
  */
-function NetworkEventActorProxy(messageManager, connID) {
+function NetworkEventActorProxy(messageManager) {
   this.id = gSequenceId();
-  this.connID = connID;
   this.messageManager = messageManager;
 }
 exports.NetworkEventActorProxy = NetworkEventActorProxy;
 
 NetworkEventActorProxy.methodFactory = function (method) {
   return DevToolsUtils.makeInfallible(function () {
     let args = Array.slice(arguments);
     let mm = this.messageManager;
-    mm.sendAsyncMessage("debug:netmonitor:" + this.connID + ":updateEvent", {
+    mm.sendAsyncMessage("debug:netmonitor:updateEvent", {
       id: this.id,
       method: method,
       args: args,
     });
   }, "NetworkEventActorProxy." + method);
 };
 
 NetworkEventActorProxy.prototype = {
@@ -1534,17 +1538,17 @@ NetworkEventActorProxy.prototype = {
    *
    * @param object event
    *        Object describing the network request.
    * @return object
    *         This object.
    */
   init: DevToolsUtils.makeInfallible(function (event) {
     let mm = this.messageManager;
-    mm.sendAsyncMessage("debug:netmonitor:" + this.connID + ":newEvent", {
+    mm.sendAsyncMessage("debug:netmonitor:newEvent", {
       id: this.id,
       event: event,
     });
     return this;
   }),
 };
 
 (function () {
@@ -1554,42 +1558,54 @@ NetworkEventActorProxy.prototype = {
                  "addResponseCookies", "addResponseContent", "addEventTimings"];
   let factory = NetworkEventActorProxy.methodFactory;
   for (let method of methods) {
     NetworkEventActorProxy.prototype[method] = factory(method);
   }
 })();
 
 /**
- * The NetworkMonitor manager used by the Webapps actor in the main process.
- * This object uses the message manager to listen for requests from the child
- * process to start/stop the network monitor.
+ * This is triggered by the child calling `setupInParent` when the child's network monitor
+ * is starting up.  This initializes the parent process side of the monitoring.
+ */
+function setupParentProcess({ mm, prefix }) {
+  let networkMonitor = new NetworkMonitorParent(mm, prefix);
+  DebuggerServer.once("disconnected-from-child:" + prefix, () => {
+    networkMonitor.destroy();
+    networkMonitor = null;
+  });
+}
+
+exports.setupParentProcess = setupParentProcess;
+
+/**
+ * The NetworkMonitorParent runs in the parent process and uses the message manager to
+ * listen for requests from the child process to start/stop the network monitor.  Most
+ * request data is only available from the parent process, so that's why the network
+ * monitor needs to run there when debugging tabs that are in the child.
  *
- * @constructor
  * @param nsIMessageManager mm
  *        The message manager for the browser we're filtering on.
- * @param string id
- *        Instance identifier to use for messages.
+ * @param string prefix
+ *        The RDP connection prefix that uniquely identifies the connection.
  */
-function NetworkMonitorManager(mm, id) {
-  this.id = id;
+function NetworkMonitorParent(mm, prefix) {
   this.messageManager = mm;
   this.onNetMonitorMessage = this.onNetMonitorMessage.bind(this);
   this.onNetworkEvent = this.onNetworkEvent.bind(this);
 
-  mm.addMessageListener("debug:netmonitor:" + id, this.onNetMonitorMessage);
+  mm.addMessageListener("debug:netmonitor", this.onNetMonitorMessage);
 }
-exports.NetworkMonitorManager = NetworkMonitorManager;
 
-NetworkMonitorManager.prototype = {
+NetworkMonitorParent.prototype = {
   netMonitor: null,
   messageManager: null,
 
   /**
-   * Handler for "debug:monitor" messages received through the message manager
+   * Handler for "debug:netmonitor" messages received through the message manager
    * from the content process.
    *
    * @param object msg
    *        Message from the content.
    */
   onNetMonitorMessage: DevToolsUtils.makeInfallible(function (msg) {
     let {action} = msg.json;
     // Pipe network monitor data from parent to child via the message manager.
@@ -1632,25 +1648,25 @@ NetworkMonitorManager.prototype = {
    * NetworkMonitor instance.
    *
    * @param object event
    *        Object describing the network request.
    * @return object
    *         A NetworkEventActorProxy instance which is notified when further
    *         data about the request is available.
    */
-  onNetworkEvent: DevToolsUtils.makeInfallible(function _onNetworkEvent(event) {
-    return new NetworkEventActorProxy(this.messageManager, this.id).init(event);
+  onNetworkEvent: DevToolsUtils.makeInfallible(function (event) {
+    return new NetworkEventActorProxy(this.messageManager).init(event);
   }),
 
   destroy: function () {
     if (this.messageManager) {
       let mm = this.messageManager;
       this.messageManager = null;
-      mm.removeMessageListener("debug:netmonitor:" + this.id, this.onNetMonitorMessage);
+      mm.removeMessageListener("debug:netmonitor", this.onNetMonitorMessage);
     }
     this.frames = null;
 
     if (this.netMonitor) {
       this.netMonitor.destroy();
       this.netMonitor = null;
     }
   },