do not keep entire window as property on a class

This commit is contained in:
Benjamin Pasero
2024-03-04 06:32:22 +01:00
parent 07ee973a43
commit 7596512985
5 changed files with 41 additions and 39 deletions
@@ -513,10 +513,10 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Themable {
return !!this.webview;
}
createWebview(codeWindow: CodeWindow): Promise<void> {
createWebview(targetWindow: CodeWindow): Promise<void> {
const baseUrl = this.asWebviewUri(this.getNotebookBaseUri(), undefined);
const htmlContent = this.generateContent(baseUrl.toString());
return this._initialize(htmlContent, codeWindow);
return this._initialize(htmlContent, targetWindow);
}
private getNotebookBaseUri() {
@@ -551,16 +551,16 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Themable {
];
}
private async _initialize(content: string, codeWindow: CodeWindow): Promise<void> {
private async _initialize(content: string, targetWindow: CodeWindow): Promise<void> {
if (!getWindow(this.element).document.body.contains(this.element)) {
throw new Error('Element is already detached from the DOM tree');
}
this.webview = this._createInset(this.webviewService, content);
this.webview.mountTo(this.element, codeWindow);
this.webview.mountTo(this.element, targetWindow);
this._register(this.webview);
this._register(new WebviewWindowDragMonitor(codeWindow, () => this.webview));
this._register(new WebviewWindowDragMonitor(targetWindow, () => this.webview));
const initializePromise = new DeferredPromise<void>();
@@ -3,10 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Dimension } from 'vs/base/browser/dom';
import { Dimension, getWindowById } from 'vs/base/browser/dom';
import { FastDomNode } from 'vs/base/browser/fastDomNode';
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
import { CodeWindow } from 'vs/base/browser/window';
import { CodeWindow, mainWindow } from 'vs/base/browser/window';
import { Emitter } from 'vs/base/common/event';
import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
@@ -36,7 +36,9 @@ export class OverlayWebview extends Disposable implements IOverlayWebview {
private _options: WebviewOptions;
private _owner: any = undefined;
public _codeWindow: CodeWindow | undefined = undefined;
private _windowId: number | undefined = undefined;
private get window() { return typeof this._windowId === 'number' ? getWindowById(this._windowId)?.window : undefined; }
private readonly _scopedContextKeyService = this._register(new MutableDisposable<IScopedContextKeyService>());
private _findWidgetVisible: IContextKey<boolean> | undefined;
@@ -105,21 +107,20 @@ export class OverlayWebview extends Disposable implements IOverlayWebview {
// Webviews cannot be reparented in the dom as it will destroy their contents.
// Mount them to a high level node to avoid this.
const layoutContainer = this._codeWindow ? this._layoutService.getContainer(this._codeWindow) : this._layoutService.mainContainer;
layoutContainer.appendChild(node);
this._layoutService.getContainer(this.window ?? mainWindow).appendChild(node);
}
return this._container.domNode;
}
public claim(owner: any, codeWindow: CodeWindow, scopedContextKeyService: IContextKeyService | undefined) {
public claim(owner: any, targetWindow: CodeWindow, scopedContextKeyService: IContextKeyService | undefined) {
if (this._isDisposed) {
return;
}
const oldOwner = this._owner;
if (this._codeWindow && this._codeWindow?.vscodeWindowId !== codeWindow.vscodeWindowId) {
if (this._windowId !== targetWindow.vscodeWindowId) {
// moving to a new window
this.release(oldOwner);
this._container?.domNode.remove();
@@ -127,8 +128,8 @@ export class OverlayWebview extends Disposable implements IOverlayWebview {
}
this._owner = owner;
this._codeWindow = codeWindow;
this._show(codeWindow);
this._windowId = targetWindow.vscodeWindowId;
this._show(targetWindow);
if (oldOwner !== owner) {
const contextKeyService = (scopedContextKeyService || this._baseContextKeyService);
@@ -195,7 +196,7 @@ export class OverlayWebview extends Disposable implements IOverlayWebview {
}
}
private _show(codeWindow: CodeWindow) {
private _show(targetWindow: CodeWindow) {
if (this._isDisposed) {
throw new Error('OverlayWebview is disposed');
}
@@ -207,7 +208,7 @@ export class OverlayWebview extends Disposable implements IOverlayWebview {
title: this._title,
options: this._options,
contentOptions: this._contentOptions,
extension: this.extension
extension: this.extension,
});
this._webview.value = webview;
webview.state = this._state;
@@ -226,7 +227,7 @@ export class OverlayWebview extends Disposable implements IOverlayWebview {
this._findWidgetEnabled?.set(!!this.options.enableFindWidget);
webview.mountTo(this.container, codeWindow);
webview.mountTo(this.container, targetWindow);
// Forward events from inner webview to outer listeners
this._webviewEvents.clear();
@@ -277,7 +277,7 @@ export interface IWebviewElement extends IWebview {
*
* @param parent Element to append the webview to.
*/
mountTo(parent: HTMLElement, codeWindow: CodeWindow): void;
mountTo(parent: HTMLElement, targetWindow: CodeWindow): void;
}
/**
@@ -307,7 +307,7 @@ export interface IOverlayWebview extends IWebview {
* @param claimant Identifier for the object claiming the webview.
* This must match the `claimant` passed to {@link IOverlayWebview.release}.
*/
claim(claimant: any, codeWindow: CodeWindow, scopedContextKeyService: IContextKeyService | undefined): void;
claim(claimant: any, targetWindow: CodeWindow, scopedContextKeyService: IContextKeyService | undefined): void;
/**
* Release ownership of the webview.
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { isFirefox } from 'vs/base/browser/browser';
import { addDisposableListener, EventType } from 'vs/base/browser/dom';
import { addDisposableListener, EventType, getWindowById } from 'vs/base/browser/dom';
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
import { promiseWithResolvers, ThrottledDelayer } from 'vs/base/common/async';
import { streamToBuffer, VSBufferReadableStream } from 'vs/base/common/buffer';
@@ -88,7 +88,8 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD
*/
public readonly origin: string;
private _codeWindow?: CodeWindow;
private _windowId: number | undefined = undefined;
private get window() { return typeof this._windowId === 'number' ? getWindowById(this._windowId)?.window : undefined; }
private _encodedWebviewOriginPromise?: Promise<string>;
private _encodedWebviewOrigin: string | undefined;
@@ -106,11 +107,11 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD
return false;
}
// code window is only available after the webview is mounted.
if (!this._codeWindow) {
if (!this.window) {
return false;
}
if (this._codeWindow.document.activeElement && this._codeWindow.document.activeElement !== this.element) {
if (this.window.document.activeElement && this.window.document.activeElement !== this.element) {
// looks like https://github.com/microsoft/vscode/issues/132641
// where the focus is actually not in the `<iframe>`
return false;
@@ -412,7 +413,7 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD
return element;
}
private _initElement(encodedWebviewOrigin: string, extension: WebviewExtensionDescription | undefined, options: WebviewOptions, codeWindow: CodeWindow) {
private _initElement(encodedWebviewOrigin: string, extension: WebviewExtensionDescription | undefined, options: WebviewOptions, targetWindow: CodeWindow) {
// The extensionId and purpose in the URL are used for filtering in js-debug:
const params: { [key: string]: string } = {
id: this.id,
@@ -421,7 +422,7 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD
extensionId: extension?.id.value ?? '',
platform: this.platform,
'vscode-resource-base-authority': webviewRootResourceAuthority,
parentOrigin: codeWindow.origin,
parentOrigin: targetWindow.origin,
};
if (this._options.disableServiceWorker) {
@@ -446,19 +447,19 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD
this.element!.setAttribute('src', `${this.webviewContentEndpoint(encodedWebviewOrigin)}/${fileName}?${queryString}`);
}
public mountTo(element: HTMLElement, codeWindow: CodeWindow) {
public mountTo(element: HTMLElement, targetWindow: CodeWindow) {
if (!this.element) {
return;
}
this._codeWindow = codeWindow;
this._encodedWebviewOriginPromise = parentOriginHash(codeWindow.origin, this.origin).then(id => this._encodedWebviewOrigin = id);
this._windowId = targetWindow.vscodeWindowId;
this._encodedWebviewOriginPromise = parentOriginHash(targetWindow.origin, this.origin).then(id => this._encodedWebviewOrigin = id);
this._encodedWebviewOriginPromise.then(encodedWebviewOrigin => {
if (!this._disposed) {
this._initElement(encodedWebviewOrigin, this.extension, this._options, codeWindow);
this._initElement(encodedWebviewOrigin, this.extension, this._options, targetWindow);
}
});
this._registerMessageHandler(codeWindow);
this._registerMessageHandler(targetWindow);
if (this._webviewFindWidget) {
element.appendChild(this._webviewFindWidget.getDomNode());
@@ -470,7 +471,7 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD
}));
}
for (const node of [element, codeWindow]) {
for (const node of [element, targetWindow]) {
this._register(addDisposableListener(node, EventType.DRAG_END, () => {
this._stopBlockingIframeDragEvents();
}));
@@ -481,8 +482,8 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD
element.appendChild(this.element);
}
private _registerMessageHandler(codeWindow: CodeWindow) {
const subscription = this._register(addDisposableListener(codeWindow, 'message', (e: MessageEvent) => {
private _registerMessageHandler(targetWindow: CodeWindow) {
const subscription = this._register(addDisposableListener(targetWindow, 'message', (e: MessageEvent) => {
if (!this._encodedWebviewOrigin || e?.data?.target !== this.id) {
return;
}
@@ -693,7 +694,7 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD
get: () => this.element,
});
// And re-dispatch
this._codeWindow?.dispatchEvent(emulatedKeyboardEvent);
this.window?.dispatchEvent(emulatedKeyboardEvent);
}
windowDidDragStart(): void {
@@ -835,7 +836,7 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD
return;
}
if (this._codeWindow?.document.activeElement && this._codeWindow.document.activeElement !== this.element && this._codeWindow.document.activeElement?.tagName !== 'BODY') {
if (this.window?.document.activeElement && this.window.document.activeElement !== this.element && this.window.document.activeElement?.tagName !== 'BODY') {
return;
}
@@ -15,10 +15,10 @@ import { IWebview } from 'vs/workbench/contrib/webview/browser/webview';
* event so it can handle editor element drag drop.
*/
export class WebviewWindowDragMonitor extends Disposable {
constructor(codeWindow: CodeWindow, getWebview: () => IWebview | undefined) {
constructor(targetWindow: CodeWindow, getWebview: () => IWebview | undefined) {
super();
this._register(DOM.addDisposableListener(codeWindow, DOM.EventType.DRAG_START, () => {
this._register(DOM.addDisposableListener(targetWindow, DOM.EventType.DRAG_START, () => {
getWebview()?.windowDidDragStart();
}));
@@ -26,8 +26,8 @@ export class WebviewWindowDragMonitor extends Disposable {
getWebview()?.windowDidDragEnd();
};
this._register(DOM.addDisposableListener(codeWindow, DOM.EventType.DRAG_END, onDragEnd));
this._register(DOM.addDisposableListener(codeWindow, DOM.EventType.MOUSE_MOVE, currentEvent => {
this._register(DOM.addDisposableListener(targetWindow, DOM.EventType.DRAG_END, onDragEnd));
this._register(DOM.addDisposableListener(targetWindow, DOM.EventType.MOUSE_MOVE, currentEvent => {
if (currentEvent.buttons === 0) {
onDragEnd();
}