diff --git a/src/vs/workbench/api/browser/mainThreadWebviews.ts b/src/vs/workbench/api/browser/mainThreadWebviews.ts index 15e71145210..ef088e5c054 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviews.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviews.ts @@ -65,8 +65,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma public async $postMessage(handle: extHostProtocol.WebviewHandle, jsonMessage: string, ...buffers: VSBuffer[]): Promise { const webview = this.getWebview(handle); const { message, arrayBuffers } = deserializeWebviewMessage(jsonMessage, buffers); - webview.postMessage(message, arrayBuffers); - return true; + return webview.postMessage(message, arrayBuffers); } private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewHandle, webview: IOverlayWebview, options: { serializeBuffersForPostMessage: boolean }) { diff --git a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts index bb25dfdc171..7231d6577fb 100644 --- a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts +++ b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts @@ -21,7 +21,6 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { private readonly _onDidWheel = this._register(new Emitter()); public readonly onDidWheel = this._onDidWheel.event; - private readonly _pendingMessages = new Set<{ readonly message: any; readonly transfer?: readonly ArrayBuffer[] }>(); private readonly _webview = this._register(new MutableDisposable()); private readonly _webviewEvents = this._register(new DisposableStore()); @@ -199,9 +198,6 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { this._state = state; this._onDidUpdateState.fire(state); })); - - this._pendingMessages.forEach(msg => webview.postMessage(msg.message, msg.transfer)); - this._pendingMessages.clear(); } this.container.style.visibility = 'visible'; @@ -268,12 +264,11 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { private readonly _onMissingCsp = this._register(new Emitter()); public readonly onMissingCsp: Event = this._onMissingCsp.event; - public postMessage(message: any, transfer?: readonly ArrayBuffer[]): void { - if (this._webview.value) { - this._webview.value.postMessage(message, transfer); - } else { - this._pendingMessages.add({ message, transfer }); + public async postMessage(message: any, transfer?: readonly ArrayBuffer[]): Promise { + if (!this._webview.value) { + return false; } + return this._webview.value.postMessage(message, transfer); } focus(): void { this._webview.value?.focus(); } diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index 0db26467cba..284a3d7f897 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -183,7 +183,7 @@ export interface IWebview extends IDisposable { readonly onMessage: Event; readonly onMissingCsp: Event; - postMessage(message: any, transfer?: readonly ArrayBuffer[]): void; + postMessage(message: any, transfer?: readonly ArrayBuffer[]): Promise; focus(): void; reload(): void; diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index 27d6ce75d84..c790753eb0b 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -85,10 +85,11 @@ namespace WebviewState { readonly type = Type.Initializing; constructor( - public readonly pendingMessages: Array<{ + public pendingMessages: Array<{ readonly channel: string; readonly data?: any; readonly transferable: Transferable[]; + readonly resolve: (posted: boolean) => void; }> ) { } } @@ -369,6 +370,13 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD this.messagePort = undefined; + if (this._state.type === WebviewState.Type.Initializing) { + for (const message of this._state.pendingMessages) { + message.resolve(false); + } + this._state.pendingMessages = []; + } + this._onDidDispose.fire(); this._resourceLoadingCts.dispose(true); @@ -410,15 +418,18 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD private readonly _onDidDispose = this._register(new Emitter()); public readonly onDidDispose = this._onDidDispose.event; - public postMessage(message: any, transfer?: ArrayBuffer[]): void { - this._send('message', { message, transfer }); + public postMessage(message: any, transfer?: ArrayBuffer[]): Promise { + return this._send('message', { message, transfer }); } - protected _send(channel: string, data?: any, transferable: Transferable[] = []): void { + protected async _send(channel: string, data?: any, transferable: Transferable[] = []): Promise { if (this._state.type === WebviewState.Type.Initializing) { - this._state.pendingMessages.push({ channel, data, transferable }); + let resolve: (x: boolean) => void; + const promise = new Promise(r => resolve = r); + this._state.pendingMessages.push({ channel, data, transferable, resolve: resolve! }); + return promise; } else { - this.doPostMessage(channel, data, transferable); + return this.doPostMessage(channel, data, transferable); } } @@ -494,10 +505,12 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD return uri.scheme + '://' + uri.authority.toLowerCase(); } - private doPostMessage(channel: string, data?: any, transferable: Transferable[] = []): void { + private doPostMessage(channel: string, data?: any, transferable: Transferable[] = []): boolean { if (this.element && this.messagePort) { this.messagePort.postMessage({ channel, args: data }, transferable); + return true; } + return false; } protected on(channel: WebviewMessageChannels, handler: (data: T, e: MessageEvent) => void): IDisposable { diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 02c2329502b..f769fd6a8b0 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -8066,6 +8066,19 @@ declare module 'vscode' { * `package.json`, any `ArrayBuffer` values that appear in `message` will be more * efficiently transferred to the webview and will also be correctly recreated inside * of the webview. + * + * @return A promise that resolves when the message is posted to a webview or when it is + * dropped because the message was not deliverable. + * + * Returns `true` if the message was posted to the webview. Messages can only be posted to + * live webviews (i.e. either visible webviews or hidden webviews that set `retainContextWhenHidden`). + * + * A response of `true` does not mean that the message was actually received by the webview. + * For example, no message listeners may be have been hooked up inside the webview or the webview may + * have been destroyed after the message was posted but before it was received. + * + * If you want confirm that a message as actually received, you can try having your webview posting a + * confirmation message back to your extension. */ postMessage(message: any): Thenable;