Skip to content

Commit f3c3463

Browse files
mjbvzrebornix
andauthored
Fix race when loading notebook webviews (microsoft#162472) (microsoft#162694)
* Fix race when loading notebook webviews This tries to fix microsoft#161520 by doing the following: - Wait for basic initialization to finish before posting messages - `initializedMarkup` should wait for the correct response instead of the first response This fixes the issue in my testing, but causes the layout to shift around during load * wait for js preload to be initialized in webview * Removed pending message queue * Remove extra async Co-authored-by: rebornix <[email protected]> Co-authored-by: rebornix <[email protected]>
1 parent a12109a commit f3c3463

File tree

3 files changed

+37
-24
lines changed

3 files changed

+37
-24
lines changed

src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor
4747
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
4848
import { FromWebviewMessage, IAckOutputHeight, IClickedDataUrlMessage, ICodeBlockHighlightRequest, IContentWidgetTopRequest, IControllerPreload, ICreationContent, ICreationRequestMessage, IFindMatch, IMarkupCellInitialization, RendererMetadata, ToWebviewMessage } from './webviewMessages';
4949
import { compressOutputItemStreams } from 'vs/workbench/contrib/notebook/browser/view/renderers/stdOutErrorPreProcessor';
50+
import { DeferredPromise } from 'vs/base/common/async';
5051

5152
export interface ICachedInset<K extends ICommonCellInfo> {
5253
outputId: string;
@@ -110,10 +111,14 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
110111
private readonly _onMessage = this._register(new Emitter<INotebookWebviewMessage>());
111112
private readonly _preloadsCache = new Set<string>();
112113
public readonly onMessage: Event<INotebookWebviewMessage> = this._onMessage.event;
113-
private _initialized?: Promise<void>;
114114
private _disposed = false;
115115
private _currentKernel?: INotebookKernel;
116116

117+
private _initialized?: DeferredPromise<void>;
118+
private _webviewPreloadInitialized?: DeferredPromise<void>;
119+
private firstInit = true;
120+
private initializeMarkupPromise?: { readonly requestId: string; readonly p: DeferredPromise<void>; readonly isFirstInit: boolean };
121+
117122
private readonly nonce = UUID.generateUuid();
118123

119124
constructor(
@@ -452,11 +457,9 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
452457
}
453458

454459
let coreDependencies = '';
455-
let resolveFunc: () => void;
456460

457-
this._initialized = new Promise<void>((resolve) => {
458-
resolveFunc = resolve;
459-
});
461+
this._initialized = new DeferredPromise();
462+
this._webviewPreloadInitialized = new DeferredPromise();
460463

461464
if (!isWeb) {
462465
const loaderUri = FileAccess.asFileUri('vs/loader.js', require);
@@ -469,7 +472,7 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
469472
</script>`;
470473
const htmlContent = this.generateContent(coreDependencies, baseUrl.toString());
471474
this._initialize(htmlContent);
472-
resolveFunc!();
475+
this._initialized.complete();
473476
} else {
474477
const loaderUri = FileAccess.asBrowserUri('vs/loader.js', require);
475478

@@ -493,16 +496,16 @@ var requirejs = (function() {
493496

494497
const htmlContent = this.generateContent(coreDependencies, baseUrl.toString());
495498
this._initialize(htmlContent);
496-
resolveFunc!();
499+
this._initialized!.complete();
497500
}, error => {
498501
// the fetch request is rejected
499502
const htmlContent = this.generateContent(coreDependencies, baseUrl.toString());
500503
this._initialize(htmlContent);
501-
resolveFunc!();
504+
this._initialized!.complete();
502505
});
503506
}
504507

505-
await this._initialized;
508+
await this._initialized.p;
506509
}
507510

508511
private getNotebookBaseUri() {
@@ -590,9 +593,17 @@ var requirejs = (function() {
590593

591594
switch (data.type) {
592595
case 'initialized': {
596+
this._webviewPreloadInitialized?.complete();
593597
this.initializeWebViewState();
594598
break;
595599
}
600+
case 'initializedMarkup': {
601+
if (this.initializeMarkupPromise?.requestId === data.requestId) {
602+
this.initializeMarkupPromise?.p.complete();
603+
this.initializeMarkupPromise = undefined;
604+
}
605+
break;
606+
}
596607
case 'dimension': {
597608
for (const update of data.updates) {
598609
const height = update.height;
@@ -932,8 +943,6 @@ var requirejs = (function() {
932943
];
933944
}
934945

935-
private firstInit = true;
936-
937946
private initializeWebViewState() {
938947
this._preloadsCache.clear();
939948
if (this._currentKernel) {
@@ -944,9 +953,9 @@ var requirejs = (function() {
944953
this._sendMessageToWebview({ ...inset.cachedCreation, initiallyHidden: this.hiddenInsetMapping.has(output) });
945954
}
946955

947-
if (this.firstInit) {
956+
if (this.initializeMarkupPromise?.isFirstInit) {
948957
// On first run the contents have already been initialized so we don't need to init them again
949-
this.firstInit = false;
958+
// no op
950959
} else {
951960
const mdCells = [...this.markupPreviewMapping.values()];
952961
this.markupPreviewMapping.clear();
@@ -1160,15 +1169,16 @@ var requirejs = (function() {
11601169
return;
11611170
}
11621171

1163-
// TODO: use proper handler
1164-
const p = new Promise<void>(resolve => {
1165-
const sub = this.webview?.onMessage(e => {
1166-
if (e.message.type === 'initializedMarkup') {
1167-
resolve();
1168-
sub?.dispose();
1169-
}
1170-
});
1171-
});
1172+
this.initializeMarkupPromise?.p.complete();
1173+
const requestId = UUID.generateUuid();
1174+
this.initializeMarkupPromise = { p: new DeferredPromise(), requestId, isFirstInit: this.firstInit };
1175+
1176+
if (this._webviewPreloadInitialized) {
1177+
// wait for webview preload script module to be loaded
1178+
await this._webviewPreloadInitialized.p;
1179+
}
1180+
1181+
this.firstInit = false;
11721182

11731183
for (const cell of cells) {
11741184
this.markupPreviewMapping.set(cell.cellId, cell);
@@ -1177,9 +1187,10 @@ var requirejs = (function() {
11771187
this._sendMessageToWebview({
11781188
type: 'initializeMarkup',
11791189
cells,
1190+
requestId,
11801191
});
11811192

1182-
await p;
1193+
return this.initializeMarkupPromise.p.p;
11831194
}
11841195

11851196
/**

src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ export interface ICellDragEndMessage extends BaseToWebviewMessage {
138138

139139
export interface IInitializedMarkupMessage extends BaseToWebviewMessage {
140140
readonly type: 'initializedMarkup';
141+
readonly requestId: string;
141142
}
142143

143144
export interface ICodeBlockHighlightRequest {
@@ -351,6 +352,7 @@ export interface IMarkupCellInitialization {
351352
export interface IInitializeMarkupCells {
352353
readonly type: 'initializeMarkup';
353354
readonly cells: readonly IMarkupCellInitialization[];
355+
readonly requestId: string;
354356
}
355357

356358
export interface INotebookStylesMessage {

src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1048,7 +1048,7 @@ async function webviewPreloads(ctx: PreloadContext) {
10481048
await Promise.all(event.data.cells.map(info => viewModel.ensureMarkupCell(info)));
10491049
} finally {
10501050
dimensionUpdater.updateImmediately();
1051-
postNotebookMessage('initializedMarkup', {});
1051+
postNotebookMessage('initializedMarkup', { requestId: event.data.requestId });
10521052
}
10531053
break;
10541054
}

0 commit comments

Comments
 (0)