forked from mirrors/gecko-dev
Oiriginal patch author: Paresh Malalur <atararx@gmail.com> Differential Revision: https://phabricator.services.mozilla.com/D187465
260 lines
8.6 KiB
JavaScript
260 lines
8.6 KiB
JavaScript
/* 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 module exports a provider that offers input history (aka adaptive
|
|
* history) results. These results map typed search strings to Urlbar results.
|
|
* That way, a user can find a particular result again by typing the same
|
|
* string.
|
|
*/
|
|
|
|
import {
|
|
UrlbarProvider,
|
|
UrlbarUtils,
|
|
} from "resource:///modules/UrlbarUtils.sys.mjs";
|
|
|
|
const lazy = {};
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
|
|
UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
|
|
UrlbarProviderOpenTabs: "resource:///modules/UrlbarProviderOpenTabs.sys.mjs",
|
|
UrlbarResult: "resource:///modules/UrlbarResult.sys.mjs",
|
|
});
|
|
|
|
// Sqlite result row index constants.
|
|
const QUERYINDEX = {
|
|
URL: 0,
|
|
TITLE: 1,
|
|
BOOKMARKED: 2,
|
|
BOOKMARKTITLE: 3,
|
|
TAGS: 4,
|
|
SWITCHTAB: 8,
|
|
};
|
|
|
|
// Constants to support an alternative frecency algorithm.
|
|
const PAGES_USE_ALT_FRECENCY = Services.prefs.getBoolPref(
|
|
"places.frecency.pages.alternative.featureGate",
|
|
false
|
|
);
|
|
const PAGES_FRECENCY_FIELD = PAGES_USE_ALT_FRECENCY
|
|
? "alt_frecency"
|
|
: "frecency";
|
|
|
|
// This SQL query fragment provides the following:
|
|
// - whether the entry is bookmarked (QUERYINDEX_BOOKMARKED)
|
|
// - the bookmark title, if it is a bookmark (QUERYINDEX_BOOKMARKTITLE)
|
|
// - the tags associated with a bookmarked entry (QUERYINDEX_TAGS)
|
|
const SQL_BOOKMARK_TAGS_FRAGMENT = `EXISTS(SELECT 1 FROM moz_bookmarks WHERE fk = h.id) AS bookmarked,
|
|
( SELECT title FROM moz_bookmarks WHERE fk = h.id AND title NOTNULL
|
|
ORDER BY lastModified DESC LIMIT 1
|
|
) AS btitle,
|
|
( SELECT GROUP_CONCAT(t.title, ', ')
|
|
FROM moz_bookmarks b
|
|
JOIN moz_bookmarks t ON t.id = +b.parent AND t.parent = :parent
|
|
WHERE b.fk = h.id
|
|
) AS tags`;
|
|
|
|
const SQL_ADAPTIVE_QUERY = `/* do not warn (bug 487789) */
|
|
SELECT h.url, h.title, ${SQL_BOOKMARK_TAGS_FRAGMENT}, h.visit_count,
|
|
h.typed, h.id, t.open_count, ${PAGES_FRECENCY_FIELD}
|
|
FROM (
|
|
SELECT ROUND(MAX(use_count) * (1 + (input = :search_string)), 1) AS rank,
|
|
place_id
|
|
FROM moz_inputhistory
|
|
WHERE input BETWEEN :search_string AND :search_string || X'FFFF'
|
|
GROUP BY place_id
|
|
) AS i
|
|
JOIN moz_places h ON h.id = i.place_id
|
|
LEFT JOIN moz_openpages_temp t
|
|
ON t.url = h.url
|
|
AND t.userContextId = :userContextId
|
|
WHERE AUTOCOMPLETE_MATCH(NULL, h.url,
|
|
IFNULL(btitle, h.title), tags,
|
|
h.visit_count, h.typed, bookmarked,
|
|
t.open_count,
|
|
:matchBehavior, :searchBehavior,
|
|
NULL)
|
|
ORDER BY rank DESC, ${PAGES_FRECENCY_FIELD} DESC
|
|
LIMIT :maxResults`;
|
|
|
|
/**
|
|
* Class used to create the provider.
|
|
*/
|
|
class ProviderInputHistory extends UrlbarProvider {
|
|
/**
|
|
* Unique name for the provider, used by the context to filter on providers.
|
|
*
|
|
* @returns {string}
|
|
*/
|
|
get name() {
|
|
return "InputHistory";
|
|
}
|
|
|
|
/**
|
|
* The type of the provider, must be one of UrlbarUtils.PROVIDER_TYPE.
|
|
*
|
|
* @returns {UrlbarUtils.PROVIDER_TYPE}
|
|
*/
|
|
get type() {
|
|
return UrlbarUtils.PROVIDER_TYPE.PROFILE;
|
|
}
|
|
|
|
/**
|
|
* Whether this provider should be invoked for the given context.
|
|
* If this method returns false, the providers manager won't start a query
|
|
* with this provider, to save on resources.
|
|
*
|
|
* @param {UrlbarQueryContext} queryContext The query context object
|
|
* @returns {boolean} Whether this provider should be invoked for the search.
|
|
*/
|
|
isActive(queryContext) {
|
|
return (
|
|
(lazy.UrlbarPrefs.get("suggest.history") ||
|
|
lazy.UrlbarPrefs.get("suggest.bookmark") ||
|
|
lazy.UrlbarPrefs.get("suggest.openpage")) &&
|
|
!queryContext.searchMode
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Starts querying. Extended classes should return a Promise resolved when the
|
|
* provider is done searching AND returning results.
|
|
*
|
|
* @param {UrlbarQueryContext} queryContext The query context object
|
|
* @param {Function} addCallback Callback invoked by the provider to add a new
|
|
* result. A UrlbarResult should be passed to it.
|
|
* @returns {Promise}
|
|
*/
|
|
async startQuery(queryContext, addCallback) {
|
|
let instance = this.queryInstance;
|
|
|
|
let conn = await lazy.PlacesUtils.promiseLargeCacheDBConnection();
|
|
if (instance != this.queryInstance) {
|
|
return;
|
|
}
|
|
|
|
let [query, params] = this._getAdaptiveQuery(queryContext);
|
|
let rows = await conn.executeCached(query, params);
|
|
if (instance != this.queryInstance) {
|
|
return;
|
|
}
|
|
|
|
for (let row of rows) {
|
|
const url = row.getResultByIndex(QUERYINDEX.URL);
|
|
const openPageCount = row.getResultByIndex(QUERYINDEX.SWITCHTAB) || 0;
|
|
const historyTitle = row.getResultByIndex(QUERYINDEX.TITLE) || "";
|
|
const bookmarked = row.getResultByIndex(QUERYINDEX.BOOKMARKED);
|
|
const bookmarkTitle = bookmarked
|
|
? row.getResultByIndex(QUERYINDEX.BOOKMARKTITLE)
|
|
: null;
|
|
const tags = row.getResultByIndex(QUERYINDEX.TAGS) || "";
|
|
|
|
let resultTitle = historyTitle;
|
|
if (openPageCount > 0 && lazy.UrlbarPrefs.get("suggest.openpage")) {
|
|
if (url == queryContext.currentPage) {
|
|
// Don't suggest switching to the current page.
|
|
continue;
|
|
}
|
|
let result = new lazy.UrlbarResult(
|
|
UrlbarUtils.RESULT_TYPE.TAB_SWITCH,
|
|
UrlbarUtils.RESULT_SOURCE.TABS,
|
|
...lazy.UrlbarResult.payloadAndSimpleHighlights(queryContext.tokens, {
|
|
url: [url, UrlbarUtils.HIGHLIGHT.TYPED],
|
|
title: [resultTitle, UrlbarUtils.HIGHLIGHT.TYPED],
|
|
icon: UrlbarUtils.getIconForUrl(url),
|
|
userContextId: queryContext.userContextId || 0,
|
|
})
|
|
);
|
|
addCallback(this, result);
|
|
continue;
|
|
}
|
|
|
|
let resultSource;
|
|
if (bookmarked && lazy.UrlbarPrefs.get("suggest.bookmark")) {
|
|
resultSource = UrlbarUtils.RESULT_SOURCE.BOOKMARKS;
|
|
resultTitle = bookmarkTitle || historyTitle;
|
|
} else if (lazy.UrlbarPrefs.get("suggest.history")) {
|
|
resultSource = UrlbarUtils.RESULT_SOURCE.HISTORY;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
let resultTags = tags
|
|
.split(",")
|
|
.map(t => t.trim())
|
|
.filter(tag => {
|
|
let lowerCaseTag = tag.toLocaleLowerCase();
|
|
return queryContext.tokens.some(token =>
|
|
lowerCaseTag.includes(token.lowerCaseValue)
|
|
);
|
|
})
|
|
.sort();
|
|
|
|
let result = new lazy.UrlbarResult(
|
|
UrlbarUtils.RESULT_TYPE.URL,
|
|
resultSource,
|
|
...lazy.UrlbarResult.payloadAndSimpleHighlights(queryContext.tokens, {
|
|
url: [url, UrlbarUtils.HIGHLIGHT.TYPED],
|
|
title: [resultTitle, UrlbarUtils.HIGHLIGHT.TYPED],
|
|
tags: [resultTags, UrlbarUtils.HIGHLIGHT.TYPED],
|
|
icon: UrlbarUtils.getIconForUrl(url),
|
|
})
|
|
);
|
|
|
|
addCallback(this, result);
|
|
}
|
|
}
|
|
|
|
onEngagement(state, queryContext, details, controller) {
|
|
let { result } = details;
|
|
if (result?.providerName != this.name) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
details.selType == "dismiss" &&
|
|
result.type == UrlbarUtils.RESULT_TYPE.URL
|
|
) {
|
|
// Even if removing history normally also removes input history, that
|
|
// doesn't happen if the page is bookmarked, so we do remove input history
|
|
// regardless for this specific search term.
|
|
UrlbarUtils.removeInputHistory(
|
|
result.payload.url,
|
|
queryContext.searchString
|
|
).catch(console.error);
|
|
// Remove browsing history for the page.
|
|
lazy.PlacesUtils.history.remove(result.payload.url).catch(console.error);
|
|
controller.removeResult(result);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtains the query to search for adaptive results.
|
|
*
|
|
* @param {UrlbarQueryContext} queryContext
|
|
* The current queryContext.
|
|
* @returns {Array} Contains the optimized query with which to search the
|
|
* database and an object containing the params to bound.
|
|
*/
|
|
_getAdaptiveQuery(queryContext) {
|
|
return [
|
|
SQL_ADAPTIVE_QUERY,
|
|
{
|
|
parent: lazy.PlacesUtils.tagsFolderId,
|
|
search_string: queryContext.searchString.toLowerCase(),
|
|
matchBehavior: Ci.mozIPlacesAutoComplete.MATCH_ANYWHERE,
|
|
searchBehavior: lazy.UrlbarPrefs.get("defaultBehavior"),
|
|
userContextId:
|
|
lazy.UrlbarProviderOpenTabs.getUserContextIdForOpenPagesTable(
|
|
queryContext.userContextId,
|
|
queryContext.isPrivate
|
|
),
|
|
maxResults: queryContext.maxResults,
|
|
},
|
|
];
|
|
}
|
|
}
|
|
|
|
export var UrlbarProviderInputHistory = new ProviderInputHistory();
|