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:
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();
}