Skip to content

Commit

Permalink
Use service workers for loading webview resources on desktop (#120654)
Browse files Browse the repository at this point in the history
This switches us from using a custom protocol to using a service worker to load resources inside webviews. Previously we had only been using service workers on web (since custom protocols are not supported on web). The service worker based approach is much cleaner to than our custom protocol work and lets us avoid some extra roundtrips between the main process and the renderer

We were previously blocked from using service workers on desktop due to an electron issue. However this has now been resolved
  • Loading branch information
mjbvz committed Apr 7, 2021
1 parent c19bae2 commit d05ded6
Show file tree
Hide file tree
Showing 17 changed files with 379 additions and 972 deletions.
2 changes: 1 addition & 1 deletion src/main.js
Expand Up @@ -59,7 +59,7 @@ if (portable && portable.isPortable) {
protocol.registerSchemesAsPrivileged([
{
scheme: 'vscode-webview',
privileges: { standard: true, secure: true, supportFetchAPI: true, corsEnabled: true }
privileges: { standard: true, secure: true, supportFetchAPI: true, corsEnabled: true, allowServiceWorkers: true, }
}, {
scheme: 'vscode-webview-resource',
privileges: { secure: true, standard: true, supportFetchAPI: true, corsEnabled: true }
Expand Down
2 changes: 1 addition & 1 deletion src/vs/code/electron-main/app.ts
Expand Up @@ -186,7 +186,7 @@ export class CodeApplication extends Disposable {

const uri = URI.parse(source);
if (uri.scheme === Schemas.vscodeWebview) {
return uri.path === '/index.html' || uri.path === '/electron-browser/index.html';
return uri.path === '/index.html' || uri.path === '/electron-browser-index.html';
}

const srcUri = uri.fsPath.toLowerCase();
Expand Down
28 changes: 0 additions & 28 deletions src/vs/platform/webview/common/webviewManagerService.ts
Expand Up @@ -3,11 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { VSBuffer } from 'vs/base/common/buffer';
import { UriComponents } from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IWebviewPortMapping } from 'vs/platform/webview/common/webviewPortMapping';

export const IWebviewManagerService = createDecorator<IWebviewManagerService>('webviewManagerService');

Expand All @@ -19,32 +15,8 @@ export interface WebviewWindowId {
readonly windowId: number;
}

export type WebviewManagerDidLoadResourceResponse =
VSBuffer
| 'not-modified'
| 'access-denied'
| 'not-found';

export interface WebviewManagerDidLoadResourceResponseDetails {
readonly etag?: string;
}

export interface IWebviewManagerService {
_serviceBrand: unknown;

registerWebview(id: string, windowId: number, metadata: RegisterWebviewMetadata): Promise<void>;
unregisterWebview(id: string): Promise<void>;
updateWebviewMetadata(id: string, metadataDelta: Partial<RegisterWebviewMetadata>): Promise<void>;

/** Note: the VSBuffer must be a top level argument so that it can be serialized and deserialized properly */
didLoadResource(requestId: number, response: WebviewManagerDidLoadResourceResponse, responseDetails?: WebviewManagerDidLoadResourceResponseDetails): void;

setIgnoreMenuShortcuts(id: WebviewWebContentsId | WebviewWindowId, enabled: boolean): Promise<void>;
}

export interface RegisterWebviewMetadata {
readonly extensionLocation: UriComponents | undefined;
readonly localResourceRoots: readonly UriComponents[];
readonly remoteConnectionData: IRemoteConnectionData | null;
readonly portMappings: readonly IWebviewPortMapping[];
}
61 changes: 2 additions & 59 deletions src/vs/platform/webview/electron-main/webviewMainService.ts
Expand Up @@ -5,34 +5,22 @@

import { session, WebContents, webContents } from 'electron';
import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { ILogService } from 'vs/platform/log/common/log';
import { ITunnelService } from 'vs/platform/remote/common/tunnel';
import { IRequestService } from 'vs/platform/request/common/request';
import { webviewPartitionId } from 'vs/platform/webview/common/resourceLoader';
import { IWebviewManagerService, RegisterWebviewMetadata, WebviewManagerDidLoadResourceResponse, WebviewManagerDidLoadResourceResponseDetails, WebviewWebContentsId, WebviewWindowId } from 'vs/platform/webview/common/webviewManagerService';
import { WebviewPortMappingProvider } from 'vs/platform/webview/electron-main/webviewPortMappingProvider';
import { IWebviewManagerService, WebviewWebContentsId, WebviewWindowId } from 'vs/platform/webview/common/webviewManagerService';
import { WebviewProtocolProvider } from 'vs/platform/webview/electron-main/webviewProtocolProvider';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';

export class WebviewMainService extends Disposable implements IWebviewManagerService {

declare readonly _serviceBrand: undefined;

private readonly protocolProvider: WebviewProtocolProvider;
private readonly portMappingProvider: WebviewPortMappingProvider;

constructor(
@IFileService fileService: IFileService,
@ILogService logService: ILogService,
@IRequestService requestService: IRequestService,
@ITunnelService tunnelService: ITunnelService,
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
) {
super();
this.protocolProvider = this._register(new WebviewProtocolProvider(fileService, logService, requestService, windowsMainService));
this.portMappingProvider = this._register(new WebviewPortMappingProvider(tunnelService));
this._register(new WebviewProtocolProvider());

const sess = session.fromPartition(webviewPartitionId);
sess.setPermissionRequestHandler((_webContents, permission, callback) => {
Expand All @@ -48,43 +36,6 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer
});
}

public async registerWebview(id: string, windowId: number, metadata: RegisterWebviewMetadata): Promise<void> {
const extensionLocation = metadata.extensionLocation ? URI.from(metadata.extensionLocation) : undefined;

this.protocolProvider.registerWebview(id, {
...metadata,
windowId: windowId,
extensionLocation,
localResourceRoots: metadata.localResourceRoots.map(x => URI.from(x))
});

this.portMappingProvider.registerWebview(id, {
extensionLocation,
mappings: metadata.portMappings,
resolvedAuthority: metadata.remoteConnectionData,
});
}

public async unregisterWebview(id: string): Promise<void> {
this.protocolProvider.unregisterWebview(id);
this.portMappingProvider.unregisterWebview(id);
}

public async updateWebviewMetadata(id: string, metaDataDelta: Partial<RegisterWebviewMetadata>): Promise<void> {
const extensionLocation = metaDataDelta.extensionLocation ? URI.from(metaDataDelta.extensionLocation) : undefined;

this.protocolProvider.updateWebviewMetadata(id, {
...metaDataDelta,
extensionLocation,
localResourceRoots: metaDataDelta.localResourceRoots?.map(x => URI.from(x)),
});

this.portMappingProvider.updateWebviewMetadata(id, {
...metaDataDelta,
extensionLocation,
});
}

public async setIgnoreMenuShortcuts(id: WebviewWebContentsId | WebviewWindowId, enabled: boolean): Promise<void> {
let contents: WebContents | undefined;

Expand All @@ -107,12 +58,4 @@ export class WebviewMainService extends Disposable implements IWebviewManagerSer
contents.setIgnoreMenuShortcuts(enabled);
}
}

public async didLoadResource(
requestId: number,
response: WebviewManagerDidLoadResourceResponse,
responseDetails?: WebviewManagerDidLoadResourceResponseDetails,
): Promise<void> {
this.protocolProvider.didLoadResource(requestId, response, responseDetails);
}
}
103 changes: 0 additions & 103 deletions src/vs/platform/webview/electron-main/webviewPortMappingProvider.ts

This file was deleted.

0 comments on commit d05ded6

Please sign in to comment.