From efc3cc82cc936ce141eecb4e577bdfc06fe7649e Mon Sep 17 00:00:00 2001 From: Jack Works Date: Wed, 12 May 2021 16:49:40 +0800 Subject: [PATCH 1/2] refactor: inject content scripts --- .../Jobs/InjectContentScripts.ts | 68 +++++++++---------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/packages/maskbook/src/extension/background-script/Jobs/InjectContentScripts.ts b/packages/maskbook/src/extension/background-script/Jobs/InjectContentScripts.ts index 306a456f4f3d..e8d307fd9153 100644 --- a/packages/maskbook/src/extension/background-script/Jobs/InjectContentScripts.ts +++ b/packages/maskbook/src/extension/background-script/Jobs/InjectContentScripts.ts @@ -2,26 +2,11 @@ import { Flags } from '../../../utils/flags' type Args = browser.webNavigation.TransitionNavListener extends browser.webNavigation.NavListener ? U : never export default function () { - const injectedScript = getInjectedScript() - const contentScripts: Array<{ code: string } | { file: string }> = [] - const contentScriptReady = fetch('/generated__content__script.html') - .then((x) => x.text()) - .then((html) => { - const parser = new DOMParser() - const root = parser.parseFromString(html, 'text/html') - root.querySelectorAll('script').forEach((script) => { - if (script.innerText) contentScripts.push({ code: script.innerText }) - else if (script.src) - contentScripts.push({ file: new URL(script.src, browser.runtime.getURL('')).pathname }) - }) - }) + const injectedScript = fetchInjectedScript() + const contentScripts = fetchInjectContentScript('/generated__content__script.html') async function onCommittedListener(arg: Args): Promise { if (arg.url === 'about:blank') return if (!arg.url.startsWith('http')) return - if (process.env.NODE_ENV === 'development') { - if (arg.url.includes('localhost')) return - if (arg.url.includes('127.0.0.1')) return - } const contains = await browser.permissions.contains({ origins: [arg.url] }) if (!contains) return /** @@ -34,9 +19,9 @@ export default function () { runAt: 'document_start', frameId: arg.frameId, // Refresh the injected script every time in the development mode. - code: process.env.NODE_ENV === 'development' ? await getInjectedScript() : await injectedScript, + code: process.env.NODE_ENV === 'development' ? await fetchInjectedScript() : await injectedScript, }) - .catch(IgnoreError(arg)) + .catch(HandleError(arg)) } if (Flags.requires_injected_script_run_directly) { browser.tabs.executeScript(arg.tabId, { @@ -45,27 +30,38 @@ export default function () { file: 'js/injected-script.js', }) } - await contentScriptReady - // it's meaningless if one of the script failed to inject. - // so it's try-for not for-try - try { - for (const script of contentScripts) { - const option: browser.extensionTypes.InjectDetails = { - runAt: 'document_idle', - frameId: arg.frameId, - ...script, - } - await browser.tabs.executeScript(arg.tabId, option) - } - } catch (e) { - IgnoreError(arg)(e) - } + contentScripts(arg.tabId, arg.frameId).catch(HandleError(arg)) } browser.webNavigation.onCommitted.addListener(onCommittedListener) return () => browser.webNavigation.onCommitted.removeListener(onCommittedListener) } -async function getInjectedScript() { +function fetchInjectContentScript(entryHTML: string) { + const contentScripts: Array<{ code: string } | { file: string }> = [] + const task = fetch(entryHTML) + .then((x) => x.text()) + .then((html) => { + const parser = new DOMParser() + const root = parser.parseFromString(html, 'text/html') + for (const script of root.querySelectorAll('script')) { + if (script.innerText) contentScripts.push({ code: script.innerText }) + else if (script.src) + contentScripts.push({ file: new URL(script.src, browser.runtime.getURL('')).pathname }) + } + }) + return async (tabID: number, frameId: number | undefined) => { + await task + for (const script of contentScripts) { + const option: browser.extensionTypes.InjectDetails = { + runAt: 'document_idle', + frameId, + ...script, + } + await browser.tabs.executeScript(tabID, option) + } + } +} +async function fetchInjectedScript() { try { return `{ const script = document.createElement('script') @@ -79,7 +75,7 @@ async function getInjectedScript() { return `console.log('Injected script failed to load.')` } } -function IgnoreError(arg: unknown): (reason: Error) => void { +function HandleError(arg: unknown): (reason: Error) => void { return (e) => { const ignoredErrorMessages = ['non-structured-clonable data', 'No tab with id'] if (ignoredErrorMessages.some((x) => e.message.includes(x))) { From 119cac88f822a43143542ea10cfc74ad6b99e232 Mon Sep 17 00:00:00 2001 From: Jack Works Date: Wed, 12 May 2021 17:03:33 +0800 Subject: [PATCH 2/2] feat: load minimal code if no SNS Adaptor started --- packages/maskbook/src/content-script.ts | 7 +++-- .../src/extension/options-page/index.tsx | 2 +- .../maskbook/src/extension/popup-page/UI.tsx | 3 --- .../src/extension/popup-page/index.tsx | 6 ++--- packages/maskbook/src/setup.ui.ts | 19 +++---------- .../src/social-network-adaptor/index.ts | 3 --- .../maskbook/src/social-network/define.ts | 27 +++++++++++++++++++ packages/maskbook/src/social-network/index.ts | 1 + packages/maskbook/src/social-network/ui.ts | 20 ++++---------- .../maskbook/src/social-network/worker.ts | 10 +------ packages/maskbook/src/utils/debug/ui.ts | 9 +++++++ packages/maskbook/src/utils/native-rpc/Web.ts | 2 +- 12 files changed, 57 insertions(+), 52 deletions(-) create mode 100644 packages/maskbook/src/social-network/define.ts create mode 100644 packages/maskbook/src/utils/debug/ui.ts diff --git a/packages/maskbook/src/content-script.ts b/packages/maskbook/src/content-script.ts index f0054c9e8151..0783486495bb 100644 --- a/packages/maskbook/src/content-script.ts +++ b/packages/maskbook/src/content-script.ts @@ -1,3 +1,6 @@ import './extension/content-script/hmr' -import './setup.ui' -import './extension/content-script/index' +import { status } from './setup.ui' + +status.then((loaded) => { + loaded && import('./extension/content-script/tasks') +}) diff --git a/packages/maskbook/src/extension/options-page/index.tsx b/packages/maskbook/src/extension/options-page/index.tsx index 039bcd329a56..7956cf412ed6 100644 --- a/packages/maskbook/src/extension/options-page/index.tsx +++ b/packages/maskbook/src/extension/options-page/index.tsx @@ -1,4 +1,4 @@ -import '../../social-network-adaptor' +import '../../social-network-adaptor/options-page' import '../../setup.ui' import { useState } from 'react' diff --git a/packages/maskbook/src/extension/popup-page/UI.tsx b/packages/maskbook/src/extension/popup-page/UI.tsx index 9a89d94a53a2..19d58fcc0d27 100644 --- a/packages/maskbook/src/extension/popup-page/UI.tsx +++ b/packages/maskbook/src/extension/popup-page/UI.tsx @@ -1,6 +1,3 @@ -import '../../social-network-adaptor/popup-page/index' -import '../../setup.ui' - import { useCallback, memo } from 'react' import { noop } from 'lodash-es' import { ThemeProvider, makeStyles, Theme, withStyles } from '@material-ui/core/styles' diff --git a/packages/maskbook/src/extension/popup-page/index.tsx b/packages/maskbook/src/extension/popup-page/index.tsx index 20fb61ec32d0..a9b8e611d88f 100644 --- a/packages/maskbook/src/extension/popup-page/index.tsx +++ b/packages/maskbook/src/extension/popup-page/index.tsx @@ -1,6 +1,6 @@ -import '../../setup.ui' -import { activateSocialNetworkUI } from '../../social-network' +import '../../social-network-adaptor/popup-page' +import { status } from '../../setup.ui' import { SSRRenderer } from '../../utils/SSRRenderer' import { Popup } from './UI' -activateSocialNetworkUI().then(() => SSRRenderer()) +status.then(() => SSRRenderer()) diff --git a/packages/maskbook/src/setup.ui.ts b/packages/maskbook/src/setup.ui.ts index c3c530ab895e..19b573d8a4c5 100644 --- a/packages/maskbook/src/setup.ui.ts +++ b/packages/maskbook/src/setup.ui.ts @@ -1,17 +1,6 @@ import './polyfill/index' -import './utils/debug/general' -import { definedSocialNetworkUIs, activateSocialNetworkUI } from './social-network/ui' -import './social-network-adaptor' -import { LiveSelector, Watcher, DOMProxy } from '@dimensiondev/holoflows-kit' -import { enhanceTypedMessageDebugger } from './protocols/typed-message/debugger' -if (typeof window === 'object') { - LiveSelector.enhanceDebugger() - Watcher.enhanceDebugger() - DOMProxy.enhanceDebugger() - enhanceTypedMessageDebugger() -} -Object.assign(globalThis, { - definedSocialNetworkUIs: definedSocialNetworkUIs, -}) -activateSocialNetworkUI() +// Start SNS adaptor +import './social-network-adaptor' +import { activateSocialNetworkUI } from './social-network/define' +export const status = activateSocialNetworkUI() diff --git a/packages/maskbook/src/social-network-adaptor/index.ts b/packages/maskbook/src/social-network-adaptor/index.ts index 91a44fe5e98f..ac56df7b93e3 100644 --- a/packages/maskbook/src/social-network-adaptor/index.ts +++ b/packages/maskbook/src/social-network-adaptor/index.ts @@ -1,6 +1,3 @@ import './facebook.com' import './twitter.com' import './instagram.com' - -import './options-page' -import './popup-page' diff --git a/packages/maskbook/src/social-network/define.ts b/packages/maskbook/src/social-network/define.ts new file mode 100644 index 000000000000..ff0e0ffac3b0 --- /dev/null +++ b/packages/maskbook/src/social-network/define.ts @@ -0,0 +1,27 @@ +import type { SocialNetworkUI } from '.' +import type { SocialNetworkWorker } from '.' + +const definedSocialNetworkUIsLocal = new Map() +export const definedSocialNetworkUIs: ReadonlyMap = + definedSocialNetworkUIsLocal + +export function activateSocialNetworkUI() { + const ui_deferred = [...definedSocialNetworkUIs.values()].find((x) => x.shouldActivate(location)) + if (!ui_deferred) return Promise.resolve(false) + return import('./ui').then((x) => x.activateSocialNetworkUIInner(ui_deferred)).then(() => true) +} +export function defineSocialNetworkUI(UI: SocialNetworkUI.DeferredDefinition) { + if (UI.notReadyForProduction) { + if (process.env.build === 'stable' && process.env.NODE_ENV === 'production') return UI + } + definedSocialNetworkUIsLocal.set(UI.networkIdentifier, UI) + return UI +} + +export const definedSocialNetworkWorkers = new Set() +export function defineSocialNetworkWorker(worker: SocialNetworkWorker.DeferredDefinition) { + if (worker.notReadyForProduction) { + if (process.env.build === 'stable' && process.env.NODE_ENV === 'production') return + } + definedSocialNetworkWorkers.add(worker) +} diff --git a/packages/maskbook/src/social-network/index.ts b/packages/maskbook/src/social-network/index.ts index 1c0016425d5b..06b102fd72e8 100644 --- a/packages/maskbook/src/social-network/index.ts +++ b/packages/maskbook/src/social-network/index.ts @@ -1,4 +1,5 @@ export * from './types' export * from './utils' export * from './ui' +export * from './define' export * from './worker' diff --git a/packages/maskbook/src/social-network/ui.ts b/packages/maskbook/src/social-network/ui.ts index a914922a68da..ef1103e27597 100644 --- a/packages/maskbook/src/social-network/ui.ts +++ b/packages/maskbook/src/social-network/ui.ts @@ -1,3 +1,5 @@ +import '../utils/debug/general' +import '../utils/debug/ui' import Services from '../extension/service' import { untilDomLoaded } from '../utils/dom' import { Flags } from '../utils/flags' @@ -12,10 +14,8 @@ import { Environment, assertNotEnvironment } from '@dimensiondev/holoflows-kit' import { startPluginSNSAdaptor } from '@dimensiondev/mask-plugin-infra' import { getCurrentSNSNetwork } from '../social-network-adaptor/utils' import { createPluginHost } from '../plugin-infra/host' +import { definedSocialNetworkUIs } from './define' -const definedSocialNetworkUIsLocal = new Map() -export const definedSocialNetworkUIs: ReadonlyMap = - definedSocialNetworkUIsLocal const definedSocialNetworkUIsResolved = new Map() export let activatedSocialNetworkUI: SocialNetworkUI.Definition = { automation: {}, @@ -38,10 +38,8 @@ export let activatedSocialNetworkUI: SocialNetworkUI.Definition = { } export let globalUIState: Readonly = {} as any -export async function activateSocialNetworkUI(): Promise { +export async function activateSocialNetworkUIInner(ui_deferred: SocialNetworkUI.DeferredDefinition): Promise { assertNotEnvironment(Environment.ManifestBackground) - const ui_deferred = [...definedSocialNetworkUIs.values()].find((x) => x.shouldActivate(location)) - if (!ui_deferred) return console.log('Activating provider', ui_deferred.networkIdentifier) const ui = (activatedSocialNetworkUI = await loadSocialNetworkUI(ui_deferred.networkIdentifier)) @@ -57,7 +55,7 @@ export async function activateSocialNetworkUI(): Promise { abort.abort() await delay(200) definedSocialNetworkUIsResolved.set(ui_deferred.networkIdentifier, newDefinition) - activateSocialNetworkUI() + activateSocialNetworkUIInner(ui_deferred) }) } await untilDomLoaded() @@ -164,11 +162,3 @@ export async function loadSocialNetworkUI(identifier: string): Promise() export const definedSocialNetworkWorkersResolved = new Set() -export function defineSocialNetworkWorker(worker: SocialNetworkWorker.DeferredDefinition) { - if (worker.notReadyForProduction) { - if (process.env.build === 'stable' && process.env.NODE_ENV === 'production') return - } - definedSocialNetworkWorkers.add(worker) -} - async function activateNetworkWorker(id: string): Promise { if (!isEnvironment(Environment.ManifestBackground)) { throw new TypeError() diff --git a/packages/maskbook/src/utils/debug/ui.ts b/packages/maskbook/src/utils/debug/ui.ts new file mode 100644 index 000000000000..e3b33c58b8ab --- /dev/null +++ b/packages/maskbook/src/utils/debug/ui.ts @@ -0,0 +1,9 @@ +import { LiveSelector, Watcher, DOMProxy } from '@dimensiondev/holoflows-kit' +import { enhanceTypedMessageDebugger } from '../../protocols/typed-message/debugger' + +if (typeof window === 'object') { + LiveSelector.enhanceDebugger() + Watcher.enhanceDebugger() + DOMProxy.enhanceDebugger() + enhanceTypedMessageDebugger() +} diff --git a/packages/maskbook/src/utils/native-rpc/Web.ts b/packages/maskbook/src/utils/native-rpc/Web.ts index 085ee5bce3c8..8777e67cfa35 100644 --- a/packages/maskbook/src/utils/native-rpc/Web.ts +++ b/packages/maskbook/src/utils/native-rpc/Web.ts @@ -1,6 +1,6 @@ import type { WebviewAPIs } from './types' import Services from '../../extension/service' -import { definedSocialNetworkWorkers } from '../../social-network/worker' +import { definedSocialNetworkWorkers } from '../../social-network/define' import { launchPageSettings } from '../../settings/settings' import stringify from 'json-stable-stringify' import { unreachable } from '../utils'