diff options
Diffstat (limited to 'browser/components/extensions/parent/ext-url-overrides.js')
-rw-r--r-- | browser/components/extensions/parent/ext-url-overrides.js | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/browser/components/extensions/parent/ext-url-overrides.js b/browser/components/extensions/parent/ext-url-overrides.js new file mode 100644 index 0000000000..07e35441ef --- /dev/null +++ b/browser/components/extensions/parent/ext-url-overrides.js @@ -0,0 +1,210 @@ +/* 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"; + +var { ExtensionParent } = ChromeUtils.importESModule( + "resource://gre/modules/ExtensionParent.sys.mjs" +); + +ChromeUtils.defineESModuleGetters(this, { + ExtensionControlledPopup: + "resource:///modules/ExtensionControlledPopup.sys.mjs", + ExtensionSettingsStore: + "resource://gre/modules/ExtensionSettingsStore.sys.mjs", +}); +ChromeUtils.defineModuleGetter( + this, + "AboutNewTab", + "resource:///modules/AboutNewTab.jsm" +); + +const STORE_TYPE = "url_overrides"; +const NEW_TAB_SETTING_NAME = "newTabURL"; +const NEW_TAB_CONFIRMED_TYPE = "newTabNotification"; +const NEW_TAB_PRIVATE_ALLOWED = "browser.newtab.privateAllowed"; +const NEW_TAB_EXTENSION_CONTROLLED = "browser.newtab.extensionControlled"; + +XPCOMUtils.defineLazyGetter(this, "newTabPopup", () => { + return new ExtensionControlledPopup({ + confirmedType: NEW_TAB_CONFIRMED_TYPE, + observerTopic: "browser-open-newtab-start", + popupnotificationId: "extension-new-tab-notification", + settingType: STORE_TYPE, + settingKey: NEW_TAB_SETTING_NAME, + descriptionId: "extension-new-tab-notification-description", + descriptionMessageId: "newTabControlled.message2", + learnMoreMessageId: "newTabControlled.learnMore", + learnMoreLink: "extension-home", + preferencesLocation: "home-newtabOverride", + preferencesEntrypoint: "addon-manage-newtab-override", + onObserverAdded() { + AboutNewTab.willNotifyUser = true; + }, + onObserverRemoved() { + AboutNewTab.willNotifyUser = false; + }, + async beforeDisableAddon(popup, win) { + // ExtensionControlledPopup will disable the add-on once this function completes. + // Disabling an add-on should remove the tabs that it has open, but we want + // to open the new New Tab in this tab (which might get closed). + // 1. Replace the tab's URL with about:blank + // 2. Return control to ExtensionControlledPopup once about:blank has loaded + // 3. Once the New Tab URL has changed, replace the tab's URL with the new New Tab URL + let gBrowser = win.gBrowser; + let tab = gBrowser.selectedTab; + await replaceUrlInTab(gBrowser, tab, Services.io.newURI("about:blank")); + Services.obs.addObserver( + { + async observe() { + await replaceUrlInTab( + gBrowser, + tab, + Services.io.newURI(AboutNewTab.newTabURL) + ); + // Now that the New Tab is loading, try to open the popup again. This + // will only open the popup if a new extension is controlling the New Tab. + popup.open(); + Services.obs.removeObserver(this, "newtab-url-changed"); + }, + }, + "newtab-url-changed" + ); + }, + }); +}); + +function setNewTabURL(extensionId, url) { + if (extensionId) { + newTabPopup.addObserver(extensionId); + let policy = ExtensionParent.WebExtensionPolicy.getByID(extensionId); + Services.prefs.setBoolPref( + NEW_TAB_PRIVATE_ALLOWED, + policy && policy.privateBrowsingAllowed + ); + Services.prefs.setBoolPref(NEW_TAB_EXTENSION_CONTROLLED, true); + } else { + newTabPopup.removeObserver(); + Services.prefs.clearUserPref(NEW_TAB_PRIVATE_ALLOWED); + Services.prefs.clearUserPref(NEW_TAB_EXTENSION_CONTROLLED); + } + if (url) { + AboutNewTab.newTabURL = url; + } +} + +// eslint-disable-next-line mozilla/balanced-listeners +ExtensionParent.apiManager.on( + "extension-setting-changed", + async (eventName, setting) => { + let extensionId, url; + if (setting.type === STORE_TYPE && setting.key === NEW_TAB_SETTING_NAME) { + // If the actual setting has changed in some way, we will have + // setting.item which is what the setting has been changed to. If + // we have an item, we always want to update the newTabUrl values. + let { item } = setting; + if (item) { + // If we're resetting, id will be undefined. + extensionId = item.id; + url = item.value || item.initialValue; + setNewTabURL(extensionId, url); + } + } + } +); + +async function processSettings(action, id) { + await ExtensionSettingsStore.initialize(); + if (ExtensionSettingsStore.hasSetting(id, STORE_TYPE, NEW_TAB_SETTING_NAME)) { + ExtensionSettingsStore[action](id, STORE_TYPE, NEW_TAB_SETTING_NAME); + } +} + +this.urlOverrides = class extends ExtensionAPI { + static async onDisable(id) { + newTabPopup.clearConfirmation(id); + await processSettings("disable", id); + } + + static async onEnabling(id) { + await processSettings("enable", id); + } + + static async onUninstall(id) { + // TODO: This can be removed once bug 1438364 is fixed and all data is cleaned up. + newTabPopup.clearConfirmation(id); + await processSettings("removeSetting", id); + } + + static async onUpdate(id, manifest) { + if ( + !manifest.chrome_url_overrides || + !manifest.chrome_url_overrides.newtab + ) { + await ExtensionSettingsStore.initialize(); + if ( + ExtensionSettingsStore.hasSetting(id, STORE_TYPE, NEW_TAB_SETTING_NAME) + ) { + ExtensionSettingsStore.removeSetting( + id, + STORE_TYPE, + NEW_TAB_SETTING_NAME + ); + } + } + } + + async onManifestEntry(entryName) { + let { extension } = this; + let { manifest } = extension; + + if (manifest.chrome_url_overrides.newtab) { + let url = extension.baseURI.resolve(manifest.chrome_url_overrides.newtab); + + await ExtensionSettingsStore.initialize(); + let item = await ExtensionSettingsStore.addSetting( + extension.id, + STORE_TYPE, + NEW_TAB_SETTING_NAME, + url, + () => AboutNewTab.newTabURL + ); + + // Set the newTabURL to the current value of the setting. + if (item) { + setNewTabURL(item.id, item.value || item.initialValue); + } + + // We need to monitor permission change and update the preferences. + // eslint-disable-next-line mozilla/balanced-listeners + extension.on("add-permissions", async (ignoreEvent, permissions) => { + if ( + permissions.permissions.includes("internal:privateBrowsingAllowed") + ) { + let item = await ExtensionSettingsStore.getSetting( + STORE_TYPE, + NEW_TAB_SETTING_NAME + ); + if (item && item.id == extension.id) { + Services.prefs.setBoolPref(NEW_TAB_PRIVATE_ALLOWED, true); + } + } + }); + // eslint-disable-next-line mozilla/balanced-listeners + extension.on("remove-permissions", async (ignoreEvent, permissions) => { + if ( + permissions.permissions.includes("internal:privateBrowsingAllowed") + ) { + let item = await ExtensionSettingsStore.getSetting( + STORE_TYPE, + NEW_TAB_SETTING_NAME + ); + if (item && item.id == extension.id) { + Services.prefs.setBoolPref(NEW_TAB_PRIVATE_ALLOWED, false); + } + } + }); + } + } +}; |