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

Commit 49c2e69

Browse files
author
nturgut
committed
updating editing state after location change. focusing on the element
1 parent 0b33046 commit 49c2e69

File tree

2 files changed

+74
-6
lines changed

2 files changed

+74
-6
lines changed

lib/web_ui/lib/src/engine/text_editing/text_editing.dart

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,9 @@ class EngineAutofillForm {
180180
// Add a seperator between element identifiers.
181181
for (final String id in ids) {
182182
if (idBuffer.length > 0) {
183-
idBuffer.write('*');
184-
}
185-
idBuffer.write(id);
183+
idBuffer.write('*');
184+
}
185+
idBuffer.write(id);
186186
}
187187

188188
final String formIdentifier = idBuffer.toString();
@@ -604,10 +604,15 @@ class GloballyPositionedTextEditingStrategy extends DefaultTextEditingStrategy {
604604

605605
@override
606606
void placeElement() {
607-
super.placeElement();
608607
if (hasAutofillGroup) {
609608
_geometry?.applyToDomElement(focusedFormElement!);
610609
placeForm();
610+
// Set the last editing state if it exists, this is critical for a
611+
// users ongoing work to continue uninterrupted when there is an update to
612+
// the transform.
613+
if (_lastEditingState != null) {
614+
_lastEditingState!.applyToDomElement(domElement);
615+
}
611616
// On Chrome, when a form is focused, it opens an autofill menu
612617
// immediately.
613618
// Flutter framework sends `setEditableSizeAndTransform` for informing
@@ -617,7 +622,9 @@ class GloballyPositionedTextEditingStrategy extends DefaultTextEditingStrategy {
617622
// `setEditableSizeAndTransform` method is called and focus on the form
618623
// only after placing it to the correct position. Hence autofill menu
619624
// does not appear on top-left of the page.
625+
// Refocus on the elements after applying the geometry.
620626
focusedFormElement!.focus();
627+
domElement.focus();
621628
} else {
622629
_geometry?.applyToDomElement(domElement);
623630
}
@@ -653,6 +660,12 @@ class SafariDesktopTextEditingStrategy extends DefaultTextEditingStrategy {
653660
_geometry?.applyToDomElement(domElement);
654661
if (hasAutofillGroup) {
655662
placeForm();
663+
// Set the last editing state if it exists, this is critical for a
664+
// users ongoing work to continue uninterrupted when there is an update to
665+
// the transform.
666+
if (_lastEditingState != null) {
667+
_lastEditingState!.applyToDomElement(domElement);
668+
}
656669
// On Safari Desktop, when a form is focused, it opens an autofill menu
657670
// immediately.
658671
// Flutter framework sends `setEditableSizeAndTransform` for informing
@@ -1223,6 +1236,12 @@ class FirefoxTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
12231236
void placeElement() {
12241237
domElement.focus();
12251238
_geometry?.applyToDomElement(domElement);
1239+
// Set the last editing state if it exists, this is critical for a
1240+
// users ongoing work to continue uninterrupted when there is an update to
1241+
// the transform.
1242+
if (_lastEditingState != null) {
1243+
_lastEditingState!.applyToDomElement(domElement);
1244+
}
12261245
}
12271246
}
12281247

lib/web_ui/test/text_editing_test.dart

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,55 @@ void testMain() {
10331033
expect(formsOnTheDom, hasLength(1));
10341034
});
10351035

1036+
test(
1037+
'singleTextField Autofill setEditableSizeAndTransform preserves'
1038+
'editing state', () {
1039+
// Create a configuration with focused element has autofil hint.
1040+
final Map<String, dynamic> flutterSingleAutofillElementConfig =
1041+
createFlutterConfig('text', autofillHint: 'username');
1042+
final MethodCall setClient = MethodCall('TextInput.setClient',
1043+
<dynamic>[123, flutterSingleAutofillElementConfig]);
1044+
sendFrameworkMessage(codec.encodeMethodCall(setClient));
1045+
1046+
const MethodCall setEditingState1 =
1047+
MethodCall('TextInput.setEditingState', <String, dynamic>{
1048+
'text': 'abcd',
1049+
'selectionBase': 2,
1050+
'selectionExtent': 3,
1051+
});
1052+
sendFrameworkMessage(codec.encodeMethodCall(setEditingState1));
1053+
1054+
const MethodCall show = MethodCall('TextInput.show');
1055+
sendFrameworkMessage(codec.encodeMethodCall(show));
1056+
1057+
// The second [setEditingState] should override the first one.
1058+
checkInputEditingState(
1059+
textEditing.editingElement.domElement, 'abcd', 2, 3);
1060+
1061+
// The transform is changed. For example after a validation error, red
1062+
// line appeared under the input field.
1063+
final MethodCall setSizeAndTransform =
1064+
configureSetSizeAndTransformMethodCall(150, 50,
1065+
Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList());
1066+
sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform));
1067+
1068+
// Check the element still has focus. User can keep editing.
1069+
expect(document.activeElement, textEditing.editingElement.domElement);
1070+
1071+
// Check the cursor location is the same.
1072+
checkInputEditingState(
1073+
textEditing.editingElement.domElement, 'abcd', 2, 3);
1074+
1075+
const MethodCall clearClient = MethodCall('TextInput.clearClient');
1076+
sendFrameworkMessage(codec.encodeMethodCall(clearClient));
1077+
1078+
// Confirm that [HybridTextEditing] didn't send any messages.
1079+
expect(spy.messages, isEmpty);
1080+
// Form stays on the DOM until autofill context is finalized.
1081+
expect(document.getElementsByTagName('form'), isNotEmpty);
1082+
expect(formsOnTheDom, hasLength(1));
1083+
});
1084+
10361085
test(
10371086
'multiTextField Autofill: setClient, setEditingState, show, '
10381087
'setEditingState, clearClient', () {
@@ -1693,7 +1742,8 @@ void testMain() {
16931742
// Autofill value is applied to the element.
16941743
expect(firstElement.name,
16951744
BrowserAutofillHints.instance.flutterToEngine('password'));
1696-
expect(firstElement.id, BrowserAutofillHints.instance.flutterToEngine('password'));
1745+
expect(firstElement.id,
1746+
BrowserAutofillHints.instance.flutterToEngine('password'));
16971747
expect(firstElement.type, 'password');
16981748
if (browserEngine == BrowserEngine.firefox) {
16991749
expect(firstElement.name,
@@ -1722,7 +1772,6 @@ void testMain() {
17221772
EngineAutofillForm.fromFrameworkMessage(
17231773
createAutofillInfo('username', 'field1'), fields);
17241774

1725-
17261775
expect(autofillForm.formIdentifier, 'aabbcc*jjkkll*zzyyxx');
17271776
});
17281777

0 commit comments

Comments
 (0)