From ed8c0e4e1da0c19868c514cef86e856b3eafb2fa Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 2 Mar 2020 17:17:56 -0800 Subject: [PATCH] Add explicit wait on protocol registration Possible fix for #89038/#91506. In both cases, it looks like we try to use the protocol before it is properly registered. If this fix doesn't work, there's probably something weird going on on the electron side of things --- .../electron-browser/webviewElement.ts | 43 +++++++++++-------- .../electron-browser/webviewProtocols.ts | 37 ++++++++-------- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 00aa8576189..e2a484ad05e 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -113,23 +113,29 @@ class WebviewSession extends Disposable { } class WebviewProtocolProvider extends Disposable { + + private _resolve!: () => void; + private _reject!: () => void; + + public readonly ready: Promise; + constructor( handle: WebviewTagHandle, - private readonly _getExtensionLocation: () => URI | undefined, - private readonly _getLocalResourceRoots: () => ReadonlyArray, - private readonly _fileService: IFileService, + getExtensionLocation: () => URI | undefined, + getLocalResourceRoots: () => ReadonlyArray, + fileService: IFileService, ) { super(); - this._register(handle.onFirstLoad(contents => { - this.registerProtocols(contents); - })); - } + this.ready = new Promise((resolve, reject) => { + this._resolve = resolve; + this._reject = reject; + }); - private registerProtocols(contents: WebContents) { - registerFileProtocol(contents, WebviewResourceScheme, this._fileService, this._getExtensionLocation(), () => - this._getLocalResourceRoots() - ); + this._register(handle.onFirstLoad(contents => { + registerFileProtocol(contents, WebviewResourceScheme, fileService, getExtensionLocation(), getLocalResourceRoots) + .then(this._resolve, this._reject); + })); } } @@ -204,6 +210,7 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme private _findStarted: boolean = false; public extension: WebviewExtensionDescription | undefined; + private readonly _protocolProvider: WebviewProtocolProvider; constructor( id: string, @@ -222,11 +229,12 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme const webviewAndContents = this._register(new WebviewTagHandle(this.element!)); const session = this._register(new WebviewSession(webviewAndContents)); - this._register(new WebviewProtocolProvider( - webviewAndContents, - () => this.extension ? this.extension.location : undefined, - () => (this.content.options.localResourceRoots || []), - fileService)); + this._protocolProvider = new WebviewProtocolProvider + (webviewAndContents, + () => this.extension ? this.extension.location : undefined, + () => (this.content.options.localResourceRoots || []), + fileService); + this._register(this._protocolProvider); this._register(new WebviewPortMappingProvider( session, @@ -322,7 +330,8 @@ export class ElectronWebviewBasedWebview extends BaseWebview impleme parent.appendChild(this.element); } - protected postMessage(channel: string, data?: any): void { + protected async postMessage(channel: string, data?: any): Promise { + await this._protocolProvider.ready; this.element?.send(channel, data); } diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts index e84ce9bf6fb..39e4349cb40 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts @@ -14,24 +14,27 @@ export function registerFileProtocol( extensionLocation: URI | undefined, getRoots: () => ReadonlyArray ) { - contents.session.protocol.registerBufferProtocol(protocol, async (request, callback: any) => { - try { - const result = await loadLocalResource(URI.parse(request.url), fileService, extensionLocation, getRoots); - if (result.type === WebviewResourceResponse.Type.Success) { - return callback({ - data: Buffer.from(result.data.buffer), - mimeType: result.mimeType - }); + return new Promise((resolve, reject) => + contents.session.protocol.registerBufferProtocol(protocol, async (request, callback: any) => { + try { + const result = await loadLocalResource(URI.parse(request.url), fileService, extensionLocation, getRoots); + if (result.type === WebviewResourceResponse.Type.Success) { + return callback({ + data: Buffer.from(result.data.buffer), + mimeType: result.mimeType + }); + } + if (result.type === WebviewResourceResponse.Type.AccessDenied) { + console.error('Webview: Cannot load resource outside of protocol root'); + return callback({ error: -10 /* ACCESS_DENIED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); + } + } catch { + // noop } - if (result.type === WebviewResourceResponse.Type.AccessDenied) { - console.error('Webview: Cannot load resource outside of protocol root'); - return callback({ error: -10 /* ACCESS_DENIED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); - } - } catch { - // noop - } - return callback({ error: -2 /* FAILED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); - }); + return callback({ error: -2 /* FAILED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); + }, (err) => { + err ? reject(err) : resolve(); + })); }