Bug 760951 - Add a filter box to the error console. r=jaws
authorTom Schuster <evilpies@gmail.com>
Thu, 07 Jun 2012 17:22:34 +0200
changeset 96081 5f99fe9d2182
parent 96080 3e12ea5cc535
child 96082 14d0960c626e
push id22875
push useremorley@mozilla.com
push date2012-06-08 10:26 +0000
treeherdermozilla-central@22bb7d46bb23 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws
bugs760951
milestone16.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 760951 - Add a filter box to the error console. r=jaws
toolkit/components/console/content/console.css
toolkit/components/console/content/console.js
toolkit/components/console/content/console.xul
toolkit/components/console/content/consoleBindings.xml
toolkit/locales/en-US/chrome/global/console.dtd
--- a/toolkit/components/console/content/console.css
+++ b/toolkit/components/console/content/console.css
@@ -55,16 +55,20 @@
 .console-box[mode="Errors"] > .console-box-internal > .console-rows 
     > .console-row[type="message"],
 .console-box[mode="Warnings"] > .console-box-internal > .console-rows 
     > .console-row[type="message"]
 {
   display: none;
 }
 
+.filtered-by-string {
+  display: none;
+}
+
 /* If line number is 0, hide the line number section */
 .lineNumberRow[line="0"] {
   display: none;
 }
 
 #TextboxEval {
   direction: ltr;
 }
--- a/toolkit/components/console/content/console.js
+++ b/toolkit/components/console/content/console.js
@@ -1,32 +1,41 @@
 # -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 var gConsole, gConsoleBundle, gTextBoxEval, gEvaluator, gCodeToEvaluate;
+var gFilter;
 
 /* :::::::: Console Initialization ::::::::::::::: */
 
 window.onload = function()
 {
   gConsole = document.getElementById("ConsoleBox");
   gConsoleBundle = document.getElementById("ConsoleBundle");
   gTextBoxEval = document.getElementById("TextboxEval")  
   gEvaluator = document.getElementById("Evaluator");
+  gFilter = document.getElementById("Filter");
   
   updateSortCommand(gConsole.sortOrder);
   updateModeCommand(gConsole.mode);
 
   gEvaluator.addEventListener("load", loadOrDisplayResult, true);
 }
 
 /* :::::::: Console UI Functions ::::::::::::::: */
 
+function changeFilter()
+{
+  gConsole.filter = gFilter.value;
+
+  document.persist("ConsoleBox", "filter")
+}
+
 function changeMode(aMode)
 {
   switch (aMode) {
     case "Errors":
     case "Warnings":
     case "Messages":
       gConsole.mode = aMode;
       break;
--- a/toolkit/components/console/content/console.xul
+++ b/toolkit/components/console/content/console.xul
@@ -72,16 +72,21 @@
                        oncommand="changeMode('Warnings');"/>
         <toolbarbutton type="radio" group="mode" id="Console:modeMessages"
                      label="&messages.label;" accesskey="&messages.accesskey;"
                      oncommand="changeMode('Messages');"/>
       </hbox>
       <toolbarseparator/>
       <toolbarbutton id="Console:clear" oncommand="clearConsole();"
                      label="&clear.label;" accesskey="&clear.accesskey;"/>
+
+      <vbox valign="middle" align="end" flex="1">
+        <textbox placeholder="&filter.label;" accesskey="&filter.accesskey;" type="search" id="Filter" 
+                 oncommand="changeFilter()" oninput="changeFilter()"/>
+      </vbox>
     </toolbar>
   
     <toolbar class="chromeclass-toolbar" id="ToolbarEval" align="center" nowindowdrag="true">
       <label value="&codeEval.label;" accesskey="&codeEval.accesskey;" control="TextboxEval"/>
       <textbox id="TextboxEval" class="toolbar" value="" onkeypress="onEvalKeyPress(event)" flex="1"/>
       <toolbarbutton id="ButtonEval" label="&evaluate.label;"
                      accesskey="&evaluate.accesskey;" oncommand="evaluateTypein()"/>
     </toolbar>
--- a/toolkit/components/console/content/consoleBindings.xml
+++ b/toolkit/components/console/content/consoleBindings.xml
@@ -51,17 +51,28 @@
           if (this.mode != val) {
             this.mMode = val || "All";
             this.setAttribute("mode", this.mMode);
             this.selectedItem = null;
           }
           return val;
         ]]></setter>
       </property>
-    
+
+      <property name="filter">
+        <setter><![CDATA[
+          val = val.toLowerCase();
+          if (this.mFilter != val) {
+            this.mFilter = val;
+            setTimeout(this.applyFilter.bind(this), 0);
+          }
+          return val;
+        ]]></setter>
+      </property>
+
       <property name="sortOrder">
         <getter>return this.getAttribute("sortOrder");</getter>
         <setter>this.setAttribute("sortOrder", val); return val;</setter>
       </property>
       <field name="mSelectedItem">null</field>
       <property name="selectedItem">
         <getter>return this.mSelectedItem</getter>
         <setter><![CDATA[
@@ -102,16 +113,17 @@
           } catch (ex) {
             appendItem(
               "Unable to display errors - couldn't get Console Service component. " +
               "(Missing @mozilla.org/consoleservice;1)");
             return;
           }          
                     
           this.mMode = this.getAttribute("mode") || "All";
+          this.mFilter = "";
 
           this.appendInitialItems();
           window.controllers.insertControllerAt(0, this._controller);
         ]]></body>
       </method>
 
       <method name="destroy">
         <body><![CDATA[
@@ -200,16 +212,17 @@
               row.setAttribute("errorDots", this.repeatChar(" ", aObject.columnNumber));
               row.setAttribute("errorCaret", " ");
             } else {
               row.setAttribute("hideCaret", "true");
             }
           } else {
             row.setAttribute("hideCode", "true");
           }
+
           this.appendConsoleRow(row);
         ]]></body>
       </method>
             
       <method name="appendMessage">
         <parameter name="aMessage"/>
         <parameter name="aType"/>
         <body><![CDATA[
@@ -262,16 +275,17 @@
           row._ConsoleBox = this;
           return row;
         ]]></body>
       </method>
             
       <method name="appendConsoleRow">
         <parameter name="aRow"/>
         <body><![CDATA[
+          this.filterElement(aRow);
           this.mConsoleRowBox.appendChild(aRow);
           if (++this.mCount > this.limit) this.deleteFirst();
         ]]></body>
       </method>
             
       <method name="deleteFirst">
         <body><![CDATA[
           var node = this.mConsoleRowBox.firstChild;
@@ -287,32 +301,68 @@
           this.mCount = 0;
           
           var newRows = this.mConsoleRowBox.cloneNode(false);
           this.mConsoleRowBox.parentNode.replaceChild(newRows, this.mConsoleRowBox);
           this.mConsoleRowBox = newRows;
           this.selectedItem = null;
         ]]></body>
       </method>
-      
+
+      <method name="filterElement">
+        <parameter name="aRow" />
+        <body><![CDATA[
+          if (this.stringMatchesFilters(aRow.getAttribute("msg"), this.mFilter)) {
+            aRow.classList.remove("filtered-by-string")
+          } else {
+            aRow.classList.add("filtered-by-string")
+          }
+        ]]></body>
+      </method>
+
+      <method name="applyFilter">
+        <parameter name="aFilter" />
+        <body><![CDATA[
+          for (let aRow of this.mConsoleRowBox.children) {
+            this.filterElement(aRow);
+          }
+        ]]></body>
+      </method>
+
       <!-- UTILITY FUNCTIONS -->
       
       <method name="repeatChar">
         <parameter name="aChar"/>
         <parameter name="aCol"/>
         <body><![CDATA[
           if (--aCol <= 0)
             return "";
 
           for (var i = 2; i < aCol; i += i)
             aChar += aChar;
 
           return aChar + aChar.slice(0, aCol - aChar.length);
         ]]></body>
       </method>
+
+      <method name="stringMatchesFilters">
+        <parameter name="aString"/>
+        <parameter name="aFilter"/>
+        <body><![CDATA[
+          if (!aString || !aFilter) {
+            return true;
+          }
+
+          let searchStr = aString.toLowerCase();
+          let filterStrings = aFilter.split(/\s+/);
+          return !filterStrings.some(function (f) {
+            return searchStr.indexOf(f) == -1;
+          });
+        ]]></body>
+      </method>
           
       <constructor> this.init(); </constructor>
       <destructor> this.destroy(); </destructor>
 
       <!-- Command controller for the copy command -->
       <field name="_controller"><![CDATA[({
         _outer: this,
 
--- a/toolkit/locales/en-US/chrome/global/console.dtd
+++ b/toolkit/locales/en-US/chrome/global/console.dtd
@@ -17,16 +17,18 @@
 <!ENTITY messages.label     "Messages">
 <!ENTITY messages.accesskey "M">
 <!ENTITY clear.label        "Clear">
 <!ENTITY clear.accesskey    "C">
 <!ENTITY codeEval.label     "Code:">
 <!ENTITY codeEval.accesskey "o">
 <!ENTITY evaluate.label     "Evaluate">
 <!ENTITY evaluate.accesskey "v">
+<!ENTITY filter.label       "Filter">
+<!ENTITY filter.accesskey   "F">
 
 <!ENTITY copyCmd.label       "Copy">  
 <!ENTITY copyCmd.accesskey   "C"> 
 <!ENTITY copyCmd.commandkey  "C"> 
 <!ENTITY sortFirst.label     "First > Last Sort Order">
 <!ENTITY sortFirst.accesskey "f">
 <!ENTITY sortLast.label      "Last > First Sort Order">
 <!ENTITY sortLast.accesskey  "l">