Bug 1701193 - Don't place suggestedIndex results in the wrong spot during view updates. r=mak

This should fix flickering related to suggested indexes once and for all, knock
on wood. It fixes the view-update logic so that if we encounter a new
suggestedIndex result that can't be placed in the correct spot during the first
loop, we stop trying to update rows at that point and instead mark the remaining
rows as stale. Then we reach the loop that appends the remaining results, and we
append them all starting with the suggestedIndex result. Those results are
hidden at first so that the suggestedIndex result never appears in the wrong
spot.

This fixes bug 1701193 and the flickering problem I noted in bug 1701136. It
also fixes the flicker we have right now with the tab-to-search onboarding
result, where during the view update we briefly have 10 results in the view even
though there should only be 9 since the tab-to-search has a span of 2.

At first I had one big test file for this, but not surprisingly it timed out on
some try machines. So I split it up, moved the new files to a new directory, and
moved the two existing browser_updateRows tests there too.

The new tests all do two searches where the first search returns search
suggestions and the second returns URL results. It would be good to test the
opposite too but this patch is already big enough.

Differential Revision: https://phabricator.services.mozilla.com/D110365
This commit is contained in:
Drew Willcoxon 2021-04-01 19:26:43 +00:00
parent 6e45123d07
commit 3dd0ab83c1
11 changed files with 4202 additions and 75 deletions

View File

@ -909,18 +909,17 @@ class UrlbarView {
return true;
}
let row = this._rows.children[rowIndex];
if (result.hasSuggestedIndex) {
// Always allow a result with a suggested index to replace any other
// result. Otherwise it can briefly end up at some larger index due to
// the presence of visible stale rows. Then, if the user makes a
// selection before the stale-rows timer fires, we'll freeze the rows with
// the suggested-index row in the wrong spot.
return true;
if (result.hasSuggestedIndex != row.result.hasSuggestedIndex) {
// Don't replace a suggestedIndex result with a non-suggestedIndex result
// or vice versa.
return false;
}
if (row.result.hasSuggestedIndex) {
// Never allow a result without a suggested index to replace a result with
// a suggested index. If the suggested-index row is not stale, then it
// needs to remain in the same spot to avoid flicker.
if (
result.hasSuggestedIndex &&
result.suggestedIndex != row.result.suggestedIndex
) {
// Don't replace a suggestedIndex result with another suggestedIndex
// result if the suggestedIndex values are different.
return false;
}
let resultIsSearchSuggestion = this._resultIsSearchSuggestion(result);
@ -949,7 +948,9 @@ class UrlbarView {
let rowIndex = 0;
let resultIndex = 0;
let visibleSpanCount = 0;
let seenMisplacedSuggestedIndex = false;
let seenSearchSuggestion = false;
// We can have more rows than the visible ones.
for (
;
@ -957,25 +958,29 @@ class UrlbarView {
++rowIndex
) {
let row = this._rows.children[rowIndex];
let result = results[resultIndex];
if (
!seenSearchSuggestion &&
!row.result.heuristic &&
this._resultIsSearchSuggestion(row.result)
) {
seenSearchSuggestion = true;
}
if (this._rowCanUpdateToResult(rowIndex, result, seenSearchSuggestion)) {
// We can replace the row's current result with the new one.
this._updateRow(row, result);
resultIndex++;
} else {
// We can't reuse the row, so mark it stale, and we'll remove it later.
row.setAttribute("stale", "true");
}
if (this._isElementVisible(row)) {
visibleSpanCount += UrlbarUtils.getSpanForResult(row.result);
}
// As long as we haven't encountered a new suggestedIndex result that
// couldn't be placed in the right spot, continue updating rows.
if (!seenMisplacedSuggestedIndex) {
seenSearchSuggestion =
seenSearchSuggestion ||
(!row.result.heuristic && this._resultIsSearchSuggestion(row.result));
let result = results[resultIndex];
if (
this._rowCanUpdateToResult(rowIndex, result, seenSearchSuggestion)
) {
// We can replace the row's current result with the new one.
this._updateRow(row, result);
resultIndex++;
continue;
}
if (result.hasSuggestedIndex || row.result.hasSuggestedIndex) {
seenMisplacedSuggestedIndex = true;
}
}
row.setAttribute("stale", "true");
}
// Mark all the remaining rows as stale and update the visible span count.
@ -995,14 +1000,30 @@ class UrlbarView {
let row = this._createRow();
let result = results[resultIndex];
this._updateRow(row, result);
// We still need to check whether the new result has a suggestedIndex and
// can't be placed in the right spot.
if (!seenMisplacedSuggestedIndex && result.hasSuggestedIndex) {
let targetIndex =
result.suggestedIndex >= 0
? Math.min(this._rows.children.length, result.suggestedIndex)
: Math.max(0, this._rows.children.length + result.suggestedIndex);
if (this._rows.children.length != targetIndex) {
seenMisplacedSuggestedIndex = true;
}
}
let newVisibleSpanCount =
visibleSpanCount + UrlbarUtils.getSpanForResult(result);
if (newVisibleSpanCount <= queryContext.maxResults) {
if (
newVisibleSpanCount <= queryContext.maxResults &&
!seenMisplacedSuggestedIndex
) {
// The new row can be visible.
visibleSpanCount = newVisibleSpanCount;
} else {
// The new row must be hidden at first because the view is already
// showing maxResults spans. We'll show it when stale rows are removed.
// showing maxResults spans, or we encountered a new suggestedIndex
// result that couldn't be placed in the right spot. We'll show it when
// stale rows are removed.
this._setRowVisibility(row, false);
}
this._rows.appendChild(row);

View File

@ -42,6 +42,7 @@ TESTING_JS_MODULES += [
BROWSER_CHROME_MANIFESTS += [
"tests/browser-proton/browser.ini",
"tests/browser-tips/browser.ini",
"tests/browser-updateResults/browser.ini",
"tests/browser/browser.ini",
"tests/ext/browser/browser.ini",
]

View File

@ -0,0 +1,14 @@
# 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/.
[DEFAULT]
support-files =
head.js
[browser_appendSpanCount.js]
[browser_reuse.js]
[browser_suggestedIndex_4_search_4_url.js]
[browser_suggestedIndex_4_search_10_url.js]
[browser_suggestedIndex_10_search_4_url.js]
[browser_suggestedIndex_10_search_10_url.js]

View File

@ -9,31 +9,6 @@
"use strict";
XPCOMUtils.defineLazyModuleGetters(this, {
UrlbarView: "resource:///modules/UrlbarView.jsm",
});
add_task(async function init() {
await PlacesUtils.history.clear();
await PlacesUtils.bookmarks.eraseEverything();
// Make absolutely sure the panel stays open during the test. There are
// spurious blurs on WebRender TV tests as the test starts that cause the
// panel to close and the query to be canceled, resulting in intermittent
// failures without this.
await SpecialPowers.pushPrefEnv({
set: [["ui.popup.disable_autohide", true]],
});
// Increase the timeout of the remove-stale-rows timer so that it doesn't
// interfere with the test.
let originalRemoveStaleRowsTimeout = UrlbarView.removeStaleRowsTimeout;
UrlbarView.removeStaleRowsTimeout = 30000;
registerCleanupFunction(() => {
UrlbarView.removeStaleRowsTimeout = originalRemoveStaleRowsTimeout;
});
});
add_task(async function viewUpdateAppendHidden() {
// We'll use this test provider to test specific results. We assume that
// history and bookmarks have been cleared (by init() above).
@ -203,17 +178,3 @@ add_task(async function viewUpdateAppendHidden() {
// more tasks to this test. It's harmless to call more than once.
UrlbarProvidersManager.unregisterProvider(provider);
});
/**
* A test provider that doesn't finish startQuery() until `finishQueryPromise`
* is resolved.
*/
class DelayingTestProvider extends UrlbarTestUtils.TestProvider {
finishQueryPromise = null;
async startQuery(context, addCallback) {
for (let result of this._results) {
addCallback(this, result);
}
await this.finishQueryPromise;
}
}

View File

@ -7,11 +7,6 @@
let TEST_BASE_URL = "http://example.com/";
add_task(async function init() {
await PlacesUtils.history.clear();
await PlacesUtils.bookmarks.eraseEverything();
});
// A URL result is replaced with a tip result and then vice versa.
add_task(async function urlToTip() {
// Add some visits that will be matched by a "test" search string.

View File

@ -0,0 +1,626 @@
/* 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/. */
// This test checks row visibility during view updates when rows with suggested
// indexes are added and removed. Each task performs two searches: Search 1
// returns 10 results with search suggestions, and search 2 returns 4 results
// with URL results.
"use strict";
// Search 1:
// 10 results, no suggestedIndex
// Search 2:
// 4 results including suggestedIndex = 1
// Expected visible rows during update:
// 10 original rows with no changes
// add_task(async function() {
// await doSuggestedIndexTest({
add_suggestedIndex_task({
search1: {
otherCount: 10,
viewCount: 10,
},
search2: {
otherCount: 2,
suggestedIndex: 1,
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{ count: 9, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
hidden: true,
},
{ count: 2, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 10 results, no suggestedIndex
// Search 2:
// 4 results including suggestedIndex = 2
// Expected visible rows during update:
// 10 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 10,
viewCount: 10,
},
search2: {
otherCount: 2,
suggestedIndex: 2,
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{ count: 9, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 2,
hidden: true,
},
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 10 results, no suggestedIndex
// Search 2:
// 4 results including suggestedIndex = 9
// Expected visible rows during update:
// 10 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 10,
viewCount: 10,
},
search2: {
otherCount: 2,
suggestedIndex: 9,
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{ count: 9, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 2, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 9,
hidden: true,
},
],
});
// Search 1:
// 10 results, no suggestedIndex
// Search 2:
// 4 results including suggestedIndex = -1
// Expected visible rows during update:
// 10 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 10,
viewCount: 10,
},
search2: {
otherCount: 2,
suggestedIndex: -1,
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{ count: 9, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 2, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
hidden: true,
},
],
});
// Search 1:
// 10 results, no suggestedIndex
// Search 2:
// 4 results including suggestedIndex = -2
// Expected visible rows during update:
// 10 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 10,
viewCount: 10,
},
search2: {
otherCount: 2,
suggestedIndex: -2,
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{ count: 9, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -2,
hidden: true,
},
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 10 results including suggestedIndex = 1
// Search 2:
// 4 results including suggestedIndex = 1
// Expected visible rows during update:
// 10 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 10,
suggestedIndex: 1,
viewCount: 10,
},
search2: {
otherCount: 2,
suggestedIndex: 1,
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
},
{ count: 8, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 2, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 10 results including suggestedIndex = 1
// Search 2:
// 4 results including suggestedIndex = 9
// Expected visible rows during update:
// 10 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 10,
suggestedIndex: 1,
viewCount: 10,
},
search2: {
otherCount: 2,
suggestedIndex: 9,
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
stale: true,
},
{ count: 8, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 2, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 9,
hidden: true,
},
],
});
// Search 1:
// 10 results including suggestedIndex = 1
// Search 2:
// 4 results including suggestedIndex = -1
// Expected visible rows during update:
// 10 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 10,
suggestedIndex: 1,
viewCount: 10,
},
search2: {
otherCount: 2,
suggestedIndex: -1,
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
stale: true,
},
{ count: 8, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 2, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
hidden: true,
},
],
});
// Search 1:
// 10 results including suggestedIndex = 1
// Search 2:
// 4 results including suggestedIndex = -3
// Expected visible rows during update:
// 10 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 10,
suggestedIndex: 1,
viewCount: 10,
},
search2: {
otherCount: 2,
suggestedIndex: -3,
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
stale: true,
},
{ count: 8, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -3,
hidden: true,
},
{ count: 2, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 10 results including suggestedIndex = 9
// Search 2:
// 4 results including suggestedIndex = 1
// Expected visible rows during update:
// 10 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 10,
suggestedIndex: 9,
viewCount: 10,
},
search2: {
otherCount: 2,
suggestedIndex: 1,
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{ count: 8, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 9,
stale: true,
},
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
hidden: true,
},
{ count: 2, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 10 results including suggestedIndex = 9
// Search 2:
// 4 results including suggestedIndex = 9
// Expected visible rows during update:
// 10 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 10,
suggestedIndex: 9,
viewCount: 10,
},
search2: {
otherCount: 2,
suggestedIndex: 9,
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{ count: 8, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 9,
stale: true,
},
{ count: 2, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 9,
hidden: true,
},
],
});
// Search 1:
// 10 results including suggestedIndex = -1
// Search 2:
// 4 results including suggestedIndex = 1
// Expected visible rows during update:
// 10 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 10,
suggestedIndex: -1,
viewCount: 10,
},
search2: {
otherCount: 2,
suggestedIndex: 1,
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{ count: 8, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
stale: true,
},
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
hidden: true,
},
{ count: 2, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 10 results including suggestedIndex = -1
// Search 2:
// 4 results including suggestedIndex = 9
// Expected visible rows during update:
// 10 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 10,
suggestedIndex: -1,
viewCount: 10,
},
search2: {
otherCount: 2,
suggestedIndex: 9,
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{ count: 8, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
stale: true,
},
{ count: 2, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 9,
hidden: true,
},
],
});
// Search 1:
// 10 results including suggestedIndex = -1
// Search 2:
// 4 results including suggestedIndex = -1
// Expected visible rows during update:
// 10 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 10,
suggestedIndex: -1,
viewCount: 10,
},
search2: {
otherCount: 2,
suggestedIndex: -1,
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{ count: 8, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
stale: true,
},
{ count: 2, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
hidden: true,
},
],
});
// Search 1:
// 10 results, no suggestedIndex
// Search 2:
// 4 results including suggestedIndex = 1 and suggestedIndex = -1
// Expected visible rows during update:
// 10 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 10,
viewCount: 10,
},
search2: {
otherCount: 1,
suggestedIndexes: [1, -1],
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{ count: 9, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
hidden: true,
},
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
hidden: true,
},
],
});
// Search 1:
// 10 results including suggestedIndex = 1
// Search 2:
// 4 results including suggestedIndex = 1 and suggestedIndex = -1
// Expected visible rows during update:
// 10 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 10,
suggestedIndex: 1,
viewCount: 10,
},
search2: {
otherCount: 1,
suggestedIndexes: [1, -1],
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
},
{ count: 8, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
hidden: true,
},
],
});
// Search 1:
// 10 results including suggestedIndex = -1
// Search 2:
// 4 results including suggestedIndex = 1 and suggestedIndex = -1
// Expected visible rows during update:
// 10 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 10,
suggestedIndex: -1,
viewCount: 10,
},
search2: {
otherCount: 1,
suggestedIndexes: [1, -1],
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{ count: 8, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
stale: true,
},
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
hidden: true,
},
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
hidden: true,
},
],
});
// Search 1:
// 9 results including suggestedIndex = 1 with resultSpan = 2
// Search 2:
// 4 results including:
// suggestedIndex = 1 with resultSpan = 2
// suggestedIndex = -1
// Expected visible rows during update:
// 9 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 10,
suggestedIndexes: [[1, 2]],
viewCount: 9,
},
search2: {
otherCount: 1,
suggestedIndexes: [[1, 2], -1],
viewCount: 4,
},
duringUpdate: [
{ count: 1 },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
resultSpan: 2,
},
{ count: 7, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
hidden: true,
},
],
});

View File

@ -0,0 +1,956 @@
/* 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/. */
// This test checks row visibility during view updates when rows with suggested
// indexes are added and removed. Each task performs two searches: Search 1
// returns 4 results with search suggestions, and search 2 returns 10 results
// with URL results.
"use strict";
// Search 1:
// 4 results, no suggestedIndex
// Search 2:
// 10 results including suggestedIndex = 1
// Expected visible rows during update:
// 4 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 3,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: 1,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 3, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
hidden: true,
},
{ count: 8, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 4 results, no suggestedIndex
// Search 2:
// 10 results including suggestedIndex = 2
// Expected visible rows during update:
// 4 original rows + 1 new row (the one before the suggestedIndex row)
add_suggestedIndex_task({
search1: {
otherCount: 3,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: 2,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 3, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 2,
hidden: true,
},
{ count: 7, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 4 results, no suggestedIndex
// Search 2:
// 10 results including suggestedIndex = 4
// Expected visible rows during update:
// 4 original rows + 3 new rows (the ones before the suggestedIndex row)
add_suggestedIndex_task({
search1: {
otherCount: 3,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: 4,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 3, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 3, type: UrlbarUtils.RESULT_TYPE.URL },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 4,
hidden: true,
},
{ count: 5, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 4 results, no suggestedIndex
// Search 2:
// 10 results including suggestedIndex = 6
// Expected visible rows during update:
// 4 original rows + 5 new rows (the ones before the suggestedIndex row)
add_suggestedIndex_task({
search1: {
otherCount: 3,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: 6,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 3, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 5, type: UrlbarUtils.RESULT_TYPE.URL },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 6,
hidden: true,
},
{ count: 3, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 4 results, no suggestedIndex
// Search 2:
// 10 results including suggestedIndex = 8
// Expected visible rows during update:
// 4 original rows + 6 new rows (some of the ones before the suggestedIndex)
add_suggestedIndex_task({
search1: {
otherCount: 3,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: 8,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 3, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 6, type: UrlbarUtils.RESULT_TYPE.URL },
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 8,
hidden: true,
},
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 4 results, no suggestedIndex
// Search 2:
// 10 results including suggestedIndex = 9
// Expected visible rows during update:
// 4 original rows + 6 new rows (some of the ones before the suggestedIndex)
add_suggestedIndex_task({
search1: {
otherCount: 3,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: 9,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 3, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 6, type: UrlbarUtils.RESULT_TYPE.URL },
{ count: 2, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 9,
hidden: true,
},
],
});
// Search 1:
// 4 results, no suggestedIndex
// Search 2:
// 10 results including suggestedIndex = -1
// Expected visible rows during update:
// 4 original rows + 6 new rows
add_suggestedIndex_task({
search1: {
otherCount: 3,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: -1,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 3, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 6, type: UrlbarUtils.RESULT_TYPE.URL },
{ count: 2, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
hidden: true,
},
],
});
// Search 1:
// 4 results, no suggestedIndex
// Search 2:
// 10 results including suggestedIndex = -2
// Expected visible rows during update:
// 4 original rows + 6 new rows
add_suggestedIndex_task({
search1: {
otherCount: 3,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: -2,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 3, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 6, type: UrlbarUtils.RESULT_TYPE.URL },
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -2,
hidden: true,
},
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 4 results, no suggestedIndex
// Search 2:
// 10 results including suggestedIndex = -4
// Expected visible rows during update:
// 4 original rows + 5 new rows (the ones before the suggestedIndex row)
add_suggestedIndex_task({
search1: {
otherCount: 3,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: -4,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 3, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 5, type: UrlbarUtils.RESULT_TYPE.URL },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -4,
hidden: true,
},
{ count: 3, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 4 results, no suggestedIndex
// Search 2:
// 10 results including suggestedIndex = -6
// Expected visible rows during update:
// 4 original rows + 3 new rows (the ones before the suggestedIndex row)
add_suggestedIndex_task({
search1: {
otherCount: 3,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: -6,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 3, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 3, type: UrlbarUtils.RESULT_TYPE.URL },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -6,
hidden: true,
},
{ count: 5, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 4 results, no suggestedIndex
// Search 2:
// 10 results including suggestedIndex = -8
// Expected visible rows during update:
// 4 original rows + 1 new rows (the ones before the suggestedIndex row)
add_suggestedIndex_task({
search1: {
otherCount: 3,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: -8,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 3, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -8,
hidden: true,
},
{ count: 7, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 4 results including suggestedIndex = 1
// Search 2:
// 10 results including suggestedIndex = 1
// Expected visible rows during update:
// 4 original rows + 6 new rows
add_suggestedIndex_task({
search1: {
otherCount: 2,
suggestedIndex: 1,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: 1,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
},
{ count: 2, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 6, type: UrlbarUtils.RESULT_TYPE.URL },
{ count: 2, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 4 results including suggestedIndex = 1
// Search 2:
// 10 results including suggestedIndex = 2
// Expected visible rows during update:
// 4 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 2,
suggestedIndex: 1,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: 2,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
stale: true,
},
{ count: 2, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 2,
hidden: true,
},
{ count: 7, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 4 results including suggestedIndex = 1
// Search 2:
// 10 results including suggestedIndex = 9
// Expected visible rows during update:
// 4 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 2,
suggestedIndex: 1,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: 9,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
stale: true,
},
{ count: 2, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 8, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 9,
hidden: true,
},
],
});
// Search 1:
// 4 results including suggestedIndex = 1
// Search 2:
// 10 results including suggestedIndex = -1
// Expected visible rows during update:
// 4 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 2,
suggestedIndex: 1,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: -1,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
stale: true,
},
{ count: 2, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 8, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
hidden: true,
},
],
});
// Search 1:
// 4 results including suggestedIndex = 1
// Search 2:
// 10 results including suggestedIndex = -2
// Expected visible rows during update:
// 4 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 2,
suggestedIndex: 1,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: -2,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
stale: true,
},
{ count: 2, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 7, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -2,
hidden: true,
},
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 4 results including suggestedIndex = 1
// Search 2:
// 10 results including suggestedIndex = -9
// Expected visible rows during update:
// 4 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 2,
suggestedIndex: 1,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: -9,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
stale: true,
},
{ count: 2, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -9,
hidden: true,
},
{ count: 8, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 4 results including suggestedIndex = 9
// Search 2:
// 10 results including suggestedIndex = 1
// Expected visible rows during update:
// 4 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 2,
suggestedIndex: 9,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: 1,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 2, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 9,
stale: true,
},
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
hidden: true,
},
{ count: 8, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 4 results including suggestedIndex = 9
// Search 2:
// 10 results including suggestedIndex = 3
// Expected visible rows during update:
// 4 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 2,
suggestedIndex: 9,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: 3,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 2, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 9,
stale: true,
},
{ count: 2, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 3,
hidden: true,
},
{ count: 6, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 4 results including suggestedIndex = 9
// Search 2:
// 10 results including suggestedIndex = 9
// Expected visible rows during update:
// 4 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 2,
suggestedIndex: 9,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: 9,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 2, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 9,
stale: true,
},
{ count: 8, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 9,
hidden: true,
},
],
});
// Search 1:
// 4 results including suggestedIndex = 9
// Search 2:
// 10 results including suggestedIndex = -1
// Expected visible rows during update:
// 4 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 2,
suggestedIndex: 9,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: -1,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 2, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 9,
stale: true,
},
{ count: 8, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
hidden: true,
},
],
});
// Search 1:
// 4 results including suggestedIndex = 9
// Search 2:
// 10 results including suggestedIndex = -7
// Expected visible rows during update:
// 4 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 2,
suggestedIndex: 9,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: -7,
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 2, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 9,
stale: true,
},
{ count: 2, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -7,
hidden: true,
},
{ count: 6, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 4 results, no suggestedIndex
// Search 2:
// 10 results including suggestedIndex = 1 and suggestedIndex = -1
// Expected visible rows during update:
// 4 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 3,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndexes: [1, -1],
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 3, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
hidden: true,
},
{ count: 7, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
hidden: true,
},
],
});
// Search 1:
// 4 results including suggestedIndex = 1
// Search 2:
// 10 results including suggestedIndex = 1 and suggestedIndex = -1
// Expected visible rows during update:
// 4 original rows + 6 new rows (some of the ones before the suggestedIndex)
add_suggestedIndex_task({
search1: {
otherCount: 2,
suggestedIndex: 1,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndexes: [1, -1],
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
},
{ count: 2, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 6, type: UrlbarUtils.RESULT_TYPE.URL },
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
hidden: true,
},
],
});
// Search 1:
// 4 results including suggestedIndex = -1
// Search 2:
// 10 results including suggestedIndex = 1 and suggestedIndex = -1
// Expected visible rows during update:
// 4 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 2,
suggestedIndex: -1,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndexes: [1, -1],
viewCount: 10,
},
duringUpdate: [
{ count: 1 },
{ count: 2, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
stale: true,
},
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
hidden: true,
},
{ count: 7, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
hidden: true,
},
],
});
// Search 1:
// 4 results, no suggestedIndex
// Search 2:
// 9 results including suggestedIndex = 1 with resultSpan = 2
// Expected visible rows during update:
// 4 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 3,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndex: 1,
resultSpan: 2,
viewCount: 9,
},
duringUpdate: [
{ count: 1 },
{ count: 3, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
resultSpan: 2,
hidden: true,
},
{ count: 7, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
],
});
// Search 1:
// 4 results, no suggestedIndex
// Search 2:
// 9 results including:
// suggestedIndex = 1 with resultSpan = 2
// suggestedIndex = -1
// Expected visible rows during update:
// 4 original rows with no changes
add_suggestedIndex_task({
search1: {
otherCount: 3,
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndexes: [[1, 2], -1],
viewCount: 9,
},
duringUpdate: [
{ count: 1 },
{ count: 3, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
resultSpan: 2,
hidden: true,
},
{ count: 6, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
hidden: true,
},
],
});
// Search 1:
// 4 results including suggestedIndex = 1 with resultSpan = 2
// Search 2:
// 9 results including:
// suggestedIndex = 1 with resultSpan = 2
// suggestedIndex = -1
// Expected visible rows during update:
// 4 original rows + 5 new rows (some of the ones before the suggestedIndex)
add_suggestedIndex_task({
search1: {
otherCount: 2,
suggestedIndexes: [[1, 2]],
viewCount: 4,
},
search2: {
otherCount: 10,
suggestedIndexes: [[1, 2], -1],
viewCount: 9,
},
duringUpdate: [
{ count: 1 },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: 1,
resultSpan: 2,
},
{ count: 2, type: UrlbarUtils.RESULT_TYPE.SEARCH, stale: true },
{ count: 5, type: UrlbarUtils.RESULT_TYPE.URL },
{ count: 1, type: UrlbarUtils.RESULT_TYPE.URL, hidden: true },
{
count: 1,
type: UrlbarUtils.RESULT_TYPE.URL,
suggestedIndex: -1,
hidden: true,
},
],
});

View File

@ -0,0 +1,445 @@
/* 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/. */
// The files in this directory test UrlbarView._updateResults().
"use strict";
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
AppConstants: "resource://gre/modules/AppConstants.jsm",
PlacesTestUtils: "resource://testing-common/PlacesTestUtils.jsm",
UrlbarProvidersManager: "resource:///modules/UrlbarProvidersManager.jsm",
UrlbarResult: "resource:///modules/UrlbarResult.jsm",
UrlbarUtils: "resource:///modules/UrlbarUtils.jsm",
UrlbarView: "resource:///modules/UrlbarView.jsm",
});
XPCOMUtils.defineLazyGetter(this, "UrlbarTestUtils", () => {
const { UrlbarTestUtils: module } = ChromeUtils.import(
"resource://testing-common/UrlbarTestUtils.jsm"
);
module.init(this);
return module;
});
add_task(async function headInit() {
await PlacesUtils.history.clear();
await PlacesUtils.bookmarks.eraseEverything();
await SpecialPowers.pushPrefEnv({
set: [
// Make absolutely sure the panel stays open during the test. There are
// spurious blurs on WebRender TV tests as the test starts that cause the
// panel to close and the query to be canceled, resulting in intermittent
// failures without this.
["ui.popup.disable_autohide", true],
// Make sure maxRichResults is 10 for sanity.
["browser.urlbar.maxRichResults", 10],
],
});
// Increase the timeout of the remove-stale-rows timer so that it doesn't
// interfere with the tests.
let originalRemoveStaleRowsTimeout = UrlbarView.removeStaleRowsTimeout;
UrlbarView.removeStaleRowsTimeout = 30000;
registerCleanupFunction(() => {
UrlbarView.removeStaleRowsTimeout = originalRemoveStaleRowsTimeout;
});
});
/**
* A test provider that doesn't finish startQuery() until `finishQueryPromise`
* is resolved.
*/
class DelayingTestProvider extends UrlbarTestUtils.TestProvider {
finishQueryPromise = null;
async startQuery(context, addCallback) {
for (let result of this._results) {
addCallback(this, result);
}
await this.finishQueryPromise;
}
}
/**
* Makes a result with a suggested index.
*
* @param {number} suggestedIndex
* @param {number} resultSpan
* The result will have this span.
* @returns {UrlbarResult}
*/
function makeSuggestedIndexResult(suggestedIndex, resultSpan = 1) {
return Object.assign(
new UrlbarResult(
UrlbarUtils.RESULT_TYPE.URL,
UrlbarUtils.RESULT_SOURCE.HISTORY,
{
url: "http://example.com/si",
displayUrl: "http://example.com/si",
title: "suggested index",
}
),
{ suggestedIndex, resultSpan }
);
}
let gSuggestedIndexTaskIndex = 0;
/**
* Adds a suggestedIndex test task. See doSuggestedIndexTest() for params.
*
* @param {object} options
* See doSuggestedIndexTest().
*/
function add_suggestedIndex_task(options) {
if (!gSuggestedIndexTaskIndex) {
initSuggestedIndexTest();
}
let testIndex = gSuggestedIndexTaskIndex++;
let testName = "test_" + testIndex;
let testDesc = JSON.stringify(options);
let func = async () => {
info(`Running task at index ${testIndex}: ${testDesc}`);
await doSuggestedIndexTest(options);
};
Object.defineProperty(func, "name", { value: testName });
add_task(func);
}
/**
* Initializes suggestedIndex tests. You don't normally need to call this from
* your test because add_suggestedIndex_task() calls it automatically.
*/
function initSuggestedIndexTest() {
// These tests can time out on Mac TV WebRender just because they do so much,
// so request a longer timeout.
if (AppConstants.platform == "macosx") {
requestLongerTimeout(3);
}
registerCleanupFunction(() => {
gSuggestedIndexTaskIndex = 0;
});
}
/**
* Runs a suggestedIndex test. Performs two searches and checks the results just
* after the view update and after the second search finishes. The caller is
* responsible for passing in a description of what the rows should look like
* just after the view update finishes but before the second search finishes,
* i.e., before stale rows are removed and hidden rows are shown -- this is the
* `duringUpdate` param. The important thing this checks is that the rows with
* suggested indexes don't move around or appear in the wrong places.
*
* @param {number} search1.otherCount
* The number of results other than the heuristic and suggestedIndex results
* that the provider should return for search 1.
* @param {number} search1.viewCount
* The total number of results expected in the view after search 1 finishes,
* including the heuristic and suggestedIndex results.
* @param {number} [search1.suggestedIndex]
* If given, the provider will return a result with this suggested index for
* search 1.
* @param {number} [search1.resultSpan]
* If this and `search1.suggestedIndex` are given, then the suggestedIndex
* result for search 1 will have this resultSpan.
* @param {array} [search1.suggestedIndexes]
* If you want the provider to return more than one suggestedIndex result for
* search 1, then use this instead of `search1.suggestedIndex`. Each item in
* this array must be one of the following:
* * suggestedIndex value
* * [suggestedIndex, resultSpan] tuple
* @param {object} search2
* This object has the same properties as the `search1` object but it applies
* to the second search.
* @param {array} duringUpdate
* An array of expected row states during the view update. Each item in the
* array must be an object with the following properties:
* * {number} count
* The number of rows in the view to which this row state object applies.
* * {UrlbarUtils.RESULT_TYPE} type
* The expected type of the rows.
* * {number} [suggestedIndex]
* The expected suggestedIndex of the row.
* * {boolean} [stale]
* Whether the rows are expected to be stale. Defaults to false.
* * {boolean} [hidden]
* Whether the rows are expected to be hidden. Defaults to false.
*/
async function doSuggestedIndexTest({ search1, search2, duringUpdate }) {
// We'll use this test provider to test specific results.
let provider = new DelayingTestProvider();
UrlbarProvidersManager.registerProvider(provider);
registerCleanupFunction(() => {
UrlbarProvidersManager.unregisterProvider(provider);
});
let maxResults = UrlbarPrefs.get("maxRichResults");
let query = "test";
let queryStrings = [];
for (let i = 0; i < maxResults; i++) {
queryStrings.push(`${query} ${i}`);
}
// Set up the first search. First, add search suggestions to the provider.
provider._results = queryStrings.slice(0, search1.otherCount).map(
suggestion =>
new UrlbarResult(
UrlbarUtils.RESULT_TYPE.SEARCH,
UrlbarUtils.RESULT_SOURCE.SEARCH,
{
query,
suggestion,
lowerCaseSuggestion: suggestion.toLocaleLowerCase(),
engine: Services.search.defaultEngine.name,
}
)
);
// Set up `suggestedIndexes`. It's an array with [suggestedIndex, resultSpan]
// tuples.
if (!search1.suggestedIndexes) {
search1.suggestedIndexes = [];
}
search1.suggestedIndexes = search1.suggestedIndexes.map(value =>
typeof value == "number" ? [value, 1] : value
);
if (typeof search1.suggestedIndex == "number") {
search1.suggestedIndexes.push([
search1.suggestedIndex,
search1.resultSpan || 1,
]);
}
// Add suggestedIndex results to the provider.
for (let [suggestedIndex, resultSpan] of search1.suggestedIndexes) {
provider._results.push(
makeSuggestedIndexResult(suggestedIndex, resultSpan)
);
}
// Do the first search.
provider.finishQueryPromise = Promise.resolve();
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: query,
});
// Sanity check the results.
Assert.equal(
UrlbarTestUtils.getResultCount(window),
search1.viewCount,
"Row count after first search"
);
for (let [suggestedIndex, resultSpan] of search1.suggestedIndexes) {
let index =
suggestedIndex >= 0
? Math.min(suggestedIndex, search1.viewCount - 1)
: search1.viewCount + suggestedIndex;
let result = await UrlbarTestUtils.getDetailsOfResultAt(window, index);
Assert.equal(
result.element.row.result.suggestedIndex,
suggestedIndex,
"suggestedIndex after first search"
);
Assert.equal(
UrlbarUtils.getSpanForResult(result.element.row.result),
resultSpan,
"resultSpan after first search"
);
}
// Set up the second search. First, add history results to the provider.
provider._results = queryStrings.slice(0, search2.otherCount).map(str => {
let url = "http://example.com/" + str;
return new UrlbarResult(
UrlbarUtils.RESULT_TYPE.URL,
UrlbarUtils.RESULT_SOURCE.HISTORY,
{ url, title: str, displayUrl: url }
);
});
// Set up `suggestedIndexes`. It's an array with [suggestedIndex, resultSpan]
// tuples.
if (!search2.suggestedIndexes) {
search2.suggestedIndexes = [];
}
search2.suggestedIndexes = search2.suggestedIndexes.map(value =>
typeof value == "number" ? [value, 1] : value
);
if (typeof search2.suggestedIndex == "number") {
search2.suggestedIndexes.push([
search2.suggestedIndex,
search2.resultSpan || 1,
]);
}
// Add suggestedIndex results to the provider.
for (let [suggestedIndex, resultSpan] of search2.suggestedIndexes) {
provider._results.push(
makeSuggestedIndexResult(suggestedIndex, resultSpan)
);
}
// Don't allow the search to finish until we check the updated rows. We'll
// accomplish that by adding a mutation observer to observe completion of the
// update and delaying resolving the provider's finishQueryPromise.
let mutationPromise = new Promise(resolve => {
let lastRowState = duringUpdate[duringUpdate.length - 1];
let observer = new MutationObserver(mutations => {
observer.disconnect();
resolve();
});
// If the last row during the update is expected to become stale, wait for
// the stale attribute to be set on it. Otherwise new rows will be appended,
// so wait for them.
if (lastRowState.stale) {
let { children } = gURLBar.view._rows;
observer.observe(children[children.length - 1], { attributes: true });
} else {
observer.observe(gURLBar.view._rows, { childList: true });
}
});
// Now do the second search but don't wait for it to finish.
let resolveQuery;
provider.finishQueryPromise = new Promise(
resolve => (resolveQuery = resolve)
);
let queryPromise = UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: query,
});
// Wait for the update to finish.
await mutationPromise;
// Check the rows. We can't use UrlbarTestUtils.getDetailsOfResultAt() here
// because it waits for the search to finish.
Assert.equal(
gURLBar.view._rows.children.length,
duringUpdate.reduce((count, rowState) => count + rowState.count, 0),
"Row count during update"
);
let rowIndex = 0;
for (let rowState of duringUpdate) {
for (let i = 0; i < rowState.count; i++) {
let row = gURLBar.view._rows.children[rowIndex];
// type
if ("type" in rowState) {
Assert.equal(
row.result.type,
rowState.type,
`Type at index ${rowIndex} during update`
);
}
// suggestedIndex
if ("suggestedIndex" in rowState) {
Assert.ok(
row.result.hasSuggestedIndex,
`Row at index ${rowIndex} has suggestedIndex during update`
);
Assert.equal(
row.result.suggestedIndex,
rowState.suggestedIndex,
`suggestedIndex at index ${rowIndex} during update`
);
} else {
Assert.ok(
!row.result.hasSuggestedIndex,
`Row at index ${rowIndex} does not have suggestedIndex during update`
);
}
// resultSpan
Assert.equal(
UrlbarUtils.getSpanForResult(row.result),
rowState.resultSpan || 1,
`resultSpan at index ${rowIndex} during update`
);
// stale
if (rowState.stale) {
Assert.equal(
row.getAttribute("stale"),
"true",
`Row at index ${rowIndex} is stale during update`
);
} else {
Assert.ok(
!row.hasAttribute("stale"),
`Row at index ${rowIndex} is not stale during update`
);
}
// visible
Assert.equal(
BrowserTestUtils.is_visible(row),
!rowState.hidden,
`Visible at index ${rowIndex} during update`
);
rowIndex++;
}
}
// Finish the search.
resolveQuery();
await queryPromise;
// Check the rows now that the search is done. First, build a map from real
// indexes to suggested index. e.g., if a suggestedIndex = -1, then the real
// index = the result count - 1.
let suggestedIndexesByRealIndex = new Map();
for (let [suggestedIndex, resultSpan] of search2.suggestedIndexes) {
let realIndex =
suggestedIndex >= 0
? Math.min(suggestedIndex, search2.viewCount - 1)
: search2.viewCount + suggestedIndex;
suggestedIndexesByRealIndex.set(realIndex, [suggestedIndex, resultSpan]);
}
Assert.equal(
UrlbarTestUtils.getResultCount(window),
search2.viewCount,
"Row count after update"
);
for (let i = 0; i < search2.viewCount; i++) {
let result = gURLBar.view._rows.children[i].result;
let tuple = suggestedIndexesByRealIndex.get(i);
if (tuple) {
let [suggestedIndex, resultSpan] = tuple;
Assert.ok(
result.hasSuggestedIndex,
`Row at index ${i} has suggestedIndex after update`
);
Assert.equal(
result.suggestedIndex,
suggestedIndex,
`suggestedIndex at index ${i} after update`
);
Assert.equal(
UrlbarUtils.getSpanForResult(result),
resultSpan,
`resultSpan at index ${i} after update`
);
} else {
Assert.ok(
!result.hasSuggestedIndex,
`Row at index ${i} does not have suggestedIndex after update`
);
}
}
await UrlbarTestUtils.promisePopupClose(window);
gURLBar.handleRevert();
UrlbarProvidersManager.unregisterProvider(provider);
}

View File

@ -249,8 +249,6 @@ support-files =
[browser_top_sites_private.js]
[browser_typed_value.js]
[browser_updateForDomainCompletion.js]
[browser_updateRows.js]
[browser_updateRows_visibility.js]
[browser_urlbar_event_telemetry.js]
support-files =
searchSuggestionEngine.xml