Split main thread webview serializer code into own file

This commit is contained in:
Matt Bierner 2020-08-20 16:38:55 -07:00
parent daf5143e35
commit 04de465524
2 changed files with 118 additions and 68 deletions

View file

@ -25,6 +25,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IProductService } from 'vs/platform/product/common/productService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo';
import { MainThreadWebviewSerializers } from 'vs/workbench/api/browser/mainThreadWebviewSerializer';
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
import { IEditorInput, IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor';
@ -104,7 +105,7 @@ const enum ModelType {
Text,
}
const webviewPanelViewType = new WebviewViewTypeTransformer('mainThreadWebview-');
export const webviewPanelViewType = new WebviewViewTypeTransformer('mainThreadWebview-');
@extHostNamedCustomer(extHostProtocol.MainContext.MainThreadWebviews)
export class MainThreadWebviews extends Disposable implements extHostProtocol.MainThreadWebviewsShape {
@ -118,12 +119,10 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
]);
private readonly _proxy: extHostProtocol.ExtHostWebviewsShape;
private readonly _proxySerializer: extHostProtocol.ExtHostWebviewSerializerShape;
private readonly _proxyViews: extHostProtocol.ExtHostWebviewViewsShape;
private readonly _proxyCustomEditors: extHostProtocol.ExtHostCustomEditorsShape;
private readonly _webviewInputs = new WebviewInputStore();
private readonly _revivers = new Map<string, IDisposable>();
private readonly _webviewViewProviders = new Map<string, IDisposable>();
private readonly _webviewViews = new Map<string, WebviewView>();
@ -131,6 +130,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
private readonly _editorProviders = new Map<string, IDisposable>();
private readonly _webviewFromDiffEditorHandles = new Set<string>();
private readonly serializers: MainThreadWebviewSerializers;
constructor(
context: extHostProtocol.IExtHostContext,
@IExtensionService extensionService: IExtensionService,
@ -149,8 +150,9 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
) {
super();
this.serializers = new MainThreadWebviewSerializers(this, context, extensionService, _editorGroupService, _webviewWorkbenchService);
this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviews);
this._proxySerializer = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewSerializer);
this._proxyViews = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewViews);
this._proxyCustomEditors = context.getProxy(extHostProtocol.ExtHostContext.ExtHostCustomEditors);
@ -167,24 +169,6 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
this.updateWebviewViewStates(this._editorService.activeEditor);
}));
// This reviver's only job is to activate extensions.
// This should trigger the real reviver to be registered from the extension host side.
this._register(_webviewWorkbenchService.registerResolver({
canResolve: (webview: WebviewInput) => {
if (webview instanceof CustomEditorInput) {
extensionService.activateByEvent(`onCustomEditor:${webview.viewType}`);
return false;
}
const viewType = webviewPanelViewType.toExternal(webview.viewType);
if (typeof viewType === 'string') {
extensionService.activateByEvent(`onWebviewPanel:${viewType}`);
}
return false;
},
resolveWebview: () => { throw new Error('not implemented'); }
}));
workingCopyFileService.registerWorkingCopyProvider((editorResource) => {
const matchedWorkingCopies: IWorkingCopy[] = [];
@ -209,6 +193,11 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
this._editorProviders.clear();
}
public addWebviewInput(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput): void {
this._webviewInputs.add(handle, input);
this.hookupWebviewEventDelegate(handle, input.webview);
}
public $createWebviewPanel(
extensionData: extHostProtocol.WebviewExtensionDescription,
handle: extHostProtocol.WebviewPanelHandle,
@ -288,53 +277,12 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
return true;
}
public $registerSerializer(viewType: string): void {
if (this._revivers.has(viewType)) {
throw new Error(`Reviver for ${viewType} already registered`);
}
this._revivers.set(viewType, this._webviewWorkbenchService.registerResolver({
canResolve: (webviewInput) => {
return webviewInput.viewType === webviewPanelViewType.fromExternal(viewType);
},
resolveWebview: async (webviewInput): Promise<void> => {
const viewType = webviewPanelViewType.toExternal(webviewInput.viewType);
if (!viewType) {
webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(webviewInput.viewType);
return;
}
const handle = webviewInput.id;
this._webviewInputs.add(handle, webviewInput);
this.hookupWebviewEventDelegate(handle, webviewInput.webview);
let state = undefined;
if (webviewInput.webview.state) {
try {
state = JSON.parse(webviewInput.webview.state);
} catch (e) {
console.error('Could not load webview state', e, webviewInput.webview.state);
}
}
try {
await this._proxySerializer.$deserializeWebviewPanel(handle, viewType, webviewInput.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options);
} catch (error) {
onUnexpectedError(error);
webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType);
}
}
}));
$registerSerializer(viewType: string): void {
this.serializers.$registerSerializer(viewType);
}
public $unregisterSerializer(viewType: string): void {
const reviver = this._revivers.get(viewType);
if (!reviver) {
throw new Error(`No reviver for ${viewType} registered`);
}
reviver.dispose();
this._revivers.delete(viewType);
$unregisterSerializer(viewType: string): void {
this.serializers.$unregisterSerializer(viewType);
}
public $registerWebviewViewProvider(viewType: string, options?: { retainContextWhenHidden?: boolean }): void {
@ -657,7 +605,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
return model;
}
private static getWebviewResolvedFailedContent(viewType: string) {
public static getWebviewResolvedFailedContent(viewType: string) {
return `<!DOCTYPE html>
<html>
<head>

View file

@ -0,0 +1,102 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { onUnexpectedError } from 'vs/base/common/errors';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { MainThreadWebviews, webviewPanelViewType } from 'vs/workbench/api/browser/mainThreadWebview';
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
import { editorGroupToViewColumn } from 'vs/workbench/api/common/shared/editor';
import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput';
import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
import { IWebviewWorkbenchService } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
export class MainThreadWebviewSerializers extends Disposable {
private readonly _proxy: extHostProtocol.ExtHostWebviewSerializerShape;
private readonly _revivers = new Map<string, IDisposable>();
constructor(
private readonly mainThreadWebviews: MainThreadWebviews,
context: extHostProtocol.IExtHostContext,
@IExtensionService extensionService: IExtensionService,
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
@IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService,
) {
super();
this._proxy = context.getProxy(extHostProtocol.ExtHostContext.ExtHostWebviewSerializer);
// This reviver's only job is to activate extensions.
// This should trigger the real reviver to be registered from the extension host side.
this._register(_webviewWorkbenchService.registerResolver({
canResolve: (webview: WebviewInput) => {
if (webview instanceof CustomEditorInput) {
extensionService.activateByEvent(`onCustomEditor:${webview.viewType}`);
return false;
}
const viewType = webviewPanelViewType.toExternal(webview.viewType);
if (typeof viewType === 'string') {
extensionService.activateByEvent(`onWebviewPanel:${viewType}`);
}
return false;
},
resolveWebview: () => { throw new Error('not implemented'); }
}));
}
public $registerSerializer(viewType: string): void {
if (this._revivers.has(viewType)) {
throw new Error(`Reviver for ${viewType} already registered`);
}
this._revivers.set(viewType, this._webviewWorkbenchService.registerResolver({
canResolve: (webviewInput) => {
return webviewInput.viewType === webviewPanelViewType.fromExternal(viewType);
},
resolveWebview: async (webviewInput): Promise<void> => {
const viewType = webviewPanelViewType.toExternal(webviewInput.viewType);
if (!viewType) {
webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(webviewInput.viewType);
return;
}
const handle = webviewInput.id;
this.mainThreadWebviews.addWebviewInput(handle, webviewInput);
let state = undefined;
if (webviewInput.webview.state) {
try {
state = JSON.parse(webviewInput.webview.state);
} catch (e) {
console.error('Could not load webview state', e, webviewInput.webview.state);
}
}
try {
await this._proxy.$deserializeWebviewPanel(handle, viewType, webviewInput.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options);
} catch (error) {
onUnexpectedError(error);
webviewInput.webview.html = MainThreadWebviews.getWebviewResolvedFailedContent(viewType);
}
}
}));
}
public $unregisterSerializer(viewType: string): void {
const reviver = this._revivers.get(viewType);
if (!reviver) {
throw new Error(`No reviver for ${viewType} registered`);
}
reviver.dispose();
this._revivers.delete(viewType);
}
}