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

Commit cfa3f34

Browse files
authored
macOS fluttertextinputplugin drops selector called if no client (#52495)
The observed behavior is that if a keypress both reach framework and flutter textinputplugin selector and the keypress handler in framework causes the the textinputplugin to resign first responder, the selector will still go through even if the textinputplugin has already resigned. The pr makes it so textinputplugin will ignore these selector call fixes flutter/flutter#143270 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent c536a14 commit cfa3f34

File tree

2 files changed

+45
-1
lines changed

2 files changed

+45
-1
lines changed

shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,10 @@ - (void)doCommandBySelector:(SEL)selector {
777777
void (*func)(id, SEL, id) = reinterpret_cast<void (*)(id, SEL, id)>(imp);
778778
func(self, selector, nil);
779779
}
780+
if (self.clientID == nil) {
781+
// The macOS may still call selector even if it is no longer a first responder.
782+
return;
783+
}
780784

781785
if (selector == @selector(insertNewline:)) {
782786
// Already handled through text insertion (multiline) or action.

shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1801,6 +1801,42 @@ - (bool)testSelectorsAreForwardedToFramework {
18011801
return true;
18021802
}
18031803

1804+
- (bool)testSelectorsNotForwardedToFrameworkIfNoClient {
1805+
id engineMock = flutter::testing::CreateMockFlutterEngine(@"");
1806+
id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
1807+
OCMStub( // NOLINT(google-objc-avoid-throwing-exception)
1808+
[engineMock binaryMessenger])
1809+
.andReturn(binaryMessengerMock);
1810+
// Make sure the selectors are not forwarded to the framework.
1811+
OCMReject([binaryMessengerMock sendOnChannel:@"flutter/textinput" message:[OCMArg any]]);
1812+
FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock
1813+
nibName:@""
1814+
bundle:nil];
1815+
1816+
FlutterTextInputPlugin* plugin =
1817+
[[FlutterTextInputPlugin alloc] initWithViewController:viewController];
1818+
1819+
// Can't run CFRunLoop in default mode because it causes crashes from scheduled
1820+
// sources from other tests.
1821+
NSString* runLoopMode = @"FlutterTestRunLoopMode";
1822+
plugin.customRunLoopMode = runLoopMode;
1823+
1824+
// Call selectors without setting a client.
1825+
[plugin doCommandBySelector:@selector(moveUp:)];
1826+
[plugin doCommandBySelector:@selector(moveRightAndModifySelection:)];
1827+
1828+
__block bool done = false;
1829+
CFRunLoopPerformBlock(CFRunLoopGetMain(), (__bridge CFStringRef)runLoopMode, ^{
1830+
done = true;
1831+
});
1832+
1833+
while (!done) {
1834+
CFRunLoopRunInMode((__bridge CFStringRef)runLoopMode, 0, true);
1835+
}
1836+
// At this point the selectors should be dropped; otherwise, OCMReject will throw.
1837+
return true;
1838+
}
1839+
18041840
@end
18051841

18061842
namespace flutter::testing {
@@ -1886,7 +1922,7 @@ - (bool)testSelectorsAreForwardedToFramework {
18861922
ASSERT_TRUE([[FlutterInputPluginTestObjc alloc] testComposingWithDelta]);
18871923
}
18881924

1889-
TEST(FlutterTextInputPluginTest, testComposingWithDeltasWhenSelectionIsActive) {
1925+
TEST(FlutterTextInputPluginTest, TestComposingWithDeltasWhenSelectionIsActive) {
18901926
ASSERT_TRUE([[FlutterInputPluginTestObjc alloc] testComposingWithDeltasWhenSelectionIsActive]);
18911927
}
18921928

@@ -1910,6 +1946,10 @@ - (bool)testSelectorsAreForwardedToFramework {
19101946
ASSERT_TRUE([[FlutterInputPluginTestObjc alloc] testSelectorsAreForwardedToFramework]);
19111947
}
19121948

1949+
TEST(FlutterTextInputPluginTest, TestSelectorsNotForwardedToFrameworkIfNoClient) {
1950+
ASSERT_TRUE([[FlutterInputPluginTestObjc alloc] testSelectorsNotForwardedToFrameworkIfNoClient]);
1951+
}
1952+
19131953
TEST(FlutterTextInputPluginTest, TestInsertNewLine) {
19141954
ASSERT_TRUE([[FlutterInputPluginTestObjc alloc] testInsertNewLine]);
19151955
}

0 commit comments

Comments
 (0)