mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-20 16:49:06 +01:00
Exploration: Better transferring of TypedArrays used in Webview.postMessage (#115664)
* Improve passing of ArrayBuffers to and from webviews Fixes #115807 * Serialize and restore typed arrays too This also makes it so that if you pass the same ArrayBuffer twice in an object, we use a single object on the receiver side too * Fix spelling * Require VS Code 1.56+
This commit is contained in:
@@ -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 <extHostProtocol.WebviewMessageArrayBufferReference>{
|
||||
$$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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user