Merge pull request #5334 from CodinGame/webgl-renderer-shadow-dom

Support shadow dom in webgl renderer
This commit is contained in:
Daniel Imms
2025-07-31 08:32:44 -07:00
committed by GitHub
3 changed files with 57 additions and 8 deletions
+2 -2
View File
@@ -139,7 +139,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
[this._rectangleRenderer.value, this._glyphRenderer.value] = this._initializeWebGLState();
this._isAttached = this._coreBrowserService.window.document.body.contains(this._core.screenElement!);
this._isAttached = this._core.screenElement!.isConnected;
this._register(toDisposable(() => {
for (const l of this._renderLayers) {
@@ -322,7 +322,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
public renderRows(start: number, end: number): void {
if (!this._isAttached) {
if (this._coreBrowserService.window.document.body.contains(this._core.screenElement!) && this._charSizeService.width && this._charSizeService.height) {
if (this._core.screenElement?.isConnected && this._charSizeService.width && this._charSizeService.height) {
this._updateDimensions();
this._refreshCharAtlas();
this._isAttached = true;
+9 -2
View File
@@ -1280,10 +1280,10 @@ enum CellColorPosition {
* treatment.
*/
export function injectSharedRendererTestsStandalone(ctx: ISharedRendererTestContext, setupCb: () => Promise<void> | void): void {
test.describe('standalone tests', () => {
const setupTests = ({ shadowDom }: { shadowDom: boolean }): void => {
test.beforeEach(async () => {
// Recreate terminal
await openTerminal(ctx.value);
await openTerminal(ctx.value, {}, { useShadowDom: shadowDom });
await ctx.value.page.evaluate(`
window.term.options.minimumContrastRatio = 1;
window.term.options.allowTransparency = false;
@@ -1308,6 +1308,13 @@ export function injectSharedRendererTestsStandalone(ctx: ISharedRendererTestCont
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 0, 255]);
});
});
};
test.describe('standalone tests', () => {
setupTests({ shadowDom: false });
});
test.describe('standalone tests (Shadow dom)', () => {
setupTests({ shadowDom: true });
});
}
+46 -4
View File
@@ -412,7 +412,14 @@ class TerminalCoreProxy {
}
}
export async function openTerminal(ctx: ITestContext, options: ITerminalOptions | ITerminalInitOnlyOptions = {}, testOptions: { loadUnicodeGraphemesAddon: boolean } = { loadUnicodeGraphemesAddon: true }): Promise<void> {
export async function openTerminal(
ctx: ITestContext,
options: ITerminalOptions | ITerminalInitOnlyOptions = {},
testOptions: { useShadowDom?: boolean, loadUnicodeGraphemesAddon?: boolean } = {}
): Promise<void> {
testOptions.useShadowDom ??= false;
testOptions.loadUnicodeGraphemesAddon ??= true;
await ctx.page.evaluate(`
if ('term' in window) {
try {
@@ -423,10 +430,45 @@ export async function openTerminal(ctx: ITestContext, options: ITerminalOptions
// HACK: Tests may have side effects that could cause the terminal not to be removed. This
// assertion catches this case early.
strictEqual(await ctx.page.evaluate(`document.querySelector('#terminal-container').children.length`), 0, 'there must be no terminals on the page');
await ctx.page.evaluate(`
let script = `
window.term = new window.Terminal(${JSON.stringify({ allowProposedApi: true, ...options })});
window.term.open(document.querySelector('#terminal-container'));
`);
let element = document.querySelector('#terminal-container');
// Remove shadow root if it exists
const newElement = element.cloneNode(false);
element.replaceWith(newElement);
element = newElement
`;
if (testOptions.useShadowDom) {
script += `
const shadowRoot = element.attachShadow({ mode: "open" });
// Copy parent styles to shadow DOM
const styles = Array.from(document.querySelectorAll('link[rel="stylesheet"]'));
styles.forEach((styleEl) => {
const clone = document.createElement('link');
clone.rel = 'stylesheet';
clone.href = styleEl.href;
shadowRoot.appendChild(clone);
});
// Create new element inside the shadow DOM
element = document.createElement('div');
element.style.width = '100%';
element.style.height = '100%';
shadowRoot.appendChild(element);
`;
}
script += `
window.term.open(element);
`;
await ctx.page.evaluate(script);
// HACK: This is a soft layer breaker that's temporarily included until unicode graphemes have
// more complete integration tests. See https://github.com/xtermjs/xterm.js/pull/4519#discussion_r1285234453
if (testOptions.loadUnicodeGraphemesAddon) {