@opensumi/ide-webview 2.21.13 → 2.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/abstract-webview.js +3 -3
- package/lib/browser/abstract-webview.js.map +1 -1
- package/lib/browser/contribution.js.map +1 -1
- package/lib/browser/electron-webview-webview.js.map +1 -1
- package/lib/browser/iframe-webview.js.map +1 -1
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/plain-webview.js +3 -3
- package/lib/browser/plain-webview.js.map +1 -1
- package/lib/browser/webview-window.js.map +1 -1
- package/lib/browser/webview.service.d.ts +1 -1
- package/lib/browser/webview.service.d.ts.map +1 -1
- package/lib/browser/webview.service.js +38 -38
- package/lib/browser/webview.service.js.map +1 -1
- package/lib/electron-main/index.js.map +1 -1
- package/package.json +13 -12
- package/src/browser/abstract-webview.ts +231 -0
- package/src/browser/contribution.ts +80 -0
- package/src/browser/editor-webview.tsx +207 -0
- package/src/browser/electron-webview-webview.ts +156 -0
- package/src/browser/iframe-webview.ts +140 -0
- package/src/browser/index.ts +19 -0
- package/src/browser/plain-webview.ts +284 -0
- package/src/browser/types.ts +274 -0
- package/src/browser/webview-window.ts +130 -0
- package/src/browser/webview.service.ts +484 -0
- package/src/common/index.ts +1 -0
- package/src/electron-main/index.ts +31 -0
- package/src/electron-webview/host-channel.ts +64 -0
- package/src/electron-webview/host-preload.js +2 -0
- package/src/electron-webview/plain-preload.js +55 -0
- package/src/index.ts +1 -0
- package/src/webview-host/common.ts +108 -0
- package/src/webview-host/web-preload.ts +78 -0
- package/src/webview-host/webview-manager.ts +368 -0
- package/src/webview-host/webview.html +11 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Injectable, Autowired } from '@opensumi/di';
|
|
2
|
+
import { Emitter, Event, Disposable, URI } from '@opensumi/ide-core-browser';
|
|
3
|
+
import { electronEnv } from '@opensumi/ide-core-browser/lib/utils/electron';
|
|
4
|
+
import { IElectronMainUIService, IElectronPlainWebviewWindowOptions } from '@opensumi/ide-core-common/lib/electron';
|
|
5
|
+
|
|
6
|
+
import { IPlainWebviewWindow } from './types';
|
|
7
|
+
|
|
8
|
+
@Injectable({ multiple: true })
|
|
9
|
+
export class ElectronPlainWebviewWindow extends Disposable implements IPlainWebviewWindow {
|
|
10
|
+
@Autowired(IElectronMainUIService)
|
|
11
|
+
electronMainUIService: IElectronMainUIService;
|
|
12
|
+
|
|
13
|
+
private _windowId: number;
|
|
14
|
+
|
|
15
|
+
private _webContentsId: number;
|
|
16
|
+
|
|
17
|
+
private _ready: Promise<void>;
|
|
18
|
+
|
|
19
|
+
private _closed = false;
|
|
20
|
+
|
|
21
|
+
constructor(options?: IElectronPlainWebviewWindowOptions, env: { [key: string]: string } = {}) {
|
|
22
|
+
super();
|
|
23
|
+
this._ready = this.electronMainUIService
|
|
24
|
+
.createBrowserWindow({
|
|
25
|
+
...options,
|
|
26
|
+
webPreferences: {
|
|
27
|
+
preload: new URI(electronEnv.plainWebviewPreload).codeUri.fsPath,
|
|
28
|
+
additionalArguments: [
|
|
29
|
+
'--additionalEnv=' + JSON.stringify(env),
|
|
30
|
+
'--parentWindowWebContentsId=' + electronEnv.currentWebContentsId,
|
|
31
|
+
],
|
|
32
|
+
...options?.webPreferences,
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
.then(async (id) => {
|
|
36
|
+
this._windowId = id;
|
|
37
|
+
this._webContentsId = await this.electronMainUIService.getWebContentsId(this._windowId);
|
|
38
|
+
})
|
|
39
|
+
.then(() => {
|
|
40
|
+
const listener = (event: any, { from, message }: { from: number; message: any }) => {
|
|
41
|
+
if (from === this._windowId) {
|
|
42
|
+
this._onMessage.fire(message);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
electronEnv.ipcRenderer.on('cross-window-webview-message', listener);
|
|
46
|
+
this.addDispose({
|
|
47
|
+
dispose: () => {
|
|
48
|
+
electronEnv.ipcRenderer.removeListener('cross-window-webview-message', listener);
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
this.addDispose(
|
|
52
|
+
this.electronMainUIService.on('windowClosed', (windowId) => {
|
|
53
|
+
if (windowId === this._windowId) {
|
|
54
|
+
this._closed = true;
|
|
55
|
+
this._onClosed.fire();
|
|
56
|
+
this.dispose();
|
|
57
|
+
}
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
this.addDispose(this._onMessage);
|
|
62
|
+
this.addDispose(this._onClosed);
|
|
63
|
+
this.addDispose({
|
|
64
|
+
dispose: () => {
|
|
65
|
+
this._close();
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
_onMessage: Emitter<any> = new Emitter<any>();
|
|
71
|
+
onMessage: Event<any> = this._onMessage.event;
|
|
72
|
+
|
|
73
|
+
_onClosed: Emitter<void> = new Emitter<void>();
|
|
74
|
+
onClosed: Event<void> = this._onClosed.event;
|
|
75
|
+
|
|
76
|
+
private _url: string;
|
|
77
|
+
|
|
78
|
+
get ready() {
|
|
79
|
+
return this._ready;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get url() {
|
|
83
|
+
return this._url;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async loadURL(url: string): Promise<void> {
|
|
87
|
+
await this._ready;
|
|
88
|
+
this._url = url;
|
|
89
|
+
return this.electronMainUIService.browserWindowLoadUrl(this._windowId, url);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async show() {
|
|
93
|
+
return this.electronMainUIService.showBrowserWindow(this._windowId);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async hide() {
|
|
97
|
+
return this.electronMainUIService.hideBrowserWindow(this._windowId);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async setSize(size: { width: number; height: number }) {
|
|
101
|
+
return this.electronMainUIService.setSize(this._windowId, size);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async setAlwaysOnTop(flag: boolean) {
|
|
105
|
+
return this.electronMainUIService.setAlwaysOnTop(this._windowId, flag);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async postMessage(message: any) {
|
|
109
|
+
return this.electronMainUIService.postMessageToBrowserWindow(this._windowId, 'webview-message', message);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
_close() {
|
|
113
|
+
if (this._closed) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
return this.electronMainUIService.closeBrowserWindow(this._windowId);
|
|
118
|
+
} catch (e) {
|
|
119
|
+
// ignore
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
get windowId() {
|
|
124
|
+
return this._windowId;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
get webContentsId() {
|
|
128
|
+
return this._webContentsId;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import { Injectable, Injector, Autowired, INJECTOR_TOKEN } from '@opensumi/di';
|
|
2
|
+
import {
|
|
3
|
+
getDebugLogger,
|
|
4
|
+
localize,
|
|
5
|
+
URI,
|
|
6
|
+
IEventBus,
|
|
7
|
+
Disposable,
|
|
8
|
+
MaybeNull,
|
|
9
|
+
ILogger,
|
|
10
|
+
arrays,
|
|
11
|
+
Emitter,
|
|
12
|
+
StorageProvider,
|
|
13
|
+
IStorage,
|
|
14
|
+
STORAGE_SCHEMA,
|
|
15
|
+
AppConfig,
|
|
16
|
+
} from '@opensumi/ide-core-browser';
|
|
17
|
+
import { IEditorGroup, WorkbenchEditorService, ResourceNeedUpdateEvent, IResource } from '@opensumi/ide-editor';
|
|
18
|
+
import {
|
|
19
|
+
EditorComponentRegistry,
|
|
20
|
+
EditorComponentRenderMode,
|
|
21
|
+
EditorPreferences,
|
|
22
|
+
EditorGroupChangeEvent,
|
|
23
|
+
EditorOpenType,
|
|
24
|
+
} from '@opensumi/ide-editor/lib/browser';
|
|
25
|
+
import { LIGHT, DARK, HIGH_CONTRAST_DARK, HIGH_CONTRAST_LIGHT, ITheme } from '@opensumi/ide-theme';
|
|
26
|
+
import { getColorRegistry } from '@opensumi/ide-theme/lib/common/color-registry';
|
|
27
|
+
|
|
28
|
+
import { EditorWebviewComponentView } from './editor-webview';
|
|
29
|
+
import { ElectronWebviewWebviewPanel } from './electron-webview-webview';
|
|
30
|
+
import { IFrameWebviewPanel } from './iframe-webview';
|
|
31
|
+
import { ElectronPlainWebview, IframePlainWebview } from './plain-webview';
|
|
32
|
+
import {
|
|
33
|
+
IWebviewService,
|
|
34
|
+
IPlainWebviewConstructionOptions,
|
|
35
|
+
IPlainWebview,
|
|
36
|
+
IWebview,
|
|
37
|
+
IWebviewContentOptions,
|
|
38
|
+
IWebviewThemeData,
|
|
39
|
+
IEditorWebviewComponent,
|
|
40
|
+
EDITOR_WEBVIEW_SCHEME,
|
|
41
|
+
IEditorWebviewMetaData,
|
|
42
|
+
IPlainWebviewComponentHandle,
|
|
43
|
+
IPlainWebviewWindow,
|
|
44
|
+
IWebviewReviver,
|
|
45
|
+
} from './types';
|
|
46
|
+
import { ElectronPlainWebviewWindow } from './webview-window';
|
|
47
|
+
|
|
48
|
+
const { addElement } = arrays;
|
|
49
|
+
|
|
50
|
+
@Injectable()
|
|
51
|
+
export class WebviewServiceImpl implements IWebviewService {
|
|
52
|
+
private webviewIdCount = 0;
|
|
53
|
+
|
|
54
|
+
private editorWebviewIdCount = 0;
|
|
55
|
+
|
|
56
|
+
public readonly editorWebviewComponents = new Map<string, EditorWebviewComponent<IWebview | IPlainWebview>>();
|
|
57
|
+
|
|
58
|
+
public readonly plainWebviewsComponents = new Map<string, IPlainWebviewComponentHandle>();
|
|
59
|
+
|
|
60
|
+
private readonly webviews = new Map<string, IWebview>();
|
|
61
|
+
|
|
62
|
+
@Autowired(INJECTOR_TOKEN)
|
|
63
|
+
private injector: Injector;
|
|
64
|
+
|
|
65
|
+
@Autowired(AppConfig)
|
|
66
|
+
private readonly appConfig: AppConfig;
|
|
67
|
+
|
|
68
|
+
@Autowired(EditorPreferences)
|
|
69
|
+
protected readonly editorPreferences: EditorPreferences;
|
|
70
|
+
|
|
71
|
+
private _revivers: IWebviewReviver[] = [];
|
|
72
|
+
|
|
73
|
+
@Autowired(ILogger)
|
|
74
|
+
logger: ILogger;
|
|
75
|
+
|
|
76
|
+
@Autowired(StorageProvider)
|
|
77
|
+
getStorage: StorageProvider;
|
|
78
|
+
|
|
79
|
+
storage: Promise<IStorage>;
|
|
80
|
+
|
|
81
|
+
constructor() {
|
|
82
|
+
this.storage = this.getStorage(new URI('editor-webview').withScheme(STORAGE_SCHEMA.SCOPE));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async tryReviveWebviewComponent(id: string) {
|
|
86
|
+
if (this._revivers.length > 0) {
|
|
87
|
+
let targetReviver:
|
|
88
|
+
| {
|
|
89
|
+
weight: number;
|
|
90
|
+
reviver: IWebviewReviver;
|
|
91
|
+
}
|
|
92
|
+
| undefined;
|
|
93
|
+
for (const reviver of this._revivers) {
|
|
94
|
+
try {
|
|
95
|
+
const weight = await reviver.handles(id);
|
|
96
|
+
if (weight >= 0 && (!targetReviver || targetReviver.weight < weight)) {
|
|
97
|
+
targetReviver = {
|
|
98
|
+
weight,
|
|
99
|
+
reviver,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
} catch (e) {
|
|
103
|
+
this.logger.error(e);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (targetReviver) {
|
|
107
|
+
try {
|
|
108
|
+
await targetReviver.reviver.revive(id);
|
|
109
|
+
return;
|
|
110
|
+
} catch (e) {
|
|
111
|
+
this.logger.error(e);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
throw new Error('Cannot revive webview ' + id);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
registerWebviewReviver(reviver: IWebviewReviver) {
|
|
119
|
+
return addElement(this._revivers, reviver);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
createPlainWebview(options: IPlainWebviewConstructionOptions = {}): IPlainWebview {
|
|
123
|
+
if (this.appConfig.isElectronRenderer) {
|
|
124
|
+
if (options.preferredImpl && options.preferredImpl === 'iframe') {
|
|
125
|
+
return new IframePlainWebview();
|
|
126
|
+
}
|
|
127
|
+
return new ElectronPlainWebview();
|
|
128
|
+
} else {
|
|
129
|
+
if (options.preferredImpl && options.preferredImpl === 'webview') {
|
|
130
|
+
getDebugLogger().warn(
|
|
131
|
+
localize('webview.webviewTagUnavailable', '无法在非Electron环境使用Webview标签。回退至使用iframe。'),
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return new IframePlainWebview();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
createWebview(options?: IWebviewContentOptions): IWebview {
|
|
139
|
+
let webview: IWebview;
|
|
140
|
+
if (this.appConfig.isElectronRenderer) {
|
|
141
|
+
webview = this.injector.get(ElectronWebviewWebviewPanel, [(this.webviewIdCount++).toString(), options]);
|
|
142
|
+
} else {
|
|
143
|
+
webview = this.injector.get(IFrameWebviewPanel, [(this.webviewIdCount++).toString(), options]);
|
|
144
|
+
}
|
|
145
|
+
this.webviews.set(webview.id, webview);
|
|
146
|
+
webview.onRemove(() => {
|
|
147
|
+
this.webviews.delete(webview.id);
|
|
148
|
+
});
|
|
149
|
+
return webview;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
getWebview(id: string): IWebview | undefined {
|
|
153
|
+
return this.webviews.get(id);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private async storeWebviewResource(id: string) {
|
|
157
|
+
return this.storage.then((storage) => {
|
|
158
|
+
if (this.editorWebviewComponents.has(id)) {
|
|
159
|
+
const res = { ...this.editorWebviewComponents.get(id)!.resource };
|
|
160
|
+
storage.set(id, JSON.stringify(res));
|
|
161
|
+
} else {
|
|
162
|
+
storage.delete(id);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
public async tryRestoredWebviewComponent(id: string): Promise<void> {
|
|
168
|
+
const storage = await this.storage;
|
|
169
|
+
const resource: IResource<IEditorWebviewMetaData> | null = storage.get(id) ? JSON.parse(storage.get(id)!) : null;
|
|
170
|
+
if (resource) {
|
|
171
|
+
const component = this.createEditorWebviewComponent(resource.metadata?.options, resource.metadata?.id);
|
|
172
|
+
component.title = resource.name;
|
|
173
|
+
component.supportsRevive = !!resource.supportsRevive;
|
|
174
|
+
this.tryReviveWebviewComponent(id);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
createEditorWebviewComponent(options?: IWebviewContentOptions, id?: string): IEditorWebviewComponent<IWebview> {
|
|
179
|
+
if (!id) {
|
|
180
|
+
id = (this.editorWebviewIdCount++).toString();
|
|
181
|
+
}
|
|
182
|
+
if (this.editorWebviewComponents.has(id)) {
|
|
183
|
+
return this.editorWebviewComponents.get(id) as IEditorWebviewComponent<IWebview>;
|
|
184
|
+
}
|
|
185
|
+
const component = this.injector.get(EditorWebviewComponent, [
|
|
186
|
+
id,
|
|
187
|
+
() => this.createWebview(options),
|
|
188
|
+
]) as EditorWebviewComponent<IWebview>;
|
|
189
|
+
this.editorWebviewComponents.set(id, component);
|
|
190
|
+
component.addDispose({
|
|
191
|
+
dispose: () => {
|
|
192
|
+
this.editorWebviewComponents.delete(id!);
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
component.onDidUpdateResource(() => {
|
|
196
|
+
this.storeWebviewResource(id!);
|
|
197
|
+
});
|
|
198
|
+
return component;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
createEditorPlainWebviewComponent(
|
|
202
|
+
options: IPlainWebviewConstructionOptions = {},
|
|
203
|
+
id: string,
|
|
204
|
+
): IEditorWebviewComponent<IPlainWebview> {
|
|
205
|
+
id = id || (this.editorWebviewIdCount++).toString();
|
|
206
|
+
if (this.editorWebviewComponents.has(id)) {
|
|
207
|
+
return this.editorWebviewComponents.get(id) as IEditorWebviewComponent<IPlainWebview>;
|
|
208
|
+
}
|
|
209
|
+
const component = this.injector.get(EditorWebviewComponent, [
|
|
210
|
+
id,
|
|
211
|
+
() => this.createPlainWebview(options),
|
|
212
|
+
]) as EditorWebviewComponent<IPlainWebview>;
|
|
213
|
+
this.editorWebviewComponents.set(id, component);
|
|
214
|
+
component.addDispose({
|
|
215
|
+
dispose: () => {
|
|
216
|
+
this.editorWebviewComponents.delete(id!);
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
return component;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
getWebviewThemeData(theme: ITheme): IWebviewThemeData {
|
|
223
|
+
const editorFontFamily = this.editorPreferences['editor.fontFamily'];
|
|
224
|
+
const editorFontWeight = this.editorPreferences['editor.fontFamily'];
|
|
225
|
+
const editorFontSize = this.editorPreferences['editor.fontSize'];
|
|
226
|
+
|
|
227
|
+
const exportedColors = getColorRegistry()
|
|
228
|
+
.getColors()
|
|
229
|
+
.reduce((colors, entry) => {
|
|
230
|
+
const color = theme.getColor(entry.id);
|
|
231
|
+
if (color) {
|
|
232
|
+
colors['vscode-' + entry.id.replace('.', '-')] = color.toString();
|
|
233
|
+
}
|
|
234
|
+
return colors;
|
|
235
|
+
}, {} as { [key: string]: string });
|
|
236
|
+
|
|
237
|
+
const styles = {
|
|
238
|
+
'vscode-font-family':
|
|
239
|
+
'-apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", ans-serif',
|
|
240
|
+
'vscode-font-weight': 'normal',
|
|
241
|
+
'vscode-font-size': '13px',
|
|
242
|
+
'vscode-editor-font-family': editorFontFamily,
|
|
243
|
+
'vscode-editor-font-weight': editorFontWeight,
|
|
244
|
+
'vscode-editor-font-size': editorFontSize,
|
|
245
|
+
...exportedColors,
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const activeTheme = ApiThemeClassName.fromTheme(theme);
|
|
249
|
+
return { styles, activeTheme };
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
getOrCreatePlainWebviewComponent(
|
|
253
|
+
id: string,
|
|
254
|
+
options?: IPlainWebviewConstructionOptions | undefined,
|
|
255
|
+
): IPlainWebviewComponentHandle {
|
|
256
|
+
if (!this.plainWebviewsComponents.has(id)) {
|
|
257
|
+
const webview = this.createPlainWebview(options);
|
|
258
|
+
const component = this.injector.get(PlainWebviewComponent, [id, webview]);
|
|
259
|
+
this.plainWebviewsComponents.set(id, component);
|
|
260
|
+
component.onDispose(() => {
|
|
261
|
+
this.plainWebviewsComponents.delete(id);
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
return this.plainWebviewsComponents.get(id)!;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
getEditorPlainWebviewComponent(id: string): IEditorWebviewComponent<IPlainWebview> | undefined {
|
|
268
|
+
const component = this.editorWebviewComponents.get(id);
|
|
269
|
+
if (component && (component.webview as IPlainWebview).loadURL) {
|
|
270
|
+
return component as IEditorWebviewComponent<IPlainWebview>;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
getPlainWebviewComponent(id: string): IPlainWebviewComponentHandle | undefined {
|
|
274
|
+
return this.plainWebviewsComponents.get(id);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
createWebviewWindow(
|
|
278
|
+
options?: Electron.BrowserWindowConstructorOptions,
|
|
279
|
+
env?: { [key: string]: string },
|
|
280
|
+
): IPlainWebviewWindow {
|
|
281
|
+
if (this.appConfig.isElectronRenderer) {
|
|
282
|
+
return this.injector.get(ElectronPlainWebviewWindow, [options, env]);
|
|
283
|
+
}
|
|
284
|
+
throw new Error('not supported!');
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
enum ApiThemeClassName {
|
|
289
|
+
light = 'vscode-light',
|
|
290
|
+
dark = 'vscode-dark',
|
|
291
|
+
highContrast = 'vscode-high-contrast',
|
|
292
|
+
highContrastLight = 'vscode-high-contrast-light',
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
namespace ApiThemeClassName {
|
|
296
|
+
export function fromTheme(theme: ITheme): ApiThemeClassName {
|
|
297
|
+
switch (theme.type) {
|
|
298
|
+
case 'light':
|
|
299
|
+
return ApiThemeClassName.light;
|
|
300
|
+
case 'dark':
|
|
301
|
+
return ApiThemeClassName.dark;
|
|
302
|
+
case 'hcDark':
|
|
303
|
+
return ApiThemeClassName.highContrast;
|
|
304
|
+
case 'hcLight':
|
|
305
|
+
return ApiThemeClassName.highContrastLight;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
@Injectable({ multiple: true })
|
|
311
|
+
export class EditorWebviewComponent<T extends IWebview | IPlainWebview>
|
|
312
|
+
extends Disposable
|
|
313
|
+
implements IEditorWebviewComponent<T>
|
|
314
|
+
{
|
|
315
|
+
@Autowired()
|
|
316
|
+
workbenchEditorService: WorkbenchEditorService;
|
|
317
|
+
|
|
318
|
+
@Autowired()
|
|
319
|
+
editorComponentRegistry: EditorComponentRegistry;
|
|
320
|
+
|
|
321
|
+
@Autowired(IEventBus)
|
|
322
|
+
eventBus: IEventBus;
|
|
323
|
+
|
|
324
|
+
private _webview: MaybeNull<T>;
|
|
325
|
+
|
|
326
|
+
private _onDidUpdateResource = new Emitter<IResource<IEditorWebviewMetaData>>();
|
|
327
|
+
public readonly onDidUpdateResource = this._onDidUpdateResource.event;
|
|
328
|
+
|
|
329
|
+
private _onDidChangeGroupIndex = new Emitter<number>();
|
|
330
|
+
public onDidChangeGroupIndex = this._onDidChangeGroupIndex.event;
|
|
331
|
+
|
|
332
|
+
private _supportsRevive = false;
|
|
333
|
+
|
|
334
|
+
get supportsRevive() {
|
|
335
|
+
return this._supportsRevive;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
set supportsRevive(value: boolean) {
|
|
339
|
+
this._supportsRevive = value;
|
|
340
|
+
this.eventBus.fire(new ResourceNeedUpdateEvent(this.webviewUri));
|
|
341
|
+
this._onDidUpdateResource.fire(this.resource);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
open(options: { groupIndex?: number; relativeGroupIndex?: number }) {
|
|
345
|
+
return this.workbenchEditorService.open(this.webviewUri, { ...options, preview: false });
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
close() {
|
|
349
|
+
this.workbenchEditorService.closeAll(this.webviewUri);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private _title = 'Webview';
|
|
353
|
+
|
|
354
|
+
private _icon = '';
|
|
355
|
+
|
|
356
|
+
get icon() {
|
|
357
|
+
return this._icon;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
set icon(icon: string) {
|
|
361
|
+
this._icon = icon;
|
|
362
|
+
this.eventBus.fire(new ResourceNeedUpdateEvent(this.webviewUri));
|
|
363
|
+
this._onDidUpdateResource.fire(this.resource);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
get title() {
|
|
367
|
+
return this._title;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
set title(title: string) {
|
|
371
|
+
this._title = title;
|
|
372
|
+
this.eventBus.fire(new ResourceNeedUpdateEvent(this.webviewUri));
|
|
373
|
+
this._onDidUpdateResource.fire(this.resource);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
get webview() {
|
|
377
|
+
if (!this._webview) {
|
|
378
|
+
this.createWebview();
|
|
379
|
+
}
|
|
380
|
+
return this._webview!;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
get resource(): IResource<IEditorWebviewMetaData> {
|
|
384
|
+
return {
|
|
385
|
+
icon: this.icon,
|
|
386
|
+
name: this.title,
|
|
387
|
+
uri: this.webviewUri,
|
|
388
|
+
metadata: {
|
|
389
|
+
id: this.id,
|
|
390
|
+
options: (this.webview as IWebview).options,
|
|
391
|
+
},
|
|
392
|
+
supportsRevive: this.supportsRevive,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
get webviewUri(): URI {
|
|
397
|
+
return URI.from({
|
|
398
|
+
scheme: EDITOR_WEBVIEW_SCHEME,
|
|
399
|
+
path: this.id,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
get editorGroup(): IEditorGroup | undefined {
|
|
404
|
+
const uri = this.webviewUri;
|
|
405
|
+
return this.workbenchEditorService.editorGroups.find(
|
|
406
|
+
(g) => g.resources.findIndex((r) => r.uri.isEqual(uri)) !== -1,
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
get group() {
|
|
411
|
+
return this.editorGroup;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
get componentId() {
|
|
415
|
+
return EDITOR_WEBVIEW_SCHEME + '_' + this.id;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
constructor(public readonly id: string, public webviewFactory: () => T) {
|
|
419
|
+
super();
|
|
420
|
+
const componentId = EDITOR_WEBVIEW_SCHEME + '_' + this.id;
|
|
421
|
+
this.addDispose(
|
|
422
|
+
this.editorComponentRegistry.registerEditorComponent<IEditorWebviewMetaData>({
|
|
423
|
+
scheme: EDITOR_WEBVIEW_SCHEME,
|
|
424
|
+
uid: componentId,
|
|
425
|
+
component: EditorWebviewComponentView,
|
|
426
|
+
renderMode: EditorComponentRenderMode.ONE_PER_WORKBENCH,
|
|
427
|
+
}),
|
|
428
|
+
);
|
|
429
|
+
this.addDispose(
|
|
430
|
+
this.editorComponentRegistry.registerEditorComponentResolver<IEditorWebviewMetaData>(
|
|
431
|
+
EDITOR_WEBVIEW_SCHEME,
|
|
432
|
+
(resource, results) => {
|
|
433
|
+
if (resource.uri.path.toString() === this.id) {
|
|
434
|
+
results.push({
|
|
435
|
+
type: EditorOpenType.component,
|
|
436
|
+
componentId,
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
},
|
|
440
|
+
),
|
|
441
|
+
);
|
|
442
|
+
this.addDispose({
|
|
443
|
+
dispose: () => {
|
|
444
|
+
this.workbenchEditorService.closeAll(this.webviewUri, true);
|
|
445
|
+
},
|
|
446
|
+
});
|
|
447
|
+
this.addDispose(
|
|
448
|
+
this.eventBus.on(EditorGroupChangeEvent, (e) => {
|
|
449
|
+
if (e.payload.newResource?.uri.isEqual(this.webviewUri)) {
|
|
450
|
+
this._onDidChangeGroupIndex.fire(e.payload.group.index);
|
|
451
|
+
}
|
|
452
|
+
}),
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
createWebview(): T {
|
|
457
|
+
this._webview = this.webviewFactory();
|
|
458
|
+
this.addDispose(this._webview!);
|
|
459
|
+
if (typeof (this._webview as IWebview).onDidFocus === 'function') {
|
|
460
|
+
this.addDispose(
|
|
461
|
+
(this._webview as IWebview).onDidFocus(() => {
|
|
462
|
+
if (this.editorGroup) {
|
|
463
|
+
(this.editorGroup as any).gainFocus();
|
|
464
|
+
}
|
|
465
|
+
}),
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
return this._webview;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
clear() {
|
|
472
|
+
const componentId = EDITOR_WEBVIEW_SCHEME + '_' + this.id;
|
|
473
|
+
this.editorComponentRegistry.clearPerWorkbenchComponentCache(componentId);
|
|
474
|
+
this.webview.remove();
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
@Injectable({ multiple: true })
|
|
479
|
+
export class PlainWebviewComponent extends Disposable implements IPlainWebviewComponentHandle {
|
|
480
|
+
constructor(public readonly id: string, public readonly webview) {
|
|
481
|
+
super();
|
|
482
|
+
this.addDispose(this.webview);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const WebviewScheme = 'kaitian-webview';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { protocol } from 'electron';
|
|
2
|
+
|
|
3
|
+
import { Injectable } from '@opensumi/di';
|
|
4
|
+
import { Domain } from '@opensumi/ide-core-common';
|
|
5
|
+
import { ElectronMainContribution, ElectronMainModule } from '@opensumi/ide-core-electron-main';
|
|
6
|
+
import { ProtocolElectronMainContribution } from '@opensumi/ide-core-electron-main/lib/bootstrap/services/protocol';
|
|
7
|
+
|
|
8
|
+
import { WebviewScheme } from '../common';
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
ProtocolElectronMainContribution.schemePrivileges.push({
|
|
12
|
+
scheme: WebviewScheme,
|
|
13
|
+
privileges: { standard: true, secure: true, supportFetchAPI: true, corsEnabled: true, allowServiceWorkers: true },
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
@Domain(ElectronMainContribution)
|
|
17
|
+
export class WebviewMainElectronContribution implements ElectronMainContribution {
|
|
18
|
+
private webviewContent =
|
|
19
|
+
'<!DOCTYPE html>\r\n<html lang="en" style="width: 100%; height: 100%">\r\n<head>\r\n\t<title>Virtual Document</title>\r\n</head>\r\n<body style="margin: 0; overflow: hidden; width: 100%; height: 100%">\r\n</body>\r\n</html>';
|
|
20
|
+
|
|
21
|
+
onStart() {
|
|
22
|
+
protocol.registerStringProtocol(WebviewScheme, (_request, callback) => {
|
|
23
|
+
callback(this.webviewContent);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@Injectable()
|
|
29
|
+
export class WebviewElectronMainModule extends ElectronMainModule {
|
|
30
|
+
providers = [WebviewMainElectronContribution];
|
|
31
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ipcRenderer } from 'electron';
|
|
2
|
+
|
|
3
|
+
import { IWebviewChannel } from '../webview-host/common';
|
|
4
|
+
import { WebviewPanelManager } from '../webview-host/webview-manager';
|
|
5
|
+
|
|
6
|
+
export class ElectronWebviewChannel implements IWebviewChannel {
|
|
7
|
+
private handlers = new Map();
|
|
8
|
+
focusIframeOnCreate?: boolean | undefined;
|
|
9
|
+
ready?: Promise<void> | undefined;
|
|
10
|
+
fakeLoad = false;
|
|
11
|
+
// tslint:disable-next-line: no-unused-variable
|
|
12
|
+
private isInDevelopmentMode = false;
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
window.addEventListener('message', (e) => {
|
|
16
|
+
if (e.data && (e.data.command === 'onmessage' || e.data.command === 'do-update-state')) {
|
|
17
|
+
// Came from inner iframe
|
|
18
|
+
this.postMessage(e.data.command, e.data.data);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const channel = e.data.channel;
|
|
23
|
+
const handler = this.handlers.get(channel);
|
|
24
|
+
if (handler) {
|
|
25
|
+
handler(e, e.data.data);
|
|
26
|
+
} else {
|
|
27
|
+
// eslint-disable-next-line no-console
|
|
28
|
+
console.warn('no handler for ', e);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
this.ready = new Promise<void>(async (resolve) => {
|
|
33
|
+
resolve();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
this.onMessage('devtools-opened', () => {
|
|
37
|
+
this.isInDevelopmentMode = true;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
postMessage(channel, data?) {
|
|
42
|
+
ipcRenderer.sendToHost(channel, data);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
onMessage(channel, handler) {
|
|
46
|
+
ipcRenderer.on(channel, handler);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
onIframeLoaded(newFrame) {
|
|
50
|
+
// newFrame.contentWindow.onbeforeunload = () => {
|
|
51
|
+
// if (this.isInDevelopmentMode) { // Allow reloads while developing a webview
|
|
52
|
+
// this.postMessage('do-reload');
|
|
53
|
+
// return false;
|
|
54
|
+
// }
|
|
55
|
+
// // Block navigation when not in development mode
|
|
56
|
+
// console.log('prevented webview navigation');
|
|
57
|
+
// return false;
|
|
58
|
+
// };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* tslint:disable */
|
|
63
|
+
new WebviewPanelManager(new ElectronWebviewChannel());
|
|
64
|
+
/* tslint:enable */
|