From 3c73f918bc70bb5d3e4f9a3d575c101335f017f4 Mon Sep 17 00:00:00 2001 From: Akshat Anand Date: Sun, 19 Jul 2026 03:23:20 +0530 Subject: [PATCH 1/2] fix: avoid retained webview flash at default iframe size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Defer OverlayWebview unhide until a connected CSS anchor is bound so retained custom editors do not briefly paint at ~300×150 on reveal. --- .../contrib/webview/browser/overlayWebview.ts | 22 +++++++++++++++---- .../webviewPanel/browser/webviewEditor.ts | 14 +++++++----- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts index a47e2a26d4b796..d1cd2c69555cf2 100644 --- a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts +++ b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts @@ -148,9 +148,12 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { this._windowId = targetWindow.vscodeWindowId; this._show(targetWindow); - if (this._anchorState) { + // Only re-apply a still-connected anchor. Unhide happens in `_updateVisibility` + // once a connected anchor is bound (see https://github.com/microsoft/vscode/issues/323890). + if (this._anchorState?.anchorElement.isConnected) { this.overlayLayout.setAnchorElement(this._anchorState.anchorElement, { clippingContainer: this._anchorState.clippingContainer }); } + this._updateVisibility(); if (oldOwner !== owner) { const contextKeyService = (scopedContextKeyService || this._baseContextKeyService); @@ -200,6 +203,18 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { this._anchorState = { anchorElement, clippingContainer }; // Force the overlay layout to be created if it doesn't exist this.overlayLayout.setAnchorElement(anchorElement, { clippingContainer }); + this._updateVisibility(); + } + + private _updateVisibility(): void { + if (!this._overlayLayout) { + return; + } + + // Keep retained iframes hidden until a connected CSS anchor is bound so they + // do not briefly paint at the browser default size (~300×150). + const shouldShow = !!this._owner && !!this._anchorState?.anchorElement.isConnected; + this._overlayLayout.content.style.visibility = shouldShow ? 'visible' : 'hidden'; } private _show(targetWindow: CodeWindow) { @@ -274,9 +289,8 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { this._shouldShowFindWidgetOnRestore = false; } - if (this._overlayLayout) { - this._overlayLayout.content.style.visibility = 'visible'; - } + // Do not unhide here: callers bind the CSS anchor after `claim`/`_show`. + // Showing early lets retained iframes flash at the default size (#323890). } public setHtml(html: string) { diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts index 75a9d7e94dcc2b..17eb4b65a3a9a6 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditor.ts @@ -161,6 +161,15 @@ export class WebviewEditor extends EditorPane { } private claimWebview(input: WebviewInput): void { + // Check if this editor is inside a modal editor + const modalEditorContainer = this._editorGroupsService.activeModalEditorPart?.modalElement; + const isModal = isHTMLElement(modalEditorContainer) && this._element && modalEditorContainer.contains(this._element); + this._clippingContainer = isModal ? undefined : this._workbenchLayoutService.getContainer(this.window, Parts.EDITOR_PART); + + // Bind the CSS anchor before claim when the editor element is already connected + // so retained webviews size correctly on first paint (#323890). + this.setWebviewAnchorElement(input.webview); + input.claim(this, this.window, this.scopedContextKeyService); if (this._element) { @@ -168,11 +177,6 @@ export class WebviewEditor extends EditorPane { DOM.setParentFlowTo(input.webview.container, this._element); } - // Check if this editor is inside a modal editor - const modalEditorContainer = this._editorGroupsService.activeModalEditorPart?.modalElement; - const isModal = isHTMLElement(modalEditorContainer) && this._element && modalEditorContainer.contains(this._element); - this._clippingContainer = isModal ? undefined : this._workbenchLayoutService.getContainer(this.window, Parts.EDITOR_PART); - this._webviewVisibleDisposables.clear(); // Webviews are not part of the normal editor dom, so we have to register our own drag and drop handler on them. From 229551506158e6f442ff4feb3fb89ec873d1be83 Mon Sep 17 00:00:00 2001 From: Akshat Anand Date: Sun, 19 Jul 2026 10:28:59 +0530 Subject: [PATCH 2/2] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/vs/workbench/contrib/webview/browser/overlayWebview.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts index d1cd2c69555cf2..699bcf8b76443c 100644 --- a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts +++ b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts @@ -289,8 +289,7 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { this._shouldShowFindWidgetOnRestore = false; } - // Do not unhide here: callers bind the CSS anchor after `claim`/`_show`. - // Showing early lets retained iframes flash at the default size (#323890). + // Visibility is updated only after a connected CSS anchor has been bound (#323890). } public setHtml(html: string) {