diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index 131774f1..18af3e4e 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -561,7 +561,7 @@ export class Terminal extends CoreTerminal implements ITerminal { } private _createRenderer(): IRenderer { - return this._instantiationService.createInstance(DomRenderer, this.element!, this.screenElement!, this._viewportElement!, this.linkifier2); + return this._instantiationService.createInstance(DomRenderer, this._document!, this.element!, this.screenElement!, this._viewportElement!, this._helperContainer!, this.linkifier2); } /** diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index 5f276954..2e9f3701 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -47,9 +47,11 @@ export class DomRenderer extends Disposable implements IRenderer { public readonly onRequestRedraw = this.register(new EventEmitter()).event; constructor( + private readonly _document: Document, private readonly _element: HTMLElement, private readonly _screenElement: HTMLElement, private readonly _viewportElement: HTMLElement, + private readonly _helperContainer: HTMLElement, private readonly _linkifier2: ILinkifier2, @IInstantiationService instantiationService: IInstantiationService, @ICharSizeService private readonly _charSizeService: ICharSizeService, @@ -59,12 +61,12 @@ export class DomRenderer extends Disposable implements IRenderer { @IThemeService private readonly _themeService: IThemeService ) { super(); - this._rowContainer = document.createElement('div'); + this._rowContainer = this._document.createElement('div'); this._rowContainer.classList.add(ROW_CONTAINER_CLASS); this._rowContainer.style.lineHeight = 'normal'; this._rowContainer.setAttribute('aria-hidden', 'true'); this._refreshRowElements(this._bufferService.cols, this._bufferService.rows); - this._selectionContainer = document.createElement('div'); + this._selectionContainer = this._document.createElement('div'); this._selectionContainer.classList.add(SELECTION_CLASS); this._selectionContainer.setAttribute('aria-hidden', 'true'); @@ -96,7 +98,7 @@ export class DomRenderer extends Disposable implements IRenderer { this._dimensionsStyleElement.remove(); })); - this._widthCache = new WidthCache(document); + this._widthCache = new WidthCache(this._document, this._helperContainer); this._widthCache.setFont( this._optionsService.rawOptions.fontFamily, this._optionsService.rawOptions.fontSize, @@ -130,7 +132,7 @@ export class DomRenderer extends Disposable implements IRenderer { } if (!this._dimensionsStyleElement) { - this._dimensionsStyleElement = document.createElement('style'); + this._dimensionsStyleElement = this._document.createElement('style'); this._screenElement.appendChild(this._dimensionsStyleElement); } @@ -150,7 +152,7 @@ export class DomRenderer extends Disposable implements IRenderer { private _injectCss(colors: ReadonlyColorSet): void { if (!this._themeStyleElement) { - this._themeStyleElement = document.createElement('style'); + this._themeStyleElement = this._document.createElement('style'); this._screenElement.appendChild(this._themeStyleElement); } @@ -276,7 +278,7 @@ export class DomRenderer extends Disposable implements IRenderer { private _refreshRowElements(cols: number, rows: number): void { // Add missing elements for (let i = this._rowElements.length; i <= rows; i++) { - const row = document.createElement('div'); + const row = this._document.createElement('div'); this._rowContainer.appendChild(row); this._rowElements.push(row); } @@ -330,7 +332,7 @@ export class DomRenderer extends Disposable implements IRenderer { } // Create the selections - const documentFragment = document.createDocumentFragment(); + const documentFragment = this._document.createDocumentFragment(); if (columnSelectMode) { const isXFlipped = start[0] > end[0]; @@ -362,7 +364,7 @@ export class DomRenderer extends Disposable implements IRenderer { * @param colEnd The end columns. */ private _createSelectionElement(row: number, colStart: number, colEnd: number, rowCount: number = 1): HTMLElement { - const element = document.createElement('div'); + const element = this._document.createElement('div'); element.style.height = `${rowCount * this.dimensions.css.cell.height}px`; element.style.top = `${row * this.dimensions.css.cell.height}px`; element.style.left = `${colStart * this.dimensions.css.cell.width}px`; diff --git a/src/browser/renderer/dom/DomRendererRowFactory.test.ts b/src/browser/renderer/dom/DomRendererRowFactory.test.ts index dfea8b01..14c4ade1 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.test.ts @@ -14,8 +14,8 @@ import { MockCoreService, MockDecorationService, MockOptionsService } from 'comm import { MockCharacterJoinerService, MockCoreBrowserService, MockThemeService } from 'browser/TestUtils.test'; import { TestWidthCache } from 'browser/renderer/dom/WidthCache.test'; - -const EMPTY_WIDTH = new TestWidthCache(new jsdom.JSDOM('').window.document); +const dom = new jsdom.JSDOM(''); +const EMPTY_WIDTH = new TestWidthCache(dom.window.document, dom.window.document.createElement('div')); describe('DomRendererRowFactory', () => { diff --git a/src/browser/renderer/dom/WidthCache.test.ts b/src/browser/renderer/dom/WidthCache.test.ts index 8efc6ff4..fc524437 100644 --- a/src/browser/renderer/dom/WidthCache.test.ts +++ b/src/browser/renderer/dom/WidthCache.test.ts @@ -36,7 +36,8 @@ function castf32(v: number): number { describe('WidthCache', () => { let wc: TestWidthCache; beforeEach(() => { - wc = new TestWidthCache(new jsdom.JSDOM('').window.document); + const dom = new jsdom.JSDOM(''); + wc = new TestWidthCache(dom.window.document, dom.window.document.createElement('div')); wc.setFont('monospace', 15, 'normal', 'bold'); }); describe('cache invalidation', () => { diff --git a/src/browser/renderer/dom/WidthCache.ts b/src/browser/renderer/dom/WidthCache.ts index 01b3f658..1527bad0 100644 --- a/src/browser/renderer/dom/WidthCache.ts +++ b/src/browser/renderer/dom/WidthCache.ts @@ -45,25 +45,28 @@ export class WidthCache implements IDisposable { private _container: HTMLDivElement; private _measureElements: HTMLSpanElement[] = []; - constructor(_document: Document) { + constructor(_document: Document, _helperContainer: HTMLElement) { this._container = _document.createElement('div'); - this._container.style.position = 'absolute'; - this._container.style.top = '-50000px'; - this._container.style.width = '50000px'; + this._container.classList.add('xterm-width-cache-measure-container'); + this._container.setAttribute('aria-hidden', 'true'); // SP should stack in spans this._container.style.whiteSpace = 'pre'; // avoid undercuts in non-monospace fonts from kerning this._container.style.fontKerning = 'none'; const regular = _document.createElement('span'); + regular.classList.add('xterm-char-measure-element'); const bold = _document.createElement('span'); + bold.classList.add('xterm-char-measure-element'); bold.style.fontWeight = 'bold'; const italic = _document.createElement('span'); + italic.classList.add('xterm-char-measure-element'); italic.style.fontStyle = 'italic'; const boldItalic = _document.createElement('span'); + boldItalic.classList.add('xterm-char-measure-element'); boldItalic.style.fontWeight = 'bold'; boldItalic.style.fontStyle = 'italic'; @@ -74,7 +77,7 @@ export class WidthCache implements IDisposable { this._container.appendChild(italic); this._container.appendChild(boldItalic); - _document.body.appendChild(this._container); + _helperContainer.appendChild(this._container); this.clear(); }