diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /browser/components/urlbar/UrlbarProviderTopSites.jsm | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/components/urlbar/UrlbarProviderTopSites.jsm')
-rw-r--r-- | browser/components/urlbar/UrlbarProviderTopSites.jsm | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/browser/components/urlbar/UrlbarProviderTopSites.jsm b/browser/components/urlbar/UrlbarProviderTopSites.jsm new file mode 100644 index 0000000000..37d3e3c873 --- /dev/null +++ b/browser/components/urlbar/UrlbarProviderTopSites.jsm @@ -0,0 +1,249 @@ +/* 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/. */ + +"use strict"; + +var EXPORTED_SYMBOLS = ["UrlbarProviderTopSites"]; + +const { XPCOMUtils } = ChromeUtils.import( + "resource://gre/modules/XPCOMUtils.jsm" +); + +XPCOMUtils.defineLazyModuleGetters(this, { + AboutNewTab: "resource:///modules/AboutNewTab.jsm", + PlacesUtils: "resource://gre/modules/PlacesUtils.jsm", + PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm", + Services: "resource://gre/modules/Services.jsm", + UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm", + UrlbarProvider: "resource:///modules/UrlbarUtils.jsm", + UrlbarProviderOpenTabs: "resource:///modules/UrlbarProviderOpenTabs.jsm", + UrlbarResult: "resource:///modules/UrlbarResult.jsm", + UrlbarSearchUtils: "resource:///modules/UrlbarSearchUtils.jsm", + UrlbarUtils: "resource:///modules/UrlbarUtils.jsm", + TOP_SITES_MAX_SITES_PER_ROW: "resource://activity-stream/common/Reducers.jsm", + TOP_SITES_DEFAULT_ROWS: "resource://activity-stream/common/Reducers.jsm", +}); + +/** + * This module exports a provider returning the user's newtab Top Sites. + */ + +/** + * A provider that returns the Top Sites shown on about:newtab. + */ +class ProviderTopSites extends UrlbarProvider { + constructor() { + super(); + } + + get PRIORITY() { + // Top sites are prioritized over the UnifiedComplete provider. + return 1; + } + + /** + * Unique name for the provider, used by the context to filter on providers. + * Not using a unique name will cause the newest registration to win. + */ + get name() { + return "UrlbarProviderTopSites"; + } + + /** + * The type of the provider. + */ + 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 ( + !queryContext.restrictSource && + !queryContext.searchString && + !queryContext.searchMode + ); + } + + /** + * Gets the provider's priority. + * @param {UrlbarQueryContext} queryContext The query context object + * @returns {number} The provider's priority for the given query. + */ + getPriority(queryContext) { + return this.PRIORITY; + } + + /** + * Starts querying. + * @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. + * @note Extended classes should return a Promise resolved when the provider + * is done searching AND returning results. + */ + async startQuery(queryContext, addCallback) { + // If system.topsites is disabled, we would get stale or empty Top Sites + // data. We check this condition here instead of in isActive because we + // still want this provider to be restricting even if this is not true. If + // it wasn't restricting, we would show the results from UnifiedComplete's + // empty search behaviour. We aren't interested in those since they are very + // similar to Top Sites and thus might be confusing, especially since users + // can configure Top Sites but cannot configure the default empty search + // results. See bug 1623666. + if ( + !UrlbarPrefs.get("suggest.topsites") || + !Services.prefs.getBoolPref( + "browser.newtabpage.activity-stream.feeds.system.topsites", + false + ) + ) { + return; + } + + let sites = AboutNewTab.getTopSites(); + + let instance = this.queryInstance; + + // Filter out empty values. Site is empty when there's a gap between tiles + // on about:newtab. + sites = sites.filter(site => site); + + // This is done here, rather than in the global scope, because + // TOP_SITES_DEFAULT_ROWS causes the import of Reducers.jsm, and we want to + // do that only when actually querying for Top Sites. + if (this.topSitesRows === undefined) { + XPCOMUtils.defineLazyPreferenceGetter( + this, + "topSitesRows", + "browser.newtabpage.activity-stream.topSitesRows", + TOP_SITES_DEFAULT_ROWS + ); + } + + // We usually respect maxRichResults, though we never show a number of Top + // Sites greater than what is visible in the New Tab Page, because the + // additional ones couldn't be managed from the page. + let numTopSites = Math.min( + UrlbarPrefs.get("maxRichResults"), + TOP_SITES_MAX_SITES_PER_ROW * this.topSitesRows + ); + sites = sites.slice(0, numTopSites); + + sites = sites.map(link => ({ + type: link.searchTopSite ? "search" : "url", + url: link.url_urlbar || link.url, + isPinned: !!link.isPinned, + isSponsored: !!link.sponsored_position, + // The newtab page allows the user to set custom site titles, which + // are stored in `label`, so prefer it. Search top sites currently + // don't have titles but `hostname` instead. + title: link.label || link.title || link.hostname || "", + favicon: link.smallFavicon || link.favicon || undefined, + sendAttributionRequest: !!link.sendAttributionRequest, + })); + + for (let site of sites) { + switch (site.type) { + case "url": { + let result = new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, + ...UrlbarResult.payloadAndSimpleHighlights(queryContext.tokens, { + title: site.title, + url: site.url, + icon: site.favicon, + isPinned: site.isPinned, + isSponsored: site.isSponsored, + sendAttributionRequest: site.sendAttributionRequest, + }) + ); + + let allowTabSwitch = + !queryContext.isPrivate || + PrivateBrowsingUtils.permanentPrivateBrowsing; + + let tabs; + if (allowTabSwitch && UrlbarPrefs.get("suggest.openpage")) { + tabs = UrlbarProviderOpenTabs.openTabs.get( + queryContext.userContextId || 0 + ); + } + + if (tabs && tabs.includes(site.url.replace(/#.*$/, ""))) { + result.type = UrlbarUtils.RESULT_TYPE.TAB_SWITCH; + result.source = UrlbarUtils.RESULT_SOURCE.TABS; + } else if (UrlbarPrefs.get("suggest.bookmark")) { + let bookmark = await PlacesUtils.bookmarks.fetch({ + url: new URL(result.payload.url), + }); + if (bookmark) { + result.source = UrlbarUtils.RESULT_SOURCE.BOOKMARKS; + } + } + + // Our query has been cancelled. + if (instance != this.queryInstance) { + break; + } + + addCallback(this, result); + break; + } + case "search": { + let engine = await UrlbarSearchUtils.engineForAlias(site.title); + + if (!engine && site.url) { + // Look up the engine by its domain. + let host; + try { + host = new URL(site.url).hostname; + } catch (err) {} + if (host) { + engine = ( + await UrlbarSearchUtils.enginesForDomainPrefix(host) + )[0]; + } + } + + if (!engine) { + // No engine found. We skip this Top Site. + break; + } + + if (instance != this.queryInstance) { + break; + } + + let result = new UrlbarResult( + UrlbarUtils.RESULT_TYPE.SEARCH, + UrlbarUtils.RESULT_SOURCE.SEARCH, + ...UrlbarResult.payloadAndSimpleHighlights(queryContext.tokens, { + title: site.title, + keyword: site.title, + providesSearchMode: true, + engine: engine.name, + query: "", + icon: site.favicon, + isPinned: site.isPinned, + }) + ); + addCallback(this, result); + break; + } + default: + Cu.reportError(`Unknown Top Site type: ${site.type}`); + break; + } + } + } +} + +var UrlbarProviderTopSites = new ProviderTopSites(); |