AboutWelcomeParent.sys.mjs (11049B)
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 const lazy = {}; 6 7 ChromeUtils.defineESModuleGetters(lazy, { 8 AboutWelcomeDefaults: 9 "resource:///modules/aboutwelcome/AboutWelcomeDefaults.sys.mjs", 10 AboutWelcomeTelemetry: 11 "resource:///modules/aboutwelcome/AboutWelcomeTelemetry.sys.mjs", 12 AddonManager: "resource://gre/modules/AddonManager.sys.mjs", 13 AWScreenUtils: "resource:///modules/aboutwelcome/AWScreenUtils.sys.mjs", 14 BackupService: "resource:///modules/backup/BackupService.sys.mjs", 15 BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs", 16 BuiltInThemes: "resource:///modules/BuiltInThemes.sys.mjs", 17 FxAccounts: "resource://gre/modules/FxAccounts.sys.mjs", 18 LangPackMatcher: "resource://gre/modules/LangPackMatcher.sys.mjs", 19 ShellService: "moz-src:///browser/components/shell/ShellService.sys.mjs", 20 SpecialMessageActions: 21 "resource://messaging-system/lib/SpecialMessageActions.sys.mjs", 22 }); 23 24 ChromeUtils.defineLazyGetter(lazy, "log", () => { 25 const { Logger } = ChromeUtils.importESModule( 26 "resource://messaging-system/lib/Logger.sys.mjs" 27 ); 28 return new Logger("AboutWelcomeParent"); 29 }); 30 31 ChromeUtils.defineLazyGetter( 32 lazy, 33 "Telemetry", 34 () => new lazy.AboutWelcomeTelemetry() 35 ); 36 37 const DID_SEE_ABOUT_WELCOME_PREF = "trailhead.firstrun.didSeeAboutWelcome"; 38 const DID_HANDLE_CAMAPAIGN_ACTION_PREF = 39 "trailhead.firstrun.didHandleCampaignAction"; 40 const AWTerminate = { 41 WINDOW_CLOSED: "welcome-window-closed", 42 TAB_CLOSED: "welcome-tab-closed", 43 APP_SHUT_DOWN: "app-shut-down", 44 ADDRESS_BAR_NAVIGATED: "address-bar-navigated", 45 }; 46 const LIGHT_WEIGHT_THEMES = { 47 AUTOMATIC: "default-theme@mozilla.org", 48 DARK: "firefox-compact-dark@mozilla.org", 49 LIGHT: "firefox-compact-light@mozilla.org", 50 ALPENGLOW: "firefox-alpenglow@mozilla.org", 51 }; 52 53 class AboutWelcomeObserver { 54 constructor() { 55 Services.obs.addObserver(this, "quit-application"); 56 57 this.win = Services.focus.activeWindow; 58 if (!this.win) { 59 return; 60 } 61 62 this.terminateReason = AWTerminate.ADDRESS_BAR_NAVIGATED; 63 64 this.onWindowClose = () => { 65 this.terminateReason = AWTerminate.WINDOW_CLOSED; 66 }; 67 68 this.onTabClose = () => { 69 this.terminateReason = AWTerminate.TAB_CLOSED; 70 }; 71 72 this.win.addEventListener("TabClose", this.onTabClose, { once: true }); 73 this.win.addEventListener("unload", this.onWindowClose, { once: true }); 74 } 75 76 observe(aSubject, aTopic) { 77 switch (aTopic) { 78 case "quit-application": 79 this.terminateReason = AWTerminate.APP_SHUT_DOWN; 80 break; 81 } 82 } 83 84 // Added for testing 85 get AWTerminate() { 86 return AWTerminate; 87 } 88 89 stop() { 90 lazy.log.debug(`Terminate reason is ${this.terminateReason}`); 91 // Clear the entrypoint pref 92 Services.prefs.clearUserPref("browser.aboutwelcome.entrypoint"); 93 Services.obs.removeObserver(this, "quit-application"); 94 if (!this.win) { 95 return; 96 } 97 this.win.removeEventListener("TabClose", this.onTabClose); 98 this.win.removeEventListener("unload", this.onWindowClose); 99 this.win = null; 100 } 101 } 102 103 export class AboutWelcomeParent extends JSWindowActorParent { 104 constructor() { 105 super(); 106 this.startAboutWelcomeObserver(); 107 } 108 109 startAboutWelcomeObserver() { 110 this.AboutWelcomeObserver = new AboutWelcomeObserver(); 111 } 112 113 // Static methods that calls into ShellService to check 114 // if Firefox is pinned or already default 115 static async doesAppNeedPin() { 116 return ( 117 (await lazy.ShellService.doesAppNeedPin()) || 118 (await lazy.ShellService.doesAppNeedStartMenuPin()) 119 ); 120 } 121 122 static isDefaultBrowser() { 123 return lazy.ShellService.isDefaultBrowser(); 124 } 125 126 didDestroy() { 127 if (this.AboutWelcomeObserver) { 128 this.AboutWelcomeObserver.stop(); 129 } 130 this.RegionHomeObserver?.stop(); 131 132 lazy.Telemetry.sendTelemetry({ 133 event: "SESSION_END", 134 event_context: { 135 reason: this.AboutWelcomeObserver.terminateReason, 136 page: "about:welcome", 137 }, 138 message_id: this.AWMessageId, 139 }); 140 } 141 142 /** 143 * Handle messages from AboutWelcomeChild.sys.mjs 144 * 145 * @param {string} type 146 * @param {any=} data 147 * @param {Browser} the xul:browser rendering the page 148 */ 149 async onContentMessage(type, data, browser) { 150 lazy.log.debug(`Received content event: ${type}`); 151 switch (type) { 152 case "AWPage:SET_WELCOME_MESSAGE_SEEN": 153 this.AWMessageId = data; 154 try { 155 Services.prefs.setBoolPref(DID_SEE_ABOUT_WELCOME_PREF, true); 156 } catch (e) { 157 lazy.log.debug(`Fails to set ${DID_SEE_ABOUT_WELCOME_PREF}.`); 158 } 159 break; 160 case "AWPage:SPECIAL_ACTION": 161 return lazy.SpecialMessageActions.handleAction(data, browser); 162 case "AWPage:FXA_METRICS_FLOW_URI": 163 return lazy.FxAccounts.config.promiseMetricsFlowURI("aboutwelcome"); 164 case "AWPage:TELEMETRY_EVENT": 165 lazy.Telemetry.sendTelemetry(data); 166 break; 167 case "AWPage:GET_ATTRIBUTION_DATA": { 168 let attributionData = 169 await lazy.AboutWelcomeDefaults.getAttributionContent(); 170 return attributionData; 171 } 172 case "AWPage:ENSURE_ADDON_INSTALLED": 173 return new Promise(resolve => { 174 let listener = { 175 onInstallEnded(install, addon) { 176 if (addon.id === data) { 177 lazy.AddonManager.removeInstallListener(listener); 178 resolve("complete"); 179 } 180 }, 181 onInstallCancelled() { 182 lazy.AddonManager.removeInstallListener(listener); 183 resolve("install cancelled"); 184 }, 185 onDownloadCancelled() { 186 lazy.AddonManager.removeInstallListener(listener); 187 resolve("install cancelled"); 188 }, 189 onInstallFailed() { 190 lazy.AddonManager.removeInstallListener(listener); 191 resolve("install failed"); 192 }, 193 }; 194 lazy.AddonManager.addInstallListener(listener); 195 }); 196 case "AWPage:GET_INSTALLED_ADDONS": 197 return lazy.AddonManager.getActiveAddons().then(response => 198 response.addons.map(addon => addon.id) 199 ); 200 case "AWPage:GET_ADDON_DETAILS": { 201 let addonDetails = 202 await lazy.AboutWelcomeDefaults.getAddonFromRepository(data); 203 204 return { 205 addonId: addonDetails.id, 206 label: addonDetails.name, 207 icon: addonDetails.iconURL, 208 type: addonDetails.type, 209 screenshots: addonDetails.screenshots, 210 url: addonDetails.url, 211 }; 212 } 213 case "AWPage:SELECT_THEME": 214 await lazy.BuiltInThemes.ensureBuiltInThemes(); 215 return lazy.AddonManager.getAddonByID(LIGHT_WEIGHT_THEMES[data]).then( 216 addon => addon.enable() 217 ); 218 case "AWPage:GET_SELECTED_THEME": { 219 let themes = await lazy.AddonManager.getAddonsByTypes(["theme"]); 220 let activeTheme = themes.find(addon => addon.isActive); 221 // Store the current theme ID so user can restore their previous theme. 222 if (activeTheme?.id) { 223 LIGHT_WEIGHT_THEMES.AUTOMATIC = activeTheme.id; 224 } 225 // convert this to the short form name that the front end code 226 // expects 227 let themeShortName = Object.keys(LIGHT_WEIGHT_THEMES).find( 228 key => LIGHT_WEIGHT_THEMES[key] === activeTheme?.id 229 ); 230 return themeShortName?.toLowerCase(); 231 } 232 case "AWPage:DOES_APP_NEED_PIN": 233 return AboutWelcomeParent.doesAppNeedPin(); 234 case "AWPage:NEED_DEFAULT": 235 // Only need to set default if we're supposed to check and not default. 236 return ( 237 Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser") && 238 !AboutWelcomeParent.isDefaultBrowser() 239 ); 240 case "AWPage:WAIT_FOR_MIGRATION_CLOSE": 241 // Support multiples types of migration: 1) content modal 2) old 242 // migration modal 3) standalone content modal 243 return new Promise(resolve => { 244 const topics = [ 245 "MigrationWizard:Closed", 246 "MigrationWizard:Destroyed", 247 ]; 248 const observer = () => { 249 topics.forEach(t => Services.obs.removeObserver(observer, t)); 250 resolve(); 251 }; 252 topics.forEach(t => Services.obs.addObserver(observer, t)); 253 }); 254 case "AWPage:GET_APP_AND_SYSTEM_LOCALE_INFO": 255 return lazy.LangPackMatcher.getAppAndSystemLocaleInfo(); 256 case "AWPage:EVALUATE_SCREEN_TARGETING": 257 return lazy.AWScreenUtils.evaluateTargetingAndRemoveScreens(data); 258 case "AWPage:ADD_SCREEN_IMPRESSION": 259 return lazy.AWScreenUtils.addScreenImpression(data); 260 case "AWPage:EVALUATE_ATTRIBUTE_TARGETING": 261 return lazy.AWScreenUtils.evaluateScreenTargeting(data); 262 case "AWPage:NEGOTIATE_LANGPACK": 263 return lazy.LangPackMatcher.negotiateLangPackForLanguageMismatch(data); 264 case "AWPage:ENSURE_LANG_PACK_INSTALLED": 265 return lazy.LangPackMatcher.ensureLangPackInstalled(data); 266 case "AWPage:SET_REQUESTED_LOCALES": 267 return lazy.LangPackMatcher.setRequestedAppLocales(data); 268 case "AWPage:SEND_TO_DEVICE_EMAILS_SUPPORTED": { 269 return lazy.BrowserUtils.sendToDeviceEmailsSupported(); 270 } 271 case "AWPage:GET_UNHANDLED_CAMPAIGN_ACTION": { 272 if ( 273 !Services.prefs.getBoolPref(DID_HANDLE_CAMAPAIGN_ACTION_PREF, false) 274 ) { 275 return lazy.AWScreenUtils.getUnhandledCampaignAction(); 276 } 277 break; 278 } 279 case "AWPage:HANDLE_CAMPAIGN_ACTION": { 280 if ( 281 !Services.prefs.getBoolPref(DID_HANDLE_CAMAPAIGN_ACTION_PREF, false) 282 ) { 283 lazy.SpecialMessageActions.handleAction({ type: data }, browser); 284 try { 285 Services.prefs.setBoolPref(DID_HANDLE_CAMAPAIGN_ACTION_PREF, true); 286 } catch (e) { 287 lazy.log.debug(`Fails to set ${DID_HANDLE_CAMAPAIGN_ACTION_PREF}.`); 288 } 289 return true; 290 } 291 break; 292 } 293 case "AWPage:BACKUP_FIND_WELL_KNOWN": { 294 // Ask the BackupService to probe default locations. 295 let bs; 296 try { 297 bs = lazy.BackupService.get(); 298 } catch { 299 bs = lazy.BackupService.init(); 300 } 301 return bs.findBackupsInWellKnownLocations(data); 302 } 303 default: 304 lazy.log.debug(`Unexpected event ${type} was not handled.`); 305 } 306 307 return undefined; 308 } 309 310 /** 311 * @param {{name: string, data?: any}} message 312 * @override 313 */ 314 receiveMessage(message) { 315 const { name, data } = message; 316 let browser; 317 318 if (this.manager.rootFrameLoader) { 319 browser = this.manager.rootFrameLoader.ownerElement; 320 return this.onContentMessage(name, data, browser); 321 } 322 323 lazy.log.warn(`Not handling ${name} because the browser doesn't exist.`); 324 return null; 325 } 326 }