Bug 597136 - Use the new nsIScriptError2 interface instead of window.onerror to determine the Web Console to which messages should be routed. r=gavin a=blocking2.0:beta8+
authorPatrick Walton <pwalton@mozilla.com>
Fri, 12 Nov 2010 09:13:00 -0400
changeset 57398 563c20646a43a432cf925c49ea278355a5523509
parent 57397 f7e0aeb5462bd73f21889be63a29fd40a0972c49
child 57399 1023c324e6be0ed191de19a1e74c148b79df7354
push id16906
push userrcampbell@mozilla.com
push dateFri, 12 Nov 2010 13:14:06 +0000
treeherdermozilla-central@563c20646a43 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgavin, blocking2.0
bugs597136
milestone2.0b8pre
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 597136 - Use the new nsIScriptError2 interface instead of window.onerror to determine the Web Console to which messages should be routed. r=gavin a=blocking2.0:beta8+
toolkit/components/console/hudservice/HUDService.jsm
toolkit/components/console/hudservice/tests/browser/Makefile.in
toolkit/components/console/hudservice/tests/browser/browser_warn_user_about_replaced_api.js
toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_580030_errors_after_page_reload.js
toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_582201_duplicate_errors.js
toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_587617_output_copy.js
toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_589162_css_filter.js
toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_597136_external_script_errors.js
toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_597136_network_requests_from_chrome.js
toolkit/components/console/hudservice/tests/browser/browser_webconsole_get_display_by_uri_spec.js
toolkit/components/console/hudservice/tests/browser/test-bug-597136-external-script-errors.html
toolkit/components/console/hudservice/tests/browser/test-bug-597136-external-script-errors.js
--- a/toolkit/components/console/hudservice/HUDService.jsm
+++ b/toolkit/components/console/hudservice/HUDService.jsm
@@ -1433,50 +1433,16 @@ HUD_SERVICE.prototype =
 
   /**
    * Whether to save the bodies of network requests and responses. Disabled by
    * default to save memory.
    */
   saveRequestAndResponseBodies: false,
 
   /**
-   * Event handler to get window errors
-   * TODO: a bit of a hack but is able to associate
-   * errors thrown in a window's scope we do not know
-   * about because of the nsIConsoleMessages not having a
-   * window reference.
-   * see bug 567165
-   *
-   * @param nsIDOMWindow aWindow
-   * @returns boolean
-   */
-  setOnErrorHandler: function HS_setOnErrorHandler(aWindow) {
-    var self = this;
-    var window = aWindow.wrappedJSObject;
-    var console = window.console;
-    var origOnerrorFunc = window.onerror;
-    window.onerror = function windowOnError(aErrorMsg, aURL, aLineNumber)
-    {
-      if (aURL && !(aURL in self.uriRegistry)) {
-        var lineNum = "";
-        if (aLineNumber) {
-          lineNum = self.getFormatStr("errLine", [aLineNumber]);
-        }
-        console.error(aErrorMsg + " @ " + aURL + " " + lineNum);
-      }
-
-      if (origOnerrorFunc) {
-        origOnerrorFunc(aErrorMsg, aURL, aLineNumber);
-      }
-
-      return false;
-    };
-  },
-
-  /**
    * Tell the HUDService that a HeadsUpDisplay can be activated
    * for the window or context that has 'aContextDOMId' node id
    *
    * @param string aContextDOMId
    * @return void
    */
   registerActiveContext: function HS_registerActiveContext(aContextDOMId)
   {
@@ -1971,52 +1937,58 @@ HUD_SERVICE.prototype =
     for (var displayId in this._headsUpDisplays) {
       this.unregisterDisplay(displayId);
     }
     // delete the storage as it holds onto channels
     delete this.storage;
   },
 
   /**
-   * get the nsIDOMNode outputNode via a nsIURI.spec
-   *
-   * @param string aURISpec
-   * @returns nsIDOMNode
-   */
-  getDisplayByURISpec: function HS_getDisplayByURISpec(aURISpec)
-  {
-    // TODO: what about data:uris? see bug 568626
-    var hudIds = this.uriRegistry[aURISpec];
-    if (hudIds.length == 1) {
-      // only one HUD connected to this URISpec
-      return this.getHeadsUpDisplay(hudIds[0]);
-    }
-    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)
   {
+    // Fast path: check the cached window registry.
     for (let hudId in this.windowRegistry) {
       if (this.windowRegistry[hudId] &&
           this.windowRegistry[hudId].indexOf(aContentWindow) != -1) {
         return hudId;
       }
     }
-    return null;
+
+    // As a fallback, do a little pointer chasing to try to find the Web
+    // Console. This fallback approach occurs when opening the Console on a
+    // page with subframes.
+    let [ , tabBrowser, browser ] = ConsoleUtils.getParents(aContentWindow);
+    if (!tabBrowser) {
+      return null;
+    }
+
+    let notificationBox = tabBrowser.getNotificationBox(browser);
+    let hudBox = notificationBox.querySelector(".hud-box");
+    if (!hudBox) {
+      return null;
+    }
+
+    // Cache it!
+    let hudId = hudBox.id;
+    this.windowRegistry[hudId].push(aContentWindow);
+
+    let uri = aContentWindow.document.location.href;
+    if (!this.uriRegistry[uri]) {
+      this.uriRegistry[uri] = [ hudId ];
+    } else {
+      this.uriRegistry[uri].push(hudId);
+    }
+
+    return hudId;
   },
 
   /**
    * Gets HUD DOM Node
    * @param string id
    *        The Heads Up Display DOM Id
    * @returns nsIDOMNode
    */
@@ -2041,30 +2013,16 @@ HUD_SERVICE.prototype =
    *
    * @returns object
    */
   displays: function HS_displays() {
     return this._headsUpDisplays;
   },
 
   /**
-   * Get an array of HUDIds that match a uri.spec
-   *
-   * @param string aURISpec
-   * @returns array
-   */
-  getHUDIdsForURISpec: function HS_getHUDIdsForURISpec(aURISpec)
-  {
-    if (this.uriRegistry[aURISpec]) {
-      return this.uriRegistry[aURISpec];
-    }
-    return [];
-  },
-
-  /**
    * Gets an array that contains active DOM Node Ids for all HUDs
    * @returns array
    */
   displaysIndex: function HS_displaysIndex()
   {
     var props = [];
     for (var prop in this._headsUpDisplays) {
       props.push(prop);
@@ -2212,44 +2170,16 @@ HUD_SERVICE.prototype =
     };
 
     let messageObject =
     this.messageFactory(msgFormat, "error", outputNode, msgFormat.activityObject);
     this.logMessage(messageObject.messageObject, outputNode, messageObject.messageNode);
   },
 
   /**
-   * report consoleMessages recieved via the HUDConsoleObserver service
-   * @param nsIConsoleMessage aConsoleMessage
-   * @returns void
-   */
-  reportConsoleServiceMessage:
-  function HS_reportConsoleServiceMessage(aConsoleMessage)
-  {
-    this.logActivity("console-listener", null, aConsoleMessage);
-  },
-
-  /**
-   * report scriptErrors recieved via the HUDConsoleObserver service
-   * @param nsIScriptError aScriptError
-   * @returns void
-   */
-  reportConsoleServiceContentScriptError:
-  function HS_reportConsoleServiceContentScriptError(aScriptError)
-  {
-    try {
-      var uri = Services.io.newURI(aScriptError.sourceName, null, null);
-    }
-    catch(ex) {
-      var uri = { spec: "" };
-    }
-    this.logActivity("console-listener", uri, aScriptError);
-  },
-
-  /**
    * generates an nsIScriptError
    *
    * @param object aMessage
    * @param integer flag
    * @returns nsIScriptError
    */
   generateConsoleMessage:
   function HS_generateConsoleMessage(aMessage, flag)
@@ -2391,18 +2321,17 @@ HUD_SERVICE.prototype =
                 header: null
               },
               timing: {
                 "REQUEST_HEADER": aTimestamp
               }
             };
 
             // Add a new output entry.
-            let loggedNode =
-              self.logActivity("network", aChannel.URI, httpActivity);
+            let loggedNode = self.logActivity("network", hudId, httpActivity);
 
             // In some cases loggedNode can be undefined (e.g. if an image was
             // requested). Don't continue in such a case.
             if (!loggedNode) {
               return;
             }
 
             // Add listener for the response body.
@@ -2579,23 +2508,25 @@ HUD_SERVICE.prototype =
         0x804b0006: "STATUS_RECEIVING_FROM"
       }
     };
 
     activityDistributor.addObserver(httpObserver);
   },
 
   /**
-   * Logs network activity
+   * Logs network activity.
    *
-   * @param nsIURI aURI
+   * @param string aType
+   *        The severity of the message.
    * @param object aActivityObject
+   *        The activity to log.
    * @returns void
    */
-  logNetActivity: function HS_logNetActivity(aType, aURI, aActivityObject)
+  logNetActivity: function HS_logNetActivity(aType, aActivityObject)
   {
     var outputNode, hudId;
     try {
       hudId = aActivityObject.hudId;
       outputNode = this.getHeadsUpDisplay(hudId).
                                   querySelector(".hud-output-node");
 
       // get an id to attach to the dom node for lookup of node
@@ -2629,53 +2560,41 @@ HUD_SERVICE.prototype =
       return messageObject;
     }
     catch (ex) {
       Cu.reportError(ex);
     }
   },
 
   /**
-   * Logs console listener activity
+   * Logs console listener activity.
    *
-   * @param nsIURI aURI
+   * @param string aHUDId
+   *        The ID of the HUD to which to send the message.
    * @param object aActivityObject
+   *        The message to log.
    * @returns void
    */
-  logConsoleActivity: function HS_logConsoleActivity(aURI, aActivityObject)
+  logConsoleActivity: function HS_logConsoleActivity(aHUDId, aActivityObject)
   {
-    var displayNode, outputNode, hudId;
-    try {
-        var hudIds = this.uriRegistry[aURI.spec];
-        hudId = hudIds[0];
-    }
-    catch (ex) {
-      // TODO: uri spec is not tracked becasue the net request is
-      // using a different loadGroup
-      // see bug 568034
-      if (!displayNode) {
-        return;
-      }
-    }
-
     var _msgLogLevel = this.scriptMsgLogLevel[aActivityObject.flags];
     var msgLogLevel = this.getStr(_msgLogLevel);
 
     var logLevel = "warn";
 
     if (aActivityObject.flags in this.scriptErrorFlags) {
       logLevel = this.scriptErrorFlags[aActivityObject.flags];
     }
 
     // in this case, the "activity object" is the
     // nsIScriptError or nsIConsoleMessage
     var message = {
       activity: aActivityObject,
       origin: "console-listener",
-      hudId: hudId,
+      hudId: aHUDId,
     };
 
     var lineColSubs = [aActivityObject.lineNumber,
                        aActivityObject.columnNumber];
     var lineCol = this.getFormatStr("errLineCol", lineColSubs);
 
     var errFileSubs = [aActivityObject.sourceName];
     var errFile = this.getFormatStr("errFile", errFileSubs);
@@ -2686,45 +2605,43 @@ HUD_SERVICE.prototype =
     message.level = logLevel;
 
     message.message = msgLogLevel + " " +
                       aActivityObject.errorMessage + " " +
                       errFile + " " +
                       lineCol + " " +
                       msgCategory + " " + aActivityObject.category;
 
-    displayNode = this.getHeadsUpDisplay(hudId);
-    outputNode = displayNode.querySelectorAll(".hud-output-node")[0];
+    outputNode = this.hudWeakReferences[aHUDId].get().outputNode;
 
     var messageObject =
     this.messageFactory(message, message.level, outputNode, aActivityObject);
 
     this.logMessage(messageObject.messageObject, outputNode, messageObject.messageNode);
   },
 
   /**
-   * Parse log messages for origin or listener type
-   * Get the correct outputNode if it exists
-   * Finally, call logMessage to write this message to
-   * storage and optionally, a DOM output node
+   * Calls logNetActivity() or logConsoleActivity() as appropriate to log the
+   * given message to the appropriate console.
    *
    * @param string aType
-   * @param nsIURI aURI
+   *        The type of message; one of "network" or "console-listener".
+   * @param string aHUDId
+   *        The ID of the console to which to send the message.
    * @param object (or nsIScriptError) aActivityObj
+   *        The message to send.
    * @returns void
    */
-  logActivity: function HS_logActivity(aType, aURI, aActivityObject)
+  logActivity: function HS_logActivity(aType, aHUDId, aActivityObject)
   {
-    var displayNode, outputNode, hudId;
-
     if (aType == "network") {
-      return this.logNetActivity(aType, aURI, aActivityObject);
+      return this.logNetActivity(aType, aActivityObject);
     }
     else if (aType == "console-listener") {
-      this.logConsoleActivity(aURI, aActivityObject);
+      this.logConsoleActivity(aHUDId, aActivityObject);
     }
   },
 
   /**
    * Builds and appends a group to the console if enough time has passed since
    * the last message.
    *
    * @param nsIDOMNode aConsoleNode
@@ -2759,35 +2676,16 @@ HUD_SERVICE.prototype =
     let groupNode = chromeDocument.createElement("vbox");
     groupNode.setAttribute("class", "hud-group");
 
     aConsoleNode.appendChild(groupNode);
     return groupNode;
   },
 
   /**
-   * 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)
-  {
-    // determine which outputNode activity tied to aURI should be logged to.
-    var display = this.getDisplayByURISpec(aURI.spec);
-    if (display) {
-      return this.getOutputNodeById(display);
-    }
-    else {
-      throw new Error("Cannot get outputNode by hudId");
-    }
-  },
-
-  /**
    * Wrapper method that generates a LogMessage object
    *
    * @param object aMessage
    * @param string aLevel
    * @param nsIDOMNode aOutputNode
    * @param object aActivityObject
    * @returns
    */
@@ -3019,19 +2917,16 @@ HUD_SERVICE.prototype =
     // our console, but warn the user about this.
     if (aContentWindow.wrappedJSObject.console) {
       this.logWarningAboutReplacedAPI(hudId);
     }
     else {
       aContentWindow.wrappedJSObject.console = hud.console;
     }
 
-    // capture JS Errors
-    this.setOnErrorHandler(aContentWindow);
-
     // register the controller to handle "select all" properly
     this.createController(xulWindow);
   },
 
   /**
    * Adds the command controller to the XUL window if it's not already present.
    *
    * @param nsIDOMWindow aWindow
@@ -5054,16 +4949,89 @@ ConsoleUtils = {
     let scrollBoxNode = aNode.parentNode;
     while (scrollBoxNode.tagName !== "scrollbox") {
       scrollBoxNode = scrollBoxNode.parentNode;
     }
 
     let boxObject = scrollBoxNode.boxObject;
     let nsIScrollBoxObject = boxObject.QueryInterface(Ci.nsIScrollBoxObject);
     nsIScrollBoxObject.ensureElementIsVisible(aNode);
+  },
+
+  /**
+   * Given an instance of nsIScriptError, attempts to work out the IDs of HUDs
+   * to which the script should be sent.
+   *
+   * @param nsIScriptError aScriptError
+   *        The script error that was received.
+   * @returns Array<string>
+   */
+  getHUDIdsForScriptError:
+  function ConsoleUtils_getHUDIdsForScriptError(aScriptError) {
+    if (aScriptError instanceof Ci.nsIScriptError2) {
+      let windowID = aScriptError.outerWindowID;
+      if (windowID) {
+        // We just need some arbitrary window here so that we can GI to
+        // nsIDOMWindowUtils...
+        let someWindow = Services.wm.getMostRecentWindow(null);
+        if (someWindow) {
+          let windowUtils = someWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                                      .getInterface(Ci.nsIDOMWindowUtils);
+
+          // In the future (post-Electrolysis), getOuterWindowWithId() could
+          // return null, because the originating window could have gone away
+          // while we were in the process of receiving and/or processing a
+          // message. For future-proofing purposes, we do a null check here.
+          let content = windowUtils.getOuterWindowWithId(windowID);
+          if (content) {
+            let hudId = HUDService.getHudIdByWindow(content);
+            if (hudId) {
+              return [ hudId ];
+            }
+          }
+        }
+      }
+    }
+
+    // The error had no window ID. As a less precise fallback, see whether we
+    // can find some consoles to send to via the URI.
+    let hudIds = HUDService.uriRegistry[aScriptError.sourceName];
+    return hudIds ? hudIds : [];
+  },
+
+  /**
+   * Returns the chrome window, the tab browser, and the browser that
+   * contain the given content window.
+   *
+   * NB: This function only works in Firefox.
+   *
+   * @param nsIDOMWindow aContentWindow
+   *        The content window to query. If this parameter is not a content
+   *        window, then [ null, null, null ] will be returned.
+   */
+  getParents: function ConsoleUtils_getParents(aContentWindow) {
+    if (aContentWindow instanceof Ci.nsIDOMChromeWindow) {
+      return [ null, null, null ];
+    }
+
+    let chromeEventHandler =
+      aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                    .getInterface(Ci.nsIWebNavigation)
+                    .QueryInterface(Ci.nsIDocShell)
+                    .chromeEventHandler;
+    let chromeWindow = chromeEventHandler.ownerDocument.defaultView;
+    let gBrowser = XPCNativeWrapper.unwrap(chromeWindow).gBrowser;
+    let documentElement = chromeWindow.document.documentElement;
+    if (!documentElement || !gBrowser ||
+        documentElement.getAttribute("windowtype") !== "navigator:browser") {
+      // Not a browser window.
+      return [ chromeWindow, null, null ];
+    }
+
+    return [ chromeWindow, gBrowser, chromeEventHandler ];
   }
 };
 
 //////////////////////////////////////////////////////////////////////////
 // HeadsUpDisplayUICommands
 //////////////////////////////////////////////////////////////////////////
 
 HeadsUpDisplayUICommands = {
@@ -5511,96 +5479,36 @@ HUDConsoleObserver = {
 
   observe: function HCO_observe(aSubject, aTopic, aData)
   {
     if (aTopic == "xpcom-shutdown") {
       Services.console.unregisterListener(this);
     }
 
     if (aSubject instanceof Ci.nsIScriptError) {
+      let hudIds = ConsoleUtils.getHUDIdsForScriptError(aSubject);
+
       switch (aSubject.category) {
         // We ignore chrome-originating errors as we only
         // care about content.
         case "XPConnect JavaScript":
-          // nsXPCWrappedJSClass::CheckForException()
-          // nsXPCComponents_Utils::ReportError()
         case "component javascript":
         case "chrome javascript":
-          // ScriptErrorEvent in nsJSEnvironment.cpp
         case "chrome registration":
-          // nsChromeRegistry::LogMessageWithContext()
         case "XBL":
-          // nsXBLService
         case "XBL Prototype Handler":
-          // nsXBLPrototypeHandler::ReportKeyConflict()
         case "XBL Content Sink":
-          // nsXBLContentSink
         case "xbl javascript":
-          // XBL_ProtoErrorReporter in nsXBLDocumentInfo.cpp
         case "FrameConstructor":
-          // nsCSSFrameConstructor::ProcessChildren()
           return;
 
-        // Display the messages from the following categories.
-        case "HUDConsole":
-        case "CSS Parser":
-          // nsCSSScanner::OutputError()
-        case "CSS Loader":
-          // SheetLoadData::OnStreamComplete()
-        case "content javascript":
-          // ScriptErrorEvent in nsJSEnvironment.cpp
-        case "DOM Events":
-          // nsHtml5StreamParser::ContinueAfterScripts()
-          // ReportUseOfDeprecatedMethod() in nsGlobalWindow.cpp,
-          // nsHTMLDocument.cpp, nsDOMEvent.cpp
-          // nsDOMEvent::ReportWrongPropertyAccessWarning()
-          // nsHTMLDocument::WriteCommon()
-        case "DOM:HTML":
-          // PrintWarningOnConsole() in nsDOMClassInfo.cpp
-        case "DOM Window":
-          // nsGlobalWindow::Close()
-          // TODO: This message is never displayed because its origin cannot be
-          // determined, no sourceName is given. See bug 603711.
-        case "SVG":
-          // nsSVGUtils::ReportToConsole()
-          // nsSVGElement::ReportAttributeParseFailure()
-        case "ImageMap":
-          // logMessage() in nsImageMap.cpp
-        case "HTML":
-          // SendJSWarning() in nsFormSubmission.cpp
-        case "Canvas":
-          // nsCanvasRenderingContext2D::SetStyleFromStringOrInterface()
-          // TODO: This message is never displayed because its origin cannot be
-          // determined, no sourceName is given. See bug 603714.
-        case "DOM3 Load":
-          // ReportUseOfDeprecatedMethod() in nsXMLDocument.cpp
-          // TODO: This message is generally not displayed because its origin
-          // (sourceName) points to the previous URI of the document object -
-          // not the URI of the page in which the script tries to load the new
-          // URI. See bug 603720.
-        case "DOM":
-          // nsDocument::ReportEmptyGetElementByIdArg()
-          //   TODO: This message is never displayed because its origin cannot
-          //   be determined, no sourceName is given. See bug 603723.
-          // nsXMLDocument::Load() - for chrome code.
-        case "malformed-xml":
-          // nsExpatDriver::HandleError()
-          // TODO: This message is only displayed when its origin (sourceName)
-          // is the same as the tab location for which a Web Console is open.
-          // See bug 603727.
-        case "DOM Worker javascript":
-          // nsReportErrorRunnable and DOMWorkerErrorReporter in
-          // nsDOMThreadService.cpp
-          // TODO: This message is never displayed because its origin
-          // (sourceName) points us only to the script that thrown the exception
-          // - no way to associate it to a specific tab. See bug 603730.
-          HUDService.reportConsoleServiceContentScriptError(aSubject);
-          return;
         default:
-          HUDService.reportConsoleServiceMessage(aSubject);
+          for (let i = 0; i < hudIds.length; i++) {
+            HUDService.logActivity("console-listener", hudIds[i], aSubject);
+          }
           return;
       }
     }
   }
 };
 
 ///////////////////////////////////////////////////////////////////////////
 // appName
--- a/toolkit/components/console/hudservice/tests/browser/Makefile.in
+++ b/toolkit/components/console/hudservice/tests/browser/Makefile.in
@@ -47,24 +47,25 @@ include $(topsrcdir)/config/rules.mk
 
 _BROWSER_TEST_FILES = \
 	browser_webconsole_bug_580030_errors_after_page_reload.js \
 	browser_webconsole_basic_net_logging.js \
 	browser_webconsole_bug_579412_input_focus.js \
 	browser_webconsole_bug_580001_closing_after_completion.js \
 	browser_webconsole_bug_580400_groups.js \
 	browser_webconsole_bug_588730_text_node_insertion.js \
+	browser_webconsole_bug_597136_external_script_errors.js \
+	browser_webconsole_bug_597136_network_requests_from_chrome.js \
 	browser_webconsole_completion.js \
 	browser_webconsole_console_logging_api.js \
 	browser_webconsole_consoleonpage.js \
 	browser_webconsole_chrome.js \
 	browser_webconsole_display_accessors.js \
 	browser_webconsole_execution_scope.js \
 	browser_webconsole_get_content_window_from_hud_id.js \
-	browser_webconsole_get_display_by_uri_spec.js \
 	browser_webconsole_get_heads_up_display.js \
 	browser_webconsole_history.js \
 	browser_webconsole_hud_getters.js \
 	browser_webconsole_js_input_and_output_styling.js \
 	browser_webconsole_js_input_expansion.js \
 	browser_webconsole_live_filtering_of_message_types.js \
 	browser_webconsole_live_filtering_on_search_strings.js \
 	browser_warn_user_about_replaced_api.js \
@@ -126,15 +127,17 @@ include $(topsrcdir)/config/rules.mk
 	test-bug-595934-css-loader.html \
 	test-bug-595934-css-loader.css \
 	test-bug-595934-css-loader.css^headers^ \
 	test-bug-595934-dom-html.html \
 	test-bug-595934-imagemap.html \
 	test-bug-595934-html.html \
 	test-bug-595934-malformedxml.xhtml \
 	test-bug-595934-svg.xhtml \
+	test-bug-597136-external-script-errors.html \
+	test-bug-597136-external-script-errors.js \
 	$(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_warn_user_about_replaced_api.js
+++ b/toolkit/components/console/hudservice/tests/browser/browser_warn_user_about_replaced_api.js
@@ -16,16 +16,17 @@
  *
  * The Initial Developer of the Original Code is Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2010
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *  David Dahl <ddahl@mozilla.com>
  *  Mihai Șucan <mihai.sucan@gmail.com>
+ *  Patrick Walton <pcwalton@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -60,15 +61,13 @@ function testOpenWebConsole()
   testWarning();
 }
 
 function testWarning()
 {
   const successMsg = "Found the warning message";
   const errMsg = "Could not find the warning message about the replaced API";
 
-  var display = HUDService.getDisplayByURISpec(content.location.href);
-  var outputNode = display.querySelectorAll(".hud-output-node")[0];
-
+  outputNode = HUDService.hudWeakReferences[hudId].get().outputNode;
   testLogEntry(outputNode, "disabled", { success: successMsg, err: errMsg });
 
   finishTest();
 }
--- a/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_580030_errors_after_page_reload.js
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_580030_errors_after_page_reload.js
@@ -90,18 +90,19 @@ var consoleObserver = {
       return;
     }
 
     Services.console.unregisterListener(this);
 
     const successMsg = "Found the error message after page reload";
     const errMsg = "Could not get the error message after page reload";
 
-    var display = HUDService.getDisplayByURISpec(content.location.href);
-    var outputNode = display.querySelector(".hud-output-node");
+    hudId = HUDService.displaysIndex()[0];
+    hud = HUDService.hudWeakReferences[hudId].get();
+    outputNode = hud.outputNode;
 
     executeSoon(function() {
       testLogEntry(outputNode, "fooBazBaz",
                    { success: successMsg, err: errMsg });
 
       finishTest();
     });
   }
--- a/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_582201_duplicate_errors.js
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_582201_duplicate_errors.js
@@ -69,18 +69,19 @@ var consoleObserver = {
     // we ignore errors we don't care about
     if (!(aMessage instanceof Ci.nsIScriptError) ||
       aMessage.category != "content javascript") {
       return;
     }
 
     Services.console.unregisterListener(this);
 
-    var display = HUDService.getDisplayByURISpec(content.location.href);
-    var outputNode = display.querySelectorAll(".hud-output-node")[0];
+    hudId = HUDService.displaysIndex()[0];
+    hud = HUDService.hudWeakReferences[hudId].get();
+    outputNode = hud.outputNode;
 
     executeSoon(function () {
       var text = outputNode.textContent;
       var error1pos = text.indexOf("fooDuplicateError1");
       ok(error1pos > -1, "found fooDuplicateError1");
       if (error1pos > -1) {
         ok(text.indexOf("fooDuplicateError1", error1pos + 1) == -1,
           "no duplicate for fooDuplicateError1");
--- a/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_587617_output_copy.js
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_587617_output_copy.js
@@ -1,32 +1,33 @@
 /* ***** BEGIN LICENSE BLOCK *****
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  *
  * Contributor(s):
  *  Mihai Șucan <mihai.sucan@gmail.com>
+ *  Patrick Walton <pcwalton@mozilla.com>
  *
  * ***** END LICENSE BLOCK ***** */
 
 const TEST_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-console.html";
 
 function test() {
   addTab(TEST_URI);
   browser.addEventListener("load", tabLoaded, true);
 }
 
 function tabLoaded() {
   browser.removeEventListener("load", tabLoaded, true);
   openConsole();
 
   // See bugs 574036, 586386 and 587617.
 
-  let HUD = HUDService.getDisplayByURISpec(
-    browser.contentWindow.wrappedJSObject.document.location.href);
+  hudId = HUDService.displaysIndex()[0];
+  let HUD = HUDService.hudWeakReferences[hudId].get().HUDBox;
   let filterBox = HUD.querySelector(".hud-filter-box");
   outputNode = HUD.querySelector(".hud-output-node");
   let selection = getSelection();
   let jstermInput = HUD.querySelector(".jsterm-input-node");
   let console = browser.contentWindow.wrappedJSObject.console;
   let contentSelection = browser.contentWindow.wrappedJSObject.getSelection();
 
   let make_selection = function () {
--- a/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_589162_css_filter.js
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_589162_css_filter.js
@@ -1,27 +1,28 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* ***** BEGIN LICENSE BLOCK *****
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  *
  * Contributor(s):
  *  Mihai Șucan <mihai.sucan@gmail.com>
+ *  Patrick Walton <pcwalton@mozilla.com>
  *
  * ***** END LICENSE BLOCK ***** */
 
 const TEST_URI = "data:text/html,<div style='font-size:3em;" +
   "foobarCssParser:baz'>test CSS parser filter</div>"
 
 function onContentLoaded()
 {
   browser.removeEventListener("load", arguments.callee, true);
 
-  let HUD = HUDService.getDisplayByURISpec(content.location.href);
-  let hudId = HUD.getAttribute("id");
+  hudId = HUDService.displaysIndex()[0];
+  HUD = HUDService.hudWeakReferences[hudId].get().HUDBox;
   let filterBox = HUD.querySelector(".hud-filter-box");
   let outputNode = HUD.querySelector(".hud-output-node");
 
   let warningFound = "the unknown CSS property warning is displayed";
   let warningNotFound = "could not find the unknown CSS property warning";
 
   testLogEntry(outputNode, "foobarCssParser",
     { success: warningFound, err: warningNotFound }, true);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_597136_external_script_errors.js
@@ -0,0 +1,47 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * Contributor(s):
+ *  Patrick Walton <pcwalton@mozilla.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const TEST_URI = "http://example.com/browser/toolkit/components/console/" +
+                 "hudservice/tests/browser/test-bug-597136-external-script-" +
+                 "errors.html";
+
+function test() {
+  addTab(TEST_URI);
+  browser.addEventListener("load", tabLoaded, true);
+}
+
+function tabLoaded(aEvent) {
+  browser.removeEventListener("load", tabLoaded, true);
+  openConsole();
+
+  browser.addEventListener("load", contentLoaded, true);
+  content.location.reload();
+}
+
+function contentLoaded(aEvent) {
+  browser.removeEventListener("load", contentLoaded, true);
+
+  let button = content.document.querySelector("button");
+  EventUtils.sendMouseEvent({ type: "click" }, button, content);
+  executeSoon(buttonClicked);
+}
+
+function buttonClicked() {
+  let hudId = HUDService.getHudIdByWindow(content);
+  let outputNode = HUDService.getOutputNodeById(hudId);
+
+  const successMsg = "the error from the external script was logged";
+  const errorMsg = "the error from the external script was not logged";
+
+  testLogEntry(outputNode, "bogus", { success: successMsg, err: errorMsg });
+
+  finishTest();
+}
+
new file mode 100644
--- /dev/null
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_597136_network_requests_from_chrome.js
@@ -0,0 +1,47 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that network requests from chrome don't cause the Web Console to
+// throw exceptions.
+
+const TEST_URI = "http://example.com/";
+
+let good = true;
+let listener = {
+    QueryInterface: XPCOMUtils.generateQI([ Ci.nsIObserver ]),
+    observe: function(aSubject, aTopic, aData) {
+        if (aSubject instanceof Ci.nsIScriptError &&
+                aSubject.category === "XPConnect JavaScript") {
+            good = false;
+        }
+    }
+};
+
+let xhr;
+
+function test() {
+    Services.console.registerListener(listener);
+
+    HUDService; // trigger a lazy-load of the HUD Service
+
+    xhr = new XMLHttpRequest();
+    xhr.addEventListener("load", xhrComplete, false);
+    xhr.open("GET", TEST_URI, true);
+    xhr.send(null);
+}
+
+function xhrComplete() {
+    xhr.removeEventListener("load", xhrComplete, false);
+    window.setTimeout(checkForException, 0);
+}
+
+function checkForException() {
+    ok(good, "no exception was thrown when sending a network request from a " +
+       "chrome window");
+
+    Services.console.unregisterListener(listener);
+    listener = null;
+
+    finishTest();
+}
+
deleted file mode 100644
--- a/toolkit/components/console/hudservice/tests/browser/browser_webconsole_get_display_by_uri_spec.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is DevTools test code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  David Dahl <ddahl@mozilla.com>
- *  Patrick Walton <pcwalton@mozilla.com>
- *  Julian Viereck <jviereck@mozilla.com>
- *  Mihai Sucan <mihai.sucan@gmail.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-// Tests the HUDService.getDisplayByURISpec() method.
-
-const TEST_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-console.html";
-
-function test() {
-  addTab(TEST_URI);
-  browser.addEventListener("DOMContentLoaded", testGetDisplayByURISpec,
-                           false);
-}
-
-function testGetDisplayByURISpec() {
-  browser.removeEventListener("DOMContentLoaded", testGetDisplayByURISpec,
-                              false);
-  openConsole();
-  outputNode = HUDService.getDisplayByURISpec(TEST_URI);
-  hudId = outputNode.getAttribute("id");
-  ok(hudId == HUDService.displaysIndex()[0], "outputNode fetched by URIspec");
-  finishTest();
-}
-
new file mode 100644
--- /dev/null
+++ b/toolkit/components/console/hudservice/tests/browser/test-bug-597136-external-script-errors.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<!--
+  ***** BEGIN LICENSE BLOCK *****
+  * Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/
+  *
+  * Contributor(s):
+  *  Patrick Walton <pcwalton@mozilla.com>
+  *
+  * ***** END LICENSE BLOCK *****
+  -->
+  <head>
+    <title>Test for bug 597136: external script errors</title>
+  </head>
+  <body>
+    <h1>Test for bug 597136: external script errors</h1>
+    <p><button onclick="f()">Click me</button</p>
+
+    <script type="text/javascript"
+      src="test-bug-597136-external-script-errors.js"></script>
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/toolkit/components/console/hudservice/tests/browser/test-bug-597136-external-script-errors.js
@@ -0,0 +1,14 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * Contributor(s):
+ *  Patrick Walton <pcwalton@mozilla.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+function f() {
+  bogus.g();
+}
+