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 214413 5634e9cd480798de6145292ca3d71385f234a8cc
parent 214247 9737f919f1c0b9fe18e3582805aef50731fae919
child 214414 0fa23eddfe123000fee67dc5c557342a4c4df62f
push id27781
push userkwierso@gmail.com
push dateFri, 07 Nov 2014 02:47:58 +0000
treeherdermozilla-central@f54c94033c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs684096
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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.
    *