commit 92325e1fa4edd0ece34dc0ecbdf34536303ead1c
parent e046b7fef69c08c01cb2f8dd0b998d0968ff419b
Author: Punam Dahiya <pdahiya@mozilla.com>
Date: Tue, 23 Dec 2025 20:17:31 +0000
Bug 2007177 - Implement SpecialMessageAction to invoke Accounts Authentication flow r=ai-frontend-reviewers,omc-reviewers,czhou,jprickett
Differential Revision: https://phabricator.services.mozilla.com/D277236
Diffstat:
7 files changed, 172 insertions(+), 0 deletions(-)
diff --git a/browser/components/aiwindow/ui/modules/AIWindowAccountAuth.sys.mjs b/browser/components/aiwindow/ui/modules/AIWindowAccountAuth.sys.mjs
@@ -0,0 +1,113 @@
+/* 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/. */
+
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ SpecialMessageActions:
+ "resource://messaging-system/lib/SpecialMessageActions.sys.mjs",
+});
+
+ChromeUtils.defineLazyGetter(lazy, "fxAccounts", () => {
+ return ChromeUtils.importESModule(
+ "resource://gre/modules/FxAccounts.sys.mjs"
+ ).getFxAccountsSingleton();
+});
+
+ChromeUtils.defineLazyGetter(lazy, "log", function () {
+ return console.createInstance({
+ prefix: "AIWindowAccountAuth",
+ maxLogLevelPref: Services.prefs.getBoolPref("browser.aiwindow.log", false)
+ ? "Debug"
+ : "Warn",
+ });
+});
+
+// Temporary gating while feature is in development
+// To be set to true by default before MVP launch
+XPCOMUtils.defineLazyPreferenceGetter(
+ lazy,
+ "AIWindowRequireSignIn",
+ "browser.aiwindow.requireSignIn",
+ false
+);
+XPCOMUtils.defineLazyPreferenceGetter(
+ lazy,
+ "hasAIWindowToSConsent",
+ "browser.aiwindow.tos.hasConsent",
+ false
+);
+
+export const AIWindowAccountAuth = {
+ get hasToSConsent() {
+ return lazy.hasAIWindowToSConsent;
+ },
+
+ set hasToSConsent(value) {
+ Services.prefs.setBoolPref("browser.aiwindow.tos.hasConsent", value);
+ },
+
+ async isSignedIn() {
+ try {
+ const userData = await lazy.fxAccounts.getSignedInUser();
+ return !!userData;
+ } catch (error) {
+ lazy.log.error("Error checking sign-in status:", error);
+ return false;
+ }
+ },
+
+ requiresSignIn() {
+ return lazy.AIWindowRequireSignIn;
+ },
+
+ async canAccessAIWindow() {
+ if (!this.requiresSignIn()) {
+ return true;
+ }
+ if (!this.hasToSConsent) {
+ return false;
+ }
+ return await this.isSignedIn();
+ },
+
+ async promptSignIn(browser) {
+ try {
+ const data = {
+ autoClose: false,
+ entrypoint: "aiwindow",
+ extraParams: {
+ service: "aiwindow",
+ },
+ };
+ const signedIn = await lazy.SpecialMessageActions.fxaSignInFlow(
+ data,
+ browser
+ );
+ if (signedIn) {
+ this.hasToSConsent = true;
+ }
+ return signedIn;
+ } catch (error) {
+ lazy.log.error("Error prompting sign-in:", error);
+ throw error;
+ }
+ },
+
+ async launchAIWindow(browser) {
+ if (!(await this.canAccessAIWindow())) {
+ const signedIn = await this.promptSignIn(browser);
+ if (!signedIn) {
+ lazy.log.error("User did not sign in successfully.");
+ return false;
+ }
+ }
+ // Proceed with launching the AI window
+ // Tobe updated with window switching toggleWindow call implemented with fix of bug 2006469
+ browser.ownerGlobal.OpenBrowserWindow({ aiWindow: true });
+ return true;
+ },
+};
diff --git a/browser/components/aiwindow/ui/moz.build b/browser/components/aiwindow/ui/moz.build
@@ -12,6 +12,7 @@ MOZ_SRC_FILES += [
"actors/AIChatContentChild.sys.mjs",
"actors/AIChatContentParent.sys.mjs",
"modules/AIWindow.sys.mjs",
+ "modules/AIWindowAccountAuth.sys.mjs",
"modules/ChatConstants.sys.mjs",
"modules/ChatConversation.sys.mjs",
"modules/ChatMessage.sys.mjs",
diff --git a/toolkit/components/messaging-system/lib/SpecialMessageActions.sys.mjs b/toolkit/components/messaging-system/lib/SpecialMessageActions.sys.mjs
@@ -10,6 +10,9 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
// eslint-disable-next-line mozilla/no-browser-refs-in-toolkit
+ AIWindowAccountAuth:
+ "moz-src:///browser/components/aiwindow/ui/modules/AIWindowAccountAuth.sys.mjs",
+ // eslint-disable-next-line mozilla/no-browser-refs-in-toolkit
CustomizableUI:
"moz-src:///browser/components/customizableui/CustomizableUI.sys.mjs",
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
@@ -749,6 +752,9 @@ export const SpecialMessageActions = {
case "FXA_SIGNIN_FLOW":
/** @returns {Promise<boolean>} */
return this.fxaSignInFlow(action.data, browser);
+ case "FXA_AIWINDOW_SIGNIN_FLOW":
+ /** @returns {Promise<boolean>} */
+ return lazy.AIWindowAccountAuth.launchAIWindow(browser);
case "OPEN_PROTECTION_PANEL": {
let { gProtectionsHandler } = window;
gProtectionsHandler.showProtectionsPopup({});
diff --git a/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/SpecialMessageActionSchemas.json b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/SpecialMessageActionSchemas.json
@@ -799,6 +799,18 @@
"properties": {
"type": {
"type": "string",
+ "enum": ["FXA_AIWINDOW_SIGNIN_FLOW"]
+ }
+ },
+ "required": ["type"],
+ "additionalProperties": false,
+ "description": "Opens AI Window after successful authentication with Accounts sign-in flow"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
"enum": ["CREATE_TASKBAR_TAB"]
}
},
diff --git a/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/index.md b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/index.md
@@ -136,6 +136,14 @@ window or tab closed before sign-in could be completed. In messaging surfaces us
Encodes some information that the origin was from about:welcome by default.
+### `FXA_AIWINDOW_SIGNIN_FLOW`
+
+Opens a customized AI Window Firefox accounts sign-up or sign-in flow, and redirects user to AI Window after successful authentication.
+
+Returns a Promise that resolves to `true` if sign-in succeeded, or to `false` if the sign-in
+window or tab closed before sign-in could be completed.
+
+- args: (none)
### `SHOW_MIGRATION_WIZARD`
diff --git a/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser.toml b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser.toml
@@ -31,6 +31,8 @@ support-files = [
["browser_sma_docs.js"]
+["browser_sma_fxa_aiwindow_signin_flow.js"]
+
["browser_sma_handle_multiaction.js"]
["browser_sma_open_about_page.js"]
diff --git a/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_fxa_aiwindow_signin_flow.js b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_fxa_aiwindow_signin_flow.js
@@ -0,0 +1,30 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { AIWindowAccountAuth } = ChromeUtils.importESModule(
+ "moz-src:///browser/components/aiwindow/ui/modules/AIWindowAccountAuth.sys.mjs"
+);
+
+add_task(async function test_FXA_AIWINDOW_SIGNIN_FLOW() {
+ let launchAIWindowStub = sinon.stub(AIWindowAccountAuth, "launchAIWindow");
+ launchAIWindowStub.resolves(true);
+
+ await SMATestUtils.executeAndValidateAction({
+ type: "FXA_AIWINDOW_SIGNIN_FLOW",
+ });
+
+ Assert.equal(
+ launchAIWindowStub.callCount,
+ 1,
+ "Should call launchAIWindow once"
+ );
+
+ Assert.ok(
+ launchAIWindowStub.firstCall.args[0],
+ "Should be called with browser argument"
+ );
+
+ launchAIWindowStub.restore();
+});