tor-browser

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

commit ceee53c237aa5fff1458dcb606b67e792651b774
parent ecdc16905e250ad631f23e9ec604b8e099f7ad53
Author: Gijs Kruitbosch <gijskruitbosch@gmail.com>
Date:   Mon,  3 Nov 2025 11:21:59 +0000

Bug 1970743 - consolidate DownloadUtils.getURIHost into BrowserUtils.formatURIForDisplay, r=mak

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

Diffstat:
Mbrowser/base/content/browser-fullScreenAndPointerLock.js | 7+++----
Mbrowser/components/downloads/DownloadsViewUI.sys.mjs | 9++++++++-
Mbrowser/components/firefoxview/fxview-tab-list.mjs | 4+++-
Mbrowser/components/firefoxview/helpers.mjs | 4+++-
Mtoolkit/modules/BrowserUtils.sys.mjs | 61++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mtoolkit/modules/tests/xpcshell/test_BrowserUtils_urlFormatting.js | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mtoolkit/mozapps/downloads/DownloadUtils.sys.mjs | 86-------------------------------------------------------------------------------
Mtoolkit/mozapps/downloads/tests/browser/browser_unknownContentType_blob.js | 2+-
Mtoolkit/mozapps/downloads/tests/unit/test_DownloadUtils.js | 31-------------------------------
9 files changed, 139 insertions(+), 134 deletions(-)

diff --git a/browser/base/content/browser-fullScreenAndPointerLock.js b/browser/base/content/browser-fullScreenAndPointerLock.js @@ -100,10 +100,9 @@ var PointerlockFsWarning = { } else { textElem.removeAttribute("hidden"); // Document's principal's URI has a host. Display a warning including it. - let { DownloadUtils } = ChromeUtils.importESModule( - "resource://gre/modules/DownloadUtils.sys.mjs" - ); - let displayHost = DownloadUtils.getURIHost(uri.spec)[0]; + let displayHost = BrowserUtils.formatURIForDisplay(uri, { + onlyBaseDomain: true, + }); let l10nString = { "fullscreen-warning": "fullscreen-warning-domain", "pointerlock-warning": "pointerlock-warning-domain", diff --git a/browser/components/downloads/DownloadsViewUI.sys.mjs b/browser/components/downloads/DownloadsViewUI.sys.mjs @@ -12,6 +12,7 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { + BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs", BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs", DownloadUtils: "resource://gre/modules/DownloadUtils.sys.mjs", Downloads: "resource://gre/modules/Downloads.sys.mjs", @@ -603,7 +604,13 @@ DownloadsViewUI.DownloadElementShell.prototype = { this.showStatus(stateLabel, hoverStatus); return; } - let [displayHost] = lazy.DownloadUtils.getURIHost(this.download.source.url); + let uri = URL.parse(this.download.source.url)?.URI; + let displayHost = uri + ? lazy.BrowserUtils.formatURIForDisplay(uri, { + onlyBaseDomain: true, + }) + : ""; + let [displayDate] = lazy.DownloadUtils.getReadableDates( new Date(this.download.endTime) ); diff --git a/browser/components/firefoxview/fxview-tab-list.mjs b/browser/components/firefoxview/fxview-tab-list.mjs @@ -517,7 +517,9 @@ export class FxviewTabRowBase extends MozLitElement { formatURIForDisplay(uriString) { return !window.IS_STORYBOOK - ? lazy.BrowserUtils.formatURIStringForDisplay(uriString) + ? lazy.BrowserUtils.formatURIStringForDisplay(uriString, { + showFilenameForLocalURIs: true, + }) : uriString; } diff --git a/browser/components/firefoxview/helpers.mjs b/browser/components/firefoxview/helpers.mjs @@ -24,7 +24,9 @@ export const LOGGING_PREF = "browser.tabs.firefox-view.logLevel"; export const MAX_TABS_FOR_RECENT_BROWSING = 5; export function formatURIForDisplay(uriString) { - return lazy.BrowserUtils.formatURIStringForDisplay(uriString); + return lazy.BrowserUtils.formatURIStringForDisplay(uriString, { + showFilenameForLocalURIs: true, + }); } export function convertTimestamp( diff --git a/toolkit/modules/BrowserUtils.sys.mjs b/toolkit/modules/BrowserUtils.sys.mjs @@ -91,7 +91,10 @@ XPCOMUtils.defineLazyPreferenceGetter( ); ChromeUtils.defineLazyGetter(lazy, "gLocalization", () => { - return new Localization(["toolkit/global/browser-utils.ftl"], true); + return new Localization( + ["toolkit/global/browser-utils.ftl", "toolkit/downloads/downloadUtils.ftl"], + true + ); }); function stringPrefToSet(prefVal) { @@ -246,8 +249,36 @@ export var BrowserUtils = { } }, + /** + * Show a URI in the UI in a user-friendly (but security-sensitive) way. + * + * @param {nsIURI} uri + * @param {object} [options={}] + * @param {boolean} [options.showInsecureHTTP=false] + * Whether to show "http://" for insecure HTTP URLs. + * @param {boolean} [options.showWWW=false] + * Whether to show "www." for URLs that have it. + * @param {boolean} [options.onlyBaseDomain=false] + * Whether to show only the base domain (eTLD+1) for HTTP(S) URLs. + * @param {boolean} [options.showFilenameForLocalURIs=false] + * If false (default), will show a protocol-specific label for local + * URIs (file:, chrome:, resource:, moz-src:, jar:). + * Otherwise, will show the filename for such URIs. Only use 'true' if + * the context in which the URI is being represented is not security- + * critical. + */ formatURIForDisplay(uri, options = {}) { - let { showInsecureHTTP = false } = options; + let { + showInsecureHTTP = false, + showWWW = false, + onlyBaseDomain = false, + showFilenameForLocalURIs = false, + } = options; + // For moz-icon and jar etc. which wrap nsIURLs, if we want to show the + // actual filename, unwrap: + if (uri && uri instanceof Ci.nsINestedURI && showFilenameForLocalURIs) { + return this.formatURIForDisplay(uri.innermostURI, options); + } switch (uri.scheme) { case "view-source": { let innerURI = uri.spec.substring("view-source:".length); @@ -257,8 +288,14 @@ export var BrowserUtils = { // Fall through. case "https": { let host = uri.displayHostPort; - if (!showInsecureHTTP && host.startsWith("www.")) { + let removeSubdomains = + !showInsecureHTTP && + (onlyBaseDomain || (!showWWW && host.startsWith("www."))); + if (removeSubdomains) { host = Services.eTLD.getSchemelessSite(uri); + if (uri.port != -1) { + host += ":" + uri.port; + } } if (showInsecureHTTP && uri.scheme == "http") { return "http://" + host; @@ -269,7 +306,7 @@ export var BrowserUtils = { return "about:" + uri.filePath; case "blob": try { - let url = new URL(uri.specIgnoringRef); + let url = URL.fromURI(uri); // _If_ we find a non-null origin, report that. if (url.origin && url.origin != "null") { return this.formatURIStringForDisplay(url.origin, options); @@ -291,8 +328,22 @@ export var BrowserUtils = { } case "chrome": case "resource": + case "moz-icon": + case "moz-src": case "jar": case "file": + if (!showFilenameForLocalURIs) { + if (uri.scheme == "file") { + return lazy.gLocalization.formatValueSync( + "download-utils-done-file-scheme" + ); + } + return lazy.gLocalization.formatValueSync( + "download-utils-done-scheme", + { scheme: uri.scheme } + ); + } + // Otherwise, fall through to show filename... default: try { let url = uri.QueryInterface(Ci.nsIURL); @@ -318,7 +369,7 @@ export var BrowserUtils = { console.error(ex); } } - return uri.asciiHost || uri.spec; + return uri.spec; }, // Given a URL returns a (possibly transformed) URL suitable for sharing, or null if diff --git a/toolkit/modules/tests/xpcshell/test_BrowserUtils_urlFormatting.js b/toolkit/modules/tests/xpcshell/test_BrowserUtils_urlFormatting.js @@ -9,7 +9,10 @@ let tempFile = new FileUtils.File(PathUtils.tempDir); const TEST_LOCAL_FILE_NAME = "hello.txt"; tempFile.append(TEST_LOCAL_FILE_NAME); -const gL10n = new Localization(["toolkit/global/browser-utils.ftl"], true); +const gL10n = new Localization( + ["toolkit/global/browser-utils.ftl", "toolkit/downloads/downloadUtils.ftl"], + true +); const DATA_URL_EXPECTED_STRING = gL10n.formatValueSync( "browser-utils-url-data" ); @@ -19,6 +22,10 @@ const EXTENSION_URL_EXPECTED_STRING = gL10n.formatValueSync( { extension: EXTENSION_NAME } ); +const FILE_URL_EXPECTED_STRING = gL10n.formatValueSync( + "download-utils-done-file-scheme" +); + const { AddonTestUtils } = ChromeUtils.importESModule( "resource://testing-common/AddonTestUtils.sys.mjs" ); @@ -88,7 +95,7 @@ const HTTP_TESTS = [ output: "www.co.uk", }, - // Other sudomains should be kept: + // Other subdomains should be kept: { input: "https://webmail.example.co.uk", output: "webmail.example.co.uk", @@ -158,6 +165,15 @@ const TESTS = [ input: "data:text/html,42", output: DATA_URL_EXPECTED_STRING, }, + + { + input: `moz-icon:${Services.io.newFileURI(tempFile).spec}`, + output: tempFile.leafName, + }, + { + input: "moz-icon://.extension?size=16", + output: "moz-icon://.extension?size=16", + }, ]; add_setup(async () => { @@ -220,7 +236,9 @@ const { BrowserUtils } = ChromeUtils.importESModule( add_task(async function test_checkStringFormatting() { for (let { input, output } of TESTS) { Assert.equal( - BrowserUtils.formatURIStringForDisplay(input), + BrowserUtils.formatURIStringForDisplay(input, { + showFilenameForLocalURIs: true, + }), output, `String ${input} formatted for output should match` ); @@ -231,13 +249,56 @@ add_task(async function test_checkURIFormatting() { for (let { input, output } of TESTS) { let uri = Services.io.newURI(input); Assert.equal( - BrowserUtils.formatURIForDisplay(uri), + BrowserUtils.formatURIForDisplay(uri, { + showFilenameForLocalURIs: true, + }), output, `URI ${input} formatted for output should match` ); } }); +add_task(async function test_checkOnlyBaseDomain() { + for (let { input, output } of [ + { input: "https://subdomain.example.com/", output: "example.com" }, + { + input: "http://www.city.mikasa.hokkaido.jp/", + output: "city.mikasa.hokkaido.jp", + }, + { input: "https://www.example.co.uk/", output: "example.co.uk" }, + { + input: "mailto:example@subdomain.example.com", + output: "mailto:example@subdomain.example.com", + }, + ]) { + let uri = Services.io.newURI(input); + Assert.equal( + BrowserUtils.formatURIForDisplay(uri, { onlyBaseDomain: true }), + output, + `URI ${input} formatted for output should match` + ); + } +}); + +add_task(async function test_checkLocalFileFormatting() { + for (let { input } of TESTS) { + let uri = Services.io.newURI(input); + if ( + ["file", "chrome", "moz-icon", "resource", "jar"].includes(uri.scheme) + ) { + Assert.equal( + BrowserUtils.formatURIForDisplay(uri, { + showFilenameForLocalURIs: false, + }), + uri.scheme == "file" + ? FILE_URL_EXPECTED_STRING + : `${uri.scheme} resource`, + `URI ${input} formatted for output should match` + ); + } + } +}); + add_task(async function test_checkViewSourceFormatting() { for (let { input, output } of HTTP_TESTS) { Assert.equal( diff --git a/toolkit/mozapps/downloads/DownloadUtils.sys.mjs b/toolkit/mozapps/downloads/DownloadUtils.sys.mjs @@ -3,8 +3,6 @@ * 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/. */ -import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; - /** * This module provides the DownloadUtils object which contains useful methods * for downloads such as displaying file sizes, transfer times, and download @@ -55,14 +53,6 @@ const TIME_UNITS = [ // with TIME_UNITS without the last item const TIME_SIZES = [60, 60, 24]; -const lazy = {}; -XPCOMUtils.defineLazyServiceGetter( - lazy, - "IDNService", - "@mozilla.org/network/idn-service;1", - Ci.nsIIDNService -); - var localeNumberFormatCache = new Map(); function getLocaleNumberFormat(fractionDigits) { if (!localeNumberFormatCache.has(fractionDigits)) { @@ -387,82 +377,6 @@ export var DownloadUtils = { }, /** - * Get the appropriate display host string for a URI string depending on if - * the URI has an eTLD + 1, is an IP address, a local file, or other protocol - * - * @param aURIString - * The URI string to try getting an eTLD + 1, etc. - * @return A pair: [display host for the URI string, full host name] - */ - getURIHost: function DU_getURIHost(aURIString) { - // Get a URI that knows about its components - let uri = URL.parse(aURIString)?.URI; - if (!uri) { - return ["", ""]; - } - - // Get the inner-most uri for schemes like jar: - if (uri instanceof Ci.nsINestedURI) { - uri = uri.innermostURI; - } - - if (uri.schemeIs("blob")) { - let origin = URL.fromURI(uri).origin; - // Origin can be "null" for blob URIs from a sandbox. - if (origin != "null") { - // `newURI` can throw (like for null) and throwing here breaks... - // a lot of stuff. So let's avoid doing that in case there are other - // edgecases we're missing here. - try { - uri = Services.io.newURI(origin); - } catch (ex) { - console.error(ex); - } - } - } - - let fullHost; - try { - // Get the full host name; some special URIs fail (data: jar:) - fullHost = uri.host; - } catch (e) { - fullHost = ""; - } - - let displayHost; - try { - // This might fail if it's an IP address or doesn't have more than 1 part - let baseDomain = Services.eTLD.getBaseDomain(uri); - - // Convert base domain for display - displayHost = lazy.IDNService.convertToDisplayIDN(baseDomain); - } catch (e) { - // Default to the host name - displayHost = fullHost; - } - - // Check if we need to show something else for the host - if (uri.schemeIs("file")) { - // Display special text for file protocol - displayHost = l10n.formatValueSync("download-utils-done-file-scheme"); - fullHost = displayHost; - } else if (!displayHost.length) { - // Got nothing; show the scheme (data: about: moz-icon:) - displayHost = l10n.formatValueSync("download-utils-done-scheme", { - scheme: uri.scheme, - }); - fullHost = displayHost; - } else if (uri.port != -1) { - // Tack on the port if it's not the default port - let port = ":" + uri.port; - displayHost += port; - fullHost += port; - } - - return [displayHost, fullHost]; - }, - - /** * Converts a number of bytes to the appropriate unit that results in an * internationalized number that needs fewer than 4 digits. * diff --git a/toolkit/mozapps/downloads/tests/browser/browser_unknownContentType_blob.js b/toolkit/mozapps/downloads/tests/browser/browser_unknownContentType_blob.js @@ -36,7 +36,7 @@ add_task(async function test_check_blob_origin_representation() { await check_blob_origin( "data:text/html,<body>Some Text<br>", "(data)", - "blob" + "(data)" ); }); diff --git a/toolkit/mozapps/downloads/tests/unit/test_DownloadUtils.js b/toolkit/mozapps/downloads/tests/unit/test_DownloadUtils.js @@ -83,16 +83,6 @@ function testFormattedTimeStatus(aSec, aExpected) { Assert.equal(status.l10n.id, aExpected); } -function testURI(aURI, aDisp, aHost) { - dump("URI Test: " + [aURI, aDisp, aHost] + "\n"); - - let [disp, host] = DownloadUtils.getURIHost(aURI); - - // Make sure we have the right display host and full host - Assert.equal(disp, aDisp); - Assert.equal(host, aHost); -} - function testGetReadableDates(aDate, aCompactValue) { const now = new Date(2000, 11, 31, 11, 59, 59); @@ -373,26 +363,5 @@ function run_test() { testFormattedTimeStatus(0, "downloading-file-opens-in-seconds-2"); testFormattedTimeStatus(30, "downloading-file-opens-in-seconds-2"); - testURI("http://www.mozilla.org/", "mozilla.org", "www.mozilla.org"); - testURI( - "http://www.city.mikasa.hokkaido.jp/", - "city.mikasa.hokkaido.jp", - "www.city.mikasa.hokkaido.jp" - ); - testURI("data:text/html,Hello World", "data resource", "data resource"); - testURI( - "jar:http://www.mozilla.com/file!/magic", - "mozilla.com", - "www.mozilla.com" - ); - testURI("file:///C:/Cool/Stuff/", "local file", "local file"); - // Don't test for moz-icon if we don't have a protocol handler for it (e.g. b2g): - if ("@mozilla.org/network/protocol;1?name=moz-icon" in Cc) { - testURI("moz-icon:file:///test.extension", "local file", "local file"); - testURI("moz-icon://.extension", "moz-icon resource", "moz-icon resource"); - } - testURI("about:config", "about resource", "about resource"); - testURI("invalid.uri", "", ""); - testAllGetReadableDates(); }