diff --git a/lib/web_ui/lib/src/engine/semantics/text_field.dart b/lib/web_ui/lib/src/engine/semantics/text_field.dart index 289248ea0c50f..3e36ffb0ad0bd 100644 --- a/lib/web_ui/lib/src/engine/semantics/text_field.dart +++ b/lib/web_ui/lib/src/engine/semantics/text_field.dart @@ -364,7 +364,11 @@ class TextField extends RoleManager { // element, so that both the framework and the browser agree on what's // currently focused. bool needsDomFocusRequest = false; - + final EditingState editingState = EditingState( + text: semanticsObject.value, + baseOffset: semanticsObject.textSelectionBase, + extentOffset: semanticsObject.textSelectionExtent, + ); if (semanticsObject.hasFocus) { if (!_hasFocused) { _hasFocused = true; @@ -374,9 +378,14 @@ class TextField extends RoleManager { if (domDocument.activeElement != editableElement) { needsDomFocusRequest = true; } + // Focused elements should have full text editing state applied. + SemanticsTextEditingStrategy.instance.setEditingState(editingState); } else if (_hasFocused) { SemanticsTextEditingStrategy.instance.deactivate(this); + // Only apply text, because this node is not focused. + editingState.applyTextToDomElement(editableElement); + if (_hasFocused && domDocument.activeElement == editableElement) { // Unlike `editableElement.focus()` we don't need to schedule `blur` // post-update because `document.activeElement` implies that the diff --git a/lib/web_ui/test/engine/semantics/text_field_test.dart b/lib/web_ui/test/engine/semantics/text_field_test.dart index fb87725ea3630..91129dc3648b1 100644 --- a/lib/web_ui/test/engine/semantics/text_field_test.dart +++ b/lib/web_ui/test/engine/semantics/text_field_test.dart @@ -4,8 +4,6 @@ @TestOn('chrome || safari || firefox') -import 'dart:typed_data'; - import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; @@ -50,11 +48,6 @@ void testMain() { testTextEditing.configuration = singlelineConfig; }); - /// Emulates sending of a message by the framework to the engine. - void sendFrameworkMessage(ByteData? message) { - testTextEditing.channel.handleTextInput(message, (ByteData? data) {}); - } - test('renders a text field', () async { semantics() ..debugOverrideTimestampFunction(() => _testTime) @@ -134,7 +127,7 @@ void testMain() { // TODO(yjbanov): https://github.com/flutter/flutter/issues/50754 skip: browserEngine != BrowserEngine.blink); - test('Syncs semantic state from framework', () async { + test('Syncs editing state from framework', () async { semantics() ..debugOverrideTimestampFunction(() => _testTime) ..semanticsEnabled = true; @@ -166,6 +159,7 @@ void testMain() { expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement); expect(appHostNode.activeElement, strategy.domElement); expect(textField.editableElement, strategy.domElement); + expect((textField.editableElement as dynamic).value, 'hello'); expect(textField.editableElement.getAttribute('aria-label'), 'greeting'); expect(textField.editableElement.style.width, '10px'); expect(textField.editableElement.style.height, '15px'); @@ -180,6 +174,7 @@ void testMain() { expect(domDocument.activeElement, domDocument.body); expect(appHostNode.activeElement, null); expect(strategy.domElement, null); + expect((textField.editableElement as dynamic).value, 'bye'); expect(textField.editableElement.getAttribute('aria-label'), 'farewell'); expect(textField.editableElement.style.width, '12px'); expect(textField.editableElement.style.height, '17px'); @@ -193,92 +188,6 @@ void testMain() { expect(actionCount, 0); }); - test( - 'Does not overwrite text value and selection editing state on semantic updates', - () async { - semantics() - ..debugOverrideTimestampFunction(() => _testTime) - ..semanticsEnabled = true; - - strategy.enable( - singlelineConfig, - onChange: (_, __) {}, - onAction: (_) {}, - ); - - final SemanticsObject textFieldSemantics = createTextFieldSemantics( - value: 'hello', - textSelectionBase: 1, - textSelectionExtent: 3, - isFocused: true, - rect: const ui.Rect.fromLTWH(0, 0, 10, 15)); - - final TextField textField = - textFieldSemantics.debugRoleManagerFor(Role.textField)! as TextField; - final DomHTMLInputElement editableElement = - textField.editableElement as DomHTMLInputElement; - - expect(editableElement, strategy.domElement); - expect(editableElement.value, ''); - expect(editableElement.selectionStart, 0); - expect(editableElement.selectionEnd, 0); - - strategy.disable(); - semantics().semanticsEnabled = false; - }); - - test( - 'Updates editing state when receiving framework messages from the text input channel', - () async { - semantics() - ..debugOverrideTimestampFunction(() => _testTime) - ..semanticsEnabled = true; - - expect(domDocument.activeElement, domDocument.body); - expect(appHostNode.activeElement, null); - - strategy.enable( - singlelineConfig, - onChange: (_, __) {}, - onAction: (_) {}, - ); - - final SemanticsObject textFieldSemantics = createTextFieldSemantics( - value: 'hello', - textSelectionBase: 1, - textSelectionExtent: 3, - isFocused: true, - rect: const ui.Rect.fromLTWH(0, 0, 10, 15)); - - final TextField textField = - textFieldSemantics.debugRoleManagerFor(Role.textField)! as TextField; - final DomHTMLInputElement editableElement = - textField.editableElement as DomHTMLInputElement; - - // No updates expected on semantic updates - expect(editableElement, strategy.domElement); - expect(editableElement.value, ''); - expect(editableElement.selectionStart, 0); - expect(editableElement.selectionEnd, 0); - - // Update from framework - const MethodCall setEditingState = - MethodCall('TextInput.setEditingState', { - 'text': 'updated', - 'selectionBase': 2, - 'selectionExtent': 3, - }); - sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); - - // Editing state should now be updated - expect(editableElement.value, 'updated'); - expect(editableElement.selectionStart, 2); - expect(editableElement.selectionEnd, 3); - - strategy.disable(); - semantics().semanticsEnabled = false; - }); - test('Gives up focus after DOM blur', () async { semantics() ..debugOverrideTimestampFunction(() => _testTime) @@ -537,8 +446,6 @@ SemanticsObject createTextFieldSemantics({ bool isFocused = false, bool isMultiline = false, ui.Rect rect = const ui.Rect.fromLTRB(0, 0, 100, 50), - int textSelectionBase = 0, - int textSelectionExtent = 0, }) { final SemanticsTester tester = SemanticsTester(semantics()); tester.updateNode( @@ -551,8 +458,6 @@ SemanticsObject createTextFieldSemantics({ hasTap: true, rect: rect, textDirection: ui.TextDirection.ltr, - textSelectionBase: textSelectionBase, - textSelectionExtent: textSelectionExtent ); tester.apply(); return tester.getSemanticsObject(0);