/* - 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; }; }