commit 10a499686de5d22bb0ee210370f7d33bef146e27
parent 64d13c7c53933bf3b4474daaf6f6fe37e8bf54b5
Author: Mark Striemer <mstriemer@mozilla.com>
Date: Thu, 8 Jan 2026 05:27:58 +0000
Bug 1990552 - Use setting-control's extension controlled message for Network settings r=bvandersloot,mconley
Differential Revision: https://phabricator.services.mozilla.com/D275332
Diffstat:
7 files changed, 92 insertions(+), 55 deletions(-)
diff --git a/browser/components/preferences/main.inc.xhtml b/browser/components/preferences/main.inc.xhtml
@@ -528,7 +528,7 @@
<label class="search-header" hidden="true"><html:h2 data-l10n-id="network-settings-title"/></label>
<description flex="1" control="connectionSettings" data-subcategory="netsettings"
class="section-description">
- <html:span id="connectionSettingsDescription"/>
+ <html:span id="connectionSettingsDescription" data-l10n-id="network-proxy-connection-description"/>
<html:a is="moz-support-link"
data-l10n-id="network-proxy-connection-learn-more"
support-page="prefs-connection-settings"
diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js
@@ -774,6 +774,11 @@ Preferences.addSetting({
Preferences.addSetting({
id: "connectionSettings",
onUserClick: () => gMainPane.showConnections(),
+ controllingExtensionInfo: {
+ storeId: PROXY_KEY,
+ l10nId: "extension-controlling-proxy-config",
+ allowControl: true,
+ },
});
Preferences.addSetting({
@@ -3045,10 +3050,6 @@ SettingGroupManager.registerGroups({
"search-l10n-ids":
"connection-window2.title,connection-proxy-option-no.label,connection-proxy-option-auto.label,connection-proxy-option-system.label,connection-proxy-option-wpad.label,connection-proxy-option-manual.label,connection-proxy-http,connection-proxy-https,connection-proxy-http-port,connection-proxy-socks,connection-proxy-socks4,connection-proxy-socks5,connection-proxy-noproxy,connection-proxy-noproxy-desc,connection-proxy-https-sharing.label,connection-proxy-autotype.label,connection-proxy-reload.label,connection-proxy-autologin-checkbox.label,connection-proxy-socks-remote-dns.label",
},
- // Bug 1990552: due to how this lays out in the legacy page, we do not include a
- // controllingExtensionInfo attribute here. We will want one in the redesigned page,
- // using storeId: "proxy.settings".
- controllingExtensionInfo: undefined,
},
],
},
@@ -4564,8 +4565,6 @@ var gMainPane = {
}
this.displayUseSystemLocale();
- this.updateProxySettingsUI();
- initializeProxyUI(gMainPane);
if (Services.prefs.getBoolPref("intl.multilingual.enabled")) {
gMainPane.initPrimaryBrowserLanguageUI();
@@ -5746,33 +5745,8 @@ var gMainPane = {
*/
showConnections() {
gSubDialog.open(
- "chrome://browser/content/preferences/dialogs/connection.xhtml",
- { closingCallback: this.updateProxySettingsUI.bind(this) }
- );
- },
-
- // Update the UI to show the proper description depending on whether an
- // extension is in control or not.
- async updateProxySettingsUI() {
- let controllingExtension = await getControllingExtension(
- PREF_SETTING_TYPE,
- PROXY_KEY
+ "chrome://browser/content/preferences/dialogs/connection.xhtml"
);
- let description = document.getElementById("connectionSettingsDescription");
-
- if (controllingExtension) {
- setControllingExtensionDescription(
- description,
- controllingExtension,
- "proxy.settings"
- );
- } else {
- setControllingExtensionDescription(
- description,
- null,
- "network-proxy-connection-description"
- );
- }
},
// FONTS
diff --git a/browser/components/preferences/tests/browser_extension_controlled.js b/browser/components/preferences/tests/browser_extension_controlled.js
@@ -861,6 +861,21 @@ add_task(async function testExtensionControlledWebNotificationsPermission() {
"The extension controlled row is now hidden"
);
+ // Close the site permissions dialog and wait for it to finish closing
+ let dialogClosed = BrowserTestUtils.waitForEvent(
+ gBrowser.contentWindow.gSubDialog._dialogStack,
+ "dialogclose"
+ );
+ sitePermissionsDialog.document
+ .querySelector("dialog")
+ .getButton("cancel")
+ .click();
+ await dialogClosed;
+ sitePermissionsDialog = null;
+
+ // Wait a tick to allow cleanup to complete
+ await new Promise(resolve => Services.tm.dispatchToMainThread(resolve));
+
await extension.unload();
BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
@@ -1167,7 +1182,7 @@ add_task(async function testExtensionControlledProxyConfig() {
const EXTENSION_ID = "@set_proxy";
const CONTROLLED_SECTION_ID = "proxyExtensionContent";
const CONTROLLED_BUTTON_ID = "disableProxyExtension";
- const CONNECTION_SETTINGS_DESC_ID = "connectionSettingsDescription";
+ const CONNECTION_SETTINGS_SETTING_ID = "connectionSettings";
const PANEL_URL =
"chrome://browser/content/preferences/dialogs/connection.xhtml";
@@ -1177,18 +1192,41 @@ add_task(async function testExtensionControlledProxyConfig() {
browser.proxy.settings.set({ value: { proxyType: "none" } });
}
- function expectedConnectionSettingsMessage(doc, isControlled) {
- return isControlled
- ? "extension-controlling-proxy-config"
- : "network-proxy-connection-description";
+ function assertConnectionSettingsMessage(doc, isControlled) {
+ let settingControl = getSettingControl(
+ CONNECTION_SETTINGS_SETTING_ID,
+ doc.ownerGlobal
+ );
+ Assert.ok(
+ settingControl,
+ `Got setting for ${CONNECTION_SETTINGS_SETTING_ID}`
+ );
+ let messageBar = settingControl.controlledMessageBarRef.value;
+ if (isControlled) {
+ Assert.ok(messageBar, "Controlled message exists");
+ Assert.ok(
+ BrowserTestUtils.isVisible(messageBar),
+ "Controlled message exists"
+ );
+ } else {
+ Assert.ok(!messageBar, "Controlled message does not exist");
+ }
}
- function connectionSettingsMessagePromise(doc, isControlled) {
- return waitForMessageContent(
- CONNECTION_SETTINGS_DESC_ID,
- expectedConnectionSettingsMessage(doc, isControlled),
- doc
+ async function connectionSettingsMessagePromise(doc, isControlled) {
+ let settingControl = getSettingControl(
+ CONNECTION_SETTINGS_SETTING_ID,
+ doc.ownerGlobal
+ );
+ Assert.ok(
+ settingControl,
+ `Got setting for ${CONNECTION_SETTINGS_SETTING_ID}`
);
+ let messageBar = settingControl.controlledMessageBarRef.value;
+ if (isControlled != messageBar) {
+ await waitForSettingChange(settingControl.setting);
+ }
+ assertConnectionSettingsMessage(doc, isControlled);
}
function verifyProxyState(doc, isControlled) {
@@ -1275,12 +1313,7 @@ add_task(async function testExtensionControlledProxyConfig() {
);
}
} else {
- let elem = doc.getElementById(CONNECTION_SETTINGS_DESC_ID);
- is(
- doc.l10n.getAttributes(elem).id,
- expectedConnectionSettingsMessage(doc, isControlled),
- "The connection settings description is as expected."
- );
+ assertConnectionSettingsMessage(doc, isControlled);
}
}
diff --git a/browser/components/preferences/tests/head.js b/browser/components/preferences/tests/head.js
@@ -1,6 +1,10 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
+/**
+ * @import { SettingControl } from "chrome://browser/content/preferences/widgets/setting-control.mjs"
+ */
+
const { NimbusTestUtils } = ChromeUtils.importESModule(
"resource://testing-common/NimbusTestUtils.sys.mjs"
);
@@ -773,16 +777,33 @@ async function waitForSettingControlChange(control) {
* Wait for the current setting pane to change.
*
* @param {string} paneId
+ * @param {Window} [win] The window to check, defaults to current window.
*/
-async function waitForPaneChange(paneId) {
- let doc = gBrowser.selectedBrowser.contentDocument;
- let event = await BrowserTestUtils.waitForEvent(doc, "paneshown");
+async function waitForPaneChange(
+ paneId,
+ win = gBrowser.selectedBrowser.ownerGlobal
+) {
+ let event = await BrowserTestUtils.waitForEvent(win.document, "paneshown");
let expectId = paneId.startsWith("pane")
? paneId
: `pane${paneId[0].toUpperCase()}${paneId.substring(1)}`;
is(event.detail.category, expectId, "Loaded the correct pane");
}
+/**
+ * Get a reference to the setting-control for a specific setting ID.
+ *
+ * @param {string} settingId The setting ID
+ * @param {Window} [win] The window to check, defaults to current window.
+ * @returns {SettingControl}
+ */
+function getSettingControl(
+ settingId,
+ win = gBrowser.selectedBrowser.ownerGlobal
+) {
+ return win.document.getElementById(`setting-control-${settingId}`);
+}
+
function getControl(doc, id) {
let control = doc.getElementById(id);
ok(control, `Control ${id} exists`);
diff --git a/browser/components/preferences/widgets/setting-control/setting-control.css b/browser/components/preferences/widgets/setting-control/setting-control.css
@@ -4,7 +4,7 @@
.extension-controlled-message-bar,
.reenable-extensions-message-bar {
- margin-block: var(--space-large);
+ margin-block-end: var(--space-large);
}
setting-control[hidden]:has(> moz-message-bar):not([slot="nested"]) {
diff --git a/browser/components/preferences/widgets/setting-control/setting-control.mjs b/browser/components/preferences/widgets/setting-control/setting-control.mjs
@@ -259,7 +259,7 @@ export class SettingControl extends SettingElement {
"?disabled":
this.setting.disabled ||
this.setting.locked ||
- this.isControlledByExtension(),
+ this.isDisabledByExtension(),
// 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,
@@ -325,6 +325,13 @@ export class SettingControl extends SettingElement {
);
}
+ isDisabledByExtension() {
+ return (
+ this.isControlledByExtension() &&
+ !this.setting.controllingExtensionInfo.allowControl
+ );
+ }
+
handleEnableExtensionDismiss() {
this.showEnableExtensionMessage = false;
}
diff --git a/toolkit/content/preferences/Setting.mjs b/toolkit/content/preferences/Setting.mjs
@@ -109,6 +109,8 @@ import { Preferences } from "chrome://global/content/preferences/Preferences.mjs
* @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.
+ * @property {string} [supportPage] A support page to show in the message.
+ * @property {boolean} [allowControl] If the control should be enabled while controlled.
*/
/**
@@ -116,7 +118,7 @@ import { Preferences } from "chrome://global/content/preferences/Preferences.mjs
* @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 {Pick<SettingControllingExtensionInfo, "storeId" | "l10nId" | "allowControl" | "supportPage">} [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.