diff options
Diffstat (limited to '')
-rw-r--r-- | browser/components/newtab/aboutwelcome/AboutWelcomeParent.jsm | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/browser/components/newtab/aboutwelcome/AboutWelcomeParent.jsm b/browser/components/newtab/aboutwelcome/AboutWelcomeParent.jsm new file mode 100644 index 0000000000..f700eda5b9 --- /dev/null +++ b/browser/components/newtab/aboutwelcome/AboutWelcomeParent.jsm @@ -0,0 +1,348 @@ +/* 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"; + +const EXPORTED_SYMBOLS = ["AboutWelcomeParent"]; + +const { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs", + BuiltInThemes: "resource:///modules/BuiltInThemes.sys.mjs", + PromiseUtils: "resource://gre/modules/PromiseUtils.sys.mjs", + Region: "resource://gre/modules/Region.sys.mjs", +}); + +XPCOMUtils.defineLazyModuleGetters(lazy, { + AddonManager: "resource://gre/modules/AddonManager.jsm", + FxAccounts: "resource://gre/modules/FxAccounts.jsm", + MigrationUtils: "resource:///modules/MigrationUtils.jsm", + SpecialMessageActions: + "resource://messaging-system/lib/SpecialMessageActions.jsm", + AboutWelcomeTelemetry: + "resource://activity-stream/aboutwelcome/lib/AboutWelcomeTelemetry.jsm", + AboutWelcomeDefaults: + "resource://activity-stream/aboutwelcome/lib/AboutWelcomeDefaults.jsm", + ShellService: "resource:///modules/ShellService.jsm", + LangPackMatcher: "resource://gre/modules/LangPackMatcher.jsm", +}); + +XPCOMUtils.defineLazyGetter(lazy, "log", () => { + const { Logger } = ChromeUtils.import( + "resource://messaging-system/lib/Logger.jsm" + ); + return new Logger("AboutWelcomeParent"); +}); + +XPCOMUtils.defineLazyGetter( + lazy, + "Telemetry", + () => new lazy.AboutWelcomeTelemetry() +); + +const DID_SEE_ABOUT_WELCOME_PREF = "trailhead.firstrun.didSeeAboutWelcome"; +const AWTerminate = { + WINDOW_CLOSED: "welcome-window-closed", + TAB_CLOSED: "welcome-tab-closed", + APP_SHUT_DOWN: "app-shut-down", + ADDRESS_BAR_NAVIGATED: "address-bar-navigated", +}; +const LIGHT_WEIGHT_THEMES = { + AUTOMATIC: "default-theme@mozilla.org", + DARK: "firefox-compact-dark@mozilla.org", + LIGHT: "firefox-compact-light@mozilla.org", + ALPENGLOW: "firefox-alpenglow@mozilla.org", + "PLAYMAKER-SOFT": "playmaker-soft-colorway@mozilla.org", + "PLAYMAKER-BALANCED": "playmaker-balanced-colorway@mozilla.org", + "PLAYMAKER-BOLD": "playmaker-bold-colorway@mozilla.org", + "EXPRESSIONIST-SOFT": "expressionist-soft-colorway@mozilla.org", + "EXPRESSIONIST-BALANCED": "expressionist-balanced-colorway@mozilla.org", + "EXPRESSIONIST-BOLD": "expressionist-bold-colorway@mozilla.org", + "VISIONARY-SOFT": "visionary-soft-colorway@mozilla.org", + "VISIONARY-BALANCED": "visionary-balanced-colorway@mozilla.org", + "VISIONARY-BOLD": "visionary-bold-colorway@mozilla.org", + "ACTIVIST-SOFT": "activist-soft-colorway@mozilla.org", + "ACTIVIST-BALANCED": "activist-balanced-colorway@mozilla.org", + "ACTIVIST-BOLD": "activist-bold-colorway@mozilla.org", + "DREAMER-SOFT": "dreamer-soft-colorway@mozilla.org", + "DREAMER-BALANCED": "dreamer-balanced-colorway@mozilla.org", + "DREAMER-BOLD": "dreamer-bold-colorway@mozilla.org", + "INNOVATOR-SOFT": "innovator-soft-colorway@mozilla.org", + "INNOVATOR-BALANCED": "innovator-balanced-colorway@mozilla.org", + "INNOVATOR-BOLD": "innovator-bold-colorway@mozilla.org", +}; + +async function getImportableSites() { + const sites = []; + + // Just handle these chromium-based browsers for now + for (const browserId of ["chrome", "chromium-edge", "chromium"]) { + // Skip if there's no profile data. + const migrator = await lazy.MigrationUtils.getMigrator(browserId); + if (!migrator) { + continue; + } + + // Check each profile for top sites + const dataPath = await migrator.wrappedJSObject._getChromeUserDataPathIfExists(); + for (const profile of await migrator.getSourceProfiles()) { + let path = PathUtils.join(dataPath, profile.id, "Top Sites"); + // Skip if top sites data is missing + if (!(await IOUtils.exists(path))) { + console.error(`Missing file at ${path}`); + continue; + } + + try { + for (const row of await lazy.MigrationUtils.getRowsFromDBWithoutLocks( + path, + `Importable ${browserId} top sites`, + `SELECT url + FROM top_sites + ORDER BY url_rank` + )) { + sites.push(row.getString(0)); + } + } catch (ex) { + console.error( + `Failed to get importable top sites from ${browserId} ${ex}` + ); + } + } + } + return sites; +} + +class AboutWelcomeObserver { + constructor() { + Services.obs.addObserver(this, "quit-application"); + + this.win = Services.focus.activeWindow; + if (!this.win) { + return; + } + + this.terminateReason = AWTerminate.ADDRESS_BAR_NAVIGATED; + + this.onWindowClose = () => { + this.terminateReason = AWTerminate.WINDOW_CLOSED; + }; + + this.onTabClose = () => { + this.terminateReason = AWTerminate.TAB_CLOSED; + }; + + this.win.addEventListener("TabClose", this.onTabClose, { once: true }); + this.win.addEventListener("unload", this.onWindowClose, { once: true }); + } + + observe(aSubject, aTopic, aData) { + switch (aTopic) { + case "quit-application": + this.terminateReason = AWTerminate.APP_SHUT_DOWN; + break; + } + } + + // Added for testing + get AWTerminate() { + return AWTerminate; + } + + stop() { + lazy.log.debug(`Terminate reason is ${this.terminateReason}`); + Services.obs.removeObserver(this, "quit-application"); + if (!this.win) { + return; + } + this.win.removeEventListener("TabClose", this.onTabClose); + this.win.removeEventListener("unload", this.onWindowClose); + this.win = null; + } +} + +class RegionHomeObserver { + observe(aSubject, aTopic, aData) { + switch (aTopic) { + case lazy.Region.REGION_TOPIC: + Services.obs.removeObserver(this, lazy.Region.REGION_TOPIC); + this.regionHomeDeferred.resolve(lazy.Region.home); + this.regionHomeDeferred = null; + break; + } + } + + promiseRegionHome() { + // Add observer and create promise that should be resolved + // with region or rejected inside didDestroy if user exits + // before region is available + if (!this.regionHomeDeferred) { + Services.obs.addObserver(this, lazy.Region.REGION_TOPIC); + this.regionHomeDeferred = lazy.PromiseUtils.defer(); + } + return this.regionHomeDeferred.promise; + } + + stop() { + if (this.regionHomeDeferred) { + Services.obs.removeObserver(this, lazy.Region.REGION_TOPIC); + // Reject unresolved deferred promise on exit + this.regionHomeDeferred.reject( + new Error("Unresolved region home promise") + ); + this.regionHomeDeferred = null; + } + } +} + +class AboutWelcomeParent extends JSWindowActorParent { + constructor() { + super(); + this.AboutWelcomeObserver = new AboutWelcomeObserver(this); + } + + // Static methods that calls into ShellService to check + // if Firefox is pinned or already default + static doesAppNeedPin() { + return lazy.ShellService.doesAppNeedPin(); + } + + static isDefaultBrowser() { + return lazy.ShellService.isDefaultBrowser(); + } + + didDestroy() { + if (this.AboutWelcomeObserver) { + this.AboutWelcomeObserver.stop(); + } + this.RegionHomeObserver?.stop(); + + lazy.Telemetry.sendTelemetry({ + event: "SESSION_END", + event_context: { + reason: this.AboutWelcomeObserver.terminateReason, + page: "about:welcome", + }, + message_id: this.AWMessageId, + }); + } + + /** + * Handle messages from AboutWelcomeChild.jsm + * + * @param {string} type + * @param {any=} data + * @param {Browser} the xul:browser rendering the page + */ + async onContentMessage(type, data, browser) { + lazy.log.debug(`Received content event: ${type}`); + switch (type) { + case "AWPage:SET_WELCOME_MESSAGE_SEEN": + this.AWMessageId = data; + try { + Services.prefs.setBoolPref(DID_SEE_ABOUT_WELCOME_PREF, true); + } catch (e) { + lazy.log.debug(`Fails to set ${DID_SEE_ABOUT_WELCOME_PREF}.`); + } + break; + case "AWPage:SPECIAL_ACTION": + lazy.SpecialMessageActions.handleAction(data, browser); + break; + case "AWPage:FXA_METRICS_FLOW_URI": + return lazy.FxAccounts.config.promiseMetricsFlowURI("aboutwelcome"); + case "AWPage:IMPORTABLE_SITES": + return getImportableSites(); + case "AWPage:TELEMETRY_EVENT": + lazy.Telemetry.sendTelemetry(data); + break; + case "AWPage:GET_ATTRIBUTION_DATA": + let attributionData = await lazy.AboutWelcomeDefaults.getAttributionContent(); + return attributionData; + case "AWPage:SELECT_THEME": + await lazy.BuiltInThemes.ensureBuiltInThemes(); + return lazy.AddonManager.getAddonByID( + LIGHT_WEIGHT_THEMES[data] + ).then(addon => addon.enable()); + case "AWPage:GET_SELECTED_THEME": + let themes = await lazy.AddonManager.getAddonsByTypes(["theme"]); + let activeTheme = themes.find(addon => addon.isActive); + // Store the current theme ID so user can restore their previous theme. + if (activeTheme?.id) { + LIGHT_WEIGHT_THEMES.AUTOMATIC = activeTheme.id; + } + // convert this to the short form name that the front end code + // expects + let themeShortName = Object.keys(LIGHT_WEIGHT_THEMES).find( + key => LIGHT_WEIGHT_THEMES[key] === activeTheme?.id + ); + return themeShortName?.toLowerCase(); + case "AWPage:GET_REGION": + if (lazy.Region.home !== null) { + return lazy.Region.home; + } + if (!this.RegionHomeObserver) { + this.RegionHomeObserver = new RegionHomeObserver(this); + } + return this.RegionHomeObserver.promiseRegionHome(); + case "AWPage:DOES_APP_NEED_PIN": + return AboutWelcomeParent.doesAppNeedPin(); + case "AWPage:NEED_DEFAULT": + // Only need to set default if we're supposed to check and not default. + return ( + Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser") && + !AboutWelcomeParent.isDefaultBrowser() + ); + case "AWPage:WAIT_FOR_MIGRATION_CLOSE": + return new Promise(resolve => + Services.ww.registerNotification(function observer(subject, topic) { + if ( + topic === "domwindowclosed" && + subject.document.documentURI === + "chrome://browser/content/migration/migration.xhtml" + ) { + Services.ww.unregisterNotification(observer); + resolve(); + } + }) + ); + case "AWPage:GET_APP_AND_SYSTEM_LOCALE_INFO": + return lazy.LangPackMatcher.getAppAndSystemLocaleInfo(); + case "AWPage:NEGOTIATE_LANGPACK": + return lazy.LangPackMatcher.negotiateLangPackForLanguageMismatch(data); + case "AWPage:ENSURE_LANG_PACK_INSTALLED": + return lazy.LangPackMatcher.ensureLangPackInstalled(data); + case "AWPage:SET_REQUESTED_LOCALES": + return lazy.LangPackMatcher.setRequestedAppLocales(data); + case "AWPage:SEND_TO_DEVICE_EMAILS_SUPPORTED": { + return lazy.BrowserUtils.sendToDeviceEmailsSupported(); + } + default: + lazy.log.debug(`Unexpected event ${type} was not handled.`); + } + + return undefined; + } + + /** + * @param {{name: string, data?: any}} message + * @override + */ + receiveMessage(message) { + const { name, data } = message; + let browser; + + if (this.manager.rootFrameLoader) { + browser = this.manager.rootFrameLoader.ownerElement; + return this.onContentMessage(name, data, browser); + } + + lazy.log.warn(`Not handling ${name} because the browser doesn't exist.`); + return null; + } +} |