Bug 1480503 - Create API for setting text (keyword) in and focusing on the awesomebar. r=mak
authorDrew Willcoxon <adw@mozilla.com>
Wed, 08 Aug 2018 19:45:52 +0000
changeset 480954 534420d993a5494bbc31cb8687928fff3f444fe9
parent 480953 a8ec1cd112b308528ecce3680658855d55dd4884
child 480955 78f22251dd11e53e3fa18e89c28cf314824f8ea3
push id1757
push userffxbld-merge
push dateFri, 24 Aug 2018 17:02:43 +0000
treeherdermozilla-release@736023aebdb1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs1480503
milestone62.0
Bug 1480503 - Create API for setting text (keyword) in and focusing on the awesomebar. r=mak Differential Revision: https://phabricator.services.mozilla.com/D2895
browser/base/content/test/performance/browser_urlbar_keyed_search.js
browser/base/content/test/performance/browser_urlbar_search.js
browser/base/content/test/urlbar/browser.ini
browser/base/content/test/urlbar/browser_urlbarSearchFunction.js
browser/base/content/urlbarBindings.xml
--- a/browser/base/content/test/performance/browser_urlbar_keyed_search.js
+++ b/browser/base/content/test/performance/browser_urlbar_keyed_search.js
@@ -15,17 +15,17 @@ requestLongerTimeout(5);
  */
 
 /* These reflows happen only the first time the awesomebar panel opens. */
 const EXPECTED_REFLOWS_FIRST_OPEN = [
   {
     stack: [
       "_rebuild@chrome://browser/content/search/search.xml",
       "set_popup@chrome://browser/content/search/search.xml",
-      "enableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
+      "set_oneOffSearchesEnabled@chrome://browser/content/urlbarBindings.xml",
       "_enableOrDisableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
       "urlbar_XBL_Constructor/<@chrome://browser/content/urlbarBindings.xml",
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml"
     ],
   },
--- a/browser/base/content/test/performance/browser_urlbar_search.js
+++ b/browser/base/content/test/performance/browser_urlbar_search.js
@@ -15,17 +15,17 @@ requestLongerTimeout(5);
  */
 
 /* These reflows happen only the first time the awesomebar panel opens. */
 const EXPECTED_REFLOWS_FIRST_OPEN = [
   {
     stack: [
       "_rebuild@chrome://browser/content/search/search.xml",
       "set_popup@chrome://browser/content/search/search.xml",
-      "enableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
+      "set_oneOffSearchesEnabled@chrome://browser/content/urlbarBindings.xml",
       "_enableOrDisableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
       "urlbar_XBL_Constructor/<@chrome://browser/content/urlbarBindings.xml",
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml"
     ],
   },
--- a/browser/base/content/test/urlbar/browser.ini
+++ b/browser/base/content/test/urlbar/browser.ini
@@ -101,16 +101,17 @@ support-files =
   searchSuggestionEngine.sjs
 [browser_urlbarPlaceholder.js]
 support-files =
   searchSuggestionEngine.xml
   searchSuggestionEngine.sjs
 [browser_urlbarPrivateBrowsingWindowChange.js]
 [browser_urlbarRaceWithTabs.js]
 [browser_urlbarRevert.js]
+[browser_urlbarSearchFunction.js]
 [browser_urlbarSearchSingleWordNotification.js]
 [browser_urlbarSearchSuggestions.js]
 support-files =
   searchSuggestionEngine.xml
   searchSuggestionEngine.sjs
 [browser_urlbarSearchSuggestions_opt-out.js]
 support-files =
   searchSuggestionEngine.xml
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/urlbar/browser_urlbarSearchFunction.js
@@ -0,0 +1,217 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This test checks the urlbar.search() function.
+
+"use strict";
+
+add_task(async function init() {
+  let which = gURLBar._whichSearchSuggestionsNotification || undefined;
+  registerCleanupFunction(async function() {
+    // Reset the search suggestions notification.
+    if (which === undefined) {
+      delete gURLBar._whichSearchSuggestionsNotification;
+    } else {
+      gURLBar._whichSearchSuggestionsNotification = which;
+    }
+    Services.prefs.clearUserPref("timesBeforeHidingSuggestionsHint");
+
+    // Make sure the popup is closed for the next test.
+    gURLBar.handleRevert();
+    gURLBar.blur();
+    Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
+  });
+});
+
+
+// Calls search() with only a search-string argument.
+add_task(async function basic() {
+  let resetNotification = enableSearchSuggestionsNotification();
+
+  gURLBar.search("basic");
+  await promiseSearchComplete();
+  await waitForAutocompleteResultAt(0);
+  assertUrlbarValue("basic");
+
+  assertSearchSuggestionsNotificationVisible(true);
+  assertOneOffButtonsVisible(true);
+
+  EventUtils.synthesizeKey("KEY_Escape");
+  await promisePopupHidden(gURLBar.popup);
+
+  resetNotification();
+});
+
+
+// Calls search() with the search-suggestions notification disabled.
+add_task(async function disableSearchSuggestionsNotification() {
+  let resetNotification = enableSearchSuggestionsNotification();
+
+  gURLBar.search("disableSearchSuggestionsNotification", {
+    disableSearchSuggestionsNotification: true,
+  });
+  await promiseSearchComplete();
+  await waitForAutocompleteResultAt(0);
+  assertUrlbarValue("disableSearchSuggestionsNotification");
+
+  assertSearchSuggestionsNotificationVisible(false);
+  assertOneOffButtonsVisible(true);
+
+  EventUtils.synthesizeKey("KEY_Escape");
+  await promisePopupHidden(gURLBar.popup);
+
+  // Open the popup again (by doing another search) to make sure the
+  // notification is shown -- i.e., that we didn't accidentally break it if
+  // it should continue being shown.
+  gURLBar.search("disableSearchSuggestionsNotification again");
+  await promiseSearchComplete();
+  await waitForAutocompleteResultAt(0);
+  assertUrlbarValue("disableSearchSuggestionsNotification again");
+  assertSearchSuggestionsNotificationVisible(true);
+  assertOneOffButtonsVisible(true);
+
+  EventUtils.synthesizeKey("KEY_Escape");
+  await promisePopupHidden(gURLBar.popup);
+
+  resetNotification();
+});
+
+
+// Calls search() with the one-off search buttons disabled.
+add_task(async function disableOneOffButtons() {
+  let resetNotification = enableSearchSuggestionsNotification();
+
+  gURLBar.search("disableOneOffButtons", {
+    disableOneOffButtons: true,
+  });
+  await promiseSearchComplete();
+  await waitForAutocompleteResultAt(0);
+  assertUrlbarValue("disableOneOffButtons");
+
+  assertSearchSuggestionsNotificationVisible(true);
+  assertOneOffButtonsVisible(false);
+
+  EventUtils.synthesizeKey("KEY_Escape");
+  await promisePopupHidden(gURLBar.popup);
+
+  // Open the popup again (by doing another search) to make sure the one-off
+  // buttons are shown -- i.e., that we didn't accidentally break them.
+  gURLBar.search("disableOneOffButtons again");
+  await promiseSearchComplete();
+  await waitForAutocompleteResultAt(0);
+  assertUrlbarValue("disableOneOffButtons again");
+  assertSearchSuggestionsNotificationVisible(true);
+  assertOneOffButtonsVisible(true);
+
+  EventUtils.synthesizeKey("KEY_Escape");
+  await promisePopupHidden(gURLBar.popup);
+
+  resetNotification();
+});
+
+
+// Calls search() with both the search-suggestions notification and the one-off
+// search buttons disabled.
+add_task(async function disableSearchSuggestionsNotificationAndOneOffButtons() {
+  let resetNotification = enableSearchSuggestionsNotification();
+
+  gURLBar.search("disableSearchSuggestionsNotificationAndOneOffButtons", {
+    disableSearchSuggestionsNotification: true,
+    disableOneOffButtons: true,
+  });
+  await promiseSearchComplete();
+  await waitForAutocompleteResultAt(0);
+  assertUrlbarValue("disableSearchSuggestionsNotificationAndOneOffButtons");
+
+  assertSearchSuggestionsNotificationVisible(false);
+  assertOneOffButtonsVisible(false);
+
+  EventUtils.synthesizeKey("KEY_Escape");
+  await promisePopupHidden(gURLBar.popup);
+
+  // Open the popup again (by doing another search) to make sure the
+  // notification and one-off buttons are shown -- i.e., that we didn't
+  // accidentally break them.
+  gURLBar.search("disableSearchSuggestionsNotificationAndOneOffButtons again");
+  await promiseSearchComplete();
+  await waitForAutocompleteResultAt(0);
+  assertUrlbarValue("disableSearchSuggestionsNotificationAndOneOffButtons again");
+  assertSearchSuggestionsNotificationVisible(true);
+  assertOneOffButtonsVisible(true);
+
+  EventUtils.synthesizeKey("KEY_Escape");
+  await promisePopupHidden(gURLBar.popup);
+
+  resetNotification();
+});
+
+
+/**
+ * Makes sure the search-suggestions notification will be shown the next several
+ * times the popup opens.
+ *
+ * @return  A function that you should call when you're done that resets the
+ *          state of the notification.
+ */
+function enableSearchSuggestionsNotification() {
+  let which = gURLBar._whichSearchSuggestionsNotification || undefined;
+  gURLBar._whichSearchSuggestionsNotification = "opt-out";
+  Services.prefs.setIntPref("timesBeforeHidingSuggestionsHint", 10);
+  return function reset() {
+    if (which === undefined) {
+      delete gURLBar._whichSearchSuggestionsNotification;
+    } else {
+      gURLBar._whichSearchSuggestionsNotification = which;
+    }
+    Services.prefs.clearUserPref("timesBeforeHidingSuggestionsHint");
+  };
+}
+
+/**
+ * Asserts that the search-suggestion notification is or isn't visible.
+ *
+ * @param visible
+ *        True if it should be visible, false if not.
+ */
+function assertSearchSuggestionsNotificationVisible(visible) {
+  Assert.equal(
+    gURLBar.popup.classList.contains("showSearchSuggestionsNotification"),
+    visible
+  );
+  Assert.equal(
+    window.getComputedStyle(gURLBar.popup.searchSuggestionsNotification).display,
+    visible ? "-moz-deck" : "none"
+  );
+}
+
+/**
+ * Asserts that the one-off search buttons are or aren't visible.
+ *
+ * @param visible
+ *        True if they should be visible, false if not.
+ */
+function assertOneOffButtonsVisible(visible) {
+  Assert.equal(gURLBar.popup.oneOffSearchesEnabled, visible);
+  Assert.equal(
+    window.getComputedStyle(gURLBar.popup.oneOffSearchButtons).display,
+    visible ? "-moz-box" : "none"
+  );
+}
+
+/**
+ * Asserts that the urlbar's input value is the given value.  Also asserts that
+ * the first (heuristic) result in the popup is a search suggestion whose search
+ * query is the given value.
+ *
+ * @param value
+ *        The urlbar's expected value.
+ */
+function assertUrlbarValue(value) {
+  Assert.equal(gURLBar.value, value);
+  let controller = gURLBar.controller;
+  Assert.ok(controller.matchCount > 0);
+  let action = gURLBar._parseActionUrl(controller.getValueAt(0));
+  Assert.ok(action);
+  Assert.equal(action.type, "searchengine");
+  Assert.equal(action.params.searchQuery, value);
+}
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1272,18 +1272,18 @@ file, You can obtain one at http://mozil
                 break;
             }
           }
         ]]></body>
       </method>
 
       <method name="_enableOrDisableOneOffSearches">
         <body><![CDATA[
-          let enable = this._prefs.getBoolPref("oneOffSearches");
-          this.popup.enableOneOffSearches(enable);
+          this.popup.oneOffSearchesEnabled =
+            this._prefs.getBoolPref("oneOffSearches");
         ]]></body>
       </method>
 
       <method name="handleEvent">
         <parameter name="aEvent"/>
         <body><![CDATA[
           switch (aEvent.type) {
             case "paste":
@@ -1603,16 +1603,62 @@ file, You can obtain one at http://mozil
               if (mouseFocused) {
                 delete this._whichSearchSuggestionsNotification;
                 this._showSearchSuggestionNotificationOnMouseFocus = false;
               }
             }
           }
         ]]></body>
       </method>
+
+      <!--
+        Sets the input's value, starts a search, and opens the popup.
+
+        @param  value
+                The input's value will be set to this value, and the search will
+                use it as its query.
+        @param  options
+                An optional object with the following optional properties:
+                * disableOneOffButtons: Set to true to hide the one-off search
+                  buttons.
+                * disableSearchSuggestionsNotification: Set to true to hide the
+                  onboarding opt-out search suggestions notification.
+      -->
+      <method name="search">
+        <parameter name="value"/>
+        <parameter name="options"/>
+        <body><![CDATA[
+          this.focus();
+          this.textValue = value;
+
+          options = options || {};
+
+          if (options.disableOneOffButtons) {
+            this.popup.addEventListener("popupshowing", () => {
+              if (this.popup.oneOffSearchesEnabled) {
+                this.popup.oneOffSearchesEnabled = false;
+                this.popup.addEventListener("popuphidden", () => {
+                  this.popup.oneOffSearchesEnabled = true;
+                }, {once: true});
+              }
+            }, {once: true});
+          }
+
+          if (options.disableSearchSuggestionsNotification &&
+              this.whichSearchSuggestionsNotification != "none") {
+            let which = this.whichSearchSuggestionsNotification;
+            this._whichSearchSuggestionsNotification = "none";
+            this.popup.addEventListener("popuphidden", () => {
+              this._whichSearchSuggestionsNotification = which;
+            }, {once: true});
+          }
+
+          this.controller.startSearch(value);
+        ]]></body>
+      </method>
     </implementation>
 
     <handlers>
       <handler event="keydown"><![CDATA[
         if (this._noActionKeys.has(event.keyCode) &&
             this.popup.selectedIndex >= 0 &&
             !this._pressedNoActionKeys.has(event.keyCode)) {
           if (this._pressedNoActionKeys.size == 0) {
@@ -1842,35 +1888,38 @@ file, You can obtain one at http://mozil
             // Ignore right-clicks.
             return;
           }
           // Otherwise "call super" -- do what autocomplete-base-popup does.
           this.input.controller.handleEnter(true, aEvent);
         ]]></body>
       </method>
 
-      <method name="enableOneOffSearches">
-        <parameter name="enable"/>
-        <body><![CDATA[
-          this._oneOffSearchesEnabled = enable;
-          if (enable) {
+      <property name="oneOffSearchesEnabled">
+        <getter><![CDATA[
+          return this._oneOffSearchesEnabled;
+        ]]></getter>
+        <setter><![CDATA[
+          this._oneOffSearchesEnabled = !!val;
+          if (val) {
             this.oneOffSearchButtons.telemetryOrigin = "urlbar";
             this.oneOffSearchButtons.style.display = "-moz-box";
             // Set .textbox first, since the popup setter will cause
             // a _rebuild call that uses it.
             this.oneOffSearchButtons.textbox = this.input;
             this.oneOffSearchButtons.popup = this;
           } else {
             this.oneOffSearchButtons.telemetryOrigin = null;
             this.oneOffSearchButtons.style.display = "none";
             this.oneOffSearchButtons.textbox = null;
             this.oneOffSearchButtons.popup = null;
           }
-        ]]></body>
-      </method>
+          return this._oneOffSearchesEnabled;
+        ]]></setter>
+      </property>
 
       <!-- Override this so that navigating between items results in an item
            always being selected. -->
       <method name="getNextIndex">
         <parameter name="reverse"/>
         <parameter name="amount"/>
         <parameter name="index"/>
         <parameter name="maxRow"/>