Bug 1545394 - Keep stale rows in the view while receiving new results. r=mak
authorDão Gottwald <dao@mozilla.com>
Fri, 19 Apr 2019 20:09:13 +0000
changeset 470262 29c131c2fd402f750064800c41037f534cea5cfd
parent 470261 0905d8963ffab547436a67d3e7d5597fb1fd4846
child 470263 a9a59b71e96aa44e602d8b5bd6144b3c0cfed172
push id83629
push userdgottwald@mozilla.com
push dateFri, 19 Apr 2019 20:32:14 +0000
treeherderautoland@29c131c2fd40 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs1545394
milestone68.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 1545394 - Keep stale rows in the view while receiving new results. r=mak Differential Revision: https://phabricator.services.mozilla.com/D28049
browser/components/urlbar/UrlbarView.jsm
--- a/browser/components/urlbar/UrlbarView.jsm
+++ b/browser/components/urlbar/UrlbarView.jsm
@@ -103,19 +103,17 @@ class UrlbarView {
   /**
    * @returns {UrlbarResult}
    *   The currently selected result.
    */
   get selectedResult() {
     if (!this.isOpen || !this._selected) {
       return null;
     }
-
-    let resultIndex = this._selected.getAttribute("resultIndex");
-    return this._queryContext.results[resultIndex];
+    return this._selected.result;
   }
 
   /**
    * Gets the result for the index.
    * @param {number} index
    *   The index to look up.
    * @returns {UrlbarResult}
    */
@@ -188,43 +186,32 @@ class UrlbarView {
    */
   close(cancelReason) {
     this.controller.cancelQuery(cancelReason);
     this.panel.hidePopup();
   }
 
   // UrlbarController listener methods.
   onQueryStarted(queryContext) {
-    this._rows.style.minHeight = this._getBoundsWithoutFlushing(this._rows).height + "px";
+    this._startRemoveStaleRowsTimer();
   }
 
   onQueryCancelled(queryContext) {
-    // Nothing.
+    this._cancelRemoveStaleRowsTimer();
   }
 
   onQueryFinished(queryContext) {
-    this._rows.style.minHeight = "";
+    this._cancelRemoveStaleRowsTimer();
+    this._removeStaleRows();
   }
 
   onQueryResults(queryContext) {
     this._queryContext = queryContext;
 
-    while (this._rows.children.length > queryContext.results.length) {
-      this._rows.lastElementChild.remove();
-    }
-    let resultIndex = 0;
-    for (let row of this._rows.children) {
-      this._updateRow(row, resultIndex);
-      resultIndex++;
-    }
-    for (; resultIndex < queryContext.results.length; resultIndex++) {
-      let row = this._createRow();
-      this._updateRow(row, resultIndex);
-      this._rows.appendChild(row);
-    }
+    this._updateResults(queryContext);
 
     let isFirstPreselectedResult = false;
     if (queryContext.lastResultCount == 0) {
       if (queryContext.preselected) {
         isFirstPreselectedResult = true;
         this._selectItem(this._rows.firstElementChild, {
           updateInput: false,
           setAccessibleFocus: false,
@@ -387,16 +374,34 @@ class UrlbarView {
     this.panel.style.marginInlineStart = px(this.window.RTL_UI ?
       inputRect.right - documentRect.right :
       documentRect.left - inputRect.left);
     this.panel.style.marginTop = px(inputRect.top - toolbarRect.top);
 
     this.panel.openPopup(this.input.textbox, "after_start");
   }
 
+  _updateResults(queryContext) {
+    let results = queryContext.results;
+    let i = 0;
+    for (let row of this._rows.children) {
+      if (i < results.length) {
+        this._updateRow(row, results[i]);
+      } else {
+        row.setAttribute("stale", "true");
+      }
+      i++;
+    }
+    for (; i < results.length; i++) {
+      let row = this._createRow();
+      this._updateRow(row, results[i]);
+      this._rows.appendChild(row);
+    }
+  }
+
   _createRow() {
     let item = this._createElement("div");
     item.className = "urlbarView-row";
     item.setAttribute("role", "option");
     item._elements = new Map;
 
     let content = this._createElement("span");
     content.className = "urlbarView-row-inner";
@@ -433,18 +438,20 @@ class UrlbarView {
     let url = this._createElement("span");
     url.className = "urlbarView-secondary urlbarView-url";
     content.appendChild(url);
     item._elements.set("url", url);
 
     return item;
   }
 
-  _updateRow(item, resultIndex) {
-    let result = this._queryContext.results[resultIndex];
+  _updateRow(item, result) {
+    let resultIndex = this._queryContext.results.indexOf(result);
+    item.result = result;
+    item.removeAttribute("stale");
     item.id = "urlbarView-row-" + resultIndex;
     item.setAttribute("resultIndex", resultIndex);
 
     if (result.type == UrlbarUtils.RESULT_TYPE.SEARCH &&
         !result.payload.isKeywordOffer) {
       item.setAttribute("type", "search");
     } else if (result.type == UrlbarUtils.RESULT_TYPE.REMOTE_TAB) {
       item.setAttribute("type", "remotetab");
@@ -515,38 +522,58 @@ class UrlbarView {
       this._addTextContentWithHighlights(url, result.payload.displayUrl,
                                          result.payloadHighlights.displayUrl || []);
     } else {
       url.textContent = "";
     }
     item._elements.get("action").textContent = action;
   }
 
+  _removeStaleRows() {
+    let row = this._rows.lastElementChild;
+    while (row) {
+      let next = row.previousElementSibling;
+      if (row.hasAttribute("stale")) {
+        row.remove();
+      }
+      row = next;
+    }
+  }
+  
+  _startRemoveStaleRowsTimer() {
+    this._removeStaleRowsTimer = this.window.setTimeout(() => {
+      this._removeStaleRowsTimer = null;
+      this._removeStaleRows();
+    }, 200);
+  }
+
+  _cancelRemoveStaleRowsTimer() {
+    if (this._removeStaleRowsTimer) {
+      this.window.clearTimeout(this._removeStaleRowsTimer);
+      this._removeStaleRowsTimer = null;
+    }
+  }
+
   _selectItem(item, {
     updateInput = true,
     setAccessibleFocus = true,
   } = {}) {
     if (this._selected) {
       this._selected.toggleAttribute("selected", false);
       this._selected.removeAttribute("aria-selected");
     }
     if (item) {
       item.toggleAttribute("selected", true);
       item.setAttribute("aria-selected", "true");
     }
     this._setAccessibleFocus(setAccessibleFocus && item);
     this._selected = item;
 
     if (updateInput) {
-      let result = null;
-      if (item) {
-        let resultIndex = item.getAttribute("resultIndex");
-        result = this._queryContext.results[resultIndex];
-      }
-      this.input.setValueFromResult(result);
+      this.input.setValueFromResult(item && item.result);
     }
   }
 
   _setAccessibleFocus(item) {
     if (item) {
       this.input.inputField.setAttribute("aria-activedescendant", item.id);
     } else {
       this.input.inputField.removeAttribute("aria-activedescendant");