Webview API prototype 3 (#44307)

* Webview API prototype 3

Part of #43713

Third try at refining the webview api. This pass reworks  #44165.  Major changes:

- Adds an `id` field to webviews. The id is provided by the extension and identifies the webview. It is used with the new event handling apis.

- Adds a new `onDidChangeActiveEditor` api. This is similar to `onDidChangeActiveTextEditor` but is also fired when you change webviews. It replaces the old `onFocus` and `onBlur` events on the webview itself

- Adds an `onDispose` event ot webviews. This is fired when a webview is closed by the user

- Perist webview state when the editor group changes. This is enabled for all webviews, not just those with keep alive.

* Throw error when trying to access disposed webview

* Improving webview documentation

* Clean up dispose management

* Throw if we receive a bad handle

* Move more event handling to input

* Simplify input updating

* Remove extra container property

* Fixing md security alert button

* Remove extra update container call

* Restore syncing of preview to active editor

* Fixing posting to webview

* Debounce preview updates

* Remove previewUri

* Enable direct window.postMessage instead of window.parent.postMessage

* Fixing scroll position not preserved when updating previews

* Revert parent.postMessage change.

Old behavior was correct

* Properly hide webview container on tab switch

* Make sure we only handle scroll events for the correct document

* Don't try setting negative scroll

* Revert vs code whitespace change
This commit is contained in:
Matt Bierner
2018-02-26 10:06:50 -08:00
committed by GitHub
parent de87042d3c
commit 2038b8fc7f
18 changed files with 688 additions and 363 deletions

View File

@@ -3,49 +3,66 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MainContext, MainThreadWebviewShape, IMainContext, ExtHostWebviewsShape } from './extHost.protocol';
import { MainContext, MainThreadWebviewsShape, IMainContext, ExtHostWebviewsShape, WebviewHandle } from './extHost.protocol';
import * as vscode from 'vscode';
import { Emitter } from 'vs/base/common/event';
import Event, { Emitter } from 'vs/base/common/event';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { Position } from 'vs/platform/editor/common/editor';
export class ExtHostWebview implements vscode.Webview {
public readonly editorType = 'webview';
class ExtHostWebview implements vscode.Webview {
private _title: string;
private _html: string;
private _options: vscode.WebviewOptions;
private _isDisposed: boolean = false;
private _viewColumn: vscode.ViewColumn;
public readonly onMessageEmitter = new Emitter<any>();
public readonly onMessage = this.onMessageEmitter.event;
public readonly onMessage: Event<any> = this.onMessageEmitter.event;
public readonly onBecameActiveEmitter = new Emitter<void>();
public readonly onBecameActive = this.onBecameActiveEmitter.event;
public readonly onDisposeEmitter = new Emitter<void>();
public readonly onDispose: Event<void> = this.onDisposeEmitter.event;
public readonly onBecameInactiveEmitter = new Emitter<void>();
public readonly onBecameInactive = this.onBecameInactiveEmitter.event;
public readonly onDidChangeViewColumnEmitter = new Emitter<vscode.ViewColumn>();
public readonly onDidChangeViewColumn: Event<vscode.ViewColumn> = this.onDidChangeViewColumnEmitter.event;
constructor(
private readonly _proxy: MainThreadWebviewShape,
private readonly _handle: number,
viewColumn: vscode.ViewColumn
private readonly _handle: WebviewHandle,
private readonly _proxy: MainThreadWebviewsShape,
private readonly _uri: vscode.Uri,
viewColumn: vscode.ViewColumn,
options: vscode.WebviewOptions
) {
this._viewColumn = viewColumn;
this._options = options;
}
public dispose() {
if (this._isDisposed) {
return;
}
this._isDisposed = true;
this._proxy.$disposeWebview(this._handle);
this.onDisposeEmitter.dispose();
this.onMessageEmitter.dispose();
this.onDidChangeViewColumnEmitter.dispose();
}
get uri(): vscode.Uri {
this.assertNotDisposed();
return this._uri;
}
get title(): string {
this.assertNotDisposed();
return this._title;
}
set title(value: string) {
this.assertNotDisposed();
if (this._title !== value) {
this._title = value;
this._proxy.$setTitle(this._handle, value);
@@ -53,10 +70,12 @@ class ExtHostWebview implements vscode.Webview {
}
get html(): string {
this.assertNotDisposed();
return this._html;
}
set html(value: string) {
this.assertNotDisposed();
if (this._html !== value) {
this._html = value;
this._proxy.$setHtml(this._handle, value);
@@ -64,63 +83,88 @@ class ExtHostWebview implements vscode.Webview {
}
get options(): vscode.WebviewOptions {
this.assertNotDisposed();
return this._options;
}
set options(value: vscode.WebviewOptions) {
this._proxy.$setOptions(this._handle, value);
get viewColumn(): vscode.ViewColumn {
this.assertNotDisposed();
return this._viewColumn;
}
get viewColumn(): vscode.ViewColumn {
return this._viewColumn;
set viewColumn(value: vscode.ViewColumn) {
this.assertNotDisposed();
this._viewColumn = value;
}
public postMessage(message: any): Thenable<any> {
return this._proxy.$sendMessage(this._handle, message);
}
private assertNotDisposed() {
if (this._isDisposed) {
throw new Error('Webview is disposed');
}
}
}
export class ExtHostWebviews implements ExtHostWebviewsShape {
private static _handlePool = 0;
private static handlePool = 0;
private readonly _proxy: MainThreadWebviewShape;
private readonly _proxy: MainThreadWebviewsShape;
private readonly _webviews = new Map<number, ExtHostWebview>();
private readonly _webviews = new Map<WebviewHandle, ExtHostWebview>();
constructor(
mainContext: IMainContext
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadWebview);
this._proxy = mainContext.getProxy(MainContext.MainThreadWebviews);
}
createWebview(
title: string,
uri: vscode.Uri,
viewColumn: vscode.ViewColumn,
options: vscode.WebviewOptions
): vscode.Webview {
const handle = ExtHostWebviews._handlePool++;
this._proxy.$createWebview(handle);
const handle = ExtHostWebviews.handlePool++;
if (!this._webviews.has(handle)) {
this._proxy.$createWebview(handle, uri, options);
const webview = new ExtHostWebview(handle, this._proxy, uri, viewColumn, options);
this._webviews.set(handle, webview);
}
const webview = new ExtHostWebview(this._proxy, handle, viewColumn);
this._webviews.set(handle, webview);
webview.title = title;
webview.options = options;
this._proxy.$show(handle, typeConverters.fromViewColumn(viewColumn));
return webview;
return this._webviews.get(handle);
}
$onMessage(handle: number, message: any): void {
$onMessage(handle: WebviewHandle, message: any): void {
const webview = this._webviews.get(handle);
webview.onMessageEmitter.fire(message);
}
$onBecameActive(handle: number): void {
$onDidChangeActiveWeview(handle: WebviewHandle | undefined): void {
const webview = this._webviews.get(handle);
webview.onBecameActiveEmitter.fire();
this._onDidChangeActiveWebview.fire(webview);
}
$onBecameInactive(handle: number): void {
$onDidDisposeWeview(handle: WebviewHandle): void {
const webview = this._webviews.get(handle);
webview.onBecameInactiveEmitter.fire();
if (webview) {
webview.onDisposeEmitter.fire();
}
}
$onDidChangePosition(handle: WebviewHandle, newPosition: Position): void {
const webview = this._webviews.get(handle);
if (webview) {
const newViewColumn = typeConverters.toViewColumn(newPosition);
webview.viewColumn = newViewColumn;
webview.onDidChangeViewColumnEmitter.fire(newViewColumn);
}
}
private readonly _onDidChangeActiveWebview = new Emitter<ExtHostWebview | undefined>();
public readonly onDidChangeActiveWebview = this._onDidChangeActiveWebview.event;
}