diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index 0664940544a9d..2ddfd93ab10fb 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -117,6 +117,8 @@ source_set("flutter_framework_source_arc") { "ios_surface_metal_skia.mm", "ios_surface_software.h", "ios_surface_software.mm", + "platform_message_handler_ios.h", + "platform_message_handler_ios.mm", "rendering_api_selection.h", "rendering_api_selection.mm", ] @@ -180,8 +182,6 @@ source_set("flutter_framework_source") { "framework/Source/accessibility_text_entry.mm", "ios_external_view_embedder.h", "ios_external_view_embedder.mm", - "platform_message_handler_ios.h", - "platform_message_handler_ios.mm", "platform_view_ios.h", "platform_view_ios.mm", ] diff --git a/shell/platform/darwin/ios/platform_message_handler_ios.h b/shell/platform/darwin/ios/platform_message_handler_ios.h index 6d49ae506f40f..b212e22230728 100644 --- a/shell/platform/darwin/ios/platform_message_handler_ios.h +++ b/shell/platform/darwin/ios/platform_message_handler_ios.h @@ -5,13 +5,10 @@ #ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_PLATFORM_MESSAGE_HANDLER_IOS_H_ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_PLATFORM_MESSAGE_HANDLER_IOS_H_ -#include - -#include "flutter/common/task_runners.h" #include "flutter/fml/platform/darwin/scoped_block.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/fml/task_runner.h" #include "flutter/shell/common/platform_message_handler.h" -#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" #import "flutter/shell/platform/darwin/ios/flutter_task_queue_dispatch.h" namespace flutter { diff --git a/shell/platform/darwin/ios/platform_message_handler_ios.mm b/shell/platform/darwin/ios/platform_message_handler_ios.mm index 38706459e00c3..e1257b56ffb40 100644 --- a/shell/platform/darwin/ios/platform_message_handler_ios.mm +++ b/shell/platform/darwin/ios/platform_message_handler_ios.mm @@ -4,15 +4,17 @@ #import "flutter/shell/platform/darwin/ios/platform_message_handler_ios.h" -#import "flutter/fml/trace_event.h" -#import "flutter/lib/ui/window/platform_message.h" -#import "flutter/shell/platform/darwin/common/buffer_conversions.h" -#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" +#include "flutter/fml/trace_event.h" +#include "flutter/lib/ui/window/platform_message.h" +#include "flutter/lib/ui/window/platform_message_response.h" +#include "flutter/shell/platform/darwin/common/buffer_conversions.h" + +FLUTTER_ASSERT_ARC static uint64_t platform_message_counter = 1; @interface FLTSerialTaskQueue : NSObject -@property(nonatomic, strong) dispatch_queue_t queue; +@property(nonatomic, readonly) dispatch_queue_t queue; @end @implementation FLTSerialTaskQueue @@ -24,11 +26,6 @@ - (instancetype)init { return self; } -- (void)dealloc { - dispatch_release(_queue); - [super dealloc]; -} - - (void)dispatch:(dispatch_block_t)block { dispatch_async(self.queue, block); } @@ -37,7 +34,7 @@ - (void)dispatch:(dispatch_block_t)block { namespace flutter { NSObject* PlatformMessageHandlerIos::MakeBackgroundTaskQueue() { - return [[[FLTSerialTaskQueue alloc] init] autorelease]; + return [[FLTSerialTaskQueue alloc] init]; } PlatformMessageHandlerIos::PlatformMessageHandlerIos( @@ -127,8 +124,8 @@ - (void)dispatch:(dispatch_block_t)block { message_handlers_.erase(channel); if (handler) { message_handlers_[channel] = { - .task_queue = fml::scoped_nsprotocol( - [static_cast*>(task_queue) retain]), + .task_queue = + fml::scoped_nsprotocol(static_cast*>(task_queue)), .handler = fml::ScopedBlock{ handler, fml::scoped_policy::OwnershipPolicy::kRetain}, diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm index 33adfa909dc65..7f884943601eb 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm @@ -777,6 +777,10 @@ - (void)doCommandBySelector:(SEL)selector { void (*func)(id, SEL, id) = reinterpret_cast(imp); func(self, selector, nil); } + if (self.clientID == nil) { + // The macOS may still call selector even if it is no longer a first responder. + return; + } if (selector == @selector(insertNewline:)) { // Already handled through text insertion (multiline) or action. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm index e3a203803d489..b3b4a8a096824 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm @@ -1801,6 +1801,42 @@ - (bool)testSelectorsAreForwardedToFramework { return true; } +- (bool)testSelectorsNotForwardedToFrameworkIfNoClient { + id engineMock = flutter::testing::CreateMockFlutterEngine(@""); + id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); + OCMStub( // NOLINT(google-objc-avoid-throwing-exception) + [engineMock binaryMessenger]) + .andReturn(binaryMessengerMock); + // Make sure the selectors are not forwarded to the framework. + OCMReject([binaryMessengerMock sendOnChannel:@"flutter/textinput" message:[OCMArg any]]); + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock + nibName:@"" + bundle:nil]; + + FlutterTextInputPlugin* plugin = + [[FlutterTextInputPlugin alloc] initWithViewController:viewController]; + + // Can't run CFRunLoop in default mode because it causes crashes from scheduled + // sources from other tests. + NSString* runLoopMode = @"FlutterTestRunLoopMode"; + plugin.customRunLoopMode = runLoopMode; + + // Call selectors without setting a client. + [plugin doCommandBySelector:@selector(moveUp:)]; + [plugin doCommandBySelector:@selector(moveRightAndModifySelection:)]; + + __block bool done = false; + CFRunLoopPerformBlock(CFRunLoopGetMain(), (__bridge CFStringRef)runLoopMode, ^{ + done = true; + }); + + while (!done) { + CFRunLoopRunInMode((__bridge CFStringRef)runLoopMode, 0, true); + } + // At this point the selectors should be dropped; otherwise, OCMReject will throw. + return true; +} + @end namespace flutter::testing { @@ -1886,7 +1922,7 @@ - (bool)testSelectorsAreForwardedToFramework { ASSERT_TRUE([[FlutterInputPluginTestObjc alloc] testComposingWithDelta]); } -TEST(FlutterTextInputPluginTest, testComposingWithDeltasWhenSelectionIsActive) { +TEST(FlutterTextInputPluginTest, TestComposingWithDeltasWhenSelectionIsActive) { ASSERT_TRUE([[FlutterInputPluginTestObjc alloc] testComposingWithDeltasWhenSelectionIsActive]); } @@ -1910,6 +1946,10 @@ - (bool)testSelectorsAreForwardedToFramework { ASSERT_TRUE([[FlutterInputPluginTestObjc alloc] testSelectorsAreForwardedToFramework]); } +TEST(FlutterTextInputPluginTest, TestSelectorsNotForwardedToFrameworkIfNoClient) { + ASSERT_TRUE([[FlutterInputPluginTestObjc alloc] testSelectorsNotForwardedToFrameworkIfNoClient]); +} + TEST(FlutterTextInputPluginTest, TestInsertNewLine) { ASSERT_TRUE([[FlutterInputPluginTestObjc alloc] testInsertNewLine]); }