commit 81206b849b429f789787e824e1c15ee3f03d2117
parent 51de2c8143aeb7db40f41d158374c09670f33c6c
Author: Mark Striemer <mstriemer@mozilla.com>
Date: Sat, 13 Dec 2025 20:56:42 +0000
Bug 1972367 - Convert Zoom settings section to config-based prefs r=fluent-reviewers,desktop-theme-reviewers,bolsson,hjones
Differential Revision: https://phabricator.services.mozilla.com/D253057
Diffstat:
8 files changed, 249 insertions(+), 143 deletions(-)
diff --git a/browser/components/preferences/main.inc.xhtml b/browser/components/preferences/main.inc.xhtml
@@ -200,21 +200,7 @@
</groupbox>
<!-- Zoom -->
-<groupbox id="zoomGroup" data-category="paneGeneral" hidden="true" data-srd-groupid="zoom">
- <label><html:h2 data-l10n-id="preferences-zoom-header"/></label>
- <hbox id="zoomBox" align="center" hidden="true">
- <label control="defaultZoom" data-l10n-id="preferences-default-zoom"/>
- <menulist id="defaultZoom">
- <menupopup/>
- </menulist>
- </hbox>
- <html:moz-message-bar id="text-zoom-override-warning" data-l10n-id="preferences-text-zoom-override-warning"/>
- <checkbox id="zoomText"
- data-l10n-id="preferences-zoom-text-only"/>
-
-</groupbox>
-
-<html:setting-group hidden="true" data-category="paneGeneral" groupid="zoom" />
+<html:setting-group id="zoomGroup" groupid="zoom" data-category="paneGeneral" hidden="true" />
<!-- Languages -->
<groupbox id="languagesGroup" data-category="paneGeneral" hidden="true">
diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js
@@ -142,6 +142,9 @@ Preferences.addAll([
{ id: "accessibility.typeaheadfind", type: "bool" },
{ id: "accessibility.blockautorefresh", type: "bool" },
+ /* Zoom */
+ { id: "browser.zoom.full", type: "bool" },
+
/* Browsing
* general.autoScroll
- when set to true, clicking the scroll wheel on the mouse activates a
@@ -633,7 +636,6 @@ Preferences.addSetting({
},
});
-Preferences.addSetting({ id: "zoomPlaceholder" });
Preferences.addSetting({
id: "containersPane",
onUserClick(e) {
@@ -1662,6 +1664,114 @@ Preferences.addSetting({
},
});
+/**
+ * Helper object for managing the various zoom related settings.
+ */
+const ZoomHelpers = {
+ win: window.browsingContext.topChromeWindow,
+ get FullZoom() {
+ return this.win.FullZoom;
+ },
+ get ZoomManager() {
+ return this.win.ZoomManager;
+ },
+
+ /**
+ * Set the global default zoom value.
+ *
+ * @param {number} newZoom - The new zoom
+ * @returns {Promise<void>}
+ */
+ async setDefaultZoom(newZoom) {
+ let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
+ Ci.nsIContentPrefService2
+ );
+ let nonPrivateLoadContext = Cu.createLoadContext();
+ let resolvers = Promise.withResolvers();
+ /* Because our setGlobal function takes in a browsing context, and
+ * because we want to keep this property consistent across both private
+ * and non-private contexts, we create a non-private context and use that
+ * to set the property, regardless of our actual context.
+ */
+ cps2.setGlobal(this.FullZoom.name, newZoom, nonPrivateLoadContext, {
+ handleCompletion: resolvers.resolve,
+ handleError: resolvers.reject,
+ });
+ return resolvers.promise;
+ },
+
+ async getDefaultZoom() {
+ /** @import { ZoomUI as GlobalZoomUI } from "resource:///modules/ZoomUI.sys.mjs" */
+ /** @type {GlobalZoomUI} */
+ let ZoomUI = this.win.ZoomUI;
+ return await ZoomUI.getGlobalValue();
+ },
+
+ /**
+ * The possible zoom values.
+ *
+ * @returns {number[]}
+ */
+ get zoomValues() {
+ return this.ZoomManager.zoomValues;
+ },
+
+ toggleFullZoom() {
+ this.ZoomManager.toggleZoom();
+ },
+};
+Preferences.addSetting(
+ class extends Preferences.AsyncSetting {
+ static id = "defaultZoom";
+ /** @type {Record<"options", object[]>} */
+ optionsConfig;
+
+ /**
+ * @param {string} val - zoom value as a string
+ */
+ async set(val) {
+ ZoomHelpers.setDefaultZoom(
+ parseFloat((parseInt(val, 10) / 100).toFixed(2))
+ );
+ }
+ async get() {
+ return Math.round((await ZoomHelpers.getDefaultZoom()) * 100);
+ }
+ async getControlConfig() {
+ if (!this.optionsConfig) {
+ this.optionsConfig = {
+ options: ZoomHelpers.zoomValues.map(a => {
+ let value = Math.round(a * 100);
+ return {
+ value,
+ l10nId: "preferences-default-zoom-value",
+ l10nArgs: { percentage: value },
+ };
+ }),
+ };
+ }
+ return this.optionsConfig;
+ }
+ }
+);
+Preferences.addSetting({
+ id: "zoomTextPref",
+ pref: "browser.zoom.full",
+});
+Preferences.addSetting({
+ id: "zoomText",
+ deps: ["zoomTextPref"],
+ // Use the Setting since the ZoomManager getter may not have updated yet.
+ get: (_, { zoomTextPref }) => !zoomTextPref.value,
+ set: () => ZoomHelpers.toggleFullZoom(),
+ disabled: ({ zoomTextPref }) => zoomTextPref.locked,
+});
+Preferences.addSetting({
+ id: "zoomWarning",
+ deps: ["zoomText"],
+ visible: ({ zoomText }) => Boolean(zoomText.value),
+});
+
SettingGroupManager.registerGroups({
containers: {
// This section is marked as in progress for testing purposes
@@ -1978,21 +2088,24 @@ SettingGroupManager.registerGroups({
],
},
zoom: {
- // This section is marked as in progress for testing purposes
- inProgress: true,
+ l10nId: "preferences-zoom-header2",
+ headingLevel: 2,
items: [
{
- id: "zoomPlaceholder",
- control: "moz-message-bar",
- controlAttrs: {
- message: "Placeholder for updated zoom controls",
- },
+ id: "defaultZoom",
+ l10nId: "preferences-default-zoom-label",
+ control: "moz-select",
},
{
- id: "containersPane",
- control: "moz-button",
+ id: "zoomText",
+ l10nId: "preferences-zoom-text-only",
+ },
+ {
+ id: "zoomWarning",
+ l10nId: "preferences-text-zoom-override-warning",
+ control: "moz-message-bar",
controlAttrs: {
- label: "Manage container settings",
+ type: "warning",
},
},
],
@@ -3361,11 +3474,6 @@ var gMainPane = {
gMainPane.initPrimaryBrowserLanguageUI();
}
- // We call `initDefaultZoomValues` to set and unhide the
- // default zoom preferences menu, and to establish a
- // listener for future menu changes.
- gMainPane.initDefaultZoomValues();
-
gMainPane.initTranslations();
// Initialize settings groups from the config object.
@@ -3736,53 +3844,6 @@ var gMainPane = {
return undefined;
},
- /**
- * Fetch the existing default zoom value, initialise and unhide
- * the preferences menu. This method also establishes a listener
- * to ensure handleDefaultZoomChange is called on future menu
- * changes.
- */
- async initDefaultZoomValues() {
- let win = window.browsingContext.topChromeWindow;
- let selected = await win.ZoomUI.getGlobalValue();
- let menulist = document.getElementById("defaultZoom");
-
- new SelectionChangedMenulist(menulist, event => {
- let parsedZoom = parseFloat((event.target.value / 100).toFixed(2));
- gMainPane.handleDefaultZoomChange(parsedZoom);
- });
-
- setEventListener("zoomText", "command", function () {
- win.ZoomManager.toggleZoom();
- document.getElementById("text-zoom-override-warning").hidden =
- !document.getElementById("zoomText").checked;
- });
-
- let zoomValues = win.ZoomManager.zoomValues.map(a => {
- return Math.round(a * 100);
- });
-
- let fragment = document.createDocumentFragment();
- for (let zoomLevel of zoomValues) {
- let menuitem = document.createXULElement("menuitem");
- document.l10n.setAttributes(menuitem, "preferences-default-zoom-value", {
- percentage: zoomLevel,
- });
- menuitem.setAttribute("value", zoomLevel);
- fragment.appendChild(menuitem);
- }
-
- let menupopup = menulist.querySelector("menupopup");
- menupopup.appendChild(fragment);
- menulist.value = Math.round(selected * 100);
-
- let checkbox = document.getElementById("zoomText");
- checkbox.checked = !win.ZoomManager.useFullZoom;
- document.getElementById("text-zoom-override-warning").hidden =
- !checkbox.checked;
- document.getElementById("zoomBox").hidden = false;
- },
-
updateColorsButton() {
document.getElementById("colors").disabled =
Preferences.get("browser.display.document_color_use").value != 2;
@@ -4404,27 +4465,6 @@ var gMainPane = {
},
/**
- * Takes as newZoom a floating point value representing the
- * new default zoom. This value should not be a string, and
- * should not carry a percentage sign/other localisation
- * characteristics.
- */
- handleDefaultZoomChange(newZoom) {
- let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
- Ci.nsIContentPrefService2
- );
- let nonPrivateLoadContext = Cu.createLoadContext();
- /* Because our setGlobal function takes in a browsing context, and
- * because we want to keep this property consistent across both private
- * and non-private contexts, we crate a non-private context and use that
- * to set the property, regardless of our actual context.
- */
-
- let win = window.browsingContext.topChromeWindow;
- cps2.setGlobal(win.FullZoom.name, newZoom, nonPrivateLoadContext);
- },
-
- /**
* Shows a subdialog containing the profile selector page.
*/
manageProfiles() {
diff --git a/browser/components/preferences/tests/browser_setting_group_in_progress.js b/browser/components/preferences/tests/browser_setting_group_in_progress.js
@@ -3,45 +3,87 @@
"use strict";
-async function openPrefsWithSettings({ allEnabled, zoomEnabled }) {
+async function openPrefsWithSettings({ allEnabled, sectionEnabled }) {
await SpecialPowers.pushPrefEnv({
set: [
["browser.settings-redesign.enabled", allEnabled],
- ["browser.settings-redesign.zoom.enabled", zoomEnabled],
+ ["browser.settings-redesign.mysection.enabled", sectionEnabled],
],
});
- await openPreferencesViaOpenPreferencesAPI("general", { leaveOpen: true });
- return gBrowser.selectedBrowser.contentDocument;
+ await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+ let doc = gBrowser.selectedBrowser.contentDocument;
+ let win = doc.ownerGlobal;
+ win.Preferences.addSetting({
+ id: "testSetting",
+ get: () => true,
+ });
+ win.SettingGroupManager.registerGroup("mysection", {
+ inProgress: true,
+ l10nId: "downloads-header-2",
+ headingLevel: 2,
+ items: [
+ {
+ id: "testSetting",
+ controlAttrs: {
+ label: "Test setting",
+ },
+ },
+ ],
+ });
+ let zoomGroup = doc.getElementById("zoomGroup");
+ let legacyGroup = doc.createXULElement("groupbox");
+ legacyGroup.id = "mysectionGroup";
+ legacyGroup.setAttribute("data-srd-groupid", "mysection");
+ legacyGroup.setAttribute("data-category", "paneGeneral");
+ legacyGroup.hidden = true;
+ let mysectionGroup = doc.createElement("setting-group");
+ mysectionGroup.setAttribute("groupid", "mysection");
+ mysectionGroup.setAttribute("data-category", "paneGeneral");
+ mysectionGroup.hidden = true;
+ zoomGroup.parentElement.insertBefore(mysectionGroup, zoomGroup);
+ zoomGroup.parentElement.insertBefore(legacyGroup, zoomGroup);
+ win.initSettingGroup("mysection");
+ let paneLoaded = waitForPaneChange("general");
+ EventUtils.synthesizeMouseAtCenter(
+ doc.getElementById("category-general"),
+ {},
+ win
+ );
+ await paneLoaded;
+ return doc;
}
add_task(async function test_section_disabled() {
- let doc = await openPrefsWithSettings({
- allEnabled: false,
- zoomEnabled: false,
- });
- let legacyGroup = doc.getElementById("zoomGroup");
- let redesignGroup = doc.querySelector('setting-group[groupid="zoom"]');
- ok(legacyGroup.checkVisibility(), "The legacy group is visible");
- is(
- legacyGroup.dataset.category,
- "paneGeneral",
- "The legacy group has a category"
- );
- ok(
- !legacyGroup.hasAttribute("data-hidden-from-search"),
- "The legacy group is visible to search"
- );
- ok(!redesignGroup, "The redesign group was removed");
- gBrowser.removeCurrentTab();
+ try {
+ let doc = await openPrefsWithSettings({
+ allEnabled: false,
+ sectionEnabled: false,
+ });
+ let legacyGroup = doc.getElementById("mysectionGroup");
+ let redesignGroup = doc.querySelector('setting-group[groupid="mysection"]');
+ ok(legacyGroup.checkVisibility(), "The legacy group is visible");
+ is(
+ legacyGroup.dataset.category,
+ "paneGeneral",
+ "The legacy group has a category"
+ );
+ ok(
+ !legacyGroup.hasAttribute("data-hidden-from-search"),
+ "The legacy group is visible to search"
+ );
+ ok(!redesignGroup, "The redesign group was removed");
+ } finally {
+ gBrowser.removeCurrentTab();
+ }
});
add_task(async function test_section_enabled() {
let doc = await openPrefsWithSettings({
allEnabled: false,
- zoomEnabled: true,
+ sectionEnabled: true,
});
- let legacyGroup = doc.getElementById("zoomGroup");
- let redesignGroup = doc.querySelector('setting-group[groupid="zoom"]');
+ let legacyGroup = doc.getElementById("mysectionGroup");
+ let redesignGroup = doc.querySelector('setting-group[groupid="mysection"]');
ok(!legacyGroup.checkVisibility(), "The legacy group is hidden");
ok(!legacyGroup.dataset.category, "The legacy group category is removed");
is(
@@ -61,10 +103,10 @@ add_task(async function test_section_enabled() {
add_task(async function test_all_enabled() {
let doc = await openPrefsWithSettings({
allEnabled: true,
- zoomEnabled: false,
+ sectionEnabled: false,
});
- let legacyGroup = doc.getElementById("zoomGroup");
- let redesignGroup = doc.querySelector('setting-group[groupid="zoom"]');
+ let legacyGroup = doc.getElementById("mysectionGroup");
+ let redesignGroup = doc.querySelector('setting-group[groupid="mysection"]');
ok(!legacyGroup.checkVisibility(), "The legacy group is hidden");
ok(!legacyGroup.dataset.category, "The legacy group category is removed");
is(
diff --git a/browser/components/preferences/widgets/setting-control/setting-control.css b/browser/components/preferences/widgets/setting-control/setting-control.css
@@ -6,3 +6,8 @@
.reenable-extensions-message-bar {
margin-block: var(--space-large);
}
+
+setting-control[hidden]:has(> moz-message-bar) {
+ display: block;
+ margin-block-start: calc(-1 * var(--space-large));
+}
diff --git a/browser/components/preferences/widgets/setting-control/setting-control.mjs b/browser/components/preferences/widgets/setting-control/setting-control.mjs
@@ -33,6 +33,8 @@ import MozInputFolder from "chrome://global/content/elements/moz-input-folder.mj
* @property {string} [control]
* The element tag to render, default assumed based on parent control.
* @property {any} [value] A value to set on the option.
+ * @property {boolean} [disabled] If the option should be disabled.
+ * @property {boolean} [hidden] If the option should be hidden.
*/
/**
@@ -222,7 +224,6 @@ export class SettingControl extends SettingElement {
*
* @override
* @param {SettingElementConfig} config
- * @returns {ReturnType<SettingElement['getCommonPropertyMapping']>}
*/
getCommonPropertyMapping(config) {
return {
@@ -238,11 +239,12 @@ export class SettingControl extends SettingElement {
* @param {SettingOptionConfig} config
*/
getOptionPropertyMapping(config) {
- const props = this.getCommonPropertyMapping(config);
- props[".value"] = config.value;
- props[".disabled"] = config.disabled;
- props[".hidden"] = config.hidden;
- return props;
+ return {
+ ...this.getCommonPropertyMapping(config),
+ ".value": config.value,
+ ".disabled": config.disabled,
+ ".hidden": config.hidden,
+ };
}
/**
@@ -251,14 +253,17 @@ export class SettingControl extends SettingElement {
* @param {SettingControlConfig} config
*/
getControlPropertyMapping(config) {
- const props = this.getCommonPropertyMapping(config);
- props[".parentDisabled"] = this.parentDisabled;
- props["?disabled"] =
- this.setting.disabled ||
- this.setting.locked ||
- this.isControlledByExtension();
-
- return props;
+ return {
+ ...this.getCommonPropertyMapping(config),
+ ".parentDisabled": this.parentDisabled,
+ "?disabled":
+ this.setting.disabled ||
+ this.setting.locked ||
+ this.isControlledByExtension(),
+ // Hide moz-message-bar directly to maintain the role=alert functionality.
+ // This setting-control will be visually hidden in CSS.
+ ".hidden": config.control == "moz-message-bar" && this.hidden,
+ };
}
getValue() {
diff --git a/browser/locales/en-US/browser/preferences/preferences.ftl b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -379,9 +379,11 @@ advanced-fonts =
.accesskey = A
# Zoom is a noun, and the message is used as header for a group of options
-preferences-zoom-header = Zoom
+preferences-zoom-header2 =
+ .label = Zoom
-preferences-default-zoom = Default zoom
+preferences-default-zoom-label =
+ .label = Default zoom
.accesskey = z
# Variables:
diff --git a/browser/themes/shared/preferences/preferences.css b/browser/themes/shared/preferences/preferences.css
@@ -14,6 +14,7 @@
--heading-font-size-xlarge: var(--font-size-xlarge);
--heading-font-size-large: var(--font-size-large);
--heading-font-size-medium: var(--font-size-medium);
+ --select-max-width: 20rem;
user-select: text;
}
diff --git a/python/l10n/fluent_migrations/bug_1972367_preferences_zoom.py b/python/l10n/fluent_migrations/bug_1972367_preferences_zoom.py
@@ -0,0 +1,25 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+from fluent.migrate.helpers import transforms_from
+
+
+def migrate(ctx):
+ """Bug 1972367 - Convert Zoom settings section to config-based prefs, part {index}."""
+
+ target = "browser/browser/preferences/preferences.ftl"
+
+ ctx.add_transforms(
+ target,
+ target,
+ transforms_from(
+ """
+preferences-zoom-header2 =
+ .label = {COPY_PATTERN(from_path, "preferences-zoom-header")}
+preferences-default-zoom-label =
+ .label = {COPY_PATTERN(from_path, "preferences-default-zoom")}
+ .accesskey = {COPY_PATTERN(from_path, "preferences-default-zoom.accesskey")}
+""",
+ from_path=target,
+ ),
+ )