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
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">