Bug 1429593 - Part 1: Extract functions for dealing with extensions into a separate file, r?mstriemer r?jaws
This moves all the functions that manage/change the UI because of extensions being in
control of certain preferences to a separate file, so it can be included in SubDialogs.
MozReview-Commit-ID: 7OkEn478Pus
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/extensionControlled.js
@@ -0,0 +1,162 @@
+/* - This Source Code Form is subject to the terms of the Mozilla Public
+ - 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/. */
+
+"use strict";
+
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+ChromeUtils.defineModuleGetter(this, "AddonManager",
+ "resource://gre/modules/AddonManager.jsm");
+ChromeUtils.defineModuleGetter(this, "BrowserUtils",
+ "resource://gre/modules/BrowserUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
+ "resource://gre/modules/ExtensionSettingsStore.jsm");
+
+XPCOMUtils.defineLazyPreferenceGetter(this, "trackingprotectionUiEnabled",
+ "privacy.trackingprotection.ui.enabled");
+
+const PREF_SETTING_TYPE = "prefs";
+
+let extensionControlledContentIds = {
+ "privacy.containers": "browserContainersExtensionContent",
+ "homepage_override": "browserHomePageExtensionContent",
+ "newTabURL": "browserNewTabExtensionContent",
+ "defaultSearch": "browserDefaultSearchExtensionContent",
+ get "websites.trackingProtectionMode"() {
+ return {
+ button: "trackingProtectionExtensionContentButton",
+ section:
+ trackingprotectionUiEnabled ?
+ "trackingProtectionExtensionContentLabel" :
+ "trackingProtectionPBMExtensionContentLabel",
+ };
+ }
+};
+
+let extensionControlledIds = {};
+
+/**
+ * Check if a pref is being managed by an extension.
+ */
+async function getControllingExtensionInfo(type, settingName) {
+ 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 handleControllingExtension(type, settingName) {
+ let info = await getControllingExtensionInfo(type, settingName);
+ let addon = info && info.id
+ && await AddonManager.getAddonByID(info.id);
+
+ // 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] = info.id;
+ showControllingExtension(settingName, addon);
+ } else {
+ let elements = getControllingExtensionEls(settingName);
+ if (extensionControlledIds[settingName]
+ && !document.hidden
+ && elements.button) {
+ showEnableExtensionMessage(settingName);
+ } else {
+ hideControllingExtension(settingName);
+ }
+ delete extensionControlledIds[settingName];
+ }
+
+ return !!addon;
+}
+
+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;
+
+ // Remove the old content from the description.
+ while (description.firstChild) {
+ description.firstChild.remove();
+ }
+
+ // Populate the description.
+ let msg = document.getElementById("bundlePreferences")
+ .getString(`extensionControlled.${settingName}`);
+ let image = document.createElement("image");
+ const defaultIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
+ image.setAttribute("src", addon.iconURL || defaultIcon);
+ image.classList.add("extension-controlled-icon");
+ let addonBit = document.createDocumentFragment();
+ addonBit.appendChild(image);
+ addonBit.appendChild(document.createTextNode(" " + addon.name));
+ let fragment = BrowserUtils.getLocalizedFragment(document, msg, addonBit);
+ description.appendChild(fragment);
+
+ 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");
+ let icon = url => {
+ let img = document.createElement("image");
+ img.src = url;
+ img.className = "extension-controlled-icon";
+ return img;
+ };
+ let addonIcon = icon("chrome://mozapps/skin/extensions/extensionGeneric-16.svg");
+ let toolbarIcon = icon("chrome://browser/skin/menu.svg");
+ let message = document.getElementById("bundlePreferences")
+ .getString("extensionControlled.enable");
+ let frag = BrowserUtils.getLocalizedFragment(document, message, addonIcon, toolbarIcon);
+ elements.description.innerHTML = "";
+ elements.description.appendChild(frag);
+ let dismissButton = document.createElement("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 disableExtension() {
+ let {id} = await getControllingExtensionInfo(type, settingName);
+ let addon = await AddonManager.getAddonByID(id);
+ addon.userDisabled = true;
+ };
+}
--- a/browser/components/preferences/in-content/jar.mn
+++ b/browser/components/preferences/in-content/jar.mn
@@ -1,14 +1,15 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# 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/.
browser.jar:
content/browser/preferences/in-content/preferences.js
+ content/browser/preferences/in-content/extensionControlled.js
* content/browser/preferences/in-content/preferences.xul
content/browser/preferences/in-content/subdialogs.js
content/browser/preferences/in-content/main.js
content/browser/preferences/in-content/search.js
content/browser/preferences/in-content/privacy.js
content/browser/preferences/in-content/containers.js
content/browser/preferences/in-content/sync.js
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -1,12 +1,13 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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-globals-from extensionControlled.js */
/* import-globals-from preferences.js */
/* import-globals-from ../../../../toolkit/mozapps/preferences/fontbuilder.js */
/* import-globals-from ../../../base/content/aboutDialog-appUpdater.js */
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/Downloads.jsm");
ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
ChromeUtils.import("resource:///modules/ShellService.jsm");
--- a/browser/components/preferences/in-content/preferences.xul
+++ b/browser/components/preferences/in-content/preferences.xul
@@ -103,16 +103,18 @@
href="chrome://browser/skin/settings.svg"/>
<script type="application/javascript"
src="chrome://browser/content/utilityOverlay.js"/>
<script type="application/javascript"
src="chrome://global/content/preferencesBindings.js"/>
<script type="application/javascript"
src="chrome://browser/content/preferences/in-content/preferences.js"/>
+ <script type="application/javascript"
+ src="chrome://browser/content/preferences/in-content/extensionControlled.js"/>
<script src="chrome://browser/content/preferences/in-content/findInPage.js"/>
<script src="chrome://browser/content/preferences/in-content/subdialogs.js"/>
<stringbundle id="bundleBrand"
src="chrome://branding/locale/brand.properties"/>
<stringbundle id="bundlePreferences"
src="chrome://browser/locale/preferences/preferences.properties"/>
<stringbundle id="pkiBundle"
--- a/browser/components/preferences/in-content/search.js
+++ b/browser/components/preferences/in-content/search.js
@@ -1,12 +1,13 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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-globals-from extensionControlled.js */
/* import-globals-from preferences.js */
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
"resource://gre/modules/ExtensionSettingsStore.jsm");