diff --git a/packages/message/custom_event_message.ts b/packages/message/custom_event_message.ts index b049524..7c0cf88 100644 --- a/packages/message/custom_event_message.ts +++ b/packages/message/custom_event_message.ts @@ -17,7 +17,7 @@ export class CustomEventMessage implements Message { EE: EventEmitter = new EventEmitter(); // 关联dom目标 - relatedTarget: Map = new Map(); + relatedTarget: Map = new Map(); constructor( protected flag: string, @@ -25,7 +25,7 @@ export class CustomEventMessage implements Message { ) { window.addEventListener((isContent ? "ct" : "fd") + flag, (event) => { if (event instanceof MouseEvent) { - this.relatedTarget.set(event.clientX, event.relatedTarget); + this.relatedTarget.set(event.clientX, event.relatedTarget!); return; } else if (event instanceof CustomEvent) { this.messageHandle(event.detail, new CustomEventPostMessage(this)); @@ -82,23 +82,7 @@ export class CustomEventMessage implements Message { }); } - nativeSend(data: any) { - let detail = data; - - // 特殊处理relatedTarget - if (detail.data && typeof detail.data.relatedTarget === "object") { - // 先将relatedTarget转换成id发送过去 - const target = detail.data.relatedTarget; - delete detail.data.relatedTarget; - detail.data.relatedTarget = Math.ceil(Math.random() * 1000000); - // 可以使用此种方式交互element - const ev = new MouseEvent((this.isContent ? "fd" : "ct") + this.flag, { - clientX: detail.data.relatedTarget, - relatedTarget: target, - }); - window.dispatchEvent(ev); - } - + nativeSend(detail: any) { if (typeof cloneInto !== "undefined") { try { LoggerCore.logger().info("nativeSend"); @@ -131,6 +115,40 @@ export class CustomEventMessage implements Message { }); } + // 同步发送消息 + // 与content页的消息通讯实际是同步,此方法不需要经过background + // 但是请注意中间不要有promise + syncSendMessage(data: any): any { + const body: WindowMessageBody = { + messageId: uuidv4(), + type: "sendMessage", + data, + }; + let ret: any; + const callback = (body: WindowMessageBody) => { + this.EE.removeListener("response:" + body.messageId, callback); + ret = body.data; + }; + this.EE.addListener("response:" + body.messageId, callback); + this.nativeSend(body); + return ret; + } + + relateId = 0; + + sendRelatedTarget(target: EventTarget): number { + // 特殊处理relatedTarget,返回id进行关联 + // 先将relatedTarget转换成id发送过去 + const id = ++this.relateId; + // 可以使用此种方式交互element + const ev = new MouseEvent((this.isContent ? "fd" : "ct") + this.flag, { + clientX: id, + relatedTarget: target, + }); + window.dispatchEvent(ev); + return id; + } + getAndDelRelatedTarget(id: number) { const target = this.relatedTarget.get(id); this.relatedTarget.delete(id); diff --git a/packages/message/server.ts b/packages/message/server.ts index 7fc61bc..515b060 100644 --- a/packages/message/server.ts +++ b/packages/message/server.ts @@ -1,5 +1,5 @@ import LoggerCore from "@App/app/logger/core"; -import { connect } from "./client"; +import { connect, sendMessage } from "./client"; export interface Message extends MessageSend { onConnect(callback: (data: any, con: MessageConnect) => void): void; @@ -20,6 +20,12 @@ export interface MessageConnect { export type MessageSender = chrome.runtime.MessageSender; +export type ExtMessageSender = { + tabId: number; + frameId?: number; + documentId?: string; +}; + export class GetSender { constructor(private sender: MessageConnect | MessageSender) {} @@ -27,12 +33,22 @@ export class GetSender { return this.sender as MessageSender; } + getExtMessageSender(): ExtMessageSender { + const sender = this.sender as MessageSender; + return { + tabId: sender.tab?.id || -1, // -1表示后台脚本 + frameId: sender.frameId, + documentId: sender.documentId, + }; + } + getConnect(): MessageConnect { return this.sender as MessageConnect; } } export type ApiFunction = (params: any, con: GetSender) => Promise | void; +export type ApiFunctionSync = (params: any, con: GetSender) => any; export class Server { private apiFunctionMap: Map = new Map(); @@ -115,11 +131,24 @@ export class Group { } // 转发消息 -export function forwardMessage(prefix: string, path: string, from: Server, to: MessageSend, middleware?: ApiFunction) { - from.on(path, async (params, fromCon) => { +export function forwardMessage( + prefix: string, + path: string, + from: Server, + to: MessageSend, + middleware?: ApiFunctionSync +) { + from.on(path, (params, fromCon) => { if (middleware) { - const resp = await middleware(params, fromCon); - if (resp !== false) { + // 此处是为了处理CustomEventMessage的同步消息情况 + const resp = middleware(params, fromCon) as any; + if (resp instanceof Promise) { + return resp.then((data) => { + if (data !== false) { + return data; + } + }); + } else if (resp !== false) { return resp; } } @@ -140,7 +169,7 @@ export function forwardMessage(prefix: string, path: string, from: Server, to: M }); }); } else { - return to.sendMessage({ action: prefix + "/" + path, data: params }); + return sendMessage(to, prefix + "/" + path, params); } }); } diff --git a/src/app/service/content/content.ts b/src/app/service/content/content.ts index 9588762..0e97142 100644 --- a/src/app/service/content/content.ts +++ b/src/app/service/content/content.ts @@ -1,5 +1,6 @@ import { ScriptRunResouce } from "@App/app/repo/scripts"; import { Client, sendMessage } from "@Packages/message/client"; +import { CustomEventMessage } from "@Packages/message/custom_event_message"; import { forwardMessage, Message, MessageSend, Server } from "@Packages/message/server"; // content页的处理 @@ -52,6 +53,32 @@ export default class ContentRuntime { xhr.send(); }); } + case "GM_addElement": { + let [parentNodeId, tagName, attr] = data.params; + let parentNode: EventTarget | undefined; + if (parentNodeId) { + parentNode = (this.msg as CustomEventMessage).getAndDelRelatedTarget(parentNodeId); + } + const el = document.createElement(tagName); + Object.keys(attr).forEach((key) => { + el.setAttribute(key, attr[key]); + }); + let textContent = ""; + if (attr) { + if (attr.textContent) { + textContent = attr.textContent; + delete attr.textContent; + } + } else { + attr = {}; + } + if (textContent) { + el.innerHTML = textContent; + } + (parentNode || document.head || document.body || document.querySelector("*")).appendChild(el); + const nodeId = (this.msg as CustomEventMessage).sendRelatedTarget(el); + return nodeId; + } } return Promise.resolve(false); } diff --git a/src/app/service/content/exec_script.ts b/src/app/service/content/exec_script.ts index f135441..5149a3c 100644 --- a/src/app/service/content/exec_script.ts +++ b/src/app/service/content/exec_script.ts @@ -4,6 +4,7 @@ import { ScriptRunResouce } from "@App/app/repo/scripts"; import GMApi from "./gm_api"; import { compileScript, createContext, proxyContext, ScriptFunc } from "./utils"; import { Message } from "@Packages/message/server"; +import { EmitEventRequest } from "../service_worker/runtime"; export type ValueUpdateSender = { runFlag: string; @@ -69,12 +70,9 @@ export default class ExecScript { } } - emitEvent(event: string, data: any) { - switch (event) { - case "menuClick": - this.sandboxContent?.menuClick(data); - break; - } + emitEvent(event: string, eventId: string, data: any) { + this.logger.debug("emit event", { event, eventId, data }); + this.sandboxContent?.emitEvent(event, eventId, data); } valueUpdate(data: ValueUpdateData) { diff --git a/src/app/service/content/gm_api.ts b/src/app/service/content/gm_api.ts index 4ac4f1f..d254b6b 100644 --- a/src/app/service/content/gm_api.ts +++ b/src/app/service/content/gm_api.ts @@ -1,5 +1,5 @@ import { ScriptRunResouce } from "@App/app/repo/scripts"; -import { getMetadataStr, getUserConfigStr, parseUserConfig } from "@App/pkg/utils/script"; +import { base64ToBlob, getMetadataStr, getUserConfigStr, parseUserConfig } from "@App/pkg/utils/script"; import { ValueUpdateData } from "./exec_script"; import { ExtVersion } from "@App/app/const"; import { Message, MessageConnect } from "@Packages/message/server"; @@ -11,7 +11,6 @@ import { getStorageName } from "@App/pkg/utils/utils"; interface ApiParam { depend?: string[]; - listener?: () => void; } export interface ApiValue { @@ -25,9 +24,6 @@ export class GMContext { public static API(param: ApiParam = {}) { return (target: any, propertyName: string, descriptor: PropertyDescriptor) => { const key = propertyName; - if (param.listener) { - param.listener(); - } if (key === "GMdotXmlHttpRequest") { GMContext.apis.set("GM.xmlHttpRequest", { api: descriptor.value, @@ -103,8 +99,8 @@ export default class GMApi { } } - menuClick(id: number) { - this.EE.emit("menuClick" + id); + emitEvent(event: string, eventId: string, data: any) { + this.EE.emit(event + ":" + eventId, data); } // 获取脚本信息和管理器信息 @@ -239,19 +235,71 @@ export default class GMApi { this.eventId += 1; const id = this.eventId; this.menuMap.set(id, name); - this.EE.addListener("menuClick" + id, listener); + this.EE.addListener("menuClick:" + id, listener); this.sendMessage("GM_registerMenuCommand", [id, name, accessKey]); return id; } + @GMContext.API() + GM_addStyle(css: string) { + // 与content页的消息通讯实际是同步,此方法不需要经过background + // 这里直接使用同步的方式去处理, 不要有promise + const resp = (this.message).syncSendMessage({ + action: this.prefix + "/runtime/gmApi", + data: { + uuid: this.scriptRes.uuid, + api: "GM_addElement", + params: [ + null, + "style", + { + textContent: css, + }, + ], + }, + }); + if (resp.code !== 0) { + throw new Error(resp.message); + } + return (this.message).getAndDelRelatedTarget(resp.data); + } + + @GMContext.API() + GM_addElement(parentNode: EventTarget | string, tagName: any, attrs?: any) { + // 与content页的消息通讯实际是同步,此方法不需要经过background + // 这里直接使用同步的方式去处理, 不要有promise + let parentNodeId: any = parentNode; + if (typeof parentNodeId !== "string") { + const id = (this.message).sendRelatedTarget(parentNodeId); + parentNodeId = id; + } else { + parentNodeId = null; + } + const resp = (this.message).syncSendMessage({ + action: this.prefix + "/runtime/gmApi", + data: { + uuid: this.scriptRes.uuid, + api: "GM_addElement", + params: [ + parentNodeId, + typeof parentNode === "string" ? parentNode : tagName, + typeof parentNode === "string" ? tagName : attrs, + ], + }, + }); + if (resp.code !== 0) { + throw new Error(resp.message); + } + return (this.message).getAndDelRelatedTarget(resp.data); + } + @GMContext.API() GM_unregisterMenuCommand(id: number): void { - console.log("unregisterMenuCommand", id); if (!this.menuMap) { this.menuMap = new Map(); } this.menuMap.delete(id); - this.EE.removeAllListeners("menuClick" + id); + this.EE.removeAllListeners("menuClick:" + id); this.sendMessage("GM_unregisterMenuCommand", [id]); } @@ -449,15 +497,19 @@ export default class GMApi { }; } - @GMContext.API() + @GMContext.API({ + depend: ["GM_closeNotification", "GM_updateNotification"], + }) public async GM_notification( detail: GMTypes.NotificationDetails | string, ondone?: GMTypes.NotificationOnDone | string, image?: string, onclick?: GMTypes.NotificationOnClick ) { - let data: GMTypes.NotificationDetails = {}; + this.eventId += 1; + let data: GMTypes.NotificationDetails; if (typeof detail === "string") { + data = {}; data.text = detail; switch (arguments.length) { case 4: @@ -470,7 +522,7 @@ export default class GMApi { break; } } else { - data = detail; + data = Object.assign({}, detail); data.ondone = data.ondone || ondone; } let click: GMTypes.NotificationOnClick; @@ -488,28 +540,54 @@ export default class GMApi { create = data.oncreate; delete data.oncreate; } - this.eventId += 1; - this.sendMessage("GM_notification", [data]); - this.EE.addListener("GM_notification:" + this.eventId, (resp: any) => { - switch (resp.event) { - case "click": { - click && click.apply({ id: resp.id }, [resp.id, resp.index]); - break; - } - case "done": { - done && done.apply({ id: resp.id }, [resp.user]); - break; - } - case "create": { - create && create.apply({ id: resp.id }, [resp.id]); - break; - } - default: - LoggerCore.logger().warn("GM_notification resp is error", { - resp, - }); - break; + this.sendMessage("GM_notification", [data]).then((id) => { + if (create) { + create.apply({ id }, [id]); } + this.EE.addListener("GM_notification:" + id, (resp: any) => { + switch (resp.event) { + case "click": + case "buttonClick": { + click && click.apply({ id }, [id, resp.params.index]); + break; + } + case "close": { + done && done.apply({ id }, [resp.params.byUser]); + this.EE.removeAllListeners("GM_notification:" + this.eventId); + break; + } + default: + LoggerCore.logger().warn("GM_notification resp is error", { + resp, + }); + break; + } + }); }); } + + @GMContext.API() + public GM_closeNotification(id: string): void { + this.sendMessage("GM_closeNotification", [id]); + } + + @GMContext.API() + public GM_updateNotification(id: string, details: GMTypes.NotificationDetails): void { + this.sendMessage("GM_updateNotification", [id, details]); + } + + @GMContext.API() + GM_getResourceURL(name: string, isBlobUrl?: boolean): string | undefined { + if (!this.scriptRes.resource) { + return undefined; + } + const r = this.scriptRes.resource[name]; + if (r) { + if (isBlobUrl) { + return URL.createObjectURL(base64ToBlob(r.base64)); + } + return r.base64; + } + return undefined; + } } diff --git a/src/app/service/content/inject.ts b/src/app/service/content/inject.ts index bc42eee..8636c3c 100644 --- a/src/app/service/content/inject.ts +++ b/src/app/service/content/inject.ts @@ -34,7 +34,7 @@ export class InjectRuntime { // 转发给脚本 const exec = this.execList.find((val) => val.scriptRes.uuid === data.uuid); if (exec) { - exec.emitEvent(data.event, data.data); + exec.emitEvent(data.event, data.eventId, data.data); } }); this.server.on("runtime/valueUpdate", (data: ValueUpdateData) => { diff --git a/src/app/service/content/utils.ts b/src/app/service/content/utils.ts index 9ae37b0..fcc2e3c 100644 --- a/src/app/service/content/utils.ts +++ b/src/app/service/content/utils.ts @@ -66,7 +66,7 @@ export function createContext(scriptRes: ScriptRunResouce, GMInfo: any, envPrefi runFlag: uuidv4(), eventId: 10000, valueUpdate: GMApi.prototype.valueUpdate, - menuClick: GMApi.prototype.menuClick, + emitEvent: GMApi.prototype.emitEvent, EE: new EventEmitter(), GM: { Info: GMInfo }, GM_info: GMInfo, diff --git a/src/app/service/sandbox/runtime.ts b/src/app/service/sandbox/runtime.ts index 24ff66d..cab680e 100644 --- a/src/app/service/sandbox/runtime.ts +++ b/src/app/service/sandbox/runtime.ts @@ -305,7 +305,7 @@ export class Runtime { // 转发给脚本 const exec = this.execScripts.get(data.uuid); if (exec) { - exec.emitEvent(data.event, data.data); + exec.emitEvent(data.event, data.eventId, data.data); } } diff --git a/src/app/service/service_worker/client.ts b/src/app/service/service_worker/client.ts index 18139fb..83c3ef8 100644 --- a/src/app/service/service_worker/client.ts +++ b/src/app/service/service_worker/client.ts @@ -119,9 +119,11 @@ export class PopupClient extends Client { return this.do("menuClick", { uuid, id: data.id, - tabId: data.tabId, - frameId: data.frameId, - documentId: data.documentId, + sender: { + tabId: data.tabId, + frameId: data.frameId, + documentId: data.documentId, + }, }); } } diff --git a/src/app/service/service_worker/gm_api.ts b/src/app/service/service_worker/gm_api.ts index fa7ccd5..7ee3d6f 100644 --- a/src/app/service/service_worker/gm_api.ts +++ b/src/app/service/service_worker/gm_api.ts @@ -1,7 +1,7 @@ import LoggerCore from "@App/app/logger/core"; import Logger from "@App/app/logger/logger"; import { Script, ScriptDAO } from "@App/app/repo/scripts"; -import { GetSender, Group, MessageSend } from "@Packages/message/server"; +import { ExtMessageSender, GetSender, Group, MessageSend } from "@Packages/message/server"; import { ValueService } from "@App/app/service/service_worker/value"; import PermissionVerify from "./permission_verify"; import { connect } from "@Packages/message/client"; @@ -9,6 +9,7 @@ import Cache, { incr } from "@App/app/cache"; import EventEmitter from "eventemitter3"; import { MessageQueue } from "@Packages/message/message_queue"; import { RuntimeService } from "./runtime"; +import { getIcon, isFirefox } from "@App/pkg/utils/utils"; // GMApi,处理脚本的GM API调用请求 @@ -52,6 +53,12 @@ export const unsafeHeaders: { [key: string]: boolean } = { via: true, }; +type NotificationData = { + uuid: string; + details: GMTypes.NotificationDetails; + sender: ExtMessageSender; +}; + export type Api = (request: Request, con: GetSender) => Promise; export default class GMApi { @@ -71,7 +78,7 @@ export default class GMApi { this.logger = LoggerCore.logger().with({ service: "runtime/gm_api" }); } - async handlerRequest(data: MessageRequest, con: GetSender) { + async handlerRequest(data: MessageRequest, sender: GetSender) { this.logger.trace("GM API request", { api: data.api, uuid: data.uuid, param: data.params }); const api = PermissionVerify.apis.get(data.api); if (!api) { @@ -84,7 +91,7 @@ export default class GMApi { this.logger.error("verify error", { api: data.api }, Logger.E(e)); return Promise.reject(e); } - return api.api.call(this, req, con); + return api.api.call(this, req, sender); } // 解析请求 @@ -240,6 +247,105 @@ export default class GMApi { }); } + @PermissionVerify.API({}) + GM_notification(request: Request, sender: GetSender) { + if (request.params.length === 0) { + return Promise.reject(new Error("param is failed")); + } + const details: GMTypes.NotificationDetails = request.params[0]; + const options: chrome.notifications.NotificationOptions = { + title: details.title || "ScriptCat", + message: details.text || "无消息内容", + iconUrl: details.image || getIcon(request.script) || chrome.runtime.getURL("assets/logo.png"), + type: isFirefox() || details.progress === undefined ? "basic" : "progress", + }; + if (!isFirefox()) { + options.silent = details.silent; + options.buttons = details.buttons; + } + options.progress = options.progress && parseInt(details.progress as any, 10); + + return new Promise((resolve) => { + chrome.notifications.create(options, (notificationId) => { + Cache.getInstance().set(`GM_notification:${notificationId}`, { + uuid: request.script.uuid, + details: details, + sender: sender.getExtMessageSender(), + }); + if (details.timeout) { + setTimeout(() => { + chrome.notifications.clear(notificationId); + Cache.getInstance().del(`GM_notification:${notificationId}`); + }, details.timeout); + } + resolve(notificationId); + }); + }); + } + + @PermissionVerify.API({ + link: "GM_notification", + }) + GM_closeNotification(request: Request) { + if (request.params.length === 0) { + return Promise.reject(new Error("param is failed")); + } + const [notificationId] = request.params; + Cache.getInstance().del(`GM_notification:${notificationId}`); + chrome.notifications.clear(notificationId); + } + + @PermissionVerify.API({ + link: "GM_notification", + }) + GM_updateNotification(request: Request) { + if (isFirefox()) { + return Promise.reject(new Error("firefox does not support this method")); + } + const id = request.params[0]; + const details: GMTypes.NotificationDetails = request.params[1]; + const options: chrome.notifications.NotificationOptions = { + title: details.title, + message: details.text, + iconUrl: details.image, + type: details.progress === undefined ? "basic" : "progress", + silent: details.silent, + progress: details.progress && parseInt(details.progress as any, 10), + }; + chrome.notifications.update(id, options); + } + + handlerNotification() { + const send = async (event: string, notificationId: string, params?: any) => { + const ret = (await Cache.getInstance().get(`GM_notification:${notificationId}`)) as NotificationData; + if (ret) { + this.runtime.emitEventToTab(ret.sender, { + event: "GM_notification", + eventId: notificationId, + uuid: ret.uuid, + data: { + event, + params, + }, + }); + } + }; + chrome.notifications.onClosed.addListener((notificationId, byUser) => { + send("close", notificationId, { + byUser, + }); + Cache.getInstance().del(`GM_notification:${notificationId}`); + }); + chrome.notifications.onClicked.addListener((notificationId) => { + send("click", notificationId); + }); + chrome.notifications.onButtonClicked.addListener((notificationId, index) => { + send("buttonClick", notificationId, { + index: index, + }); + }); + } + // 处理GM_xmlhttpRequest请求 handlerGmXhr() { chrome.webRequest.onBeforeSendHeaders.addListener( @@ -290,5 +396,6 @@ export default class GMApi { start() { this.group.on("gmApi", this.handlerRequest.bind(this)); this.handlerGmXhr(); + this.handlerNotification(); } } diff --git a/src/app/service/service_worker/popup.ts b/src/app/service/service_worker/popup.ts index dda3332..3ff02ea 100644 --- a/src/app/service/service_worker/popup.ts +++ b/src/app/service/service_worker/popup.ts @@ -1,5 +1,5 @@ import { MessageQueue } from "@Packages/message/message_queue"; -import { Group } from "@Packages/message/server"; +import { ExtMessageSender, Group } from "@Packages/message/server"; import { RuntimeService, ScriptMatchInfo } from "./runtime"; import Cache from "@App/app/cache"; import { GetPopupDataReq, GetPopupDataRes } from "./client"; @@ -315,32 +315,13 @@ export class PopupService { }); } - menuClick({ - uuid, - id, - tabId, - frameId, - documentId, - }: { - uuid: string; - id: number; - tabId: number; - frameId: number; - documentId: string; - }) { + menuClick({ uuid, id, sender }: { uuid: string; id: number; sender: ExtMessageSender }) { // 菜单点击事件 - this.runtime.EmitEventToTab( - tabId, - { - uuid, - event: "menuClick", - data: id, - }, - { - frameId, - documentId: documentId, - } - ); + this.runtime.emitEventToTab(sender, { + uuid, + event: "menuClick", + eventId: id.toString(), + }); return Promise.resolve(true); } @@ -384,9 +365,11 @@ export class PopupService { this.menuClick({ uuid: script.uuid, id: menuItem.id, - tabId: bgscript ? -1 : tab!.id!, - frameId: menuItem.frameId || 0, - documentId: menuItem.documentId || "", + sender: { + tabId: bgscript ? -1 : tab!.id!, + frameId: menuItem.frameId || 0, + documentId: menuItem.documentId || "", + }, }); return; } diff --git a/src/app/service/service_worker/runtime.ts b/src/app/service/service_worker/runtime.ts index 10546fd..8ef93e9 100644 --- a/src/app/service/service_worker/runtime.ts +++ b/src/app/service/service_worker/runtime.ts @@ -1,5 +1,5 @@ import { MessageQueue } from "@Packages/message/message_queue"; -import { GetSender, Group, MessageSend } from "@Packages/message/server"; +import { ExtMessageSender, GetSender, Group, MessageSend } from "@Packages/message/server"; import { Script, SCRIPT_STATUS, @@ -32,7 +32,8 @@ export interface ScriptMatchInfo extends ScriptRunResouce { export interface EmitEventRequest { uuid: string; event: string; - data: any; + eventId: string; + data?: any; } export class RuntimeService { @@ -122,36 +123,35 @@ export class RuntimeService { } // 给指定tab发送消息 - sendMessageToTab( - tabId: number, - action: string, - data: any, - options?: { - documentId?: string; - frameId?: number; - } - ) { - if (tabId === -1) { + sendMessageToTab(to: ExtMessageSender, action: string, data: any) { + if (to.tabId === -1) { // 如果是-1, 代表给offscreen发送消息 return sendMessage(this.sender, "offscreen/runtime/" + action, data); } - return sendMessage(new ExtensionContentMessageSend(tabId, options), "content/runtime/" + action, data); + return sendMessage( + new ExtensionContentMessageSend(to.tabId, { + documentId: to.documentId, + frameId: to.frameId, + }), + "content/runtime/" + action, + data + ); } // 给指定脚本触发事件 - EmitEventToTab( - tabId: number, - req: EmitEventRequest, - options?: { - documentId?: string; - frameId?: number; - } - ) { - if (tabId === -1) { + emitEventToTab(to: ExtMessageSender, req: EmitEventRequest) { + if (to.tabId === -1) { // 如果是-1, 代表给offscreen发送消息 return sendMessage(this.sender, "offscreen/runtime/emitEvent", req); } - return sendMessage(new ExtensionContentMessageSend(tabId, options), "content/runtime/emitEvent", req); + return sendMessage( + new ExtensionContentMessageSend(to.tabId, { + documentId: to.documentId, + frameId: to.frameId, + }), + "content/runtime/emitEvent", + req + ); } async getPageScriptUuidByUrl(url: string, includeCustomize?: boolean) { diff --git a/src/app/service/service_worker/value.ts b/src/app/service/service_worker/value.ts index 5977585..158a885 100644 --- a/src/app/service/service_worker/value.ts +++ b/src/app/service/service_worker/value.ts @@ -73,12 +73,24 @@ export class ValueService { tabs.forEach(async (tab) => { const scriptMenu = await this.popup!.getScriptMenu(tab.id!); if (scriptMenu.find((item) => item.storageName === storageName)) { - this.runtime!.sendMessageToTab(tab.id!, "valueUpdate", sendData); + this.runtime!.sendMessageToTab( + { + tabId: tab.id!, + }, + "valueUpdate", + sendData + ); } }); }); // 推送到offscreen中 - sendMessage(this.send, "offscreen/runtime/valueUpdate", sendData); + this.runtime!.sendMessageToTab( + { + tabId: -1, + }, + "valueUpdate", + sendData + ); return Promise.resolve(true); } diff --git a/src/manifest.json b/src/manifest.json index 647e972..cfd8e5e 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -30,6 +30,7 @@ "webRequest", "userScripts", "contextMenus", + "notifications", "unlimitedStorage", "declarativeNetRequest" ], diff --git a/src/pkg/utils/utils.ts b/src/pkg/utils/utils.ts index fe6b954..0c4ce23 100644 --- a/src/pkg/utils/utils.ts +++ b/src/pkg/utils/utils.ts @@ -224,3 +224,13 @@ export function getStorageName(script: Script): string { } return script.uuid; } + +export function getIcon(script: Script): string | undefined { + return ( + (script.metadata.icon && script.metadata.icon[0]) || + (script.metadata.iconurl && script.metadata.iconurl[0]) || + (script.metadata.defaulticon && script.metadata.defaulticon[0]) || + (script.metadata.icon64 && script.metadata.icon64[0]) || + (script.metadata.icon64url && script.metadata.icon64url[0]) + ); +}