Bug 1158506 - [rule view] Filter styles should handle escape keycode correctly r=bgrins
authorGabriel Luong <gabriel.luong@gmail.com>
Mon, 04 May 2015 14:11:07 -0700
changeset 242268 d0e7f60dafaf059b4975d486f5438f0bddac4753
parent 242267 77f5a6d23ffc9684b5c59ed66b02649588c02698
child 242269 2cf04909a23b96eeffdef7505b6a8d04ff2157bf
push id12722
push userkwierso@gmail.com
push dateMon, 04 May 2015 21:11:22 +0000
treeherderfx-team@2cf04909a23b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbgrins
bugs1158506
milestone40.0a1
Bug 1158506 - [rule view] Filter styles should handle escape keycode correctly r=bgrins
browser/devtools/styleinspector/computed-view.js
browser/devtools/styleinspector/rule-view.js
browser/devtools/styleinspector/test/browser.ini
browser/devtools/styleinspector/test/browser_computedview_search-filter_escape-keypress.js
browser/devtools/styleinspector/test/browser_ruleview_search-filter_escape-keypress.js
--- a/browser/devtools/styleinspector/computed-view.js
+++ b/browser/devtools/styleinspector/computed-view.js
@@ -147,32 +147,34 @@ function CssHtmlTree(aStyleInspector, aP
   this.focusWindow = this.focusWindow.bind(this);
   this._onContextMenu = this._onContextMenu.bind(this);
   this._contextMenuUpdate = this._contextMenuUpdate.bind(this);
   this._onSelectAll = this._onSelectAll.bind(this);
   this._onClick = this._onClick.bind(this);
   this._onCopy = this._onCopy.bind(this);
   this._onCopyColor = this._onCopyColor.bind(this);
   this._onFilterStyles = this._onFilterStyles.bind(this);
+  this._onFilterKeyPress = this._onFilterKeyPress.bind(this);
   this._onClearSearch = this._onClearSearch.bind(this);
   this._onIncludeBrowserStyles = this._onIncludeBrowserStyles.bind(this);
   this._onFilterTextboxContextMenu = this._onFilterTextboxContextMenu.bind(this);
 
   let doc = this.styleDocument;
   this.root = doc.getElementById("root");
   this.element = doc.getElementById("propertyContainer");
   this.searchField = doc.getElementById("computedview-searchbox");
   this.searchClearButton = doc.getElementById("computedview-searchinput-clear");
   this.includeBrowserStylesCheckbox = doc.getElementById("browser-style-checkbox");
 
   this.styleDocument.addEventListener("mousedown", this.focusWindow);
   this.element.addEventListener("click", this._onClick);
   this.element.addEventListener("copy", this._onCopy);
   this.element.addEventListener("contextmenu", this._onContextMenu);
   this.searchField.addEventListener("input", this._onFilterStyles);
+  this.searchField.addEventListener("keypress", this._onFilterKeyPress);
   this.searchField.addEventListener("contextmenu", this._onFilterTextboxContextMenu);
   this.searchClearButton.addEventListener("click", this._onClearSearch);
   this.includeBrowserStylesCheckbox.addEventListener("command",
     this._onIncludeBrowserStyles);
 
   this.searchClearButton.hidden = true;
 
   // No results text.
@@ -540,16 +542,56 @@ CssHtmlTree.prototype = {
       }
 
       this.refreshPanel();
       this._filterChangeTimeout = null;
     }, filterTimeout);
   },
 
   /**
+   * Handle the search box's keypress event. If the escape key is pressed,
+   * clear the search box field.
+   */
+  _onFilterKeyPress: function(aEvent) {
+    if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE &&
+        this._onClearSearch()) {
+      aEvent.preventDefault();
+      aEvent.stopPropagation();
+    }
+  },
+
+  /**
+   * Context menu handler for filter style search box.
+   */
+  _onFilterTextboxContextMenu: function(event) {
+    try {
+      this.styleDocument.defaultView.focus();
+      let contextmenu = this.inspector.toolbox.textboxContextMenuPopup;
+      contextmenu.openPopupAtScreen(event.screenX, event.screenY, true);
+    } catch(e) {
+      console.error(e);
+    }
+  },
+
+  /**
+   * Called when the user clicks on the clear button in the filter style search
+   * box. Returns true if the search box is cleared and false otherwise.
+   */
+  _onClearSearch: function() {
+    if (this.searchField.value) {
+      this.searchField.value = "";
+      this.searchField.focus();
+      this._onFilterStyles();
+      return true;
+    }
+
+    return false;
+  },
+
+  /**
    * The change event handler for the includeBrowserStyles checkbox.
    *
    * @param {Event} aEvent the DOM Event object.
    */
   _onIncludeBrowserStyles: function(aEvent)
   {
     this.refreshSourceFilter();
     this.refreshPanel();
@@ -826,39 +868,16 @@ CssHtmlTree.prototype = {
    *  Toggle the original sources pref.
    */
   _onToggleOrigSources: function()
   {
     let isEnabled = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
     Services.prefs.setBoolPref(PREF_ORIG_SOURCES, !isEnabled);
   },
 
-   /**
-   * Context menu handler for filter style search box.
-   */
-  _onFilterTextboxContextMenu: function(event) {
-    try {
-      this.styleDocument.defaultView.focus();
-      let contextmenu = this.inspector.toolbox.textboxContextMenuPopup;
-      contextmenu.openPopupAtScreen(event.screenX, event.screenY, true);
-    } catch(e) {
-      console.error(e);
-    }
-  },
-
-  /**
-   * Called when the user clicks on the clear button in the filter style search
-   * box.
-   */
-  _onClearSearch: function() {
-    this.searchField.value = "";
-    this.searchField.focus();
-    this._onFilterStyles();
-  },
-
   /**
    * Destructor for CssHtmlTree.
    */
   destroy: function CssHtmlTree_destroy()
   {
     this.viewedElement = null;
     this._outputParser = null;
 
@@ -901,16 +920,17 @@ CssHtmlTree.prototype = {
     this.highlighters.destroy();
 
     // Remove bound listeners
     this.styleDocument.removeEventListener("mousedown", this.focusWindow);
     this.element.removeEventListener("click", this._onClick);
     this.element.removeEventListener("copy", this._onCopy);
     this.element.removeEventListener("contextmenu", this._onContextMenu);
     this.searchField.removeEventListener("input", this._onFilterStyles);
+    this.searchField.removeEventListener("keypress", this._onFilterKeyPress);
     this.searchField.removeEventListener("contextmenu", this._onFilterTextboxContextMenu);
     this.searchClearButton.removeEventListener("click", this._onClearSearch);
     this.includeBrowserStylesCheckbox.removeEventListener("command",
       this.includeBrowserStylesChanged);
 
     // Nodes used in templating
     this.root = null;
     this.element = null;
--- a/browser/devtools/styleinspector/rule-view.js
+++ b/browser/devtools/styleinspector/rule-view.js
@@ -1124,28 +1124,30 @@ function CssRuleView(aInspector, aDoc, a
   this._contextMenuUpdate = this._contextMenuUpdate.bind(this);
   this._onAddRule = this._onAddRule.bind(this);
   this._onSelectAll = this._onSelectAll.bind(this);
   this._onCopy = this._onCopy.bind(this);
   this._onCopyColor = this._onCopyColor.bind(this);
   this._onToggleOrigSources = this._onToggleOrigSources.bind(this);
   this._onShowMdnDocs = this._onShowMdnDocs.bind(this);
   this._onFilterStyles = this._onFilterStyles.bind(this);
+  this._onFilterKeyPress = this._onFilterKeyPress.bind(this);
   this._onClearSearch = this._onClearSearch.bind(this);
   this._onFilterTextboxContextMenu = this._onFilterTextboxContextMenu.bind(this);
 
   this.element = this.doc.getElementById("ruleview-container");
   this.searchField = this.doc.getElementById("ruleview-searchbox");
   this.searchClearButton = this.doc.getElementById("ruleview-searchinput-clear");
 
   this.searchClearButton.hidden = true;
 
   this.element.addEventListener("copy", this._onCopy);
   this.element.addEventListener("contextmenu", this._onContextMenu);
   this.searchField.addEventListener("input", this._onFilterStyles);
+  this.searchField.addEventListener("keypress", this._onFilterKeyPress);
   this.searchField.addEventListener("contextmenu", this._onFilterTextboxContextMenu);
   this.searchClearButton.addEventListener("click", this._onClearSearch);
 
   this._handlePrefChange = this._handlePrefChange.bind(this);
   this._onSourcePrefChanged = this._onSourcePrefChanged.bind(this);
 
   this._prefObserver = new PrefObserver("devtools.");
   this._prefObserver.on(PREF_ORIG_SOURCES, this._onSourcePrefChanged);
@@ -1643,36 +1645,53 @@ CssRuleView.prototype = {
 
       this.inspector.emit("ruleview-filtered");
 
       this._filterChangeTimeout = null;
     }, filterTimeout);
   },
 
   /**
+   * Handle the search box's keypress event. If the escape key is pressed,
+   * clear the search box field.
+   */
+  _onFilterKeyPress: function(event) {
+    if (event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE &&
+        this._onClearSearch()) {
+      event.preventDefault();
+      event.stopPropagation();
+    }
+  },
+
+  /**
    * Context menu handler for filter style search box.
    */
   _onFilterTextboxContextMenu: function(event) {
     try {
       this.doc.defaultView.focus();
       let contextmenu = this.inspector.toolbox.textboxContextMenuPopup;
       contextmenu.openPopupAtScreen(event.screenX, event.screenY, true);
     } catch(e) {
       console.error(e);
     }
   },
 
   /**
    * Called when the user clicks on the clear button in the filter style search
-   * box.
+   * box. Returns true if the search box is cleared and false otherwise.
    */
   _onClearSearch: function() {
-    this.searchField.value = "";
-    this.searchField.focus();
-    this._onFilterStyles();
+    if (this.searchField.value) {
+      this.searchField.value = "";
+      this.searchField.focus();
+      this._onFilterStyles();
+      return true;
+    }
+
+    return false;
   },
 
   destroy: function() {
     this.isDestroyed = true;
     this.clear();
 
     gDummyPromise = null;
 
@@ -1716,16 +1735,17 @@ CssRuleView.prototype = {
 
     this.tooltips.destroy();
     this.highlighters.destroy();
 
     // Remove bound listeners
     this.element.removeEventListener("copy", this._onCopy);
     this.element.removeEventListener("contextmenu", this._onContextMenu);
     this.searchField.removeEventListener("input", this._onFilterStyles);
+    this.searchField.removeEventListener("keypress", this._onFilterKeyPress);
     this.searchField.removeEventListener("contextmenu",
       this._onFilterTextboxContextMenu);
     this.searchClearButton.removeEventListener("click", this._onClearSearch);
     this.searchField = null;
     this.searchClearButton = null;
 
     if (this.element.parentNode) {
       this.element.parentNode.removeChild(this.element);
--- a/browser/devtools/styleinspector/test/browser.ini
+++ b/browser/devtools/styleinspector/test/browser.ini
@@ -37,16 +37,17 @@ support-files =
 [browser_computedview_media-queries.js]
 [browser_computedview_no-results-placeholder.js]
 [browser_computedview_original-source-link.js]
 [browser_computedview_pseudo-element_01.js]
 [browser_computedview_refresh-on-style-change_01.js]
 [browser_computedview_search-filter.js]
 [browser_computedview_search-filter_clear.js]
 [browser_computedview_search-filter_context-menu.js]
+[browser_computedview_search-filter_escape-keypress.js]
 [browser_computedview_select-and-copy-styles.js]
 [browser_computedview_style-editor-link.js]
 [browser_ruleview_add-property-and-reselect.js]
 [browser_ruleview_add-property-cancel_01.js]
 [browser_ruleview_add-property-cancel_02.js]
 [browser_ruleview_add-property-cancel_03.js]
 [browser_ruleview_add-property_01.js]
 [browser_ruleview_add-property_02.js]
@@ -118,16 +119,17 @@ skip-if = e10s # Bug 1090340
 [browser_ruleview_search-filter_05.js]
 [browser_ruleview_search-filter_06.js]
 [browser_ruleview_search-filter_07.js]
 [browser_ruleview_search-filter_08.js]
 [browser_ruleview_search-filter_09.js]
 [browser_ruleview_search-filter_10.js]
 [browser_ruleview_search-filter_clear.js]
 [browser_ruleview_search-filter_context-menu.js]
+[browser_ruleview_search-filter_escape-keypress.js]
 [browser_ruleview_select-and-copy-styles.js]
 [browser_ruleview_selector-highlighter_01.js]
 [browser_ruleview_selector-highlighter_02.js]
 [browser_ruleview_selector-highlighter_03.js]
 [browser_ruleview_style-editor-link.js]
 skip-if = e10s # bug 1040670 Cannot open inline styles in viewSourceUtils
 [browser_ruleview_urls-clickable.js]
 [browser_ruleview_user-agent-styles.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_computedview_search-filter_escape-keypress.js
@@ -0,0 +1,71 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that search filter escape keypress will clear the search field.
+
+let TEST_URI = [
+  '<style type="text/css">',
+  '  .matches {',
+  '    color: #F00;',
+  '  }',
+  '</style>',
+  '<span id="matches" class="matches">Some styled text</span>'
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {toolbox, inspector, view} = yield openComputedView();
+  yield selectNode("#matches", inspector);
+  yield testAddTextInFilter(inspector, view);
+  yield testEscapeKeypress(inspector, view);
+});
+
+function* testAddTextInFilter(inspector, computedView) {
+  info("Setting filter text to \"background-color\"");
+
+  let win = computedView.styleWindow;
+  let propertyViews = computedView.propertyViews;
+  let searchField = computedView.searchField;
+  let checkbox = computedView.includeBrowserStylesCheckbox;
+
+  info("Include browser styles");
+  checkbox.click();
+  yield inspector.once("computed-view-refreshed");
+
+  searchField.focus();
+  synthesizeKeys("background-color", win);
+  yield inspector.once("computed-view-refreshed");
+
+  info("Check that the correct properties are visible");
+
+  propertyViews.forEach((propView) => {
+    let name = propView.name;
+    is(propView.visible, name.indexOf("background-color") > -1,
+      "span " + name + " property visibility check");
+  });
+}
+
+function* testEscapeKeypress(inspector, computedView) {
+  info("Pressing the escape key on search filter");
+
+  let win = computedView.styleWindow;
+  let propertyViews = computedView.propertyViews;
+  let searchField = computedView.searchField;
+  let onRefreshed = inspector.once("computed-view-refreshed");
+
+  searchField.focus();
+  EventUtils.synthesizeKey("VK_ESCAPE", {}, win);
+  yield onRefreshed;
+
+  info("Check that the correct properties are visible");
+
+  ok(!searchField.value, "Search filter is cleared");
+  propertyViews.forEach((propView) => {
+    let name = propView.name;
+    is(propView.visible, true,
+      "span " + name + " property is visible");
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_search-filter_escape-keypress.js
@@ -0,0 +1,66 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the rule view search filter escape keypress will clear the search
+// field.
+
+let TEST_URI = [
+  '<style type="text/css">',
+  '  #testid {',
+  '    background-color: #00F;',
+  '  }',
+  '  .testclass {',
+  '    width: 100%;',
+  '  }',
+  '</style>',
+  '<div id="testid" class="testclass">Styled Node</div>'
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {toolbox, inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testAddTextInFilter(inspector, view);
+  yield testEscapeKeypress(inspector, view);
+});
+
+function* testAddTextInFilter(inspector, ruleView) {
+  info("Setting filter text to \"00F\"");
+
+  let win = ruleView.doc.defaultView;
+  let searchField = ruleView.searchField;
+  let onRuleViewFiltered = inspector.once("ruleview-filtered");
+
+  searchField.focus();
+  synthesizeKeys("00F", win);
+  yield onRuleViewFiltered;
+
+  info("Check that the correct rules are visible");
+  is(ruleView.element.children.length, 2, "Should have 2 rules.");
+  is(getRuleViewRuleEditor(ruleView, 0).rule.selectorText, "element", "First rule is inline element.");
+  is(getRuleViewRuleEditor(ruleView, 1).rule.selectorText, "#testid", "Second rule is #testid.");
+  ok(getRuleViewRuleEditor(ruleView, 1).rule.textProps[0].editor.element.classList.contains("ruleview-highlight"),
+    "background-color text property is correctly highlighted.");
+}
+
+function* testEscapeKeypress(inspector, ruleView) {
+  info("Pressing the escape key on search filter");
+
+  let doc = ruleView.doc;
+  let win = ruleView.doc.defaultView;
+  let searchField = ruleView.searchField;
+  let onRuleViewFiltered = inspector.once("ruleview-filtered");
+
+  searchField.focus();
+  EventUtils.synthesizeKey("VK_ESCAPE", {}, win);
+  yield onRuleViewFiltered;
+
+  info("Check the search filter is cleared and no rules are highlighted");
+  is(ruleView.element.children.length, 3, "Should have 3 rules.");
+  ok(!searchField.value, "Search filter is cleared");
+  ok(!doc.querySelectorAll(".ruleview-highlight").length &&
+     !ruleView._highlightedElements.length, "No rules are higlighted");
+}