diff --git a/packages/EdgeTranslate/src/background/background.js b/packages/EdgeTranslate/src/background/background.js index 2463dd5c..7665255e 100644 --- a/packages/EdgeTranslate/src/background/background.js +++ b/packages/EdgeTranslate/src/background/background.js @@ -17,6 +17,7 @@ import { setDefaultSettings, } from "common/scripts/settings.js"; import { resolveContextMenuSelection } from "./library/context_menu.js"; +import { registerSubframePointerDownForwarder } from "./library/frame_events.js"; import "./library/pdf_file_access.js"; if (typeof BUILD_ENV !== "undefined" && BUILD_ENV === "development") { @@ -183,7 +184,7 @@ function showUpdateNotification() { /** * Create communication channel. */ -const channel = new Channel(); +const channel = registerSubframePointerDownForwarder(new Channel()); /** * Create translator manager and register event listeners and service providers. diff --git a/packages/EdgeTranslate/src/background/library/frame_events.js b/packages/EdgeTranslate/src/background/library/frame_events.js new file mode 100644 index 00000000..7de39b9e --- /dev/null +++ b/packages/EdgeTranslate/src/background/library/frame_events.js @@ -0,0 +1,27 @@ +import { + SUBFRAME_POINTER_DOWN_EVENT, + TOP_FRAME_POINTER_DOWN_EVENT, +} from "common/scripts/frame_events.js"; + +export function registerSubframePointerDownForwarder(channel) { + channel.on(SUBFRAME_POINTER_DOWN_EVENT, (_detail, sender) => { + forwardSubframePointerDown(sender); + }); + return channel; +} + +export function forwardSubframePointerDown(sender) { + const tabId = sender.tab?.id; + if (!Number.isInteger(tabId) || !Number.isInteger(sender.frameId) || sender.frameId <= 0) { + return false; + } + + const message = JSON.stringify({ + type: "event", + event: TOP_FRAME_POINTER_DOWN_EVENT, + }); + chrome.tabs.sendMessage(tabId, message, { frameId: 0 }, () => { + void chrome.runtime.lastError; + }); + return true; +} diff --git a/packages/EdgeTranslate/src/common/scripts/frame_events.js b/packages/EdgeTranslate/src/common/scripts/frame_events.js new file mode 100644 index 00000000..5aed5d63 --- /dev/null +++ b/packages/EdgeTranslate/src/common/scripts/frame_events.js @@ -0,0 +1,2 @@ +export const SUBFRAME_POINTER_DOWN_EVENT = "subframe_pointer_down"; +export const TOP_FRAME_POINTER_DOWN_EVENT = "top_frame_pointer_down"; diff --git a/packages/EdgeTranslate/src/content/display/library/moveable/draggable.js b/packages/EdgeTranslate/src/content/display/library/moveable/draggable.js index 1a6bfeab..6793083c 100644 --- a/packages/EdgeTranslate/src/content/display/library/moveable/draggable.js +++ b/packages/EdgeTranslate/src/content/display/library/moveable/draggable.js @@ -28,6 +28,7 @@ export default class draggable { * 2. add mouse down event listener to the target draggable element */ dragInitiate() { + this.dragCancelHandler = () => this.finishDrag(undefined, true); this.dragEnd(); this.setBounds(this.options.bounds); // wrap a drag start event handler @@ -112,6 +113,7 @@ export default class draggable { if (this.dragging) { e.preventDefault(); this.options.container.addEventListener("mousemove", this.dragHandler); + window.addEventListener("blur", this.dragCancelHandler, { once: true }); } } @@ -203,18 +205,25 @@ export default class draggable { */ dragEnd() { this.options.container.addEventListener("mouseup", (e) => { - if (this.dragging) { - this.dragging = false; - this.options.container.removeEventListener("mousemove", this.dragHandler); - if (this.handlers.dragEnd) - this.handlers.dragEnd({ - inputEvent: e, - translate: [this.store.currentTranslate[0], this.store.currentTranslate[1]], - }); // deep copy - } + this.finishDrag(e); }); } + finishDrag(inputEvent, canceled = false) { + if (!this.dragging) return false; + this.dragging = false; + this.bounding = false; + this.options.container.removeEventListener("mousemove", this.dragHandler); + window.removeEventListener("blur", this.dragCancelHandler); + if (this.handlers.dragEnd) + this.handlers.dragEnd({ + inputEvent, + canceled, + translate: [this.store.currentTranslate[0], this.store.currentTranslate[1]], + }); // deep copy + return true; + } + /** * the event handler when target element start to exceed the boundary * @param {string} direction in which direction the element exceeds the boundary diff --git a/packages/EdgeTranslate/src/content/display/library/moveable/resizable.js b/packages/EdgeTranslate/src/content/display/library/moveable/resizable.js index aba324d7..42042e2f 100644 --- a/packages/EdgeTranslate/src/content/display/library/moveable/resizable.js +++ b/packages/EdgeTranslate/src/content/display/library/moveable/resizable.js @@ -39,6 +39,7 @@ export default class resizable { * 3. add mouse down event listener to the resizable div element */ resizeInitiate() { + this.resizeCancelHandler = () => this.finishResize(undefined, true); this.resizeEnd(); // wrap a resize start event handler this.resizeStartHandler = (e) => { @@ -177,6 +178,7 @@ export default class resizable { /* call the drag start handler written by the user */ this.handlers.resizeStart && this.handlers.resizeStart({ + inputEvent: e, // set the start position set: (position) => { this.store.startTranslate = [position[0], position[1]]; // deep copy @@ -198,6 +200,7 @@ export default class resizable { if (this.resizing) { e.preventDefault(); this.options.container.addEventListener("mousemove", this.resizeHandler); + window.addEventListener("blur", this.resizeCancelHandler, { once: true }); } } @@ -304,21 +307,27 @@ export default class resizable { */ resizeEnd() { this.options.container.addEventListener("mouseup", (e) => { - if (this.resizing) { - this.resizing = false; - this.options.container.removeEventListener("mousemove", this.resizeHandler); - if (this.handlers.resizeEnd) - this.handlers.resizeEnd({ - target: this.targetElement, - inputEvent: e, - translate: [this.store.currentTranslate[0], this.store.currentTranslate[1]], // deep copy - width: this.store.currentSize[0], - height: this.store.currentSize[1], - }); - } + this.finishResize(e); }); } + finishResize(inputEvent, canceled = false) { + if (!this.resizing) return false; + this.resizing = false; + this.options.container.removeEventListener("mousemove", this.resizeHandler); + window.removeEventListener("blur", this.resizeCancelHandler); + if (this.handlers.resizeEnd) + this.handlers.resizeEnd({ + target: this.targetElement, + inputEvent, + canceled, + translate: [this.store.currentTranslate[0], this.store.currentTranslate[1]], // deep copy + width: this.store.currentSize[0], + height: this.store.currentSize[1], + }); + return true; + } + /** * resize the target resizable element to the request size * @param {Object} resizeParameter {width: resize to the ${width} value, height: resize to the ${height} value} diff --git a/packages/EdgeTranslate/src/content/display/panel/PanelView.jsx b/packages/EdgeTranslate/src/content/display/panel/PanelView.jsx index 819ca533..d136800f 100644 --- a/packages/EdgeTranslate/src/content/display/panel/PanelView.jsx +++ b/packages/EdgeTranslate/src/content/display/panel/PanelView.jsx @@ -13,6 +13,7 @@ import CloseIcon from "../icons/close.svg"; import { MaxZIndex, GlobalStyle, + DragShield, Panel, Head, HeadIcons, @@ -39,6 +40,7 @@ export default function PanelView(props) { +