Skip to content

Commit 0469c5f

Browse files
authored
feat: capture Electron IPC messages for opensumi devtools (#1583)
* 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
1 parent 880e0d8 commit 0469c5f

File tree

11 files changed

+119
-25
lines changed

11 files changed

+119
-25
lines changed

packages/addons/src/browser/chrome-devtools.contribution.ts

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Autowired } from '@opensumi/di';
2-
import { ClientAppContribution } from '@opensumi/ide-core-browser';
2+
import { AppConfig, ClientAppContribution } from '@opensumi/ide-core-browser';
33
import { Domain } from '@opensumi/ide-core-common/lib/di-helper';
44

55
import { ConnectionRTTBrowserServiceToken, ConnectionRTTBrowserService } from './connection-rtt-service';
@@ -15,6 +15,9 @@ enum DevtoolsCommand {
1515

1616
@Domain(ClientAppContribution)
1717
export class ChromeDevtoolsContribution implements ClientAppContribution {
18+
@Autowired(AppConfig)
19+
private readonly appConfig: AppConfig;
20+
1821
@Autowired(ConnectionRTTBrowserServiceToken)
1922
protected readonly rttService: ConnectionRTTBrowserService;
2023

@@ -23,25 +26,28 @@ export class ChromeDevtoolsContribution implements ClientAppContribution {
2326
static INTERVAL = 1000;
2427

2528
initialize() {
26-
// receive notification from opensumi devtools by custom event
27-
window.addEventListener(DevtoolsEvent.Latency, (event) => {
28-
const { command } = event.detail;
29-
if (command === DevtoolsCommand.Start) {
29+
// only runs when devtools supoprt is enabled
30+
if (this.appConfig.devtools) {
31+
// receive notification from opensumi devtools by custom event
32+
window.addEventListener(DevtoolsEvent.Latency, (event) => {
33+
const { command } = event.detail;
34+
if (command === DevtoolsCommand.Start) {
35+
if (!this.interval) {
36+
this.startRTTInterval();
37+
}
38+
} else if (command === DevtoolsCommand.Stop) {
39+
if (this.interval) {
40+
global.clearInterval(this.interval);
41+
this.interval = undefined;
42+
}
43+
}
44+
});
45+
46+
// if opensumi devtools has started capturing before this contribution point is registered
47+
if (window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__?.captureRPC) {
3048
if (!this.interval) {
3149
this.startRTTInterval();
3250
}
33-
} else if (command === DevtoolsCommand.Stop) {
34-
if (this.interval) {
35-
global.clearInterval(this.interval);
36-
this.interval = undefined;
37-
}
38-
}
39-
});
40-
41-
// if opensumi devtools has started capturing before this contribution point is registered
42-
if (window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__?.capture) {
43-
if (!this.interval) {
44-
this.startRTTInterval();
4551
}
4652
}
4753
}
@@ -52,7 +58,7 @@ export class ChromeDevtoolsContribution implements ClientAppContribution {
5258
await this.rttService.measure();
5359
const rtt = Date.now() - start;
5460
// "if" below is to prevent setting latency after stoping capturing
55-
if (window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__.capture) {
61+
if (window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__.captureRPC) {
5662
window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__.latency = rtt;
5763
}
5864
}, ChromeDevtoolsContribution.INTERVAL);

packages/connection/src/common/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ export function parse(input: string, reviver?: (this: any, key: string, value: a
3737
}
3838

3939
export function getCapturer() {
40-
if (typeof window !== 'undefined' && window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__?.capture) {
41-
return window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__.capture;
40+
if (typeof window !== 'undefined' && window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__?.captureRPC) {
41+
return window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__.captureRPC;
4242
}
4343
return;
4444
}

packages/core-browser/src/bootstrap/app.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,6 @@ export class ClientApp implements IClientApp, IDisposable {
146146
stateService: ClientAppStateService;
147147

148148
constructor(opts: IClientAppOpts) {
149-
// set a global so the opensumi devtools can identify that
150-
// the current page is powered by opensumi core
151-
window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__ = {};
152-
153149
const {
154150
modules,
155151
contributions,
@@ -161,8 +157,10 @@ export class ClientApp implements IClientApp, IDisposable {
161157
editorBackgroundImage,
162158
defaultPreferences,
163159
allowSetDocumentTitleFollowWorkspaceDir = true,
160+
devtools = false, // if not set, disable devtools support as default
164161
...restOpts // rest part 为 AppConfig
165162
} = opts;
163+
166164
this.initEarlyPreference(opts.workspaceDir || '');
167165
setLanguageId(getPreferenceLanguageId(defaultPreferences));
168166
this.injector = opts.injector || new Injector();
@@ -188,8 +186,15 @@ export class ClientApp implements IClientApp, IDisposable {
188186
layoutConfig: opts.layoutConfig as LayoutConfig,
189187
editorBackgroundImage: opts.editorBackgroundImage || editorBackgroundImage,
190188
allowSetDocumentTitleFollowWorkspaceDir,
189+
devtools,
191190
};
192191

192+
if (devtools) {
193+
// set a global so the opensumi devtools can identify that
194+
// the current page is powered by opensumi core
195+
window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__ = {};
196+
}
197+
193198
if (this.config.isElectronRenderer && electronEnv.metadata?.extensionDevelopmentHost) {
194199
this.config.extensionDevelopmentHost = electronEnv.metadata.extensionDevelopmentHost;
195200
}

packages/core-browser/src/react-providers/config-provider.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ export interface AppConfig {
218218
* 这将影响 Terminal 与 Extension 模块与子进程的连接方式
219219
*/
220220
isRemote?: boolean;
221+
/**
222+
* 是否开启对 OpenSumi DevTools 的支持
223+
* 默认值为 false
224+
*/
225+
devtools?: boolean;
221226
}
222227

223228
export const ConfigContext = React.createContext<AppConfig>({

packages/core-electron-main/browser-preload/index.js

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,64 @@ const os = require('os');
33

44
const { ipcRenderer } = require('electron');
55

6+
// for generating requestId to pair request and response
7+
const uuid = () => Date.now().toString(36) + Math.random().toString(36).substr(2);
8+
9+
const initForDevtools = () => {
10+
const getCapturer = () => {
11+
if (window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__?.captureIPC) {
12+
return window.__OPENSUMI_DEVTOOLS_GLOBAL_HOOK__.captureIPC;
13+
}
14+
return;
15+
};
16+
17+
const capture = (message) => {
18+
const capturer = getCapturer();
19+
if (capturer !== undefined) {
20+
capturer(message);
21+
}
22+
};
23+
24+
// ipcRenderer.on
25+
const originalIpcRendererOn = ipcRenderer.on;
26+
ipcRenderer.on = (channel, handler) => {
27+
const proxyHandler = (event, ...args) => {
28+
capture({ ipcMethod: 'ipcRenderer.on', channel, args });
29+
handler(event, ...args);
30+
};
31+
return originalIpcRendererOn.call(ipcRenderer, channel, proxyHandler);
32+
};
33+
34+
// ipcRenderer.send
35+
const originalIpcRendererSend = ipcRenderer.send;
36+
ipcRenderer.send = (channel, ...args) => {
37+
capture({ ipcMethod: 'ipcRenderer.send', channel, args });
38+
return originalIpcRendererSend.call(ipcRenderer, channel, ...args);
39+
};
40+
41+
// ipcRenderer.sendSync
42+
const originalIpcRendererSendSync = ipcRenderer.sendSync;
43+
ipcRenderer.sendSync = (channel, ...args) => {
44+
const requestId = uuid();
45+
capture({ ipcMethod: 'ipcRenderer.sendSync', channel, requestId, args });
46+
const result = originalIpcRendererSendSync.call(ipcRenderer, channel, ...args);
47+
capture({ ipcMethod: 'ipcRenderer.sendSync', channel, requestId, result });
48+
return result;
49+
};
50+
51+
// ipcRenderer.invoke
52+
const originalIpcRendererInvoke = ipcRenderer.invoke;
53+
ipcRenderer.invoke = (channel, ...args) => {
54+
const requestId = uuid();
55+
capture({ ipcMethod: 'ipcRenderer.invoke', channel, requestId, args });
56+
const resultPromise = originalIpcRendererInvoke.call(ipcRenderer, channel, ...args);
57+
resultPromise.then((result) => {
58+
capture({ ipcMethod: 'ipcRenderer.invoke', channel, requestId, result });
59+
});
60+
return resultPromise;
61+
};
62+
};
63+
664
const electronEnv = {};
765

866
const urlParams = new URLSearchParams(decodeURIComponent(window.location.search));
@@ -32,7 +90,10 @@ electronEnv.currentWebContentsId = webContentsId;
3290
electronEnv.onigWasmPath = require.resolve('vscode-oniguruma/release/onig.wasm');
3391

3492
const metaData = JSON.parse(ipcRenderer.sendSync('window-metadata', electronEnv.currentWindowId));
35-
93+
// if devtools support enabled
94+
if (metaData.devtools) {
95+
initForDevtools();
96+
}
3697
electronEnv.metadata = metaData;
3798
process.env = Object.assign({}, process.env, metaData.env, { WORKSPACE_DIR: metaData.workspace });
3899

packages/core-electron-main/src/bootstrap/app.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
IEventBus,
1010
EventBusImpl,
1111
asExtensionCandidate,
12+
isUndefined,
1213
} from '@opensumi/ide-core-common';
1314
import { IElectronMainLifeCycleService } from '@opensumi/ide-core-common/lib/electron';
1415
import { argv } from '@opensumi/ide-core-common/lib/node/cli';
@@ -100,6 +101,11 @@ export class ElectronMainApp {
100101
this.onBeforeReadyContribution();
101102
this.registerMainApis();
102103
this.registerURLHandlers();
104+
105+
// if not set, disable devtools support as default
106+
if (isUndefined(this.config.devtools)) {
107+
this.config.devtools = false;
108+
}
103109
}
104110

105111
async init() {

packages/core-electron-main/src/bootstrap/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ export interface ElectronAppConfig {
8080
* 如有外部 injector,优先使用外部
8181
*/
8282
injector?: Injector;
83+
84+
/**
85+
* 是否开启对 OpenSumi DevTools 的支持
86+
* 默认值为 false
87+
*/
88+
devtools?: boolean;
8389
}
8490

8591
export const ElectronAppConfig = Symbol('ElectronAppConfig');

packages/core-electron-main/src/bootstrap/window.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
9494
...this.appConfig.overrideBrowserOptions,
9595
...options,
9696
});
97+
9798
if (options) {
9899
if (options.extensionDir) {
99100
this.extensionDir = options.extensionDir;
@@ -131,6 +132,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
131132
workerHostEntry: this.appConfig.extensionWorkerEntry,
132133
extensionDevelopmentHost: this.appConfig.extensionDevelopmentHost,
133134
appPath: app.getAppPath(),
135+
devtools: this.appConfig.devtools,
134136
});
135137
}
136138
};

packages/startup/entry/web/app.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,5 @@ renderApp({
4747
bottom: '@opensumi/ide-terminal-next',
4848
right: '',
4949
},
50+
devtools: true, // 开启 core-browser 对 OpenSumi DevTools 的支持,默认为关闭
5051
});

tools/electron/src/browser/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,5 @@ renderApp({
103103
},
104104
modules: [...CommonBrowserModules, ElectronBasicModule],
105105
layoutConfig: customLayoutConfig,
106+
devtools: true, // 开启 core-browser 对 OpenSumi DevTools 的支持,默认为关闭
106107
});

0 commit comments

Comments
 (0)