commit a732f23dada401e66d35fd2754a64ffb8f623a0b
parent ee0aa61469c0c92714b456b38962725f6644f26f
Author: Giulia Cardieri <gcardieri@mozilla.com>
Date: Tue, 25 Nov 2025 14:25:21 +0000
Bug 1929675 - Update shortcut interaction from hover to click. r=ngrato,ai-frontend-reviewers,yjamora
Differential Revision: https://phabricator.services.mozilla.com/D273450
Diffstat:
3 files changed, 114 insertions(+), 14 deletions(-)
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
@@ -2206,6 +2206,7 @@ pref("browser.ml.chat.provider", "");
pref("browser.ml.chat.shortcuts", true);
pref("browser.ml.chat.shortcuts.custom", true);
pref("browser.ml.chat.shortcuts.longPress", 60000);
+pref("browser.ml.chat.shortcut.onboardingMouseoverCount", 0);
pref("browser.ml.chat.sidebar", true);
pref("browser.ml.linkPreview.allowedLanguages", "en");
diff --git a/browser/components/genai/GenAI.sys.mjs b/browser/components/genai/GenAI.sys.mjs
@@ -108,6 +108,12 @@ XPCOMUtils.defineLazyPreferenceGetter(
"sidebarTools",
"sidebar.main.tools"
);
+XPCOMUtils.defineLazyPreferenceGetter(
+ lazy,
+ "shortcutMouseoverCount",
+ "browser.ml.chat.shortcut.onboardingMouseoverCount",
+ 0
+);
export const GenAI = {
// Cache of potentially localized prompt
@@ -448,12 +454,8 @@ export const GenAI = {
return mozMessageBarEl;
};
- // Detect hover to build and open the popup
- aiActionButton.addEventListener("mouseover", async () => {
- if (chatShortcutsOptionsPanel.state != "closed") {
- return;
- }
-
+ // build the ask popup
+ const buildPopup = async () => {
aiActionButton.setAttribute("type", buttonActiveState);
const vbox = chatShortcutsOptionsPanel.querySelector("vbox");
vbox.innerHTML = "";
@@ -551,6 +553,35 @@ export const GenAI = {
provider: this.getProviderId(),
warning: showWarning,
});
+ };
+
+ // ask popup shows on mouseover only in the first two times
+ const hasMouseoverOnPopup = () => {
+ const mouseoverCounter = lazy.shortcutMouseoverCount;
+ const maxMouseoverCount = 2;
+
+ if (mouseoverCounter >= maxMouseoverCount) {
+ return;
+ }
+
+ if (chatShortcutsOptionsPanel.state == "closed") {
+ Services.prefs.setIntPref(
+ "browser.ml.chat.shortcut.onboardingMouseoverCount",
+ mouseoverCounter + 1
+ );
+ buildPopup();
+ }
+ };
+
+ aiActionButton.addEventListener("mouseover", hasMouseoverOnPopup);
+
+ // Detect click to build and toggle the popup
+ aiActionButton.addEventListener("click", async () => {
+ if (chatShortcutsOptionsPanel.state != "closed") {
+ chatShortcutsOptionsPanel.hidePopup();
+ return;
+ }
+ buildPopup();
});
},
diff --git a/browser/components/genai/tests/browser/browser_chat_shortcuts.js b/browser/components/genai/tests/browser/browser_chat_shortcuts.js
@@ -38,6 +38,7 @@ add_task(async function test_show_shortcuts() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.ml.chat.shortcuts", true],
+ ["browser.ml.chat.shortcut.onboardingMouseoverCount", 0],
["browser.ml.chat.provider", "http://localhost:8080"],
],
});
@@ -73,15 +74,82 @@ add_task(async function test_show_shortcuts() {
const popup = document.getElementById("chat-shortcuts-options-panel");
Assert.equal(popup.state, "closed", "Popup is closed");
+ Assert.equal(
+ Services.prefs.getIntPref(
+ "browser.ml.chat.shortcut.onboardingMouseoverCount"
+ ),
+ 0,
+ "Pref should start at 0"
+ );
+
EventUtils.sendMouseEvent({ type: "mouseover" }, shortcuts);
await BrowserTestUtils.waitForEvent(popup, "popupshown");
+ Assert.equal(
+ Services.prefs.getIntPref(
+ "browser.ml.chat.shortcut.onboardingMouseoverCount"
+ ),
+ 1,
+ "Pref should be 1 after mouseover"
+ );
+ Assert.equal(
+ popup.state,
+ "open",
+ "Popup is open with mouseover the first time"
+ );
+ const hidePopup = BrowserTestUtils.waitForEvent(popup, "popuphidden");
+ popup.hidePopup();
+ await hidePopup;
- Assert.equal(popup.state, "open", "Popup is open");
- events = Glean.genaiChatbot.shortcutsExpanded.testGetValue();
- Assert.equal(events.length, 1, "One shortcuts opened");
- Assert.equal(events[0].extra.selection, 2, "Selected hi");
+ EventUtils.sendMouseEvent({ type: "mouseover" }, shortcuts);
+ await BrowserTestUtils.waitForEvent(popup, "popupshown");
+ Assert.equal(
+ Services.prefs.getIntPref(
+ "browser.ml.chat.shortcut.onboardingMouseoverCount"
+ ),
+ 2,
+ "Pref should be 2 after second mouseover"
+ );
+
+ Assert.equal(
+ popup.state,
+ "open",
+ "Popup is open with mouseover the second time"
+ );
+ const hidePopupSecondTime = BrowserTestUtils.waitForEvent(
+ popup,
+ "popuphidden"
+ );
+ popup.hidePopup();
+ await hidePopupSecondTime;
+
+ EventUtils.sendMouseEvent({ type: "mouseover" }, shortcuts);
+ Assert.equal(
+ Services.prefs.getIntPref(
+ "browser.ml.chat.shortcut.onboardingMouseoverCount"
+ ),
+ 2,
+ "Pref should still be 2 after third mouseover"
+ );
+ Assert.equal(
+ popup.state,
+ "closed",
+ "Popup doesn't open with mouseover after the second time"
+ );
+
+ let beforeClick = Glean.genaiChatbot.shortcutsExpanded.testGetValue();
+ EventUtils.sendMouseEvent({ type: "click" }, shortcuts);
+ await BrowserTestUtils.waitForEvent(popup, "popupshown");
+ Assert.equal(popup.state, "open", "Popup open with click");
+ let afterClick = Glean.genaiChatbot.shortcutsExpanded.testGetValue();
+ Assert.equal(
+ afterClick.length,
+ beforeClick.length + 1,
+ "One shortcuts opened"
+ );
+ const lastEvent = afterClick[afterClick.length - 1];
+ Assert.equal(lastEvent.extra.selection, 2, "Selected hi");
Assert.equal(
- events[0].extra.warning,
+ lastEvent.extra.warning,
"false",
"Warning lable value is correct"
);
@@ -145,7 +213,7 @@ add_task(async function test_show_shortcuts_second_tab() {
const stub = sandbox.stub(GenAI, "addAskChatItems");
const shortcuts = document.querySelector("#ai-action-button");
- EventUtils.sendMouseEvent({ type: "mouseover" }, shortcuts);
+ EventUtils.sendMouseEvent({ type: "click" }, shortcuts);
Assert.equal(stub.callCount, 1, "Shortcuts added on select");
Assert.equal(stub.firstCall.args[0], browser, "Got correct browser");
@@ -202,8 +270,8 @@ add_task(async function test_show_warning_label() {
"Selected enough text"
);
- // Hover button
- EventUtils.sendMouseEvent({ type: "mouseover" }, aiActionButton);
+ // Click button
+ EventUtils.sendMouseEvent({ type: "click" }, aiActionButton);
const chatShortcutsOptionsPanel = document.getElementById(
"chat-shortcuts-options-panel"