tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 21800a1aa98c0cebfb60d053a6a39ec2620827bc
parent 792a8cf8b786f909d983351437bba5a6acf4604c
Author: Daisuke Akatsuka <daisuke@birchill.co.jp>
Date:   Thu, 11 Dec 2025 01:12:04 +0000

Bug 1995227: Make title calculation lazily r=adw

Differential Revision: https://phabricator.services.mozilla.com/D274563

Diffstat:
Mbrowser/components/urlbar/UrlbarResult.sys.mjs | 91+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mbrowser/components/urlbar/UrlbarView.sys.mjs | 15+++++++++++----
Mbrowser/components/urlbar/private/YelpSuggestions.sys.mjs | 19+++++++++++--------
Mbrowser/components/urlbar/tests/UrlbarTestUtils.sys.mjs | 2+-
Mbrowser/components/urlbar/tests/browser/browser_paste_multi_lines.js | 2+-
Mbrowser/components/urlbar/tests/browser/browser_selectStaleResults.js | 4++--
Mbrowser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest.js | 26++++++++++++--------------
Mbrowser/components/urlbar/tests/unit/test_providerPlaces.js | 28++++++++--------------------
8 files changed, 91 insertions(+), 96 deletions(-)

diff --git a/browser/components/urlbar/UrlbarResult.sys.mjs b/browser/components/urlbar/UrlbarResult.sys.mjs @@ -277,26 +277,6 @@ export class UrlbarResult { } /** - * Returns a title that could be used as a label for this result. - * - * @returns {string} The label to show in a simplified title / url view. - */ - get title() { - let value = this.#getTitleOrHighlights(this.payload); - return !value || Array.isArray(value) ? "" : value; - } - - /** - * Returns an array of highlights for the title. - * - * @returns {Array} The array of highlights. - */ - get titleHighlights() { - let value = this.#getTitleOrHighlights(this.payloadHighlights); - return Array.isArray(value) ? value : []; - } - - /** * Returns an icon url. * * @returns {string} url of the icon. @@ -357,6 +337,11 @@ export class UrlbarResult { } } + let isTitle = payloadName == "title"; + if (isTitle) { + payloadName = this.#getDisplayableTitlePayloadName(); + } + let value = this.payload[payloadName]; if (!value) { return {}; @@ -366,35 +351,55 @@ export class UrlbarResult { value = lazy.UrlbarUtils.prepareUrlForDisplay(value); } + let highlightable = value; + let highlightType; + if (isTitle) { + if (payloadName == "qsSuggestion") { + value = this.payload.qsSuggestion + " — " + this.payload.title; + } else if (payloadName == "url") { + // If there's no title, show the domain as the title. Not all valid URLs + // have a domain. + try { + value = highlightable = new URL(this.payload.url).URI.displayHostPort; + highlightType = lazy.UrlbarUtils.HIGHLIGHT.TYPED; + } catch (e) {} + } + } + if (typeof value == "string") { value = value.substring(0, lazy.UrlbarUtils.MAX_TEXT_LENGTH); } - if (!options.tokens?.length || !this.#highlights?.[payloadName]) { + highlightType ??= this.#highlights?.[payloadName]; + + if (!options.tokens?.length || !highlightType) { let cached = { value, options }; this.#displayValuesCache.set(payloadName, cached); return cached; } - let type = this.#highlights[payloadName]; - let highlights = Array.isArray(value) + let highlights = Array.isArray(highlightable) ? value.map(subval => - lazy.UrlbarUtils.getTokenMatches(options.tokens, subval, type) + lazy.UrlbarUtils.getTokenMatches( + options.tokens, + subval, + highlightType + ) ) - : lazy.UrlbarUtils.getTokenMatches(options.tokens, value || "", type); + : lazy.UrlbarUtils.getTokenMatches( + options.tokens, + highlightable + ? highlightable.substring(0, lazy.UrlbarUtils.MAX_TEXT_LENGTH) + : "", + highlightType + ); let cached = { value, highlights, options }; this.#displayValuesCache.set(payloadName, cached); return cached; } - /** - * Returns title or highlights of given payload or payloadHighlights. - * - * @param {object} target payload or payloadHighlights - * @returns {string|Array} The title or array of highlights. - */ - #getTitleOrHighlights(target) { + #getDisplayableTitlePayloadName() { switch (this.type) { case lazy.UrlbarUtils.RESULT_TYPE.KEYWORD: case lazy.UrlbarUtils.RESULT_TYPE.TAB_SWITCH: @@ -402,34 +407,28 @@ export class UrlbarResult { case lazy.UrlbarUtils.RESULT_TYPE.OMNIBOX: case lazy.UrlbarUtils.RESULT_TYPE.REMOTE_TAB: if (this.payload.qsSuggestion) { - if (this.payload == target) { - // We will initially only be targeting en-US users with this - // experiment but will need to change this to work properly with - // l10n. - return target.qsSuggestion + " — " + target.title; - } - return target.qsSuggestion; + return "qsSuggestion"; } if (this.payload.fallbackTitle) { - return target.fallbackTitle; + return "fallbackTitle"; } if (this.payload.title) { - return target.title; + return "title"; } - return target.url; + return "url"; case lazy.UrlbarUtils.RESULT_TYPE.SEARCH: if (this.payload.title) { - return target.title; + return "title"; } if (this.payload.providesSearchMode) { return null; } if (this.payload.tail && this.payload.tailOffsetIndex >= 0) { - return target.tail; + return "tail"; } else if (this.payload.suggestion) { - return target.suggestion; + return "suggestion"; } - return target.query; + return "query"; } return null; } diff --git a/browser/components/urlbar/UrlbarView.sys.mjs b/browser/components/urlbar/UrlbarView.sys.mjs @@ -2110,7 +2110,10 @@ export class UrlbarView { title.removeAttribute("aria-label"); } - this.#updateOverflowTooltip(title, result.title); + this.#updateOverflowTooltip( + title, + result.getDisplayableValueAndHighlights("title").value + ); let tagsContainer = item._elements.get("tagsContainer"); if (tagsContainer) { @@ -3147,10 +3150,14 @@ export class UrlbarView { } this.#l10nCache.removeElementL10n(titleNode); + + let titleAndHighlights = result.getDisplayableValueAndHighlights("title", { + tokens: this.#queryContext.tokens, + }); lazy.UrlbarUtils.addTextContentWithHighlights( titleNode, - result.title, - result.titleHighlights + titleAndHighlights.value, + titleAndHighlights.highlights ); } @@ -3723,7 +3730,7 @@ export class UrlbarView { title.textContent = localSearchMode || engine ? this.#queryContext.searchString - : result.title; + : result.getDisplayableValueAndHighlights("title").value; // Set the restyled-search attribute so the action text and title // separator are shown or hidden via CSS as appropriate. diff --git a/browser/components/urlbar/private/YelpSuggestions.sys.mjs b/browser/components/urlbar/private/YelpSuggestions.sys.mjs @@ -194,11 +194,6 @@ export class YelpSuggestions extends SuggestProvider { } } - let titleHighlights = lazy.UrlbarUtils.getTokenMatches( - queryContext.tokens, - title, - lazy.UrlbarUtils.HIGHLIGHT.TYPED - ); let payload = { url: url.toString(), originalUrl: suggestion.url, @@ -207,12 +202,17 @@ export class YelpSuggestions extends SuggestProvider { }, iconBlob: suggestion.icon_blob, }; - let payloadHighlights = {}; + let highlights; if ( lazy.UrlbarPrefs.get("yelpServiceResultDistinction") && suggestion.subjectType === lazy.YelpSubjectType.SERVICE ) { + let titleHighlights = lazy.UrlbarUtils.getTokenMatches( + queryContext.tokens, + title, + lazy.UrlbarUtils.HIGHLIGHT.TYPED + ); payload.titleL10n = { id: "firefox-suggest-yelp-service-title", args: { @@ -224,15 +224,18 @@ export class YelpSuggestions extends SuggestProvider { }; } else { payload.title = title; - payloadHighlights.title = titleHighlights; + highlights = { + title: lazy.UrlbarUtils.HIGHLIGHT.TYPED, + }; } return new lazy.UrlbarResult({ + queryContext, type: lazy.UrlbarUtils.RESULT_TYPE.URL, source: lazy.UrlbarUtils.RESULT_SOURCE.SEARCH, ...resultProperties, payload, - payloadHighlights, + highlights, }); } diff --git a/browser/components/urlbar/tests/UrlbarTestUtils.sys.mjs b/browser/components/urlbar/tests/UrlbarTestUtils.sys.mjs @@ -564,7 +564,7 @@ class UrlbarInputTestUtils { details.autofill = !!result.autofill; details.image = element.getElementsByClassName("urlbarView-favicon")[0]?.src; - details.title = result.title; + details.title = result.getDisplayableValueAndHighlights("title").value; details.tags = "tags" in result.payload ? result.payload.tags : []; details.isSponsored = result.payload.isSponsored; details.userContextId = result.payload.userContextId; diff --git a/browser/components/urlbar/tests/browser/browser_paste_multi_lines.js b/browser/components/urlbar/tests/browser/browser_paste_multi_lines.js @@ -154,7 +154,7 @@ const TEST_DATA = [ input: "\r\n\r\n\r\n\r\n\r\n", expected: { urlbar: "", - title: "", + title: undefined, type: UrlbarUtils.RESULT_TYPE.SEARCH, }, }, diff --git a/browser/components/urlbar/tests/browser/browser_selectStaleResults.js b/browser/components/urlbar/tests/browser/browser_selectStaleResults.js @@ -85,8 +85,8 @@ add_task(async function viewContainsStaleRows() { let lastMatchingResultUpdatedPromise = TestUtils.waitForCondition(() => { let row = UrlbarTestUtils.getRowAt(window, halfResults); - console.log(row.result.title); - return row.result.title.startsWith("xx"); + let { value } = row.result.getDisplayableValueAndHighlights("title"); + return value.startsWith("xx"); }, "Wait for the result to be updated"); // Type another "x" so that we search for "xx", but don't wait for the search diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest.js b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest.js @@ -279,16 +279,15 @@ add_task(async function allEnabled_sponsoredEnabled_sponsoredSearch() { // The title should include the full keyword and em dash, and the part of the // title that the search string does not match should be highlighted. let result = context.results[0]; + let { value, highlights } = result.getDisplayableValueAndHighlights("title", { + tokens: context.tokens, + }); Assert.equal( - result.title, + value, `${SPONSORED_SEARCH_STRING} — Amp Suggestion`, - "result.title should be correct" - ); - Assert.deepEqual( - result.titleHighlights, - [], - "result.titleHighlights should be correct" + "The title should be correct" ); + Assert.deepEqual(highlights, [], "The highlights should be correct"); }); // Tests with both `all` and sponsored enabled with a non-sponsored search @@ -310,16 +309,15 @@ add_task(async function allEnabled_sponsoredEnabled_nonsponsoredSearch() { // The title should include the full keyword and em dash, and the part of the // title that the search string does not match should be highlighted. let result = context.results[0]; + let { value, highlights } = result.getDisplayableValueAndHighlights("title", { + tokens: context.tokens, + }); Assert.equal( - result.title, + value, `${NONSPONSORED_SEARCH_STRING} — Wikipedia Suggestion`, - "result.title should be correct" - ); - Assert.deepEqual( - result.titleHighlights, - [], - "result.titleHighlights should be correct" + "The title should be correct" ); + Assert.deepEqual(highlights, [], "The highlights should be correct"); }); // Tests with both `all` and sponsored enabled with a search string that doesn't diff --git a/browser/components/urlbar/tests/unit/test_providerPlaces.js b/browser/components/urlbar/tests/unit/test_providerPlaces.js @@ -68,10 +68,7 @@ add_task(async function test_places() { await controller.startQuery(context); - info( - "Results:\n" + - context.results.map(m => `${m.title} - ${m.payload.url}`).join("\n") - ); + info("Results:\n" + context.results.map(m => m.payload.url).join("\n")); Assert.equal( context.results.length, 7, @@ -102,7 +99,7 @@ add_task(async function test_places() { "Test tab", "Test history", ], - context.results.map(m => m.title), + context.results.map(m => m.getDisplayableValueAndHighlights("title").value), "Check match titles" ); @@ -167,10 +164,7 @@ add_task(async function test_bookmarkBehaviorDisabled_tagged() { await controller.startQuery(context); - info( - "Results:\n" + - context.results.map(m => `${m.title} - ${m.payload.url}`).join("\n") - ); + info("Results:\n" + context.results.map(m => m.payload.url).join("\n")); Assert.equal( context.results.length, 2, @@ -185,7 +179,7 @@ add_task(async function test_bookmarkBehaviorDisabled_tagged() { Assert.deepEqual( [searchString, "Test bookmark"], - context.results.map(m => m.title), + context.results.map(m => m.getDisplayableValueAndHighlights("title").value), "Check match titles" ); @@ -218,10 +212,7 @@ add_task(async function test_bookmarkBehaviorDisabled_untagged() { await controller.startQuery(context); - info( - "Results:\n" + - context.results.map(m => `${m.title} - ${m.payload.url}`).join("\n") - ); + info("Results:\n" + context.results.map(m => m.payload.url).join("\n")); Assert.equal( context.results.length, 2, @@ -236,7 +227,7 @@ add_task(async function test_bookmarkBehaviorDisabled_untagged() { Assert.deepEqual( [searchString, "Test bookmark"], - context.results.map(m => m.title), + context.results.map(m => m.getDisplayableValueAndHighlights("title").value), "Check match titles" ); @@ -266,10 +257,7 @@ add_task(async function test_diacritics() { await controller.startQuery(context); - info( - "Results:\n" + - context.results.map(m => `${m.title} - ${m.payload.url}`).join("\n") - ); + info("Results:\n" + context.results.map(m => m.payload.url).join("\n")); Assert.equal( context.results.length, 2, @@ -284,7 +272,7 @@ add_task(async function test_diacritics() { Assert.deepEqual( [searchString, "Test bookmark with accents in path"], - context.results.map(m => m.title), + context.results.map(m => m.getDisplayableValueAndHighlights("title").value), "Check match titles" );