diff --git a/src/entrypoints/background.ts b/src/entrypoints/background.ts index 4e05ad4..781b530 100644 --- a/src/entrypoints/background.ts +++ b/src/entrypoints/background.ts @@ -3,6 +3,7 @@ import { browser } from 'wxt/browser'; import { MessageAction, onMessage, sendMessage } from '@/utils/messages'; import { createAllContextMenus, parseContextMenuClick } from '@/utils/contextMenu'; import { saveContextMenuData } from '@/utils/useContextMenuData'; +import { mainWorldInjectionScript } from '@/utils/rightClickInjection'; export default defineBackground(() => { // 1. 扩展初次安装或更新时,注册右键上下文菜单 @@ -71,68 +72,7 @@ export default defineBackground(() => { try { await browser.scripting.executeScript({ target: { tabId }, - func: () => { - 'use strict'; - const w = window as unknown as Record; - if (w.__testingToolsRightClickPatched) return; - w.__testingToolsRightClickPatched = true; - - const PROTECTED = ['contextmenu', 'copy', 'paste', 'cut', 'selectstart']; - - const _origPreventDefault = MouseEvent.prototype.preventDefault; - Object.defineProperty(MouseEvent.prototype, 'preventDefault', { - value: function (this: MouseEvent) { - const t = this.type; - if (PROTECTED.includes(t) || (t === 'mousedown' && this.button === 2)) { - return; - } - return _origPreventDefault.call(this); - }, - writable: true, - configurable: true, - }); - - const _origStopPropagation = Event.prototype.stopPropagation; - Object.defineProperty(Event.prototype, 'stopPropagation', { - value: function (this: Event) { - if (PROTECTED.includes(this.type)) return; - return _origStopPropagation.call(this); - }, - writable: true, - configurable: true, - }); - - const _origStopImmediatePropagation = Event.prototype.stopImmediatePropagation; - Object.defineProperty(Event.prototype, 'stopImmediatePropagation', { - value: function (this: Event) { - if (PROTECTED.includes(this.type)) return; - return _origStopImmediatePropagation.call(this); - }, - writable: true, - configurable: true, - }); - - let _docOnContextMenu: unknown = null; - Object.defineProperty(document, 'oncontextmenu', { - get() { - return _docOnContextMenu; - }, - set(fn: unknown) { - if (typeof fn === 'function') { - _docOnContextMenu = function (this: GlobalEventHandlers, e: MouseEvent) { - const r = (fn as (this: GlobalEventHandlers, ev: MouseEvent) => unknown).call( - this, - e, - ); - return r === false ? true : r; - }; - } else { - _docOnContextMenu = fn; - } - }, - configurable: true, - }); - }, + func: mainWorldInjectionScript, world: 'MAIN', }); diff --git a/src/utils/rightClickInjection.ts b/src/utils/rightClickInjection.ts new file mode 100644 index 0000000..6645fa1 --- /dev/null +++ b/src/utils/rightClickInjection.ts @@ -0,0 +1,71 @@ +/** + * 右键解锁功能的主环境注入脚本 + * + * 由于 content script 运行在 Isolated World,无法直接修改网页主环境的 + * Event.prototype.preventDefault 等原生方法。需要通过 background + * 的 scripting.executeScript({ world: 'MAIN' }) 注入此脚本。 + */ + +/** + * 在网页主环境中执行的注入函数 + * 注意:此函数会被序列化后通过 executeScript 注入,不能引用外部变量 + */ +export function mainWorldInjectionScript(): void { + 'use strict'; + const w = window as unknown as Record; + if (w.__testingToolsRightClickPatched) return; + w.__testingToolsRightClickPatched = true; + + const PROTECTED = ['contextmenu', 'copy', 'paste', 'cut', 'selectstart']; + + const _origPreventDefault = MouseEvent.prototype.preventDefault; + Object.defineProperty(MouseEvent.prototype, 'preventDefault', { + value: function (this: MouseEvent) { + const t = this.type; + if (PROTECTED.includes(t) || (t === 'mousedown' && this.button === 2)) { + return; + } + return _origPreventDefault.call(this); + }, + writable: true, + configurable: true, + }); + + const _origStopPropagation = Event.prototype.stopPropagation; + Object.defineProperty(Event.prototype, 'stopPropagation', { + value: function (this: Event) { + if (PROTECTED.includes(this.type)) return; + return _origStopPropagation.call(this); + }, + writable: true, + configurable: true, + }); + + const _origStopImmediatePropagation = Event.prototype.stopImmediatePropagation; + Object.defineProperty(Event.prototype, 'stopImmediatePropagation', { + value: function (this: Event) { + if (PROTECTED.includes(this.type)) return; + return _origStopImmediatePropagation.call(this); + }, + writable: true, + configurable: true, + }); + + let _docOnContextMenu: unknown = null; + Object.defineProperty(document, 'oncontextmenu', { + get() { + return _docOnContextMenu; + }, + set(fn: unknown) { + if (typeof fn === 'function') { + _docOnContextMenu = function (this: GlobalEventHandlers, e: MouseEvent) { + const r = (fn as (this: GlobalEventHandlers, ev: MouseEvent) => unknown).call(this, e); + return r === false ? true : r; + }; + } else { + _docOnContextMenu = fn; + } + }, + configurable: true, + }); +}