commit 5bd38320f8e67fcfd65eb4c1b7e5770523cd5c52 parent 4e6cad19475fbd99bd6c10efa73db9dac22b2ebc Author: Narcis Beleuzu <nbeleuzu@mozilla.com> Date: Sat, 15 Nov 2025 00:43:26 +0200 Revert "Bug 1975762 - Tests for setting-pane sub pages r=desktop-theme-reviewers,mconley,tgiles" for causing mochites failure on browser_HCM_telemetry.js This reverts commit 12b4adb7efa5bbcd4eff9522044c2fef91d84af8. This reverts commit 34b94c2ba9563b11b24f89a825b0254e3a0f64aa. This reverts commit d2e94fc8ba3c0914e6393092a8bbbb1e7006fcbb. Diffstat:
25 files changed, 693 insertions(+), 1320 deletions(-)
diff --git a/browser/components/preferences/findInPage.js b/browser/components/preferences/findInPage.js @@ -9,12 +9,9 @@ // inside the button, which allows the text to be highlighted when the user // is searching. -/** @import MozInputSearch from "chrome://global/content/elements/moz-input-search.mjs" */ - -const MozButtonClass = customElements.get("button"); -class HighlightableButton extends MozButtonClass { +const MozButton = customElements.get("button"); +class HighlightableButton extends MozButton { static get inheritedAttributes() { - // @ts-expect-error super is MozButton from toolkit/content/widgets/button.js return Object.assign({}, super.inheritedAttributes, { ".button-text": "text=label,accesskey,crop", }); @@ -25,14 +22,9 @@ customElements.define("highlightable-button", HighlightableButton, { }); var gSearchResultsPane = { - /** @type {string} */ - query: undefined, listSearchTooltips: new Set(), listSearchMenuitemIndicators: new Set(), - /** @type {MozInputSearch} */ searchInput: null, - /** @type {HTMLDivElement} */ - searchTooltipContainer: null, // A map of DOM Elements to a string of keywords used in search // XXX: We should invalidate this cache on `intl:app-locales-changed` searchKeywords: new WeakMap(), @@ -57,11 +49,9 @@ var gSearchResultsPane = { return; } this.inited = true; - this.searchInput = /** @type {MozInputSearch} */ ( - document.getElementById("searchInput") - ); - this.searchTooltipContainer = /** @type {HTMLDivElement} */ ( - document.getElementById("search-tooltip-container") + this.searchInput = document.getElementById("searchInput"); + this.searchTooltipContainer = document.getElementById( + "search-tooltip-container" ); window.addEventListener("resize", () => { @@ -81,7 +71,6 @@ var gSearchResultsPane = { ensureScrollPadding(); }, - /** @param {InputEvent} event */ async handleEvent(event) { // Ensure categories are initialized if idle callback didn't run sooo enough. await this.initializeCategories(); diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js @@ -2,6 +2,10 @@ * 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 MozButton from "chrome://global/content/elements/moz-button.mjs"; */ +/** @import { SettingGroup } from "./widgets/setting-group/setting-group.mjs" */ +/** @import { PreferencesSettingsConfig } from "chrome://global/content/preferences/Preferences.mjs" */ + /* import-globals-from extensionControlled.js */ /* import-globals-from preferences.js */ /* import-globals-from /toolkit/mozapps/preferences/fontbuilder.js */ @@ -224,122 +228,104 @@ Preferences.addSetting({ pref: "browser.privatebrowsing.autostart", }); -Preferences.addSetting( - /** @type {{ _getLaunchOnLoginApprovedCachedValue: boolean } & SettingConfig} */ ({ - id: "launchOnLoginApproved", - _getLaunchOnLoginApprovedCachedValue: true, - get() { - return this._getLaunchOnLoginApprovedCachedValue; - }, - // Check for a launch on login registry key - // This accounts for if a user manually changes it in the registry - // Disabling in Task Manager works outside of just deleting the registry key - // in HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run - // but it is not possible to change it back to enabled as the disabled value is just a random - // hexadecimal number - setup() { - if (AppConstants.platform !== "win") { - /** - * WindowsLaunchOnLogin isnt available if not on windows - * but this setup function still fires, so must prevent - * WindowsLaunchOnLogin.getLaunchOnLoginApproved - * below from executing unnecessarily. - */ - return; - } - // @ts-ignore bug 1996860 - WindowsLaunchOnLogin.getLaunchOnLoginApproved().then(val => { - this._getLaunchOnLoginApprovedCachedValue = val; - }); - }, - }) -); +Preferences.addSetting({ + id: "launchOnLoginApproved", + _getLaunchOnLoginApprovedCachedValue: true, + get() { + return this._getLaunchOnLoginApprovedCachedValue; + }, + // Check for a launch on login registry key + // This accounts for if a user manually changes it in the registry + // Disabling in Task Manager works outside of just deleting the registry key + // in HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run + // but it is not possible to change it back to enabled as the disabled value is just a random + // hexadecimal number + async setup() { + if (AppConstants.platform !== "win") { + /** + * WindowsLaunchOnLogin isnt available if not on windows + * but this setup function still fires, so must prevent + * WindowsLaunchOnLogin.getLaunchOnLoginApproved + * below from executing unnecessarily. + */ + return; + } + this._getLaunchOnLoginApprovedCachedValue = + await WindowsLaunchOnLogin.getLaunchOnLoginApproved(); + }, +}); Preferences.addSetting({ id: "windowsLaunchOnLoginEnabled", pref: "browser.startup.windowsLaunchOnLogin.enabled", }); -Preferences.addSetting( - /** @type {{_getLaunchOnLoginEnabledValue: boolean, startWithLastProfile: boolean} & SettingConfig} */ ({ - id: "windowsLaunchOnLogin", - deps: ["launchOnLoginApproved", "windowsLaunchOnLoginEnabled"], - _getLaunchOnLoginEnabledValue: false, - get startWithLastProfile() { - return Cc["@mozilla.org/toolkit/profile-service;1"].getService( - Ci.nsIToolkitProfileService - ).startWithLastProfile; - }, - get() { - return this._getLaunchOnLoginEnabledValue; - }, - setup(emitChange) { - if (AppConstants.platform !== "win") { - /** - * WindowsLaunchOnLogin isnt available if not on windows - * but this setup function still fires, so must prevent - * WindowsLaunchOnLogin.getLaunchOnLoginEnabled - * below from executing unnecessarily. - */ - return; - } +Preferences.addSetting({ + id: "windowsLaunchOnLogin", + deps: ["launchOnLoginApproved", "windowsLaunchOnLoginEnabled"], + _getLaunchOnLoginEnabledValue: false, + get startWithLastProfile() { + return Cc["@mozilla.org/toolkit/profile-service;1"].getService( + Ci.nsIToolkitProfileService + ).startWithLastProfile; + }, + get() { + return this._getLaunchOnLoginEnabledValue; + }, + async setup(emitChange) { + if (AppConstants.platform !== "win") { + /** + * WindowsLaunchOnLogin isnt available if not on windows + * but this setup function still fires, so must prevent + * WindowsLaunchOnLogin.getLaunchOnLoginEnabled + * below from executing unnecessarily. + */ + return; + } - /** @type {boolean} */ - let getLaunchOnLoginEnabledValue; - let maybeEmitChange = () => { - if ( - getLaunchOnLoginEnabledValue !== this._getLaunchOnLoginEnabledValue - ) { - this._getLaunchOnLoginEnabledValue = getLaunchOnLoginEnabledValue; - emitChange(); - } - }; - if (!this.startWithLastProfile) { - getLaunchOnLoginEnabledValue = false; - maybeEmitChange(); - } else { - // @ts-ignore bug 1996860 - WindowsLaunchOnLogin.getLaunchOnLoginEnabled().then(val => { - getLaunchOnLoginEnabledValue = val; - maybeEmitChange(); - }); - } - }, - visible: ({ windowsLaunchOnLoginEnabled }) => { - let isVisible = - AppConstants.platform === "win" && windowsLaunchOnLoginEnabled.value; - if (isVisible) { - // @ts-ignore bug 1996860 - NimbusFeatures.windowsLaunchOnLogin.recordExposureEvent({ - once: true, - }); - } - return isVisible; - }, - disabled({ launchOnLoginApproved }) { - return !this.startWithLastProfile || !launchOnLoginApproved.value; - }, - onUserChange(checked) { - if (checked) { - // windowsLaunchOnLogin has been checked: create registry key or shortcut - // The shortcut is created with the same AUMID as Firefox itself. However, - // this is not set during browser tests and the fallback of checking the - // registry fails. As such we pass an arbitrary AUMID for the purpose - // of testing. - // @ts-ignore bug 1996860 - WindowsLaunchOnLogin.createLaunchOnLogin(); - Services.prefs.setBoolPref( - "browser.startup.windowsLaunchOnLogin.disableLaunchOnLoginPrompt", - true - ); - } else { - // windowsLaunchOnLogin has been unchecked: delete registry key and shortcut - // @ts-ignore bug 1996860 - WindowsLaunchOnLogin.removeLaunchOnLogin(); - } - }, - }) -); + let getLaunchOnLoginEnabledValue; + if (!this.startWithLastProfile) { + getLaunchOnLoginEnabledValue = false; + } else { + getLaunchOnLoginEnabledValue = + await WindowsLaunchOnLogin.getLaunchOnLoginEnabled(); + } + if (getLaunchOnLoginEnabledValue !== this._getLaunchOnLoginEnabledValue) { + this._getLaunchOnLoginEnabledValue = getLaunchOnLoginEnabledValue; + emitChange(); + } + }, + visible: ({ windowsLaunchOnLoginEnabled }) => { + let isVisible = + AppConstants.platform === "win" && windowsLaunchOnLoginEnabled.value; + if (isVisible) { + NimbusFeatures.windowsLaunchOnLogin.recordExposureEvent({ + once: true, + }); + } + return isVisible; + }, + disabled({ launchOnLoginApproved }) { + return !this.startWithLastProfile || !launchOnLoginApproved.value; + }, + onUserChange(checked) { + if (checked) { + // windowsLaunchOnLogin has been checked: create registry key or shortcut + // The shortcut is created with the same AUMID as Firefox itself. However, + // this is not set during browser tests and the fallback of checking the + // registry fails. As such we pass an arbitrary AUMID for the purpose + // of testing. + WindowsLaunchOnLogin.createLaunchOnLogin(); + Services.prefs.setBoolPref( + "browser.startup.windowsLaunchOnLogin.disableLaunchOnLoginPrompt", + true + ); + } else { + // windowsLaunchOnLogin has been unchecked: delete registry key and shortcut + WindowsLaunchOnLogin.removeLaunchOnLogin(); + } + }, +}); Preferences.addSetting({ id: "windowsLaunchOnLoginDisabledProfileBox", @@ -407,7 +393,6 @@ Preferences.addSetting({ if (checked) { // We need to restore the blank homepage setting in our other pref if (startupPref.value === gMainPane.STARTUP_PREF_BLANK) { - // @ts-ignore bug 1996860 HomePage.safeSet("about:blank"); } newValue = gMainPane.STARTUP_PREF_RESTORE_SESSION; @@ -446,56 +431,51 @@ Preferences.addSetting({ id: "useCursorNavigation", pref: "accessibility.browsewithcaret", }); -Preferences.addSetting( - /** @type {{ _storedFullKeyboardNavigation: number } & SettingConfig} */ ({ - _storedFullKeyboardNavigation: -1, - id: "useFullKeyboardNavigation", - pref: "accessibility.tabfocus", - visible: () => AppConstants.platform == "macosx", - /** - * Returns true if any full keyboard nav is enabled and false otherwise, caching - * the current value to enable proper pref restoration if the checkbox is - * never changed. - * - * accessibility.tabfocus - * - an integer controlling the focusability of: - * 1 text controls - * 2 form elements - * 4 links - * 7 all of the above - */ - get(prefVal) { - this._storedFullKeyboardNavigation = prefVal; - return prefVal == 7; - }, - /** - * Returns the value of the full keyboard nav preference represented by UI, - * preserving the preference's "hidden" value if the preference is - * unchanged and represents a value not strictly allowed in UI. - */ - set(checked) { - if (checked) { - return 7; - } - if (this._storedFullKeyboardNavigation != 7) { - // 1/2/4 values set via about:config should persist - return this._storedFullKeyboardNavigation; - } - // When the checkbox is unchecked, default to just text controls. - return 1; - }, - }) -); +Preferences.addSetting({ + id: "useFullKeyboardNavigation", + pref: "accessibility.tabfocus", + visible: () => AppConstants.platform == "macosx", + /** + * Returns true if any full keyboard nav is enabled and false otherwise, caching + * the current value to enable proper pref restoration if the checkbox is + * never changed. + * + * accessibility.tabfocus + * - an integer controlling the focusability of: + * 1 text controls + * 2 form elements + * 4 links + * 7 all of the above + */ + get(prefVal) { + this._storedFullKeyboardNavigation = prefVal; + return prefVal == 7; + }, + /** + * Returns the value of the full keyboard nav preference represented by UI, + * preserving the preference's "hidden" value if the preference is + * unchanged and represents a value not strictly allowed in UI. + */ + set(checked) { + if (checked) { + return 7; + } + if (this._storedFullKeyboardNavigation != 7) { + // 1/2/4 values set via about:config should persist + return this._storedFullKeyboardNavigation; + } + // When the checkbox is unchecked, default to just text controls. + return 1; + }, +}); Preferences.addSetting({ id: "linkPreviewEnabled", pref: "browser.ml.linkPreview.enabled", - // @ts-ignore bug 1996860 visible: () => LinkPreview.canShowPreferences, }); Preferences.addSetting({ id: "linkPreviewKeyPoints", pref: "browser.ml.linkPreview.optin", - // @ts-ignore bug 1996860 visible: () => LinkPreview.canShowKeyPoints, }); Preferences.addSetting({ @@ -552,46 +532,38 @@ Preferences.addSetting({ }, }); -Preferences.addSetting( - /** @type {{ themeNames: string[] } & SettingConfig}} */ ({ - id: "web-appearance-chooser", - themeNames: ["dark", "light", "auto"], - pref: "layout.css.prefers-color-scheme.content-override", - setup(emitChange) { - Services.obs.addObserver(emitChange, "look-and-feel-changed"); - return () => - Services.obs.removeObserver(emitChange, "look-and-feel-changed"); - }, - get(val, _, setting) { - return ( - this.themeNames[val] || - this.themeNames[/** @type {number} */ (setting.pref.defaultValue)] - ); - }, - /** @param {string} val */ - set(val) { - return this.themeNames.indexOf(val); - }, - getControlConfig(config) { - // Set the auto theme image to the light/dark that matches. - let systemThemeIndex = Services.appinfo - .contentThemeDerivedColorSchemeIsDark - ? 2 - : 1; - config.options[0].controlAttrs = { - ...config.options[0].controlAttrs, - imagesrc: config.options[systemThemeIndex].controlAttrs.imagesrc, - }; - return config; - }, - }) -); +Preferences.addSetting({ + id: "web-appearance-chooser", + themeNames: ["dark", "light", "auto"], + pref: "layout.css.prefers-color-scheme.content-override", + setup(emitChange) { + Services.obs.addObserver(emitChange, "look-and-feel-changed"); + return () => + Services.obs.removeObserver(emitChange, "look-and-feel-changed"); + }, + get(val, _, setting) { + return this.themeNames[val] || this.themeNames[setting.pref.defaultValue]; + }, + set(val) { + return this.themeNames.indexOf(val); + }, + getControlConfig(config) { + // Set the auto theme image to the light/dark that matches. + let systemThemeIndex = Services.appinfo.contentThemeDerivedColorSchemeIsDark + ? 2 + : 1; + config.options[0].controlAttrs = { + ...config.options[0].controlAttrs, + imagesrc: config.options[systemThemeIndex].controlAttrs.imagesrc, + }; + return config; + }, +}); Preferences.addSetting({ id: "web-appearance-manage-themes-link", onUserClick: e => { e.preventDefault(); - // @ts-ignore topChromeWindow global window.browsingContext.topChromeWindow.BrowserAddonUI.openAddonsMgr( "addons://list/theme" ); @@ -921,11 +893,7 @@ const DefaultBrowserHelper = { * @type {typeof import('../shell/ShellService.sys.mjs').ShellService | undefined} */ get shellSvc() { - return ( - AppConstants.HAVE_SHELL_SERVICE && - // @ts-ignore from utilityOverlay.js - getShellService() - ); + return AppConstants.HAVE_SHELL_SERVICE && getShellService(); }, /** @@ -1049,7 +1017,7 @@ Preferences.addSetting({ * browser is already the default browser. */ visible: () => DefaultBrowserHelper.canCheck, - disabled: (_, setting) => + disabled: (deps, setting) => !DefaultBrowserHelper.canCheck || setting.locked || DefaultBrowserHelper.isBrowserDefault, @@ -1127,7 +1095,10 @@ Preferences.addSetting({ }, }); -SettingGroupManager.registerGroups({ +/** + * @type {Record<string, PreferencesSettingsConfig>} SettingConfig + */ +let SETTINGS_CONFIG = { containers: { // This section is marked as in progress for testing purposes inProgress: true, @@ -1878,7 +1849,7 @@ SettingGroupManager.registerGroups({ }, ], }, -}); +}; /** * @param {string} id - ID of {@link SettingGroup} custom element. @@ -1886,7 +1857,7 @@ SettingGroupManager.registerGroups({ function initSettingGroup(id) { /** @type {SettingGroup} */ let group = document.querySelector(`setting-group[groupid=${id}]`); - const config = SettingGroupManager.get(id); + const config = SETTINGS_CONFIG[id]; if (group && config) { if (config.inProgress && !srdSectionEnabled(id)) { group.remove(); @@ -2604,7 +2575,7 @@ var gMainPane = { * The fully initialized state. * * @param {Object} supportedLanguages - * @param {Array<{ langTag: string, displayName: string}>} languageList + * @param {Array<{ langTag: string, displayName: string}} languageList * @param {Map<string, DownloadPhase>} downloadPhases */ constructor(supportedLanguages, languageList, downloadPhases) { @@ -2640,8 +2611,8 @@ var gMainPane = { /** * Determine the download phase of each language file. * - * @param {Array<{ langTag: string, displayName: string}>} languageList - * @returns {Promise<Map<string, DownloadPhase>>} Map the language tag to whether it is downloaded. + * @param {Array<{ langTag: string, displayName: string}} languageList. + * @returns {Map<string, DownloadPhase>} Map the language tag to whether it is downloaded. */ static async createDownloadPhases(languageList) { const downloadPhases = new Map(); @@ -2914,7 +2885,7 @@ var gMainPane = { case "loading": downloadButton.hidden = false; deleteButton.hidden = true; - downloadButton.setAttribute("disabled", "true"); + downloadButton.setAttribute("disabled", true); break; } } @@ -3338,7 +3309,7 @@ var gMainPane = { * The search mode is only available from the menu to change the primary browser * language. * - * @param {{ search: boolean }} search + * @param {{ search: boolean }} */ showBrowserLanguagesSubDialog({ search }) { // Record the telemetry event with an id to associate related actions. diff --git a/browser/components/preferences/preferences.js b/browser/components/preferences/preferences.js @@ -16,12 +16,6 @@ /* import-globals-from /browser/base/content/utilityOverlay.js */ /* import-globals-from /toolkit/content/preferencesBindings.js */ -/** @import MozButton from "chrome://global/content/elements/moz-button.mjs" */ -/** @import {SettingConfig, SettingEmitChange} from "chrome://global/content/preferences/Setting.mjs" */ -/** @import {SettingControlConfig} from "chrome://browser/content/preferences/widgets/setting-control.mjs" */ -/** @import {SettingGroup, SettingGroupConfig} from "chrome://browser/content/preferences/widgets/setting-group.mjs" */ -/** @import {SettingPane, SettingPaneConfig} from "chrome://browser/content/preferences/widgets/setting-pane.mjs" */ - "use strict"; var { AppConstants } = ChromeUtils.importESModule( @@ -152,7 +146,6 @@ ChromeUtils.defineLazyGetter(this, "gSubDialog", function () { }); }); -/** @type {Record<string, boolean>} */ const srdSectionPrefs = {}; XPCOMUtils.defineLazyPreferenceGetter( srdSectionPrefs, @@ -161,9 +154,6 @@ XPCOMUtils.defineLazyPreferenceGetter( false ); -/** - * @param {string} section - */ function srdSectionEnabled(section) { if (!(section in srdSectionPrefs)) { XPCOMUtils.defineLazyPreferenceGetter( @@ -176,102 +166,13 @@ function srdSectionEnabled(section) { return srdSectionPrefs.all || srdSectionPrefs[section]; } -var SettingPaneManager = { - /** @type {Map<string, SettingPaneConfig>} */ - _data: new Map(), - - /** - * @param {string} id - */ - get(id) { - if (!this._data.has(id)) { - throw new Error(`Setting pane "${id}" not found`); - } - return this._data.get(id); - }, - - /** - * @param {string} id - * @param {SettingPaneConfig} config - */ - registerPane(id, config) { - if (this._data.has(id)) { - throw new Error(`Setting pane "${id}" already registered`); - } - this._data.set(id, config); - let subPane = friendlyPrefCategoryNameToInternalName(id); - let settingPane = /** @type {SettingPane} */ ( - document.createElement("setting-pane") - ); - settingPane.name = subPane; - settingPane.config = config; - settingPane.isSubPane = !!config.parent; - document.getElementById("mainPrefPane").append(settingPane); - register_module(subPane, { - init() { - settingPane.init(); - }, - }); - }, - - /** - * @param {Record<string, SettingPaneConfig>} paneConfigs - */ - registerPanes(paneConfigs) { - for (let id in paneConfigs) { - this.registerPane(id, paneConfigs[id]); - } - }, -}; - -var SettingGroupManager = { - /** @type {Map<string, SettingGroupConfig>} */ - _data: new Map(), - - /** - * @param {string} id - */ - get(id) { - if (!this._data.has(id)) { - throw new Error(`Setting group "${id}" not found`); - } - return this._data.get(id); - }, - - /** - * @param {string} id - * @param {SettingGroupConfig} config - */ - registerGroup(id, config) { - if (this._data.has(id)) { - throw new Error(`Setting group "${id}" already registered`); - } - this._data.set(id, config); - }, - - /** - * @param {Record<string, SettingGroupConfig>} groupConfigs - */ - registerGroups(groupConfigs) { - for (let id in groupConfigs) { - this.registerGroup(id, groupConfigs[id]); - } - }, -}; - -/** - * Register initial config-based setting panes here. If you need to register a - * pane elsewhere, use {@link SettingPaneManager['registerPane']}. - * - * @type {Record<string, SettingPaneConfig>} - */ -const CONFIG_PANES = Object.freeze({ +const CONFIG_PANES = { containers2: { parent: "general", l10nId: "containers-section-header", groupIds: ["containers"], }, -}); +}; var gLastCategory = { category: undefined, subcategory: undefined }; const gXULDOMParser = new DOMParser(); @@ -325,8 +226,18 @@ function init_all() { register_module("panePrivacy", gPrivacyPane); register_module("paneContainers", gContainersPane); - for (let [id, config] of Object.entries(CONFIG_PANES)) { - SettingPaneManager.registerPane(id, config); + for (let [subPane, config] of Object.entries(CONFIG_PANES)) { + subPane = friendlyPrefCategoryNameToInternalName(subPane); + let settingPane = document.createElement("setting-pane"); + settingPane.name = subPane; + settingPane.config = config; + settingPane.isSubPane = config.parent; + document.getElementById("mainPrefPane").append(settingPane); + register_module(subPane, { + init() { + settingPane.init(); + }, + }); } if (Services.prefs.getBoolPref("browser.translations.newSettingsUI.enable")) { @@ -407,12 +318,6 @@ function onHashChange() { gotoPref(null, "Hash"); } -/** - * @param {string} [aCategory] The pane to show, defaults to the hash of URL or general - * @param {"Click"|"Initial"|"Hash"} [aShowReason] - * What triggered the navigation. Defaults to "Click" if aCategory is provided, - * otherwise "Initial". - */ async function gotoPref( aCategory, aShowReason = aCategory ? "Click" : "Initial" @@ -421,7 +326,7 @@ async function gotoPref( const kDefaultCategoryInternalName = "paneGeneral"; const kDefaultCategory = "general"; let hash = document.location.hash; - let category = aCategory || hash.substring(1) || kDefaultCategoryInternalName; + let category = aCategory || hash.substr(1) || kDefaultCategoryInternalName; let breakIndex = category.indexOf("-"); // Subcategories allow for selecting smaller sections of the preferences @@ -457,8 +362,8 @@ async function gotoPref( element.hidden = true; } - item = /** @type {HTMLElement} */ ( - categories.querySelector(".category[value=" + CSS.escape(category) + "]") + item = categories.querySelector( + ".category[value=" + CSS.escape(category) + "]" ); if (!item || item.hidden) { unknownCategory = true; @@ -492,10 +397,8 @@ async function gotoPref( gLastCategory.category = category; gLastCategory.subcategory = subcategory; if (item) { - // @ts-ignore MozElements.RichListBox categories.selectedItem = item; } else { - // @ts-ignore MozElements.RichListBox categories.clearSelection(); } window.history.replaceState(category, document.title); @@ -548,10 +451,7 @@ async function gotoPref( categoryModule.handlePrefControlledSection?.(); // Record which category is shown - let gleanId = /** @type {"showClick" | "showHash" | "showInitial"} */ ( - "show" + aShowReason - ); - Glean.aboutpreferences[gleanId].record({ value: category }); + Glean.aboutpreferences["show" + aShowReason].record({ value: category }); document.dispatchEvent( new CustomEvent("paneshown", { @@ -564,15 +464,9 @@ async function gotoPref( ); } -/** - * @param {string} aQuery - * @param {string} aAttribute - */ function search(aQuery, aAttribute) { let mainPrefPane = document.getElementById("mainPrefPane"); - let elements = /** @type {HTMLElement[]} */ ( - Array.from(mainPrefPane.children) - ); + let elements = mainPrefPane.children; for (let element of elements) { // If the "data-hidden-from-search" is "true", the // element will not get considered during search. diff --git a/browser/components/preferences/privacy.js b/browser/components/preferences/privacy.js @@ -564,73 +564,58 @@ if (Services.prefs.getBoolPref("privacy.ui.status_card", false)) { pref: "browser.contentblocking.category", get: prefValue => prefValue == "strict", }); - Preferences.addSetting( - /** @type {{ cachedValue: number, loadTrackerCount: (emitChange: SettingEmitChange) => Promise<void> } & SettingConfig} */ ({ - id: "trackerCount", - cachedValue: null, - async loadTrackerCount(emitChange) { - const now = Date.now(); - const aMonthAgo = new Date(now - 30 * 24 * 60 * 60 * 1000); - /** @type {{ getResultByName: (_: string) => number }[]} */ - const events = await lazy.TrackingDBService.getEventsByDateRange( - now, - aMonthAgo - ); - - const total = events.reduce((acc, day) => { - return acc + day.getResultByName("count"); - }, 0); - this.cachedValue = total; - emitChange(); - }, - setup(emitChange) { - this.loadTrackerCount(emitChange); - }, - get() { - return this.cachedValue; - }, - }) - ); - Preferences.addSetting( - /** @type {{ cachedValue: any } & SettingConfig} */ ({ - id: "appUpdateStatus", - cachedValue: AppUpdater.STATUS.NO_UPDATER, - setup(emitChange) { - if (AppConstants.MOZ_UPDATER && !gIsPackagedApp) { - let appUpdater = new AppUpdater(); - /** - * @param {number} status - * @param {any[]} _args - */ - let listener = (status, ..._args) => { - this.cachedValue = status; - emitChange(); - }; - appUpdater.addListener(listener); - appUpdater.check(); - return () => { - appUpdater.removeListener(listener); - appUpdater.stop(); - }; - } - return () => {}; - }, - get() { - return this.cachedValue; - }, - set(value) { - this.cachedValue = value; - }, - }) - ); + Preferences.addSetting({ + id: "trackerCount", + cachedValue: null, + async setup(emitChange) { + const now = Date.now(); + const aMonthAgo = new Date(now - 30 * 24 * 60 * 60 * 1000); + const events = await lazy.TrackingDBService.getEventsByDateRange( + now, + aMonthAgo + ); + const total = events.reduce((acc, day) => { + return acc + day.getResultByName("count"); + }, 0); + this.cachedValue = total; + emitChange(); + }, + get() { + return this.cachedValue; + }, + }); + Preferences.addSetting({ + id: "appUpdateStatus", + cachedValue: AppUpdater.STATUS.NO_UPDATER, + async setup(emitChange) { + if (AppConstants.MOZ_UPDATER && !gIsPackagedApp) { + let appUpdater = new AppUpdater(); + let listener = (status, ..._args) => { + this.cachedValue = status; + emitChange(); + }; + appUpdater.addListener(listener); + await appUpdater.check(); + return () => { + appUpdater.removeListener(listener); + appUpdater.stop(); + }; + } + return () => {}; + }, + get() { + return this.cachedValue; + }, + set(value) { + this.cachedValue = value; + }, + }); } /** * This class is used to create Settings that are used to warn the user about * potential misconfigurations. It should be passed into Preferences.addSetting * to create the Preference for a <moz-box-item> because it creates * separate members on pref.config - * - * @implements {SettingConfig} */ class WarningSettingConfig { /** @@ -700,9 +685,9 @@ class WarningSettingConfig { * This initializes the Setting created with this config, starting listeners for all dependent * Preferences and providing a cleanup callback to remove them * - * @param {() => any} emitChange - a callback to be invoked any time that the Setting created + * @param {Function} emitChange - a callback to be invoked any time that the Setting created * with this config is changed - * @returns {() => any} a function that cleans up the state from this Setting, namely pref change listeners. + * @returns {Function} a function that cleans up the state from this Setting, namely pref change listeners. */ setup(emitChange) { for (let [getter, prefId] of Object.entries(this.prefMapping)) { @@ -721,7 +706,7 @@ class WarningSettingConfig { * "dismiss" action depending on the target, and those callbacks are defined * in this class. * - * @param {PointerEvent} event - The event for the user click + * @param {Event} event - The event for the user click */ onUserClick(event) { switch (event.target.id) { @@ -1123,7 +1108,6 @@ if (Services.prefs.getBoolPref("privacy.ui.status_card", false)) { ); } -/** @type {SettingControlConfig[]} */ const SECURITY_WARNINGS = [ { l10nId: "security-privacy-issue-warning-test", @@ -1219,41 +1203,39 @@ const SECURITY_WARNINGS = [ }, ]; -Preferences.addSetting( - /** @type {{ makeSecurityWarningItems: () => SettingControlConfig[] } & SettingConfig} */ ({ - id: "securityWarningsGroup", - makeSecurityWarningItems() { - return SECURITY_WARNINGS.map(({ id, l10nId }) => ({ - id, - l10nId, - control: "moz-box-item", - options: [ - { - control: "moz-button", - l10nId: "issue-card-reset-button", - controlAttrs: { slot: "actions", size: "small", id: "reset" }, - }, - { - control: "moz-button", - l10nId: "issue-card-dismiss-button", - controlAttrs: { - slot: "actions", - size: "small", - iconsrc: "chrome://global/skin/icons/close.svg", - id: "dismiss", - }, +Preferences.addSetting({ + id: "securityWarningsGroup", + makeSecurityWarningItems() { + return SECURITY_WARNINGS.map(({ id, l10nId }) => ({ + id, + l10nId, + control: "moz-box-item", + options: [ + { + control: "moz-button", + l10nId: "issue-card-reset-button", + controlAttrs: { slot: "actions", size: "small", id: "reset" }, + }, + { + control: "moz-button", + l10nId: "issue-card-dismiss-button", + controlAttrs: { + slot: "actions", + size: "small", + iconsrc: "chrome://global/skin/icons/close.svg", + id: "dismiss", }, - ], - })); - }, - getControlConfig(config) { - if (!config.items) { - return { ...config, items: this.makeSecurityWarningItems() }; - } - return config; - }, - }) -); + }, + ], + })); + }, + getControlConfig(config) { + if (!config.items) { + return { ...config, items: this.makeSecurityWarningItems() }; + } + return config; + }, +}); Preferences.addSetting({ id: "privacyCard", @@ -1522,7 +1504,7 @@ Preferences.addSetting({ deps.blockUnwantedDownloads.value = value; let malwareTable = Preferences.get("urlclassifier.malwareTable"); - let malware = /** @type {string} */ (malwareTable.value) + let malware = malwareTable.value .split(",") .filter( x => @@ -1559,68 +1541,61 @@ Preferences.addSetting({ Preferences.addSetting({ id: "manageDataSettingsGroup", }); -Preferences.addSetting( - /** @type {{ isUpdatingSites: boolean, usage: { value: number, unit: string } | void } & SettingConfig} */ ({ - id: "siteDataSize", - usage: null, - isUpdatingSites: false, - setup(emitChange) { - let onUsageChanged = async () => { - let [siteDataUsage, cacheUsage] = await Promise.all([ - SiteDataManager.getTotalUsage(), - SiteDataManager.getCacheSize(), - ]); - let totalUsage = siteDataUsage + cacheUsage; - let [value, unit] = DownloadUtils.convertByteUnits(totalUsage); - this.usage = { value, unit }; - - this.isUpdatingSites = false; - emitChange(); - }; +Preferences.addSetting({ + id: "siteDataSize", + setup(emitChange) { + let onUsageChanged = async () => { + let [siteDataUsage, cacheUsage] = await Promise.all([ + SiteDataManager.getTotalUsage(), + SiteDataManager.getCacheSize(), + ]); + let totalUsage = siteDataUsage + cacheUsage; + let [value, unit] = DownloadUtils.convertByteUnits(totalUsage); + this.usage = { value, unit }; - let onUpdatingSites = () => { - this.isUpdatingSites = true; - emitChange(); - }; + this.isUpdatingSites = false; + emitChange(); + }; + + let onUpdatingSites = () => { + this.isUpdatingSites = true; + emitChange(); + }; - Services.obs.addObserver(onUsageChanged, "sitedatamanager:sites-updated"); - Services.obs.addObserver( + Services.obs.addObserver(onUsageChanged, "sitedatamanager:sites-updated"); + Services.obs.addObserver(onUpdatingSites, "sitedatamanager:updating-sites"); + + return () => { + Services.obs.removeObserver( + onUsageChanged, + "sitedatamanager:sites-updated" + ); + Services.obs.removeObserver( onUpdatingSites, "sitedatamanager:updating-sites" ); - - return () => { - Services.obs.removeObserver( - onUsageChanged, - "sitedatamanager:sites-updated" - ); - Services.obs.removeObserver( - onUpdatingSites, - "sitedatamanager:updating-sites" - ); - }; - }, - getControlConfig(config) { - if (this.isUpdatingSites || !this.usage) { - // Data not retrieved yet, show a loading state. - return { - ...config, - l10nId: "sitedata-total-size-calculating", - }; - } - - let { value, unit } = this.usage; + }; + }, + getControlConfig(config) { + if (this.isUpdatingSites || !this.usage) { + // Data not retrieved yet, show a loading state. return { ...config, - l10nId: "sitedata-total-size2", - l10nArgs: { - value, - unit, - }, + l10nId: "sitedata-total-size-calculating", }; - }, - }) -); + } + + let { value, unit } = this.usage; + return { + ...config, + l10nId: "sitedata-total-size2", + l10nArgs: { + value, + unit, + }, + }; + }, +}); Preferences.addSetting({ id: "deleteOnCloseInfo", @@ -1630,104 +1605,91 @@ Preferences.addSetting({ }, }); -Preferences.addSetting( - /** @type {{ isUpdatingSites: boolean } & SettingConfig} */ ({ - id: "clearSiteDataButton", - isUpdatingSites: false, - setup(emitChange) { - let onSitesUpdated = async () => { - this.isUpdatingSites = false; - emitChange(); - }; +Preferences.addSetting({ + id: "clearSiteDataButton", + setup(emitChange) { + let onSitesUpdated = async () => { + this.isUpdatingSites = false; + emitChange(); + }; - let onUpdatingSites = () => { - this.isUpdatingSites = true; - emitChange(); - }; + let onUpdatingSites = () => { + this.isUpdatingSites = true; + emitChange(); + }; - Services.obs.addObserver(onSitesUpdated, "sitedatamanager:sites-updated"); - Services.obs.addObserver( + Services.obs.addObserver(onSitesUpdated, "sitedatamanager:sites-updated"); + Services.obs.addObserver(onUpdatingSites, "sitedatamanager:updating-sites"); + + return () => { + Services.obs.removeObserver( + onSitesUpdated, + "sitedatamanager:sites-updated" + ); + Services.obs.removeObserver( onUpdatingSites, "sitedatamanager:updating-sites" ); + }; + }, + onUserClick() { + let uri; + if (useOldClearHistoryDialog) { + uri = "chrome://browser/content/preferences/dialogs/clearSiteData.xhtml"; + } else { + uri = "chrome://browser/content/sanitize_v2.xhtml"; + } - return () => { - Services.obs.removeObserver( - onSitesUpdated, - "sitedatamanager:sites-updated" - ); - Services.obs.removeObserver( - onUpdatingSites, - "sitedatamanager:updating-sites" - ); - }; - }, - onUserClick() { - let uri; - if (useOldClearHistoryDialog) { - uri = - "chrome://browser/content/preferences/dialogs/clearSiteData.xhtml"; - } else { - uri = "chrome://browser/content/sanitize_v2.xhtml"; + gSubDialog.open( + uri, + { + features: "resizable=no", + }, + { + mode: "clearSiteData", } + ); + }, + disabled() { + return this.isUpdatingSites; + }, +}); +Preferences.addSetting({ + id: "siteDataSettings", + setup(emitChange) { + let onSitesUpdated = async () => { + this.isUpdatingSites = false; + emitChange(); + }; - gSubDialog.open( - uri, - { - features: "resizable=no", - }, - { - mode: "clearSiteData", - } - ); - }, - disabled() { - return this.isUpdatingSites; - }, - }) -); -Preferences.addSetting( - /** @type {{ isUpdatingSites: boolean } & SettingConfig} */ ({ - id: "siteDataSettings", - isUpdatingSites: false, - setup(emitChange) { - let onSitesUpdated = async () => { - this.isUpdatingSites = false; - emitChange(); - }; + let onUpdatingSites = () => { + this.isUpdatingSites = true; + emitChange(); + }; - let onUpdatingSites = () => { - this.isUpdatingSites = true; - emitChange(); - }; + Services.obs.addObserver(onSitesUpdated, "sitedatamanager:sites-updated"); + Services.obs.addObserver(onUpdatingSites, "sitedatamanager:updating-sites"); - Services.obs.addObserver(onSitesUpdated, "sitedatamanager:sites-updated"); - Services.obs.addObserver( + return () => { + Services.obs.removeObserver( + onSitesUpdated, + "sitedatamanager:sites-updated" + ); + Services.obs.removeObserver( onUpdatingSites, "sitedatamanager:updating-sites" ); - - return () => { - Services.obs.removeObserver( - onSitesUpdated, - "sitedatamanager:sites-updated" - ); - Services.obs.removeObserver( - onUpdatingSites, - "sitedatamanager:updating-sites" - ); - }; - }, - onUserClick() { - gSubDialog.open( - "chrome://browser/content/preferences/dialogs/siteDataSettings.xhtml" - ); - }, - disabled() { - return this.isUpdatingSites; - }, - }) -); + }; + }, + onUserClick() { + gSubDialog.open( + "chrome://browser/content/preferences/dialogs/siteDataSettings.xhtml" + ); + }, + disabled() { + return this.isUpdatingSites; + }, +}); Preferences.addSetting({ id: "cookieExceptions", onUserClick() { diff --git a/browser/components/preferences/tests/browser.toml b/browser/components/preferences/tests/browser.toml @@ -20,9 +20,7 @@ tags = "os_integration" ["browser_about_settings.js"] ["browser_advanced_update.js"] -run-if = [ - "updater", -] +run-if = ["updater"] ["browser_application_xml_handle_internally.js"] @@ -33,9 +31,7 @@ run-if = [ ["browser_basic_rebuild_fonts_test.js"] ["browser_browser_languages_subdialog.js"] -skip-if = [ - "tsan", -] +skip-if = ["tsan"] ["browser_bug410900.js"] @@ -57,9 +53,7 @@ support-files = ["browser_bug1184989_prevent_scrolling_when_preferences_flipped. ["browser_cert_export.js"] ["browser_change_app_handler.js"] -run-if = [ - "os == 'win'", # Windows-specific handler application selection dialog -] +run-if = ["os == 'win'"] # Windows-specific handler application selection dialog ["browser_checkspelling.js"] @@ -74,9 +68,7 @@ run-if = [ ["browser_connection_bug1505330.js"] ["browser_connection_system_wpad.js"] -run-if = [ - "os == 'win'", -] +run-if = ["os == 'win'"] ["browser_connection_telemetry.js"] @@ -108,10 +100,10 @@ run-if = [ ["browser_experimental_features_filter.js"] skip-if = [ - "os == 'linux' && os_version == '24.04' && arch == 'x86_64'", # Bug 1969933 - "os == 'mac' && os_version == '10.15' && arch == 'x86_64' && opt", # Bug 1969933 - "os == 'win' && os_version == '11.26100' && arch == 'x86'", # Bug 1969933 - "os == 'win' && os_version == '11.26100' && arch == 'x86_64'", # Bug 1969933 + "os == 'linux' && os_version == '24.04' && processor == 'x86_64'", # Bug 1969933 + "os == 'mac' && os_version == '10.15' && processor == 'x86_64' && opt", # Bug 1969933 + "os == 'win' && os_version == '11.26100' && processor == 'x86_64'", # Bug 1969933 + "os == 'win' && os_version == '11.26100' && processor == 'x86'", # Bug 1969933 ] ["browser_experimental_features_hidden_when_not_public.js"] @@ -147,18 +139,14 @@ https_first_disabled = true ["browser_ignore_invalid_capability.js"] ["browser_keyboardfocus.js"] -run-if = [ - "os == 'mac'", -] +run-if = ["os == 'mac'"] ["browser_languages_subdialog.js"] ["browser_layersacceleration.js"] ["browser_localSearchShortcuts.js"] -fail-if = [ - "a11y_checks", # Bug 1854636 clicked treechildren#engineChildren may not be focusable -] +fail-if = ["a11y_checks"] # Bug 1854636 clicked treechildren#engineChildren may not be focusable ["browser_moreFromMozilla.js"] @@ -184,9 +172,7 @@ support-files = ["empty_pdf_file.pdf"] ["browser_performance_e10srollout.js"] ["browser_performance_non_e10s.js"] -skip-if = [ - "true", -] +skip-if = ["true"] ["browser_permissions_checkPermissionsWereAdded.js"] @@ -201,9 +187,7 @@ skip-if = [ ["browser_primaryPassword.js"] ["browser_privacy_allowListPreference.js"] -skip-if = [ - "os == 'linux' && os_version == '24.04' && arch == 'x86_64' && tsan", # Bug 1984525 -] +skip-if = ["os == 'linux' && os_version == '24.04' && processor == 'x86_64' && tsan"] # Bug 1984525 ["browser_privacy_allowListPreference_dialog.js"] @@ -255,9 +239,7 @@ support-files = ["!/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js ["browser_searchShowSuggestionsFirst.js"] ["browser_search_engineList.js"] -fail-if = [ - "a11y_checks", # Bug 1854636 clicked treechildren#engineChildren may not be focusable -] +fail-if = ["a11y_checks"] # Bug 1854636 clicked treechildren#engineChildren may not be focusable ["browser_search_firefoxSuggest.js"] @@ -270,9 +252,7 @@ fail-if = [ ["browser_search_subdialog_tooltip_saved_addresses.js"] ["browser_search_subdialogs_within_preferences_1.js"] -skip-if = [ - "tsan", # Bug 1678829 -] +skip-if = ["tsan"] # Bug 1678829 ["browser_search_subdialogs_within_preferences_2.js"] @@ -310,8 +290,6 @@ skip-if = [ ["browser_setting_group_in_progress.js"] -["browser_setting_pane_sub_pane.js"] - ["browser_site_login_exceptions.js"] ["browser_site_login_exceptions_policy.js"] @@ -345,11 +323,7 @@ support-files = [ ["browser_warning_permanent_private_browsing.js"] ["browser_windows_launch_on_login.js"] -run-if = [ - "os == 'win' && !msix", -] +run-if = ["os == 'win' && !msix"] ["browser_windows_launch_on_login_msix.js"] -run-if = [ - "os == 'win' && msix", -] +run-if = ["os == 'win' && msix"] diff --git a/browser/components/preferences/tests/browser_setting_pane_sub_pane.js b/browser/components/preferences/tests/browser_setting_pane_sub_pane.js @@ -1,90 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - https://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -describe("setting-pane", () => { - let doc, win, testSubPaneButton; - - beforeEach(async function setup() { - await openPreferencesViaOpenPreferencesAPI("general", { leaveOpen: true }); - doc = gBrowser.selectedBrowser.contentDocument; - win = doc.ownerGlobal; - testSubPaneButton = doc.createElement("moz-button"); - testSubPaneButton.hidden = true; - testSubPaneButton.setAttribute("data-category", "panePrivacy"); - testSubPaneButton.textContent = "Go to test pane"; - testSubPaneButton.addEventListener("click", () => - win.gotoPref("paneTestSubPane") - ); - let privacyHeading = doc.getElementById("browserPrivacyCategory"); - privacyHeading.insertAdjacentElement("afterend", testSubPaneButton); - win.Preferences.addSetting({ - id: "testSetting", - get: () => true, - }); - win.SettingGroupManager.registerGroup("testGroup", { - l10nId: "downloads-header-2", - headingLevel: 2, - items: [ - { - id: "testSetting", - controlAttrs: { - label: "Test setting", - }, - }, - ], - }); - win.SettingPaneManager.registerPane("testSubPane", { - parent: "privacy", - l10nId: "containers-section-header", - groupIds: ["testGroup"], - }); - }); - - afterEach(() => BrowserTestUtils.removeTab(gBrowser.selectedTab)); - - it("can load/go back sub-pane", async () => { - let pane = doc.querySelector( - 'setting-pane[data-category="paneTestSubPane"]' - ); - is_element_hidden(pane, "Sub pane is initially hidden"); - - // Load the privacy pane - let paneLoaded = waitForPaneChange("privacy"); - EventUtils.synthesizeMouseAtCenter( - doc.getElementById("category-privacy"), - {}, - win - ); - await paneLoaded; - - // Load the sub pane - paneLoaded = waitForPaneChange("testSubPane"); - EventUtils.synthesizeMouseAtCenter(testSubPaneButton, {}, win); - await paneLoaded; - - is_element_visible(pane, "Page header is visible"); - ok(pane, "There is a setting pane"); - await pane.updateComplete; - let pageHeader = pane.pageHeaderEl; - ok(pageHeader, "There is a page header"); - is( - pageHeader.dataset.l10nId, - "containers-section-header", - "Page header has its l10nId" - ); - let heading = pageHeader.headingEl; - ok(heading, "There is a heading in the header"); - ok(heading.innerText, "The heading has text"); - is(heading.innerText, pageHeader.heading, "The text is localized"); - let backButton = pageHeader.backButtonEl; - ok(backButton, "There is a back button"); - - // Go back - paneLoaded = waitForPaneChange("privacy"); - EventUtils.synthesizeMouseAtCenter(backButton, {}, win); - await paneLoaded; - is_element_hidden(pane, "Sub pane is hidden again"); - }); -}); diff --git a/browser/components/preferences/tests/head.js b/browser/components/preferences/tests/head.js @@ -704,17 +704,3 @@ async function updateCheckBox(win, id, value) { // Toggle the state. await EventUtils.synthesizeMouseAtCenter(checkbox, {}, checkbox.ownerGlobal); } - -/** - * Wait for the current setting pane to change. - * - * @param {string} paneId - */ -async function waitForPaneChange(paneId) { - let doc = gBrowser.selectedBrowser.contentDocument; - let event = await BrowserTestUtils.waitForEvent(doc, "paneshown"); - let expectId = paneId.startsWith("pane") - ? paneId - : `pane${paneId[0].toUpperCase()}${paneId.substring(1)}`; - is(event.detail.category, expectId, "Loaded the correct pane"); -} diff --git a/browser/components/preferences/widgets/setting-control/setting-control.mjs b/browser/components/preferences/widgets/setting-control/setting-control.mjs @@ -15,55 +15,25 @@ import { SettingElement, spread, } from "chrome://browser/content/preferences/widgets/setting-element.mjs"; -import { MozBaseInputElement } from "chrome://global/content/lit-utils.mjs"; -import { MozRadio } from "chrome://global/content/elements/moz-radio-group.mjs"; -import MozInputFolder from "chrome://global/content/elements/moz-input-folder.mjs"; -/** @import { LitElement, Ref, TemplateResult } from "chrome://global/content/vendor/lit.all.mjs" */ -/** @import { SettingElementConfig } from "chrome://browser/content/preferences/widgets/setting-element.mjs" */ -/** @import { Setting } from "chrome://global/content/preferences/Setting.mjs" */ +/** @import MozCheckbox from "../../../../../toolkit/content/widgets/moz-checkbox/moz-checkbox.mjs"*/ +/** @import { Setting } from "chrome://global/content/preferences/Setting.mjs"; */ +/** @import { PreferencesSettingConfigNestedControlOption } from "chrome://global/content/preferences/Preferences.mjs"; */ /** - * @typedef {Object} SettingNestedConfig - * @property {SettingControlConfig[]} [items] Additional nested SettingControls to render. - * @property {SettingOptionConfig[]} [options] - * Additional nested plain elements to render (may have SettingControls nested within them, though). - */ - -/** - * @typedef {Object} SettingOptionConfigExtensions - * @property {string} [control] - * The element tag to render, default assumed based on parent control. - * @property {any} [value] A value to set on the option. - */ - -/** - * @typedef {Object} SettingControlConfigExtensions - * @property {string} id - * The ID for the Setting, also set in the DOM unless overridden with controlAttrs.id - * @property {string} [control] The element to render, default to "moz-checkbox". - * @property {string} [controllingExtensionInfo] - * ExtensionSettingStore id for checking if a setting is controlled by an extension. - */ - -/** - * @typedef {SettingOptionConfigExtensions & SettingElementConfig & SettingNestedConfig} SettingOptionConfig - * @typedef {SettingControlConfigExtensions & SettingElementConfig & SettingNestedConfig} SettingControlConfig - * @typedef {{ control: SettingControl } & HTMLElement} SettingControlChild - */ - -/** - * @template T=Event - * @typedef {T & { target: SettingControlChild }} SettingControlEvent - * SettingControlEvent simplifies the types in this file, but causes issues when - * doing more involved work when used in Setting.mjs. When casting the - * `event.target` to a more specific type like MozButton (or even - * HTMLButtonElement) it gets flagged as being too different from SettingControlChild. + * Properties that represent a nested HTML element that will be a direct descendant of this setting control element + * + * @typedef {object} SettingNestedElementOption + * @property {Array<SettingNestedElementOption>} [options] + * @property {string} control - The {@link HTMLElement#localName} of any HTML element + * @property {Record<string, string>} [controlAttrs] - Attributes for the element */ /** * Mapping of parent control tag names to the literal tag name for their * expected children. eg. "moz-radio-group"->literal`moz-radio`. + * + * @type Map<string, literal> */ const KNOWN_OPTIONS = new Map([ ["moz-radio-group", literal`moz-radio`], @@ -119,7 +89,6 @@ export class SettingControl extends SettingElement { constructor() { super(); - /** @type {Ref<LitElement>} */ this.controlRef = createRef(); /** @@ -133,7 +102,7 @@ export class SettingControl extends SettingElement { this.setting = undefined; /** - * @type {SettingControlConfig | undefined} + * @type {PreferencesSettingsConfig | undefined} */ this.config = undefined; @@ -153,7 +122,7 @@ export class SettingControl extends SettingElement { } focus() { - this.controlEl.focus(); + this.controlRef.value.focus(); } get controlEl() { @@ -193,6 +162,9 @@ export class SettingControl extends SettingElement { } } + /** + * @type {MozLitElement['updated']} + */ updated() { const control = this.controlRef?.value; if (!control) { @@ -217,7 +189,7 @@ export class SettingControl extends SettingElement { * or controlled by an extension but not both. * * @override - * @param {SettingElementConfig} config + * @param {PreferencesSettingsConfig} config * @returns {ReturnType<SettingElement['getCommonPropertyMapping']>} */ getCommonPropertyMapping(config) { @@ -231,7 +203,7 @@ export class SettingControl extends SettingElement { /** * The default properties for an option. * - * @param {SettingOptionConfig} config + * @param {PreferencesSettingConfigNestedControlOption | SettingNestedElementOption} config */ getOptionPropertyMapping(config) { const props = this.getCommonPropertyMapping(config); @@ -243,8 +215,6 @@ export class SettingControl extends SettingElement { /** * The default properties for this control. - * - * @param {SettingControlConfig} config */ getControlPropertyMapping(config) { const props = this.getCommonPropertyMapping(config); @@ -266,34 +236,23 @@ export class SettingControl extends SettingElement { }; /** - * @param {HTMLElement} el - * @returns {any} + * @param {MozCheckbox | HTMLInputElement} el + * @returns {boolean | string | undefined} */ controlValue(el) { - if (el instanceof MozBaseInputElement && !(el instanceof MozRadio)) { - let C = /** @type {typeof MozBaseInputElement} */ (el.constructor); - return el[C.activatedProperty]; - } - if (el instanceof MozInputFolder) { + if (el.constructor.activatedProperty && el.localName != "moz-radio") { + return el[el.constructor.activatedProperty]; + } else if (el.localName == "moz-input-folder") { return el.folder; } - return "value" in el ? el.value : null; + return el.value; } - /** - * Called by our parent when our input changed. - * - * @param {SettingControlChild} el - */ + // Called by our parent when our input changed. onChange(el) { this.setting.userChange(this.controlValue(el)); } - /** - * Called by our parent when our input is clicked. - * - * @param {MouseEvent} event - */ onClick(event) { this.setting.userClick(event); } @@ -314,14 +273,9 @@ export class SettingControl extends SettingElement { this.showEnableExtensionMessage = false; } - /** - * @param {MouseEvent} event - */ navigateToAddons(event) { - let link = /** @type {HTMLAnchorElement} */ (event.target); - if (link.matches("a[data-l10n-name='addons-link']")) { + if (event.target.matches("a[data-l10n-name='addons-link']")) { event.preventDefault(); - // @ts-ignore let mainWindow = window.browsingContext.topChromeWindow; mainWindow.BrowserAddonUI.openAddonsMgr("addons://list/theme"); } @@ -338,8 +292,8 @@ export class SettingControl extends SettingElement { /** * Prepare nested item config and settings. * - * @param {SettingControlConfig | SettingOptionConfig} config - * @returns {TemplateResult[]} + * @param {PreferencesSettingConfigNestedControlOption} config + * @returns {Array<string>} */ itemsTemplate(config) { if (!config.items) { @@ -365,8 +319,8 @@ export class SettingControl extends SettingElement { /** * Prepares any children (and any of its children's children) that this element may need. * - * @param {SettingOptionConfig} config - * @returns {TemplateResult[]} + * @param {PreferencesSettingConfigNestedControlOption | SettingNestedElementOption} config + * @returns {Array<string>} */ optionsTemplate(config) { if (!config.options) { @@ -377,11 +331,9 @@ export class SettingControl extends SettingElement { let optionTag = opt.control ? unsafeStatic(opt.control) : KNOWN_OPTIONS.get(control); - let children = - "items" in opt ? this.itemsTemplate(opt) : this.optionsTemplate(opt); return staticHtml`<${optionTag} ${spread(this.getOptionPropertyMapping(opt))} - >${children}</${optionTag}>`; + >${"items" in opt ? this.itemsTemplate(opt) : this.optionsTemplate(opt)}</${optionTag}>`; }); } diff --git a/browser/components/preferences/widgets/setting-element/setting-element.mjs b/browser/components/preferences/widgets/setting-element/setting-element.mjs @@ -11,19 +11,6 @@ import { import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; -/** @import { AttributePart } from "chrome://global/content/vendor/lit.all.mjs" */ - -/** - * @typedef {Object} SettingElementConfig - * @property {string} [id] - The ID for the Setting, this should match the layout id - * @property {string} [l10nId] - The Fluent l10n ID for the setting - * @property {Record<string, string>} [l10nArgs] - An object containing l10n IDs and their values that will be translated with Fluent - * @property {Record<string, any>} [controlAttrs] - An object of additional attributes to be set on the control. These can be used to further customize the control for example a message bar of the warning type, or what dialog a button should open - * @property {string} [iconSrc] - A path to the icon for the control (if the control supports one) - * @property {string} [supportPage] - The SUMO support page slug for the setting - * @property {string} [subcategory] - The sub-category slug used for direct linking to a setting from SUMO - */ - /** * A Lit directive that applies all properties of an object to a DOM element. * @@ -51,10 +38,9 @@ class SpreadDirective extends Directive { * Render nothing by default as all changes are made in update using DOM APIs * on the element directly. * - * @param {Record<string, unknown>} props The props to apply to this element. + * @returns {typeof nothing} */ - // eslint-disable-next-line no-unused-vars - render(props) { + render() { return nothing; } @@ -73,6 +59,7 @@ class SpreadDirective extends Directive { // implementing the auto-clearing hopefully the consumer will do something // that fits their use case. + /** @type {HTMLElement} */ let el = part.element; for (let [key, value] of Object.entries(props)) { @@ -88,7 +75,6 @@ class SpreadDirective extends Directive { if (key.startsWith("?")) { el.toggleAttribute(key.slice(1), Boolean(value)); } else if (key.startsWith(".")) { - // @ts-ignore el[key.slice(1)] = value; } else if (key.startsWith("@")) { throw new Error( @@ -112,19 +98,28 @@ export class SettingElement extends MozLitElement { /** * The default properties that the setting element accepts. * - * @param {SettingElementConfig} config + * @param {PreferencesSettingsConfig} config + * @returns {Record<string, any>} */ getCommonPropertyMapping(config) { - return { + /** + * @type {Record<string, any>} + */ + const result = { id: config.id, - "data-l10n-id": config.l10nId ? config.l10nId : undefined, "data-l10n-args": config.l10nArgs ? JSON.stringify(config.l10nArgs) : undefined, ".iconSrc": config.iconSrc, "data-subcategory": config.subcategory, - ".supportPage": config.supportPage ? config.supportPage : undefined, ...config.controlAttrs, }; + if (config.supportPage) { + result[".supportPage"] = config.supportPage; + } + if (config.l10nId) { + result["data-l10n-id"] = config.l10nId; + } + return result; } } diff --git a/browser/components/preferences/widgets/setting-group/setting-group.mjs b/browser/components/preferences/widgets/setting-group/setting-group.mjs @@ -8,19 +8,8 @@ import { spread, } from "chrome://browser/content/preferences/widgets/setting-element.mjs"; -/** @import { SettingElementConfig } from "chrome://browser/content/preferences/widgets/setting-element.mjs" */ -/** @import { SettingControlConfig, SettingControlEvent } from "../setting-control/setting-control.mjs" */ -/** @import { Preferences } from "chrome://global/content/preferences/Preferences.mjs" */ - -/** - * @typedef {object} SettingGroupConfigExtensions - * @property {SettingControlConfig[]} items Array of SettingControlConfigs to render. - * @property {number} [headingLevel] A heading level to create the legend as (1-6). - * @property {boolean} [inProgress] - * Hide this section unless the browser.settings-redesign.enabled or - * browser.settings-redesign.<groupid>.enabled prefs are true. - */ -/** @typedef {SettingElementConfig & SettingGroupConfigExtensions} SettingGroupConfig */ +/** @import { SettingControl } from "../setting-control/setting-control.mjs"; */ +/** @import {PreferencesSettingsConfig, Preferences} from "chrome://global/content/preferences/Preferences.mjs" */ const CLICK_HANDLERS = new Set([ "dialog-button", @@ -41,7 +30,7 @@ export class SettingGroup extends SettingElement { this.getSetting = undefined; /** - * @type {SettingGroupConfig | undefined} + * @type {PreferencesSettingsConfig | undefined} */ this.config = undefined; } @@ -62,10 +51,9 @@ export class SettingGroup extends SettingElement { async handleVisibilityChange() { await this.updateComplete; - // @ts-expect-error bug 1997478 let hasVisibleControls = [...this.controlEls].some(el => !el.hidden); this.hidden = !hasVisibleControls; - let groupbox = /** @type {XULElement} */ (this.closest("groupbox")); + let groupbox = this.closest("groupbox"); if (hasVisibleControls) { this.removeAttribute("data-hidden-from-search"); if (groupbox && groupbox.hasAttribute("data-hidden-by-setting-group")) { @@ -85,7 +73,6 @@ export class SettingGroup extends SettingElement { async getUpdateComplete() { let result = await super.getUpdateComplete(); - // @ts-expect-error bug 1997478 await Promise.all([...this.controlEls].map(el => el.updateComplete)); return result; } @@ -94,31 +81,24 @@ export class SettingGroup extends SettingElement { * Notify child controls when their input has fired an event. When controls * are nested the parent receives events for the nested controls, so this is * actually easier to manage here; it also registers fewer listeners. - * - * @param {SettingControlEvent<InputEvent>} e */ onChange(e) { let inputEl = e.target; - inputEl.control?.onChange(inputEl); + let control = inputEl.control; + control?.onChange(inputEl); } - /** - * Notify child controls when their input has been clicked. When controls - * are nested the parent receives events for the nested controls, so this is - * actually easier to manage here; it also registers fewer listeners. - * - * @param {SettingControlEvent<MouseEvent>} e - */ onClick(e) { - let inputEl = e.target; - if (!CLICK_HANDLERS.has(inputEl.localName)) { + if (!CLICK_HANDLERS.has(e.target.localName)) { return; } - inputEl.control?.onClick(e); + let inputEl = e.target; + let control = inputEl.control; + control?.onClick(e); } /** - * @param {SettingControlConfig} item + * @param {PreferencesSettingsConfig} item */ itemTemplate(item) { let setting = this.getSetting(item.id); diff --git a/browser/components/preferences/widgets/setting-pane/setting-pane.mjs b/browser/components/preferences/widgets/setting-pane/setting-pane.mjs @@ -5,13 +5,6 @@ import { html } from "chrome://global/content/vendor/lit.all.mjs"; import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; -/** - * @typedef {object} SettingPaneConfig - * @property {string} [parent] The pane that links to this one. - * @property {string} l10nId Fluent id for the heading/description. - * @property {string[]} groupIds What setting groups should be rendered. - */ - export class SettingPane extends MozLitElement { static properties = { name: { type: String }, @@ -19,31 +12,10 @@ export class SettingPane extends MozLitElement { config: { type: Object }, }; - static queries = { - pageHeaderEl: "moz-page-header", - }; - - constructor() { - super(); - /** @type {string} */ - this.name = undefined; - /** @type {boolean} */ - this.isSubPane = false; - /** @type {SettingPaneConfig} */ - this.config = undefined; - } - createRenderRoot() { return this; } - async getUpdateComplete() { - let result = await super.getUpdateComplete(); - // @ts-ignore bug 1997478 - await this.pageHeaderEl.updateComplete; - return result; - } - goBack() { window.gotoPref(this.config.parent); } @@ -78,7 +50,6 @@ export class SettingPane extends MozLitElement { document.getElementById("categories").append(categoryButton); } - /** @param {string} groupId */ groupTemplate(groupId) { return html`<setting-group groupid=${groupId}></setting-group>`; } diff --git a/toolkit/components/extensions/ExtensionSettingsStore.sys.mjs b/toolkit/components/extensions/ExtensionSettingsStore.sys.mjs @@ -548,7 +548,7 @@ export var ExtensionSettingsStore = { * The id of the extension for which the setting is being retrieved. * Defaults to undefined, in which case the top setting is returned. * - * @returns {{ id: string, key: string, value: string}} An object with properties for key, value and id. + * @returns {object} An object with properties for key, value and id. */ getSetting(type, key, id) { return getItem(type, key, id); diff --git a/toolkit/content/preferences/AsyncSetting.mjs b/toolkit/content/preferences/AsyncSetting.mjs @@ -6,25 +6,21 @@ const { EventEmitter } = ChromeUtils.importESModule( "resource://gre/modules/EventEmitter.sys.mjs" ); -/** @import { SettingControlConfig } from "chrome://browser/content/preferences/widgets/setting-control.mjs" */ -/** @import { SettingConfig, SettingValue } from "./Setting.mjs" */ - /** * This is the interface for the async setting classes to implement. * * For the actual implementation see AsyncSettingMixin. */ export class AsyncSetting extends EventEmitter { + /** @type {string} */ static id = ""; - /** @type {SettingConfig['controllingExtensionInfo']} */ + /** @type {Object} */ static controllingExtensionInfo; - /** @type {SettingValue} */ defaultValue = ""; defaultDisabled = false; defaultVisible = true; - /** @type {Partial<SettingControlConfig>} */ defaultGetControlConfig = {}; /** @@ -39,7 +35,7 @@ export class AsyncSetting extends EventEmitter { * Setup any external listeners that are required for managing this * setting's state. When the state needs to update the Setting.emitChange method should be called. * - * @returns {ReturnType<SettingConfig['setup']>} Teardown function to clean up external listeners. + * @returns {Function | void} Teardown function to clean up external listeners. */ setup() {} @@ -47,7 +43,7 @@ export class AsyncSetting extends EventEmitter { * Get the value of this setting. * * @abstract - * @returns {Promise<SettingValue>} + * @returns {Promise<boolean | number | string | void>} */ async get() {} @@ -55,11 +51,10 @@ export class AsyncSetting extends EventEmitter { * Set the value of this setting. * * @abstract - * @param {SettingValue} value The value from the input that triggered the update. + * @param value {any} The value from the input that triggered the update. * @returns {Promise<void>} */ - // eslint-disable-next-line no-unused-vars - async set(value) {} + async set() {} /** * Whether the control should be disabled. @@ -83,7 +78,7 @@ export class AsyncSetting extends EventEmitter { * Override the initial control config. This will be spread into the * initial config, with this object taking precedence. * - * @returns {Promise<Partial<SettingControlConfig>>} + * @returns {Promise<Object>} */ async getControlConfig() { return {}; @@ -93,25 +88,15 @@ export class AsyncSetting extends EventEmitter { * Callback fired after a user has changed the setting's value. Useful for * recording telemetry. * - * @param {SettingValue} value - */ - // eslint-disable-next-line no-unused-vars - onUserChange(value) {} - - /** - * Callback fired after a user has clicked a setting's control. - * - * @param {MouseEvent} event + * @param value {any} + * @returns {Promise<void>} */ - // eslint-disable-next-line no-unused-vars - onUserClick(event) {} + async onUserChange() {} } /** * Wraps an AsyncSetting and adds caching of values to provide a synchronous * API to the Setting class. - * - * @implements {SettingConfig} */ export class AsyncSettingHandler { /** @type {AsyncSetting} */ @@ -120,48 +105,41 @@ export class AsyncSettingHandler { /** @type {Function} */ #emitChange; - /** @type {string} */ - pref; - - /** - * Dependencies are not supported on AsyncSettings, but we include an empty - * array for consistency with {@link SettingConfig}. - * - * @type {string[]} - */ - deps = []; + /** @type {import("./Setting.mjs").PreferenceSettingDepsMap} */ + deps; - /** @type {SettingConfig['controllingExtensionInfo']} */ - controllingExtensionInfo; + /** @type {Setting} */ + setting; /** - * @param {string} id - * @param {typeof AsyncSetting} AsyncSettingClass + * @param {AsyncSetting} asyncSetting */ - constructor(id, AsyncSettingClass) { - this.asyncSetting = new AsyncSettingClass(); - this.id = id; - this.controllingExtensionInfo = AsyncSettingClass.controllingExtensionInfo; + constructor(asyncSetting) { + this.asyncSetting = asyncSetting; this.#emitChange = () => {}; // Initialize cached values with defaults - this.cachedValue = this.asyncSetting.defaultValue; - this.cachedDisabled = this.asyncSetting.defaultDisabled; - this.cachedVisible = this.asyncSetting.defaultVisible; - this.cachedGetControlConfig = this.asyncSetting.defaultGetControlConfig; + this.cachedValue = asyncSetting.defaultValue; + this.cachedDisabled = asyncSetting.defaultDisabled; + this.cachedVisible = asyncSetting.defaultVisible; + this.cachedGetControlConfig = asyncSetting.defaultGetControlConfig; // Listen for change events from the async setting this.asyncSetting.on("change", () => this.refresh()); } /** - * @param {() => any} emitChange - * @returns {ReturnType<SettingConfig['setup']>} + * @param emitChange {Function} + * @param deps {Record<string, Setting>} + * @param setting {Setting} + * @returns {Function | void} */ - setup(emitChange) { + setup(emitChange, deps, setting) { let teardown = this.asyncSetting.setup(); this.#emitChange = emitChange; + this.deps = deps; + this.setting = setting; this.refresh(); return teardown; @@ -186,14 +164,14 @@ export class AsyncSettingHandler { } /** - * @returns {SettingValue} + * @returns {boolean | number | string | void} */ get() { return this.cachedValue; } /** - * @param {any} value + * @param value {any} * @returns {Promise<void>} */ set(value) { @@ -215,8 +193,8 @@ export class AsyncSettingHandler { } /** - * @param {SettingControlConfig} config - * @returns {SettingControlConfig} + * @param config {Object} + * @returns {Object} */ getControlConfig(config) { return { @@ -226,16 +204,10 @@ export class AsyncSettingHandler { } /** - * @param {SettingValue} value + * @param value {any} + * @returns {Promise<void>} */ onUserChange(value) { return this.asyncSetting.onUserChange(value); } - - /** - * @param {MouseEvent} event - */ - onUserClick(event) { - this.asyncSetting.onUserClick(event); - } } diff --git a/toolkit/content/preferences/Preference.mjs b/toolkit/content/preferences/Preference.mjs @@ -9,11 +9,7 @@ const { EventEmitter } = ChromeUtils.importESModule( ); /** - * @typedef {string | boolean | number | nsIFile | void} PreferenceValue - */ - -/** - * @typedef {object} PreferenceConfig + * @typedef {object} PreferenceConfigInfo * @property {string} id * @property {string} type * @property {boolean} [inverted] @@ -33,12 +29,12 @@ function getElementsByAttribute(name, value) { export class Preference extends EventEmitter { /** - * @type {PreferenceValue} + * @type {string | undefined | null} */ _value; /** - * @param {PreferenceConfig} configInfo + * @param {PreferenceConfigInfo} configInfo */ constructor({ id, type, inverted }) { super(); @@ -93,7 +89,7 @@ export class Preference extends EventEmitter { } if (aElement.labels?.length) { for (let label of aElement.labels) { - /** @type {Element} */ (label).toggleAttribute("disabled", this.locked); + label.toggleAttribute("disabled", this.locked); } } @@ -121,14 +117,9 @@ export class Preference extends EventEmitter { * the property setter does not yet exist by setting the same attribute * on the XUL element using DOM apis and assuming the element's * constructor or property getters appropriately handle this state. - * - * @param {Element} element - * @param {string} attribute - * @param {string} value */ function setValue(element, attribute, value) { if (attribute in element) { - // @ts-expect-error The property might not be writable... element[attribute] = value; } else if (attribute === "checked" || attribute === "pressed") { // The "checked" attribute can't simply be set to the specified value; @@ -160,7 +151,7 @@ export class Preference extends EventEmitter { /** * @param {HTMLElement} aElement - * @returns {PreferenceValue} + * @returns {string} */ getElementValue(aElement) { if (Preferences._syncToPrefListeners.has(aElement)) { @@ -179,14 +170,10 @@ export class Preference extends EventEmitter { * attribute is a property on the element's node API. If the property * is not present in the API, then assume its value is contained in * an attribute, as is the case before a binding has been attached. - * - * @param {Element} element - * @param {string} attribute - * @returns {any} */ function getValue(element, attribute) { if (attribute in element) { - return element[/** @type {keyof typeof element} */ (attribute)]; + return element[attribute]; } return element.getAttribute(attribute); } @@ -194,8 +181,7 @@ export class Preference extends EventEmitter { if ( aElement.localName == "checkbox" || aElement.localName == "moz-checkbox" || - (aElement.localName == "input" && - /** @type {HTMLInputElement} */ (aElement).type == "checkbox") + (aElement.localName == "input" && aElement.type == "checkbox") ) { value = getValue(aElement, "checked"); } else if (aElement.localName == "moz-toggle") { @@ -215,6 +201,7 @@ export class Preference extends EventEmitter { /** * @param {HTMLElement} aElement + * @returns {boolean} */ isElementEditable(aElement) { switch (aElement.localName) { @@ -254,14 +241,14 @@ export class Preference extends EventEmitter { } /** - * @type {PreferenceValue} + * @type {Preference['_value']} */ get value() { return this._value; } /** - * @param {PreferenceValue} val + * @param {string} val */ set value(val) { if (this.value !== val) { @@ -306,7 +293,6 @@ export class Preference extends EventEmitter { return Services.prefs.prefHasUserValue(this.id) && this.value !== undefined; } - /** @returns {PreferenceValue} */ get defaultValue() { this._useDefault = true; const val = this.valueFromPreferences; @@ -319,7 +305,7 @@ export class Preference extends EventEmitter { } /** - * @type {PreferenceValue} + * @type {string} */ get valueFromPreferences() { try { @@ -358,7 +344,7 @@ export class Preference extends EventEmitter { } /** - * @param {PreferenceValue} val + * @param {string} val */ set valueFromPreferences(val) { // Exit early if nothing to do. @@ -375,23 +361,23 @@ export class Preference extends EventEmitter { // Force a resync of preferences with value. switch (this.type) { case "int": - Services.prefs.setIntPref(this.id, /** @type {number} */ (val)); + Services.prefs.setIntPref(this.id, val); break; case "bool": - Services.prefs.setBoolPref(this.id, this.inverted ? !val : !!val); + Services.prefs.setBoolPref(this.id, this.inverted ? !val : val); break; case "wstring": { const pls = Cc["@mozilla.org/pref-localizedstring;1"].createInstance( Ci.nsIPrefLocalizedString ); - pls.data = /** @type {string} */ (val); + pls.data = val; Services.prefs.setComplexValue(this.id, Ci.nsIPrefLocalizedString, pls); break; } case "string": case "unichar": case "fontname": - Services.prefs.setStringPref(this.id, /** @type {string} */ (val)); + Services.prefs.setStringPref(this.id, val); break; case "file": { let lf; @@ -402,7 +388,7 @@ export class Preference extends EventEmitter { lf.initWithPath(val); } } else { - lf = /** @type {nsIFile} */ (val).QueryInterface(Ci.nsIFile); + lf = val.QueryInterface(Ci.nsIFile); } Services.prefs.setComplexValue(this.id, Ci.nsIFile, lf); break; diff --git a/toolkit/content/preferences/Preferences.mjs b/toolkit/content/preferences/Preferences.mjs @@ -6,17 +6,134 @@ import { AsyncSetting } from "chrome://global/content/preferences/AsyncSetting.m import { Preference } from "chrome://global/content/preferences/Preference.mjs"; import { Setting } from "chrome://global/content/preferences/Setting.mjs"; -/** @import {PreferenceConfig} from "chrome://global/content/preferences/Preference.mjs" */ -/** @import {SettingConfig} from "chrome://global/content/preferences/Setting.mjs" */ -/** @import {DeferredTask} from "resource://gre/modules/DeferredTask.sys.mjs" */ +/** @import {PreferenceConfigInfo} from "chrome://global/content/preferences/Preference.mjs" */ +/** @import {PreferenceSettingDepsMap} from "chrome://global/content/preferences/Setting.mjs" */ /** - * @typedef {{ _deferredValueUpdateTask: DeferredTask }} DeferredValueObject - * @typedef {DeferredValueObject & HTMLElement} DeferredValueHTMLElement + * @callback PreferenceSettingVisibleFunction + * @param {PreferenceSettingDepsMap} deps + * @param {Setting} setting + * @returns {boolean | string | undefined} If truthy shows the setting in the UI, or hides it if not + */ + +/** + * Gets the value of a {@link PreferencesSettingsConfig}. + * + * @callback PreferenceSettingGetter + * @param {string | number} val - The value that was retrieved from the preferences backend + * @param {PreferenceSettingDepsMap} deps + * @param {Setting} setting + * @returns {any} - The value to set onto the setting + */ + +/** + * Sets the value of a {@link PreferencesSettingsConfig}. + * + * @callback PreferenceSettingSetter + * @param {string | undefined} val - The value/pressed/checked from the input (control) associated with the setting + * @param {PreferenceSettingDepsMap} deps + * @param {Setting} setting + * @returns {void} + */ + +/** + * @callback PreferencesSettingOnUserChangeFunction + * @param {string} val - The value/pressed/checked from the input of the control associated with the setting + * @param {PreferenceSettingDepsMap} deps + * @param {Setting} setting + * @returns {void} + */ + +/** + * @callback PreferencesSettingConfigDisabledFunction + * @param {PreferenceSettingDepsMap} deps + * @param {Setting} setting + * @returns {boolean} + */ + +/** + * @callback PreferencesSettingGetControlConfigFunction + * @param {PreferencesSettingsConfig} config + * @param {PreferenceSettingDepsMap} deps + * @param {Setting} setting + * @returns {PreferencesSettingsConfig | undefined} + */ + +/** + * @callback PreferencesSettingConfigTeardownFunction + * @returns {void} + */ + +/** + * @callback PreferencesSettingConfigSetupFunction + * @param {Function} emitChange + * @param {PreferenceSettingDepsMap} deps + * @param {Setting} setting + * @returns {PreferencesSettingConfigTeardownFunction | void} + */ + +/** + * @callback PreferencesSettingConfigOnUserClickFunction + * @param {Event} event + * @param {PreferenceSettingDepsMap} deps + * @param {Setting} setting + * @returns {void} + */ + +/** + * @typedef {Record<string, any>} PreferencesSettingConfigControlAttributes + */ + +/** + * @typedef {Omit<PreferencesSettingConfigNestedControlOption, 'id | value'>} PreferencesSettingConfigNestedElementOption + */ + +/** + * A set of properties that represent a nested control or element. + * + * @typedef {object} PreferencesSettingConfigNestedControlOption + * @property {string} [control] - The {@link HTMLElement#localName} of any HTML element that will be nested as a direct descendant of the control element. A moz-checkbox will be rendered by default. + * @property {PreferencesSettingConfigControlAttributes} [controlAttrs] - A map of any attributes to add to the control + * @property {string} [l10nId] - The fluent ID of the control + * @property {Array<PreferencesSettingConfigNestedElementOption>} [options] - Options for additional nested HTML elements. This will be overridden if items property is used. + * @property {string} [id] + * @property {string} [value] - An optional initial value used for the control element if it's an input element that supports a value property + * @property {Array<PreferencesSettingsConfig>} [items] - A list of setting control items that will get rendered as direct descendants of the setting control. This overrides the options property. + */ + +/** + * @typedef {object} PreferencesSettingsConfig + * @property {string} id - The ID for the Setting, this should match the layout id + * @property {string} [l10nId] - The Fluent l10n ID for the setting + * @property {Record<string, string>} [l10nArgs] - An object containing l10n IDs and their values that will be translated with Fluent + * @property {string} [pref] - A {@link Services.prefs} id that will be used as the backend if it is provided + * @property {PreferenceSettingVisibleFunction} [visible] - Function to determine if a setting is visible in the UI + * @property {PreferenceSettingGetter} [get] - Function to get the value of the setting. Optional if {@link PreferencesSettingsConfig#pref} is set. + * @property {PreferenceSettingSetter} [set] - Function to set the value of the setting. Optional if {@link PreferencesSettingsConfig#pref} is set. + * @property {PreferencesSettingGetControlConfigFunction} [getControlConfig] - Function that allows the setting to modify its layout, this is intended to be used to provide the options, {@link PreferencesSettingsConfig#l10nId} or {@link PreferencesSettingsConfig#l10nArgs} data if necessary, but technically it can change anything (that doesn't mean it will have any effect though). + * @property {PreferencesSettingOnUserChangeFunction} [onUserChange] - A function that will be called when the setting + * has been modified by the user, it is passed the value/pressed/checked from its input. NOTE: This should be used for + * additional work that needs to happen, such as recording telemetry. + * If you want to set the value of the Setting then use the {@link PreferencesSettingsConfig.set} function. + * @property {Array<PreferencesSettingsConfig> | undefined} [items] + * @property {PreferencesSettingConfigNestedControlOption['control']} [control] - The {@link HTMLElement#localName} of any HTML element that will be rendered as a control in the UI for the setting. + * @property {PreferencesSettingConfigSetupFunction} [setup] - A function to be called to register listeners for + * the setting. It should return a {@link PreferencesSettingConfigTeardownFunction} function to + * remove the listeners if necessary. This should emit change events when the setting has changed to + * ensure the UI stays in sync if possible. + * @property {PreferencesSettingConfigDisabledFunction} [disabled] - A function to determine if a setting should be disabled + * @property {PreferencesSettingConfigOnUserClickFunction} [onUserClick] - A function that will be called when a setting has been + * clicked, the element name must be included in the CLICK_HANDLERS array + * in {@link file://./../../browser/components/preferences/widgets/setting-group/setting-group.mjs}. This should be + * used for controls that aren’t regular form controls but instead perform an action when clicked, like a button or link. + * @property {Array<string> | void} [deps] - An array of setting IDs that this setting depends on, when these settings change this setting will emit a change event to update the UI + * @property {PreferencesSettingConfigControlAttributes} [controlAttrs] - An object of additional attributes to be set on the control. These can be used to further customize the control for example a message bar of the warning type, or what dialog a button should open + * @property {Array<PreferencesSettingConfigNestedControlOption>} [options] - An optional list of nested controls for this setting (select options, radio group radios, etc) + * @property {string} [iconSrc] - A path to the icon for the control (if the control supports one) + * @property {string} [supportPage] - The SUMO support page slug for the setting + * @property {string} [subcategory] - The sub-category slug used for direct linking to a setting from SUMO */ -/** @type {{ DeferredTask: typeof DeferredTask }} */ -// @ts-expect-error bug 1996860 const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs", @@ -41,7 +158,7 @@ export const Preferences = { _settings: new Map(), /** - * @param {PreferenceConfig} prefInfo + * @param {PreferenceConfigInfo} prefInfo */ _add(prefInfo) { if (this._all[prefInfo.id]) { @@ -58,7 +175,7 @@ export const Preferences = { }, /** - * @param {PreferenceConfig} prefInfo + * @param {PreferenceConfigInfo} prefInfo * @returns {Preference} */ add(prefInfo) { @@ -67,7 +184,7 @@ export const Preferences = { }, /** - * @param {Array<PreferenceConfig>} prefInfos + * @param {Array<PreferenceConfigInfo>} prefInfos */ addAll(prefInfos) { prefInfos.map(prefInfo => this._add(prefInfo)); @@ -82,7 +199,7 @@ export const Preferences = { }, /** - * @returns {Array<Preference>} + * @returns {Array<PreferenceConfigInfo>} */ getAll() { return Object.values(this._all); @@ -93,7 +210,7 @@ export const Preferences = { * that includes all of the configuration for the control * such as its Fluent strings, support page, subcategory etc. * - * @param {SettingConfig} settingConfig + * @param {PreferencesSettingsConfig} settingConfig */ addSetting(settingConfig) { this._settings.set( @@ -135,12 +252,7 @@ export const Preferences = { this._instantApplyForceEnabled = true; }, - /** - * @param {nsISupports} _ - * @param {string} __ - * @param {string} data - */ - observe(_, __, data) { + observe(subject, topic, data) { const pref = this._all[data]; if (pref) { pref.value = pref.valueFromPreferences; @@ -181,21 +293,15 @@ export const Preferences = { }); }, - /** - * @param {Event} _ - */ - onUnload(_) { + onUnload() { this._settings.forEach(setting => setting?.destroy?.()); - Services.prefs.removeObserver("", /** @type {nsIObserver} */ (this)); + Services.prefs.removeObserver("", this); }, QueryInterface: ChromeUtils.generateQI(["nsITimerCallback", "nsIObserver"]), _deferredValueUpdateElements: new Set(), - /** - * @param {boolean} aFlushToDisk - */ writePreferences(aFlushToDisk) { // Write all values to preferences. if (this._deferredValueUpdateElements.size) { @@ -213,9 +319,6 @@ export const Preferences = { } }, - /** - * @param {HTMLElement} aStartElement - */ getPreferenceElement(aStartElement) { let temp = aStartElement; while ( @@ -223,15 +326,11 @@ export const Preferences = { temp.nodeType == Node.ELEMENT_NODE && !temp.hasAttribute("preference") ) { - // @ts-expect-error temp = temp.parentNode; } return temp && temp.nodeType == Node.ELEMENT_NODE ? temp : aStartElement; }, - /** - * @param {DeferredValueHTMLElement} aElement - */ _deferredValueUpdate(aElement) { delete aElement._deferredValueUpdateTask; const prefID = aElement.getAttribute("preference"); @@ -249,13 +348,8 @@ export const Preferences = { } }, - /** - * @param {HTMLElement} aElement - */ userChangedValue(aElement) { - const element = /** @type {DeferredValueHTMLElement} */ ( - this.getPreferenceElement(aElement) - ); + const element = this.getPreferenceElement(aElement); if (element.hasAttribute("preference")) { if (element.getAttribute("delayprefsave") != "true") { const preference = Preferences.get(element.getAttribute("preference")); @@ -277,37 +371,27 @@ export const Preferences = { } }, - /** - * @typedef {{ sourceEvent: CommandEventWithSource } & CommandEvent} CommandEventWithSource - * @param {CommandEventWithSource} event - */ onCommand(event) { // This "command" event handler tracks changes made to preferences by // the user in this window. if (event.sourceEvent) { event = event.sourceEvent; } - this.userChangedValue(/** @type {HTMLElement} */ (event.target)); + this.userChangedValue(event.target); }, - /** @param {Event} event */ onChange(event) { // This "change" event handler tracks changes made to preferences by // the user in this window. - this.userChangedValue(/** @type {HTMLElement} */ (event.target)); + this.userChangedValue(event.target); }, - /** @param {Event} event */ onInput(event) { // This "input" event handler tracks changes made to preferences by // the user in this window. - this.userChangedValue(/** @type {HTMLElement} */ (event.target)); + this.userChangedValue(event.target); }, - /** - * @param {string} aEventName - * @param {HTMLElement} aTarget - */ _fireEvent(aEventName, aTarget) { try { const event = new CustomEvent(aEventName, { @@ -321,9 +405,6 @@ export const Preferences = { return false; }, - /** - * @param {Event} event - */ onDialogAccept(event) { let dialog = document.querySelector("dialog"); if (!this._fireEvent("beforeaccept", dialog)) { @@ -334,9 +415,6 @@ export const Preferences = { return true; }, - /** - * @param {Event} event - */ close(event) { if (Preferences.instantApply) { window.close(); @@ -345,16 +423,13 @@ export const Preferences = { event.preventDefault(); }, - /** - * @param {Event} event - */ handleEvent(event) { switch (event.type) { case "toggle": case "change": return this.onChange(event); case "command": - return this.onCommand(/** @type {CommandEventWithSource} */ (event)); + return this.onCommand(event); case "dialogaccept": return this.onDialogAccept(event); case "input": @@ -409,16 +484,10 @@ export const Preferences = { } }, - /** - * @param {Element} aElement - */ removeSyncFromPrefListener(aElement) { this._syncFromPrefListeners.delete(aElement); }, - /** - * @param {Element} aElement - */ removeSyncToPrefListener(aElement) { this._syncToPrefListeners.delete(aElement); }, @@ -428,7 +497,7 @@ export const Preferences = { Setting, }; -Services.prefs.addObserver("", /** @type {nsIObserver} */ (Preferences)); +Services.prefs.addObserver("", Preferences); window.addEventListener("toggle", Preferences); window.addEventListener("change", Preferences); window.addEventListener("command", Preferences); diff --git a/toolkit/content/preferences/Setting.mjs b/toolkit/content/preferences/Setting.mjs @@ -8,140 +8,13 @@ import { } from "chrome://global/content/preferences/AsyncSetting.mjs"; import { Preferences } from "chrome://global/content/preferences/Preferences.mjs"; -/** - * @import { type Preference } from "chrome://global/content/preferences/Preference.mjs" - * @import { SettingControlConfig } from "chrome://browser/content/preferences/widgets/setting-control.mjs" - * @import { ExtensionSettingsStore } from "resource://gre/modules/ExtensionSettingsStore.sys.mjs" - * @import { AddonManager } from "resource://gre/modules/AddonManager.sys.mjs" - * @import { Management } from "resource://gre/modules/Extension.sys.mjs" - */ - -/** - * A map of Setting instances (values) along with their IDs - * (keys) so that the dependencies of a setting can - * be easily looked up by just their ID. - * - * @typedef {Record<string, Setting>} SettingDeps - */ - -/** - * @typedef {string | boolean | number | nsIFile | void} SettingValue - */ - -/** - * @callback SettingVisibleCallback - * @param {SettingDeps} deps - * @param {Setting} setting - * @returns {any} If truthy shows the setting in the UI, or hides it if not - */ - -/** - * Gets the value of a {@link Setting}. - * - * @callback SettingGetCallback - * @param {any} val - The value that was retrieved from the preferences backend - * @param {SettingDeps} deps - * @param {Setting} setting - * @returns {any} - The value to set onto the setting - */ - -/** - * Sets the value of a {@link Setting}. - * - * @callback SettingSetCallback - * @param {SettingValue} val - The value/pressed/checked from the input (control) associated with the setting - * @param {SettingDeps} deps - * @param {Setting} setting - * @returns {void} - */ - -/** - * @callback SettingOnUserChangeCallback - * @param {SettingValue} val - The value/pressed/checked from the input of the control associated with the setting - * @param {SettingDeps} deps - * @param {Setting} setting - * @returns {void} - */ - -/** - * @callback SettingDisabledCallback - * @param {SettingDeps} deps - * @param {Setting} setting - * @returns {any} - */ - -/** - * @callback SettingGetControlConfigCallback - * @param {SettingControlConfig} config - * @param {SettingDeps} deps - * @param {Setting} setting - * @returns {SettingControlConfig} - */ - -/** - * @callback SettingTeardownCallback - * @returns {void} - */ - -/** - * @callback SettingEmitChange - */ - -/** - * @callback SettingSetupCallback - * @param {SettingEmitChange} emitChange Notify listeners of a change to this setting. - * @param {SettingDeps} deps - * @param {Setting} setting - * @returns {SettingTeardownCallback | void} - */ - -/** - * @callback SettingOnUserClickCallback - * @param {MouseEvent} event - * @param {SettingDeps} deps - * @param {Setting} setting - * @returns {void} - */ - -/** - * @typedef {Object} SettingControllingExtensionInfo - * @property {string} storeId The ExtensionSettingsStore id that controls this setting. - * @property {string} l10nId A fluent id to show in a controlled by extension message. - * @property {string} [name] The controlling extension's name. - * @property {string} [id] The controlling extension's id. - */ - -/** - * @typedef {object} SettingConfig - * @property {string} id - The ID for the Setting, this should match the layout id - * @property {string} [pref] - A {@link Services.prefs} id that will be used as the backend if it is provided - * @property {string[]} [deps] - An array of setting IDs that this setting depends on, when these settings change this setting will emit a change event to update the UI - * @property {Pick<SettingControllingExtensionInfo, "storeId" | "l10nId">} [controllingExtensionInfo] Data related to the setting being controlled by an extension. - * @property {SettingVisibleCallback} [visible] - Function to determine if a setting is visible in the UI - * @property {SettingGetCallback} [get] - Function to get the value of the setting. Optional if {@link SettingConfig#pref} is set. - * @property {SettingSetCallback} [set] - Function to set the value of the setting. Optional if {@link SettingConfig#pref} is set. - * @property {SettingGetControlConfigCallback} [getControlConfig] - Function that allows the setting to modify its layout, this is intended to be used to provide the options, {@link SettingConfig#l10nId} or {@link SettingConfig#l10nArgs} data if necessary, but technically it can change anything (that doesn't mean it will have any effect though). - * @property {SettingOnUserChangeCallback} [onUserChange] - A function that will be called when the setting - * has been modified by the user, it is passed the value/pressed/checked from its input. NOTE: This should be used for - * additional work that needs to happen, such as recording telemetry. - * If you want to set the value of the Setting then use the {@link SettingConfig.set} function. - * @property {SettingSetupCallback} [setup] - A function to be called to register listeners for - * the setting. It should return a {@link SettingTeardownCallback} function to - * remove the listeners if necessary. This should emit change events when the setting has changed to - * ensure the UI stays in sync if possible. - * @property {SettingDisabledCallback} [disabled] - A function to determine if a setting should be disabled - * @property {SettingOnUserClickCallback} [onUserClick] - A function that will be called when a setting has been - * clicked, the element name must be included in the CLICK_HANDLERS array - * in {@link file://./../../browser/components/preferences/widgets/setting-group/setting-group.mjs}. This should be - * used for controls that aren’t regular form controls but instead perform an action when clicked, like a button or link. - */ +/** @import { type Preference } from "chrome://global/content/preferences/Preference.mjs" */ +/** @import { PreferencesSettingsConfig } from "chrome://global/content/preferences/Preferences.mjs" */ const { EventEmitter } = ChromeUtils.importESModule( "resource://gre/modules/EventEmitter.sys.mjs" ); -/** @type {{ ExtensionSettingsStore: typeof ExtensionSettingsStore, AddonManager: typeof AddonManager, Management: typeof Management }} */ -// @ts-expect-error bug 1996860 const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { ExtensionSettingsStore: @@ -150,11 +23,19 @@ ChromeUtils.defineESModuleGetters(lazy, { Management: "resource://gre/modules/Extension.sys.mjs", }); +/** + * A map of Setting instances (values) along with their IDs + * (keys) so that the dependencies of a setting can + * be easily looked up by just their ID. + * + * @typedef {Record<string, Setting | undefined>} PreferenceSettingDepsMap + */ + +/** + * @typedef {string | boolean | number} SettingValue + */ + class PreferenceNotAddedError extends Error { - /** - * @param {string} settingId - * @param {string} prefId - */ constructor(settingId, prefId) { super( `Setting "${settingId}" was unable to find Preference "${prefId}". Did you register it with Preferences.add/addAll?` @@ -167,7 +48,7 @@ class PreferenceNotAddedError extends Error { export class Setting extends EventEmitter { /** - * @type {Preference} + * @type {Preference | undefined | null} */ pref; @@ -175,45 +56,36 @@ export class Setting extends EventEmitter { * Keeps a cache of each dep's Setting so that * it can be easily looked up by its ID. * - * @type {SettingDeps} + * @type {PreferenceSettingDepsMap | undefined} */ _deps; /** - * @type {SettingConfig | AsyncSettingHandler} + * @type {PreferencesSettingsConfig} */ config; /** - * @param {SettingConfig['id']} id - * @param {SettingConfig | typeof AsyncSetting} config + * @param {PreferencesSettingsConfig['id']} id + * @param {PreferencesSettingsConfig} config * @throws {Error} Will throw an error (PreferenceNotAddedError) if * config.pref was not registered */ constructor(id, config) { super(); - /** @type {SettingConfig | AsyncSettingHandler} */ - let configObj; - if (Object.getPrototypeOf(config) == AsyncSetting) { - configObj = new AsyncSettingHandler( - id, - /** @type {typeof AsyncSetting} */ (config) - ); - } else { - configObj = config; + config = new AsyncSettingHandler(new config()); } this.id = id; - this.config = configObj; - this.pref = configObj.pref && Preferences.get(configObj.pref); - if (configObj.pref && !this.pref) { - throw new PreferenceNotAddedError(id, configObj.pref); + this.config = config; + this.pref = config.pref && Preferences.get(config.pref); + if (config.pref && !this.pref) { + throw new PreferenceNotAddedError(id, config.pref); } this._emitting = false; - /** @type {SettingControllingExtensionInfo} */ this.controllingExtensionInfo = { ...this.config.controllingExtensionInfo, }; @@ -241,14 +113,14 @@ export class Setting extends EventEmitter { /** * A map of each dep and it's associated {@link Setting} instance. * - * @type {SettingDeps} + * @type {PreferenceSettingDepsMap} */ get deps() { if (this._deps) { return this._deps; } /** - * @type {SettingDeps} + * @type {PreferenceSettingDepsMap} */ const deps = {}; @@ -285,7 +157,7 @@ export class Setting extends EventEmitter { */ set value(val) { let newVal = this.config.set ? this.config.set(val, this.deps, this) : val; - if (this.pref && newVal instanceof Object && !("then" in newVal)) { + if (this.pref) { this.pref.value = newVal; } } @@ -306,8 +178,8 @@ export class Setting extends EventEmitter { } /** - * @param {SettingControlConfig} config - * @returns {SettingControlConfig} + * @param {PreferencesSettingsConfig} config + * @returns {PreferencesSettingsConfig | undefined} */ getControlConfig(config) { if (this.config.getControlConfig) { @@ -316,9 +188,6 @@ export class Setting extends EventEmitter { return config; } - /** - * @param {MouseEvent} event - */ userClick(event) { if (this.config.onUserClick) { this.config.onUserClick(event, this.deps, this); @@ -341,7 +210,7 @@ export class Setting extends EventEmitter { this.controllingExtensionInfo.id ) { await lazy.ExtensionSettingsStore.initialize(); - let { id } = lazy.ExtensionSettingsStore.getSetting( + let { id } = await lazy.ExtensionSettingsStore.getSetting( "prefs", this.controllingExtensionInfo.storeId ); @@ -352,10 +221,6 @@ export class Setting extends EventEmitter { } } - /** - * @param {any} _ - * @param {{ key: string, type: string }} setting ExtensionSettingsStore setting - */ _observeExtensionSettingChanged = (_, setting) => { if ( setting.key == this.config.controllingExtensionInfo.storeId && diff --git a/toolkit/content/widgets/lit-utils.mjs b/toolkit/content/widgets/lit-utils.mjs @@ -263,10 +263,7 @@ export class MozBaseInputElement extends MozLitElement { ariaLabel: { type: String, mapped: true }, ariaDescription: { type: String, mapped: true }, }; - /** @type {"inline" | "block"} */ static inputLayout = "inline"; - /** @type {keyof MozBaseInputElement} */ - static activatedProperty = null; constructor() { super(); @@ -308,9 +305,7 @@ export class MozBaseInputElement extends MozLitElement { if (changedProperties.has("value")) { this.setFormValue(this.value); } - let activatedProperty = /** @type {typeof MozBaseInputElement} */ ( - this.constructor - ).activatedProperty; + let activatedProperty = this.constructor.activatedProperty; if ( (activatedProperty && changedProperties.has(activatedProperty)) || changedProperties.has("disabled") || diff --git a/toolkit/content/widgets/moz-button/moz-button.css b/toolkit/content/widgets/moz-button/moz-button.css @@ -2,10 +2,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/. */ -:host([hidden]) { - display: none !important; -} - :host { display: inline-block; height: fit-content; diff --git a/toolkit/content/widgets/moz-page-header/moz-page-header.mjs b/toolkit/content/widgets/moz-page-header/moz-page-header.mjs @@ -29,11 +29,6 @@ export default class MozPageHeader extends MozLitElement { backButton: { type: Boolean }, }; - static queries = { - headingEl: "h1", - backButtonEl: "moz-button", - }; - constructor() { super(); this.heading = ""; diff --git a/tools/@types/generated/lib.gecko.dom.d.ts b/tools/@types/generated/lib.gecko.dom.d.ts @@ -26247,7 +26247,7 @@ interface CreateScriptURLCallback { } interface CustomElementConstructor { - new (...params: any[]): HTMLElement; + (): any; } interface CustomElementCreationCallback { diff --git a/tools/@types/generated/lib.gecko.linux.d.ts b/tools/@types/generated/lib.gecko.linux.d.ts @@ -11,8 +11,6 @@ declare global { interface nsIGNOMEShellService extends nsIShellService { readonly canSetDesktopBackground: boolean; isDefaultForScheme(aScheme: string): boolean; - getGSettingsString(aScheme: string, aKey: string): string; - setGSettingsString(aScheme: string, aKey: string, aValue: string): void; } // https://searchfox.org/mozilla-central/source/browser/components/shell/nsIOpenTabsProvider.idl diff --git a/tools/@types/generated/lib.gecko.modules.d.ts b/tools/@types/generated/lib.gecko.modules.d.ts @@ -29,7 +29,6 @@ export interface Modules { "chrome://global/content/ml/ClusterAlgos.sys.mjs": typeof import("chrome://global/content/ml/ClusterAlgos.sys.mjs"), "chrome://global/content/ml/EmbeddingsGenerator.sys.mjs": typeof import("chrome://global/content/ml/EmbeddingsGenerator.sys.mjs"), "chrome://global/content/ml/EngineProcess.sys.mjs": typeof import("chrome://global/content/ml/EngineProcess.sys.mjs"), - "chrome://global/content/ml/MLTelemetry.sys.mjs": typeof import("chrome://global/content/ml/MLTelemetry.sys.mjs"), "chrome://global/content/ml/ModelHub.sys.mjs": typeof import("chrome://global/content/ml/ModelHub.sys.mjs"), "chrome://global/content/ml/NLPUtils.sys.mjs": typeof import("chrome://global/content/ml/NLPUtils.sys.mjs"), "chrome://global/content/ml/OPFS.sys.mjs": typeof import("chrome://global/content/ml/OPFS.sys.mjs"), @@ -102,7 +101,6 @@ export interface Modules { "chrome://remote/content/shared/Navigate.sys.mjs": typeof import("chrome://remote/content/shared/Navigate.sys.mjs"), "chrome://remote/content/shared/NavigationManager.sys.mjs": typeof import("chrome://remote/content/shared/NavigationManager.sys.mjs"), "chrome://remote/content/shared/NetworkCacheManager.sys.mjs": typeof import("chrome://remote/content/shared/NetworkCacheManager.sys.mjs"), - "chrome://remote/content/shared/NetworkDataBytes.sys.mjs": typeof import("chrome://remote/content/shared/NetworkDataBytes.sys.mjs"), "chrome://remote/content/shared/NetworkDecodedBodySizeMap.sys.mjs": typeof import("chrome://remote/content/shared/NetworkDecodedBodySizeMap.sys.mjs"), "chrome://remote/content/shared/NetworkRequest.sys.mjs": typeof import("chrome://remote/content/shared/NetworkRequest.sys.mjs"), "chrome://remote/content/shared/NetworkResponse.sys.mjs": typeof import("chrome://remote/content/shared/NetworkResponse.sys.mjs"), @@ -283,7 +281,6 @@ export interface Modules { "moz-src:///browser/components/urlbar/private/AmpSuggestions.sys.mjs": typeof import("moz-src:///browser/components/urlbar/private/AmpSuggestions.sys.mjs"), "moz-src:///browser/components/urlbar/private/GeolocationUtils.sys.mjs": typeof import("moz-src:///browser/components/urlbar/private/GeolocationUtils.sys.mjs"), "moz-src:///browser/components/urlbar/private/MLSuggest.sys.mjs": typeof import("moz-src:///browser/components/urlbar/private/MLSuggest.sys.mjs"), - "moz-src:///browser/components/urlbar/private/SportsSuggestions.sys.mjs": typeof import("moz-src:///browser/components/urlbar/private/SportsSuggestions.sys.mjs"), "moz-src:///browser/components/urlbar/private/SuggestBackendRust.sys.mjs": typeof import("moz-src:///browser/components/urlbar/private/SuggestBackendRust.sys.mjs"), "moz-src:///browser/modules/CanvasPermissionPromptHelper.sys.mjs": typeof import("moz-src:///browser/modules/CanvasPermissionPromptHelper.sys.mjs"), "moz-src:///browser/modules/ContextId.sys.mjs": typeof import("moz-src:///browser/modules/ContextId.sys.mjs"), @@ -360,7 +357,6 @@ export interface Modules { "resource:///modules/ChromeProfileMigrator.sys.mjs": typeof import("resource:///modules/ChromeProfileMigrator.sys.mjs"), "resource:///modules/ChromeWindowsLoginCrypto.sys.mjs": typeof import("resource:///modules/ChromeWindowsLoginCrypto.sys.mjs"), "resource:///modules/ContentCrashHandlers.sys.mjs": typeof import("resource:///modules/ContentCrashHandlers.sys.mjs"), - "resource:///modules/CustomKeys.sys.mjs": typeof import("resource:///modules/CustomKeys.sys.mjs"), "resource:///modules/Dedupe.sys.mjs": typeof import("resource:///modules/Dedupe.sys.mjs"), "resource:///modules/DevToolsStartup.sys.mjs": typeof import("resource:///modules/DevToolsStartup.sys.mjs"), "resource:///modules/Discovery.sys.mjs": typeof import("resource:///modules/Discovery.sys.mjs"), @@ -468,7 +464,6 @@ export interface Modules { "resource:///modules/ipprotection/IPPProxyManager.sys.mjs": typeof import("resource:///modules/ipprotection/IPPProxyManager.sys.mjs"), "resource:///modules/ipprotection/IPPSignInWatcher.sys.mjs": typeof import("resource:///modules/ipprotection/IPPSignInWatcher.sys.mjs"), "resource:///modules/ipprotection/IPPStartupCache.sys.mjs": typeof import("resource:///modules/ipprotection/IPPStartupCache.sys.mjs"), - "resource:///modules/ipprotection/IPPVPNAddonHelper.sys.mjs": typeof import("resource:///modules/ipprotection/IPPVPNAddonHelper.sys.mjs"), "resource:///modules/ipprotection/IPProtection.sys.mjs": typeof import("resource:///modules/ipprotection/IPProtection.sys.mjs"), "resource:///modules/ipprotection/IPProtectionHelpers.sys.mjs": typeof import("resource:///modules/ipprotection/IPProtectionHelpers.sys.mjs"), "resource:///modules/ipprotection/IPProtectionPanel.sys.mjs": typeof import("resource:///modules/ipprotection/IPProtectionPanel.sys.mjs"), @@ -664,7 +659,6 @@ export interface Modules { "resource://gre/modules/CustomElementsListener.sys.mjs": typeof import("resource://gre/modules/CustomElementsListener.sys.mjs"), "resource://gre/modules/DAPIncrementality.sys.mjs": typeof import("resource://gre/modules/DAPIncrementality.sys.mjs"), "resource://gre/modules/DAPReportController.sys.mjs": typeof import("resource://gre/modules/DAPReportController.sys.mjs"), - "resource://gre/modules/DAPSender.sys.mjs": typeof import("resource://gre/modules/DAPSender.sys.mjs"), "resource://gre/modules/DAPTelemetrySender.sys.mjs": typeof import("resource://gre/modules/DAPTelemetrySender.sys.mjs"), "resource://gre/modules/DAPVisitCounter.sys.mjs": typeof import("resource://gre/modules/DAPVisitCounter.sys.mjs"), "resource://gre/modules/DateTimePickerPanel.sys.mjs": typeof import("resource://gre/modules/DateTimePickerPanel.sys.mjs"), @@ -1018,7 +1012,6 @@ export interface Modules { "resource://newtab/lib/PrefsFeed.sys.mjs": typeof import("resource://newtab/lib/PrefsFeed.sys.mjs"), "resource://newtab/lib/RecommendationProvider.sys.mjs": typeof import("resource://newtab/lib/RecommendationProvider.sys.mjs"), "resource://newtab/lib/Screenshots.sys.mjs": typeof import("resource://newtab/lib/Screenshots.sys.mjs"), - "resource://newtab/lib/SectionsLayoutManager.sys.mjs": typeof import("resource://newtab/lib/SectionsLayoutManager.sys.mjs"), "resource://newtab/lib/SectionsManager.sys.mjs": typeof import("resource://newtab/lib/SectionsManager.sys.mjs"), "resource://newtab/lib/SmartShortcutsFeed.sys.mjs": typeof import("resource://newtab/lib/SmartShortcutsFeed.sys.mjs"), "resource://newtab/lib/SmartShortcutsRanker/RankShortcuts.mjs": typeof import("resource://newtab/lib/SmartShortcutsRanker/RankShortcuts.mjs"), diff --git a/tools/@types/generated/lib.gecko.xpcom.d.ts b/tools/@types/generated/lib.gecko.xpcom.d.ts @@ -10333,28 +10333,6 @@ interface mozISandboxSettings extends nsISupports { readonly contentWin32kLockdownStateString: string; } -// https://searchfox.org/mozilla-central/source/security/sandbox/linux/interfaces/mozISandboxReporter.idl - -interface mozISandboxReport extends nsISupports { - readonly msecAgo: u64; - readonly pid: i32; - readonly tid: i32; - readonly procType: string; - readonly syscall: u32; - readonly numArgs: u32; - getArg(aIndex: u32): string; -} - -interface mozISandboxReportArray extends nsISupports { - readonly begin: u64; - readonly end: u64; - getElement(aIndex: u64): mozISandboxReport; -} - -interface mozISandboxReporter extends nsISupports { - snapshot(): mozISandboxReportArray; -} - // https://searchfox.org/mozilla-central/source/toolkit/components/satchel/nsIFormFillController.idl interface nsIFormFillController extends nsISupports { @@ -13666,12 +13644,6 @@ interface nsIAvailableMemoryWatcherBase extends nsISupports { onUnloadAttemptCompleted(aResult: nsresult): void; } -// https://searchfox.org/mozilla-central/source/xpcom/base/nsIAvailableMemoryWatcherTestingLinux.idl - -interface nsIAvailableMemoryWatcherTestingLinux extends nsISupports { - setPSIPathForTesting(aPSIPath: string): void; -} - // https://searchfox.org/mozilla-central/source/xpcom/base/nsIConsoleListener.idl type nsIConsoleListener = Callable<{ @@ -16226,9 +16198,6 @@ interface nsIXPCComponents_Interfaces { nsIApplicationReputationQuery: nsJSIID<nsIApplicationReputationQuery>; nsIApplicationReputationCallback: nsJSIID<nsIApplicationReputationCallback>; mozISandboxSettings: nsJSIID<mozISandboxSettings>; - mozISandboxReport: nsJSIID<mozISandboxReport>; - mozISandboxReportArray: nsJSIID<mozISandboxReportArray>; - mozISandboxReporter: nsJSIID<mozISandboxReporter>; nsIFormFillController: nsJSIID<nsIFormFillController>; nsIFormFillCompleteObserver: nsJSIID<nsIFormFillCompleteObserver>; mozIBridgedSyncEngineCallback: nsJSIID<mozIBridgedSyncEngineCallback>; @@ -16464,7 +16433,6 @@ interface nsIXPCComponents_Interfaces { nsIWindowWatcher: nsJSIID<nsIWindowWatcher>; nsITabUnloader: nsJSIID<nsITabUnloader>; nsIAvailableMemoryWatcherBase: nsJSIID<nsIAvailableMemoryWatcherBase>; - nsIAvailableMemoryWatcherTestingLinux: nsJSIID<nsIAvailableMemoryWatcherTestingLinux>; nsIConsoleListener: nsJSIID<nsIConsoleListener>; nsIConsoleMessage: nsJSIID<nsIConsoleMessage>; nsIConsoleService: nsJSIID<nsIConsoleService, typeof nsIConsoleService_OutputMode>; diff --git a/tools/@types/generated/tspaths.json b/tools/@types/generated/tspaths.json @@ -92,9 +92,6 @@ "chrome://browser/content/ipprotection/ipprotection-signedout.mjs": [ "browser/components/ipprotection/content/ipprotection-signedout.mjs" ], - "chrome://browser/content/ipprotection/ipprotection-status-card.mjs": [ - "browser/components/ipprotection/content/ipprotection-status-card.mjs" - ], "chrome://browser/content/ipprotection/ipprotection-timer.mjs": [ "browser/components/ipprotection/content/ipprotection-timer.mjs" ], @@ -125,9 +122,6 @@ "chrome://browser/content/preferences/widgets/setting-group.mjs": [ "browser/components/preferences/widgets/setting-group/setting-group.mjs" ], - "chrome://browser/content/preferences/widgets/setting-pane.mjs": [ - "browser/components/preferences/widgets/setting-pane/setting-pane.mjs" - ], "chrome://browser/content/preferences/widgets/sync-device-name.mjs": [ "browser/components/preferences/widgets/sync-device-name/sync-device-name.mjs" ], @@ -251,12 +245,6 @@ "chrome://global/content/elements/moz-fieldset.mjs": [ "toolkit/content/widgets/moz-fieldset/moz-fieldset.mjs" ], - "chrome://global/content/elements/moz-input-folder.mjs": [ - "toolkit/content/widgets/moz-input-folder/moz-input-folder.mjs" - ], - "chrome://global/content/elements/moz-input-search.mjs": [ - "toolkit/content/widgets/moz-input-search/moz-input-search.mjs" - ], "chrome://global/content/elements/moz-input-text.mjs": [ "toolkit/content/widgets/moz-input-text/moz-input-text.mjs" ], @@ -266,9 +254,6 @@ "chrome://global/content/elements/moz-message-bar.mjs": [ "toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs" ], - "chrome://global/content/elements/moz-radio-group.mjs": [ - "toolkit/content/widgets/moz-radio-group/moz-radio-group.mjs" - ], "chrome://global/content/elements/moz-support-link.mjs": [ "toolkit/content/widgets/moz-support-link/moz-support-link.mjs" ], @@ -311,9 +296,6 @@ "chrome://global/content/ml/EngineProcess.sys.mjs": [ "toolkit/components/ml/content/EngineProcess.sys.mjs" ], - "chrome://global/content/ml/MLTelemetry.sys.mjs": [ - "toolkit/components/ml/MLTelemetry.sys.mjs" - ], "chrome://global/content/ml/ModelHub.sys.mjs": [ "toolkit/components/ml/content/ModelHub.sys.mjs" ], @@ -590,9 +572,6 @@ "chrome://remote/content/shared/NetworkCacheManager.sys.mjs": [ "remote/shared/NetworkCacheManager.sys.mjs" ], - "chrome://remote/content/shared/NetworkDataBytes.sys.mjs": [ - "remote/shared/NetworkDataBytes.sys.mjs" - ], "chrome://remote/content/shared/NetworkDecodedBodySizeMap.sys.mjs": [ "remote/shared/NetworkDecodedBodySizeMap.sys.mjs" ], @@ -959,9 +938,6 @@ "resource:///modules/ContentCrashHandlers.sys.mjs": [ "browser/modules/ContentCrashHandlers.sys.mjs" ], - "resource:///modules/CustomKeys.sys.mjs": [ - "browser/components/customkeys/CustomKeys.sys.mjs" - ], "resource:///modules/Dedupe.sys.mjs": [ "browser/modules/Dedupe.sys.mjs" ], @@ -1301,9 +1277,6 @@ "resource:///modules/ipprotection/IPPStartupCache.sys.mjs": [ "browser/components/ipprotection/IPPStartupCache.sys.mjs" ], - "resource:///modules/ipprotection/IPPVPNAddonHelper.sys.mjs": [ - "browser/components/ipprotection/IPPVPNAddonHelper.sys.mjs" - ], "resource:///modules/ipprotection/IPProtection.sys.mjs": [ "browser/components/ipprotection/IPProtection.sys.mjs" ], @@ -5405,9 +5378,6 @@ "resource://gre/modules/DAPReportController.sys.mjs": [ "toolkit/components/dap/DAPReportController.sys.mjs" ], - "resource://gre/modules/DAPSender.sys.mjs": [ - "toolkit/components/dap/DAPSender.sys.mjs" - ], "resource://gre/modules/DAPTelemetrySender.sys.mjs": [ "toolkit/components/dap/DAPTelemetrySender.sys.mjs" ], @@ -6572,9 +6542,6 @@ "resource://newtab/lib/Screenshots.sys.mjs": [ "browser/extensions/newtab/lib/Screenshots.sys.mjs" ], - "resource://newtab/lib/SectionsLayoutManager.sys.mjs": [ - "browser/extensions/newtab/lib/SectionsLayoutManager.sys.mjs" - ], "resource://newtab/lib/SectionsManager.sys.mjs": [ "browser/extensions/newtab/lib/SectionsManager.sys.mjs" ], diff --git a/tools/ts/build_dom.js b/tools/ts/build_dom.js @@ -25,19 +25,6 @@ const GENERATED_WEDIDL_FILES = [ "CSSStyleProperties.webidl", ]; -// Support overrides using the syntax from @typescript/dom-lib-generator which -// is parsing our webidl files and generating types. -// https://github.com/microsoft/TypeScript-DOM-lib-generator/blob/main/inputfiles/overridingTypes.jsonc -const OVERRIDE_TYPES = { - callbackFunctions: { - callbackFunction: { - CustomElementConstructor: { - overrideSignatures: ["new (...params: any[]): HTMLElement"], - }, - }, - }, -}; - const HEADER = `/** * NOTE: Do not modify this file by hand. * Content was generated from source .webidl files. @@ -180,8 +167,6 @@ async function emitDom(webidls, builtin = "builtin.webidl") { } } - merge(all, OVERRIDE_TYPES); - let additionalExports = customize(all, baseTypeConversionMap); let exposed = getExposedTypes(all, ["Window"], new Set()); let dts = await Promise.all([