diff --git a/mail/components/preferences/connection.js b/mail/components/preferences/connection.js
index 8100dc216a..be9b5f9fdf 100644
--- a/mail/components/preferences/connection.js
+++ b/mail/components/preferences/connection.js
@@ -113,13 +113,8 @@ window.addEventListener(
);
document
- .getElementById("disableProxyExtension")
- .addEventListener(
- "command",
- makeDisableControllingExtension(PREF_SETTING_TYPE, PROXY_KEY).bind(
- gConnectionsDialog
- )
- );
+ .getElementById("proxyExtensionDisable")
+ .addEventListener("click", disableControllingProxyExtension);
gConnectionsDialog.updateProxySettingsUI();
initializeProxyUI(gConnectionsDialog);
},
@@ -433,11 +428,9 @@ var gConnectionsDialog = {
if (isLocked) {
// An extension can't control this setting if any pref is locked.
- hideControllingExtension(PROXY_KEY);
+ hideControllingProxyExtension();
} else {
- handleControllingExtension(PREF_SETTING_TYPE, PROXY_KEY).then(
- setInputsDisabledState
- );
+ handleControllingProxyExtension().then(setInputsDisabledState);
}
},
diff --git a/mail/components/preferences/connection.xhtml b/mail/components/preferences/connection.xhtml
index f69815ceda..3338f123c8 100644
--- a/mail/components/preferences/connection.xhtml
+++ b/mail/components/preferences/connection.xhtml
@@ -31,13 +31,19 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/mail/components/preferences/extensionControlled.js b/mail/components/preferences/extensionControlled.js
index 054e0b0da5..8bc260758e 100644
--- a/mail/components/preferences/extensionControlled.js
+++ b/mail/components/preferences/extensionControlled.js
@@ -11,11 +11,6 @@ ChromeUtils.defineModuleGetter(
"AddonManager",
"resource://gre/modules/AddonManager.jsm"
);
-ChromeUtils.defineModuleGetter(
- this,
- "BrowserUtils",
- "resource://gre/modules/BrowserUtils.jsm"
-);
ChromeUtils.defineModuleGetter(
this,
"DeferredTask",
@@ -26,19 +21,7 @@ ChromeUtils.defineModuleGetter(
"ExtensionSettingsStore",
"resource://gre/modules/ExtensionSettingsStore.jsm"
);
-ChromeUtils.defineModuleGetter(
- this,
- "ExtensionPreferencesManager",
- "resource://gre/modules/ExtensionPreferencesManager.jsm"
-);
-ChromeUtils.defineModuleGetter(
- this,
- "Management",
- "resource://gre/modules/Extension.jsm"
-);
-const PREF_SETTING_TYPE = "prefs";
-const PROXY_KEY = "proxy.settings";
const API_PROXY_PREFS = [
"network.proxy.type",
"network.proxy.http",
@@ -55,251 +38,88 @@ const API_PROXY_PREFS = [
"signon.autologin.proxy",
];
-let extensionControlledContentIds = {
- "privacy.containers": "browserContainersExtensionContent",
- webNotificationsDisabled: "browserNotificationsPermissionExtensionContent",
- "services.passwordSavingEnabled": "passwordManagerExtensionContent",
- "proxy.settings": "proxyExtensionContent",
- get "websites.trackingProtectionMode"() {
- return {
- button: "contentBlockingDisableTrackingProtectionExtension",
- section: "contentBlockingTrackingProtectionExtensionContentLabel",
- };
- },
-};
-
-const extensionControlledL10nKeys = {
- webNotificationsDisabled: "web-notifications",
- "services.passwordSavingEnabled": "password-saving",
- "privacy.containers": "privacy-containers",
- "websites.trackingProtectionMode": "websites-content-blocking-all-trackers",
- "proxy.settings": "proxy-config",
-};
-
-let extensionControlledIds = {};
-
/**
* Check if a pref is being managed by an extension.
+ *
+ * NOTE: We only currently handle proxy.settings.
*/
-async function getControllingExtensionInfo(type, settingName) {
+/**
+ * Get the addon extension that is controlling the proxy settings.
+ *
+ * @return - The found addon, or undefined if none was found.
+ */
+async function getControllingProxyExtensionAddon() {
await ExtensionSettingsStore.initialize();
- return ExtensionSettingsStore.getSetting(type, settingName);
-}
-
-function getControllingExtensionEls(settingName) {
- let idInfo = extensionControlledContentIds[settingName];
- let section = document.getElementById(idInfo.section || idInfo);
- let button = idInfo.button
- ? document.getElementById(idInfo.button)
- : section.querySelector("button");
- return {
- section,
- button,
- description: section.querySelector("description"),
- };
-}
-
-async function getControllingExtension(type, settingName) {
- let info = await getControllingExtensionInfo(type, settingName);
- let addon = info && info.id && (await AddonManager.getAddonByID(info.id));
- return addon;
-}
-
-async function handleControllingExtension(type, settingName) {
- let addon = await getControllingExtension(type, settingName);
-
- // Sometimes the ExtensionSettingsStore gets in a bad state where it thinks
- // an extension is controlling a setting but the extension has been uninstalled
- // outside of the regular lifecycle. If the extension isn't currently installed
- // then we should treat the setting as not being controlled.
- // See https://bugzilla.mozilla.org/show_bug.cgi?id=1411046 for an example.
- if (addon) {
- extensionControlledIds[settingName] = addon.id;
- showControllingExtension(settingName, addon);
- } else {
- let elements = getControllingExtensionEls(settingName);
- if (
- extensionControlledIds[settingName] &&
- !document.hidden &&
- elements.button
- ) {
- showEnableExtensionMessage(settingName);
- } else {
- hideControllingExtension(settingName);
- }
- delete extensionControlledIds[settingName];
+ let id = ExtensionSettingsStore.getSetting("prefs", "proxy.settings")?.id;
+ if (id) {
+ return AddonManager.getAddonByID(id);
}
+ return undefined;
+}
+/**
+ * Show or hide the proxy extension message depending on whether or not the
+ * proxy settings are controlled by an extension.
+ *
+ * @return {boolean} - Whether the proxy settings are controlled by an
+ * extension.
+ */
+async function handleControllingProxyExtension() {
+ let addon = await getControllingProxyExtensionAddon();
+ if (addon) {
+ showControllingProxyExtension(addon);
+ } else {
+ hideControllingProxyExtension();
+ }
return !!addon;
}
-function settingNameToL10nID(settingName) {
- if (!extensionControlledL10nKeys.hasOwnProperty(settingName)) {
- throw new Error(
- `Unknown extension controlled setting name: ${settingName}`
+/**
+ * Show the proxy extension message.
+ *
+ * @param {Object} addon - The addon extension that is currently controlling the
+ * proxy settings.
+ * @param {string} addon.name - The addon name.
+ * @param {string} [addon.iconUrl] - The addon icon source.
+ */
+function showControllingProxyExtension(addon) {
+ let description = document.getElementById("proxyExtensionDescription");
+ description
+ .querySelector("img")
+ .setAttribute(
+ "src",
+ addon.iconUrl || "chrome://mozapps/skin/extensions/extensionGeneric.svg"
);
- }
- return `extension-controlled-${extensionControlledL10nKeys[settingName]}`;
-}
-
-/**
- * Set the localization data for the description of the controlling extension.
- *
- * The function alters the inner DOM structure of the fragment to, depending
- * on the `addon` argument, remove the `` element or ensure it's
- * set to the correct src.
- * This allows Fluent DOM Overlays to localize the fragment.
- *
- * @param elem {Element}
- * element to be annotated
- * @param addon {Object?}
- * Addon object with meta information about the addon (or null)
- * @param settingName {String}
- * If `addon` is set this handled the name of the setting that will be used
- * to fetch the l10n id for the given message.
- * If `addon` is set to null, this will be the full l10n-id assigned to the
- * element.
- */
-function setControllingExtensionDescription(elem, addon, settingName) {
- const existingImg = elem.querySelector("img");
- if (addon === null) {
- // If the element has an image child element,
- // remove it.
- if (existingImg) {
- existingImg.remove();
- }
- document.l10n.setAttributes(elem, settingName);
- return;
- }
-
- const defaultIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
- const src = addon.iconURL || defaultIcon;
-
- if (!existingImg) {
- // If an element doesn't have an image child
- // node, add it.
- let image = document.createElementNS("http://www.w3.org/1999/xhtml", "img");
- image.setAttribute("src", src);
- image.setAttribute("data-l10n-name", "icon");
- image.classList.add("extension-controlled-icon");
- elem.appendChild(image);
- } else if (existingImg.getAttribute("src") !== src) {
- existingImg.setAttribute("src", src);
- }
-
- const l10nId = settingNameToL10nID(settingName);
- document.l10n.setAttributes(elem, l10nId, {
- name: addon.name,
- });
-}
-
-async function showControllingExtension(settingName, addon) {
- // Tell the user what extension is controlling the setting.
- let elements = getControllingExtensionEls(settingName);
-
- elements.section.classList.remove("extension-controlled-disabled");
- let description = elements.description;
-
- setControllingExtensionDescription(description, addon, settingName);
-
- if (elements.button) {
- elements.button.hidden = false;
- }
-
- // Show the controlling extension row and hide the old label.
- elements.section.hidden = false;
-}
-
-function hideControllingExtension(settingName) {
- let elements = getControllingExtensionEls(settingName);
- elements.section.hidden = true;
- if (elements.button) {
- elements.button.hidden = true;
- }
-}
-
-function showEnableExtensionMessage(settingName) {
- let elements = getControllingExtensionEls(settingName);
-
- elements.button.hidden = true;
- elements.section.classList.add("extension-controlled-disabled");
-
- elements.description.textContent = "";
-
- // We replace localization of the with a DOM Fragment containing
- // the enable-extension-enable message. That means a change from:
- //
- //
- //
- // to:
- //
- //
- //
- //
- //
- //
- // We need to remove the l10n-id annotation from the to prevent
- // Fluent from overwriting the element in case of any retranslation.
- elements.description.removeAttribute("data-l10n-id");
-
- let icon = (url, name) => {
- let img = document.createElementNS("http://www.w3.org/1999/xhtml", "img");
- img.src = url;
- img.setAttribute("data-l10n-name", name);
- img.className = "extension-controlled-icon";
- return img;
- };
- let label = document.createXULElement("label");
- let addonIcon = icon(
- "chrome://mozapps/skin/extensions/extensionGeneric.svg",
- "addons-icon"
+ document.l10n.setAttributes(
+ description,
+ "proxy-settings-controlled-by-extension",
+ { name: addon.name }
);
- let toolbarIcon = icon("chrome://browser/skin/menu.svg", "menu-icon");
- label.appendChild(addonIcon);
- label.appendChild(toolbarIcon);
- document.l10n.setAttributes(label, "extension-controlled-enable");
- elements.description.appendChild(label);
- let dismissButton = document.createXULElement("image");
- dismissButton.setAttribute("class", "extension-controlled-icon close-icon");
- dismissButton.addEventListener("click", function dismissHandler() {
- hideControllingExtension(settingName);
- dismissButton.removeEventListener("click", dismissHandler);
- });
- elements.description.appendChild(dismissButton);
-}
-function makeDisableControllingExtension(type, settingName) {
- return async function() {
- let { id } = await getControllingExtensionInfo(type, settingName);
- let addon = await AddonManager.getAddonByID(id);
- await addon.disable();
- };
+ document.getElementById("proxyExtensionContent").hidden = false;
}
/**
- * Initialize listeners though the Management API to update the UI
- * when an extension is controlling a pref.
- * @param {string} type
- * @param {string} prefId The unique id of the setting
- * @param {HTMLElement} controlledElement
+ * Hide the proxy extension message.
*/
-async function initListenersForPrefChange(type, prefId, controlledElement) {
- await Management.asyncLoadSettingsModules();
-
- let managementObserver = async () => {
- let managementControlled = await handleControllingExtension(type, prefId);
- // Enterprise policy may have locked the pref, so we need to preserve that
- controlledElement.disabled =
- managementControlled || Services.prefs.prefIsLocked(prefId);
- };
- managementObserver();
- Management.on(`extension-setting-changed:${prefId}`, managementObserver);
-
- window.addEventListener("unload", () => {
- Management.off(`extension-setting-changed:${prefId}`, managementObserver);
- });
+function hideControllingProxyExtension() {
+ document.getElementById("proxyExtensionContent").hidden = true;
}
+/**
+ * Disable the addon extension that is currently controlling the proxy settings.
+ */
+function disableControllingProxyExtension() {
+ getControllingProxyExtensionAddon().then(addon => addon?.disable());
+}
+
+/**
+ * Start listening to the proxy settings, and update the UI accordingly.
+ *
+ * @param {Object} container - The proxy container.
+ * @param {Function} container.updateProxySettingsUI - A callback to call
+ * whenever the proxy settings change.
+ */
function initializeProxyUI(container) {
let deferredUpdate = new DeferredTask(() => {
container.updateProxySettingsUI();
diff --git a/mail/locales/en-US/messenger/preferences/connection.ftl b/mail/locales/en-US/messenger/preferences/connection.ftl
index f0e429a61d..b85d4ea40e 100644
--- a/mail/locales/en-US/messenger/preferences/connection.ftl
+++ b/mail/locales/en-US/messenger/preferences/connection.ftl
@@ -25,8 +25,14 @@ connection-dialog-window =
*[other] width: 49em !important
}
-connection-disable-extension =
- .label = Disable Extension
+disable-extension-button = Disable Extension
+
+# Variables:
+# $name (String) - The extension that is controlling the proxy settings.
+#
+# The extension-icon is the extension's icon, or a fallback image. It should be
+# purely decoration for the actual extension name, with alt="".
+proxy-settings-controlled-by-extension = An extension, { $name }, is controlling how { -brand-short-name } connects to the Internet.
connection-proxy-legend = Configure Proxies to Access the Internet
diff --git a/mail/themes/shared/mail/preferences/preferences.css b/mail/themes/shared/mail/preferences/preferences.css
index 422bd0e05e..299696e241 100644
--- a/mail/themes/shared/mail/preferences/preferences.css
+++ b/mail/themes/shared/mail/preferences/preferences.css
@@ -602,6 +602,28 @@ checkbox {
* Connection dialog
*/
+#proxyExtensionContent:not([hidden]) {
+ display: flex;
+ align-items: center;
+}
+
+#proxyExtensionDescription {
+ margin-block: 0;
+ flex: 1 1 auto;
+}
+
+#proxyExtensionDescription > img {
+ height: 20px;
+ width: 20px;
+ vertical-align: text-bottom;
+ -moz-context-properties: fill;
+ fill: currentColor;
+}
+
+#proxyExtensionDisable {
+ flex: 0 0 auto;
+}
+
#proxy-grid,
#dnsOverHttps-grid {
display: grid;