Check for selection change instead of just having a selection. Support inputs/textarea selection. Hide before unload. Differential Revision: https://phabricator.services.mozilla.com/D219105
111 lines
3.1 KiB
JavaScript
111 lines
3.1 KiB
JavaScript
/* 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,
|
|
"shortcutsDelay",
|
|
"browser.ml.chat.shortcuts.longPress"
|
|
);
|
|
|
|
// Additional events to listen with others to create the actor in BrowserGlue
|
|
const EVENTS = ["mousedown", "mouseup", "pagehide"];
|
|
|
|
/**
|
|
* JSWindowActor to detect content page events to send GenAI related data.
|
|
*/
|
|
export class GenAIChild extends JSWindowActorChild {
|
|
actorCreated() {
|
|
this.document.addEventListener("selectionchange", this);
|
|
// Use capture as some pages might stop the events
|
|
EVENTS.forEach(ev => this.contentWindow.addEventListener(ev, this, true));
|
|
}
|
|
|
|
didDestroy() {
|
|
this.document.removeEventListener("selectionchange", this);
|
|
EVENTS.forEach(ev =>
|
|
this.contentWindow?.removeEventListener(ev, this, true)
|
|
);
|
|
}
|
|
|
|
handleEvent(event) {
|
|
const sendHide = () =>
|
|
this.sendAsyncMessage("GenAI:HideShortcuts", event.type);
|
|
switch (event.type) {
|
|
case "mousedown":
|
|
this.downSelection = this.getSelectionInfo().selection;
|
|
this.downTime = Date.now();
|
|
sendHide();
|
|
break;
|
|
case "mouseup": {
|
|
// Only handle plain clicks
|
|
if (
|
|
event.button ||
|
|
event.altKey ||
|
|
event.ctrlKey ||
|
|
event.metaKey ||
|
|
event.shiftKey
|
|
) {
|
|
return;
|
|
}
|
|
|
|
// Show immediately on selection or allow long press with no selection
|
|
const selectionInfo = this.getSelectionInfo();
|
|
const delay = Date.now() - (this.downTime ?? 0);
|
|
if (
|
|
(selectionInfo.selection &&
|
|
selectionInfo.selection != this.downSelection) ||
|
|
delay > lazy.shortcutsDelay
|
|
) {
|
|
this.sendAsyncMessage("GenAI:ShowShortcuts", {
|
|
...selectionInfo,
|
|
delay,
|
|
x: event.screenX,
|
|
y: event.screenY,
|
|
});
|
|
}
|
|
break;
|
|
}
|
|
case "pagehide":
|
|
case "resize":
|
|
case "scroll":
|
|
case "selectionchange":
|
|
// Hide if selection might have shifted away from shortcuts
|
|
sendHide();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provide the selected text and input type.
|
|
*
|
|
* @returns {object} selection info
|
|
*/
|
|
getSelectionInfo() {
|
|
// Handle regular selection outside of inputs
|
|
const { activeElement } = this.document;
|
|
const selection = this.contentWindow.getSelection()?.toString().trim();
|
|
if (selection) {
|
|
return {
|
|
inputType: activeElement.closest("[contenteditable]")
|
|
? "contenteditable"
|
|
: "",
|
|
selection,
|
|
};
|
|
}
|
|
|
|
// Selection within input elements
|
|
const { selectionStart, value } = activeElement;
|
|
if (selectionStart != null && value != null) {
|
|
return {
|
|
inputType: activeElement.localName,
|
|
selection: value.slice(selectionStart, activeElement.selectionEnd),
|
|
};
|
|
}
|
|
return { inputType: "", selection: "" };
|
|
}
|
|
}
|