From 64fd258f4344249b32595bd26c9747892693bc3e Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 13 Nov 2023 12:36:29 -0800 Subject: [PATCH 1/5] fix #192278 --- .../browser/controller/textAreaHandler.ts | 6 ++- .../browser/controller/textAreaInput.ts | 41 ++++++++++++++----- .../test/browser/controller/imeTester.ts | 4 +- .../browser/controller/textAreaInput.test.ts | 4 +- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index e7f16198870..2727542276c 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -36,6 +36,7 @@ import { ColorId, ITokenPresentation } from 'vs/editor/common/encodedTokenAttrib import { Color } from 'vs/base/common/color'; import { IME } from 'vs/base/common/ime'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export interface IVisibleRangeProvider { visibleRangeForPosition(position: Position): HorizontalPosition | null; @@ -145,7 +146,8 @@ export class TextAreaHandler extends ViewPart { context: ViewContext, viewController: ViewController, visibleRangeProvider: IVisibleRangeProvider, - @IKeybindingService private readonly _keybindingService: IKeybindingService + @IKeybindingService private readonly _keybindingService: IKeybindingService, + @IInstantiationService private readonly _instantiationService: IInstantiationService ) { super(context); @@ -303,7 +305,7 @@ export class TextAreaHandler extends ViewPart { }; const textAreaWrapper = this._register(new TextAreaWrapper(this.textArea.domNode)); - this._textAreaInput = this._register(new TextAreaInput(textAreaInputHost, textAreaWrapper, platform.OS, { + this._textAreaInput = this._register(this._instantiationService.createInstance(TextAreaInput, textAreaInputHost, textAreaWrapper, platform.OS, { isAndroid: browser.isAndroid, isChrome: browser.isChrome, isFirefox: browser.isFirefox, diff --git a/src/vs/editor/browser/controller/textAreaInput.ts b/src/vs/editor/browser/controller/textAreaInput.ts index d0a06a18c51..6aa2e9f5ae3 100644 --- a/src/vs/editor/browser/controller/textAreaInput.ts +++ b/src/vs/editor/browser/controller/textAreaInput.ts @@ -11,13 +11,15 @@ import { inputLatency } from 'vs/base/browser/performance'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { Mimes } from 'vs/base/common/mime'; import { OperatingSystem } from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; import { ITextAreaWrapper, ITypeData, TextAreaState, _debugComposition } from 'vs/editor/browser/controller/textAreaState'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { ILogService } from 'vs/platform/log/common/log'; export namespace TextAreaSyntethicEvents { export const Tap = '-monaco-textarea-synthetic-tap'; @@ -193,7 +195,8 @@ export class TextAreaInput extends Disposable { // --- private readonly _asyncTriggerCut: RunOnceScheduler; - private readonly _asyncFocusGainWriteScreenReaderContent: RunOnceScheduler; + + private _asyncFocusGainWriteScreenReaderContent: MutableDisposable = new MutableDisposable(); private _textAreaState: TextAreaState; @@ -210,16 +213,27 @@ export class TextAreaInput extends Disposable { private readonly _host: ITextAreaInputHost, private readonly _textArea: ICompleteTextAreaWrapper, private readonly _OS: OperatingSystem, - private readonly _browser: IBrowser + private readonly _browser: IBrowser, + @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, + @ILogService private readonly _logService: ILogService ) { super(); this._asyncTriggerCut = this._register(new RunOnceScheduler(() => this._onCut.fire(), 0)); - this._asyncFocusGainWriteScreenReaderContent = this._register(new RunOnceScheduler(() => this.writeScreenReaderContent('asyncFocusGain'), 0)); - + if (this._accessibilityService.isScreenReaderOptimized()) { + this._asyncFocusGainWriteScreenReaderContent.value = this._register(new RunOnceScheduler(() => this.writeScreenReaderContent('asyncFocusGain'), 0)); + } this._textAreaState = TextAreaState.EMPTY; this._selectionChangeListener = null; - this.writeScreenReaderContent('ctor'); - + if (this._accessibilityService.isScreenReaderOptimized()) { + this.writeScreenReaderContent('ctor'); + } + this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => { + if (this._accessibilityService.isScreenReaderOptimized() && !this._asyncFocusGainWriteScreenReaderContent.value) { + this._asyncFocusGainWriteScreenReaderContent.value = this._register(new RunOnceScheduler(() => this.writeScreenReaderContent('asyncFocusGain'), 0)); + } else { + this._asyncFocusGainWriteScreenReaderContent.clear(); + } + })); this._hasFocus = false; this._currentComposition = null; @@ -431,10 +445,14 @@ export class TextAreaInput extends Disposable { this._setHasFocus(true); - if (this._browser.isSafari && !hadFocus && this._hasFocus) { + if (this._accessibilityService.isScreenReaderOptimized() && this._browser.isSafari && !hadFocus && this._hasFocus) { // When "tabbing into" the textarea, immediately after dispatching the 'focus' event, // Safari will always move the selection at offset 0 in the textarea - this._asyncFocusGainWriteScreenReaderContent.schedule(); + if (!this._asyncFocusGainWriteScreenReaderContent.value) { + this._asyncFocusGainWriteScreenReaderContent.value = new RunOnceScheduler(() => this.writeScreenReaderContent('asyncFocusGain'), 0); + this._asyncFocusGainWriteScreenReaderContent.value.schedule(); + } + this._asyncFocusGainWriteScreenReaderContent.value.schedule(); } })); this._register(this._textArea.onBlur(() => { @@ -622,11 +640,12 @@ export class TextAreaInput extends Disposable { } public writeScreenReaderContent(reason: string): void { - if (this._currentComposition) { + if (((reason === 'asyncFocusGain' || reason === 'render') && !this._accessibilityService.isScreenReaderOptimized()) || this._currentComposition) { + // Do not write on async focus gain / render unless a screen reader is being used #192278 // Do not write to the text area when doing composition return; } - + this._logService.trace(`writeScreenReaderContent(${reason})`); this._setAndWriteTextAreaState(reason, this._host.getScreenReaderContent()); } diff --git a/src/vs/editor/test/browser/controller/imeTester.ts b/src/vs/editor/test/browser/controller/imeTester.ts index 25cdbd57c75..1b1224fa535 100644 --- a/src/vs/editor/test/browser/controller/imeTester.ts +++ b/src/vs/editor/test/browser/controller/imeTester.ts @@ -12,6 +12,8 @@ import * as dom from 'vs/base/browser/dom'; import * as browser from 'vs/base/browser/browser'; import * as platform from 'vs/base/common/platform'; import { mainWindow } from 'vs/base/browser/window'; +import { TestAccessibilityService } from 'vs/platform/accessibility/test/common/testAccessibilityService'; +import { NullLogService } from 'vs/platform/log/common/log'; // To run this test, open imeTester.html @@ -127,7 +129,7 @@ function doCreateTest(description: string, inputStr: string, expectedStr: string isFirefox: browser.isFirefox, isChrome: browser.isChrome, isSafari: browser.isSafari, - }); + }, new TestAccessibilityService(), new NullLogService()); const output = document.createElement('pre'); output.className = 'output'; diff --git a/src/vs/editor/test/browser/controller/textAreaInput.test.ts b/src/vs/editor/test/browser/controller/textAreaInput.test.ts index 6874020e739..19fcf3b1970 100644 --- a/src/vs/editor/test/browser/controller/textAreaInput.test.ts +++ b/src/vs/editor/test/browser/controller/textAreaInput.test.ts @@ -12,6 +12,8 @@ import { ClipboardDataToCopy, IBrowser, ICompleteTextAreaWrapper, ITextAreaInput import { TextAreaState } from 'vs/editor/browser/controller/textAreaState'; import { Position } from 'vs/editor/common/core/position'; import { IRecorded, IRecordedEvent, IRecordedTextareaState } from 'vs/editor/test/browser/controller/imeRecordedTypes'; +import { TestAccessibilityService } from 'vs/platform/accessibility/test/common/testAccessibilityService'; +import { NullLogService } from 'vs/platform/log/common/log'; suite('TextAreaInput', () => { @@ -203,7 +205,7 @@ suite('TextAreaInput', () => { public hasFocus(): boolean { return true; } }); - const input = disposables.add(new TextAreaInput(host, wrapper, recorded.env.OS, recorded.env.browser)); + const input = disposables.add(new TextAreaInput(host, wrapper, recorded.env.OS, recorded.env.browser, new TestAccessibilityService(), new NullLogService())); wrapper._initialize(recorded.initial); input._initializeFromTest(); From 84aa769b9e5d187f18648b6ff86aa445602c9477 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 14 Nov 2023 09:29:19 -0800 Subject: [PATCH 2/5] address feedback --- .../browser/controller/textAreaHandler.ts | 8 +++---- .../browser/controller/textAreaInput.ts | 24 ++++++++----------- .../test/browser/controller/imeTester.ts | 2 +- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 2727542276c..f1661da1fc4 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -480,7 +480,7 @@ export class TextAreaHandler extends ViewPart { } public writeScreenReaderContent(reason: string): void { - this._textAreaInput.writeScreenReaderContent(reason); + this._textAreaInput.writeNativeTextAreaContent(reason); } public override dispose(): void { @@ -631,7 +631,7 @@ export class TextAreaHandler extends ViewPart { } if (e.hasChanged(EditorOption.accessibilitySupport)) { - this._textAreaInput.writeScreenReaderContent('strategy changed'); + this._textAreaInput.writeNativeTextAreaContent('strategy changed'); } return true; @@ -641,7 +641,7 @@ export class TextAreaHandler extends ViewPart { this._modelSelections = e.modelSelections.slice(0); // We must update the