Skip to content

Commit

Permalink
feat: capture Electron IPC messages for opensumi devtools (#1583)
Browse files Browse the repository at this point in the history
* feat: capture Electron IPC messages for opensumi devtools

* refactor: rename rpc capture function

* chore: restore tools/electron/src/main/index.ts

* refactor: use capitalized RPC/IPC rather Ipc/Rpc in variable names

* feat: provide configuration to enable or disable devtools support

* feat: toggle devtools support in browser-preload/index.js

* feat: toggle devtools support in chrome-devtools.contribution.ts

* chore: use isUndefined() to test if a variable is not defined

* refactor: do not capture ipcMain messages

But capture the return values of ipcRenderer.sendSync
and ipcRenderer.invoke
  • Loading branch information
tyn1998 authored Sep 26, 2022
1 parent 880e0d8 commit 0469c5f
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 25 deletions.
42 changes: 24 additions & 18 deletions packages/addons/src/browser/chrome-devtools.contribution.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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;

Expand All @@ -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();
}
}
}
Expand All @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions packages/connection/src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
13 changes: 9 additions & 4 deletions packages/core-browser/src/bootstrap/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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();
Expand All @@ -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;
}
Expand Down
5 changes: 5 additions & 0 deletions packages/core-browser/src/react-providers/config-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ export interface AppConfig {
* 这将影响 Terminal 与 Extension 模块与子进程的连接方式
*/
isRemote?: boolean;
/**
* 是否开启对 OpenSumi DevTools 的支持
* 默认值为 false
*/
devtools?: boolean;
}

export const ConfigContext = React.createContext<AppConfig>({
Expand Down
63 changes: 62 additions & 1 deletion packages/core-electron-main/browser-preload/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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 });

Expand Down
6 changes: 6 additions & 0 deletions packages/core-electron-main/src/bootstrap/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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() {
Expand Down
6 changes: 6 additions & 0 deletions packages/core-electron-main/src/bootstrap/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ export interface ElectronAppConfig {
* 如有外部 injector,优先使用外部
*/
injector?: Injector;

/**
* 是否开启对 OpenSumi DevTools 的支持
* 默认值为 false
*/
devtools?: boolean;
}

export const ElectronAppConfig = Symbol('ElectronAppConfig');
Expand Down
2 changes: 2 additions & 0 deletions packages/core-electron-main/src/bootstrap/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
...this.appConfig.overrideBrowserOptions,
...options,
});

if (options) {
if (options.extensionDir) {
this.extensionDir = options.extensionDir;
Expand Down Expand Up @@ -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,
});
}
};
Expand Down
1 change: 1 addition & 0 deletions packages/startup/entry/web/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,5 @@ renderApp({
bottom: '@opensumi/ide-terminal-next',
right: '',
},
devtools: true, // 开启 core-browser 对 OpenSumi DevTools 的支持,默认为关闭
});
1 change: 1 addition & 0 deletions tools/electron/src/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,5 @@ renderApp({
},
modules: [...CommonBrowserModules, ElectronBasicModule],
layoutConfig: customLayoutConfig,
devtools: true, // 开启 core-browser 对 OpenSumi DevTools 的支持,默认为关闭
});
1 change: 1 addition & 0 deletions tools/electron/src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const electronApp = new ElectronMainApp({
extensionDir: getExtensionDir(),
extensionCandidate: [],
overrideWebPreferences: {},
devtools: true, // 开启 core-electron-main 对 OpenSumi DevTools 的支持,默认为关闭
});

electronApp.init().then(() => {
Expand Down

0 comments on commit 0469c5f

Please sign in to comment.