Bug 684096 - "The webconsole should display column numbers in addition to line numbers". c=mdibaiee
authorMahdi Dibaiee <mdibaiee@aol.com>
Wed, 05 Nov 2014 05:43:00 +0100
changeset 214223 5634e9cd480798de6145292ca3d71385f234a8cc
parent 214222 9737f919f1c0b9fe18e3582805aef50731fae919
child 214224 0fa23eddfe123000fee67dc5c557342a4c4df62f
push id9806
push usercbook@mozilla.com
push dateThu, 06 Nov 2014 10:01:35 +0000
treeherderfx-team@0fa23eddfe12 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs684096
milestone36.0a1
Bug 684096 - "The webconsole should display column numbers in addition to line numbers". c=mdibaiee
browser/devtools/scratchpad/test/browser_scratchpad_wrong_window_focus.js
browser/devtools/webconsole/console-output.js
browser/devtools/webconsole/test/browser.ini
browser/devtools/webconsole/test/browser_webconsole_column_numbers.js
browser/devtools/webconsole/test/test-console-column.html
browser/devtools/webconsole/test/test-repeated-messages.html
browser/devtools/webconsole/webconsole.js
--- a/browser/devtools/scratchpad/test/browser_scratchpad_wrong_window_focus.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_wrong_window_focus.js
@@ -46,17 +46,17 @@ function testFocus(sw, hud) {
   let sp = sw.Scratchpad;
 
   function onMessage(event, messages) {
     let msg = [...messages][0];
     let node = msg.node;
 
     var loc = node.querySelector(".message-location");
     ok(loc, "location element exists");
-    is(loc.textContent.trim(), sw.Scratchpad.uniqueName + ":1",
+    is(loc.textContent.trim(), sw.Scratchpad.uniqueName + ":1:0",
         "location value is correct");
 
     sw.addEventListener("focus", function onFocus() {
       sw.removeEventListener("focus", onFocus, true);
 
       let win = Services.wm.getMostRecentWindow("devtools:scratchpad");
 
       ok(win, "there are active Scratchpad windows");
--- a/browser/devtools/webconsole/console-output.js
+++ b/browser/devtools/webconsole/console-output.js
@@ -938,24 +938,26 @@ Messages.Simple.prototype = Heritage.ext
    * @return Element
    */
   _renderLocation: function()
   {
     if (!this.location) {
       return null;
     }
 
-    let {url, line} = this.location;
+    let {url, line, column} = this.location;
     if (IGNORED_SOURCE_URLS.indexOf(url) != -1) {
       return null;
     }
 
     // The ConsoleOutput owner is a WebConsoleFrame instance from webconsole.js.
     // TODO: move createLocationNode() into this file when bug 778766 is fixed.
-    return this.output.owner.createLocationNode(url, line);
+    return this.output.owner.createLocationNode({url: url,
+                                                 line: line,
+                                                 column: column});
   },
 }); // Messages.Simple.prototype
 
 
 /**
  * The Extended message.
  *
  * @constructor
@@ -1245,16 +1247,17 @@ Messages.ConsoleGeneric = function(packe
     timestamp: packet.timeStamp,
     category: "webdev",
     severity: CONSOLE_API_LEVELS_TO_SEVERITIES[packet.level],
     private: packet.private,
     filterDuplicates: true,
     location: {
       url: packet.filename,
       line: packet.lineNumber,
+      column: packet.columnNumber
     },
   };
 
   switch (packet.level) {
     case "count": {
       let counter = packet.counter, label = counter.label;
       if (!label) {
         label = l10n.getStr("noCounterLabel");
@@ -3441,18 +3444,18 @@ Widgets.Stacktrace.prototype = Heritage.
       span.textContent = frame.functionName;
       fn.appendChild(span);
       fn.appendChild(this.document.createTextNode("()"));
     } else {
       fn.classList.add("cm-comment");
       fn.textContent = l10n.getStr("stacktrace.anonymousFunction");
     }
 
-    let location = this.output.owner.createLocationNode(frame.filename,
-                                                        frame.lineNumber,
+    let location = this.output.owner.createLocationNode({url: frame.filename,
+                                                        line: frame.lineNumber},
                                                         "jsdebugger");
 
     // .devtools-monospace sets font-size to 80%, however .body already has
     // .devtools-monospace. If we keep it here, the location would be rendered
     // smaller.
     location.classList.remove("devtools-monospace");
 
     let elem = this.document.createElementNS(XHTML_NS, "li");
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -77,16 +77,17 @@ support-files =
   test-console-replaced-api.html
   test-console.html
   test-console-table.html
   test-console-output-02.html
   test-console-output-03.html
   test-console-output-04.html
   test-console-output-dom-elements.html
   test-console-output-events.html
+  test-console-column.html
   test-consoleiframes.html
   test-data.json
   test-data.json^headers^
   test-duplicate-error.html
   test-encoding-ISO-8859-1.html
   test-error.html
   test-eval-in-stackframe.html
   test-file-location.js
@@ -319,8 +320,9 @@ skip-if = buildapp == 'mulet'
 [browser_webconsole_output_table.js]
 [browser_console_variables_view_highlighter.js]
 [browser_webconsole_start_netmon_first.js]
 [browser_webconsole_console_trace_duplicates.js]
 [browser_webconsole_cd_iframe.js]
 [browser_webconsole_autocomplete_crossdomain_iframe.js]
 [browser_webconsole_console_custom_styles.js]
 [browser_webconsole_console_api_stackframe.js]
+[browser_webconsole_column_numbers.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_column_numbers.js
@@ -0,0 +1,48 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+ // Check if console provides the right column number alongside line number
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-column.html";
+
+let hud;
+
+function test() {
+  addTab(TEST_URI);
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    openConsole(null, consoleOpened);
+  }, true);
+}
+
+function consoleOpened(aHud) {
+  hud = aHud;
+
+  waitForMessages({
+    webconsole: hud,
+    messages: [{
+      text: 'Error Message',
+      category: CATEGORY_WEBDEV,
+      severity: SEVERITY_ERROR
+    }]
+  }).then(testLocationColumn);
+}
+
+function testLocationColumn() {
+  let messages = hud.outputNode.children;
+  let expected = ['6:6', '6:38', '7:8', '8:10', '9:8', '10:6'];
+
+  let valid = true;
+
+  for(let i = 0, len = messages.length; i < len; i++) {
+    let msg = messages[i].textContent;
+
+    is(!msg.contains(expected[i]), true, 'Found expected line:column of ' + expected[i])
+  }
+
+  is(valid, true, 'column numbers match expected results');
+
+  finishTest();
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/test-console-column.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+  <head>
+    <!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+    <meta charset="utf-8">
+    <title>Console test</title>
+
+    <script type="text/javascript">
+      console.info("INLINE SCRIPT:"); console.log('Further');
+        console.warn("I'm warning you, he will eat up all yr bacon.");
+          console.error("Error Message");
+        console.log('Rainbooooww');
+      console.log('NYAN CATZ');
+    </script>
+  </head>
+</html>
--- a/browser/devtools/webconsole/test/test-repeated-messages.html
+++ b/browser/devtools/webconsole/test/test-repeated-messages.html
@@ -1,16 +1,19 @@
 <!DOCTYPE HTML>
 <html dir="ltr" xml:lang="en-US" lang="en-US">
   <head>
     <meta charset="utf8">
     <title>Test for bugs 720180, 800510 and 865288</title>
     <script>
       function testConsole() {
-        console.log("foo repeat"); console.log("foo repeat");
+        // same line and column number
+        for(var i = 0; i < 2; i++) {
+          console.log("foo repeat");
+        }
         console.log("foo repeat"); console.error("foo repeat");
       }
       function testConsoleObjects() {
         for (var i = 0; i < 3; i++) {
           var o = { id: "abba" + i };
           console.log("abba", o);
         }
       }
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -1354,19 +1354,29 @@ WebConsoleFrame.prototype = {
    *        The error message to report.
    * @return nsIDOMElement|undefined
    *         The message element to display in the Web Console output.
    */
   reportPageError: function WCF_reportPageError(aCategory, aScriptError)
   {
     // Warnings and legacy strict errors become warnings; other types become
     // errors.
-    let severity = SEVERITY_ERROR;
+    let severity = 'error';
     if (aScriptError.warning || aScriptError.strict) {
-      severity = SEVERITY_WARNING;
+      severity = 'warning';
+    }
+
+    let category = 'js';
+    switch(aCategory) {
+      case CATEGORY_CSS:
+        category = 'css';
+        break;
+      case CATEGORY_SECURITY:
+        category = 'security';
+        break;
     }
 
     let objectActors = new Set();
 
     // Gather the actor IDs.
     for (let prop of ["errorMessage", "lineText"]) {
       let grip = aScriptError[prop];
       if (WebConsoleUtils.isActorGrip(grip)) {
@@ -1374,31 +1384,37 @@ WebConsoleFrame.prototype = {
       }
     }
 
     let errorMessage = aScriptError.errorMessage;
     if (errorMessage.type && errorMessage.type == "longString") {
       errorMessage = errorMessage.initial;
     }
 
-    let node = this.createMessageNode(aCategory, severity,
-                                      errorMessage,
-                                      aScriptError.sourceName,
-                                      aScriptError.lineNumber, null, null,
-                                      aScriptError.timeStamp);
+    // Create a new message
+    let msg = new Messages.Simple(errorMessage, {
+      location: {
+        url: aScriptError.sourceName,
+        line: aScriptError.lineNumber,
+        column: aScriptError.columnNumber
+      },
+      category: category,
+      severity: severity,
+      timestamp: aScriptError.timeStamp,
+      private: aScriptError.private,
+      filterDuplicates: true
+    });
+
+    let node = msg.init(this.output).render().element;
 
     // Select the body of the message node that is displayed in the console
     let msgBody = node.getElementsByClassName("message-body")[0];
     // Add the more info link node to messages that belong to certain categories
     this.addMoreInfoLink(msgBody, aScriptError);
 
-    if (aScriptError.private) {
-      node.setAttribute("private", true);
-    }
-
     if (objectActors.size > 0) {
       node._objectActors = objectActors;
     }
 
     return node;
   },
 
   /**
@@ -2556,17 +2572,18 @@ WebConsoleFrame.prototype = {
 
     let timestampString = l10n.timestampString(timestamp);
     timestampNode.textContent = timestampString + " ";
 
     // Create the source location (e.g. www.example.com:6) that sits on the
     // right side of the message, if applicable.
     let locationNode;
     if (aSourceURL && IGNORED_SOURCE_URLS.indexOf(aSourceURL) == -1) {
-      locationNode = this.createLocationNode(aSourceURL, aSourceLine);
+      locationNode = this.createLocationNode({url: aSourceURL,
+                                              line: aSourceLine});
     }
 
     node.appendChild(timestampNode);
     node.appendChild(indentNode);
     node.appendChild(iconContainer);
 
     // Display the variables view after the message node.
     if (aLevel == "dir") {
@@ -2599,95 +2616,92 @@ WebConsoleFrame.prototype = {
 
     return node;
   },
 
   /**
    * Creates the anchor that displays the textual location of an incoming
    * message.
    *
-   * @param string aSourceURL
-   *        The URL of the source file responsible for the error.
-   * @param number aSourceLine [optional]
-   *        The line number on which the error occurred. If zero or omitted,
-   *        there is no line number associated with this message.
+   * @param object aLocation
+   *        An object containing url, line and column number of the message source (destructured).
    * @param string aTarget [optional]
    *        Tells which tool to open the link with, on click. Supported tools:
    *        jsdebugger, styleeditor, scratchpad.
    * @return nsIDOMNode
    *         The new anchor element, ready to be added to the message node.
    */
   createLocationNode:
-  function WCF_createLocationNode(aSourceURL, aSourceLine, aTarget)
+  function WCF_createLocationNode({url, line, column}, aTarget)
   {
-    if (!aSourceURL) {
-      aSourceURL = "";
+    if (!url) {
+      url = "";
     }
     let locationNode = this.document.createElementNS(XHTML_NS, "a");
     let filenameNode = this.document.createElementNS(XHTML_NS, "span");
 
     // Create the text, which consists of an abbreviated version of the URL
     // Scratchpad URLs should not be abbreviated.
     let filename;
     let fullURL;
     let isScratchpad = false;
 
-    if (/^Scratchpad\/\d+$/.test(aSourceURL)) {
-      filename = aSourceURL;
-      fullURL = aSourceURL;
+    if (/^Scratchpad\/\d+$/.test(url)) {
+      filename = url;
+      fullURL = url;
       isScratchpad = true;
     }
     else {
-      fullURL = aSourceURL.split(" -> ").pop();
+      fullURL = url.split(" -> ").pop();
       filename = WebConsoleUtils.abbreviateSourceURL(fullURL);
     }
 
     filenameNode.className = "filename";
     filenameNode.textContent = " " + (filename || l10n.getStr("unknownLocation"));
     locationNode.appendChild(filenameNode);
 
     locationNode.href = isScratchpad || !fullURL ? "#" : fullURL;
     locationNode.draggable = false;
     if (aTarget) {
       locationNode.target = aTarget;
     }
-    locationNode.setAttribute("title", aSourceURL);
+    locationNode.setAttribute("title", url);
     locationNode.className = "message-location theme-link devtools-monospace";
 
     // Make the location clickable.
     let onClick = () => {
       let target = locationNode.target;
       if (target == "scratchpad" || isScratchpad) {
-        this.owner.viewSourceInScratchpad(aSourceURL);
+        this.owner.viewSourceInScratchpad(url);
         return;
       }
 
       let category = locationNode.parentNode.category;
       if (target == "styleeditor" || category == CATEGORY_CSS) {
-        this.owner.viewSourceInStyleEditor(fullURL, aSourceLine);
+        this.owner.viewSourceInStyleEditor(fullURL, line);
       }
       else if (target == "jsdebugger" ||
                category == CATEGORY_JS || category == CATEGORY_WEBDEV) {
-        this.owner.viewSourceInDebugger(fullURL, aSourceLine);
+        this.owner.viewSourceInDebugger(fullURL, line);
       }
       else {
-        this.owner.viewSource(fullURL, aSourceLine);
+        this.owner.viewSource(fullURL, line);
       }
     };
 
     if (fullURL) {
       this._addMessageLinkCallback(locationNode, onClick);
     }
 
-    if (aSourceLine) {
+    if (line) {
       let lineNumberNode = this.document.createElementNS(XHTML_NS, "span");
       lineNumberNode.className = "line-number";
-      lineNumberNode.textContent = ":" + aSourceLine;
+      lineNumberNode.textContent = ":" + line + (column >= 0 ? ":" + column : "");
       locationNode.appendChild(lineNumberNode);
-      locationNode.sourceLine = aSourceLine;
+      locationNode.sourceLine = line;
     }
 
     return locationNode;
   },
 
   /**
    * Adjusts the category and severity of the given message.
    *