From c790bb111addb91491cce8eb71d0200e45ad1a17 Mon Sep 17 00:00:00 2001 From: Yegor Date: Mon, 28 Apr 2025 09:25:03 -0700 Subject: [PATCH] [web] denull some of text_editing.dart (#166595) According to https://github.com/flutter/flutter/blob/485d6b8ae388bd16186e78c37d21d6f505d155e2/packages/flutter/lib/src/services/text_input.dart#L1086, the framework never sends null values for any of the fields. So there's no need for the engine to do all the null handling. --------- Co-authored-by: Mouad Debbar --- .../lib/web_ui/lib/src/engine/dom.dart | 4 +- .../src/engine/semantics/incrementable.dart | 2 +- .../text_editing/composition_aware_mixin.dart | 4 +- .../src/engine/text_editing/text_editing.dart | 86 +++++----- .../web_ui/test/engine/composition_test.dart | 8 +- .../engine/semantics/text_field_test.dart | 4 +- .../web_ui/test/engine/text_editing_test.dart | 151 ++++++++++++++++-- 7 files changed, 192 insertions(+), 67 deletions(-) diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/dom.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/dom.dart index 6887a9d12df..aebbda2fd02 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/dom.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/dom.dart @@ -1480,7 +1480,7 @@ extension type DomHTMLTextAreaElement._(JSObject _) implements DomHTMLElement { external double? get selectionEnd; external set selectionStart(double? value); external set selectionEnd(double? value); - external String? get value; + external String get value; @JS('setSelectionRange') external void _setSelectionRange(int start, int end, [String direction]); @@ -1855,7 +1855,7 @@ extension type DomHTMLInputElement._(JSObject _) implements DomHTMLElement { external String? type; external set max(String? value); external set min(String value); - external String? value; + external String value; external bool? disabled; external String placeholder; external String? name; diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/semantics/incrementable.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/semantics/incrementable.dart index e6034e18934..59d1cc496de 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/semantics/incrementable.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/semantics/incrementable.dart @@ -41,7 +41,7 @@ class SemanticIncrementable extends SemanticRole { return; } _pendingResync = true; - final int newInputValue = int.parse(_element.value!); + final int newInputValue = int.parse(_element.value); if (newInputValue > _currentSurrogateValue) { _currentSurrogateValue += 1; EnginePlatformDispatcher.instance.invokeOnSemanticsAction( diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/text_editing/composition_aware_mixin.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/text_editing/composition_aware_mixin.dart index a3915dfb95f..6341eebe779 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/text_editing/composition_aware_mixin.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/text_editing/composition_aware_mixin.dart @@ -74,11 +74,11 @@ mixin CompositionAwareMixin { } EditingState determineCompositionState(EditingState editingState) { - if (editingState.extentOffset == null || composingText == null || editingState.text == null) { + if (composingText == null) { return editingState; } - final int composingBase = editingState.extentOffset! - composingText!.length; + final int composingBase = editingState.extentOffset - composingText!.length; if (composingBase < 0) { return editingState; diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/text_editing/text_editing.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/text_editing/text_editing.dart index 4ca507bd96f..7e0dcd5826c 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/text_editing/text_editing.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/text_editing/text_editing.dart @@ -617,24 +617,22 @@ class TextEditingDeltaState { // If the deletion is forward, [deltaStart] is set to the new editing state baseOffset // and [deltaEnd] is set to [deltaStart] incremented by the length of the deletion. final int deletedLength = - newTextEditingDeltaState.oldText.length - newEditingState.text!.length; + newTextEditingDeltaState.oldText.length - newEditingState.text.length; final bool backwardDeletion = newEditingState.baseOffset != lastEditingState?.baseOffset; if (backwardDeletion) { newTextEditingDeltaState.deltaStart = newTextEditingDeltaState.deltaEnd - deletedLength; } else { // Forward deletion - newTextEditingDeltaState.deltaStart = newEditingState.baseOffset!; + newTextEditingDeltaState.deltaStart = newEditingState.baseOffset; newTextEditingDeltaState.deltaEnd = newTextEditingDeltaState.deltaStart + deletedLength; } } else if (isTextBeingChangedAtActiveSelection) { final bool isPreviousSelectionInverted = - lastEditingState!.baseOffset! > lastEditingState.extentOffset!; + lastEditingState!.baseOffset > lastEditingState.extentOffset; // When a selection of text is replaced by a copy/paste operation we set the starting range // of the delta to be the beginning of the selection of the previous editing state. newTextEditingDeltaState.deltaStart = - isPreviousSelectionInverted - ? lastEditingState.extentOffset! - : lastEditingState.baseOffset!; + isPreviousSelectionInverted ? lastEditingState.extentOffset : lastEditingState.baseOffset; } // If we are composing then set the delta range to the composing region we @@ -673,7 +671,7 @@ class TextEditingDeltaState { newTextEditingDeltaState.deltaText, replacementRange, ); - final bool isDeltaVerified = textAfterDelta == newEditingState.text!; + final bool isDeltaVerified = textAfterDelta == newEditingState.text; if (!isDeltaVerified) { // 1. Find all matches for deltaText. @@ -681,7 +679,7 @@ class TextEditingDeltaState { // new editing state's text value. final bool isPeriodInsertion = newTextEditingDeltaState.deltaText.contains('.'); final RegExp deltaTextPattern = RegExp(RegExp.escape(newTextEditingDeltaState.deltaText)); - for (final Match match in deltaTextPattern.allMatches(newEditingState.text!)) { + for (final Match match in deltaTextPattern.allMatches(newEditingState.text)) { String textAfterMatch; int actualEnd; final bool isMatchWithinOldTextBounds = @@ -702,7 +700,7 @@ class TextEditingDeltaState { ); } - if (textAfterMatch == newEditingState.text!) { + if (textAfterMatch == newEditingState.text) { newTextEditingDeltaState.deltaStart = match.start; newTextEditingDeltaState.deltaEnd = actualEnd; break; @@ -789,15 +787,15 @@ class TextEditingDeltaState { /// The current text and selection state of a text field. class EditingState { EditingState({ - this.text, - int? baseOffset, - int? extentOffset, + required this.text, + required int baseOffset, + required int extentOffset, this.composingBaseOffset = -1, this.composingExtentOffset = -1, }) : // Don't allow negative numbers. - baseOffset = math.max(0, baseOffset ?? 0), + baseOffset = math.max(0, baseOffset), // Don't allow negative numbers. - extentOffset = math.max(0, extentOffset ?? 0); + extentOffset = math.max(0, extentOffset); /// Creates an [EditingState] instance using values from an editing state Map /// coming from Flutter. @@ -819,20 +817,18 @@ class EditingState { /// -1, if so 0 assigned to the [baseOffset] and [extentOffset]. -1 is not a /// valid selection range for input DOM elements. factory EditingState.fromFrameworkMessage(Map flutterEditingState) { - final String? text = flutterEditingState.tryString('text'); - + final String text = flutterEditingState.readString('text'); final int selectionBase = flutterEditingState.readInt('selectionBase'); final int selectionExtent = flutterEditingState.readInt('selectionExtent'); - - final int? composingBase = flutterEditingState.tryInt('composingBase'); - final int? composingExtent = flutterEditingState.tryInt('composingExtent'); + final int composingBase = flutterEditingState.readInt('composingBase'); + final int composingExtent = flutterEditingState.readInt('composingExtent'); return EditingState( text: text, baseOffset: selectionBase, extentOffset: selectionExtent, - composingBaseOffset: composingBase ?? -1, - composingExtentOffset: composingExtent ?? -1, + composingBaseOffset: composingBase, + composingExtentOffset: composingExtent, ); } @@ -841,35 +837,39 @@ class EditingState { /// /// [domElement] can be a [InputElement] or a [TextAreaElement] depending on /// the [InputType] of the text field. - factory EditingState.fromDomElement(DomHTMLElement? domElement) { - if (domElement != null && domElement.isA()) { - final DomHTMLInputElement element = domElement as DomHTMLInputElement; + factory EditingState.fromDomElement(DomHTMLElement domElement) { + if (domElement.isA()) { + final element = domElement as DomHTMLInputElement; + final selectionEnd = element.selectionEnd?.toInt() ?? 0; + final selectionStart = element.selectionStart?.toInt() ?? 0; if (element.selectionDirection == 'backward') { return EditingState( text: element.value, - baseOffset: element.selectionEnd?.toInt(), - extentOffset: element.selectionStart?.toInt(), + baseOffset: selectionEnd, + extentOffset: selectionStart, ); } else { return EditingState( text: element.value, - baseOffset: element.selectionStart?.toInt(), - extentOffset: element.selectionEnd?.toInt(), + baseOffset: selectionStart, + extentOffset: selectionEnd, ); } - } else if (domElement != null && domElement.isA()) { - final DomHTMLTextAreaElement element = domElement as DomHTMLTextAreaElement; + } else if (domElement.isA()) { + final element = domElement as DomHTMLTextAreaElement; + final selectionEnd = element.selectionEnd?.toInt() ?? 0; + final selectionStart = element.selectionStart?.toInt() ?? 0; if (element.selectionDirection == 'backward') { return EditingState( text: element.value, - baseOffset: element.selectionEnd?.toInt(), - extentOffset: element.selectionStart?.toInt(), + baseOffset: selectionEnd, + extentOffset: selectionStart, ); } else { return EditingState( text: element.value, - baseOffset: element.selectionStart?.toInt(), - extentOffset: element.selectionEnd?.toInt(), + baseOffset: selectionStart, + extentOffset: selectionEnd, ); } } else { @@ -878,9 +878,9 @@ class EditingState { } // Pick the smallest selection index for base. - int get minOffset => math.min(baseOffset ?? 0, extentOffset ?? 0); + int get minOffset => math.min(baseOffset, extentOffset); // Pick the greatest selection index for extent. - int get maxOffset => math.max(baseOffset ?? 0, extentOffset ?? 0); + int get maxOffset => math.max(baseOffset, extentOffset); EditingState copyWith({ String? text, @@ -910,13 +910,13 @@ class EditingState { }; /// The current text being edited. - final String? text; + final String text; /// The offset at which the text selection originates. - final int? baseOffset; + final int baseOffset; /// The offset at which the text selection terminates. - final int? extentOffset; + final int extentOffset; /// The offset at which [CompositionAwareMixin.composingText] begins, if any. final int composingBaseOffset; @@ -925,7 +925,7 @@ class EditingState { final int composingExtentOffset; /// Whether the current editing state is valid or not. - bool get isValid => baseOffset! >= 0 && extentOffset! >= 0; + bool get isValid => baseOffset >= 0 && extentOffset >= 0; @override int get hashCode => @@ -1275,7 +1275,7 @@ abstract class DefaultTextEditingStrategy TextEditingDeltaState? _editingDeltaState; TextEditingDeltaState get editingDeltaState { - _editingDeltaState ??= TextEditingDeltaState(oldText: lastEditingState!.text!); + _editingDeltaState ??= TextEditingDeltaState(oldText: lastEditingState!.text); return _editingDeltaState!; } @@ -1516,9 +1516,9 @@ abstract class DefaultTextEditingStrategy if (inputType != null) { final bool isSelectionInverted = - lastEditingState!.baseOffset! > lastEditingState!.extentOffset!; + lastEditingState!.baseOffset > lastEditingState!.extentOffset; final int deltaOffset = - isSelectionInverted ? lastEditingState!.baseOffset! : lastEditingState!.extentOffset!; + isSelectionInverted ? lastEditingState!.baseOffset : lastEditingState!.extentOffset; if (inputType.contains('delete')) { // The deltaStart is set in handleChange because there is where we get access // to the new selection baseOffset which is our new deltaStart. diff --git a/engine/src/flutter/lib/web_ui/test/engine/composition_test.dart b/engine/src/flutter/lib/web_ui/test/engine/composition_test.dart index e35070a8de9..c75e34d072f 100644 --- a/engine/src/flutter/lib/web_ui/test/engine/composition_test.dart +++ b/engine/src/flutter/lib/web_ui/test/engine/composition_test.dart @@ -114,7 +114,11 @@ Future testMain() async { group('determine composition state', () { test('should return editing state if extentOffset is null', () { - final EditingState editingState = EditingState(text: 'Test'); + final EditingState editingState = EditingState( + text: 'Test', + baseOffset: 0, + extentOffset: 0, + ); final _MockWithCompositionAwareMixin mockWithCompositionAwareMixin = _MockWithCompositionAwareMixin(); @@ -137,7 +141,7 @@ Future testMain() async { }); test('should return editing state if text is null', () { - final EditingState editingState = EditingState(baseOffset: 0, extentOffset: 0); + final EditingState editingState = EditingState(text: '', baseOffset: 0, extentOffset: 0); final _MockWithCompositionAwareMixin mockWithCompositionAwareMixin = _MockWithCompositionAwareMixin(); diff --git a/engine/src/flutter/lib/web_ui/test/engine/semantics/text_field_test.dart b/engine/src/flutter/lib/web_ui/test/engine/semantics/text_field_test.dart index 7e72f0d7e05..11c8557a2af 100644 --- a/engine/src/flutter/lib/web_ui/test/engine/semantics/text_field_test.dart +++ b/engine/src/flutter/lib/web_ui/test/engine/semantics/text_field_test.dart @@ -283,6 +283,8 @@ void testMain() { 'text': 'updated', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState), testTextEditing); @@ -351,7 +353,7 @@ void testMain() { expect(owner().semanticsHost.ownerDocument?.activeElement, domDocument.body); // The input will have focus after editing state is set and semantics updated. - strategy.setEditingState(EditingState(text: 'foo')); + strategy.setEditingState(EditingState(text: 'foo', baseOffset: 0, extentOffset: 0)); // NOTE: at this point some browsers, e.g. some versions of Safari will // have set the focus on the editing element as a result of setting diff --git a/engine/src/flutter/lib/web_ui/test/engine/text_editing_test.dart b/engine/src/flutter/lib/web_ui/test/engine/text_editing_test.dart index aa9cd5ec92a..0f13c1b6fed 100644 --- a/engine/src/flutter/lib/web_ui/test/engine/text_editing_test.dart +++ b/engine/src/flutter/lib/web_ui/test/engine/text_editing_test.dart @@ -813,6 +813,8 @@ Future testMain() async { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); @@ -844,6 +846,8 @@ Future testMain() async { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); @@ -914,7 +918,13 @@ Future testMain() async { const MethodCall setEditingState = MethodCall( 'TextInput.setEditingState', - {'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3}, + { + 'text': 'abcd', + 'selectionBase': 2, + 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, + }, ); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); @@ -937,6 +947,8 @@ Future testMain() async { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); @@ -973,6 +985,8 @@ Future testMain() async { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); @@ -1026,6 +1040,8 @@ Future testMain() async { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); @@ -1071,7 +1087,13 @@ Future testMain() async { const MethodCall setEditingState = MethodCall( 'TextInput.setEditingState', - {'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3}, + { + 'text': 'abcd', + 'selectionBase': 2, + 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, + }, ); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); @@ -1124,6 +1146,8 @@ Future testMain() async { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); @@ -1177,6 +1201,8 @@ Future testMain() async { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState1)); @@ -1229,6 +1255,8 @@ Future testMain() async { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState1)); @@ -1281,6 +1309,8 @@ Future testMain() async { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState1)); @@ -1340,6 +1370,8 @@ Future testMain() async { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( 150, @@ -1386,6 +1418,8 @@ Future testMain() async { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); @@ -1443,6 +1477,8 @@ Future testMain() async { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState1)); @@ -1464,6 +1500,8 @@ Future testMain() async { 'text': 'xyz', 'selectionBase': 0, 'selectionExtent': 2, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState2)); @@ -1494,6 +1532,8 @@ Future testMain() async { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState1)); @@ -1546,6 +1586,8 @@ Future testMain() async { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState1)); @@ -1619,6 +1661,8 @@ Future testMain() async { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState1)); @@ -1667,6 +1711,8 @@ Future testMain() async { 'text': '', 'selectionBase': 0, 'selectionExtent': 0, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState1)); @@ -1703,6 +1749,8 @@ Future testMain() async { 'text': '', 'selectionBase': 0, 'selectionExtent': 0, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState1)); @@ -1736,6 +1784,8 @@ Future testMain() async { 'text': '', 'selectionBase': 0, 'selectionExtent': 0, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState1)); @@ -1774,7 +1824,13 @@ Future testMain() async { const MethodCall setEditingState = MethodCall( 'TextInput.setEditingState', - {'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3}, + { + 'text': 'abcd', + 'selectionBase': 2, + 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, + }, ); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); @@ -1835,7 +1891,13 @@ Future testMain() async { const MethodCall setEditingState = MethodCall( 'TextInput.setEditingState', - {'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3}, + { + 'text': 'abcd', + 'selectionBase': 2, + 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, + }, ); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); @@ -1903,7 +1965,13 @@ Future testMain() async { const MethodCall setEditingState = MethodCall( 'TextInput.setEditingState', - {'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3}, + { + 'text': 'abcd', + 'selectionBase': 2, + 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, + }, ); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); @@ -1965,6 +2033,8 @@ Future testMain() async { 'text': 'xyz', 'selectionBase': 1, 'selectionExtent': 2, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState1)); @@ -1989,6 +2059,8 @@ Future testMain() async { 'text': 'xyz', 'selectionBase': -1, 'selectionExtent': -1, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState2)); @@ -2009,6 +2081,8 @@ Future testMain() async { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); @@ -2072,6 +2146,8 @@ Future testMain() async { 'text': '', 'selectionBase': -1, 'selectionExtent': -1, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); @@ -2129,6 +2205,8 @@ Future testMain() async { 'text': 'Hello world', 'selectionBase': 9, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); @@ -2194,6 +2272,8 @@ Future testMain() async { 'text': 'Hello world', 'selectionBase': 9, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); @@ -2275,6 +2355,8 @@ Future testMain() async { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState1)); @@ -2369,8 +2451,8 @@ Future testMain() async { 'text': 'foo\nbar', 'selectionBase': 2, 'selectionExtent': 3, - 'composingBase': null, - 'composingExtent': null, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); checkTextAreaEditingState(textarea, 'foo\nbar', 2, 3); @@ -2598,8 +2680,8 @@ Future testMain() async { 'text': '1\n2\n3\n4\n', 'selectionBase': 8, 'selectionExtent': 8, - 'composingBase': null, - 'composingExtent': null, + 'composingBase': -1, + 'composingExtent': -1, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); checkTextAreaEditingState(textarea, '1\n2\n3\n4\n', 8, 8); @@ -3292,18 +3374,24 @@ Future testMain() async { test('Fix flipped base and extent offsets', () { expect( - EditingState(baseOffset: 10, extentOffset: 4), - EditingState(baseOffset: 4, extentOffset: 10), + EditingState(text: '', baseOffset: 10, extentOffset: 4), + EditingState(text: '', baseOffset: 4, extentOffset: 10), ); expect( EditingState.fromFrameworkMessage({ + 'text': '', 'selectionBase': 10, 'selectionExtent': 4, + 'composingBase': -1, + 'composingExtent': -1, }), EditingState.fromFrameworkMessage({ + 'text': '', 'selectionBase': 4, 'selectionExtent': 10, + 'composingBase': -1, + 'composingExtent': -1, }), ); }); @@ -3311,7 +3399,13 @@ Future testMain() async { test('Sets default composing offsets if none given', () { final EditingState editingState = EditingState(text: 'Test', baseOffset: 2, extentOffset: 4); final EditingState editingStateFromFrameworkMsg = EditingState.fromFrameworkMessage( - {'selectionBase': 10, 'selectionExtent': 4}, + { + 'text': '', + 'selectionBase': 10, + 'selectionExtent': 4, + 'composingBase': -1, + 'composingExtent': -1, + }, ); expect(editingState.composingBaseOffset, -1); @@ -3322,8 +3416,16 @@ Future testMain() async { }); test('Correctly identifies min and max offsets', () { - final EditingState flippedEditingState = EditingState(baseOffset: 10, extentOffset: 4); - final EditingState normalEditingState = EditingState(baseOffset: 2, extentOffset: 6); + final EditingState flippedEditingState = EditingState( + text: '', + baseOffset: 10, + extentOffset: 4, + ); + final EditingState normalEditingState = EditingState( + text: '', + baseOffset: 2, + extentOffset: 6, + ); expect(flippedEditingState.minOffset, 4); expect(flippedEditingState.maxOffset, 10); @@ -3435,8 +3537,16 @@ Future testMain() async { }); test('Takes flipped base and extent offsets into account', () { - final EditingState flippedEditingState = EditingState(baseOffset: 10, extentOffset: 4); - final EditingState normalEditingState = EditingState(baseOffset: 4, extentOffset: 10); + final EditingState flippedEditingState = EditingState( + text: '', + baseOffset: 10, + extentOffset: 4, + ); + final EditingState normalEditingState = EditingState( + text: '', + baseOffset: 4, + extentOffset: 10, + ); expect(normalEditingState, flippedEditingState); @@ -3445,14 +3555,23 @@ Future testMain() async { test('takes composition range into account', () { final EditingState editingState1 = EditingState( + text: '', + baseOffset: 0, + extentOffset: 0, composingBaseOffset: 1, composingExtentOffset: 2, ); final EditingState editingState2 = EditingState( + text: '', + baseOffset: 0, + extentOffset: 0, composingBaseOffset: 1, composingExtentOffset: 2, ); final EditingState editingState3 = EditingState( + text: '', + baseOffset: 0, + extentOffset: 0, composingBaseOffset: 4, composingExtentOffset: 8, );