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 | 41 +++++++++++-------- .../electron-browser/webviewProtocols.ts | 37 +++++++++-------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 00aa85761895c..e2a484ad05e99 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -113,24 +113,30 @@ 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.ready = new Promise((resolve, reject) => { + this._resolve = resolve; + this._reject = reject; + }); + this._register(handle.onFirstLoad(contents => { - this.registerProtocols(contents); + registerFileProtocol(contents, WebviewResourceScheme, fileService, getExtensionLocation(), getLocalResourceRoots) + .then(this._resolve, this._reject); })); } - - private registerProtocols(contents: WebContents) { - registerFileProtocol(contents, WebviewResourceScheme, this._fileService, this._getExtensionLocation(), () => - this._getLocalResourceRoots() - ); - } } class WebviewPortMappingProvider extends Disposable { @@ -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 e84ce9bf6fbcb..39e4349cb40ce 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(); + })); }