Bug 1675611 - Part 1 - Add UrlbarProvider.onSelection. r=mak
authorHarry Twyford <htwyford@mozilla.com>
Fri, 13 Nov 2020 20:15:08 +0000
changeset 557215 3505eb4f3af05cba284e46dccaef52cec8d8558d
parent 557214 1b0ac6c836ba4e82428caeb9101072fa7a6fa7bc
child 557216 519424ad964e1c825dbfc2bcf7df48b432c806c7
push id130941
push userhtwyford@mozilla.com
push dateFri, 13 Nov 2020 20:19:51 +0000
treeherderautoland@519424ad964e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs1675611
milestone84.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1675611 - Part 1 - Add UrlbarProvider.onSelection. r=mak Differential Revision: https://phabricator.services.mozilla.com/D97023
browser/components/urlbar/UrlbarUtils.jsm
browser/components/urlbar/UrlbarView.jsm
browser/components/urlbar/tests/UrlbarTestUtils.jsm
browser/components/urlbar/tests/browser/browser.ini
browser/components/urlbar/tests/browser/browser_result_onSelection.js
--- a/browser/components/urlbar/UrlbarUtils.jsm
+++ b/browser/components/urlbar/UrlbarUtils.jsm
@@ -1611,16 +1611,31 @@ class UrlbarProvider {
    *
    * @param {boolean} isPrivate True if the engagement is in a private context.
    * @param {string} state The state of the engagement, one of: start,
    *        engagement, abandonment, discard.
    */
   onEngagement(isPrivate, state) {}
 
   /**
+   * Called when a result from the provider is selected. "Selected" refers to
+   * the user highlighing the result with the arrow keys/Tab, before it is
+   * picked. onSelection is also called when a user clicks a result. In the
+   * event of a click, onSelection is called just before pickResult. Note that
+   * this is called when heuristic results are pre-selected.
+   *
+   * @param {UrlbarResult} result
+   *   The result that was selected.
+   * @param {Element} element
+   *   The element in the result's view that was selected.
+   * @abstract
+   */
+  onSelection(result, element) {}
+
+  /**
    * This is called only for dynamic result types, when the urlbar view updates
    * the view of one of the results of the provider.  It should return an object
    * describing the view update that looks like this:
    *
    *   {
    *     nodeNameFoo: {
    *       attributes: {
    *         someAttribute: someValue,
--- a/browser/components/urlbar/UrlbarView.jsm
+++ b/browser/components/urlbar/UrlbarView.jsm
@@ -1588,16 +1588,21 @@ class UrlbarView {
     this._selectedElement = item;
 
     let result = item?.closest(".urlbarView-row")?.result;
     if (updateInput) {
       this.input.setValueFromResult(result);
     } else {
       this.input.setResultForCurrentValue(result);
     }
+
+    let provider = UrlbarProvidersManager.getProvider(result?.providerName);
+    if (provider) {
+      provider.tryMethod("onSelection", result, item);
+    }
   }
 
   /**
    * Returns true if the given element is selectable.
    *
    * @param {Element} element
    *   The element to test.
    * @returns {boolean}
--- a/browser/components/urlbar/tests/UrlbarTestUtils.jsm
+++ b/browser/components/urlbar/tests/UrlbarTestUtils.jsm
@@ -838,24 +838,26 @@ class TestProvider extends UrlbarProvide
    */
   constructor({
     results,
     name = Math.floor(Math.random() * 100000),
     type = UrlbarUtils.PROVIDER_TYPE.PROFILE,
     priority = 0,
     addTimeout = 0,
     onCancel = null,
+    onSelection = null,
   } = {}) {
     super();
     this._results = results;
     this._name = name;
     this._type = type;
     this._priority = priority;
     this._addTimeout = addTimeout;
     this._onCancel = onCancel;
+    this._onSelection = onSelection;
   }
   get name() {
     return "TestProvider" + this._name;
   }
   get type() {
     return this._type;
   }
   getPriority(context) {
@@ -878,11 +880,17 @@ class TestProvider extends UrlbarProvide
       }
     }
   }
   cancelQuery(context) {
     if (this._onCancel) {
       this._onCancel();
     }
   }
+
+  onSelection(result, element) {
+    if (this._onSelection) {
+      this._onSelection(result, element);
+    }
+  }
 }
 
 UrlbarTestUtils.TestProvider = TestProvider;
--- a/browser/components/urlbar/tests/browser/browser.ini
+++ b/browser/components/urlbar/tests/browser/browser.ini
@@ -137,16 +137,17 @@ support-files =
 [browser_redirect_error.js]
 support-files = redirect_error.sjs
 [browser_remoteness_switch.js]
 run-if = e10s
 [browser_remotetab.js]
 [browser_remove_match.js]
 [browser_removeUnsafeProtocolsFromURLBarPaste.js]
 [browser_restoreEmptyInput.js]
+[browser_result_onSelection.js]
 [browser_resultSpan.js]
 [browser_retainedResultsOnFocus.js]
 [browser_revert.js]
 [browser_searchFunction.js]
 [browser_searchMode_alias_replacement.js]
 support-files =
   searchSuggestionEngine.xml
   searchSuggestionEngine.sjs
new file mode 100644
--- /dev/null
+++ b/browser/components/urlbar/tests/browser/browser_result_onSelection.js
@@ -0,0 +1,55 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test() {
+  let results = [
+    new UrlbarResult(
+      UrlbarUtils.RESULT_TYPE.URL,
+      UrlbarUtils.RESULT_SOURCE.HISTORY,
+      { url: "http://mozilla.org/1" }
+    ),
+    new UrlbarResult(
+      UrlbarUtils.RESULT_TYPE.URL,
+      UrlbarUtils.RESULT_SOURCE.HISTORY,
+      { url: "http://mozilla.org/2" }
+    ),
+    new UrlbarResult(
+      UrlbarUtils.RESULT_TYPE.TIP,
+      UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
+      {
+        text: "This is a test tip.",
+        buttonText: "Done",
+        type: "test",
+        helpUrl: "about:about",
+      }
+    ),
+  ];
+
+  results[0].heuristic = true;
+
+  let selectionCount = 0;
+  let provider = new UrlbarTestUtils.TestProvider({
+    results,
+    priority: 1,
+    onSelection: (result, element) => {
+      selectionCount++;
+    },
+  });
+  UrlbarProvidersManager.registerProvider(provider);
+
+  await UrlbarTestUtils.promiseAutocompleteResultPopup({
+    window,
+    value: "test",
+  });
+
+  let oneOffs = UrlbarTestUtils.getOneOffSearchButtons(window);
+
+  while (!oneOffs.selectedButton) {
+    EventUtils.synthesizeKey("KEY_ArrowDown");
+  }
+
+  Assert.equal(selectionCount, 4, "We selected the four elements in the view.");
+  UrlbarProvidersManager.unregisterProvider(provider);
+});