From 41c62288f055a615302fa7fa95ee7f4b140496a4 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 13 Oct 2023 13:41:53 -0700 Subject: [PATCH 01/12] Expose a documentOverride setting This lets the embedder choose which Document object to use for creating elements, adding event listeners, etc. The reason this exists is because when working with multiple windows it can be convenient to create all elements under the primary window to make instanceof usage consistent. See microsoft/vscode#195595 --- src/browser/Terminal.ts | 3 +++ src/common/services/OptionsService.ts | 1 + src/common/services/Services.ts | 1 + typings/xterm.d.ts | 11 +++++++++++ 4 files changed, 16 insertions(+) diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index 18af3e4e2..621ee44c5 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -398,6 +398,9 @@ export class Terminal extends CoreTerminal implements ITerminal { } this._document = parent.ownerDocument!; + if (this.options.documentOverride && this.options.documentOverride instanceof Document) { + this._document = this.optionsService.rawOptions.documentOverride as Document; + } // Create main element container this.element = this._document.createElement('div'); diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 3c572445e..eb9dbfa8c 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -18,6 +18,7 @@ export const DEFAULT_OPTIONS: Readonly> = { cursorInactiveStyle: 'outline', customGlyphs: true, drawBoldTextInBrightColors: true, + documentOverride: null, fastScrollModifier: 'alt', fastScrollSensitivity: 5, fontFamily: 'courier-new, courier, monospace', diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 52c2a79fa..6ac73e37a 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -217,6 +217,7 @@ export interface ITerminalOptions { cursorInactiveStyle?: CursorInactiveStyle; customGlyphs?: boolean; disableStdin?: boolean; + documentOverride?: any | null; drawBoldTextInBrightColors?: boolean; fastScrollModifier?: 'none' | 'alt' | 'ctrl' | 'shift'; fastScrollSensitivity?: number; diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 3f603b9e1..b8028a181 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -89,6 +89,17 @@ declare module 'xterm' { */ disableStdin?: boolean; + /** + * A {@link Document} to use instead of the one that xterm.js was attached + * to. The purpose of this is to improve support in multi-window + * applications where HTML elements may be references across multiple + * windows which can cause problems with `instanceof`. + * + * The type is `any` because using `Document` can cause TS to have + * performance/compiler problems. + */ + documentOverride?: any | null; + /** * Whether to draw bold text in bright colors. The default is true. */ From c80453f602e700bb774757c1d86cdf0037e062bc Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:09:36 -0700 Subject: [PATCH 02/12] Use correct document when creating elements --- addons/xterm-addon-canvas/src/BaseRenderLayer.ts | 2 +- addons/xterm-addon-webgl/src/WebglRenderer.ts | 2 +- .../src/renderLayer/BaseRenderLayer.ts | 2 +- src/browser/AccessibilityManager.ts | 11 ++++++----- src/browser/Terminal.ts | 16 ++++++++-------- src/browser/TestUtils.test.ts | 3 +++ .../decorations/BufferDecorationRenderer.ts | 5 +++-- src/browser/decorations/OverviewRulerRenderer.ts | 16 ++++++++-------- src/browser/renderer/shared/CustomGlyphs.ts | 2 +- src/browser/services/CoreBrowserService.ts | 3 ++- src/browser/services/Services.ts | 5 +++++ src/common/Color.ts | 1 + 12 files changed, 40 insertions(+), 28 deletions(-) diff --git a/addons/xterm-addon-canvas/src/BaseRenderLayer.ts b/addons/xterm-addon-canvas/src/BaseRenderLayer.ts index 8c1993366..62fe71a74 100644 --- a/addons/xterm-addon-canvas/src/BaseRenderLayer.ts +++ b/addons/xterm-addon-canvas/src/BaseRenderLayer.ts @@ -59,7 +59,7 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer ) { super(); this._cellColorResolver = new CellColorResolver(this._terminal, this._selectionModel, this._decorationService, this._coreBrowserService, this._themeService); - this._canvas = document.createElement('canvas'); + this._canvas = this._coreBrowserService.mainDocument.createElement('canvas'); this._canvas.classList.add(`xterm-${id}-layer`); this._canvas.style.zIndex = zIndex.toString(); this._initCanvas(); diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index 8cc81b92c..517499845 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -88,7 +88,7 @@ export class WebglRenderer extends Disposable implements IRenderer { this._updateCursorBlink(); this.register(_optionsService.onOptionChange(() => this._handleOptionsChanged())); - this._canvas = document.createElement('canvas'); + this._canvas = this._coreBrowserService.mainDocument.createElement('canvas'); const contextAttributes = { antialias: false, diff --git a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts index 3aaac435b..11f96ed8c 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts @@ -38,7 +38,7 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer protected readonly _themeService: IThemeService ) { super(); - this._canvas = document.createElement('canvas'); + this._canvas = this._coreBrowserService.mainDocument.createElement('canvas'); this._canvas.classList.add(`xterm-${id}-layer`); this._canvas.style.zIndex = zIndex.toString(); this._initCanvas(); diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index c878bd0af..1005414f0 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -8,7 +8,7 @@ import { ITerminal, IRenderDebouncer } from 'browser/Types'; import { TimeBasedDebouncer } from 'browser/TimeBasedDebouncer'; import { Disposable, toDisposable } from 'common/Lifecycle'; import { ScreenDprMonitor } from 'browser/ScreenDprMonitor'; -import { IRenderService } from 'browser/services/Services'; +import { ICoreBrowserService, IRenderService } from 'browser/services/Services'; import { addDisposableDomListener } from 'browser/Lifecycle'; import { IBuffer } from 'common/buffer/Types'; @@ -49,13 +49,14 @@ export class AccessibilityManager extends Disposable { constructor( private readonly _terminal: ITerminal, + @ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService, @IRenderService private readonly _renderService: IRenderService ) { super(); - this._accessibilityContainer = document.createElement('div'); + this._accessibilityContainer = this._coreBrowserService.mainDocument.createElement('div'); this._accessibilityContainer.classList.add('xterm-accessibility'); - this._rowContainer = document.createElement('div'); + this._rowContainer = this._coreBrowserService.mainDocument.createElement('div'); this._rowContainer.setAttribute('role', 'list'); this._rowContainer.classList.add('xterm-accessibility-tree'); this._rowElements = []; @@ -72,7 +73,7 @@ export class AccessibilityManager extends Disposable { this._refreshRowsDimensions(); this._accessibilityContainer.appendChild(this._rowContainer); - this._liveRegion = document.createElement('div'); + this._liveRegion = this._coreBrowserService.mainDocument.createElement('div'); this._liveRegion.classList.add('live-region'); this._liveRegion.setAttribute('aria-live', 'assertive'); this._accessibilityContainer.appendChild(this._liveRegion); @@ -261,7 +262,7 @@ export class AccessibilityManager extends Disposable { } private _createAccessibilityTreeNode(): HTMLElement { - const element = document.createElement('div'); + const element = this._coreBrowserService.mainDocument.createElement('div'); element.setAttribute('role', 'listitem'); element.tabIndex = -1; this._refreshRowDimensions(element); diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index 621ee44c5..9d4ca5a02 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -411,25 +411,25 @@ export class Terminal extends CoreTerminal implements ITerminal { // Performance: Use a document fragment to build the terminal // viewport and helper elements detached from the DOM - const fragment = document.createDocumentFragment(); - this._viewportElement = document.createElement('div'); + const fragment = this._document.createDocumentFragment(); + this._viewportElement = this._document.createElement('div'); this._viewportElement.classList.add('xterm-viewport'); fragment.appendChild(this._viewportElement); - this._viewportScrollArea = document.createElement('div'); + this._viewportScrollArea = this._document.createElement('div'); this._viewportScrollArea.classList.add('xterm-scroll-area'); this._viewportElement.appendChild(this._viewportScrollArea); - this.screenElement = document.createElement('div'); + this.screenElement = this._document.createElement('div'); this.screenElement.classList.add('xterm-screen'); // Create the container that will hold helpers like the textarea for // capturing DOM Events. Then produce the helpers. - this._helperContainer = document.createElement('div'); + this._helperContainer = this._document.createElement('div'); this._helperContainer.classList.add('xterm-helpers'); this.screenElement.appendChild(this._helperContainer); fragment.appendChild(this.screenElement); - this.textarea = document.createElement('textarea'); + this.textarea = this._document.createElement('textarea'); this.textarea.classList.add('xterm-helper-textarea'); this.textarea.setAttribute('aria-label', Strings.promptLabel); if (!Browser.isChromeOS) { @@ -444,7 +444,7 @@ export class Terminal extends CoreTerminal implements ITerminal { // Register the core browser service before the generic textarea handlers are registered so it // handles them first. Otherwise the renderers may use the wrong focus state. - this._coreBrowserService = this._instantiationService.createInstance(CoreBrowserService, this.textarea, this._document.defaultView ?? window); + this._coreBrowserService = this._instantiationService.createInstance(CoreBrowserService, this.textarea, parent.ownerDocument.defaultView ?? window, this._document ?? window.document); this._instantiationService.setService(ICoreBrowserService, this._coreBrowserService); this.register(addDisposableDomListener(this.textarea, 'focus', (ev: KeyboardEvent) => this._handleTextAreaFocus(ev))); @@ -466,7 +466,7 @@ export class Terminal extends CoreTerminal implements ITerminal { this.register(this._renderService.onRenderedViewportChange(e => this._onRender.fire(e))); this.onResize(e => this._renderService!.resize(e.cols, e.rows)); - this._compositionView = document.createElement('div'); + this._compositionView = this._document.createElement('div'); this._compositionView.classList.add('composition-view'); this._compositionHelper = this._instantiationService.createInstance(CompositionHelper, this.textarea, this._compositionView); this._helperContainer.appendChild(this._compositionView); diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index 7b464a338..b36e3b497 100644 --- a/src/browser/TestUtils.test.ts +++ b/src/browser/TestUtils.test.ts @@ -355,6 +355,9 @@ export class MockCoreBrowserService implements ICoreBrowserService { public get window(): Window & typeof globalThis { throw Error('Window object not available in tests'); } + public get mainDocument(): Document { + throw Error('Document object not available in tests'); + } public dpr: number = 1; } diff --git a/src/browser/decorations/BufferDecorationRenderer.ts b/src/browser/decorations/BufferDecorationRenderer.ts index ba4f6ca8f..0cac7bc78 100644 --- a/src/browser/decorations/BufferDecorationRenderer.ts +++ b/src/browser/decorations/BufferDecorationRenderer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { addDisposableDomListener } from 'browser/Lifecycle'; -import { IRenderService } from 'browser/services/Services'; +import { ICoreBrowserService, IRenderService } from 'browser/services/Services'; import { Disposable, toDisposable } from 'common/Lifecycle'; import { IBufferService, IDecorationService, IInternalDecoration } from 'common/services/Services'; @@ -19,6 +19,7 @@ export class BufferDecorationRenderer extends Disposable { constructor( private readonly _screenElement: HTMLElement, @IBufferService private readonly _bufferService: IBufferService, + @ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService, @IDecorationService private readonly _decorationService: IDecorationService, @IRenderService private readonly _renderService: IRenderService ) { @@ -70,7 +71,7 @@ export class BufferDecorationRenderer extends Disposable { } private _createElement(decoration: IInternalDecoration): HTMLElement { - const element = document.createElement('div'); + const element = this._coreBrowserService.mainDocument.createElement('div'); element.classList.add('xterm-decoration'); element.classList.toggle('xterm-decoration-top-layer', decoration?.options?.layer === 'top'); element.style.width = `${Math.round((decoration.options.width || 1) * this._renderService.dimensions.css.cell.width)}px`; diff --git a/src/browser/decorations/OverviewRulerRenderer.ts b/src/browser/decorations/OverviewRulerRenderer.ts index d57653859..d63d87450 100644 --- a/src/browser/decorations/OverviewRulerRenderer.ts +++ b/src/browser/decorations/OverviewRulerRenderer.ts @@ -52,10 +52,10 @@ export class OverviewRulerRenderer extends Disposable { @IDecorationService private readonly _decorationService: IDecorationService, @IRenderService private readonly _renderService: IRenderService, @IOptionsService private readonly _optionsService: IOptionsService, - @ICoreBrowserService private readonly _coreBrowseService: ICoreBrowserService + @ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService ) { super(); - this._canvas = document.createElement('canvas'); + this._canvas = this._coreBrowserService.mainDocument.createElement('canvas'); this._canvas.classList.add('xterm-decoration-overview-ruler'); this._refreshCanvasDimensions(); this._viewportElement.parentElement?.insertBefore(this._canvas, this._viewportElement); @@ -112,7 +112,7 @@ export class OverviewRulerRenderer extends Disposable { // overview ruler width changed this.register(this._optionsService.onSpecificOptionChange('overviewRulerWidth', () => this._queueRefresh(true))); // device pixel ratio changed - this.register(addDisposableDomListener(this._coreBrowseService.window, 'resize', () => this._queueRefresh(true))); + this.register(addDisposableDomListener(this._coreBrowserService.window, 'resize', () => this._queueRefresh(true))); // set the canvas dimensions this._queueRefresh(true); } @@ -135,11 +135,11 @@ export class OverviewRulerRenderer extends Disposable { } private _refreshDrawHeightConstants(): void { - drawHeight.full = Math.round(2 * this._coreBrowseService.dpr); + drawHeight.full = Math.round(2 * this._coreBrowserService.dpr); // Calculate actual pixels per line const pixelsPerLine = this._canvas.height / this._bufferService.buffer.lines.length; // Clamp actual pixels within a range - const nonFullHeight = Math.round(Math.max(Math.min(pixelsPerLine, 12), 6) * this._coreBrowseService.dpr); + const nonFullHeight = Math.round(Math.max(Math.min(pixelsPerLine, 12), 6) * this._coreBrowserService.dpr); drawHeight.left = nonFullHeight; drawHeight.center = nonFullHeight; drawHeight.right = nonFullHeight; @@ -157,9 +157,9 @@ export class OverviewRulerRenderer extends Disposable { private _refreshCanvasDimensions(): void { this._canvas.style.width = `${this._width}px`; - this._canvas.width = Math.round(this._width * this._coreBrowseService.dpr); + this._canvas.width = Math.round(this._width * this._coreBrowserService.dpr); this._canvas.style.height = `${this._screenElement.clientHeight}px`; - this._canvas.height = Math.round(this._screenElement.clientHeight * this._coreBrowseService.dpr); + this._canvas.height = Math.round(this._screenElement.clientHeight * this._coreBrowserService.dpr); this._refreshDrawConstants(); this._refreshColorZonePadding(); } @@ -211,7 +211,7 @@ export class OverviewRulerRenderer extends Disposable { if (this._animationFrame !== undefined) { return; } - this._animationFrame = this._coreBrowseService.window.requestAnimationFrame(() => { + this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => { this._refreshDecorations(); this._animationFrame = undefined; }); diff --git a/src/browser/renderer/shared/CustomGlyphs.ts b/src/browser/renderer/shared/CustomGlyphs.ts index c08bc4b16..cf6292af4 100644 --- a/src/browser/renderer/shared/CustomGlyphs.ts +++ b/src/browser/renderer/shared/CustomGlyphs.ts @@ -474,7 +474,7 @@ function drawPatternChar( if (!pattern) { const width = charDefinition[0].length; const height = charDefinition.length; - const tmpCanvas = document.createElement('canvas'); + const tmpCanvas = ctx.canvas.ownerDocument.createElement('canvas'); tmpCanvas.width = width; tmpCanvas.height = height; const tmpCtx = throwIfFalsy(tmpCanvas.getContext('2d')); diff --git a/src/browser/services/CoreBrowserService.ts b/src/browser/services/CoreBrowserService.ts index e992f2554..ad61deecb 100644 --- a/src/browser/services/CoreBrowserService.ts +++ b/src/browser/services/CoreBrowserService.ts @@ -13,7 +13,8 @@ export class CoreBrowserService implements ICoreBrowserService { constructor( private _textarea: HTMLTextAreaElement, - public readonly window: Window & typeof globalThis + public readonly window: Window & typeof globalThis, + public readonly mainDocument: Document ) { this._textarea.addEventListener('focus', () => this._isFocused = true); this._textarea.addEventListener('blur', () => this._isFocused = false); diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts index d96285f79..50c718258 100644 --- a/src/browser/services/Services.ts +++ b/src/browser/services/Services.ts @@ -34,6 +34,11 @@ export interface ICoreBrowserService { * window. */ readonly window: Window & typeof globalThis; + /** + * The document of the primary window if working with multiple windows. This + * is set by the documentOverride setting. + */ + readonly mainDocument: Document; /** * Helper for getting the devicePixelRatio of the parent window. */ diff --git a/src/common/Color.ts b/src/common/Color.ts index 108d72f36..9bfed4e64 100644 --- a/src/common/Color.ts +++ b/src/common/Color.ts @@ -113,6 +113,7 @@ export namespace css { let $ctx: CanvasRenderingContext2D | undefined; let $litmusColor: CanvasGradient | undefined; if (!isNode) { + // This is guaranteed to run in the first window, so document should be correct const canvas = document.createElement('canvas'); canvas.width = 1; canvas.height = 1; From 45605db3c5028c5116c1bf7e732722ab2e2bdf86 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 17 Oct 2023 08:39:48 -0700 Subject: [PATCH 03/12] Listen to window change and pass through to ScreenDprMonitor --- src/browser/AccessibilityManager.ts | 4 +++- src/browser/ScreenDprMonitor.ts | 12 +++++++++- src/browser/Terminal.ts | 21 +++++++++++++---- src/browser/TestUtils.test.ts | 1 + .../decorations/OverviewRulerRenderer.ts | 1 + src/browser/services/CoreBrowserService.ts | 23 +++++++++++++++++-- src/browser/services/RenderService.ts | 6 ++--- src/browser/services/Services.ts | 14 ++++++----- 8 files changed, 64 insertions(+), 18 deletions(-) diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index 1005414f0..e8cd58886 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -11,6 +11,7 @@ import { ScreenDprMonitor } from 'browser/ScreenDprMonitor'; import { ICoreBrowserService, IRenderService } from 'browser/services/Services'; import { addDisposableDomListener } from 'browser/Lifecycle'; import { IBuffer } from 'common/buffer/Types'; +import { IInstantiationService } from 'common/services/Services'; const MAX_ROWS_TO_READ = 20; @@ -49,6 +50,7 @@ export class AccessibilityManager extends Disposable { constructor( private readonly _terminal: ITerminal, + @IInstantiationService instantiationService: IInstantiationService, @ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService, @IRenderService private readonly _renderService: IRenderService ) { @@ -95,7 +97,7 @@ export class AccessibilityManager extends Disposable { this.register(this._terminal.onBlur(() => this._clearLiveRegion())); this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions())); - this._screenDprMonitor = new ScreenDprMonitor(window); + this._screenDprMonitor = this.register(instantiationService.createInstance(ScreenDprMonitor)); this.register(this._screenDprMonitor); this._screenDprMonitor.setListener(() => this._refreshRowsDimensions()); // This shouldn't be needed on modern browsers but is present in case the diff --git a/src/browser/ScreenDprMonitor.ts b/src/browser/ScreenDprMonitor.ts index 1c3f31b75..5bb2aa721 100644 --- a/src/browser/ScreenDprMonitor.ts +++ b/src/browser/ScreenDprMonitor.ts @@ -3,6 +3,7 @@ * @license MIT */ +import { ICoreBrowserService } from 'browser/services/Services'; import { Disposable, toDisposable } from 'common/Lifecycle'; export type ScreenDprListener = (newDevicePixelRatio?: number, oldDevicePixelRatio?: number) => void; @@ -22,9 +23,18 @@ export class ScreenDprMonitor extends Disposable { private _outerListener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | undefined; private _listener: ScreenDprListener | undefined; private _resolutionMediaMatchList: MediaQueryList | undefined; + private _parentWindow: Window; - constructor(private _parentWindow: Window) { + constructor(@ICoreBrowserService coreBrowserService: ICoreBrowserService) { super(); + this._parentWindow = coreBrowserService.window; + this.register(coreBrowserService.onWindowChange(w => { + this._parentWindow = w; + if (this._listener && this._parentWindow.devicePixelRatio !== this._currentDevicePixelRatio) { + this._listener(this._parentWindow.devicePixelRatio, this._currentDevicePixelRatio); + } + this._updateDpr(); + })); this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio; this.register(toDisposable(() => { this.clearListener(); diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index 9d4ca5a02..b64c3b1d9 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -58,9 +58,6 @@ import { IDecoration, IDecorationOptions, IDisposable, ILinkProvider, IMarker } import { WindowsOptionsReportType } from '../common/InputHandler'; import { AccessibilityManager } from './AccessibilityManager'; -// Let it work inside Node.js for automated testing purposes. -const document: Document = (typeof window !== 'undefined') ? window.document : null as any; - export class Terminal extends CoreTerminal implements ITerminal { public textarea: HTMLTextAreaElement | undefined; public element: HTMLElement | undefined; @@ -397,7 +394,16 @@ export class Terminal extends CoreTerminal implements ITerminal { this._logService.debug('Terminal.open was called on an element that was not attached to the DOM'); } - this._document = parent.ownerDocument!; + // If the terminal is already opened + if (this.element?.ownerDocument.defaultView && this._coreBrowserService) { + // Adjust the window if needed + if (this.element.ownerDocument.defaultView !== this._coreBrowserService.window) { + this._coreBrowserService.window = this.element.ownerDocument.defaultView; + } + return; + } + + this._document = parent.ownerDocument; if (this.options.documentOverride && this.options.documentOverride instanceof Document) { this._document = this.optionsService.rawOptions.documentOverride as Document; } @@ -444,7 +450,12 @@ export class Terminal extends CoreTerminal implements ITerminal { // Register the core browser service before the generic textarea handlers are registered so it // handles them first. Otherwise the renderers may use the wrong focus state. - this._coreBrowserService = this._instantiationService.createInstance(CoreBrowserService, this.textarea, parent.ownerDocument.defaultView ?? window, this._document ?? window.document); + this._coreBrowserService = this.register(this._instantiationService.createInstance(CoreBrowserService, + this.textarea, + parent.ownerDocument.defaultView ?? window, + // Force unsafe null in node.js environment for tests + this._document ?? (typeof window !== 'undefined') ? window.document : null as any + )); this._instantiationService.setService(ICoreBrowserService, this._coreBrowserService); this.register(addDisposableDomListener(this.textarea, 'focus', (ev: KeyboardEvent) => this._handleTextAreaFocus(ev))); diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index b36e3b497..1ad5bc76b 100644 --- a/src/browser/TestUtils.test.ts +++ b/src/browser/TestUtils.test.ts @@ -350,6 +350,7 @@ export class MockCompositionHelper implements ICompositionHelper { } export class MockCoreBrowserService implements ICoreBrowserService { + public onWindowChange = new EventEmitter().event; public serviceBrand: undefined; public isFocused: boolean = true; public get window(): Window & typeof globalThis { diff --git a/src/browser/decorations/OverviewRulerRenderer.ts b/src/browser/decorations/OverviewRulerRenderer.ts index d63d87450..8648248a7 100644 --- a/src/browser/decorations/OverviewRulerRenderer.ts +++ b/src/browser/decorations/OverviewRulerRenderer.ts @@ -112,6 +112,7 @@ export class OverviewRulerRenderer extends Disposable { // overview ruler width changed this.register(this._optionsService.onSpecificOptionChange('overviewRulerWidth', () => this._queueRefresh(true))); // device pixel ratio changed + // TODO: Observe DPR instead this.register(addDisposableDomListener(this._coreBrowserService.window, 'resize', () => this._queueRefresh(true))); // set the canvas dimensions this._queueRefresh(true); diff --git a/src/browser/services/CoreBrowserService.ts b/src/browser/services/CoreBrowserService.ts index ad61deecb..2c598606f 100644 --- a/src/browser/services/CoreBrowserService.ts +++ b/src/browser/services/CoreBrowserService.ts @@ -3,23 +3,42 @@ * @license MIT */ +import { Disposable } from 'common/Lifecycle'; import { ICoreBrowserService } from './Services'; +import { EventEmitter } from 'common/EventEmitter'; -export class CoreBrowserService implements ICoreBrowserService { +export class CoreBrowserService extends Disposable implements ICoreBrowserService { public serviceBrand: undefined; private _isFocused = false; private _cachedIsFocused: boolean | undefined = undefined; + private readonly _onWindowChange = this.register(new EventEmitter()); + public readonly onWindowChange = this._onWindowChange.event; + constructor( private _textarea: HTMLTextAreaElement, - public readonly window: Window & typeof globalThis, + // TODO: Add getter and setter and event + private _window: Window & typeof globalThis, public readonly mainDocument: Document ) { + super(); + this._textarea.addEventListener('focus', () => this._isFocused = true); this._textarea.addEventListener('blur', () => this._isFocused = false); } + public get window(): Window & typeof globalThis { + return this._window; + } + + public set window(value: Window & typeof globalThis) { + if (this._window !== value) { + this._window = value; + this._onWindowChange.fire(this._window); + } + } + public get dpr(): number { return this.window.devicePixelRatio; } diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index e6d259c28..9fc3f8a9e 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -12,7 +12,7 @@ import { ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } import { EventEmitter } from 'common/EventEmitter'; import { Disposable, MutableDisposable } from 'common/Lifecycle'; import { DebouncedIdleTask } from 'common/TaskQueue'; -import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services'; +import { IBufferService, IDecorationService, IInstantiationService, IOptionsService } from 'common/services/Services'; interface ISelectionState { start: [number, number] | undefined; @@ -59,6 +59,7 @@ export class RenderService extends Disposable implements IRenderService { @IDecorationService decorationService: IDecorationService, @IBufferService bufferService: IBufferService, @ICoreBrowserService coreBrowserService: ICoreBrowserService, + @IInstantiationService instantiationService: IInstantiationService, @IThemeService themeService: IThemeService ) { super(); @@ -66,9 +67,8 @@ export class RenderService extends Disposable implements IRenderService { this._renderDebouncer = new RenderDebouncer(coreBrowserService.window, (start, end) => this._renderRows(start, end)); this.register(this._renderDebouncer); - this._screenDprMonitor = new ScreenDprMonitor(coreBrowserService.window); + this._screenDprMonitor = this.register(instantiationService.createInstance(ScreenDprMonitor)); this._screenDprMonitor.setListener(() => this.handleDevicePixelRatioChange()); - this.register(this._screenDprMonitor); this.register(bufferService.onResize(() => this._fullRefresh())); this.register(bufferService.buffers.onBufferActivate(() => this._renderer.value?.clear())); diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts index 50c718258..a1faab3d1 100644 --- a/src/browser/services/Services.ts +++ b/src/browser/services/Services.ts @@ -28,15 +28,17 @@ export interface ICoreBrowserService { serviceBrand: undefined; readonly isFocused: boolean; + + onWindowChange: IEvent; /** - * Parent window that the terminal is rendered into. DOM and rendering APIs - * (e.g. requestAnimationFrame) should be invoked in the context of this - * window. + * Gets or sets the parent window that the terminal is rendered into. DOM and rendering APIs (e.g. + * requestAnimationFrame) should be invoked in the context of this window. This should be set when + * the window hosting the xterm.js instance changes. */ - readonly window: Window & typeof globalThis; + window: Window & typeof globalThis; /** - * The document of the primary window if working with multiple windows. This - * is set by the documentOverride setting. + * The document of the primary window to be used to create elements when working with multiple + * windows. This is defined by the documentOverride setting. */ readonly mainDocument: Document; /** From 63790f8854d5b6773b0cf1e303b9e5167da5681e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 17 Oct 2023 08:49:28 -0700 Subject: [PATCH 04/12] Change ScreenDprMonitor to be event based --- src/browser/AccessibilityManager.ts | 4 +- src/browser/ScreenDprMonitor.ts | 52 +++++++++---------- .../decorations/BufferDecorationRenderer.ts | 1 + .../decorations/OverviewRulerRenderer.ts | 2 +- src/browser/services/RenderService.ts | 3 +- 5 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index e8cd58886..ed2c12b0b 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -98,10 +98,10 @@ export class AccessibilityManager extends Disposable { this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions())); this._screenDprMonitor = this.register(instantiationService.createInstance(ScreenDprMonitor)); - this.register(this._screenDprMonitor); - this._screenDprMonitor.setListener(() => this._refreshRowsDimensions()); + this.register(this._screenDprMonitor.onDprChange(() => this._refreshRowsDimensions())); // This shouldn't be needed on modern browsers but is present in case the // media query that drives the ScreenDprMonitor isn't supported + // TODO: Listen to window change this.register(addDisposableDomListener(window, 'resize', () => this._refreshRowsDimensions())); this._refreshRows(); diff --git a/src/browser/ScreenDprMonitor.ts b/src/browser/ScreenDprMonitor.ts index 5bb2aa721..6ff016e5d 100644 --- a/src/browser/ScreenDprMonitor.ts +++ b/src/browser/ScreenDprMonitor.ts @@ -4,10 +4,9 @@ */ import { ICoreBrowserService } from 'browser/services/Services'; +import { EventEmitter } from 'common/EventEmitter'; import { Disposable, toDisposable } from 'common/Lifecycle'; -export type ScreenDprListener = (newDevicePixelRatio?: number, oldDevicePixelRatio?: number) => void; - /** * The screen device pixel ratio monitor allows listening for when the * window.devicePixelRatio value changes. This is done not with polling but with @@ -21,39 +20,41 @@ export type ScreenDprListener = (newDevicePixelRatio?: number, oldDevicePixelRat export class ScreenDprMonitor extends Disposable { private _currentDevicePixelRatio: number; private _outerListener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | undefined; - private _listener: ScreenDprListener | undefined; private _resolutionMediaMatchList: MediaQueryList | undefined; private _parentWindow: Window; + private readonly _onDprChange = this.register(new EventEmitter()); + public readonly onDprChange = this._onDprChange.event; + constructor(@ICoreBrowserService coreBrowserService: ICoreBrowserService) { super(); - this._parentWindow = coreBrowserService.window; - this.register(coreBrowserService.onWindowChange(w => { - this._parentWindow = w; - if (this._listener && this._parentWindow.devicePixelRatio !== this._currentDevicePixelRatio) { - this._listener(this._parentWindow.devicePixelRatio, this._currentDevicePixelRatio); - } - this._updateDpr(); - })); - this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio; - this.register(toDisposable(() => { - this.clearListener(); - })); - } - public setListener(listener: ScreenDprListener): void { - if (this._listener) { - this.clearListener(); - } - this._listener = listener; + this._parentWindow = coreBrowserService.window; + + // Initialize listener and dpr value this._outerListener = () => { - if (!this._listener) { - return; + if (this._parentWindow.devicePixelRatio !== this._currentDevicePixelRatio) { + this._onDprChange.fire(this._parentWindow.devicePixelRatio); } - this._listener(this._parentWindow.devicePixelRatio, this._currentDevicePixelRatio); this._updateDpr(); }; + this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio; this._updateDpr(); + + // Listen for window changes + this.register(coreBrowserService.onWindowChange(w => { + this._parentWindow = w; + if (this._parentWindow.devicePixelRatio !== this._currentDevicePixelRatio) { + this._onDprChange.fire(this._parentWindow.devicePixelRatio); + } + this._updateDpr(); + })); + + // Setup additional disposables + this.register(toDisposable(() => this.clearListener())); + } + + public setListener(): void { } private _updateDpr(): void { @@ -71,12 +72,11 @@ export class ScreenDprMonitor extends Disposable { } public clearListener(): void { - if (!this._resolutionMediaMatchList || !this._listener || !this._outerListener) { + if (!this._resolutionMediaMatchList || !this._outerListener) { return; } this._resolutionMediaMatchList.removeListener(this._outerListener); this._resolutionMediaMatchList = undefined; - this._listener = undefined; this._outerListener = undefined; } } diff --git a/src/browser/decorations/BufferDecorationRenderer.ts b/src/browser/decorations/BufferDecorationRenderer.ts index 0cac7bc78..3a0d78621 100644 --- a/src/browser/decorations/BufferDecorationRenderer.ts +++ b/src/browser/decorations/BufferDecorationRenderer.ts @@ -34,6 +34,7 @@ export class BufferDecorationRenderer extends Disposable { this._dimensionsChanged = true; this._queueRefresh(); })); + // TODO: Listen to window change this.register(addDisposableDomListener(window, 'resize', () => this._queueRefresh())); this.register(this._bufferService.buffers.onBufferActivate(() => { this._altBufferIsActive = this._bufferService.buffer === this._bufferService.buffers.alt; diff --git a/src/browser/decorations/OverviewRulerRenderer.ts b/src/browser/decorations/OverviewRulerRenderer.ts index 8648248a7..57b6dfc62 100644 --- a/src/browser/decorations/OverviewRulerRenderer.ts +++ b/src/browser/decorations/OverviewRulerRenderer.ts @@ -112,7 +112,7 @@ export class OverviewRulerRenderer extends Disposable { // overview ruler width changed this.register(this._optionsService.onSpecificOptionChange('overviewRulerWidth', () => this._queueRefresh(true))); // device pixel ratio changed - // TODO: Observe DPR instead + // TODO: Observe DPR instead / listen to window change this.register(addDisposableDomListener(this._coreBrowserService.window, 'resize', () => this._queueRefresh(true))); // set the canvas dimensions this._queueRefresh(true); diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 9fc3f8a9e..60c277980 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -68,7 +68,7 @@ export class RenderService extends Disposable implements IRenderService { this.register(this._renderDebouncer); this._screenDprMonitor = this.register(instantiationService.createInstance(ScreenDprMonitor)); - this._screenDprMonitor.setListener(() => this.handleDevicePixelRatioChange()); + this.register(this._screenDprMonitor.onDprChange(() => this.handleDevicePixelRatioChange())); this.register(bufferService.onResize(() => this._fullRefresh())); this.register(bufferService.buffers.onBufferActivate(() => this._renderer.value?.clear())); @@ -106,6 +106,7 @@ export class RenderService extends Disposable implements IRenderService { // dprchange should handle this case, we need this as well for browsers that don't support the // matchMedia query. + // TODO: Listen to window change this.register(addDisposableDomListener(coreBrowserService.window, 'resize', () => this.handleDevicePixelRatioChange())); this.register(themeService.onChangeColors(() => this._fullRefresh())); From 4cc51ca08fa8a38e8859d60a5a594a876e28c3d5 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 17 Oct 2023 08:55:07 -0700 Subject: [PATCH 05/12] Move ScreenDprMonitor into CoreBrowserService --- src/browser/AccessibilityManager.ts | 6 +----- src/browser/ScreenDprMonitor.ts | 22 +++++++--------------- src/browser/TestUtils.test.ts | 1 + src/browser/services/CoreBrowserService.ts | 10 ++++++++-- src/browser/services/RenderService.ts | 5 +---- src/browser/services/Services.ts | 4 +++- 6 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index ed2c12b0b..703459f41 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -7,7 +7,6 @@ import * as Strings from 'browser/LocalizableStrings'; import { ITerminal, IRenderDebouncer } from 'browser/Types'; import { TimeBasedDebouncer } from 'browser/TimeBasedDebouncer'; import { Disposable, toDisposable } from 'common/Lifecycle'; -import { ScreenDprMonitor } from 'browser/ScreenDprMonitor'; import { ICoreBrowserService, IRenderService } from 'browser/services/Services'; import { addDisposableDomListener } from 'browser/Lifecycle'; import { IBuffer } from 'common/buffer/Types'; @@ -30,8 +29,6 @@ export class AccessibilityManager extends Disposable { private _liveRegionLineCount: number = 0; private _liveRegionDebouncer: IRenderDebouncer; - private _screenDprMonitor: ScreenDprMonitor; - private _topBoundaryFocusListener: (e: FocusEvent) => void; private _bottomBoundaryFocusListener: (e: FocusEvent) => void; @@ -97,8 +94,7 @@ export class AccessibilityManager extends Disposable { this.register(this._terminal.onBlur(() => this._clearLiveRegion())); this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions())); - this._screenDprMonitor = this.register(instantiationService.createInstance(ScreenDprMonitor)); - this.register(this._screenDprMonitor.onDprChange(() => this._refreshRowsDimensions())); + this.register(this._coreBrowserService.onDprChange(() => this._refreshRowsDimensions())); // This shouldn't be needed on modern browsers but is present in case the // media query that drives the ScreenDprMonitor isn't supported // TODO: Listen to window change diff --git a/src/browser/ScreenDprMonitor.ts b/src/browser/ScreenDprMonitor.ts index 6ff016e5d..1152a55a5 100644 --- a/src/browser/ScreenDprMonitor.ts +++ b/src/browser/ScreenDprMonitor.ts @@ -3,7 +3,6 @@ * @license MIT */ -import { ICoreBrowserService } from 'browser/services/Services'; import { EventEmitter } from 'common/EventEmitter'; import { Disposable, toDisposable } from 'common/Lifecycle'; @@ -21,16 +20,13 @@ export class ScreenDprMonitor extends Disposable { private _currentDevicePixelRatio: number; private _outerListener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | undefined; private _resolutionMediaMatchList: MediaQueryList | undefined; - private _parentWindow: Window; private readonly _onDprChange = this.register(new EventEmitter()); public readonly onDprChange = this._onDprChange.event; - constructor(@ICoreBrowserService coreBrowserService: ICoreBrowserService) { + constructor(private _parentWindow: Window) { super(); - this._parentWindow = coreBrowserService.window; - // Initialize listener and dpr value this._outerListener = () => { if (this._parentWindow.devicePixelRatio !== this._currentDevicePixelRatio) { @@ -41,20 +37,16 @@ export class ScreenDprMonitor extends Disposable { this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio; this._updateDpr(); - // Listen for window changes - this.register(coreBrowserService.onWindowChange(w => { - this._parentWindow = w; - if (this._parentWindow.devicePixelRatio !== this._currentDevicePixelRatio) { - this._onDprChange.fire(this._parentWindow.devicePixelRatio); - } - this._updateDpr(); - })); - // Setup additional disposables this.register(toDisposable(() => this.clearListener())); } - public setListener(): void { + public setWindow(parentWindow: Window): void { + this._parentWindow = parentWindow; + if (this._parentWindow.devicePixelRatio !== this._currentDevicePixelRatio) { + this._onDprChange.fire(this._parentWindow.devicePixelRatio); + } + this._updateDpr(); } private _updateDpr(): void { diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index 1ad5bc76b..d557cd989 100644 --- a/src/browser/TestUtils.test.ts +++ b/src/browser/TestUtils.test.ts @@ -350,6 +350,7 @@ export class MockCompositionHelper implements ICompositionHelper { } export class MockCoreBrowserService implements ICoreBrowserService { + public onDprChange = new EventEmitter().event; public onWindowChange = new EventEmitter().event; public serviceBrand: undefined; public isFocused: boolean = true; diff --git a/src/browser/services/CoreBrowserService.ts b/src/browser/services/CoreBrowserService.ts index 2c598606f..8a189315d 100644 --- a/src/browser/services/CoreBrowserService.ts +++ b/src/browser/services/CoreBrowserService.ts @@ -5,25 +5,31 @@ import { Disposable } from 'common/Lifecycle'; import { ICoreBrowserService } from './Services'; -import { EventEmitter } from 'common/EventEmitter'; +import { EventEmitter, forwardEvent } from 'common/EventEmitter'; +import { ScreenDprMonitor } from 'browser/ScreenDprMonitor'; export class CoreBrowserService extends Disposable implements ICoreBrowserService { public serviceBrand: undefined; private _isFocused = false; private _cachedIsFocused: boolean | undefined = undefined; + private _screenDprMonitor = new ScreenDprMonitor(this._window); + private readonly _onDprChange = this.register(new EventEmitter()); + public readonly onDprChange = this._onDprChange.event; private readonly _onWindowChange = this.register(new EventEmitter()); public readonly onWindowChange = this._onWindowChange.event; constructor( private _textarea: HTMLTextAreaElement, - // TODO: Add getter and setter and event private _window: Window & typeof globalThis, public readonly mainDocument: Document ) { super(); + this.register(this.onWindowChange(w => this._screenDprMonitor.setWindow(w))); + this.register(forwardEvent(this._screenDprMonitor.onDprChange, this._onDprChange)); + this._textarea.addEventListener('focus', () => this._isFocused = true); this._textarea.addEventListener('blur', () => this._isFocused = false); } diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 60c277980..179ddeb2c 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -5,7 +5,6 @@ import { addDisposableDomListener } from 'browser/Lifecycle'; import { RenderDebouncer } from 'browser/RenderDebouncer'; -import { ScreenDprMonitor } from 'browser/ScreenDprMonitor'; import { IRenderDebouncerWithCallback } from 'browser/Types'; import { IRenderDimensions, IRenderer } from 'browser/renderer/shared/Types'; import { ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; @@ -25,7 +24,6 @@ export class RenderService extends Disposable implements IRenderService { private _renderer: MutableDisposable = this.register(new MutableDisposable()); private _renderDebouncer: IRenderDebouncerWithCallback; - private _screenDprMonitor: ScreenDprMonitor; private _pausedResizeTask = new DebouncedIdleTask(); private _isPaused: boolean = false; @@ -67,8 +65,7 @@ export class RenderService extends Disposable implements IRenderService { this._renderDebouncer = new RenderDebouncer(coreBrowserService.window, (start, end) => this._renderRows(start, end)); this.register(this._renderDebouncer); - this._screenDprMonitor = this.register(instantiationService.createInstance(ScreenDprMonitor)); - this.register(this._screenDprMonitor.onDprChange(() => this.handleDevicePixelRatioChange())); + this.register(coreBrowserService.onDprChange(() => this.handleDevicePixelRatioChange())); this.register(bufferService.onResize(() => this._fullRefresh())); this.register(bufferService.buffers.onBufferActivate(() => this._renderer.value?.clear())); diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts index a1faab3d1..5c14fa8af 100644 --- a/src/browser/services/Services.ts +++ b/src/browser/services/Services.ts @@ -29,7 +29,9 @@ export interface ICoreBrowserService { readonly isFocused: boolean; - onWindowChange: IEvent; + readonly onDprChange: IEvent; + readonly onWindowChange: IEvent; + /** * Gets or sets the parent window that the terminal is rendered into. DOM and rendering APIs (e.g. * requestAnimationFrame) should be invoked in the context of this window. This should be set when From 92535e539f371d46ea28de7666de1ce68f1f770f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 17 Oct 2023 08:56:02 -0700 Subject: [PATCH 06/12] Inline ScreenDprMonitor into CoreBrowserService file --- src/browser/ScreenDprMonitor.ts | 74 ---------------------- src/browser/services/CoreBrowserService.ts | 71 ++++++++++++++++++++- 2 files changed, 69 insertions(+), 76 deletions(-) delete mode 100644 src/browser/ScreenDprMonitor.ts diff --git a/src/browser/ScreenDprMonitor.ts b/src/browser/ScreenDprMonitor.ts deleted file mode 100644 index 1152a55a5..000000000 --- a/src/browser/ScreenDprMonitor.ts +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { EventEmitter } from 'common/EventEmitter'; -import { Disposable, toDisposable } from 'common/Lifecycle'; - -/** - * The screen device pixel ratio monitor allows listening for when the - * window.devicePixelRatio value changes. This is done not with polling but with - * the use of window.matchMedia to watch media queries. When the event fires, - * the listener will be reattached using a different media query to ensure that - * any further changes will register. - * - * The listener should fire on both window zoom changes and switching to a - * monitor with a different DPI. - */ -export class ScreenDprMonitor extends Disposable { - private _currentDevicePixelRatio: number; - private _outerListener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | undefined; - private _resolutionMediaMatchList: MediaQueryList | undefined; - - private readonly _onDprChange = this.register(new EventEmitter()); - public readonly onDprChange = this._onDprChange.event; - - constructor(private _parentWindow: Window) { - super(); - - // Initialize listener and dpr value - this._outerListener = () => { - if (this._parentWindow.devicePixelRatio !== this._currentDevicePixelRatio) { - this._onDprChange.fire(this._parentWindow.devicePixelRatio); - } - this._updateDpr(); - }; - this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio; - this._updateDpr(); - - // Setup additional disposables - this.register(toDisposable(() => this.clearListener())); - } - - public setWindow(parentWindow: Window): void { - this._parentWindow = parentWindow; - if (this._parentWindow.devicePixelRatio !== this._currentDevicePixelRatio) { - this._onDprChange.fire(this._parentWindow.devicePixelRatio); - } - this._updateDpr(); - } - - private _updateDpr(): void { - if (!this._outerListener) { - return; - } - - // Clear listeners for old DPR - this._resolutionMediaMatchList?.removeListener(this._outerListener); - - // Add listeners for new DPR - this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio; - this._resolutionMediaMatchList = this._parentWindow.matchMedia(`screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)`); - this._resolutionMediaMatchList.addListener(this._outerListener); - } - - public clearListener(): void { - if (!this._resolutionMediaMatchList || !this._outerListener) { - return; - } - this._resolutionMediaMatchList.removeListener(this._outerListener); - this._resolutionMediaMatchList = undefined; - this._outerListener = undefined; - } -} diff --git a/src/browser/services/CoreBrowserService.ts b/src/browser/services/CoreBrowserService.ts index 8a189315d..dfd3474d0 100644 --- a/src/browser/services/CoreBrowserService.ts +++ b/src/browser/services/CoreBrowserService.ts @@ -3,10 +3,9 @@ * @license MIT */ -import { Disposable } from 'common/Lifecycle'; +import { Disposable, toDisposable } from 'common/Lifecycle'; import { ICoreBrowserService } from './Services'; import { EventEmitter, forwardEvent } from 'common/EventEmitter'; -import { ScreenDprMonitor } from 'browser/ScreenDprMonitor'; export class CoreBrowserService extends Disposable implements ICoreBrowserService { public serviceBrand: undefined; @@ -57,3 +56,71 @@ export class CoreBrowserService extends Disposable implements ICoreBrowserServic return this._cachedIsFocused; } } + + +/** + * The screen device pixel ratio monitor allows listening for when the + * window.devicePixelRatio value changes. This is done not with polling but with + * the use of window.matchMedia to watch media queries. When the event fires, + * the listener will be reattached using a different media query to ensure that + * any further changes will register. + * + * The listener should fire on both window zoom changes and switching to a + * monitor with a different DPI. + */ +class ScreenDprMonitor extends Disposable { + private _currentDevicePixelRatio: number; + private _outerListener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | undefined; + private _resolutionMediaMatchList: MediaQueryList | undefined; + + private readonly _onDprChange = this.register(new EventEmitter()); + public readonly onDprChange = this._onDprChange.event; + + constructor(private _parentWindow: Window) { + super(); + + // Initialize listener and dpr value + this._outerListener = () => { + if (this._parentWindow.devicePixelRatio !== this._currentDevicePixelRatio) { + this._onDprChange.fire(this._parentWindow.devicePixelRatio); + } + this._updateDpr(); + }; + this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio; + this._updateDpr(); + + // Setup additional disposables + this.register(toDisposable(() => this.clearListener())); + } + + public setWindow(parentWindow: Window): void { + this._parentWindow = parentWindow; + if (this._parentWindow.devicePixelRatio !== this._currentDevicePixelRatio) { + this._onDprChange.fire(this._parentWindow.devicePixelRatio); + } + this._updateDpr(); + } + + private _updateDpr(): void { + if (!this._outerListener) { + return; + } + + // Clear listeners for old DPR + this._resolutionMediaMatchList?.removeListener(this._outerListener); + + // Add listeners for new DPR + this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio; + this._resolutionMediaMatchList = this._parentWindow.matchMedia(`screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)`); + this._resolutionMediaMatchList.addListener(this._outerListener); + } + + public clearListener(): void { + if (!this._resolutionMediaMatchList || !this._outerListener) { + return; + } + this._resolutionMediaMatchList.removeListener(this._outerListener); + this._resolutionMediaMatchList = undefined; + this._outerListener = undefined; + } +} From 6e12d94f11bdb2b12d5b6bedd419f893c9004f64 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 17 Oct 2023 09:10:04 -0700 Subject: [PATCH 07/12] Move window resize listeners over to onWindowChange --- src/browser/AccessibilityManager.ts | 9 ++++++--- src/browser/decorations/BufferDecorationRenderer.ts | 9 ++++++--- src/browser/decorations/OverviewRulerRenderer.ts | 9 ++++++--- src/browser/services/RenderService.ts | 8 ++++++-- src/common/EventEmitter.ts | 5 +++++ typings/xterm.d.ts | 3 ++- 6 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index 703459f41..de00a1d34 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -6,11 +6,12 @@ import * as Strings from 'browser/LocalizableStrings'; import { ITerminal, IRenderDebouncer } from 'browser/Types'; import { TimeBasedDebouncer } from 'browser/TimeBasedDebouncer'; -import { Disposable, toDisposable } from 'common/Lifecycle'; +import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; import { ICoreBrowserService, IRenderService } from 'browser/services/Services'; import { addDisposableDomListener } from 'browser/Lifecycle'; import { IBuffer } from 'common/buffer/Types'; import { IInstantiationService } from 'common/services/Services'; +import { runAndSubscribe } from 'common/EventEmitter'; const MAX_ROWS_TO_READ = 20; @@ -97,8 +98,10 @@ export class AccessibilityManager extends Disposable { this.register(this._coreBrowserService.onDprChange(() => this._refreshRowsDimensions())); // This shouldn't be needed on modern browsers but is present in case the // media query that drives the ScreenDprMonitor isn't supported - // TODO: Listen to window change - this.register(addDisposableDomListener(window, 'resize', () => this._refreshRowsDimensions())); + const windowResizeListener = this.register(new MutableDisposable()); + this.register(runAndSubscribe(this._coreBrowserService.onWindowChange, () => { + windowResizeListener.value = addDisposableDomListener(this._coreBrowserService.window, 'resize', () => this._refreshRowsDimensions()); + })); this._refreshRows(); this.register(toDisposable(() => { diff --git a/src/browser/decorations/BufferDecorationRenderer.ts b/src/browser/decorations/BufferDecorationRenderer.ts index 3a0d78621..3fe56512b 100644 --- a/src/browser/decorations/BufferDecorationRenderer.ts +++ b/src/browser/decorations/BufferDecorationRenderer.ts @@ -5,7 +5,8 @@ import { addDisposableDomListener } from 'browser/Lifecycle'; import { ICoreBrowserService, IRenderService } from 'browser/services/Services'; -import { Disposable, toDisposable } from 'common/Lifecycle'; +import { runAndSubscribe } from 'common/EventEmitter'; +import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; import { IBufferService, IDecorationService, IInternalDecoration } from 'common/services/Services'; export class BufferDecorationRenderer extends Disposable { @@ -34,8 +35,10 @@ export class BufferDecorationRenderer extends Disposable { this._dimensionsChanged = true; this._queueRefresh(); })); - // TODO: Listen to window change - this.register(addDisposableDomListener(window, 'resize', () => this._queueRefresh())); + const windowResizeListener = this.register(new MutableDisposable()); + this.register(runAndSubscribe(this._coreBrowserService.onWindowChange, () => { + windowResizeListener.value = addDisposableDomListener(this._coreBrowserService.window, 'resize', () => this._queueRefresh()); + })); this.register(this._bufferService.buffers.onBufferActivate(() => { this._altBufferIsActive = this._bufferService.buffer === this._bufferService.buffers.alt; })); diff --git a/src/browser/decorations/OverviewRulerRenderer.ts b/src/browser/decorations/OverviewRulerRenderer.ts index 57b6dfc62..157775f92 100644 --- a/src/browser/decorations/OverviewRulerRenderer.ts +++ b/src/browser/decorations/OverviewRulerRenderer.ts @@ -6,7 +6,8 @@ import { ColorZoneStore, IColorZone, IColorZoneStore } from 'browser/decorations/ColorZoneStore'; import { addDisposableDomListener } from 'browser/Lifecycle'; import { ICoreBrowserService, IRenderService } from 'browser/services/Services'; -import { Disposable, toDisposable } from 'common/Lifecycle'; +import { runAndSubscribe } from 'common/EventEmitter'; +import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services'; // Helper objects to avoid excessive calculation and garbage collection during rendering. These are @@ -112,8 +113,10 @@ export class OverviewRulerRenderer extends Disposable { // overview ruler width changed this.register(this._optionsService.onSpecificOptionChange('overviewRulerWidth', () => this._queueRefresh(true))); // device pixel ratio changed - // TODO: Observe DPR instead / listen to window change - this.register(addDisposableDomListener(this._coreBrowserService.window, 'resize', () => this._queueRefresh(true))); + const windowResizeListener = this.register(new MutableDisposable()); + this.register(runAndSubscribe(this._coreBrowserService.onWindowChange, () => { + windowResizeListener.value = addDisposableDomListener(this._coreBrowserService.window, 'resize', () => this._queueRefresh(true)); + })); // set the canvas dimensions this._queueRefresh(true); } diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 179ddeb2c..7b9e6d79c 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -8,7 +8,7 @@ import { RenderDebouncer } from 'browser/RenderDebouncer'; import { IRenderDebouncerWithCallback } from 'browser/Types'; import { IRenderDimensions, IRenderer } from 'browser/renderer/shared/Types'; import { ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; -import { EventEmitter } from 'common/EventEmitter'; +import { EventEmitter, runAndSubscribe } from 'common/EventEmitter'; import { Disposable, MutableDisposable } from 'common/Lifecycle'; import { DebouncedIdleTask } from 'common/TaskQueue'; import { IBufferService, IDecorationService, IInstantiationService, IOptionsService } from 'common/services/Services'; @@ -103,7 +103,11 @@ export class RenderService extends Disposable implements IRenderService { // dprchange should handle this case, we need this as well for browsers that don't support the // matchMedia query. - // TODO: Listen to window change + // TODO: Merge this into onDprChange? + const windowResizeListener = this.register(new MutableDisposable()); + this.register(runAndSubscribe(coreBrowserService.onWindowChange, () => { + windowResizeListener.value = addDisposableDomListener(coreBrowserService.window, 'resize', () => this.handleDevicePixelRatioChange()); + })); this.register(addDisposableDomListener(coreBrowserService.window, 'resize', () => this.handleDevicePixelRatioChange())); this.register(themeService.onChangeColors(() => this._fullRefresh())); diff --git a/src/common/EventEmitter.ts b/src/common/EventEmitter.ts index fd9590424..589748a30 100644 --- a/src/common/EventEmitter.ts +++ b/src/common/EventEmitter.ts @@ -71,3 +71,8 @@ export class EventEmitter implements IEventEmitter { export function forwardEvent(from: IEvent, to: IEventEmitter): IDisposable { return from(e => to.fire(e)); } + +export function runAndSubscribe(event: IEvent, handler: (e: T | undefined) => any): IDisposable { + handler(undefined); + return event(e => handler(e)); +} diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index b8028a181..b8c114554 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -973,7 +973,8 @@ declare module 'xterm' { resize(columns: number, rows: number): void; /** - * Opens the terminal within an element. + * Opens the terminal within an element. This should also be called if the + * xterm.js element ever changes browser window. * @param parent The element to create the terminal within. This element * must be visible (have dimensions) when `open` is called as several DOM- * based measurements need to be performed when this function is called. From 81e0718a74635840c7fdbcf7bbb96ec3c7708002 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 17 Oct 2023 09:18:46 -0700 Subject: [PATCH 08/12] Move window resize edge case into dpr monitor --- src/browser/AccessibilityManager.ts | 7 ------ .../decorations/BufferDecorationRenderer.ts | 9 ++----- .../decorations/OverviewRulerRenderer.ts | 9 ++----- src/browser/services/CoreBrowserService.ts | 25 +++++++++++++------ src/browser/services/RenderService.ts | 12 +-------- 5 files changed, 23 insertions(+), 39 deletions(-) diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index de00a1d34..54f50ef2c 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -94,14 +94,7 @@ export class AccessibilityManager extends Disposable { this.register(this._terminal.onKey(e => this._handleKey(e.key))); this.register(this._terminal.onBlur(() => this._clearLiveRegion())); this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions())); - this.register(this._coreBrowserService.onDprChange(() => this._refreshRowsDimensions())); - // This shouldn't be needed on modern browsers but is present in case the - // media query that drives the ScreenDprMonitor isn't supported - const windowResizeListener = this.register(new MutableDisposable()); - this.register(runAndSubscribe(this._coreBrowserService.onWindowChange, () => { - windowResizeListener.value = addDisposableDomListener(this._coreBrowserService.window, 'resize', () => this._refreshRowsDimensions()); - })); this._refreshRows(); this.register(toDisposable(() => { diff --git a/src/browser/decorations/BufferDecorationRenderer.ts b/src/browser/decorations/BufferDecorationRenderer.ts index 3fe56512b..6ba9ae1ec 100644 --- a/src/browser/decorations/BufferDecorationRenderer.ts +++ b/src/browser/decorations/BufferDecorationRenderer.ts @@ -3,10 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { addDisposableDomListener } from 'browser/Lifecycle'; import { ICoreBrowserService, IRenderService } from 'browser/services/Services'; -import { runAndSubscribe } from 'common/EventEmitter'; -import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; +import { Disposable, toDisposable } from 'common/Lifecycle'; import { IBufferService, IDecorationService, IInternalDecoration } from 'common/services/Services'; export class BufferDecorationRenderer extends Disposable { @@ -35,10 +33,7 @@ export class BufferDecorationRenderer extends Disposable { this._dimensionsChanged = true; this._queueRefresh(); })); - const windowResizeListener = this.register(new MutableDisposable()); - this.register(runAndSubscribe(this._coreBrowserService.onWindowChange, () => { - windowResizeListener.value = addDisposableDomListener(this._coreBrowserService.window, 'resize', () => this._queueRefresh()); - })); + this.register(this._coreBrowserService.onDprChange(() => this._queueRefresh())); this.register(this._bufferService.buffers.onBufferActivate(() => { this._altBufferIsActive = this._bufferService.buffer === this._bufferService.buffers.alt; })); diff --git a/src/browser/decorations/OverviewRulerRenderer.ts b/src/browser/decorations/OverviewRulerRenderer.ts index 157775f92..103d5d9a6 100644 --- a/src/browser/decorations/OverviewRulerRenderer.ts +++ b/src/browser/decorations/OverviewRulerRenderer.ts @@ -4,10 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { ColorZoneStore, IColorZone, IColorZoneStore } from 'browser/decorations/ColorZoneStore'; -import { addDisposableDomListener } from 'browser/Lifecycle'; import { ICoreBrowserService, IRenderService } from 'browser/services/Services'; -import { runAndSubscribe } from 'common/EventEmitter'; -import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; +import { Disposable, toDisposable } from 'common/Lifecycle'; import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services'; // Helper objects to avoid excessive calculation and garbage collection during rendering. These are @@ -113,10 +111,7 @@ export class OverviewRulerRenderer extends Disposable { // overview ruler width changed this.register(this._optionsService.onSpecificOptionChange('overviewRulerWidth', () => this._queueRefresh(true))); // device pixel ratio changed - const windowResizeListener = this.register(new MutableDisposable()); - this.register(runAndSubscribe(this._coreBrowserService.onWindowChange, () => { - windowResizeListener.value = addDisposableDomListener(this._coreBrowserService.window, 'resize', () => this._queueRefresh(true)); - })); + this.register(this._coreBrowserService.onDprChange(() => this._queueRefresh(true))); // set the canvas dimensions this._queueRefresh(true); } diff --git a/src/browser/services/CoreBrowserService.ts b/src/browser/services/CoreBrowserService.ts index dfd3474d0..575b62b63 100644 --- a/src/browser/services/CoreBrowserService.ts +++ b/src/browser/services/CoreBrowserService.ts @@ -3,9 +3,10 @@ * @license MIT */ -import { Disposable, toDisposable } from 'common/Lifecycle'; +import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; import { ICoreBrowserService } from './Services'; import { EventEmitter, forwardEvent } from 'common/EventEmitter'; +import { addDisposableDomListener } from 'browser/Lifecycle'; export class CoreBrowserService extends Disposable implements ICoreBrowserService { public serviceBrand: undefined; @@ -26,6 +27,7 @@ export class CoreBrowserService extends Disposable implements ICoreBrowserServic ) { super(); + // Monitor device pixel ratio this.register(this.onWindowChange(w => this._screenDprMonitor.setWindow(w))); this.register(forwardEvent(this._screenDprMonitor.onDprChange, this._onDprChange)); @@ -72,6 +74,7 @@ class ScreenDprMonitor extends Disposable { private _currentDevicePixelRatio: number; private _outerListener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | undefined; private _resolutionMediaMatchList: MediaQueryList | undefined; + private _windowResizeListener = this.register(new MutableDisposable()); private readonly _onDprChange = this.register(new EventEmitter()); public readonly onDprChange = this._onDprChange.event; @@ -80,21 +83,29 @@ class ScreenDprMonitor extends Disposable { super(); // Initialize listener and dpr value - this._outerListener = () => { - if (this._parentWindow.devicePixelRatio !== this._currentDevicePixelRatio) { - this._onDprChange.fire(this._parentWindow.devicePixelRatio); - } - this._updateDpr(); - }; + this._outerListener = () => this._setDprAndFireIfDiffers(); this._currentDevicePixelRatio = this._parentWindow.devicePixelRatio; this._updateDpr(); + // Monitor active window resize + this._setWindowResizeListener(); + // Setup additional disposables this.register(toDisposable(() => this.clearListener())); } + public setWindow(parentWindow: Window): void { this._parentWindow = parentWindow; + this._setWindowResizeListener(); + this._setDprAndFireIfDiffers(); + } + + private _setWindowResizeListener(): void { + this._windowResizeListener.value = addDisposableDomListener(this._parentWindow, 'resize', () => this._setDprAndFireIfDiffers()); + } + + private _setDprAndFireIfDiffers(): void { if (this._parentWindow.devicePixelRatio !== this._currentDevicePixelRatio) { this._onDprChange.fire(this._parentWindow.devicePixelRatio); } diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 7b9e6d79c..9fa8d234c 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -3,12 +3,11 @@ * @license MIT */ -import { addDisposableDomListener } from 'browser/Lifecycle'; import { RenderDebouncer } from 'browser/RenderDebouncer'; import { IRenderDebouncerWithCallback } from 'browser/Types'; import { IRenderDimensions, IRenderer } from 'browser/renderer/shared/Types'; import { ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; -import { EventEmitter, runAndSubscribe } from 'common/EventEmitter'; +import { EventEmitter } from 'common/EventEmitter'; import { Disposable, MutableDisposable } from 'common/Lifecycle'; import { DebouncedIdleTask } from 'common/TaskQueue'; import { IBufferService, IDecorationService, IInstantiationService, IOptionsService } from 'common/services/Services'; @@ -101,15 +100,6 @@ export class RenderService extends Disposable implements IRenderService { 'cursorStyle' ], () => this.refreshRows(bufferService.buffer.y, bufferService.buffer.y, true))); - // dprchange should handle this case, we need this as well for browsers that don't support the - // matchMedia query. - // TODO: Merge this into onDprChange? - const windowResizeListener = this.register(new MutableDisposable()); - this.register(runAndSubscribe(coreBrowserService.onWindowChange, () => { - windowResizeListener.value = addDisposableDomListener(coreBrowserService.window, 'resize', () => this.handleDevicePixelRatioChange()); - })); - this.register(addDisposableDomListener(coreBrowserService.window, 'resize', () => this.handleDevicePixelRatioChange())); - this.register(themeService.onChangeColors(() => this._fullRefresh())); // Detect whether IntersectionObserver is detected and enable renderer pause From 20a26a65897c51dcadc7be244bb8b27b66b03ac4 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 17 Oct 2023 10:54:29 -0700 Subject: [PATCH 09/12] Remove unused imports --- src/browser/AccessibilityManager.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/browser/AccessibilityManager.ts b/src/browser/AccessibilityManager.ts index 54f50ef2c..5f5295892 100644 --- a/src/browser/AccessibilityManager.ts +++ b/src/browser/AccessibilityManager.ts @@ -6,12 +6,10 @@ import * as Strings from 'browser/LocalizableStrings'; import { ITerminal, IRenderDebouncer } from 'browser/Types'; import { TimeBasedDebouncer } from 'browser/TimeBasedDebouncer'; -import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; +import { Disposable, toDisposable } from 'common/Lifecycle'; import { ICoreBrowserService, IRenderService } from 'browser/services/Services'; -import { addDisposableDomListener } from 'browser/Lifecycle'; import { IBuffer } from 'common/buffer/Types'; import { IInstantiationService } from 'common/services/Services'; -import { runAndSubscribe } from 'common/EventEmitter'; const MAX_ROWS_TO_READ = 20; From 4681b319ca4712ae5d05433f6834a6f8a4a258b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Oct 2023 11:47:16 +0000 Subject: [PATCH 10/12] Bump @babel/traverse from 7.22.8 to 7.23.2 Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.8 to 7.23.2. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 94 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 77 insertions(+), 17 deletions(-) diff --git a/yarn.lock b/yarn.lock index ffebb5c5d..a0cb39da5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,6 +15,14 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" +"@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + "@babel/code-frame@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" @@ -48,7 +56,7 @@ json5 "^2.2.2" semver "^6.3.1" -"@babel/generator@^7.22.7", "@babel/generator@^7.22.9": +"@babel/generator@^7.22.9": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.9.tgz#572ecfa7a31002fa1de2a9d91621fd895da8493d" integrity sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw== @@ -58,6 +66,16 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" + integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== + dependencies: + "@babel/types" "^7.23.0" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-compilation-targets@^7.22.9": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz#f9d0a7aaaa7cd32a3f31c9316a69f5a9bcacb892" @@ -69,18 +87,23 @@ lru-cache "^5.1.1" semver "^6.3.1" +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + "@babel/helper-environment-visitor@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== -"@babel/helper-function-name@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" - integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== dependencies: - "@babel/template" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" "@babel/helper-hoist-variables@^7.22.5": version "7.22.5" @@ -126,6 +149,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/helper-validator-identifier@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" @@ -145,6 +173,15 @@ "@babel/traverse" "^7.22.6" "@babel/types" "^7.22.5" +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + "@babel/highlight@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" @@ -154,6 +191,11 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" + integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== + "@babel/parser@^7.22.5", "@babel/parser@^7.22.7": version "7.22.7" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" @@ -166,6 +208,15 @@ dependencies: regenerator-runtime "^0.13.11" +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + "@babel/template@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" @@ -176,21 +227,30 @@ "@babel/types" "^7.22.5" "@babel/traverse@^7.22.6", "@babel/traverse@^7.22.8": - version "7.22.8" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.8.tgz#4d4451d31bc34efeae01eac222b514a77aa4000e" - integrity sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw== + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" + integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== dependencies: - "@babel/code-frame" "^7.22.5" - "@babel/generator" "^7.22.7" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.22.7" - "@babel/types" "^7.22.5" + "@babel/parser" "^7.23.0" + "@babel/types" "^7.23.0" debug "^4.1.0" globals "^11.1.0" +"@babel/types@^7.22.15", "@babel/types@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" + integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + "@babel/types@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" @@ -1145,7 +1205,7 @@ chai@^4.3.4: pathval "^1.1.1" type-detect "^4.0.5" -chalk@^2.0.0: +chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== From 6c5fb36c36d194f8ca990eb68556069f970159ec Mon Sep 17 00:00:00 2001 From: David Fiala Date: Thu, 19 Oct 2023 09:52:29 -0700 Subject: [PATCH 11/12] proper isNode detection for node v21.0.0 --- src/common/Platform.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/Platform.ts b/src/common/Platform.ts index 41d8552e4..2f3291995 100644 --- a/src/common/Platform.ts +++ b/src/common/Platform.ts @@ -13,7 +13,7 @@ interface INavigator { // we want this module to live in common. declare const navigator: INavigator; -export const isNode = (typeof navigator === 'undefined') ? true : false; +export const isNode = (typeof process !== 'undefined') ? true : false; const userAgent = (isNode) ? 'node' : navigator.userAgent; const platform = (isNode) ? 'node' : navigator.platform; From 0d0166b4bcae1db109baf3691d4472ea16171589 Mon Sep 17 00:00:00 2001 From: David Fiala Date: Thu, 19 Oct 2023 11:19:27 -0700 Subject: [PATCH 12/12] provide a declaration for 'process' needed for nodejs detect We provide no further type information as it will not be used except for a presence check for nodejs detection. --- src/common/Platform.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/Platform.ts b/src/common/Platform.ts index 2f3291995..1007fc0a3 100644 --- a/src/common/Platform.ts +++ b/src/common/Platform.ts @@ -12,6 +12,7 @@ interface INavigator { // We're declaring a navigator global here as we expect it in all runtimes (node and browser), but // we want this module to live in common. declare const navigator: INavigator; +declare const process: unknown; export const isNode = (typeof process !== 'undefined') ? true : false; const userAgent = (isNode) ? 'node' : navigator.userAgent;