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

Commit 5932bad

Browse files
authored
Revert "Revert "[web] Don't overwrite editing state with semantic updates (#38271)" (#38562)" (#38854)
This reverts commit 5713a21.
1 parent e655580 commit 5932bad

File tree

2 files changed

+99
-13
lines changed

2 files changed

+99
-13
lines changed

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -364,11 +364,7 @@ class TextField extends RoleManager {
364364
// element, so that both the framework and the browser agree on what's
365365
// currently focused.
366366
bool needsDomFocusRequest = false;
367-
final EditingState editingState = EditingState(
368-
text: semanticsObject.value,
369-
baseOffset: semanticsObject.textSelectionBase,
370-
extentOffset: semanticsObject.textSelectionExtent,
371-
);
367+
372368
if (semanticsObject.hasFocus) {
373369
if (!_hasFocused) {
374370
_hasFocused = true;
@@ -378,14 +374,9 @@ class TextField extends RoleManager {
378374
if (domDocument.activeElement != editableElement) {
379375
needsDomFocusRequest = true;
380376
}
381-
// Focused elements should have full text editing state applied.
382-
SemanticsTextEditingStrategy.instance.setEditingState(editingState);
383377
} else if (_hasFocused) {
384378
SemanticsTextEditingStrategy.instance.deactivate(this);
385379

386-
// Only apply text, because this node is not focused.
387-
editingState.applyTextToDomElement(editableElement);
388-
389380
if (_hasFocused && domDocument.activeElement == editableElement) {
390381
// Unlike `editableElement.focus()` we don't need to schedule `blur`
391382
// post-update because `document.activeElement` implies that the

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

Lines changed: 98 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
@TestOn('chrome || safari || firefox')
66

7+
import 'dart:typed_data';
8+
79
import 'package:test/bootstrap/browser.dart';
810
import 'package:test/test.dart';
911

@@ -48,6 +50,11 @@ void testMain() {
4850
testTextEditing.configuration = singlelineConfig;
4951
});
5052

53+
/// Emulates sending of a message by the framework to the engine.
54+
void sendFrameworkMessage(ByteData? message) {
55+
testTextEditing.channel.handleTextInput(message, (ByteData? data) {});
56+
}
57+
5158
test('renders a text field', () async {
5259
semantics()
5360
..debugOverrideTimestampFunction(() => _testTime)
@@ -127,7 +134,7 @@ void testMain() {
127134
// TODO(yjbanov): https://github.com/flutter/flutter/issues/50754
128135
skip: browserEngine != BrowserEngine.blink);
129136

130-
test('Syncs editing state from framework', () async {
137+
test('Syncs semantic state from framework', () async {
131138
semantics()
132139
..debugOverrideTimestampFunction(() => _testTime)
133140
..semanticsEnabled = true;
@@ -159,7 +166,6 @@ void testMain() {
159166
expect(domDocument.activeElement, flutterViewEmbedder.glassPaneElement);
160167
expect(appHostNode.activeElement, strategy.domElement);
161168
expect(textField.editableElement, strategy.domElement);
162-
expect((textField.editableElement as dynamic).value, 'hello');
163169
expect(textField.editableElement.getAttribute('aria-label'), 'greeting');
164170
expect(textField.editableElement.style.width, '10px');
165171
expect(textField.editableElement.style.height, '15px');
@@ -174,7 +180,6 @@ void testMain() {
174180
expect(domDocument.activeElement, domDocument.body);
175181
expect(appHostNode.activeElement, null);
176182
expect(strategy.domElement, null);
177-
expect((textField.editableElement as dynamic).value, 'bye');
178183
expect(textField.editableElement.getAttribute('aria-label'), 'farewell');
179184
expect(textField.editableElement.style.width, '12px');
180185
expect(textField.editableElement.style.height, '17px');
@@ -188,6 +193,92 @@ void testMain() {
188193
expect(actionCount, 0);
189194
});
190195

196+
test(
197+
'Does not overwrite text value and selection editing state on semantic updates',
198+
() async {
199+
semantics()
200+
..debugOverrideTimestampFunction(() => _testTime)
201+
..semanticsEnabled = true;
202+
203+
strategy.enable(
204+
singlelineConfig,
205+
onChange: (_, __) {},
206+
onAction: (_) {},
207+
);
208+
209+
final SemanticsObject textFieldSemantics = createTextFieldSemantics(
210+
value: 'hello',
211+
textSelectionBase: 1,
212+
textSelectionExtent: 3,
213+
isFocused: true,
214+
rect: const ui.Rect.fromLTWH(0, 0, 10, 15));
215+
216+
final TextField textField =
217+
textFieldSemantics.debugRoleManagerFor(Role.textField)! as TextField;
218+
final DomHTMLInputElement editableElement =
219+
textField.editableElement as DomHTMLInputElement;
220+
221+
expect(editableElement, strategy.domElement);
222+
expect(editableElement.value, '');
223+
expect(editableElement.selectionStart, 0);
224+
expect(editableElement.selectionEnd, 0);
225+
226+
strategy.disable();
227+
semantics().semanticsEnabled = false;
228+
});
229+
230+
test(
231+
'Updates editing state when receiving framework messages from the text input channel',
232+
() async {
233+
semantics()
234+
..debugOverrideTimestampFunction(() => _testTime)
235+
..semanticsEnabled = true;
236+
237+
expect(domDocument.activeElement, domDocument.body);
238+
expect(appHostNode.activeElement, null);
239+
240+
strategy.enable(
241+
singlelineConfig,
242+
onChange: (_, __) {},
243+
onAction: (_) {},
244+
);
245+
246+
final SemanticsObject textFieldSemantics = createTextFieldSemantics(
247+
value: 'hello',
248+
textSelectionBase: 1,
249+
textSelectionExtent: 3,
250+
isFocused: true,
251+
rect: const ui.Rect.fromLTWH(0, 0, 10, 15));
252+
253+
final TextField textField =
254+
textFieldSemantics.debugRoleManagerFor(Role.textField)! as TextField;
255+
final DomHTMLInputElement editableElement =
256+
textField.editableElement as DomHTMLInputElement;
257+
258+
// No updates expected on semantic updates
259+
expect(editableElement, strategy.domElement);
260+
expect(editableElement.value, '');
261+
expect(editableElement.selectionStart, 0);
262+
expect(editableElement.selectionEnd, 0);
263+
264+
// Update from framework
265+
const MethodCall setEditingState =
266+
MethodCall('TextInput.setEditingState', <String, dynamic>{
267+
'text': 'updated',
268+
'selectionBase': 2,
269+
'selectionExtent': 3,
270+
});
271+
sendFrameworkMessage(codec.encodeMethodCall(setEditingState));
272+
273+
// Editing state should now be updated
274+
expect(editableElement.value, 'updated');
275+
expect(editableElement.selectionStart, 2);
276+
expect(editableElement.selectionEnd, 3);
277+
278+
strategy.disable();
279+
semantics().semanticsEnabled = false;
280+
});
281+
191282
test('Gives up focus after DOM blur', () async {
192283
semantics()
193284
..debugOverrideTimestampFunction(() => _testTime)
@@ -446,6 +537,8 @@ SemanticsObject createTextFieldSemantics({
446537
bool isFocused = false,
447538
bool isMultiline = false,
448539
ui.Rect rect = const ui.Rect.fromLTRB(0, 0, 100, 50),
540+
int textSelectionBase = 0,
541+
int textSelectionExtent = 0,
449542
}) {
450543
final SemanticsTester tester = SemanticsTester(semantics());
451544
tester.updateNode(
@@ -458,6 +551,8 @@ SemanticsObject createTextFieldSemantics({
458551
hasTap: true,
459552
rect: rect,
460553
textDirection: ui.TextDirection.ltr,
554+
textSelectionBase: textSelectionBase,
555+
textSelectionExtent: textSelectionExtent
461556
);
462557
tester.apply();
463558
return tester.getSemanticsObject(0);

0 commit comments

Comments
 (0)