@@ -5,7 +5,7 @@ Subject: [PATCH] feat: support shadow dom
55
66---
77 src/vs/base/browser/dom.ts | 61 ++++++++++++++++---
8- src/vs/base/browser/domStylesheets.ts | 21 +++++ --
8+ src/vs/base/browser/domStylesheets.ts | 22 ++++- --
99 src/vs/base/browser/keyboardEvent.ts | 12 +++-
1010 .../browser/ui/contextview/contextview.ts | 2 +-
1111 src/vs/base/browser/ui/dialog/dialog.ts | 6 +-
@@ -42,14 +42,16 @@ Subject: [PATCH] feat: support shadow dom
4242 .../view/renderers/backLayerWebView.ts | 3 +-
4343 .../browser/preferences.contribution.ts | 3 +-
4444 .../preferences/browser/settingsEditor2.ts | 6 +-
45+ .../terminal/browser/terminalService.ts | 7 +--
4546 .../terminal/browser/terminalTabbedView.ts | 2 +-
4647 .../contrib/terminal/browser/terminalView.ts | 2 +-
4748 .../contrib/webview/browser/webviewElement.ts | 12 ++--
4849 .../browser/gettingStarted.ts | 4 +-
4950 .../browser/walkThroughPart.ts | 6 +-
51+ .../browser/auxiliaryWindowService.ts | 6 +-
5052 .../suggest/browser/simpleSuggestWidget.ts | 4 +-
51- .../themes/browser/workbenchThemeService.ts | 2 +-
52- 45 files changed, 192 insertions(+), 99 deletions(-)
53+ .../themes/browser/workbenchThemeService.ts | 33 ++++++++ +-
54+ 47 files changed, 227 insertions(+), 109 deletions(-)
5355
5456diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts
5557index e943bf0a557..6ebc4568a39 100644
@@ -180,13 +182,23 @@ index e943bf0a557..6ebc4568a39 100644
180182 parentFocusable?.focus();
181183 }
182184diff --git a/src/vs/base/browser/domStylesheets.ts b/src/vs/base/browser/domStylesheets.ts
183- index 72fa42df1a6..369b16a0e5a 100644
185+ index 72fa42df1a6..f82797cdd26 100644
184186--- a/src/vs/base/browser/domStylesheets.ts
185187+++ b/src/vs/base/browser/domStylesheets.ts
186- @@ -39,6 +39,14 @@ class WrappedStyleElement extends Disposable {
188+ @@ -26,7 +26,7 @@ class WrappedStyleElement extends Disposable {
189+ this._currentCssStyle = cssStyle;
190+
191+ if (!this._styleSheet) {
192+ - this._styleSheet = createStyleSheet(mainWindow.document.head, s => s.textContent = cssStyle, this._store);
193+ + this._styleSheet = createStyleSheet(undefined, s => s.textContent = cssStyle, this._store);
194+ } else {
195+ this._styleSheet.textContent = cssStyle;
196+ }
197+ @@ -39,7 +39,15 @@ class WrappedStyleElement extends Disposable {
187198 }
188199 }
189200
201+ - export function createStyleSheet(container: HTMLElement = mainWindow.document.head, beforeAppend?: (style: HTMLStyleElement) => void, disposableStore?: DisposableStore): HTMLStyleElement {
190202+ export let shadowRootContainer: ShadowRoot | undefined;
191203+ export function setContainerElement(container: HTMLElement) {
192204+ const root = container.getRootNode();
@@ -195,24 +207,29 @@ index 72fa42df1a6..369b16a0e5a 100644
195207+ }
196208+ }
197209+
198- export function createStyleSheet(container: HTMLElement = mainWindow.document.head, beforeAppend?: (style: HTMLStyleElement) => void, disposableStore?: DisposableStore): HTMLStyleElement {
210+ + export function createStyleSheet(container: HTMLElement | ShadowRoot = shadowRootContainer ?? mainWindow.document.head, beforeAppend?: (style: HTMLStyleElement) => void, disposableStore?: DisposableStore): HTMLStyleElement {
199211 const style = document.createElement('style');
200212 style.type = 'text/css';
201- @@ -64,7 +72,12 @@ export function createStyleSheet(container: HTMLElement = mainWindow.document.he
213+ style.media = 'screen';
214+ @@ -52,7 +60,7 @@ export function createStyleSheet(container: HTMLElement = mainWindow.document.he
215+
216+ // With <head> as container, the stylesheet becomes global and is tracked
217+ // to support auxiliary windows to clone the stylesheet.
218+ - if (container === mainWindow.document.head) {
219+ + if (container === (shadowRootContainer ?? mainWindow.document.head)) {
220+ const globalStylesheetClones = new Set<HTMLStyleElement>();
221+ globalStylesheets.set(style, globalStylesheetClones);
222+ if (disposableStore) {
223+ @@ -64,7 +72,7 @@ export function createStyleSheet(container: HTMLElement = mainWindow.document.he
202224 continue; // main window is already tracked
203225 }
204226
205227- const cloneDisposable = disposables.add(cloneGlobalStyleSheet(style, globalStylesheetClones, targetWindow));
206228+ const cloneDisposable = disposables.add(cloneGlobalStyleSheet(style, globalStylesheetClones, targetWindow.document.head));
207- + disposableStore?.add(cloneDisposable);
208- + }
209- +
210- + if (shadowRootContainer !== undefined) {
211- + const cloneDisposable = cloneGlobalStyleSheet(style, globalStylesheetClones, shadowRootContainer);
212229 disposableStore?.add(cloneDisposable);
213230 }
214231 }
215- @@ -76,17 +89 ,17 @@ export function cloneGlobalStylesheets(targetWindow: Window): IDisposable {
232+ @@ -76,17 +84 ,17 @@ export function cloneGlobalStylesheets(targetWindow: Window): IDisposable {
216233 const disposables = new DisposableStore();
217234
218235 for (const [globalStylesheet, clonedGlobalStylesheets] of globalStylesheets) {
@@ -1074,6 +1091,45 @@ index c754843a640..ab5ca9b057b 100644
10741091 }
10751092
10761093 private refreshSingleElement(element: SettingsTreeSettingElement): void {
1094+ diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts
1095+ index effcc11e410..c3e617145a8 100644
1096+ --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts
1097+ +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts
1098+ @@ -53,7 +53,7 @@ import { mark } from '../../../../base/common/performance.js';
1099+ import { DetachedTerminal } from './detachedTerminal.js';
1100+ import { ITerminalCapabilityImplMap, TerminalCapability } from '../../../../platform/terminal/common/capabilities/capabilities.js';
1101+ import { createInstanceCapabilityEventMultiplexer } from './terminalEvents.js';
1102+ - import { isAuxiliaryWindow, mainWindow } from '../../../../base/browser/window.js';
1103+ + import { isAuxiliaryWindow } from '../../../../base/browser/window.js';
1104+ import { GroupIdentifier } from '../../../common/editor.js';
1105+ import { getActiveWindow } from '../../../../base/browser/dom.js';
1106+ import { hasKey, isString } from '../../../../base/common/types.js';
1107+ @@ -238,7 +238,7 @@ export class TerminalService extends Disposable implements ITerminalService {
1108+ this._initializePrimaryBackend();
1109+
1110+ // Create async as the class depends on `this`
1111+ - timeout(0).then(() => this._register(this._instantiationService.createInstance(TerminalEditorStyle, mainWindow.document.head)));
1112+ + timeout(0).then(() => this._register(this._instantiationService.createInstance(TerminalEditorStyle)));
1113+ }
1114+
1115+ async showProfileQuickPick(type: 'setDefault' | 'createInstance', cwd?: string | URI): Promise<ITerminalInstance | undefined> {
1116+ @@ -1395,7 +1395,6 @@ class TerminalEditorStyle extends Themable {
1117+ private _styleElement: HTMLElement;
1118+
1119+ constructor(
1120+ - container: HTMLElement,
1121+ @ITerminalService private readonly _terminalService: ITerminalService,
1122+ @IThemeService private readonly _themeService: IThemeService,
1123+ @ITerminalProfileService private readonly _terminalProfileService: ITerminalProfileService,
1124+ @@ -1403,7 +1402,7 @@ class TerminalEditorStyle extends Themable {
1125+ ) {
1126+ super(_themeService);
1127+ this._registerListeners();
1128+ - this._styleElement = domStylesheets.createStyleSheet(container);
1129+ + this._styleElement = domStylesheets.createStyleSheet();
1130+ this._register(toDisposable(() => this._styleElement.remove()));
1131+ this.updateStyles();
1132+ }
10771133diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts
10781134index 7a3dd584458..b58e3170ff1 100644
10791135--- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts
@@ -1191,6 +1247,37 @@ index c6fb17f8525..8691b2f7a37 100644
11911247 while (active && active !== this.content) {
11921248 active = active.parentElement;
11931249 }
1250+ diff --git a/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts b/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts
1251+ index 15b221bafb8..0dc8b7ce37a 100644
1252+ --- a/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts
1253+ +++ b/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts
1254+ @@ -469,6 +469,8 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
1255+ mapOriginalToClone.set(originalNode, clonedNode);
1256+ }
1257+
1258+ + const container = shadowRootContainer ?? mainWindow.document.head;
1259+ +
1260+ // Clone all style elements and stylesheet links from the window to the child window
1261+ // and keep track of <link> elements to settle to signal that styles have loaded
1262+ // Increment pending links right from the beginning to ensure we only settle when
1263+ @@ -476,7 +478,7 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
1264+ pendingLinksToSettle++;
1265+ try {
1266+ // eslint-disable-next-line no-restricted-syntax
1267+ - for (const originalNode of mainWindow.document.head.querySelectorAll('link[rel="stylesheet"], style')) {
1268+ + for (const originalNode of container.querySelectorAll('link[rel="stylesheet"], style')) {
1269+ cloneNode(originalNode);
1270+ }
1271+ } finally {
1272+ @@ -490,7 +492,7 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
1273+
1274+ // Listen to new stylesheets as they are being added or removed in the main window
1275+ // and apply to child window (including changes to existing stylesheets elements)
1276+ - disposables.add(sharedMutationObserver.observe(mainWindow.document.head, disposables, { childList: true, subtree: true })(mutations => {
1277+ + disposables.add(sharedMutationObserver.observe(container, disposables, { childList: true, subtree: true })(mutations => {
1278+ for (const mutation of mutations) {
1279+ if (
1280+ mutation.type !== 'childList' || // only interested in added/removed nodes
11941281diff --git a/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts b/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts
11951282index 1245acce54c..a2312b0d332 100644
11961283--- a/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts
@@ -1214,15 +1301,55 @@ index 1245acce54c..a2312b0d332 100644
12141301 return;
12151302 }
12161303diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts
1217- index 99405e3d855..07b705e99e7 100644
1304+ index 99405e3d855..2f96d707b62 100644
12181305--- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts
12191306+++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts
1220- @@ -874,7 +874,7 @@ class ThemeFileWatcher {
1307+ @@ -18,7 +18,7 @@ import { Event, Emitter } from '../../../../base/common/event.js';
1308+ import { registerFileIconThemeSchemas } from '../common/fileIconThemeSchema.js';
1309+ import { IDisposable, Disposable, DisposableStore } from '../../../../base/common/lifecycle.js';
1310+ import { FileIconThemeData, FileIconThemeLoader } from './fileIconThemeData.js';
1311+ - import { createStyleSheet } from '../../../../base/browser/domStylesheets.js';
1312+ + import { createStyleSheet, shadowRootContainer } from '../../../../base/browser/domStylesheets.js';
1313+ import { IBrowserWorkbenchEnvironmentService } from '../../environment/browser/environmentService.js';
1314+ import { IFileService, FileChangeType } from '../../../../platform/files/common/files.js';
1315+ import { URI } from '../../../../base/common/uri.js';
1316+ @@ -872,9 +872,38 @@ class ThemeFileWatcher {
1317+ }
1318+ }
12211319
1320+ + const injectedFontFaces = new Set();
1321+ + function injectFontFacesInHead(styleSheetContent: string) {
1322+ + const sheet = new mainWindow.CSSStyleSheet();
1323+ + sheet.replaceSync(styleSheetContent);
1324+ + const fontFaces = Array.from(sheet.cssRules)
1325+ + .filter((rule) => rule instanceof mainWindow.CSSFontFaceRule)
1326+ + .map((r) => r.cssText);
1327+ + const missingFontFaces = fontFaces.filter((fontFace) => !injectedFontFaces.has(fontFace));
1328+ +
1329+ + if (missingFontFaces.length > 0) {
1330+ + const fontFaceStyleSheet = new mainWindow.CSSStyleSheet();
1331+ + for (const fontFace of missingFontFaces) {
1332+ + fontFaceStyleSheet.insertRule(fontFace);
1333+ + }
1334+ + mainWindow.document.adoptedStyleSheets = [
1335+ + ...mainWindow.document.adoptedStyleSheets,
1336+ + fontFaceStyleSheet
1337+ + ];
1338+ + }
1339+ + }
1340+ +
12221341 function _applyRules(styleSheetContent: string, rulesClassName: string) {
1342+ + if (shadowRootContainer) {
1343+ + // Font-faces injected in shadow doms are ignored
1344+ + // They need to be injected in the page head
1345+ + // So we need to extract them and inject it in the head by hands if the container is a shadow dom
1346+ + injectFontFacesInHead(styleSheetContent);
1347+ + }
1348+ +
1349+ + const container: HTMLElement | ShadowRoot = shadowRootContainer ?? mainWindow.document.head;
12231350 // eslint-disable-next-line no-restricted-syntax
12241351- const themeStyles = mainWindow.document.head.getElementsByClassName(rulesClassName);
1225- + const themeStyles = mainWindow.document.head .querySelectorAll(`.${rulesClassName}`);
1352+ + const themeStyles = container .querySelectorAll(`.${rulesClassName}`);
12261353 if (themeStyles.length === 0) {
12271354 const elStyle = createStyleSheet();
12281355 elStyle.className = rulesClassName;
0 commit comments