Bug 862341 Part 1: Move the network request storage from the console frontend to the console client so the netmonitor can reuse it. r=vporof
authorPanos Astithas <past@mozilla.com>
Fri, 24 Apr 2015 21:38:09 +0300
changeset 242939 019d595855ed9a1b1052983cf00134e771c5d9ad
parent 242938 de4f4de880fd428dce92c94ef168532f57dff547
child 242940 cba10c0c94343d97b88d78242bafefaff0245ee9
push id28714
push userkwierso@gmail.com
push dateFri, 08 May 2015 17:29:48 +0000
treeherdermozilla-central@5e8adf0e7f2c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvporof
bugs862341
milestone40.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 862341 Part 1: Move the network request storage from the console frontend to the console client so the netmonitor can reuse it. r=vporof
browser/devtools/webconsole/test/head.js
browser/devtools/webconsole/webconsole.js
toolkit/devtools/webconsole/client.js
--- a/browser/devtools/webconsole/test/head.js
+++ b/browser/devtools/webconsole/test/head.js
@@ -10,17 +10,16 @@ let {console} = Cu.import("resource://gr
 let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 let {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
 let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let {require, TargetFactory} = devtools;
 let {Utils: WebConsoleUtils} = require("devtools/toolkit/webconsole/utils");
 let {Messages} = require("devtools/webconsole/console-output");
 const asyncStorage = require("devtools/toolkit/shared/async-storage");
 
-// promise._reportErrors = true; // please never leave me.
 //Services.prefs.setBoolPref("devtools.debugger.log", true);
 
 let gPendingOutputTest = 0;
 
 // The various categories of messages.
 const CATEGORY_NETWORK = 0;
 const CATEGORY_CSS = 1;
 const CATEGORY_JS = 2;
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -200,17 +200,16 @@ function WebConsoleFrame(aWebConsoleOwne
   this.owner = aWebConsoleOwner;
   this.hudId = this.owner.hudId;
   this.window = this.owner.iframeWindow;
 
   this._repeatNodes = {};
   this._outputQueue = [];
   this._itemDestroyQueue = [];
   this._pruneCategoriesQueue = {};
-  this._networkRequests = {};
   this.filterPrefs = {};
 
   this.output = new ConsoleOutput(this);
 
   this._toggleFilter = this._toggleFilter.bind(this);
   this._onPanelSelected = this._onPanelSelected.bind(this);
   this._flushMessageQueue = this._flushMessageQueue.bind(this);
   this._onToolboxPrefChanged = this._onToolboxPrefChanged.bind(this);
@@ -249,24 +248,16 @@ WebConsoleFrame.prototype = {
   /**
    * Holds the initialization promise object.
    * @private
    * @type object
    */
   _initDefer: null,
 
   /**
-   * Holds the network requests currently displayed by the Web Console. Each key
-   * represents the connection ID and the value is network request information.
-   * @private
-   * @type object
-   */
-  _networkRequests: null,
-
-  /**
    * Last time when we displayed any message in the output.
    *
    * @private
    * @type number
    *       Timestamp in milliseconds since the Unix epoch.
    */
   _lastOutputFlush: 0,
 
@@ -1523,29 +1514,24 @@ WebConsoleFrame.prototype = {
       node._objectActors = new Set([aPacket.message.actor]);
     }
     return node;
   },
 
   /**
    * Log network event.
    *
-   * @param object aActor
-   *        The network event actor to log.
+   * @param object networkInfo
+   *        The network request information to log.
    * @return nsIDOMElement|null
    *         The message element to display in the Web Console output.
    */
-  logNetEvent: function WCF_logNetEvent(aActor)
+  logNetEvent: function(networkInfo)
   {
-    let actorId = aActor.actor;
-    let networkInfo = this._networkRequests[actorId];
-    if (!networkInfo) {
-      return null;
-    }
-
+    let actorId = networkInfo.actor;
     let request = networkInfo.request;
     let clipboardText = request.method + " " + request.url;
     let severity = SEVERITY_LOG;
     if (networkInfo.isXHR) {
       clipboardText = request.method + " XHR " + request.url;
       severity = SEVERITY_INFO;
     }
     let mixedRequest =
@@ -1793,94 +1779,39 @@ WebConsoleFrame.prototype = {
     let node = this.createMessageNode(CATEGORY_JS, SEVERITY_WARNING,
                                       l10n.getStr("ConsoleAPIDisabled"));
     this.outputMessage(CATEGORY_JS, node);
   },
 
   /**
    * Handle the network events coming from the remote Web Console.
    *
-   * @param object aActor
-   *        The NetworkEventActor grip.
+   * @param object networkInfo
+   *        The network request information.
    */
-  handleNetworkEvent: function WCF_handleNetworkEvent(aActor)
+  handleNetworkEvent: function(networkInfo)
   {
-    let networkInfo = {
-      node: null,
-      actor: aActor.actor,
-      discardRequestBody: true,
-      discardResponseBody: true,
-      startedDateTime: aActor.startedDateTime,
-      request: {
-        url: aActor.url,
-        method: aActor.method,
-      },
-      isXHR: aActor.isXHR,
-      response: {},
-      timings: {},
-      updates: [], // track the list of network event updates
-      private: aActor.private,
-    };
-
-    this._networkRequests[aActor.actor] = networkInfo;
-    this.outputMessage(CATEGORY_NETWORK, this.logNetEvent, [aActor]);
+    this.outputMessage(CATEGORY_NETWORK, this.logNetEvent, [networkInfo]);
   },
 
   /**
    * Handle network event updates coming from the server.
    *
-   * @param string aActorId
-   *        The network event actor ID.
-   * @param string aType
-   *        Update type.
-   * @param object aPacket
+   * @param object networkInfo
+   *        The network request information.
+   * @param object packet
    *        Update details.
    */
-  handleNetworkEventUpdate:
-  function WCF_handleNetworkEventUpdate(aActorId, aType, aPacket)
+  handleNetworkEventUpdate: function(networkInfo, packet)
   {
-    let networkInfo = this._networkRequests[aActorId];
-    if (!networkInfo) {
-      return;
-    }
-
-    networkInfo.updates.push(aType);
-
-    switch (aType) {
-      case "requestHeaders":
-        networkInfo.request.headersSize = aPacket.headersSize;
-        break;
-      case "requestPostData":
-        networkInfo.discardRequestBody = aPacket.discardRequestBody;
-        networkInfo.request.bodySize = aPacket.dataSize;
-        break;
-      case "responseStart":
-        networkInfo.response.httpVersion = aPacket.response.httpVersion;
-        networkInfo.response.status = aPacket.response.status;
-        networkInfo.response.statusText = aPacket.response.statusText;
-        networkInfo.response.headersSize = aPacket.response.headersSize;
-        networkInfo.discardResponseBody = aPacket.response.discardResponseBody;
-        break;
-      case "responseContent":
-        networkInfo.response.content = {
-          mimeType: aPacket.mimeType,
-        };
-        networkInfo.response.bodySize = aPacket.contentSize;
-        networkInfo.discardResponseBody = aPacket.discardResponseBody;
-        break;
-      case "eventTimings":
-        networkInfo.totalTime = aPacket.totalTime;
-        break;
-    }
-
-    if (networkInfo.node && this._updateNetMessage(aActorId)) {
+    if (networkInfo.node && this._updateNetMessage(packet.from)) {
       this.emit("new-messages", new Set([{
         update: true,
         node: networkInfo.node,
-        response: aPacket,
+        response: packet,
       }]));
     }
 
     // For unit tests we pass the HTTP activity object to the test callback,
     // once requests complete.
     if (this.owner.lastFinishedRequestCallback &&
         networkInfo.updates.indexOf("responseContent") > -1 &&
         networkInfo.updates.indexOf("eventTimings") > -1) {
@@ -1895,17 +1826,17 @@ WebConsoleFrame.prototype = {
    * @private
    * @param string aActorId
    *        The network event actor ID for which you want to update the message.
    * @return boolean
    *         |true| if the message node was updated, or |false| otherwise.
    */
   _updateNetMessage: function WCF__updateNetMessage(aActorId)
   {
-    let networkInfo = this._networkRequests[aActorId];
+    let networkInfo = this.webConsoleClient.getNetworkRequest(aActorId);
     if (!networkInfo || !networkInfo.node) {
       return;
     }
 
     let messageNode = networkInfo.node;
     let updates = networkInfo.updates;
     let hasEventTimings = updates.indexOf("eventTimings") > -1;
     let hasResponseStart = updates.indexOf("responseStart") > -1;
@@ -2441,18 +2372,18 @@ WebConsoleFrame.prototype = {
     if (category == CATEGORY_NETWORK) {
       let connectionId = null;
       if (methodOrNode == this.logNetEvent) {
         connectionId = args[0].actor;
       }
       else if (typeof methodOrNode != "function") {
         connectionId = methodOrNode._connectionId;
       }
-      if (connectionId && connectionId in this._networkRequests) {
-        delete this._networkRequests[connectionId];
+      if (connectionId && this.webConsoleClient.hasNetworkRequest(connectionId)) {
+        this.webConsoleClient.removeNetworkRequest(connectionId);
         this._releaseObject(connectionId);
       }
     }
     else if (category == CATEGORY_WEBDEV &&
              methodOrNode == this.logConsoleAPIMessage) {
       args[0].arguments.forEach((aValue) => {
         if (WebConsoleUtils.isActorGrip(aValue)) {
           this._releaseObject(aValue.actor);
@@ -2519,17 +2450,17 @@ WebConsoleFrame.prototype = {
         aNode.category == CATEGORY_SECURITY) {
       let repeatNode = aNode.getElementsByClassName("message-repeats")[0];
       if (repeatNode && repeatNode._uid) {
         delete this._repeatNodes[repeatNode._uid];
       }
     }
     else if (aNode._connectionId &&
              aNode.category == CATEGORY_NETWORK) {
-      delete this._networkRequests[aNode._connectionId];
+      this.webConsoleClient.removeNetworkRequest(aNode._connectionId);
       this._releaseObject(aNode._connectionId);
     }
     else if (aNode.classList.contains("inlined-variables-view")) {
       let view = aNode._variablesView;
       if (view) {
         view.controller.releaseActors();
       }
       aNode._variablesView = null;
@@ -3013,17 +2944,17 @@ WebConsoleFrame.prototype = {
     gDevTools.off("pref-changed", this._onToolboxPrefChanged);
 
     this._repeatNodes = {};
     this._outputQueue.forEach(this._destroyItem, this);
     this._outputQueue = [];
     this._itemDestroyQueue.forEach(this._destroyItem, this);
     this._itemDestroyQueue = [];
     this._pruneCategoriesQueue = {};
-    this._networkRequests = {};
+    this.webConsoleClient.clearNetworkRequests();
 
     if (this._outputTimerInitialized) {
       this._outputTimerInitialized = false;
       this._outputTimer.cancel();
     }
     this._outputTimer = null;
     if (this.jsterm) {
       this.jsterm.destroy();
@@ -3967,17 +3898,17 @@ JSTerm.prototype = {
     let node;
     while ((node = outputNode.firstChild)) {
       hud.removeOutputMessage(node);
     }
 
     hud.groupDepth = 0;
     hud._outputQueue.forEach(hud._destroyItem, hud);
     hud._outputQueue = [];
-    hud._networkRequests = {};
+    this.webConsoleClient.clearNetworkRequests();
     hud._repeatNodes = {};
 
     if (aClearStorage) {
       this.webConsoleClient.clearMessagesCache();
     }
 
     this._sidebarDestroy();
 
@@ -5109,18 +5040,16 @@ WebConsoleConnectionProxy.prototype = {
       this._connectTimer = null;
     });
 
     let client = this.client = this.target.client;
 
     client.addListener("logMessage", this._onLogMessage);
     client.addListener("pageError", this._onPageError);
     client.addListener("consoleAPICall", this._onConsoleAPICall);
-    client.addListener("networkEvent", this._onNetworkEvent);
-    client.addListener("networkEventUpdate", this._onNetworkEventUpdate);
     client.addListener("fileActivity", this._onFileActivity);
     client.addListener("reflowActivity", this._onReflowActivity);
     client.addListener("lastPrivateContextExited", this._onLastPrivateContextExited);
     this.target.on("will-navigate", this._onTabNavigated);
     this.target.on("navigate", this._onTabNavigated);
 
     this._consoleActor = this.target.form.consoleActor;
     if (this.target.isTabActor) {
@@ -5175,16 +5104,18 @@ WebConsoleConnectionProxy.prototype = {
                      aResponse.message);
       this._connectDefer.reject(aResponse);
       return;
     }
 
     this.webConsoleClient = aWebConsoleClient;
 
     this._hasNativeConsoleAPI = aResponse.nativeConsoleAPI;
+    this.webConsoleClient.on("networkEvent", this._onNetworkEvent);
+    this.webConsoleClient.on("networkEventUpdate", this._onNetworkEventUpdate);
 
     let msgs = ["PageError", "ConsoleAPI"];
     this.webConsoleClient.getCachedMessages(msgs, this._onCachedMessages);
 
     this.owner._updateReflowActivityListener();
   },
 
   /**
@@ -5270,43 +5201,44 @@ WebConsoleConnectionProxy.prototype = {
     }
   },
 
   /**
    * The "networkEvent" message type handler. We redirect any message to
    * the UI for displaying.
    *
    * @private
-   * @param string aType
+   * @param string type
    *        Message type.
-   * @param object aPacket
-   *        The message received from the server.
+   * @param object networkInfo
+   *        The network request information.
    */
-  _onNetworkEvent: function WCCP__onNetworkEvent(aType, aPacket)
+  _onNetworkEvent: function(type, networkInfo)
   {
-    if (this.owner && aPacket.from == this._consoleActor) {
-      this.owner.handleNetworkEvent(aPacket.eventActor);
+    if (this.owner) {
+      this.owner.handleNetworkEvent(networkInfo);
     }
   },
 
   /**
    * The "networkEventUpdate" message type handler. We redirect any message to
    * the UI for displaying.
    *
    * @private
-   * @param string aType
+   * @param string type
    *        Message type.
-   * @param object aPacket
+   * @param object packet
    *        The message received from the server.
+   * @param object networkInfo
+   *        The network request information.
    */
-  _onNetworkEventUpdate: function WCCP__onNetworkEvenUpdatet(aType, aPacket)
+  _onNetworkEventUpdate: function(type, { packet, networkInfo })
   {
     if (this.owner) {
-      this.owner.handleNetworkEventUpdate(aPacket.from, aPacket.updateType,
-                                          aPacket);
+      this.owner.handleNetworkEventUpdate(networkInfo, packet);
     }
   },
 
   /**
    * The "fileActivity" message type handler. We redirect any message to
    * the UI for displaying.
    *
    * @private
@@ -5396,21 +5328,21 @@ WebConsoleConnectionProxy.prototype = {
     if (!this.client) {
       this._disconnecter.resolve(null);
       return this._disconnecter.promise;
     }
 
     this.client.removeListener("logMessage", this._onLogMessage);
     this.client.removeListener("pageError", this._onPageError);
     this.client.removeListener("consoleAPICall", this._onConsoleAPICall);
-    this.client.removeListener("networkEvent", this._onNetworkEvent);
-    this.client.removeListener("networkEventUpdate", this._onNetworkEventUpdate);
     this.client.removeListener("fileActivity", this._onFileActivity);
     this.client.removeListener("reflowActivity", this._onReflowActivity);
     this.client.removeListener("lastPrivateContextExited", this._onLastPrivateContextExited);
+    this.webConsoleClient.off("networkEvent", this._onNetworkEvent);
+    this.webConsoleClient.off("networkEventUpdate", this._onNetworkEventUpdate);
     this.target.off("will-navigate", this._onTabNavigated);
     this.target.off("navigate", this._onTabNavigated);
 
     this.client = null;
     this.webConsoleClient = null;
     this.target = null;
     this.connected = false;
     this.owner = null;
--- a/toolkit/devtools/webconsole/client.js
+++ b/toolkit/devtools/webconsole/client.js
@@ -3,16 +3,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 {Cc, Ci, Cu} = require("chrome");
 const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
+const EventEmitter = require("devtools/toolkit/event-emitter");
 
 loader.lazyImporter(this, "LongStringClient", "resource://gre/modules/devtools/dbg-client.jsm");
 
 /**
  * A WebConsoleClient is used as a front end for the WebConsoleActor that is
  * created on the server, hiding implementation details.
  *
  * @param object aDebuggerClient
@@ -23,47 +24,161 @@ loader.lazyImporter(this, "LongStringCli
  */
 function WebConsoleClient(aDebuggerClient, aResponse)
 {
   this._actor = aResponse.from;
   this._client = aDebuggerClient;
   this._longStrings = {};
   this.traits = aResponse.traits || {};
   this.events = [];
+  this._networkRequests = new Map();
 
   this.pendingEvaluationResults = new Map();
   this.onEvaluationResult = this.onEvaluationResult.bind(this);
+  this.onNetworkEvent = this._onNetworkEvent.bind(this);
+  this.onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
 
   this._client.addListener("evaluationResult", this.onEvaluationResult);
+  this._client.addListener("networkEvent", this.onNetworkEvent);
+  this._client.addListener("networkEventUpdate", this.onNetworkEventUpdate);
+  EventEmitter.decorate(this);
 }
 
 exports.WebConsoleClient = WebConsoleClient;
 
 WebConsoleClient.prototype = {
   _longStrings: null,
   traits: null,
 
+  /**
+   * Holds the network requests currently displayed by the Web Console. Each key
+   * represents the connection ID and the value is network request information.
+   * @private
+   * @type object
+   */
+  _networkRequests: null,
+
+  getNetworkRequest(actorId) {
+    return this._networkRequests.get(actorId);
+  },
+
+  hasNetworkRequest(actorId) {
+    return this._networkRequests.has(actorId);
+  },
+
+  removeNetworkRequest(actorId) {
+    this._networkRequests.delete(actorId);
+  },
+
   get actor() { return this._actor; },
 
   /**
+   * The "networkEvent" message type handler. We redirect any message to
+   * the UI for displaying.
+   *
+   * @private
+   * @param string type
+   *        Message type.
+   * @param object packet
+   *        The message received from the server.
+   */
+  _onNetworkEvent: function (type, packet)
+  {
+    if (packet.from == this._actor) {
+      let actor = packet.eventActor;
+      let networkInfo = {
+        node: null,
+        actor: actor.actor,
+        discardRequestBody: true,
+        discardResponseBody: true,
+        startedDateTime: actor.startedDateTime,
+        request: {
+          url: actor.url,
+          method: actor.method,
+        },
+        isXHR: actor.isXHR,
+        response: {},
+        timings: {},
+        updates: [], // track the list of network event updates
+        private: actor.private,
+      };
+      this._networkRequests.set(actor.actor, networkInfo);
+
+      this.emit("networkEvent", networkInfo);
+    }
+  },
+
+  /**
+   * The "networkEventUpdate" message type handler. We redirect any message to
+   * the UI for displaying.
+   *
+   * @private
+   * @param string type
+   *        Message type.
+   * @param object packet
+   *        The message received from the server.
+   */
+  _onNetworkEventUpdate: function (type, packet)
+  {
+    let networkInfo = this.getNetworkRequest(packet.from);
+    if (!networkInfo) {
+      return;
+    }
+
+    networkInfo.updates.push(packet.updateType);
+
+    switch (packet.updateType) {
+      case "requestHeaders":
+        networkInfo.request.headersSize = packet.headersSize;
+        break;
+      case "requestPostData":
+        networkInfo.discardRequestBody = packet.discardRequestBody;
+        networkInfo.request.bodySize = packet.dataSize;
+        break;
+      case "responseStart":
+        networkInfo.response.httpVersion = packet.response.httpVersion;
+        networkInfo.response.status = packet.response.status;
+        networkInfo.response.statusText = packet.response.statusText;
+        networkInfo.response.headersSize = packet.response.headersSize;
+        networkInfo.discardResponseBody = packet.response.discardResponseBody;
+        break;
+      case "responseContent":
+        networkInfo.response.content = {
+          mimeType: packet.mimeType,
+        };
+        networkInfo.response.bodySize = packet.contentSize;
+        networkInfo.discardResponseBody = packet.discardResponseBody;
+        break;
+      case "eventTimings":
+        networkInfo.totalTime = packet.totalTime;
+        break;
+    }
+
+    this.emit("networkEventUpdate", {
+      packet: packet,
+      networkInfo
+    });
+  },
+
+  /**
    * Retrieve the cached messages from the server.
    *
    * @see this.CACHED_MESSAGES
-   * @param array aTypes
+   * @param array types
    *        The array of message types you want from the server. See
    *        this.CACHED_MESSAGES for known types.
    * @param function aOnResponse
    *        The function invoked when the response is received.
    */
-  getCachedMessages: function WCC_getCachedMessages(aTypes, aOnResponse)
+  getCachedMessages: function WCC_getCachedMessages(types, aOnResponse)
   {
     let packet = {
       to: this._actor,
       type: "getCachedMessages",
-      messageTypes: aTypes,
+      messageTypes: types,
     };
     this._client.request(packet, aOnResponse);
   },
 
   /**
    * Inspect the properties of an object.
    *
    * @param string aActor
@@ -468,15 +583,23 @@ WebConsoleClient.prototype = {
    * detaches from the console actor.
    *
    * @param function aOnResponse
    *        Function to invoke when the server response is received.
    */
   detach: function WCC_detach(aOnResponse)
   {
     this._client.removeListener("evaluationResult", this.onEvaluationResult);
+    this._client.removeListener("networkEvent", this.onNetworkEvent);
+    this._client.removeListener("networkEventUpdate", this.onNetworkEventUpdate);
     this.stopListeners(null, aOnResponse);
     this._longStrings = null;
     this._client = null;
     this.pendingEvaluationResults.clear();
     this.pendingEvaluationResults = null;
+    this.clearNetworkRequests();
+    this._networkRequests = null;
   },
+
+  clearNetworkRequests: function () {
+    this._networkRequests.clear();
+  }
 };