This commit is contained in:
IndieKKY
2024-10-06 13:07:05 +08:00
parent 6c008feb3f
commit 240f760ae9
6 changed files with 114 additions and 113 deletions

View File

@@ -2,6 +2,7 @@ import {v4} from 'uuid'
import {handleTask, initTaskService, tasksMap} from './taskService' import {handleTask, initTaskService, tasksMap} from './taskService'
import {MESSAGE_TARGET_INJECT, MESSAGE_TO_EXTENSION_ADD_TASK, MESSAGE_TO_EXTENSION_CLOSE_SIDE_PANEL, MESSAGE_TO_EXTENSION_GET_TASK, MESSAGE_TO_EXTENSION_SHOW_FLAG, MESSAGE_TO_INJECT_TOGGLE_DISPLAY, STORAGE_ENV} from '@/consts/const' import {MESSAGE_TARGET_INJECT, MESSAGE_TO_EXTENSION_ADD_TASK, MESSAGE_TO_EXTENSION_CLOSE_SIDE_PANEL, MESSAGE_TO_EXTENSION_GET_TASK, MESSAGE_TO_EXTENSION_SHOW_FLAG, MESSAGE_TO_INJECT_TOGGLE_DISPLAY, STORAGE_ENV} from '@/consts/const'
import ExtensionMessage from '@/messaging/ExtensionMessage' import ExtensionMessage from '@/messaging/ExtensionMessage'
import { TAG_TARGET_INJECT } from '@/messaging/const'
const setBadgeOk = async (tabId: number, ok: boolean) => { const setBadgeOk = async (tabId: number, ok: boolean) => {
await chrome.action.setBadgeText({ await chrome.action.setBadgeText({
@@ -114,7 +115,7 @@ chrome.action.onClicked.addListener(async (tab) => {
}) })
} else { } else {
closeSidePanel() closeSidePanel()
extensionMessage.broadcastMessageExact([tab.id!], MESSAGE_TARGET_INJECT, MESSAGE_TO_INJECT_TOGGLE_DISPLAY).catch(console.error) extensionMessage.broadcastMessageExact([tab.id!], [TAG_TARGET_INJECT], MESSAGE_TO_INJECT_TOGGLE_DISPLAY).catch(console.error)
} }
}) })
}) })

View File

@@ -1,7 +1,6 @@
import { MESSAGE_TARGET_INJECT } from '@/consts/const' import { MESSAGE_TARGET_INJECT } from '@/consts/const'
import Layer1Protocol from './Layer1Protocol' import Layer1Protocol from './Layer1Protocol'
import { L2ReqMsg, L2ResMsg, MESSAGE_TO_EXTENSION_ROUTE_MSG } from './const' import { L2ReqMsg, L2ResMsg, MESSAGE_TO_EXTENSION_HANDSHAKE, MESSAGE_TO_EXTENSION_ROUTE_MSG } from './const'
export type PortContext = { export type PortContext = {
id: string id: string
@@ -10,91 +9,83 @@ export type PortContext = {
portMessageHandler: Layer1Protocol portMessageHandler: Layer1Protocol
ready: boolean ready: boolean
tabId?: number tabId?: number // 所属tab
type?: 'inject' | 'app' tags?: string[] // 标签,用来筛选消息发送目标
}
export type L2MethodHandler = (params: any, context: MethodContext, portContext: PortContext) => Promise<any>
export type L2MethodHandlers = {
[key: string]: L2MethodHandler
} }
class ExtensionMessage { class ExtensionMessage {
portIdToPort: Map<string, PortContext> = new Map() portIdToPort: Map<string, PortContext> = new Map()
methods?: { methods?: L2MethodHandlers
[key: string]: (params: any, context: MethodContext) => Promise<any>
}
debug = (...args: any[]) => { debug = (...args: any[]) => {
console.debug('[Extension Messaging]', ...args) console.debug('[Extension Messaging]', ...args)
} }
init = (methods: {
[key: string]: (params: any, context: MethodContext) => Promise<any>
}) => {
const innerMethods = {
[MESSAGE_TO_EXTENSION_ROUTE_MSG]: (params: any, context: MethodContext) => {
return this.broadcastMessageExact([context.tabId!], params.target, params.method, params.params)
}
}
this.methods = {...innerMethods, ...methods} init = (methods: L2MethodHandlers) => {
const innerMethods: L2MethodHandlers = {
[MESSAGE_TO_EXTENSION_HANDSHAKE]: async (params: any, context: MethodContext, portContext: PortContext) => {
const tags = params.tags
let tabId = params.tabId
const handler = async (req: L2ReqMsg, portContext: PortContext): Promise<L2ResMsg> => { //get current tabId
const { tabId } = portContext if (tabId == null) {
const method = this.methods?.[req.method] const tabs = await chrome.tabs.query({
if (method != null) { active: true,
return method(req.params, { currentWindow: true,
from: req.from, })
event: req, tabId = tabs[0]?.id
tabId,
// sender: portContext.port.sender,
}).then(data => ({
code: 200,
data,
})).catch(err => {
console.error(err)
return {
code: 500,
message: err.message,
}
})
} else {
return {
code: 501,
message: 'Unknown method: ' + req.method,
} }
}
portContext.tabId = tabId
portContext.tags = tags
portContext.ready = true
},
[MESSAGE_TO_EXTENSION_ROUTE_MSG]: async (params: any, context: MethodContext) => {
return this.broadcastMessageExact([context.tabId!], params.tags, params.method, params.params)
},
} }
this.methods = { ...innerMethods, ...methods }
chrome.runtime.onConnect.addListener((port: chrome.runtime.Port) => { chrome.runtime.onConnect.addListener((port: chrome.runtime.Port) => {
this.debug('onConnect', port) this.debug('onConnect', port)
const id = crypto.randomUUID() const id = crypto.randomUUID()
const name = port.name const name = port.name
// 创建消息处理器
const portMessageHandler = new Layer1Protocol<L2ReqMsg, L2ResMsg>(async (req: L2ReqMsg) => { const portMessageHandler = new Layer1Protocol<L2ReqMsg, L2ResMsg>(async (req: L2ReqMsg) => {
// 初始化消息 const { tabId } = portContext
if (req.method === '_init') { const method = this.methods?.[req.method]
const type = req.params.type if (method != null) {
let tabId = req.params.tabId return method(req.params, {
from: req.from,
//get current tabId event: req,
if (tabId == null) { tabId,
const tabs = await chrome.tabs.query({ // sender: portContext.port.sender,
active: true, }, portContext).then(data => ({
currentWindow: true,
})
tabId = tabs[0]?.id
}
portContext.tabId = tabId
portContext.type = type
portContext.ready = true
return {
code: 200, code: 200,
} as L2ResMsg data,
})).catch(err => {
console.error(err)
return {
code: 500,
msg: err.message,
}
})
} else {
return {
code: 501,
msg: 'Unknown method: ' + req.method,
}
} }
// 处理消息
return handler(req, portContext)
}, port) }, port)
const portContext: PortContext = {id, name, port, portMessageHandler, ready: false} // 创建portContext
const portContext: PortContext = { id, name, port, portMessageHandler, ready: false }
this.portIdToPort.set(id, portContext) this.portIdToPort.set(id, portContext)
// 监听断开连接 // 监听断开连接
@@ -105,15 +96,22 @@ class ExtensionMessage {
}) })
} }
//tags 如果为null则不检查tags如果为空数组则不会发送消息
//返回:最后一个响应(因此如果只发送给一个tab则返回的是该tab的响应) //返回:最后一个响应(因此如果只发送给一个tab则返回的是该tab的响应)
broadcastMessageExact = async (tabIds: number[], target: string, method: string, params?: any) => { broadcastMessageExact = async (tabIds: number[], tags: string[] | null, method: string, params?: any) => {
const targetType = target === MESSAGE_TARGET_INJECT ? 'inject' : 'app' // 如果tags为空数组则不会发送消息
if (tags != null && tags.length === 0) {
return
}
let res: L2ResMsg | undefined let res: L2ResMsg | undefined
for (const portContext of this.portIdToPort.values()) { for (const portContext of this.portIdToPort.values()) {
//check tabId
if (tabIds.includes(portContext.tabId!)) { if (tabIds.includes(portContext.tabId!)) {
if (targetType === portContext.type) { //check tags
if (tags == null || tags.some(tag => portContext.tags?.includes(tag))) {
try { try {
const req: L2ReqMsg = {target, method, params, from: 'extension'} const req: L2ReqMsg = { method, params, from: 'extension' }
res = await portContext.portMessageHandler.sendMessage(req) res = await portContext.portMessageHandler.sendMessage(req)
} catch (e) { } catch (e) {
console.error('send message to port error', portContext.id, e) console.error('send message to port error', portContext.id, e)
@@ -124,13 +122,13 @@ class ExtensionMessage {
return res?.data return res?.data
} }
broadcastMessage = async (ignoreTabIds: number[] | undefined | null, target: string, method: string, params?: any) => { broadcastMessage = async (ignoreTabIds: number[] | undefined | null, tags: string[], method: string, params?: any) => {
const tabs = await chrome.tabs.query({ const tabs = await chrome.tabs.query({
discarded: false, discarded: false,
}) })
const tabIds: number[] = tabs.map(tab => tab.id).filter(tabId => tabId != null) as number[] const tabIds: number[] = tabs.map(tab => tab.id).filter(tabId => tabId != null) as number[]
const filteredTabIds: number[] = tabIds.filter(tabId => !ignoreTabIds?.includes(tabId)) const filteredTabIds: number[] = tabIds.filter(tabId => !ignoreTabIds?.includes(tabId))
await this.broadcastMessageExact(filteredTabIds, target, method, params) await this.broadcastMessageExact(filteredTabIds, tags, method, params)
} }
} }

View File

@@ -1,6 +1,6 @@
import { MESSAGE_TARGET_APP, MESSAGE_TARGET_EXTENSION, MESSAGE_TARGET_INJECT } from '@/consts/const' import { MESSAGE_TARGET_APP, MESSAGE_TARGET_EXTENSION, MESSAGE_TARGET_INJECT } from '@/consts/const'
import Layer1Protocol from './Layer1Protocol' import Layer1Protocol from './Layer1Protocol'
import { L2ReqMsg, L2ResMsg, MESSAGE_TO_EXTENSION_ROUTE_MSG } from './const' import { L2ReqMsg, L2ResMsg, MESSAGE_TO_EXTENSION_HANDSHAKE, MESSAGE_TO_EXTENSION_ROUTE_MSG, TAG_TARGET_APP, TAG_TARGET_INJECT } from './const'
class InjectMessage { class InjectMessage {
port?: chrome.runtime.Port port?: chrome.runtime.Port
@@ -18,11 +18,11 @@ class InjectMessage {
this.debug(`${req.from} => `, JSON.stringify(req)) this.debug(`${req.from} => `, JSON.stringify(req))
// check event target // check event target
if (req.target !== MESSAGE_TARGET_INJECT) return Promise.resolve({ // if (req.target !== MESSAGE_TARGET_INJECT) return Promise.resolve({
success: false, // success: false,
code: 501, // code: 501,
message: 'Target Error: ' + req.target, // message: 'Target Error: ' + req.target,
}) // })
const method = this.methods?.[req.method] const method = this.methods?.[req.method]
if (method != null) { if (method != null) {
@@ -38,23 +38,23 @@ class InjectMessage {
} }
}).catch(err => { }).catch(err => {
console.error(err) console.error(err)
let message let msg
if (err instanceof Error) { if (err instanceof Error) {
message = err.message msg = err.message
} else if (typeof err === 'string') { } else if (typeof err === 'string') {
message = err msg = err
} else { } else {
message = 'error: ' + JSON.stringify(err) msg = 'error: ' + JSON.stringify(err)
} }
return { return {
code: 500, code: 500,
message, msg,
} }
}) })
} else { } else {
return { return {
code: 501, code: 501,
message: 'Unknown method: ' + req.method, msg: 'Unknown method: ' + req.method,
} }
} }
} }
@@ -69,26 +69,24 @@ class InjectMessage {
this.portMessageHandler = new Layer1Protocol<L2ReqMsg, L2ResMsg>(this.messageHandler, this.port) this.portMessageHandler = new Layer1Protocol<L2ReqMsg, L2ResMsg>(this.messageHandler, this.port)
this.portMessageHandler.sendMessage({ this.portMessageHandler.sendMessage({
from: 'inject', from: 'inject',
target: MESSAGE_TARGET_EXTENSION, method: MESSAGE_TO_EXTENSION_HANDSHAKE,
method: '_init',
params: { params: {
type: 'inject', type: 'inject',
tags: [TAG_TARGET_INJECT],
}, },
}) })
} }
sendExtension = async <T = any>(method: string, params?: any): Promise<T> => { sendExtension = async <T = any>(method: string, params?: any): Promise<T> => {
const req: L2ReqMsg = { return await this.portMessageHandler!.sendMessage({
from: 'inject', from: 'inject',
target: MESSAGE_TARGET_EXTENSION,
method, method,
params: params ?? {}, params: params ?? {},
} }).then((res) => {
return await this.portMessageHandler!.sendMessage(req).then((res) => {
if (res.code === 200) { if (res.code === 200) {
return res.data as T return res.data as T
} else { } else {
throw new Error(res.message) throw new Error(res.msg)
} }
}) })
} }
@@ -98,7 +96,7 @@ class InjectMessage {
console.log('sendApp>>>', method, params) console.log('sendApp>>>', method, params)
} }
return this.sendExtension(MESSAGE_TO_EXTENSION_ROUTE_MSG, { return this.sendExtension(MESSAGE_TO_EXTENSION_ROUTE_MSG, {
target: MESSAGE_TARGET_APP, tags: [TAG_TARGET_APP],
method, method,
params, params,
}) })

View File

@@ -1,7 +1,7 @@
// 请求信息 // 请求信息
export type L2ReqMsg = { export type L2ReqMsg = {
from: 'extension' | 'inject' | 'app' from: 'extension' | 'inject' | 'app'
target: string // target: string
method: string method: string
params?: any params?: any
// [key: string]: any // [key: string]: any
@@ -10,8 +10,12 @@ export type L2ReqMsg = {
// 响应信息 // 响应信息
export type L2ResMsg<L2Res = any> = { export type L2ResMsg<L2Res = any> = {
code: number code: number
message?: string msg?: string
data?: L2Res data?: L2Res
} }
export const MESSAGE_TO_EXTENSION_HANDSHAKE = 'handshake'
export const MESSAGE_TO_EXTENSION_ROUTE_MSG = 'routeMsg' export const MESSAGE_TO_EXTENSION_ROUTE_MSG = 'routeMsg'
export const TAG_TARGET_INJECT = 'target:inject'
export const TAG_TARGET_APP = 'target:app'

View File

@@ -2,7 +2,7 @@ import { MESSAGE_TARGET_EXTENSION, MESSAGE_TARGET_INJECT } from '@/consts/const'
import { injectWaiter } from './useMessageService' import { injectWaiter } from './useMessageService'
import { useCallback } from 'react' import { useCallback } from 'react'
import Layer1Protocol from './Layer1Protocol' import Layer1Protocol from './Layer1Protocol'
import { L2ReqMsg, L2ResMsg, MESSAGE_TO_EXTENSION_ROUTE_MSG } from './const' import { L2ReqMsg, L2ResMsg, MESSAGE_TO_EXTENSION_ROUTE_MSG, TAG_TARGET_INJECT } from './const'
const useMessage = () => { const useMessage = () => {
const sendExtension = useCallback(async <T = any>(method: string, params?: any) => { const sendExtension = useCallback(async <T = any>(method: string, params?: any) => {
@@ -11,20 +11,19 @@ const useMessage = () => {
// send message // send message
const res = await pmh.sendMessage({ const res = await pmh.sendMessage({
from: 'app', from: 'app',
target: MESSAGE_TARGET_EXTENSION,
method, method,
params: params ?? {}, params: params ?? {},
}) })
if (res.code === 200) { if (res.code === 200) {
return res.data as T return res.data as T
} else { } else {
throw new Error(res.message) throw new Error(res.msg)
} }
}, []) }, [])
const sendInject = useCallback(async <T = any>(method: string, params?: any): Promise<T> => { const sendInject = useCallback(async <T = any>(method: string, params?: any): Promise<T> => {
return await sendExtension(MESSAGE_TO_EXTENSION_ROUTE_MSG, { return await sendExtension(MESSAGE_TO_EXTENSION_ROUTE_MSG, {
target: MESSAGE_TARGET_INJECT, tags: [TAG_TARGET_INJECT],
method, method,
params: params ?? {}, params: params ?? {},
}) })

View File

@@ -4,7 +4,7 @@ import {
} from '@/consts/const' } from '@/consts/const'
import { Waiter } from '@kky002/kky-util' import { Waiter } from '@kky002/kky-util'
import Layer1Protocol from './Layer1Protocol' import Layer1Protocol from './Layer1Protocol'
import { L2ReqMsg, L2ResMsg } from './const' import { L2ReqMsg, L2ResMsg, MESSAGE_TO_EXTENSION_HANDSHAKE, TAG_TARGET_APP } from './const'
const debug = (...args: any[]) => { const debug = (...args: any[]) => {
console.debug('[App Messaging]', ...args) console.debug('[App Messaging]', ...args)
@@ -25,11 +25,11 @@ const useMessageService = (methods?: {
const messageHandler = useCallback(async (req: L2ReqMsg): Promise<L2ResMsg> => { const messageHandler = useCallback(async (req: L2ReqMsg): Promise<L2ResMsg> => {
debug(`${req.from} => `, JSON.stringify(req)) debug(`${req.from} => `, JSON.stringify(req))
// check event target // // check event target
if (req.target !== MESSAGE_TARGET_APP) return { // if (req.target !== MESSAGE_TARGET_APP) return {
code: 501, // code: 501,
message: 'Target Error: ' + req.target, // msg: 'Target Error: ' + req.target,
} // }
const method = methods?.[req.method] const method = methods?.[req.method]
if (method != null) { if (method != null) {
@@ -44,23 +44,23 @@ const useMessageService = (methods?: {
} }
}).catch(err => { }).catch(err => {
console.error(err) console.error(err)
let message let msg
if (err instanceof Error) { if (err instanceof Error) {
message = err.message msg = err.message
} else if (typeof err === 'string') { } else if (typeof err === 'string') {
message = err msg = err
} else { } else {
message = 'error: ' + JSON.stringify(err) msg = 'error: ' + JSON.stringify(err)
} }
return { return {
code: 500, code: 500,
message, msg,
} }
}) })
} else { } else {
return { return {
code: 501, code: 501,
message: 'Unknown method: ' + req.method, msg: 'Unknown method: ' + req.method,
} }
} }
}, [methods]) }, [methods])
@@ -79,12 +79,13 @@ const useMessageService = (methods?: {
let tabId = tabIdStr ? parseInt(tabIdStr) : undefined let tabId = tabIdStr ? parseInt(tabIdStr) : undefined
// 初始化 // 初始化
pmh.sendMessage({ pmh.sendMessage({
method: '_init', from: 'app',
method: MESSAGE_TO_EXTENSION_HANDSHAKE,
params: { params: {
type: 'app',
tabId, tabId,
tags: [TAG_TARGET_APP],
}, },
} as L2ReqMsg) })
portMessageHandlerInit = true portMessageHandlerInit = true
return pmh return pmh