Bug 1429593 - Part 1: Extract functions for dealing with extensions into a separate file, r?mstriemer r?jaws draft
authorBob Silverberg <bsilverberg@mozilla.com>
Mon, 22 Jan 2018 14:40:20 -0500
changeset 752041 e95237fc3fc7b2cde3ed81488a796b05ec9d7d40
parent 752021 4fe6f6560083f8c8257282bef1d4e0ced9d1b975
child 752042 7e278f0ee060ab47574bad2d3a7371b31a68ca45
child 752137 1963a708293dd8103216e0bde784b65d1dc2c88e
push id98144
push userbmo:bob.silverberg@gmail.com
push dateWed, 07 Feb 2018 13:03:42 +0000
reviewersmstriemer, jaws
bugs1429593
milestone60.0a1
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
browser/components/preferences/in-content/extensionControlled.js
browser/components/preferences/in-content/jar.mn
browser/components/preferences/in-content/main.js
browser/components/preferences/in-content/preferences.xul
browser/components/preferences/in-content/search.js
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");