Bug 611440 - Smart abbreviation for URLs in the Web Console; f=mihai.sucan,dolske r=dolske approval2.0=dolske
authorPatrick Walton <pwalton@mozilla.com>
Mon, 24 Jan 2011 12:05:05 -0400
changeset 61176 812710794ca1a65b9f5c5014babc948d32376bbb
parent 61175 e09b598992e8f4495e1bd7d51f8b95d39ee966de
child 61177 9ec42a4717062c495cc70fe6d7b10c23b3d79243
push idunknown
push userunknown
push dateunknown
reviewersdolske
bugs611440
milestone2.0b10pre
Bug 611440 - Smart abbreviation for URLs in the Web Console; f=mihai.sucan,dolske r=dolske approval2.0=dolske
toolkit/components/console/hudservice/HUDService.jsm
toolkit/components/console/hudservice/tests/browser/browser_webconsole_basic_net_logging.js
toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_582201_duplicate_errors.js
toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_593003_iframe_wrong_hud.js
toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_601177_log_levels.js
toolkit/components/console/hudservice/tests/browser/head.js
toolkit/locales/en-US/chrome/global/headsUpDisplay.properties
toolkit/themes/gnomestripe/global/webConsole.css
toolkit/themes/pinstripe/global/webConsole.css
toolkit/themes/winstripe/global/webConsole.css
--- a/toolkit/components/console/hudservice/HUDService.jsm
+++ b/toolkit/components/console/hudservice/HUDService.jsm
@@ -2183,17 +2183,17 @@ HUD_SERVICE.prototype =
             // Copy the request header data.
             aChannel.visitRequestHeaders({
               visitHeader: function(aName, aValue) {
                 httpActivity.request.header[aName] = aValue;
               }
             });
 
             // Store the loggedNode and the httpActivity object for later reuse.
-            let linkNode = loggedNode.querySelector(".webconsole-msg-url");
+            let linkNode = loggedNode.querySelector(".webconsole-msg-link");
 
             httpActivity.messageObject = {
               messageNode: loggedNode,
               linkNode:    linkNode
             };
             self.openRequests[httpActivity.id] = httpActivity;
 
             // Make the network span clickable.
@@ -2231,17 +2231,17 @@ HUD_SERVICE.prototype =
             if (!httpActivity) {
               return;
             }
 
             hudId = httpActivity.hudId;
             let msgObject = httpActivity.messageObject;
 
             let updatePanel = false;
-            let data, textNode;
+            let data;
             // Store the time information for this activity subtype.
             httpActivity.timing[transCodes[aActivitySubtype]] = aTimestamp;
 
             switch (aActivitySubtype) {
               case activityDistributor.ACTIVITY_SUBTYPE_REQUEST_BODY_SENT: {
                 if (!self.saveRequestAndResponseBodies) {
                   httpActivity.request.bodyDiscarded = true;
                   break;
@@ -2281,35 +2281,27 @@ HUD_SERVICE.prototype =
                 // Note: The response header is not saved here. Calling the
                 //       aChannel.visitResponseHeaders at this point sometimes
                 //       causes an NS_ERROR_NOT_AVAILABLE exception. Therefore,
                 //       the response header and response body is stored on the
                 //       httpActivity object within the RL_onStopRequest function.
                 httpActivity.response.status =
                   aExtraStringData.split(/\r\n|\n|\r/)[0];
 
-                // Remove the text node from the URL node and add a new one that
-                // contains the response status.
-                textNode = msgObject.linkNode.firstChild;
-                textNode.parentNode.removeChild(textNode);
-
-                data = [ httpActivity.url,
-                         httpActivity.response.status ];
-
-                // Format the pieces of data. The result will be something like
-                // "http://example.com/ [HTTP/1.0 200 OK]".
-                let text = self.getFormatStr("networkUrlWithStatus", data);
-
-                // Replace the displayed text and the clipboard text with the
-                // new data.
-                let chromeDocument = msgObject.messageNode.ownerDocument;
-                msgObject.linkNode.appendChild(
-                  chromeDocument.createTextNode(text));
+                // Add the response status.
+                let linkNode = msgObject.linkNode;
+                let statusNode = linkNode.
+                  querySelector(".webconsole-msg-status");
+                let statusText = "[" + httpActivity.response.status + "]";
+                statusNode.setAttribute("value", statusText);
+
+                let clipboardTextPieces =
+                  [ httpActivity.method, httpActivity.url, statusText ];
                 msgObject.messageNode.clipboardText =
-                  msgObject.messageNode.textContent;
+                  clipboardTextPieces.join(" ");
 
                 let status = parseInt(httpActivity.response.status.
                   replace(/^HTTP\/\d\.\d (\d+).+$/, "$1"));
 
                 if (status >= MIN_HTTP_ERROR_CODE &&
                     status < MAX_HTTP_ERROR_CODE) {
                   ConsoleUtils.setMessageType(msgObject.messageNode,
                                               CATEGORY_NETWORK,
@@ -2320,37 +2312,31 @@ HUD_SERVICE.prototype =
               }
 
               case activityDistributor.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE: {
                 let timing = httpActivity.timing;
                 let requestDuration =
                   Math.round((timing.RESPONSE_COMPLETE -
                                 timing.REQUEST_HEADER) / 1000);
 
-                // Remove the text node from the link node and add a new one
-                // that contains the request duration.
-                textNode = msgObject.linkNode.firstChild;
-                textNode.parentNode.removeChild(textNode);
-
-                data = [ httpActivity.url,
-                         httpActivity.response.status,
-                         requestDuration ];
-
-                // Format the pieces of data. The result will be something like
-                // "http://example.com/ [HTTP/1.0 200 OK 200 ms]".
-                let text = self.getFormatStr("networkUrlWithStatusAndDuration",
-                                             data);
-
-                // Replace the displayed text and the clipboard text with the
-                // new data.
-                let chromeDocument = msgObject.messageNode.ownerDocument;
-                msgObject.linkNode.appendChild(
-                  chromeDocument.createTextNode(text));
+                // Add the request duration.
+                let linkNode = msgObject.linkNode;
+                let statusNode = linkNode.
+                  querySelector(".webconsole-msg-status");
+
+                let statusText = httpActivity.response.status;
+                let timeText = self.getFormatStr("NetworkPanel.durationMS",
+                                                 [ requestDuration ]);
+                let fullStatusText = "[" + statusText + " " + timeText + "]";
+                statusNode.setAttribute("value", fullStatusText);
+
+                let clipboardTextPieces =
+                  [ httpActivity.method, httpActivity.url, fullStatusText ];
                 msgObject.messageNode.clipboardText =
-                  msgObject.messageNode.textContent;
+                  clipboardTextPieces.join(" ");
 
                 delete self.openRequests[item.id];
                 updatePanel = true;
                 break;
               }
             }
 
             if (updatePanel) {
@@ -2447,29 +2433,46 @@ HUD_SERVICE.prototype =
    * @returns void
    */
   logNetActivity: function HS_logNetActivity(aActivityObject)
   {
     let hudId = aActivityObject.hudId;
     let outputNode = this.hudReferences[hudId].outputNode;
 
     let chromeDocument = outputNode.ownerDocument;
-    let msgNode = chromeDocument.createElementNS(HTML_NS, "html:span");
-
-    // Create the method part of the message (e.g. "GET").
-    let method = chromeDocument.createTextNode(aActivityObject.method + " ");
-    msgNode.appendChild(method);
-
-    // Create the clickable URL part of the message.
-    let linkNode = chromeDocument.createElementNS(HTML_NS, "html:span");
-    linkNode.appendChild(chromeDocument.createTextNode(aActivityObject.url));
-    linkNode.classList.add("hud-clickable");
-    linkNode.classList.add("webconsole-msg-url");
+    let msgNode = chromeDocument.createElementNS(XUL_NS, "xul:hbox");
+
+    let methodNode = chromeDocument.createElementNS(XUL_NS, "xul:label");
+    methodNode.setAttribute("value", aActivityObject.method);
+    methodNode.classList.add("webconsole-msg-body-piece");
+    msgNode.appendChild(methodNode);
+
+    let linkNode = chromeDocument.createElementNS(XUL_NS, "xul:hbox");
+    linkNode.setAttribute("flex", "1");
+    linkNode.classList.add("webconsole-msg-body-piece");
+    linkNode.classList.add("webconsole-msg-link");
     msgNode.appendChild(linkNode);
 
+    let urlNode = chromeDocument.createElementNS(XUL_NS, "xul:label");
+    urlNode.setAttribute("crop", "center");
+    urlNode.setAttribute("flex", "1");
+    urlNode.setAttribute("title", aActivityObject.url);
+    urlNode.setAttribute("value", aActivityObject.url);
+    urlNode.classList.add("hud-clickable");
+    urlNode.classList.add("webconsole-msg-body-piece");
+    urlNode.classList.add("webconsole-msg-url");
+    linkNode.appendChild(urlNode);
+
+    let statusNode = chromeDocument.createElementNS(XUL_NS, "xul:label");
+    statusNode.setAttribute("value", "");
+    statusNode.classList.add("hud-clickable");
+    statusNode.classList.add("webconsole-msg-body-piece");
+    statusNode.classList.add("webconsole-msg-status");
+    linkNode.appendChild(statusNode);
+
     let clipboardText = aActivityObject.method + " " + aActivityObject.url;
 
     let messageNode = ConsoleUtils.createMessageNode(chromeDocument,
                                                      CATEGORY_NETWORK,
                                                      SEVERITY_LOG,
                                                      msgNode,
                                                      null,
                                                      null,
--- a/toolkit/components/console/hudservice/tests/browser/browser_webconsole_basic_net_logging.js
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_basic_net_logging.js
@@ -55,22 +55,20 @@ function onLoad(aEvent) {
 
   browser.addEventListener("load", testBasicNetLogging, true);
   content.location = TEST_NETWORK_URI;
 }
 
 function testBasicNetLogging(aEvent) {
   browser.removeEventListener(aEvent.type, arguments.callee, true);
 
-  hudBox = HUDService.getHeadsUpDisplay(hudId);
+  outputNode = HUDService.hudReferences[hudId].outputNode;
 
   executeSoon(function() {
-    let text = hudBox.querySelector(".hud-output-node").textContent;
-
-    isnot(text.indexOf("test-network.html"), -1, "found test-network.html");
-    isnot(text.indexOf("testscript.js"), -1, "found testscript.js");
-    isnot(text.indexOf("test-image.png"), -1, "found test-image.png");
-    isnot(text.indexOf("network console"), -1, "found network console");
+    findLogEntry("test-network.html");
+    findLogEntry("testscript.js");
+    findLogEntry("test-image.png");
+    findLogEntry("network console");
 
     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
@@ -82,15 +82,14 @@ var consoleObserver = {
       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");
       }
 
-      ok(text.indexOf("test-duplicate-error.html") > -1,
-        "found test-duplicate-error.html");
+      findLogEntry("test-duplicate-error.html");
 
       finishTest();
     });
   }
 };
--- a/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_593003_iframe_wrong_hud.js
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_593003_iframe_wrong_hud.js
@@ -15,16 +15,17 @@
  * 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):
  *  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
--- a/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_601177_log_levels.js
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_601177_log_levels.js
@@ -13,51 +13,45 @@ const TEST_URI = "http://example.com/bro
 let msgs;
 
 function onContentLoaded()
 {
   let hudId = HUDService.getHudIdByWindow(content);
   let HUD = HUDService.hudReferences[hudId];
   msgs = HUD.outputNode.querySelectorAll(".hud-msg-node");
 
-  ok(findEntry("hud-networkinfo", "test-bug-601177-log-levels.html"),
-    "found test-bug-601177-log-levels.html");
+  findEntry(HUD, "hud-networkinfo", "test-bug-601177-log-levels.html",
+            "found test-bug-601177-log-levels.html");
 
-  ok(findEntry("hud-networkinfo", "test-bug-601177-log-levels.js"),
-    "found test-bug-601177-log-levels.js");
+  findEntry(HUD, "hud-networkinfo", "test-bug-601177-log-levels.js",
+            "found test-bug-601177-log-levels.js");
 
-  ok(findEntry("hud-networkinfo", "test-image.png"),
-    "found test-image.png");
+  findEntry(HUD, "hud-networkinfo", "test-image.png", "found test-image.png");
 
-  ok(findEntry("hud-network", "foobar-known-to-fail.png"),
-    "found foobar-known-to-fail.png");
+  findEntry(HUD, "hud-network", "foobar-known-to-fail.png",
+            "found foobar-known-to-fail.png");
 
-  ok(findEntry("hud-exception", "foobarBug601177exception"),
-    "found exception");
+  findEntry(HUD, "hud-exception", "foobarBug601177exception",
+            "found exception");
 
-  ok(findEntry("hud-jswarn", "undefinedPropertyBug601177"),
-    "found strict warning");
+  findEntry(HUD, "hud-jswarn", "undefinedPropertyBug601177",
+            "found strict warning");
 
-  ok(findEntry("hud-jswarn", "foobarBug601177strictError"),
-    "found strict error");
+  findEntry(HUD, "hud-jswarn", "foobarBug601177strictError",
+            "found strict error");
 
   msgs = null;
   Services.prefs.setBoolPref("javascript.options.strict", false);
   finishTest();
 }
 
-function findEntry(aClass, aString)
+function findEntry(aHUD, aClass, aString, aMessage)
 {
-  for (let i = 0, n = msgs.length; i < n; i++) {
-    if (msgs[i].classList.contains(aClass) &&
-        msgs[i].textContent.indexOf(aString) > -1) {
-      return true;
-    }
-  }
-  return false;
+  return testLogEntry(aHUD.outputNode, aString, aMessage, false, false,
+                      aClass);
 }
 
 function test()
 {
   addTab("data:text/html,Web Console test for bug 601177: log levels");
 
   Services.prefs.setBoolPref("javascript.options.strict", true);
 
--- a/toolkit/components/console/hudservice/tests/browser/head.js
+++ b/toolkit/components/console/hudservice/tests/browser/head.js
@@ -90,38 +90,64 @@ function addTab(aURL)
  * @param {string} aMatchString
  *        the string you want to check if it exists in the output node.
  * @param {string} aMsg
  *        the message describing the test
  * @param {boolean} [aOnlyVisible=false]
  *        find only messages that are visible, not hidden by the filter.
  * @param {boolean} [aFailIfFound=false]
  *        fail the test if the string is found in the output node.
+ * @param {string} aClass [optional]
+ *        find only messages with the given CSS class.
  */
 function testLogEntry(aOutputNode, aMatchString, aMsg, aOnlyVisible,
-                      aFailIfFound)
+                      aFailIfFound, aClass)
 {
   let selector = ".hud-msg-node";
   // Skip entries that are hidden by the filter.
   if (aOnlyVisible) {
     selector += ":not(.hud-filtered-by-type)";
   }
+  if (aClass) {
+    selector += "." + aClass;
+  }
 
   let msgs = aOutputNode.querySelectorAll(selector);
   let found = false;
   for (let i = 0, n = msgs.length; i < n; i++) {
     let message = msgs[i].textContent.indexOf(aMatchString);
     if (message > -1) {
       found = true;
       break;
     }
+
+    // Search the labels too.
+    let labels = msgs[i].querySelectorAll("label");
+    for (let j = 0; j < labels.length; j++) {
+      if (labels[j].getAttribute("value").indexOf(aMatchString) > -1) {
+        found = true;
+        break;
+      }
+    }
   }
+
   is(found, !aFailIfFound, aMsg);
 }
 
+/**
+ * A convenience method to call testLogEntry().
+ *
+ * @param string aString
+ *        The string to find.
+ */
+function findLogEntry(aString)
+{
+  testLogEntry(outputNode, aString, "found " + aString);
+}
+
 function openConsole()
 {
   HUDService.activateHUDForContext(tab);
 }
 
 function closeConsole()
 {
   HUDService.deactivateHUDForContext(tab);
--- a/toolkit/locales/en-US/chrome/global/headsUpDisplay.properties
+++ b/toolkit/locales/en-US/chrome/global/headsUpDisplay.properties
@@ -82,37 +82,16 @@ copyCmd.label=Copy
 copyCmd.accesskey=C
 selectAllCmd.label=Select All
 selectAllCmd.accesskey=A
 # LOCALIZATION NOTE (timestampFormat): %1$02S = hours (24-hour clock),
 # %2$02S = minutes, %3$02S = seconds, %4$03S = milliseconds.
 timestampFormat=%02S:%02S:%02S.%03S
 
 helperFuncUnsupportedTypeError=Can't call pprint on this type of object.
-# LOCALIZATION NOTE (networkUrlWithStatus):
-#
-# When the HTTP request is started only the URL of the request is printed to the
-# WebConsole. As the response status of the HTTP request arrives, the URL string
-# is replaced by this string (the response status can look like `HTTP/1.1 200 OK`).
-# The bracket is not closed to mark that this request is not done by now. As the
-# request is finished (the HTTP connection is closed) this string is replaced
-# by `networkUrlWithStatusAndDuration` which has a closing the braket.
-#
-# %1$S = URL of network request
-# %2$S = response status code from the server (e.g. `HTTP/1.1 200 OK`)
-networkUrlWithStatus=%1$S [%2$S
-# LOCALIZATION NOTE (networkUrlWithStatusAndDuration):
-#
-# When the HTTP request is finished (the HTTP connection is closed) this string
-# replaces the former `networkUrlWithStatus` string in the WebConsole.
-#
-# %1$S = URL of network request
-# %2$S = response status code from the server (e.g. `HTTP/1.1 200 OK`)
-# %3$S = duration for the complete network request in milliseconds
-networkUrlWithStatusAndDuration=%1$S [%2$S %3$Sms]
 NetworkPanel.label=Inspect Network Request
 # LOCALIZATION NOTE (NetworkPanel.deltaDurationMS):
 #
 # This string is used to show the duration between two network events (e.g
 # request and respones header or response header and response body).
 NetworkPanel.durationMS=%Sms
 # LOCALIZATION NOTE (NetworkPanel.imageSizeDeltaDurationMS):
 # This string is used to show the duration between the response header and the
--- a/toolkit/themes/gnomestripe/global/webConsole.css
+++ b/toolkit/themes/gnomestripe/global/webConsole.css
@@ -86,16 +86,24 @@
 
 .webconsole-msg-body {
   margin-top: 0;
   margin-bottom: 3px;
   -moz-margin-start: 3px;
   -moz-margin-end: 6px;
 }
 
+.webconsole-msg-body-piece {
+  margin: 0;
+}
+
+.webconsole-msg-url {
+  margin: 0 6px;
+}
+
 .webconsole-location {
   margin-top: 0;
   margin-bottom: 0;
   -moz-margin-start: 0;
   -moz-margin-end: 6px;
   width: 10em;
   text-align: end;
 }
--- a/toolkit/themes/pinstripe/global/webConsole.css
+++ b/toolkit/themes/pinstripe/global/webConsole.css
@@ -89,16 +89,24 @@
 
 .webconsole-msg-body {
   margin-top: 0;
   margin-bottom: 3px;
   -moz-margin-start: 3px;
   -moz-margin-end: 6px;
 }
 
+.webconsole-msg-body-piece {
+  margin: 0;
+}
+
+.webconsole-msg-url {
+  margin: 0 6px;
+}
+
 .webconsole-location {
   margin-top: 0;
   margin-bottom: 0;
   -moz-margin-start: 0;
   -moz-margin-end: 6px;
   width: 10em;
   text-align: end;
 }
--- a/toolkit/themes/winstripe/global/webConsole.css
+++ b/toolkit/themes/winstripe/global/webConsole.css
@@ -85,16 +85,24 @@
 
 .webconsole-msg-body {
   margin-top: 0;
   margin-bottom: 3px;
   -moz-margin-start: 3px;
   -moz-margin-end: 6px;
 }
 
+.webconsole-msg-body-piece {
+  margin: 0;
+}
+
+.webconsole-msg-url {
+  margin: 0 6px;
+}
+
 .webconsole-location {
   margin-top: 0;
   margin-bottom: 0;
   -moz-margin-start: 0;
   -moz-margin-end: 6px;
   width: 10em;
   text-align: end;
 }