/
webviewPortMappingProvider.ts
103 lines (88 loc) · 3.34 KB
/
webviewPortMappingProvider.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { OnBeforeRequestListenerDetails, session, webContents } from 'electron';
import { Disposable } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { IAddress } from 'vs/platform/remote/common/remoteAgentConnection';
import { ITunnelService } from 'vs/platform/remote/common/tunnel';
import { webviewPartitionId } from 'vs/platform/webview/common/resourceLoader';
import { IWebviewPortMapping, WebviewPortMappingManager } from 'vs/platform/webview/common/webviewPortMapping';
interface OnBeforeRequestListenerDetails_Extended extends OnBeforeRequestListenerDetails {
readonly lastCommittedOrigin?: string;
}
interface PortMappingData {
readonly extensionLocation: URI | undefined;
readonly mappings: readonly IWebviewPortMapping[];
readonly resolvedAuthority: IAddress | null | undefined;
}
interface WebviewData {
readonly manager: WebviewPortMappingManager;
readonly metadata: PortMappingData;
}
export class WebviewPortMappingProvider extends Disposable {
private readonly _webviewData = new Map<string, WebviewData>();
constructor(
@ITunnelService private readonly _tunnelService: ITunnelService,
) {
super();
const sess = session.fromPartition(webviewPartitionId);
sess.webRequest.onBeforeRequest({
urls: [
'*://localhost:*/*',
'*://127.0.0.1:*/*',
'*://0.0.0.0:*/*',
]
}, async (details: OnBeforeRequestListenerDetails_Extended, callback) => {
let webviewId: string | undefined;
try {
if (details.lastCommittedOrigin) {
const origin = URI.parse(details.lastCommittedOrigin);
webviewId = origin.authority;
} else if (typeof details.webContentsId === 'number') {
const contents = webContents.fromId(details.webContentsId);
const url = URI.parse(contents.getURL());
if (url.scheme === Schemas.vscodeWebview) {
webviewId = url.authority;
}
}
} catch {
return callback({});
}
if (!webviewId) {
return callback({});
}
const entry = this._webviewData.get(webviewId);
if (!entry) {
return callback({});
}
const redirect = await entry.manager.getRedirect(entry.metadata.resolvedAuthority, details.url);
return callback(redirect ? { redirectURL: redirect } : {});
});
}
public async registerWebview(id: string, metadata: PortMappingData): Promise<void> {
const manager = new WebviewPortMappingManager(
() => this._webviewData.get(id)?.metadata.extensionLocation,
() => this._webviewData.get(id)?.metadata.mappings || [],
this._tunnelService);
this._webviewData.set(id, { metadata, manager });
}
public unregisterWebview(id: string): void {
const existing = this._webviewData.get(id);
if (existing) {
existing.manager.dispose();
this._webviewData.delete(id);
}
}
public async updateWebviewMetadata(id: string, metadataDelta: Partial<PortMappingData>): Promise<void> {
const entry = this._webviewData.get(id);
if (entry) {
this._webviewData.set(id, {
...entry,
...metadataDelta,
});
}
}
}