Skip to content

Commit c201d89

Browse files
committed
Create local style sheets if the editor is in a shadow DOM
1 parent a3d4675 commit c201d89

13 files changed

Lines changed: 190 additions & 33 deletions

File tree

src/vs/base/browser/dom.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,23 @@ export function hasParentWithClass(node: HTMLElement, clazz: string, stopAtClazz
820820
return !!findParentWithClass(node, clazz, stopAtClazzOrNode);
821821
}
822822

823+
export function isShadowRoot(node: Node): node is ShadowRoot {
824+
return (
825+
node && !!(<ShadowRoot>node).host && !!(<ShadowRoot>node).mode
826+
);
827+
}
828+
829+
export function isInShadowDOM(domNode: Node): boolean {
830+
while (domNode.parentNode) {
831+
if (domNode === document.body) {
832+
// reached the body
833+
return false;
834+
}
835+
domNode = domNode.parentNode;
836+
}
837+
return isShadowRoot(domNode);
838+
}
839+
823840
export function createStyleSheet(container: HTMLElement = document.getElementsByTagName('head')[0]): HTMLStyleElement {
824841
let style = document.createElement('style');
825842
style.type = 'text/css';

src/vs/editor/browser/editorBrowser.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,11 @@ export interface ICodeEditor extends editorCommon.IEditor {
723723
*/
724724
getTelemetryData(): { [key: string]: any } | undefined;
725725

726+
/**
727+
* Returns the editor's container dom node
728+
*/
729+
getContainerDomNode(): HTMLElement;
730+
726731
/**
727732
* Returns the editor's dom node
728733
*/

src/vs/editor/browser/services/abstractCodeEditorService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC
8989
return editorWithWidgetFocus;
9090
}
9191

92-
abstract registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void;
92+
abstract registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void;
9393
abstract removeDecorationType(key: string): void;
9494
abstract resolveDecorationOptions(decorationTypeKey: string | undefined, writable: boolean): IModelDecorationOptions;
9595

src/vs/editor/browser/services/codeEditorService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export interface ICodeEditorService {
3737
*/
3838
getFocusedCodeEditor(): ICodeEditor | null;
3939

40-
registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void;
40+
registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void;
4141
removeDecorationType(key: string): void;
4242
resolveDecorationOptions(typeKey: string, writable: boolean): IModelDecorationOptions;
4343

src/vs/editor/browser/services/codeEditorServiceImpl.ts

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,101 @@ import { IModelDecorationOptions, IModelDecorationOverviewRulerOptions, Overview
1414
import { IResourceInput } from 'vs/platform/editor/common/editor';
1515
import { ITheme, IThemeService, ThemeColor } from 'vs/platform/theme/common/themeService';
1616

17+
class RefCountedStyleSheet {
18+
19+
private readonly _parent: CodeEditorServiceImpl;
20+
private readonly _editorId: string;
21+
public readonly styleSheet: HTMLStyleElement;
22+
private _refCount: number;
23+
24+
constructor(parent: CodeEditorServiceImpl, editorId: string, styleSheet: HTMLStyleElement) {
25+
this._parent = parent;
26+
this._editorId = editorId;
27+
this.styleSheet = styleSheet;
28+
this._refCount = 0;
29+
}
30+
31+
public ref(): void {
32+
this._refCount++;
33+
}
34+
35+
public unref(): void {
36+
this._refCount--;
37+
if (this._refCount === 0) {
38+
this.styleSheet.parentNode?.removeChild(this.styleSheet);
39+
this._parent._removeEditorStyleSheets(this._editorId);
40+
}
41+
}
42+
}
43+
44+
class GlobalStyleSheet {
45+
public readonly styleSheet: HTMLStyleElement;
46+
47+
constructor(styleSheet: HTMLStyleElement) {
48+
this.styleSheet = styleSheet;
49+
}
50+
51+
public ref(): void {
52+
}
53+
54+
public unref(): void {
55+
}
56+
}
57+
1758
export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService {
1859

19-
private readonly _styleSheet: HTMLStyleElement;
60+
private _globalStyleSheet: GlobalStyleSheet | null;
2061
private readonly _decorationOptionProviders = new Map<string, IModelDecorationOptionsProvider>();
62+
private readonly _editorStyleSheets = new Map<string, RefCountedStyleSheet>();
2163
private readonly _themeService: IThemeService;
2264

23-
constructor(@IThemeService themeService: IThemeService, styleSheet = dom.createStyleSheet()) {
65+
constructor(@IThemeService themeService: IThemeService, styleSheet: HTMLStyleElement | null = null) {
2466
super();
25-
this._styleSheet = styleSheet;
67+
this._globalStyleSheet = styleSheet ? new GlobalStyleSheet(styleSheet) : null;
2668
this._themeService = themeService;
2769
}
2870

29-
public registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void {
71+
private _getOrCreateGlobalStyleSheet(): GlobalStyleSheet {
72+
if (!this._globalStyleSheet) {
73+
this._globalStyleSheet = new GlobalStyleSheet(dom.createStyleSheet());
74+
}
75+
return this._globalStyleSheet;
76+
}
77+
78+
private _getOrCreateStyleSheet(editor: ICodeEditor | undefined): GlobalStyleSheet | RefCountedStyleSheet {
79+
if (!editor) {
80+
return this._getOrCreateGlobalStyleSheet();
81+
}
82+
const domNode = editor.getContainerDomNode();
83+
if (!dom.isInShadowDOM(domNode)) {
84+
return this._getOrCreateGlobalStyleSheet();
85+
}
86+
const editorId = editor.getId();
87+
if (!this._editorStyleSheets.has(editorId)) {
88+
const refCountedStyleSheet = new RefCountedStyleSheet(this, editorId, dom.createStyleSheet(domNode));
89+
this._editorStyleSheets.set(editorId, refCountedStyleSheet);
90+
}
91+
return this._editorStyleSheets.get(editorId)!;
92+
}
93+
94+
_removeEditorStyleSheets(editorId: string): void {
95+
this._editorStyleSheets.delete(editorId);
96+
}
97+
98+
public registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void {
3099
let provider = this._decorationOptionProviders.get(key);
31100
if (!provider) {
101+
const styleSheet = this._getOrCreateStyleSheet(editor);
32102
const providerArgs: ProviderArguments = {
33-
styleSheet: this._styleSheet,
103+
styleSheet: styleSheet.styleSheet,
34104
key: key,
35105
parentTypeKey: parentTypeKey,
36106
options: options || Object.create(null)
37107
};
38108
if (!parentTypeKey) {
39-
provider = new DecorationTypeOptionsProvider(this._themeService, providerArgs);
109+
provider = new DecorationTypeOptionsProvider(this._themeService, styleSheet, providerArgs);
40110
} else {
41-
provider = new DecorationSubTypeOptionsProvider(this._themeService, providerArgs);
111+
provider = new DecorationSubTypeOptionsProvider(this._themeService, styleSheet, providerArgs);
42112
}
43113
this._decorationOptionProviders.set(key, provider);
44114
}
@@ -76,13 +146,16 @@ interface IModelDecorationOptionsProvider extends IDisposable {
76146

77147
class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvider {
78148

149+
private readonly _styleSheet: GlobalStyleSheet | RefCountedStyleSheet;
79150
public refCount: number;
80151

81152
private readonly _parentTypeKey: string | undefined;
82153
private _beforeContentRules: DecorationCSSRules | null;
83154
private _afterContentRules: DecorationCSSRules | null;
84155

85-
constructor(themeService: IThemeService, providerArgs: ProviderArguments) {
156+
constructor(themeService: IThemeService, styleSheet: GlobalStyleSheet | RefCountedStyleSheet, providerArgs: ProviderArguments) {
157+
this._styleSheet = styleSheet;
158+
this._styleSheet.ref();
86159
this._parentTypeKey = providerArgs.parentTypeKey;
87160
this.refCount = 0;
88161

@@ -110,6 +183,7 @@ class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvide
110183
this._afterContentRules.dispose();
111184
this._afterContentRules = null;
112185
}
186+
this._styleSheet.unref();
113187
}
114188
}
115189

@@ -124,6 +198,7 @@ interface ProviderArguments {
124198
class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider {
125199

126200
private readonly _disposables = new DisposableStore();
201+
private readonly _styleSheet: GlobalStyleSheet | RefCountedStyleSheet;
127202
public refCount: number;
128203

129204
public className: string | undefined;
@@ -136,7 +211,9 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider {
136211
public overviewRuler: IModelDecorationOverviewRulerOptions | undefined;
137212
public stickiness: TrackedRangeStickiness | undefined;
138213

139-
constructor(themeService: IThemeService, providerArgs: ProviderArguments) {
214+
constructor(themeService: IThemeService, styleSheet: GlobalStyleSheet | RefCountedStyleSheet, providerArgs: ProviderArguments) {
215+
this._styleSheet = styleSheet;
216+
this._styleSheet.ref();
140217
this.refCount = 0;
141218

142219
const createCSSRules = (type: ModelDecorationCSSRuleType) => {
@@ -202,6 +279,7 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider {
202279

203280
public dispose(): void {
204281
this._disposables.dispose();
282+
this._styleSheet.unref();
205283
}
206284
}
207285

src/vs/editor/browser/widget/codeEditorWidget.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1165,6 +1165,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
11651165
return this._modelData.view.createOverviewRuler(cssClassName);
11661166
}
11671167

1168+
public getContainerDomNode(): HTMLElement {
1169+
return this._domElement;
1170+
}
1171+
11681172
public getDomNode(): HTMLElement | null {
11691173
if (!this._modelData || !this._modelData.hasRealView) {
11701174
return null;
@@ -1548,7 +1552,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
15481552
}
15491553

15501554
private _registerDecorationType(key: string, options: editorCommon.IDecorationRenderOptions, parentTypeKey?: string): void {
1551-
this._codeEditorService.registerDecorationType(key, options, parentTypeKey);
1555+
this._codeEditorService.registerDecorationType(key, options, parentTypeKey, this);
15521556
}
15531557

15541558
private _removeDecorationType(key: string): void {

src/vs/editor/contrib/codelens/codelensController.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
1818
import { INotificationService } from 'vs/platform/notification/common/notification';
1919
import { ICodeLensCache } from 'vs/editor/contrib/codelens/codeLensCache';
2020
import { EditorOption } from 'vs/editor/common/config/editorOptions';
21-
import { createStyleSheet } from 'vs/base/browser/dom';
21+
import * as dom from 'vs/base/browser/dom';
2222
import { hash } from 'vs/base/common/hash';
2323

2424
export class CodeLensContribution implements IEditorContribution {
@@ -65,7 +65,11 @@ export class CodeLensContribution implements IEditorContribution {
6565
this._onModelChange();
6666

6767
this._styleClassName = hash(this._editor.getId()).toString(16);
68-
this._styleElement = createStyleSheet();
68+
this._styleElement = dom.createStyleSheet(
69+
dom.isInShadowDOM(this._editor.getContainerDomNode())
70+
? this._editor.getContainerDomNode()
71+
: null
72+
);
6973
this._updateLensStyle();
7074
}
7175

src/vs/editor/contrib/colorPicker/colorDetector.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ export class ColorDetector extends Disposable implements IEditorContribution {
192192
border: 'solid 0.1em #eee'
193193
}
194194
}
195-
});
195+
}, undefined, this._editor);
196196
}
197197

198198
newDecorationsTypes[key] = true;

src/vs/editor/standalone/browser/standaloneCodeEditor.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon
351351
@IAccessibilityService accessibilityService: IAccessibilityService
352352
) {
353353
applyConfigurationValues(configurationService, options, false);
354+
const themeDomRegistration = themeService.registerEditorContainer(domElement);
354355
options = options || {};
355356
if (typeof options.theme === 'string') {
356357
themeService.setTheme(options.theme);
@@ -362,6 +363,7 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon
362363
this._contextViewService = <ContextViewService>contextViewService;
363364
this._configurationService = configurationService;
364365
this._register(toDispose);
366+
this._register(themeDomRegistration);
365367

366368
let model: ITextModel | null;
367369
if (typeof _model === 'undefined') {
@@ -430,6 +432,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon
430432
@optional(IClipboardService) clipboardService: IClipboardService | null,
431433
) {
432434
applyConfigurationValues(configurationService, options, true);
435+
const themeDomRegistration = themeService.registerEditorContainer(domElement);
433436
options = options || {};
434437
if (typeof options.theme === 'string') {
435438
options.theme = themeService.setTheme(options.theme);
@@ -441,6 +444,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon
441444
this._configurationService = configurationService;
442445

443446
this._register(toDispose);
447+
this._register(themeDomRegistration);
444448

445449
this._contextViewService.setContainer(this._containerDomElement);
446450
}

0 commit comments

Comments
 (0)