添加GM element API
This commit is contained in:
parent
a2870eb18e
commit
7ca85801ef
@ -17,7 +17,7 @@ export class CustomEventMessage implements Message {
|
||||
EE: EventEmitter = new EventEmitter();
|
||||
|
||||
// 关联dom目标
|
||||
relatedTarget: Map<number, Document> = new Map();
|
||||
relatedTarget: Map<number, EventTarget> = 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, <Document>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);
|
||||
|
@ -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<any> | void;
|
||||
export type ApiFunctionSync = (params: any, con: GetSender) => any;
|
||||
|
||||
export class Server {
|
||||
private apiFunctionMap: Map<string, ApiFunction> = 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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 = <Element>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;
|
||||
}
|
||||
(<Element>parentNode || document.head || document.body || document.querySelector("*")).appendChild(el);
|
||||
const nodeId = (this.msg as CustomEventMessage).sendRelatedTarget(el);
|
||||
return nodeId;
|
||||
}
|
||||
}
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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 = (<CustomEventMessage>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 (<CustomEventMessage>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 = (<CustomEventMessage>this.message).sendRelatedTarget(parentNodeId);
|
||||
parentNodeId = id;
|
||||
} else {
|
||||
parentNodeId = null;
|
||||
}
|
||||
const resp = (<CustomEventMessage>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 (<CustomEventMessage>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 || <GMTypes.NotificationOnDone>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;
|
||||
}
|
||||
}
|
||||
|
@ -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) => {
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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<any>;
|
||||
|
||||
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<true> = {
|
||||
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(<string>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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
"webRequest",
|
||||
"userScripts",
|
||||
"contextMenus",
|
||||
"notifications",
|
||||
"unlimitedStorage",
|
||||
"declarativeNetRequest"
|
||||
],
|
||||
|
@ -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])
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user