diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 6a55afad6c24a..11dcfb785d97a 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1194,8 +1194,9 @@ pref("browser.smartwindow.insights", "{}"); pref("browser.smartwindow.key", "sk-xblVm-OUfsPY0C1dER1LLQ"); pref("browser.smartwindow.model", "qwen3-235b-a22b-instruct-2507-maas"); pref("browser.smartwindow.chatHistory.loglevel", "Warn"); -pref("browser.smartwindow.skipOnboarding", true); pref("browser.smartwindow.requireSignIn", false); +pref("browser.smartwindow.tos", false); +pref("browser.smartwindow.isfirstrun", false); // Scripts & Windows prefs pref("dom.disable_open_during_load", true); diff --git a/browser/base/content/browser-smart-window.js b/browser/base/content/browser-smart-window.js index 898350ca3579d..8aab6eea12685 100644 --- a/browser/base/content/browser-smart-window.js +++ b/browser/base/content/browser-smart-window.js @@ -8,6 +8,9 @@ var SmartWindow = { PAGE_URL: Services.io.newURI( "chrome://browser/content/smartwindow/smartwindow.html" ), + FIRST_RUN_URL: Services.io.newURI( + "chrome://browser/content/smartwindow/firstrun.html" + ), _initialized: false, _viewInitialized: false, @@ -67,7 +70,7 @@ var SmartWindow = { }, _isSmartPage(browser) { - return !!browser?.currentURI?.equalsExceptRef(this.PAGE_URL); + return !!browser?.currentURI?.equalsExceptRef(this.PAGE_URL) || !!browser?.currentURI?.equalsExceptRef(this.FIRST_RUN_URL); }, _ensureViewInitialized() { @@ -92,61 +95,51 @@ var SmartWindow = { this.toggleSmartWindow(); break; case "smart-window-switch-smart": { - const skipOnboarding = Services.prefs.getBoolPref( - "browser.smartwindow.skipOnboarding", - true - ); - const completedOnboarding = Services.prefs.getBoolPref( - "messaging-system-action.smart-window-tos", + const requireSignIn = Services.prefs.getBoolPref( + "browser.smartwindow.requireSignIn", false ); - if (skipOnboarding || completedOnboarding) { - const requireSignIn = Services.prefs.getBoolPref( - "browser.smartwindow.requireSignIn", - false + if (!requireSignIn) { + this.toggleSmartWindow(); + break; + } + const { UIState } = ChromeUtils.importESModule( + "resource://services-sync/UIState.sys.mjs" + ); + const currentState = UIState.get(); + + if (currentState.status !== UIState.STATUS_SIGNED_IN) { + console.warn( + "[Smart Window] User not authenticated, sign in with FxA" ); - if (requireSignIn) { - const { UIState } = ChromeUtils.importESModule( - "resource://services-sync/UIState.sys.mjs" + try { + const { SpecialMessageActions } = ChromeUtils.importESModule( + "resource://messaging-system/lib/SpecialMessageActions.sys.mjs" + ); + // FXA_SMART_WINDOW_SIGNIN_FLOW handles toggling smart window on success + // TODO: we should await handleAction and set tos and isfirstrun pref here + // instead of SpecialMessageActions + SpecialMessageActions.handleAction( + { + type: "FXA_SMART_WINDOW_SIGNIN_FLOW", + data: { + entrypoint: "aimode", + }, + }, + gBrowser.selectedBrowser + ); + break; + } catch (error) { + console.error( + "[Smart Window] Error during FxA sign-in:", + error ); - const currentState = UIState.get(); - - if (currentState.status !== UIState.STATUS_SIGNED_IN) { - console.warn( - "[Smart Window] User not authenticated, sign in with FxA" - ); - - try { - const { SpecialMessageActions } = ChromeUtils.importESModule( - "resource://messaging-system/lib/SpecialMessageActions.sys.mjs" - ); - // FXA_SMART_WINDOW_SIGNIN_FLOW handles toggling smart window on success - SpecialMessageActions.handleAction( - { - type: "FXA_SMART_WINDOW_SIGNIN_FLOW", - data: { - entrypoint: "aimode", - }, - }, - gBrowser.selectedBrowser - ); - break; - } catch (error) { - console.error( - "[Smart Window] Error during FxA sign-in:", - error - ); - } - } } - - this.toggleSmartWindow(); } else { - this.showOnboarding(); + this.toggleSmartWindow(); } - break; } case "smart-window-dev-onboarding": @@ -187,13 +180,18 @@ var SmartWindow = { } }, - showOnboarding() { - window.openTrustedLinkIn( - Services.urlFormatter.formatURL( - "chrome://browser/content/smartwindow/welcome.html" - ), - "tab" - ); + // Shows Post login first run onboarding + async showOnboarding() { + return new Promise(resolve => { + window.openTrustedLinkIn( + Services.urlFormatter.formatURL( + "chrome://browser/content/smartwindow/firstrun.html" + ), + "tab" + ); + // Resolve after a brief delay to ensure the window is opened + setTimeout(() => resolve(), 2000); + }); }, toggleSmartWindow() { @@ -223,8 +221,14 @@ var SmartWindow = { * Can be omitted if not switching from classic to smart * window mode. */ - reconcileUIToSmartWindowState(oldNewTabURL = "") { + async reconcileUIToSmartWindowState(oldNewTabURL = "") { if (this.isSmartWindowActive()) { + // Show the first run onboarding first time user switches to smart window mode + if (Services.prefs.getBoolPref("browser.smartwindow.isfirstrun", false)) { + Services.prefs.setBoolPref("browser.smartwindow.isfirstrun", false); + await this.showOnboarding(); + } + // Check if we're on a smart window page const isSmartWindowPage = this._isSmartPage(gBrowser.selectedBrowser); diff --git a/browser/components/smartwindow/content/firstrun.html b/browser/components/smartwindow/content/firstrun.html new file mode 100644 index 0000000000000..e3da9cb7ff6ce --- /dev/null +++ b/browser/components/smartwindow/content/firstrun.html @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + Welcome Screen + + + +
+ + + + + diff --git a/browser/components/smartwindow/content/firstrun.js b/browser/components/smartwindow/content/firstrun.js new file mode 100644 index 0000000000000..0bf53b752395e --- /dev/null +++ b/browser/components/smartwindow/content/firstrun.js @@ -0,0 +1,276 @@ +/* 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 lazy = {}; +const { topChromeWindow } = window.browsingContext; +const { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); + +ChromeUtils.defineESModuleGetters(lazy, { + AboutWelcomeParent: "resource:///actors/AboutWelcomeParent.sys.mjs", +}); + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "onboardingConfig", + "browser.smartwindow.onboarding.config", + JSON.stringify({ + id: "smart-window-welcome", + template: "spotlight", + modal: "tab", + transitions: true, + backdrop: "linear-gradient(0deg, #D9C7F8 0%, #F6EEFC 102.21%)", + screens: [ + { + id: "welcome_screen", + auto_advance: "additional_button", + force_hide_steps_indicator: true, + content: { + fullscreen: true, + hide_secondary_section: "responsive", + narrow: true, + position: "split", + title: { + fontWeight: 600, + fontSize: "55px", + width: "800px", + textAlign: "center", + raw: "Welcome to Smart Window", + }, + title_style: "fancy shine", + text_color: "dark", + primary_button: { + label: "Next", + action: { + navigate: true + }, + }, + }, + }, + { + id: "CHOOSE_MODEL", + force_hide_steps_indicator: true, + content: { + fullscreen: true, + hide_secondary_section: "responsive", + narrow: true, + position: "split", + screen_style: { + width: "650px", + height: "500px", + }, + title: { + raw: "Pick a model to start 1", + }, + subtitle: { + raw: "You can switch anytime - and trying a few helps you find the best fit.", + }, + tiles: { + type: "single-select", + autoTrigger: false, + action: { + picker: "", + }, + data: [ + { + defaultValue: true, + id: "model_1", + icon: { + background: + "center / contain no-repeat url('https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/733144c8-a453-49eb-aff7-27a10786fbc1.svg')", + marginBlockStart: "8px", + + }, + + label: { + raw: "Fast & Clear", + fontSize: 17, + fontWeight: 600, + }, + body: { + raw: "Quick answer to everyday questions", + color: "var(--text-color-deemphasized)", + }, + action: { + type: "SET_PREF", + data: { + pref: { + name: "aidemo.model.choice", + value: "model_1", + }, + }, + }, + }, + { + id: "model_2", + icon: { + background: + "center / contain no-repeat url('https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/733144c8-a453-49eb-aff7-27a10786fbc1.svg')", + marginBlockStart: "8px", + + }, + label: { + raw: "Fast & Clear", + fontSize: 17, + fontWeight: 600, + }, + body: { + raw: "Quick answer to everyday questions", + color: "var(--text-color-deemphasized)", + }, + action: { + type: "SET_PREF", + data: { + pref: { + name: "aidemo.model.choice", + value: "model_2", + }, + }, + }, + }, + { + id: "model_3", + icon: { + background: + "center / contain no-repeat url('https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/733144c8-a453-49eb-aff7-27a10786fbc1.svg')", + marginBlockStart: "8px", + + }, + label: { + raw: "Fast & Clear", + fontSize: 17, + fontWeight: 600, + }, + body: { + raw: "Quick answer to everyday questions", + color: "var(--text-color-deemphasized)", + }, + action: { + type: "SET_PREF", + data: { + pref: { + name: "aidemo.model.choice", + value: "model_3", + }, + }, + }, + } + ] + }, + primary_button: { + label: { + raw: "Next" + }, + action: { + navigate: true + } + }, + } + }, + { + id: "APPLY_INSIGHTS", + content: { + position: "center", + screen_style: { + width: "650px", + height: "500px", + }, + title: { + raw: "Smarter browsing starts now", + }, + subtitle: { + raw: "Get personalized answers fast. Compare info across tabs. Find what you need in your history in your words, not keywords.", + }, + above_button_content: [ + { + type: "image", + url: "chrome://browser/content/smartwindow/insights.png", + width: "500px", + height: "200px" + } + ], + primary_button: { + label: { + raw: "Back" + }, + action: { + navigate: true, + goBack: true + } + }, + additional_button: { + label: "Let's Go", + style: "primary", + flow: "row", + action: { + navigate: true + } + }, + } + } + ], + }) +); + +function addStylesheet(href) { + const link = document.head.appendChild(document.createElement("link")); + link.rel = "stylesheet"; + link.href = href; +} + +function renderMultistage(ready) { + const AWParent = new lazy.AboutWelcomeParent(); + const receive = name => data => + AWParent.onContentMessage( + `AWPage:${name}`, + data, + topChromeWindow.gBrowser.selectedBrowser + ); + + // Expose top level functions expected by the bundle. + window.AWGetFeatureConfig = () => JSON.parse(lazy.onboardingConfig); + window.AWGetSelectedTheme = receive("GET_SELECTED_THEME"); + window.AWGetInstalledAddons = receive("GET_INSTALLED_ADDONS"); + window.AWSelectTheme = data => receive("SELECT_THEME")(data?.toUpperCase()); + window.AWSendEventTelemetry = receive("TELEMETRY_EVENT"); + + window.AWSendToDeviceEmailsSupported = receive( + "SEND_TO_DEVICE_EMAILS_SUPPORTED" + ); + window.AWAddScreenImpression = receive("ADD_SCREEN_IMPRESSION"); + window.AWSendToParent = (name, data) => receive(name)(data); + window.AWFinish = () => { + window.close(); + }; + window.AWWaitForMigrationClose = receive("WAIT_FOR_MIGRATION_CLOSE"); + window.AWEvaluateScreenTargeting = receive("EVALUATE_SCREEN_TARGETING"); + window.AWEvaluateAttributeTargeting = receive("EVALUATE_ATTRIBUTE_TARGETING"); + + // Update styling to be compatible with about:welcome. + addStylesheet("chrome://browser/content/aboutwelcome/aboutwelcome.css"); + + document.body.classList.add("onboardingContainer"); + document.body.id = "multi-stage-message-root"; + // This value is reported as the "page" in telemetry + document.body.dataset.page = "smart-window-welcome"; + const bundleScript = document.head.appendChild( + document.createElement("script") + ); + bundleScript.src = + "chrome://browser/content/aboutwelcome/aboutwelcome.bundle.js"; + + ready(); +} + +// Initialize when DOM is ready +if (document.readyState === "loading") { + document.addEventListener( + "DOMContentLoaded", + () => renderMultistage(() => { }), + { once: true } + ); +} else { + renderMultistage(() => { }); +} diff --git a/browser/components/smartwindow/content/insights.png b/browser/components/smartwindow/content/insights.png new file mode 100644 index 0000000000000..1cb533db4db6d Binary files /dev/null and b/browser/components/smartwindow/content/insights.png differ diff --git a/browser/components/smartwindow/content/smartwindow.css b/browser/components/smartwindow/content/smartwindow.css index a47491aba6c4e..be25bd796a47e 100644 --- a/browser/components/smartwindow/content/smartwindow.css +++ b/browser/components/smartwindow/content/smartwindow.css @@ -1255,12 +1255,22 @@ body { } #multi-stage-message-root { + --background-color-canvas: transparent; + /*--single-select-hover-color: #FCFAFE;*/ + height: 100vh; .welcome-text { max-width: 600px; } - .main-content { - background-color: rgba(247, 230, 251, 1); + /*background-color: #FCFAFE;*/ + } + + .screen { + /*background-color: rgba(247, 230, 251, 1);*/ + } + + .onboardingContainer .tiles-single-select-section.single-select .select-item:hover{ + background-color: #FCFAFE; } } diff --git a/browser/components/smartwindow/content/welcome.js b/browser/components/smartwindow/content/welcome.js index 750957a88035d..f9f975d0b9747 100644 --- a/browser/components/smartwindow/content/welcome.js +++ b/browser/components/smartwindow/content/welcome.js @@ -57,15 +57,6 @@ XPCOMUtils.defineLazyPreferenceGetter( dismiss: true, data: { actions: [ - { - type: "SET_PREF", - data: { - pref: { - name: "messaging-system-action.smart-window-tos", - value: true, - }, - }, - }, { data: { entrypoint: "aimode", @@ -100,12 +91,6 @@ function addStylesheet(href) { link.href = href; } -// TODO -// Check if a) is signedIn b) Has FxAccount and not signed in c) doesn't have FxAccount -// a) Check consent stored in pref `messaging-system-action.smart-window-tos` , if yes toggle to smart window without redirect to account.firefox.com -// b) and c) Redirect to account.firefox.com for account creation and signin and on success call toggle smart window -// See GENAI-2201 - function renderMultistage(ready) { const AWParent = new lazy.AboutWelcomeParent(); const receive = name => data => diff --git a/browser/components/smartwindow/jar.mn b/browser/components/smartwindow/jar.mn index 34e67304f076b..fb0beaf19d26b 100644 --- a/browser/components/smartwindow/jar.mn +++ b/browser/components/smartwindow/jar.mn @@ -23,3 +23,6 @@ browser.jar: content/browser/smartwindow/utils.mjs (content/utils.mjs) content/browser/smartwindow/welcome.html (content/welcome.html) content/browser/smartwindow/welcome.js (content/welcome.js) + content/browser/smartwindow/firstrun.html (content/firstrun.html) + content/browser/smartwindow/firstrun.js (content/firstrun.js) + content/browser/smartwindow/insights.png (content/insights.png) diff --git a/toolkit/components/messaging-system/lib/SpecialMessageActions.sys.mjs b/toolkit/components/messaging-system/lib/SpecialMessageActions.sys.mjs index a5937e9a02e3c..1b24fd3faf968 100644 --- a/toolkit/components/messaging-system/lib/SpecialMessageActions.sys.mjs +++ b/toolkit/components/messaging-system/lib/SpecialMessageActions.sys.mjs @@ -284,6 +284,7 @@ export const SpecialMessageActions = { "termsofuse.minimumVersion", "privacy.trackingprotection.allow_list.baseline.enabled", "privacy.trackingprotection.allow_list.convenience.enabled", + "browser.smartwindow.tos", ]; // Array of prefs that are allowed to be edited when SET_PREF is called on @@ -756,6 +757,10 @@ export const SpecialMessageActions = { } if (await this.fxaSignInFlow(action.data, browser)) { + if (!Services.prefs.getBoolPref("browser.smartwindow.tos", false)){ + Services.prefs.setBoolPref("browser.smartwindow.isfirstrun", true); + } + Services.prefs.setBoolPref("browser.smartwindow.tos", true); window.SmartWindow.toggleSmartWindow(); } break;