Bug 587276 - KeyEvent in TabCandy window affect browser [r=ian]
authorRaymond Lee <raymond@raysquare.com>
Tue, 29 Mar 2011 10:51:26 +0800
changeset 64513 565c588e3e5190ef2dd2680e1fc23e2f2f07d2de
parent 64512 86aa491210310000597b75ebeaa572769e55479b
child 64514 c5bd92bec6d6ed9480b0771e3f4ab0ce5cdd6a18
push idunknown
push userunknown
push dateunknown
reviewersian
bugs587276
milestone2.2a1pre
Bug 587276 - KeyEvent in TabCandy window affect browser [r=ian]
browser/base/content/browser-sets.inc
browser/base/content/browser-tabview.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_bug587276.js
browser/base/content/test/tabview/browser_tabview_bug595191.js
browser/base/content/test/tabview/browser_tabview_bug595518.js
browser/base/content/test/tabview/browser_tabview_bug595560.js
browser/base/content/test/tabview/browser_tabview_bug595930.js
browser/base/content/test/tabview/browser_tabview_bug597980.js
browser/base/content/test/tabview/browser_tabview_launch.js
browser/base/content/test/tabview/head.js
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -75,17 +75,17 @@
 
 
     <commandset id="editMenuCommands"/>
 
     <command id="View:PageSource" oncommand="BrowserViewSourceOfDocument(content.document);" observes="isImage"/>
     <command id="View:PageInfo" oncommand="BrowserPageInfo();"/>
     <command id="View:FullScreen" oncommand="BrowserFullScreen();"/>
     <command id="cmd_find"
-             oncommand="if (TabView.isVisible()) TabView.enableSearch(event); else gFindBar.onFindCommand();"
+             oncommand="gFindBar.onFindCommand();"
              observes="isImage"/>
     <command id="cmd_findAgain"
              oncommand="gFindBar.onFindAgainCommand(false);"
              observes="isImage"/>
     <command id="cmd_findPrevious"
              oncommand="gFindBar.onFindAgainCommand(true);"
              observes="isImage"/>
     <!-- work-around bug 392512 -->
--- a/browser/base/content/browser-tabview.js
+++ b/browser/base/content/browser-tabview.js
@@ -258,22 +258,16 @@ let TabView = {
       let self = this;
       this._initFrame(function() {
         self._window.GroupItems.moveTabToGroupItem(tab, groupItemId);
       });
     }
   },
 
   // ----------
-  enableSearch: function TabView_enableSearch(event) {
-    if (this._window)
-      this._window.UI.enableSearch(event);
-  },
-
-  // ----------
   // Adds new key commands to the browser, for invoking the Tab Candy UI
   // and for switching between groups of tabs when outside of the Tab Candy UI.
   _setBrowserKeyHandlers: function TabView__setBrowserKeyHandlers() {
     if (this._browserKeyHandlerInitialized)
       return;
 
     this._browserKeyHandlerInitialized = true;
 
--- a/browser/base/content/tabview/search.js
+++ b/browser/base/content/tabview/search.js
@@ -338,18 +338,17 @@ SearchEventHandlerClass.prototype = {
   },
 
   // ----------
   // Function: init
   // Initializes the searchbox to be focused, and everything
   // else to be hidden, and to have everything have the appropriate
   // event handlers;
   init: function () {
-    var self = this;
-    iQ("#searchbox")[0].focus(); 
+    let self = this;
     iQ("#search").hide();
     iQ("#searchshade").hide().click(function(event) {
       if ( event.target.id != "searchbox")
         hideSearch();
     });
     
     iQ("#searchbox").keyup(function() {
       performSearch();
@@ -385,16 +384,23 @@ SearchEventHandlerClass.prototype = {
         (!event.keyCode && !event.charCode)) {
       return;
     }
 
     // If we are already in an input field, allow typing as normal.
     if (event.target.nodeName == "INPUT")
       return;
 
+    // / is used to activate the search feature so the key shouldn't be entered 
+    // into the search box.
+    if (event.keyCode == KeyEvent.DOM_VK_SLASH) {
+      event.stopPropagation();
+      event.preventDefault();
+    }
+
     this.switchToInMode();
     this.initiatedBy = "keydown";
     ensureSearchShown(true);
   },
 
   // ----------
   // Function: inSearchKeyHandler
   // Handles all keydown while search mode.
@@ -521,31 +527,36 @@ var TabHandlers = {
   
   _mouseDownLocation: null
 };
 
 function createSearchTabMacher() {
   return new TabMatcher(iQ("#searchbox").val());
 }
 
-function hideSearch(event){
+function hideSearch(event) {
   iQ("#searchbox").val("");
   iQ("#searchshade").hide();
   iQ("#search").hide();
 
   iQ("#searchbutton").css({ opacity:.8 });
 
 #ifdef XP_MACOSX
   UI.setTitlebarColors(true);
 #endif
 
   performSearch();
   SearchEventHandler.switchToBeforeMode();
 
-  if (event){
+  if (event) {
+    // when hiding the search mode, we need to prevent the keypress handler
+    // in UI__setTabViewFrameKeyHandlers to handle the key press again. e.g. Esc
+    // which is already handled by the key down in this class.
+    if (event.type == "keydown")
+      UI.ignoreKeypressForSearch = true;
     event.preventDefault();
     event.stopPropagation();
   }
 
   // Return focus to the tab window
   UI.blurAll();
   gTabViewFrame.contentWindow.focus();
 
--- a/browser/base/content/tabview/ui.js
+++ b/browser/base/content/tabview/ui.js
@@ -127,16 +127,24 @@ let UI = {
   // Used to keep track of how many calls to storageBusy vs storageReady.
   _storageBusyCount: 0,
 
   // Variable: isDOMWindowClosing
   // Tells wether we already received the "domwindowclosed" event and the parent
   // windows is about to close.
   isDOMWindowClosing: false,
 
+  // Variable: _browserKeys
+  // Used to keep track of allowed browser keys.
+  _browserKeys: null,
+
+  // Variable: ignoreKeypressForSearch
+  // Used to prevent keypress being handled after quitting search mode.
+  ignoreKeypressForSearch: false,
+
   // ----------
   // Function: toString
   // Prints [UI] for debug use
   toString: function UI_toString() {
     return "[UI]";
   },
 
   // ----------
@@ -927,52 +935,137 @@ let UI = {
         cl = item;
         clDist = testDist;
       }
     });
     return cl;
   },
 
   // ----------
+  // Function: _setupBrowserKeys
+  // Sets up the allowed browser keys using key elements.
+  _setupBrowserKeys: function UI__setupKeyWhiteList() {
+    let keys = {};
+
+    [
+#ifdef XP_UNIX
+      "quitApplication",
+#endif
+#ifdef XP_MACOSX
+      "preferencesCmdMac", "minimizeWindow",
+#endif
+      "newNavigator", "newNavigatorTab", "find"
+     ].forEach(function(key) {
+      let element = gWindow.document.getElementById("key_" + key);
+      keys[key] = element.getAttribute("key").toLocaleLowerCase().charCodeAt(0);
+    });
+
+    // for key combinations with shift key, the charCode of upper case letters 
+    // are different to the lower case ones so need to handle them differently.
+    ["closeWindow", "tabview", "undoCloseTab", "undoCloseWindow",
+     "privatebrowsing"].forEach(function(key) {
+      let element = gWindow.document.getElementById("key_" + key);
+      keys[key] = element.getAttribute("key").toLocaleUpperCase().charCodeAt(0);
+    });
+
+    delete this._browserKeys;
+    this._browserKeys = keys;
+  },
+
+  // ----------
   // Function: _setTabViewFrameKeyHandlers
   // Sets up the key handlers for navigating between tabs within the TabView UI.
   _setTabViewFrameKeyHandlers: function UI__setTabViewFrameKeyHandlers() {
-    var self = this;
+    let self = this;
+
+    this._setupBrowserKeys();
 
     iQ(window).keyup(function(event) {
-      if (!event.metaKey) 
+      if (!event.metaKey)
         Keys.meta = false;
     });
 
-    iQ(window).keydown(function(event) {
-      if (event.metaKey) 
+    iQ(window).keypress(function(event) {
+      if (event.metaKey)
         Keys.meta = true;
 
-      if ((iQ(":focus").length > 0 && iQ(":focus")[0].nodeName == "INPUT") || 
-          isSearchEnabled())
+      function processBrowserKeys(evt) {
+#ifdef XP_MACOSX
+        if (evt.metaKey) {
+#else
+        if (evt.ctrlKey) {
+#endif
+          let preventDefault = true;
+          if (evt.shiftKey) {
+            switch (evt.charCode) {
+              case self._browserKeys.privatebrowsing:
+              case self._browserKeys.undoCloseTab:
+              case self._browserKeys.undoCloseWindow:
+              case self._browserKeys.closeWindow:
+                preventDefault = false;
+                break;
+              case self._browserKeys.tabview:
+                self.exit();
+                break;
+            }
+          } else {
+            switch (evt.charCode) {
+              case self._browserKeys.find:
+                self.enableSearch();
+                break;
+              case self._browserKeys.newNavigator:
+              case self._browserKeys.newNavigatorTab:
+                preventDefault = false;
+                break;
+#ifdef XP_UNIX
+              case self._browserKeys.quitApplication:
+                preventDefault = false;
+                break;
+#endif
+#ifdef XP_MACOSX
+              case self._browserKeys.preferencesCmdMac:
+              case self._browserKeys.minimizeWindow:
+                preventDefault = false;
+                break;
+#endif
+            }
+          }
+          if (preventDefault) {
+            evt.stopPropagation();
+            evt.preventDefault();
+          }
+        }
+      }
+      if ((iQ(":focus").length > 0 && iQ(":focus")[0].nodeName == "INPUT") ||
+          isSearchEnabled() || self.ignoreKeypressForSearch) {
+        self.ignoreKeypressForSearch = false;
+        processBrowserKeys(event);
         return;
+      }
 
       function getClosestTabBy(norm) {
         if (!self.getActiveTab())
           return null;
-        var centers =
+        let centers =
           [[item.bounds.center(), item]
              for each(item in TabItems.getItems()) if (!item.parent || !item.parent.hidden)];
-        var myCenter = self.getActiveTab().bounds.center();
-        var matches = centers
+        let myCenter = self.getActiveTab().bounds.center();
+        let matches = centers
           .filter(function(item){return norm(item[0], myCenter)})
           .sort(function(a,b){
             return myCenter.distance(a[0]) - myCenter.distance(b[0]);
           });
         if (matches.length > 0)
           return matches[0][1];
         return null;
       }
 
-      var norm = null;
+      let preventDefault = true;
+      let activeTab;
+      let norm = null;
       switch (event.keyCode) {
         case KeyEvent.DOM_VK_RIGHT:
           norm = function(a, me){return a.x > me.x};
           break;
         case KeyEvent.DOM_VK_LEFT:
           norm = function(a, me){return a.x < me.x};
           break;
         case KeyEvent.DOM_VK_DOWN:
@@ -985,87 +1078,75 @@ let UI = {
 
       if (norm != null) {
         var nextTab = getClosestTabBy(norm);
         if (nextTab) {
           if (nextTab.isStacked && !nextTab.parent.expanded)
             nextTab = nextTab.parent.getChild(0);
           self.setActiveTab(nextTab);
         }
-        event.stopPropagation();
-        event.preventDefault();
-      } else if (event.keyCode == KeyEvent.DOM_VK_ESCAPE) {
-        let activeGroupItem = GroupItems.getActiveGroupItem();
-        if (activeGroupItem && activeGroupItem.expanded)
-          activeGroupItem.collapse();
-        else 
-          self.exit();
-
-        event.stopPropagation();
-        event.preventDefault();
-      } else if (event.keyCode == KeyEvent.DOM_VK_RETURN ||
-                 event.keyCode == KeyEvent.DOM_VK_ENTER) {
-        let activeTab = self.getActiveTab();
-        if (activeTab)
-          activeTab.zoomIn();
+      } else {
+        switch(event.keyCode) {
+          case KeyEvent.DOM_VK_ESCAPE:
+            let activeGroupItem = GroupItems.getActiveGroupItem();
+            if (activeGroupItem && activeGroupItem.expanded)
+              activeGroupItem.collapse();
+            else
+              self.exit();
+            break;
+          case KeyEvent.DOM_VK_RETURN:
+          case KeyEvent.DOM_VK_ENTER:
+            activeTab = self.getActiveTab();
+            if (activeTab)
+              activeTab.zoomIn();
+            break;
+          case KeyEvent.DOM_VK_TAB:
+            // tab/shift + tab to go to the next tab.
+            activeTab = self.getActiveTab();
+            if (activeTab) {
+              let tabItems = (activeTab.parent ? activeTab.parent.getChildren() :
+                              [activeTab]);
+              let length = tabItems.length;
+              let currentIndex = tabItems.indexOf(activeTab);
 
-        event.stopPropagation();
-        event.preventDefault();
-      } else if (event.keyCode == KeyEvent.DOM_VK_TAB) {
-        // tab/shift + tab to go to the next tab.
-        var activeTab = self.getActiveTab();
-        if (activeTab) {
-          var tabItems = (activeTab.parent ? activeTab.parent.getChildren() :
-                          [activeTab]);
-          var length = tabItems.length;
-          var currentIndex = tabItems.indexOf(activeTab);
-
-          if (length > 1) {
-            if (event.shiftKey) {
-              if (currentIndex == 0)
-                newIndex = (length - 1);
-              else
-                newIndex = (currentIndex - 1);
-            } else {
-              if (currentIndex == (length - 1))
-                newIndex = 0;
-              else
-                newIndex = (currentIndex + 1);
+              if (length > 1) {
+                if (event.shiftKey) {
+                  if (currentIndex == 0)
+                    newIndex = (length - 1);
+                  else
+                    newIndex = (currentIndex - 1);
+                } else {
+                  if (currentIndex == (length - 1))
+                    newIndex = 0;
+                  else
+                    newIndex = (currentIndex + 1);
+                }
+                self.setActiveTab(tabItems[newIndex]);
+              }
             }
-            self.setActiveTab(tabItems[newIndex]);
-          }
+            break;
+          default:
+            processBrowserKeys(event);
+            preventDefault = false;
         }
-        event.stopPropagation();
-        event.preventDefault();
-      } else if (event.keyCode == KeyEvent.DOM_VK_SLASH) {
-        // the / event handler for find bar is defined in the findbar.xml
-        // binding.  To keep things in its own module, we handle our slash here.
-        self.enableSearch(event);
-      } else if (event.keyCode == KeyEvent.DOM_VK_BACK_SPACE) {
-        // prevent navigating backward in the selected tab's history
-        event.stopPropagation();
-        event.preventDefault();
+        if (preventDefault) {
+          event.stopPropagation();
+          event.preventDefault();
+        }
       }
     });
   },
 
   // ----------
   // Function: enableSearch
   // Enables the search feature.
-  // Parameters:
-  //   event - the event triggers this action.
-  enableSearch: function UI_enableSearch(event) {
+  enableSearch: function UI_enableSearch() {
     if (!isSearchEnabled()) {
       ensureSearchShown();
       SearchEventHandler.switchToInMode();
-      
-      if (event) {
-        event.stopPropagation();
-        event.preventDefault();
-      }
     }
   },
 
   // ----------
   // Function: _createGroupItemOnDrag
   // Called in response to a mousedown in empty space in the TabView UI;
   // creates a new groupItem based on the user's drag.
   _createGroupItemOnDrag: function UI__createGroupItemOnDrag(e) {
--- a/browser/base/content/test/tabview/Makefile.in
+++ b/browser/base/content/test/tabview/Makefile.in
@@ -45,16 +45,17 @@ include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
                  browser_tabview_alltabs.js \
                  browser_tabview_apptabs.js \
                  browser_tabview_bug580412.js \
                  browser_tabview_bug586553.js \
                  browser_tabview_bug587043.js \
                  browser_tabview_bug587231.js \
+                 browser_tabview_bug587276.js \
                  browser_tabview_bug587351.js \
                  browser_tabview_bug587503.js \
                  browser_tabview_bug587990.js \
                  browser_tabview_bug588265.js \
                  browser_tabview_bug589324.js \
                  browser_tabview_bug590606.js \
                  browser_tabview_bug591706.js \
                  browser_tabview_bug594958.js \
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabview/browser_tabview_bug587276.js
@@ -0,0 +1,102 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let contentWindow;
+
+function test() {
+  waitForExplicitFinish();
+
+  showTabView(test1);
+}
+
+function test1() {
+  ok(TabView.isVisible(), "Tab View is visible");
+
+  contentWindow = document.getElementById("tab-view").contentWindow;
+  whenTabViewIsHidden(function() {
+    ok(!TabView.isVisible(), "Tab View is not visible");
+    showTabView(test2);
+  });
+  EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true }, contentWindow);
+}
+
+function test2() {
+  ok(TabView.isVisible(), "Tab View is visible");
+
+  whenSearchIsEnabled(function() {
+    ok(contentWindow.isSearchEnabled(), "The search is enabled")
+
+    whenSearchIsDisabled(test3);
+    hideSearch();
+  });
+  EventUtils.synthesizeKey("f", { accelKey: true }, contentWindow);
+}
+
+function test3() {
+  ok(!contentWindow.isSearchEnabled(), "The search is disabled")
+
+  is(gBrowser.tabs.length, 1, "There is one tab before cmd/ctrl + t is pressed");
+  EventUtils.synthesizeKey("t", { accelKey: true }, contentWindow);
+  is(gBrowser.tabs.length, 2, "There are two tabs after cmd/ctrl + t is pressed");
+
+  gBrowser.tabs[0].linkedBrowser.loadURI("about:robots");
+  gBrowser.tabs[1].linkedBrowser.loadURI("http://example.com/");
+
+  afterAllTabsLoaded(function () {
+    showTabView(test4);
+  });
+}
+
+function test4() {
+  is(gBrowser.tabs.length, 2, "There are two tabs");
+  
+  let onTabClose = function() {
+    gBrowser.tabContainer.removeEventListener("TabClose", onTabClose, true);
+    executeSoon(function() {
+      is(gBrowser.tabs.length, 1, "There is one tab after removing one");
+
+      EventUtils.synthesizeKey("T", { accelKey: true, shiftKey: true }, contentWindow);
+      is(gBrowser.tabs.length, 2, "There are two tabs after restoring one");
+
+      gBrowser.tabs[0].linkedBrowser.loadURI("about:blank");
+      gBrowser.removeTab(gBrowser.tabs[1]);
+      test8();
+    });
+  };
+  gBrowser.tabContainer.addEventListener("TabClose", onTabClose, true);
+  gBrowser.removeTab(gBrowser.tabs[1]);
+}
+
+// below key combination shouldn't trigger actions in tabview UI
+function test8() {
+  let newTab = gBrowser.loadOneTab("about:blank", { inBackground: true });
+
+  is(gBrowser.tabs.length, 2, "There are two tabs before cmd/ctrl + w is pressed");
+  EventUtils.synthesizeKey("w", { accelKey: true }, contentWindow);
+  is(gBrowser.tabs.length, 2, "There are two tabs after cmd/ctrl + w is pressed");
+
+  gBrowser.removeTab(newTab);
+  test9();
+}
+
+function test9() {
+  let zoomLevel = ZoomManager.zoom;
+  EventUtils.synthesizeKey("+", { accelKey: true }, contentWindow);
+  is(ZoomManager.zoom, zoomLevel, "The zoom level remains unchanged after cmd/ctrl + + is pressed");
+
+  EventUtils.synthesizeKey("-", { accelKey: true }, contentWindow);
+  is(ZoomManager.zoom, zoomLevel, "The zoom level remains unchanged after cmd/ctrl + - is pressed");
+
+  test10();
+}
+
+function test10() {
+  is(gBrowser.tabs.length, 1, "There is one tab before cmd/ctrl + shift + a is pressed");
+  // it would open about:addons on a new tab if it passes through the white list.
+  EventUtils.synthesizeKey("A", { accelKey: true, shiftKey: true }, contentWindow);
+
+  executeSoon(function() {
+    is(gBrowser.tabs.length, 1, "There is still one tab after cmd/ctrl + shift + a is pressed");
+    hideTabView(finish);
+  })
+}
--- a/browser/base/content/test/tabview/browser_tabview_bug595191.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug595191.js
@@ -52,10 +52,10 @@ function toggleTabViewTest(contentWindow
   let onTabViewHidden = function() {
     contentWindow.removeEventListener("tabviewhidden", onTabViewHidden, false);
 
     ok(!TabView.isVisible(), "Tab View is hidden");
     finish();
   }
   contentWindow.addEventListener("tabviewhidden", onTabViewHidden, false);
   // Use keyboard shortcut to toggle back to browser view
-  EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true });
+  EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true });
 }
--- a/browser/base/content/test/tabview/browser_tabview_bug595518.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug595518.js
@@ -23,17 +23,17 @@ function onTabViewWindowLoaded() {
     
     // verify that the exit button no longer has focus
     is(contentWindow.iQ("#exit-button:focus").length, 0, 
        "The exit button doesn't have the focus");
 
     // verify that the keyboard combo works (this is the crux of bug 595518)
     // Prepare the key combo
     window.addEventListener("tabviewshown", onTabViewShown, false);
-    EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true }, contentWindow);
+    EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true }, contentWindow);
   }
   
   let onTabViewShown = function() {
     window.removeEventListener("tabviewshown", onTabViewShown, false);
     
     // test if the key combo worked
     ok(TabView.isVisible(), "Tab View is visible");
 
--- a/browser/base/content/test/tabview/browser_tabview_bug595560.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug595560.js
@@ -28,18 +28,18 @@ function onTabViewWindowLoaded() {
   let contentWindow = document.getElementById("tab-view").contentWindow;
   testOne(contentWindow);
 }
 
 function testOne(contentWindow) {
   onSearchEnabledAndDisabled(contentWindow, function() {
     testTwo(contentWindow); 
   });
-  // execute a find command (i.e. press cmd/ctrl F)
-  document.getElementById("cmd_find").doCommand();
+  // press cmd/ctrl F
+  EventUtils.synthesizeKey("f", { accelKey: true });
 }
 
 function testTwo(contentWindow) {
   onSearchEnabledAndDisabled(contentWindow, function() { 
     testThree(contentWindow);
   });
   // press /
   EventUtils.synthesizeKey("VK_SLASH", { type: "keydown" }, contentWindow);
--- a/browser/base/content/test/tabview/browser_tabview_bug595930.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug595930.js
@@ -36,17 +36,17 @@ function onTabViewWindowLoaded() {
       finish();
     };
     window.addEventListener("tabviewhidden", onTabViewHidden, false);
 
     // delay to give time for hidden group DOM element to be removed so
     // the appropriate group would get selected when the key
     // combination is pressed
     executeSoon(function() { 
-      EventUtils.synthesizeKey("e", {accelKey : true, shiftKey: true}, contentWindow);
+      EventUtils.synthesizeKey("E", {accelKey : true, shiftKey: true}, contentWindow);
     });
   });
 
   group1.addSubscriber(group1, "groupHidden", function() {
     group1.removeSubscriber(group1, "groupHidden");
 
     // close undo group
     let closeButton = group1.$undoContainer.find(".close");
--- a/browser/base/content/test/tabview/browser_tabview_bug597980.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug597980.js
@@ -73,13 +73,13 @@ function part2(win) {
 
     win.addEventListener("tabviewhidden", function () {
       win.removeEventListener("tabviewhidden", arguments.callee, false);
       is(win.gBrowser.selectedTab, newTab, "The seleted tab should be the same as before (new tab)");
        win.close();
        finish();
     }, false);
     // show tabview
-    EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true }, win);
+    EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true }, win);
     // hide tabview
-    EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true }, win);
+    EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true }, win);
   })
 }
--- a/browser/base/content/test/tabview/browser_tabview_launch.js
+++ b/browser/base/content/test/tabview/browser_tabview_launch.js
@@ -69,15 +69,15 @@ function onTabViewShown() {
 
 // ----------
 function onTabViewHidden() {
   ok(!TabView.isVisible(), "Tab View is hidden. Count: " + tabViewShownCount);
 
   if (tabViewShownCount == 1) {
     document.getElementById("menu_tabview").doCommand();
   } else if (tabViewShownCount == 2) {
-    EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true });
+    EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true });
   } else if (tabViewShownCount == 3) {
     window.removeEventListener("tabviewshown", onTabViewShown, false);
     window.removeEventListener("tabviewhidden", onTabViewHidden, false);
     finish();
   }
 }
--- a/browser/base/content/test/tabview/head.js
+++ b/browser/base/content/test/tabview/head.js
@@ -168,16 +168,77 @@ function whenTabViewIsShown(callback, wi
 
   win.addEventListener('tabviewshown', function () {
     win.removeEventListener('tabviewshown', arguments.callee, false);
     callback();
   }, false);
 }
 
 // ----------
+function showSearch(callback, win) {
+  win = win || window;
+
+  let contentWindow = win.document.getElementById("tab-view").contentWindow;
+  if (contentWindow.isSearchEnabled()) {
+    callback();
+    return;
+  }
+
+  whenSearchIsEnabled(callback, win);
+  contentWindow.performSearch();
+}
+
+// ----------
+function hideSearch(callback, win) {
+  win = win || window;
+
+  let contentWindow = win.document.getElementById("tab-view").contentWindow;
+  if (!contentWindow.isSearchEnabled()) {
+    callback();
+    return;
+  }
+
+  whenSearchIsDisabled(callback, win);
+  contentWindow.hideSearch();
+}
+
+// ----------
+function whenSearchIsEnabled(callback, win) {
+  win = win || window;
+
+  let contentWindow = win.document.getElementById("tab-view").contentWindow;
+  if (contentWindow.isSearchEnabled()) {
+    callback();
+    return;
+  }
+
+  contentWindow.addEventListener("tabviewsearchenabled", function () {
+    contentWindow.removeEventListener("tabviewsearchenabled", arguments.callee, false);
+    callback();
+  }, false);
+}
+
+// ----------
+function whenSearchIsDisabled(callback, win) {
+  win = win || window;
+
+  let contentWindow = win.document.getElementById("tab-view").contentWindow;
+  if (!contentWindow.isSearchEnabled()) {
+    callback();
+    return;
+  }
+
+  contentWindow.addEventListener("tabviewsearchdisabled", function () {
+    contentWindow.removeEventListener("tabviewsearchdisabled", arguments.callee, false);
+    callback();
+  }, false);
+}
+
+
+// ----------
 function hideGroupItem(groupItem, callback) {
   if (groupItem.hidden) {
     callback();
     return;
   }
 
   groupItem.addSubscriber(groupItem, "groupHidden", function () {
     groupItem.removeSubscriber(groupItem, "groupHidden");