diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /browser/components/newtab/aboutwelcome | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/components/newtab/aboutwelcome')
7 files changed, 5591 insertions, 0 deletions
diff --git a/browser/components/newtab/aboutwelcome/AboutWelcomeChild.jsm b/browser/components/newtab/aboutwelcome/AboutWelcomeChild.jsm new file mode 100644 index 0000000000..0da59a5aa7 --- /dev/null +++ b/browser/components/newtab/aboutwelcome/AboutWelcomeChild.jsm @@ -0,0 +1,411 @@ +/* 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 = ["AboutWelcomeChild"]; + +const { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); + +const lazy = {}; + +XPCOMUtils.defineLazyModuleGetters(lazy, { + DEFAULT_SITES: "resource://activity-stream/lib/DefaultSites.jsm", + ExperimentAPI: "resource://nimbus/ExperimentAPI.jsm", + shortURL: "resource://activity-stream/lib/ShortURL.jsm", + TippyTopProvider: "resource://activity-stream/lib/TippyTopProvider.jsm", + AboutWelcomeDefaults: + "resource://activity-stream/aboutwelcome/lib/AboutWelcomeDefaults.jsm", + NimbusFeatures: "resource://nimbus/ExperimentAPI.jsm", +}); + +XPCOMUtils.defineLazyGetter(lazy, "log", () => { + const { Logger } = ChromeUtils.import( + "resource://messaging-system/lib/Logger.jsm" + ); + return new Logger("AboutWelcomeChild"); +}); + +XPCOMUtils.defineLazyGetter(lazy, "tippyTopProvider", () => + (async () => { + const provider = new lazy.TippyTopProvider(); + await provider.init(); + return provider; + })() +); + +const SEARCH_REGION_PREF = "browser.search.region"; + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "searchRegion", + SEARCH_REGION_PREF, + "" +); + +/** + * Lazily get importable sites from parent or reuse cached ones. + */ +function getImportableSites(child) { + return ( + getImportableSites.cache ?? + (getImportableSites.cache = (async () => { + // Use tippy top to get packaged rich icons + const tippyTop = await lazy.tippyTopProvider; + // Remove duplicate entries if they would appear the same + return `[${[ + ...new Set( + (await child.sendQuery("AWPage:IMPORTABLE_SITES")).map(url => { + // Get both rich icon and short name and save for deduping + const site = { url }; + tippyTop.processSite(site, "*"); + return JSON.stringify({ + icon: site.tippyTopIcon, + label: lazy.shortURL(site), + }); + }) + ), + ]}]`; + })()) + ); +} + +async function getDefaultSites(child) { + // Get default TopSites by region + let sites = lazy.DEFAULT_SITES.get( + lazy.DEFAULT_SITES.has(lazy.searchRegion) ? lazy.searchRegion : "" + ); + + // Use tippy top to get packaged rich icons + const tippyTop = await lazy.tippyTopProvider; + let defaultSites = sites.split(",").map(link => { + let site = { url: link }; + tippyTop.processSite(site); + return { + icon: site.tippyTopIcon, + title: lazy.shortURL(site), + }; + }); + return Cu.cloneInto(defaultSites, child.contentWindow); +} + +async function getSelectedTheme(child) { + let activeThemeId = await child.sendQuery("AWPage:GET_SELECTED_THEME"); + return activeThemeId; +} + +class AboutWelcomeChild extends JSWindowActorChild { + actorCreated() { + this.exportFunctions(); + } + + /** + * Send event that can be handled by the page + * @param {{type: string, data?: any}} action + */ + sendToPage(action) { + lazy.log.debug(`Sending to page: ${action.type}`); + const win = this.document.defaultView; + const event = new win.CustomEvent("AboutWelcomeChromeToContent", { + detail: Cu.cloneInto(action, win), + }); + win.dispatchEvent(event); + } + + /** + * Export functions that can be called by page js + */ + exportFunctions() { + let window = this.contentWindow; + + Cu.exportFunction(this.AWGetFeatureConfig.bind(this), window, { + defineAs: "AWGetFeatureConfig", + }); + + Cu.exportFunction(this.AWGetFxAMetricsFlowURI.bind(this), window, { + defineAs: "AWGetFxAMetricsFlowURI", + }); + + Cu.exportFunction(this.AWGetImportableSites.bind(this), window, { + defineAs: "AWGetImportableSites", + }); + + Cu.exportFunction(this.AWGetDefaultSites.bind(this), window, { + defineAs: "AWGetDefaultSites", + }); + + Cu.exportFunction(this.AWGetSelectedTheme.bind(this), window, { + defineAs: "AWGetSelectedTheme", + }); + + Cu.exportFunction(this.AWGetRegion.bind(this), window, { + defineAs: "AWGetRegion", + }); + + Cu.exportFunction(this.AWSelectTheme.bind(this), window, { + defineAs: "AWSelectTheme", + }); + + Cu.exportFunction(this.AWSendEventTelemetry.bind(this), window, { + defineAs: "AWSendEventTelemetry", + }); + + Cu.exportFunction(this.AWSendToParent.bind(this), window, { + defineAs: "AWSendToParent", + }); + + Cu.exportFunction(this.AWWaitForMigrationClose.bind(this), window, { + defineAs: "AWWaitForMigrationClose", + }); + + Cu.exportFunction(this.AWFinish.bind(this), window, { + defineAs: "AWFinish", + }); + + Cu.exportFunction(this.AWEnsureLangPackInstalled.bind(this), window, { + defineAs: "AWEnsureLangPackInstalled", + }); + + Cu.exportFunction( + this.AWNegotiateLangPackForLanguageMismatch.bind(this), + window, + { + defineAs: "AWNegotiateLangPackForLanguageMismatch", + } + ); + + Cu.exportFunction(this.AWSetRequestedLocales.bind(this), window, { + defineAs: "AWSetRequestedLocales", + }); + + Cu.exportFunction(this.AWSendToDeviceEmailsSupported.bind(this), window, { + defineAs: "AWSendToDeviceEmailsSupported", + }); + + Cu.exportFunction(this.AWNewScreen.bind(this), window, { + defineAs: "AWNewScreen", + }); + } + + /** + * Wrap a promise so content can use Promise methods. + */ + wrapPromise(promise) { + return new this.contentWindow.Promise((resolve, reject) => + promise.then(resolve, reject) + ); + } + + /** + * Clones the result of the query into the content window. + */ + sendQueryAndCloneForContent(...sendQueryArgs) { + return this.wrapPromise( + (async () => { + return Cu.cloneInto( + await this.sendQuery(...sendQueryArgs), + this.contentWindow + ); + })() + ); + } + + AWSelectTheme(data) { + return this.wrapPromise( + this.sendQuery("AWPage:SELECT_THEME", data.toUpperCase()) + ); + } + + /** + * Send initial data to page including experiment information + */ + async getAWContent() { + let attributionData = await this.sendQuery("AWPage:GET_ATTRIBUTION_DATA"); + + // Return to AMO gets returned early. + if (attributionData?.template) { + lazy.log.debug("Loading about:welcome with RTAMO attribution data"); + return Cu.cloneInto(attributionData, this.contentWindow); + } else if (attributionData?.ua) { + lazy.log.debug("Loading about:welcome with UA attribution"); + } + + let experimentMetadata = + lazy.ExperimentAPI.getExperimentMetaData({ + featureId: "aboutwelcome", + }) || {}; + + lazy.log.debug( + `Loading about:welcome with ${experimentMetadata?.slug ?? + "no"} experiment` + ); + + let featureConfig = lazy.NimbusFeatures.aboutwelcome.getAllVariables(); + featureConfig.needDefault = await this.sendQuery("AWPage:NEED_DEFAULT"); + featureConfig.needPin = await this.sendQuery("AWPage:DOES_APP_NEED_PIN"); + if (featureConfig.languageMismatchEnabled) { + featureConfig.appAndSystemLocaleInfo = await this.sendQuery( + "AWPage:GET_APP_AND_SYSTEM_LOCALE_INFO" + ); + } + + // The MR2022 onboarding variable overrides the about:welcome templateMR + // variable if enrolled. + const useMROnboarding = lazy.NimbusFeatures.majorRelease2022.getVariable( + "onboarding" + ); + const useTemplateMR = useMROnboarding ?? featureConfig.templateMR; + + // FeatureConfig (from experiments) has higher precendence + // to defaults. But the `screens` property isn't defined we shouldn't + // override the default with `null` + let defaults = lazy.AboutWelcomeDefaults.getDefaults(useTemplateMR); + + const content = await lazy.AboutWelcomeDefaults.prepareContentForReact({ + ...attributionData, + ...experimentMetadata, + ...defaults, + ...featureConfig, + templateMR: useTemplateMR, + screens: featureConfig.screens ?? defaults.screens, + backdrop: featureConfig.backdrop ?? defaults.backdrop, + }); + + return Cu.cloneInto(content, this.contentWindow); + } + + AWGetFeatureConfig() { + return this.wrapPromise(this.getAWContent()); + } + + AWGetFxAMetricsFlowURI() { + return this.wrapPromise(this.sendQuery("AWPage:FXA_METRICS_FLOW_URI")); + } + + AWGetImportableSites() { + return this.wrapPromise(getImportableSites(this)); + } + + AWGetDefaultSites() { + return this.wrapPromise(getDefaultSites(this)); + } + + AWGetSelectedTheme() { + return this.wrapPromise(getSelectedTheme(this)); + } + + /** + * Send Event Telemetry + * @param {object} eventData + */ + AWSendEventTelemetry(eventData) { + this.AWSendToParent("TELEMETRY_EVENT", { + ...eventData, + event_context: { + ...eventData.event_context, + }, + }); + } + + /** + * Send message that can be handled by AboutWelcomeParent.jsm + * @param {string} type + * @param {any=} data + */ + AWSendToParent(type, data) { + this.sendAsyncMessage(`AWPage:${type}`, data); + } + + AWWaitForMigrationClose() { + return this.wrapPromise(this.sendQuery("AWPage:WAIT_FOR_MIGRATION_CLOSE")); + } + + AWGetRegion() { + return this.wrapPromise(this.sendQuery("AWPage:GET_REGION")); + } + + AWFinish() { + this.contentWindow.location.href = "about:home"; + } + + AWEnsureLangPackInstalled(negotiated, screenContent) { + const content = Cu.cloneInto(screenContent, {}); + return this.wrapPromise( + this.sendQuery( + "AWPage:ENSURE_LANG_PACK_INSTALLED", + negotiated.langPack + ).then(() => { + const formatting = []; + const l10n = new Localization( + ["branding/brand.ftl", "browser/newtab/onboarding.ftl"], + false, + undefined, + // Use the system-ish then app then default locale. + [...negotiated.requestSystemLocales, "en-US"] + ); + + // Add the negotiated language name as args. + function addMessageArgsAndUseLangPack(obj) { + for (const value of Object.values(obj)) { + if (value?.string_id) { + value.args = { + ...value.args, + negotiatedLanguage: negotiated.langPackDisplayName, + }; + + // Expose fluent strings wanting lang pack as raw. + if (value.useLangPack) { + formatting.push( + l10n.formatValue(value.string_id, value.args).then(raw => { + delete value.string_id; + value.raw = raw; + }) + ); + } + } + } + } + addMessageArgsAndUseLangPack(content.languageSwitcher); + addMessageArgsAndUseLangPack(content); + return Promise.all(formatting).then(() => + Cu.cloneInto(content, this.contentWindow) + ); + }) + ); + } + + AWSetRequestedLocales(requestSystemLocales) { + return this.sendQueryAndCloneForContent( + "AWPage:SET_REQUESTED_LOCALES", + requestSystemLocales + ); + } + + AWNegotiateLangPackForLanguageMismatch(appAndSystemLocaleInfo) { + return this.sendQueryAndCloneForContent( + "AWPage:NEGOTIATE_LANGPACK", + appAndSystemLocaleInfo + ); + } + + AWSendToDeviceEmailsSupported() { + return this.wrapPromise( + this.sendQuery("AWPage:SEND_TO_DEVICE_EMAILS_SUPPORTED") + ); + } + + AWNewScreen(screenId) { + return this.wrapPromise(this.sendQuery("AWPage:NEW_SCREEN", screenId)); + } + + /** + * @param {{type: string, detail?: any}} event + * @override + */ + handleEvent(event) { + lazy.log.debug(`Received page event ${event.type}`); + } +} 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; + } +} diff --git a/browser/components/newtab/aboutwelcome/content/aboutwelcome.bundle.js b/browser/components/newtab/aboutwelcome/content/aboutwelcome.bundle.js new file mode 100644 index 0000000000..36aa2615f1 --- /dev/null +++ b/browser/components/newtab/aboutwelcome/content/aboutwelcome.bundle.js @@ -0,0 +1,2223 @@ +/*! + * + * NOTE: This file is generated by webpack from aboutwelcome.jsx + * using the npm bundle task. + * + */ +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ([ +/* 0 */, +/* 1 */ +/***/ ((module) => { + +module.exports = React; + +/***/ }), +/* 2 */ +/***/ ((module) => { + +module.exports = ReactDOM; + +/***/ }), +/* 3 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "AboutWelcomeUtils": () => (/* binding */ AboutWelcomeUtils), +/* harmony export */ "DEFAULT_RTAMO_CONTENT": () => (/* binding */ DEFAULT_RTAMO_CONTENT) +/* harmony export */ }); +/* 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/. */ +// If we're in a subdialog, then this is a spotlight modal. +// Otherwise, this is about:welcome or a Feature Callout +// in another "about" page and we should return the current page. +const page = document.querySelector(":root[dialogroot=true]") ? "spotlight" : document.location.href; +const AboutWelcomeUtils = { + handleUserAction(action) { + window.AWSendToParent("SPECIAL_ACTION", action); + }, + + sendImpressionTelemetry(messageId, context) { + var _window$AWSendEventTe, _window; + + (_window$AWSendEventTe = (_window = window).AWSendEventTelemetry) === null || _window$AWSendEventTe === void 0 ? void 0 : _window$AWSendEventTe.call(_window, { + event: "IMPRESSION", + event_context: { ...context, + page + }, + message_id: messageId + }); + }, + + sendActionTelemetry(messageId, elementId, eventName = "CLICK_BUTTON") { + var _window$AWSendEventTe2, _window2; + + const ping = { + event: eventName, + event_context: { + source: elementId, + page + }, + message_id: messageId + }; + (_window$AWSendEventTe2 = (_window2 = window).AWSendEventTelemetry) === null || _window$AWSendEventTe2 === void 0 ? void 0 : _window$AWSendEventTe2.call(_window2, ping); + }, + + sendDismissTelemetry(messageId, elementId) { + // Don't send DISMISS telemetry in spotlight modals since they already send + // their own equivalent telemetry. + if (page !== "spotlight") { + this.sendActionTelemetry(messageId, elementId, "DISMISS"); + } + }, + + async fetchFlowParams(metricsFlowUri) { + let flowParams; + + try { + const response = await fetch(metricsFlowUri, { + credentials: "omit" + }); + + if (response.status === 200) { + const { + deviceId, + flowId, + flowBeginTime + } = await response.json(); + flowParams = { + deviceId, + flowId, + flowBeginTime + }; + } else { + console.error("Non-200 response", response); + } + } catch (e) { + flowParams = null; + } + + return flowParams; + }, + + sendEvent(type, detail) { + document.dispatchEvent(new CustomEvent(`AWPage:${type}`, { + bubbles: true, + detail + })); + } + +}; +const DEFAULT_RTAMO_CONTENT = { + template: "return_to_amo", + utm_term: "rtamo", + content: { + position: "split", + title: { + string_id: "mr1-return-to-amo-subtitle" + }, + has_noodles: false, + subtitle: { + string_id: "mr1-return-to-amo-addon-title" + }, + backdrop: "var(--mr-welcome-background-color) var(--mr-welcome-background-gradient)", + background: "url('chrome://activity-stream/content/data/content/assets/mr-rtamo-background-image.svg') no-repeat center", + progress_bar: true, + primary_button: { + label: { + string_id: "mr1-return-to-amo-add-extension-label" + }, + source_id: "ADD_EXTENSION_BUTTON", + action: { + type: "INSTALL_ADDON_FROM_URL", + data: { + url: null, + telemetrySource: "rtamo" + } + } + }, + secondary_button: { + label: { + string_id: "onboarding-not-now-button-label" + }, + source_id: "RTAMO_START_BROWSING_BUTTON", + action: { + type: "OPEN_AWESOME_BAR" + } + }, + secondary_button_top: { + label: { + string_id: "mr1-onboarding-sign-in-button-label" + }, + source_id: "RTAMO_FXA_SIGNIN_BUTTON", + action: { + data: { + entrypoint: "activity-stream-firstrun", + where: "tab" + }, + type: "SHOW_FIREFOX_ACCOUNTS", + addFlowParams: true + } + } + } +}; + +/***/ }), +/* 4 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "MultiStageAboutWelcome": () => (/* binding */ MultiStageAboutWelcome), +/* harmony export */ "SecondaryCTA": () => (/* binding */ SecondaryCTA), +/* harmony export */ "StepsIndicator": () => (/* binding */ StepsIndicator), +/* harmony export */ "ProgressBar": () => (/* binding */ ProgressBar), +/* harmony export */ "WelcomeScreen": () => (/* binding */ WelcomeScreen) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); +/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3); +/* harmony import */ var _MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6); +/* harmony import */ var _LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(11); +/* harmony import */ var _asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(16); +/* 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/. */ + + + + + + // Amount of milliseconds for all transitions to complete (including delays). + +const TRANSITION_OUT_TIME = 1000; +const MultiStageAboutWelcome = props => { + let { + screens + } = props; + const [index, setScreenIndex] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(props.startScreen); + const [previousOrder, setPreviousOrder] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(props.startScreen - 1); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + const screenInitials = screens.map(({ + id + }) => { + var _id$split$; + + return id === null || id === void 0 ? void 0 : (_id$split$ = id.split("_")[1]) === null || _id$split$ === void 0 ? void 0 : _id$split$[0]; + }).join(""); // Send impression ping when respective screen first renders + + screens.forEach((screen, order) => { + if (index === order) { + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendImpressionTelemetry(`${props.message_id}_${order}_${screen.id}_${screenInitials}`); + } + }); // Remember that a new screen has loaded for browser navigation + + if (props.updateHistory && index > window.history.state) { + window.history.pushState(index, ""); + } // Remember the previous screen index so we can animate the transition + + + setPreviousOrder(index); + }, [index]); + const [flowParams, setFlowParams] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); + const { + metricsFlowUri + } = props; + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + (async () => { + if (metricsFlowUri) { + setFlowParams(await _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.fetchFlowParams(metricsFlowUri)); + } + })(); + }, [metricsFlowUri]); // Allow "in" style to render to actually transition towards regular state, + // which also makes using browser back/forward navigation skip transitions. + + const [transition, setTransition] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(props.transitions ? "in" : ""); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + if (transition === "in") { + requestAnimationFrame(() => requestAnimationFrame(() => setTransition(""))); + } + }, [transition]); // Transition to next screen, opening about:home on last screen button CTA + + const handleTransition = () => { + // Only handle transitioning out from a screen once. + if (transition === "out") { + return; + } // Start transitioning things "out" immediately when moving forwards. + + + setTransition(props.transitions ? "out" : ""); // Actually move forwards after all transitions finish. + + setTimeout(() => { + if (index < screens.length - 1) { + setTransition(props.transitions ? "in" : ""); + setScreenIndex(prevState => prevState + 1); + } else { + window.AWFinish(); + } + }, props.transitions ? TRANSITION_OUT_TIME : 0); + }; + + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + if (props.updateHistory) { + // Switch to the screen tracked in state (null for initial state) + // or last screen index if a user navigates by pressing back + // button from about:home + const handler = ({ + state + }) => { + if (transition === "out") { + return; + } + + setTransition(props.transitions ? "out" : ""); + setTimeout(() => { + setTransition(props.transitions ? "in" : ""); + setScreenIndex(Math.min(state, screens.length - 1)); + }, props.transitions ? TRANSITION_OUT_TIME : 0); + }; // Handle page load, e.g., going back to about:welcome from about:home + + + const { + state + } = window.history; + + if (state) { + setScreenIndex(Math.min(state, screens.length - 1)); + setPreviousOrder(Math.min(state, screens.length - 1)); + } // Watch for browser back/forward button navigation events + + + window.addEventListener("popstate", handler); + return () => window.removeEventListener("popstate", handler); + } + + return false; + }, []); // Update top sites with default sites by region when region is available + + const [region, setRegion] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + (async () => { + setRegion(await window.AWGetRegion()); + })(); + }, []); // Save the active multi select state containing array of checkbox ids + // used in handleAction to update MULTI_ACTION data + + const [activeMultiSelect, setActiveMultiSelect] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); // Get the active theme so the rendering code can make it selected + // by default. + + const [activeTheme, setActiveTheme] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); + const [initialTheme, setInitialTheme] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + (async () => { + let theme = await window.AWGetSelectedTheme(); + setInitialTheme(theme); + setActiveTheme(theme); + })(); + }, []); + const useImportable = props.message_id.includes("IMPORTABLE"); // Track whether we have already sent the importable sites impression telemetry + + const importTelemetrySent = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(false); + const [topSites, setTopSites] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + (async () => { + var _window$AWGetDefaultS, _window, _window$AWGetImportab, _window2; + + let DEFAULT_SITES = await ((_window$AWGetDefaultS = (_window = window).AWGetDefaultSites) === null || _window$AWGetDefaultS === void 0 ? void 0 : _window$AWGetDefaultS.call(_window)); + const importable = JSON.parse((await ((_window$AWGetImportab = (_window2 = window).AWGetImportableSites) === null || _window$AWGetImportab === void 0 ? void 0 : _window$AWGetImportab.call(_window2))) || "[]"); + const showImportable = useImportable && importable.length >= 5; + + if (!importTelemetrySent.current) { + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendImpressionTelemetry(`${props.message_id}_SITES`, { + display: showImportable ? "importable" : "static", + importable: importable.length + }); + importTelemetrySent.current = true; + } + + setTopSites(showImportable ? { + data: importable, + showImportable + } : { + data: DEFAULT_SITES, + showImportable + }); + })(); + }, [useImportable, region]); + const centeredScreens = props.screens.filter(s => s.content.position !== "corner"); + const { + negotiatedLanguage, + langPackInstallPhase, + languageFilteredScreens + } = (0,_LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__.useLanguageSwitcher)(props.appAndSystemLocaleInfo, screens, index, setScreenIndex); + screens = languageFilteredScreens; + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: `outer-wrapper onboardingContainer proton transition-${transition}`, + style: props.backdrop ? { + background: props.backdrop + } : {} + }, screens.map((screen, order) => { + const isFirstCenteredScreen = (!screen.content.position || screen.content.position === "center") && screen === centeredScreens[0]; + const isLastCenteredScreen = (!screen.content.position || screen.content.position === "center") && screen === centeredScreens[centeredScreens.length - 1]; + /* If first screen is corner positioned, don't include it in the count for the steps indicator. This assumes corner positioning will only be used on the first screen. */ + + const totalNumberOfScreens = screens[0].content.position === "corner" ? screens.length - 1 : screens.length; + /* Don't include a starting corner screen when determining step indicator order */ + + const stepOrder = screens[0].content.position === "corner" ? order - 1 : order; + return index === order ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(WelcomeScreen, { + key: screen.id + order, + id: screen.id, + totalNumberOfScreens: totalNumberOfScreens, + isFirstCenteredScreen: isFirstCenteredScreen, + isLastCenteredScreen: isLastCenteredScreen, + stepOrder: stepOrder, + order: order, + previousOrder: previousOrder, + content: screen.content, + navigate: handleTransition, + topSites: topSites, + messageId: `${props.message_id}_${order}_${screen.id}`, + UTMTerm: props.utm_term, + flowParams: flowParams, + activeTheme: activeTheme, + initialTheme: initialTheme, + setActiveTheme: setActiveTheme, + setInitialTheme: setInitialTheme, + activeMultiSelect: activeMultiSelect, + setActiveMultiSelect: setActiveMultiSelect, + autoAdvance: screen.auto_advance, + negotiatedLanguage: negotiatedLanguage, + langPackInstallPhase: langPackInstallPhase + }) : null; + }))); +}; +const SecondaryCTA = props => { + var _props$content$second; + + let targetElement = props.position ? `secondary_button_${props.position}` : `secondary_button`; + const buttonStyling = (_props$content$second = props.content.secondary_button) !== null && _props$content$second !== void 0 && _props$content$second.has_arrow_icon ? `secondary text-link arrow-icon` : `secondary text-link`; + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: props.position ? `secondary-cta ${props.position}` : "secondary-cta" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: props.content[targetElement].text + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: props.content[targetElement].label + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { + className: buttonStyling, + value: targetElement, + onClick: props.handleAction + }))); +}; +const StepsIndicator = props => { + let steps = []; + + for (let i = 0; i < props.totalNumberOfScreens; i++) { + let className = `${i === props.order ? "current" : ""} ${i < props.order ? "complete" : ""}`; + steps.push( /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + key: i, + className: `indicator ${className}`, + role: "presentation" + })); + } + + return steps; +}; +const ProgressBar = ({ + step, + previousStep, + totalNumberOfScreens +}) => { + const [progress, setProgress] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(previousStep / totalNumberOfScreens); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + // We don't need to hook any dependencies because any time the step changes, + // the screen's entire DOM tree will be re-rendered. + setProgress(step / totalNumberOfScreens); + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "indicator", + role: "presentation", + style: { + "--progress-bar-progress": `${progress * 100}%` + } + }); +}; +class WelcomeScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) { + constructor(props) { + super(props); + this.handleAction = this.handleAction.bind(this); + } + + handleOpenURL(action, flowParams, UTMTerm) { + let { + type, + data + } = action; + + if (type === "SHOW_FIREFOX_ACCOUNTS") { + let params = { ..._asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_5__.BASE_PARAMS, + utm_term: `aboutwelcome-${UTMTerm}-screen` + }; + + if (action.addFlowParams && flowParams) { + params = { ...params, + ...flowParams + }; + } + + data = { ...data, + extraParams: params + }; + } else if (type === "OPEN_URL") { + let url = new URL(data.args); + (0,_asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_5__.addUtmParams)(url, `aboutwelcome-${UTMTerm}-screen`); + + if (action.addFlowParams && flowParams) { + url.searchParams.append("device_id", flowParams.deviceId); + url.searchParams.append("flow_id", flowParams.flowId); + url.searchParams.append("flow_begin_time", flowParams.flowBeginTime); + } + + data = { ...data, + args: url.toString() + }; + } + + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction({ + type, + data + }); + } + + async handleAction(event) { + let { + props + } = this; + const value = event.currentTarget.value ?? event.currentTarget.getAttribute("value"); + let targetContent = props.content[value] || props.content.tiles || props.content.languageSwitcher; + + if (!(targetContent && targetContent.action)) { + return; + } // Send telemetry before waiting on actions + + + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, value, event.name); // Send additional telemetry if a messaging surface like feature callout is + // dismissed via the dismiss button. Other causes of dismissal will be + // handled separately by the messaging surface's own code. + + if (value === "dismiss_button" && !event.name) { + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendDismissTelemetry(props.messageId, value); + } + + let { + action + } = targetContent; + + if (action.collectSelect) { + // Populate MULTI_ACTION data actions property with selected checkbox actions from tiles data + action.data = { + actions: this.props.activeMultiSelect.map(id => { + var _props$content, _props$content$tiles, _props$content$tiles$; + + return (_props$content = props.content) === null || _props$content === void 0 ? void 0 : (_props$content$tiles = _props$content.tiles) === null || _props$content$tiles === void 0 ? void 0 : (_props$content$tiles$ = _props$content$tiles.data.find(ckbx => ckbx.id === id)) === null || _props$content$tiles$ === void 0 ? void 0 : _props$content$tiles$.action; + }) + }; // Send telemetry with selected checkbox ids + + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, props.activeMultiSelect, "SELECT_CHECKBOX"); + } + + if (["OPEN_URL", "SHOW_FIREFOX_ACCOUNTS"].includes(action.type)) { + this.handleOpenURL(action, props.flowParams, props.UTMTerm); + } else if (action.type) { + var _action$data; + + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.handleUserAction(action); // Wait until migration closes to complete the action + + if (action.type === "SHOW_MIGRATION_WIZARD" || action.type === "MULTI_ACTION" && action !== null && action !== void 0 && (_action$data = action.data) !== null && _action$data !== void 0 && _action$data.actions.find(subAction => subAction.type === "SHOW_MIGRATION_WIZARD")) { + await window.AWWaitForMigrationClose(); + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, "migrate_close"); + } + } // A special tiles.action.theme value indicates we should use the event's value vs provided value. + + + if (action.theme) { + let themeToUse = action.theme === "<event>" ? event.currentTarget.value : this.props.initialTheme || action.theme; + this.props.setActiveTheme(themeToUse); + window.AWSelectTheme(themeToUse); + } // If the action has persistActiveTheme: true, we set the initial theme to the currently active theme + // so that it can be reverted to in the event that the user navigates away from the screen + + + if (action.persistActiveTheme) { + this.props.setInitialTheme(this.props.activeTheme); + } + + if (action.navigate) { + props.navigate(); + } + } + + render() { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_3__.MultiStageProtonScreen, { + content: this.props.content, + id: this.props.id, + order: this.props.order, + stepOrder: this.props.stepOrder, + previousOrder: this.props.previousOrder, + activeTheme: this.props.activeTheme, + activeMultiSelect: this.props.activeMultiSelect, + setActiveMultiSelect: this.props.setActiveMultiSelect, + totalNumberOfScreens: this.props.totalNumberOfScreens, + appAndSystemLocaleInfo: this.props.appAndSystemLocaleInfo, + negotiatedLanguage: this.props.negotiatedLanguage, + langPackInstallPhase: this.props.langPackInstallPhase, + handleAction: this.handleAction, + messageId: this.props.messageId, + isFirstCenteredScreen: this.props.isFirstCenteredScreen, + isLastCenteredScreen: this.props.isLastCenteredScreen, + startsWithCorner: this.props.startsWithCorner, + autoAdvance: this.props.autoAdvance + }); + } + +} + +/***/ }), +/* 5 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Localized": () => (/* binding */ Localized) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* 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/. */ + +const CONFIGURABLE_STYLES = ["color", "fontSize", "fontWeight", "letterSpacing", "lineHeight", "marginBlock", "marginInline", "paddingBlock", "paddingInline"]; +const ZAP_SIZE_THRESHOLD = 160; +/** + * Based on the .text prop, localizes an inner element if a string_id + * is provided, OR renders plain text, OR hides it if nothing is provided. + * Allows configuring of some styles including zap underline and color. + * + * Examples: + * + * Localized text + * ftl: + * title = Welcome + * jsx: + * <Localized text={{string_id: "title"}}><h1 /></Localized> + * output: + * <h1 data-l10n-id="title">Welcome</h1> + * + * Unlocalized text + * jsx: + * <Localized text="Welcome"><h1 /></Localized> + * <Localized text={{raw: "Welcome"}}><h1 /></Localized> + * output: + * <h1>Welcome</h1> + */ + +const Localized = ({ + text, + children +}) => { + // Dynamically determine the size of the zap style. + const zapRef = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createRef(); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + const { + current + } = zapRef; + if (current) requestAnimationFrame(() => current === null || current === void 0 ? void 0 : current.classList.replace("short", current.getBoundingClientRect().width > ZAP_SIZE_THRESHOLD ? "long" : "short")); + }); // Skip rendering of children with no text. + + if (!text) { + return null; + } // Allow augmenting existing child container properties. + + + const props = { + children: [], + className: "", + style: {}, + ...(children === null || children === void 0 ? void 0 : children.props) + }; // Support nested Localized by starting with their children. + + const textNodes = Array.isArray(props.children) ? props.children : [props.children]; // Pick desired fluent or raw/plain text to render. + + if (text.string_id) { + // Set the key so React knows not to reuse when switching to plain text. + props.key = text.string_id; + props["data-l10n-id"] = text.string_id; + if (text.args) props["data-l10n-args"] = JSON.stringify(text.args); + } else if (text.raw) { + textNodes.push(text.raw); + } else if (typeof text === "string") { + textNodes.push(text); + } // Add zap style and content in a way that allows fluent to insert too. + + + if (text.zap) { + props.className += " welcomeZap"; + textNodes.push( /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { + className: "short zap", + "data-l10n-name": "zap", + ref: zapRef + }, text.zap)); + } // Apply certain configurable styles. + + + CONFIGURABLE_STYLES.forEach(style => { + if (text[style] !== undefined) props.style[style] = text[style]; + }); + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().cloneElement( // Provide a default container for the text if necessary. + children ?? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null), props, // Conditionally pass in as void elements can't accept empty array. + textNodes.length ? textNodes : null); +}; + +/***/ }), +/* 6 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "MultiStageProtonScreen": () => (/* binding */ MultiStageProtonScreen), +/* harmony export */ "ProtonScreenActionButtons": () => (/* binding */ ProtonScreenActionButtons), +/* harmony export */ "ProtonScreen": () => (/* binding */ ProtonScreen) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); +/* harmony import */ var _MRColorways__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7); +/* harmony import */ var _MobileDownloads__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8); +/* harmony import */ var _MultiSelect__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9); +/* harmony import */ var _Themes__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(10); +/* harmony import */ var _MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(4); +/* harmony import */ var _LanguageSwitcher__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(11); +/* harmony import */ var _CTAParagraph__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(12); +/* harmony import */ var _HeroImage__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(13); +/* harmony import */ var _OnboardingVideo__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(14); +/* harmony import */ var _AdditionalCTA__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(15); +/* 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/. */ + + + + + + + + + + + + +const MultiStageProtonScreen = props => { + const { + autoAdvance, + handleAction, + order + } = props; + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + if (autoAdvance) { + const timer = setTimeout(() => { + handleAction({ + currentTarget: { + value: autoAdvance + }, + name: "AUTO_ADVANCE" + }); + }, 20000); + return () => clearTimeout(timer); + } + + return () => {}; + }, [autoAdvance, handleAction, order]); + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(ProtonScreen, { + content: props.content, + id: props.id, + order: props.order, + activeTheme: props.activeTheme, + activeMultiSelect: props.activeMultiSelect, + setActiveMultiSelect: props.setActiveMultiSelect, + totalNumberOfScreens: props.totalNumberOfScreens, + handleAction: props.handleAction, + isFirstCenteredScreen: props.isFirstCenteredScreen, + isLastCenteredScreen: props.isLastCenteredScreen, + stepOrder: props.stepOrder, + previousOrder: props.previousOrder, + autoAdvance: props.autoAdvance, + isRtamo: props.isRtamo, + addonName: props.addonName, + isTheme: props.isTheme, + iconURL: props.iconURL, + messageId: props.messageId, + negotiatedLanguage: props.negotiatedLanguage, + langPackInstallPhase: props.langPackInstallPhase + }); +}; +const ProtonScreenActionButtons = props => { + var _content$checkbox, _content$primary_butt, _content$primary_butt2; + + const { + content, + addonName + } = props; + const defaultValue = (_content$checkbox = content.checkbox) === null || _content$checkbox === void 0 ? void 0 : _content$checkbox.defaultValue; + const [isChecked, setIsChecked] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(defaultValue || false); + + if (!content.primary_button && !content.secondary_button && !content.additional_button) { + return null; + } + + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: `action-buttons ${content.additional_button ? "additional-cta-container" : ""}` + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: (_content$primary_butt = content.primary_button) === null || _content$primary_butt === void 0 ? void 0 : _content$primary_butt.label + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { + className: "primary" // Whether or not the checkbox is checked determines which action + // should be handled. By setting value here, we indicate to + // this.handleAction() where in the content tree it should take + // the action to execute from. + , + value: isChecked ? "checkbox" : "primary_button", + disabled: ((_content$primary_butt2 = content.primary_button) === null || _content$primary_butt2 === void 0 ? void 0 : _content$primary_butt2.disabled) === true, + onClick: props.handleAction, + "data-l10n-args": addonName ? JSON.stringify({ + "addon-name": addonName + }) : "" + })), content.additional_button ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_AdditionalCTA__WEBPACK_IMPORTED_MODULE_11__.AdditionalCTA, { + content: content, + handleAction: props.handleAction + }) : null, content.checkbox ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "checkbox-container" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { + type: "checkbox", + id: "action-checkbox", + checked: isChecked, + onChange: () => { + setIsChecked(!isChecked); + } + }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: content.checkbox.label + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { + htmlFor: "action-checkbox" + }))) : null, content.secondary_button ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_6__.SecondaryCTA, { + content: content, + handleAction: props.handleAction + }) : null); +}; +class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) { + componentDidMount() { + this.mainContentHeader.focus(); + } + + getScreenClassName(isFirstCenteredScreen, isLastCenteredScreen, includeNoodles, isVideoOnboarding) { + const screenClass = `screen-${this.props.order % 2 !== 0 ? 1 : 2}`; + if (isVideoOnboarding) return "with-video"; + return `${isFirstCenteredScreen ? `dialog-initial` : ``} ${isLastCenteredScreen ? `dialog-last` : ``} ${includeNoodles ? `with-noodles` : ``} ${screenClass}`; + } + + renderLogo({ + imageURL = "chrome://branding/content/about-logo.svg", + darkModeImageURL, + reducedMotionImageURL, + darkModeReducedMotionImageURL, + alt = "", + height + }) { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("picture", { + className: "logo-container" + }, darkModeReducedMotionImageURL ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("source", { + srcSet: darkModeReducedMotionImageURL, + media: "(prefers-color-scheme: dark) and (prefers-reduced-motion: reduce)" + }) : null, darkModeImageURL ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("source", { + srcSet: darkModeImageURL, + media: "(prefers-color-scheme: dark)" + }) : null, reducedMotionImageURL ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("source", { + srcSet: reducedMotionImageURL, + media: "(prefers-reduced-motion: reduce)" + }) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", { + className: "brand-logo", + style: { + height + }, + src: imageURL, + alt: alt, + role: alt ? null : "presentation" + })); + } + + renderContentTiles() { + const { + content + } = this.props; + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, content.tiles && content.tiles.type === "colorway" && content.tiles.colorways ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MRColorways__WEBPACK_IMPORTED_MODULE_2__.Colorways, { + content: content, + activeTheme: this.props.activeTheme, + handleAction: this.props.handleAction + }) : null, content.tiles && content.tiles.type === "theme" && content.tiles.data ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_Themes__WEBPACK_IMPORTED_MODULE_5__.Themes, { + content: content, + activeTheme: this.props.activeTheme, + handleAction: this.props.handleAction + }) : null, content.tiles && content.tiles.type === "mobile_downloads" && content.tiles.data ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MobileDownloads__WEBPACK_IMPORTED_MODULE_3__.MobileDownloads, { + data: content.tiles.data, + handleAction: this.props.handleAction + }) : null, content.tiles && content.tiles.type === "multiselect" && content.tiles.data ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiSelect__WEBPACK_IMPORTED_MODULE_4__.MultiSelect, { + content: content, + activeMultiSelect: this.props.activeMultiSelect, + setActiveMultiSelect: this.props.setActiveMultiSelect, + handleAction: this.props.handleAction + }) : null); + } + + renderNoodles() { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "noodle orange-L" + }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "noodle purple-C" + }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "noodle solid-L" + }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "noodle outline-L" + }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "noodle yellow-circle" + })); + } + + renderLanguageSwitcher() { + return this.props.content.languageSwitcher ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_LanguageSwitcher__WEBPACK_IMPORTED_MODULE_7__.LanguageSwitcher, { + content: this.props.content, + handleAction: this.props.handleAction, + negotiatedLanguage: this.props.negotiatedLanguage, + langPackInstallPhase: this.props.langPackInstallPhase, + messageId: this.props.messageId + }) : null; + } + + renderDismissButton() { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { + className: "dismiss-button", + onClick: this.props.handleAction, + value: "dismiss_button", + "data-l10n-id": "spotlight-dialog-close-button" + }); + } + + renderStepsIndicator() { + const currentStep = (this.props.order ?? 0) + 1; + const previousStep = (this.props.previousOrder ?? -1) + 1; + const { + content, + totalNumberOfScreens: total + } = this.props; + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + id: "steps", + className: `steps${content.progress_bar ? " progress-bar" : ""}`, + "data-l10n-id": "onboarding-welcome-steps-indicator-label", + "data-l10n-args": JSON.stringify({ + current: currentStep, + total: total ?? 0 + }), + "data-l10n-attrs": "aria-label", + role: "progressbar", + "aria-valuenow": currentStep, + "aria-valuemin": 1, + "aria-valuemax": total + }, content.progress_bar ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_6__.ProgressBar, { + step: currentStep, + previousStep: previousStep, + totalNumberOfScreens: total + }) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_6__.StepsIndicator, { + order: this.props.stepOrder, + totalNumberOfScreens: total + })); + } + + renderSecondarySection(content) { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "section-secondary", + style: content.background ? { + background: content.background, + "--mr-secondary-background-position-y": content.split_narrow_bkg_position + } : {} + }, content.image_alt_text ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "sr-only image-alt", + role: "img", + "data-l10n-id": content.image_alt_text.string_id + }) : null, content.hero_image ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_HeroImage__WEBPACK_IMPORTED_MODULE_9__.HeroImage, { + url: content.hero_image.url + }) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "message-text" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "spacer-top" + }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: content.hero_text + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h1", null)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "spacer-bottom" + })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: content.help_text + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { + className: "attrib-text" + })))); + } + + render() { + var _this$props$appAndSys, _this$props$messageId; + + const { + autoAdvance, + content, + isRtamo, + isTheme, + isFirstCenteredScreen, + isLastCenteredScreen + } = this.props; + const includeNoodles = content.has_noodles; // The default screen position is "center" + + const isCenterPosition = content.position === "center" || !content.position; + const hideStepsIndicator = autoAdvance || (content === null || content === void 0 ? void 0 : content.video_container) || isFirstCenteredScreen && isLastCenteredScreen; + const textColorClass = content.text_color ? `${content.text_color}-text` : ""; // Assign proton screen style 'screen-1' or 'screen-2' to centered screens + // by checking if screen order is even or odd. + + const screenClassName = isCenterPosition ? this.getScreenClassName(isFirstCenteredScreen, isLastCenteredScreen, includeNoodles, content === null || content === void 0 ? void 0 : content.video_container) : ""; + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("main", { + className: `screen ${this.props.id || ""} ${screenClassName} ${textColorClass}`, + role: "alertdialog", + pos: content.position || "center", + tabIndex: "-1", + "aria-labelledby": "mainContentHeader", + ref: input => { + this.mainContentHeader = input; + } + }, isCenterPosition ? null : this.renderSecondarySection(content), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "section-main" + }, content.secondary_button_top ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_6__.SecondaryCTA, { + content: content, + handleAction: this.props.handleAction, + position: "top" + }) : null, includeNoodles ? this.renderNoodles() : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: `main-content ${hideStepsIndicator ? "no-steps" : ""}`, + style: content.background && isCenterPosition ? { + background: content.background + } : {} + }, content.dismiss_button ? this.renderDismissButton() : null, content.logo ? this.renderLogo(content.logo) : null, isRtamo ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "rtamo-icon" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", { + className: `${isTheme ? "rtamo-theme-icon" : "brand-logo"}`, + src: this.props.iconURL, + role: "presentation", + alt: "" + })) : null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "main-content-inner" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: `welcome-text ${content.title_style || ""}` + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: content.title + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h1", { + id: "mainContentHeader" + })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: content.subtitle + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h2", { + "data-l10n-args": JSON.stringify({ + "addon-name": this.props.addonName, + ...((_this$props$appAndSys = this.props.appAndSystemLocaleInfo) === null || _this$props$appAndSys === void 0 ? void 0 : _this$props$appAndSys.displayNames) + }), + "aria-flowto": (_this$props$messageId = this.props.messageId) !== null && _this$props$messageId !== void 0 && _this$props$messageId.includes("FEATURE_TOUR") ? "steps" : "" + })), content.cta_paragraph ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_CTAParagraph__WEBPACK_IMPORTED_MODULE_8__.CTAParagraph, { + content: content.cta_paragraph, + handleAction: this.props.handleAction + }) : null), content.video_container ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_OnboardingVideo__WEBPACK_IMPORTED_MODULE_10__.OnboardingVideo, { + content: content.video_container, + handleAction: this.props.handleAction + }) : null, this.renderContentTiles(), this.renderLanguageSwitcher(), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(ProtonScreenActionButtons, { + content: content, + addonName: this.props.addonName, + handleAction: this.props.handleAction + })), hideStepsIndicator ? null : this.renderStepsIndicator()))); + } + +} + +/***/ }), +/* 7 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "ColorwayDescription": () => (/* binding */ ColorwayDescription), +/* harmony export */ "computeColorWay": () => (/* binding */ computeColorWay), +/* harmony export */ "computeVariationIndex": () => (/* binding */ computeVariationIndex), +/* harmony export */ "Colorways": () => (/* binding */ Colorways) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); +/* 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/. */ + + +const ColorwayDescription = props => { + const { + colorway + } = props; + + if (!colorway) { + return null; + } + + const { + label, + description + } = colorway; + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: description + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "colorway-text", + "data-l10n-args": JSON.stringify({ + colorwayName: label + }) + })); +}; // Return colorway as "default" for default theme variations Automatic, Light, Dark, +// Alpenglow theme and legacy colorways which is not supported in Colorway picker. +// For themes other then default, theme names exist in +// format colorway-variationId inside LIGHT_WEIGHT_THEMES in AboutWelcomeParent + +function computeColorWay(themeName, systemVariations) { + return !themeName || themeName === "alpenglow" || systemVariations.includes(themeName) ? "default" : themeName.split("-")[0]; +} // Set variationIndex based off activetheme value e.g. 'light', 'expressionist-soft' + +function computeVariationIndex(themeName, systemVariations, variations, defaultVariationIndex) { + // Check if themeName is in systemVariations, if yes choose variationIndex by themeName + let index = systemVariations.findIndex(theme => theme === themeName); + + if (index >= 0) { + return index; + } // If themeName is one of the colorways, select variation index from colorways + + + let variation = themeName === null || themeName === void 0 ? void 0 : themeName.split("-")[1]; + index = variations.findIndex(element => element === variation); + + if (index >= 0) { + return index; + } + + return defaultVariationIndex; +} +function Colorways(props) { + let { + colorways, + darkVariation, + defaultVariationIndex, + systemVariations, + variations + } = props.content.tiles; + let hasReverted = false; // Active theme id from JSON e.g. "expressionist" + + const activeId = computeColorWay(props.activeTheme, systemVariations); + const [colorwayId, setState] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(activeId); + const [variationIndex, setVariationIndex] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(defaultVariationIndex); + + function revertToDefaultTheme() { + if (hasReverted) return; // Spoofing an event with current target value of "navigate_away" + // helps the handleAction method to read the colorways theme as "revert" + // which causes the initial theme to be activated. + // The "navigate_away" action is set in content in the colorways screen JSON config. + // Any value in the JSON for theme will work, provided it is not `<event>`. + + const event = { + currentTarget: { + value: "navigate_away" + } + }; + props.handleAction(event); + hasReverted = true; + } // Revert to default theme if the user navigates away from the page or spotlight modal + // before clicking on the primary button to officially set theme. + + + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + addEventListener("beforeunload", revertToDefaultTheme); + addEventListener("pagehide", revertToDefaultTheme); + return () => { + removeEventListener("beforeunload", revertToDefaultTheme); + removeEventListener("pagehide", revertToDefaultTheme); + }; + }); // Update state any time activeTheme changes. + + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + setState(computeColorWay(props.activeTheme, systemVariations)); + setVariationIndex(computeVariationIndex(props.activeTheme, systemVariations, variations, defaultVariationIndex)); // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.activeTheme]); //select a random colorway + + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + //We don't want the default theme to be selected + const randomIndex = Math.floor(Math.random() * (colorways.length - 1)) + 1; + const randomColorwayId = colorways[randomIndex].id; // Change the variation to be the dark variation if configured and dark. + // Additional colorway changes will remain dark while system is unchanged. + + if (darkVariation !== undefined && window.matchMedia("(prefers-color-scheme: dark)").matches) { + variations[variationIndex] = variations[darkVariation]; + } + + const value = `${randomColorwayId}-${variations[variationIndex]}`; + props.handleAction({ + currentTarget: { + value + } + }); // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "tiles-theme-container" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("fieldset", { + className: "tiles-theme-section" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: props.content.subtitle + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("legend", { + className: "sr-only" + })), colorways.map(({ + id, + label, + tooltip + }) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + key: id + label, + text: typeof tooltip === "object" ? tooltip : {} + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { + className: "theme", + title: label, + "data-l10n-args": JSON.stringify({ + colorwayName: label + }) + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: typeof tooltip === "object" ? tooltip : {} + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { + className: "sr-only colorway label", + id: `${id}-label`, + "data-l10n-args": JSON.stringify({ + colorwayName: tooltip + }) + })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: typeof label === "object" ? label : {} + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { + type: "radio", + "data-colorway": id, + name: "theme", + value: id === "default" ? systemVariations[variationIndex] : `${id}-${variations[variationIndex]}`, + checked: colorwayId === id, + className: "sr-only input", + onClick: props.handleAction, + "data-l10n-args": JSON.stringify({ + colorwayName: label + }), + "aria-labelledby": `${id}-label` + })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: `icon colorway ${colorwayId === id ? "selected" : ""} ${id}` + })))))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(ColorwayDescription, { + colorway: colorways.find(colorway => colorway.id === activeId) + })); +} + +/***/ }), +/* 8 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "MarketplaceButtons": () => (/* binding */ MarketplaceButtons), +/* harmony export */ "MobileDownloads": () => (/* binding */ MobileDownloads) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); +/* 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/. */ + + +const MarketplaceButtons = props => { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("ul", { + className: "mobile-download-buttons" + }, props.buttons.includes("ios") ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("li", { + className: "ios" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { + "data-l10n-id": "spotlight-ios-marketplace-button", + value: "ios", + onClick: props.handleAction + })) : null, props.buttons.includes("android") ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("li", { + className: "android" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { + "data-l10n-id": "spotlight-android-marketplace-button", + value: "android", + onClick: props.handleAction + })) : null); +}; +const MobileDownloads = props => { + const { + QR_code: QRCode + } = props.data; + const showEmailLink = props.data.email && window.AWSendToDeviceEmailsSupported(); + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "mobile-downloads" + }, QRCode ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", { + "data-l10n-id": QRCode.alt_text.string_id ? QRCode.alt_text.string_id : null, + className: "qr-code-image", + alt: typeof QRCode.alt_text === "string" ? QRCode.alt_text : "", + src: QRCode.image_url + }) : null, showEmailLink ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: props.data.email.link_text + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { + className: "email-link", + value: "email_link", + onClick: props.handleAction + }))) : null, props.data.marketplace_buttons ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(MarketplaceButtons, { + buttons: props.data.marketplace_buttons, + handleAction: props.handleAction + }) : null); +}; + +/***/ }), +/* 9 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "MultiSelect": () => (/* binding */ MultiSelect) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); +/* 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/. */ + + +const MultiSelect = props => { + let handleChange = event => { + if (event.currentTarget.checked) { + props.setActiveMultiSelect([...props.activeMultiSelect, event.currentTarget.value]); + } else { + props.setActiveMultiSelect(props.activeMultiSelect.filter(id => id !== event.currentTarget.value)); + } + }; + + let { + data + } = props.content.tiles; // When screen renders for first time, update state + // with checkbox ids that has defaultvalue true + + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + if (!props.activeMultiSelect) { + props.setActiveMultiSelect(data.map(item => item.defaultValue && item.id).filter(item => !!item)); + } // eslint-disable-next-line react-hooks/exhaustive-deps + + }, []); + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "multi-select-container" + }, props.content.tiles.data.map(({ + label, + id + }) => { + var _props$activeMultiSel; + + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + key: id + label, + className: "checkbox-container" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { + type: "checkbox", + id: id, + value: id, + checked: (_props$activeMultiSel = props.activeMultiSelect) === null || _props$activeMultiSel === void 0 ? void 0 : _props$activeMultiSel.includes(id), + onChange: handleChange + }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: label + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { + htmlFor: id + }))); + })); +}; + +/***/ }), +/* 10 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Themes": () => (/* binding */ Themes) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); +/* 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/. */ + + +const Themes = props => { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "tiles-theme-container" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("fieldset", { + className: "tiles-theme-section" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: props.content.subtitle + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("legend", { + className: "sr-only" + })), props.content.tiles.data.map(({ + theme, + label, + tooltip, + description + }) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + key: theme + label, + text: typeof tooltip === "object" ? tooltip : {} + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { + className: "theme", + title: theme + label + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: typeof description === "object" ? description : {} + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", { + type: "radio", + value: theme, + name: "theme", + checked: theme === props.activeTheme, + className: "sr-only input", + onClick: props.handleAction + })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: `icon ${theme === props.activeTheme ? " selected" : ""} ${theme}` + }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: label + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "text" + })))))))); +}; + +/***/ }), +/* 11 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "useLanguageSwitcher": () => (/* binding */ useLanguageSwitcher), +/* harmony export */ "LanguageSwitcher": () => (/* binding */ LanguageSwitcher) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); +/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3); +/* 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/. */ + + + +/** + * The language switcher implements a hook that should be placed at a higher level + * than the actual language switcher component, as it needs to preemptively fetch + * and install langpacks for the user if there is a language mismatch screen. + */ + +function useLanguageSwitcher(appAndSystemLocaleInfo, screens, screenIndex, setScreenIndex) { + const languageMismatchScreenIndex = screens.findIndex(({ + id + }) => id === "AW_LANGUAGE_MISMATCH"); + const screen = screens[languageMismatchScreenIndex]; // Ensure fluent messages have the negotiatedLanguage args set, as they are rendered + // before the negotiatedLanguage is known. If the arg isn't present then Firefox will + // crash in development mode. + + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + var _screen$content; + + if (screen !== null && screen !== void 0 && (_screen$content = screen.content) !== null && _screen$content !== void 0 && _screen$content.languageSwitcher) { + for (const text of Object.values(screen.content.languageSwitcher)) { + if (text !== null && text !== void 0 && text.args && text.args.negotiatedLanguage === undefined) { + text.args.negotiatedLanguage = ""; + } + } + } + }, [screen]); // If there is a mismatch, then Firefox can negotiate a better langpack to offer + // the user. + + const [negotiatedLanguage, setNegotiatedLanguage] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function getNegotiatedLanguage() { + if (!appAndSystemLocaleInfo) { + return; + } + + if (appAndSystemLocaleInfo.matchType !== "language-mismatch") { + // There is no language mismatch, so there is no need to negotiate a langpack. + return; + } + + (async () => { + const { + langPack, + langPackDisplayName + } = await window.AWNegotiateLangPackForLanguageMismatch(appAndSystemLocaleInfo); + + if (langPack) { + setNegotiatedLanguage({ + langPackDisplayName, + appDisplayName: appAndSystemLocaleInfo.displayNames.appLanguage, + langPack, + requestSystemLocales: [langPack.target_locale, appAndSystemLocaleInfo.appLocaleRaw], + originalAppLocales: [appAndSystemLocaleInfo.appLocaleRaw] + }); + } else { + setNegotiatedLanguage({ + langPackDisplayName: null, + appDisplayName: null, + langPack: null, + requestSystemLocales: null + }); + } + })(); + }, [appAndSystemLocaleInfo]); + /** + * @type { + * "before-installation" + * | "installing" + * | "installed" + * | "installation-error" + * | "none-available" + * } + */ + + const [langPackInstallPhase, setLangPackInstallPhase] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)("before-installation"); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function ensureLangPackInstalled() { + if (!negotiatedLanguage) { + // There are no negotiated languages to download yet. + return; + } + + setLangPackInstallPhase("installing"); + window.AWEnsureLangPackInstalled(negotiatedLanguage, screen === null || screen === void 0 ? void 0 : screen.content).then(content => { + // Update screen content with strings that might have changed. + screen.content = content; + setLangPackInstallPhase("installed"); + }, error => { + console.error(error); + setLangPackInstallPhase("installation-error"); + }); + }, [negotiatedLanguage]); + const [languageFilteredScreens, setLanguageFilteredScreens] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(screens); + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function filterScreen() { + // Remove the language screen if it exists (already removed for no live + // reload) and we either don't-need-to or can't switch. + if (screen && ((appAndSystemLocaleInfo === null || appAndSystemLocaleInfo === void 0 ? void 0 : appAndSystemLocaleInfo.matchType) !== "language-mismatch" || (negotiatedLanguage === null || negotiatedLanguage === void 0 ? void 0 : negotiatedLanguage.langPack) === null)) { + if (screenIndex > languageMismatchScreenIndex) { + setScreenIndex(screenIndex - 1); + } + + setLanguageFilteredScreens(screens.filter(s => s.id !== "AW_LANGUAGE_MISMATCH")); + } else { + setLanguageFilteredScreens(screens); + } + }, [screens, negotiatedLanguage]); + return { + negotiatedLanguage, + langPackInstallPhase, + languageFilteredScreens + }; +} +/** + * The language switcher is a separate component as it needs to perform some asynchronous + * network actions such as retrieving the list of langpacks available, and downloading + * a new langpack. On a fast connection, this won't be noticeable, but on slow or unreliable + * internet this may fail for a user. + */ + +function LanguageSwitcher(props) { + const { + content, + handleAction, + negotiatedLanguage, + langPackInstallPhase, + messageId + } = props; + const [isAwaitingLangpack, setIsAwaitingLangpack] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false); // Determine the status of the langpack installation. + + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + if (isAwaitingLangpack && langPackInstallPhase !== "installing") { + window.AWSetRequestedLocales(negotiatedLanguage.requestSystemLocales); + requestAnimationFrame(() => { + handleAction( // Simulate the click event. + { + currentTarget: { + value: "download_complete" + } + }); + }); + } + }, [isAwaitingLangpack, langPackInstallPhase]); + let showWaitingScreen = false; + let showPreloadingScreen = false; + let showReadyScreen = false; + + if (isAwaitingLangpack && langPackInstallPhase !== "installed") { + showWaitingScreen = true; + } else if (langPackInstallPhase === "before-installation") { + showPreloadingScreen = true; + } else { + showReadyScreen = true; + } // Use {display: "none"} rather than if statements to prevent layout thrashing with + // the localized text elements rendering as blank, then filling in the text. + + + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "action-buttons language-switcher-container" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + style: { + display: showPreloadingScreen ? "block" : "none" + } + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { + className: "primary", + value: "primary_button", + disabled: true, + type: "button" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", { + className: "language-loader", + src: "chrome://browser/skin/tabbrowser/tab-connecting.png", + alt: "" + }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: content.languageSwitcher.waiting + })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "secondary-cta" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: content.languageSwitcher.skip + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { + value: "decline_waiting", + type: "button", + className: "secondary text-link arrow-icon", + onClick: handleAction + })))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + style: { + display: showWaitingScreen ? "block" : "none" + } + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { + className: "primary", + value: "primary_button", + disabled: true, + type: "button" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", { + className: "language-loader", + src: "chrome://browser/skin/tabbrowser/tab-connecting.png", + alt: "" + }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: content.languageSwitcher.downloading + })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "secondary-cta" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: content.languageSwitcher.cancel + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { + type: "button", + className: "secondary text-link", + onClick: () => { + setIsAwaitingLangpack(false); + handleAction({ + currentTarget: { + value: "cancel_waiting" + } + }); + } + })))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + style: { + display: showReadyScreen ? "block" : "none" + } + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { + className: "primary", + value: "primary_button", + onClick: () => { + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendActionTelemetry(messageId, "download_langpack"); + setIsAwaitingLangpack(true); + } + }, content.languageSwitcher.switch ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: content.languageSwitcher.switch + }) : // This is the localized name from the Intl.DisplayNames API. + negotiatedLanguage === null || negotiatedLanguage === void 0 ? void 0 : negotiatedLanguage.langPackDisplayName)), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { + type: "button", + className: "primary", + value: "decline", + onClick: event => { + window.AWSetRequestedLocales(negotiatedLanguage.originalAppLocales); + handleAction(event); + } + }, content.languageSwitcher.continue ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: content.languageSwitcher.continue + }) : // This is the localized name from the Intl.DisplayNames API. + negotiatedLanguage === null || negotiatedLanguage === void 0 ? void 0 : negotiatedLanguage.appDisplayName)))); +} + +/***/ }), +/* 12 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "CTAParagraph": () => (/* binding */ CTAParagraph) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); +/* 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/. */ + + +const CTAParagraph = props => { + const { + content, + handleAction + } = props; + + if (!(content !== null && content !== void 0 && content.text)) { + return null; + } + + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h2", { + className: "cta-paragraph" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: content.text + }, content.text.string_name && typeof handleAction === "function" ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { + "data-l10n-id": content.text.string_id, + onClick: handleAction, + onKeyUp: event => ["Enter", " "].includes(event.key) ? handleAction(event) : null, + value: "cta_paragraph", + role: "button", + tabIndex: "0" + }, " ", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", { + role: "button", + tabIndex: "0", + "data-l10n-name": content.text.string_name + }, " ")) : null)); +}; + +/***/ }), +/* 13 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "HeroImage": () => (/* binding */ HeroImage) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* 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/. */ + +const HeroImage = props => { + const { + height, + url, + alt + } = props; + + if (!url) { + return null; + } + + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "hero-image" + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("img", { + style: height ? { + height + } : null, + src: url, + alt: alt || "", + role: alt ? null : "presentation" + })); +}; + +/***/ }), +/* 14 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "OnboardingVideo": () => (/* binding */ OnboardingVideo) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* 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/. */ + +const OnboardingVideo = props => { + const vidUrl = props.content.video_url; + const autoplay = props.content.autoPlay; + + const handleVideoAction = event => { + props.handleAction({ + currentTarget: { + value: event + } + }); + }; + + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("video", { + // eslint-disable-line jsx-a11y/media-has-caption + controls: true, + autoPlay: autoplay, + src: vidUrl, + width: "604px", + height: "340px", + onPlay: () => handleVideoAction("video_start"), + onEnded: () => handleVideoAction("video_end") + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("source", { + src: vidUrl + }))); +}; + +/***/ }), +/* 15 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "AdditionalCTA": () => (/* binding */ AdditionalCTA) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); +/* 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/. */ + + +const AdditionalCTA = ({ + content, + handleAction +}) => { + var _content$additional_b, _content$additional_b4, _content$additional_b5; + + let buttonStyle = ""; + + if (!((_content$additional_b = content.additional_button) !== null && _content$additional_b !== void 0 && _content$additional_b.style)) { + buttonStyle = "primary"; + } else { + var _content$additional_b2, _content$additional_b3; + + buttonStyle = ((_content$additional_b2 = content.additional_button) === null || _content$additional_b2 === void 0 ? void 0 : _content$additional_b2.style) === "link" ? "cta-link" : (_content$additional_b3 = content.additional_button) === null || _content$additional_b3 === void 0 ? void 0 : _content$additional_b3.style; + } + + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { + text: (_content$additional_b4 = content.additional_button) === null || _content$additional_b4 === void 0 ? void 0 : _content$additional_b4.label + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { + className: `${buttonStyle} additional-cta`, + onClick: handleAction, + value: "additional_button", + disabled: ((_content$additional_b5 = content.additional_button) === null || _content$additional_b5 === void 0 ? void 0 : _content$additional_b5.disabled) === true + })); +}; + +/***/ }), +/* 16 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "BASE_PARAMS": () => (/* binding */ BASE_PARAMS), +/* harmony export */ "addUtmParams": () => (/* binding */ addUtmParams) +/* harmony export */ }); +/* 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/. */ + +/** + * BASE_PARAMS keys/values can be modified from outside this file + */ +const BASE_PARAMS = { + utm_source: "activity-stream", + utm_campaign: "firstrun", + utm_medium: "referral" +}; +/** + * Takes in a url as a string or URL object and returns a URL object with the + * utm_* parameters added to it. If a URL object is passed in, the paraemeters + * are added to it (the return value can be ignored in that case as it's the + * same object). + */ + +function addUtmParams(url, utmTerm) { + let returnUrl = url; + + if (typeof returnUrl === "string") { + returnUrl = new URL(url); + } + + Object.keys(BASE_PARAMS).forEach(key => { + returnUrl.searchParams.append(key, BASE_PARAMS[key]); + }); + returnUrl.searchParams.append("utm_term", utmTerm); + return returnUrl; +} + +/***/ }), +/* 17 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "ReturnToAMO": () => (/* binding */ ReturnToAMO) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3); +/* harmony import */ var _MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6); +/* harmony import */ var _asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(16); +/* 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/. */ + + + + +class ReturnToAMO extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) { + constructor(props) { + super(props); + this.fetchFlowParams = this.fetchFlowParams.bind(this); + this.handleAction = this.handleAction.bind(this); + } + + async fetchFlowParams() { + if (this.props.metricsFlowUri) { + this.setState({ + flowParams: await _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.fetchFlowParams(this.props.metricsFlowUri) + }); + } + } + + componentDidUpdate() { + this.fetchFlowParams(); + } + + handleAction(event) { + const { + content, + message_id, + url, + utm_term + } = this.props; + let { + action, + source_id + } = content[event.currentTarget.value]; + let { + type, + data + } = action; + + if (type === "INSTALL_ADDON_FROM_URL") { + if (!data) { + return; + } // Set add-on url in action.data.url property from JSON + + + data = { ...data, + url + }; + } else if (type === "SHOW_FIREFOX_ACCOUNTS") { + let params = { ..._asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_3__.BASE_PARAMS, + utm_term: `aboutwelcome-${utm_term}-screen` + }; + + if (action.addFlowParams && this.state.flowParams) { + params = { ...params, + ...this.state.flowParams + }; + } + + data = { ...data, + extraParams: params + }; + } + + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.handleUserAction({ + type, + data + }); + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.sendActionTelemetry(message_id, source_id); + } + + render() { + var _this$props$themeScre; + + const { + content, + type + } = this.props; + + if (!content) { + return null; + } + + if (content !== null && content !== void 0 && content.primary_button.label) { + content.primary_button.label.string_id = type.includes("theme") ? "return-to-amo-add-theme-label" : "mr1-return-to-amo-add-extension-label"; + } // For experiments, when needed below rendered UI allows settings hard coded strings + // directly inside JSON except for ReturnToAMOText which picks add-on name and icon from fluent string + + + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { + className: "outer-wrapper onboardingContainer proton", + style: content.backdrop ? { + background: content.backdrop + } : {} + }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_2__.MultiStageProtonScreen, { + content: content, + isRtamo: true, + isTheme: type.includes("theme"), + id: this.props.messageId, + order: this.props.order, + totalNumberOfScreens: this.props.totalNumberOfScreens, + autoAdvance: this.props.auto_advance, + iconURL: type.includes("theme") ? (_this$props$themeScre = this.props.themeScreenshots[0]) === null || _this$props$themeScre === void 0 ? void 0 : _this$props$themeScre.url : this.props.iconURL, + addonName: this.props.name, + handleAction: this.handleAction, + addExtension: this.onClickAddExtension + })); + } + +} +ReturnToAMO.defaultProps = _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_1__.DEFAULT_RTAMO_CONTENT; + +/***/ }) +/******/ ]); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat get default export */ +/******/ (() => { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = (module) => { +/******/ var getter = module && module.__esModule ? +/******/ () => (module['default']) : +/******/ () => (module); +/******/ __webpack_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +(() => { +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2); +/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3); +/* harmony import */ var _components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4); +/* harmony import */ var _components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(17); +function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } + +/* 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/. */ + + + + + + +class AboutWelcome extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponent) { + constructor(props) { + super(props); + this.state = { + metricsFlowUri: null + }; + this.fetchFxAFlowUri = this.fetchFxAFlowUri.bind(this); + } + + async fetchFxAFlowUri() { + var _window$AWGetFxAMetri, _window; + + this.setState({ + metricsFlowUri: await ((_window$AWGetFxAMetri = (_window = window).AWGetFxAMetricsFlowURI) === null || _window$AWGetFxAMetri === void 0 ? void 0 : _window$AWGetFxAMetri.call(_window)) + }); + } + + componentDidMount() { + if (!this.props.skipFxA) { + this.fetchFxAFlowUri(); + } // Record impression with performance data after allowing the page to load + + + const recordImpression = domState => { + const { + domComplete, + domInteractive + } = performance.getEntriesByType("navigation").pop(); + _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.sendImpressionTelemetry(this.props.messageId, { + domComplete, + domInteractive, + mountStart: performance.getEntriesByName("mount").pop().startTime, + domState, + source: this.props.UTMTerm + }); + }; + + if (document.readyState === "complete") { + // Page might have already triggered a load event because it waited for async data, + // e.g., attribution, so the dom load timing could be of a empty content + // with domState in telemetry captured as 'complete' + recordImpression(document.readyState); + } else { + window.addEventListener("load", () => recordImpression("load"), { + once: true + }); + } // Captures user has seen about:welcome by setting + // firstrun.didSeeAboutWelcome pref to true and capturing welcome UI unique messageId + + + window.AWSendToParent("SET_WELCOME_MESSAGE_SEEN", this.props.messageId); + } + + render() { + const { + props + } = this; + + if (props.template === "return_to_amo") { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_components_ReturnToAMO__WEBPACK_IMPORTED_MODULE_4__.ReturnToAMO, { + message_id: props.messageId, + type: props.type, + name: props.name, + url: props.url, + iconURL: props.iconURL, + themeScreenshots: props.screenshots, + metricsFlowUri: this.state.metricsFlowUri + }); + } + + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__.MultiStageAboutWelcome, { + message_id: props.messageId, + screens: props.screens, + updateHistory: !props.disableHistoryUpdates, + metricsFlowUri: this.state.metricsFlowUri, + utm_term: props.UTMTerm, + transitions: props.transitions, + backdrop: props.backdrop, + startScreen: props.startScreen || 0, + appAndSystemLocaleInfo: props.appAndSystemLocaleInfo + }); + } + +} // Computes messageId and UTMTerm info used in telemetry + + +function ComputeTelemetryInfo(welcomeContent, experimentId, branchId) { + let messageId = welcomeContent.template === "return_to_amo" ? `RTAMO_DEFAULT_WELCOME_${welcomeContent.type.toUpperCase()}` : "DEFAULT_ID"; + let UTMTerm = "default"; + + if (welcomeContent.id) { + messageId = welcomeContent.id.toUpperCase(); + } + + if (experimentId && branchId) { + UTMTerm = `${experimentId}-${branchId}`.toLowerCase(); + } + + return { + messageId, + UTMTerm + }; +} + +async function retrieveRenderContent() { + // Feature config includes RTAMO attribution data if exists + // else below data in order specified + // user prefs + // experiment data + // defaults + let featureConfig = await window.AWGetFeatureConfig(); + let { + messageId, + UTMTerm + } = ComputeTelemetryInfo(featureConfig, featureConfig.slug, featureConfig.branch && featureConfig.branch.slug); + return { + featureConfig, + messageId, + UTMTerm + }; +} + +async function mount() { + let { + featureConfig: aboutWelcomeProps, + messageId, + UTMTerm + } = await retrieveRenderContent(); + react_dom__WEBPACK_IMPORTED_MODULE_1___default().render( /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(AboutWelcome, _extends({ + messageId: messageId, + UTMTerm: UTMTerm + }, aboutWelcomeProps)), document.getElementById("root")); +} + +performance.mark("mount"); +mount(); +})(); + +/******/ })() +;
\ No newline at end of file diff --git a/browser/components/newtab/aboutwelcome/content/aboutwelcome.css b/browser/components/newtab/aboutwelcome/content/aboutwelcome.css new file mode 100644 index 0000000000..d2a40fc396 --- /dev/null +++ b/browser/components/newtab/aboutwelcome/content/aboutwelcome.css @@ -0,0 +1,1644 @@ +/* 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/. */ +.onboardingContainer.featureCallout { + position: absolute; + transition: opacity 0.5s ease; + z-index: 2147483645; +} +.onboardingContainer.featureCallout.hidden { + opacity: 0; + pointer-events: none; +} +.onboardingContainer.featureCallout, .onboardingContainer.featureCallout .outer-wrapper { + height: auto; +} +.onboardingContainer.featureCallout .screen:dir(rtl) { + transform: none; +} +.onboardingContainer.featureCallout .screen[pos=callout] { + z-index: 2147483646; + height: fit-content; + min-height: unset; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); +} +.onboardingContainer.featureCallout .screen[pos=callout] .logo-container { + display: flex; + justify-content: center; +} +.onboardingContainer.featureCallout .screen[pos=callout] .logo-container .brand-logo { + margin: 30px 45px 0; +} +.onboardingContainer.featureCallout .screen[pos=callout] .logo-container .brand-logo:dir(rtl) { + transform: rotateY(180deg); +} +.onboardingContainer.featureCallout .screen[pos=callout] .welcome-text { + align-items: baseline; + text-align: start; + margin-inline: 30px; + padding: 20px 0 0; +} +.onboardingContainer.featureCallout .screen[pos=callout] .welcome-text h1, +.onboardingContainer.featureCallout .screen[pos=callout] .welcome-text h2 { + font-size: 0.8em; + margin: 0; + color: ButtonText; +} +.onboardingContainer.featureCallout .screen[pos=callout] .welcome-text h1 { + font-weight: bold; +} +.onboardingContainer.featureCallout .screen[pos=callout] .welcome-text h2 { + margin-block: 10px; +} +.onboardingContainer.featureCallout .screen[pos=callout] .section-secondary { + display: none; +} +.onboardingContainer.featureCallout .screen[pos=callout] .section-main { + height: fit-content; + width: fit-content; +} +.onboardingContainer.featureCallout .screen[pos=callout] .section-main .main-content { + overflow: hidden; + border: 1px solid #CFCFD8; + box-shadow: 0 2px 14px rgba(58, 57, 68, 0.2); + border-radius: 4px; + padding-block: 0 24px; + width: 25em; + background-color: #FFF; +} +@media (prefers-color-scheme: dark) { + .onboardingContainer.featureCallout .screen[pos=callout] .section-main .main-content { + background-color: #1c1b22; + } +} +.onboardingContainer.featureCallout .screen[pos=callout] .section-main .main-content .steps { + align-self: baseline; + margin: -38px 30px -30px; + padding-block: 0; +} +.onboardingContainer.featureCallout .screen[pos=callout] .section-main .main-content .steps .indicator { + border: 4px solid WindowText; +} +.onboardingContainer.featureCallout .screen[pos=callout] .section-main .main-content .steps .indicator.current { + border-color: LinkText; +} +.onboardingContainer.featureCallout .screen[pos=callout] .section-main .main-content .steps .indicator.current, .onboardingContainer.featureCallout .screen[pos=callout] .section-main .main-content .steps.progress-bar .indicator.complete { + border-color: LinkText; +} +.onboardingContainer.featureCallout .screen[pos=callout] .section-main .main-content .dismiss-button { + font-size: 1em; + position: absolute; + margin-block: 15px 0; + margin-inline: 0 15px; + z-index: 1; + background-color: #FFF; +} +@media (prefers-color-scheme: dark) { + .onboardingContainer.featureCallout .screen[pos=callout] .section-main .main-content .dismiss-button { + background-color: #1c1b22; + } +} +.onboardingContainer.featureCallout .screen[pos=callout] .action-buttons { + margin-block: 0 16px; +} +.onboardingContainer.featureCallout .screen[pos=callout] .action-buttons .primary, +.onboardingContainer.featureCallout .screen[pos=callout] .action-buttons .secondary-cta .secondary { + padding: 4px 16px; + font-size: 0.8em; + height: 2em; +} +.onboardingContainer.featureCallout .screen[pos=callout] .action-buttons .primary { + font-weight: bold; + float: inline-end; + margin-inline: 10px 30px; + padding: 4px 16px; + font-size: 0.8em; + line-height: 16px; + min-height: 32px; + background-color: rgba(207, 207, 216, 0.33); +} +@media (prefers-color-scheme: dark) { + .onboardingContainer.featureCallout .screen[pos=callout] .action-buttons .primary { + background-color: #2b2a33; + } +} +.onboardingContainer.featureCallout .screen[pos=callout] .action-buttons .secondary-cta { + float: inline-end; +} +.onboardingContainer.featureCallout .screen[pos=callout] .action-buttons .primary, +.onboardingContainer.featureCallout .screen[pos=callout] .main-content .dismiss-button { + border-radius: 4px; + border: 0; + cursor: pointer; + color: ButtonText; +} +.onboardingContainer.featureCallout .screen[pos=callout] .action-buttons .primary:hover, +.onboardingContainer.featureCallout .screen[pos=callout] .main-content .dismiss-button:hover { + background-color: #e8e3e4; +} +@media (prefers-color-scheme: dark) { + .onboardingContainer.featureCallout .screen[pos=callout] .action-buttons .primary, +.onboardingContainer.featureCallout .screen[pos=callout] .main-content .dismiss-button { + background-color: #2b2a33; + } + .onboardingContainer.featureCallout .screen[pos=callout] .action-buttons .primary:hover, +.onboardingContainer.featureCallout .screen[pos=callout] .main-content .dismiss-button:hover { + background-color: #363434; + } +} +@media (prefers-contrast) { + .onboardingContainer.featureCallout .screen[pos=callout] .action-buttons .primary, +.onboardingContainer.featureCallout .screen[pos=callout] .main-content .dismiss-button { + border: 1px solid ButtonText; + background-color: #FFF; + } +} +@media (prefers-contrast) and (prefers-color-scheme: dark) { + .onboardingContainer.featureCallout .screen[pos=callout] .action-buttons .primary, +.onboardingContainer.featureCallout .screen[pos=callout] .main-content .dismiss-button { + background-color: #1c1b22; + } +} +@media (prefers-contrast) { + .onboardingContainer.featureCallout .screen[pos=callout] .action-buttons .primary:hover, +.onboardingContainer.featureCallout .screen[pos=callout] .main-content .dismiss-button:hover { + background-color: ButtonText; + color: #FFF; + } +} +@media (prefers-contrast) and (prefers-color-scheme: dark) { + .onboardingContainer.featureCallout .screen[pos=callout] .action-buttons .primary:hover, +.onboardingContainer.featureCallout .screen[pos=callout] .main-content .dismiss-button:hover { + color: #1c1b22; + } +} +.onboardingContainer.featureCallout.callout-arrow::before, .onboardingContainer.featureCallout.callout-arrow::after { + content: ""; + position: absolute; + width: 24px; + height: 24px; + transform: rotate(45deg); +} +.onboardingContainer.featureCallout.callout-arrow:dir(rtl)::before, .onboardingContainer.featureCallout.callout-arrow:dir(rtl)::after { + transform: rotate(315deg); +} +.onboardingContainer.featureCallout.callout-arrow::before { + z-index: 2147483647; + background-color: #FFF; +} +@media (prefers-color-scheme: dark) { + .onboardingContainer.featureCallout.callout-arrow::before { + background-color: #1c1b22; + } +} +.onboardingContainer.featureCallout.callout-arrow::after { + background: transparent; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); + z-index: -1; +} +.onboardingContainer.featureCallout.arrow-top::before, .onboardingContainer.featureCallout.arrow-top::after { + top: -12px; + inset-inline-start: calc(50% - 12px); +} +.onboardingContainer.featureCallout.arrow-top::before { + border-top: 1px solid #CFCFD8; + border-inline-start: 1px solid #CFCFD8; +} +.onboardingContainer.featureCallout.arrow-bottom::before, .onboardingContainer.featureCallout.arrow-bottom::after { + top: calc(100% - 12px); + inset-inline-start: calc(50% - 12px); +} +.onboardingContainer.featureCallout.arrow-bottom::before { + border-inline-end: 1px solid #CFCFD8; + border-bottom: 1px solid #CFCFD8; +} +.onboardingContainer.featureCallout.arrow-inline-end::before, .onboardingContainer.featureCallout.arrow-inline-end::after { + top: calc(50% - 12px); + inset-inline-start: calc(100% - 12px); +} +.onboardingContainer.featureCallout.arrow-inline-end::before { + border-top: 1px solid #CFCFD8; + border-inline-end: 1px solid #CFCFD8; +} +.onboardingContainer.featureCallout.arrow-inline-start::before, .onboardingContainer.featureCallout.arrow-inline-start::after { + top: calc(50% - 12px); + inset-inline-start: -12px; +} +.onboardingContainer.featureCallout.arrow-inline-start::before { + border-bottom: 1px solid #CFCFD8; + border-inline-start: 1px solid #CFCFD8; +} +.onboardingContainer.featureCallout.arrow-top-end::before, .onboardingContainer.featureCallout.arrow-top-end::after { + top: -12px; + inset-inline-end: 12px; +} +.onboardingContainer.featureCallout.arrow-top-end::before { + border-top: 1px solid #CFCFD8; + border-inline-start: 1px solid #CFCFD8; +} +.onboardingContainer.featureCallout.arrow-top-start::before, .onboardingContainer.featureCallout.arrow-top-start::after { + top: -12px; + inset-inline-start: 12px; +} +.onboardingContainer.featureCallout.arrow-top-start::before { + border-top: 1px solid #CFCFD8; + border-inline-start: 1px solid #CFCFD8; +} + +html { + height: 100%; +} + +.dummy { + background: var(--mr-welcome-background-color) var(--mr-welcome-background-gradient) var(--mr-secondary-position) var(--mr-screen-background-color); +} + +:root[dialogroot] { + background-color: transparent; +} +:root[dialogroot] body { + padding: 0; +} +:root[dialogroot] .onboardingContainer { + height: 100%; + background-color: transparent; +} +:root[dialogroot] .onboardingContainer:dir(rtl) { + transform: unset; +} +:root[dialogroot] .onboardingContainer .logo-container { + pointer-events: none; +} +:root[dialogroot] .onboardingContainer .screen:dir(rtl) { + transform: unset; +} + +.welcome-container .onboardingContainer { + min-height: 610px; + min-width: fit-content; +} + +.onboardingContainer { + --grey-subtitle-1: #696977; + --newtab-button-secondary-color: #0060DF; + --mr-welcome-background-color: #F8F6F4; + --mr-screen-heading-color: var(--in-content-text-color); + --mr-welcome-background-gradient: linear-gradient(0deg, rgba(144, 89, 255, 0.2) 0%, rgba(2, 144, 238, 0.2) 100%); + --mr-screen-background-color: #F8F6F4; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Ubuntu", "Helvetica Neue", sans-serif; + font-size: 16px; + position: relative; + text-align: center; + height: 100vh; + --transition: 0.6s opacity, 0.6s scale, 0.6s rotate, 0.6s translate; +} +@media (prefers-color-scheme: dark) { + .onboardingContainer { + --grey-subtitle-1: #FFF; + --newtab-button-secondary-color: #FFF; + --mr-welcome-background-color: #333336; + --mr-welcome-background-gradient: linear-gradient(0deg, rgba(144, 89, 255, 0.3) 0%, rgba(2, 144, 238, 0.3) 100%); + --mr-screen-background-color: #62697A; + } +} +@media (prefers-contrast) { + .onboardingContainer { + --mr-screen-background-color: buttontext; + --mr-screen-heading-color: buttonface; + background-color: var(--in-content-page-background); + } +} +@media (prefers-reduced-motion: no-preference) { + .onboardingContainer { + --translate: 30px; + --rotate: 20deg; + --scale: 0.4; + --progress-bar-transition: 0.6s translate; + } + .onboardingContainer:dir(rtl) { + --scale: -0.4 0.4; + } +} +@media (prefers-reduced-motion: reduce) { + .onboardingContainer { + --translate: 0; + --rotate: 0deg; + --scale: 1; + --progress-bar-transition: none; + } + .onboardingContainer:dir(rtl) { + --scale: -1 1; + } +} +.onboardingContainer:dir(rtl) { + transform: rotateY(180deg); +} +.onboardingContainer .section-main { + display: flex; + flex-direction: column; + justify-content: center; + width: 504px; + flex-shrink: 0; +} +.onboardingContainer .main-content { + background-color: var(--in-content-page-background); + border-radius: 20px; + box-shadow: 0 2px 14px 0 rgba(0, 0, 0, 0.2); + display: flex; + flex-direction: column; + height: 100%; + padding: 0; + transition: var(--transition); + z-index: 1; + box-sizing: border-box; +} +.onboardingContainer .main-content.no-steps { + padding-bottom: 48px; +} +.onboardingContainer .main-content .main-content-inner { + display: flex; + flex-direction: column; + flex-grow: 1; + justify-content: space-around; +} +.onboardingContainer .main-content .no-steps[pos=corner] .main-content, .onboardingContainer .main-content .no-steps[pos=center].dialog-initial.dialog-last .main-content, +.onboardingContainer .screen[pos=corner] .main-content, +.onboardingContainer .screen[pos=center].dialog-initial.dialog-last .main-content { + padding-bottom: 48px; +} +.onboardingContainer .main-content .no-steps[pos=corner] .steps, .onboardingContainer .main-content .no-steps[pos=center].dialog-initial.dialog-last .steps, +.onboardingContainer .screen[pos=corner] .steps, +.onboardingContainer .screen[pos=center].dialog-initial.dialog-last .steps { + display: none; +} +.onboardingContainer .screen { + display: flex; + position: relative; + flex-flow: row nowrap; + height: 100%; + min-height: 500px; + overflow: hidden; + --in-content-link-color: var(--in-content-primary-button-background); + --in-content-link-color-hover: var(--in-content-primary-button-background-hover); + --in-content-link-color-active: var(--in-content-primary-button-background-active); + --in-content-link-color-visited: var(--in-content-link-color); +} +.onboardingContainer .screen.light-text { + --in-content-page-color: rgb(251, 251, 254); + --in-content-primary-button-text-color: rgb(43, 42, 51); + --in-content-primary-button-text-color-hover: rgb(43, 42, 51); + --in-content-primary-button-background: rgb(0, 221, 255); + --in-content-primary-button-background-hover: rgb(128, 235, 255); + --in-content-primary-button-background-active: rgb(170, 242, 255); + --checkbox-checked-bgcolor: var(--in-content-primary-button-background); + --in-content-button-text-color: var(--in-content-page-color); +} +.onboardingContainer .screen.dark-text { + --in-content-page-color: rgb(21, 20, 26); + --in-content-primary-button-text-color: rgb(251, 251, 254); + --in-content-primary-button-text-color-hover: rgb(251, 251, 254); + --in-content-primary-button-background: #0061E0; + --in-content-primary-button-background-hover: #0250BB; + --in-content-primary-button-background-active: #053E94; + --in-content-primary-button-border-color: transparent; + --in-content-primary-button-border-hover: transparent; + --checkbox-checked-bgcolor: var(--in-content-primary-button-background); + --in-content-button-text-color: var(--in-content-page-color); +} +.onboardingContainer .screen:dir(rtl) { + transform: rotateY(180deg); +} +.onboardingContainer .screen[pos=center] { + background-color: rgba(21, 20, 26, 0.5); + min-width: 504px; +} +.onboardingContainer .screen[pos=center].with-noodles { + min-width: 610px; + min-height: 610px; +} +.onboardingContainer .screen[pos=center].with-noodles .section-main { + height: 504px; +} +.onboardingContainer .screen[pos=center].with-video { + justify-content: center; + background: none; + align-items: center; +} +.onboardingContainer .screen[pos=center].with-video .section-main { + width: 800px; + height: 550px; +} +.onboardingContainer .screen[pos=center].with-video .main-content { + background-color: var(--mr-welcome-background-color); + border-radius: 8px; + box-shadow: 0 2px 14px rgba(58, 57, 68, 0.2); + padding: 44px 85px 20px; +} +.onboardingContainer .screen[pos=center].with-video .main-content .welcome-text { + margin: 0; +} +.onboardingContainer .screen[pos=center].with-video .main-content .main-content-inner { + justify-content: space-between; +} +.onboardingContainer .screen[pos=center].with-video .main-content h1, +.onboardingContainer .screen[pos=center].with-video .main-content h2 { + align-self: start; +} +.onboardingContainer .screen[pos=center].with-video .main-content h1 { + font-size: 24px; + line-height: 28.8px; +} +.onboardingContainer .screen[pos=center].with-video .main-content h2 { + font-size: 15px; + line-height: 22px; +} +.onboardingContainer .screen[pos=center].with-video .main-content .secondary-cta { + justify-content: end; +} +.onboardingContainer .screen[pos=center].with-video .main-content .secondary-cta .arrow-icon { + -moz-context-properties: fill; + fill: currentColor; +} +.onboardingContainer .screen[pos=center].with-video .main-content .secondary-cta .arrow-icon::after { + content: ""; + padding-inline-end: 12px; + margin-inline-start: 4px; + background: url("chrome://browser/skin/forward.svg") no-repeat center/12px; +} +.onboardingContainer .screen[pos=center].with-video .main-content .secondary-cta .arrow-icon:dir(rtl)::after { + background-image: url("chrome://browser/skin/back.svg"); +} +.onboardingContainer .screen[pos=center].with-video .main-content .secondary-cta .secondary { + background-color: var(--in-content-button-background) !important; + border: 1px solid var(--in-content-button-border-color); + line-height: 12px; + font-size: 0.72em; + font-weight: 600; + padding: 8px 16px; + color: var(--in-content-button-text-color); +} +.onboardingContainer .screen[pos=center].with-video .main-content .secondary-cta .secondary:hover { + text-decoration: none; + background-color: var(--in-content-button-background-hover) !important; + color: var(--in-content-button-text-color-hover); +} +.onboardingContainer .screen:not([pos=split]) .secondary-cta .secondary { + background: none; + color: var(--in-content-link-color); + font-size: 14px; + font-weight: normal; + line-height: 20px; +} +.onboardingContainer .screen:not([pos=split]) .secondary-cta.top button { + color: #FFF; +} +.onboardingContainer .screen:not([pos=split]) .secondary-cta.top button:hover { + color: #E0E0E6; +} +.onboardingContainer .screen[pos=split] { + margin: auto; + min-height: 550px; +} +.onboardingContainer .screen[pos=split]::before { + content: ""; + position: absolute; + box-shadow: 0 2px 14px 0 rgba(0, 0, 0, 0.2); + width: 800px; + height: 550px; + border-radius: 8px; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: auto; + pointer-events: none; +} +.onboardingContainer .screen[pos=split] .section-secondary, +.onboardingContainer .screen[pos=split] .section-main { + width: 400px; + height: 550px; +} +.onboardingContainer .screen[pos=split] .secondary-cta.top { + padding-inline-end: 0; +} +.onboardingContainer .screen[pos=split] .secondary-cta.top button { + color: var(--in-content-page-color); +} +.onboardingContainer .screen[pos=split] .section-main { + flex-direction: row; + display: block; + margin: auto auto auto 0; +} +.onboardingContainer .screen[pos=split] .section-main:dir(rtl) { + margin: auto 0 auto auto; +} +.onboardingContainer .screen[pos=split] .section-main .main-content { + border-radius: 0 8px 8px 0; + overflow: hidden; + padding-inline: 35px 20px; + padding-block: 120px 0; + box-shadow: none; +} +.onboardingContainer .screen[pos=split] .section-main .main-content:dir(rtl) { + border-radius: 8px 0 0 8px; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .main-content-inner { + min-height: 330px; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .main-content-inner .language-switcher-container .primary { + margin-bottom: 5px; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons { + position: relative; + text-align: initial; + height: 100%; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .checkbox-container { + font-size: 13px; + margin-block: 1em; + transition: var(--transition); +} +.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .checkbox-container input, +.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .checkbox-container label { + vertical-align: middle; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .additional-cta { + margin: 8px 0; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .additional-cta.cta-link { + color: var(--in-content-link-color); + background: none; + padding: 0; + font-weight: normal; + text-decoration: underline; + cursor: pointer; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .additional-cta.cta-link:hover { + color: var(--in-content-link-color-hover); +} +.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .additional-cta.secondary:hover { + background-color: var(--in-content-button-background-hover); +} +.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons.additional-cta-container { + display: flex; + flex-direction: column; + align-items: start; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .secondary-cta { + position: absolute; + bottom: -30px; + inset-inline-end: 0; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .secondary-cta .secondary { + background-color: var(--in-content-button-background) !important; + border: 1px solid var(--in-content-button-border-color); + line-height: 12px; + font-size: 0.72em; + font-weight: 600; + padding: 8px 16px; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .secondary-cta .secondary:hover { + text-decoration: none; + background-color: var(--in-content-button-background-hover) !important; + color: var(--in-content-button-text-color-hover); +} +.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .secondary-cta .arrow-icon { + -moz-context-properties: fill; + fill: currentColor; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .secondary-cta .arrow-icon::after { + content: ""; + padding-inline-end: 12px; + margin-inline-start: 4px; + background: url("chrome://browser/skin/forward.svg") no-repeat center/12px; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .secondary-cta .arrow-icon:dir(rtl)::after { + background-image: url("chrome://browser/skin/back.svg"); +} +.onboardingContainer .screen[pos=split] .section-main .main-content .logo-container { + text-align: start; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .brand-logo { + height: 25px; + margin-block: 0; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .welcome-text { + margin-inline: 0 10px; + margin-block: 10px 35px; + text-align: initial; + align-items: initial; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .welcome-text h1 { + font-size: 24px; + line-height: 1.2; + width: 300px; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .welcome-text h2 { + margin: 10px 0 0; + min-height: 1em; + font-size: 15px; + line-height: 1.5; +} +@media (prefers-contrast: no-preference) { + .onboardingContainer .screen[pos=split] .section-main .main-content .welcome-text h2 { + color: #5B5B66; + } +} +.onboardingContainer .screen[pos=split] .section-main .main-content .welcome-text h1, +.onboardingContainer .screen[pos=split] .section-main .main-content .primary { + margin: 0; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .steps { + z-index: 1; +} +.onboardingContainer .screen[pos=split] .section-main .main-content .steps.progress-bar { + width: 400px; + margin-inline: -35px; +} +@media (prefers-contrast) { + .onboardingContainer .screen[pos=split] .section-main .main-content { + border: 1px solid var(--in-content-page-color); + } + .onboardingContainer .screen[pos=split] .section-main .main-content .steps.progress-bar { + border-top: 1px solid var(--in-content-page-color); + background-color: var(--in-content-page-background); + } + .onboardingContainer .screen[pos=split] .section-main .main-content .steps.progress-bar .indicator { + background-color: var(--in-content-accent-color); + } +} +.onboardingContainer .screen[pos=split] .section-secondary { + --mr-secondary-position: center center / auto 350px; + border-radius: 8px 0 0 8px; + margin: auto 0 auto auto; + display: flex; + align-items: center; + -moz-context-properties: fill, stroke, fill-opacity, stroke-opacity; + stroke: currentColor; +} +.onboardingContainer .screen[pos=split] .section-secondary:dir(rtl) { + border-radius: 0 8px 8px 0; + margin: auto auto auto 0; +} +.onboardingContainer .screen[pos=split] .section-secondary h1 { + color: var(--mr-screen-heading-color); + font-weight: 700; + font-size: 47px; + line-height: 110%; + max-width: 340px; + text-align: initial; + white-space: pre-wrap; + text-shadow: none; + margin-inline: 40px 0; +} +.onboardingContainer .screen[pos=split] .section-secondary .image-alt { + width: inherit; + height: inherit; +} +.onboardingContainer .screen[pos=split] .section-secondary .hero-image { + flex: 1; + display: flex; + justify-content: center; + max-height: 100%; +} +.onboardingContainer .screen[pos=split] .section-secondary .hero-image img { + width: 100%; + max-width: 180px; + margin: 25px 0; + padding-bottom: 30px; +} +@media only screen and (max-width: 800px) { + .onboardingContainer .screen[pos=split] .section-secondary .hero-image img { + padding-bottom: unset; + } +} +.onboardingContainer .screen[pos=split] .tiles-theme-container { + margin-block: -20px auto; + align-items: initial; +} +.onboardingContainer .screen[pos=split] .tiles-theme-container .colorway-text { + text-align: initial; + transition: var(--transition); + font-size: 13px; + line-height: 1.5; + min-height: 4.5em; + margin-block: 10px 20px; +} +.onboardingContainer .screen[pos=split] .tiles-theme-container .theme { + min-width: 38px; +} +@media (prefers-contrast: no-preference) and (prefers-color-scheme: dark) { + .onboardingContainer .screen[pos=split] .section-main .main-content .welcome-text h2 { + color: #CFCFD8; + } + .onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .secondary { + background-color: #2B2A33; + } +} +@media only screen and (min-width: 800px) { + .onboardingContainer .screen[pos=split] .tiles-theme-section { + margin-inline-start: -10px; + } +} +@media only screen and (max-width: 800px) { + .onboardingContainer .screen[pos=split] { + flex-direction: column; + min-height: 550px; + } + .onboardingContainer .screen[pos=split]::before { + width: 400px; + } + .onboardingContainer .screen[pos=split] .section-secondary, +.onboardingContainer .screen[pos=split] .section-main { + width: 400px; + } + .onboardingContainer .screen[pos=split] .section-secondary { + --mr-secondary-background-position-y: top; + --mr-secondary-position: center var(--mr-secondary-background-position-y) / 75%; + border-radius: 8px 8px 0 0; + margin: auto auto 0; + height: 100px; + } + .onboardingContainer .screen[pos=split] .section-secondary .hero-image img { + margin: 6px 0; + } + .onboardingContainer .screen[pos=split] .section-secondary .message-text { + margin-inline: auto; + } + .onboardingContainer .screen[pos=split] .section-secondary h1 { + font-size: 35px; + text-align: center; + white-space: normal; + margin-inline: auto; + margin-block: 14px 6px; + } + .onboardingContainer .screen[pos=split] .section-secondary:dir(rtl) { + margin: auto auto 0; + border-radius: 8px 8px 0 0; + } + .onboardingContainer .screen[pos=split] .section-main { + margin: 0 auto auto; + height: 450px; + } + .onboardingContainer .screen[pos=split] .section-main .main-content { + border-radius: 0 0 8px 8px; + padding: 30px 0 0; + } + .onboardingContainer .screen[pos=split] .section-main .main-content .main-content-inner { + align-items: center; + } + .onboardingContainer .screen[pos=split] .section-main .main-content .logo-container { + text-align: center; + } + .onboardingContainer .screen[pos=split] .section-main .main-content .logo-container .brand-logo { + min-height: 25px; + } + .onboardingContainer .screen[pos=split] .section-main .main-content .logo-container .brand-logo, .onboardingContainer .screen[pos=split] .section-main .main-content .logo-container .brand-logo:dir(rtl) { + background-position: center; + } + .onboardingContainer .screen[pos=split] .section-main .main-content .welcome-text { + align-items: center; + text-align: center; + margin-inline: 0; + padding-inline: 30px; + } + .onboardingContainer .screen[pos=split] .section-main .main-content .welcome-text .spacer-bottom, +.onboardingContainer .screen[pos=split] .section-main .main-content .welcome-text .spacer-top { + display: none; + } + .onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons { + text-align: center; + } + .onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .checkbox-container { + display: none; + } + .onboardingContainer .screen[pos=split] .section-main .main-content .action-buttons .secondary-cta { + position: relative; + margin-block: 10px 0; + bottom: 0; + } + .onboardingContainer .screen[pos=split] .section-main .main-content .primary, +.onboardingContainer .screen[pos=split] .section-main .main-content .secondary { + min-width: 240px; + } + .onboardingContainer .screen[pos=split] .section-main .main-content .colorway-text { + text-align: center; + margin-inline: 30px; + } + .onboardingContainer .screen[pos=split] .section-main .main-content .steps { + padding-block: 0; + margin: 0; + } + .onboardingContainer .screen[pos=split] .section-main .main-content .steps.progress-bar { + margin-inline: 0; + } + .onboardingContainer .screen[pos=split] .section-main .additional-cta.cta-link { + align-self: center; + } + .onboardingContainer .screen[pos=split] .section-main:dir(rtl) { + margin: 0 auto auto; + } + .onboardingContainer .screen[pos=split] .section-main:dir(rtl) .main-content { + border-radius: 0 0 8px 8px; + } +} +@media only screen and (max-height: 650px) and (min-width: 800px) and (max-width: 990px) { + .onboardingContainer .screen[pos=split] .section-main { + position: relative; + } + .onboardingContainer .screen[pos=split] .section-main .secondary-cta.top { + padding: 0; + top: 20px; + inset-inline-end: 20px; + } +} +@media only screen and (max-height: 650px) and (max-width: 590px) { + .onboardingContainer .screen[pos=split] .section-main { + position: relative; + } + .onboardingContainer .screen[pos=split] .section-main .secondary-cta.top { + padding: 0; + top: 0; + inset-inline-end: 0; + } +} +.onboardingContainer .brand-logo { + margin-block: 60px 10px; + transition: var(--transition); + height: 80px; +} +.onboardingContainer .brand-logo.cta-top { + margin-top: 25px; +} +.onboardingContainer .brand-logo.hide { + visibility: hidden; + padding: unset; + margin-top: 50px; +} +.onboardingContainer .rtamo-theme-icon { + max-height: 30px; + border-radius: 2px; + margin-bottom: 10px; + margin-top: 24px; +} +.onboardingContainer .rtamo-icon { + text-align: start; +} +@media only screen and (max-width: 800px) { + .onboardingContainer .rtamo-icon { + text-align: center; + } +} +.onboardingContainer .welcome-text { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin: 0.5em 1em; + transition: var(--transition); +} +.onboardingContainer .welcome-text h1, +.onboardingContainer .welcome-text h2 { + color: var(--in-content-page-color); + line-height: 1.5; +} +.onboardingContainer .welcome-text h1 { + font-size: 24px; + font-weight: 600; + margin: 0 6px; + letter-spacing: -0.02em; + outline: none; +} +.onboardingContainer .welcome-text h2 { + font-size: 16px; + font-weight: normal; + margin: 10px 6px 0; + max-width: 750px; + letter-spacing: -0.01em; +} +.onboardingContainer .welcome-text.fancy h1 { + background-image: linear-gradient(90deg, #9059FF, #FF4AA2, #FF8C00, #FF4AA2, #9059FF); + background-clip: text; + background-size: 200%; +} +@media (prefers-contrast: no-preference) { + .onboardingContainer .welcome-text.fancy h1 { + color: transparent; + } +} +@media (prefers-color-scheme: dark) { + .onboardingContainer .welcome-text.fancy h1 { + background-image: linear-gradient(90deg, #C688FF, #FF84C0, #FFBD4F, #FF84C0, #C688FF); + } + .onboardingContainer .welcome-text.fancy h1::selection { + color: #FFF; + background-color: #696977; + } +} +.onboardingContainer .welcome-text.shine h1 { + animation: shine 50s linear infinite; + background-size: 400%; +} +@keyframes shine { + to { + background-position: 400%; + } +} +.onboardingContainer .welcome-text .cta-paragraph a { + margin: 0; + text-decoration: underline; + cursor: pointer; +} +.onboardingContainer .screen.light-text .welcome-text.fancy h1 { + background-image: linear-gradient(90deg, #C688FF, #FF84C0, #FFBD4F, #FF84C0, #C688FF); +} +.onboardingContainer .screen.dark-text .welcome-text.fancy h1 { + background-image: linear-gradient(90deg, #9059FF, #FF4AA2, #FF8C00, #FF4AA2, #9059FF); +} +.onboardingContainer .welcomeZap span { + position: relative; + z-index: 1; + white-space: nowrap; +} +.onboardingContainer .welcomeZap .zap::after { + display: block; + background-repeat: no-repeat; + background-size: 100% 100%; + content: ""; + position: absolute; + top: calc(100% - 0.15em); + width: 100%; + height: 0.3em; + left: 0; + z-index: -1; + transform: scaleY(3); +} +.onboardingContainer .welcomeZap .zap.short::after { + background-image: url("chrome://activity-stream/content/data/content/assets/short-zap.svg"); +} +.onboardingContainer .welcomeZap .zap.long::after { + background-image: url("chrome://activity-stream/content/data/content/assets/long-zap.svg"); +} +.onboardingContainer .language-loader { + filter: invert(1); + margin-inline-end: 10px; + position: relative; + top: 3px; + width: 16px; + height: 16px; + margin-top: -6px; +} +@media (prefers-color-scheme: dark) { + .onboardingContainer .language-loader { + filter: invert(0); + } +} +.onboardingContainer .tiles-theme-container { + display: flex; + flex-direction: column; + align-items: center; + margin: 10px auto; +} +.onboardingContainer .sr-only { + opacity: 0; + overflow: hidden; + position: absolute; +} +.onboardingContainer .sr-only.input { + height: 1px; + width: 1px; +} +.onboardingContainer .tiles-theme-section { + border: 0; + display: flex; + flex-wrap: wrap; + gap: 5px; + justify-content: space-evenly; + margin-inline: 10px; + padding: 10px; + transition: var(--transition); +} +.onboardingContainer .tiles-theme-section:hover, .onboardingContainer .tiles-theme-section:active, .onboardingContainer .tiles-theme-section:focus-within { + border-radius: 8px; + outline: 2px solid var(--in-content-primary-button-background); +} +.onboardingContainer .tiles-theme-section .theme { + align-items: center; + display: flex; + flex-direction: column; + flex: 1; + padding: 0; + min-width: 50px; + width: 180px; + color: #000; + box-shadow: none; + border-radius: 4px; + cursor: pointer; + z-index: 0; +} +.onboardingContainer .tiles-theme-section .theme.colorway { + width: auto; +} +.onboardingContainer .tiles-theme-section .theme:focus, .onboardingContainer .tiles-theme-section .theme:active { + outline: initial; + outline-offset: initial; +} +.onboardingContainer .tiles-theme-section .theme .icon.colorway, +.onboardingContainer .tiles-theme-section .theme .label.colorway { + width: 20px; + height: 20px; +} +.onboardingContainer .tiles-theme-section .theme .icon { + background-size: cover; + width: 40px; + height: 40px; + border-radius: 40px; + outline: 1px solid var(--in-content-border-color); + outline-offset: -0.5px; + z-index: -1; +} +.onboardingContainer .tiles-theme-section .theme .icon:dir(rtl) { + transform: scaleX(-1); +} +.onboardingContainer .tiles-theme-section .theme .icon:focus, .onboardingContainer .tiles-theme-section .theme .icon:active, .onboardingContainer .tiles-theme-section .theme .icon.selected { + outline: 2px solid var(--in-content-primary-button-background); + outline-offset: 2px; +} +.onboardingContainer .tiles-theme-section .theme .icon.light { + background-image: url("resource://builtin-themes/light/icon.svg"); +} +.onboardingContainer .tiles-theme-section .theme .icon.dark { + background-image: url("resource://builtin-themes/dark/icon.svg"); +} +.onboardingContainer .tiles-theme-section .theme .icon.alpenglow { + background-image: url("resource://builtin-themes/alpenglow/icon.svg"); +} +.onboardingContainer .tiles-theme-section .theme .icon.default, .onboardingContainer .tiles-theme-section .theme .icon.automatic { + background-image: url("resource://default-theme/icon.svg"); +} +.onboardingContainer .tiles-theme-section .theme .icon.default.colorway, .onboardingContainer .tiles-theme-section .theme .icon.automatic.colorway { + background-image: url("chrome://activity-stream/content/data/content/assets/default.svg"); +} +.onboardingContainer .tiles-theme-section .theme .icon.playmaker { + background-image: url("resource://builtin-themes/colorways/2022playmaker/balanced/icon.svg"); +} +.onboardingContainer .tiles-theme-section .theme .icon.expressionist { + background-image: url("resource://builtin-themes/colorways/2022expressionist/balanced/icon.svg"); +} +.onboardingContainer .tiles-theme-section .theme .icon.visionary { + background-image: url("resource://builtin-themes/colorways/2022visionary/balanced/icon.svg"); +} +.onboardingContainer .tiles-theme-section .theme .icon.dreamer { + background-image: url("resource://builtin-themes/colorways/2022dreamer/balanced/icon.svg"); +} +.onboardingContainer .tiles-theme-section .theme .icon.innovator { + background-image: url("resource://builtin-themes/colorways/2022innovator/balanced/icon.svg"); +} +.onboardingContainer .tiles-theme-section .theme .icon.activist { + background-image: url("resource://builtin-themes/colorways/2022activist/balanced/icon.svg"); +} +.onboardingContainer .tiles-theme-section .theme .text { + display: flex; + color: var(--in-content-page-color); + font-size: 14px; + font-weight: normal; + line-height: 20px; + margin-inline-start: 0; + margin-top: 9px; +} +.onboardingContainer .tiles-theme-section legend { + cursor: default; +} +.onboardingContainer .tiles-container { + margin: 10px auto; +} +.onboardingContainer .tiles-container.info { + padding: 6px 12px 12px; +} +.onboardingContainer .tiles-container.info:hover, .onboardingContainer .tiles-container.info:focus { + background-color: rgba(217, 217, 227, 0.3); + border-radius: 4px; +} +.onboardingContainer .tiles-delayed { + animation: fadein 0.4s; +} +.onboardingContainer .multi-select-container { + display: flex; + flex-direction: column; + align-items: flex-start; + margin-block: -1em 1em; + color: #5B5B66; + font-weight: 400; + font-size: 14px; + transition: var(--transition); + z-index: 1; +} +.onboardingContainer .multi-select-container .checkbox-container { + display: flex; + margin-bottom: 16px; + align-items: center; +} +@media (prefers-color-scheme: dark) { + .onboardingContainer .multi-select-container { + color: #CFCFD8; + } +} +.onboardingContainer .mobile-downloads .qr-code-image { + margin: 24px 0 10px; + width: 113px; + height: 113px; +} +.onboardingContainer .mobile-downloads .email-link { + font-size: 16px; + font-weight: 400; + text-decoration: underline; + color: var(--in-content-link-color); + background: none; +} +.onboardingContainer .mobile-downloads .email-link:hover { + background: none; +} +.onboardingContainer .mobile-downloads .ios button { + background-image: url("chrome://app-marketplace-icons/locale/ios.svg"); +} +.onboardingContainer .mobile-downloads .android button { + background-image: url("chrome://app-marketplace-icons/locale/android.png"); +} +.onboardingContainer .mobile-download-buttons { + list-style: none; + padding: 10px 0; + margin: 0; +} +.onboardingContainer .mobile-download-buttons li { + display: inline-block; +} +.onboardingContainer .mobile-download-buttons li button { + display: inline-block; + height: 45px; + width: 152px; + background-repeat: no-repeat; + background-size: contain; + background-position: center; + box-shadow: none; + border: 0; +} +.onboardingContainer .mobile-download-buttons li:not(:first-child) { + margin-inline: 5px 0; +} +.onboardingContainer .dismiss-button { + padding: 0; + margin-block: 30px -45px; + margin-inline: 0 30px; + display: block; + float: inline-end; + background: url("chrome://global/skin/icons/close.svg") no-repeat center/cover; + height: 15px; + width: 15px; + align-self: end; + min-height: 15px; + min-width: 15px; + -moz-context-properties: fill; + fill: currentColor; +} +@keyframes fadein { + from { + opacity: 0; + } +} +.onboardingContainer .secondary-cta { + display: flex; + align-items: end; + flex-direction: row; + justify-content: center; + font-size: 14px; + transition: var(--transition); +} +.onboardingContainer .secondary-cta.top { + justify-content: end; + padding-inline-end: min(150px, 500px - 70vh); + padding-top: 4px; + position: absolute; + top: 10px; + inset-inline-end: 20px; + z-index: 2; +} +.onboardingContainer .secondary-cta span { + color: var(--grey-subtitle-1); + margin: 0 4px; +} +.onboardingContainer .message-text, +.onboardingContainer .attrib-text { + transition: var(--transition); +} +.onboardingContainer .helptext { + padding: 1em; + text-align: center; + color: var(--grey-subtitle-1); + font-size: 12px; + line-height: 18px; +} +.onboardingContainer .helptext.default { + align-self: center; + max-width: 40%; +} +.onboardingContainer .helptext span { + padding-inline-end: 4px; +} +.onboardingContainer .helptext-img { + height: 1.5em; + width: 1.5em; + margin-inline-end: 4px; + vertical-align: middle; +} +.onboardingContainer .helptext-img.end { + margin: 4px; +} +.onboardingContainer .helptext-img.footer { + vertical-align: bottom; +} +.onboardingContainer .steps { + display: flex; + flex-direction: row; + justify-content: center; + margin-top: 0; + padding-block: 16px 0; + transition: var(--transition); + z-index: -1; + height: 48px; + box-sizing: border-box; +} +.onboardingContainer .steps.has-helptext { + padding-bottom: 0; +} +.onboardingContainer .steps .indicator { + width: 0; + height: 0; + margin-inline-end: 4px; + margin-inline-start: 4px; + background: var(--grey-subtitle-1); + border-radius: 5px; + border: 3px solid var(--in-content-button-text-color); + opacity: 0.35; + box-sizing: inherit; +} +.onboardingContainer .steps .indicator.current { + opacity: 1; + border-color: var(--checkbox-checked-bgcolor); +} +.onboardingContainer .steps .indicator.current:last-of-type:first-of-type { + opacity: 0; +} +.onboardingContainer .steps.progress-bar { + height: 6px; + padding-block: 0; + margin-block: 42px 0; + background-color: color-mix(in srgb, var(--in-content-button-text-color) 25%, transparent); + justify-content: start; + opacity: 1; + transition: none; +} +.onboardingContainer .steps.progress-bar .indicator { + width: 100%; + height: 100%; + margin-inline: -1px; + background-color: var(--checkbox-checked-bgcolor); + border: 0; + border-radius: 0; + opacity: 1; + transition: var(--progress-bar-transition); + translate: calc(var(--progress-bar-progress, 0%) - 100%); +} +.onboardingContainer .steps.progress-bar .indicator:dir(rtl) { + translate: calc(var(--progress-bar-progress, 0%) * -1 + 100%); +} +.onboardingContainer .primary, +.onboardingContainer .secondary, +.onboardingContainer .additional-cta { + font-size: 13px; + line-height: 16px; + padding: 11px 15px; + transition: var(--transition); +} +.onboardingContainer .primary.rtamo, +.onboardingContainer .secondary.rtamo, +.onboardingContainer .additional-cta.rtamo { + margin-top: 24px; +} +.onboardingContainer .secondary { + background-color: var(--in-content-button-background); + color: var(--in-content-button-text-color); +} +.onboardingContainer .noodle { + display: block; + background-repeat: no-repeat; + background-size: 100% 100%; + position: absolute; + transition: var(--transition); +} +.onboardingContainer .noodle:dir(rtl) { + scale: -1 1; +} +.onboardingContainer .outline-L { + background-image: url("chrome://activity-stream/content/data/content/assets/noodle-outline-L.svg"); +} +.onboardingContainer .solid-L { + background-image: url("chrome://activity-stream/content/data/content/assets/noodle-solid-L.svg"); + -moz-context-properties: fill; + fill: var(--in-content-page-background); + display: none; +} +.onboardingContainer .purple-C { + background-image: url("chrome://activity-stream/content/data/content/assets/noodle-C.svg"); + -moz-context-properties: fill; + fill: #E7258C; +} +.onboardingContainer .orange-L { + background-image: url("chrome://activity-stream/content/data/content/assets/noodle-solid-L.svg"); + -moz-context-properties: fill; + fill: #FFA437; +} +.onboardingContainer [pos=corner] { + min-width: 700px; +} +.onboardingContainer [pos=corner] .section-secondary { + display: flex; + flex-direction: column; + margin-inline-start: 6vw; +} +.onboardingContainer [pos=corner] .section-secondary .message-text { + flex-grow: 1; + display: flex; + flex-direction: column; + justify-content: center; +} +.onboardingContainer [pos=corner] .section-secondary .message-text .spacer-top { + flex: 1; +} +.onboardingContainer [pos=corner] .section-secondary .message-text .spacer-bottom { + flex: 1; +} +.onboardingContainer [pos=corner] .section-secondary .message-text h1 { + color: #FFF; + font-weight: bold; + font-size: clamp(48px, 7vw, 104px); + line-height: 1em; + margin: 0 6px; + max-width: 5em; + text-align: initial; + white-space: pre-wrap; + text-shadow: 0 4px 8px rgba(0, 0, 0, 0.35); +} +.onboardingContainer [pos=corner] .section-secondary .attrib-text { + height: 18px; + margin-bottom: 25px; + text-align: initial; + font-size: 12px; + line-height: 18px; + color: #F9F9FB; + text-shadow: 0 0 7px rgba(0, 0, 0, 0.6); + transition-delay: 0.5s; +} +.onboardingContainer [pos=corner] .section-main { + z-index: 1; +} +.onboardingContainer [pos=corner] .section-main .main-content { + background: transparent; + box-shadow: none; + display: flex; + position: absolute; + height: auto; + width: 350px; + bottom: 0; + inset-inline-end: 30px; + overflow: visible; + transition: var(--transition); +} +.onboardingContainer [pos=corner] .section-main .brand-logo { + margin-top: 0; + transition-delay: 1.2s; +} +.onboardingContainer [pos=corner] .section-main .primary, +.onboardingContainer [pos=corner] .section-main .secondary-cta:not(.top) { + transition-delay: 1.7s; +} +.onboardingContainer [pos=corner] .section-main .secondary-cta.top { + transition-delay: 0.5s; +} +.onboardingContainer [pos=corner] .section-main .welcome-text { + transition-delay: 1.2s; +} +.onboardingContainer [pos=corner] .section-main .welcome-text h2 { + margin: 10px 6px; +} +.onboardingContainer [pos=corner] .solid-L { + display: block; + width: 1300px; + height: 1050px; + bottom: -390px; + inset-inline-end: -640px; + transform: rotate(-80deg); + transition-delay: 0.5s; +} +.onboardingContainer [pos=corner] .orange-L { + width: 466px; + height: 356px; + bottom: 270px; + inset-inline-end: -245px; + transition-delay: 0.8s; +} +.onboardingContainer [pos=corner] .purple-C { + width: 467px; + height: 382px; + bottom: -100px; + inset-inline-end: 125px; + transform: rotate(-75deg); + transition-delay: 0.8s; +} +.onboardingContainer .screen-1 .section-main { + z-index: 1; + margin: auto; +} +.onboardingContainer .screen-1 .outline-L { + width: 87px; + height: 80px; + transform: rotate(10deg) translate(-30%, 200%); + transition-delay: 0.4s; + z-index: 2; +} +.onboardingContainer .screen-1 .orange-L { + width: 550px; + height: 660px; + transform: rotate(-155deg) translate(11%, -18%); + transition-delay: 0.2s; +} +.onboardingContainer .screen-1 .purple-C { + width: 310px; + height: 260px; + transform: translate(-18%, -67%); +} +.onboardingContainer .screen-1 .yellow-circle { + width: 165px; + height: 165px; + border-radius: 50%; + transform: translate(230%, -5%); + background: #952BB9; + transition-delay: -0.2s; +} +.onboardingContainer .dialog-initial .brand-logo { + transition-delay: 0.6s; +} +.onboardingContainer .dialog-initial .welcome-text, +.onboardingContainer .dialog-initial .multi-select-container { + transition-delay: 0.8s; +} +.onboardingContainer .dialog-initial .tiles-theme-section { + transition-delay: 0.9s; +} +.onboardingContainer .dialog-initial .primary, +.onboardingContainer .dialog-initial .secondary, +.onboardingContainer .dialog-initial .secondary-cta, +.onboardingContainer .dialog-initial .steps, +.onboardingContainer .dialog-initial .cta-link { + transition-delay: 1s; +} +.onboardingContainer .screen:not(.dialog-initial):not([pos=corner]) .tiles-theme-section, +.onboardingContainer .screen:not(.dialog-initial):not([pos=corner]) .colorway-text { + transition-delay: 0.2s; +} +.onboardingContainer .screen:not(.dialog-initial):not([pos=corner]) .primary, +.onboardingContainer .screen:not(.dialog-initial):not([pos=corner]) .secondary, +.onboardingContainer .screen:not(.dialog-initial):not([pos=corner]) .secondary-cta, +.onboardingContainer .screen:not(.dialog-initial):not([pos=corner]) .cta-link { + transition-delay: 0.4s; +} +.onboardingContainer .screen-2 .section-main { + z-index: 1; + margin: auto; +} +.onboardingContainer .screen-2 .outline-L { + width: 87px; + height: 80px; + transform: rotate(250deg) translate(-420%, 425%); + transition-delay: 0.2s; + z-index: 2; +} +.onboardingContainer .screen-2 .orange-L { + height: 800px; + width: 660px; + transform: rotate(35deg) translate(-10%, -7%); + transition-delay: -0.4s; +} +.onboardingContainer .screen-2 .purple-C { + width: 392px; + height: 394px; + transform: rotate(260deg) translate(-34%, -35%); + transition-delay: -0.2s; + fill: #952BB9; +} +.onboardingContainer .screen-2 .yellow-circle { + width: 165px; + height: 165px; + border-radius: 50%; + transform: translate(160%, 130%); + background: #E7258C; +} +.onboardingContainer.transition-in .noodle { + opacity: 0; + rotate: var(--rotate); + scale: var(--scale); +} +.onboardingContainer.transition-in [pos=corner] .brand-logo, +.onboardingContainer.transition-in [pos=corner] .welcome-text, +.onboardingContainer.transition-in [pos=corner] .primary, +.onboardingContainer.transition-in [pos=corner] .checkbox-container, +.onboardingContainer.transition-in [pos=corner] .secondary, +.onboardingContainer.transition-in [pos=corner] .secondary-cta:not(.top), +.onboardingContainer.transition-in [pos=corner] .message-text { + opacity: 0; + translate: 0 var(--translate); +} +.onboardingContainer.transition-in [pos=corner] .attrib-text, +.onboardingContainer.transition-in [pos=corner] .secondary-cta.top { + opacity: 0; +} +.onboardingContainer.transition-in .dialog-initial .main-content { + translate: 0 calc(-2 * var(--translate)); +} +.onboardingContainer.transition-in .dialog-initial .brand-logo, +.onboardingContainer.transition-in .dialog-initial .steps { + opacity: 0; + translate: 0 calc(-1 * var(--translate)); +} +.onboardingContainer.transition-in .screen:not(.dialog-initial):not([pos=corner]) .steps:not(.progress-bar) { + opacity: 0.2; +} +.onboardingContainer.transition-in .screen:not([pos=corner]) .welcome-text, +.onboardingContainer.transition-in .screen:not([pos=corner]) .tiles-theme-section, +.onboardingContainer.transition-in .screen:not([pos=corner]) .colorway-text, +.onboardingContainer.transition-in .screen:not([pos=corner]) .primary, +.onboardingContainer.transition-in .screen:not([pos=corner]) .checkbox-container, +.onboardingContainer.transition-in .screen:not([pos=corner]) .secondary, +.onboardingContainer.transition-in .screen:not([pos=corner]) .secondary-cta:not(.top), +.onboardingContainer.transition-in .screen:not([pos=corner]) .cta-link { + opacity: 0; + translate: 0 calc(-1 * var(--translate)); +} +.onboardingContainer.transition-out .noodle { + opacity: 0; + rotate: var(--rotate); + scale: var(--scale); + transition-delay: 0.2s; +} +.onboardingContainer.transition-out [pos=corner] .message-text { + opacity: 0; + translate: 0 var(--translate); +} +.onboardingContainer.transition-out [pos=corner] .main-content { + opacity: 0; + translate: 0 var(--translate); + transition-delay: 0.2s; +} +.onboardingContainer.transition-out [pos=corner] .solid-L { + transition-delay: 0.4s; +} +.onboardingContainer.transition-out [pos=corner] .attrib-text, +.onboardingContainer.transition-out [pos=corner] .secondary-cta.top { + opacity: 0; + transition-delay: 0.2s; +} +.onboardingContainer.transition-out .screen:not(.dialog-last):not([pos=corner]) .main-content { + overflow: hidden; +} +.onboardingContainer.transition-out .screen:not(.dialog-last):not([pos=corner]) .welcome-text, +.onboardingContainer.transition-out .screen:not(.dialog-last):not([pos=corner]) .multi-select-container { + opacity: 0; + translate: 0 var(--translate); + transition-delay: 0.1s; +} +.onboardingContainer.transition-out .screen:not(.dialog-last):not([pos=corner]) .tiles-theme-section, +.onboardingContainer.transition-out .screen:not(.dialog-last):not([pos=corner]) .colorway-text { + opacity: 0; + translate: 0 var(--translate); + transition-delay: 0.2s; +} +.onboardingContainer.transition-out .screen:not(.dialog-last):not([pos=corner]) .primary, +.onboardingContainer.transition-out .screen:not(.dialog-last):not([pos=corner]) .checkbox-container, +.onboardingContainer.transition-out .screen:not(.dialog-last):not([pos=corner]) .secondary, +.onboardingContainer.transition-out .screen:not(.dialog-last):not([pos=corner]) .secondary-cta:not(.top), +.onboardingContainer.transition-out .screen:not(.dialog-last):not([pos=corner]) .cta-link { + opacity: 0; + translate: 0 var(--translate); + transition-delay: 0.3s; +} +.onboardingContainer.transition-out .screen:not(.dialog-last):not([pos=corner]) .steps:not(.progress-bar) { + opacity: 0.2; + transition-delay: 0.5s; +} +.onboardingContainer.transition-out .dialog-last .noodle { + transition-delay: 0s; +} +.onboardingContainer.transition-out .dialog-last .main-content { + opacity: 0; + translate: 0 calc(2 * var(--translate)); + transition-delay: 0.4s; +} diff --git a/browser/components/newtab/aboutwelcome/content/aboutwelcome.html b/browser/components/newtab/aboutwelcome/content/aboutwelcome.html new file mode 100644 index 0000000000..ffbb573232 --- /dev/null +++ b/browser/components/newtab/aboutwelcome/content/aboutwelcome.html @@ -0,0 +1,29 @@ +<!-- 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/. --> + +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <meta name="color-scheme" content="light dark"> + <meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; media-src resource: chrome: https://assets.mozilla.net https://www.mozilla.org; connect-src https:; img-src https: data: blob: chrome:; style-src resource: chrome:;"> + <title data-l10n-id="onboarding-welcome-header"></title> + <link rel="icon" type="image/png" href="chrome://branding/content/icon32.png"> + <link rel="stylesheet" href="chrome://global/skin/in-content/common.css"> + <link rel="stylesheet" href="chrome://activity-stream/content/aboutwelcome/aboutwelcome.css"> + <link rel="localization" href="branding/brand.ftl"/> + <link rel="localization" href="browser/branding/sync-brand.ftl"/> + <link rel="localization" href="browser/branding/brandings.ftl"/> + <link rel="localization" href="browser/newtab/onboarding.ftl"/> + <link rel="localization" href="browser/spotlight.ftl"/> + </head> + <body> + <div id="root" class="welcome-container" role="presentation"> + </div> + <script src="resource://activity-stream/vendor/react.js"></script> + <script src="resource://activity-stream/vendor/react-dom.js"></script> + <script src="chrome://browser/content/contentTheme.js"></script> + <script src="resource://activity-stream/aboutwelcome/aboutwelcome.bundle.js"></script> + </body> +</html> diff --git a/browser/components/newtab/aboutwelcome/lib/AboutWelcomeDefaults.jsm b/browser/components/newtab/aboutwelcome/lib/AboutWelcomeDefaults.jsm new file mode 100644 index 0000000000..45fc7480b1 --- /dev/null +++ b/browser/components/newtab/aboutwelcome/lib/AboutWelcomeDefaults.jsm @@ -0,0 +1,820 @@ +/* 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 = ["AboutWelcomeDefaults", "DEFAULT_WELCOME_CONTENT"]; + +const { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); +const { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs", +}); + +XPCOMUtils.defineLazyModuleGetters(lazy, { + AddonRepository: "resource://gre/modules/addons/AddonRepository.jsm", + AttributionCode: "resource:///modules/AttributionCode.jsm", +}); + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "usesFirefoxSync", + "services.sync.username" +); + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "mobileDevices", + "services.sync.clients.devices.mobile", + 0 +); + +const DEFAULT_WELCOME_CONTENT = { + id: "DEFAULT_ABOUTWELCOME_PROTON", + template: "multistage", + // Allow tests to easily disable transitions. + transitions: Services.prefs.getBoolPref( + "browser.aboutwelcome.transitions", + true + ), + backdrop: + "#212121 url('chrome://activity-stream/content/data/content/assets/proton-bkg.avif') center/cover no-repeat fixed", + screens: [ + { + id: "AW_PIN_FIREFOX", + content: { + position: "corner", + logo: {}, + title: { + string_id: "mr1-onboarding-pin-header", + }, + hero_text: { + string_id: "mr1-welcome-screen-hero-text", + }, + help_text: { + string_id: "mr1-onboarding-welcome-image-caption", + }, + has_noodles: true, + primary_button: { + label: { + string_id: "mr1-onboarding-pin-primary-button-label", + }, + action: { + navigate: true, + type: "PIN_FIREFOX_TO_TASKBAR", + }, + }, + secondary_button: { + label: { + string_id: "mr1-onboarding-set-default-secondary-button-label", + }, + action: { + navigate: true, + }, + }, + secondary_button_top: { + label: { + string_id: "mr1-onboarding-sign-in-button-label", + }, + action: { + data: { + entrypoint: "activity-stream-firstrun", + }, + type: "SHOW_FIREFOX_ACCOUNTS", + addFlowParams: true, + }, + }, + }, + }, + { + id: "AW_LANGUAGE_MISMATCH", + content: { + logo: {}, + title: { string_id: "onboarding-live-language-header" }, + has_noodles: true, + languageSwitcher: { + downloading: { + string_id: "onboarding-live-language-button-label-downloading", + }, + cancel: { + string_id: "onboarding-live-language-secondary-cancel-download", + }, + waiting: { string_id: "onboarding-live-language-waiting-button" }, + skip: { string_id: "onboarding-live-language-skip-button-label" }, + action: { + navigate: true, + }, + }, + }, + }, + { + id: "AW_SET_DEFAULT", + content: { + logo: {}, + title: { + string_id: "mr1-onboarding-default-header", + }, + subtitle: { + string_id: "mr1-onboarding-default-subtitle", + }, + has_noodles: true, + primary_button: { + label: { + string_id: "mr1-onboarding-default-primary-button-label", + }, + action: { + navigate: true, + type: "SET_DEFAULT_BROWSER", + }, + }, + secondary_button: { + label: { + string_id: "mr1-onboarding-set-default-secondary-button-label", + }, + action: { + navigate: true, + }, + }, + }, + }, + { + id: "AW_IMPORT_SETTINGS", + content: { + logo: {}, + title: { + string_id: "mr1-onboarding-import-header", + }, + subtitle: { + string_id: "mr1-onboarding-import-subtitle", + }, + has_noodles: true, + primary_button: { + label: { + string_id: + "mr1-onboarding-import-primary-button-label-no-attribution", + }, + action: { + type: "SHOW_MIGRATION_WIZARD", + data: {}, + navigate: true, + }, + }, + secondary_button: { + label: { + string_id: "mr1-onboarding-import-secondary-button-label", + }, + action: { + navigate: true, + }, + }, + }, + }, + { + id: "AW_CHOOSE_THEME", + content: { + logo: {}, + title: { + string_id: "mr1-onboarding-theme-header", + }, + subtitle: { + string_id: "mr1-onboarding-theme-subtitle", + }, + has_noodles: true, + tiles: { + type: "theme", + action: { + theme: "<event>", + }, + data: [ + { + theme: "automatic", + label: { + string_id: "mr1-onboarding-theme-label-system", + }, + tooltip: { + string_id: "mr1-onboarding-theme-tooltip-system", + }, + description: { + string_id: "mr1-onboarding-theme-description-system", + }, + }, + { + theme: "light", + label: { + string_id: "mr1-onboarding-theme-label-light", + }, + tooltip: { + string_id: "mr1-onboarding-theme-tooltip-light", + }, + description: { + string_id: "mr1-onboarding-theme-description-light", + }, + }, + { + theme: "dark", + label: { + string_id: "mr1-onboarding-theme-label-dark", + }, + tooltip: { + string_id: "mr1-onboarding-theme-tooltip-dark", + }, + description: { + string_id: "mr1-onboarding-theme-description-dark", + }, + }, + { + theme: "alpenglow", + label: { + string_id: "mr1-onboarding-theme-label-alpenglow", + }, + tooltip: { + string_id: "mr1-onboarding-theme-tooltip-alpenglow", + }, + description: { + string_id: "mr1-onboarding-theme-description-alpenglow", + }, + }, + ], + }, + primary_button: { + label: { + string_id: "onboarding-theme-primary-button-label", + }, + action: { + navigate: true, + }, + }, + secondary_button: { + label: { + string_id: "mr1-onboarding-theme-secondary-button-label", + }, + action: { + theme: "automatic", + navigate: true, + }, + }, + }, + }, + ], +}; + +// Message to be updated based on finalized MR designs +const MR_ABOUT_WELCOME_DEFAULT = { + id: "MR_WELCOME_DEFAULT", + template: "multistage", + // Allow tests to easily disable transitions. + transitions: Services.prefs.getBoolPref( + "browser.aboutwelcome.transitions", + true + ), + backdrop: + "var(--mr-welcome-background-color) var(--mr-welcome-background-gradient)", + screens: [ + { + id: "AW_PIN_FIREFOX", + content: { + position: "split", + split_narrow_bkg_position: "-155px", + image_alt_text: { + string_id: "mr2022-onboarding-pin-image-alt", + }, + background: + "url('chrome://activity-stream/content/data/content/assets/mr-pintaskbar.svg') var(--mr-secondary-position) no-repeat var(--mr-screen-background-color)", + progress_bar: true, + logo: {}, + title: { + string_id: "mr2022-onboarding-welcome-pin-header", + }, + subtitle: { + string_id: "mr2022-onboarding-welcome-pin-subtitle", + }, + primary_button: { + label: { + string_id: "mr2022-onboarding-pin-primary-button-label", + }, + action: { + navigate: true, + type: "PIN_FIREFOX_TO_TASKBAR", + }, + }, + secondary_button: { + label: { + string_id: "mr2022-onboarding-secondary-skip-button-label", + }, + action: { + navigate: true, + }, + has_arrow_icon: true, + }, + secondary_button_top: { + label: { + string_id: "mr1-onboarding-sign-in-button-label", + }, + action: { + data: { + entrypoint: "activity-stream-firstrun", + where: "tab", + }, + type: "SHOW_FIREFOX_ACCOUNTS", + addFlowParams: true, + }, + }, + }, + }, + { + id: "AW_LANGUAGE_MISMATCH", + content: { + position: "split", + background: "var(--mr-screen-background-color)", + progress_bar: true, + logo: {}, + title: { + string_id: "mr2022-onboarding-live-language-text", + }, + subtitle: { + string_id: "mr2022-language-mismatch-subtitle", + }, + hero_text: { + string_id: "mr2022-onboarding-live-language-text", + useLangPack: true, + }, + languageSwitcher: { + downloading: { + string_id: "onboarding-live-language-button-label-downloading", + }, + cancel: { + string_id: "onboarding-live-language-secondary-cancel-download", + }, + waiting: { string_id: "onboarding-live-language-waiting-button" }, + skip: { string_id: "mr2022-onboarding-secondary-skip-button-label" }, + action: { + navigate: true, + }, + switch: { + string_id: "mr2022-onboarding-live-language-switch-to", + useLangPack: true, + }, + continue: { + string_id: "mr2022-onboarding-live-language-continue-in", + }, + }, + }, + }, + { + id: "AW_SET_DEFAULT", + content: { + position: "split", + split_narrow_bkg_position: "-60px", + image_alt_text: { + string_id: "mr2022-onboarding-default-image-alt", + }, + background: + "url('chrome://activity-stream/content/data/content/assets/mr-settodefault.svg') var(--mr-secondary-position) no-repeat var(--mr-screen-background-color)", + progress_bar: true, + logo: {}, + title: { + string_id: "mr2022-onboarding-set-default-title", + }, + subtitle: { + string_id: "mr2022-onboarding-set-default-subtitle", + }, + primary_button: { + label: { + string_id: "mr2022-onboarding-set-default-primary-button-label", + }, + action: { + navigate: true, + type: "SET_DEFAULT_BROWSER", + }, + }, + secondary_button: { + label: { + string_id: "mr2022-onboarding-secondary-skip-button-label", + }, + action: { + navigate: true, + }, + has_arrow_icon: true, + }, + }, + }, + { + id: "AW_IMPORT_SETTINGS", + content: { + position: "split", + split_narrow_bkg_position: "-42px", + image_alt_text: { + string_id: "mr2022-onboarding-import-image-alt", + }, + background: + "url('chrome://activity-stream/content/data/content/assets/mr-import.svg') var(--mr-secondary-position) no-repeat var(--mr-screen-background-color)", + progress_bar: true, + logo: {}, + title: { + string_id: "mr2022-onboarding-import-header", + }, + subtitle: { + string_id: "mr2022-onboarding-import-subtitle", + }, + primary_button: { + label: { + string_id: + "mr2022-onboarding-import-primary-button-label-no-attribution", + }, + action: { + type: "SHOW_MIGRATION_WIZARD", + data: {}, + navigate: true, + }, + }, + secondary_button: { + label: { + string_id: "mr2022-onboarding-secondary-skip-button-label", + }, + action: { + navigate: true, + }, + has_arrow_icon: true, + }, + }, + }, + { + id: "AW_MOBILE_DOWNLOAD", + content: { + position: "split", + split_narrow_bkg_position: "-160px", + image_alt_text: { + string_id: "mr2022-onboarding-mobile-download-image-alt", + }, + background: + "url('chrome://activity-stream/content/data/content/assets/mr-mobilecrosspromo.svg') var(--mr-secondary-position) no-repeat var(--mr-screen-background-color)", + progress_bar: true, + logo: {}, + title: { + string_id: "mr2022-onboarding-mobile-download-title", + }, + subtitle: { + string_id: "mr2022-onboarding-mobile-download-subtitle", + }, + hero_image: { + url: + "chrome://activity-stream/content/data/content/assets/mobile-download-qr-new-user.svg", + }, + cta_paragraph: { + text: { + string_id: "mr2022-onboarding-mobile-download-cta-text", + string_name: "download-label", + }, + action: { + type: "OPEN_URL", + data: { + args: + "https://www.mozilla.org/firefox/mobile/get-app/?utm_medium=firefox-desktop&utm_source=onboarding-modal&utm_campaign=mr2022&utm_content=new-global", + where: "tab", + }, + }, + }, + secondary_button: { + label: { + string_id: "mr2022-onboarding-secondary-skip-button-label", + }, + action: { + navigate: true, + }, + has_arrow_icon: true, + }, + }, + }, + { + id: "AW_GRATITUDE", + content: { + position: "split", + split_narrow_bkg_position: "-228px", + image_alt_text: { + string_id: "mr2022-onboarding-gratitude-image-alt", + }, + background: + "url('chrome://activity-stream/content/data/content/assets/mr-gratitude.svg') var(--mr-secondary-position) no-repeat var(--mr-screen-background-color)", + progress_bar: true, + logo: {}, + title: { + string_id: "mr2022-onboarding-gratitude-title", + }, + subtitle: { + string_id: "mr2022-onboarding-gratitude-subtitle", + }, + primary_button: { + label: { + string_id: "mr2022-onboarding-gratitude-primary-button-label", + }, + action: { + type: "OPEN_FIREFOX_VIEW", + navigate: true, + }, + }, + secondary_button: { + label: { + string_id: "mr2022-onboarding-gratitude-secondary-button-label", + }, + action: { + navigate: true, + }, + }, + }, + }, + ], +}; + +async function getAddonFromRepository(data) { + const [addonInfo] = await lazy.AddonRepository.getAddonsByIDs([data]); + if (addonInfo.sourceURI.scheme !== "https") { + return null; + } + + return { + name: addonInfo.name, + url: addonInfo.sourceURI.spec, + iconURL: addonInfo.icons["64"] || addonInfo.icons["32"], + type: addonInfo.type, + screenshots: addonInfo.screenshots, + }; +} + +async function getAddonInfo(attrbObj) { + let { content, source } = attrbObj; + try { + if (!content || source !== "addons.mozilla.org") { + return null; + } + // Attribution data can be double encoded + while (content.includes("%")) { + try { + const result = decodeURIComponent(content); + if (result === content) { + break; + } + content = result; + } catch (e) { + break; + } + } + // return_to_amo embeds the addon id in the content + // param, prefixed with "rta:". Translating that + // happens in AddonRepository, however we can avoid + // an API call if we check up front here. + if (content.startsWith("rta:")) { + return await getAddonFromRepository(content); + } + } catch (e) { + console.error("Failed to get the latest add-on version for Return to AMO"); + } + return null; +} + +async function getAttributionContent() { + let attribution = await lazy.AttributionCode.getAttrDataAsync(); + if (attribution?.source === "addons.mozilla.org") { + let addonInfo = await getAddonInfo(attribution); + if (addonInfo) { + return { + ...addonInfo, + template: "return_to_amo", + }; + } + } + if (attribution?.ua) { + return { + ua: decodeURIComponent(attribution.ua), + }; + } + return null; +} + +// Return default multistage welcome content +function getDefaults(templateMR = false) { + const defaultContent = templateMR + ? MR_ABOUT_WELCOME_DEFAULT + : DEFAULT_WELCOME_CONTENT; + return Cu.cloneInto(defaultContent, {}); +} + +let gSourceL10n = null; + +// Localize Firefox download source from user agent attribution to show inside +// import primary button label such as 'Import from <localized browser name>'. +// no firefox as import wizard doesn't show it +const allowedUAs = ["chrome", "edge", "ie"]; +function getLocalizedUA(ua) { + if (!gSourceL10n) { + gSourceL10n = new Localization(["browser/migration.ftl"]); + } + if (allowedUAs.includes(ua)) { + return gSourceL10n.formatValue(`source-name-${ua.toLowerCase()}`); + } + return null; +} + +// Helper to find screens and remove them where applicable. +function removeScreens(check, screens) { + for (let i = 0; i < screens?.length; i++) { + if (check(screens[i])) { + screens.splice(i--, 1); + } + } +} + +// Function to evalute the appropriate string for the welcome screen button label +function evaluateWelcomeScreenButtonLabel(removeDefault, content) { + if (content.templateMR) { + return removeDefault + ? "mr2022-onboarding-get-started-primary-button-label" + : "mr2022-onboarding-set-default-primary-button-label"; + } + return removeDefault + ? "mr1-onboarding-get-started-primary-button-label" + : "mr1-onboarding-set-default-only-primary-button-label"; +} + +function prepareMobileDownload(screens) { + let mobileContent = screens.find(screen => screen.id === "AW_MOBILE_DOWNLOAD") + ?.content; + + if (!mobileContent) { + return; + } + if (!lazy.BrowserUtils.sendToDeviceEmailsSupported()) { + // If send to device emails are not supported for a user's locale, + // remove the send to device link and update the screen text + delete mobileContent.cta_paragraph.action; + mobileContent.cta_paragraph.text = { + string_id: "mr2022-onboarding-no-mobile-download-cta-text", + }; + } + // Update CN specific QRCode url + if (AppConstants.isChinaRepack()) { + mobileContent.hero_image.url = `${mobileContent.hero_image.url.slice( + 0, + mobileContent.hero_image.url.indexOf(".svg") + )}-cn.svg`; + } +} + +function prepareMRContent(content) { + // Expand with logic for finalized MR designs + const { screens } = content; + + // Do not show the screen to users who are already using firefox sync + // and syncing to a mobile device + if (lazy.usesFirefoxSync && lazy.mobileDevices > 0) { + removeScreens(screen => screen.id === "AW_MOBILE_DOWNLOAD", screens); + } else { + prepareMobileDownload(screens); + } + + return content; +} + +async function prepareContentForReact(content) { + const { screens } = content; + + if (content?.template === "return_to_amo") { + return content; + } + + // Change content for Windows 7 because non-light themes aren't quite right. + if (AppConstants.isPlatformAndVersionAtMost("win", "6.1")) { + removeScreens( + screen => ["theme"].includes(screen.content?.tiles?.type), + screens + ); + } + + // Set the primary import button source based on attribution. + if (content?.ua) { + // If available, add the browser source to action data + // and localized browser string args to primary button label + const { label, action } = + content?.screens?.find( + screen => + screen?.content?.primary_button?.action?.type === + "SHOW_MIGRATION_WIZARD" + )?.content?.primary_button ?? {}; + + if (action) { + action.data = { ...action.data, source: content.ua }; + } + + let browserStr = await getLocalizedUA(content.ua); + + if (label?.string_id) { + label.string_id = browserStr + ? "mr1-onboarding-import-primary-button-label-attribution" + : "mr1-onboarding-import-primary-button-label-no-attribution"; + + label.args = browserStr ? { previous: browserStr } : {}; + } + } + + // If already pinned, convert "pin" screen to "welcome" with desired action. + let removeDefault = !content.needDefault; + if (!content.needPin) { + const pinScreen = content.screens?.find(screen => + screen.id?.startsWith("AW_PIN_FIREFOX") + ); + if (pinScreen?.content) { + pinScreen.id = removeDefault ? "AW_GET_STARTED" : "AW_ONLY_DEFAULT"; + pinScreen.content.title = { + string_id: content.templateMR + ? "mr2022-onboarding-welcome-pin-header" + : "mr1-onboarding-welcome-header", + }; + + if (content.templateMR) { + pinScreen.content.subtitle = { + string_id: removeDefault + ? "mr2022-onboarding-get-started-primary-subtitle" + : "mr2022-onboarding-set-default-only-subtitle", + }; + } + pinScreen.content.primary_button = { + label: { + string_id: evaluateWelcomeScreenButtonLabel(removeDefault, content), + }, + action: { + navigate: true, + }, + }; + + // Get started content will navigate without action, so remove "Not now." + if (removeDefault) { + if (!content.templateMR) delete pinScreen.content.secondary_button; + } else { + // The "pin" screen will now handle "default" so remove other "default." + pinScreen.content.primary_button.action.type = "SET_DEFAULT_BROWSER"; + removeDefault = true; + } + } + } + if (removeDefault) { + removeScreens(screen => screen.id?.startsWith("AW_SET_DEFAULT"), screens); + } + + // Remove Firefox Accounts related UI and prevent related metrics. + if (!Services.prefs.getBoolPref("identity.fxaccounts.enabled", false)) { + delete content.screens?.find( + screen => + screen.content?.secondary_button_top?.action?.type === + "SHOW_FIREFOX_ACCOUNTS" + )?.content.secondary_button_top; + content.skipFxA = true; + } + + // Remove the English-only image caption. + if (Services.locale.appLocaleAsBCP47.split("-")[0] !== "en") { + delete content.screens?.find( + screen => screen.content?.help_text?.deleteIfNotEn + )?.content.help_text; + } + + let shouldRemoveLanguageMismatchScreen = true; + if (content.languageMismatchEnabled) { + const screen = content?.screens?.find(s => s.id === "AW_LANGUAGE_MISMATCH"); + if (screen && content.appAndSystemLocaleInfo.canLiveReload) { + // Add the display names for the OS and Firefox languages, like "American English". + function addMessageArgs(obj) { + for (const value of Object.values(obj)) { + if (value?.string_id) { + value.args = content.appAndSystemLocaleInfo.displayNames; + } + } + } + + addMessageArgs(screen.content.languageSwitcher); + addMessageArgs(screen.content); + shouldRemoveLanguageMismatchScreen = false; + } + } + + if (shouldRemoveLanguageMismatchScreen) { + removeScreens(screen => screen.id === "AW_LANGUAGE_MISMATCH", screens); + } + + if (content.templateMR) { + return prepareMRContent(content); + } + + return content; +} + +const AboutWelcomeDefaults = { + prepareContentForReact, + getDefaults, + getAttributionContent, +}; diff --git a/browser/components/newtab/aboutwelcome/lib/AboutWelcomeTelemetry.jsm b/browser/components/newtab/aboutwelcome/lib/AboutWelcomeTelemetry.jsm new file mode 100644 index 0000000000..7725ae6c22 --- /dev/null +++ b/browser/components/newtab/aboutwelcome/lib/AboutWelcomeTelemetry.jsm @@ -0,0 +1,116 @@ +/* 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 = ["AboutWelcomeTelemetry"]; +const { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + ClientID: "resource://gre/modules/ClientID.sys.mjs", + TelemetrySession: "resource://gre/modules/TelemetrySession.sys.mjs", +}); + +XPCOMUtils.defineLazyModuleGetters(lazy, { + PingCentre: "resource:///modules/PingCentre.jsm", + AttributionCode: "resource:///modules/AttributionCode.jsm", +}); +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "structuredIngestionEndpointBase", + "browser.newtabpage.activity-stream.telemetry.structuredIngestion.endpoint", + "" +); +XPCOMUtils.defineLazyGetter(lazy, "telemetryClientId", () => + lazy.ClientID.getClientID() +); +XPCOMUtils.defineLazyGetter( + lazy, + "browserSessionId", + () => lazy.TelemetrySession.getMetadata("").sessionId +); +const TELEMETRY_TOPIC = "about:welcome"; +const PING_TYPE = "onboarding"; +const PING_VERSION = "1"; +const STRUCTURED_INGESTION_NAMESPACE_MS = "messaging-system"; + +class AboutWelcomeTelemetry { + constructor() { + XPCOMUtils.defineLazyPreferenceGetter( + this, + "telemetryEnabled", + "browser.newtabpage.activity-stream.telemetry", + false + ); + } + + /** + * Lazily initialize PingCentre for Activity Stream to send pings + */ + get pingCentre() { + Object.defineProperty(this, "pingCentre", { + value: new lazy.PingCentre({ topic: TELEMETRY_TOPIC }), + }); + return this.pingCentre; + } + + _generateStructuredIngestionEndpoint() { + const uuid = Services.uuid.generateUUID().toString(); + // Structured Ingestion does not support the UUID generated by Services.uuid, + // because it contains leading and trailing braces. Need to trim them first. + const docID = uuid.slice(1, -1); + const extension = `${STRUCTURED_INGESTION_NAMESPACE_MS}/${PING_TYPE}/${PING_VERSION}/${docID}`; + return `${lazy.structuredIngestionEndpointBase}/${extension}`; + } + + /** + * Attach browser attribution data to a ping payload. + * + * It intentionally queries the *cached* attribution data other than calling + * `getAttrDataAsync()` in order to minimize the overhead here. + * For the same reason, we are not querying the attribution data from + * `TelemetryEnvironment.currentEnvironment.settings`. + * + * In practice, it's very likely that the attribution data is already read + * and cached at some point by `AboutWelcomeParent`, so it should be able to + * read the cached results for the most if not all of the pings. + */ + _maybeAttachAttribution(ping) { + const attribution = lazy.AttributionCode.getCachedAttributionData(); + if (attribution && Object.keys(attribution).length) { + ping.attribution = attribution; + } + return ping; + } + + async _createPing(event) { + if (event.event_context && typeof event.event_context === "object") { + event.event_context = JSON.stringify(event.event_context); + } + let ping = { + ...event, + addon_version: Services.appinfo.appBuildID, + locale: Services.locale.appLocaleAsBCP47, + client_id: await lazy.telemetryClientId, + browser_session_id: lazy.browserSessionId, + }; + + return this._maybeAttachAttribution(ping); + } + + async sendTelemetry(event) { + if (!this.telemetryEnabled) { + return; + } + + const ping = await this._createPing(event); + this.pingCentre.sendStructuredIngestionPing( + ping, + this._generateStructuredIngestionEndpoint() + ); + } +} |