popup页与注册菜单
Some checks failed
test / Run tests (push) Failing after 3s
build / Build (push) Failing after 6s
Some checks failed
test / Run tests (push) Failing after 3s
build / Build (push) Failing after 6s
This commit is contained in:
parent
a7620dd7e5
commit
c43afb0a94
@ -84,3 +84,25 @@ export class ExtensionMessageConnect implements MessageConnect {
|
|||||||
this.con.onDisconnect.addListener(callback);
|
this.con.onDisconnect.addListener(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ExtensionContentMessageSend extends ExtensionMessageSend {
|
||||||
|
constructor(private tabId: number) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage(data: any): Promise<any> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
chrome.tabs.sendMessage(this.tabId, data, (resp) => {
|
||||||
|
resolve(resp);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(data: any): Promise<MessageConnect> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const con = chrome.tabs.connect(this.tabId);
|
||||||
|
con.postMessage(data);
|
||||||
|
resolve(new ExtensionMessageConnect(con));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,7 +17,7 @@ export interface MessageConnect {
|
|||||||
onDisconnect(callback: () => void): void;
|
onDisconnect(callback: () => void): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MessageSender = any;
|
export type MessageSender = chrome.runtime.MessageSender;
|
||||||
|
|
||||||
export class GetSender {
|
export class GetSender {
|
||||||
constructor(private sender: MessageConnect | MessageSender) {}
|
constructor(private sender: MessageConnect | MessageSender) {}
|
||||||
@ -117,13 +117,13 @@ export class Group {
|
|||||||
export function forwardMessage(prefix: string, path: string, from: Server, to: MessageSend, middleware?: ApiFunction) {
|
export function forwardMessage(prefix: string, path: string, from: Server, to: MessageSend, middleware?: ApiFunction) {
|
||||||
from.on(path, async (params, fromCon) => {
|
from.on(path, async (params, fromCon) => {
|
||||||
if (middleware) {
|
if (middleware) {
|
||||||
const resp = await middleware(params, new GetSender(fromCon));
|
const resp = await middleware(params, fromCon);
|
||||||
if (resp !== false) {
|
if (resp !== false) {
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fromCon) {
|
|
||||||
const fromConnect = fromCon.getConnect();
|
const fromConnect = fromCon.getConnect();
|
||||||
|
if (fromConnect) {
|
||||||
to.connect({ action: prefix + "/" + path, data: params }).then((toCon) => {
|
to.connect({ action: prefix + "/" + path, data: params }).then((toCon) => {
|
||||||
fromConnect.onMessage((data) => {
|
fromConnect.onMessage((data) => {
|
||||||
toCon.sendMessage(data);
|
toCon.sendMessage(data);
|
||||||
|
@ -14,7 +14,7 @@ export class OffscreenManager {
|
|||||||
|
|
||||||
private windowMessage = new WindowMessage(window, sandbox, true);
|
private windowMessage = new WindowMessage(window, sandbox, true);
|
||||||
|
|
||||||
private windowApi: Server = new Server("offscreen", this.windowMessage);
|
private windowServer: Server = new Server("offscreen", this.windowMessage);
|
||||||
|
|
||||||
private messageQueue: MessageQueue = new MessageQueue();
|
private messageQueue: MessageQueue = new MessageQueue();
|
||||||
|
|
||||||
@ -36,21 +36,21 @@ export class OffscreenManager {
|
|||||||
|
|
||||||
async initManager() {
|
async initManager() {
|
||||||
// 监听消息
|
// 监听消息
|
||||||
this.windowApi.on("logger", this.logger.bind(this));
|
this.windowServer.on("logger", this.logger.bind(this));
|
||||||
this.windowApi.on("preparationSandbox", this.preparationSandbox.bind(this));
|
this.windowServer.on("preparationSandbox", this.preparationSandbox.bind(this));
|
||||||
this.windowApi.on("sendMessageToServiceWorker", this.sendMessageToServiceWorker.bind(this));
|
this.windowServer.on("sendMessageToServiceWorker", this.sendMessageToServiceWorker.bind(this));
|
||||||
const script = new ScriptService(
|
const script = new ScriptService(
|
||||||
this.windowApi.group("script"),
|
this.windowServer.group("script"),
|
||||||
this.extensionMessage,
|
this.extensionMessage,
|
||||||
this.windowMessage,
|
this.windowMessage,
|
||||||
this.messageQueue
|
this.messageQueue
|
||||||
);
|
);
|
||||||
script.init();
|
script.init();
|
||||||
// 转发从sandbox来的gm api请求
|
// 转发从sandbox来的gm api请求
|
||||||
forwardMessage("serviceWorker", "runtime/gmApi", this.windowApi, this.extensionMessage);
|
forwardMessage("serviceWorker", "runtime/gmApi", this.windowServer, this.extensionMessage);
|
||||||
// 转发message queue请求
|
// 转发message queue请求
|
||||||
|
|
||||||
const gmApi = new GMApi(this.windowApi.group("gmApi"));
|
const gmApi = new GMApi(this.windowServer.group("gmApi"));
|
||||||
gmApi.init();
|
gmApi.init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,3 +27,19 @@ export function subscribeScriptRunStatus(
|
|||||||
) {
|
) {
|
||||||
return messageQueue.subscribe("scriptRunStatus", callback);
|
return messageQueue.subscribe("scriptRunStatus", callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ScriptMenuRegisterCallbackValue = {
|
||||||
|
uuid: string;
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
accessKey: string;
|
||||||
|
tabId: number;
|
||||||
|
frameId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function subscribeScriptMenuRegister(
|
||||||
|
messageQueue: MessageQueue,
|
||||||
|
callback: (message: ScriptMenuRegisterCallbackValue) => void
|
||||||
|
) {
|
||||||
|
return messageQueue.subscribe("registerMenuCommand", callback);
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ import { Client } from "@Packages/message/client";
|
|||||||
import { InstallSource } from ".";
|
import { InstallSource } from ".";
|
||||||
import { Resource } from "@App/app/repo/resource";
|
import { Resource } from "@App/app/repo/resource";
|
||||||
import { MessageSend } from "@Packages/message/server";
|
import { MessageSend } from "@Packages/message/server";
|
||||||
|
import { ScriptMenu, ScriptMenuItem } from "./popup";
|
||||||
|
|
||||||
export class ServiceWorkerClient extends Client {
|
export class ServiceWorkerClient extends Client {
|
||||||
constructor(msg: MessageSend) {
|
constructor(msg: MessageSend) {
|
||||||
@ -90,3 +91,27 @@ export class RuntimeClient extends Client {
|
|||||||
return this.do("scriptLoad", { flag, uuid });
|
return this.do("scriptLoad", { flag, uuid });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type GetPopupDataReq = {
|
||||||
|
tabId: number;
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetPopupDataRes = {
|
||||||
|
scriptList: ScriptMenu[];
|
||||||
|
backScriptList: ScriptMenu[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export class PopupClient extends Client {
|
||||||
|
constructor(msg: MessageSend) {
|
||||||
|
super(msg, "serviceWorker/popup");
|
||||||
|
}
|
||||||
|
|
||||||
|
getPopupData(data: GetPopupDataReq): Promise<GetPopupDataRes> {
|
||||||
|
return this.do("getPopupData", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
menuClick(uuid: string, data: ScriptMenuItem) {
|
||||||
|
return this.do("menuClick", { uuid, id: data.id, tabId: data.tabId, frameId: data.frameId });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import LoggerCore from "@App/app/logger/core";
|
import LoggerCore from "@App/app/logger/core";
|
||||||
import Logger from "@App/app/logger/logger";
|
import Logger from "@App/app/logger/logger";
|
||||||
import { Script, ScriptDAO } from "@App/app/repo/scripts";
|
import { Script, ScriptDAO } from "@App/app/repo/scripts";
|
||||||
import { GetSender, Group, MessageSend, MessageSender } from "@Packages/message/server";
|
import { GetSender, Group, MessageSend } from "@Packages/message/server";
|
||||||
import { ValueService } from "@App/app/service/service_worker/value";
|
import { ValueService } from "@App/app/service/service_worker/value";
|
||||||
import PermissionVerify from "./permission_verify";
|
import PermissionVerify from "./permission_verify";
|
||||||
import { connect } from "@Packages/message/client";
|
import { connect } from "@Packages/message/client";
|
||||||
@ -21,7 +21,6 @@ export type MessageRequest = {
|
|||||||
|
|
||||||
export type Request = MessageRequest & {
|
export type Request = MessageRequest & {
|
||||||
script: Script;
|
script: Script;
|
||||||
sender: MessageSender;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Api = (request: Request, con: GetSender) => Promise<any>;
|
export type Api = (request: Request, con: GetSender) => Promise<any>;
|
||||||
@ -48,7 +47,7 @@ export default class GMApi {
|
|||||||
if (!api) {
|
if (!api) {
|
||||||
return Promise.reject(new Error("gm api is not found"));
|
return Promise.reject(new Error("gm api is not found"));
|
||||||
}
|
}
|
||||||
const req = await this.parseRequest(data, { tabId: 0 });
|
const req = await this.parseRequest(data);
|
||||||
try {
|
try {
|
||||||
await this.permissionVerify.verify(req, api);
|
await this.permissionVerify.verify(req, api);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -59,14 +58,13 @@ export default class GMApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 解析请求
|
// 解析请求
|
||||||
async parseRequest(data: MessageRequest, sender: MessageSender): Promise<Request> {
|
async parseRequest(data: MessageRequest): Promise<Request> {
|
||||||
const script = await this.scriptDAO.get(data.uuid);
|
const script = await this.scriptDAO.get(data.uuid);
|
||||||
if (!script) {
|
if (!script) {
|
||||||
return Promise.reject(new Error("script is not found"));
|
return Promise.reject(new Error("script is not found"));
|
||||||
}
|
}
|
||||||
const req: Request = <Request>data;
|
const req: Request = <Request>data;
|
||||||
req.script = script;
|
req.script = script;
|
||||||
req.sender = sender;
|
|
||||||
return Promise.resolve(req);
|
return Promise.resolve(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,8 +74,6 @@ export default class GMApi {
|
|||||||
return Promise.reject(new Error("param is failed"));
|
return Promise.reject(new Error("param is failed"));
|
||||||
}
|
}
|
||||||
const [key, value] = request.params;
|
const [key, value] = request.params;
|
||||||
const sender = <MessageSender & { runFlag: string }>request.sender;
|
|
||||||
sender.runFlag = request.runFlag;
|
|
||||||
return this.value.setValue(request.script.uuid, key, value);
|
return this.value.setValue(request.script.uuid, key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,8 +179,8 @@ export default class GMApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PermissionVerify.API()
|
@PermissionVerify.API()
|
||||||
GM_registerMenuCommand(request: Request, con: GetSender) {
|
GM_registerMenuCommand(request: Request, sender: GetSender) {
|
||||||
console.log("registerMenuCommand", request.params);
|
console.log("registerMenuCommand", request.params, sender.getSender(), sender.getSender().tab!.id!);
|
||||||
const [id, name, accessKey] = request.params;
|
const [id, name, accessKey] = request.params;
|
||||||
// 触发菜单注册, 在popup中处理
|
// 触发菜单注册, 在popup中处理
|
||||||
this.mq.emit("registerMenuCommand", {
|
this.mq.emit("registerMenuCommand", {
|
||||||
@ -192,24 +188,20 @@ export default class GMApi {
|
|||||||
id: id,
|
id: id,
|
||||||
name: name,
|
name: name,
|
||||||
accessKey: accessKey,
|
accessKey: accessKey,
|
||||||
con: con.getConnect(),
|
tabId: sender.getSender().tab!.id!,
|
||||||
});
|
frameId: sender.getSender().frameId,
|
||||||
con.getConnect().onDisconnect(() => {
|
|
||||||
// 取消注册
|
|
||||||
this.mq.emit("unregisterMenuCommand", {
|
|
||||||
uuid: request.script.uuid,
|
|
||||||
name: name,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@PermissionVerify.API()
|
@PermissionVerify.API()
|
||||||
GM_unregisterMenuCommand(request: Request) {
|
GM_unregisterMenuCommand(request: Request, sender: GetSender) {
|
||||||
const [id] = request.params;
|
const [id] = request.params;
|
||||||
// 触发菜单取消注册, 在popup中处理
|
// 触发菜单取消注册, 在popup中处理
|
||||||
this.mq.emit("unregisterMenuCommand", {
|
this.mq.emit("unregisterMenuCommand", {
|
||||||
uuid: request.script.uuid,
|
uuid: request.script.uuid,
|
||||||
id: id,
|
id: id,
|
||||||
|
tabId: sender.getSender().tab!.id!,
|
||||||
|
frameId: sender.getSender().frameId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,26 +1,279 @@
|
|||||||
import { MessageQueue } from "@Packages/message/message_queue";
|
import { MessageQueue } from "@Packages/message/message_queue";
|
||||||
import { GetSender, Group } from "@Packages/message/server";
|
import { Group } from "@Packages/message/server";
|
||||||
import { RuntimeService } from "./runtime";
|
import { RuntimeService, ScriptMatchInfo } from "./runtime";
|
||||||
|
import Cache from "@App/app/cache";
|
||||||
|
import { GetPopupDataReq, GetPopupDataRes } from "./client";
|
||||||
|
import {
|
||||||
|
SCRIPT_RUN_STATUS,
|
||||||
|
Metadata,
|
||||||
|
SCRIPT_STATUS_ENABLE,
|
||||||
|
Script,
|
||||||
|
ScriptDAO,
|
||||||
|
SCRIPT_TYPE_NORMAL,
|
||||||
|
} from "@App/app/repo/scripts";
|
||||||
|
import {
|
||||||
|
ScriptMenuRegisterCallbackValue,
|
||||||
|
subscribeScriptDelete,
|
||||||
|
subscribeScriptEnable,
|
||||||
|
subscribeScriptInstall,
|
||||||
|
subscribeScriptMenuRegister,
|
||||||
|
subscribeScriptRunStatus,
|
||||||
|
} from "../queue";
|
||||||
|
|
||||||
|
export type ScriptMenuItem = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
accessKey?: string;
|
||||||
|
tabId: number | "background";
|
||||||
|
frameId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ScriptMenu = {
|
||||||
|
uuid: string; // 脚本uuid
|
||||||
|
name: string; // 脚本名称
|
||||||
|
enable: boolean; // 脚本是否启用
|
||||||
|
updatetime: number; // 脚本更新时间
|
||||||
|
hasUserConfig: boolean; // 是否有用户配置
|
||||||
|
metadata: Metadata; // 脚本元数据
|
||||||
|
runStatus?: SCRIPT_RUN_STATUS; // 脚本运行状态
|
||||||
|
runNum: number; // 脚本运行次数
|
||||||
|
runNumByIframe: number; // iframe运行次数
|
||||||
|
menus: ScriptMenuItem[]; // 脚本菜单
|
||||||
|
customExclude: string[]; // 自定义排除
|
||||||
|
};
|
||||||
|
|
||||||
// 处理popup页面的数据
|
// 处理popup页面的数据
|
||||||
export class PopupService {
|
export class PopupService {
|
||||||
|
scriptDAO = new ScriptDAO();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private group: Group,
|
private group: Group,
|
||||||
private mq: MessageQueue,
|
private mq: MessageQueue,
|
||||||
private runtime: RuntimeService
|
private runtime: RuntimeService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
registerMenuCommand(message: { uuid: string; id: string; name: string; accessKey: string; con: GetSender }) {
|
async registerMenuCommand(message: ScriptMenuRegisterCallbackValue) {
|
||||||
console.log("registerMenuCommand", message);
|
// 给脚本添加菜单
|
||||||
|
const data = await this.getScriptMenu(message.tabId);
|
||||||
|
const script = data.find((item) => item.uuid === message.uuid);
|
||||||
|
if (script) {
|
||||||
|
const menu = script.menus.find((item) => item.id === message.id);
|
||||||
|
if (!menu) {
|
||||||
|
script.menus.push({
|
||||||
|
id: message.id,
|
||||||
|
name: message.name,
|
||||||
|
accessKey: message.accessKey,
|
||||||
|
tabId: message.tabId,
|
||||||
|
frameId: message.frameId,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
menu.name = message.name;
|
||||||
|
menu.accessKey = message.accessKey;
|
||||||
|
menu.tabId = message.tabId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(data);
|
||||||
|
Cache.getInstance().set("tabScript:" + message.tabId, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
unregisterMenuCommand(message: { id: string }) {
|
async unregisterMenuCommand({ id, uuid, tabId }: { id: number; uuid: string; tabId: number }) {
|
||||||
console.log("unregisterMenuCommand", message);
|
const data = await this.getScriptMenu(tabId);
|
||||||
|
// 删除脚本菜单
|
||||||
|
const script = data.find((item) => item.uuid === uuid);
|
||||||
|
if (script) {
|
||||||
|
script.menus = script.menus.filter((item) => item.id !== id);
|
||||||
|
}
|
||||||
|
Cache.getInstance().set("tabScript:" + tabId, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptToMenu(script: Script): ScriptMenu {
|
||||||
|
return {
|
||||||
|
uuid: script.uuid,
|
||||||
|
name: script.name,
|
||||||
|
enable: script.status === SCRIPT_STATUS_ENABLE,
|
||||||
|
updatetime: script.updatetime || 0,
|
||||||
|
hasUserConfig: !!script.config,
|
||||||
|
metadata: script.metadata,
|
||||||
|
runStatus: script.runStatus,
|
||||||
|
runNum: 0,
|
||||||
|
runNumByIframe: 0,
|
||||||
|
menus: [],
|
||||||
|
customExclude: (script as ScriptMatchInfo).customizeExcludeMatches || [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取popup页面数据
|
||||||
|
async getPopupData(req: GetPopupDataReq): Promise<GetPopupDataRes> {
|
||||||
|
// 获取当前tabId
|
||||||
|
const scriptUuid = await this.runtime.getPageScriptByUrl(req.url);
|
||||||
|
// 与运行时脚本进行合并
|
||||||
|
const runScript = await this.getScriptMenu(req.tabId);
|
||||||
|
// 筛选出未运行的脚本
|
||||||
|
const notRunScript = scriptUuid.filter((script) => {
|
||||||
|
return !runScript.find((item) => item.uuid === script.uuid);
|
||||||
|
});
|
||||||
|
// 将未运行的脚本转换为菜单
|
||||||
|
const scriptList = notRunScript.map((script): ScriptMenu => {
|
||||||
|
return this.scriptToMenu(script);
|
||||||
|
});
|
||||||
|
runScript.push(...scriptList);
|
||||||
|
// 后台脚本只显示开启或者运行中的脚本
|
||||||
|
|
||||||
|
return { scriptList: runScript, backScriptList: await this.getScriptMenu(-1) };
|
||||||
|
}
|
||||||
|
|
||||||
|
async getScriptMenu(tabId: number) {
|
||||||
|
return ((await Cache.getInstance().get("tabScript:" + tabId)) || []) as ScriptMenu[];
|
||||||
|
}
|
||||||
|
|
||||||
|
async addScriptRunNumber({
|
||||||
|
tabId,
|
||||||
|
frameId,
|
||||||
|
scripts,
|
||||||
|
}: {
|
||||||
|
tabId: number;
|
||||||
|
frameId: number;
|
||||||
|
scripts: ScriptMatchInfo[];
|
||||||
|
}) {
|
||||||
|
if (frameId === undefined) {
|
||||||
|
// 清理数据
|
||||||
|
await Cache.getInstance().del("tabScript:" + tabId);
|
||||||
|
}
|
||||||
|
// 设置数据
|
||||||
|
const data = await this.getScriptMenu(tabId);
|
||||||
|
// 设置脚本运行次数
|
||||||
|
scripts.forEach((script) => {
|
||||||
|
const scriptMenu = data.find((item) => item.uuid === script.uuid);
|
||||||
|
if (scriptMenu) {
|
||||||
|
scriptMenu.runNum = (scriptMenu.runNum || 0) + 1;
|
||||||
|
if (frameId) {
|
||||||
|
scriptMenu.runNumByIframe = (scriptMenu.runNumByIframe || 0) + 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const item = this.scriptToMenu(script);
|
||||||
|
item.runNum = 1;
|
||||||
|
if (frameId) {
|
||||||
|
item.runNumByIframe = 1;
|
||||||
|
}
|
||||||
|
data.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Cache.getInstance().set("tabScript:" + tabId, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
dealBackgroundScriptInstall() {
|
||||||
|
// 处理后台脚本
|
||||||
|
subscribeScriptInstall(this.mq, async ({ script }) => {
|
||||||
|
if (script.type === SCRIPT_TYPE_NORMAL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const menu = await this.getScriptMenu(-1);
|
||||||
|
const scriptMenu = menu.find((item) => item.uuid === script.uuid);
|
||||||
|
if (script.status === SCRIPT_STATUS_ENABLE) {
|
||||||
|
// 加入菜单
|
||||||
|
if (!scriptMenu) {
|
||||||
|
const item = this.scriptToMenu(script);
|
||||||
|
menu.push(item);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 移出菜单
|
||||||
|
if (scriptMenu) {
|
||||||
|
menu.splice(menu.indexOf(scriptMenu), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Cache.getInstance().set("tabScript:" + -1, menu);
|
||||||
|
});
|
||||||
|
subscribeScriptEnable(this.mq, async ({ uuid }) => {
|
||||||
|
const script = await this.scriptDAO.get(uuid);
|
||||||
|
if (!script) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (script.type === SCRIPT_TYPE_NORMAL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const menu = await this.getScriptMenu(-1);
|
||||||
|
const scriptMenu = menu.find((item) => item.uuid === uuid);
|
||||||
|
if (script.status === SCRIPT_STATUS_ENABLE) {
|
||||||
|
// 加入菜单
|
||||||
|
if (!scriptMenu) {
|
||||||
|
const item = this.scriptToMenu(script);
|
||||||
|
menu.push(item);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 移出菜单
|
||||||
|
if (scriptMenu) {
|
||||||
|
menu.splice(menu.indexOf(scriptMenu), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Cache.getInstance().set("tabScript:" + -1, menu);
|
||||||
|
});
|
||||||
|
subscribeScriptDelete(this.mq, async ({ uuid }) => {
|
||||||
|
const menu = await this.getScriptMenu(-1);
|
||||||
|
const scriptMenu = menu.find((item) => item.uuid === uuid);
|
||||||
|
if (scriptMenu) {
|
||||||
|
menu.splice(menu.indexOf(scriptMenu), 1);
|
||||||
|
Cache.getInstance().set("tabScript:" + -1, menu);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
subscribeScriptRunStatus(this.mq, async ({ uuid, runStatus }) => {
|
||||||
|
const menu = await this.getScriptMenu(-1);
|
||||||
|
const scriptMenu = menu.find((item) => item.uuid === uuid);
|
||||||
|
if (scriptMenu) {
|
||||||
|
scriptMenu.runStatus = runStatus;
|
||||||
|
Cache.getInstance().set("tabScript:" + -1, menu);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
menuClick({ uuid, id, tabId, frameId }: { uuid: string; id: number; tabId: number; frameId: number }) {
|
||||||
|
// 菜单点击事件
|
||||||
|
console.log("click menu", uuid, id, tabId);
|
||||||
|
this.runtime.sendMessageToTab(tabId, "menuClick", {
|
||||||
|
uuid,
|
||||||
|
id,
|
||||||
|
tabId,
|
||||||
|
frameId,
|
||||||
|
});
|
||||||
|
return Promise.resolve(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
// 处理脚本菜单数据
|
// 处理脚本菜单数据
|
||||||
this.mq.subscribe("registerMenuCommand", this.registerMenuCommand.bind(this));
|
subscribeScriptMenuRegister(this.mq, this.registerMenuCommand.bind(this));
|
||||||
this.mq.subscribe("unregisterMenuCommand", this.unregisterMenuCommand.bind(this));
|
this.mq.subscribe("unregisterMenuCommand", this.unregisterMenuCommand.bind(this));
|
||||||
|
this.group.on("getPopupData", this.getPopupData.bind(this));
|
||||||
|
this.group.on("menuClick", this.menuClick.bind(this));
|
||||||
|
this.dealBackgroundScriptInstall();
|
||||||
|
|
||||||
|
// 监听tab开关
|
||||||
|
chrome.tabs.onRemoved.addListener((tabId) => {
|
||||||
|
// 清理数据
|
||||||
|
Cache.getInstance().del("tabScript:" + tabId);
|
||||||
|
});
|
||||||
|
// 监听运行次数
|
||||||
|
this.mq.subscribe(
|
||||||
|
"pageLoad",
|
||||||
|
async ({ tabId, frameId, scripts }: { tabId: number; frameId: number; scripts: ScriptMatchInfo[] }) => {
|
||||||
|
this.addScriptRunNumber({ tabId, frameId, scripts });
|
||||||
|
// 设置角标和脚本
|
||||||
|
chrome.action.getBadgeText(
|
||||||
|
{
|
||||||
|
tabId: tabId,
|
||||||
|
},
|
||||||
|
(res: string) => {
|
||||||
|
if (res || scripts.length) {
|
||||||
|
chrome.action.setBadgeText({
|
||||||
|
text: (scripts.length + (parseInt(res, 10) || 0)).toString(),
|
||||||
|
tabId: tabId,
|
||||||
|
});
|
||||||
|
chrome.action.setBadgeBackgroundColor({
|
||||||
|
color: "#4e5969",
|
||||||
|
tabId: tabId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
import { MessageQueue } from "@Packages/message/message_queue";
|
import { MessageQueue } from "@Packages/message/message_queue";
|
||||||
import { GetSender, Group, MessageSend } from "@Packages/message/server";
|
import { GetSender, Group, MessageSend } from "@Packages/message/server";
|
||||||
import { Script, SCRIPT_STATUS_ENABLE, SCRIPT_TYPE_NORMAL, ScriptDAO, ScriptRunResouce } from "@App/app/repo/scripts";
|
import {
|
||||||
|
Script,
|
||||||
|
SCRIPT_STATUS,
|
||||||
|
SCRIPT_STATUS_DISABLE,
|
||||||
|
SCRIPT_STATUS_ENABLE,
|
||||||
|
SCRIPT_TYPE_NORMAL,
|
||||||
|
ScriptDAO,
|
||||||
|
ScriptRunResouce,
|
||||||
|
} from "@App/app/repo/scripts";
|
||||||
import { ValueService } from "./value";
|
import { ValueService } from "./value";
|
||||||
import GMApi from "./gm_api";
|
import GMApi from "./gm_api";
|
||||||
import { subscribeScriptDelete, subscribeScriptEnable, subscribeScriptInstall } from "../queue";
|
import { subscribeScriptDelete, subscribeScriptEnable, subscribeScriptInstall } from "../queue";
|
||||||
@ -11,17 +19,21 @@ import { randomString } from "@App/pkg/utils/utils";
|
|||||||
import { compileInjectScript } from "@App/runtime/content/utils";
|
import { compileInjectScript } from "@App/runtime/content/utils";
|
||||||
import Cache from "@App/app/cache";
|
import Cache from "@App/app/cache";
|
||||||
import { dealPatternMatches, UrlMatch } from "@App/pkg/utils/match";
|
import { dealPatternMatches, UrlMatch } from "@App/pkg/utils/match";
|
||||||
|
import { ExtensionContentMessageSend } from "@Packages/message/extension_message";
|
||||||
|
import { sendMessage } from "@Packages/message/client";
|
||||||
|
|
||||||
// 为了优化性能,存储到缓存时删除了code与value
|
// 为了优化性能,存储到缓存时删除了code与value
|
||||||
export interface ScriptMatchInfo extends ScriptRunResouce {
|
export interface ScriptMatchInfo extends ScriptRunResouce {
|
||||||
matches: string[];
|
matches: string[];
|
||||||
excludeMatches: string[];
|
excludeMatches: string[];
|
||||||
|
customizeExcludeMatches: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RuntimeService {
|
export class RuntimeService {
|
||||||
scriptDAO: ScriptDAO = new ScriptDAO();
|
scriptDAO: ScriptDAO = new ScriptDAO();
|
||||||
|
|
||||||
scriptMatch: UrlMatch<string> = new UrlMatch<string>();
|
scriptMatch: UrlMatch<string> = new UrlMatch<string>();
|
||||||
|
scriptCustomizeMatch: UrlMatch<string> = new UrlMatch<string>();
|
||||||
scriptMatchCache: Map<string, ScriptMatchInfo> | null | undefined;
|
scriptMatchCache: Map<string, ScriptMatchInfo> | null | undefined;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -52,11 +64,10 @@ export class RuntimeService {
|
|||||||
// 如果是普通脚本, 在service worker中进行注册
|
// 如果是普通脚本, 在service worker中进行注册
|
||||||
// 如果是后台脚本, 在offscreen中进行处理
|
// 如果是后台脚本, 在offscreen中进行处理
|
||||||
if (script.type === SCRIPT_TYPE_NORMAL) {
|
if (script.type === SCRIPT_TYPE_NORMAL) {
|
||||||
// 注册入页面脚本
|
// 加载页面脚本
|
||||||
if (data.enable) {
|
await this.loadPageScript(script);
|
||||||
this.registryPageScript(script);
|
if (!data.enable) {
|
||||||
} else {
|
await this.unregistryPageScript(script);
|
||||||
this.unregistryPageScript(script);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -67,7 +78,7 @@ export class RuntimeService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (script.type === SCRIPT_TYPE_NORMAL) {
|
if (script.type === SCRIPT_TYPE_NORMAL) {
|
||||||
this.registryPageScript(script);
|
await this.loadPageScript(script);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 监听脚本删除
|
// 监听脚本删除
|
||||||
@ -77,7 +88,8 @@ export class RuntimeService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (script.type === SCRIPT_TYPE_NORMAL) {
|
if (script.type === SCRIPT_TYPE_NORMAL) {
|
||||||
this.unregistryPageScript(script);
|
await this.unregistryPageScript(script);
|
||||||
|
this.deleteScriptMatch(script.uuid);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -85,20 +97,22 @@ export class RuntimeService {
|
|||||||
const scriptDao = new ScriptDAO();
|
const scriptDao = new ScriptDAO();
|
||||||
const list = await scriptDao.all();
|
const list = await scriptDao.all();
|
||||||
list.forEach((script) => {
|
list.forEach((script) => {
|
||||||
if (script.status !== SCRIPT_STATUS_ENABLE || script.type !== SCRIPT_TYPE_NORMAL) {
|
if (script.type !== SCRIPT_TYPE_NORMAL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.mq.publish("enableScript", { uuid: script.uuid, enable: true });
|
this.mq.publish("enableScript", { uuid: script.uuid, enable: script.status === SCRIPT_STATUS_ENABLE });
|
||||||
});
|
});
|
||||||
// 监听offscreen环境初始化, 初始化完成后, 再将后台脚本运行起来
|
// 监听offscreen环境初始化, 初始化完成后, 再将后台脚本运行起来
|
||||||
this.mq.subscribe("preparationOffscreen", () => {
|
this.mq.subscribe("preparationOffscreen", () => {
|
||||||
list.forEach((script) => {
|
list.forEach((script) => {
|
||||||
if (script.status !== SCRIPT_STATUS_ENABLE || script.type === SCRIPT_TYPE_NORMAL) {
|
if (script.type === SCRIPT_TYPE_NORMAL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.mq.publish("enableScript", { uuid: script.uuid, enable: true });
|
this.mq.publish("enableScript", { uuid: script.uuid, enable: script.status === SCRIPT_STATUS_ENABLE });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.loadScriptMatchInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
messageFlag() {
|
messageFlag() {
|
||||||
@ -107,23 +121,72 @@ export class RuntimeService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async pageLoad(_, sender: GetSender) {
|
// 给指定tab发送消息
|
||||||
const [scriptFlag, match] = await Promise.all([this.messageFlag(), this.loadScriptMatchInfo()]);
|
sendMessageToTab(tabId: number, action: string, data: any) {
|
||||||
|
if (tabId === -1) {
|
||||||
|
// 如果是-1, 代表给offscreen发送消息
|
||||||
|
return sendMessage(this.sender, "offscreen/runtime/" + action, data);
|
||||||
|
}
|
||||||
|
return sendMessage(new ExtensionContentMessageSend(tabId), "content/runtime/" + action, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPageScriptUuidByUrl(url: string) {
|
||||||
|
const match = await this.loadScriptMatchInfo();
|
||||||
|
// 匹配当前页面的脚本
|
||||||
|
const matchScriptUuid = match.match(url!);
|
||||||
|
// 排除自定义匹配
|
||||||
|
const excludeScriptUuid = this.scriptCustomizeMatch.match(url!);
|
||||||
|
const excludeMatch = new Set<string>();
|
||||||
|
excludeScriptUuid.forEach((uuid) => {
|
||||||
|
excludeMatch.add(uuid);
|
||||||
|
});
|
||||||
|
return matchScriptUuid.filter((value) => {
|
||||||
|
// 过滤掉自定义排除的脚本
|
||||||
|
return !excludeMatch.has(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPageScriptByUrl(url: string) {
|
||||||
|
const matchScriptUuid = await this.getPageScriptUuidByUrl(url);
|
||||||
|
return matchScriptUuid.map((uuid) => {
|
||||||
|
return Object.assign({}, this.scriptMatchCache?.get(uuid));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async pageLoad(_: any, sender: GetSender) {
|
||||||
|
const [scriptFlag] = await Promise.all([this.messageFlag(), this.loadScriptMatchInfo()]);
|
||||||
const chromeSender = sender.getSender() as chrome.runtime.MessageSender;
|
const chromeSender = sender.getSender() as chrome.runtime.MessageSender;
|
||||||
|
|
||||||
// 匹配当前页面的脚本
|
// 匹配当前页面的脚本
|
||||||
const matchScriptUuid = match.match(chromeSender.url!);
|
const matchScriptUuid = await this.getPageScriptUuidByUrl(chromeSender.url!);
|
||||||
|
|
||||||
const scripts = await Promise.all(
|
const scripts = await Promise.all(
|
||||||
matchScriptUuid.map(
|
matchScriptUuid.map(async (uuid): Promise<undefined | ScriptRunResouce> => {
|
||||||
(uuid) =>
|
|
||||||
new Promise((resolve) => {
|
|
||||||
// 获取value
|
|
||||||
const scriptRes = Object.assign({}, this.scriptMatchCache?.get(uuid));
|
const scriptRes = Object.assign({}, this.scriptMatchCache?.get(uuid));
|
||||||
resolve(scriptRes);
|
// 判断脚本是否开启
|
||||||
|
if (scriptRes.status === SCRIPT_STATUS_DISABLE) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
// 如果是iframe,判断是否允许在iframe里运行
|
||||||
|
if (chromeSender.frameId !== undefined) {
|
||||||
|
if (scriptRes.metadata.noframes) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 获取value
|
||||||
|
return scriptRes;
|
||||||
})
|
})
|
||||||
)
|
|
||||||
);
|
);
|
||||||
return Promise.resolve({ flag: scriptFlag, scripts: scripts });
|
|
||||||
|
const enableScript = scripts.filter((item) => item);
|
||||||
|
|
||||||
|
this.mq.emit("pageLoad", {
|
||||||
|
tabId: chromeSender.tab?.id,
|
||||||
|
frameId: chromeSender.frameId,
|
||||||
|
scripts: enableScript,
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.resolve({ flag: scriptFlag, scripts: enableScript });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 停止脚本
|
// 停止脚本
|
||||||
@ -174,7 +237,7 @@ export class RuntimeService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadScripting: Promise<void> | null | undefined;
|
loadingScript: Promise<void> | null | undefined;
|
||||||
|
|
||||||
// 加载脚本匹配信息,由于service_worker的机制,如果由不活动状态恢复过来时,会优先触发事件
|
// 加载脚本匹配信息,由于service_worker的机制,如果由不活动状态恢复过来时,会优先触发事件
|
||||||
// 可能当时会没有脚本匹配信息,所以使用脚本信息时,尽量使用此方法获取
|
// 可能当时会没有脚本匹配信息,所以使用脚本信息时,尽量使用此方法获取
|
||||||
@ -182,29 +245,33 @@ export class RuntimeService {
|
|||||||
if (this.scriptMatchCache) {
|
if (this.scriptMatchCache) {
|
||||||
return this.scriptMatch;
|
return this.scriptMatch;
|
||||||
}
|
}
|
||||||
if (this.loadScripting) {
|
if (this.loadingScript) {
|
||||||
await this.loadScripting;
|
await this.loadingScript;
|
||||||
} else {
|
} else {
|
||||||
// 如果没有缓存, 则创建一个新的缓存
|
// 如果没有缓存, 则创建一个新的缓存
|
||||||
this.loadScripting = Cache.getInstance()
|
const cache = new Map<string, ScriptMatchInfo>();
|
||||||
|
this.loadingScript = Cache.getInstance()
|
||||||
.get("scriptMatch")
|
.get("scriptMatch")
|
||||||
.then((data: { [key: string]: ScriptMatchInfo }) => {
|
.then((data: { [key: string]: ScriptMatchInfo }) => {
|
||||||
this.scriptMatchCache = new Map<string, ScriptMatchInfo>();
|
|
||||||
if (data) {
|
if (data) {
|
||||||
Object.keys(data).forEach((key) => {
|
Object.keys(data).forEach((key) => {
|
||||||
const item = data[key];
|
const item = data[key];
|
||||||
this.scriptMatchCache!.set(item.uuid, item);
|
cache.set(item.uuid, item);
|
||||||
item.matches.forEach((match) => {
|
item.matches.forEach((match) => {
|
||||||
this.scriptMatch.add(match, item.uuid);
|
this.scriptMatch.add(match, item.uuid);
|
||||||
});
|
});
|
||||||
item.excludeMatches.forEach((match) => {
|
item.excludeMatches.forEach((match) => {
|
||||||
this.scriptMatch.exclude(match, item.uuid);
|
this.scriptMatch.exclude(match, item.uuid);
|
||||||
});
|
});
|
||||||
|
item.customizeExcludeMatches.forEach((match) => {
|
||||||
|
this.scriptCustomizeMatch.exclude(match, item.uuid);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await this.loadScripting;
|
await this.loadingScript;
|
||||||
this.loadScripting = null;
|
this.loadingScript = null;
|
||||||
|
this.scriptMatchCache = cache;
|
||||||
}
|
}
|
||||||
return this.scriptMatch;
|
return this.scriptMatch;
|
||||||
}
|
}
|
||||||
@ -229,28 +296,43 @@ export class RuntimeService {
|
|||||||
await this.loadScriptMatchInfo();
|
await this.loadScriptMatchInfo();
|
||||||
}
|
}
|
||||||
this.scriptMatchCache!.set(item.uuid, item);
|
this.scriptMatchCache!.set(item.uuid, item);
|
||||||
|
// 清理一下老数据
|
||||||
|
this.scriptMatch.del(item.uuid);
|
||||||
|
this.scriptCustomizeMatch.del(item.uuid);
|
||||||
|
// 添加新的数据
|
||||||
item.matches.forEach((match) => {
|
item.matches.forEach((match) => {
|
||||||
this.scriptMatch.add(match, item.uuid);
|
this.scriptMatch.add(match, item.uuid);
|
||||||
});
|
});
|
||||||
item.excludeMatches.forEach((match) => {
|
item.excludeMatches.forEach((match) => {
|
||||||
this.scriptMatch.exclude(match, item.uuid);
|
this.scriptMatch.exclude(match, item.uuid);
|
||||||
});
|
});
|
||||||
|
item.customizeExcludeMatches.forEach((match) => {
|
||||||
|
this.scriptCustomizeMatch.exclude(match, item.uuid);
|
||||||
|
});
|
||||||
this.saveScriptMatchInfo();
|
this.saveScriptMatchInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteScriptMatch(uuid: string) {
|
async updateScriptStatus(uuid: string, status: SCRIPT_STATUS) {
|
||||||
if (!this.scriptMatchCache) {
|
if (!this.scriptMatchCache) {
|
||||||
await this.loadScriptMatchInfo();
|
await this.loadScriptMatchInfo();
|
||||||
}
|
}
|
||||||
this.scriptMatchCache!.delete(uuid);
|
this.scriptMatchCache!.get(uuid)!.status = status;
|
||||||
this.scriptMatch.del(uuid);
|
|
||||||
this.saveScriptMatchInfo();
|
this.saveScriptMatchInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
async registryPageScript(script: Script) {
|
deleteScriptMatch(uuid: string) {
|
||||||
if (await Cache.getInstance().has("registryScript:" + script.uuid)) {
|
if (!this.scriptMatchCache) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.scriptMatchCache.delete(uuid);
|
||||||
|
this.scriptMatch.del(uuid);
|
||||||
|
this.scriptCustomizeMatch.del(uuid);
|
||||||
|
this.saveScriptMatchInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载页面脚本, 会把脚本信息放入缓存中
|
||||||
|
// 如果脚本开启, 则注册脚本
|
||||||
|
async loadPageScript(script: Script) {
|
||||||
const matches = script.metadata["match"];
|
const matches = script.metadata["match"];
|
||||||
if (!matches) {
|
if (!matches) {
|
||||||
return;
|
return;
|
||||||
@ -262,7 +344,7 @@ export class RuntimeService {
|
|||||||
matches.push(...(script.metadata["include"] || []));
|
matches.push(...(script.metadata["include"] || []));
|
||||||
const patternMatches = dealPatternMatches(matches);
|
const patternMatches = dealPatternMatches(matches);
|
||||||
const scriptMatchInfo: ScriptMatchInfo = Object.assign(
|
const scriptMatchInfo: ScriptMatchInfo = Object.assign(
|
||||||
{ matches: patternMatches.result, excludeMatches: [] },
|
{ matches: patternMatches.result, excludeMatches: [], customizeExcludeMatches: [] },
|
||||||
scriptRes
|
scriptRes
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -272,30 +354,46 @@ export class RuntimeService {
|
|||||||
matches: patternMatches.patternResult,
|
matches: patternMatches.patternResult,
|
||||||
world: "MAIN",
|
world: "MAIN",
|
||||||
};
|
};
|
||||||
if (!script.metadata["noframes"]) {
|
|
||||||
registerScript.allFrames = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (script.metadata["exclude-match"]) {
|
if (script.metadata["exclude"]) {
|
||||||
const excludeMatches = script.metadata["exclude-match"];
|
const excludeMatches = script.metadata["exclude"];
|
||||||
excludeMatches.push(...(script.metadata["exclude"] || []));
|
|
||||||
const result = dealPatternMatches(excludeMatches);
|
const result = dealPatternMatches(excludeMatches);
|
||||||
|
|
||||||
registerScript.excludeMatches = result.patternResult;
|
registerScript.excludeMatches = result.patternResult;
|
||||||
scriptMatchInfo.excludeMatches = result.result;
|
scriptMatchInfo.excludeMatches = result.result;
|
||||||
}
|
}
|
||||||
|
// 自定义排除
|
||||||
|
if (script.selfMetadata && script.selfMetadata.exclude) {
|
||||||
|
const excludeMatches = script.selfMetadata.exclude;
|
||||||
|
const result = dealPatternMatches(excludeMatches);
|
||||||
|
|
||||||
|
if (!registerScript.excludeMatches) {
|
||||||
|
registerScript.excludeMatches = [];
|
||||||
|
}
|
||||||
|
registerScript.excludeMatches.push(...result.patternResult);
|
||||||
|
scriptMatchInfo.customizeExcludeMatches = result.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将脚本match信息放入缓存中
|
||||||
|
this.addScriptMatch(scriptMatchInfo);
|
||||||
|
|
||||||
|
// 如果脚本开启, 则注册脚本
|
||||||
|
if (script.status === SCRIPT_STATUS_ENABLE) {
|
||||||
|
if (!script.metadata["noframes"]) {
|
||||||
|
registerScript.allFrames = true;
|
||||||
|
}
|
||||||
if (script.metadata["run-at"]) {
|
if (script.metadata["run-at"]) {
|
||||||
registerScript.runAt = getRunAt(script.metadata["run-at"]);
|
registerScript.runAt = getRunAt(script.metadata["run-at"]);
|
||||||
}
|
}
|
||||||
chrome.userScripts.register([registerScript], async () => {
|
await chrome.userScripts.register([registerScript]);
|
||||||
// 标记为已注册
|
await Cache.getInstance().set("registryScript:" + script.uuid, true);
|
||||||
Cache.getInstance().set("registryScript:" + script.uuid, true);
|
}
|
||||||
// 将脚本match信息放入缓存中
|
|
||||||
this.addScriptMatch(scriptMatchInfo);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unregistryPageScript(script: Script) {
|
async unregistryPageScript(script: Script) {
|
||||||
|
if (!(await Cache.getInstance().get("registryScript:" + script.uuid))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
chrome.userScripts.unregister(
|
chrome.userScripts.unregister(
|
||||||
{
|
{
|
||||||
ids: [script.uuid],
|
ids: [script.uuid],
|
||||||
@ -303,6 +401,8 @@ export class RuntimeService {
|
|||||||
() => {
|
() => {
|
||||||
// 删除缓存
|
// 删除缓存
|
||||||
Cache.getInstance().del("registryScript:" + script.uuid);
|
Cache.getInstance().del("registryScript:" + script.uuid);
|
||||||
|
// 修改脚本状态为disable
|
||||||
|
this.updateScriptStatus(script.uuid, SCRIPT_STATUS_DISABLE);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -17,3 +17,45 @@ export function getRunAt(runAts: string[]): chrome.userScripts.RunAt {
|
|||||||
}
|
}
|
||||||
return "document_idle";
|
return "document_idle";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mapToObject(map: Map<string, any>): { [key: string]: any } {
|
||||||
|
const obj: { [key: string]: any } = {};
|
||||||
|
map.forEach((value, key) => {
|
||||||
|
if (value instanceof Map) {
|
||||||
|
obj[key] = mapToObject(value);
|
||||||
|
} else if (obj[key] instanceof Array) {
|
||||||
|
obj[key].push(value);
|
||||||
|
} else {
|
||||||
|
obj[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function objectToMap(obj: { [key: string]: any }): Map<string, any> {
|
||||||
|
const map = new Map<string, any>();
|
||||||
|
Object.keys(obj).forEach((key) => {
|
||||||
|
if (obj[key] instanceof Map) {
|
||||||
|
map.set(key, objectToMap(obj[key]));
|
||||||
|
} else if (obj[key] instanceof Array) {
|
||||||
|
map.set(key, obj[key]);
|
||||||
|
} else {
|
||||||
|
map.set(key, obj[key]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function arrayToObject(arr: Array<any>): any[] {
|
||||||
|
const obj: any[] = [];
|
||||||
|
arr.forEach((item) => {
|
||||||
|
if (item instanceof Map) {
|
||||||
|
obj.push(mapToObject(item));
|
||||||
|
} else if (item instanceof Array) {
|
||||||
|
obj.push(arrayToObject(item));
|
||||||
|
} else {
|
||||||
|
obj.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import LoggerCore from "./app/logger/core";
|
import LoggerCore from "./app/logger/core";
|
||||||
import MessageWriter from "./app/logger/message_writer";
|
import MessageWriter from "./app/logger/message_writer";
|
||||||
import { ExtensionMessageSend } from "@Packages/message/extension_message";
|
import { ExtensionMessage, ExtensionMessageSend } from "@Packages/message/extension_message";
|
||||||
import { CustomEventMessage } from "@Packages/message/custom_event_message";
|
import { CustomEventMessage } from "@Packages/message/custom_event_message";
|
||||||
import { RuntimeClient } from "./app/service/service_worker/client";
|
import { RuntimeClient } from "./app/service/service_worker/client";
|
||||||
import ContentRuntime from "./runtime/content/content";
|
import ContentRuntime from "./runtime/content/content";
|
||||||
@ -18,9 +18,11 @@ const loggerCore = new LoggerCore({
|
|||||||
const client = new RuntimeClient(send);
|
const client = new RuntimeClient(send);
|
||||||
client.pageLoad().then((data) => {
|
client.pageLoad().then((data) => {
|
||||||
loggerCore.logger().debug("content start");
|
loggerCore.logger().debug("content start");
|
||||||
|
const extMsg = new ExtensionMessage();
|
||||||
const msg = new CustomEventMessage(data.flag, true);
|
const msg = new CustomEventMessage(data.flag, true);
|
||||||
const server = new Server("content", msg);
|
const server = new Server("content", msg);
|
||||||
|
const extServer = new Server("content", extMsg);
|
||||||
// 初始化运行环境
|
// 初始化运行环境
|
||||||
const runtime = new ContentRuntime(server, send, msg);
|
const runtime = new ContentRuntime(extServer, server, send, msg);
|
||||||
runtime.start(data.scripts);
|
runtime.start(data.scripts);
|
||||||
});
|
});
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
"default_locale": "zh_CN",
|
"default_locale": "zh_CN",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"tabs",
|
"tabs",
|
||||||
|
"action",
|
||||||
"storage",
|
"storage",
|
||||||
"offscreen",
|
"offscreen",
|
||||||
"scripting",
|
"scripting",
|
||||||
|
@ -1,15 +1,5 @@
|
|||||||
/* eslint-disable no-nested-ternary */
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { ScriptMenu } from "@App/runtime/service_worker/runtime";
|
import { Button, Collapse, Empty, Message, Popconfirm, Space, Switch } from "@arco-design/web-react";
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Collapse,
|
|
||||||
Empty,
|
|
||||||
Message,
|
|
||||||
Popconfirm,
|
|
||||||
Space,
|
|
||||||
Switch,
|
|
||||||
} from "@arco-design/web-react";
|
|
||||||
import {
|
import {
|
||||||
IconCaretDown,
|
IconCaretDown,
|
||||||
IconCaretUp,
|
IconCaretUp,
|
||||||
@ -23,6 +13,11 @@ import { SCRIPT_RUN_STATUS_RUNNING } from "@App/app/repo/scripts";
|
|||||||
import { RiPlayFill, RiStopFill } from "react-icons/ri";
|
import { RiPlayFill, RiStopFill } from "react-icons/ri";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { ScriptIcons } from "@App/pages/options/routes/utils";
|
import { ScriptIcons } from "@App/pages/options/routes/utils";
|
||||||
|
import { ScriptMenu, ScriptMenuItem } from "@App/app/service/service_worker/popup";
|
||||||
|
import { MessageSender } from "@Packages/message/server";
|
||||||
|
import { selectMenuExpandNum } from "@App/pages/store/features/setting";
|
||||||
|
import { useAppSelector } from "@App/pages/store/hooks";
|
||||||
|
import { popupClient } from "@App/pages/store/features/script";
|
||||||
|
|
||||||
const CollapseItem = Collapse.Item;
|
const CollapseItem = Collapse.Item;
|
||||||
|
|
||||||
@ -49,12 +44,13 @@ const ScriptMenuList: React.FC<{
|
|||||||
[key: string]: boolean;
|
[key: string]: boolean;
|
||||||
}>({});
|
}>({});
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const menuExpandNum = useAppSelector(selectMenuExpandNum);
|
||||||
|
|
||||||
let url: URL;
|
let url: URL;
|
||||||
try {
|
try {
|
||||||
url = new URL(currentUrl);
|
url = new URL(currentUrl);
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
// ignore error
|
console.error("Invalid URL:", e);
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setList(script);
|
setList(script);
|
||||||
@ -78,20 +74,10 @@ const ScriptMenuList: React.FC<{
|
|||||||
// };
|
// };
|
||||||
// }, []);
|
// }, []);
|
||||||
|
|
||||||
const sendMenuAction = (sender: MessageSender, channelFlag: string) => {
|
const sendMenuAction = (uuid: string, menu: ScriptMenuItem) => {
|
||||||
let id = sender.tabId;
|
popupClient.menuClick(uuid, menu).then(() => {
|
||||||
if (sender.frameId) {
|
|
||||||
id = sender.frameId;
|
|
||||||
}
|
|
||||||
message.broadcastChannel(
|
|
||||||
{
|
|
||||||
tag: sender.targetTag,
|
|
||||||
id: [id!],
|
|
||||||
},
|
|
||||||
channelFlag,
|
|
||||||
"click"
|
|
||||||
);
|
|
||||||
window.close();
|
window.close();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
// 监听菜单按键
|
// 监听菜单按键
|
||||||
|
|
||||||
@ -101,16 +87,14 @@ const ScriptMenuList: React.FC<{
|
|||||||
<>
|
<>
|
||||||
{list.length === 0 && <Empty />}
|
{list.length === 0 && <Empty />}
|
||||||
{list.map((item, index) => (
|
{list.map((item, index) => (
|
||||||
<Collapse bordered={false} expandIconPosition="right" key={item.id}>
|
<Collapse bordered={false} expandIconPosition="right" key={item.uuid}>
|
||||||
<CollapseItem
|
<CollapseItem
|
||||||
header={
|
header={
|
||||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
|
|
||||||
<div
|
<div
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}}
|
}}
|
||||||
title={
|
title={
|
||||||
// eslint-disable-next-line no-nested-ternary
|
|
||||||
item.enable
|
item.enable
|
||||||
? item.runNumByIframe
|
? item.runNumByIframe
|
||||||
? t("script_total_runs", {
|
? t("script_total_runs", {
|
||||||
@ -159,7 +143,7 @@ const ScriptMenuList: React.FC<{
|
|||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
name={item.id.toString()}
|
name={item.uuid}
|
||||||
contentStyle={{ padding: "0 0 0 40px" }}
|
contentStyle={{ padding: "0 0 0 40px" }}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
@ -167,13 +151,7 @@ const ScriptMenuList: React.FC<{
|
|||||||
<Button
|
<Button
|
||||||
className="text-left"
|
className="text-left"
|
||||||
type="secondary"
|
type="secondary"
|
||||||
icon={
|
icon={item.runStatus !== SCRIPT_RUN_STATUS_RUNNING ? <RiPlayFill /> : <RiStopFill />}
|
||||||
item.runStatus !== SCRIPT_RUN_STATUS_RUNNING ? (
|
|
||||||
<RiPlayFill />
|
|
||||||
) : (
|
|
||||||
<RiStopFill />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (item.runStatus !== SCRIPT_RUN_STATUS_RUNNING) {
|
if (item.runStatus !== SCRIPT_RUN_STATUS_RUNNING) {
|
||||||
runtimeCtrl.startScript(item.id);
|
runtimeCtrl.startScript(item.id);
|
||||||
@ -182,9 +160,7 @@ const ScriptMenuList: React.FC<{
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item.runStatus !== SCRIPT_RUN_STATUS_RUNNING
|
{item.runStatus !== SCRIPT_RUN_STATUS_RUNNING ? t("run_once") : t("stop")}
|
||||||
? t("run_once")
|
|
||||||
: t("stop")}
|
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
@ -192,10 +168,7 @@ const ScriptMenuList: React.FC<{
|
|||||||
type="secondary"
|
type="secondary"
|
||||||
icon={<IconEdit />}
|
icon={<IconEdit />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.open(
|
window.open(`/src/options.html#/script/editor/${item.id}`, "_blank");
|
||||||
`/src/options.html#/script/editor/${item.id}`,
|
|
||||||
"_blank"
|
|
||||||
);
|
|
||||||
window.close();
|
window.close();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -208,20 +181,12 @@ const ScriptMenuList: React.FC<{
|
|||||||
type="secondary"
|
type="secondary"
|
||||||
icon={<IconMinus />}
|
icon={<IconMinus />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
scriptCtrl
|
scriptCtrl.exclude(item.id, `*://${url.host}*`, isExclude(item, url.host)).finally(() => {
|
||||||
.exclude(
|
|
||||||
item.id,
|
|
||||||
`*://${url.host}*`,
|
|
||||||
isExclude(item, url.host)
|
|
||||||
)
|
|
||||||
.finally(() => {
|
|
||||||
window.close();
|
window.close();
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isExclude(item, url.host)
|
{isExclude(item, url.host) ? t("exclude_on") : t("exclude_off")}
|
||||||
? t("exclude_on")
|
|
||||||
: t("exclude_off")}
|
|
||||||
{` ${url.host} ${t("exclude_execution")}`}
|
{` ${url.host} ${t("exclude_execution")}`}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@ -235,32 +200,24 @@ const ScriptMenuList: React.FC<{
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button className="text-left" status="danger" type="secondary" icon={<IconDelete />}>
|
||||||
className="text-left"
|
|
||||||
status="danger"
|
|
||||||
type="secondary"
|
|
||||||
icon={<IconDelete />}
|
|
||||||
>
|
|
||||||
{t("delete")}
|
{t("delete")}
|
||||||
</Button>
|
</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
</CollapseItem>
|
</CollapseItem>
|
||||||
<div
|
<div className="arco-collapse-item-content-box flex flex-col" style={{ padding: "0 0 0 40px" }}>
|
||||||
className="arco-collapse-item-content-box flex flex-col"
|
|
||||||
style={{ padding: "0 0 0 40px" }}
|
|
||||||
>
|
|
||||||
{/* 判断菜单数量,再判断是否展开 */}
|
{/* 判断菜单数量,再判断是否展开 */}
|
||||||
{(item.menus && item.menus?.length > systemConfig.menuExpandNum
|
{(item.menus.length > menuExpandNum
|
||||||
? expandMenuIndex[index]
|
? expandMenuIndex[index]
|
||||||
? item.menus
|
? item.menus
|
||||||
: item.menus?.slice(0, systemConfig.menuExpandNum)
|
: item.menus?.slice(0, menuExpandNum)
|
||||||
: item.menus
|
: item.menus
|
||||||
)?.map((menu) => {
|
)?.map((menu) => {
|
||||||
if (menu.accessKey) {
|
if (menu.accessKey) {
|
||||||
document.addEventListener("keypress", (e) => {
|
document.addEventListener("keypress", (e) => {
|
||||||
if (e.key.toUpperCase() === menu.accessKey!.toUpperCase()) {
|
if (e.key.toUpperCase() === menu.accessKey!.toUpperCase()) {
|
||||||
sendMenuAction(menu.sender, menu.channelFlag);
|
sendMenuAction(item.uuid, menu);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -271,7 +228,7 @@ const ScriptMenuList: React.FC<{
|
|||||||
type="secondary"
|
type="secondary"
|
||||||
icon={<IconMenu />}
|
icon={<IconMenu />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
sendMenuAction(menu.sender, menu.channelFlag);
|
sendMenuAction(item.uuid, menu);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{menu.name}
|
{menu.name}
|
||||||
@ -279,14 +236,12 @@ const ScriptMenuList: React.FC<{
|
|||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{item.menus && item.menus?.length > systemConfig.menuExpandNum && (
|
{item.menus.length > menuExpandNum && (
|
||||||
<Button
|
<Button
|
||||||
className="text-left"
|
className="text-left"
|
||||||
key="expand"
|
key="expand"
|
||||||
type="secondary"
|
type="secondary"
|
||||||
icon={
|
icon={expandMenuIndex[index] ? <IconCaretUp /> : <IconCaretDown />}
|
||||||
expandMenuIndex[index] ? <IconCaretUp /> : <IconCaretDown />
|
|
||||||
}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setExpandMenuIndex({
|
setExpandMenuIndex({
|
||||||
...expandMenuIndex,
|
...expandMenuIndex,
|
||||||
@ -304,10 +259,7 @@ const ScriptMenuList: React.FC<{
|
|||||||
type="secondary"
|
type="secondary"
|
||||||
icon={<IconSettings />}
|
icon={<IconSettings />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.open(
|
window.open(`/src/options.html#/?userConfig=${item.id}`, "_blank");
|
||||||
`/src/options.html#/?userConfig=${item.id}`,
|
|
||||||
"_blank"
|
|
||||||
);
|
|
||||||
window.close();
|
window.close();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -15,7 +15,8 @@ import { RiMessage2Line } from "react-icons/ri";
|
|||||||
import semver from "semver";
|
import semver from "semver";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import ScriptMenuList from "../components/ScriptMenuList";
|
import ScriptMenuList from "../components/ScriptMenuList";
|
||||||
import { ScriptMenu } from "@App/runtime/service_worker/runtime";
|
import { popupClient } from "../store/features/script";
|
||||||
|
import { ScriptMenu } from "@App/app/service/service_worker/popup";
|
||||||
|
|
||||||
const CollapseItem = Collapse.Item;
|
const CollapseItem = Collapse.Item;
|
||||||
|
|
||||||
@ -43,8 +44,7 @@ function App() {
|
|||||||
// ignore error
|
// ignore error
|
||||||
}
|
}
|
||||||
|
|
||||||
// const message = IoC.instance(MessageInternal) as MessageInternal;
|
useEffect(() => {
|
||||||
// useEffect(() => {
|
|
||||||
// systemManage.getNotice().then((res) => {
|
// systemManage.getNotice().then((res) => {
|
||||||
// if (res) {
|
// if (res) {
|
||||||
// setNotice(res.notice);
|
// setNotice(res.notice);
|
||||||
@ -54,30 +54,29 @@ function App() {
|
|||||||
// systemManage.getVersion().then((res) => {
|
// systemManage.getVersion().then((res) => {
|
||||||
// res && setVersion(res);
|
// res && setVersion(res);
|
||||||
// });
|
// });
|
||||||
// chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||||||
// if (!tabs.length) {
|
if (!tabs.length) {
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
// setCurrentUrl(tabs[0].url || "");
|
setCurrentUrl(tabs[0].url || "");
|
||||||
// message
|
popupClient.getPopupData({ url: tabs[0].url!, tabId: tabs[0].id! }).then((resp) => {
|
||||||
// .syncSend("queryPageScript", { url: tabs[0].url, tabId: tabs[0].id })
|
console.log(resp);
|
||||||
// .then((resp: { scriptList: ScriptMenu[]; backScriptList: ScriptMenu[] }) => {
|
// 按照开启状态和更新时间排序
|
||||||
// // 按照开启状态和更新时间排序
|
const list = resp.scriptList;
|
||||||
// const list = resp.scriptList;
|
list.sort((a, b) => {
|
||||||
// list.sort((a, b) => {
|
if (a.enable === b.enable) {
|
||||||
// if (a.enable === b.enable) {
|
if (a.runNum !== b.runNum) {
|
||||||
// if (a.runNum !== b.runNum) {
|
return b.runNum - a.runNum;
|
||||||
// return b.runNum - a.runNum;
|
}
|
||||||
// }
|
return b.updatetime - a.updatetime;
|
||||||
// return b.updatetime - a.updatetime;
|
}
|
||||||
// }
|
return a.enable ? -1 : 1;
|
||||||
// return a.enable ? -1 : 1;
|
});
|
||||||
// });
|
setScriptList(list);
|
||||||
// setScriptList(list);
|
setBackScriptList(resp.backScriptList);
|
||||||
// setBackScriptList(resp.backScriptList);
|
});
|
||||||
// });
|
});
|
||||||
// });
|
}, []);
|
||||||
// }, []);
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
size="small"
|
size="small"
|
||||||
@ -178,7 +177,6 @@ function App() {
|
|||||||
<Alert
|
<Alert
|
||||||
style={{ marginBottom: 20, display: showAlert ? "flex" : "none" }}
|
style={{ marginBottom: 20, display: showAlert ? "flex" : "none" }}
|
||||||
type="info"
|
type="info"
|
||||||
// eslint-disable-next-line react/no-danger
|
|
||||||
content={<div dangerouslySetInnerHTML={{ __html: notice }} />}
|
content={<div dangerouslySetInnerHTML={{ __html: notice }} />}
|
||||||
/>
|
/>
|
||||||
<Collapse bordered={false} defaultActiveKey={["script", "background"]} style={{ maxWidth: 640 }}>
|
<Collapse bordered={false} defaultActiveKey={["script", "background"]} style={{ maxWidth: 640 }}>
|
||||||
|
@ -8,11 +8,12 @@ import {
|
|||||||
ScriptDAO,
|
ScriptDAO,
|
||||||
} from "@App/app/repo/scripts";
|
} from "@App/app/repo/scripts";
|
||||||
import { arrayMove } from "@dnd-kit/sortable";
|
import { arrayMove } from "@dnd-kit/sortable";
|
||||||
import { RuntimeClient, ScriptClient } from "@App/app/service/service_worker/client";
|
import { PopupClient, RuntimeClient, ScriptClient } from "@App/app/service/service_worker/client";
|
||||||
import { message } from "../global";
|
import { message } from "../global";
|
||||||
|
|
||||||
export const scriptClient = new ScriptClient(message);
|
export const scriptClient = new ScriptClient(message);
|
||||||
export const runtimeClient = new RuntimeClient(message);
|
export const runtimeClient = new RuntimeClient(message);
|
||||||
|
export const popupClient = new PopupClient(message);
|
||||||
|
|
||||||
export const fetchAndSortScriptList = createAsyncThunk("script/fetchScriptList", async () => {
|
export const fetchAndSortScriptList = createAsyncThunk("script/fetchScriptList", async () => {
|
||||||
// 排序
|
// 排序
|
||||||
|
@ -11,6 +11,7 @@ export const settingSlice = createAppSlice({
|
|||||||
config: "",
|
config: "",
|
||||||
},
|
},
|
||||||
scriptListColumnWidth: {} as { [key: string]: number },
|
scriptListColumnWidth: {} as { [key: string]: number },
|
||||||
|
menuExpandNum: 5,
|
||||||
},
|
},
|
||||||
reducers: (create) => {
|
reducers: (create) => {
|
||||||
// 初始化黑夜模式
|
// 初始化黑夜模式
|
||||||
@ -42,14 +43,18 @@ export const settingSlice = createAppSlice({
|
|||||||
editor.setTheme(action.payload === "dark" ? "vs-dark" : "vs");
|
editor.setTheme(action.payload === "dark" ? "vs-dark" : "vs");
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
menuExpandNum: create.reducer((state, action: PayloadAction<number>) => {
|
||||||
|
state.menuExpandNum = action.payload;
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
selectors: {
|
selectors: {
|
||||||
selectThemeMode: (state) => state.lightMode,
|
selectThemeMode: (state) => state.lightMode,
|
||||||
selectScriptListColumnWidth: (state) => state.scriptListColumnWidth,
|
selectScriptListColumnWidth: (state) => state.scriptListColumnWidth,
|
||||||
|
selectMenuExpandNum: (state) => state.menuExpandNum,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { setDarkMode } = settingSlice.actions;
|
export const { setDarkMode } = settingSlice.actions;
|
||||||
|
|
||||||
export const { selectThemeMode, selectScriptListColumnWidth } = settingSlice.selectors;
|
export const { selectThemeMode, selectScriptListColumnWidth, selectMenuExpandNum } = settingSlice.selectors;
|
||||||
|
@ -1,22 +1,26 @@
|
|||||||
import { ScriptRunResouce } from "@App/app/repo/scripts";
|
import { ScriptRunResouce } from "@App/app/repo/scripts";
|
||||||
import { Client } from "@Packages/message/client";
|
import { Client } from "@Packages/message/client";
|
||||||
import { forwardMessage, GetSender, Message, MessageSend, Server } from "@Packages/message/server";
|
import { forwardMessage, Message, MessageSend, Server } from "@Packages/message/server";
|
||||||
|
|
||||||
// content页的处理
|
// content页的处理
|
||||||
export default class ContentRuntime {
|
export default class ContentRuntime {
|
||||||
constructor(
|
constructor(
|
||||||
|
private extServer: Server,
|
||||||
private server: Server,
|
private server: Server,
|
||||||
private send: MessageSend,
|
private send: MessageSend,
|
||||||
private msg: Message
|
private msg: Message
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
start(scripts: ScriptRunResouce[]) {
|
start(scripts: ScriptRunResouce[]) {
|
||||||
forwardMessage(
|
this.extServer.on("runtime/menuClick", (action, data) => {
|
||||||
"serviceWorker",
|
// gm菜单点击
|
||||||
"runtime/gmApi",
|
console.log("runtime/menuClick", action, data);
|
||||||
this.server,
|
});
|
||||||
this.send,
|
this.extServer.on("runtime/valueUpdate", (action, data) => {
|
||||||
(data: { api: string; params: any }, con: GetSender) => {
|
// gm value变化
|
||||||
|
console.log(action, data);
|
||||||
|
});
|
||||||
|
forwardMessage("serviceWorker", "runtime/gmApi", this.server, this.send, (data: { api: string; params: any }) => {
|
||||||
// 拦截关注的api
|
// 拦截关注的api
|
||||||
switch (data.api) {
|
switch (data.api) {
|
||||||
case "CAT_createBlobUrl": {
|
case "CAT_createBlobUrl": {
|
||||||
@ -45,114 +49,8 @@ export default class ContentRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
}
|
});
|
||||||
);
|
|
||||||
// 由content到background
|
|
||||||
// 转发gmApi消息
|
|
||||||
// this.contentMessage.setHandler("gmApi", (action, data) => {
|
|
||||||
// return this.internalMessage.syncSend(action, data);
|
|
||||||
// });
|
|
||||||
// // 转发log消息
|
|
||||||
// this.contentMessage.setHandler("log", (action, data) => {
|
|
||||||
// this.internalMessage.send(action, data);
|
|
||||||
// });
|
|
||||||
// // 转发externalMessage消息
|
|
||||||
// this.contentMessage.setHandler(ExternalMessage, (action, data) => {
|
|
||||||
// return this.internalMessage.syncSend(action, data);
|
|
||||||
// });
|
|
||||||
// 处理GM_addElement
|
|
||||||
// @ts-ignore
|
|
||||||
// this.contentMessage.setHandler("GM_addElement", (action, data) => {
|
|
||||||
// const parma = data.param;
|
|
||||||
// let attr: { [x: string]: any; textContent?: any };
|
|
||||||
// let textContent = "";
|
|
||||||
// if (!parma[1]) {
|
|
||||||
// attr = {};
|
|
||||||
// } else {
|
|
||||||
// attr = { ...parma[1] };
|
|
||||||
// if (attr.textContent) {
|
|
||||||
// textContent = attr.textContent;
|
|
||||||
// delete attr.textContent;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// const el = <Element>document.createElement(parma[0]);
|
|
||||||
// Object.keys(attr).forEach((key) => {
|
|
||||||
// el.setAttribute(key, attr[key]);
|
|
||||||
// });
|
|
||||||
// if (textContent) {
|
|
||||||
// el.innerHTML = textContent;
|
|
||||||
// }
|
|
||||||
// let parentNode;
|
|
||||||
// if (data.relatedTarget) {
|
|
||||||
// parentNode = (<MessageContent>this.contentMessage).getAndDelRelatedTarget(data.relatedTarget);
|
|
||||||
// }
|
|
||||||
// (<Element>parentNode || document.head || document.body || document.querySelector("*")).appendChild(el);
|
|
||||||
// return {
|
|
||||||
// relatedTarget: el,
|
|
||||||
// };
|
|
||||||
// });
|
|
||||||
// // 转发长连接的gmApi消息
|
|
||||||
// this.contentMessage.setHandlerWithChannel("gmApiChannel", (inject, action, data) => {
|
|
||||||
// const background = this.internalMessage.channel();
|
|
||||||
// // 转发inject->background
|
|
||||||
// inject.setHandler((req) => {
|
|
||||||
// background.send(req.data);
|
|
||||||
// });
|
|
||||||
// inject.setCatch((err) => {
|
|
||||||
// background.throw(err);
|
|
||||||
// });
|
|
||||||
// inject.setDisChannelHandler(() => {
|
|
||||||
// background.disChannel();
|
|
||||||
// });
|
|
||||||
// // 转发background->inject
|
|
||||||
// background.setHandler((bgResp) => {
|
|
||||||
// inject.send(bgResp);
|
|
||||||
// });
|
|
||||||
// background.setCatch((err) => {
|
|
||||||
// inject.throw(err);
|
|
||||||
// });
|
|
||||||
// background.setDisChannelHandler(() => {
|
|
||||||
// inject.disChannel();
|
|
||||||
// });
|
|
||||||
// // 建立连接
|
|
||||||
// background.channel(action, data);
|
|
||||||
// });
|
|
||||||
// this.listenCATApi();
|
|
||||||
// // 由background到content
|
|
||||||
// // 转发value更新事件
|
|
||||||
// this.internalMessage.setHandler("valueUpdate", (action, data) => {
|
|
||||||
// this.contentMessage.send(action, data);
|
|
||||||
// });
|
|
||||||
// this.msg.sendMessage({ action: "pageLoad", data: { scripts } });
|
|
||||||
const client = new Client(this.msg, "inject");
|
const client = new Client(this.msg, "inject");
|
||||||
client.do("pageLoad", { scripts });
|
client.do("pageLoad", { scripts });
|
||||||
}
|
}
|
||||||
|
|
||||||
listenCATApi() {
|
|
||||||
// 处理特殊的消息,不需要转发到background
|
|
||||||
this.contentMessage.setHandler("CAT_fetchBlob", (_action, data: string) => {
|
|
||||||
return fetch(data).then((res) => res.blob());
|
|
||||||
});
|
|
||||||
this.contentMessage.setHandler("CAT_createBlobUrl", (_action, data: Blob) => {
|
|
||||||
const url = URL.createObjectURL(data);
|
|
||||||
setTimeout(() => {
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
}, 60 * 1000);
|
|
||||||
return Promise.resolve(url);
|
|
||||||
});
|
|
||||||
// 处理CAT_fetchDocument
|
|
||||||
this.contentMessage.setHandler("CAT_fetchDocument", (_action, data) => {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
const xhr = new XMLHttpRequest();
|
|
||||||
xhr.responseType = "document";
|
|
||||||
xhr.open("GET", data);
|
|
||||||
xhr.onload = () => {
|
|
||||||
resolve({
|
|
||||||
relatedTarget: xhr.response,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
xhr.send();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -222,16 +222,17 @@ export default class GMApi {
|
|||||||
this.menuId += 1;
|
this.menuId += 1;
|
||||||
}
|
}
|
||||||
const id = this.menuId;
|
const id = this.menuId;
|
||||||
this.connect("GM_registerMenuCommand", [id, name, accessKey]).then((con) => {
|
this.sendMessage("GM_registerMenuCommand", [id, name, accessKey]);
|
||||||
con.onMessage((data: { action: string; data: any }) => {
|
// .then((con) => {
|
||||||
if (data.action === "onClick") {
|
// con.onMessage((data: { action: string; data: any }) => {
|
||||||
listener();
|
// if (data.action === "onClick") {
|
||||||
}
|
// listener();
|
||||||
});
|
// }
|
||||||
con.onDisconnect(() => {
|
// });
|
||||||
this.menuMap?.delete(id);
|
// con.onDisconnect(() => {
|
||||||
});
|
// this.menuMap?.delete(id);
|
||||||
});
|
// });
|
||||||
|
// });
|
||||||
this.menuMap.set(id, name);
|
this.menuMap.set(id, name);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user