Skip to content

Commit ae369f9

Browse files
author
Loïc Mangeonjean
committed
feat: fix obvious VSCode issues with shadow dom
1 parent 8161745 commit ae369f9

File tree

2 files changed

+196
-2
lines changed

2 files changed

+196
-2
lines changed

src/service-override/keybindings.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import 'vs/workbench/contrib/keybindings/browser/keybindings.contribution'
3737
import 'vs/workbench/contrib/preferences/browser/keybindingsEditorContribution'
3838
import 'vs/workbench/contrib/commands/common/commands.contribution'
3939
import { getService } from '../services'
40+
import { ILayoutService } from 'vs/platform/layout/browser/layoutService.service'
4041

4142
// This is the default value, but can be overriden by overriding the Environment or UserDataProfileService service
4243
const defaultUserKeybindindsFile = URI.from({
@@ -87,7 +88,8 @@ class DynamicWorkbenchKeybindingService
8788
@IFileService fileService: IFileService,
8889
@IUriIdentityService uriIdentityService: IUriIdentityService,
8990
@ILogService logService: ILogService,
90-
@IKeyboardLayoutService keyboardLayoutService: IKeyboardLayoutService
91+
@IKeyboardLayoutService keyboardLayoutService: IKeyboardLayoutService,
92+
@ILayoutService layoutService: ILayoutService
9193
) {
9294
super(
9395
contextKeyService,
@@ -100,7 +102,8 @@ class DynamicWorkbenchKeybindingService
100102
fileService,
101103
uriIdentityService,
102104
logService,
103-
keyboardLayoutService
105+
keyboardLayoutService,
106+
layoutService
104107
)
105108
}
106109

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2+
From: =?UTF-8?q?Lo=C3=AFc=20Mangeonjean?= <[email protected]>
3+
Date: Fri, 2 May 2025 10:13:06 +0200
4+
Subject: [PATCH] feat: support shadow dom
5+
6+
---
7+
src/vs/base/browser/domStylesheets.ts | 21 +++++++++++++++----
8+
.../editor/browser/controller/mouseHandler.ts | 2 +-
9+
src/vs/workbench/browser/layout.ts | 3 +++
10+
src/vs/workbench/browser/workbench.ts | 6 +++---
11+
.../keybinding/browser/keybindingService.ts | 10 ++++++---
12+
.../themes/browser/workbenchThemeService.ts | 4 ++--
13+
6 files changed, 33 insertions(+), 13 deletions(-)
14+
15+
diff --git a/src/vs/base/browser/domStylesheets.ts b/src/vs/base/browser/domStylesheets.ts
16+
index ebc249c6608..5f5da69f73b 100644
17+
--- a/src/vs/base/browser/domStylesheets.ts
18+
+++ b/src/vs/base/browser/domStylesheets.ts
19+
@@ -45,6 +45,14 @@ class WrappedStyleElement {
20+
}
21+
}
22+
23+
+export let shadowRootContainer: ShadowRoot | undefined;
24+
+export function setContainerElement(container: HTMLElement) {
25+
+ const root = container.getRootNode();
26+
+ if (root instanceof ShadowRoot) {
27+
+ shadowRootContainer = root;
28+
+ }
29+
+}
30+
+
31+
export function createStyleSheet(container: HTMLElement = mainWindow.document.head, beforeAppend?: (style: HTMLStyleElement) => void, disposableStore?: DisposableStore): HTMLStyleElement {
32+
const style = document.createElement('style');
33+
style.type = 'text/css';
34+
@@ -67,7 +75,12 @@ export function createStyleSheet(container: HTMLElement = mainWindow.document.he
35+
continue; // main window is already tracked
36+
}
37+
38+
- const cloneDisposable = disposables.add(cloneGlobalStyleSheet(style, globalStylesheetClones, targetWindow));
39+
+ const cloneDisposable = disposables.add(cloneGlobalStyleSheet(style, globalStylesheetClones, targetWindow.document.head));
40+
+ disposableStore?.add(cloneDisposable);
41+
+ }
42+
+
43+
+ if (shadowRootContainer !== undefined) {
44+
+ const cloneDisposable = cloneGlobalStyleSheet(style, globalStylesheetClones, shadowRootContainer);
45+
disposableStore?.add(cloneDisposable);
46+
}
47+
}
48+
@@ -79,17 +92,17 @@ export function cloneGlobalStylesheets(targetWindow: Window): IDisposable {
49+
const disposables = new DisposableStore();
50+
51+
for (const [globalStylesheet, clonedGlobalStylesheets] of globalStylesheets) {
52+
- disposables.add(cloneGlobalStyleSheet(globalStylesheet, clonedGlobalStylesheets, targetWindow));
53+
+ disposables.add(cloneGlobalStyleSheet(globalStylesheet, clonedGlobalStylesheets, targetWindow.document.head));
54+
}
55+
56+
return disposables;
57+
}
58+
59+
-function cloneGlobalStyleSheet(globalStylesheet: HTMLStyleElement, globalStylesheetClones: Set<HTMLStyleElement>, targetWindow: Window): IDisposable {
60+
+function cloneGlobalStyleSheet(globalStylesheet: HTMLStyleElement, globalStylesheetClones: Set<HTMLStyleElement>, windowElement: HTMLElement | ShadowRoot): IDisposable {
61+
const disposables = new DisposableStore();
62+
63+
const clone = globalStylesheet.cloneNode(true) as HTMLStyleElement;
64+
- targetWindow.document.head.appendChild(clone);
65+
+ windowElement.appendChild(clone);
66+
disposables.add(toDisposable(() => clone.remove()));
67+
68+
for (const rule of getDynamicStyleSheetRules(globalStylesheet)) {
69+
diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts
70+
index b1e326d835a..3e4f84e209d 100644
71+
--- a/src/vs/editor/browser/controller/mouseHandler.ts
72+
+++ b/src/vs/editor/browser/controller/mouseHandler.ts
73+
@@ -100,7 +100,7 @@ export class MouseHandler extends ViewEventHandler {
74+
// remove this listener
75+
76+
if (!this._mouseLeaveMonitor) {
77+
- this._mouseLeaveMonitor = dom.addDisposableListener(this.viewHelper.viewDomNode.ownerDocument, 'mousemove', (e) => {
78+
+ this._mouseLeaveMonitor = dom.addDisposableListener(this.viewHelper.viewDomNode.getRootNode(), 'mousemove', (e) => {
79+
if (!this.viewHelper.viewDomNode.contains(e.target as Node | null)) {
80+
// went outside the editor!
81+
this._onMouseLeave(new EditorMouseEvent(e, false, this.viewHelper.viewDomNode));
82+
diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts
83+
index b3464af2ed7..08dd03921ae 100644
84+
--- a/src/vs/workbench/browser/layout.ts
85+
+++ b/src/vs/workbench/browser/layout.ts
86+
@@ -48,6 +48,7 @@ import { AuxiliaryBarPart } from './parts/auxiliarybar/auxiliaryBarPart.js';
87+
import { ITelemetryService } from '../../platform/telemetry/common/telemetry.js';
88+
import { IAuxiliaryWindowService } from '../services/auxiliaryWindow/browser/auxiliaryWindowService.js';
89+
import { CodeWindow, mainWindow } from '../../base/browser/window.js';
90+
+import { setContainerElement } from '../../base/browser/domStylesheets.js';
91+
92+
//#region Layout Implementation
93+
94+
@@ -300,6 +301,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
95+
protected readonly parent: HTMLElement
96+
) {
97+
super();
98+
+
99+
+ setContainerElement(parent);
100+
}
101+
102+
protected initLayout(accessor: ServicesAccessor): void {
103+
diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts
104+
index e112acb5e7e..6b332f5fa11 100644
105+
--- a/src/vs/workbench/browser/workbench.ts
106+
+++ b/src/vs/workbench/browser/workbench.ts
107+
@@ -70,6 +70,9 @@ export class Workbench extends Layout {
108+
mark('code/willStartWorkbench');
109+
110+
this.registerErrorHandler(logService);
111+
+
112+
+ // Add Workbench to DOM
113+
+ this.parent.appendChild(this.mainContainer);
114+
}
115+
116+
protected registerErrorHandler(logService: ILogService): void {
117+
@@ -326,9 +329,6 @@ export class Workbench extends Layout {
118+
119+
// Notification Handlers
120+
this.createNotificationsHandlers(instantiationService, notificationService);
121+
-
122+
- // Add Workbench to DOM
123+
- this.parent.appendChild(this.mainContainer);
124+
}
125+
126+
private createPart(id: string, role: string, classes: string[]): HTMLElement {
127+
diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts
128+
index f10b332d8e2..96b173622ca 100644
129+
--- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts
130+
+++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts
131+
@@ -55,6 +55,7 @@ import { IKeyboard, INavigatorWithKeyboard } from './navigatorKeyboard.js';
132+
import { getAllUnboundCommands } from './unboundCommands.js';
133+
import { IUserKeybindingItem, KeybindingIO, OutputBuilder } from '../common/keybindingIO.js';
134+
import { IUserDataProfileService } from '../../userDataProfile/common/userDataProfile.js';
135+
+import { ILayoutService } from '../../../../platform/layout/browser/layoutService.js';
136+
137+
interface ContributedKeyBinding {
138+
command: string;
139+
@@ -198,7 +199,8 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService {
140+
@IFileService fileService: IFileService,
141+
@IUriIdentityService uriIdentityService: IUriIdentityService,
142+
@ILogService logService: ILogService,
143+
- @IKeyboardLayoutService private readonly keyboardLayoutService: IKeyboardLayoutService
144+
+ @IKeyboardLayoutService private readonly keyboardLayoutService: IKeyboardLayoutService,
145+
+ @ILayoutService private readonly layoutService: ILayoutService
146+
) {
147+
super(contextKeyService, commandService, telemetryService, notificationService, logService);
148+
149+
@@ -269,8 +271,10 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService {
150+
private _registerKeyListeners(window: Window): IDisposable {
151+
const disposables = new DisposableStore();
152+
153+
+ const container = this.layoutService.getContainer(window);
154+
+
155+
// for standard keybindings
156+
- disposables.add(dom.addDisposableListener(window, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
157+
+ disposables.add(dom.addDisposableListener(container, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
158+
if (this._keybindingHoldMode) {
159+
return;
160+
}
161+
@@ -286,7 +290,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService {
162+
}));
163+
164+
// for single modifier chord keybindings (e.g. shift shift)
165+
- disposables.add(dom.addDisposableListener(window, dom.EventType.KEY_UP, (e: KeyboardEvent) => {
166+
+ disposables.add(dom.addDisposableListener(container, dom.EventType.KEY_UP, (e: KeyboardEvent) => {
167+
this._resetKeybindingHoldMode();
168+
this.isComposingGlobalContextKey.set(e.isComposing);
169+
const keyEvent = new StandardKeyboardEvent(e);
170+
diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts
171+
index 56a3b2d8f18..99d592b37cc 100644
172+
--- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts
173+
+++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts
174+
@@ -18,7 +18,7 @@ import { Event, Emitter } from '../../../../base/common/event.js';
175+
import { registerFileIconThemeSchemas } from '../common/fileIconThemeSchema.js';
176+
import { IDisposable, dispose, Disposable } from '../../../../base/common/lifecycle.js';
177+
import { FileIconThemeData, FileIconThemeLoader } from './fileIconThemeData.js';
178+
-import { createStyleSheet } from '../../../../base/browser/domStylesheets.js';
179+
+import { createStyleSheet, shadowRootContainer } from '../../../../base/browser/domStylesheets.js';
180+
import { IBrowserWorkbenchEnvironmentService } from '../../environment/browser/environmentService.js';
181+
import { IFileService, FileChangeType } from '../../../../platform/files/common/files.js';
182+
import { URI } from '../../../../base/common/uri.js';
183+
@@ -790,7 +790,7 @@ class ThemeFileWatcher {
184+
}
185+
186+
function _applyRules(styleSheetContent: string, rulesClassName: string) {
187+
- const themeStyles = mainWindow.document.head.getElementsByClassName(rulesClassName);
188+
+ const themeStyles = (shadowRootContainer ?? mainWindow.document.head).querySelectorAll(`.${rulesClassName}`);
189+
if (themeStyles.length === 0) {
190+
const elStyle = createStyleSheet();
191+
elStyle.className = rulesClassName;

0 commit comments

Comments
 (0)