commit b656ac6c1251ce1d28760cb9eca3b8ebc11b8f0e
parent ffcae969c4f8f28ed884e504b6142e5c1eb7f7bf
Author: Mark Banner <standard8@mozilla.com>
Date: Fri, 31 Oct 2025 14:52:05 +0000
Bug 1996615 - Fix type issues and add more type information to UrlbarValueFormatter.sys.mjs. r=urlbar-reviewers,daisuke
Differential Revision: https://phabricator.services.mozilla.com/D270207
Diffstat:
2 files changed, 166 insertions(+), 113 deletions(-)
diff --git a/browser/components/urlbar/UrlbarValueFormatter.sys.mjs b/browser/components/urlbar/UrlbarValueFormatter.sys.mjs
@@ -2,9 +2,9 @@
* 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/. */
-const lazy = {};
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
-ChromeUtils.defineESModuleGetters(lazy, {
+const lazy = XPCOMUtils.declareLazy({
BrowserUIUtils: "resource:///modules/BrowserUIUtils.sys.mjs",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
UrlbarPrefs: "moz-src:///browser/components/urlbar/UrlbarPrefs.sys.mjs",
@@ -21,38 +21,24 @@ export class UrlbarValueFormatter {
* The parent instance of UrlbarInput
*/
constructor(urlbarInput) {
- this.urlbarInput = urlbarInput;
- this.window = this.urlbarInput.window;
- this.document = this.window.document;
+ this.#urlbarInput = urlbarInput;
- // This is used only as an optimization to avoid removing formatting in
- // the _remove* format methods when no formatting is actually applied.
- this._formattingApplied = false;
-
- this.window.addEventListener("resize", this);
- }
-
- get inputField() {
- return this.urlbarInput.inputField;
- }
-
- get scheme() {
- return this.urlbarInput.querySelector("#urlbar-scheme");
+ this.#window.addEventListener("resize", this);
}
async update() {
- let instance = (this._updateInstance = {});
+ let instance = (this.#updateInstance = {});
- // _getUrlMetaData does URI fixup, which depends on the search service, so
+ // #getUrlMetaData does URI fixup, which depends on the search service, so
// make sure it's initialized, or URIFixup may force synchronous
// initialization. It can be uninitialized here on session restore. Skip
// this if the service is already initialized in order to avoid the async
// call in the common case. However, we can't access Service.search before
// first paint (delayed startup) because there's a performance test that
// prohibits it, so first await delayed startup.
- if (!this.window.gBrowserInit.delayedStartupFinished) {
- await this.window.delayedStartupPromise;
- if (this._updateInstance != instance) {
+ if (!this.#window.gBrowserInit.delayedStartupFinished) {
+ await this.#window.delayedStartupPromise;
+ if (this.#updateInstance != instance) {
return;
}
}
@@ -61,39 +47,62 @@ export class UrlbarValueFormatter {
await Services.search.init();
} catch {}
- if (this._updateInstance != instance) {
+ if (this.#updateInstance != instance) {
return;
}
}
// If this window is being torn down, stop here
- if (!this.window.docShell) {
+ if (!this.#window.docShell) {
return;
}
// Cleanup that must be done in any case, even if there's no value.
- this.urlbarInput.removeAttribute("domaindir");
- this.scheme.value = "";
+ this.#urlbarInput.removeAttribute("domaindir");
+ this.#scheme.value = "";
- if (!this.urlbarInput.value) {
+ if (!this.#urlbarInput.value) {
return;
}
// Remove the current formatting.
- this._removeURLFormat();
- this._removeSearchAliasFormat();
+ this.#removeURLFormat();
+ this.#removeSearchAliasFormat();
// Apply new formatting. Formatter methods should return true if they
// successfully formatted the value and false if not. We apply only
// one formatter at a time, so we stop at the first successful one.
- this.window.requestAnimationFrame(() => {
- if (this._updateInstance != instance) {
+ this.#window.requestAnimationFrame(() => {
+ if (this.#updateInstance != instance) {
return;
}
- this._formattingApplied = this._formatURL() || this._formatSearchAlias();
+ this.#formattingApplied = this.#formatURL() || this.#formatSearchAlias();
});
}
+ /**
+ * The parent instance of UrlbarInput
+ */
+ #urlbarInput;
+
+ get #document() {
+ return this.#urlbarInput.document;
+ }
+
+ get #inputField() {
+ return this.#urlbarInput.inputField;
+ }
+
+ get #window() {
+ return this.#urlbarInput.window;
+ }
+
+ get #scheme() {
+ return /** @type {HTMLInputElement} */ (
+ this.#urlbarInput.querySelector("#urlbar-scheme")
+ );
+ }
+
#ensureFormattedHostVisible(urlMetaData) {
// Make sure the host is always visible. Since it is aligned on
// the first strong directional character, we set scrollLeft
@@ -102,39 +111,39 @@ export class UrlbarValueFormatter {
// In the future, for example in bug 525831, we may add a forceRTL
// char just after the domain, and in such a case we should not
// scroll to the left.
- urlMetaData = urlMetaData || this._getUrlMetaData();
+ urlMetaData = urlMetaData || this.#getUrlMetaData();
if (!urlMetaData) {
- this.urlbarInput.removeAttribute("domaindir");
+ this.#urlbarInput.removeAttribute("domaindir");
return;
}
let { url, preDomain, domain } = urlMetaData;
- let directionality = this.window.windowUtils.getDirectionFromText(domain);
+ let directionality = this.#window.windowUtils.getDirectionFromText(domain);
if (
- directionality == this.window.windowUtils.DIRECTION_RTL &&
+ directionality == this.#window.windowUtils.DIRECTION_RTL &&
url[preDomain.length + domain.length] != "\u200E"
) {
- this.urlbarInput.setAttribute("domaindir", "rtl");
- this.inputField.scrollLeft = this.inputField.scrollLeftMax;
+ this.#urlbarInput.setAttribute("domaindir", "rtl");
+ this.#inputField.scrollLeft = this.#inputField.scrollLeftMax;
} else {
- this.urlbarInput.setAttribute("domaindir", "ltr");
- this.inputField.scrollLeft = 0;
+ this.#urlbarInput.setAttribute("domaindir", "ltr");
+ this.#inputField.scrollLeft = 0;
}
- this.urlbarInput.updateTextOverflow();
+ this.#urlbarInput.updateTextOverflow();
}
- _getUrlMetaData() {
- if (this.urlbarInput.focused) {
+ #getUrlMetaData() {
+ if (this.#urlbarInput.focused) {
return null;
}
- let inputValue = this.urlbarInput.value;
+ let inputValue = this.#urlbarInput.value;
// getFixupURIInfo logs an error if the URL is empty. Avoid that by
// returning early.
if (!inputValue) {
return null;
}
- let browser = this.window.gBrowser.selectedBrowser;
- let browserState = this.urlbarInput.getBrowserState(browser);
+ let browser = this.#window.gBrowser.selectedBrowser;
+ let browserState = this.#urlbarInput.getBrowserState(browser);
// Since doing a full URIFixup and offset calculations is expensive, we
// keep the metadata cached in the browser itself, so when switching tabs
@@ -142,13 +151,14 @@ export class UrlbarValueFormatter {
if (
browserState.urlMetaData &&
browserState.urlMetaData.inputValue == inputValue &&
- browserState.urlMetaData.untrimmedValue == this.urlbarInput.untrimmedValue
+ browserState.urlMetaData.untrimmedValue ==
+ this.#urlbarInput.untrimmedValue
) {
return browserState.urlMetaData.data;
}
browserState.urlMetaData = {
inputValue,
- untrimmedValue: this.urlbarInput.untrimmedValue,
+ untrimmedValue: this.#urlbarInput.untrimmedValue,
data: null,
};
@@ -156,14 +166,14 @@ export class UrlbarValueFormatter {
let flags =
Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS |
Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
- if (lazy.PrivateBrowsingUtils.isWindowPrivate(this.window)) {
+ if (lazy.PrivateBrowsingUtils.isWindowPrivate(this.#window)) {
flags |= Services.uriFixup.FIXUP_FLAG_PRIVATE_CONTEXT;
}
let uriInfo;
try {
uriInfo = Services.uriFixup.getFixupURIInfo(
- this.urlbarInput.untrimmedValue,
+ this.#urlbarInput.untrimmedValue,
flags
);
} catch (ex) {}
@@ -187,7 +197,7 @@ export class UrlbarValueFormatter {
let trimmedLength = 0;
let trimmedProtocol = lazy.BrowserUIUtils.trimURLProtocol;
if (
- this.urlbarInput.untrimmedValue.startsWith(trimmedProtocol) &&
+ this.#urlbarInput.untrimmedValue.startsWith(trimmedProtocol) &&
!inputValue.startsWith(trimmedProtocol)
) {
// The protocol has been trimmed, so we add it back.
@@ -227,17 +237,17 @@ export class UrlbarValueFormatter {
return null;
}
if (replaceUrl) {
- if (this._inGetUrlMetaData) {
+ if (this.#inGetUrlMetaData) {
// Protect from infinite recursion.
return null;
}
try {
- this._inGetUrlMetaData = true;
- this.window.gBrowser.userTypedValue = null;
- this.urlbarInput.setURI(uriInfo.fixedURI);
- return this._getUrlMetaData();
+ this.#inGetUrlMetaData = true;
+ this.#window.gBrowser.userTypedValue = null;
+ this.#urlbarInput.setURI(uriInfo.fixedURI);
+ return this.#getUrlMetaData();
} finally {
- this._inGetUrlMetaData = false;
+ this.#inGetUrlMetaData = false;
}
}
@@ -251,18 +261,18 @@ export class UrlbarValueFormatter {
});
}
- _removeURLFormat() {
- if (!this._formattingApplied) {
+ #removeURLFormat() {
+ if (!this.#formattingApplied) {
return;
}
- let controller = this.urlbarInput.editor.selectionController;
+ let controller = this.#urlbarInput.editor.selectionController;
let strikeOut = controller.getSelection(controller.SELECTION_URLSTRIKEOUT);
strikeOut.removeAllRanges();
let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
selection.removeAllRanges();
- this._formatScheme(controller.SELECTION_URLSTRIKEOUT, true);
- this._formatScheme(controller.SELECTION_URLSECONDARY, true);
- this.inputField.style.setProperty("--urlbar-scheme-size", "0px");
+ this.#formatScheme(controller.SELECTION_URLSTRIKEOUT, true);
+ this.#formatScheme(controller.SELECTION_URLSECONDARY, true);
+ this.#inputField.style.setProperty("--urlbar-scheme-size", "0px");
}
/**
@@ -288,21 +298,66 @@ export class UrlbarValueFormatter {
this.formattingEnabled &&
!lazy.UrlbarPrefs.get("security.insecure_connection_text.enabled") &&
val.startsWith("https://") &&
- val == this.urlbarInput.value &&
+ val == this.#urlbarInput.value &&
this.#showingMixedContentLoadedPageUrl
);
}
/**
+ * This is used only as an optimization to avoid removing formatting in
+ * the _remove* format methods when no formatting is actually applied.
+ *
+ * @type {boolean}
+ */
+ #formattingApplied = false;
+
+ /**
+ * An empty object, which is used as a lock to avoid updating old instances.
+ *
+ * @type {?object}
+ */
+ #updateInstance;
+
+ /**
+ * The previously selected result.
+ *
+ * @type {?UrlbarResult}
+ */
+ #selectedResult;
+
+ /**
+ * The timer handling the resize throttling.
+ *
+ * @type {?number}
+ */
+ #resizeThrottleTimeout;
+
+ /**
+ * An empty object, which is used to avoid updating old instances.
+ *
+ * @type {?object}
+ */
+ #resizeInstance;
+
+ /**
+ * Used to protect against re-entry in getUrlMetaData.
+ *
+ * @type {boolean}
+ */
+ #inGetUrlMetaData = false;
+
+ /**
* Whether the currently loaded page is in mixed content mode.
*
* @returns {boolean} whether the loaded page has active mixed content.
*/
get #showingMixedContentLoadedPageUrl() {
return (
- this.urlbarInput.getAttribute("pageproxystate") == "valid" &&
- this.window.gBrowser.securityUI.state &
+ this.#urlbarInput.getAttribute("pageproxystate") == "valid" &&
+ !!(
+ this.#window.gBrowser.securityUI.state &
Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT
+ )
);
}
@@ -315,13 +370,13 @@ export class UrlbarValueFormatter {
* @returns {boolean}
* True if formatting was applied and false if not.
*/
- _formatURL() {
- let urlMetaData = this._getUrlMetaData();
+ #formatURL() {
+ let urlMetaData = this.#getUrlMetaData();
if (!urlMetaData) {
return false;
}
- let state = this.urlbarInput.getBrowserState(
- this.window.gBrowser.selectedBrowser
+ let state = this.#urlbarInput.getBrowserState(
+ this.#window.gBrowser.selectedBrowser
);
if (state.searchTerms) {
return false;
@@ -343,10 +398,10 @@ export class UrlbarValueFormatter {
if (
!lazy.UrlbarPrefs.get("security.insecure_connection_text.enabled") &&
!isUnformattedMixedContent &&
- this.urlbarInput.value.startsWith(schemeWSlashes)
+ this.#urlbarInput.value.startsWith(schemeWSlashes)
) {
- this.scheme.value = schemeWSlashes;
- this.inputField.style.setProperty(
+ this.#scheme.value = schemeWSlashes;
+ this.#inputField.style.setProperty(
"--urlbar-scheme-size",
schemeWSlashes.length + "ch"
);
@@ -358,24 +413,24 @@ export class UrlbarValueFormatter {
return false;
}
- let editor = this.urlbarInput.editor;
+ let editor = this.#urlbarInput.editor;
let controller = editor.selectionController;
- this._formatScheme(controller.SELECTION_URLSECONDARY);
+ this.#formatScheme(controller.SELECTION_URLSECONDARY);
let textNode = editor.rootElement.firstChild;
// Strike out the "https" part if mixed active content status should be
// shown.
- if (this.willShowFormattedMixedContentProtocol(this.urlbarInput.value)) {
- let range = this.document.createRange();
+ if (this.willShowFormattedMixedContentProtocol(this.#urlbarInput.value)) {
+ let range = this.#document.createRange();
range.setStart(textNode, 0);
range.setEnd(textNode, 5);
let strikeOut = controller.getSelection(
controller.SELECTION_URLSTRIKEOUT
);
strikeOut.addRange(range);
- this._formatScheme(controller.SELECTION_URLSTRIKEOUT);
+ this.#formatScheme(controller.SELECTION_URLSTRIKEOUT);
}
let baseDomain = domain;
@@ -400,7 +455,7 @@ export class UrlbarValueFormatter {
let rangeLength = preDomain.length + subDomain.length - trimmedLength;
if (rangeLength) {
- let range = this.document.createRange();
+ let range = this.#document.createRange();
range.setStart(textNode, 0);
range.setEnd(textNode, rangeLength);
selection.addRange(range);
@@ -408,7 +463,7 @@ export class UrlbarValueFormatter {
let startRest = preDomain.length + domain.length - trimmedLength;
if (startRest < url.length - trimmedLength) {
- let range = this.document.createRange();
+ let range = this.#document.createRange();
range.setStart(textNode, startRest);
range.setEnd(textNode, url.length - trimmedLength);
selection.addRange(range);
@@ -417,26 +472,26 @@ export class UrlbarValueFormatter {
return true;
}
- _formatScheme(selectionType, clear) {
- let editor = this.scheme.editor;
+ #formatScheme(selectionType, clear) {
+ let editor = this.#scheme.editor;
let controller = editor.selectionController;
let textNode = editor.rootElement.firstChild;
let selection = controller.getSelection(selectionType);
if (clear) {
selection.removeAllRanges();
} else {
- let r = this.document.createRange();
+ let r = this.#document.createRange();
r.setStart(textNode, 0);
r.setEnd(textNode, textNode.textContent.length);
selection.addRange(r);
}
}
- _removeSearchAliasFormat() {
- if (!this._formattingApplied) {
+ #removeSearchAliasFormat() {
+ if (!this.#formattingApplied) {
return;
}
- let selection = this.urlbarInput.editor.selectionController.getSelection(
+ let selection = this.#urlbarInput.editor.selectionController.getSelection(
Ci.nsISelectionController.SELECTION_FIND
);
selection.removeAllRanges();
@@ -448,25 +503,24 @@ export class UrlbarValueFormatter {
* @returns {boolean}
* True if formatting was applied and false if not.
*/
- _formatSearchAlias() {
+ #formatSearchAlias() {
if (!this.formattingEnabled) {
return false;
}
- let editor = this.urlbarInput.editor;
+ let editor = this.#urlbarInput.editor;
let textNode = editor.rootElement.firstChild;
let value = textNode.textContent;
let trimmedValue = value.trim();
if (
!trimmedValue.startsWith("@") ||
- (this.urlbarInput.popup || this.urlbarInput.view).oneOffSearchButtons
- .selectedButton
+ this.#urlbarInput.view.oneOffSearchButtons.selectedButton
) {
return false;
}
- let alias = this._findEngineAliasOrRestrictKeyword();
+ let alias = this.#findEngineAliasOrRestrictKeyword();
if (!alias) {
return false;
}
@@ -491,7 +545,7 @@ export class UrlbarValueFormatter {
Ci.nsISelectionController.SELECTION_FIND
);
- let range = this.document.createRange();
+ let range = this.#document.createRange();
range.setStart(textNode, index);
range.setEnd(textNode, index + alias.length);
selection.addRange(range);
@@ -511,8 +565,8 @@ export class UrlbarValueFormatter {
// them, which we can do by passing "currentColor". See
// nsTextPaintStyle::GetHighlightColors for details.
if (
- this.document.documentElement.hasAttribute("lwtheme") ||
- this.window.matchMedia("(prefers-contrast)").matches
+ this.#document.documentElement.hasAttribute("lwtheme") ||
+ this.#window.matchMedia("(prefers-contrast)").matches
) {
// non-default theme(s)
selection.setColors(fg, bg, "currentColor", "currentColor");
@@ -524,24 +578,24 @@ export class UrlbarValueFormatter {
return true;
}
- _findEngineAliasOrRestrictKeyword() {
+ #findEngineAliasOrRestrictKeyword() {
// To determine whether the input contains a valid alias, check if the
// selected result is a search result with an alias. If there is no selected
// result, we check the first result in the view, for cases when we do not
// highlight token alias results. The selected result is null when the popup
// is closed, but we want to continue highlighting the alias when the popup
// is closed, and that's why we keep around the previously selected result
- // in _selectedResult.
- this._selectedResult =
- this.urlbarInput.view.selectedResult ||
- this.urlbarInput.view.getResultAtIndex(0) ||
- this._selectedResult;
+ // in #selectedResult.
+ this.#selectedResult =
+ this.#urlbarInput.view.selectedResult ||
+ this.#urlbarInput.view.getResultAtIndex(0) ||
+ this.#selectedResult;
- if (!this._selectedResult) {
+ if (!this.#selectedResult) {
return null;
}
- let { type, payload } = this._selectedResult;
+ let { type, payload } = this.#selectedResult;
if (type === lazy.UrlbarUtils.RESULT_TYPE.SEARCH) {
return payload.keyword || null;
@@ -570,20 +624,20 @@ export class UrlbarValueFormatter {
}
_on_resize(event) {
- if (event.target != this.window) {
+ if (event.target != this.#window) {
return;
}
// Make sure the host remains visible in the input field when the window is
// resized. We don't want to hurt resize performance though, so do this
// only after resize events have stopped and a small timeout has elapsed.
- if (this._resizeThrottleTimeout) {
- this.window.clearTimeout(this._resizeThrottleTimeout);
- }
- this._resizeThrottleTimeout = this.window.setTimeout(() => {
- this._resizeThrottleTimeout = null;
- let instance = (this._resizeInstance = {});
- this.window.requestAnimationFrame(() => {
- if (instance == this._resizeInstance) {
+ if (this.#resizeThrottleTimeout) {
+ this.#window.clearTimeout(this.#resizeThrottleTimeout);
+ }
+ this.#resizeThrottleTimeout = this.#window.setTimeout(() => {
+ this.#resizeThrottleTimeout = null;
+ let instance = (this.#resizeInstance = {});
+ this.#window.requestAnimationFrame(() => {
+ if (instance == this.#resizeInstance) {
this.#ensureFormattedHostVisible();
}
});
diff --git a/browser/components/urlbar/tsconfig.json b/browser/components/urlbar/tsconfig.json
@@ -6,7 +6,6 @@
"UrlbarProviderPlaces.sys.mjs",
"UrlbarProvidersManager.sys.mjs",
"UrlbarSearchOneOffs.sys.mjs",
- "UrlbarValueFormatter.sys.mjs",
"UrlbarView.sys.mjs",
"private/MLSuggest.sys.mjs",
"private/SuggestBackendRust.sys.mjs",