Skip to content

Commit 5889344

Browse files
authored
Merge pull request #4611 from SimonSiefke/constructed-stylesheets
feature: use constructed style sheets for dom render so that it works with csp style-src 'self'
2 parents 679c149 + 5f80fe0 commit 5889344

File tree

2 files changed

+50
-12
lines changed

2 files changed

+50
-12
lines changed

src/browser/renderer/dom/DomRenderer.ts

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { color } from 'common/Color';
1313
import { EventEmitter } from 'common/EventEmitter';
1414
import { Disposable, toDisposable } from 'common/Lifecycle';
1515
import { IBufferService, IInstantiationService, IOptionsService } from 'common/services/Services';
16+
import { createStyle, IStyleSheet } from './StyleSheet';
1617

1718
const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-';
1819
const ROW_CONTAINER_CLASS = 'xterm-rows';
@@ -32,8 +33,8 @@ export class DomRenderer extends Disposable implements IRenderer {
3233
private _rowFactory: DomRendererRowFactory;
3334
private _terminalClass: number = nextTerminalId++;
3435

35-
private _themeStyleElement!: HTMLStyleElement;
36-
private _dimensionsStyleElement!: HTMLStyleElement;
36+
private _themeStyle!: IStyleSheet;
37+
private _dimensionsStyle!: IStyleSheet;
3738
private _rowContainer: HTMLElement;
3839
private _rowElements: HTMLElement[] = [];
3940
private _selectionContainer: HTMLElement;
@@ -88,8 +89,8 @@ export class DomRenderer extends Disposable implements IRenderer {
8889
// https://github.com/xtermjs/xterm.js/issues/2960
8990
this._rowContainer.remove();
9091
this._selectionContainer.remove();
91-
this._themeStyleElement.remove();
92-
this._dimensionsStyleElement.remove();
92+
this._themeStyle.dispose();
93+
this._dimensionsStyle.dispose();
9394
}));
9495
}
9596

@@ -116,9 +117,8 @@ export class DomRenderer extends Disposable implements IRenderer {
116117
element.style.overflow = 'hidden';
117118
}
118119

119-
if (!this._dimensionsStyleElement) {
120-
this._dimensionsStyleElement = document.createElement('style');
121-
this._screenElement.appendChild(this._dimensionsStyleElement);
120+
if (!this._dimensionsStyle) {
121+
this._dimensionsStyle = createStyle(this._screenElement);
122122
}
123123

124124
const styles =
@@ -129,17 +129,16 @@ export class DomRenderer extends Disposable implements IRenderer {
129129
` width: ${this.dimensions.css.cell.width}px` +
130130
`}`;
131131

132-
this._dimensionsStyleElement.textContent = styles;
132+
this._dimensionsStyle.setCss(styles);
133133

134134
this._selectionContainer.style.height = this._viewportElement.style.height;
135135
this._screenElement.style.width = `${this.dimensions.css.canvas.width}px`;
136136
this._screenElement.style.height = `${this.dimensions.css.canvas.height}px`;
137137
}
138138

139139
private _injectCss(colors: ReadonlyColorSet): void {
140-
if (!this._themeStyleElement) {
141-
this._themeStyleElement = document.createElement('style');
142-
this._screenElement.appendChild(this._themeStyleElement);
140+
if (!this._themeStyle) {
141+
this._themeStyle = createStyle(this._screenElement);
143142
}
144143

145144
// Base CSS
@@ -236,7 +235,7 @@ export class DomRenderer extends Disposable implements IRenderer {
236235
`${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR}.${DIM_CLASS} { color: ${color.multiplyOpacity(color.opaque(colors.background), 0.5).css}; }` +
237236
`${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${colors.foreground.css}; }`;
238237

239-
this._themeStyleElement.textContent = styles;
238+
this._themeStyle.setCss(styles);
240239
}
241240

242241
public handleDevicePixelRatioChange(): void {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
export interface IStyleSheet {
2+
dispose: () => void;
3+
setCss: (value: string) => void;
4+
}
5+
6+
const createCssStyleSheet = (): IStyleSheet => {
7+
const sheet = new CSSStyleSheet();
8+
document.adoptedStyleSheets.push(sheet);
9+
return {
10+
dispose() {
11+
const index = document.adoptedStyleSheets.indexOf(sheet);
12+
document.adoptedStyleSheets.splice(index, 1);
13+
},
14+
setCss(css) {
15+
sheet.replaceSync(css);
16+
}
17+
};
18+
};
19+
20+
const createStyleElement = (parent: HTMLElement): IStyleSheet => {
21+
const element = document.createElement('style');
22+
parent.append(element);
23+
return {
24+
dispose() {
25+
element.remove();
26+
},
27+
setCss(css) {
28+
element.textContent = css;
29+
}
30+
};
31+
};
32+
33+
export const createStyle = (parent: HTMLElement): IStyleSheet => {
34+
try {
35+
return createCssStyleSheet();
36+
} catch {
37+
return createStyleElement(parent);
38+
}
39+
};

0 commit comments

Comments
 (0)