From ea95b85e6377df4a7a87b49c19e16015cd1104eb Mon Sep 17 00:00:00 2001 From: Renzo Olivares Date: Fri, 26 May 2023 14:07:02 -0700 Subject: [PATCH] [macOS] Do not bounce editing state back to framework on setEditState (flutter/engine#42091) Some context for this change https://github.com/flutter/flutter/issues/118642#issuecomment-1550864240 and https://github.com/flutter/flutter/issues/118759#issuecomment-1551906489 . Tldr, the use-case that this bouncing was originally introduced for in #13702 is no longer an issue. This ack response is also not present on other desktop platforms or iOS. Fixes [#118759](https://github.com/flutter/flutter/issues/118759) and [#118642](https://github.com/flutter/flutter/issues/118642) --- .../Source/FlutterTextInputPlugin.mm | 13 -- .../Source/FlutterTextInputPluginTest.mm | 152 ++++++------------ 2 files changed, 45 insertions(+), 120 deletions(-) diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm index 78a65b73e1c..e249cd5601b 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm @@ -448,19 +448,6 @@ static char markerKey; } else if ([method isEqualToString:kSetEditingStateMethod]) { NSDictionary* state = call.arguments; [self setEditingState:state]; - - // Close the loop, since the framework state could have been updated by the - // engine since it sent this update, and needs to now be made to match the - // engine's version of the state. - if (!_enableDeltaModel) { - [self updateEditState]; - } else { - // Send an "empty" delta. The client can compare the old_text with their - // current text and update with that if the race condition described above - // occurs. - [self updateEditStateWithDelta:flutter::TextEditingDelta(_activeModel->GetText().c_str(), - flutter::TextRange(0, 0), "")]; - } } else if ([method isEqualToString:kSetEditableSizeAndTransform]) { NSDictionary* state = call.arguments; [self setEditableTransform:state[kTransformKey]]; diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm index d5d31b5115f..00bbb68dc69 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm @@ -87,34 +87,19 @@ @"composingExtent" : @(-1), }]; - NSDictionary* expectedState = @{ - @"selectionBase" : @(0), - @"selectionExtent" : @(0), - @"selectionAffinity" : @"TextAffinity.upstream", - @"selectionIsDirectional" : @(NO), - @"composingBase" : @(-1), - @"composingExtent" : @(-1), - @"text" : @"Text", - }; - - NSData* updateCall = [[FlutterJSONMethodCodec sharedInstance] - encodeMethodCall:[FlutterMethodCall - methodCallWithMethodName:@"TextInputClient.updateEditingState" - arguments:@[ @(1), expectedState ]]]; - - OCMExpect( // NOLINT(google-objc-avoid-throwing-exception) - [binaryMessengerMock sendOnChannel:@"flutter/textinput" message:updateCall]); - [plugin handleMethodCall:call result:^(id){ }]; - @try { - OCMVerify( // NOLINT(google-objc-avoid-throwing-exception) - [binaryMessengerMock sendOnChannel:@"flutter/textinput" message:updateCall]); - } @catch (...) { - return false; - } + // Verify editing state was set. + NSDictionary* editingState = [plugin editingState]; + EXPECT_STREQ([editingState[@"text"] UTF8String], "Text"); + EXPECT_STREQ([editingState[@"selectionAffinity"] UTF8String], "TextAffinity.upstream"); + EXPECT_FALSE([editingState[@"selectionIsDirectional"] boolValue]); + EXPECT_EQ([editingState[@"selectionBase"] intValue], 0); + EXPECT_EQ([editingState[@"selectionExtent"] intValue], 0); + EXPECT_EQ([editingState[@"composingBase"] intValue], -1); + EXPECT_EQ([editingState[@"composingExtent"] intValue], -1); return true; } @@ -298,30 +283,15 @@ result:^(id){ }]; - NSDictionary* expectedState = @{ - @"selectionBase" : @(2), - @"selectionExtent" : @(2), - @"selectionAffinity" : @"TextAffinity.upstream", - @"selectionIsDirectional" : @(NO), - @"composingBase" : @(-1), - @"composingExtent" : @(-1), - @"text" : @"Te", - }; - - NSData* updateCall = [[FlutterJSONMethodCodec sharedInstance] - encodeMethodCall:[FlutterMethodCall - methodCallWithMethodName:@"TextInputClient.updateEditingState" - arguments:@[ @(1), expectedState ]]]; - - OCMExpect( // NOLINT(google-objc-avoid-throwing-exception) - [binaryMessengerMock sendOnChannel:@"flutter/textinput" message:updateCall]); - - @try { - OCMVerify( // NOLINT(google-objc-avoid-throwing-exception) - [binaryMessengerMock sendOnChannel:@"flutter/textinput" message:updateCall]); - } @catch (...) { - return false; - } + // Verify editing state was set. + NSDictionary* editingState = [plugin editingState]; + EXPECT_STREQ([editingState[@"text"] UTF8String], "Te"); + EXPECT_STREQ([editingState[@"selectionAffinity"] UTF8String], "TextAffinity.upstream"); + EXPECT_FALSE([editingState[@"selectionIsDirectional"] boolValue]); + EXPECT_EQ([editingState[@"selectionBase"] intValue], 2); + EXPECT_EQ([editingState[@"selectionExtent"] intValue], 2); + EXPECT_EQ([editingState[@"composingBase"] intValue], -1); + EXPECT_EQ([editingState[@"composingExtent"] intValue], -1); return true; } @@ -902,20 +872,15 @@ result:^(id){ }]; - // The setEditingState call is ACKed back to the framework. - @try { - OCMVerify( // NOLINT(google-objc-avoid-throwing-exception) - [binaryMessengerMock - sendOnChannel:@"flutter/textinput" - message:[OCMArg checkWithBlock:^BOOL(NSData* callData) { - FlutterMethodCall* call = - [[FlutterJSONMethodCodec sharedInstance] decodeMethodCall:callData]; - return [[call method] - isEqualToString:@"TextInputClient.updateEditingStateWithDeltas"]; - }]]); - } @catch (...) { - return false; - } + // Verify editing state was set. + NSDictionary* editingState = [plugin editingState]; + EXPECT_STREQ([editingState[@"text"] UTF8String], "Text"); + EXPECT_STREQ([editingState[@"selectionAffinity"] UTF8String], "TextAffinity.upstream"); + EXPECT_FALSE([editingState[@"selectionIsDirectional"] boolValue]); + EXPECT_EQ([editingState[@"selectionBase"] intValue], 0); + EXPECT_EQ([editingState[@"selectionExtent"] intValue], 0); + EXPECT_EQ([editingState[@"composingBase"] intValue], -1); + EXPECT_EQ([editingState[@"composingExtent"] intValue], -1); return true; } @@ -1505,58 +1470,31 @@ @"composingExtent" : @(-1), }]; - NSDictionary* expectedState = @{ - @"selectionBase" : @(4), - @"selectionExtent" : @(4), - @"selectionAffinity" : @"TextAffinity.upstream", - @"selectionIsDirectional" : @(NO), - @"composingBase" : @(-1), - @"composingExtent" : @(-1), - @"text" : @"Text", - }; - - NSData* updateCall = [[FlutterJSONMethodCodec sharedInstance] - encodeMethodCall:[FlutterMethodCall - methodCallWithMethodName:@"TextInputClient.updateEditingState" - arguments:@[ @(1), expectedState ]]]; - - OCMExpect( // NOLINT(google-objc-avoid-throwing-exception) - [binaryMessengerMock sendOnChannel:@"flutter/textinput" message:updateCall]); - [plugin handleMethodCall:call result:^(id){ }]; - @try { - OCMVerify( // NOLINT(google-objc-avoid-throwing-exception) - [binaryMessengerMock sendOnChannel:@"flutter/textinput" message:updateCall]); - } @catch (...) { - return false; - } + // Verify editing state was set. + NSDictionary* editingState = [plugin editingState]; + EXPECT_STREQ([editingState[@"text"] UTF8String], "Text"); + EXPECT_STREQ([editingState[@"selectionAffinity"] UTF8String], "TextAffinity.upstream"); + EXPECT_FALSE([editingState[@"selectionIsDirectional"] boolValue]); + EXPECT_EQ([editingState[@"selectionBase"] intValue], 4); + EXPECT_EQ([editingState[@"selectionExtent"] intValue], 4); + EXPECT_EQ([editingState[@"composingBase"] intValue], -1); + EXPECT_EQ([editingState[@"composingExtent"] intValue], -1); [plugin doCommandBySelector:@selector(insertNewline:)]; - NSDictionary* updatedState = @{ - @"selectionBase" : @(5), - @"selectionExtent" : @(5), - @"selectionAffinity" : @"TextAffinity.upstream", - @"selectionIsDirectional" : @(NO), - @"composingBase" : @(-1), - @"composingExtent" : @(-1), - @"text" : @"Text\n", - }; - - updateCall = [[FlutterJSONMethodCodec sharedInstance] - encodeMethodCall:[FlutterMethodCall - methodCallWithMethodName:@"TextInputClient.updateEditingState" - arguments:@[ @(1), updatedState ]]]; - - @try { - OCMVerify( // NOLINT(google-objc-avoid-throwing-exception) - [binaryMessengerMock sendOnChannel:@"flutter/textinput" message:updateCall]); - } @catch (...) { - return false; - } + // Verify editing state was set. + editingState = [plugin editingState]; + EXPECT_STREQ([editingState[@"text"] UTF8String], "Text\n"); + EXPECT_STREQ([editingState[@"selectionAffinity"] UTF8String], "TextAffinity.upstream"); + EXPECT_FALSE([editingState[@"selectionIsDirectional"] boolValue]); + EXPECT_EQ([editingState[@"selectionBase"] intValue], 5); + EXPECT_EQ([editingState[@"selectionExtent"] intValue], 5); + EXPECT_EQ([editingState[@"composingBase"] intValue], -1); + EXPECT_EQ([editingState[@"composingExtent"] intValue], -1); return true; }