@@ -4,59 +4,99 @@ Date: Fri, 2 May 2025 10:13:06 +0200
44Subject: [PATCH] feat: support shadow dom
55
66---
7- src/vs/base/browser/dom.ts | 28 +++++++++++++--- ---
8- src/vs/base/browser/domStylesheets.ts | 21 +++++++++++- --
9- src/vs/base/browser/keyboardEvent.ts | 12 +++++++ -
7+ src/vs/base/browser/dom.ts | 61 ++++++++++++++++ ---
8+ src/vs/base/browser/domStylesheets.ts | 21 +++++--
9+ src/vs/base/browser/keyboardEvent.ts | 12 +++-
1010 .../browser/ui/contextview/contextview.ts | 2 +-
11- src/vs/base/browser/ui/dialog/dialog.ts | 6 ++- -
12- src/vs/base/browser/ui/dnd/dnd.ts | 4 +--
11+ src/vs/base/browser/ui/dialog/dialog.ts | 6 +-
12+ src/vs/base/browser/ui/dnd/dnd.ts | 4 +-
1313 src/vs/base/browser/ui/findinput/findInput.ts | 3 +-
1414 .../base/browser/ui/findinput/replaceInput.ts | 3 +-
1515 src/vs/base/browser/ui/inputbox/inputBox.ts | 2 +-
1616 src/vs/base/browser/ui/sash/sash.ts | 2 +-
17- src/vs/base/browser/ui/splitview/paneview.ts | 6 ++- -
18- src/vs/base/browser/ui/splitview/splitview.ts | 6 ++- -
19- .../native/screenReaderContentRich.ts | 6 ++- -
20- .../native/screenReaderContentSimple.ts | 6 ++- -
17+ src/vs/base/browser/ui/splitview/paneview.ts | 6 +-
18+ src/vs/base/browser/ui/splitview/splitview.ts | 6 +-
19+ .../native/screenReaderContentRich.ts | 6 +-
20+ .../native/screenReaderContentSimple.ts | 6 +-
2121 .../editor/browser/controller/mouseHandler.ts | 2 +-
2222 .../editor/browser/controller/mouseTarget.ts | 2 +-
2323 src/vs/editor/browser/view/viewPart.ts | 3 +-
2424 .../contentWidgets/contentWidgets.ts | 2 +-
2525 .../hover/browser/contentHoverController.ts | 3 +-
2626 .../hover/browser/contentHoverWidget.ts | 2 +-
2727 .../actionWidget/browser/actionList.ts | 2 +-
28- .../clipboard/browser/clipboardService.ts | 4 +--
29- .../contextkey/browser/contextKeyService.ts | 15 ++ ++++----
28+ .../clipboard/browser/clipboardService.ts | 4 +-
29+ .../contextkey/browser/contextKeyService.ts | 26 ++++----
3030 src/vs/platform/hover/browser/hoverWidget.ts | 2 +-
31- .../browser/actions/developerActions.ts | 4 +--
32- src/vs/workbench/browser/layout.ts | 3 ++
33- .../browser/parts/editor/editorGroupView.ts | 4 +--
34- src/vs/workbench/browser/workbench.ts | 6 ++- -
31+ .../browser/actions/developerActions.ts | 4 +-
32+ src/vs/workbench/browser/layout.ts | 3 +
33+ .../browser/parts/editor/editorGroupView.ts | 4 +-
34+ src/vs/workbench/browser/workbench.ts | 6 +-
3535 .../contrib/chat/browser/chatInputPart.ts | 2 +-
3636 .../chatSessions/view/sessionsTreeRenderer.ts | 3 +-
37- .../files/browser/views/explorerViewer.ts | 4 ++ -
38- .../notebook/browser/notebookEditorWidget.ts | 6 ++- -
39- .../browser/view/cellParts/cellStatusPart.ts | 5 ++- -
40- .../browser/view/cellParts/codeCell.ts | 6 ++- -
41- .../browser/view/cellParts/markupCell.ts | 6 ++- -
42- .../notebook/browser/view/notebookCellList.ts | 5 ++- -
37+ .../files/browser/views/explorerViewer.ts | 4 +-
38+ .../notebook/browser/notebookEditorWidget.ts | 6 +-
39+ .../browser/view/cellParts/cellStatusPart.ts | 5 +-
40+ .../browser/view/cellParts/codeCell.ts | 6 +-
41+ .../browser/view/cellParts/markupCell.ts | 6 +-
42+ .../notebook/browser/view/notebookCellList.ts | 5 +-
4343 .../view/renderers/backLayerWebView.ts | 3 +-
4444 .../browser/preferences.contribution.ts | 3 +-
45- .../preferences/browser/settingsEditor2.ts | 6 ++- -
45+ .../preferences/browser/settingsEditor2.ts | 6 +-
4646 .../terminal/browser/terminalTabbedView.ts | 2 +-
4747 .../contrib/terminal/browser/terminalView.ts | 2 +-
48- .../contrib/webview/browser/webviewElement.ts | 12 +++++- --
49- .../browser/gettingStarted.ts | 4 +--
50- .../browser/walkThroughPart.ts | 6 ++- -
51- .../suggest/browser/simpleSuggestWidget.ts | 4 +--
48+ .../contrib/webview/browser/webviewElement.ts | 12 ++--
49+ .../browser/gettingStarted.ts | 4 +-
50+ .../browser/walkThroughPart.ts | 6 +-
51+ .../suggest/browser/simpleSuggestWidget.ts | 4 +-
5252 .../themes/browser/workbenchThemeService.ts | 2 +-
53- 46 files changed, 148 insertions(+), 94 deletions(-)
53+ 46 files changed, 186 insertions(+), 100 deletions(-)
5454
5555diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts
56- index be401204e06..f181ffdea9f 100644
56+ index be401204e06..b110cb2b742 100644
5757--- a/src/vs/base/browser/dom.ts
5858+++ b/src/vs/base/browser/dom.ts
59- @@ -619,8 +619,7 @@ export function getTopLeftOffset(element: HTMLElement): IDomPosition {
59+ @@ -165,6 +165,39 @@ export function addDisposableListener(node: EventTarget, type: string, handler:
60+ return new DomListener(node, type, handler, useCaptureOrOptions);
61+ }
62+
63+ + export interface IActiveElementTracker extends Disposable {
64+ + readonly onDidChangeActiveElement: event.Event<Element | null>;
65+ + activeElement: Element | null;
66+ + }
67+ +
68+ + class ActiveElementTracker extends Disposable implements IActiveElementTracker {
69+ + activeElement: Element | null = null;
70+ + private readonly _onDidChangeActiveElement = this._register(new event.Emitter<Element | null>());
71+ + get onDidChangeActiveElement() { return this._onDidChangeActiveElement.event; }
72+ +
73+ + constructor(private window: CodeWindow) {
74+ + super();
75+ + this._register(addDisposableListener(this.window, EventType.FOCUS_IN, this._onFocusIn));
76+ + }
77+ +
78+ + private _onFocusIn = (e: FocusEvent) => {
79+ + this.activeElement = getActiveElement(this.window.document);
80+ + this._onDidChangeActiveElement.fire(this.activeElement);
81+ +
82+ + if (e.target instanceof Element && e.target.shadowRoot) {
83+ + const disposableStore = new DisposableStore();
84+ + disposableStore.add(addDisposableListener(e.target.shadowRoot, EventType.FOCUS_IN, this._onFocusIn));
85+ + disposableStore.add(addDisposableListener(e.target, EventType.FOCUS_OUT, () => {
86+ + disposableStore.dispose();
87+ + }));
88+ + }
89+ + };
90+ + }
91+ +
92+ + export function trackActiveElement(window: CodeWindow): IActiveElementTracker {
93+ + return new ActiveElementTracker(window);
94+ + }
95+ +
96+ export interface IAddStandardDisposableListenerSignature {
97+ (node: HTMLElement | Element | Document, type: 'click', handler: (event: IMouseEvent) => void, useCapture?: boolean): IDisposable;
98+ (node: HTMLElement | Element | Document, type: 'mousedown', handler: (event: IMouseEvent) => void, useCapture?: boolean): IDisposable;
99+ @@ -619,8 +652,7 @@ export function getTopLeftOffset(element: HTMLElement): IDomPosition {
60100
61101 while (
62102 (element = <HTMLElement>element.parentNode) !== null
@@ -66,7 +106,7 @@ index be401204e06..f181ffdea9f 100644
66106 ) {
67107 top -= element.scrollTop;
68108 const c = isShadowRoot(element) ? null : getComputedStyle(element);
69- @@ -722,7 +721 ,7 @@ export function getDomNodeZoomLevel(domNode: HTMLElement): number {
109+ @@ -722,7 +754 ,7 @@ export function getDomNodeZoomLevel(domNode: HTMLElement): number {
70110 }
71111
72112 testElement = testElement.parentElement;
@@ -75,7 +115,7 @@ index be401204e06..f181ffdea9f 100644
75115
76116 return zoom;
77117 }
78- @@ -800,7 +799 ,7 @@ function getParentFlowToElement(node: HTMLElement): HTMLElement | null {
118+ @@ -800,7 +832 ,7 @@ function getParentFlowToElement(node: HTMLElement): HTMLElement | null {
79119 const flowToParentId = node.dataset[parentFlowToDataKey];
80120 if (typeof flowToParentId === 'string') {
81121 // eslint-disable-next-line no-restricted-syntax
@@ -84,7 +124,7 @@ index be401204e06..f181ffdea9f 100644
84124 }
85125 return null;
86126 }
87- @@ -883,8 +882 ,8 @@ export function getShadowRoot(domNode: Node): ShadowRoot | null {
127+ @@ -883,8 +915 ,8 @@ export function getShadowRoot(domNode: Node): ShadowRoot | null {
88128 * based on document focus. Falls back to the main
89129 * window if no window has focus.
90130 */
@@ -95,7 +135,7 @@ index be401204e06..f181ffdea9f 100644
95135
96136 while (result?.shadowRoot) {
97137 result = result.shadowRoot.activeElement;
98- @@ -893,6 +892 ,14 @@ export function getActiveElement(): Element | null {
138+ @@ -893,6 +925 ,14 @@ export function getActiveElement(): Element | null {
99139 return result;
100140 }
101141
@@ -110,7 +150,7 @@ index be401204e06..f181ffdea9f 100644
110150 /**
111151 * Returns true if the focused window active element matches
112152 * the provided element. Falls back to the main window if no
113- @@ -1044,6 +1051 ,11 @@ export function isHTMLDivElement(e: unknown): e is HTMLDivElement {
153+ @@ -1044,6 +1084 ,11 @@ export function isHTMLDivElement(e: unknown): e is HTMLDivElement {
114154 return e instanceof HTMLDivElement || e instanceof getWindow(e as Node).HTMLDivElement;
115155 }
116156
@@ -122,7 +162,7 @@ index be401204e06..f181ffdea9f 100644
122162 export function isSVGElement(e: unknown): e is SVGElement {
123163 // eslint-disable-next-line no-restricted-syntax
124164 return e instanceof SVGElement || e instanceof getWindow(e as Node).SVGElement;
125- @@ -1189,7 +1201 ,7 @@ class FocusTracker extends Disposable implements IFocusTracker {
165+ @@ -1189,7 +1234 ,7 @@ class FocusTracker extends Disposable implements IFocusTracker {
126166 private static hasFocusWithin(element: HTMLElement | Window): boolean {
127167 if (isHTMLElement(element)) {
128168 const shadowRoot = getShadowRoot(element);
@@ -131,7 +171,7 @@ index be401204e06..f181ffdea9f 100644
131171 return isAncestor(activeElement, element);
132172 } else {
133173 const window = element;
134- @@ -1409,7 +1421 ,7 @@ export function removeTabIndexAndUpdateFocus(node: HTMLElement): void {
174+ @@ -1409,7 +1454 ,7 @@ export function removeTabIndexAndUpdateFocus(node: HTMLElement): void {
135175 // standard DOM behavior is to move focus to the <body> element. We
136176 // typically never want that, rather put focus to the closest element
137177 // in the hierarchy of the parent DOM nodes.
@@ -612,57 +652,67 @@ index 6e0a6e018d0..358aa682cda 100644
612652 const textArea: HTMLTextAreaElement = activeDocument.body.appendChild($('textarea', { 'aria-hidden': true }));
613653 textArea.style.height = '1px';
614654diff --git a/src/vs/platform/contextkey/browser/contextKeyService.ts b/src/vs/platform/contextkey/browser/contextKeyService.ts
615- index 7f749b68489..53629be4418 100644
655+ index 7f749b68489..93362cb86a6 100644
616656--- a/src/vs/platform/contextkey/browser/contextKeyService.ts
617657+++ b/src/vs/platform/contextkey/browser/contextKeyService.ts
618- @@ -18,7 +18,8 @@ import { ContextKeyExpression, ContextKeyInfo, ContextKeyValue, IContext, IConte
658+ @@ -3,6 +3,8 @@
659+ * Licensed under the MIT License. See License.txt in the project root for license information.
660+ *--------------------------------------------------------------------------------------------*/
661+
662+ + import { getActiveDocument, getActiveElement, isEditableElement, onDidRegisterWindow, trackActiveElement, trackFocus } from '../../../base/browser/dom.js';
663+ + import { mainWindow } from '../../../base/browser/window.js';
664+ import { Emitter, Event, PauseableEmitter } from '../../../base/common/event.js';
665+ import { Iterable } from '../../../base/common/iterator.js';
666+ import { Disposable, DisposableStore, IDisposable, MutableDisposable } from '../../../base/common/lifecycle.js';
667+ @@ -14,11 +16,9 @@ import { URI } from '../../../base/common/uri.js';
668+ import { localize } from '../../../nls.js';
669+ import { CommandsRegistry } from '../../commands/common/commands.js';
670+ import { ConfigurationTarget, IConfigurationService } from '../../configuration/common/configuration.js';
671+ - import { ContextKeyExpression, ContextKeyInfo, ContextKeyValue, IContext, IContextKey, IContextKeyChangeEvent, IContextKeyService, IContextKeyServiceTarget, IReadableSet, IScopedContextKeyService, RawContextKey } from '../common/contextkey.js';
619672 import { ServicesAccessor } from '../../instantiation/common/instantiation.js';
673+ + import { ContextKeyExpression, ContextKeyInfo, ContextKeyValue, IContext, IContextKey, IContextKeyChangeEvent, IContextKeyService, IContextKeyServiceTarget, IReadableSet, IScopedContextKeyService, RawContextKey } from '../common/contextkey.js';
620674 import { InputFocusedContext } from '../common/contextkeys.js';
621- import { mainWindow } from '../../../base/browser/window.js';
675+ - import { mainWindow } from '../../../base/browser/window.js';
622676- import { addDisposableListener, EventType, getActiveWindow, isEditableElement, onDidRegisterWindow, trackFocus } from '../../../base/browser/dom.js';
623- + import { addDisposableListener, EventType, getActiveDocument, getActiveElement, isEditableElement, onDidRegisterWindow, trackFocus } from '../../../base/browser/dom.js';
624- + import { ILayoutService } from '../../layout/browser/layoutService.js';
625677
626678 const KEYBINDING_CONTEXT_ATTR = 'data-keybinding-context';
627679
628- @@ -383,7 +384,7 @@ export class ContextKeyService extends AbstractContextKeyService implements ICon
629-
630- private inputFocusedContext: IContextKey<boolean>;
631-
632- - constructor(@IConfigurationService configurationService: IConfigurationService) {
633- + constructor(@IConfigurationService configurationService: IConfigurationService, @ILayoutService layoutService: ILayoutService) {
634- super(0);
635- this._lastContextId = 0;
636- this.inputFocusedContext = InputFocusedContext.bindTo(this);
637- @@ -404,7 +405,7 @@ export class ContextKeyService extends AbstractContextKeyService implements ICon
680+ @@ -404,24 +404,26 @@ export class ContextKeyService extends AbstractContextKeyService implements ICon
638681
639682 this._register(Event.runAndSubscribe(onDidRegisterWindow, ({ window, disposables }) => {
640683 const onFocusDisposables = disposables.add(new MutableDisposable<DisposableStore>());
641684- disposables.add(addDisposableListener(window, EventType.FOCUS_IN, () => {
642- + disposables.add(addDisposableListener(layoutService.getContainer(window), EventType.FOCUS_IN, () => {
685+ +
686+ + const activeElementTracker = disposables.add(trackActiveElement(window));
687+ + activeElementTracker.onDidChangeActiveElement((activeElement) => {
643688 onFocusDisposables.value = new DisposableStore();
644- this.updateInputContextKeys(window.document, onFocusDisposables.value);
645- }, true));
646- @@ -414,14 +415,16 @@ export class ContextKeyService extends AbstractContextKeyService implements ICon
647- private updateInputContextKeys(ownerDocument: Document, disposables: DisposableStore): void {
689+ - this.updateInputContextKeys(window.document, onFocusDisposables.value);
690+ - }, true));
691+ + this.updateInputContextKeys(window.document, activeElement, onFocusDisposables.value);
692+ + });
693+ }, { window: mainWindow, disposables: this._store }));
694+ }
695+
696+ - private updateInputContextKeys(ownerDocument: Document, disposables: DisposableStore): void {
697+ + private updateInputContextKeys(ownerDocument: Document, activeElement: Element | null, disposables: DisposableStore): void {
648698
649- function activeElementIsInput(): boolean {
699+ - function activeElementIsInput(): boolean {
650700- return !!ownerDocument.activeElement && isEditableElement(ownerDocument.activeElement);
651- + const activeElement = getActiveElement(ownerDocument);
701+ + function activeElementIsInput( activeElement: Element | null = getActiveElement(ownerDocument)): boolean {
652702+ return !!activeElement && isEditableElement(activeElement);
653703 }
654704
655- const isInputFocused = activeElementIsInput();
705+ - const isInputFocused = activeElementIsInput();
706+ + const isInputFocused = activeElementIsInput(activeElement);
656707 this.inputFocusedContext.set(isInputFocused);
657708
658709 if (isInputFocused) {
659710- const tracker = disposables.add(trackFocus(ownerDocument.activeElement as HTMLElement));
660- + const activeElement = getActiveElement(ownerDocument);
661711+ const tracker = disposables.add(trackFocus(activeElement as HTMLElement));
662712 Event.once(tracker.onDidBlur)(() => {
663713
664714 // Ensure we are only updating the context key if we are
665- @@ -432,7 +435 ,7 @@ export class ContextKeyService extends AbstractContextKeyService implements ICon
715+ @@ -432,7 +434 ,7 @@ export class ContextKeyService extends AbstractContextKeyService implements ICon
666716 // blur events from the focus tracker are emitted with a
667717 // timeout of 0.
668718
0 commit comments