match
Some checks failed
build / Build (push) Failing after 6s
test / Run tests (push) Failing after 8s
Some checks failed
build / Build (push) Failing after 6s
test / Run tests (push) Failing after 8s
This commit is contained in:
parent
db8c5ec7b5
commit
20124be0e4
@ -1,4 +1,4 @@
|
|||||||
import { Message, MessageConnect, MessageSend } from "./server";
|
import { Message, MessageConnect, MessageSend, MessageSender } from "./server";
|
||||||
|
|
||||||
export class ExtensionMessageSend implements MessageSend {
|
export class ExtensionMessageSend implements MessageSend {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
@ -21,6 +21,28 @@ export class ExtensionMessageSend implements MessageSend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 由于service worker的限制,特殊处理chrome.runtime.onConnect/Message
|
||||||
|
export class ServiceWorkerMessage extends ExtensionMessageSend implements Message {
|
||||||
|
onConnect(callback: (data: any, con: MessageConnect) => void): void {
|
||||||
|
chrome.runtime.onConnect.addListener((port) => {
|
||||||
|
const handler = (msg: any) => {
|
||||||
|
port.onMessage.removeListener(handler);
|
||||||
|
callback(msg, new ExtensionMessageConnect(port));
|
||||||
|
};
|
||||||
|
port.onMessage.addListener(handler);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMessage(callback: (data: any, sendResponse: (data: any) => void, sender: MessageSender) => void): void {
|
||||||
|
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
||||||
|
if (msg.action === "messageQueue") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return callback(msg, sendResponse, sender);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class ExtensionMessage extends ExtensionMessageSend implements Message {
|
export class ExtensionMessage extends ExtensionMessageSend implements Message {
|
||||||
onConnect(callback: (data: any, con: MessageConnect) => void) {
|
onConnect(callback: (data: any, con: MessageConnect) => void) {
|
||||||
chrome.runtime.onConnect.addListener((port) => {
|
chrome.runtime.onConnect.addListener((port) => {
|
||||||
@ -33,12 +55,12 @@ export class ExtensionMessage extends ExtensionMessageSend implements Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 注意chrome.runtime.onMessage.addListener的回调函数需要返回true才能处理异步请求
|
// 注意chrome.runtime.onMessage.addListener的回调函数需要返回true才能处理异步请求
|
||||||
onMessage(callback: (data: any, sendResponse: (data: any) => void) => void) {
|
onMessage(callback: (data: any, sendResponse: (data: any) => void, sender: MessageSender) => void): void {
|
||||||
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
||||||
if (msg.action === "messageQueue") {
|
if (msg.action === "messageQueue") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return callback(msg, sendResponse);
|
return callback(msg, sendResponse, sender);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import EventEmitter from "eventemitter3";
|
import EventEmitter from "eventemitter3";
|
||||||
import Logger from "@App/app/logger/logger";
|
|
||||||
import LoggerCore from "@App/app/logger/core";
|
import LoggerCore from "@App/app/logger/core";
|
||||||
|
|
||||||
export type SubscribeCallback = (message: any) => void;
|
export type SubscribeCallback = (message: any) => void;
|
||||||
|
@ -2,7 +2,7 @@ import LoggerCore from "@App/app/logger/core";
|
|||||||
|
|
||||||
export interface Message extends MessageSend {
|
export interface Message extends MessageSend {
|
||||||
onConnect(callback: (data: any, con: MessageConnect) => void): void;
|
onConnect(callback: (data: any, con: MessageConnect) => void): void;
|
||||||
onMessage(callback: (data: any, sendResponse: (data: any) => void) => void): void;
|
onMessage(callback: (data: any, sendResponse: (data: any) => void, sender?: MessageSender) => void): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MessageSend {
|
export interface MessageSend {
|
||||||
@ -17,11 +17,21 @@ export interface MessageConnect {
|
|||||||
onDisconnect(callback: () => void): void;
|
onDisconnect(callback: () => void): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MessageSender = {
|
export type MessageSender = any;
|
||||||
tabId: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ApiFunction = (params: any, con: MessageConnect | null) => Promise<any> | void;
|
export class GetSender {
|
||||||
|
constructor(private sender: MessageConnect | MessageSender) {}
|
||||||
|
|
||||||
|
getSender(): MessageSender {
|
||||||
|
return this.sender as MessageSender;
|
||||||
|
}
|
||||||
|
|
||||||
|
getConnect(): MessageConnect {
|
||||||
|
return this.sender as MessageConnect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiFunction = (params: any, con: GetSender) => Promise<any> | void;
|
||||||
|
|
||||||
export class Server {
|
export class Server {
|
||||||
private apiFunctionMap: Map<string, ApiFunction> = new Map();
|
private apiFunctionMap: Map<string, ApiFunction> = new Map();
|
||||||
@ -37,10 +47,10 @@ export class Server {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
message.onMessage((msg: { action: string; data: any }, sendResponse) => {
|
message.onMessage((msg: { action: string; data: any }, sendResponse, sender) => {
|
||||||
this.logger.trace("server onMessage", { msg: msg as any });
|
this.logger.trace("server onMessage", { msg: msg as any });
|
||||||
if (msg.action.startsWith(prefix)) {
|
if (msg.action.startsWith(prefix)) {
|
||||||
return this.messageHandle(msg.action.slice(prefix.length + 1), msg.data, sendResponse);
|
return this.messageHandle(msg.action.slice(prefix.length + 1), msg.data, sendResponse, sender);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
@ -57,15 +67,15 @@ export class Server {
|
|||||||
private connectHandle(msg: string, params: any, con: MessageConnect) {
|
private connectHandle(msg: string, params: any, con: MessageConnect) {
|
||||||
const func = this.apiFunctionMap.get(msg);
|
const func = this.apiFunctionMap.get(msg);
|
||||||
if (func) {
|
if (func) {
|
||||||
func(params, con);
|
func(params, new GetSender(con));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private messageHandle(msg: string, params: any, sendResponse: (response: any) => void) {
|
private messageHandle(msg: string, params: any, sendResponse: (response: any) => void, sender?: MessageSender) {
|
||||||
const func = this.apiFunctionMap.get(msg);
|
const func = this.apiFunctionMap.get(msg);
|
||||||
if (func) {
|
if (func) {
|
||||||
try {
|
try {
|
||||||
const ret = func(params, null);
|
const ret = func(params, new GetSender(sender!));
|
||||||
if (ret instanceof Promise) {
|
if (ret instanceof Promise) {
|
||||||
ret.then((data) => {
|
ret.then((data) => {
|
||||||
sendResponse({ code: 0, data });
|
sendResponse({ code: 0, data });
|
||||||
@ -108,18 +118,19 @@ export function forwardMessage(prefix: string, path: string, from: Server, to: M
|
|||||||
from.on(path, (params, fromCon) => {
|
from.on(path, (params, fromCon) => {
|
||||||
console.log("forwardMessage", path, prefix, params);
|
console.log("forwardMessage", path, prefix, params);
|
||||||
if (fromCon) {
|
if (fromCon) {
|
||||||
|
const fromConnect = fromCon.getConnect();
|
||||||
to.connect({ action: prefix + "/" + path, data: params }).then((toCon) => {
|
to.connect({ action: prefix + "/" + path, data: params }).then((toCon) => {
|
||||||
fromCon.onMessage((data) => {
|
fromConnect.onMessage((data) => {
|
||||||
toCon.sendMessage(data);
|
toCon.sendMessage(data);
|
||||||
});
|
});
|
||||||
toCon.onMessage((data) => {
|
toCon.onMessage((data) => {
|
||||||
fromCon.sendMessage(data);
|
fromConnect.sendMessage(data);
|
||||||
});
|
});
|
||||||
fromCon.onDisconnect(() => {
|
fromConnect.onDisconnect(() => {
|
||||||
toCon.disconnect();
|
toCon.disconnect();
|
||||||
});
|
});
|
||||||
toCon.onDisconnect(() => {
|
toCon.onDisconnect(() => {
|
||||||
fromCon.disconnect();
|
fromConnect.disconnect();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -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 { Group, MessageConnect, MessageSend, MessageSender } from "@Packages/message/server";
|
import { GetSender, Group, MessageConnect, MessageSend, MessageSender } 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";
|
||||||
@ -40,7 +40,7 @@ export default class GMApi {
|
|||||||
this.logger = LoggerCore.logger().with({ service: "runtime/gm_api" });
|
this.logger = LoggerCore.logger().with({ service: "runtime/gm_api" });
|
||||||
}
|
}
|
||||||
|
|
||||||
async handlerRequest(data: MessageRequest, con: MessageConnect | null) {
|
async handlerRequest(data: MessageRequest, con: GetSender) {
|
||||||
this.logger.trace("GM API request", { api: data.api, uuid: data.uuid, param: data.params });
|
this.logger.trace("GM API request", { api: data.api, uuid: data.uuid, param: data.params });
|
||||||
const api = PermissionVerify.apis.get(data.api);
|
const api = PermissionVerify.apis.get(data.api);
|
||||||
if (!api) {
|
if (!api) {
|
||||||
@ -53,7 +53,7 @@ export default class GMApi {
|
|||||||
this.logger.error("verify error", { api: data.api }, Logger.E(e));
|
this.logger.error("verify error", { api: data.api }, Logger.E(e));
|
||||||
return Promise.reject(e);
|
return Promise.reject(e);
|
||||||
}
|
}
|
||||||
return api.api.call(this, req, con);
|
return api.api.call(this, req, con.getConnect());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析请求
|
// 解析请求
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import { MessageQueue } from "@Packages/message/message_queue";
|
import { MessageQueue } from "@Packages/message/message_queue";
|
||||||
import { 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_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";
|
||||||
import { ScriptService } from "./script";
|
import { ScriptService } from "./script";
|
||||||
import { runScript, stopScript } from "../offscreen/client";
|
import { runScript, stopScript } from "../offscreen/client";
|
||||||
import { dealMatches, getRunAt } from "./utils";
|
import { getRunAt } from "./utils";
|
||||||
import { randomString } from "@App/pkg/utils/utils";
|
import { randomString } from "@App/pkg/utils/utils";
|
||||||
import { compileInjectScript, compileScriptCode } from "@App/runtime/content/utils";
|
import { compileInjectScript, compileScriptCode } from "@App/runtime/content/utils";
|
||||||
import Cache from "@App/app/cache";
|
import Cache from "@App/app/cache";
|
||||||
|
import { dealMatches } from "@App/pkg/utils/match";
|
||||||
|
|
||||||
export class RuntimeService {
|
export class RuntimeService {
|
||||||
scriptDAO: ScriptDAO = new ScriptDAO();
|
scriptDAO: ScriptDAO = new ScriptDAO();
|
||||||
@ -99,7 +100,10 @@ export class RuntimeService {
|
|||||||
this.group.on("pageLoad", this.pageLoad.bind(this));
|
this.group.on("pageLoad", this.pageLoad.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
pageLoad() {
|
pageLoad(_, sender: GetSender) {
|
||||||
|
const chromeSender = sender.getSender() as chrome.runtime.MessageSender;
|
||||||
|
// 匹配当前页面的脚本
|
||||||
|
|
||||||
return Promise.resolve({ flag: this.scriptFlag });
|
return Promise.resolve({ flag: this.scriptFlag });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,25 +123,41 @@ export class RuntimeService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerInjectScript() {
|
registerInjectScript() {
|
||||||
fetch("inject.js")
|
chrome.userScripts.getScripts({ ids: ["scriptcat-inject"] }).then((res) => {
|
||||||
.then((res) => res.text())
|
if (res.length == 0) {
|
||||||
.then((injectJs) => {
|
fetch("inject.js")
|
||||||
// 替换ScriptFlag
|
.then((res) => res.text())
|
||||||
const code = `(function (ScriptFlag) {\n${injectJs}\n})('${this.scriptFlag}')`;
|
.then((injectJs) => {
|
||||||
chrome.userScripts.register([
|
// 替换ScriptFlag
|
||||||
|
const code = `(function (ScriptFlag) {\n${injectJs}\n})('${this.scriptFlag}')`;
|
||||||
|
chrome.userScripts.register([
|
||||||
|
{
|
||||||
|
id: "scriptcat-inject",
|
||||||
|
js: [{ code }],
|
||||||
|
matches: ["<all_urls>"],
|
||||||
|
allFrames: true,
|
||||||
|
world: "MAIN",
|
||||||
|
runAt: "document_start",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
chrome.scripting.registerContentScripts([
|
||||||
{
|
{
|
||||||
id: "scriptcat-inject",
|
id: "scriptcat-content",
|
||||||
js: [{ code }],
|
js: ["src/content.js"],
|
||||||
matches: ["<all_urls>"],
|
matches: ["<all_urls>"],
|
||||||
allFrames: true,
|
allFrames: true,
|
||||||
world: "MAIN",
|
|
||||||
runAt: "document_start",
|
runAt: "document_start",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async registryPageScript(script: Script) {
|
async registryPageScript(script: Script) {
|
||||||
|
if (await Cache.getInstance().has("registryScript:" + script.uuid)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const matches = script.metadata["match"];
|
const matches = script.metadata["match"];
|
||||||
if (!matches) {
|
if (!matches) {
|
||||||
return;
|
return;
|
||||||
@ -167,12 +187,21 @@ export class RuntimeService {
|
|||||||
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]);
|
chrome.userScripts.register([registerScript], () => {
|
||||||
|
Cache.getInstance().set("registryScript:" + script.uuid, true);
|
||||||
|
});
|
||||||
|
// 标记为已注册
|
||||||
}
|
}
|
||||||
|
|
||||||
unregistryPageScript(script: Script) {
|
unregistryPageScript(script: Script) {
|
||||||
chrome.userScripts.unregister({
|
chrome.userScripts.unregister(
|
||||||
ids: [script.uuid],
|
{
|
||||||
});
|
ids: [script.uuid],
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
// 删除缓存
|
||||||
|
Cache.getInstance().del("registryScript:" + script.uuid);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,6 @@ export function isExtensionRequest(details: chrome.webRequest.ResourceRequest &
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理油猴的match和include为chrome的matches
|
|
||||||
export function dealMatches(matches: string[]) {
|
|
||||||
return matches;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getRunAt(runAts: string[]): chrome.userScripts.RunAt {
|
export function getRunAt(runAts: string[]): chrome.userScripts.RunAt {
|
||||||
if (runAts.length === 0) {
|
if (runAts.length === 0) {
|
||||||
return "document_idle";
|
return "document_idle";
|
||||||
|
@ -17,18 +17,6 @@
|
|||||||
"128": "assets/logo.png"
|
"128": "assets/logo.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"content_scripts": [
|
|
||||||
{
|
|
||||||
"matches": [
|
|
||||||
"<all_urls>"
|
|
||||||
],
|
|
||||||
"js": [
|
|
||||||
"src/content.js"
|
|
||||||
],
|
|
||||||
"run_at": "document_start",
|
|
||||||
"all_frames": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"icons": {
|
"icons": {
|
||||||
"128": "assets/logo.png"
|
"128": "assets/logo.png"
|
||||||
},
|
},
|
||||||
|
11
src/pkg/utils/match.test.ts
Normal file
11
src/pkg/utils/match.test.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { describe, it } from "vitest";
|
||||||
|
import { dealMatches, parseURL } from "./match";
|
||||||
|
|
||||||
|
// https://developer.chrome.com/docs/extensions/mv3/match_patterns/
|
||||||
|
describe("dealMatches", () => {
|
||||||
|
it("*://link.17173.com*", () => {
|
||||||
|
const url = parseURL("*://link.17173.com*");
|
||||||
|
const matches = dealMatches(["*://link.17173.com*"]);
|
||||||
|
console.log(url, matches);
|
||||||
|
});
|
||||||
|
});
|
52
src/pkg/utils/match.ts
Normal file
52
src/pkg/utils/match.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
export interface Url {
|
||||||
|
scheme: string;
|
||||||
|
host: string;
|
||||||
|
path: string;
|
||||||
|
search: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据https://developer.chrome.com/docs/extensions/develop/concepts/match-patterns?hl=zh-cn进行匹配
|
||||||
|
export class Match {}
|
||||||
|
|
||||||
|
export function parseURL(url: string): Url | undefined {
|
||||||
|
const match = /^(.+?):\/\/(.*?)((\/.*?)(\?.*?|)|)$/.exec(url);
|
||||||
|
if (match) {
|
||||||
|
return {
|
||||||
|
scheme: match[1],
|
||||||
|
host: match[2],
|
||||||
|
path: match[4] || (url[url.length - 1] === "*" ? "*" : "/"),
|
||||||
|
search: match[5],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// 处理一些特殊情况
|
||||||
|
switch (url) {
|
||||||
|
case "*":
|
||||||
|
return {
|
||||||
|
scheme: "*",
|
||||||
|
host: "*",
|
||||||
|
path: "*",
|
||||||
|
search: "*",
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理油猴的match和include为chrome的matches
|
||||||
|
export function dealMatches(matches: string[]) {
|
||||||
|
const result: string[] = [];
|
||||||
|
for (let i = 0; i < matches.length; i++) {
|
||||||
|
const url = parseURL(matches[i]);
|
||||||
|
if (url) {
|
||||||
|
// *开头但是不是*.的情况
|
||||||
|
if (url.host.startsWith("*")) {
|
||||||
|
if (!url.host.startsWith("*.")) {
|
||||||
|
// 删除开头的*号
|
||||||
|
url.host = url.host.slice(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push(`${url.scheme}://${url.host}${url.path}` + (url.search ? "?" + url.search : ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
@ -217,13 +217,3 @@ export function sleep(time: number) {
|
|||||||
setTimeout(resolve, time);
|
setTimeout(resolve, time);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使service_worker长时间存活
|
|
||||||
export async function waitUntil(promise: Promise<any>) {
|
|
||||||
const keepAlive = setInterval(chrome.runtime.getPlatformInfo, 25 * 1000);
|
|
||||||
try {
|
|
||||||
await promise;
|
|
||||||
} finally {
|
|
||||||
clearInterval(keepAlive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user