Skip to content

Commit 83e4461

Browse files
authored
Adding check that EditContext is supported before using it (microsoft#230400)
* add check against edit context * adding code * choosing dom node of the overflow guard container * DebugEditContext no longer implements EditContext
1 parent 2fb92a5 commit 83e4461

File tree

5 files changed

+96
-45
lines changed

5 files changed

+96
-45
lines changed

src/typings/editContext.d.ts

+7-8
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55

66
type DOMString = string;
77

8-
declare class EditContext extends EventTarget {
9-
constructor(options?: EditContextInit);
8+
interface EditContext extends EventTarget {
109

1110
updateText(rangeStart: number, rangeEnd: number, text: DOMString): void;
1211
updateSelection(start: number, end: number): void;
@@ -59,7 +58,7 @@ interface EditContextEventHandlersEventMap {
5958

6059
type EventHandler<TEvent extends Event = Event> = (event: TEvent) => void;
6160

62-
declare class TextUpdateEvent extends Event {
61+
interface TextUpdateEvent extends Event {
6362
new(type: DOMString, options?: TextUpdateEventInit): TextUpdateEvent;
6463

6564
readonly updateRangeStart: number;
@@ -79,7 +78,7 @@ interface TextUpdateEventInit extends EventInit {
7978
compositionEnd: number;
8079
}
8180

82-
declare class TextFormat {
81+
interface TextFormat {
8382
new(options?: TextFormatInit): TextFormat;
8483

8584
readonly rangeStart: number;
@@ -95,10 +94,10 @@ interface TextFormatInit {
9594
underlineThickness: UnderlineThickness;
9695
}
9796

98-
type UnderlineStyle = "none" | "solid" | "dotted" | "dashed" | "wavy";
99-
type UnderlineThickness = "none" | "thin" | "thick";
97+
type UnderlineStyle = 'none' | 'solid' | 'dotted' | 'dashed' | 'wavy';
98+
type UnderlineThickness = 'none' | 'thin' | 'thick';
10099

101-
declare class TextFormatUpdateEvent extends Event {
100+
interface TextFormatUpdateEvent extends Event {
102101
new(type: DOMString, options?: TextFormatUpdateEventInit): TextFormatUpdateEvent;
103102
getTextFormats(): TextFormat[];
104103
}
@@ -107,7 +106,7 @@ interface TextFormatUpdateEventInit extends EventInit {
107106
textFormats: TextFormat[];
108107
}
109108

110-
declare class CharacterBoundsUpdateEvent extends Event {
109+
interface CharacterBoundsUpdateEvent extends Event {
111110
new(type: DOMString, options?: CharacterBoundsUpdateEventInit): CharacterBoundsUpdateEvent;
112111

113112
readonly rangeStart: number;

src/vs/editor/browser/controller/editContext/native/debugEditContext.ts

+55-35
Original file line numberDiff line numberDiff line change
@@ -3,49 +3,69 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import { EditContext } from './editContextFactory.js';
7+
68
const COLOR_FOR_CONTROL_BOUNDS = 'blue';
79
const COLOR_FOR_SELECTION_BOUNDS = 'red';
810
const COLOR_FOR_CHARACTER_BOUNDS = 'green';
911

10-
export class DebugEditContext extends EditContext {
12+
export class DebugEditContext {
1113
private _isDebugging = true;
1214
private _controlBounds: DOMRect | null = null;
1315
private _selectionBounds: DOMRect | null = null;
1416
private _characterBounds: { rangeStart: number; characterBounds: DOMRect[] } | null = null;
1517

16-
constructor(options?: EditContextInit | undefined) {
17-
super(options);
18+
private _editContext: EditContext;
19+
20+
constructor(window: Window, options?: EditContextInit | undefined) {
21+
this._editContext = EditContext.create(window, options);
22+
}
23+
24+
get text(): DOMString {
25+
return this._editContext.text;
26+
}
27+
28+
get selectionStart(): number {
29+
return this._editContext.selectionStart;
30+
}
31+
32+
get selectionEnd(): number {
33+
return this._editContext.selectionEnd;
34+
}
35+
36+
get characterBoundsRangeStart(): number {
37+
return this._editContext.characterBoundsRangeStart;
1838
}
1939

20-
override updateText(rangeStart: number, rangeEnd: number, text: string): void {
21-
super.updateText(rangeStart, rangeEnd, text);
40+
updateText(rangeStart: number, rangeEnd: number, text: string): void {
41+
this._editContext.updateText(rangeStart, rangeEnd, text);
2242
this.renderDebug();
2343
}
24-
override updateSelection(start: number, end: number): void {
25-
super.updateSelection(start, end);
44+
updateSelection(start: number, end: number): void {
45+
this._editContext.updateSelection(start, end);
2646
this.renderDebug();
2747
}
28-
override updateControlBounds(controlBounds: DOMRect): void {
29-
super.updateControlBounds(controlBounds);
48+
updateControlBounds(controlBounds: DOMRect): void {
49+
this._editContext.updateControlBounds(controlBounds);
3050
this._controlBounds = controlBounds;
3151
this.renderDebug();
3252
}
33-
override updateSelectionBounds(selectionBounds: DOMRect): void {
34-
super.updateSelectionBounds(selectionBounds);
53+
updateSelectionBounds(selectionBounds: DOMRect): void {
54+
this._editContext.updateSelectionBounds(selectionBounds);
3555
this._selectionBounds = selectionBounds;
3656
this.renderDebug();
3757
}
38-
override updateCharacterBounds(rangeStart: number, characterBounds: DOMRect[]): void {
39-
super.updateCharacterBounds(rangeStart, characterBounds);
58+
updateCharacterBounds(rangeStart: number, characterBounds: DOMRect[]): void {
59+
this._editContext.updateCharacterBounds(rangeStart, characterBounds);
4060
this._characterBounds = { rangeStart, characterBounds };
4161
this.renderDebug();
4262
}
43-
override attachedElements(): HTMLElement[] {
44-
return super.attachedElements();
63+
attachedElements(): HTMLElement[] {
64+
return this._editContext.attachedElements();
4565
}
4666

47-
override characterBounds(): DOMRect[] {
48-
return super.characterBounds();
67+
characterBounds(): DOMRect[] {
68+
return this._editContext.characterBounds();
4969
}
5070

5171
private readonly _ontextupdateWrapper = new EventListenerWrapper('textupdate', this);
@@ -54,22 +74,22 @@ export class DebugEditContext extends EditContext {
5474
private readonly _oncompositionstartWrapper = new EventListenerWrapper('compositionstart', this);
5575
private readonly _oncompositionendWrapper = new EventListenerWrapper('compositionend', this);
5676

57-
override get ontextupdate(): EventHandler | null { return this._ontextupdateWrapper.eventHandler; }
58-
override set ontextupdate(value: EventHandler | null) { this._ontextupdateWrapper.eventHandler = value; }
59-
override get ontextformatupdate(): EventHandler | null { return this._ontextformatupdateWrapper.eventHandler; }
60-
override set ontextformatupdate(value: EventHandler | null) { this._ontextformatupdateWrapper.eventHandler = value; }
61-
override get oncharacterboundsupdate(): EventHandler | null { return this._oncharacterboundsupdateWrapper.eventHandler; }
62-
override set oncharacterboundsupdate(value: EventHandler | null) { this._oncharacterboundsupdateWrapper.eventHandler = value; }
63-
override get oncompositionstart(): EventHandler | null { return this._oncompositionstartWrapper.eventHandler; }
64-
override set oncompositionstart(value: EventHandler | null) { this._oncompositionstartWrapper.eventHandler = value; }
65-
override get oncompositionend(): EventHandler | null { return this._oncompositionendWrapper.eventHandler; }
66-
override set oncompositionend(value: EventHandler | null) { this._oncompositionendWrapper.eventHandler = value; }
77+
get ontextupdate(): EventHandler | null { return this._ontextupdateWrapper.eventHandler; }
78+
set ontextupdate(value: EventHandler | null) { this._ontextupdateWrapper.eventHandler = value; }
79+
get ontextformatupdate(): EventHandler | null { return this._ontextformatupdateWrapper.eventHandler; }
80+
set ontextformatupdate(value: EventHandler | null) { this._ontextformatupdateWrapper.eventHandler = value; }
81+
get oncharacterboundsupdate(): EventHandler | null { return this._oncharacterboundsupdateWrapper.eventHandler; }
82+
set oncharacterboundsupdate(value: EventHandler | null) { this._oncharacterboundsupdateWrapper.eventHandler = value; }
83+
get oncompositionstart(): EventHandler | null { return this._oncompositionstartWrapper.eventHandler; }
84+
set oncompositionstart(value: EventHandler | null) { this._oncompositionstartWrapper.eventHandler = value; }
85+
get oncompositionend(): EventHandler | null { return this._oncompositionendWrapper.eventHandler; }
86+
set oncompositionend(value: EventHandler | null) { this._oncompositionendWrapper.eventHandler = value; }
6787

6888

6989
private readonly _listenerMap = new Map<EventListenerOrEventListenerObject, EventListenerOrEventListenerObject>();
7090

71-
override addEventListener<K extends keyof EditContextEventHandlersEventMap>(type: K, listener: (this: GlobalEventHandlers, ev: EditContextEventHandlersEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
72-
override addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void {
91+
addEventListener<K extends keyof EditContextEventHandlersEventMap>(type: K, listener: (this: GlobalEventHandlers, ev: EditContextEventHandlersEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
92+
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void {
7393
if (!listener) { return; }
7494

7595
const debugListener = (event: Event) => {
@@ -84,22 +104,22 @@ export class DebugEditContext extends EditContext {
84104
}
85105
};
86106
this._listenerMap.set(listener, debugListener);
87-
super.addEventListener(type, debugListener, options);
107+
this._editContext.addEventListener(type, debugListener, options);
88108
this.renderDebug();
89109
}
90110

91-
override removeEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | EventListenerOptions | undefined): void {
111+
removeEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | EventListenerOptions | undefined): void {
92112
if (!listener) { return; }
93113
const debugListener = this._listenerMap.get(listener);
94114
if (debugListener) {
95-
super.removeEventListener(type, debugListener, options);
115+
this._editContext.removeEventListener(type, debugListener, options);
96116
this._listenerMap.delete(listener);
97117
}
98118
this.renderDebug();
99119
}
100120

101-
override dispatchEvent(event: Event): boolean {
102-
return super.dispatchEvent(event);
121+
dispatchEvent(event: Event): boolean {
122+
return this._editContext.dispatchEvent(event);
103123
}
104124

105125
public startDebugging() {
@@ -131,7 +151,7 @@ export class DebugEditContext extends EditContext {
131151
this._disposables.push(createRect(rect, COLOR_FOR_CHARACTER_BOUNDS));
132152
}
133153
}
134-
this._disposables.push(createDiv(this.text, this.selectionStart, this.selectionEnd));
154+
this._disposables.push(createDiv(this._editContext.text, this._editContext.selectionStart, this._editContext.selectionEnd));
135155
}
136156
}
137157

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
7+
export namespace EditContext {
8+
9+
/**
10+
* Checks if the EditContext is supported in the given window.
11+
*/
12+
export function supported(obj: any & Window): boolean {
13+
console.log('typeof obj?.EditContext : ', typeof obj?.EditContext);
14+
if (typeof obj?.EditContext === 'function') {
15+
return true;
16+
}
17+
return false;
18+
}
19+
20+
/**
21+
* Create an edit context. Check that the EditContext is supported using the method {@link EditContext.supported}
22+
*/
23+
export function create(window: Window, options?: EditContextInit): EditContext {
24+
return new (window as any).EditContext(options);
25+
}
26+
}

src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { Position } from '../../../../common/core/position.js';
2727
import { IVisibleRangeProvider } from '../textArea/textAreaEditContext.js';
2828
import { PositionOffsetTransformer } from '../../../../common/core/positionToOffset.js';
2929
import { IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js';
30+
import { EditContext } from './editContextFactory.js';
3031

3132
// Corresponds to classes in nativeEditContext.css
3233
enum CompositionClassName {
@@ -77,7 +78,8 @@ export class NativeEditContext extends AbstractEditContext {
7778
this._context.viewModel.setHasFocus(newFocusValue);
7879
}));
7980

80-
this._editContext = new EditContext();
81+
const window = getWindow(this.domNode.domNode);
82+
this._editContext = EditContext.create(window);
8183
this.setEditContextOnDomNode();
8284

8385
this._screenReaderSupport = instantiationService.createInstance(ScreenReaderSupport, this.domNode, context);

src/vs/editor/browser/view.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import { AbstractEditContext } from './controller/editContext/editContext.js';
6161
import { IVisibleRangeProvider, TextAreaEditContext } from './controller/editContext/textArea/textAreaEditContext.js';
6262
import { NativeEditContext } from './controller/editContext/native/nativeEditContext.js';
6363
import { RulersGpu } from './viewParts/rulersGpu/rulersGpu.js';
64+
import { EditContext } from './controller/editContext/native/editContextFactory.js';
6465

6566

6667
export interface IContentWidgetData {
@@ -267,7 +268,10 @@ export class View extends ViewEventHandler {
267268
}
268269

269270
private _instantiateEditContext(experimentalEditContextEnabled: boolean): AbstractEditContext {
270-
return this._instantiationService.createInstance(experimentalEditContextEnabled ? NativeEditContext : TextAreaEditContext, this._context, this._overflowGuardContainer, this._viewController, this._createTextAreaHandlerHelper());
271+
const domNode = dom.getWindow(this._overflowGuardContainer.domNode);
272+
const isEditContextSupported = EditContext.supported(domNode);
273+
const EditContextType = (experimentalEditContextEnabled && isEditContextSupported) ? NativeEditContext : TextAreaEditContext;
274+
return this._instantiationService.createInstance(EditContextType, this._context, this._overflowGuardContainer, this._viewController, this._createTextAreaHandlerHelper());
271275
}
272276

273277
private _updateEditContext(): void {

0 commit comments

Comments
 (0)