Compare commits
2 commits
main
...
webview-ty
Author | SHA1 | Date | |
---|---|---|---|
ba8fa699dd | |||
671ccf261c |
|
@ -12,7 +12,7 @@
|
|||
"activationEvents": [],
|
||||
"main": "./out/extension",
|
||||
"engines": {
|
||||
"vscode": "^1.25.0"
|
||||
"vscode": "^1.55.0"
|
||||
},
|
||||
"contributes": {
|
||||
"configuration": {
|
||||
|
|
|
@ -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*/`
|
||||
<script>
|
||||
const vscode = acquireVsCodeApi();
|
||||
|
||||
window.addEventListener('message', (message) => {
|
||||
switch (message.data.type) {
|
||||
case 'add1':
|
||||
const arrayBuffer = message.data.array;
|
||||
const uint8Array = new Uint8Array(arrayBuffer);
|
||||
|
||||
for (let i = 0; i < uint8Array.length; ++i) {
|
||||
uint8Array[i] = uint8Array[i] + 1;
|
||||
}
|
||||
|
||||
vscode.postMessage({ array: arrayBuffer }, [arrayBuffer]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
vscode.postMessage({ type: 'ready' });
|
||||
</script>`);
|
||||
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*/`
|
||||
<script>
|
||||
const vscode = acquireVsCodeApi();
|
||||
|
||||
window.addEventListener('message', (message) => {
|
||||
switch (message.data.type) {
|
||||
case 'add1':
|
||||
const uint8Array = message.data.array1;
|
||||
|
||||
// This should update both buffers since they use the same ArrayBuffer storage
|
||||
const uint16Array = message.data.array2;
|
||||
for (let i = 0; i < uint16Array.length; ++i) {
|
||||
uint16Array[i] = uint16Array[i] + 1;
|
||||
}
|
||||
|
||||
vscode.postMessage({ array1: uint8Array, array2: uint16Array, }, [uint16Array.buffer]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
vscode.postMessage({ type: 'ready' });
|
||||
</script>`);
|
||||
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 {
|
||||
|
|
21
src/vs/vscode.proposed.d.ts
vendored
21
src/vs/vscode.proposed.d.ts
vendored
|
@ -2692,6 +2692,27 @@ 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` in 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+, 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<boolean>;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region https://github.com/microsoft/vscode/issues/115616 @alexr00
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -137,9 +137,9 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
|||
|
||||
public get webviewInputs(): Iterable<WebviewInput> { 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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<boolean> {
|
||||
public async $postMessage(handle: extHostProtocol.WebviewHandle, jsonMessage: string, ...buffers: VSBuffer[]): Promise<boolean> {
|
||||
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(() => {
|
||||
|
|
|
@ -668,10 +668,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<boolean>
|
||||
$postMessage(handle: WebviewHandle, value: any, ...buffers: VSBuffer[]): Promise<boolean>
|
||||
}
|
||||
|
||||
export interface MainThreadWebviewPanelsShape extends IDisposable {
|
||||
|
@ -683,6 +712,7 @@ export interface MainThreadWebviewPanelsShape extends IDisposable {
|
|||
title: string;
|
||||
webviewOptions: IWebviewOptions;
|
||||
panelOptions: IWebviewPanelOptions;
|
||||
serializeBuffersForPostMessage: boolean;
|
||||
},
|
||||
showOptions: WebviewPanelShowOptions,
|
||||
): void;
|
||||
|
@ -691,13 +721,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;
|
||||
|
@ -705,7 +735,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;
|
||||
|
@ -723,7 +753,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 >= 55;
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
|
122
src/vs/workbench/api/common/extHostWebviewMessaging.ts
Normal file
122
src/vs/workbench/api/common/extHostWebviewMessaging.ts
Normal file
|
@ -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 <extHostProtocol.WebviewMessageArrayBufferReference>{
|
||||
$$vscode_array_buffer_reference$$: true,
|
||||
index,
|
||||
};
|
||||
} else if (ArrayBuffer.isView(value)) {
|
||||
const type = getTypedArrayType(value);
|
||||
if (type) {
|
||||
const index = arrayBuffers.add(value.buffer);
|
||||
return <extHostProtocol.WebviewMessageArrayBufferReference>{
|
||||
$$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 };
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -789,7 +789,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;
|
||||
}
|
||||
|
@ -1009,7 +1010,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();
|
||||
|
@ -1219,7 +1221,7 @@ var requirejs = (function() {
|
|||
// TODO: use proper handler
|
||||
const p = new Promise<void>(resolve => {
|
||||
this.webview?.onMessage(e => {
|
||||
if (e.type === 'initializedMarkdownPreview') {
|
||||
if (e.message.type === 'initializedMarkdownPreview') {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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<T extends HTMLElement> 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<T extends HTMLElement> extends Disposable {
|
|||
private readonly _onDidReload = this._register(new Emitter<void>());
|
||||
public readonly onDidReload = this._onDidReload.event;
|
||||
|
||||
private readonly _onMessage = this._register(new Emitter<any>());
|
||||
private readonly _onMessage = this._register(new Emitter<WebviewMessageReceivedEvent>());
|
||||
public readonly onMessage = this._onMessage.event;
|
||||
|
||||
private readonly _onDidScroll = this._register(new Emitter<{ readonly scrollYPercentage: number; }>());
|
||||
|
@ -209,8 +212,8 @@ export abstract class BaseWebview<T extends HTMLElement> extends Disposable {
|
|||
private readonly _onDidDispose = this._register(new Emitter<void>());
|
||||
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 {
|
||||
|
|
|
@ -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<IMouseWheelEvent>());
|
||||
public readonly onDidWheel = this._onDidWheel.event;
|
||||
|
||||
private readonly _pendingMessages = new Set<any>();
|
||||
private readonly _pendingMessages = new Set<{ readonly message: any, readonly transfer?: readonly ArrayBuffer[] }>();
|
||||
private readonly _webview = this._register(new MutableDisposable<WebviewElement>());
|
||||
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<string | undefined>());
|
||||
public readonly onDidUpdateState: Event<string | undefined> = this._onDidUpdateState.event;
|
||||
|
||||
private readonly _onMessage = this._register(new Emitter<any>());
|
||||
public readonly onMessage: Event<any> = this._onMessage.event;
|
||||
private readonly _onMessage = this._register(new Emitter<WebviewMessageReceivedEvent>());
|
||||
public readonly onMessage = this._onMessage.event;
|
||||
|
||||
private readonly _onMissingCsp = this._register(new Emitter<ExtensionIdentifier>());
|
||||
public readonly onMissingCsp: Event<any> = 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 });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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<IMouseWheelEvent>;
|
||||
readonly onDidUpdateState: Event<string | undefined>;
|
||||
readonly onDidReload: Event<void>;
|
||||
readonly onMessage: Event<any>;
|
||||
readonly onMessage: Event<WebviewMessageReceivedEvent>;
|
||||
readonly onMissingCsp: Event<ExtensionIdentifier>;
|
||||
|
||||
postMessage(data: any): void;
|
||||
postMessage(message: any, transfer?: readonly ArrayBuffer[]): void;
|
||||
|
||||
focus(): void;
|
||||
reload(): void;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue