diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json
index d3cb465581d..0abdff1acbe 100644
--- a/extensions/vscode-api-tests/package.json
+++ b/extensions/vscode-api-tests/package.json
@@ -9,7 +9,7 @@
"activationEvents": [],
"main": "./out/extension",
"engines": {
- "vscode": "^1.25.0"
+ "vscode": "^1.55.0"
},
"contributes": {
"configuration": {
diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts
index 14947da78e2..629fbaace14 100644
--- a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts
+++ b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts
@@ -399,6 +399,118 @@ suite.skip('vscode API - webview', () => {
assert.strictEqual(await vscode.env.clipboard.readText(), expectedText);
});
}
+
+ test('webviews should transfer ArrayBuffers to and from webviews', async () => {
+ const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true }));
+ const ready = getMessage(webview);
+ webview.webview.html = createHtmlDocumentWithBody(/*html*/`
+ `);
+ await ready;
+
+ const responsePromise = getMessage(webview);
+
+ const bufferLen = 100;
+
+ {
+ const arrayBuffer = new ArrayBuffer(bufferLen);
+ const uint8Array = new Uint8Array(arrayBuffer);
+ for (let i = 0; i < bufferLen; ++i) {
+ uint8Array[i] = i;
+ }
+ webview.webview.postMessage({
+ type: 'add1',
+ array: arrayBuffer
+ });
+ }
+ {
+ const response = await responsePromise;
+ assert.ok(response.array instanceof ArrayBuffer);
+
+ const uint8Array = new Uint8Array(response.array);
+ for (let i = 0; i < bufferLen; ++i) {
+ assert.strictEqual(uint8Array[i], i + 1);
+ }
+ }
+ });
+
+ test('webviews should transfer Typed arrays to and from webviews', async () => {
+ const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true }));
+ const ready = getMessage(webview);
+ webview.webview.html = createHtmlDocumentWithBody(/*html*/`
+ `);
+ await ready;
+
+ const responsePromise = getMessage(webview);
+
+ const bufferLen = 100;
+ {
+ const arrayBuffer = new ArrayBuffer(bufferLen);
+ const uint8Array = new Uint8Array(arrayBuffer);
+ const uint16Array = new Uint16Array(arrayBuffer);
+ for (let i = 0; i < uint16Array.length; ++i) {
+ uint16Array[i] = i;
+ }
+
+ webview.webview.postMessage({
+ type: 'add1',
+ array1: uint8Array,
+ array2: uint16Array,
+ });
+ }
+ {
+ const response = await responsePromise;
+
+ assert.ok(response.array1 instanceof Uint8Array);
+ assert.ok(response.array2 instanceof Uint16Array);
+ assert.ok(response.array1.buffer === response.array2.buffer);
+
+ const uint8Array = response.array1;
+ for (let i = 0; i < bufferLen; ++i) {
+ if (i % 2 === 0) {
+ assert.strictEqual(uint8Array[i], Math.floor(i / 2) + 1);
+ } else {
+ assert.strictEqual(uint8Array[i], 0);
+ }
+ }
+ }
+ });
});
function createHtmlDocumentWithBody(body: string): string {
diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts
index 9474a7533f4..e5ffe981e95 100644
--- a/src/vs/vscode.proposed.d.ts
+++ b/src/vs/vscode.proposed.d.ts
@@ -2850,6 +2850,28 @@ declare module 'vscode' {
*/
readonly triggerKind: CodeActionTriggerKind;
}
+
+ //#endregion
+
+ //#region https://github.com/microsoft/vscode/issues/115807
+
+ export interface Webview {
+ /**
+ * @param message A json serializable message to send to the webview.
+ *
+ * For older versions of vscode, if an `ArrayBuffer` is included in `message`,
+ * it will not be serialized properly and will not be received by the webview.
+ * Similarly any TypedArrays, such as a `Uint8Array`, will be very inefficiently
+ * serialized and will also not be recreated as a typed array inside the webview.
+ *
+ * However if your extension targets vscode 1.55+ in the `engines` field of its
+ * `package.json` any `ArrayBuffer` values that appear in `message` will be more
+ * efficiently transferred to the webview and will also be recreated inside of
+ * the webview.
+ */
+ postMessage(message: any): Thenable;
+ }
+
//#endregion
//#region https://github.com/microsoft/vscode/issues/115616 @alexr00
diff --git a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts
index 140fa60e9d4..746d31461f7 100644
--- a/src/vs/workbench/api/browser/mainThreadCodeInsets.ts
+++ b/src/vs/workbench/api/browser/mainThreadCodeInsets.ts
@@ -105,7 +105,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
disposables.add(editor.onDidDispose(remove));
disposables.add(webviewZone);
disposables.add(webview);
- disposables.add(webview.onMessage(msg => this._proxy.$onDidReceiveMessage(handle, msg)));
+ disposables.add(webview.onMessage(msg => this._proxy.$onDidReceiveMessage(handle, msg.message)));
this._insets.set(handle, webviewZone);
}
diff --git a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts
index 6693da4f051..fec3341bc32 100644
--- a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts
+++ b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts
@@ -99,12 +99,12 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
this._editorProviders.clear();
}
- public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: extHostProtocol.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities): void {
- this.registerEditorProvider(CustomEditorModelType.Text, reviveWebviewExtension(extensionData), viewType, options, capabilities, true);
+ public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: extHostProtocol.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities, serializeBuffersForPostMessage: boolean): void {
+ this.registerEditorProvider(CustomEditorModelType.Text, reviveWebviewExtension(extensionData), viewType, options, capabilities, true, serializeBuffersForPostMessage);
}
- public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: extHostProtocol.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void {
- this.registerEditorProvider(CustomEditorModelType.Custom, reviveWebviewExtension(extensionData), viewType, options, {}, supportsMultipleEditorsPerDocument);
+ public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: extHostProtocol.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean, serializeBuffersForPostMessage: boolean): void {
+ this.registerEditorProvider(CustomEditorModelType.Custom, reviveWebviewExtension(extensionData), viewType, options, {}, supportsMultipleEditorsPerDocument, serializeBuffersForPostMessage);
}
private registerEditorProvider(
@@ -114,6 +114,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
options: extHostProtocol.IWebviewPanelOptions,
capabilities: extHostProtocol.CustomTextEditorCapabilities,
supportsMultipleEditorsPerDocument: boolean,
+ serializeBuffersForPostMessage: boolean,
): void {
if (this._editorProviders.has(viewType)) {
throw new Error(`Provider for ${viewType} already registered`);
@@ -133,7 +134,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
const handle = webviewInput.id;
const resource = webviewInput.resource;
- this.mainThreadWebviewPanels.addWebviewInput(handle, webviewInput);
+ this.mainThreadWebviewPanels.addWebviewInput(handle, webviewInput, { serializeBuffersForPostMessage });
webviewInput.webview.options = options;
webviewInput.webview.extension = extension;
diff --git a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts
index 0d2dc68dc3b..f7177ef7717 100644
--- a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts
+++ b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts
@@ -137,9 +137,9 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
public get webviewInputs(): Iterable { return this._webviewInputs; }
- public addWebviewInput(handle: extHostProtocol.WebviewHandle, input: WebviewInput): void {
+ public addWebviewInput(handle: extHostProtocol.WebviewHandle, input: WebviewInput, options: { serializeBuffersForPostMessage: boolean }): void {
this._webviewInputs.add(handle, input);
- this._mainThreadWebviews.addWebview(handle, input.webview);
+ this._mainThreadWebviews.addWebview(handle, input.webview, options);
input.webview.onDidDispose(() => {
this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => {
@@ -156,6 +156,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
title: string;
webviewOptions: extHostProtocol.IWebviewOptions;
panelOptions: extHostProtocol.IWebviewPanelOptions;
+ serializeBuffersForPostMessage: boolean;
},
showOptions: { viewColumn?: EditorGroupColumn, preserveFocus?: boolean; },
): void {
@@ -168,7 +169,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
const extension = reviveWebviewExtension(extensionData);
const webview = this._webviewWorkbenchService.createWebview(handle, this.webviewPanelViewType.fromExternal(viewType), initData.title, mainThreadShowOptions, reviveWebviewOptions(initData.panelOptions), reviveWebviewContentOptions(initData.webviewOptions), extension);
- this.addWebviewInput(handle, webview);
+ this.addWebviewInput(handle, webview, { serializeBuffersForPostMessage: initData.serializeBuffersForPostMessage });
/* __GDPR__
"webviews:createWebviewPanel" : {
@@ -205,7 +206,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
}
}
- public $registerSerializer(viewType: string): void {
+ public $registerSerializer(viewType: string, options: { serializeBuffersForPostMessage: boolean }): void {
if (this._revivers.has(viewType)) {
throw new Error(`Reviver for ${viewType} already registered`);
}
@@ -223,7 +224,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
const handle = webviewInput.id;
- this.addWebviewInput(handle, webviewInput);
+ this.addWebviewInput(handle, webviewInput, options);
let state = undefined;
if (webviewInput.webview.state) {
diff --git a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts
index e369b4f088d..d2d670a6089 100644
--- a/src/vs/workbench/api/browser/mainThreadWebviewViews.ts
+++ b/src/vs/workbench/api/browser/mainThreadWebviewViews.ts
@@ -55,7 +55,7 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc
public $registerWebviewViewProvider(
extensionData: extHostProtocol.WebviewExtensionDescription,
viewType: string,
- options?: { retainContextWhenHidden?: boolean }
+ options: { retainContextWhenHidden?: boolean, serializeBuffersForPostMessage: boolean }
): void {
if (this._webviewViewProviders.has(viewType)) {
throw new Error(`View provider for ${viewType} already registered`);
@@ -68,7 +68,7 @@ export class MainThreadWebviewsViews extends Disposable implements extHostProtoc
const handle = webviewView.webview.id;
this._webviewViews.set(handle, webviewView);
- this.mainThreadWebviews.addWebview(handle, webviewView.webview);
+ this.mainThreadWebviews.addWebview(handle, webviewView.webview, { serializeBuffersForPostMessage: options.serializeBuffersForPostMessage });
let state = undefined;
if (webviewView.webview.state) {
diff --git a/src/vs/workbench/api/browser/mainThreadWebviews.ts b/src/vs/workbench/api/browser/mainThreadWebviews.ts
index b49181ee42d..86550fa8e5a 100644
--- a/src/vs/workbench/api/browser/mainThreadWebviews.ts
+++ b/src/vs/workbench/api/browser/mainThreadWebviews.ts
@@ -3,6 +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 { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { isWeb } from 'vs/base/common/platform';
@@ -13,6 +14,8 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IProductService } from 'vs/platform/product/common/productService';
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
+import { serializeMessage } from 'vs/workbench/api/common/extHostWebview';
+import { deserializeWebviewMessage } from 'vs/workbench/api/common/extHostWebviewMessaging';
import { Webview, WebviewContentOptions, WebviewExtensionDescription, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview';
export class MainThreadWebviews extends Disposable implements extHostProtocol.MainThreadWebviewsShape {
@@ -39,13 +42,13 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews);
}
- public addWebview(handle: extHostProtocol.WebviewHandle, webview: WebviewOverlay): void {
+ public addWebview(handle: extHostProtocol.WebviewHandle, webview: WebviewOverlay, options: { serializeBuffersForPostMessage: boolean }): void {
if (this._webviews.has(handle)) {
throw new Error('Webview already registered');
}
this._webviews.set(handle, webview);
- this.hookupWebviewEventDelegate(handle, webview);
+ this.hookupWebviewEventDelegate(handle, webview, options);
}
public $setHtml(handle: extHostProtocol.WebviewHandle, value: string): void {
@@ -58,17 +61,23 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
webview.contentOptions = reviveWebviewContentOptions(options);
}
- public async $postMessage(handle: extHostProtocol.WebviewHandle, message: any): Promise {
+ public async $postMessage(handle: extHostProtocol.WebviewHandle, jsonMessage: string, ...buffers: VSBuffer[]): Promise {
const webview = this.getWebview(handle);
- webview.postMessage(message);
+ const { message, arrayBuffers } = deserializeWebviewMessage(jsonMessage, buffers);
+ webview.postMessage(message, arrayBuffers);
return true;
}
- private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewHandle, webview: WebviewOverlay) {
+ private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewHandle, webview: WebviewOverlay, options: { serializeBuffersForPostMessage: boolean }) {
const disposables = new DisposableStore();
disposables.add(webview.onDidClickLink((uri) => this.onDidClickLink(handle, uri)));
- disposables.add(webview.onMessage((message: any) => { this._proxy.$onMessage(handle, message); }));
+
+ disposables.add(webview.onMessage((message) => {
+ const serialized = serializeMessage(message.message, options);
+ this._proxy.$onMessage(handle, serialized.message, ...serialized.buffers);
+ }));
+
disposables.add(webview.onMissingCsp((extension: ExtensionIdentifier) => this._proxy.$onMissingCsp(handle, extension.value)));
disposables.add(webview.onDidDispose(() => {
diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts
index 7d4e886828a..5362193a889 100644
--- a/src/vs/workbench/api/common/extHost.protocol.ts
+++ b/src/vs/workbench/api/common/extHost.protocol.ts
@@ -671,10 +671,39 @@ export interface CustomTextEditorCapabilities {
readonly supportsMove?: boolean;
}
+export const enum WebviewMessageArrayBufferViewType {
+ Int8Array = 1,
+ Uint8Array = 2,
+ Uint8ClampedArray = 3,
+ Int16Array = 4,
+ Uint16Array = 5,
+ Int32Array = 6,
+ Uint32Array = 7,
+ Float32Array = 8,
+ Float64Array = 9,
+ BigInt64Array = 10,
+ BigUint64Array = 11,
+}
+
+export interface WebviewMessageArrayBufferReference {
+ readonly $$vscode_array_buffer_reference$$: true,
+
+ readonly index: number;
+
+ /**
+ * Tracks if the reference is to a view instead of directly to an ArrayBuffer.
+ */
+ readonly view?: {
+ readonly type: WebviewMessageArrayBufferViewType;
+ readonly byteLength: number;
+ readonly byteOffset: number;
+ };
+}
+
export interface MainThreadWebviewsShape extends IDisposable {
$setHtml(handle: WebviewHandle, value: string): void;
$setOptions(handle: WebviewHandle, options: IWebviewOptions): void;
- $postMessage(handle: WebviewHandle, value: any): Promise
+ $postMessage(handle: WebviewHandle, value: any, ...buffers: VSBuffer[]): Promise
}
export interface MainThreadWebviewPanelsShape extends IDisposable {
@@ -686,6 +715,7 @@ export interface MainThreadWebviewPanelsShape extends IDisposable {
title: string;
webviewOptions: IWebviewOptions;
panelOptions: IWebviewPanelOptions;
+ serializeBuffersForPostMessage: boolean;
},
showOptions: WebviewPanelShowOptions,
): void;
@@ -694,13 +724,13 @@ export interface MainThreadWebviewPanelsShape extends IDisposable {
$setTitle(handle: WebviewHandle, value: string): void;
$setIconPath(handle: WebviewHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void;
- $registerSerializer(viewType: string): void;
+ $registerSerializer(viewType: string, options: { serializeBuffersForPostMessage: boolean }): void;
$unregisterSerializer(viewType: string): void;
}
export interface MainThreadCustomEditorsShape extends IDisposable {
- $registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, capabilities: CustomTextEditorCapabilities): void;
- $registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void;
+ $registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, capabilities: CustomTextEditorCapabilities, serializeBuffersForPostMessage: boolean): void;
+ $registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean, serializeBuffersForPostMessage: boolean): void;
$unregisterEditorProvider(viewType: string): void;
$onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void;
@@ -708,7 +738,7 @@ export interface MainThreadCustomEditorsShape extends IDisposable {
}
export interface MainThreadWebviewViewsShape extends IDisposable {
- $registerWebviewViewProvider(extension: WebviewExtensionDescription, viewType: string, options?: { retainContextWhenHidden?: boolean }): void;
+ $registerWebviewViewProvider(extension: WebviewExtensionDescription, viewType: string, options: { retainContextWhenHidden?: boolean, serializeBuffersForPostMessage: boolean }): void;
$unregisterWebviewViewProvider(viewType: string): void;
$setWebviewViewTitle(handle: WebviewHandle, value: string | undefined): void;
@@ -726,7 +756,7 @@ export interface WebviewPanelViewStateData {
}
export interface ExtHostWebviewsShape {
- $onMessage(handle: WebviewHandle, message: any): void;
+ $onMessage(handle: WebviewHandle, jsonSerializedMessage: string, ...buffers: VSBuffer[]): void;
$onMissingCsp(handle: WebviewHandle, extensionId: string): void;
}
diff --git a/src/vs/workbench/api/common/extHostCustomEditors.ts b/src/vs/workbench/api/common/extHostCustomEditors.ts
index ab9e63c1c15..69557c2a49b 100644
--- a/src/vs/workbench/api/common/extHostCustomEditors.ts
+++ b/src/vs/workbench/api/common/extHostCustomEditors.ts
@@ -14,7 +14,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
-import { ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
+import { ExtHostWebviews, shouldSerializeBuffersForPostMessage, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels';
import { EditorGroupColumn } from 'vs/workbench/common/editor';
import type * as vscode from 'vscode';
@@ -183,7 +183,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
disposables.add(this._editorProviders.addTextProvider(viewType, extension, provider));
this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, {
supportsMove: !!provider.moveCustomTextEditor,
- });
+ }, shouldSerializeBuffersForPostMessage(extension));
} else {
disposables.add(this._editorProviders.addCustomProvider(viewType, extension, provider));
@@ -199,7 +199,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
}));
}
- this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, !!options.supportsMultipleEditorsPerDocument);
+ this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options.webviewOptions || {}, !!options.supportsMultipleEditorsPerDocument, shouldSerializeBuffersForPostMessage(extension));
}
return extHostTypes.Disposable.from(
diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts
index d6b9376d018..b4a280b116b 100644
--- a/src/vs/workbench/api/common/extHostWebview.ts
+++ b/src/vs/workbench/api/common/extHostWebview.ts
@@ -3,11 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
+import { VSBuffer } from 'vs/base/common/buffer';
import { Emitter, Event } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
+import { normalizeVersion, parseVersion } from 'vs/platform/extensions/common/extensionValidator';
import { ILogService } from 'vs/platform/log/common/log';
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
+import { deserializeWebviewMessage } from 'vs/workbench/api/common/extHostWebviewMessaging';
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
import type * as vscode from 'vscode';
@@ -28,6 +31,8 @@ export class ExtHostWebview implements vscode.Webview {
#isDisposed: boolean = false;
#hasCalledAsWebviewUri = false;
+ #serializeBuffersForPostMessage = false;
+
constructor(
handle: extHostProtocol.WebviewHandle,
proxy: extHostProtocol.MainThreadWebviewsShape,
@@ -43,6 +48,7 @@ export class ExtHostWebview implements vscode.Webview {
this.#initData = initData;
this.#workspace = workspace;
this.#extension = extension;
+ this.#serializeBuffersForPostMessage = shouldSerializeBuffersForPostMessage(extension);
this.#deprecationService = deprecationService;
}
@@ -104,7 +110,8 @@ export class ExtHostWebview implements vscode.Webview {
if (this.#isDisposed) {
return false;
}
- return this.#proxy.$postMessage(this.#handle, message);
+ const serialized = serializeMessage(message, { serializeBuffersForPostMessage: this.#serializeBuffersForPostMessage });
+ return this.#proxy.$postMessage(this.#handle, serialized.message, ...serialized.buffers);
}
private assertNotDisposed() {
@@ -114,6 +121,49 @@ export class ExtHostWebview implements vscode.Webview {
}
}
+export function shouldSerializeBuffersForPostMessage(extension: IExtensionDescription): boolean {
+ if (!extension.enableProposedApi) {
+ return false;
+ }
+
+ try {
+ const version = normalizeVersion(parseVersion(extension.engines.vscode));
+ return !!version && version.majorBase >= 1 && version.minorBase >= 56;
+ } catch {
+ return false;
+ }
+}
+
+export function serializeMessage(message: any, options: { serializeBuffersForPostMessage?: boolean }): { message: string, buffers: VSBuffer[] } {
+ if (options.serializeBuffersForPostMessage) {
+ // Extract all ArrayBuffers from the message and replace them with references.
+ const vsBuffers: Array<{ original: ArrayBuffer, vsBuffer: VSBuffer }> = [];
+
+ const replacer = (_key: string, value: any) => {
+ if (value && value instanceof ArrayBuffer) {
+ let index = vsBuffers.findIndex(x => x.original === value);
+ if (index === -1) {
+ const bytes = new Uint8Array(value);
+ const vsBuffer = VSBuffer.wrap(bytes);
+ index = vsBuffers.length;
+ vsBuffers.push({ original: value, vsBuffer });
+ }
+
+ return {
+ $$vscode_array_buffer_reference$$: true,
+ index,
+ };
+ }
+ return value;
+ };
+
+ const serializedMessage = JSON.stringify(message, replacer);
+ return { message: serializedMessage, buffers: vsBuffers.map(x => x.vsBuffer) };
+ } else {
+ return { message: JSON.stringify(message), buffers: [] };
+ }
+}
+
export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
private readonly _webviewProxy: extHostProtocol.MainThreadWebviewsShape;
@@ -132,10 +182,12 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
public $onMessage(
handle: extHostProtocol.WebviewHandle,
- message: any
+ jsonMessage: string,
+ ...buffers: VSBuffer[]
): void {
const webview = this.getWebview(handle);
if (webview) {
+ const { message } = deserializeWebviewMessage(jsonMessage, buffers);
webview._onMessageEmitter.fire(message);
}
}
diff --git a/src/vs/workbench/api/common/extHostWebviewMessaging.ts b/src/vs/workbench/api/common/extHostWebviewMessaging.ts
new file mode 100644
index 00000000000..06e76ab5b5e
--- /dev/null
+++ b/src/vs/workbench/api/common/extHostWebviewMessaging.ts
@@ -0,0 +1,122 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { VSBuffer } from 'vs/base/common/buffer';
+import * as extHostProtocol from './extHost.protocol';
+
+class ArrayBufferSet {
+ public readonly buffers: ArrayBuffer[] = [];
+
+ public add(buffer: ArrayBuffer): number {
+ let index = this.buffers.indexOf(buffer);
+ if (index < 0) {
+ index = this.buffers.length;
+ this.buffers.push(buffer);
+ }
+ return index;
+ }
+}
+
+export function serializeWebviewMessage(
+ message: any,
+ transfer?: readonly ArrayBuffer[]
+): { message: string, buffers: VSBuffer[] } {
+ if (transfer) {
+ // Extract all ArrayBuffers from the message and replace them with references.
+ const arrayBuffers = new ArrayBufferSet();
+
+ const replacer = (_key: string, value: any) => {
+ if (value instanceof ArrayBuffer) {
+ const index = arrayBuffers.add(value);
+ return {
+ $$vscode_array_buffer_reference$$: true,
+ index,
+ };
+ } else if (ArrayBuffer.isView(value)) {
+ const type = getTypedArrayType(value);
+ if (type) {
+ const index = arrayBuffers.add(value.buffer);
+ return {
+ $$vscode_array_buffer_reference$$: true,
+ index,
+ view: {
+ type: type,
+ byteLength: value.byteLength,
+ byteOffset: value.byteOffset,
+ }
+ };
+ }
+ }
+
+ return value;
+ };
+
+ const serializedMessage = JSON.stringify(message, replacer);
+
+ const buffers = arrayBuffers.buffers.map(arrayBuffer => {
+ const bytes = new Uint8Array(arrayBuffer);
+ return VSBuffer.wrap(bytes);
+ });
+
+ return { message: serializedMessage, buffers };
+ } else {
+ return { message: JSON.stringify(message), buffers: [] };
+ }
+}
+
+function getTypedArrayType(value: ArrayBufferView): extHostProtocol.WebviewMessageArrayBufferViewType | undefined {
+ switch (value.constructor.name) {
+ case 'Int8Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Int8Array;
+ case 'Uint8Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Uint8Array;
+ case 'Uint8ClampedArray': return extHostProtocol.WebviewMessageArrayBufferViewType.Uint8ClampedArray;
+ case 'Int16Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Int16Array;
+ case 'Uint16Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Uint16Array;
+ case 'Int32Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Int32Array;
+ case 'Uint32Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Uint32Array;
+ case 'Float32Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Float32Array;
+ case 'Float64Array': return extHostProtocol.WebviewMessageArrayBufferViewType.Float64Array;
+ case 'BigInt64Array': return extHostProtocol.WebviewMessageArrayBufferViewType.BigInt64Array;
+ case 'BigUint64Array': return extHostProtocol.WebviewMessageArrayBufferViewType.BigUint64Array;
+ }
+ return undefined;
+}
+
+export function deserializeWebviewMessage(jsonMessage: string, buffers: VSBuffer[]): { message: any, arrayBuffers: ArrayBuffer[] } {
+ const arrayBuffers: ArrayBuffer[] = buffers.map(buffer => {
+ const arrayBuffer = new ArrayBuffer(buffer.byteLength);
+ const uint8Array = new Uint8Array(arrayBuffer);
+ uint8Array.set(buffer.buffer);
+ return arrayBuffer;
+ });
+
+ const reviver = !buffers.length ? undefined : (_key: string, value: any) => {
+ if (typeof value === 'object' && (value as extHostProtocol.WebviewMessageArrayBufferReference).$$vscode_array_buffer_reference$$) {
+ const ref = value as extHostProtocol.WebviewMessageArrayBufferReference;
+ const { index } = ref;
+ const arrayBuffer = arrayBuffers[index];
+ if (ref.view) {
+ switch (ref.view.type) {
+ case extHostProtocol.WebviewMessageArrayBufferViewType.Int8Array: return new Int8Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Int8Array.BYTES_PER_ELEMENT);
+ case extHostProtocol.WebviewMessageArrayBufferViewType.Uint8Array: return new Uint8Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Uint8Array.BYTES_PER_ELEMENT);
+ case extHostProtocol.WebviewMessageArrayBufferViewType.Uint8ClampedArray: return new Uint8ClampedArray(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Uint8ClampedArray.BYTES_PER_ELEMENT);
+ case extHostProtocol.WebviewMessageArrayBufferViewType.Int16Array: return new Int16Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Int16Array.BYTES_PER_ELEMENT);
+ case extHostProtocol.WebviewMessageArrayBufferViewType.Uint16Array: return new Uint16Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Uint16Array.BYTES_PER_ELEMENT);
+ case extHostProtocol.WebviewMessageArrayBufferViewType.Int32Array: return new Int32Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Int32Array.BYTES_PER_ELEMENT);
+ case extHostProtocol.WebviewMessageArrayBufferViewType.Uint32Array: return new Uint32Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Uint32Array.BYTES_PER_ELEMENT);
+ case extHostProtocol.WebviewMessageArrayBufferViewType.Float32Array: return new Float32Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Float32Array.BYTES_PER_ELEMENT);
+ case extHostProtocol.WebviewMessageArrayBufferViewType.Float64Array: return new Float64Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / Float64Array.BYTES_PER_ELEMENT);
+ case extHostProtocol.WebviewMessageArrayBufferViewType.BigInt64Array: return new BigInt64Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / BigInt64Array.BYTES_PER_ELEMENT);
+ case extHostProtocol.WebviewMessageArrayBufferViewType.BigUint64Array: return new BigUint64Array(arrayBuffer, ref.view.byteOffset, ref.view.byteLength / BigUint64Array.BYTES_PER_ELEMENT);
+ default: throw new Error('Unknown array buffer view type');
+ }
+ }
+ return arrayBuffer;
+ }
+ return value;
+ };
+
+ const message = JSON.parse(jsonMessage, reviver);
+ return { message, arrayBuffers };
+}
diff --git a/src/vs/workbench/api/common/extHostWebviewPanels.ts b/src/vs/workbench/api/common/extHostWebviewPanels.ts
index 3e30cffcf47..ace45ef742b 100644
--- a/src/vs/workbench/api/common/extHostWebviewPanels.ts
+++ b/src/vs/workbench/api/common/extHostWebviewPanels.ts
@@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
-import { serializeWebviewOptions, ExtHostWebview, ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
+import { serializeWebviewOptions, ExtHostWebview, ExtHostWebviews, toExtensionData, shouldSerializeBuffersForPostMessage } from 'vs/workbench/api/common/extHostWebview';
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { EditorGroupColumn } from 'vs/workbench/common/editor';
import type * as vscode from 'vscode';
@@ -199,11 +199,13 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
preserveFocus: typeof showOptions === 'object' && !!showOptions.preserveFocus
};
+ const serializeBuffersForPostMessage = shouldSerializeBuffersForPostMessage(extension);
const handle = ExtHostWebviewPanels.newHandle();
this._proxy.$createWebviewPanel(toExtensionData(extension), handle, viewType, {
title,
panelOptions: serializeWebviewPanelOptions(options),
webviewOptions: serializeWebviewOptions(extension, this.workspace, options),
+ serializeBuffersForPostMessage,
}, webviewShowOptions);
const webview = this.webviews.createNewWebview(handle, options, extension);
@@ -263,7 +265,9 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
}
this._serializers.set(viewType, { serializer, extension });
- this._proxy.$registerSerializer(viewType);
+ this._proxy.$registerSerializer(viewType, {
+ serializeBuffersForPostMessage: shouldSerializeBuffersForPostMessage(extension)
+ });
return new extHostTypes.Disposable(() => {
this._serializers.delete(viewType);
diff --git a/src/vs/workbench/api/common/extHostWebviewView.ts b/src/vs/workbench/api/common/extHostWebviewView.ts
index 205ad24c09e..6f28d51b938 100644
--- a/src/vs/workbench/api/common/extHostWebviewView.ts
+++ b/src/vs/workbench/api/common/extHostWebviewView.ts
@@ -146,7 +146,10 @@ export class ExtHostWebviewViews implements extHostProtocol.ExtHostWebviewViewsS
}
this._viewProviders.set(viewType, { provider, extension });
- this._proxy.$registerWebviewViewProvider(toExtensionData(extension), viewType, webviewOptions);
+ this._proxy.$registerWebviewViewProvider(toExtensionData(extension), viewType, {
+ retainContextWhenHidden: webviewOptions?.retainContextWhenHidden,
+ serializeBuffersForPostMessage: false,
+ });
return new extHostTypes.Disposable(() => {
this._viewProviders.delete(viewType);
diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts
index 87649ab0baa..88988a7ec94 100644
--- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts
+++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts
@@ -800,7 +800,8 @@ var requirejs = (function() {
}
}));
- this._register(this.webview.onMessage((data: FromWebviewMessage | { readonly __vscode_notebook_message: undefined }) => {
+ this._register(this.webview.onMessage((message) => {
+ const data: FromWebviewMessage | { readonly __vscode_notebook_message: undefined } = message.message;
if (this._disposed) {
return;
}
@@ -1020,7 +1021,8 @@ var requirejs = (function() {
resolveFunc = resolve;
});
- const dispose = webview.onMessage((data: FromWebviewMessage) => {
+ const dispose = webview.onMessage((message) => {
+ const data: FromWebviewMessage = message.message;
if (data.__vscode_notebook_message && data.type === 'initialized') {
resolveFunc();
dispose.dispose();
@@ -1230,7 +1232,7 @@ var requirejs = (function() {
// TODO: use proper handler
const p = new Promise(resolve => {
this.webview?.onMessage(e => {
- if (e.type === 'initializedMarkdownPreview') {
+ if (e.message.type === 'initializedMarkdownPreview') {
resolve();
}
});
diff --git a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts
index 2c3f6c87d3e..086e64311e5 100644
--- a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts
+++ b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts
@@ -13,7 +13,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/browser/themeing';
-import { areWebviewContentOptionsEqual, WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview';
+import { areWebviewContentOptionsEqual, WebviewContentOptions, WebviewExtensionDescription, WebviewMessageReceivedEvent, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
export const enum WebviewMessageChannels {
@@ -120,8 +120,11 @@ export abstract class BaseWebview extends Disposable {
this._onDidClickLink.fire(uri);
}));
- this._register(this.on(WebviewMessageChannels.onmessage, (data: any) => {
- this._onMessage.fire(data);
+ this._register(this.on(WebviewMessageChannels.onmessage, (data: { message: any, transfer?: ArrayBuffer[] }) => {
+ this._onMessage.fire({
+ message: data.message,
+ transfer: data.transfer,
+ });
}));
this._register(this.on(WebviewMessageChannels.didScroll, (scrollYPercentage: number) => {
@@ -188,7 +191,7 @@ export abstract class BaseWebview extends Disposable {
private readonly _onDidReload = this._register(new Emitter());
public readonly onDidReload = this._onDidReload.event;
- private readonly _onMessage = this._register(new Emitter());
+ private readonly _onMessage = this._register(new Emitter());
public readonly onMessage = this._onMessage.event;
private readonly _onDidScroll = this._register(new Emitter<{ readonly scrollYPercentage: number; }>());
@@ -209,8 +212,8 @@ export abstract class BaseWebview extends Disposable {
private readonly _onDidDispose = this._register(new Emitter());
public readonly onDidDispose = this._onDidDispose.event;
- public postMessage(data: any): void {
- this._send('message', data);
+ public postMessage(message: any, transfer?: ArrayBuffer[]): void {
+ this._send('message', { message, transfer });
}
protected _send(channel: string, data?: any): void {
diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts
index 102f7132e9c..1a9d6c2f159 100644
--- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts
+++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts
@@ -7,12 +7,12 @@ import { Dimension } from 'vs/base/browser/dom';
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
import { memoize } from 'vs/base/common/decorators';
import { Emitter, Event } from 'vs/base/common/event';
-import { URI } from 'vs/base/common/uri';
import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
+import { URI } from 'vs/base/common/uri';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
-import { IWebviewService, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_ENABLED, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, Webview, WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewOptions, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview';
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
+import { IWebviewService, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_ENABLED, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, Webview, WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewMessageReceivedEvent, WebviewOptions, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview';
/**
* Webview editor overlay that creates and destroys the underlying webview as needed.
@@ -22,7 +22,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv
private readonly _onDidWheel = this._register(new Emitter());
public readonly onDidWheel = this._onDidWheel.event;
- private readonly _pendingMessages = new Set();
+ 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());
@@ -182,7 +182,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv
this._onDidUpdateState.fire(state);
}));
- this._pendingMessages.forEach(msg => webview.postMessage(msg));
+ this._pendingMessages.forEach(msg => webview.postMessage(msg.message, msg.transfer));
this._pendingMessages.clear();
}
@@ -244,17 +244,17 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewOv
private readonly _onDidUpdateState = this._register(new Emitter());
public readonly onDidUpdateState: Event = this._onDidUpdateState.event;
- private readonly _onMessage = this._register(new Emitter());
- public readonly onMessage: Event = this._onMessage.event;
+ private readonly _onMessage = this._register(new Emitter());
+ public readonly onMessage = this._onMessage.event;
private readonly _onMissingCsp = this._register(new Emitter());
public readonly onMissingCsp: Event = this._onMissingCsp.event;
- postMessage(data: any): void {
+ public postMessage(message: any, transfer?: readonly ArrayBuffer[]): void {
if (this._webview.value) {
- this._webview.value.postMessage(data);
+ this._webview.value.postMessage(message, transfer);
} else {
- this._pendingMessages.add(data);
+ this._pendingMessages.add({ message, transfer });
}
}
diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js
index e7fb4afe174..6224340b658 100644
--- a/src/vs/workbench/contrib/webview/browser/pre/main.js
+++ b/src/vs/workbench/contrib/webview/browser/pre/main.js
@@ -150,13 +150,13 @@
*/
function getVsCodeApiScript(allowMultipleAPIAcquire, useParentPostMessage, state) {
const encodedState = state ? encodeURIComponent(state) : undefined;
- return `
+ return /* js */`
globalThis.acquireVsCodeApi = (function() {
const originalPostMessage = window.parent['${useParentPostMessage ? 'postMessage' : vscodePostMessageFuncName}'].bind(window.parent);
- const doPostMessage = (channel, data) => {
+ const doPostMessage = (channel, data, transfer) => {
${useParentPostMessage
- ? `originalPostMessage({ command: channel, data: data }, '*');`
- : `originalPostMessage(channel, data);`
+ ? `originalPostMessage({ command: channel, data: data }, '*', transfer);`
+ : `originalPostMessage(channel, data, transfer);`
}
};
@@ -170,8 +170,8 @@
}
acquired = true;
return Object.freeze({
- postMessage: function(msg) {
- doPostMessage('onmessage', msg);
+ postMessage: function(message, transfer) {
+ doPostMessage('onmessage', { message, transfer }, transfer);
},
setState: function(newState) {
state = newState;
@@ -197,6 +197,7 @@
// state
let firstLoad = true;
let loadTimeout;
+ /** @type {Array<{ readonly message: any, transfer?: ArrayBuffer[] }>} */
let pendingMessages = [];
const initData = {
@@ -622,8 +623,8 @@
contentWindow.focus();
}
- pendingMessages.forEach((data) => {
- contentWindow.postMessage(data, '*');
+ pendingMessages.forEach((message) => {
+ contentWindow.postMessage(message.message, '*', message.transfer);
});
pendingMessages = [];
}
@@ -678,12 +679,12 @@
});
// Forward message to the embedded iframe
- host.onMessage('message', (_event, data) => {
+ host.onMessage('message', (_event, /** @type {{message: any, transfer?: ArrayBuffer[] }} */ data) => {
const pending = getPendingFrame();
if (!pending) {
const target = getActiveFrame();
if (target) {
- target.contentWindow.postMessage(data, '*');
+ target.contentWindow.postMessage(data.message, '*', data.transfer);
return;
}
}
@@ -707,7 +708,7 @@
onBlur: () => host.postMessage('did-blur')
});
- (/** @type {any} */ (window))[vscodePostMessageFuncName] = (command, data) => {
+ (/** @type {any} */ (window))[vscodePostMessageFuncName] = (command, data, transfer) => {
switch (command) {
case 'onmessage':
case 'do-update-state':
diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts
index 63b8303ad67..5764a055c11 100644
--- a/src/vs/workbench/contrib/webview/browser/webview.ts
+++ b/src/vs/workbench/contrib/webview/browser/webview.ts
@@ -101,6 +101,11 @@ export interface IDataLinkClickEvent {
downloadName?: string;
}
+export interface WebviewMessageReceivedEvent {
+ readonly message: any;
+ readonly transfer?: readonly ArrayBuffer[];
+}
+
export interface Webview extends IDisposable {
readonly id: string;
@@ -123,10 +128,10 @@ export interface Webview extends IDisposable {
readonly onDidWheel: Event;
readonly onDidUpdateState: Event;
readonly onDidReload: Event;
- readonly onMessage: Event;
+ readonly onMessage: Event;
readonly onMissingCsp: Event;
- postMessage(data: any): void;
+ postMessage(message: any, transfer?: readonly ArrayBuffer[]): void;
focus(): void;
reload(): void;
diff --git a/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js b/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js
index bac642ca74c..5c6a14cbd4b 100644
--- a/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js
+++ b/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js
@@ -65,8 +65,8 @@
document.addEventListener('DOMContentLoaded', e => {
// Forward messages from the embedded iframe
- window.onmessage = (message) => {
- ipcRenderer.sendToHost(message.data.command, message.data.data);
+ window.onmessage = (/** @type {MessageEvent} */ event) => {
+ ipcRenderer.sendToHost(event.data.command, event.data.data);
};
});