Skip to content

Commit

Permalink
[web] TextField a11y focus should call didGain/didLose a11y focus act…
Browse files Browse the repository at this point in the history
…ion (flutter#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
  • Loading branch information
chunhtai authored and kjlubick committed Jul 14, 2023
1 parent 440da35 commit c5be111
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 18 deletions.
11 changes: 10 additions & 1 deletion lib/web_ui/lib/src/engine/semantics/text_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,16 @@ class TextField extends PrimaryRoleManager {
}

EnginePlatformDispatcher.instance.invokeOnSemanticsAction(
semanticsObject.id, ui.SemanticsAction.tap, null);
semanticsObject.id, ui.SemanticsAction.didGainAccessibilityFocus, null);
}));
activeEditableElement.addEventListener('blur',
createDomEventListener((DomEvent event) {
if (semanticsObject.owner.gestureMode != GestureMode.browserGestures) {
return;
}

EnginePlatformDispatcher.instance.invokeOnSemanticsAction(
semanticsObject.id, ui.SemanticsAction.didLoseAccessibilityFocus, null);
}));
}

Expand Down
6 changes: 3 additions & 3 deletions lib/web_ui/test/engine/semantics/semantics_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1517,7 +1517,7 @@ void _testTextField() {

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

expect(appHostNode.ownerDocument?.activeElement, textField);
expect(await logger.idLog.first, 0);
expect(await logger.actionLog.first, ui.SemanticsAction.tap);
expect(await logger.actionLog.first, ui.SemanticsAction.didGainAccessibilityFocus);

semantics().semanticsEnabled = false;
}, // TODO(yjbanov): https://github.com/flutter/flutter/issues/46638
Expand Down
34 changes: 20 additions & 14 deletions lib/web_ui/test/engine/semantics/text_field_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -92,25 +92,31 @@ void testMain() {
</sem>''');
});

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

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

expect(appHostNode.ownerDocument?.activeElement, isNot(textField));
expect(appHostNode.ownerDocument?.activeElement, isNot(textField));

textField.focus();
textField.focus();

expect(appHostNode.ownerDocument?.activeElement, textField);
expect(await logger.idLog.first, 0);
expect(await logger.actionLog.first, ui.SemanticsAction.tap);
expect(appHostNode.ownerDocument?.activeElement, textField);
expect(await logger.idLog.first, 0);
expect(await logger.actionLog.first, ui.SemanticsAction.didGainAccessibilityFocus);

textField.blur();

expect(appHostNode.ownerDocument?.activeElement, isNot(textField));
expect(await logger.idLog.first, 0);
expect(await logger.actionLog.first, ui.SemanticsAction.didLoseAccessibilityFocus);
}, // TODO(yjbanov): https://github.com/flutter/flutter/issues/46638
// TODO(yjbanov): https://github.com/flutter/flutter/issues/50590
skip: browserEngine != BrowserEngine.blink);
// TODO(yjbanov): https://github.com/flutter/flutter/issues/50590
skip: browserEngine != BrowserEngine.blink);

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

0 comments on commit c5be111

Please sign in to comment.