Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit d3adaed

Browse files
authored
[web] TextField a11y focus should call didGain/didLose a11y focus action (#43279)
fixes flutter/flutter#128709 requires flutter/flutter#129652 The issue is that when textfield focus in framework and web engine a11y are out of sync, the framework keep sending update with textfield focus = true and causes web engine to keep refocusing the textfield. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 5845102 commit d3adaed

File tree

3 files changed

+33
-18
lines changed

3 files changed

+33
-18
lines changed

lib/web_ui/lib/src/engine/semantics/text_field.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,16 @@ class TextField extends PrimaryRoleManager {
301301
}
302302

303303
EnginePlatformDispatcher.instance.invokeOnSemanticsAction(
304-
semanticsObject.id, ui.SemanticsAction.tap, null);
304+
semanticsObject.id, ui.SemanticsAction.didGainAccessibilityFocus, null);
305+
}));
306+
activeEditableElement.addEventListener('blur',
307+
createDomEventListener((DomEvent event) {
308+
if (semanticsObject.owner.gestureMode != GestureMode.browserGestures) {
309+
return;
310+
}
311+
312+
EnginePlatformDispatcher.instance.invokeOnSemanticsAction(
313+
semanticsObject.id, ui.SemanticsAction.didLoseAccessibilityFocus, null);
305314
}));
306315
}
307316

lib/web_ui/test/engine/semantics/semantics_test.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,7 +1517,7 @@ void _testTextField() {
15171517

15181518
// TODO(yjbanov): this test will need to be adjusted for Safari when we add
15191519
// Safari testing.
1520-
test('sends a tap action when text field is activated', () async {
1520+
test('sends a focus action when text field is activated', () async {
15211521
final SemanticsActionLogger logger = SemanticsActionLogger();
15221522
semantics()
15231523
..debugOverrideTimestampFunction(() => _testTime)
@@ -1526,7 +1526,7 @@ void _testTextField() {
15261526
final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder();
15271527
updateNode(
15281528
builder,
1529-
actions: 0 | ui.SemanticsAction.tap.index,
1529+
actions: 0 | ui.SemanticsAction.didGainAccessibilityFocus.index,
15301530
flags: 0 | ui.SemanticsFlag.isTextField.index,
15311531
value: 'hello',
15321532
transform: Matrix4.identity().toFloat64(),
@@ -1544,7 +1544,7 @@ void _testTextField() {
15441544

15451545
expect(appHostNode.ownerDocument?.activeElement, textField);
15461546
expect(await logger.idLog.first, 0);
1547-
expect(await logger.actionLog.first, ui.SemanticsAction.tap);
1547+
expect(await logger.actionLog.first, ui.SemanticsAction.didGainAccessibilityFocus);
15481548

15491549
semantics().semanticsEnabled = false;
15501550
}, // TODO(yjbanov): https://github.com/flutter/flutter/issues/46638

lib/web_ui/test/engine/semantics/text_field_test.dart

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -92,25 +92,31 @@ void testMain() {
9292
</sem>''');
9393
});
9494

95-
// TODO(yjbanov): this test will need to be adjusted for Safari when we add
96-
// Safari testing.
97-
test('sends a tap action when browser requests focus', () async {
98-
final SemanticsActionLogger logger = SemanticsActionLogger();
99-
createTextFieldSemantics(value: 'hello');
95+
// TODO(yjbanov): this test will need to be adjusted for Safari when we add
96+
// Safari testing.
97+
test('sends a didGainAccessibilityFocus/didLoseAccessibilityFocus action when browser requests focus/blur', () async {
98+
final SemanticsActionLogger logger = SemanticsActionLogger();
99+
createTextFieldSemantics(value: 'hello');
100100

101-
final DomElement textField = appHostNode
102-
.querySelector('input[data-semantics-role="text-field"]')!;
101+
final DomElement textField = appHostNode
102+
.querySelector('input[data-semantics-role="text-field"]')!;
103103

104-
expect(appHostNode.ownerDocument?.activeElement, isNot(textField));
104+
expect(appHostNode.ownerDocument?.activeElement, isNot(textField));
105105

106-
textField.focus();
106+
textField.focus();
107107

108-
expect(appHostNode.ownerDocument?.activeElement, textField);
109-
expect(await logger.idLog.first, 0);
110-
expect(await logger.actionLog.first, ui.SemanticsAction.tap);
108+
expect(appHostNode.ownerDocument?.activeElement, textField);
109+
expect(await logger.idLog.first, 0);
110+
expect(await logger.actionLog.first, ui.SemanticsAction.didGainAccessibilityFocus);
111+
112+
textField.blur();
113+
114+
expect(appHostNode.ownerDocument?.activeElement, isNot(textField));
115+
expect(await logger.idLog.first, 0);
116+
expect(await logger.actionLog.first, ui.SemanticsAction.didLoseAccessibilityFocus);
111117
}, // TODO(yjbanov): https://github.com/flutter/flutter/issues/46638
112-
// TODO(yjbanov): https://github.com/flutter/flutter/issues/50590
113-
skip: browserEngine != BrowserEngine.blink);
118+
// TODO(yjbanov): https://github.com/flutter/flutter/issues/50590
119+
skip: browserEngine != BrowserEngine.blink);
114120

115121
test('Syncs semantic state from framework', () {
116122
expect(appHostNode.ownerDocument?.activeElement, domDocument.body);

0 commit comments

Comments
 (0)