2
2
// Use of this source code is governed by a BSD-style license that can be
3
3
// found in the LICENSE file.
4
4
5
-
6
5
part of engine;
7
6
8
7
/// Make the content editable span visible to facilitate debugging.
@@ -115,8 +114,10 @@ class EngineAutofillForm {
115
114
if (fields != null ) {
116
115
for (Map <String , dynamic > field in fields.cast <Map <String , dynamic >>()) {
117
116
final Map <String , dynamic > autofillInfo = field['autofill' ];
118
- final AutofillInfo autofill =
119
- AutofillInfo .fromFrameworkMessage (autofillInfo);
117
+ final AutofillInfo autofill = AutofillInfo .fromFrameworkMessage (
118
+ autofillInfo,
119
+ textCapitalization: TextCapitalizationConfig .fromInputConfiguration (
120
+ field['textCapitalization' ]));
120
121
121
122
// The focused text editing element will not be created here.
122
123
final AutofillInfo focusedElement =
@@ -170,16 +171,24 @@ class EngineAutofillForm {
170
171
keys.forEach ((String key) {
171
172
final html.Element element = elements! [key]! ;
172
173
subscriptions.add (element.onInput.listen ((html.Event e) {
173
- _handleChange (element, key);
174
+ if (items! [key] == null ) {
175
+ throw StateError (
176
+ 'Autofill would not work withuot Autofill value set' );
177
+ } else {
178
+ final AutofillInfo autofillInfo = items! [key] as AutofillInfo ;
179
+ _handleChange (element, autofillInfo);
180
+ }
174
181
}));
175
182
});
176
183
return subscriptions;
177
184
}
178
185
179
- void _handleChange (html.Element domElement, String ? tag) {
180
- EditingState newEditingState = EditingState .fromDomElement (domElement as html.HtmlElement ? );
186
+ void _handleChange (html.Element domElement, AutofillInfo autofillInfo) {
187
+ EditingState newEditingState = EditingState .fromDomElement (
188
+ domElement as html.HtmlElement ? ,
189
+ textCapitalization: autofillInfo.textCapitalization);
181
190
182
- _sendAutofillEditingState (tag , newEditingState);
191
+ _sendAutofillEditingState (autofillInfo.uniqueIdentifier , newEditingState);
183
192
}
184
193
185
194
/// Sends the 'TextInputClient.updateEditingStateWithTag' message to the framework.
@@ -207,7 +216,11 @@ class EngineAutofillForm {
207
216
/// These values are to be used when a text field have autofill enabled.
208
217
@visibleForTesting
209
218
class AutofillInfo {
210
- AutofillInfo ({required this .editingState, required this .uniqueIdentifier, required this .hint});
219
+ AutofillInfo (
220
+ {required this .editingState,
221
+ required this .uniqueIdentifier,
222
+ required this .hint,
223
+ required this .textCapitalization});
211
224
212
225
/// The current text and selection state of a text field.
213
226
final EditingState editingState;
@@ -217,14 +230,29 @@ class AutofillInfo {
217
230
/// Used as id of the text field.
218
231
final String uniqueIdentifier;
219
232
233
+ /// Information on how should autofilled text capitalized.
234
+ ///
235
+ /// For example for [TextCapitalization.characters] each letter is converted
236
+ /// to upper case.
237
+ ///
238
+ /// This value is not necessary for autofilling the focused element since
239
+ /// [DefaultTextEditingStrategy._inputConfiguration] already has this
240
+ /// information.
241
+ ///
242
+ /// On the other hand for the multi element forms, for the input elements
243
+ /// other the focused field, we need to use this information.
244
+ final TextCapitalizationConfig textCapitalization;
245
+
220
246
/// Attribute used for autofill.
221
247
///
222
248
/// Used as a guidance to the browser as to the type of information expected
223
249
/// in the field.
224
250
/// See: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete
225
251
final String hint;
226
252
227
- factory AutofillInfo .fromFrameworkMessage (Map <String , dynamic > autofill) {
253
+ factory AutofillInfo .fromFrameworkMessage (Map <String , dynamic > autofill,
254
+ {TextCapitalizationConfig textCapitalization =
255
+ const TextCapitalizationConfig .defaultCapitalization ()}) {
228
256
assert (autofill != null ); // ignore: unnecessary_null_comparison
229
257
final String uniqueIdentifier = autofill['uniqueIdentifier' ]! ;
230
258
final List <dynamic > hintsList = autofill['hints' ];
@@ -233,7 +261,8 @@ class AutofillInfo {
233
261
return AutofillInfo (
234
262
uniqueIdentifier: uniqueIdentifier,
235
263
hint: BrowserAutofillHints .instance.flutterToEngine (hintsList[0 ]),
236
- editingState: editingState);
264
+ editingState: editingState,
265
+ textCapitalization: textCapitalization);
237
266
}
238
267
239
268
void applyToDomElement (html.HtmlElement domElement,
@@ -302,7 +331,9 @@ class EditingState {
302
331
///
303
332
/// [domElement] can be a [InputElement] or a [TextAreaElement] depending on
304
333
/// the [InputType] of the text field.
305
- factory EditingState .fromDomElement (html.HtmlElement ? domElement) {
334
+ factory EditingState .fromDomElement (html.HtmlElement ? domElement,
335
+ {TextCapitalizationConfig textCapitalization =
336
+ const TextCapitalizationConfig .defaultCapitalization ()}) {
306
337
if (domElement is html.InputElement ) {
307
338
html.InputElement element = domElement;
308
339
return EditingState (
@@ -352,10 +383,10 @@ class EditingState {
352
383
if (runtimeType != other.runtimeType) {
353
384
return false ;
354
385
}
355
- return other is EditingState
356
- && other.text == text
357
- && other.baseOffset == baseOffset
358
- && other.extentOffset == extentOffset;
386
+ return other is EditingState &&
387
+ other.text == text &&
388
+ other.baseOffset == baseOffset &&
389
+ other.extentOffset == extentOffset;
359
390
}
360
391
361
392
@override
@@ -396,6 +427,7 @@ class InputConfiguration {
396
427
required this .inputAction,
397
428
required this .obscureText,
398
429
required this .autocorrect,
430
+ required this .textCapitalization,
399
431
this .autofill,
400
432
this .autofillGroup,
401
433
});
@@ -407,9 +439,12 @@ class InputConfiguration {
407
439
inputAction = flutterInputConfiguration['inputAction' ],
408
440
obscureText = flutterInputConfiguration['obscureText' ],
409
441
autocorrect = flutterInputConfiguration['autocorrect' ],
442
+ textCapitalization = TextCapitalizationConfig .fromInputConfiguration (
443
+ flutterInputConfiguration['textCapitalization' ]),
410
444
autofill = flutterInputConfiguration.containsKey ('autofill' )
411
- ? AutofillInfo .fromFrameworkMessage (flutterInputConfiguration['autofill' ])
412
- : null ,
445
+ ? AutofillInfo .fromFrameworkMessage (
446
+ flutterInputConfiguration['autofill' ])
447
+ : null ,
413
448
autofillGroup = EngineAutofillForm .fromFrameworkMessage (
414
449
flutterInputConfiguration['autofill' ],
415
450
flutterInputConfiguration['fields' ]);
@@ -435,6 +470,8 @@ class InputConfiguration {
435
470
final AutofillInfo ? autofill;
436
471
437
472
final EngineAutofillForm ? autofillGroup;
473
+
474
+ final TextCapitalizationConfig textCapitalization;
438
475
}
439
476
440
477
typedef _OnChangeCallback = void Function (EditingState ? editingState);
@@ -500,18 +537,18 @@ class GloballyPositionedTextEditingStrategy extends DefaultTextEditingStrategy {
500
537
void placeElement () {
501
538
super .placeElement ();
502
539
if (hasAutofillGroup) {
503
- _geometry? .applyToDomElement (focusedFormElement! );
504
- placeForm ();
505
- // On Chrome, when a form is focused, it opens an autofill menu
506
- // immeddiately.
507
- // Flutter framework sends `setEditableSizeAndTransform` for informing
508
- // the engine about the location of the text field. This call will
509
- // arrive after `show` call.
510
- // Therefore on Chrome we place the element when
511
- // `setEditableSizeAndTransform` method is called and focus on the form
512
- // only after placing it to the correct position. Hence autofill menu
513
- // does not appear on top-left of the page.
514
- focusedFormElement! .focus ();
540
+ _geometry? .applyToDomElement (focusedFormElement! );
541
+ placeForm ();
542
+ // On Chrome, when a form is focused, it opens an autofill menu
543
+ // immeddiately.
544
+ // Flutter framework sends `setEditableSizeAndTransform` for informing
545
+ // the engine about the location of the text field. This call will
546
+ // arrive after `show` call.
547
+ // Therefore on Chrome we place the element when
548
+ // `setEditableSizeAndTransform` method is called and focus on the form
549
+ // only after placing it to the correct position. Hence autofill menu
550
+ // does not appear on top-left of the page.
551
+ focusedFormElement! .focus ();
515
552
} else {
516
553
_geometry? .applyToDomElement (domElement);
517
554
}
@@ -551,6 +588,7 @@ abstract class DefaultTextEditingStrategy implements TextEditingStrategy {
551
588
set domElement (html.HtmlElement element) {
552
589
_domElement = element;
553
590
}
591
+
554
592
html.HtmlElement ? _domElement;
555
593
556
594
late InputConfiguration _inputConfiguration;
@@ -694,7 +732,8 @@ abstract class DefaultTextEditingStrategy implements TextEditingStrategy {
694
732
void _handleChange (html.Event event) {
695
733
assert (isEnabled);
696
734
697
- EditingState newEditingState = EditingState .fromDomElement (domElement);
735
+ EditingState newEditingState = EditingState .fromDomElement (domElement,
736
+ textCapitalization: _inputConfiguration.textCapitalization);
698
737
699
738
if (newEditingState != _lastEditingState) {
700
739
_lastEditingState = newEditingState;
@@ -818,6 +857,7 @@ class IOSTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
818
857
} else {
819
858
domRenderer.glassPaneElement! .append (domElement);
820
859
}
860
+ inputConfig.textCapitalization.setAutocapitalizeAttribute (domElement);
821
861
}
822
862
823
863
@override
@@ -948,6 +988,7 @@ class AndroidTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
948
988
} else {
949
989
domRenderer.glassPaneElement! .append (domElement);
950
990
}
991
+ inputConfig.textCapitalization.setAutocapitalizeAttribute (domElement);
951
992
}
952
993
953
994
@override
0 commit comments