@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,140 @@
|
|
|
1
|
+
import { Injectable, Autowired } from '@opensumi/di';
|
|
2
|
+
import { Disposable, DomListener, getDebugLogger, IDisposable, AppConfig } from '@opensumi/ide-core-browser';
|
|
3
|
+
|
|
4
|
+
import { AbstractWebviewPanel } from './abstract-webview';
|
|
5
|
+
import { IWebview, IWebviewContentOptions } from './types';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@Injectable({ multiple: true })
|
|
9
|
+
export class IFrameWebviewPanel extends AbstractWebviewPanel implements IWebview {
|
|
10
|
+
private iframe: HTMLIFrameElement;
|
|
11
|
+
|
|
12
|
+
private _needReload = false;
|
|
13
|
+
|
|
14
|
+
private _iframeDisposer: Disposable | null = new Disposable();
|
|
15
|
+
|
|
16
|
+
private _isReady: boolean;
|
|
17
|
+
|
|
18
|
+
@Autowired(AppConfig)
|
|
19
|
+
config: AppConfig;
|
|
20
|
+
|
|
21
|
+
constructor(public readonly id: string, options: IWebviewContentOptions = {}) {
|
|
22
|
+
super(id, options);
|
|
23
|
+
|
|
24
|
+
this.iframe = document.createElement('iframe');
|
|
25
|
+
this.iframe.setAttribute('allow', 'autoplay');
|
|
26
|
+
|
|
27
|
+
const sandboxRules = new Set(['allow-same-origin', 'allow-scripts']);
|
|
28
|
+
|
|
29
|
+
if (options.allowForms) {
|
|
30
|
+
sandboxRules.add('allow-forms');
|
|
31
|
+
}
|
|
32
|
+
this.iframe.setAttribute('sandbox', Array.from(sandboxRules).join(' '));
|
|
33
|
+
this.iframe.setAttribute('src', `${this.config.webviewEndpoint}/index.html?id=${this.id}`);
|
|
34
|
+
this.iframe.style.border = 'none';
|
|
35
|
+
this.iframe.style.width = '100%';
|
|
36
|
+
this.iframe.style.position = 'absolute';
|
|
37
|
+
this.iframe.style.height = '100%';
|
|
38
|
+
this.iframe.style.zIndex = '2';
|
|
39
|
+
|
|
40
|
+
super.init();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
prepareContainer() {
|
|
44
|
+
this.clear();
|
|
45
|
+
this._iframeDisposer = new Disposable();
|
|
46
|
+
this._ready = new Promise<void>((resolve) => {
|
|
47
|
+
// tslint:disable-next-line: no-unused-variable
|
|
48
|
+
const disposer = this._onWebviewMessage('webview-ready', () => {
|
|
49
|
+
if (this._isReady) {
|
|
50
|
+
// 这种情况一般是由于iframe在dom中的位置变动导致了重载。
|
|
51
|
+
// 此时我们需要重新初始化
|
|
52
|
+
this.initEvents();
|
|
53
|
+
this.doUpdateContent();
|
|
54
|
+
}
|
|
55
|
+
this._isReady = true;
|
|
56
|
+
resolve();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
this._needReload = false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getDomNode() {
|
|
63
|
+
return this.iframe;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
protected _sendToWebview(channel: string, data: any) {
|
|
67
|
+
if (!this._isListening) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
this._ready
|
|
71
|
+
.then(() => {
|
|
72
|
+
if (!this.iframe) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
this.iframe.contentWindow!.postMessage(
|
|
76
|
+
{
|
|
77
|
+
channel,
|
|
78
|
+
data,
|
|
79
|
+
},
|
|
80
|
+
'*',
|
|
81
|
+
);
|
|
82
|
+
})
|
|
83
|
+
.catch((err) => {
|
|
84
|
+
getDebugLogger().error(err);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
protected _onWebviewMessage(channel: string, listener: (data: any) => any): IDisposable {
|
|
89
|
+
return this._iframeDisposer!.addDispose(
|
|
90
|
+
new DomListener(window, 'message', (e) => {
|
|
91
|
+
if (e.data && e.data.target === this.id && e.data.channel === channel) {
|
|
92
|
+
if (!this._isListening) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
listener(e.data.data);
|
|
96
|
+
}
|
|
97
|
+
}),
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
appendTo(container: HTMLElement) {
|
|
102
|
+
if (this.iframe) {
|
|
103
|
+
if (container.style.position === 'static' || !container.style.position) {
|
|
104
|
+
container.style.position = 'relative';
|
|
105
|
+
}
|
|
106
|
+
container.innerHTML = '';
|
|
107
|
+
container.appendChild(this.iframe);
|
|
108
|
+
if (this._needReload) {
|
|
109
|
+
this.init();
|
|
110
|
+
this.doUpdateContent();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
remove() {
|
|
116
|
+
if (this.iframe) {
|
|
117
|
+
this.iframe.remove();
|
|
118
|
+
this._onRemove.fire();
|
|
119
|
+
// remove 只是视图被销毁,但是html,state等内容保留,因此这里之前是改坏了
|
|
120
|
+
// this.dispose();
|
|
121
|
+
this.clear();
|
|
122
|
+
this._needReload = true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
clear() {
|
|
127
|
+
if (this._iframeDisposer) {
|
|
128
|
+
this._iframeDisposer.dispose();
|
|
129
|
+
this._iframeDisposer = null;
|
|
130
|
+
}
|
|
131
|
+
this._isReady = false;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
dispose() {
|
|
135
|
+
super.dispose();
|
|
136
|
+
if (this._iframeDisposer) {
|
|
137
|
+
this._iframeDisposer.dispose();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Provider, Injectable } from '@opensumi/di';
|
|
2
|
+
import { BrowserModule } from '@opensumi/ide-core-browser';
|
|
3
|
+
|
|
4
|
+
import { WebviewModuleContribution } from './contribution';
|
|
5
|
+
import { IWebviewService } from './types';
|
|
6
|
+
import { WebviewServiceImpl } from './webview.service';
|
|
7
|
+
export * from './types';
|
|
8
|
+
export { PlainWebview } from './editor-webview';
|
|
9
|
+
|
|
10
|
+
@Injectable()
|
|
11
|
+
export class WebviewModule extends BrowserModule {
|
|
12
|
+
providers: Provider[] = [
|
|
13
|
+
{
|
|
14
|
+
token: IWebviewService,
|
|
15
|
+
useClass: WebviewServiceImpl,
|
|
16
|
+
},
|
|
17
|
+
WebviewModuleContribution,
|
|
18
|
+
];
|
|
19
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { Disposable, DomListener, electronEnv, Emitter, Deferred, Event } from '@opensumi/ide-core-browser';
|
|
2
|
+
|
|
3
|
+
import { IPlainWebview } from './types';
|
|
4
|
+
|
|
5
|
+
export class IframePlainWebview extends Disposable implements IPlainWebview {
|
|
6
|
+
private _url: string | undefined;
|
|
7
|
+
|
|
8
|
+
private _iframe: HTMLIFrameElement | null;
|
|
9
|
+
|
|
10
|
+
private wrapper: HTMLIFrameElement | null;
|
|
11
|
+
|
|
12
|
+
_onMessage = new Emitter<any>();
|
|
13
|
+
onMessage = this._onMessage.event;
|
|
14
|
+
|
|
15
|
+
_onRemove: Emitter<void> = new Emitter<void>();
|
|
16
|
+
onRemove: Event<void> = this._onRemove.event;
|
|
17
|
+
|
|
18
|
+
_onLoadURL: Emitter<string> = new Emitter<string>();
|
|
19
|
+
onLoadURL: Event<string> = this._onLoadURL.event;
|
|
20
|
+
|
|
21
|
+
private _ready = new Deferred<void>();
|
|
22
|
+
|
|
23
|
+
get ready() {
|
|
24
|
+
return this._ready.promise;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
constructor() {
|
|
28
|
+
super();
|
|
29
|
+
/**
|
|
30
|
+
* Here we create a `outer iframe`, and later we will create an `inner iframe` and append it into outer's child.
|
|
31
|
+
*
|
|
32
|
+
* So we can capture message in the `outer iframe` instead of in the `main renderer`.
|
|
33
|
+
* +---------------------------------------------------------+
|
|
34
|
+
* | Main renderer |
|
|
35
|
+
* | +---------------------------------------------------+ |
|
|
36
|
+
* | | Outer iframe ^ | |
|
|
37
|
+
* | | | capture message here | |
|
|
38
|
+
* | | +-----------------+---------------------------+ | |
|
|
39
|
+
* | | | Inner iframe | | | |
|
|
40
|
+
* | | | postMessage | | |
|
|
41
|
+
* | | | | | |
|
|
42
|
+
* | | +---------------------------------------------+ | |
|
|
43
|
+
* | +---------------------------------------------------+ |
|
|
44
|
+
* +---------------------------------------------------------+
|
|
45
|
+
*/
|
|
46
|
+
this.wrapper = document.createElement('iframe');
|
|
47
|
+
this.wrapper.setAttribute('src', 'javascript:""');
|
|
48
|
+
this.wrapper.style.width = '100%';
|
|
49
|
+
this.wrapper.style.height = '100%';
|
|
50
|
+
this.wrapper.style.display = 'block';
|
|
51
|
+
this.wrapper.style.position = 'absolute';
|
|
52
|
+
this.wrapper.style.border = 'none';
|
|
53
|
+
this.wrapper.style.zIndex = '2';
|
|
54
|
+
const disposer = this.addDispose(
|
|
55
|
+
new DomListener(this.wrapper, 'load', () => {
|
|
56
|
+
this.addDispose(
|
|
57
|
+
new DomListener(this.wrapper!.contentWindow!, 'message', (e) => {
|
|
58
|
+
this._onMessage.fire(e.data);
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
61
|
+
this.wrapper!.contentDocument!.body.style.margin = '0';
|
|
62
|
+
this._ready.resolve();
|
|
63
|
+
disposer.dispose();
|
|
64
|
+
}),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
this.addDispose(this._onMessage);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get url() {
|
|
71
|
+
return this._url;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
setPartition(value: string): void {
|
|
75
|
+
// noop
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async loadURL(url: string): Promise<void> {
|
|
79
|
+
if (!this.wrapper) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
await this.ready;
|
|
83
|
+
if (!this.wrapper) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this._url = url;
|
|
87
|
+
if (!this._iframe) {
|
|
88
|
+
this._iframe = document.createElement('iframe');
|
|
89
|
+
this._iframe.style.width = '100%';
|
|
90
|
+
this._iframe.style.height = '100%';
|
|
91
|
+
this._iframe.style.display = 'block';
|
|
92
|
+
this._iframe.style.border = 'none';
|
|
93
|
+
this.wrapper!.contentWindow!.document.body.appendChild(this._iframe);
|
|
94
|
+
}
|
|
95
|
+
this._iframe.setAttribute('src', url);
|
|
96
|
+
this._onLoadURL.fire(url);
|
|
97
|
+
return new Promise<void>((resolve) => {
|
|
98
|
+
this.addDispose(
|
|
99
|
+
new DomListener(this._iframe!, 'load', () => {
|
|
100
|
+
resolve();
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
getDomNode() {
|
|
107
|
+
return this.wrapper;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
appendTo(container: HTMLElement): void {
|
|
111
|
+
if (this.wrapper) {
|
|
112
|
+
if (this.wrapper.parentElement) {
|
|
113
|
+
this.wrapper.remove();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
container.innerHTML = '';
|
|
117
|
+
container.appendChild(this.wrapper!);
|
|
118
|
+
if (this._url) {
|
|
119
|
+
this.loadURL(this._url);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
dispose() {
|
|
124
|
+
super.dispose();
|
|
125
|
+
if (this.wrapper) {
|
|
126
|
+
this.wrapper!.remove();
|
|
127
|
+
this.wrapper = null;
|
|
128
|
+
}
|
|
129
|
+
if (this._iframe) {
|
|
130
|
+
this._iframe!.remove();
|
|
131
|
+
this._iframe = null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
postMessage(message: any) {
|
|
136
|
+
if (this._iframe) {
|
|
137
|
+
this._iframe!.contentWindow!.postMessage(message, '*');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
remove() {
|
|
142
|
+
if (this.wrapper) {
|
|
143
|
+
this.wrapper.remove();
|
|
144
|
+
if (this._iframe) {
|
|
145
|
+
this._iframe.remove();
|
|
146
|
+
this._iframe = null;
|
|
147
|
+
}
|
|
148
|
+
this._onRemove.fire();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
interface IAllowedWebviewAttributes {
|
|
154
|
+
partition?: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export class ElectronPlainWebview extends Disposable implements IPlainWebview {
|
|
158
|
+
private _url: string | undefined;
|
|
159
|
+
|
|
160
|
+
private webview: Electron.WebviewTag | null;
|
|
161
|
+
|
|
162
|
+
private wrapper: HTMLDivElement | null;
|
|
163
|
+
|
|
164
|
+
private webviewDomReady: Deferred<void> = new Deferred();
|
|
165
|
+
|
|
166
|
+
_onMessage = new Emitter<any>();
|
|
167
|
+
onMessage = this._onMessage.event;
|
|
168
|
+
|
|
169
|
+
_onRemove: Emitter<void> = new Emitter<void>();
|
|
170
|
+
onRemove: Event<void> = this._onRemove.event;
|
|
171
|
+
|
|
172
|
+
_onLoadURL: Emitter<string> = new Emitter<string>();
|
|
173
|
+
onLoadURL: Event<string> = this._onLoadURL.event;
|
|
174
|
+
|
|
175
|
+
constructor() {
|
|
176
|
+
super();
|
|
177
|
+
this.wrapper = document.createElement('div');
|
|
178
|
+
this.wrapper.style.width = '100%';
|
|
179
|
+
this.wrapper.style.height = '100%';
|
|
180
|
+
this.wrapper.style.display = 'block';
|
|
181
|
+
this.wrapper.style.position = 'absolute';
|
|
182
|
+
this.wrapper.style.border = 'none';
|
|
183
|
+
this.wrapper.style.zIndex = '2';
|
|
184
|
+
this.addDispose(this._onMessage);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
get url() {
|
|
188
|
+
return this._url;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
getDomNode() {
|
|
192
|
+
return this.wrapper;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
getWebviewElement() {
|
|
196
|
+
return this.webview;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
extendAttributes: IAllowedWebviewAttributes = {};
|
|
200
|
+
|
|
201
|
+
setPartition(value: string): void {
|
|
202
|
+
this.extendAttributes.partition = value;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async loadURL(url: string): Promise<void> {
|
|
206
|
+
if (!this.wrapper) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
this._url = url;
|
|
210
|
+
if (!this.webview) {
|
|
211
|
+
this.webview = document.createElement('webview');
|
|
212
|
+
this.webview.style.width = '100%';
|
|
213
|
+
this.webview.style.height = '100%';
|
|
214
|
+
this.webview.style.border = 'none';
|
|
215
|
+
this.webview.style.zIndex = '2';
|
|
216
|
+
this.webview.src = url;
|
|
217
|
+
this.webview.preload = electronEnv.plainWebviewPreload;
|
|
218
|
+
if (this.extendAttributes.partition) {
|
|
219
|
+
this.webview.partition = this.extendAttributes.partition;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
this.wrapper!.appendChild(this.webview);
|
|
223
|
+
this.webview.addEventListener('ipc-message', (event) => {
|
|
224
|
+
if (event.channel === 'webview-message') {
|
|
225
|
+
this._onMessage.fire(event.args[0]);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
this.addDispose(
|
|
229
|
+
new DomListener(this.webview!, 'dom-ready', () => {
|
|
230
|
+
this.webviewDomReady.resolve();
|
|
231
|
+
}),
|
|
232
|
+
);
|
|
233
|
+
this.addDispose(
|
|
234
|
+
new DomListener(this.webview!, 'destroyed', () => {
|
|
235
|
+
this.webviewDomReady = new Deferred();
|
|
236
|
+
}),
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
this._url = url;
|
|
240
|
+
if (document.body.contains(this.wrapper)) {
|
|
241
|
+
return this.doLoadURL();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private async doLoadURL(): Promise<void> {
|
|
246
|
+
return new Promise<void>(async (resolve) => {
|
|
247
|
+
this.webview!.src = this.url!;
|
|
248
|
+
const disposer = this.addDispose(
|
|
249
|
+
new DomListener(this.webview!, 'did-finish-load', () => {
|
|
250
|
+
disposer.dispose();
|
|
251
|
+
this._onLoadURL.fire(this.url!);
|
|
252
|
+
resolve();
|
|
253
|
+
}),
|
|
254
|
+
);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
appendTo(container: HTMLElement): void {
|
|
259
|
+
container.appendChild(this.wrapper!);
|
|
260
|
+
if (this._url) {
|
|
261
|
+
this.doLoadURL();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
dispose() {
|
|
266
|
+
this.wrapper!.remove();
|
|
267
|
+
this.wrapper = null;
|
|
268
|
+
this.webview!.remove();
|
|
269
|
+
this.webview = null;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
postMessage(message: any) {
|
|
273
|
+
if (this.webview) {
|
|
274
|
+
this.webview!.send('webview-message', message);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
remove() {
|
|
279
|
+
if (this.wrapper) {
|
|
280
|
+
this.wrapper.remove();
|
|
281
|
+
this._onRemove.fire();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { Event, URI, IDisposable, MaybeNull, MaybePromise } from '@opensumi/ide-core-common';
|
|
2
|
+
import { IEditorGroup } from '@opensumi/ide-editor';
|
|
3
|
+
import { ITheme } from '@opensumi/ide-theme';
|
|
4
|
+
|
|
5
|
+
export const EDITOR_WEBVIEW_SCHEME = 'editor-webview';
|
|
6
|
+
/**
|
|
7
|
+
* webview Panel实际上是一个iframe中的内容
|
|
8
|
+
* 叫webview panel只是为了和以前vscode中的说法一致
|
|
9
|
+
* 它会存在两种具体的实现,
|
|
10
|
+
* 对于Electron, 使用webview进行一次包裹,里面再嵌入iframe,可以保证各个WebviewPanel的内容的独立性
|
|
11
|
+
* 对于Web, 需要将每个iframe的src挂载在一个webviewEndPoint上,(同一个webviewEndPoint的内容共享一个线程,
|
|
12
|
+
* 因此如果有些webview需要保证安全和稳定性的话需要使用不同的EndPoint)
|
|
13
|
+
*/
|
|
14
|
+
export interface IWebview extends IDisposable {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
|
|
17
|
+
readonly options: IWebviewContentOptions;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 一开始的滚动位置
|
|
21
|
+
*/
|
|
22
|
+
initialScrollProgress: number;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 状态值
|
|
26
|
+
*/
|
|
27
|
+
state: any;
|
|
28
|
+
|
|
29
|
+
postMessage(message: any): Promise<void>;
|
|
30
|
+
|
|
31
|
+
getContent(): string;
|
|
32
|
+
|
|
33
|
+
setContent(html: string): Promise<void>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 更新选项
|
|
37
|
+
* @param options 选项
|
|
38
|
+
* @param longLive 是否保留webview对象在内存中
|
|
39
|
+
*/
|
|
40
|
+
updateOptions(options: IWebviewContentOptions): void;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 更新内部iframe大小使其适应外部大小
|
|
44
|
+
*/
|
|
45
|
+
layout(): void;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 挂载一个
|
|
49
|
+
* @param parent
|
|
50
|
+
*/
|
|
51
|
+
appendTo(container: HTMLElement): void;
|
|
52
|
+
|
|
53
|
+
focus(): void;
|
|
54
|
+
|
|
55
|
+
reload(): void;
|
|
56
|
+
|
|
57
|
+
getDomNode(): MaybeNull<HTMLElement>;
|
|
58
|
+
|
|
59
|
+
remove(): void;
|
|
60
|
+
|
|
61
|
+
onDispose: Event<void>;
|
|
62
|
+
|
|
63
|
+
readonly onDidFocus: Event<void>;
|
|
64
|
+
readonly onDidBlur: Event<void>;
|
|
65
|
+
readonly onDidClickLink: Event<URI>;
|
|
66
|
+
readonly onDidScroll: Event<IWebviewContentScrollPosition>;
|
|
67
|
+
readonly onDidUpdateState: Event<any>;
|
|
68
|
+
readonly onMessage: Event<any>;
|
|
69
|
+
readonly onRemove: Event<void>;
|
|
70
|
+
|
|
71
|
+
setListenMessages(listening: boolean): void;
|
|
72
|
+
|
|
73
|
+
setKeybindingDomTarget(target: HTMLElement): void;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface IWebviewContentOptions {
|
|
77
|
+
readonly allowScripts?: boolean;
|
|
78
|
+
readonly allowForms?: boolean;
|
|
79
|
+
readonly svgWhiteList?: string[];
|
|
80
|
+
readonly localResourceRoots?: ReadonlyArray<URI>;
|
|
81
|
+
readonly longLive?: boolean;
|
|
82
|
+
readonly enableFindWidget?: boolean;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface IWebviewContentScrollPosition {
|
|
86
|
+
scrollYPercentage: number;
|
|
87
|
+
scrollXPercentage: number;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 纯粹的 webview 或 iframe 元素
|
|
92
|
+
* 用以加载指定的 url
|
|
93
|
+
*/
|
|
94
|
+
export interface IPlainWebview extends IDisposable {
|
|
95
|
+
readonly url: string | undefined;
|
|
96
|
+
|
|
97
|
+
loadURL(url: string): Promise<void>;
|
|
98
|
+
|
|
99
|
+
appendTo(container: HTMLElement): void;
|
|
100
|
+
|
|
101
|
+
postMessage(message: any): void;
|
|
102
|
+
|
|
103
|
+
onMessage: Event<any>;
|
|
104
|
+
|
|
105
|
+
getDomNode(): MaybeNull<HTMLElement>;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* In `webview` element it will use [partition](https://www.electronjs.org/docs/latest/api/webview-tag#partition)
|
|
109
|
+
*
|
|
110
|
+
* A `String` that sets the session used by the page. If `partition` starts with
|
|
111
|
+
* `persist:`, the page will use a persistent session available to all pages in the
|
|
112
|
+
* app with the same `partition`. if there is no `persist:` prefix, the page will
|
|
113
|
+
* use an in-memory session. By assigning the same `partition`, multiple pages can
|
|
114
|
+
* share the same session. If the `partition` is unset then default session of the
|
|
115
|
+
* app will be used.
|
|
116
|
+
*
|
|
117
|
+
* This value can only be modified before the first navigation, since the session
|
|
118
|
+
* of an active renderer process cannot change. Subsequent attempts to modify the
|
|
119
|
+
* value will fail with a DOM exception.
|
|
120
|
+
*
|
|
121
|
+
* **not work in `iframe` element.**
|
|
122
|
+
*/
|
|
123
|
+
setPartition(value: string): void;
|
|
124
|
+
|
|
125
|
+
onDispose: Event<void>;
|
|
126
|
+
|
|
127
|
+
readonly onRemove: Event<void>;
|
|
128
|
+
|
|
129
|
+
remove(): void;
|
|
130
|
+
|
|
131
|
+
onLoadURL: Event<string>;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 打开一个新的窗口,并显示一个url中的内容
|
|
136
|
+
* 暂时仅限 electron 中使用
|
|
137
|
+
*/
|
|
138
|
+
export interface IPlainWebviewWindow extends IDisposable {
|
|
139
|
+
ready: Promise<void>;
|
|
140
|
+
|
|
141
|
+
readonly url: string | undefined;
|
|
142
|
+
|
|
143
|
+
loadURL(url: string): Promise<void>;
|
|
144
|
+
|
|
145
|
+
show(): Promise<void>;
|
|
146
|
+
|
|
147
|
+
hide(): Promise<void>;
|
|
148
|
+
|
|
149
|
+
postMessage(message: any): Promise<void>;
|
|
150
|
+
|
|
151
|
+
onMessage: Event<any>;
|
|
152
|
+
|
|
153
|
+
onClosed: Event<void>;
|
|
154
|
+
|
|
155
|
+
setSize(size: { width?: number; height?: number }): Promise<void>;
|
|
156
|
+
|
|
157
|
+
setAlwaysOnTop(flag: boolean): Promise<void>;
|
|
158
|
+
|
|
159
|
+
windowId: number;
|
|
160
|
+
|
|
161
|
+
webContentsId: number;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export const IWebviewService = Symbol('IWebviewService');
|
|
165
|
+
|
|
166
|
+
export interface IWebviewService {
|
|
167
|
+
createPlainWebview(options?: IPlainWebviewConstructionOptions): IPlainWebview;
|
|
168
|
+
|
|
169
|
+
createWebview(options?: IWebviewContentOptions): IWebview;
|
|
170
|
+
|
|
171
|
+
getWebview(id: string): IWebview | undefined;
|
|
172
|
+
|
|
173
|
+
createEditorWebviewComponent(options?: IWebviewContentOptions, id?: string): IEditorWebviewComponent<IWebview>;
|
|
174
|
+
|
|
175
|
+
createEditorPlainWebviewComponent(
|
|
176
|
+
options?: IPlainWebviewConstructionOptions,
|
|
177
|
+
id?: string,
|
|
178
|
+
): IEditorWebviewComponent<IPlainWebview>;
|
|
179
|
+
|
|
180
|
+
getWebviewThemeData(theme: ITheme): IWebviewThemeData;
|
|
181
|
+
|
|
182
|
+
getOrCreatePlainWebviewComponent(
|
|
183
|
+
id: string,
|
|
184
|
+
options?: IPlainWebviewConstructionOptions,
|
|
185
|
+
): IPlainWebviewComponentHandle;
|
|
186
|
+
|
|
187
|
+
getEditorPlainWebviewComponent(id: string): IEditorWebviewComponent<IPlainWebview> | undefined;
|
|
188
|
+
|
|
189
|
+
getPlainWebviewComponent(id: string): IPlainWebviewComponentHandle | undefined;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* 创建一个 window 的 webview 容器, 暂时只支持 electron
|
|
193
|
+
* @param options electron的创建的options
|
|
194
|
+
* @param env 会传递给 webview内的 window.env 的内容
|
|
195
|
+
*/
|
|
196
|
+
createWebviewWindow(
|
|
197
|
+
options?: Electron.BrowserWindowConstructorOptions,
|
|
198
|
+
env?: { [key: string]: string },
|
|
199
|
+
): IPlainWebviewWindow;
|
|
200
|
+
|
|
201
|
+
registerWebviewReviver(reviver: IWebviewReviver): IDisposable;
|
|
202
|
+
|
|
203
|
+
tryReviveWebviewComponent(id: string): Promise<void>;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface IPlainWebviewConstructionOptions {
|
|
207
|
+
// 喜好使用的实现
|
|
208
|
+
// 在web上无法使用 webview
|
|
209
|
+
preferredImpl?: 'webview' | 'iframe';
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export interface IWebviewThemeData {
|
|
213
|
+
readonly activeTheme: string;
|
|
214
|
+
readonly styles: { readonly [key: string]: string | number };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface IEditorWebviewComponent<T extends IWebview | IPlainWebview> extends IDisposable {
|
|
218
|
+
// 唯一id
|
|
219
|
+
id: string;
|
|
220
|
+
|
|
221
|
+
// webview
|
|
222
|
+
webview: T;
|
|
223
|
+
|
|
224
|
+
// 容纳它的
|
|
225
|
+
group: IEditorGroup | undefined;
|
|
226
|
+
|
|
227
|
+
icon: string;
|
|
228
|
+
|
|
229
|
+
title: string;
|
|
230
|
+
|
|
231
|
+
open(options: { groupIndex?: number; relativeGroupIndex?: number });
|
|
232
|
+
|
|
233
|
+
close();
|
|
234
|
+
|
|
235
|
+
webviewUri: URI;
|
|
236
|
+
|
|
237
|
+
componentId: string;
|
|
238
|
+
|
|
239
|
+
onDidChangeGroupIndex: Event<number>;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* 是否支持恢复
|
|
243
|
+
*/
|
|
244
|
+
supportsRevive: boolean;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export interface IPlainWebviewComponentHandle extends IDisposable {
|
|
248
|
+
// 唯一id
|
|
249
|
+
id: string;
|
|
250
|
+
|
|
251
|
+
webview: IPlainWebview;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export interface IEditorWebviewMetaData {
|
|
255
|
+
id: string;
|
|
256
|
+
options?: IWebviewContentOptions;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export interface IWebviewReviver {
|
|
260
|
+
/**
|
|
261
|
+
* revive 动作
|
|
262
|
+
*/
|
|
263
|
+
revive: (id: string) => MaybePromise<void>;
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* 返回是否由它revive的优先级
|
|
267
|
+
* 负数表示不处理
|
|
268
|
+
*/
|
|
269
|
+
handles: (id: string) => MaybePromise<number>;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function isWebview(webview: IWebview | IPlainWebview): webview is IWebview {
|
|
273
|
+
return webview && !!(webview as IWebview).setContent;
|
|
274
|
+
}
|