Bug 1464454 - Expire adaptive history after 90 days and limit the number of top adaptive history matches in the Address Bar. r=adw

Reduce adaptive history domination of the Address Bar results by expiring unused
entries sooner and limiting the number of top adaptive results.

MozReview-Commit-ID: EGOs6rVYGj6

--HG--
rename : toolkit/components/places/tests/unit/test_adaptive.js => toolkit/components/places/tests/unifiedcomplete/test_adaptive.js
rename : toolkit/components/places/tests/unit/test_adaptive_bug527311.js => toolkit/components/places/tests/unifiedcomplete/test_adaptive_behaviors.js
extra : rebase_source : 490d6373b2001101b961e4d2d12b6c02272300a4
This commit is contained in:
Marco Bonardo 2018-05-28 17:38:11 +02:00
Родитель 6d3e29e131
Коммит bf4c38050e
9 изменённых файлов: 183 добавлений и 196 удалений

Просмотреть файл

@ -60,6 +60,7 @@ const PREF_OTHER_DEFAULTS = new Map([
const QUERYTYPE_FILTERED = 0;
const QUERYTYPE_AUTOFILL_ORIGIN = 1;
const QUERYTYPE_AUTOFILL_URL = 2;
const QUERYTYPE_ADAPTIVE = 3;
// This separator is used as an RTL-friendly way to split the title and tags.
// It can also be used by an nsIAutoCompleteResult consumer to re-split the
@ -930,6 +931,10 @@ function Search(searchString, searchParam, autocompleteListener,
}
}
// Used to limit the number of adaptive results.
this._adaptiveCount = 0;
this._extraAdaptiveRows = [];
// This is a replacement for this._result.matchCount, to be used when you need
// to check how many "current" matches have been inserted.
// Indeed this._result.matchCount may include matches from the previous search.
@ -1211,6 +1216,12 @@ Search.prototype = {
return;
}
// If we have some unused adaptive matches, add them now.
while (this._extraAdaptiveRows.length &&
this._currentMatchCount < Prefs.get("maxRichResults")) {
this._addFilteredQueryMatch(this._extraAdaptiveRows.shift());
}
// Ideally we should wait until MATCH_BOUNDARY_ANYWHERE, but that query
// may be really slow and we may end up showing old results for too long.
this._cleanUpNonCurrentMatches(MATCHTYPE.GENERAL);
@ -1806,6 +1817,9 @@ Search.prototype = {
this._result.setDefaultIndex(0);
this._addURLAutofillMatch(row);
break;
case QUERYTYPE_ADAPTIVE:
this._addAdaptiveQueryMatch(row);
break;
case QUERYTYPE_FILTERED:
this._addFilteredQueryMatch(row);
break;
@ -2116,6 +2130,22 @@ Search.prototype = {
});
},
// This is the same as _addFilteredQueryMatch, but it only returns a few
// results, caching the others. If at the end we don't find other results, we
// can add these.
_addAdaptiveQueryMatch(row) {
// Allow one quarter of the results to be adaptive results.
// Note: ideally adaptive results should have their own provider and the
// results muxer should decide what to show. But that's too complex to
// support in the current code, so that's left for a future refactoring.
if (this._adaptiveCount < Math.ceil(Prefs.get("maxRichResults") / 4)) {
this._addFilteredQueryMatch(row);
} else {
this._extraAdaptiveRows.push(row);
}
this._adaptiveCount++;
},
_addFilteredQueryMatch(row) {
let match = {};
match.placeId = row.getResultByIndex(QUERYINDEX_PLACEID);
@ -2278,7 +2308,7 @@ Search.prototype = {
{
parent: PlacesUtils.tagsFolderId,
search_string: this._searchString,
query_type: QUERYTYPE_FILTERED,
query_type: QUERYTYPE_ADAPTIVE,
matchBehavior: this._matchBehavior,
searchBehavior: this._behavior,
userContextId: this._userContextId,

Просмотреть файл

@ -110,6 +110,8 @@ using namespace mozilla::places;
// This is a 'hidden' pref for the purposes of unit tests.
#define PREF_FREC_DECAY_RATE "places.frecency.decayRate"
#define PREF_FREC_DECAY_RATE_DEF 0.975f
// An adaptive history entry is removed if unused for these many days.
#define ADAPTIVE_HISTORY_EXPIRE_DAYS 90
// In order to avoid calling PR_now() too often we use a cached "now" value
// for repeating stuff. These are milliseconds between "now" cache refreshes.
@ -2521,7 +2523,12 @@ nsNavHistory::DecayFrecency()
nsresult rv = FixInvalidFrecencies();
NS_ENSURE_SUCCESS(rv, rv);
float decayRate = Preferences::GetFloat(PREF_FREC_DECAY_RATE, PREF_FREC_DECAY_RATE_DEF);
float decayRate = Preferences::GetFloat(PREF_FREC_DECAY_RATE,
PREF_FREC_DECAY_RATE_DEF);
if (decayRate > 1.0f) {
MOZ_ASSERT(false, "The frecency decay rate should not be greater than 1.0");
decayRate = PREF_FREC_DECAY_RATE_DEF;
}
// Globally decay places frecency rankings to estimate reduced frecency
// values of pages that haven't been visited for a while, i.e., they do
@ -2534,7 +2541,6 @@ nsNavHistory::DecayFrecency()
"WHERE frecency > 0"
);
NS_ENSURE_STATE(decayFrecency);
rv = decayFrecency->BindDoubleByName(NS_LITERAL_CSTRING("decay_rate"),
static_cast<double>(decayRate));
NS_ENSURE_SUCCESS(rv, rv);
@ -2542,15 +2548,22 @@ nsNavHistory::DecayFrecency()
// Decay potentially unused adaptive entries (e.g. those that are at 1)
// to allow better chances for new entries that will start at 1.
nsCOMPtr<mozIStorageAsyncStatement> decayAdaptive = mDB->GetAsyncStatement(
"UPDATE moz_inputhistory SET use_count = use_count * .975"
"UPDATE moz_inputhistory SET use_count = use_count * :decay_rate"
);
NS_ENSURE_STATE(decayAdaptive);
rv = decayAdaptive->BindDoubleByName(NS_LITERAL_CSTRING("decay_rate"),
static_cast<double>(decayRate));
NS_ENSURE_SUCCESS(rv, rv);
// Delete any adaptive entries that won't help in ordering anymore.
nsCOMPtr<mozIStorageAsyncStatement> deleteAdaptive = mDB->GetAsyncStatement(
"DELETE FROM moz_inputhistory WHERE use_count < .01"
"DELETE FROM moz_inputhistory WHERE use_count < :use_count"
);
NS_ENSURE_STATE(deleteAdaptive);
rv = deleteAdaptive->BindDoubleByName(NS_LITERAL_CSTRING("use_count"),
std::pow(static_cast<double>(decayRate),
ADAPTIVE_HISTORY_EXPIRE_DAYS));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageConnection> conn = mDB->MainConn();
if (!conn) {
@ -2563,8 +2576,7 @@ nsNavHistory::DecayFrecency()
};
nsCOMPtr<mozIStoragePendingStatement> ps;
RefPtr<PlacesDecayFrecencyCallback> cb = new PlacesDecayFrecencyCallback();
rv = conn->ExecuteAsync(stmts, ArrayLength(stmts), cb,
getter_AddRefs(ps));
rv = conn->ExecuteAsync(stmts, ArrayLength(stmts), cb, getter_AddRefs(ps));
NS_ENSURE_SUCCESS(rv, rv);
mDecayFrecencyPendingCount++;

Просмотреть файл

@ -492,3 +492,28 @@ add_task(async function ensure_search_engine() {
let engine = Services.search.getEngineByName("MozSearch");
Services.search.currentEngine = engine;
});
/**
* Add a adaptive result for a given (url, string) tuple.
* @param {string} aUrl
* The url to add an adaptive result for.
* @param {string} aSearch
* The string to add an adaptive result for.
* @resolves When the operation is complete.
*/
function addAdaptiveFeedback(aUrl, aSearch) {
let promise = TestUtils.topicObserved("places-autocomplete-feedback-updated");
let thing = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIAutoCompleteInput,
Ci.nsIAutoCompletePopup,
Ci.nsIAutoCompleteController]),
get popup() { return thing; },
get controller() { return thing; },
popupOpen: true,
selectedIndex: 0,
getValueAt: () => aUrl,
searchString: aSearch
};
Services.obs.notifyObservers(thing, "autocomplete-will-enter-text");
return promise;
}

Просмотреть файл

@ -18,53 +18,6 @@
* learning.
*/
function AutoCompleteInput(aSearches) {
this.searches = aSearches;
}
AutoCompleteInput.prototype = {
constructor: AutoCompleteInput,
get minResultsForPopup() {
return 0;
},
get timeout() {
return 10;
},
get searchParam() {
return "";
},
get textValue() {
return "";
},
get disableAutoComplete() {
return false;
},
get completeDefaultIndex() {
return false;
},
get searchCount() {
return this.searches.length;
},
getSearchAt(aIndex) {
return this.searches[aIndex];
},
onSearchBegin() {},
onSearchComplete() {},
get popupOpen() {
return false;
},
popup: {
set selectedIndex(aIndex) {},
invalidate() {},
QueryInterface: ChromeUtils.generateQI([Ci.nsIAutoCompletePopup])
},
QueryInterface: ChromeUtils.generateQI([Ci.nsIAutoCompleteInput])
};
/**
* Checks that autocomplete results are ordered correctly.
*/
@ -80,8 +33,10 @@ function ensure_results(expected, searchTerm, callback) {
input.onSearchComplete = function() {
Assert.equal(controller.searchStatus,
Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH);
Assert.equal(controller.matchCount, expected.length);
Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH,
"The search should be complete");
Assert.equal(controller.matchCount, expected.length,
"All the expected results should have been found");
for (let i = 0; i < controller.matchCount; i++) {
print("Testing for '" + expected[i].uri.spec + "' got '" + controller.getValueAt(i) + "'");
Assert.equal(controller.getValueAt(i), expected[i].uri.spec);
@ -368,6 +323,21 @@ var tests = [
await task_setCountRank(uri1, c1, c1, s2, "tag");
await task_setCountRank(uri2, c1, c2, s2);
},
// Test that many results are all shown if no other results are available.
async function() {
print("Test 14 - many results");
let n = 10;
observer.results = Array(n).fill(0).map(
(e, i) => makeResult(Services.io.newURI("http://site.tld/" + i))
);
observer.search = s2;
observer.runCount = n * (n + 1) / 2;
let c = n;
for (let result of observer.results) {
task_setCountRank(result.uri, c, c, s2);
c--;
}
},
];
/**
@ -376,7 +346,12 @@ var tests = [
add_task(async function test_adaptive() {
// Disable autoFill for this test.
Services.prefs.setBoolPref("browser.urlbar.autoFill", false);
registerCleanupFunction(() => Services.prefs.clearUserPref("browser.urlbar.autoFill"));
registerCleanupFunction(async function() {
await PlacesUtils.bookmarks.eraseEverything();
await PlacesUtils.history.clear();
});
for (let test of tests) {
// Cleanup.
await PlacesUtils.bookmarks.eraseEverything();

Просмотреть файл

@ -0,0 +1,40 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
// Test for Bug 527311
// Addressbar suggests adaptive results regardless of the requested behavior.
const TEST_URL = "http://adapt.mozilla.org/";
const SEARCH_STRING = "adapt";
const SUGGEST_TYPES = ["history", "bookmark", "openpage"];
add_task(async function test_adaptive_search_specific() {
Services.prefs.setBoolPref("browser.urlbar.autoFill", false);
// Add a bookmark to our url.
await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
title: "test_book",
url: TEST_URL,
});
registerCleanupFunction(async function() {
await PlacesUtils.bookmarks.eraseEverything();
});
// We want to search only history.
for (let type of SUGGEST_TYPES) {
type == "history" ? Services.prefs.setBoolPref("browser.urlbar.suggest." + type, true)
: Services.prefs.setBoolPref("browser.urlbar.suggest." + type, false);
}
// Add an adaptive entry.
await addAdaptiveFeedback(TEST_URL, SEARCH_STRING);
await check_autocomplete({
search: SEARCH_STRING,
matches: [],
});
});

Просмотреть файл

@ -0,0 +1,41 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that top adaptive results are limited, remaining ones are enqueued.
add_task(async function() {
Services.prefs.setBoolPref("browser.urlbar.autoFill", false);
let n = 10;
let uris = Array(n).fill(0).map((e, i) => "http://site.tld/" + i);
// Add a bookmark to one url.
let bm = await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
title: "test_book",
url: uris.shift(),
});
// Make remaining ones adaptive results.
for (let uri of uris) {
await PlacesTestUtils.addVisits(uri);
await addAdaptiveFeedback(uri, "book");
}
registerCleanupFunction(async function() {
await PlacesUtils.bookmarks.eraseEverything();
await PlacesUtils.history.clear();
});
let matches = uris.map(uri => ({ uri: Services.io.newURI(uri),
title: "test visit for " + uri }));
let book_index = Math.ceil(Services.prefs.getIntPref("browser.urlbar.maxRichResults") / 4);
matches.splice(book_index, 0, { uri: Services.io.newURI(bm.url.href),
title: "test_book", "style": ["bookmark"] });
await check_autocomplete({
search: "book",
matches,
checkSorting: true,
});
});

Просмотреть файл

@ -13,6 +13,9 @@ support-files =
[test_417798.js]
[test_418257.js]
[test_422277.js]
[test_adaptive.js]
[test_adaptive_behaviors.js]
[test_adaptive_limited.js]
[test_autocomplete_functional.js]
[test_autocomplete_stopSearch_no_throw.js]
[test_autofill_origins.js]

Просмотреть файл

@ -1,137 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
const TEST_URL = "http://adapt.mozilla.org/";
const SEARCH_STRING = "adapt";
const SUGGEST_TYPES = ["history", "bookmark", "openpage"];
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
const PLACES_AUTOCOMPLETE_FEEDBACK_UPDATED_TOPIC =
"places-autocomplete-feedback-updated";
function cleanup() {
for (let type of SUGGEST_TYPES) {
Services.prefs.clearUserPref("browser.urlbar.suggest." + type);
}
}
function AutoCompleteInput(aSearches) {
this.searches = aSearches;
}
AutoCompleteInput.prototype = {
constructor: AutoCompleteInput,
searches: null,
minResultsForPopup: 0,
timeout: 10,
searchParam: "",
textValue: "",
disableAutoComplete: false,
completeDefaultIndex: false,
get searchCount() {
return this.searches.length;
},
getSearchAt: function ACI_getSearchAt(aIndex) {
return this.searches[aIndex];
},
onSearchComplete: function ACI_onSearchComplete() {},
popupOpen: false,
popup: {
setSelectedIndex() {},
invalidate() {},
QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"])
},
onSearchBegin() {},
QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"])
};
function check_results() {
return new Promise(resolve => {
let controller = Cc["@mozilla.org/autocomplete/controller;1"].
getService(Ci.nsIAutoCompleteController);
let input = new AutoCompleteInput(["unifiedcomplete"]);
controller.input = input;
input.onSearchComplete = function() {
Assert.equal(controller.searchStatus,
Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH);
Assert.equal(controller.matchCount, 0);
PlacesUtils.bookmarks.eraseEverything().then(() => {
cleanup();
resolve();
});
};
controller.startSearch(SEARCH_STRING);
});
}
function addAdaptiveFeedback(aUrl, aSearch) {
return new Promise(resolve => {
let observer = {
observe(aSubject, aTopic, aData) {
Services.obs.removeObserver(observer, PLACES_AUTOCOMPLETE_FEEDBACK_UPDATED_TOPIC);
do_timeout(0, resolve);
}
};
Services.obs.addObserver(observer, PLACES_AUTOCOMPLETE_FEEDBACK_UPDATED_TOPIC);
let thing = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIAutoCompleteInput,
Ci.nsIAutoCompletePopup,
Ci.nsIAutoCompleteController]),
get popup() { return thing; },
get controller() { return thing; },
popupOpen: true,
selectedIndex: 0,
getValueAt: () => aUrl,
searchString: aSearch
};
Services.obs.notifyObservers(thing, "autocomplete-will-enter-text");
});
}
add_task(function init() {
Services.prefs.setBoolPref("browser.urlbar.autoFill", false);
registerCleanupFunction(() => {
Services.prefs.clearUserPref("browser.urlbar.autoFill");
});
});
add_task(async function test_adaptive_search_specific() {
// Add a bookmark to our url.
await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
title: "test_book",
url: TEST_URL,
});
// We want to search only history.
for (let type of SUGGEST_TYPES) {
type == "history" ? Services.prefs.setBoolPref("browser.urlbar.suggest." + type, true)
: Services.prefs.setBoolPref("browser.urlbar.suggest." + type, false);
}
// Add an adaptive entry.
await addAdaptiveFeedback(TEST_URL, SEARCH_STRING);
await check_results();
await PlacesTestUtils.promiseAsyncUpdates();
});

Просмотреть файл

@ -42,8 +42,6 @@ skip-if = os == "linux"
[test_1085291.js]
[test_1105208.js]
[test_1105866.js]
[test_adaptive.js]
[test_adaptive_bug527311.js]
[test_annotations.js]
[test_asyncExecuteLegacyQueries.js]
[test_async_transactions.js]