Bug 831020 - Errors in the error console with very long reference lines hang the browser. r=neil
authorJared Wein <jwein@mozilla.com>
Wed, 23 Jan 2013 13:10:07 -0500
changeset 119666 7c5ab9bc4a5dc7073938d12195f1126ec507aedb
parent 119665 4b6d8e1061da10189d3ca2d5bb84d6e5a5379387
child 119667 ef03bfa48a45b03d107e9f77324bef124e2058e9
push id24219
push userryanvm@gmail.com
push dateThu, 24 Jan 2013 17:36:06 +0000
treeherdermozilla-central@fa969919b1bb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersneil
bugs831020
milestone21.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 831020 - Errors in the error console with very long reference lines hang the browser. r=neil
toolkit/components/console/content/consoleBindings.xml
--- a/toolkit/components/console/content/consoleBindings.xml
+++ b/toolkit/components/console/content/consoleBindings.xml
@@ -19,18 +19,18 @@
       </xul:vbox>
     </content>
   
     <implementation>
       <field name="limit" readonly="true">
         250
       </field>
 
-      <field name="hrefMaxLength" readonly="true">
-        <!-- Limit displayed script error source URL to avoid performance issues. (Bug 796179) -->
+      <field name="fieldMaxLength" readonly="true">
+        <!-- Limit displayed string lengths to avoid performance issues. (Bug 796179 and 831020) -->
         200
       </field>
 
       <field name="_showChromeErrors">-1</field>
 
       <property name="showChromeErrors">
         <getter><![CDATA[
           if (this._showChromeErrors != -1)
@@ -185,47 +185,82 @@
             } catch (ex2) {
               // Give up and append the object itself as a string
               this.appendMessage(aObject);
             }
           }
         ]]></body>
       </method>
 
+      <method name="_truncateIfNecessary">
+        <parameter name="aString"/>
+        <parameter name="aMiddleCharacter"/>
+        <body><![CDATA[
+          if (!aString || aString.length <= this.fieldMaxLength)
+            return {string: aString, column: aMiddleCharacter};
+          let halfLimit = this.fieldMaxLength / 2;
+          if (!aMiddleCharacter || aMiddleCharacter < 0 || aMiddleCharacter > aString.length)
+            aMiddleCharacter = halfLimit;
+
+          let startPosition = 0;
+          let endPosition = aString.length;
+          if (aMiddleCharacter - halfLimit >= 0)
+            startPosition = aMiddleCharacter - halfLimit;
+          if (aMiddleCharacter + halfLimit <= aString.length)
+            endPosition = aMiddleCharacter + halfLimit;
+          if (endPosition - startPosition < this.fieldMaxLength)
+            endPosition += this.fieldMaxLength - (endPosition - startPosition);
+          let truncatedString = aString.substring(startPosition, endPosition);
+          let Ci = Components.interfaces;
+          let ellipsis = Services.prefs.getComplexValue("intl.ellipsis",
+                                                        Ci.nsIPrefLocalizedString).data;
+          if (startPosition > 0) {
+            truncatedString = ellipsis + truncatedString;
+            aMiddleCharacter += ellipsis.length;
+          }
+          if (endPosition < aString.length)
+            truncatedString = truncatedString + ellipsis;
+
+          return {
+            string: truncatedString,
+            column: aMiddleCharacter - startPosition
+          };
+        ]]></body>
+      </method>
+
       <method name="appendError">
         <parameter name="aObject"/>
         <body><![CDATA[
           var row = this.createConsoleRow();
           var nsIScriptError = Components.interfaces.nsIScriptError;
           
           // Is this error actually just a non-fatal warning?
           var warning = aObject.flags & nsIScriptError.warningFlag != 0;
 
           var typetext = warning ? "typeWarning" : "typeError";
           row.setAttribute("typetext", this.mStrBundle.getString(typetext));
           row.setAttribute("type", warning ? "warning" : "error");
           row.setAttribute("msg", aObject.errorMessage);
           row.setAttribute("category", aObject.category);
           row.setAttribute("time", this.properFormatTime(aObject.timeStamp));
           if (aObject.lineNumber || aObject.sourceName) {
-            if (aObject.sourceName.length <= this.hrefMaxLength) {
-              row.setAttribute("href", aObject.sourceName);
-            } else {
-              row.setAttribute("href", aObject.sourceName.substring(0, this.hrefMaxLength) + '…');
-            }
+            row.setAttribute("href", this._truncateIfNecessary(aObject.sourceName).string);
             row.mSourceName = aObject.sourceName;
             row.setAttribute("line", aObject.lineNumber);
           } else {
             row.setAttribute("hideSource", "true");
           }
           if (aObject.sourceLine) {
-            row.setAttribute("code", aObject.sourceLine.replace(/\s/g, " "));
+            let sourceLine = aObject.sourceLine.replace(/\s/g, " ");
+            let truncatedLineObj = this._truncateIfNecessary(sourceLine, aObject.columnNumber);
+            row.setAttribute("code", truncatedLineObj.string);
+            row.mSourceLine = sourceLine;
             if (aObject.columnNumber) {
               row.setAttribute("col", aObject.columnNumber);
-              row.setAttribute("errorDots", this.repeatChar(" ", aObject.columnNumber));
+              row.setAttribute("errorDots", this.repeatChar(" ", truncatedLineObj.column));
               row.setAttribute("errorCaret", " ");
             } else {
               row.setAttribute("hideCaret", "true");
             }
           } else {
             row.setAttribute("hideCode", "true");
           }
 
@@ -316,21 +351,24 @@
           this.mConsoleRowBox = newRows;
           this.selectedItem = null;
         ]]></body>
       </method>
 
       <method name="filterElement">
         <parameter name="aRow" />
         <body><![CDATA[
-          let anyMatch = ["msg", "line", "code"].some(function (key) {
-            return (aRow.hasAttribute(key) &&
-                    this.stringMatchesFilters(aRow.getAttribute(key), this.mFilter));
-          }, this) || (aRow.mSourceName &&
-                       this.stringMatchesFilters(aRow.mSourceName, this.mFilter));
+          let anyMatch = (aRow.hasAttribute("msg") &&
+                          this.stringMatchesFilters(aRow.getAttribute("msg"), this.mFilter)) ||
+                         (aRow.hasAttribute("code") &&
+                          this.stringMatchesFilters(aRow.getAttribute("code"), this.mFilter)) ||
+                         (aRow.mSourceName &&
+                          this.stringMatchesFilters(aRow.mSourceName, this.mFilter)) ||
+                         (aRow.mSourceLine &&
+                          this.stringMatchesFilters(aRow.mSourceLine, this.mFilter));
 
           if (anyMatch) {
             aRow.classList.remove("filtered-by-string")
           } else {
             aRow.classList.add("filtered-by-string")
           }
         ]]></body>
       </method>
@@ -452,35 +490,36 @@
             </xul:box>
           </xul:vbox>
         </xul:vbox>
       </xul:box>
     </content>
 
     <implementation>
       <field name="mSourceName">null</field>
+      <field name="mSourceLine">null</field>
 
       <method name="toString">
         <body><![CDATA[
           let msg = "";
           let strBundle = this._ConsoleBox.mStrBundle;
 
           if (this.hasAttribute("time"))
             msg += strBundle.getFormattedString("errTime", [this.getAttribute("time")]) + "\n";
 
           msg += this.getAttribute("typetext") + " " + this.getAttribute("msg");
 
           if (this.hasAttribute("line") && this.mSourceName) {
             msg += "\n" + strBundle.getFormattedString("errFile",
                                         [this.mSourceName]) + "\n";
             if (this.hasAttribute("col")) {
               msg += strBundle.getFormattedString("errLineCol",
-                         [this.getAttribute("line"), this.getAttribute("col")]);
+                         [this.mSourceLine, this.getAttribute("col")]);
             } else
-              msg += strBundle.getFormattedString("errLine", [this.getAttribute("line")]);
+              msg += strBundle.getFormattedString("errLine", [this.mSourceLine]);
           }
 
           if (this.hasAttribute("code"))
             msg += "\n" + strBundle.getString("errCode") + "\n" + this.getAttribute("code");
 
           return msg;
         ]]></body>
       </method>