Bug 1525269 - Implement UrlbarView::selectedIndex. r=Standard8
authorDão Gottwald <dao@mozilla.com>
Tue, 12 Feb 2019 14:53:22 +0000
changeset 458737 99ec4f94c2fe
parent 458736 fa4298251cf2
child 458738 05ec02aaa6bb
push id35548
push useropoprus@mozilla.com
push dateWed, 13 Feb 2019 09:48:26 +0000
treeherdermozilla-central@93e37c529818 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersStandard8
bugs1525269
milestone67.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 1525269 - Implement UrlbarView::selectedIndex. r=Standard8 Differential Revision: https://phabricator.services.mozilla.com/D19472
browser/components/urlbar/UrlbarView.jsm
browser/components/urlbar/tests/UrlbarTestUtils.jsm
--- a/browser/components/urlbar/UrlbarView.jsm
+++ b/browser/components/urlbar/UrlbarView.jsm
@@ -59,16 +59,41 @@ class UrlbarView {
   /**
    * @returns {boolean}
    *   Whether the panel is open.
    */
   get isOpen() {
     return this.panel.state == "open" || this.panel.state == "showing";
   }
 
+  get selectedIndex() {
+    if (!this.isOpen || !this._selected) {
+      return -1;
+    }
+    return parseInt(this._selected.getAttribute("resultIndex"));
+  }
+
+  set selectedIndex(val) {
+    if (!this.isOpen) {
+      throw new Error("UrlbarView: Cannot select an item if the view isn't open.");
+    }
+
+    if (val < 0) {
+      this._selectItem(null);
+      return val;
+    }
+
+    let items = this._rows.children;
+    if (val >= items.length) {
+      throw new Error(`UrlbarView: Index ${val} is out of bounds.`);
+    }
+    this._selectItem(items[val]);
+    return val;
+  }
+
   /**
    * @returns {UrlbarResult}
    *   The currently selected result.
    */
   get selectedResult() {
     if (!this.isOpen || !this._selected) {
       return null;
     }
@@ -85,38 +110,27 @@ class UrlbarView {
    *   Set to true to select the previous item. By default the next item
    *   will be selected.
    */
   selectNextItem({reverse = false} = {}) {
     if (!this.isOpen) {
       throw new Error("UrlbarView: Cannot select an item if the view isn't open.");
     }
 
-    // TODO: handle one-off search buttons
+    // TODO bug 1527260: handle one-off search buttons
 
     let row;
     if (reverse) {
       row = (this._selected && this._selected.previousElementSibling) ||
             this._rows.lastElementChild;
     } else {
       row = (this._selected && this._selected.nextElementSibling) ||
             this._rows.firstElementChild;
     }
-
-    if (this._selected) {
-      this._selected.toggleAttribute("selected", false);
-    }
-    this._selected = row;
-    row.toggleAttribute("selected", true);
-
-    let resultIndex = row.getAttribute("resultIndex");
-    let result = this._queryContext.results[resultIndex];
-    if (result) {
-      this.input.setValueFromResult(result);
-    }
+    this._selectItem(row);
   }
 
   /**
    * Closes the autocomplete results popup.
    */
   close() {
     this.panel.hidePopup();
   }
@@ -139,30 +153,27 @@ class UrlbarView {
 
     let fragment = this.document.createDocumentFragment();
     for (let resultIndex in queryContext.results) {
       fragment.appendChild(this._createRow(resultIndex));
     }
 
     if (queryContext.lastResultCount == 0) {
       if (queryContext.preselected) {
-        this._selected = fragment.firstElementChild;
-        this._selected.toggleAttribute("selected", true);
-      } else if (this._selected) {
+        this._selectItem(fragment.firstElementChild);
+      } else {
         // Clear the selection when we get a new set of results.
-        this._selected.toggleAttribute("selected", false);
-        this._selected = null;
+        this._selectItem(null);
       }
     } else if (this._selected) {
       // Ensure the selection is stable.
       // TODO bug 1523602: the selection should stay on the node that had it, if
       // it's still in the current result set.
       let resultIndex = this._selected.getAttribute("resultIndex");
-      this._selected = fragment.children[resultIndex];
-      this._selected.toggleAttribute("selected", true);
+      this._selectItem(fragment.children[resultIndex]);
     }
 
     // TODO bug 1523602: For now, clear the results for each set received.
     // We should be updating the existing list instead.
     this._rows.textContent = "";
     this._rows.appendChild(fragment);
 
     this._openPanel();
@@ -191,21 +202,18 @@ class UrlbarView {
     }
 
     // Select the row at the same index, if possible.
     let newSelectionIndex = index;
     if (index >= this._queryContext.results.length) {
       newSelectionIndex = this._queryContext.results.length - 1;
     }
     if (newSelectionIndex >= 0) {
-      this._selected = this._rows.children[newSelectionIndex];
-      this._selected.setAttribute("selected", true);
+      this.selectedIndex = newSelectionIndex;
     }
-
-    this.input.setValueFromResult(this._queryContext.results[newSelectionIndex]);
   }
 
   /**
    * Passes DOM events for the view to the _on_<event type> methods.
    * @param {Event} event
    *   DOM event from the <view>.
    */
   handleEvent(event) {
@@ -397,16 +405,38 @@ class UrlbarView {
     }
     if (url) {
       content.appendChild(url);
     }
 
     return item;
   }
 
+  _selectItem(item, updateInput = true) {
+    if (this._selected) {
+      this._selected.toggleAttribute("selected", false);
+      this._selected = null;
+    }
+
+    if (!item) {
+      return;
+    }
+    this._selected = item;
+    item.toggleAttribute("selected", true);
+
+    if (!updateInput) {
+      return;
+    }
+    let resultIndex = item.getAttribute("resultIndex");
+    let result = this._queryContext.results[resultIndex];
+    if (result) {
+      this.input.setValueFromResult(result);
+    }
+  }
+
   /**
    * Adds text content to a node, placing substrings that should be highlighted
    * inside <em> nodes.
    *
    * @param {Node} parentNode
    *   The text content will be added to this node.
    * @param {string} textContent
    *   The text content to give the node.
@@ -460,23 +490,18 @@ class UrlbarView {
       // Ignore right clicks.
       return;
     }
 
     let row = event.target;
     while (!row.classList.contains("urlbarView-row")) {
       row = row.parentNode;
     }
-    let resultIndex = row.getAttribute("resultIndex");
-    if (this._selected) {
-      this._selected.toggleAttribute("selected", false);
-    }
-    this._selected = this._rows.children[resultIndex];
-    this._selected.toggleAttribute("selected", true);
-    this.controller.speculativeConnect(this._queryContext, resultIndex, "mousedown");
+    this._selectItem(row, false);
+    this.controller.speculativeConnect(this._queryContext, this.selectedIndex, "mousedown");
   }
 
   _on_mouseup(event) {
     if (event.button == 2) {
       // Ignore right clicks.
       return;
     }
 
--- a/browser/components/urlbar/tests/UrlbarTestUtils.jsm
+++ b/browser/components/urlbar/tests/UrlbarTestUtils.jsm
@@ -299,21 +299,18 @@ class UrlbarAbstraction {
     if (this.quantumbar) {
       return this.urlbar.view._selected || null;
     }
     return this.panel.selectedIndex >= 0 ?
       this.panel.richlistbox.itemChildren[this.panel.selectedIndex] : null;
   }
 
   getSelectedIndex() {
-    if (!this.quantumbar) {
-      return this.panel.selectedIndex;
-    }
-
-    return parseInt(this.urlbar.view._selected.getAttribute("resultIndex"));
+    return this.quantumbar ? this.urlbar.view.selectedIndex
+                           : this.panel.selectedIndex;
   }
 
   getResultCount() {
     return this.quantumbar ? this.urlbar.view._rows.children.length
                            : this.urlbar.controller.matchCount;
   }
 
   async getDetailsOfResultAt(index) {