Skip to content

Commit 307cb32

Browse files
mjbvzBenjamin Pasero
andauthored
Add vscode-webview-resource protocol (microsoft#97777)
* Add vscode-webview-resource protocol Adds a new protocol (`vscode-webview-resource`) for loading resources insides of webviews. This replaces the existing `vscode-resource` protocol and is registered on the main thread instead of in each renderer This change also adds some rewriting logic to update any `vscode-resource:` references found in the main html to use `vscode-webview-resource` instead. * Move webview protcol provider to own file * Remove registration of vscode-resource scheme * Remove use or parition for each webview Now that we have a single shared protocol handler, we do not need to run each webview in its own partition * Fix rewriting csp to use new protocol * Update src/vs/code/electron-main/app.ts Co-authored-by: Benjamin Pasero <benjpas@microsoft.com> Co-authored-by: Benjamin Pasero <benjpas@microsoft.com>
1 parent 946d994 commit 307cb32

26 files changed

Lines changed: 220 additions & 130 deletions

File tree

src/main.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,15 @@ setCurrentWorkingDirectory();
7979

8080
// Register custom schemes with privileges
8181
protocol.registerSchemesAsPrivileged([
82-
{ scheme: 'vscode-resource', privileges: { secure: true, supportFetchAPI: true, corsEnabled: true } }
82+
{
83+
scheme: 'vscode-webview-resource',
84+
privileges: {
85+
secure: true,
86+
standard: true,
87+
supportFetchAPI: true,
88+
corsEnabled: true,
89+
}
90+
},
8391
]);
8492

8593
// Global app listeners

src/vs/base/common/network.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ export namespace Schemas {
5959
export const vscodeSettings = 'vscode-settings';
6060

6161
export const webviewPanel = 'webview-panel';
62+
63+
export const vscodeWebviewResource = 'vscode-webview-resource';
6264
}
6365

6466
class RemoteAuthoritiesImpl {

src/vs/code/electron-main/app.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common
8181
import { StorageKeysSyncRegistryChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
8282
import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService';
8383
import { mnemonicButtonLabel, getPathLabel } from 'vs/base/common/labels';
84+
import { IFileService } from 'vs/platform/files/common/files';
85+
import { WebviewProtocolProvider } from 'vs/platform/webview/electron-main/webviewProtocolProvider';
8486

8587
export class CodeApplication extends Disposable {
8688
private windowsMainService: IWindowsMainService | undefined;
@@ -89,6 +91,7 @@ export class CodeApplication extends Disposable {
8991
constructor(
9092
private readonly mainIpcServer: Server,
9193
private readonly userEnv: IProcessEnvironment,
94+
@IFileService fileService: IFileService,
9295
@IInstantiationService private readonly instantiationService: IInstantiationService,
9396
@ILogService private readonly logService: ILogService,
9497
@IEnvironmentService private readonly environmentService: INativeEnvironmentService,
@@ -99,6 +102,8 @@ export class CodeApplication extends Disposable {
99102
super();
100103

101104
this.registerListeners();
105+
106+
this._register(new WebviewProtocolProvider(fileService));
102107
}
103108

104109
private registerListeners(): void {
File renamed without changes.

src/vs/workbench/contrib/webview/common/resourceLoader.ts renamed to src/vs/platform/webview/common/resourceLoader.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ import { sep } from 'vs/base/common/path';
88
import { URI } from 'vs/base/common/uri';
99
import { IFileService } from 'vs/platform/files/common/files';
1010
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
11-
import { getWebviewContentMimeType } from 'vs/workbench/contrib/webview/common/mimeTypes';
1211
import { isUNC } from 'vs/base/common/extpath';
13-
14-
export const WebviewResourceScheme = 'vscode-resource';
12+
import { getWebviewContentMimeType } from 'vs/platform/webview/common/mimeTypes';
13+
import { Schemas } from 'vs/base/common/network';
1514

1615
export namespace WebviewResourceResponse {
1716
export enum Type { Success, Failed, AccessDenied }
@@ -77,7 +76,7 @@ export async function loadLocalResource(
7776
}
7877

7978
function normalizeRequestPath(requestUri: URI) {
80-
if (requestUri.scheme !== WebviewResourceScheme) {
79+
if (requestUri.scheme !== Schemas.vscodeWebviewResource) {
8180
return requestUri;
8281
}
8382

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { ipcMain as ipc, IpcMainEvent, MimeTypedBuffer, protocol } from 'electron';
7+
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
8+
import { Schemas } from 'vs/base/common/network';
9+
import { URI, UriComponents } from 'vs/base/common/uri';
10+
import { IFileService } from 'vs/platform/files/common/files';
11+
import { loadLocalResource, WebviewResourceResponse } from 'vs/platform/webview/common/resourceLoader';
12+
13+
export interface RegisterWebviewMetadata {
14+
readonly extensionLocation: URI | undefined;
15+
readonly localResourceRoots: readonly URI[];
16+
}
17+
18+
type ErrorCallback = (response: MimeTypedBuffer | { error: number }) => void;
19+
20+
21+
export class WebviewProtocolProvider extends Disposable {
22+
23+
private readonly webviewMetadata = new Map<string, {
24+
readonly extensionLocation: URI | undefined;
25+
readonly localResourceRoots: URI[];
26+
}>();
27+
28+
constructor(
29+
@IFileService private readonly fileService: IFileService,
30+
) {
31+
super();
32+
33+
ipc.on('vscode:registerWebview', (event: IpcMainEvent, id: string, data: RegisterWebviewMetadata) => {
34+
this.webviewMetadata.set(id, {
35+
extensionLocation: data.extensionLocation ? URI.from(data.extensionLocation) : undefined,
36+
localResourceRoots: data.localResourceRoots.map((x: UriComponents) => URI.from(x)),
37+
});
38+
39+
event.sender.send(`vscode:didRegisterWebview-${id}`);
40+
});
41+
42+
ipc.on('vscode:unregisterWebview', (_event: IpcMainEvent, id: string) => {
43+
this.webviewMetadata.delete(id);
44+
});
45+
46+
47+
protocol.registerBufferProtocol(Schemas.vscodeWebviewResource, async (request, callback): Promise<void> => {
48+
try {
49+
const uri = URI.parse(request.url);
50+
const resource = URI.parse(uri.path.replace(/^\/(\w+)/, '$1:'));
51+
52+
const id = uri.authority;
53+
const metadata = this.webviewMetadata.get(id);
54+
if (metadata) {
55+
const result = await loadLocalResource(resource, this.fileService, metadata.extensionLocation, () => metadata.localResourceRoots);
56+
if (result.type === WebviewResourceResponse.Type.Success) {
57+
return callback({
58+
data: Buffer.from(result.data.buffer),
59+
mimeType: result.mimeType
60+
});
61+
}
62+
63+
if (result.type === WebviewResourceResponse.Type.AccessDenied) {
64+
console.error('Webview: Cannot load resource outside of protocol root');
65+
return (callback as ErrorCallback)({ error: -10 /* ACCESS_DENIED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ });
66+
}
67+
}
68+
} catch {
69+
// noop
70+
}
71+
72+
return (callback as ErrorCallback)({ error: -2 });
73+
});
74+
75+
this._register(toDisposable(() => protocol.unregisterProtocol(Schemas.vscodeWebviewResource)));
76+
}
77+
}

src/vs/vscode.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6610,7 +6610,7 @@ declare module 'vscode' {
66106610
readonly enableCommandUris?: boolean;
66116611

66126612
/**
6613-
* Root paths from which the webview can load local (filesystem) resources using the `vscode-resource:` scheme.
6613+
* Root paths from which the webview can load local (filesystem) resources using uris from `asWebviewUri`
66146614
*
66156615
* Default to the root folders of the current workspace plus the extension's install directory.
66166616
*

src/vs/workbench/api/browser/mainThreadCodeInsets.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
9494
}, {
9595
allowScripts: options.enableScripts,
9696
localResourceRoots: options.localResourceRoots ? options.localResourceRoots.map(uri => URI.revive(uri)) : undefined
97-
});
98-
webview.extension = { id: extensionId, location: URI.revive(extensionLocation) };
97+
}, { id: extensionId, location: URI.revive(extensionLocation) });
9998

10099
const webviewZone = new EditorWebviewZone(editor, line, height, webview);
101100

src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,8 @@ export class CustomEditorInputFactory extends WebviewEditorInputFactory {
9797
const webview = webviewService.createWebviewOverlay(data.id, {
9898
enableFindWidget: data.options.enableFindWidget,
9999
retainContextWhenHidden: data.options.retainContextWhenHidden
100-
}, data.options);
100+
}, data.options, data.extension);
101101
webview.state = data.state;
102-
webview.extension = data.extension;
103102
return webview;
104103
});
105104
}

src/vs/workbench/contrib/customEditor/browser/customEditors.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
231231

232232
const id = generateUuid();
233233
const webview = new Lazy(() => {
234-
return this.webviewService.createWebviewOverlay(id, { customClasses: options?.customClasses }, {});
234+
return this.webviewService.createWebviewOverlay(id, { customClasses: options?.customClasses }, {}, undefined);
235235
});
236236
const input = this.instantiationService.createInstance(CustomEditorInput, resource, viewType, id, webview, {});
237237
if (typeof group !== 'undefined') {

0 commit comments

Comments
 (0)