Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: move worker to content script from background script #38

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import React, { Dispatch, SetStateAction, useContext, useState } from "react";
import { browser } from "webextension-polyfill-ts";
import * as Comlink from "comlink";
import { forward } from "comlink-extension";
import { backgroundPopupObject } from "../background";
import { BackgroundPopupObject } from "../background";
const { port1, port2 } = new MessageChannel();
forward(port1, browser.runtime.connect());
// content-script <-> background page
const port = Comlink.wrap<backgroundPopupObject>(port2);
const port = Comlink.wrap<BackgroundPopupObject>(port2);
export type AppState = {
value: number;
};
Expand All @@ -16,7 +16,7 @@ const initialState: AppState = {
value: 0
};

const PortStateContext = React.createContext<backgroundPopupObject>(port);
const PortStateContext = React.createContext<BackgroundPopupObject>(port);
const AppStateContext = React.createContext<AppState>(initialState);
const SetAppStateContext = React.createContext<Dispatch<SetStateAction<AppState>>>(() => {});

Expand Down
93 changes: 17 additions & 76 deletions packages/webextension/app/scripts/background.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { browser } from "webextension-polyfill-ts";
import { createBackgroundEndpoint, isMessagePort } from "comlink-extension";
import * as Comlink from "comlink";
import { createTextlintWorker } from "./background/textlint";
import { openDatabase } from "./background/database";
import { LintEngineAPI } from "textchecker-element";
import { TextlintResult } from "@textlint/types";
import { scriptWorkerSet } from "./background/scriptWorkerSet";
import { openDatabase, Script } from "./background/database";
import { logger } from "./utils/logger";

// browser.runtime.onInstalled.addListener((details) => {
Expand Down Expand Up @@ -59,17 +55,21 @@ browser.webRequest.onHeadersReceived.addListener(
type ThenArg<T> = T extends PromiseLike<infer U> ? U : T;

type DataBase = ThenArg<ReturnType<typeof openDatabase>>;
export type backgroundExposedObject = {
addScript: DataBase["addScript"];
} & LintEngineAPI;
export type backgroundPopupObject = {
export type BackgroundPopupObject = {
findScriptsWithPatten: DataBase["findScriptsWithPatten"];
findScriptsWithName: DataBase["findScriptsWithName"];
deleteScript: DataBase["deleteScript"];
updateScript: DataBase["updateScript"];
addScript: DataBase["addScript"];
openEditor: (options: { name: string; namespace: string }) => void;
};

// Background → Content Script events
export type BackgroundToContentEventBoot = {
type: "textlint-editor-boot";
scripts: Script[];
};
export type BackgroundToContentEvents = BackgroundToContentEventBoot;
browser.runtime.onConnect.addListener(async (port) => {
if (isMessagePort(port)) {
return;
Expand All @@ -80,12 +80,14 @@ browser.runtime.onConnect.addListener(async (port) => {
if (!originUrl) {
return;
}
if (/^(moz|chrome)-extension:\/\/.*\/(edit-script.html|popup.html)/.test(originUrl)) {
const exports: backgroundPopupObject = {
// internal page
if (/^(moz|chrome)-extension:\/\/.*\/(install-dialog.html|edit-script.html|popup.html)/.test(originUrl)) {
const exports: BackgroundPopupObject = {
findScriptsWithPatten: db.findScriptsWithPatten,
findScriptsWithName: db.findScriptsWithName,
deleteScript: db.deleteScript,
updateScript: db.updateScript,
addScript: db.addScript,
openEditor: (options: { name: string; namespace: string }) => {
const editPageUrl = browser.runtime.getURL("/pages/edit-script.html");
browser.tabs.create({
Expand All @@ -97,72 +99,11 @@ browser.runtime.onConnect.addListener(async (port) => {
};
return Comlink.expose(exports, createBackgroundEndpoint(port));
}
// website
const scripts = await db.findScriptsWithPatten(originUrl);
logger.log("scripts", scripts);
const scriptWorkers = scripts.map((script) => {
const runningWorker = scriptWorkerSet.get(script);
if (runningWorker) {
return {
worker: runningWorker,
ext: script.ext
};
}
// TODO: comment support for textlintrc
const textlintWorker = createTextlintWorker(script);
scriptWorkerSet.add({ script, worker: textlintWorker, url: originUrl });
return {
worker: textlintWorker,
ext: script.ext
};
});
logger.log("workers started", scriptWorkers);
// Support multiple workers
const lintEngine: LintEngineAPI = {
async lintText({ text }: { text: string }): Promise<TextlintResult[]> {
logger.log("text:", text);
const allLintResults = await Promise.all(
scriptWorkers.map(({ worker, ext }) => {
return worker.createLintEngine({ ext }).lintText({ text });
})
);
logger.log("lintText", allLintResults);
return allLintResults.flat();
},
async fixText({ text }): Promise<{ output: string }> {
let output = text;
for (const { worker, ext } of scriptWorkers) {
await worker
.createLintEngine({ ext })
.fixText({ text: output, messages: [] })
.then((result) => {
output = result.output;
return result;
});
}
return {
output
};
},
async ignoreText(): Promise<boolean> {
throw new Error("No implement ignoreText on background");
}
};
const backgroundExposedObject: backgroundExposedObject = {
...lintEngine,
addScript: (script) => {
return db.addScript(script);
}
};
port.onDisconnect.addListener(async () => {
logger.log("dispose worker");
const scripts = await db.findScriptsWithPatten(originUrl);
scripts.forEach((script) => {
scriptWorkerSet.delete({ script: script, url: originUrl });
});
port.postMessage({
type: "textlint-editor-boot",
scripts
});
logger.log("content port", port);
scriptWorkerSet.dump();
Comlink.expose(backgroundExposedObject, createBackgroundEndpoint(port));
await Promise.all(scriptWorkers.map(({ worker }) => worker.ready()));
port.postMessage("textlint-editor-boot");
});
74 changes: 60 additions & 14 deletions packages/webextension/app/scripts/contentScript.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,64 @@
import { LintEngineAPI } from "textchecker-element";
import { browser } from "webextension-polyfill-ts";
import { createEndpoint } from "comlink-extension";
import * as Comlink from "comlink";
import type { backgroundExposedObject } from "./background";
import { nonRandomKey } from "./shared/page-contents-shared";
import { logger } from "./utils/logger";
import type { Script } from "./background/database";
import { createTextlintWorker } from "./background/textlint";
import type { TextlintResult } from "@textlint/types";
import { BackgroundToContentEvents } from "./background";

const rawPort = browser.runtime.connect();
// content-script <-> background page
const port = Comlink.wrap<backgroundExposedObject>(createEndpoint(rawPort));
rawPort.onMessage.addListener((event) => {
if (event === "textlint-editor-boot") {
logger.log("[ContentScript]", "boot event received");
// const port = Comlink.wrap<backgroundExposedObject>(createEndpoint(rawPort));
const createLintEngine = async (scripts: Script[]) => {
const scriptWorkers = scripts.map((script) => {
// TODO: comment support for textlintrc
const textlintWorker = createTextlintWorker(script);
return {
worker: textlintWorker,
ext: script.ext
};
});
await Promise.all(scriptWorkers.map(({ worker }) => worker.ready()));
const lintEngine: LintEngineAPI = {
async lintText({ text }: { text: string }): Promise<TextlintResult[]> {
logger.log("text:", text);
const allLintResults = await Promise.all(
scriptWorkers.map(({ worker, ext }) => {
return worker.createLintEngine({ ext }).lintText({ text });
})
);
logger.log("lintText", allLintResults);
return allLintResults.flat();
},
async fixText({ text }): Promise<{ output: string }> {
let output = text;
for (const { worker, ext } of scriptWorkers) {
await worker
.createLintEngine({ ext })
.fixText({ text: output, messages: [] })
.then((result) => {
output = result.output;
return result;
});
}
return {
output
};
},
async ignoreText(): Promise<boolean> {
throw new Error("No implement ignoreText on background");
}
};
return lintEngine;
};

let lintEngine: LintEngineAPI | null = null;
rawPort.onMessage.addListener(async (event: BackgroundToContentEvents) => {
if (event.type === "textlint-editor-boot") {
logger.log("[ContentScript]", "boot event received", event.scripts);
lintEngine = await createLintEngine(event.scripts);
logger.log("[ContentScript]", "created LingEngine", lintEngine);
// Inject page-script
try {
const script = browser.extension.getURL("scripts/pageScript.js");
Expand All @@ -23,6 +70,7 @@ rawPort.onMessage.addListener((event) => {
}
}
});

// page-script <-> content-script
window.addEventListener("message", (event) => {
if (
Expand All @@ -31,17 +79,15 @@ window.addEventListener("message", (event) => {
event.data.direction == "from-page-script" &&
event.data.nonRandomKey === nonRandomKey
) {
const lintEngine: LintEngineAPI = {
lintText: port.lintText,
fixText: port.fixText,
ignoreText: port.ignoreText
};
if (!lintEngine) {
throw new Error("No ready lintEngine");
}
const command = event.data.command as keyof typeof lintEngine;
const args = event.data.args;
const lintEngineElement = lintEngine[command];
if (Object.prototype.hasOwnProperty.call(lintEngine, command) && typeof lintEngineElement === "function") {
const newVar: Promise<any> = lintEngineElement(args);
newVar.then((result) => {
const lintCommendResult: Promise<any> = lintEngineElement(args);
lintCommendResult.then((result) => {
window.postMessage(
{
command: command + "::response",
Expand Down
4 changes: 2 additions & 2 deletions packages/webextension/app/scripts/install-dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { createEndpoint } from "comlink-extension";
import * as Comlink from "comlink";
import type { backgroundExposedObject } from "./background";
import type { BackgroundPopupObject } from "./background";
import { browser } from "webextension-polyfill-ts";
import { parseMetadata, TextlintScriptMetadata } from "@textlint/script-parser";
import { logger } from "./utils/logger";
import * as React from "react";
import { useCallback, useEffect, useState } from "react";
import * as ReactDOM from "react-dom";

const port = Comlink.wrap<backgroundExposedObject>(createEndpoint(browser.runtime.connect()));
const port = Comlink.wrap<BackgroundPopupObject>(createEndpoint(browser.runtime.connect()));

async function installHandler({
script,
Expand Down