commit 438a3ce10eb77fb50d968463b7741117aec5bb4a
parent 170744127aa75c8de003df70574f8a6d6fdf7432
Author: unifolia <jlewis@mozilla.com>
Date: Fri, 12 Dec 2025 04:13:06 +0000
Bug 2005590 - Handle AI Window Opening From Within BrowserWindowTracker r=Mardak,ai-frontend-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D276072
Diffstat:
4 files changed, 83 insertions(+), 32 deletions(-)
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
@@ -1692,8 +1692,6 @@ function OpenBrowserWindow(options) {
options ??= {};
options.openerWindow ??= window;
- AIWindow.handleAIWindowOptions(window, options);
-
let win = BrowserWindowTracker.openWindow(options);
win.addEventListener(
diff --git a/browser/components/aiwindow/ui/modules/AIWindow.sys.mjs b/browser/components/aiwindow/ui/modules/AIWindow.sys.mjs
@@ -36,43 +36,51 @@ export const AIWindow = {
/**
* Sets options for new AI Window if new or inherited conditions are met
*
- * @param {object} win opener window
- * @param {object} options options to be passed into BrowserWindowTracker.openWindow
+ * @param {object} options Used in BrowserWindowTracker.openWindow
+ * @param {object} options.openerWindow Window making the BrowserWindowTracker.openWindow call
+ * @param {object} options.args Array of arguments to pass to new window
+ * @param {boolean} options.aiWindow Should new window be AI Window
+ * @param {boolean} options.private Should new window be Private Window
+ *
+ * @returns {object} Modified arguments appended to the options object
*/
- handleAIWindowOptions(win, options = {}) {
- const { openerWindow } = options;
-
+ handleAIWindowOptions({
+ openerWindow,
+ args,
+ aiWindow,
+ private: isPrivate,
+ } = {}) {
+ // Indicates whether the new window should inherit AI Window state from opener window
const canInheritAIWindow =
- this.isAIWindowActive(win) &&
- !options.private &&
- !Object.hasOwn(options, "aiWindow");
+ this.isAIWindowEnabled() &&
+ this.isAIWindowActive(openerWindow) &&
+ !isPrivate &&
+ !aiWindow;
const willOpenAIWindow =
- openerWindow &&
- openerWindow.AIWindow?.isAIWindowEnabled &&
- (options.aiWindow || canInheritAIWindow);
+ (aiWindow && this.isAIWindowEnabled()) || canInheritAIWindow;
if (!willOpenAIWindow) {
- return;
+ return args;
}
- options.args ??= Cc["@mozilla.org/array;1"].createInstance(
- Ci.nsIMutableArray
- );
+ args ??= Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
- if (!options.args.length) {
+ if (!args.length) {
const aiWindowURI = Cc["@mozilla.org/supports-string;1"].createInstance(
Ci.nsISupportsString
);
aiWindowURI.data = "chrome://browser/content/aiwindow/aiWindow.html";
- options.args.appendElement(aiWindowURI);
+ args.appendElement(aiWindowURI);
const aiOption = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
Ci.nsIWritablePropertyBag2
);
- aiOption.setPropertyAsBool("ai-window", options.aiWindow);
- options.args.appendElement(aiOption);
+ aiOption.setPropertyAsBool("ai-window", aiWindow);
+ args.appendElement(aiOption);
}
+
+ return args;
},
/**
@@ -83,7 +91,7 @@ export const AIWindow = {
*/
isAIWindowActive(win) {
- return win.document.documentElement.hasAttribute("ai-window");
+ return !!win && win.document.documentElement.hasAttribute("ai-window");
},
/**
diff --git a/browser/components/aiwindow/ui/test/browser/browser_open_aiwindow.js b/browser/components/aiwindow/ui/test/browser/browser_open_aiwindow.js
@@ -147,6 +147,43 @@ add_task(async function test_button_actions() {
await SpecialPowers.popPrefEnv();
});
+add_task(async function test_openNewBrowserWindow_and_ai_inherit() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.aiwindow.enabled", true]],
+ });
+
+ const newAIWindow = await BrowserTestUtils.openNewBrowserWindow({
+ openerWindow: null,
+ aiWindow: true,
+ });
+
+ Assert.ok(
+ newAIWindow.document.documentElement.hasAttribute("ai-window"),
+ "BrowserTestUtils.openNewBrowserWindow({ aiWindow: true }) should open an AI Window"
+ );
+
+ await SpecialPowers.popPrefEnv();
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.aiwindow.enabled", false]],
+ });
+
+ const newWindowAfterDisabledAI = await BrowserTestUtils.openNewBrowserWindow({
+ openerWindow: newAIWindow,
+ aiWindow: false,
+ });
+
+ Assert.ok(
+ !newWindowAfterDisabledAI.document.documentElement.hasAttribute(
+ "ai-window"
+ ),
+ "BrowserTestUtils.openNewBrowserWindow({ aiWindow: false }) should not open a new AI Window from an existing AI Window"
+ );
+
+ await BrowserTestUtils.closeWindow(newAIWindow);
+ await BrowserTestUtils.closeWindow(newWindowAfterDisabledAI);
+ await SpecialPowers.popPrefEnv();
+});
+
function checkMenuItemVisibility(
aiWindowEnabled,
aiOpenerButton,
diff --git a/browser/modules/BrowserWindowTracker.sys.mjs b/browser/modules/BrowserWindowTracker.sys.mjs
@@ -19,6 +19,8 @@ XPCOMUtils.defineLazyServiceGetters(lazy, {
});
ChromeUtils.defineESModuleGetters(lazy, {
+ AIWindow:
+ "moz-src:///browser/components/aiwindow/ui/modules/AIWindow.sys.mjs",
HomePage: "resource:///modules/HomePage.sys.mjs",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
});
@@ -301,6 +303,8 @@ export const BrowserWindowTracker = {
* An existing browser window to open the new one from.
* @param {boolean} [options.private]
* True to make the window a private browsing window.
+ * @param {boolean} [options.aiWindow]
+ * True to make the window an AI browsing window.
* @param {string} [options.features]
* Additional window features to give the new window.
* @param {boolean} [options.all]
@@ -317,16 +321,20 @@ export const BrowserWindowTracker = {
*
* @returns {Window}
*/
- openWindow({
- openerWindow = undefined,
- private: isPrivate = false,
- aiWindow = false,
- features = undefined,
- all = true,
- args = null,
- remote = undefined,
- fission = undefined,
- } = {}) {
+ openWindow(options = {}) {
+ let {
+ openerWindow = undefined,
+ private: isPrivate = false,
+ aiWindow = false,
+ features = undefined,
+ all = true,
+ args = null,
+ remote = undefined,
+ fission = undefined,
+ } = options;
+
+ args = lazy.AIWindow.handleAIWindowOptions(options);
+
let windowFeatures = "chrome,dialog=no";
if (all) {
windowFeatures += ",all";