diff --git a/packages/addons/src/browser/chrome-devtools.contribution.ts b/packages/addons/src/browser/chrome-devtools.contribution.ts index fb43193bea..e5ab6f90c9 100644 --- a/packages/addons/src/browser/chrome-devtools.contribution.ts +++ b/packages/addons/src/browser/chrome-devtools.contribution.ts @@ -1,5 +1,5 @@ import { Autowired } from '@opensumi/di'; -import { ClientAppContribution } from '@opensumi/ide-core-browser'; +import { AppConfig, ClientAppContribution } from '@opensumi/ide-core-browser'; import { Domain } from '@opensumi/ide-core-common/lib/di-helper'; import { ConnectionRTTBrowserServiceToken, ConnectionRTTBrowserService } from './connection-rtt-service'; @@ -15,6 +15,9 @@ enum DevtoolsCommand { @Domain(ClientAppContribution) export class ChromeDevtoolsContribution implements ClientAppContribution { + @Autowired(AppConfig) + private readonly appConfig: AppConfig; + @Autowired(ConnectionRTTBrowserServiceToken) protected readonly rttService: ConnectionRTTBrowserService; @@ -23,25 +26,28 @@ export class ChromeDevtoolsContribution implements ClientAppContribution { static INTERVAL = 1000; initialize() { - // receive notification from opensumi devtools by custom event - window.addEventListener(DevtoolsEvent.Latency, (event) => { - const { command } = event.detail; - if (command === DevtoolsCommand.Start) { + // only runs when devtools supoprt is enabled + if (this.appConfig.devtools) { + // receive notification from opensumi devtools by custom event + window.addEventListener(DevtoolsEvent.Latency, (event) => { + const { command } = event.detail; + if (command === DevtoolsCommand.Start) { + if (!this.interval) { + this.startRTTInterval(); + } + } else if (command === DevtoolsCommand.Stop) { + if (this.interval) { + global.clearInterval(this.interval); + this.interval = undefined; + } + } + }); + + // if opensumi devtools has started capturing before this contribution point is registered + if (window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__?.captureRPC) { if (!this.interval) { this.startRTTInterval(); } - } else if (command === DevtoolsCommand.Stop) { - if (this.interval) { - global.clearInterval(this.interval); - this.interval = undefined; - } - } - }); - - // if opensumi devtools has started capturing before this contribution point is registered - if (window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__?.capture) { - if (!this.interval) { - this.startRTTInterval(); } } } @@ -52,7 +58,7 @@ export class ChromeDevtoolsContribution implements ClientAppContribution { await this.rttService.measure(); const rtt = Date.now() - start; // "if" below is to prevent setting latency after stoping capturing - if (window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__.capture) { + if (window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__.captureRPC) { window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__.latency = rtt; } }, ChromeDevtoolsContribution.INTERVAL); diff --git a/packages/connection/src/common/utils.ts b/packages/connection/src/common/utils.ts index 321715628e..cdc3dbe7ab 100644 --- a/packages/connection/src/common/utils.ts +++ b/packages/connection/src/common/utils.ts @@ -37,8 +37,8 @@ export function parse(input: string, reviver?: (this: any, key: string, value: a } export function getCapturer() { - if (typeof window !== 'undefined' && window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__?.capture) { - return window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__.capture; + if (typeof window !== 'undefined' && window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__?.captureRPC) { + return window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__.captureRPC; } return; } diff --git a/packages/core-browser/src/bootstrap/app.ts b/packages/core-browser/src/bootstrap/app.ts index 8a3589977a..67c961848b 100644 --- a/packages/core-browser/src/bootstrap/app.ts +++ b/packages/core-browser/src/bootstrap/app.ts @@ -146,10 +146,6 @@ export class ClientApp implements IClientApp, IDisposable { stateService: ClientAppStateService; constructor(opts: IClientAppOpts) { - // set a global so the opensumi devtools can identify that - // the current page is powered by opensumi core - window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__ = {}; - const { modules, contributions, @@ -161,8 +157,10 @@ export class ClientApp implements IClientApp, IDisposable { editorBackgroundImage, defaultPreferences, allowSetDocumentTitleFollowWorkspaceDir = true, + devtools = false, // if not set, disable devtools support as default ...restOpts // rest part 为 AppConfig } = opts; + this.initEarlyPreference(opts.workspaceDir || ''); setLanguageId(getPreferenceLanguageId(defaultPreferences)); this.injector = opts.injector || new Injector(); @@ -188,8 +186,15 @@ export class ClientApp implements IClientApp, IDisposable { layoutConfig: opts.layoutConfig as LayoutConfig, editorBackgroundImage: opts.editorBackgroundImage || editorBackgroundImage, allowSetDocumentTitleFollowWorkspaceDir, + devtools, }; + if (devtools) { + // set a global so the opensumi devtools can identify that + // the current page is powered by opensumi core + window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__ = {}; + } + if (this.config.isElectronRenderer && electronEnv.metadata?.extensionDevelopmentHost) { this.config.extensionDevelopmentHost = electronEnv.metadata.extensionDevelopmentHost; } diff --git a/packages/core-browser/src/react-providers/config-provider.tsx b/packages/core-browser/src/react-providers/config-provider.tsx index 2a949db2fc..f3080ae745 100644 --- a/packages/core-browser/src/react-providers/config-provider.tsx +++ b/packages/core-browser/src/react-providers/config-provider.tsx @@ -218,6 +218,11 @@ export interface AppConfig { * 这将影响 Terminal 与 Extension 模块与子进程的连接方式 */ isRemote?: boolean; + /** + * 是否开启对 OpenSumi DevTools 的支持 + * 默认值为 false + */ + devtools?: boolean; } export const ConfigContext = React.createContext({ diff --git a/packages/core-electron-main/browser-preload/index.js b/packages/core-electron-main/browser-preload/index.js index 8557797077..915a4df853 100644 --- a/packages/core-electron-main/browser-preload/index.js +++ b/packages/core-electron-main/browser-preload/index.js @@ -3,6 +3,64 @@ const os = require('os'); const { ipcRenderer } = require('electron'); +// for generating requestId to pair request and response +const uuid = () => Date.now().toString(36) + Math.random().toString(36).substr(2); + +const initForDevtools = () => { + const getCapturer = () => { + if (window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__?.captureIPC) { + return window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__.captureIPC; + } + return; + }; + + const capture = (message) => { + const capturer = getCapturer(); + if (capturer !== undefined) { + capturer(message); + } + }; + + // ipcRenderer.on + const originalIpcRendererOn = ipcRenderer.on; + ipcRenderer.on = (channel, handler) => { + const proxyHandler = (event, ...args) => { + capture({ ipcMethod: 'ipcRenderer.on', channel, args }); + handler(event, ...args); + }; + return originalIpcRendererOn.call(ipcRenderer, channel, proxyHandler); + }; + + // ipcRenderer.send + const originalIpcRendererSend = ipcRenderer.send; + ipcRenderer.send = (channel, ...args) => { + capture({ ipcMethod: 'ipcRenderer.send', channel, args }); + return originalIpcRendererSend.call(ipcRenderer, channel, ...args); + }; + + // ipcRenderer.sendSync + const originalIpcRendererSendSync = ipcRenderer.sendSync; + ipcRenderer.sendSync = (channel, ...args) => { + const requestId = uuid(); + capture({ ipcMethod: 'ipcRenderer.sendSync', channel, requestId, args }); + const result = originalIpcRendererSendSync.call(ipcRenderer, channel, ...args); + capture({ ipcMethod: 'ipcRenderer.sendSync', channel, requestId, result }); + return result; + }; + + // ipcRenderer.invoke + const originalIpcRendererInvoke = ipcRenderer.invoke; + ipcRenderer.invoke = (channel, ...args) => { + const requestId = uuid(); + capture({ ipcMethod: 'ipcRenderer.invoke', channel, requestId, args }); + const resultPromise = originalIpcRendererInvoke.call(ipcRenderer, channel, ...args); + resultPromise.then((result) => { + capture({ ipcMethod: 'ipcRenderer.invoke', channel, requestId, result }); + }); + return resultPromise; + }; +}; + const electronEnv = {}; const urlParams = new URLSearchParams(decodeURIComponent(window.location.search)); @@ -32,7 +90,10 @@ electronEnv.currentWebContentsId = webContentsId; electronEnv.onigWasmPath = require.resolve('vscode-oniguruma/release/onig.wasm'); const metaData = JSON.parse(ipcRenderer.sendSync('window-metadata', electronEnv.currentWindowId)); - +// if devtools support enabled +if (metaData.devtools) { + initForDevtools(); +} electronEnv.metadata = metaData; process.env = Object.assign({}, process.env, metaData.env, { WORKSPACE_DIR: metaData.workspace }); diff --git a/packages/core-electron-main/src/bootstrap/app.ts b/packages/core-electron-main/src/bootstrap/app.ts index 17eac85c92..597f72dfb2 100644 --- a/packages/core-electron-main/src/bootstrap/app.ts +++ b/packages/core-electron-main/src/bootstrap/app.ts @@ -9,6 +9,7 @@ import { IEventBus, EventBusImpl, asExtensionCandidate, + isUndefined, } from '@opensumi/ide-core-common'; import { IElectronMainLifeCycleService } from '@opensumi/ide-core-common/lib/electron'; import { argv } from '@opensumi/ide-core-common/lib/node/cli'; @@ -100,6 +101,11 @@ export class ElectronMainApp { this.onBeforeReadyContribution(); this.registerMainApis(); this.registerURLHandlers(); + + // if not set, disable devtools support as default + if (isUndefined(this.config.devtools)) { + this.config.devtools = false; + } } async init() { diff --git a/packages/core-electron-main/src/bootstrap/types.ts b/packages/core-electron-main/src/bootstrap/types.ts index b60ef59955..0f2b81deed 100644 --- a/packages/core-electron-main/src/bootstrap/types.ts +++ b/packages/core-electron-main/src/bootstrap/types.ts @@ -80,6 +80,12 @@ export interface ElectronAppConfig { * 如有外部 injector,优先使用外部 */ injector?: Injector; + + /** + * 是否开启对 OpenSumi DevTools 的支持 + * 默认值为 false + */ + devtools?: boolean; } export const ElectronAppConfig = Symbol('ElectronAppConfig'); diff --git a/packages/core-electron-main/src/bootstrap/window.ts b/packages/core-electron-main/src/bootstrap/window.ts index 05b7308114..bdb1e00648 100644 --- a/packages/core-electron-main/src/bootstrap/window.ts +++ b/packages/core-electron-main/src/bootstrap/window.ts @@ -94,6 +94,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { ...this.appConfig.overrideBrowserOptions, ...options, }); + if (options) { if (options.extensionDir) { this.extensionDir = options.extensionDir; @@ -131,6 +132,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { workerHostEntry: this.appConfig.extensionWorkerEntry, extensionDevelopmentHost: this.appConfig.extensionDevelopmentHost, appPath: app.getAppPath(), + devtools: this.appConfig.devtools, }); } }; diff --git a/packages/startup/entry/web/app.tsx b/packages/startup/entry/web/app.tsx index c84f85f850..3b4bde7a5f 100644 --- a/packages/startup/entry/web/app.tsx +++ b/packages/startup/entry/web/app.tsx @@ -46,4 +46,5 @@ renderApp({ bottom: '@opensumi/ide-terminal-next', right: '', }, + devtools: true, // 开启 core-browser 对 OpenSumi DevTools 的支持,默认为关闭 }); diff --git a/tools/electron/src/browser/index.ts b/tools/electron/src/browser/index.ts index c132885d61..33ab499f49 100644 --- a/tools/electron/src/browser/index.ts +++ b/tools/electron/src/browser/index.ts @@ -103,4 +103,5 @@ renderApp({ }, modules: [...CommonBrowserModules, ElectronBasicModule], layoutConfig: customLayoutConfig, + devtools: true, // 开启 core-browser 对 OpenSumi DevTools 的支持,默认为关闭 }); diff --git a/tools/electron/src/main/index.ts b/tools/electron/src/main/index.ts index 537f95710f..1457d4cc81 100644 --- a/tools/electron/src/main/index.ts +++ b/tools/electron/src/main/index.ts @@ -27,6 +27,7 @@ const electronApp = new ElectronMainApp({ extensionDir: getExtensionDir(), extensionCandidate: [], overrideWebPreferences: {}, + devtools: true, // 开启 core-electron-main 对 OpenSumi DevTools 的支持,默认为关闭 }); electronApp.init().then(() => {