注入脚本和inject/content通信
Some checks failed
build / Build (push) Failing after 6s
test / Run tests (push) Failing after 2s
Some checks failed
build / Build (push) Failing after 6s
test / Run tests (push) Failing after 2s
This commit is contained in:
133
packages/message/custom_event_message.ts
Normal file
133
packages/message/custom_event_message.ts
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import { Message, MessageConnect } from "./server";
|
||||||
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
import { PostMessage, WindowMessageBody, WindowMessageConnect } from "./window_message";
|
||||||
|
import LoggerCore from "@App/app/logger/core";
|
||||||
|
import EventEmitter from "eventemitter3";
|
||||||
|
|
||||||
|
export class CustomEventPostMessage implements PostMessage {
|
||||||
|
constructor(private send: CustomEventMessage) {}
|
||||||
|
|
||||||
|
postMessage(message: any): void {
|
||||||
|
this.send.nativeSend(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用CustomEvent来进行通讯, 可以在content与inject中传递一些dom对象
|
||||||
|
export class CustomEventMessage implements Message {
|
||||||
|
EE: EventEmitter = new EventEmitter();
|
||||||
|
|
||||||
|
// 关联dom目标
|
||||||
|
relatedTarget: Map<number, Element> = new Map();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected flag: string,
|
||||||
|
protected isContent: boolean
|
||||||
|
) {
|
||||||
|
window.addEventListener((isContent ? "ct" : "fd") + flag, (event) => {
|
||||||
|
if (event instanceof MouseEvent) {
|
||||||
|
this.relatedTarget.set(event.clientX, <Element>event.relatedTarget);
|
||||||
|
return;
|
||||||
|
} else if (event instanceof CustomEvent) {
|
||||||
|
this.messageHandle(event.detail, new CustomEventPostMessage(this));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
messageHandle(data: WindowMessageBody, target: PostMessage) {
|
||||||
|
// 处理消息
|
||||||
|
if (data.type === "sendMessage") {
|
||||||
|
// 接收到消息
|
||||||
|
this.EE.emit("message", data.data, (resp: any) => {
|
||||||
|
// 发送响应消息
|
||||||
|
// 无消息id则不发送响应消息
|
||||||
|
if (!data.messageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const body: WindowMessageBody = {
|
||||||
|
messageId: data.messageId,
|
||||||
|
type: "respMessage",
|
||||||
|
data: resp,
|
||||||
|
};
|
||||||
|
target.postMessage(body);
|
||||||
|
});
|
||||||
|
} else if (data.type === "respMessage") {
|
||||||
|
// 接收到响应消息
|
||||||
|
this.EE.emit("response:" + data.messageId, data);
|
||||||
|
} else if (data.type === "connect") {
|
||||||
|
this.EE.emit("connect", data.data, new WindowMessageConnect(data.messageId, this.EE, target));
|
||||||
|
} else if (data.type === "disconnect") {
|
||||||
|
this.EE.emit("disconnect:" + data.messageId);
|
||||||
|
} else if (data.type === "connectMessage") {
|
||||||
|
this.EE.emit("connectMessage:" + data.messageId, data.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onConnect(callback: (data: any, con: MessageConnect) => void): void {
|
||||||
|
this.EE.addListener("connect", callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMessage(callback: (data: any, sendResponse: (data: any) => void) => void): void {
|
||||||
|
this.EE.addListener("message", callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(data: any): Promise<MessageConnect> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const body: WindowMessageBody = {
|
||||||
|
messageId: uuidv4(),
|
||||||
|
type: "connect",
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
this.nativeSend(body);
|
||||||
|
resolve(new WindowMessageConnect(body.messageId, this.EE, new CustomEventPostMessage(this)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof cloneInto !== "undefined") {
|
||||||
|
try {
|
||||||
|
LoggerCore.logger().info("nativeSend");
|
||||||
|
detail = cloneInto(detail, document.defaultView);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
LoggerCore.logger().info("error data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ev = new CustomEvent((this.isContent ? "fd" : "ct") + this.flag, {
|
||||||
|
detail,
|
||||||
|
});
|
||||||
|
window.dispatchEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage(data: any): Promise<any> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const body: WindowMessageBody = {
|
||||||
|
messageId: uuidv4(),
|
||||||
|
type: "sendMessage",
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
const callback = (body: WindowMessageBody) => {
|
||||||
|
this.EE.removeListener("response:" + body.messageId, callback);
|
||||||
|
resolve(body.data);
|
||||||
|
};
|
||||||
|
this.EE.addListener("response:" + body.messageId, callback);
|
||||||
|
this.nativeSend(body);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ import { Message, MessageConnect, MessageSend } from "./server";
|
|||||||
|
|
||||||
import EventEmitter from "eventemitter3";
|
import EventEmitter from "eventemitter3";
|
||||||
|
|
||||||
interface PostMessage {
|
export interface PostMessage {
|
||||||
postMessage(message: any): void;
|
postMessage(message: any): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ export default defineConfig({
|
|||||||
service_worker: `${src}/service_worker.ts`,
|
service_worker: `${src}/service_worker.ts`,
|
||||||
offscreen: `${src}/offscreen.ts`,
|
offscreen: `${src}/offscreen.ts`,
|
||||||
sandbox: `${src}/sandbox.ts`,
|
sandbox: `${src}/sandbox.ts`,
|
||||||
|
content: `${src}/content.ts`,
|
||||||
|
inject: `${src}/inject.ts`,
|
||||||
popup: `${src}/pages/popup/main.tsx`,
|
popup: `${src}/pages/popup/main.tsx`,
|
||||||
install: `${src}/pages/install/main.tsx`,
|
install: `${src}/pages/install/main.tsx`,
|
||||||
options: `${src}/pages/options/main.tsx`,
|
options: `${src}/pages/options/main.tsx`,
|
||||||
|
@ -6,7 +6,7 @@ import { ResourceClient, ScriptClient, ValueClient } from "../service_worker/cli
|
|||||||
import { SCRIPT_STATUS_ENABLE, SCRIPT_TYPE_NORMAL, ScriptRunResouce } from "@App/app/repo/scripts";
|
import { SCRIPT_STATUS_ENABLE, SCRIPT_TYPE_NORMAL, ScriptRunResouce } from "@App/app/repo/scripts";
|
||||||
import { disableScript, enableScript, runScript, stopScript } from "../sandbox/client";
|
import { disableScript, enableScript, runScript, stopScript } from "../sandbox/client";
|
||||||
import { Group, MessageSend } from "@Packages/message/server";
|
import { Group, MessageSend } from "@Packages/message/server";
|
||||||
import { subscribeScriptEnable, subscribeScriptInstall } from "../queue";
|
import { subscribeScriptDelete, subscribeScriptEnable, subscribeScriptInstall } from "../queue";
|
||||||
|
|
||||||
export class ScriptService {
|
export class ScriptService {
|
||||||
logger: Logger;
|
logger: Logger;
|
||||||
@ -34,7 +34,6 @@ export class ScriptService {
|
|||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
subscribeScriptEnable(this.messageQueue, async (data) => {
|
subscribeScriptEnable(this.messageQueue, async (data) => {
|
||||||
console.log("subscribeScriptEnable", data);
|
|
||||||
const script = await this.scriptClient.info(data.uuid);
|
const script = await this.scriptClient.info(data.uuid);
|
||||||
if (script.type === SCRIPT_TYPE_NORMAL) {
|
if (script.type === SCRIPT_TYPE_NORMAL) {
|
||||||
return;
|
return;
|
||||||
@ -48,7 +47,6 @@ export class ScriptService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
subscribeScriptInstall(this.messageQueue, async (data) => {
|
subscribeScriptInstall(this.messageQueue, async (data) => {
|
||||||
console.log("subscribeScriptInstall", data);
|
|
||||||
// 判断是开启还是关闭
|
// 判断是开启还是关闭
|
||||||
if (data.script.status === SCRIPT_STATUS_ENABLE) {
|
if (data.script.status === SCRIPT_STATUS_ENABLE) {
|
||||||
// 构造脚本运行资源,发送给沙盒运行
|
// 构造脚本运行资源,发送给沙盒运行
|
||||||
@ -58,6 +56,9 @@ export class ScriptService {
|
|||||||
disableScript(this.windowMessage, data.script.uuid);
|
disableScript(this.windowMessage, data.script.uuid);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
subscribeScriptDelete(this.messageQueue, async (data) => {
|
||||||
|
disableScript(this.windowMessage, data.uuid);
|
||||||
|
});
|
||||||
|
|
||||||
this.group.on("runScript", this.runScript.bind(this));
|
this.group.on("runScript", this.runScript.bind(this));
|
||||||
this.group.on("stopScript", this.stopScript.bind(this));
|
this.group.on("stopScript", this.stopScript.bind(this));
|
||||||
|
@ -81,4 +81,8 @@ export class RuntimeClient extends Client {
|
|||||||
stopScript(uuid: string) {
|
stopScript(uuid: string) {
|
||||||
return this.do("stopScript", uuid);
|
return this.do("stopScript", uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pageLoad(): Promise<{ flag: string; scripts: ScriptRunResouce[] }> {
|
||||||
|
return this.do("pageLoad");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,29 @@
|
|||||||
import { MessageQueue } from "@Packages/message/message_queue";
|
import { MessageQueue } from "@Packages/message/message_queue";
|
||||||
import { Group, MessageSend } from "@Packages/message/server";
|
import { Group, MessageSend } from "@Packages/message/server";
|
||||||
import { Script, SCRIPT_STATUS_ENABLE, SCRIPT_TYPE_NORMAL, ScriptAndCode, ScriptDAO } 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 { subscribeScriptEnable } 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 } from "./utils";
|
import { dealMatches, getRunAt } from "./utils";
|
||||||
|
import { randomString } from "@App/pkg/utils/utils";
|
||||||
|
import { compileInjectScript, compileScriptCode } from "@App/runtime/content/utils";
|
||||||
|
|
||||||
export class RuntimeService {
|
export class RuntimeService {
|
||||||
scriptDAO: ScriptDAO = new ScriptDAO();
|
scriptDAO: ScriptDAO = new ScriptDAO();
|
||||||
|
|
||||||
|
scriptFlag: string = randomString(8);
|
||||||
|
|
||||||
|
// 运行中的页面脚本
|
||||||
|
runningPageScript = new Map<string, ScriptRunResouce>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private group: Group,
|
private group: Group,
|
||||||
private sender: MessageSend,
|
private sender: MessageSend,
|
||||||
@ -20,6 +33,8 @@ export class RuntimeService {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
|
// 读取inject.js注入页面
|
||||||
|
this.registerInjectScript();
|
||||||
// 监听脚本开启
|
// 监听脚本开启
|
||||||
subscribeScriptEnable(this.mq, async (data) => {
|
subscribeScriptEnable(this.mq, async (data) => {
|
||||||
const script = await this.scriptDAO.getAndCode(data.uuid);
|
const script = await this.scriptDAO.getAndCode(data.uuid);
|
||||||
@ -37,6 +52,26 @@ export class RuntimeService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// 监听脚本安装
|
||||||
|
subscribeScriptInstall(this.mq, async (data) => {
|
||||||
|
const script = await this.scriptDAO.get(data.script.uuid);
|
||||||
|
if (!script) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (script.type === SCRIPT_TYPE_NORMAL) {
|
||||||
|
this.registryPageScript(script);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 监听脚本删除
|
||||||
|
subscribeScriptDelete(this.mq, async (data) => {
|
||||||
|
const script = await this.scriptDAO.get(data.uuid);
|
||||||
|
if (!script) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (script.type === SCRIPT_TYPE_NORMAL) {
|
||||||
|
this.unregistryPageScript(script);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 将开启的脚本发送一次enable消息
|
// 将开启的脚本发送一次enable消息
|
||||||
const scriptDao = new ScriptDAO();
|
const scriptDao = new ScriptDAO();
|
||||||
@ -63,6 +98,11 @@ export class RuntimeService {
|
|||||||
|
|
||||||
this.group.on("stopScript", this.stopScript.bind(this));
|
this.group.on("stopScript", this.stopScript.bind(this));
|
||||||
this.group.on("runScript", this.runScript.bind(this));
|
this.group.on("runScript", this.runScript.bind(this));
|
||||||
|
this.group.on("pageLoad", this.pageLoad.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
pageLoad() {
|
||||||
|
return Promise.resolve({ flag: this.scriptFlag });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 停止脚本
|
// 停止脚本
|
||||||
@ -80,16 +120,41 @@ export class RuntimeService {
|
|||||||
return runScript(this.sender, res);
|
return runScript(this.sender, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
registryPageScript(script: ScriptAndCode) {
|
registerInjectScript() {
|
||||||
console.log(script);
|
fetch("inject.js")
|
||||||
|
.then((res) => res.text())
|
||||||
|
.then((injectJs) => {
|
||||||
|
// 替换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",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async registryPageScript(script: Script) {
|
||||||
const matches = script.metadata["match"];
|
const matches = script.metadata["match"];
|
||||||
if (!matches) {
|
if (!matches) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const scriptRes = await this.script.buildScriptRunResource(script);
|
||||||
|
|
||||||
|
scriptRes.code = compileScriptCode(scriptRes);
|
||||||
|
scriptRes.code = compileInjectScript(scriptRes);
|
||||||
|
|
||||||
|
this.runningPageScript.set(scriptRes.uuid, scriptRes);
|
||||||
|
|
||||||
matches.push(...(script.metadata["include"] || []));
|
matches.push(...(script.metadata["include"] || []));
|
||||||
const registerScript: chrome.userScripts.RegisteredUserScript = {
|
const registerScript: chrome.userScripts.RegisteredUserScript = {
|
||||||
id: script.uuid,
|
id: scriptRes.uuid,
|
||||||
js: [{ code: script.code }],
|
js: [{ code: scriptRes.code }],
|
||||||
matches: dealMatches(matches),
|
matches: dealMatches(matches),
|
||||||
world: "MAIN",
|
world: "MAIN",
|
||||||
};
|
};
|
||||||
@ -102,7 +167,7 @@ export class RuntimeService {
|
|||||||
registerScript.excludeMatches = dealMatches(excludeMatches);
|
registerScript.excludeMatches = dealMatches(excludeMatches);
|
||||||
}
|
}
|
||||||
if (script.metadata["run-at"]) {
|
if (script.metadata["run-at"]) {
|
||||||
registerScript.runAt = script.metadata["run-at"][0] as chrome.userScripts.RunAt;
|
registerScript.runAt = getRunAt(script.metadata["run-at"]);
|
||||||
}
|
}
|
||||||
chrome.userScripts.register([registerScript]);
|
chrome.userScripts.register([registerScript]);
|
||||||
}
|
}
|
||||||
|
@ -273,7 +273,8 @@ export class ScriptService {
|
|||||||
if (!code) {
|
if (!code) {
|
||||||
throw new Error("code is null");
|
throw new Error("code is null");
|
||||||
}
|
}
|
||||||
ret.code = compileScriptCode(ret, code.code);
|
ret.code = code.code;
|
||||||
|
ret.code = compileScriptCode(ret);
|
||||||
|
|
||||||
return Promise.resolve(ret);
|
return Promise.resolve(ret);
|
||||||
}
|
}
|
||||||
|
@ -9,3 +9,16 @@ export function isExtensionRequest(details: chrome.webRequest.ResourceRequest &
|
|||||||
export function dealMatches(matches: string[]) {
|
export function dealMatches(matches: string[]) {
|
||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getRunAt(runAts: string[]): chrome.userScripts.RunAt {
|
||||||
|
if (runAts.length === 0) {
|
||||||
|
return "document_idle";
|
||||||
|
}
|
||||||
|
const runAt = runAts[0];
|
||||||
|
if (runAt === "document-start") {
|
||||||
|
return "document_start";
|
||||||
|
} else if (runAt === "document-end") {
|
||||||
|
return "document_end";
|
||||||
|
}
|
||||||
|
return "document_idle";
|
||||||
|
}
|
||||||
|
@ -1,24 +1,25 @@
|
|||||||
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 { SandboxManager } from "./app/service/sandbox";
|
|
||||||
import { ExtensionMessageSend } from "@Packages/message/extension_message";
|
import { ExtensionMessageSend } from "@Packages/message/extension_message";
|
||||||
|
import { CustomEventMessage } from "@Packages/message/custom_event_message";
|
||||||
|
import { RuntimeClient } from "./app/service/service_worker/client";
|
||||||
import ContentRuntime from "./runtime/content/content";
|
import ContentRuntime from "./runtime/content/content";
|
||||||
|
|
||||||
function main() {
|
// 建立与service_worker页面的连接
|
||||||
// 建立与service_worker页面的连接
|
const send = new ExtensionMessageSend();
|
||||||
const msg = new ExtensionMessageSend();
|
|
||||||
|
|
||||||
// 初始化日志组件
|
// 初始化日志组件
|
||||||
const loggerCore = new LoggerCore({
|
const loggerCore = new LoggerCore({
|
||||||
writer: new MessageWriter(msg),
|
writer: new MessageWriter(send),
|
||||||
labels: { env: "content" },
|
labels: { env: "content" },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const client = new RuntimeClient(send);
|
||||||
|
client.pageLoad().then((data) => {
|
||||||
loggerCore.logger().debug("content start");
|
loggerCore.logger().debug("content start");
|
||||||
|
console.log("content", data);
|
||||||
|
const msg = new CustomEventMessage(data.flag, true);
|
||||||
// 初始化运行环境
|
// 初始化运行环境
|
||||||
const contentMessage = new MessageContent(scriptFlag, true);
|
const runtime = new ContentRuntime(send, msg);
|
||||||
const runtime = new ContentRuntime(contentMessage, internalMessage);
|
runtime.start(data.scripts);
|
||||||
runtime.start();
|
});
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
|
26
src/inject.ts
Normal file
26
src/inject.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import LoggerCore from "./app/logger/core";
|
||||||
|
import MessageWriter from "./app/logger/message_writer";
|
||||||
|
import { CustomEventMessage } from "@Packages/message/custom_event_message";
|
||||||
|
import { Server } from "@Packages/message/server";
|
||||||
|
import { InjectRuntime } from "./runtime/content/inject";
|
||||||
|
import { ScriptRunResouce } from "./app/repo/scripts";
|
||||||
|
|
||||||
|
// 通过flag与content建立通讯,这个ScriptFlag是后端注入时候生成的
|
||||||
|
const flag = ScriptFlag;
|
||||||
|
|
||||||
|
const msg = new CustomEventMessage(flag, false);
|
||||||
|
|
||||||
|
// 加载logger组件
|
||||||
|
const logger = new LoggerCore({
|
||||||
|
writer: new MessageWriter(msg),
|
||||||
|
labels: { env: "inject", href: window.location.href },
|
||||||
|
});
|
||||||
|
|
||||||
|
const server = new Server("inject", msg);
|
||||||
|
|
||||||
|
server.on("pageLoad", (data: ScriptRunResouce[]) => {
|
||||||
|
logger.logger().debug("inject start");
|
||||||
|
console.log("inject", data);
|
||||||
|
const runtime = new InjectRuntime(msg, data);
|
||||||
|
runtime.start();
|
||||||
|
});
|
@ -1,23 +0,0 @@
|
|||||||
import LoggerCore from "./app/logger/core";
|
|
||||||
import MessageWriter from "./app/logger/message_writer";
|
|
||||||
import MessageContent from "./app/message/content";
|
|
||||||
import InjectRuntime from "./runtime/content/inject";
|
|
||||||
|
|
||||||
// 通过flag与content建立通讯,这个ScriptFlag是后端注入时候生成的
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
const flag = ScriptFlag;
|
|
||||||
|
|
||||||
const message = new MessageContent(flag, false);
|
|
||||||
|
|
||||||
// 加载logger组件
|
|
||||||
const logger = new LoggerCore({
|
|
||||||
debug: process.env.NODE_ENV === "development",
|
|
||||||
writer: new MessageWriter(message),
|
|
||||||
labels: { env: "inject", href: window.location.href },
|
|
||||||
});
|
|
||||||
|
|
||||||
message.setHandler("pageLoad", (_action, data) => {
|
|
||||||
logger.logger().debug("inject start");
|
|
||||||
const runtime = new InjectRuntime(message, data.scripts, flag);
|
|
||||||
runtime.start();
|
|
||||||
});
|
|
@ -17,6 +17,18 @@
|
|||||||
"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"
|
||||||
},
|
},
|
||||||
|
@ -1,115 +1,94 @@
|
|||||||
import { ExternalMessage } from "@App/app/const";
|
|
||||||
import MessageContent from "@App/app/message/content";
|
|
||||||
import MessageInternal from "@App/app/message/internal";
|
|
||||||
import { MessageHander, MessageManager } from "@App/app/message/message";
|
|
||||||
import { ScriptRunResouce } from "@App/app/repo/scripts";
|
import { ScriptRunResouce } from "@App/app/repo/scripts";
|
||||||
|
import { Client } from "@Packages/message/client";
|
||||||
|
import { Message, MessageSend } from "@Packages/message/server";
|
||||||
|
|
||||||
// content页的处理
|
// content页的处理
|
||||||
export default class ContentRuntime {
|
export default class ContentRuntime {
|
||||||
contentMessage: MessageHander & MessageManager;
|
|
||||||
|
|
||||||
internalMessage: MessageInternal;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
contentMessage: MessageHander & MessageManager,
|
private send: MessageSend,
|
||||||
internalMessage: MessageInternal
|
private msg: Message
|
||||||
) {
|
) {}
|
||||||
this.contentMessage = contentMessage;
|
|
||||||
this.internalMessage = internalMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
start(resp: { scripts: ScriptRunResouce[] }) {
|
start(scripts: ScriptRunResouce[]) {
|
||||||
// 由content到background
|
// 由content到background
|
||||||
// 转发gmApi消息
|
// 转发gmApi消息
|
||||||
this.contentMessage.setHandler("gmApi", (action, data) => {
|
// this.contentMessage.setHandler("gmApi", (action, data) => {
|
||||||
return this.internalMessage.syncSend(action, data);
|
// return this.internalMessage.syncSend(action, data);
|
||||||
});
|
// });
|
||||||
// 转发log消息
|
// // 转发log消息
|
||||||
this.contentMessage.setHandler("log", (action, data) => {
|
// this.contentMessage.setHandler("log", (action, data) => {
|
||||||
this.internalMessage.send(action, data);
|
// this.internalMessage.send(action, data);
|
||||||
});
|
// });
|
||||||
// 转发externalMessage消息
|
// // 转发externalMessage消息
|
||||||
this.contentMessage.setHandler(ExternalMessage, (action, data) => {
|
// this.contentMessage.setHandler(ExternalMessage, (action, data) => {
|
||||||
return this.internalMessage.syncSend(action, data);
|
// return this.internalMessage.syncSend(action, data);
|
||||||
});
|
// });
|
||||||
// 处理GM_addElement
|
// 处理GM_addElement
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.contentMessage.setHandler("GM_addElement", (action, data) => {
|
// this.contentMessage.setHandler("GM_addElement", (action, data) => {
|
||||||
const parma = data.param;
|
// const parma = data.param;
|
||||||
let attr: { [x: string]: any; textContent?: any };
|
// let attr: { [x: string]: any; textContent?: any };
|
||||||
let textContent = "";
|
// let textContent = "";
|
||||||
if (!parma[1]) {
|
// if (!parma[1]) {
|
||||||
attr = {};
|
// attr = {};
|
||||||
} else {
|
// } else {
|
||||||
attr = { ...parma[1] };
|
// attr = { ...parma[1] };
|
||||||
if (attr.textContent) {
|
// if (attr.textContent) {
|
||||||
textContent = attr.textContent;
|
// textContent = attr.textContent;
|
||||||
delete attr.textContent;
|
// delete attr.textContent;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
const el = <Element>document.createElement(parma[0]);
|
// const el = <Element>document.createElement(parma[0]);
|
||||||
Object.keys(attr).forEach((key) => {
|
// Object.keys(attr).forEach((key) => {
|
||||||
el.setAttribute(key, attr[key]);
|
// el.setAttribute(key, attr[key]);
|
||||||
});
|
// });
|
||||||
if (textContent) {
|
// if (textContent) {
|
||||||
el.innerHTML = textContent;
|
// el.innerHTML = textContent;
|
||||||
}
|
// }
|
||||||
let parentNode;
|
// let parentNode;
|
||||||
if (data.relatedTarget) {
|
// if (data.relatedTarget) {
|
||||||
parentNode = (<MessageContent>(
|
// parentNode = (<MessageContent>this.contentMessage).getAndDelRelatedTarget(data.relatedTarget);
|
||||||
this.contentMessage
|
// }
|
||||||
)).getAndDelRelatedTarget(data.relatedTarget);
|
// (<Element>parentNode || document.head || document.body || document.querySelector("*")).appendChild(el);
|
||||||
}
|
// return {
|
||||||
(
|
// relatedTarget: el,
|
||||||
<Element>parentNode ||
|
// };
|
||||||
document.head ||
|
// });
|
||||||
document.body ||
|
// // 转发长连接的gmApi消息
|
||||||
document.querySelector("*")
|
// this.contentMessage.setHandlerWithChannel("gmApiChannel", (inject, action, data) => {
|
||||||
).appendChild(el);
|
// const background = this.internalMessage.channel();
|
||||||
return {
|
// // 转发inject->background
|
||||||
relatedTarget: el,
|
// inject.setHandler((req) => {
|
||||||
};
|
// background.send(req.data);
|
||||||
});
|
// });
|
||||||
|
// inject.setCatch((err) => {
|
||||||
// 转发长连接的gmApi消息
|
// background.throw(err);
|
||||||
this.contentMessage.setHandlerWithChannel(
|
// });
|
||||||
"gmApiChannel",
|
// inject.setDisChannelHandler(() => {
|
||||||
(inject, action, data) => {
|
// background.disChannel();
|
||||||
const background = this.internalMessage.channel();
|
// });
|
||||||
// 转发inject->background
|
// // 转发background->inject
|
||||||
inject.setHandler((req) => {
|
// background.setHandler((bgResp) => {
|
||||||
background.send(req.data);
|
// inject.send(bgResp);
|
||||||
});
|
// });
|
||||||
inject.setCatch((err) => {
|
// background.setCatch((err) => {
|
||||||
background.throw(err);
|
// inject.throw(err);
|
||||||
});
|
// });
|
||||||
inject.setDisChannelHandler(() => {
|
// background.setDisChannelHandler(() => {
|
||||||
background.disChannel();
|
// inject.disChannel();
|
||||||
});
|
// });
|
||||||
// 转发background->inject
|
// // 建立连接
|
||||||
background.setHandler((bgResp) => {
|
// background.channel(action, data);
|
||||||
inject.send(bgResp);
|
// });
|
||||||
});
|
// this.listenCATApi();
|
||||||
background.setCatch((err) => {
|
// // 由background到content
|
||||||
inject.throw(err);
|
// // 转发value更新事件
|
||||||
});
|
// this.internalMessage.setHandler("valueUpdate", (action, data) => {
|
||||||
background.setDisChannelHandler(() => {
|
// this.contentMessage.send(action, data);
|
||||||
inject.disChannel();
|
// });
|
||||||
});
|
// this.msg.sendMessage({ action: "pageLoad", data: { scripts } });
|
||||||
// 建立连接
|
const client = new Client(this.msg, "inject");
|
||||||
background.channel(action, data);
|
client.do("pageLoad", { scripts });
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.listenCATApi();
|
|
||||||
|
|
||||||
// 由background到content
|
|
||||||
// 转发value更新事件
|
|
||||||
this.internalMessage.setHandler("valueUpdate", (action, data) => {
|
|
||||||
this.contentMessage.send(action, data);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.contentMessage.send("pageLoad", resp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
listenCATApi() {
|
listenCATApi() {
|
||||||
@ -117,16 +96,13 @@ export default class ContentRuntime {
|
|||||||
this.contentMessage.setHandler("CAT_fetchBlob", (_action, data: string) => {
|
this.contentMessage.setHandler("CAT_fetchBlob", (_action, data: string) => {
|
||||||
return fetch(data).then((res) => res.blob());
|
return fetch(data).then((res) => res.blob());
|
||||||
});
|
});
|
||||||
this.contentMessage.setHandler(
|
this.contentMessage.setHandler("CAT_createBlobUrl", (_action, data: Blob) => {
|
||||||
"CAT_createBlobUrl",
|
|
||||||
(_action, data: Blob) => {
|
|
||||||
const url = URL.createObjectURL(data);
|
const url = URL.createObjectURL(data);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
}, 60 * 1000);
|
}, 60 * 1000);
|
||||||
return Promise.resolve(url);
|
return Promise.resolve(url);
|
||||||
}
|
});
|
||||||
);
|
|
||||||
// 处理CAT_fetchDocument
|
// 处理CAT_fetchDocument
|
||||||
this.contentMessage.setHandler("CAT_fetchDocument", (_action, data) => {
|
this.contentMessage.setHandler("CAT_fetchDocument", (_action, data) => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
|
11
src/runtime/content/inject.ts
Normal file
11
src/runtime/content/inject.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { ScriptRunResouce } from "@App/app/repo/scripts";
|
||||||
|
import { Message } from "@Packages/message/server";
|
||||||
|
|
||||||
|
export class InjectRuntime {
|
||||||
|
constructor(
|
||||||
|
private msg: Message,
|
||||||
|
private scripts: ScriptRunResouce[]
|
||||||
|
) {}
|
||||||
|
|
||||||
|
start(){}
|
||||||
|
}
|
@ -5,7 +5,7 @@ import { has } from "@App/pkg/utils/lodash";
|
|||||||
import { Message } from "@Packages/message/server";
|
import { Message } from "@Packages/message/server";
|
||||||
|
|
||||||
// 构建脚本运行代码
|
// 构建脚本运行代码
|
||||||
export function compileScriptCode(scriptRes: ScriptRunResouce, code: string): string {
|
export function compileScriptCode(scriptRes: ScriptRunResouce): string {
|
||||||
let require = "";
|
let require = "";
|
||||||
if (scriptRes.metadata.require) {
|
if (scriptRes.metadata.require) {
|
||||||
scriptRes.metadata.require.forEach((val) => {
|
scriptRes.metadata.require.forEach((val) => {
|
||||||
@ -15,7 +15,7 @@ export function compileScriptCode(scriptRes: ScriptRunResouce, code: string): st
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
code = require + code;
|
const code = require + scriptRes.code;
|
||||||
return `with (context) return (async ()=>{\n${code}\n//# sourceURL=${chrome.runtime.getURL(
|
return `with (context) return (async ()=>{\n${code}\n//# sourceURL=${chrome.runtime.getURL(
|
||||||
`/${encodeURI(scriptRes.name)}.user.js`
|
`/${encodeURI(scriptRes.name)}.user.js`
|
||||||
)}\n})()`;
|
)}\n})()`;
|
||||||
|
@ -55,7 +55,8 @@ async function main() {
|
|||||||
});
|
});
|
||||||
loggerCore.logger().debug("service worker start");
|
loggerCore.logger().debug("service worker start");
|
||||||
// 初始化管理器
|
// 初始化管理器
|
||||||
const server = new Server("serviceWorker", new ExtensionMessage());
|
const message = new ExtensionMessage();
|
||||||
|
const server = new Server("serviceWorker", message);
|
||||||
const manager = new ServiceWorkerManager(server, new MessageQueue(), new ServiceWorkerMessageSend());
|
const manager = new ServiceWorkerManager(server, new MessageQueue(), new ServiceWorkerMessageSend());
|
||||||
manager.initManager();
|
manager.initManager();
|
||||||
// 初始化沙盒环境
|
// 初始化沙盒环境
|
||||||
|
5
src/types/main.d.ts
vendored
5
src/types/main.d.ts
vendored
@ -7,6 +7,11 @@ declare const sandbox: Window;
|
|||||||
|
|
||||||
declare const self: ServiceWorkerGlobalScope;
|
declare const self: ServiceWorkerGlobalScope;
|
||||||
|
|
||||||
|
declare const ScriptFlag: string;
|
||||||
|
|
||||||
|
// 可以让content与inject环境交换携带dom的对象
|
||||||
|
declare let cloneInto: ((detail: any, view: any) => any) | undefined;
|
||||||
|
|
||||||
declare namespace GMSend {
|
declare namespace GMSend {
|
||||||
interface XHRDetails {
|
interface XHRDetails {
|
||||||
method?: "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS";
|
method?: "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS";
|
||||||
|
Reference in New Issue
Block a user