ext-url-overrides.js (7037B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 var { ExtensionParent } = ChromeUtils.importESModule( 8 "resource://gre/modules/ExtensionParent.sys.mjs" 9 ); 10 11 ChromeUtils.defineESModuleGetters(this, { 12 AboutNewTab: "resource:///modules/AboutNewTab.sys.mjs", 13 ExtensionControlledPopup: 14 "resource:///modules/ExtensionControlledPopup.sys.mjs", 15 ExtensionSettingsStore: 16 "resource://gre/modules/ExtensionSettingsStore.sys.mjs", 17 }); 18 19 const STORE_TYPE = "url_overrides"; 20 const NEW_TAB_SETTING_NAME = "newTabURL"; 21 const NEW_TAB_CONFIRMED_TYPE = "newTabNotification"; 22 const NEW_TAB_PRIVATE_ALLOWED = "browser.newtab.privateAllowed"; 23 const NEW_TAB_EXTENSION_CONTROLLED = "browser.newtab.extensionControlled"; 24 25 ChromeUtils.defineLazyGetter(this, "newTabPopup", () => { 26 return new ExtensionControlledPopup({ 27 confirmedType: NEW_TAB_CONFIRMED_TYPE, 28 observerTopic: "browser-open-newtab-start", 29 popupnotificationId: "extension-new-tab-notification", 30 settingType: STORE_TYPE, 31 settingKey: NEW_TAB_SETTING_NAME, 32 descriptionId: "extension-new-tab-notification-description", 33 descriptionMessageId: "newTabControlled.message2", 34 learnMoreLink: "extension-home", 35 preferencesLocation: "home-newtabOverride", 36 preferencesEntrypoint: "addon-manage-newtab-override", 37 onObserverAdded() { 38 AboutNewTab.willNotifyUser = true; 39 }, 40 onObserverRemoved() { 41 AboutNewTab.willNotifyUser = false; 42 }, 43 async beforeDisableAddon(popup, win) { 44 // ExtensionControlledPopup will disable the add-on once this function completes. 45 // Disabling an add-on should remove the tabs that it has open, but we want 46 // to open the new New Tab in this tab (which might get closed). 47 // 1. Replace the tab's URL with about:blank 48 // 2. Return control to ExtensionControlledPopup once about:blank has loaded 49 // 3. Once the New Tab URL has changed, replace the tab's URL with the new New Tab URL 50 let gBrowser = win.gBrowser; 51 let tab = gBrowser.selectedTab; 52 await replaceUrlInTab(gBrowser, tab, Services.io.newURI("about:blank")); 53 Services.obs.addObserver( 54 { 55 async observe() { 56 await replaceUrlInTab( 57 gBrowser, 58 tab, 59 Services.io.newURI(AboutNewTab.newTabURL) 60 ); 61 // Now that the New Tab is loading, try to open the popup again. This 62 // will only open the popup if a new extension is controlling the New Tab. 63 popup.open(); 64 Services.obs.removeObserver(this, "newtab-url-changed"); 65 }, 66 }, 67 "newtab-url-changed" 68 ); 69 }, 70 }); 71 }); 72 73 function setNewTabURL(extensionId, url) { 74 if (extensionId) { 75 newTabPopup.addObserver(extensionId); 76 let policy = ExtensionParent.WebExtensionPolicy.getByID(extensionId); 77 Services.prefs.setBoolPref( 78 NEW_TAB_PRIVATE_ALLOWED, 79 policy && policy.privateBrowsingAllowed 80 ); 81 Services.prefs.setBoolPref(NEW_TAB_EXTENSION_CONTROLLED, true); 82 } else { 83 newTabPopup.removeObserver(); 84 Services.prefs.clearUserPref(NEW_TAB_PRIVATE_ALLOWED); 85 Services.prefs.clearUserPref(NEW_TAB_EXTENSION_CONTROLLED); 86 } 87 if (url) { 88 AboutNewTab.newTabURL = url; 89 } 90 } 91 92 // eslint-disable-next-line mozilla/balanced-listeners 93 ExtensionParent.apiManager.on( 94 "extension-setting-changed", 95 async (eventName, setting) => { 96 let extensionId, url; 97 if (setting.type === STORE_TYPE && setting.key === NEW_TAB_SETTING_NAME) { 98 // If the actual setting has changed in some way, we will have 99 // setting.item which is what the setting has been changed to. If 100 // we have an item, we always want to update the newTabUrl values. 101 let { item } = setting; 102 if (item) { 103 // If we're resetting, id will be undefined. 104 extensionId = item.id; 105 url = item.value || item.initialValue; 106 setNewTabURL(extensionId, url); 107 } 108 } 109 } 110 ); 111 112 async function processSettings(action, id) { 113 await ExtensionSettingsStore.initialize(); 114 if (ExtensionSettingsStore.hasSetting(id, STORE_TYPE, NEW_TAB_SETTING_NAME)) { 115 ExtensionSettingsStore[action](id, STORE_TYPE, NEW_TAB_SETTING_NAME); 116 } 117 } 118 119 this.urlOverrides = class extends ExtensionAPI { 120 static async onDisable(id) { 121 newTabPopup.clearConfirmation(id); 122 await processSettings("disable", id); 123 } 124 125 static async onEnabling(id) { 126 await processSettings("enable", id); 127 } 128 129 static async onUninstall(id) { 130 // TODO: This can be removed once bug 1438364 is fixed and all data is cleaned up. 131 newTabPopup.clearConfirmation(id); 132 await processSettings("removeSetting", id); 133 } 134 135 static async onUpdate(id, manifest) { 136 if ( 137 !manifest.chrome_url_overrides || 138 !manifest.chrome_url_overrides.newtab 139 ) { 140 await ExtensionSettingsStore.initialize(); 141 if ( 142 ExtensionSettingsStore.hasSetting(id, STORE_TYPE, NEW_TAB_SETTING_NAME) 143 ) { 144 ExtensionSettingsStore.removeSetting( 145 id, 146 STORE_TYPE, 147 NEW_TAB_SETTING_NAME 148 ); 149 } 150 } 151 } 152 153 async onManifestEntry() { 154 let { extension } = this; 155 let { manifest } = extension; 156 157 if (manifest.chrome_url_overrides.newtab) { 158 let url = extension.baseURI.resolve(manifest.chrome_url_overrides.newtab); 159 160 await ExtensionSettingsStore.initialize(); 161 let item = await ExtensionSettingsStore.addSetting( 162 extension.id, 163 STORE_TYPE, 164 NEW_TAB_SETTING_NAME, 165 url, 166 () => AboutNewTab.newTabURL 167 ); 168 169 // Set the newTabURL to the current value of the setting. 170 if (item) { 171 setNewTabURL(item.id, item.value || item.initialValue); 172 } 173 174 // We need to monitor permission change and update the preferences. 175 // eslint-disable-next-line mozilla/balanced-listeners 176 extension.on("add-permissions", async (ignoreEvent, permissions) => { 177 if ( 178 permissions.permissions.includes("internal:privateBrowsingAllowed") 179 ) { 180 let item = await ExtensionSettingsStore.getSetting( 181 STORE_TYPE, 182 NEW_TAB_SETTING_NAME 183 ); 184 if (item && item.id == extension.id) { 185 Services.prefs.setBoolPref(NEW_TAB_PRIVATE_ALLOWED, true); 186 } 187 } 188 }); 189 // eslint-disable-next-line mozilla/balanced-listeners 190 extension.on("remove-permissions", async (ignoreEvent, permissions) => { 191 if ( 192 permissions.permissions.includes("internal:privateBrowsingAllowed") 193 ) { 194 let item = await ExtensionSettingsStore.getSetting( 195 STORE_TYPE, 196 NEW_TAB_SETTING_NAME 197 ); 198 if (item && item.id == extension.id) { 199 Services.prefs.setBoolPref(NEW_TAB_PRIVATE_ALLOWED, false); 200 } 201 } 202 }); 203 } 204 } 205 };