Bug 686487 - Ctrl+K should open a new global search tab if searchbox is not available. r=asuth,aceman
authorThomas Duellmann <bugzilla2007@duellmann24.net>
Mon, 17 Apr 2017 19:42:35 +0200
changeset 28535 a6e0661213c93f7efdb3221e9190fb58718cabc0
parent 28534 f82b267381aef171c360c385249748c610926cf6
child 28536 045fa1d535b59654f4fa3ddbe01364ee51738db9
push id1986
push userclokep@gmail.com
push dateWed, 02 Aug 2017 14:43:31 +0000
treeherdercomm-beta@b51c9adf2c9e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth, aceman
bugs686487, 1357078
Bug 686487 - Ctrl+K should open a new global search tab if searchbox is not available. r=asuth,aceman Bug 1357078 - Focus search input on faceted global search results. Started by Wei Zhang, polished by Aceman.
mail/base/content/glodaFacetTab.js
mail/base/content/mailWindowOverlay.js
mail/base/content/tabmail.xml
mail/locales/en-US/chrome/messenger/glodaFacetView.properties
--- a/mail/base/content/glodaFacetTab.js
+++ b/mail/base/content/glodaFacetTab.js
@@ -32,16 +32,17 @@ var glodaFacetTabType = {
     aTab.iframe = aTab.panel.querySelector("iframe");
 
     // Wire up the search input icon click event
     let searchInput = aTab.panel.querySelector(".remote-gloda-search");
     let searchIcon = aTab.panel.querySelector(".gloda-search-icon");
     searchIcon.addEventListener("click", function(e) {
       searchInput.doSearch();
     });
+    searchInput.focus();
 
     if ("query" in aArgs) {
       aTab.query = aArgs.query;
       aTab.collection = aTab.query.getCollection();
 
       aTab.title = this.strings.get("glodaFacetView.tab.query.label");
       aTab.searchString = null;
     }
@@ -51,18 +52,19 @@ var glodaFacetTabType = {
       aTab.query = aTab.searcher.query;
       if ("IMSearcher" in aArgs) {
         aTab.IMSearcher = aArgs.IMSearcher;
         aTab.IMCollection = aArgs.IMSearcher.getCollection();
         aTab.IMQuery = aTab.IMSearcher.query;
       }
 
       let searchString = aTab.searcher.searchString;
-      aTab.title = aTab.searchInputValue = aTab.searchString =
-        searchString;
+      aTab.searchInputValue = aTab.searchString = searchString;
+      aTab.title = searchString ? searchString
+                   : this.strings.get("glodaFacetView.tab.search.label");
     }
     else if ("collection" in aArgs) {
       aTab.collection = aArgs.collection;
 
       aTab.title = this.strings.get("glodaFacetView.tab.query.label");
       aTab.searchString = null;
     }
 
--- a/mail/base/content/mailWindowOverlay.js
+++ b/mail/base/content/mailWindowOverlay.js
@@ -3551,43 +3551,74 @@ function SendMDNResponse()
   gMessageNotificationBar.mdnGenerator.userAgreed();
 }
 
 function IgnoreMDNResponse()
 {
   gMessageNotificationBar.mdnGenerator.userDeclined();
 }
 
+/***
+ * Focus the gloda global search input box on current tab, or,
+ * if the search box is not available, open a new gloda search tab
+ * (with its search box focused).
+ */
 function QuickSearchFocus()
 {
+  // Default to focusing the search box on the current tab
+  let newTab = false;
+  let searchInput;
   let tabmail = document.getElementById('tabmail');
-
-  // If we're currently viewing a Gloda tab, drill down to find the
-  // built-in search input, and select that.
-  if (tabmail
-      && tabmail.currentTabInfo.mode.name == "glodaFacet") {
-    let searchInput = tabmail.currentTabInfo
-                             .panel
-                             .querySelector(".remote-gloda-search");
-    if (searchInput)
-      searchInput.select();
-
+  if (!tabmail) {
+    // This should never happen.
     return;
   }
 
-  if (tabmail && tabmail.currentTabInfo.mode.name == "chat") {
-    let searchInput = document.getElementById("IMSearchInput");
-    if (searchInput)
-      searchInput.select();
-    return;
+  switch (tabmail.currentTabInfo.mode.name) {
+    case  "glodaFacet":
+      // If we're currently viewing a Gloda tab, drill down to find the
+      // built-in search input, and select that.
+      searchInput = tabmail.currentTabInfo
+                           .panel
+                           .querySelector(".remote-gloda-search");
+      break;
+    case "chat":
+      searchInput = document.getElementById("IMSearchInput");
+      break;
+    default:
+      searchInput = document.getElementById("searchInput");
+  }
+
+  if (!searchInput) {
+    // If searchInput is not found on current tab (e.g. removed by user),
+    // use a new tab.
+    newTab = true;
   }
-
-  var quickSearchTextBox = document.getElementById('searchInput');
-  if (quickSearchTextBox)
-    quickSearchTextBox.select();
+  else {
+    // The searchInput element exists on current tab.
+    // However, via toolbar customization, it can be in different places:
+    // Toolbars, tab bar, menu bar, etc. If the containing elements are hidden,
+    // searchInput will also be hidden, so clientHeight and clientWidth of the
+    // searchbox or one of its parents will typically be zero and we can test
+    // for that. If searchInput is hidden, use a new tab.
+    let element = searchInput;
+    while (element) {
+      if ((element.clientHeight == 0) || (element.clientWidth == 0))
+        newTab = true;
+      element = element.parentElement;
+    }
+  }
+
+  if (!newTab) {
+    // Focus and select global search box on current tab.
+    searchInput.select();
+  } else {
+    // Open a new global search tab (with focus on its global search box)
+    tabmail.openTab("glodaFacet");
+  }
 }
 
 /**
  * Opens a search window with the given folder, or the displayed one if none is
  * chosen.
  *
  * @param [aFolder] the folder to open the search window for, if different from
  *                  the displayed one
--- a/mail/base/content/tabmail.xml
+++ b/mail/base/content/tabmail.xml
@@ -467,16 +467,24 @@
           let tabMode = this.tabModes[aTabModeName];
           // if we are already at our limit for this mode, show an existing one
           if (tabMode.tabs.length == tabMode.maxTabs) {
             let desiredTab = tabMode.tabs[0];
             this.tabContainer.selectedIndex = this.tabInfo.indexOf(desiredTab);
             return null;
           }
 
+          // For "glodaFacet" tab mode, if aArgs is omitted,
+          // default to a blank user search.
+          if (aTabModeName=="glodaFacet" && !aArgs) {
+            aArgs = {
+              searcher: new GlodaMsgSearcher(null, "")
+            };
+          }
+
           // Do this so that we don't generate strict warnings
           let background = ("background" in aArgs) && aArgs.background;
 
           // If the mode wants us to, we should switch to an existing tab
           // rather than open a new one. We shouldn't switch to the tab if
           // we're opening it in the background, though.
           let shouldSwitchToFunc = tabMode.shouldSwitchTo ||
                                    tabMode.tabType.shouldSwitchTo;
--- a/mail/locales/en-US/chrome/messenger/glodaFacetView.properties
+++ b/mail/locales/en-US/chrome/messenger/glodaFacetView.properties
@@ -1,21 +1,28 @@
 # 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/.
 
-# LOCALIZATION NOTE (glodaFacetView.tab.query.label): The title to display for
-#  tabs that are based on a gloda (global database) query or collection rather
-#  than a user search.  In the case of a user search, we just display the
-#  search string they entered.  At some point we might try and explain what
-#  the query/collection is an automatic fashion, but not today.
+# LOCALIZATION NOTE (glodaFacetView.tab.query.label):
+#  The tab title to display for tabs that are based on a gloda (global database)
+#  query or collection rather than a user search.  At some point we might try
+#  and explain what the query/collection is in automatic fashion, but not today.
 glodaFacetView.tab.query.label=Search
 
+# LOCALIZATION NOTE (glodaFacetView.tab.search.label):
+#  The tab title to display for tabs with a new gloda (global database)
+#  user search (rather than a query or collection) without a search string.
+#  After the search has been started, we just display the search string entered
+#  by the user.
+glodaFacetView.tab.search.label=Search
+
 # LOCALIZATION NOTE(glodaFacetView.search.label):
 #  The heading for the search page.
+#  A short description of user's search query will be appended.
 glodaFacetView.search.label=Search
 
 # LOCALIZATION NOTE(glodaFacetView.constraints.query.fulltext.label):
 #  The label to display to describe when our base query was a fulltext search
 #  across messages.  The value is displayed following the label.
 glodaFacetView.constraints.query.fulltext.label=Searching for #1
 glodaFacetView.constraints.query.fulltext.andJoinWord=and
 glodaFacetView.constraints.query.fulltext.orJoinWord=or