Bug 587573 - Log image requests to the WebConsole r=sdwilsh a=blocking2.0
authorJulian Viereck <jviereck@mozilla.com>
Thu, 26 Aug 2010 09:49:28 -0700
changeset 51516 9d622492520635aadb56decb88d1ebc4c94af341
parent 51515 bb8020341d712d4128703bbbeace9076c0bd2db4
child 51517 b99d25697307fa3a91db77525f59f9a6da646730
push id15339
push userddahl@mozilla.com
push dateThu, 26 Aug 2010 17:50:18 +0000
treeherdermozilla-central@0e40a49c27bb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssdwilsh, blocking2.0
bugs587573
milestone2.0b5pre
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 587573 - Log image requests to the WebConsole r=sdwilsh a=blocking2.0
toolkit/components/console/hudservice/HUDService.jsm
toolkit/components/console/hudservice/tests/browser/Makefile.in
toolkit/components/console/hudservice/tests/browser/browser_HUDServiceTestsAll.js
toolkit/components/console/hudservice/tests/browser/test-image.png
toolkit/components/console/hudservice/tests/browser/test-network.html
--- a/toolkit/components/console/hudservice/HUDService.jsm
+++ b/toolkit/components/console/hudservice/HUDService.jsm
@@ -102,16 +102,120 @@ const SEARCH_DELAY = 200;
 
 const ERRORS = { LOG_MESSAGE_MISSING_ARGS:
                  "Missing arguments: aMessage, aConsoleNode and aMessageNode are required.",
                  CANNOT_GET_HUD: "Cannot getHeads Up Display with provided ID",
                  MISSING_ARGS: "Missing arguments",
                  LOG_OUTPUT_FAILED: "Log Failure: Could not append messageNode to outputNode",
 };
 
+/**
+ * Helper object for networking stuff.
+ *
+ * All of the following functions have been taken from the Firebug source. They
+ * have been modified to match the Firefox coding rules.
+ */
+
+// FIREBUG CODE BEGIN.
+
+/*
+ * Software License Agreement (BSD License)
+ *
+ * Copyright (c) 2007, Parakey Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use of this software in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ *   copyright notice, this list of conditions and the
+ *   following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the
+ *   following disclaimer in the documentation and/or other
+ *   materials provided with the distribution.
+ *
+ * * Neither the name of Parakey Inc. nor the names of its
+ *   contributors may be used to endorse or promote products
+ *   derived from this software without specific prior
+ *   written permission of Parakey Inc.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Creator:
+ *  Joe Hewitt
+ * Contributors
+ *  John J. Barton (IBM Almaden)
+ *  Jan Odvarko (Mozilla Corp.)
+ *  Max Stepanov (Aptana Inc.)
+ *  Rob Campbell (Mozilla Corp.)
+ *  Hans Hillen (Paciello Group, Mozilla)
+ *  Curtis Bartley (Mozilla Corp.)
+ *  Mike Collins (IBM Almaden)
+ *  Kevin Decker
+ *  Mike Ratcliffe (Comartis AG)
+ *  Hernan Rodríguez Colmeiro
+ *  Austin Andrews
+ *  Christoph Dorn
+ *  Steven Roussey (AppCenter Inc, Network54)
+ */
+var NetworkHelper =
+{
+  /**
+   * Gets the nsIDOMWindow that is associated with aRequest.
+   *
+   * @param nsIHttpChannel aRequest
+   * @returns nsIDOMWindow or null
+   */
+  getWindowForRequest: function NH_getWindowForRequest(aRequest)
+  {
+    let loadContext = this.getRequestLoadContext(aRequest);
+    if (loadContext) {
+      return loadContext.associatedWindow;
+    }
+    return null;
+  },
+
+  /**
+   * Gets the nsILoadContext that is associated with aRequest.
+   *
+   * @param nsIHttpChannel aRequest
+   * @returns nsILoadContext or null
+   */
+  getRequestLoadContext: function NH_getRequestLoadContext(aRequest)
+  {
+    if (aRequest && aRequest.notificationCallbacks) {
+      try {
+        return aRequest.notificationCallbacks.getInterface(Ci.nsILoadContext);
+      } catch (ex) { }
+    }
+
+    if (aRequest && aRequest.loadGroup
+                 && aRequest.loadGroup.notificationCallbacks) {
+      try {
+        return aRequest.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
+      } catch (ex) { }
+    }
+
+    return null;
+   }
+}
+
+// FIREBUG CODE END.
+
 function HUD_SERVICE()
 {
   // TODO: provide mixins for FENNEC: bug 568621
   if (appName() == "FIREFOX") {
     var mixins = new FirefoxApplicationHooks();
   }
   else {
     throw new Error("Unsupported Application");
@@ -175,26 +279,26 @@ HUD_SERVICE.prototype =
   _headsUpDisplays: {},
 
   /**
    * Mapping of HUDIds to URIspecs
    */
   displayRegistry: {},
 
   /**
+   * Mapping of HUDIds to contentWindows.
+   */
+  windowRegistry: {},
+
+  /**
    * Mapping of URISpecs to HUDIds
    */
   uriRegistry: {},
 
   /**
-   * The nsILoadGroups being tracked
-   */
-  loadGroups: {},
-
-  /**
    * The sequencer is a generator (after initialization) that returns unique
    * integers
    */
   sequencer: null,
 
   /**
    * Each HeadsUpDisplay has a set of filter preferences
    */
@@ -599,50 +703,59 @@ HUD_SERVICE.prototype =
   {
     delete this.hudWeakReferences[aHUDId].get();
   },
 
   /**
    * Register a new Heads Up Display
    *
    * @param string aHUDId
-   * @param string aURISpec
+   * @param nsIDOMWindow aContentWindow
    * @returns void
    */
-  registerDisplay: function HS_registerDisplay(aHUDId, aURISpec)
+  registerDisplay: function HS_registerDisplay(aHUDId, aContentWindow)
   {
     // register a display DOM node Id and HUD uriSpec with the service
 
-    if (!aHUDId || !aURISpec){
+    if (!aHUDId || !aContentWindow){
       throw new Error(ERRORS.MISSING_ARGS);
     }
+    var URISpec = aContentWindow.document.location.href
     this.filterPrefs[aHUDId] = this.defaultFilterPrefs;
-    this.displayRegistry[aHUDId] = aURISpec;
+    this.displayRegistry[aHUDId] = URISpec;
     this._headsUpDisplays[aHUDId] = { id: aHUDId, };
     this.registerActiveContext(aHUDId);
     // init storage objects:
     this.storage.createDisplay(aHUDId);
 
-    var huds = this.uriRegistry[aURISpec];
+    var huds = this.uriRegistry[URISpec];
     var foundHUDId = false;
 
     if (huds) {
       var len = huds.length;
       for (var i = 0; i < len; i++) {
         if (huds[i] == aHUDId) {
           foundHUDId = true;
           break;
         }
       }
       if (!foundHUDId) {
-        this.uriRegistry[aURISpec].push(aHUDId);
+        this.uriRegistry[URISpec].push(aHUDId);
       }
     }
     else {
-      this.uriRegistry[aURISpec] = [aHUDId];
+      this.uriRegistry[URISpec] = [aHUDId];
+    }
+
+    var windows = this.windowRegistry[aHUDId];
+    if (!windows) {
+      this.windowRegistry[aHUDId] = [aContentWindow];
+    }
+    else {
+      windows.push(aContentWindow);
     }
   },
 
   /**
    * When a display is being destroyed, unregister it first
    *
    * @param string aId
    * @returns void
@@ -664,16 +777,19 @@ HUD_SERVICE.prototype =
     // remove the DOM Nodes
     parent.removeChild(outputNode);
     // remove our record of the DOM Nodes from the registry
     delete this._headsUpDisplays[aId];
     // remove the HeadsUpDisplay object from memory
     this.deleteHeadsUpDisplay(aId);
     // remove the related storage object
     this.storage.removeDisplay(aId);
+    // remove the related window objects
+    delete this.windowRegistry[aId];
+
     let displays = this.displays();
 
     var uri  = this.displayRegistry[aId];
     var specHudArr = this.uriRegistry[uri];
 
     for (var i = 0; i < specHudArr.length; i++) {
       if (specHudArr[i] == aId) {
         specHudArr.splice(i, 1);
@@ -725,16 +841,34 @@ HUD_SERVICE.prototype =
     else {
       // TODO: how to determine more fully the origination of this activity?
       // see bug 567165
       return this.getHeadsUpDisplay(hudIds[0]);
     }
   },
 
   /**
+   * Returns the hudId that is corresponding to the hud activated for the
+   * passed aContentWindow. If there is no matching hudId null is returned.
+   *
+   * @param nsIDOMWindow aContentWindow
+   * @returns string or null
+   */
+  getHudIdByWindow: function HS_getHudIdByWindow(aContentWindow)
+  {
+    for (let hudId in this.windowRegistry) {
+      if (this.windowRegistry[hudId] &&
+          this.windowRegistry[hudId].indexOf(aContentWindow) != -1) {
+        return hudId;
+      }
+    }
+    return null;
+  },
+
+  /**
    * Gets HUD DOM Node
    * @param string id
    *        The Heads Up Display DOM Id
    * @returns nsIDOMNode
    */
   getHeadsUpDisplay: function HS_getHeadsUpDisplay(aId)
   {
     return this.mixins.getOutputNodeById(aId);
@@ -953,61 +1087,16 @@ HUD_SERVICE.prototype =
 
   /**
    * Registry of ApplicationHooks used by specified Gecko Apps
    *
    * @returns Specific Gecko 'ApplicationHooks' Object/Mixin
    */
   applicationHooks: null,
 
-  /**
-   * Given an nsIChannel, return the corresponding nsILoadContext
-   *
-   * @param nsIChannel aChannel
-   * @returns nsILoadContext
-   */
-  getLoadContext: function HS_getLoadContext(aChannel)
-  {
-    if (!aChannel) {
-      return null;
-    }
-    var loadContext;
-    var callbacks = aChannel.notificationCallbacks;
-
-    loadContext =
-      aChannel.notificationCallbacks.getInterface(Ci.nsILoadContext);
-    if (!loadContext) {
-      loadContext =
-        aChannel.QueryInterface(Ci.nsIRequest).loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
-    }
-    return loadContext;
-  },
-
-  /**
-   * Given an nsILoadContext, return the corresponding nsIDOMWindow
-   *
-   * @param nsILoadContext aLoadContext
-   * @returns nsIDOMWindow
-   */
-  getWindowFromContext: function HS_getWindowFromContext(aLoadContext)
-  {
-    if (!aLoadContext) {
-      throw new Error("loadContext is null");
-    }
-    if (aLoadContext.isContent) {
-      if (aLoadContext.associatedWindow) {
-        return aLoadContext.associatedWindow;
-      }
-      else if (aLoadContext.topWindow) {
-        return aLoadContext.topWindow;
-      }
-    }
-    throw new Error("Cannot get window from " + aLoadContext);
-  },
-
   getChromeWindowFromContentWindow:
   function HS_getChromeWindowFromContentWindow(aContentWindow)
   {
     if (!aContentWindow) {
       throw new Error("Cannot get contentWindow via nsILoadContext");
     }
     var win = aContentWindow.QueryInterface(Ci.nsIDOMWindow)
       .QueryInterface(Ci.nsIInterfaceRequestor)
@@ -1016,138 +1105,69 @@ HUD_SERVICE.prototype =
       .rootTreeItem
       .QueryInterface(Ci.nsIInterfaceRequestor)
       .getInterface(Ci.nsIDOMWindow)
       .QueryInterface(Ci.nsIDOMChromeWindow);
     return win;
   },
 
   /**
-   * get the outputNode from the window object
-   *
-   * @param nsIDOMWindow aWindow
-   * @returns nsIDOMNode
-   */
-  getOutputNodeFromWindow:
-  function HS_getOutputNodeFromWindow(aWindow)
-  {
-    var browser = gBrowser.getBrowserForDocument(aWindow.top.document);
-    var tabId = gBrowser.getNotificationBox(browser).getAttribute("id");
-    var hudId = "hud_" + tabId;
-    var displayNode = this.getHeadsUpDisplay(hudId);
-    return displayNode.querySelectorAll(".hud-output-node")[0];
-  },
-
-  /**
-   * Try to get the outputNode via the nsIRequest
-   * TODO: get node via request, see bug 552140
-   * @param nsIRequest aRequest
-   * @returns nsIDOMNode
-   */
-  getOutputNodeFromRequest: function HS_getOutputNodeFromRequest(aRequest)
-  {
-    var context = this.getLoadContext(aRequest);
-    var window = this.getWindowFromContext(context);
-    return this.getOutputNodeFromWindow(window);
-  },
-
-  getLoadContextFromChannel: function HS_getLoadContextFromChannel(aChannel)
-  {
-    try {
-      return aChannel.QueryInterface(Ci.nsIChannel).notificationCallbacks.getInterface(Ci.nsILoadContext);
-    }
-    catch (ex) {
-      // noop, keep this output quiet. see bug 552140
-    }
-    try {
-      return aChannel.QueryInterface(Ci.nsIChannel).loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
-    }
-    catch (ex) {
-      // noop, keep this output quiet. see bug 552140
-    }
-    return null;
-  },
-
-  getWindowFromLoadContext:
-  function HS_getWindowFromLoadContext(aLoadContext)
-  {
-    if (aLoadContext.topWindow) {
-      return aLoadContext.topWindow;
-    }
-    else {
-      return aLoadContext.associatedWindow;
-    }
-  },
-
-  /**
    * Begin observing HTTP traffic that we care about,
    * namely traffic that originates inside any context that a Heads Up Display
    * is active for.
    */
   startHTTPObservation: function HS_httpObserverFactory()
   {
     // creates an observer for http traffic
     var self = this;
     var httpObserver = {
       observeActivity :
       function (aChannel, aActivityType, aActivitySubtype,
                 aTimestamp, aExtraSizeData, aExtraStringData)
       {
         var loadGroup;
         if (aActivityType ==
             activityDistributor.ACTIVITY_TYPE_HTTP_TRANSACTION) {
-          try {
-            var loadContext = self.getLoadContextFromChannel(aChannel);
-            // TODO: get image request data from the channel
-            // see bug 552140
-            var window = self.getWindowFromLoadContext(loadContext);
-            window = XPCNativeWrapper.unwrap(window);
-            var chromeWin = self.getChromeWindowFromContentWindow(window);
-            var vboxes =
-              chromeWin.document.getElementsByTagName("vbox");
-            var hudId;
-            for (var i = 0; i < vboxes.length; i++) {
-              if (vboxes[i].getAttribute("class") == "hud-box") {
-                hudId = vboxes[i].getAttribute("id");
-              }
-            }
-            loadGroup = self.getLoadGroup(hudId);
-          }
-          catch (ex) {
-            loadGroup = aChannel.QueryInterface(Ci.nsIChannel)
-                        .QueryInterface(Ci.nsIRequest).loadGroup;
-          }
-
-          if (!loadGroup) {
-              return;
-          }
 
           aChannel = aChannel.QueryInterface(Ci.nsIHttpChannel);
 
           var transCodes = this.httpTransactionCodes;
 
-          var httpActivity = {
-            channel: aChannel,
-            loadGroup: loadGroup,
-            type: aActivityType,
-            subType: aActivitySubtype,
-            timestamp: aTimestamp,
-            extraSizeData: aExtraSizeData,
-            extraStringData: aExtraStringData,
-            stage: transCodes[aActivitySubtype],
-          };
           if (aActivitySubtype ==
               activityDistributor.ACTIVITY_SUBTYPE_REQUEST_HEADER ) {
-                // create a unique ID to track this transaction and be able to
-                // update the logged node with subsequent http transactions
-                httpActivity.httpId = self.sequenceId();
-                let loggedNode =
-                  self.logActivity("network", aChannel.URI, httpActivity);
-                self.httpTransactions[aChannel] =
-                  new Number(httpActivity.httpId);
+            // Try to get the source window of the request.
+            let win = NetworkHelper.getWindowForRequest(aChannel);
+            if (!win) {
+              return;
+            }
+
+            // Try to get the hudId that is associated to the window.
+            let hudId = self.getHudIdByWindow(win);
+            if (!hudId) {
+              return;
+            }
+
+            var httpActivity = {
+              channel: aChannel,
+              type: aActivityType,
+              subType: aActivitySubtype,
+              timestamp: aTimestamp,
+              extraSizeData: aExtraSizeData,
+              extraStringData: aExtraStringData,
+              stage: transCodes[aActivitySubtype],
+              hudId: hudId
+            };
+
+            // create a unique ID to track this transaction and be able to
+            // update the logged node with subsequent http transactions
+            httpActivity.httpId = self.sequenceId();
+            let loggedNode =
+              self.logActivity("network", aChannel.URI, httpActivity);
+            self.httpTransactions[aChannel] =
+              new Number(httpActivity.httpId);
           }
         }
       },
 
       httpTransactionCodes: {
         0x5001: "REQUEST_HEADER",
         0x5002: "REQUEST_BODY_SENT",
         0x5003: "RESPONSE_START",
@@ -1168,32 +1188,21 @@ HUD_SERVICE.prototype =
    * Logs network activity
    *
    * @param nsIURI aURI
    * @param object aActivityObject
    * @returns void
    */
   logNetActivity: function HS_logNetActivity(aType, aURI, aActivityObject)
   {
-    var displayNode, outputNode, hudId;
+    var outputNode, hudId;
     try {
-      displayNode =
-      this.getDisplayByLoadGroup(aActivityObject.loadGroup,
-                                 {URI: aURI}, aActivityObject);
-      if (!displayNode) {
-        return;
-      }
-      outputNode = displayNode.querySelectorAll(".hud-output-node")[0];
-      hudId = displayNode.getAttribute("id");
-
-      if (!outputNode) {
-        outputNode = this.getOutputNodeFromRequest(aActivityObject.request);
-        hudId = outputNode.ownerDocument.querySelectorAll(".hud-box")[0].
-                getAttribute("id");
-      }
+      hudId = aActivityObject.hudId;
+      outputNode = this.getHeadsUpDisplay(hudId).
+                                  querySelector(".hud-output-node");
 
       // get an id to attach to the dom node for lookup of node
       // when updating the log entry with additional http transactions
       var domId = "hud-log-node-" + this.sequenceId();
 
       var message = { logLevel: aType,
                       activityObj: aActivityObject,
                       hudId: hudId,
@@ -1346,98 +1355,16 @@ HUD_SERVICE.prototype =
     separatorNode.setAttribute("orient", "horizontal");
     groupNode.appendChild(separatorNode);
 
     aConsoleNode.appendChild(groupNode);
     return groupNode;
   },
 
   /**
-   * update loadgroup when the window object is re-created
-   *
-   * @param string aId
-   * @param nsILoadGroup aLoadGroup
-   * @returns void
-   */
-  updateLoadGroup: function HS_updateLoadGroup(aId, aLoadGroup)
-  {
-    if (this.loadGroups[aId] == undefined) {
-      this.loadGroups[aId] = { id: aId,
-                               loadGroup: Cu.getWeakReference(aLoadGroup) };
-    }
-    else {
-      this.loadGroups[aId].loadGroup = Cu.getWeakReference(aLoadGroup);
-    }
-  },
-
-  /**
-   * gets the load group that corresponds to a HUDId
-   *
-   * @param string aId
-   * @returns nsILoadGroup
-   */
-  getLoadGroup: function HS_getLoadGroup(aId)
-  {
-    try {
-      return this.loadGroups[aId].loadGroup.get();
-    }
-    catch (ex) {
-      return null;
-    }
-  },
-
-  /**
-   * gets outputNode for a specific heads up display by loadGroup
-   *
-   * @param nsILoadGroup aLoadGroup
-   * @returns nsIDOMNode
-   */
-  getDisplayByLoadGroup:
-  function HS_getDisplayByLoadGroup(aLoadGroup, aChannel, aActivityObject)
-  {
-    if (!aLoadGroup) {
-      return null;
-    }
-    var trackedLoadGroups = this.getAllLoadGroups();
-    var len = trackedLoadGroups.length;
-    for (var i = 0; i < len; i++) {
-      try {
-        var unwrappedLoadGroup =
-        XPCNativeWrapper.unwrap(trackedLoadGroups[i].loadGroup);
-        if (aLoadGroup == unwrappedLoadGroup) {
-          return this.getOutputNodeById(trackedLoadGroups[i].hudId);
-        }
-      }
-      catch (ex) {
-        // noop
-      }
-    }
-    // TODO: also need to check parent loadGroup(s) incase of iframe activity?;
-    // see bug 568643
-    return null;
-  },
-
-  /**
-   * gets all nsILoadGroups that are being tracked by this service
-   * the loadgroups are matched to HUDIds in an object and an array is returned
-   * @returns array
-   */
-  getAllLoadGroups: function HS_getAllLoadGroups()
-  {
-    var loadGroups = [];
-    for (var hudId in this.loadGroups) {
-      let loadGroupObj = { loadGroup: this.loadGroups[hudId].loadGroup.get(),
-                           hudId: this.loadGroups[hudId].id,
-                         };
-      loadGroups.push(loadGroupObj);
-    }
-    return loadGroups;
-  },
-
-  /**
    * gets the DOM Node that maps back to what context/tab that
    * activity originated via the URI
    *
    * @param nsIURI aURI
    * @returns nsIDOMNode
    */
   getActivityOutputNode: function HS_getActivityOutputNode(aURI)
   {
@@ -1597,17 +1524,17 @@ HUD_SERVICE.prototype =
     let nBox = gBrowser.getNotificationBox(_browser);
     let nBoxId = nBox.getAttribute("id");
     let hudId = "hud_" + nBoxId;
 
     if (!this.canActivateContext(hudId)) {
       return;
     }
 
-    this.registerDisplay(hudId, aContentWindow.document.location.href);
+    this.registerDisplay(hudId, aContentWindow);
 
     // check if aContentWindow has a console Object
     let _console = aContentWindow.wrappedJSObject.console;
     if (!_console) {
       // no console exists. does the HUD exist?
       let hudNode;
       let childNodes = nBox.childNodes;
 
@@ -1861,30 +1788,16 @@ HeadsUpDisplay.prototype = {
       this.createConsole();
     }
     else {
       throw new Error("Cannot get output node");
     }
   },
 
   /**
-   * Gets the loadGroup for the contentWindow
-   *
-   * @returns nsILoadGroup
-   */
-  get loadGroup()
-  {
-    var loadGroup = this.contentWindow
-                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocumentLoader).loadGroup;
-    return loadGroup;
-  },
-
-  /**
    * Shortcut to make XUL nodes
    *
    * @param string aTag
    * @returns nsIDOMNode
    */
   makeXULNode:
   function HUD_makeXULNode(aTag)
   {
@@ -2197,18 +2110,16 @@ function HUDConsole(aHeadsUpDisplay)
 {
   let hud = aHeadsUpDisplay;
   let hudId = hud.hudId;
   let outputNode = hud.outputNode;
   let chromeDocument = hud.chromeDocument;
 
   aHeadsUpDisplay._console = this;
 
-  HUDService.updateLoadGroup(hudId, hud.loadGroup);
-
   let sendToHUDService = function console_send(aLevel, aArguments)
   {
     let ts = ConsoleUtils.timestamp();
     let messageNode = hud.makeXULNode("label");
 
     let klass = "hud-msg-node hud-" + aLevel;
 
     messageNode.setAttribute("class", klass);
@@ -3332,17 +3243,17 @@ ConsoleUtils = {
   timestamp: function ConsoleUtils_timestamp()
   {
     return Date.now();
   },
 
   /**
    * Generates a formatted timestamp string for displaying in console messages.
    *
-   * @param integer [ms] Optional, allows you to specify the timestamp in 
+   * @param integer [ms] Optional, allows you to specify the timestamp in
    * milliseconds since the UNIX epoch.
    * @returns string The timestamp formatted for display.
    */
   timestampString: function ConsoleUtils_timestampString(ms)
   {
     // TODO: L10N see bug 568656
     var d = new Date(ms ? ms : null);
 
--- a/toolkit/components/console/hudservice/tests/browser/Makefile.in
+++ b/toolkit/components/console/hudservice/tests/browser/Makefile.in
@@ -53,15 +53,16 @@ include $(topsrcdir)/config/rules.mk
 	test-mutation.html \
 	testscript.js \
 	test-filter.html \
 	test-observe-http-ajax.html \
 	test-data.json \
 	test-property-provider.html \
 	test-error.html \
 	test-duplicate-error.html \
+	test-image.png \
 	$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
 
 libs:: $(_BROWSER_TEST_PAGES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/toolkit/components/console/hudservice/tests/browser/browser_HUDServiceTestsAll.js
+++ b/toolkit/components/console/hudservice/tests/browser/browser_HUDServiceTestsAll.js
@@ -59,18 +59,16 @@ let log = function _log(msg) {
 };
 
 const TEST_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-console.html";
 
 const TEST_HTTP_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-observe-http-ajax.html";
 
 const TEST_NETWORK_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-network.html";
 
-const TEST_FILTER_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-filter.html";
-
 const TEST_PROPERTY_PROVIDER_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-property-provider.html";
 
 const TEST_ERROR_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-error.html";
 
 const TEST_DUPLICATE_ERROR_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-duplicate-error.html";
 
 function noCacheUriSpec(aUriSpec) {
   return aUriSpec + "?_=" + Date.now();
@@ -154,58 +152,16 @@ function getAllHUDS() {
 
   ok(typeof idx == "object", "displays is an object");
   ok(typeof idx.push == "function", "displaysIndex is an array");
 
   var len = idx.length;
   ok(idx.length > 0, "idx.length > 0: " + len);
 }
 
-function testGetDisplayByLoadGroup() {
-  var outputNode = HUDService.getDisplayByURISpec(TEST_URI);
-  var hudId = outputNode.getAttribute("id");
-  var loadGroup = HUDService.getLoadGroup(hudId);
-  var display = HUDService.getDisplayByLoadGroup(loadGroup);
-  ok(display.getAttribute("id") == hudId, "got display by loadGroup");
-
-  content.location = TEST_HTTP_URI;
-
-  executeSoon(function () {
-                let id = HUDService.displaysIndex()[0];
-
-                let domLogEntries =
-                  outputNode.childNodes;
-
-                let count = outputNode.childNodes.length;
-                ok(count > 0, "LogCount: " + count);
-
-                let klasses = ["hud-network"];
-
-                function verifyClass(klass) {
-                  let len = klasses.length;
-                  for (var i = 0; i < len; i++) {
-                    if (klass == klasses[i]) {
-                      return true;
-                    }
-                  }
-                  return false;
-                }
-
-                for (var i = 0; i < count; i++) {
-                  let klass = domLogEntries[i].getAttribute("class");
-                  if (klass != "hud-network") {
-                    continue;
-                  }
-                  ok(verifyClass(klass),
-                     "Log Node network class verified");
-                }
-
-              });
-}
-
 function testUnregister()  {
   HUDService.deactivateHUDForContext(tab);
   ok(HUDService.displays()[0] == undefined,
      "No heads up displays are registered");
   HUDService.shutdown();
 }
 
 function getHUDById() {
@@ -280,31 +236,39 @@ function testLogEntry(aOutputNode, aMatc
 }
 
 // test network logging
 function testNet()
 {
   HUDService.setFilterState(hudId, "network", true);
   setStringFilter("");
 
-  browser.addEventListener("DOMContentLoaded", function onTestNetLoad () {
-    browser.removeEventListener("DOMContentLoaded", onTestNetLoad, false);
+  let HUD = HUDService.hudWeakReferences[hudId].get();
+  let jsterm = HUD.jsterm;
+  let outputNode = jsterm.outputNode;
+  jsterm.clearOutput();
 
-    var successMsg =
-      "Found the loggged network message referencing a js file";
-    var errMsg = "Could not get logged network message for js file";
+  browser.addEventListener("load", function onTestNetLoad () {
+    browser.removeEventListener("load", onTestNetLoad, true);
+
+    let group = outputNode.querySelector(".hud-group");
+    is(group.childNodes.length, 5, "Four children in output");
+    let outputChildren = group.childNodes;
 
-    var display = HUDService.getDisplayByURISpec(TEST_NETWORK_URI);
-    var outputNode = display.querySelectorAll(".hud-output-node")[0];
+    isnot(outputChildren[1].textContent.indexOf("test-network.html"), -1,
+                                              "html page is logged");
+    isnot(outputChildren[2].textContent.indexOf("testscript.js"), -1,
+                                              "javascript is logged");
+    isnot(outputChildren[3].textContent.indexOf("test-image.png"), -1,
+                                              "image is logged");
+    isnot(outputChildren[4].textContent.
+      indexOf("running network console logging tests"), -1, "log() is logged");
 
-    testLogEntry(outputNode, "Network:",
-      { success: successMsg, err: errMsg });
-
-    testPageReload();
-  }, false);
+    testLiveFilteringForMessageTypes();
+  }, true);
 
   content.location = TEST_NETWORK_URI;
 }
 
 // General driver for filter tests.
 function testLiveFiltering(callback) {
   HUDService.setFilterState(hudId, "network", true);
   setStringFilter("");
@@ -336,16 +300,18 @@ function testLiveFilteringForMessageType
   testLiveFiltering(function(countNetworkNodes) {
     HUDService.setFilterState(hudId, "network", false);
     is(countNetworkNodes(), 0, "the network nodes are hidden when the " +
       "corresponding filter is switched off");
 
     HUDService.setFilterState(hudId, "network", true);
     isnot(countNetworkNodes(), 0, "the network nodes reappear when the " +
       "corresponding filter is switched on");
+
+    testLiveFilteringForSearchStrings();
   });
 }
 
 // Tests the live filtering on search strings.
 function testLiveFilteringForSearchStrings()
 {
   testLiveFiltering(function(countNetworkNodes) {
     setStringFilter("http");
@@ -382,16 +348,18 @@ function testLiveFilteringForSearchStrin
 
     setStringFilter("'foo'");
     is(countNetworkNodes(), 0, "the network nodes are hidden when searching " +
       "for the string 'foo'");
 
     setStringFilter("foo\"bar'baz\"boo'");
     is(countNetworkNodes(), 0, "the network nodes are hidden when searching " +
       "for the string \"foo\"bar'baz\"boo'\"");
+
+    testPageReload();
   });
 }
 
 function testOutputOrder()
 {
   let HUD = HUDService.hudWeakReferences[hudId].get();
   let jsterm = HUD.jsterm;
   let outputNode = jsterm.outputNode;
@@ -799,17 +767,17 @@ function testHUDGetters()
 
   var hudconsole = HUD.console;
   is(typeof hudconsole, "object", "HUD.console is an object");
   is(typeof hudconsole.log, "function", "HUD.console.log is a function");
   is(typeof hudconsole.info, "function", "HUD.console.info is a function");
 }
 
 function testPageReload() {
-  // see bug 578437 - The HUD console fails to re-attach the window.console 
+  // see bug 578437 - The HUD console fails to re-attach the window.console
   // object after page reload.
 
   browser.addEventListener("DOMContentLoaded", function onDOMLoad () {
     browser.removeEventListener("DOMContentLoaded", onDOMLoad, false);
 
     var console = browser.contentWindow.wrappedJSObject.console;
 
     is(typeof console, "object", "window.console is an object, after page reload");
@@ -985,21 +953,18 @@ function test() {
     executeSoon(function () {
       testRegistries();
       testGetDisplayByURISpec();
       testHUDGetters();
       introspectLogNodes();
       getAllHUDS();
       getHUDById();
       testInputFocus();
-      testGetDisplayByLoadGroup();
       testGetContentWindowFromHUDId();
 
-      content.location.href = TEST_FILTER_URI;
-
       testConsoleLoggingAPI("log");
       testConsoleLoggingAPI("info");
       testConsoleLoggingAPI("warn");
       testConsoleLoggingAPI("error");
       testConsoleLoggingAPI("exception");
 
       // ConsoleStorageTests
       testCreateDisplay();
@@ -1011,14 +976,18 @@ function test() {
       testGroups();
       testNullUndefinedOutput();
       testJSInputAndOutputStyling();
       testExecutionScope();
       testCompletion();
       testPropertyProvider();
       testJSInputExpand();
       testPropertyPanel();
+
+      // NOTE: Put any sync test above this comment.
+      //
+      // There is a set of async tests that have to run one after another.
+      // Currently, when one test is done the next one is called from within the
+      // test function until testEnd() is called that terminates the test.
       testNet();
-      testLiveFilteringForMessageTypes();
-      testLiveFilteringForSearchStrings();
     });
   }, false);
 }
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..769c636340e11f9d2a0b7eb6a84d574dd9563f0c
GIT binary patch
literal 580
zc$@)50=xZ*P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ
zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz)=5M`RCwBA
z{Qv(y10?_;fS8au@87>?@9gaSt*os4-<6T^zln*-e<1(Y>eZ{a-@A8D7MlS80mJ}u
z0SKQtbH>fs*!aH-1H=C_K)ecw?*efe5W7I}s#U9Y!PLVrKmdV>nKNfT1{(H16si$K
zmjm&CV+al6D=8`c7X*o+82}JK4A3z64>JJB_`e(E3S)?2<zN>X{|^lf1sei(+1<Me
zFarPr2&}oIqvIC?)OL^o?-&p^!wh-{#k-;2f_VoZfS{%bLi}zFF<>UtAY{W<LBj?n
zz6$CsfB=HI;P*^44eyZn#$YcBg1rF~2L~`P&;bI75#$0;v@zVf$It;Z4d`qJS0Dzu
zh@l)BQx!mb7Roj*FK2kaXAgtR*|Q9LfP8=e0=od{pY0$UI-sVXf!f>w#jt?wfcn3@
zy!=0d5|Evi_8%aC7~Z{m#}1AKK|~<_M{=g1px}QcP=ErR57Iaj7$e5OumVLr$n^jL
z1djz!sJcLHd57c@W2lWFi_p^m2m=HVoB>kgf@C|$pqa*ykjAAMgaHBwo)>`0c=vmt
z+g3yQpuk*x7A(#H^u|wInF%0(FiZq_r2`t3pnwGh6fWCA7$ATcDb3CR0R{jJCzQv)
SYsoAC0000<MNUMnLSTYrIq9PS
--- a/toolkit/components/console/hudservice/tests/browser/test-network.html
+++ b/toolkit/components/console/hudservice/tests/browser/test-network.html
@@ -1,9 +1,10 @@
 <!DOCTYPE HTML>
 <html dir="ltr" xml:lang="en-US" lang="en-US"><head>
     <title>Console network test</title>
     <script src="testscript.js"></script>
   </head>
   <body>
     <h1>Heads Up Display Network  Test Page</h1>
+    <img src="test-image.png"></img>
   </body>
 </html>