From 198bbdf35eff992267104ced9bb575152cd645a7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 18 Oct 2023 09:33:39 +0200 Subject: [PATCH] aux window - avoid more global `window` (#195869) * aux window - avoid more global `window` * aux window - avoid more global `document` --- src/vs/base/browser/dom.ts | 9 +-------- src/vs/workbench/browser/layout.ts | 6 +++--- .../parts/notifications/notificationsCenter.ts | 6 +++--- .../browser/parts/notifications/notificationsList.ts | 6 +++--- .../parts/notifications/notificationsToasts.ts | 6 +++--- .../contrib/accessibility/browser/accessibleView.ts | 4 ++-- .../contrib/codeEditor/browser/toggleWordWrap.ts | 11 ++++++++--- .../workbench/contrib/debug/browser/linkDetector.ts | 3 ++- src/vs/workbench/contrib/debug/browser/repl.ts | 10 ++++++---- .../contrib/files/browser/views/explorerView.ts | 2 +- .../contrib/files/browser/views/explorerViewer.ts | 7 ++++--- .../workbench/contrib/markers/browser/markersView.ts | 4 ++-- .../browser/contrib/clipboard/notebookClipboard.ts | 4 ++-- .../contrib/notebook/browser/notebookEditorWidget.ts | 2 +- .../workbench/contrib/scm/browser/scm.contribution.ts | 7 ++++--- src/vs/workbench/contrib/search/browser/searchView.ts | 4 ++-- .../welcomeWalkthrough/browser/walkThroughPart.ts | 4 ++-- 17 files changed, 49 insertions(+), 46 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index d8302103599..024e9988544 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -602,14 +602,7 @@ export function getLargestChildWidth(parent: HTMLElement, children: HTMLElement[ // ---------------------------------------------------------------------------------------- export function isAncestor(testChild: Node | null, testAncestor: Node | null): boolean { - while (testChild) { - if (testChild === testAncestor) { - return true; - } - testChild = testChild.parentNode; - } - - return false; + return Boolean(testAncestor?.contains(testChild)); } const parentFlowToDataKey = 'parentFlowToElementId'; diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 2708caa9350..51a6a96f7bc 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -5,7 +5,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; -import { EventType, addDisposableListener, getClientArea, Dimension, position, size, IDimension, isAncestorUsingFlowTo, computeScreenAwareSize, getActiveDocument, getWindows } from 'vs/base/browser/dom'; +import { EventType, addDisposableListener, getClientArea, Dimension, position, size, IDimension, isAncestorUsingFlowTo, computeScreenAwareSize, getActiveDocument, getWindows, getActiveWindow } from 'vs/base/browser/dom'; import { onDidChangeFullscreen, isFullscreen, isWCOEnabled } from 'vs/base/browser/browser'; import { IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup'; import { isWindows, isLinux, isMacintosh, isWeb, isNative, isIOS } from 'vs/base/common/platform'; @@ -1463,8 +1463,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } resizePart(part: Parts, sizeChangeWidth: number, sizeChangeHeight: number): void { - const sizeChangePxWidth = Math.sign(sizeChangeWidth) * computeScreenAwareSize(window, Math.abs(sizeChangeWidth)); - const sizeChangePxHeight = Math.sign(sizeChangeHeight) * computeScreenAwareSize(window, Math.abs(sizeChangeHeight)); + const sizeChangePxWidth = Math.sign(sizeChangeWidth) * computeScreenAwareSize(getActiveWindow(), Math.abs(sizeChangeWidth)); + const sizeChangePxHeight = Math.sign(sizeChangeHeight) * computeScreenAwareSize(getActiveWindow(), Math.abs(sizeChangeHeight)); let viewSize: IViewSize; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index c7e9ab26652..51115350f94 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -14,7 +14,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { INotificationsCenterController, NotificationActionRunner } from 'vs/workbench/browser/parts/notifications/notificationsCommands'; import { NotificationsList } from 'vs/workbench/browser/parts/notifications/notificationsList'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { isAncestor, Dimension } from 'vs/base/browser/dom'; +import { Dimension, isAncestorOfActiveElement } from 'vs/base/browser/dom'; import { widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { localize } from 'vs/nls'; @@ -222,7 +222,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente notificationsList.updateNotificationsList(e.index, 1, [e.item]); break; case NotificationChangeType.REMOVE: - focusEditor = isAncestor(document.activeElement, notificationsCenterContainer); + focusEditor = isAncestorOfActiveElement(notificationsCenterContainer); notificationsList.updateNotificationsList(e.index, 1); e.item.updateVisibility(false); break; @@ -247,7 +247,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente return; // already hidden } - const focusEditor = isAncestor(document.activeElement, this.notificationsCenterContainer); + const focusEditor = isAncestorOfActiveElement(this.notificationsCenterContainer); // Hide this._isVisible = false; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsList.ts b/src/vs/workbench/browser/parts/notifications/notificationsList.ts index 727e2e866a3..5ac8c173685 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsList.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsList.ts @@ -5,7 +5,7 @@ import 'vs/css!./media/notificationsList'; import { localize } from 'vs/nls'; -import { isAncestor, trackFocus } from 'vs/base/browser/dom'; +import { isAncestorOfActiveElement, trackFocus } from 'vs/base/browser/dom'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IListAccessibilityProvider, IListOptions } from 'vs/base/browser/ui/list/listWidget'; @@ -133,7 +133,7 @@ export class NotificationsList extends Disposable { updateNotificationsList(start: number, deleteCount: number, items: INotificationViewItem[] = []) { const [list, listContainer] = assertAllDefined(this.list, this.listContainer); - const listHasDOMFocus = isAncestor(document.activeElement, listContainer); + const listHasDOMFocus = isAncestorOfActiveElement(listContainer); // Remember focus and relative top of that item const focusedIndex = list.getFocus()[0]; @@ -223,7 +223,7 @@ export class NotificationsList extends Disposable { return false; // not created yet } - return isAncestor(document.activeElement, this.listContainer); + return isAncestorOfActiveElement(this.listContainer); } layout(width: number, maxHeight?: number): void { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 35785314672..f6fa63e49f2 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/notificationsToasts'; import { localize } from 'vs/nls'; import { INotificationsModel, NotificationChangeType, INotificationChangeEvent, INotificationViewItem, NotificationViewItemContentChangeKind } from 'vs/workbench/common/notifications'; import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { isAncestor, addDisposableListener, EventType, Dimension, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; +import { addDisposableListener, EventType, Dimension, scheduleAtNextAnimationFrame, isAncestorOfActiveElement } from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { NotificationsList } from 'vs/workbench/browser/parts/notifications/notificationsList'; import { Event, Emitter } from 'vs/base/common/event'; @@ -322,7 +322,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast // UI const notificationToast = this.mapNotificationToToast.get(item); if (notificationToast) { - const toastHasDOMFocus = isAncestor(document.activeElement, notificationToast.container); + const toastHasDOMFocus = isAncestorOfActiveElement(notificationToast.container); if (toastHasDOMFocus) { focusEditor = !(this.focusNext() || this.focusPrevious()); // focus next if any, otherwise focus editor } @@ -380,7 +380,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast } hide(): void { - const focusEditor = this.notificationsToastsContainer ? isAncestor(document.activeElement, this.notificationsToastsContainer) : false; + const focusEditor = this.notificationsToastsContainer ? isAncestorOfActiveElement(this.notificationsToastsContainer) : false; this.removeToasts(); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index f7090c8f8e8..c73385b4246 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EventType, addDisposableListener, isActiveElement } from 'vs/base/browser/dom'; +import { EventType, addDisposableListener, getActiveWindow, isActiveElement } from 'vs/base/browser/dom'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { alert } from 'vs/base/browser/ui/aria/aria'; @@ -264,7 +264,7 @@ export class AccessibleView extends Disposable { return; } const delegate: IContextViewDelegate = { - getAnchor: () => { return { x: (window.innerWidth / 2) - ((Math.min(this._layoutService.dimension.width * 0.62 /* golden cut */, DIMENSIONS.MAX_WIDTH)) / 2), y: this._layoutService.offset.quickPickTop }; }, + getAnchor: () => { return { x: (getActiveWindow().innerWidth / 2) - ((Math.min(this._layoutService.dimension.width * 0.62 /* golden cut */, DIMENSIONS.MAX_WIDTH)) / 2), y: this._layoutService.offset.quickPickTop }; }, render: (container) => { container.classList.add('accessible-view-container'); return this._render(provider!, container, showAccessibleViewHelp); diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts index 85be3c855a2..d3849333ddc 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts @@ -21,6 +21,8 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Event } from 'vs/base/common/event'; +import { addDisposableListener, onDidRegisterWindow } from 'vs/base/browser/dom'; const transientWordWrapState = 'transientWordWrapState'; const isWordWrapMinifiedKey = 'isWordWrapMinified'; @@ -255,7 +257,7 @@ function canToggleWordWrap(codeEditorService: ICodeEditorService, editor: ICodeE return true; } -class EditorWordWrapContextKeyTracker implements IWorkbenchContribution { +class EditorWordWrapContextKeyTracker extends Disposable implements IWorkbenchContribution { private readonly _canToggleWordWrap: IContextKey; private readonly _editorWordWrap: IContextKey; @@ -267,8 +269,11 @@ class EditorWordWrapContextKeyTracker implements IWorkbenchContribution { @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, @IContextKeyService private readonly _contextService: IContextKeyService, ) { - window.addEventListener('focus', () => this._update(), true); - window.addEventListener('blur', () => this._update(), true); + super(); + this._register(Event.runAndSubscribe(onDidRegisterWindow, ({ window, disposableStore }) => { + disposableStore.add(addDisposableListener(window, 'focus', () => this._update(), true)); + disposableStore.add(addDisposableListener(window, 'blur', () => this._update(), true)); + }, { window, disposableStore: this._store })); this._editorService.onDidActiveEditorChange(() => this._update()); this._canToggleWordWrap = CAN_TOGGLE_WORD_WRAP.bindTo(this._contextService); this._editorWordWrap = EDITOR_WORD_WRAP.bindTo(this._contextService); diff --git a/src/vs/workbench/contrib/debug/browser/linkDetector.ts b/src/vs/workbench/contrib/debug/browser/linkDetector.ts index abfca65cb23..484f3286dba 100644 --- a/src/vs/workbench/contrib/debug/browser/linkDetector.ts +++ b/src/vs/workbench/contrib/debug/browser/linkDetector.ts @@ -18,6 +18,7 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { localize } from 'vs/nls'; import { ITunnelService } from 'vs/platform/tunnel/common/tunnel'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { getWindow } from 'vs/base/browser/dom'; const CONTROL_CODES = '\\u0000-\\u0020\\u007f-\\u009f'; const WEB_LINK_REGEX = new RegExp('(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\\/\\/|data:|www\\.)[^\\s' + CONTROL_CODES + '"]{2,}[^\\s' + CONTROL_CODES + '"\')}\\],:;.!?]', 'ug'); @@ -201,7 +202,7 @@ export class LinkDetector { link.onmousemove = (event) => { link.classList.toggle('pointer', platform.isMacintosh ? event.metaKey : event.ctrlKey); }; link.onmouseleave = () => link.classList.remove('pointer'); link.onclick = (event) => { - const selection = window.getSelection(); + const selection = getWindow(link).getSelection(); if (!selection || selection.type === 'Range') { return; // do not navigate when user is selecting } diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 7be4e8eb0bd..9805ab089a4 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -570,16 +570,18 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { this._register(registerNavigableContainer({ focusNotifiers: [this, this.filterWidget], focusNextWidget: () => { + const element = this.tree?.getHTMLElement(); if (this.filterWidget.hasFocus()) { this.tree?.domFocus(); - } else if (this.tree?.getHTMLElement() === document.activeElement) { + } else if (element && dom.isActiveElement(element)) { this.focus(); } }, focusPreviousWidget: () => { + const element = this.tree?.getHTMLElement(); if (this.replInput.hasTextFocus()) { this.tree?.domFocus(); - } else if (this.tree?.getHTMLElement() === document.activeElement) { + } else if (element && dom.isActiveElement(element)) { this.focusFilter(); } } @@ -648,7 +650,7 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { this._register(tree.onContextMenu(e => this.onContextMenu(e))); let lastSelectedString: string; this._register(tree.onMouseClick(() => { - const selection = window.getSelection(); + const selection = dom.getWindow(this.treeContainer).getSelection(); if (!selection || selection.type !== 'Range' || lastSelectedString === selection.toString()) { // only focus the input if the user is not currently selecting. this.replInput.focus(); @@ -1070,7 +1072,7 @@ registerAction2(class extends Action2 { async run(accessor: ServicesAccessor, element: IReplElement): Promise { const clipboardService = accessor.get(IClipboardService); const debugService = accessor.get(IDebugService); - const nativeSelection = window.getSelection(); + const nativeSelection = dom.getActiveWindow().getSelection(); const selectedText = nativeSelection?.toString(); if (selectedText && selectedText.length > 0) { return clipboardService.writeText(selectedText); diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index d3d8d5c7e59..ca2b5d06823 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -325,7 +325,7 @@ export class ExplorerView extends ViewPane implements IExplorerView { } hasFocus(): boolean { - return DOM.isAncestor(document.activeElement, this.container); + return DOM.isAncestorOfActiveElement(this.container); } getContext(respectMultiSelection: boolean): ExplorerItem[] { diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index bfebab5f73d..d567ec80916 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -621,11 +621,12 @@ export class FilesRenderer implements ICompressibleTreeRenderer { const contextKeyService = accessor.get(IContextKeyService); - const context = contextKeyService.getContext(document.activeElement); + const context = contextKeyService.getContext(getActiveElement()); const repositoryId = context.getValue('scmRepository'); if (!repositoryId) { @@ -336,7 +337,7 @@ const viewNextCommitCommand = { handler: (accessor: ServicesAccessor) => { const contextKeyService = accessor.get(IContextKeyService); const scmService = accessor.get(ISCMService); - const context = contextKeyService.getContext(document.activeElement); + const context = contextKeyService.getContext(getActiveElement()); const repositoryId = context.getValue('scmRepository'); const repository = repositoryId ? scmService.getRepository(repositoryId) : undefined; repository?.input.showNextHistoryValue(); @@ -349,7 +350,7 @@ const viewPreviousCommitCommand = { handler: (accessor: ServicesAccessor) => { const contextKeyService = accessor.get(IContextKeyService); const scmService = accessor.get(ISCMService); - const context = contextKeyService.getContext(document.activeElement); + const context = contextKeyService.getContext(getActiveElement()); const repositoryId = context.getValue('scmRepository'); const repository = repositoryId ? scmService.getRepository(repositoryId) : undefined; repository?.input.showPreviousHistoryValue(); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index f1434f91db9..6bc3f38e680 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1072,7 +1072,7 @@ export class SearchView extends ViewPane { } private updateTextFromFindWidget(controller: CommonFindController, { allowSearchOnType = true }): boolean { - if (!this.searchConfig.seedWithNearestWord && (window.getSelection()?.toString() ?? '') === '') { + if (!this.searchConfig.seedWithNearestWord && (dom.getActiveWindow().getSelection()?.toString() ?? '') === '') { return false; } @@ -1275,7 +1275,7 @@ export class SearchView extends ViewPane { } private getSearchTextFromEditor(allowUnselectedWord: boolean, editor?: IEditor): string | null { - if (dom.isAncestor(document.activeElement, this.getContainer())) { + if (dom.isAncestorOfActiveElement(this.getContainer())) { return null; } diff --git a/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts index 002c6ef9c5f..29f5aace84c 100644 --- a/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts @@ -32,7 +32,7 @@ import { UILabelProvider } from 'vs/base/common/keybindingLabels'; import { OS, OperatingSystem } from 'vs/base/common/platform'; import { deepClone } from 'vs/base/common/objects'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { addDisposableListener, Dimension, safeInnerHtml, size } from 'vs/base/browser/dom'; +import { addDisposableListener, Dimension, getWindow, safeInnerHtml, size } from 'vs/base/browser/dom'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -156,7 +156,7 @@ export class WalkThroughPart extends EditorPane { this.content.addEventListener('click', event => { for (let node = event.target as HTMLElement; node; node = node.parentNode as HTMLElement) { if (node instanceof HTMLAnchorElement && node.href) { - const baseElement = window.document.getElementsByTagName('base')[0] || window.location; + const baseElement = node.ownerDocument.getElementsByTagName('base')[0] || getWindow(node).location; if (baseElement && node.href.indexOf(baseElement.href) >= 0 && node.hash) { const scrollTarget = this.content.querySelector(node.hash); const innerContent = this.content.firstElementChild;