This commit is contained in:
IndieKKY
2024-10-06 12:35:35 +08:00
parent b91e1476d6
commit 6c008feb3f
6 changed files with 83 additions and 76 deletions

View File

@@ -2,7 +2,6 @@ export const MESSAGE_TARGET_EXTENSION = 'BilibiliExtension'
export const MESSAGE_TARGET_INJECT = 'BilibiliInject' export const MESSAGE_TARGET_INJECT = 'BilibiliInject'
export const MESSAGE_TARGET_APP = 'BilibiliAPP' export const MESSAGE_TARGET_APP = 'BilibiliAPP'
export const MESSAGE_TO_EXTENSION_ROUTE_MSG = 'routeMsg'
export const MESSAGE_TO_EXTENSION_CLOSE_SIDE_PANEL = 'closeSidePanel' export const MESSAGE_TO_EXTENSION_CLOSE_SIDE_PANEL = 'closeSidePanel'
export const MESSAGE_TO_EXTENSION_ADD_TASK = 'addTask' export const MESSAGE_TO_EXTENSION_ADD_TASK = 'addTask'
export const MESSAGE_TO_EXTENSION_GET_TASK = 'getTask' export const MESSAGE_TO_EXTENSION_GET_TASK = 'getTask'

View File

@@ -1,5 +1,7 @@
import { MESSAGE_TARGET_INJECT, MESSAGE_TO_EXTENSION_ROUTE_MSG } 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'
export type PortContext = { export type PortContext = {
id: string id: string
@@ -17,9 +19,6 @@ class ExtensionMessage {
methods?: { methods?: {
[key: string]: (params: any, context: MethodContext) => Promise<any> [key: string]: (params: any, context: MethodContext) => Promise<any>
} }
innerMethods?: {
[key: string]: (params: any, context: MethodContext) => Promise<any>
}
debug = (...args: any[]) => { debug = (...args: any[]) => {
console.debug('[Extension Messaging]', ...args) console.debug('[Extension Messaging]', ...args)
@@ -28,39 +27,37 @@ class ExtensionMessage {
init = (methods: { init = (methods: {
[key: string]: (params: any, context: MethodContext) => Promise<any> [key: string]: (params: any, context: MethodContext) => Promise<any>
}) => { }) => {
this.innerMethods = { const innerMethods = {
[MESSAGE_TO_EXTENSION_ROUTE_MSG]: (params: any, context: MethodContext) => { [MESSAGE_TO_EXTENSION_ROUTE_MSG]: (params: any, context: MethodContext) => {
return this.broadcastMessageExact([context.tabId!], params.target, params.method, params.params) return this.broadcastMessageExact([context.tabId!], params.target, params.method, params.params)
} }
} }
this.methods = {...this.innerMethods, ...methods}
const handler = async (event: MessageData, portContext: PortContext): Promise<MessageResult> => { this.methods = {...innerMethods, ...methods}
const handler = async (req: L2ReqMsg, portContext: PortContext): Promise<L2ResMsg> => {
const { tabId } = portContext const { tabId } = portContext
const method = this.methods?.[event.method] const method = this.methods?.[req.method]
if (method != null) { if (method != null) {
return method(event.params, { return method(req.params, {
from: event.from, from: req.from,
event, event: req,
tabId, tabId,
// sender: portContext.port.sender, // sender: portContext.port.sender,
}).then(data => ({ }).then(data => ({
success: true,
code: 200, code: 200,
data, data,
})).catch(err => { })).catch(err => {
console.error(err) console.error(err)
return { return {
success: false,
code: 500, code: 500,
message: err.message, message: err.message,
} }
}) })
} else { } else {
return { return {
success: false,
code: 501, code: 501,
message: 'Unknown method: ' + event.method, message: 'Unknown method: ' + req.method,
} }
} }
} }
@@ -70,11 +67,11 @@ class ExtensionMessage {
const id = crypto.randomUUID() const id = crypto.randomUUID()
const name = port.name const name = port.name
const portMessageHandler = new Layer1Protocol<MessageData, MessageResult>(async (value: MessageData) => { const portMessageHandler = new Layer1Protocol<L2ReqMsg, L2ResMsg>(async (req: L2ReqMsg) => {
// 初始化消息 // 初始化消息
if (value.method === '_init') { if (req.method === '_init') {
const type = value.params.type const type = req.params.type
let tabId = value.params.tabId let tabId = req.params.tabId
//get current tabId //get current tabId
if (tabId == null) { if (tabId == null) {
@@ -90,13 +87,12 @@ class ExtensionMessage {
portContext.ready = true portContext.ready = true
return { return {
success: true,
code: 200, code: 200,
} as MessageResult } as L2ResMsg
} }
// 处理消息 // 处理消息
return handler(value, portContext) return handler(req, portContext)
}, port) }, port)
const portContext: PortContext = {id, name, port, portMessageHandler, ready: false} const portContext: PortContext = {id, name, port, portMessageHandler, ready: false}
this.portIdToPort.set(id, portContext) this.portIdToPort.set(id, portContext)
@@ -109,22 +105,23 @@ class ExtensionMessage {
}) })
} }
//返回:最后一个响应(因此如果只发送给一个tab则返回的是该tab的响应)
broadcastMessageExact = async (tabIds: number[], target: string, method: string, params?: any) => { broadcastMessageExact = async (tabIds: number[], target: string, method: string, params?: any) => {
const targetType = target === MESSAGE_TARGET_INJECT ? 'inject' : 'app' const targetType = target === MESSAGE_TARGET_INJECT ? 'inject' : 'app'
let resp: MessageResult | undefined let res: L2ResMsg | undefined
for (const portContext of this.portIdToPort.values()) { for (const portContext of this.portIdToPort.values()) {
if (tabIds.includes(portContext.tabId!)) { if (tabIds.includes(portContext.tabId!)) {
if (targetType === portContext.type) { if (targetType === portContext.type) {
try { try {
const messageData: MessageData = {target, method, params, from: 'extension'} const req: L2ReqMsg = {target, method, params, from: 'extension'}
resp = await portContext.portMessageHandler.sendMessage(messageData) 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)
} }
} }
} }
} }
return resp?.data return res?.data
} }
broadcastMessage = async (ignoreTabIds: number[] | undefined | null, target: string, method: string, params?: any) => { broadcastMessage = async (ignoreTabIds: number[] | undefined | null, target: string, method: string, params?: any) => {

View File

@@ -1,38 +1,38 @@
import { MESSAGE_TARGET_APP, MESSAGE_TARGET_EXTENSION, MESSAGE_TARGET_INJECT, MESSAGE_TO_EXTENSION_ROUTE_MSG } 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'
class InjectMessage { class InjectMessage {
port?: chrome.runtime.Port port?: chrome.runtime.Port
portMessageHandler?: Layer1Protocol portMessageHandler?: Layer1Protocol<L2ReqMsg, L2ResMsg>
//类实例 //类实例
methods?: { methods?: {
[key: string]: (params: any, context: MethodContext) => Promise<any> [key: string]: (params: any, context: MethodContext) => Promise<L2ResMsg>
} }
debug = (...args: any[]) => { debug = (...args: any[]) => {
console.debug('[Inject Messaging]', ...args) console.debug('[Inject Messaging]', ...args)
} }
messageHandler = async (event: MessageData): Promise<MessageResult> => { messageHandler = async (req: L2ReqMsg): Promise<L2ResMsg> => {
this.debug(`${event.from} => `, JSON.stringify(event)) this.debug(`${req.from} => `, JSON.stringify(req))
// check event target // check event target
if (event.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: ' + event.target, message: 'Target Error: ' + req.target,
}) })
const method = this.methods?.[event.method] const method = this.methods?.[req.method]
if (method != null) { if (method != null) {
return method(event.params, { return method(req.params, {
from: event.from, from: req.from,
event, event: req,
// sender, // sender,
}).then(data => { }).then(data => {
// debug(`${source} <= `, event.method, JSON.stringify(data)) // debug(`${source} <= `, event.method, JSON.stringify(data))
return { return {
success: true,
code: 200, code: 200,
data, data,
} }
@@ -47,16 +47,14 @@ class InjectMessage {
message = 'error: ' + JSON.stringify(err) message = 'error: ' + JSON.stringify(err)
} }
return { return {
success: false,
code: 500, code: 500,
message, message,
} }
}) })
} else { } else {
return { return {
success: false,
code: 501, code: 501,
message: 'Unknown method: ' + event.method, message: 'Unknown method: ' + req.method,
} }
} }
} }
@@ -68,27 +66,29 @@ class InjectMessage {
this.port = chrome.runtime.connect(import.meta.env.VITE_EXTENSION_ID, { this.port = chrome.runtime.connect(import.meta.env.VITE_EXTENSION_ID, {
name: MESSAGE_TARGET_INJECT, name: MESSAGE_TARGET_INJECT,
}) })
this.portMessageHandler = new Layer1Protocol<MessageData, MessageResult>(this.messageHandler, this.port) this.portMessageHandler = new Layer1Protocol<L2ReqMsg, L2ResMsg>(this.messageHandler, this.port)
this.portMessageHandler.sendMessage({ this.portMessageHandler.sendMessage({
from: 'inject',
target: MESSAGE_TARGET_EXTENSION,
method: '_init', method: '_init',
params: { params: {
type: 'inject', type: 'inject',
}, },
} as MessageData) })
} }
sendExtension = async <T = any>(method: string, params?: any): Promise<T> => { sendExtension = async <T = any>(method: string, params?: any): Promise<T> => {
const messageData: MessageData = { const req: L2ReqMsg = {
from: 'inject', from: 'inject',
target: MESSAGE_TARGET_EXTENSION, target: MESSAGE_TARGET_EXTENSION,
method, method,
params: params ?? {}, params: params ?? {},
} }
return await this.portMessageHandler!.sendMessage(messageData).then((messageResult) => { return await this.portMessageHandler!.sendMessage(req).then((res) => {
if (messageResult.success) { if (res.code === 200) {
return messageResult.data as T return res.data as T
} else { } else {
throw new Error(messageResult.message) throw new Error(res.message)
} }
}) })
} }

View File

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

View File

@@ -1,27 +1,24 @@
import { MESSAGE_TARGET_EXTENSION, MESSAGE_TARGET_INJECT, MESSAGE_TO_EXTENSION_ROUTE_MSG } from '@/consts/const' 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'
const useMessage = () => { const useMessage = () => {
const sendExtension = useCallback(async <T = any>(method: string, params?: any) => { const sendExtension = useCallback(async <T = any>(method: string, params?: any) => {
// wait // wait
const portMessageHandler = await injectWaiter.wait() as Layer1Protocol<MessageData, MessageResult> const pmh = await injectWaiter.wait() as Layer1Protocol<L2ReqMsg, L2ResMsg>
// send message // send message
const messageResult = await portMessageHandler.sendMessage({ const res = await pmh.sendMessage({
from: 'app', from: 'app',
target: MESSAGE_TARGET_EXTENSION, target: MESSAGE_TARGET_EXTENSION,
method, method,
params: params ?? {}, params: params ?? {},
}) as MessageResult | undefined })
if (messageResult != null) { if (res.code === 200) {
if (messageResult.success) { return res.data as T
return messageResult.data as T
} else { } else {
throw new Error(messageResult.message) throw new Error(res.message)
}
} else {
throw new Error('no response')
} }
}, []) }, [])

View File

@@ -4,13 +4,14 @@ 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'
const debug = (...args: any[]) => { const debug = (...args: any[]) => {
console.debug('[App Messaging]', ...args) console.debug('[App Messaging]', ...args)
} }
let portMessageHandlerInit: boolean = false let portMessageHandlerInit: boolean = false
let portMessageHandler: Layer1Protocol<MessageData, MessageResult> | undefined let portMessageHandler: Layer1Protocol<L2ReqMsg, L2ResMsg> | undefined
// let postInjectMessage: (method: string, params: PostMessagePayload) => Promise<PostMessageResponse> | undefined // let postInjectMessage: (method: string, params: PostMessagePayload) => Promise<PostMessageResponse> | undefined
export const injectWaiter = new Waiter<any>(() => ({ export const injectWaiter = new Waiter<any>(() => ({
@@ -21,25 +22,23 @@ export const injectWaiter = new Waiter<any>(() => ({
const useMessageService = (methods?: { const useMessageService = (methods?: {
[key: string]: (params: any, context: MethodContext) => Promise<any> [key: string]: (params: any, context: MethodContext) => Promise<any>
}) => { }) => {
const messageHandler = useCallback(async (event: MessageData): Promise<MessageResult> => { const messageHandler = useCallback(async (req: L2ReqMsg): Promise<L2ResMsg> => {
debug(`${event.from} => `, JSON.stringify(event)) debug(`${req.from} => `, JSON.stringify(req))
// check event target // check event target
if (event.target !== MESSAGE_TARGET_APP) return { if (req.target !== MESSAGE_TARGET_APP) return {
success: false,
code: 501, code: 501,
message: 'Target Error: ' + event.target, message: 'Target Error: ' + req.target,
} }
const method = methods?.[event.method] const method = methods?.[req.method]
if (method != null) { if (method != null) {
return method(event.params, { return method(req.params, {
from: event.from, from: req.from,
event, event: req,
}).then(data => { }).then(data => {
// debug(`${source} <= `, event.method, JSON.stringify(data)) // debug(`${source} <= `, event.method, JSON.stringify(data))
return { return {
success: true,
code: 200, code: 200,
data, data,
} }
@@ -54,16 +53,14 @@ const useMessageService = (methods?: {
message = 'error: ' + JSON.stringify(err) message = 'error: ' + JSON.stringify(err)
} }
return { return {
success: false,
code: 500, code: 500,
message, message,
} }
}) })
} else { } else {
return { return {
success: false,
code: 501, code: 501,
message: 'Unknown method: ' + event.method, message: 'Unknown method: ' + req.method,
} }
} }
}, [methods]) }, [methods])
@@ -75,7 +72,7 @@ const useMessageService = (methods?: {
}, []) }, [])
portMessageHandler = useMemo(() => { portMessageHandler = useMemo(() => {
if (messageHandler && port) { if (messageHandler && port) {
const pmh = new Layer1Protocol<MessageData, MessageResult>(messageHandler, port) const pmh = new Layer1Protocol<L2ReqMsg, L2ResMsg>(messageHandler, port)
//get tabId from url params //get tabId from url params
let tabIdStr = window.location.search.split('tabId=')[1] let tabIdStr = window.location.search.split('tabId=')[1]
@@ -87,7 +84,7 @@ const useMessageService = (methods?: {
type: 'app', type: 'app',
tabId, tabId,
}, },
} as MessageData) } as L2ReqMsg)
portMessageHandlerInit = true portMessageHandlerInit = true
return pmh return pmh