From 607ae4af940c9bb70eebf64d94ee4974f884a791 Mon Sep 17 00:00:00 2001 From: BlackGlory Date: Sat, 4 May 2024 12:39:20 +0800 Subject: [PATCH] fix: shortcuts now send messages to all frames --- .../selection-as-concatenated-plain-text.ts | 3 ++ .../handlers/selection-as-markdown.ts | 3 ++ .../handlers/selection-as-plain-text.ts | 3 ++ src/background/handlers/utils.ts | 7 ++++- src/background/index.ts | 29 +++++++++++++++---- src/content-script.ts | 17 +++++------ src/contract.ts | 6 ++-- src/manifest.dev.json | 1 + src/manifest.prod.json | 1 + src/utils/is-dev.ts | 3 ++ 10 files changed, 53 insertions(+), 20 deletions(-) create mode 100644 src/utils/is-dev.ts diff --git a/src/background/handlers/selection-as-concatenated-plain-text.ts b/src/background/handlers/selection-as-concatenated-plain-text.ts index 21cd21f..c8336e1 100644 --- a/src/background/handlers/selection-as-concatenated-plain-text.ts +++ b/src/background/handlers/selection-as-concatenated-plain-text.ts @@ -3,6 +3,7 @@ import { createTabClient } from '@delight-rpc/webextension' import { IFrameAPI } from '@src/contract.js' import { CommandHandler } from './types.js' import { concatPlainText } from '@utils/concat-plain-text.js' +import { assert, isntNull } from '@blackglory/prelude' export const commandSelectionAsConcatenatedPlainText: CommandHandler = async (info, tab) => { if (tab.id) { @@ -10,7 +11,9 @@ export const commandSelectionAsConcatenatedPlainText: CommandHandler = async (in tabId: tab.id , frameId: info.frameId }) + const text = await tabClient.getSelectionText() + assert(isntNull(text)) return plainText(concatPlainText(text)) } diff --git a/src/background/handlers/selection-as-markdown.ts b/src/background/handlers/selection-as-markdown.ts index b0704d4..ec7a7f5 100644 --- a/src/background/handlers/selection-as-markdown.ts +++ b/src/background/handlers/selection-as-markdown.ts @@ -5,6 +5,7 @@ import { CommandHandler } from './types.js' import { getConfig } from '@background/storage.js' import { pipeAsync } from 'extra-utils' import { offscreen } from '@background/offscreen-client.js' +import { assert, isntNull } from '@blackglory/prelude' export const commandSelectionAsMarkdown: CommandHandler = async (info, tab) => { if (tab.id) { @@ -16,7 +17,9 @@ export const commandSelectionAsMarkdown: CommandHandler = async (info, tab) => { tabId: tab.id , frameId: info.frameId }) + const html = await client.getSelectionHTML() + assert(isntNull(html)) return plainText( await pipeAsync( diff --git a/src/background/handlers/selection-as-plain-text.ts b/src/background/handlers/selection-as-plain-text.ts index b292580..c0628f4 100644 --- a/src/background/handlers/selection-as-plain-text.ts +++ b/src/background/handlers/selection-as-plain-text.ts @@ -2,6 +2,7 @@ import { plainText } from './utils.js' import { createTabClient } from '@delight-rpc/webextension' import { IFrameAPI } from '@src/contract.js' import { CommandHandler } from './types.js' +import { assert, isntNull } from '@blackglory/prelude' export const commandSelectionAsPlainText: CommandHandler = async (info, tab) => { if (tab.id) { @@ -9,7 +10,9 @@ export const commandSelectionAsPlainText: CommandHandler = async (info, tab) => tabId: tab.id , frameId: info.frameId }) + const text = await tabClient.getSelectionText() + assert(isntNull(text)) return plainText(text) } diff --git a/src/background/handlers/utils.ts b/src/background/handlers/utils.ts index e8da064..391f912 100644 --- a/src/background/handlers/utils.ts +++ b/src/background/handlers/utils.ts @@ -1,3 +1,4 @@ +import { assert, isntNull } from '@blackglory/prelude' import { CommandResultType, CommandResult } from './types.js' import { createTabClient } from '@delight-rpc/webextension' import { IFrameAPI } from '@src/contract.js' @@ -21,5 +22,9 @@ export async function getActiveElementTextContent( , frameId?: number ): Promise { const tabClient = createTabClient({ tabId, frameId }) - return await tabClient.getActiveElementTextContent() + + const text = await tabClient.getActiveElementTextContent() + assert(isntNull(text)) + + return text } diff --git a/src/background/index.ts b/src/background/index.ts index f3b47b3..1e53cb1 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -8,6 +8,8 @@ import { IBackgroundAPI } from '@src/contract.js' import { ImplementationOf } from 'delight-rpc' import { createServer } from '@delight-rpc/webextension' import { updateMenu } from './menu.js' +import { isDev } from '@utils/is-dev.js' +import { assert, isntUndefined } from '@blackglory/prelude' const launched = new Deferred() @@ -72,14 +74,29 @@ chrome.contextMenus.onClicked.addListener(async (info, tab) => { }) chrome.commands.onCommand.addListener(async (command, tab) => { - const result = await commandHandlers[command]( - {} - , tab ?? await getActiveTab() + tab = tab ?? await getActiveTab() + + const tabId = tab.id + assert(isntUndefined(tabId)) + + const frames = await chrome.webNavigation.getAllFrames({ tabId }) ?? [] + + const result = await Promise.any( + frames.map(async frame => { + const result = await commandHandlers[command]( + { + frameUrl: frame.url + , frameId: frame.frameId + } + , tab + ) + assert(isntUndefined(result)) + + return result + }) ) - if (result) { - await handleCommandResult(result) - } + await handleCommandResult(result) }) async function ensureOffscreenDocument(): Promise { diff --git a/src/content-script.ts b/src/content-script.ts index 4981d89..1af1cbe 100644 --- a/src/content-script.ts +++ b/src/content-script.ts @@ -1,5 +1,6 @@ import { createServer } from '@delight-rpc/webextension' import { IFrameAPI } from '@src/contract.js' +import { isDev } from '@utils/is-dev.js' if (isDev()) { console.info(`[${chrome.runtime.getManifest().name}] The content script is injected`) @@ -12,7 +13,7 @@ createServer({ , getSelectionText }) -function getSelectionHTML(): string { +function getSelectionHTML(): string | null { const userSelection = window.getSelection() if (userSelection && userSelection.rangeCount) { const range = userSelection.getRangeAt(0) @@ -21,22 +22,18 @@ function getSelectionHTML(): string { div.appendChild(clonedSelection) return div.innerHTML } else { - return '' + return null } } -function getSelectionText(): string { - return window.getSelection()?.toString() ?? '' +function getSelectionText(): string | null { + return window.getSelection()?.toString() ?? null } -function getActiveElementTextContent(): string { - return document.activeElement?.textContent ?? '' +function getActiveElementTextContent(): string | null { + return document.activeElement?.textContent ?? null } function getDocumentTitle(): string { return document.title } - -function isDev(): boolean { - return process.env.NODE_ENV === 'development' -} diff --git a/src/contract.ts b/src/contract.ts index e8aebe1..96aa9fa 100644 --- a/src/contract.ts +++ b/src/contract.ts @@ -114,9 +114,9 @@ export interface IHTMLCleanerAllowlistItem { } export interface IFrameAPI { - getSelectionHTML(): string - getSelectionText(): string - getActiveElementTextContent(): string + getSelectionHTML(): string | null + getSelectionText(): string | null + getActiveElementTextContent(): string | null getDocumentTitle(): string } diff --git a/src/manifest.dev.json b/src/manifest.dev.json index f465d2e..aef5e85 100644 --- a/src/manifest.dev.json +++ b/src/manifest.dev.json @@ -93,6 +93,7 @@ , "clipboardWrite" , "offscreen" , "storage" + , "webNavigation" ] , "host_permissions": [ "" diff --git a/src/manifest.prod.json b/src/manifest.prod.json index add9ca6..53246d2 100644 --- a/src/manifest.prod.json +++ b/src/manifest.prod.json @@ -93,6 +93,7 @@ , "clipboardWrite" , "offscreen" , "storage" + , "webNavigation" ] , "host_permissions": [ "" diff --git a/src/utils/is-dev.ts b/src/utils/is-dev.ts new file mode 100644 index 0000000..a49f26e --- /dev/null +++ b/src/utils/is-dev.ts @@ -0,0 +1,3 @@ +export function isDev(): boolean { + return process.env.NODE_ENV === 'development' +}