commit bc4dae42f9df10e46aa3dd74e6dc1a16ec2bb0b7
parent c14367dcaf72b3d880a474e8bc05382954e045d2
Author: Tom Zhang <tzhang@mozilla.com>
Date: Wed, 31 Dec 2025 00:28:59 +0000
Bug 2007444 - Fix pulling description for current tab; added browser test r=omarg,ai-models-reviewers,bjohns
Differential Revision: https://phabricator.services.mozilla.com/D277612
Diffstat:
4 files changed, 219 insertions(+), 14 deletions(-)
diff --git a/browser/components/aiwindow/models/ChatUtils.sys.mjs b/browser/components/aiwindow/models/ChatUtils.sys.mjs
@@ -62,10 +62,24 @@ export async function getCurrentTabMetadata(depsOverride) {
let description = "";
if (url) {
- description =
- PageDataService.getCached(url)?.description ||
- (await PageDataService.fetchPageData(url))?.description ||
- "";
+ const cachedData = PageDataService.getCached(url);
+ if (cachedData?.description) {
+ description = cachedData.description;
+ } else {
+ try {
+ const actor =
+ browser.browsingContext?.currentWindowGlobal?.getActor("PageData");
+ if (actor) {
+ const pageData = await actor.collectPageData();
+ description = pageData?.description || "";
+ }
+ } catch (e) {
+ console.error(
+ "Failed to collect page description data from current tab:",
+ e
+ );
+ }
+ }
}
return { url, title, description };
diff --git a/browser/components/aiwindow/models/tests/browser/browser.toml b/browser/components/aiwindow/models/tests/browser/browser.toml
@@ -6,5 +6,6 @@ prefs = [
"browser.aiwindow.enabled=true",
]
+["browser_getCurrentTabMetadata.js"]
["browser_get_page_content.js"]
window_attributes = "ai-window"
diff --git a/browser/components/aiwindow/models/tests/browser/browser_getCurrentTabMetadata.js b/browser/components/aiwindow/models/tests/browser/browser_getCurrentTabMetadata.js
@@ -0,0 +1,169 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { getCurrentTabMetadata } = ChromeUtils.importESModule(
+ "moz-src:///browser/components/aiwindow/models/ChatUtils.sys.mjs"
+);
+
+const { PageDataService } = ChromeUtils.importESModule(
+ "moz-src:///browser/components/pagedata/PageDataService.sys.mjs"
+);
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.pagedata.enabled", true]],
+ });
+
+ PageDataService.init();
+
+ registerCleanupFunction(() => {
+ PageDataService.uninit();
+ });
+});
+
+add_task(async function test_getCurrentTabMetadata_basic() {
+ const html = `
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <meta charset="utf-8">
+ <title>Test Page Title</title>
+ <meta name="description" content="This is a test page description">
+ </head>
+ <body>
+ <h1>Test Page Content</h1>
+ </body>
+ </html>
+ `;
+
+ const { url, server } = serveHTML(html);
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url, true);
+
+ try {
+ const metadata = await getCurrentTabMetadata();
+
+ is(metadata.url, url, "Should return the correct URL");
+ is(metadata.title, "Test Page Title", "Should return the correct title");
+ Assert.strictEqual(
+ typeof metadata.description,
+ "string",
+ "Should return a string description"
+ );
+ } finally {
+ BrowserTestUtils.removeTab(tab);
+ await new Promise(resolve => server.stop(resolve));
+ }
+});
+
+add_task(async function test_getCurrentTabMetadata_no_description() {
+ const html = `
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <meta charset="utf-8">
+ <title>Page Without Description</title>
+ </head>
+ <body>
+ <p>This page has no description metadata.</p>
+ </body>
+ </html>
+ `;
+
+ const { url, server } = serveHTML(html);
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url, true);
+
+ try {
+ const metadata = await getCurrentTabMetadata();
+
+ is(metadata.url, url, "Should return the correct URL");
+ is(
+ metadata.title,
+ "Page Without Description",
+ "Should return the correct title"
+ );
+ is(
+ metadata.description,
+ "",
+ "Should return empty string when no description available"
+ );
+ } finally {
+ BrowserTestUtils.removeTab(tab);
+ await new Promise(resolve => server.stop(resolve));
+ }
+});
+
+add_task(async function test_getCurrentTabMetadata_about_blank() {
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:blank",
+ true
+ );
+
+ try {
+ const metadata = await getCurrentTabMetadata();
+
+ is(metadata.url, "about:blank", "Should handle about:blank URL");
+ Assert.notStrictEqual(
+ metadata.title,
+ undefined,
+ "Should have a title (may be empty)"
+ );
+ is(
+ metadata.description,
+ "",
+ "Should have empty description for about:blank"
+ );
+ } finally {
+ BrowserTestUtils.removeTab(tab);
+ }
+});
+
+add_task(async function test_getCurrentTabMetadata_with_cached_data() {
+ const html = `
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <meta charset="utf-8">
+ <title>Cached Test Page</title>
+ <meta name="description" content="Cached page description">
+ </head>
+ <body>
+ <p>Testing cached metadata.</p>
+ </body>
+ </html>
+ `;
+
+ const { url, server } = serveHTML(html);
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url, true);
+
+ try {
+ // Lock the entry first to create a cache entry
+ const browser = tab.linkedBrowser;
+ PageDataService.lockEntry(browser, url);
+
+ // Now add the cached data
+ PageDataService.pageDataDiscovered({
+ url,
+ date: Date.now(),
+ description: "Test cached description",
+ });
+
+ const metadata = await getCurrentTabMetadata();
+
+ is(metadata.url, url, "Should return the correct URL");
+ is(metadata.title, "Cached Test Page", "Should return the correct title");
+ is(
+ metadata.description,
+ "Cached page description",
+ "Should return cached description"
+ );
+
+ // Unlock the entry in cleanup
+ PageDataService.unlockEntry(browser, url);
+ } finally {
+ BrowserTestUtils.removeTab(tab);
+ await new Promise(resolve => server.stop(resolve));
+ }
+});
diff --git a/browser/components/aiwindow/models/tests/xpcshell/test_ChatUtils.js b/browser/components/aiwindow/models/tests/xpcshell/test_ChatUtils.js
@@ -108,21 +108,27 @@ add_task(async function test_getCurrentTabMetadata_fetch_fallback() {
const tracker = { getTopWindow: sb.stub() };
const pageData = {
getCached: sb.stub(),
- fetchPageData: sb.stub(),
+ };
+ const fakeActor = {
+ collectPageData: sb.stub().resolves({
+ description: "Collected description",
+ }),
};
const fakeBrowser = {
currentURI: { spec: "https://example.com/article" },
contentTitle: "",
documentTitle: "Example Article",
+ browsingContext: {
+ currentWindowGlobal: {
+ getActor: sb.stub().returns(fakeActor),
+ },
+ },
};
tracker.getTopWindow.returns({
gBrowser: { selectedBrowser: fakeBrowser },
});
pageData.getCached.returns(null);
- const fetchStub = pageData.fetchPageData.resolves({
- description: "Fetched description",
- });
try {
const result = await getCurrentTabMetadata({
@@ -132,9 +138,18 @@ add_task(async function test_getCurrentTabMetadata_fetch_fallback() {
Assert.deepEqual(result, {
url: "https://example.com/article",
title: "Example Article",
- description: "Fetched description",
+ description: "Collected description",
});
- Assert.ok(fetchStub.calledOnce, "Should fetch description when not cached");
+ Assert.ok(
+ fakeActor.collectPageData.calledOnce,
+ "Should collect page data from actor when not cached"
+ );
+ Assert.ok(
+ fakeBrowser.browsingContext.currentWindowGlobal.getActor.calledWith(
+ "PageData"
+ ),
+ "Should get PageData actor"
+ );
} finally {
sb.restore();
}
@@ -146,13 +161,20 @@ add_task(
const tracker = { getTopWindow: sb.stub() };
const pageData = {
getCached: sb.stub(),
- fetchPageData: sb.stub(),
};
const locale = Services.locale.appLocaleAsBCP47;
+ const fakeActor = {
+ collectPageData: sb.stub(),
+ };
const fakeBrowser = {
currentURI: { spec: "https://mozilla.org" },
contentTitle: "Mozilla",
documentTitle: "Mozilla",
+ browsingContext: {
+ currentWindowGlobal: {
+ getActor: sb.stub().returns(fakeActor),
+ },
+ },
};
tracker.getTopWindow.returns({
@@ -161,7 +183,6 @@ add_task(
pageData.getCached.returns({
description: "Internet for people",
});
- const fetchStub = pageData.fetchPageData;
const clock = sb.useFakeTimers({ now: Date.UTC(2025, 11, 27, 14, 0, 0) });
try {
@@ -191,8 +212,8 @@ add_task(
"Should include tab description"
);
Assert.ok(
- fetchStub.notCalled,
- "Should not fetch when cached data exists"
+ fakeActor.collectPageData.notCalled,
+ "Should not collect page data when cached data exists"
);
} finally {
clock.restore();