From 9c467b969c39fc86be5f4c8eaa40dfdf07b5870a Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 21 Jul 2020 11:06:53 -0700 Subject: [PATCH] lazy load webview if there is no webview outputs or not kernel dependencies --- .../src/notebook.test.ts | 2 +- .../notebook/browser/notebookEditorWidget.ts | 124 ++++++++++++------ .../view/renderers/backLayerWebView.ts | 14 +- 3 files changed, 97 insertions(+), 43 deletions(-) diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-notebook-tests/src/notebook.test.ts index de8ae1952c5..c5dc289d3b8 100644 --- a/extensions/vscode-notebook-tests/src/notebook.test.ts +++ b/extensions/vscode-notebook-tests/src/notebook.test.ts @@ -933,7 +933,7 @@ suite('webview', () => { // await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); // assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); // const uri = vscode.notebook.activeNotebookEditor!.asWebviewUri(vscode.Uri.file('./hello.png')); - // assert.equal(uri.scheme, 'vscode-resource'); + // assert.equal(uri.scheme, 'vscode-webview-resource'); // await vscode.commands.executeCommand('workbench.action.closeAllEditors'); // }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index cb56c3475d7..6070a6cffc2 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -75,6 +75,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private _overlayContainer!: HTMLElement; private _body!: HTMLElement; private _webview: BackLayerWebView | null = null; + private _webviewResolved: boolean = false; + private _webviewResolvePromise: Promise | null = null; private _webviewTransparentCover: HTMLElement | null = null; private _list: INotebookCellList | undefined; private _dndController: CellDragAndDropController | null = null; @@ -591,7 +593,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor // @deprecated if (provider && provider.kernel) { // it has a builtin kernel, don't automatically choose a kernel - this._loadKernelPreloads(provider.providerExtensionLocation, provider.kernel); + await this._loadKernelPreloads(provider.providerExtensionLocation, provider.kernel); tokenSource.dispose(); return; } @@ -610,7 +612,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor // the provider doesn't have a builtin kernel, choose a kernel this.activeKernel = availableKernels[0]; if (this.activeKernel) { - this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); + await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); } tokenSource.dispose(); @@ -630,7 +632,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } if (this.activeKernel) { - this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); + await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); await this.activeKernel.resolve(this.viewModel!.uri, this.getId(), tokenSource.token); } @@ -643,7 +645,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor if (kernelsFromSameExtension.length) { const preferedKernel = kernelsFromSameExtension.find(kernel => kernel.isPreferred) || kernelsFromSameExtension[0]; this.activeKernel = preferedKernel; - this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); + await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); await preferedKernel.resolve(this.viewModel!.uri, this.getId(), tokenSource.token); tokenSource.dispose(); return; @@ -652,15 +654,16 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor // the provider doesn't have a builtin kernel, choose a kernel this.activeKernel = kernels[0]; if (this.activeKernel) { - this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); + await this._loadKernelPreloads(this.activeKernel.extensionLocation, this.activeKernel); await this.activeKernel.resolve(this.viewModel!.uri, this.getId(), tokenSource.token); } tokenSource.dispose(); } - private _loadKernelPreloads(extensionLocation: URI, kernel: INotebookKernelInfoDto) { - if (kernel.preloads) { + private async _loadKernelPreloads(extensionLocation: URI, kernel: INotebookKernelInfoDto) { + if (kernel.preloads && kernel.preloads.length) { + await this._resolveWebview(); this._webview?.updateKernelPreloads([extensionLocation], kernel.preloads.map(preload => URI.revive(preload))); } } @@ -675,34 +678,63 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._notebookExecuting?.set(notebookMetadata.runState === NotebookRunState.Running); } + private async _resolveWebview(): Promise { + if (!this.textModel) { + return null; + } + + if (this._webviewResolvePromise) { + return this._webviewResolvePromise; + } + + if (!this._webview) { + this._webview = this.instantiationService.createInstance(BackLayerWebView, this, this.getId(), this.textModel!.uri); + // attach the webview container to the DOM tree first + this._list?.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element); + } + + this._webviewResolvePromise = new Promise(async resolve => { + await this._webview!.createWebview(); + this._webview!.webview!.onDidBlur(() => { + this._outputFocus?.set(false); + this.updateEditorFocus(); + + if (this._overlayContainer.contains(document.activeElement)) { + this._webiewFocused = false; + } + }); + this._webview!.webview!.onDidFocus(() => { + this._outputFocus?.set(true); + this.updateEditorFocus(); + this._onDidFocusEmitter.fire(); + + if (this._overlayContainer.contains(document.activeElement)) { + this._webiewFocused = true; + } + }); + + this._localStore.add(this._webview!.onMessage(({ message, forRenderer }) => { + if (this.viewModel) { + this.notebookService.onDidReceiveMessage(this.viewModel.viewType, this.getId(), forRenderer, message); + } + })); + + if (this.viewModel && this.viewModel!.renderers.size) { + this._webview?.updateRendererPreloads(this.viewModel!.renderers); + } + + this._webviewResolved = true; + + resolve(this._webview!); + }); + + return this._webviewResolvePromise; + } + private async _createWebview(id: string, resource: URI): Promise { this._webview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource); // attach the webview container to the DOM tree first this._list?.rowsContainer.insertAdjacentElement('afterbegin', this._webview.element); - await this._webview.createWebview(); - this._webview.webview.onDidBlur(() => { - this._outputFocus?.set(false); - this.updateEditorFocus(); - - if (this._overlayContainer.contains(document.activeElement)) { - this._webiewFocused = false; - } - }); - this._webview.webview.onDidFocus(() => { - this._outputFocus?.set(true); - this.updateEditorFocus(); - this._onDidFocusEmitter.fire(); - - if (this._overlayContainer.contains(document.activeElement)) { - this._webiewFocused = true; - } - }); - - this._localStore.add(this._webview.onMessage(({ message, forRenderer }) => { - if (this.viewModel) { - this.notebookService.onDidReceiveMessage(this.viewModel.viewType, this.getId(), forRenderer, message); - } - })); } private async _attachModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined) { @@ -736,10 +768,17 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } } - this._webview?.updateRendererPreloads(this.viewModel.renderers); + if (this.viewModel.renderers.size) { + await this._resolveWebview(); + this._webview?.updateRendererPreloads(this.viewModel.renderers); + } this._localStore.add(this._list!.onWillScroll(e => { - this._webview!.updateViewScrollTop(-e.scrollTop, []); + if (!this._webviewResolved) { + return; + } + + this._webview?.updateViewScrollTop(-e.scrollTop, []); this._webviewTransparentCover!.style.top = `${e.scrollTop}px`; })); @@ -751,6 +790,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor const scrollTop = this._list?.scrollTop || 0; const scrollHeight = this._list?.scrollHeight || 0; + + if (!this._webviewResolved) { + return; + } + this._webview!.element.style.height = `${scrollHeight}px`; if (this._webview?.insetMapping) { @@ -1375,6 +1419,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return; } + await this._resolveWebview(); + let preloads = this._notebookViewModel!.renderers; if (!this._webview!.insetMapping.has(output)) { @@ -1389,7 +1435,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } removeInset(output: IProcessedOutput) { - if (!this._webview) { + if (!this._webview || !this._webviewResolved) { return; } @@ -1397,7 +1443,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } hideInset(output: IProcessedOutput) { - if (!this._webview) { + if (!this._webview || !this._webviewResolved) { return; } @@ -1409,10 +1455,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } postMessage(forRendererId: string | undefined, message: any) { + if (!this._webview || !this._webviewResolved) { + return; + } + if (forRendererId === undefined) { - this._webview?.webview.postMessage(message); + this._webview.webview?.postMessage(message); } else { - this._webview?.postRendererMessage(forRendererId, message); + this._webview.postRendererMessage(forRendererId, message); } } 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 41761f5ce9e..35a3ebf3654 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -219,7 +219,7 @@ export interface INotebookWebviewMessage { let version = 0; export class BackLayerWebView extends Disposable { element: HTMLElement; - webview!: WebviewElement; + webview: WebviewElement | undefined = undefined; insetMapping: Map = new Map(); hiddenInsetMapping: Set = new Set(); reversedInsetMapping: Map = new Map(); @@ -714,7 +714,7 @@ ${loaderJs} return; } - this.webview.focus(); + this.webview?.focus(); } focusOutput(cellId: string) { @@ -722,7 +722,7 @@ ${loaderJs} return; } - this.webview.focus(); + this.webview?.focus(); setTimeout(() => { // Need this, or focus decoration is not shown. No clue. this._sendMessageToWebview({ type: 'focus-output', @@ -814,6 +814,10 @@ ${loaderJs} } private _updatePreloads(resources: IPreloadResource[], source: 'renderer' | 'kernel') { + if (!this.webview) { + return; + } + const mixedResourceRoots = [...(this.localResourceRootsCache || []), ...this.rendererRootsCache, ...this.kernelRootsCache]; this.webview.localResourcesRoot = mixedResourceRoots; @@ -830,7 +834,7 @@ ${loaderJs} return; } - this.webview.postMessage(message); + this.webview?.postMessage(message); } clearPreloadsCache() { @@ -839,7 +843,7 @@ ${loaderJs} dispose() { this._disposed = true; - this.webview.dispose(); + this.webview?.dispose(); super.dispose(); } }