Bug 597399 - Panorama: Typing in a digit when the search box isn't displayed produces letter in the box [r=ian, a=blocking2.0]
authorRaymond Lee <raymond@raysquare.com>
Wed, 17 Nov 2010 18:49:59 +0800
changeset 57907 cf9ba871de20914dc7334738dfec1d98906580b8
parent 57906 c5e14ccf7622c316292839b976ff98ff4eb01520
child 57908 84e356d05e982a8bc88c14f031b8d69873fb40a5
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersian, blocking2
bugs597399
milestone2.0b8pre
Bug 597399 - Panorama: Typing in a digit when the search box isn't displayed produces letter in the box [r=ian, a=blocking2.0]
browser/base/content/tabview/iq.js
browser/base/content/tabview/search.js
browser/base/content/tabview/ui.js
browser/base/content/test/tabview/Makefile.in
browser/base/content/test/tabview/browser_tabview_bug595191.js
browser/base/content/test/tabview/browser_tabview_bug597399.js
--- a/browser/base/content/tabview/iq.js
+++ b/browser/base/content/tabview/iq.js
@@ -694,16 +694,17 @@ iQClass.prototype = {
   }
 };
 
 // ----------
 // Create various event aliases
 let events = [
   'keyup',
   'keydown',
+  'keypress',
   'mouseup',
   'mousedown',
   'mouseover',
   'mouseout',
   'mousemove',
   'click',
   'resize',
   'change',
--- a/browser/base/content/tabview/search.js
+++ b/browser/base/content/tabview/search.js
@@ -330,74 +330,75 @@ SearchEventHandlerClass.prototype = {
     this.switchToBeforeMode();
   },
   
   // ----------
   // Function: beforeSearchKeyHandler
   // Handles all keypresses before the search interface is brought up.
   beforeSearchKeyHandler: function (event) {
     // Only match reasonable text-like characters for quick search.
-    var key = String.fromCharCode(event.which);
     // TODO: Also include funky chars. Bug 593904
-    if (!key.match(/[A-Z0-9]/) || event.altKey || event.ctrlKey || event.metaKey)
+    if (!String.fromCharCode(event.which).match(/[a-zA-Z0-9]/) || event.altKey || 
+        event.ctrlKey || event.metaKey)
       return;
 
     // If we are already in an input field, allow typing as normal.
     if (event.target.nodeName == "INPUT")
       return;
 
     this.switchToInMode();
     ensureSearchShown(event);
   },
 
   // ----------
   // Function: inSearchKeyHandler
   // Handles all keypresses while search mode.
   inSearchKeyHandler: function (event) {
     var term = iQ("#searchbox").val();
-    
-    if (event.which == event.DOM_VK_ESCAPE) 
+
+    if ((event.keyCode == event.DOM_VK_ESCAPE) || 
+        (event.keyCode == event.DOM_VK_BACK_SPACE && term.length <= 1)) {
       hideSearch(event);
-    if (event.which == event.DOM_VK_BACK_SPACE && term.length <= 1) 
-      hideSearch(event);
+      return;
+    }
 
     var matcher = new TabMatcher(term);
     var matches = matcher.matched();
     var others =  matcher.matchedTabsFromOtherWindows();
-    if (event.which == event.DOM_VK_RETURN && (matches.length > 0 || others.length > 0)) {
+    if (event.keyCode == event.DOM_VK_RETURN && (matches.length > 0 || others.length > 0)) {
       hideSearch(event);
       if (matches.length > 0) 
         matches[0].zoomIn();
       else
         TabUtils.focus(others[0]);
     }
   },
 
   // ----------
   // Function: switchToBeforeMode
   // Make sure the event handlers are appropriate for
   // the before-search mode. 
   switchToBeforeMode: function switchToBeforeMode() {
     var self = this;
     if (this.currentHandler)
-      iQ(document).unbind("keydown", this.currentHandler);
+      iQ(window).unbind("keypress", this.currentHandler);
     this.currentHandler = function(event) self.beforeSearchKeyHandler(event);
-    iQ(document).keydown(self.currentHandler);
+    iQ(window).keypress(this.currentHandler);
   },
   
   // ----------
   // Function: switchToInMode
   // Make sure the event handlers are appropriate for
   // the in-search mode.   
   switchToInMode: function switchToInMode() {
     var self = this;
     if (this.currentHandler)
-      iQ(document).unbind("keydown", this.currentHandler);
+      iQ(window).unbind("keypress", this.currentHandler);
     this.currentHandler = function(event) self.inSearchKeyHandler(event);
-    iQ(document).keydown(self.currentHandler);
+    iQ(window).keypress(this.currentHandler);
   }
 };
 
 var TabHandlers = {
   onMatch: function(tab, index){
     tab.addClass("onTop");
     index != 0 ? tab.addClass("notMainMatch") : tab.removeClass("notMainMatch");
 
@@ -483,23 +484,23 @@ function hideSearch(event){
   performSearch();
   SearchEventHandler.switchToBeforeMode();
 
   if (event){
     event.preventDefault();
     event.stopPropagation();
   }
 
+  // Return focus to the tab window
+  UI.blurAll();
+  gTabViewFrame.contentWindow.focus();
+
   let newEvent = document.createEvent("Events");
   newEvent.initEvent("tabviewsearchdisabled", false, false);
   dispatchEvent(newEvent);
-
-  // Return focus to the tab window
-  UI.blurAll();
-  gTabViewFrame.contentWindow.focus();
 }
 
 function performSearch() {
   var matcher = new TabMatcher(iQ("#searchbox").val());
 
   // Remove any previous other-window search results and
   // hide the display area.
   iQ("#results").empty();
@@ -508,42 +509,44 @@ function performSearch() {
 
   matcher.doSearch(TabHandlers.onMatch, TabHandlers.onUnmatch, TabHandlers.onOther);
 }
 
 function ensureSearchShown(event){
   var $search = iQ("#search");
   var $searchbox = iQ("#searchbox");
   iQ("#searchbutton").css({ opacity: 1 });
-  
-  
-  if ($search.css("display") == "none") {
+
+
+  if (!isSearchEnabled()) {
     $search.show();
     var mainWindow = gWindow.document.getElementById("main-window");
     mainWindow.setAttribute("activetitlebarcolor", "#717171");       
-        
+
     // Marshal the focusing, otherwise you end up with
     // a race condition where only sometimes would the
     // first keystroke be registered by the search box.
     // When you marshal it never gets registered, so we
     // manually 
     setTimeout(function focusSearch() {
       $searchbox[0].focus();
       $searchbox[0].val = '0';
       $searchbox.css({"z-index":"1015"});
-      if (event != null){
-        var keyCode = event.which + (event.shiftKey ? 0 : 32);
-        $searchbox.val(String.fromCharCode(keyCode));        
-      }
+      if (event != null)
+        $searchbox.val(String.fromCharCode(event.charCode));        
+
+      let newEvent = document.createEvent("Events");
+      newEvent.initEvent("tabviewsearchenabled", false, false);
+      dispatchEvent(newEvent);
     }, 0);
+  }
+}
 
-    let newEvent = document.createEvent("Events");
-    newEvent.initEvent("tabviewsearchenabled", false, false);
-    dispatchEvent(newEvent);
-  }
+function isSearchEnabled() {
+  return iQ("#search").css("display") != "none";
 }
 
 var SearchEventHandler = new SearchEventHandlerClass();
 
 // Features to add:
 // (1) Make sure this looks good on Windows. Bug 594429
 // (2) Make sure that we don't put the matched tab over the search box. Bug 594433
 // (3) Group all of the highlighted tabs into a group? Bug 594434
--- a/browser/base/content/tabview/ui.js
+++ b/browser/base/content/tabview/ui.js
@@ -838,21 +838,26 @@ let UI = {
 
   // ----------
   // Function: _setTabViewFrameKeyHandlers
   // Sets up the key handlers for navigating between tabs within the TabView UI.
   _setTabViewFrameKeyHandlers: function UI__setTabViewFrameKeyHandlers() {
     var self = this;
 
     iQ(window).keyup(function(event) {
-      if (!event.metaKey) Keys.meta = false;
+      if (!event.metaKey) 
+        Keys.meta = false;
     });
 
     iQ(window).keydown(function(event) {
-      if (event.metaKey) Keys.meta = true;
+      if (event.metaKey) 
+        Keys.meta = true;
+
+      if (isSearchEnabled())
+        return;
 
       function getClosestTabBy(norm) {
         if (!self.getActiveTab())
           return null;
         var centers =
           [[item.bounds.center(), item]
              for each(item in TabItems.getItems()) if (!item.parent || !item.parent.hidden)];
         var myCenter = self.getActiveTab().bounds.center();
--- a/browser/base/content/test/tabview/Makefile.in
+++ b/browser/base/content/test/tabview/Makefile.in
@@ -55,16 +55,17 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_bug591706.js \
                  browser_tabview_bug594176.js \
                  browser_tabview_bug595191.js \
                  browser_tabview_bug595518.js \
                  browser_tabview_bug595521.js \
                  browser_tabview_bug595804.js \
                  browser_tabview_bug595930.js \
                  browser_tabview_bug595943.js \
+                 browser_tabview_bug597399.js \
                  browser_tabview_bug598600.js \
                  browser_tabview_bug599626.js \
                  browser_tabview_dragdrop.js \
                  browser_tabview_exit_button.js \
                  browser_tabview_group.js \
                  browser_tabview_launch.js \
                  browser_tabview_orphaned_tabs.js \
                  browser_tabview_privatebrowsing.js \
--- a/browser/base/content/test/tabview/browser_tabview_bug595191.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug595191.js
@@ -50,56 +50,46 @@ function onTabViewWindowLoaded() {
   ok(TabView.isVisible(), "Tab View is visible");
 
   let contentWindow = document.getElementById("tab-view").contentWindow;
   let searchButton = contentWindow.document.getElementById("searchbutton");
 
   ok(searchButton, "Search button exists");
   
   let onSearchEnabled = function() {
+    contentWindow.removeEventListener(
+      "tabviewsearchenabled", onSearchEnabled, false);
     let search = contentWindow.document.getElementById("search");
     ok(search.style.display != "none", "Search is enabled");
-    contentWindow.removeEventListener(
-      "tabviewsearchenabled", onSearchEnabled, false);
     escapeTest(contentWindow);
   }
   contentWindow.addEventListener("tabviewsearchenabled", onSearchEnabled, 
     false);
   // enter search mode
   EventUtils.sendMouseEvent({ type: "mousedown" }, searchButton, 
     contentWindow);
 }
 
 function escapeTest(contentWindow) {  
   let onSearchDisabled = function() {
-    let search = contentWindow.document.getElementById("search");
-
-    ok(search.style.display == "none", "Search is disabled");
-
     contentWindow.removeEventListener(
       "tabviewsearchdisabled", onSearchDisabled, false);
+
+    let search = contentWindow.document.getElementById("search");
+    ok(search.style.display == "none", "Search is disabled");
     toggleTabViewTest(contentWindow);
   }
   contentWindow.addEventListener("tabviewsearchdisabled", onSearchDisabled, 
     false);
-  // the search box focus()es in a function on the timeout queue, so we just
-  // want to queue behind it.
-  setTimeout( function() {
-    EventUtils.synthesizeKey("VK_ESCAPE", {});
-  }, 0);
+  EventUtils.synthesizeKey("VK_ESCAPE", { type: "keypress" }, contentWindow);
 }
 
 function toggleTabViewTest(contentWindow) {
   let onTabViewHidden = function() {
     contentWindow.removeEventListener("tabviewhidden", onTabViewHidden, false);
 
     ok(!TabView.isVisible(), "Tab View is hidden");
-
     finish();
   }
   contentWindow.addEventListener("tabviewhidden", onTabViewHidden, false);
-  // When search is hidden, it focus()es on the background, so avert the 
-  // race condition by delaying ourselves on the timeout queue
-  setTimeout( function() {
-    // Use keyboard shortcut to toggle back to browser view
-    EventUtils.synthesizeKey("e", { accelKey: true });
-  }, 0);
+  // Use keyboard shortcut to toggle back to browser view
+  EventUtils.synthesizeKey("e", { accelKey: true });
 }
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabview/browser_tabview_bug597399.js
@@ -0,0 +1,82 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is a test for bug 597399.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Raymond Lee <raymond@appcoast.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+function test() {
+  waitForExplicitFinish();
+
+  window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
+  TabView.toggle();
+}
+
+function onTabViewWindowLoaded() {
+  window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
+
+  let contentWindow = document.getElementById("tab-view").contentWindow;
+  let number = -1;
+
+  let onSearchEnabled = function() {
+    let searchBox = contentWindow.document.getElementById("searchbox");
+    is(searchBox.value, number, "The seach box matches the number: " + number);
+    contentWindow.hideSearch(null);
+  }
+  let onSearchDisabled = function() {
+    if (++number <= 9) {
+      EventUtils.synthesizeKey(String(number), { }, contentWindow);
+    } else {
+      contentWindow.removeEventListener(
+        "tabviewsearchenabled", onSearchEnabled, false);
+      contentWindow.removeEventListener(
+        "tabviewsearchdisabled", onSearchDisabled, false);
+
+      let endGame = function() {
+        window.removeEventListener("tabviewhidden", endGame, false);
+
+        ok(!TabView.isVisible(), "Tab View is hidden");
+        finish();
+      }
+      window.addEventListener("tabviewhidden", endGame, false);
+      TabView.toggle();
+    }
+  }
+  contentWindow.addEventListener(
+    "tabviewsearchenabled", onSearchEnabled, false);
+  contentWindow.addEventListener(
+    "tabviewsearchdisabled", onSearchDisabled, false);
+
+  onSearchDisabled();
+}
+