Bug 1943391 - Initial entrypoint to detect preview reqeust - r=Mardak,firefox-desktop-core-reviewers ,firefox-ai-ml-reviewers,mossop
- alt+hover to trigger LinkPreview - add LinkPreview.sys.mjs modules - disable browser.ml.linkPreview.enabled by default - use setOverLink and LinkPreview.sys to handle keyboard shortcut - dispatch CustomEvent OverLink from browser.js - add/remove event listeners in sync with state of "browser.ml.linkPreview.enabled" - update all_files_referenced to handle moz-src Differential Revision: https://phabricator.services.mozilla.com/D239535
This commit is contained in:
@@ -2114,6 +2114,9 @@ pref("browser.ml.chat.shortcuts.custom", true);
|
||||
pref("browser.ml.chat.shortcuts.longPress", 60000);
|
||||
pref("browser.ml.chat.sidebar", true);
|
||||
|
||||
// Whether link preview feature should be active
|
||||
pref("browser.ml.linkPreview.enabled", false);
|
||||
|
||||
// Block insecure active content on https pages
|
||||
pref("security.mixed_content.block_active_content", true);
|
||||
|
||||
|
||||
@@ -2359,6 +2359,12 @@ var XULBrowserWindow = {
|
||||
* passed on to LinkTargetDisplay.
|
||||
*/
|
||||
setOverLink(url, options = undefined) {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("OverLink", {
|
||||
detail: { url },
|
||||
})
|
||||
);
|
||||
|
||||
if (url) {
|
||||
url = Services.textToSubURI.unEscapeURIForUI(url);
|
||||
|
||||
|
||||
@@ -496,7 +496,10 @@ function parseManifest(manifestUri) {
|
||||
} else if (type == "category") {
|
||||
if (gInterestingCategories.has(argv[0])) {
|
||||
gReferencesFromCode.set(argv[2], null);
|
||||
} else if (argv[1].startsWith("resource://")) {
|
||||
} else if (
|
||||
argv[1].startsWith("resource://") ||
|
||||
argv[1].startsWith("moz-src://")
|
||||
) {
|
||||
// Assume that any resource paths immediately after the category name
|
||||
// are for use with BrowserUtils.callModulesFromCategory (rather than
|
||||
// having to hardcode a list of categories in this test).
|
||||
|
||||
@@ -27,10 +27,12 @@ category browser-window-domcontentloaded resource:///modules/CustomizableUI.sys.
|
||||
|
||||
category browser-window-delayed-startup resource:///modules/ContentAnalysis.sys.mjs ContentAnalysis.initialize
|
||||
category browser-window-delayed-startup resource:///modules/HomePage.sys.mjs HomePage.delayedStartup
|
||||
category browser-window-delayed-startup moz-src:///browser/components/genai/LinkPreview.sys.mjs LinkPreview.init
|
||||
category browser-window-delayed-startup resource:///modules/ReportBrokenSite.sys.mjs ReportBrokenSite.init
|
||||
category browser-window-delayed-startup moz-src:///browser/components/search/SearchUIUtils.sys.mjs SearchUIUtils.init
|
||||
|
||||
category browser-window-unload resource:///modules/BrowserDOMWindow.sys.mjs BrowserDOMWindow.teardownInWindow
|
||||
category browser-window-unload moz-src:///browser/components/genai/LinkPreview.sys.mjs LinkPreview.teardown
|
||||
category browser-window-unload moz-src:///browser/components/tabbrowser/NewTabPagePreloading.sys.mjs NewTabPagePreloading.removePreloadedBrowser
|
||||
|
||||
# App startup consumers
|
||||
|
||||
179
browser/components/genai/LinkPreview.sys.mjs
Normal file
179
browser/components/genai/LinkPreview.sys.mjs
Normal file
@@ -0,0 +1,179 @@
|
||||
/* eslint-disable no-console */
|
||||
/**
|
||||
* 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 = {};
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
lazy,
|
||||
"gLinkPreviewEnabled",
|
||||
"browser.ml.linkPreview.enabled",
|
||||
false,
|
||||
(_pref, _old, val) => LinkPreview.onEnabledPref(val)
|
||||
);
|
||||
|
||||
export const LinkPreview = {
|
||||
keyboardComboActive: false,
|
||||
_windowStates: new Map(),
|
||||
|
||||
/**
|
||||
* Handles the preference change for enabling/disabling Link Preview.
|
||||
* It adds or removes event listeners for all tracked windows based on the new preference value.
|
||||
*
|
||||
* @param {boolean} enabled - The new state of the Link Preview preference.
|
||||
*/
|
||||
onEnabledPref(enabled) {
|
||||
const method = enabled ? "_addEventListeners" : "_removeEventListeners";
|
||||
for (const win of this._windowStates.keys()) {
|
||||
this[method](win);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles startup tasks such as telemetry and adding listeners.
|
||||
*
|
||||
* @param {Window} win - The window context used to add event listeners.
|
||||
*/
|
||||
init(win) {
|
||||
this._windowStates.set(win, {});
|
||||
|
||||
if (lazy.gLinkPreviewEnabled) {
|
||||
this._addEventListeners(win);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Teardown the Link Preview feature for the given window.
|
||||
* Removes event listeners from the specified window and removes it from the window map.
|
||||
*
|
||||
* @param {Window} win - The window context to uninitialize.
|
||||
*/
|
||||
teardown(win) {
|
||||
// Remove event listeners from the specified window
|
||||
if (lazy.gLinkPreviewEnabled) {
|
||||
this._removeEventListeners(win);
|
||||
}
|
||||
|
||||
// Remove the window from the map
|
||||
this._windowStates.delete(win);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds all needed event listeners and updates the state.
|
||||
*
|
||||
* @param {Window} win - The window to which event listeners are added.
|
||||
*/
|
||||
_addEventListeners(win) {
|
||||
win.addEventListener("OverLink", this, true);
|
||||
win.addEventListener("keydown", this, true);
|
||||
win.addEventListener("keyup", this, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes all event listeners and updates the state.
|
||||
*
|
||||
* @param {Window} win - The window from which event listeners are removed.
|
||||
*/
|
||||
_removeEventListeners(win) {
|
||||
win.removeEventListener("OverLink", this, true);
|
||||
win.removeEventListener("keydown", this, true);
|
||||
win.removeEventListener("keyup", this, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles keyboard events ("keydown" and "keyup") for the Link Preview feature.
|
||||
* Adjusts the state of keyboardComboActive based on modifier keys.
|
||||
*
|
||||
* @param {KeyboardEvent} event - The keyboard event to be processed.
|
||||
*/
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "keydown":
|
||||
this._onKeyDown(event);
|
||||
break;
|
||||
case "keyup":
|
||||
this._onKeyUp(event);
|
||||
break;
|
||||
case "OverLink":
|
||||
this._onLinkPreview(event);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles "keydown" events.
|
||||
*
|
||||
* @param {KeyboardEvent} event - The keyboard event to be processed.
|
||||
*/
|
||||
_onKeyDown(event) {
|
||||
const win = event.currentTarget;
|
||||
if (event.altKey) {
|
||||
if (!this.keyboardComboActive) {
|
||||
this.keyboardComboActive = true;
|
||||
this._maybeLinkPreview(win);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles "keyup" events.
|
||||
*
|
||||
* @param {KeyboardEvent} event - The keyboard event to be processed.
|
||||
*/
|
||||
_onKeyUp(event) {
|
||||
const win = event.currentTarget;
|
||||
// Clear the flag when the Alt key is released.
|
||||
if (!event.altKey) {
|
||||
if (this.keyboardComboActive) {
|
||||
this.keyboardComboActive = false;
|
||||
this._maybeLinkPreview(win);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles "OverLink" events.
|
||||
* Stores the hovered link URL in the per-window state object and processes the
|
||||
* link preview if the keyboard combination is active.
|
||||
*
|
||||
* @param {CustomEvent} event - The event object containing details about the link preview.
|
||||
*/
|
||||
_onLinkPreview(event) {
|
||||
const win = event.currentTarget;
|
||||
const url = event.detail.url;
|
||||
|
||||
// Store the current overLink in the per-window state object
|
||||
const stateObject = this._windowStates.get(win);
|
||||
stateObject.overLink = url;
|
||||
|
||||
if (this.keyboardComboActive) {
|
||||
this._maybeLinkPreview(win);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Determines whether to process or cancel the link preview based on the current state.
|
||||
* If a URL is available and the keyboard combination is active, it processes the link preview.
|
||||
* Otherwise, it cancels the link preview.
|
||||
*
|
||||
* @param {Window} win - The window context in which the link preview may occur.
|
||||
*/
|
||||
_maybeLinkPreview(win) {
|
||||
// Retrieve the overLink from the per-window state object
|
||||
const stateObject = this._windowStates.get(win);
|
||||
const url = stateObject.overLink;
|
||||
|
||||
if (url && this.keyboardComboActive) {
|
||||
// Process the link preview
|
||||
console.log(`Previewing link: ${url}`);
|
||||
} else {
|
||||
// Handle unhover event
|
||||
console.log("Link preview canceled");
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -11,6 +11,10 @@ EXTRA_JS_MODULES += [
|
||||
"GenAI.sys.mjs",
|
||||
]
|
||||
|
||||
MOZ_SRC_FILES += [
|
||||
"LinkPreview.sys.mjs",
|
||||
]
|
||||
|
||||
FINAL_TARGET_FILES.actors += [
|
||||
"GenAIChild.sys.mjs",
|
||||
"GenAIParent.sys.mjs",
|
||||
|
||||
@@ -13,3 +13,4 @@ prefs = [
|
||||
["browser_chat_telemetry.js"]
|
||||
["browser_genai_actors.js"]
|
||||
["browser_genai_init.js"]
|
||||
["browser_link_preview.js"]
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const { LinkPreview } = ChromeUtils.importESModule(
|
||||
"moz-src:///browser/components/genai/LinkPreview.sys.mjs"
|
||||
);
|
||||
const { sinon } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/Sinon.sys.mjs"
|
||||
);
|
||||
const TEST_LINK_URL = "https://example.com";
|
||||
|
||||
/**
|
||||
* Tests that the Link Preview feature is correctly triggered when the Alt key is pressed.
|
||||
*
|
||||
* This test performs the following steps:
|
||||
* 1. Enables the Link Preview feature via the preference `"browser.ml.linkPreview.enabled"`.
|
||||
* 2. Creates and dispatches a `keydown` event with the `altKey` property set to `true`.
|
||||
* 3. Sets an over link using `XULBrowserWindow.setOverLink`.
|
||||
* 4. Verifies that the `_maybeLinkPreview` method of `LinkPreview` is called with the correct window.
|
||||
*/
|
||||
add_task(async function test_link_preview_with_alt_key_event() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.ml.linkPreview.enabled", true]],
|
||||
});
|
||||
|
||||
let stub = sinon.stub(LinkPreview, "_maybeLinkPreview");
|
||||
|
||||
let keydownEvent = new KeyboardEvent("keydown", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
altKey: true,
|
||||
});
|
||||
window.dispatchEvent(keydownEvent);
|
||||
|
||||
XULBrowserWindow.setOverLink(TEST_LINK_URL, {});
|
||||
|
||||
ok(
|
||||
stub.calledWith(window),
|
||||
"_maybeLinkPreview was called with the correct window"
|
||||
);
|
||||
|
||||
stub.restore();
|
||||
Services.prefs.clearUserPref("browser.ml.linkPreview.enabled");
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that no event is dispatched when the Link Preview feature is disabled, even if the Alt key is pressed.
|
||||
*
|
||||
* This test performs the following steps:
|
||||
* 1. Disables the Link Preview feature via the preference `"browser.ml.linkPreview.enabled"`.
|
||||
* 2. Creates and dispatches a `keydown` event with the `altKey` property set to `true`.
|
||||
* 3. Sets an over link using `XULBrowserWindow.setOverLink`.
|
||||
* 4. Verifies that the `_maybeLinkPreview` method of `LinkPreview` is not called.
|
||||
*/
|
||||
add_task(async function test_no_event_triggered_when_disabled_with_alt_key() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.ml.linkPreview.enabled", false]],
|
||||
});
|
||||
|
||||
let stub = sinon.stub(LinkPreview, "_maybeLinkPreview");
|
||||
|
||||
let keydownEvent = new KeyboardEvent("keydown", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
altKey: true,
|
||||
});
|
||||
window.dispatchEvent(keydownEvent);
|
||||
|
||||
XULBrowserWindow.setOverLink(TEST_LINK_URL, {});
|
||||
|
||||
ok(
|
||||
!stub.called,
|
||||
"_maybeLinkPreview should not be called when the feature is disabled"
|
||||
);
|
||||
|
||||
stub.restore();
|
||||
Services.prefs.clearUserPref("browser.ml.linkPreview.enabled");
|
||||
});
|
||||
Reference in New Issue
Block a user