From 66de2308ce7aa1037979e81cd4ce668a34b5b336 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 9 Mar 2022 03:16:00 -0800 Subject: [PATCH 01/11] First impl with 1 test passing --- .../framework/Source/FlutterKeyboardManager.h | 39 +- .../Source/FlutterKeyboardManager.mm | 73 ++- .../Source/FlutterKeyboardManagerUnittests.mm | 418 +++++++++++------- .../framework/Source/FlutterViewController.mm | 24 +- 4 files changed, 321 insertions(+), 233 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h index fc83771d2dd66..3ca7471ec8cb5 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h @@ -2,12 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h" - #import -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeySecondaryResponder.h" +#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h" + +namespace { +// Someohow this pointer type must be defined as a single type for the compiler +// to compile the function pointer type (due to _Nullable). +typedef NSResponder* _NSResponderPtr; +} + +typedef _Nullable _NSResponderPtr (^NextResponderProvider)(); /** * A hub that manages how key events are dispatched to various Flutter key @@ -39,27 +45,16 @@ */ @interface FlutterKeyboardManager : NSObject +// TODO /** - * Create a manager by specifying the owner. + * Create a manager by specifying a weak pointer to the owner view controller. * - * The owner should be an object that handles the lifecycle of this instance. - * The |owner.nextResponder| can be nil, but if it isn't, it will be where the - * key events are propagated to if no responders handle the event. The owner - * is typically a |FlutterViewController|. - */ -- (nonnull instancetype)initWithOwner:(nonnull NSResponder*)weakOwner; - -/** - * Add a primary responder, which asynchronously decides whether to handle an - * event. - */ -- (void)addPrimaryResponder:(nonnull id)responder; - -/** - * Add a secondary responder, which synchronously decides whether to handle an - * event in order if no earlier responders handle. + * The |viewController.nextResponder| can be nil, but if it isn't, it will be where the + * key events are propagated to if no responders handle the event. */ -- (void)addSecondaryResponder:(nonnull id)responder; +- (nonnull instancetype)initWithEngine:(nonnull FlutterEngine*)engine + textInputPlugin:(nonnull FlutterTextInputPlugin*)textInputPlugin + getNextResponder:(nonnull NextResponderProvider)getNextResponder; /** * Dispatch a key event to all responders, and possibly the next |NSResponder| diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm index 63e6487368270..679486f7dc53b 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm @@ -4,12 +4,17 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponder.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h" + @interface FlutterKeyboardManager () /** - * The owner set by initWithOwner. + * The text input plugin set by initialization. */ -@property(nonatomic, weak) NSResponder* owner; +@property(nonatomic) FlutterTextInputPlugin* textInputPlugin; /** * The primary responders added by addPrimaryResponder. @@ -17,22 +22,44 @@ @interface FlutterKeyboardManager () @property(nonatomic) NSMutableArray>* primaryResponders; /** - * The secondary responders added by addSecondaryResponder. + * Add a primary responder, which asynchronously decides whether to handle an + * event. */ -@property(nonatomic) NSMutableArray>* secondaryResponders; +- (void)addPrimaryResponder:(nonnull id)responder; - (void)dispatchToSecondaryResponders:(NSEvent*)event; @end -@implementation FlutterKeyboardManager +@implementation FlutterKeyboardManager { + NextResponderProvider _getNextResponder; +} -- (nonnull instancetype)initWithOwner:(NSResponder*)weakOwner { +- (nonnull instancetype)initWithEngine:(nonnull FlutterEngine*)engine + textInputPlugin:(nonnull FlutterTextInputPlugin*)textInputPlugin + getNextResponder:(nonnull NextResponderProvider)getNextResponder { self = [super init]; if (self != nil) { - _owner = weakOwner; + _getNextResponder = getNextResponder; + _textInputPlugin = textInputPlugin; + _primaryResponders = [[NSMutableArray alloc] init]; - _secondaryResponders = [[NSMutableArray alloc] init]; + __weak FlutterEngine* weakEngine = engine; + [self addPrimaryResponder:[[FlutterEmbedderKeyResponder alloc] + initWithSendEvent:^(const FlutterKeyEvent& event, + FlutterKeyEventCallback callback, + void* userData) { + [weakEngine sendKeyEvent:event + callback:callback + userData:userData]; + }]]; + [self + addPrimaryResponder:[[FlutterChannelKeyResponder alloc] + initWithChannel:[FlutterBasicMessageChannel + messageChannelWithName:@"flutter/keyevent" + binaryMessenger:engine.binaryMessenger + codec:[FlutterJSONMessageCodec + sharedInstance]]]]; } return self; } @@ -41,10 +68,6 @@ - (void)addPrimaryResponder:(nonnull id)responder { [_primaryResponders addObject:responder]; } -- (void)addSecondaryResponder:(nonnull id)responder { - [_secondaryResponders addObject:responder]; -} - - (void)handleEvent:(nonnull NSEvent*)event { // Be sure to add a handling method in propagateKeyEvent when allowing more // event types here. @@ -74,28 +97,34 @@ - (void)handleEvent:(nonnull NSEvent*)event { } } +- (BOOL)isFirstResponder { + return [self.textInputPlugin isFirstResponder]; +} + #pragma mark - Private - (void)dispatchToSecondaryResponders:(NSEvent*)event { - for (id responder in _secondaryResponders) { - if ([responder handleKeyEvent:event]) { - return; - } + if ([_textInputPlugin handleKeyEvent:event]) { + return; + } + NSResponder* nextResponder = _getNextResponder(); + if (nextResponder == nil) { + return; } switch (event.type) { case NSEventTypeKeyDown: - if ([_owner.nextResponder respondsToSelector:@selector(keyDown:)]) { - [_owner.nextResponder keyDown:event]; + if ([nextResponder respondsToSelector:@selector(keyDown:)]) { + [nextResponder keyDown:event]; } break; case NSEventTypeKeyUp: - if ([_owner.nextResponder respondsToSelector:@selector(keyUp:)]) { - [_owner.nextResponder keyUp:event]; + if ([nextResponder respondsToSelector:@selector(keyUp:)]) { + [nextResponder keyUp:event]; } break; case NSEventTypeFlagsChanged: - if ([_owner.nextResponder respondsToSelector:@selector(flagsChanged:)]) { - [_owner.nextResponder flagsChanged:event]; + if ([nextResponder respondsToSelector:@selector(flagsChanged:)]) { + [nextResponder flagsChanged:event]; } break; default: diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm index 3d0589dd1c4ff..d48092e807331 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm @@ -5,16 +5,102 @@ #import #import +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h" #import "flutter/testing/testing.h" #include "third_party/googletest/googletest/include/gtest/gtest.h" +@interface KeyboardTester : NSObject +- (nonnull instancetype)init; + +@property(nonatomic) NSMutableArray* callbacks; +@property(nonatomic) FlutterKeyboardManager* manager; +@property(nonatomic) NSResponder* nextResponder; + +#pragma mark - Private +- (void)handleEmbedderEvent:(const FlutterKeyEvent&)event + callback:(nullable FlutterKeyEventCallback)callback + userData:(nullable void*)userData; + +- (void)handleChannelMessage:(NSString*)channel + message:(NSData* _Nullable)message + binaryReply:(FlutterBinaryReply _Nullable)callback; + +- (BOOL)handleTextEvent:(NSEvent*)event; +@end + +@implementation KeyboardTester { +} + +- (nonnull instancetype)init { + self = [super init]; + if (self == nil) { + return nil; + } + + _callbacks = [NSMutableArray array]; + _nextResponder = OCMClassMock([NSResponder class]); + + id engineMock = OCMStrictClassMock([FlutterEngine class]); + OCMStub( // NOLINT(google-objc-avoid-throwing-exception) + [engineMock binaryMessenger]) + .andReturn(engineMock); + OCMStub([engineMock sendKeyEvent:FlutterKeyEvent {} callback:nil userData:nil]) + .ignoringNonObjectArgs() + .andCall(self, @selector(handleEmbedderEvent:callback:userData:)); + OCMStub([engineMock sendOnChannel:@"flutter/keyevent" + message:[OCMArg any] + binaryReply:[OCMArg any]]) + .andCall(self, @selector(handleChannelMessage:message:binaryReply:)); + + id textInputPluginMock = OCMStrictClassMock([FlutterTextInputPlugin class]); + OCMStub([textInputPluginMock handleKeyEvent:[OCMArg any]]) + .andCall(self, @selector(handleTextEvent:)); + + _manager = [[FlutterKeyboardManager alloc] initWithEngine:engineMock + textInputPlugin:textInputPluginMock + getNextResponder:^() { + return _nextResponder; + }]; + NSLog(@"Manager 0x%llx", reinterpret_cast(_manager)); + return self; +} + +- (void)handleEmbedderEvent:(const FlutterKeyEvent&)event + callback:(nullable FlutterKeyEventCallback)callback + userData:(nullable void*)userData { + if (callback == nullptr) { + [_callbacks addObject:nil]; + } else { + [_callbacks addObject:^(BOOL handled) { + callback(handled, userData); + }]; + } +} + +- (void)handleChannelMessage:(NSString*)channel + message:(NSData* _Nullable)message + binaryReply:(FlutterBinaryReply _Nullable)callback { + NSDictionary* result = @{ + @"handled" : @false, + }; + NSData* encodedKeyEvent = [[FlutterJSONMessageCodec sharedInstance] encode:result]; + callback(encodedKeyEvent); +} + +- (BOOL)handleTextEvent:(NSEvent*)event { + return NO; +} + +@end + @interface FlutterKeyboardManagerUnittestsObjC : NSObject -- (bool)nextResponderShouldThrowOnKeyUp; +// - (bool)nextResponderShouldThrowOnKeyUp; - (bool)singlePrimaryResponder; -- (bool)doublePrimaryResponder; -- (bool)singleSecondaryResponder; -- (bool)emptyNextResponder; +// - (bool)doublePrimaryResponder; +// - (bool)singleSecondaryResponder; +// - (bool)emptyNextResponder; @end namespace flutter::testing { @@ -68,52 +154,51 @@ id checkKeyDownEvent(unsigned short keyCode) { return owner; } -typedef void (^KeyCallbackSetter)(FlutterAsyncKeyCallback callback); typedef BOOL (^BoolGetter)(); -id mockPrimaryResponder(KeyCallbackSetter callbackSetter) { - id mock = - OCMStrictProtocolMock(@protocol(FlutterKeyPrimaryResponder)); - OCMStub([mock handleEvent:[OCMArg any] callback:[OCMArg any]]) - .andDo((^(NSInvocation* invocation) { - FlutterAsyncKeyCallback callback; - [invocation getArgument:&callback atIndex:3]; - callbackSetter(callback); - })); - return mock; -} - -id mockSecondaryResponder(BoolGetter resultGetter) { - id mock = - OCMStrictProtocolMock(@protocol(FlutterKeySecondaryResponder)); - OCMStub([mock handleKeyEvent:[OCMArg any]]).andDo((^(NSInvocation* invocation) { - BOOL result = resultGetter(); - [invocation setReturnValue:&result]; - })); - return mock; -} +// id mockPrimaryResponder(KeyCallbackSetter callbackSetter) { +// id mock = +// OCMStrictProtocolMock(@protocol(FlutterKeyPrimaryResponder)); +// OCMStub([mock handleEvent:[OCMArg any] callback:[OCMArg any]]) +// .andDo((^(NSInvocation* invocation) { +// FlutterAsyncKeyCallback callback; +// [invocation getArgument:&callback atIndex:3]; +// callbackSetter(callback); +// })); +// return mock; +// } + +// id mockSecondaryResponder(BoolGetter resultGetter) { +// id mock = +// OCMStrictProtocolMock(@protocol(FlutterKeySecondaryResponder)); +// OCMStub([mock handleKeyEvent:[OCMArg any]]).andDo((^(NSInvocation* invocation) { +// BOOL result = resultGetter(); +// [invocation setReturnValue:&result]; +// })); +// return mock; +// } } // namespace -TEST(FlutterKeyboardManagerUnittests, NextResponderShouldThrowOnKeyUp) { - ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] nextResponderShouldThrowOnKeyUp]); -} +// TEST(FlutterKeyboardManagerUnittests, NextResponderShouldThrowOnKeyUp) { +// ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] nextResponderShouldThrowOnKeyUp]); +// } TEST(FlutterKeyboardManagerUnittests, SinglePrimaryResponder) { ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] singlePrimaryResponder]); } -TEST(FlutterKeyboardManagerUnittests, DoublePrimaryResponder) { - ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] doublePrimaryResponder]); -} +// TEST(FlutterKeyboardManagerUnittests, DoublePrimaryResponder) { +// ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] doublePrimaryResponder]); +// } -TEST(FlutterKeyboardManagerUnittests, SingleFinalResponder) { - ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] singleSecondaryResponder]); -} +// TEST(FlutterKeyboardManagerUnittests, SingleFinalResponder) { +// ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] singleSecondaryResponder]); +// } -TEST(FlutterKeyboardManagerUnittests, EmptyNextResponder) { - ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] emptyNextResponder]); -} +// TEST(FlutterKeyboardManagerUnittests, EmptyNextResponder) { +// ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] emptyNextResponder]); +// } } // namespace flutter::testing @@ -132,146 +217,139 @@ - (bool)nextResponderShouldThrowOnKeyUp { } - (bool)singlePrimaryResponder { - NSResponder* owner = flutter::testing::mockOwnerWithDownOnlyNext(); - FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc] initWithOwner:owner]; - - __block NSMutableArray* callbacks = - [NSMutableArray array]; - [manager addPrimaryResponder:flutter::testing::mockPrimaryResponder( - ^(FlutterAsyncKeyCallback callback) { - [callbacks addObject:callback]; - })]; + KeyboardTester* tester = [[KeyboardTester alloc] init]; // Case: The responder reports FALSE - [manager handleEvent:flutter::testing::keyDownEvent(0x50)]; - EXPECT_EQ([callbacks count], 1u); - callbacks[0](FALSE); - OCMVerify([owner.nextResponder keyDown:flutter::testing::checkKeyDownEvent(0x50)]); - [callbacks removeAllObjects]; + [tester.manager handleEvent:flutter::testing::keyDownEvent(0x50)]; + EXPECT_EQ([tester.callbacks count], 1u); + tester.callbacks[0](FALSE); + OCMVerify([tester.nextResponder keyDown:flutter::testing::checkKeyDownEvent(0x50)]); + [tester.callbacks removeAllObjects]; // Case: The responder reports TRUE - [manager handleEvent:flutter::testing::keyUpEvent(0x50)]; - EXPECT_EQ([callbacks count], 1u); - callbacks[0](TRUE); + [tester.manager handleEvent:flutter::testing::keyUpEvent(0x50)]; + EXPECT_EQ([tester.callbacks count], 1u); + tester.callbacks[0](TRUE); // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. + NSLog(@"End"); return true; } -- (bool)doublePrimaryResponder { - NSResponder* owner = flutter::testing::mockOwnerWithDownOnlyNext(); - FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc] initWithOwner:owner]; - - __block NSMutableArray* callbacks1 = - [NSMutableArray array]; - [manager addPrimaryResponder:flutter::testing::mockPrimaryResponder( - ^(FlutterAsyncKeyCallback callback) { - [callbacks1 addObject:callback]; - })]; - - __block NSMutableArray* callbacks2 = - [NSMutableArray array]; - [manager addPrimaryResponder:flutter::testing::mockPrimaryResponder( - ^(FlutterAsyncKeyCallback callback) { - [callbacks2 addObject:callback]; - })]; - - // Case: Both responder report TRUE. - [manager handleEvent:flutter::testing::keyUpEvent(0x50)]; - EXPECT_EQ([callbacks1 count], 1u); - EXPECT_EQ([callbacks2 count], 1u); - callbacks1[0](TRUE); - callbacks2[0](TRUE); - EXPECT_EQ([callbacks1 count], 1u); - EXPECT_EQ([callbacks2 count], 1u); - // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. - [callbacks1 removeAllObjects]; - [callbacks2 removeAllObjects]; - - // Case: One responder reports TRUE. - [manager handleEvent:flutter::testing::keyUpEvent(0x50)]; - EXPECT_EQ([callbacks1 count], 1u); - EXPECT_EQ([callbacks2 count], 1u); - callbacks1[0](FALSE); - callbacks2[0](TRUE); - EXPECT_EQ([callbacks1 count], 1u); - EXPECT_EQ([callbacks2 count], 1u); - // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. - [callbacks1 removeAllObjects]; - [callbacks2 removeAllObjects]; - - // Case: Both responders report FALSE. - [manager handleEvent:flutter::testing::keyDownEvent(0x50)]; - EXPECT_EQ([callbacks1 count], 1u); - EXPECT_EQ([callbacks2 count], 1u); - callbacks1[0](FALSE); - callbacks2[0](FALSE); - EXPECT_EQ([callbacks1 count], 1u); - EXPECT_EQ([callbacks2 count], 1u); - OCMVerify([owner.nextResponder keyDown:flutter::testing::checkKeyDownEvent(0x50)]); - [callbacks1 removeAllObjects]; - [callbacks2 removeAllObjects]; - - return true; -} - -- (bool)singleSecondaryResponder { - NSResponder* owner = flutter::testing::mockOwnerWithDownOnlyNext(); - FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc] initWithOwner:owner]; - - __block NSMutableArray* callbacks = - [NSMutableArray array]; - [manager addPrimaryResponder:flutter::testing::mockPrimaryResponder( - ^(FlutterAsyncKeyCallback callback) { - [callbacks addObject:callback]; - })]; - - __block BOOL nextResponse; - [manager addSecondaryResponder:flutter::testing::mockSecondaryResponder(^() { - return nextResponse; - })]; - - // Case: Primary responder responds TRUE. The event shouldn't be handled by - // the secondary responder. - nextResponse = FALSE; - [manager handleEvent:flutter::testing::keyUpEvent(0x50)]; - EXPECT_EQ([callbacks count], 1u); - callbacks[0](TRUE); - // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. - [callbacks removeAllObjects]; - - // Case: Primary responder responds FALSE. The secondary responder returns - // TRUE. - nextResponse = TRUE; - [manager handleEvent:flutter::testing::keyUpEvent(0x50)]; - EXPECT_EQ([callbacks count], 1u); - callbacks[0](FALSE); - // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. - [callbacks removeAllObjects]; - - // Case: Primary responder responds FALSE. The secondary responder returns FALSE. - nextResponse = FALSE; - [manager handleEvent:flutter::testing::keyDownEvent(0x50)]; - EXPECT_EQ([callbacks count], 1u); - callbacks[0](FALSE); - OCMVerify([owner.nextResponder keyDown:flutter::testing::checkKeyDownEvent(0x50)]); - [callbacks removeAllObjects]; - - return true; -} - -- (bool)emptyNextResponder { - NSResponder* owner = OCMStrictClassMock([NSResponder class]); - OCMStub([owner nextResponder]).andReturn(nil); - - FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc] initWithOwner:owner]; - - [manager addPrimaryResponder:flutter::testing::mockPrimaryResponder( - ^(FlutterAsyncKeyCallback callback) { - callback(FALSE); - })]; - // Passes if no error is thrown. - return true; -} +//- (bool)doublePrimaryResponder { +// NSResponder* owner = flutter::testing::mockOwnerWithDownOnlyNext(); +// FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc] initWithOwner:owner]; +// +// __block NSMutableArray* callbacks1 = +// [NSMutableArray array]; +// [manager addPrimaryResponder:flutter::testing::mockPrimaryResponder( +// ^(FlutterAsyncKeyCallback callback) { +// [callbacks1 addObject:callback]; +// })]; +// +// __block NSMutableArray* callbacks2 = +// [NSMutableArray array]; +// [manager addPrimaryResponder:flutter::testing::mockPrimaryResponder( +// ^(FlutterAsyncKeyCallback callback) { +// [callbacks2 addObject:callback]; +// })]; +// +// // Case: Both responder report TRUE. +// [manager handleEvent:flutter::testing::keyUpEvent(0x50)]; +// EXPECT_EQ([callbacks1 count], 1u); +// EXPECT_EQ([callbacks2 count], 1u); +// callbacks1[0](TRUE); +// callbacks2[0](TRUE); +// EXPECT_EQ([callbacks1 count], 1u); +// EXPECT_EQ([callbacks2 count], 1u); +// // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. +// [callbacks1 removeAllObjects]; +// [callbacks2 removeAllObjects]; +// +// // Case: One responder reports TRUE. +// [manager handleEvent:flutter::testing::keyUpEvent(0x50)]; +// EXPECT_EQ([callbacks1 count], 1u); +// EXPECT_EQ([callbacks2 count], 1u); +// callbacks1[0](FALSE); +// callbacks2[0](TRUE); +// EXPECT_EQ([callbacks1 count], 1u); +// EXPECT_EQ([callbacks2 count], 1u); +// // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. +// [callbacks1 removeAllObjects]; +// [callbacks2 removeAllObjects]; +// +// // Case: Both responders report FALSE. +// [manager handleEvent:flutter::testing::keyDownEvent(0x50)]; +// EXPECT_EQ([callbacks1 count], 1u); +// EXPECT_EQ([callbacks2 count], 1u); +// callbacks1[0](FALSE); +// callbacks2[0](FALSE); +// EXPECT_EQ([callbacks1 count], 1u); +// EXPECT_EQ([callbacks2 count], 1u); +// OCMVerify([owner.nextResponder keyDown:flutter::testing::checkKeyDownEvent(0x50)]); +// [callbacks1 removeAllObjects]; +// [callbacks2 removeAllObjects]; +// +// return true; +//} +// +//- (bool)singleSecondaryResponder { +// NSResponder* owner = flutter::testing::mockOwnerWithDownOnlyNext(); +// FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc] initWithOwner:owner]; +// +// __block NSMutableArray* callbacks = +// [NSMutableArray array]; +// [manager addPrimaryResponder:flutter::testing::mockPrimaryResponder( +// ^(FlutterAsyncKeyCallback callback) { +// [callbacks addObject:callback]; +// })]; +// +// __block BOOL nextResponse; +// [manager addSecondaryResponder:flutter::testing::mockSecondaryResponder(^() { +// return nextResponse; +// })]; +// +// // Case: Primary responder responds TRUE. The event shouldn't be handled by +// // the secondary responder. +// nextResponse = FALSE; +// [manager handleEvent:flutter::testing::keyUpEvent(0x50)]; +// EXPECT_EQ([callbacks count], 1u); +// callbacks[0](TRUE); +// // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. +// [callbacks removeAllObjects]; +// +// // Case: Primary responder responds FALSE. The secondary responder returns +// // TRUE. +// nextResponse = TRUE; +// [manager handleEvent:flutter::testing::keyUpEvent(0x50)]; +// EXPECT_EQ([callbacks count], 1u); +// callbacks[0](FALSE); +// // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. +// [callbacks removeAllObjects]; +// +// // Case: Primary responder responds FALSE. The secondary responder returns FALSE. +// nextResponse = FALSE; +// [manager handleEvent:flutter::testing::keyDownEvent(0x50)]; +// EXPECT_EQ([callbacks count], 1u); +// callbacks[0](FALSE); +// OCMVerify([owner.nextResponder keyDown:flutter::testing::checkKeyDownEvent(0x50)]); +// [callbacks removeAllObjects]; +// +// return true; +//} +// +//- (bool)emptyNextResponder { +// NSResponder* owner = OCMStrictClassMock([NSResponder class]); +// OCMStub([owner nextResponder]).andReturn(nil); +// +// FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc] initWithOwner:owner]; +// +// [manager addPrimaryResponder:flutter::testing::mockPrimaryResponder( +// ^(FlutterAsyncKeyCallback callback) { +// callback(FALSE); +// })]; +// // Passes if no error is thrown. +// return true; +//} @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 2bd004cc373f4..4e8a0d21abf5f 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -9,8 +9,6 @@ #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h" #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponder.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponder.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h" @@ -448,23 +446,11 @@ - (void)configureTrackingArea { - (void)initializeKeyboard { __weak FlutterViewController* weakSelf = self; _textInputPlugin = [[FlutterTextInputPlugin alloc] initWithViewController:weakSelf]; - _keyboardManager = [[FlutterKeyboardManager alloc] initWithOwner:weakSelf]; - [_keyboardManager addPrimaryResponder:[[FlutterEmbedderKeyResponder alloc] - initWithSendEvent:^(const FlutterKeyEvent& event, - FlutterKeyEventCallback callback, - void* userData) { - [weakSelf.engine sendKeyEvent:event - callback:callback - userData:userData]; - }]]; - [_keyboardManager - addPrimaryResponder:[[FlutterChannelKeyResponder alloc] - initWithChannel:[FlutterBasicMessageChannel - messageChannelWithName:@"flutter/keyevent" - binaryMessenger:_engine.binaryMessenger - codec:[FlutterJSONMessageCodec - sharedInstance]]]]; - [_keyboardManager addSecondaryResponder:_textInputPlugin]; + _keyboardManager = [[FlutterKeyboardManager alloc] initWithEngine:_engine + textInputPlugin:_textInputPlugin + getNextResponder:^() { + return weakSelf.nextResponder; + }]; } - (void)addInternalPlugins { From 95b062d8d5fa601b10bcfadef8366e05f6e52c7b Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 9 Mar 2022 04:05:42 -0800 Subject: [PATCH 02/11] FlutterKeyboardViewDelegate --- ci/licenses_golden/licenses_flutter | 1 + .../framework/Source/FlutterKeyboardManager.h | 5 ++--- .../framework/Source/FlutterKeyboardManager.mm | 16 +++++----------- .../Source/FlutterKeyboardManagerUnittests.mm | 16 +++++++--------- .../Source/FlutterKeyboardViewDelegate.h | 15 +++++++++++++++ .../framework/Source/FlutterViewController.mm | 12 +++++++----- .../Source/FlutterViewController_Internal.h | 3 ++- 7 files changed, 39 insertions(+), 29 deletions(-) create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 16a564aa2ca35..f54b5d12d1dc9 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1427,6 +1427,7 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeySeco FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardManager.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardManager.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardManagerTest.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardViewDelegate.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h index 3ca7471ec8cb5..f883f2e959ce6 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h @@ -5,7 +5,7 @@ #import #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h" namespace { // Someohow this pointer type must be defined as a single type for the compiler @@ -53,8 +53,7 @@ typedef _Nullable _NSResponderPtr (^NextResponderProvider)(); * key events are propagated to if no responders handle the event. */ - (nonnull instancetype)initWithEngine:(nonnull FlutterEngine*)engine - textInputPlugin:(nonnull FlutterTextInputPlugin*)textInputPlugin - getNextResponder:(nonnull NextResponderProvider)getNextResponder; + viewDelegate:(nonnull id)viewDelegate; /** * Dispatch a key event to all responders, and possibly the next |NSResponder| diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm index 679486f7dc53b..f5504e10224dd 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm @@ -14,7 +14,7 @@ @interface FlutterKeyboardManager () /** * The text input plugin set by initialization. */ -@property(nonatomic) FlutterTextInputPlugin* textInputPlugin; +@property(nonatomic) id viewDelegate; /** * The primary responders added by addPrimaryResponder. @@ -36,12 +36,10 @@ @implementation FlutterKeyboardManager { } - (nonnull instancetype)initWithEngine:(nonnull FlutterEngine*)engine - textInputPlugin:(nonnull FlutterTextInputPlugin*)textInputPlugin - getNextResponder:(nonnull NextResponderProvider)getNextResponder { + viewDelegate:(nonnull id)viewDelegate { self = [super init]; if (self != nil) { - _getNextResponder = getNextResponder; - _textInputPlugin = textInputPlugin; + _viewDelegate = viewDelegate; _primaryResponders = [[NSMutableArray alloc] init]; __weak FlutterEngine* weakEngine = engine; @@ -97,17 +95,13 @@ - (void)handleEvent:(nonnull NSEvent*)event { } } -- (BOOL)isFirstResponder { - return [self.textInputPlugin isFirstResponder]; -} - #pragma mark - Private - (void)dispatchToSecondaryResponders:(NSEvent*)event { - if ([_textInputPlugin handleKeyEvent:event]) { + if ([_viewDelegate onTextInputKeyEvent:event]) { return; } - NSResponder* nextResponder = _getNextResponder(); + NSResponder* nextResponder = _viewDelegate.nextResponder; if (nextResponder == nil) { return; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm index d48092e807331..2360a5ef4667a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm @@ -27,7 +27,7 @@ - (void)handleChannelMessage:(NSString*)channel message:(NSData* _Nullable)message binaryReply:(FlutterBinaryReply _Nullable)callback; -- (BOOL)handleTextEvent:(NSEvent*)event; +- (BOOL)handleTextInputKeyEvent:(NSEvent*)event; @end @implementation KeyboardTester { @@ -54,15 +54,13 @@ - (nonnull instancetype)init { binaryReply:[OCMArg any]]) .andCall(self, @selector(handleChannelMessage:message:binaryReply:)); - id textInputPluginMock = OCMStrictClassMock([FlutterTextInputPlugin class]); - OCMStub([textInputPluginMock handleKeyEvent:[OCMArg any]]) - .andCall(self, @selector(handleTextEvent:)); + id viewDelegateMock = OCMStrictProtocolMock(@protocol(FlutterKeyboardViewDelegate)); + OCMStub([viewDelegateMock nextResponder]).andReturn(_nextResponder); + OCMStub([viewDelegateMock onTextInputKeyEvent:[OCMArg any]]) + .andCall(self, @selector(handleTextInputKeyEvent:)); _manager = [[FlutterKeyboardManager alloc] initWithEngine:engineMock - textInputPlugin:textInputPluginMock - getNextResponder:^() { - return _nextResponder; - }]; + viewDelegate:viewDelegateMock]; NSLog(@"Manager 0x%llx", reinterpret_cast(_manager)); return self; } @@ -89,7 +87,7 @@ - (void)handleChannelMessage:(NSString*)channel callback(encodedKeyEvent); } -- (BOOL)handleTextEvent:(NSEvent*)event { +- (BOOL)handleTextInputKeyEvent:(NSEvent*)event { return NO; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h new file mode 100644 index 0000000000000..39c571fbe67f1 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h @@ -0,0 +1,15 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +@protocol FlutterKeyboardViewDelegate + +@required + +@property(nonatomic, readonly, nullable) NSResponder *nextResponder; + +- (BOOL)onTextInputKeyEvent:(nonnull NSEvent*)event; + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 4e8a0d21abf5f..ba2e0a2200ad4 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -446,11 +446,7 @@ - (void)configureTrackingArea { - (void)initializeKeyboard { __weak FlutterViewController* weakSelf = self; _textInputPlugin = [[FlutterTextInputPlugin alloc] initWithViewController:weakSelf]; - _keyboardManager = [[FlutterKeyboardManager alloc] initWithEngine:_engine - textInputPlugin:_textInputPlugin - getNextResponder:^() { - return weakSelf.nextResponder; - }]; + _keyboardManager = [[FlutterKeyboardManager alloc] initWithEngine:_engine viewDelegate:weakSelf]; } - (void)addInternalPlugins { @@ -663,6 +659,12 @@ - (void)viewDidReshape:(NSView*)view { return [_engine registrarForPlugin:pluginName]; } +#pragma mark - FlutterKeyboardViewDelegate + +- (BOOL)onTextInputKeyEvent:(nonnull NSEvent*)event { + return [_textInputPlugin handleKeyEvent:event]; +} + #pragma mark - NSResponder - (BOOL)acceptsFirstResponder { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h index f6dd076d112dd..fad993b215068 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h @@ -5,10 +5,11 @@ #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeySecondaryResponder.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h" -@interface FlutterViewController () +@interface FlutterViewController () // The FlutterView for this view controller. @property(nonatomic, readonly, nullable) FlutterView* flutterView; From 0d36de25a87909c9b3905849cf10943a00945f79 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 9 Mar 2022 14:41:42 -0800 Subject: [PATCH 03/11] recordEmbedderCallsTo --- .../Source/FlutterKeyboardManagerUnittests.mm | 257 ++++++++++-------- .../Source/FlutterKeyboardViewDelegate.h | 2 +- 2 files changed, 146 insertions(+), 113 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm index 2360a5ef4667a..d563602359919 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm @@ -11,14 +11,95 @@ #import "flutter/testing/testing.h" #include "third_party/googletest/googletest/include/gtest/gtest.h" +namespace { + +typedef BOOL (^BoolGetter)(); +typedef void (^AsyncKeyCallback)(BOOL handled); +typedef void (^AsyncKeyCallbackHandler)(AsyncKeyCallback callback); + +NSEvent* keyDownEvent(unsigned short keyCode) { + return [NSEvent keyEventWithType:NSEventTypeKeyDown + location:NSZeroPoint + modifierFlags:0x100 + timestamp:0 + windowNumber:0 + context:nil + characters:@"" + charactersIgnoringModifiers:@"" + isARepeat:NO + keyCode:keyCode]; +} + +NSEvent* keyUpEvent(unsigned short keyCode) { + return [NSEvent keyEventWithType:NSEventTypeKeyUp + location:NSZeroPoint + modifierFlags:0x100 + timestamp:0 + windowNumber:0 + context:nil + characters:@"" + charactersIgnoringModifiers:@"" + isARepeat:NO + keyCode:keyCode]; +} + +id checkKeyDownEvent(unsigned short keyCode) { + return [OCMArg checkWithBlock:^BOOL(id value) { + if (![value isKindOfClass:[NSEvent class]]) { + return NO; + } + NSEvent* event = value; + return event.keyCode == keyCode; + }]; +} + +NSResponder* mockOwnerWithDownOnlyNext() { + NSResponder* nextResponder = OCMStrictClassMock([NSResponder class]); + OCMStub([nextResponder keyDown:[OCMArg any]]).andDo(nil); + // The nextResponder is a strict mock and hasn't stubbed keyUp. + // An error will be thrown on keyUp. + + NSResponder* owner = OCMStrictClassMock([NSResponder class]); + OCMStub([owner nextResponder]).andReturn(nextResponder); + return owner; +} + +// id mockPrimaryResponder(KeyCallbackSetter callbackSetter) { +// id mock = +// OCMStrictProtocolMock(@protocol(FlutterKeyPrimaryResponder)); +// OCMStub([mock handleEvent:[OCMArg any] callback:[OCMArg any]]) +// .andDo((^(NSInvocation* invocation) { +// FlutterAsyncKeyCallback callback; +// [invocation getArgument:&callback atIndex:3]; +// callbackSetter(callback); +// })); +// return mock; +// } + +// id mockSecondaryResponder(BoolGetter resultGetter) { +// id mock = +// OCMStrictProtocolMock(@protocol(FlutterKeySecondaryResponder)); +// OCMStub([mock handleKeyEvent:[OCMArg any]]).andDo((^(NSInvocation* invocation) { +// BOOL result = resultGetter(); +// [invocation setReturnValue:&result]; +// })); +// return mock; +// } + +} // namespace + @interface KeyboardTester : NSObject - (nonnull instancetype)init; +- (void)respondEmbedderCallsWith:(BOOL)response; +- (void)recordEmbedderCallsTo:(nonnull NSMutableArray*)storage; +- (void)respondChannelCallsWith:(BOOL)response; +- (void)recordChannelCallsTo:(nonnull NSMutableArray*)storage; -@property(nonatomic) NSMutableArray* callbacks; @property(nonatomic) FlutterKeyboardManager* manager; @property(nonatomic) NSResponder* nextResponder; #pragma mark - Private + - (void)handleEmbedderEvent:(const FlutterKeyEvent&)event callback:(nullable FlutterKeyEventCallback)callback userData:(nullable void*)userData; @@ -31,6 +112,8 @@ - (BOOL)handleTextInputKeyEvent:(NSEvent*)event; @end @implementation KeyboardTester { + AsyncKeyCallbackHandler _embedderHandler; + AsyncKeyCallbackHandler _channelHandler; } - (nonnull instancetype)init { @@ -39,8 +122,9 @@ - (nonnull instancetype)init { return nil; } - _callbacks = [NSMutableArray array]; _nextResponder = OCMClassMock([NSResponder class]); + [self respondChannelCallsWith:false]; + [self respondEmbedderCallsWith:false]; id engineMock = OCMStrictClassMock([FlutterEngine class]); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) @@ -61,20 +145,43 @@ - (nonnull instancetype)init { _manager = [[FlutterKeyboardManager alloc] initWithEngine:engineMock viewDelegate:viewDelegateMock]; - NSLog(@"Manager 0x%llx", reinterpret_cast(_manager)); return self; } +- (void)respondEmbedderCallsWith:(BOOL)response { + _embedderHandler = ^(AsyncKeyCallback callback) { + if (callback != nil) + callback(response); + }; +} + +- (void)recordEmbedderCallsTo:(nonnull NSMutableArray*)storage { + _embedderHandler = ^(AsyncKeyCallback callback) { + [storage addObject:callback]; + }; +} + +- (void)respondChannelCallsWith:(BOOL)response { + _channelHandler = ^(AsyncKeyCallback callback) { + if (callback != nil) + callback(response); + }; +} + +- (void)recordChannelCallsTo:(nonnull NSMutableArray*)storage { + _channelHandler = ^(AsyncKeyCallback callback) { + [storage addObject:callback]; + }; +} + +#pragma mark - Private + - (void)handleEmbedderEvent:(const FlutterKeyEvent&)event callback:(nullable FlutterKeyEventCallback)callback userData:(nullable void*)userData { - if (callback == nullptr) { - [_callbacks addObject:nil]; - } else { - [_callbacks addObject:^(BOOL handled) { - callback(handled, userData); - }]; - } + _embedderHandler(^(BOOL handled) { + callback(handled, userData); + }); } - (void)handleChannelMessage:(NSString*)channel @@ -102,82 +209,6 @@ - (bool)singlePrimaryResponder; @end namespace flutter::testing { - -namespace { - -NSEvent* keyDownEvent(unsigned short keyCode) { - return [NSEvent keyEventWithType:NSEventTypeKeyDown - location:NSZeroPoint - modifierFlags:0x100 - timestamp:0 - windowNumber:0 - context:nil - characters:@"" - charactersIgnoringModifiers:@"" - isARepeat:NO - keyCode:keyCode]; -} - -NSEvent* keyUpEvent(unsigned short keyCode) { - return [NSEvent keyEventWithType:NSEventTypeKeyUp - location:NSZeroPoint - modifierFlags:0x100 - timestamp:0 - windowNumber:0 - context:nil - characters:@"" - charactersIgnoringModifiers:@"" - isARepeat:NO - keyCode:keyCode]; -} - -id checkKeyDownEvent(unsigned short keyCode) { - return [OCMArg checkWithBlock:^BOOL(id value) { - if (![value isKindOfClass:[NSEvent class]]) { - return NO; - } - NSEvent* event = value; - return event.keyCode == keyCode; - }]; -} - -NSResponder* mockOwnerWithDownOnlyNext() { - NSResponder* nextResponder = OCMStrictClassMock([NSResponder class]); - OCMStub([nextResponder keyDown:[OCMArg any]]).andDo(nil); - // The nextResponder is a strict mock and hasn't stubbed keyUp. - // An error will be thrown on keyUp. - - NSResponder* owner = OCMStrictClassMock([NSResponder class]); - OCMStub([owner nextResponder]).andReturn(nextResponder); - return owner; -} - -typedef BOOL (^BoolGetter)(); - -// id mockPrimaryResponder(KeyCallbackSetter callbackSetter) { -// id mock = -// OCMStrictProtocolMock(@protocol(FlutterKeyPrimaryResponder)); -// OCMStub([mock handleEvent:[OCMArg any] callback:[OCMArg any]]) -// .andDo((^(NSInvocation* invocation) { -// FlutterAsyncKeyCallback callback; -// [invocation getArgument:&callback atIndex:3]; -// callbackSetter(callback); -// })); -// return mock; -// } - -// id mockSecondaryResponder(BoolGetter resultGetter) { -// id mock = -// OCMStrictProtocolMock(@protocol(FlutterKeySecondaryResponder)); -// OCMStub([mock handleKeyEvent:[OCMArg any]]).andDo((^(NSInvocation* invocation) { -// BOOL result = resultGetter(); -// [invocation setReturnValue:&result]; -// })); -// return mock; -// } - -} // namespace - // TEST(FlutterKeyboardManagerUnittests, NextResponderShouldThrowOnKeyUp) { // ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] nextResponderShouldThrowOnKeyUp]); // } @@ -205,9 +236,9 @@ @implementation FlutterKeyboardManagerUnittestsObjC // Verify that the nextResponder returned from mockOwnerWithDownOnlyNext() // throws exception when keyUp is called. - (bool)nextResponderShouldThrowOnKeyUp { - NSResponder* owner = flutter::testing::mockOwnerWithDownOnlyNext(); + NSResponder* owner = mockOwnerWithDownOnlyNext(); @try { - [owner.nextResponder keyUp:flutter::testing::keyUpEvent(0x50)]; + [owner.nextResponder keyUp:keyUpEvent(0x50)]; return false; } @catch (...) { return true; @@ -216,44 +247,46 @@ - (bool)nextResponderShouldThrowOnKeyUp { - (bool)singlePrimaryResponder { KeyboardTester* tester = [[KeyboardTester alloc] init]; + NSMutableArray* embedderCallbacks = + [NSMutableArray array]; + [tester recordEmbedderCallsTo:embedderCallbacks]; // Case: The responder reports FALSE - [tester.manager handleEvent:flutter::testing::keyDownEvent(0x50)]; - EXPECT_EQ([tester.callbacks count], 1u); - tester.callbacks[0](FALSE); - OCMVerify([tester.nextResponder keyDown:flutter::testing::checkKeyDownEvent(0x50)]); - [tester.callbacks removeAllObjects]; + [tester.manager handleEvent:keyDownEvent(0x50)]; + EXPECT_EQ([embedderCallbacks count], 1u); + embedderCallbacks[0](FALSE); + OCMVerify([tester.nextResponder keyDown:checkKeyDownEvent(0x50)]); + [embedderCallbacks removeAllObjects]; // Case: The responder reports TRUE - [tester.manager handleEvent:flutter::testing::keyUpEvent(0x50)]; - EXPECT_EQ([tester.callbacks count], 1u); - tester.callbacks[0](TRUE); + [tester.manager handleEvent:keyUpEvent(0x50)]; + EXPECT_EQ([embedderCallbacks count], 1u); + embedderCallbacks[0](TRUE); // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. - NSLog(@"End"); return true; } //- (bool)doublePrimaryResponder { -// NSResponder* owner = flutter::testing::mockOwnerWithDownOnlyNext(); +// NSResponder* owner = mockOwnerWithDownOnlyNext(); // FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc] initWithOwner:owner]; // // __block NSMutableArray* callbacks1 = // [NSMutableArray array]; -// [manager addPrimaryResponder:flutter::testing::mockPrimaryResponder( +// [manager addPrimaryResponder:mockPrimaryResponder( // ^(FlutterAsyncKeyCallback callback) { // [callbacks1 addObject:callback]; // })]; // // __block NSMutableArray* callbacks2 = // [NSMutableArray array]; -// [manager addPrimaryResponder:flutter::testing::mockPrimaryResponder( +// [manager addPrimaryResponder:mockPrimaryResponder( // ^(FlutterAsyncKeyCallback callback) { // [callbacks2 addObject:callback]; // })]; // // // Case: Both responder report TRUE. -// [manager handleEvent:flutter::testing::keyUpEvent(0x50)]; +// [manager handleEvent:keyUpEvent(0x50)]; // EXPECT_EQ([callbacks1 count], 1u); // EXPECT_EQ([callbacks2 count], 1u); // callbacks1[0](TRUE); @@ -265,7 +298,7 @@ - (bool)singlePrimaryResponder { // [callbacks2 removeAllObjects]; // // // Case: One responder reports TRUE. -// [manager handleEvent:flutter::testing::keyUpEvent(0x50)]; +// [manager handleEvent:keyUpEvent(0x50)]; // EXPECT_EQ([callbacks1 count], 1u); // EXPECT_EQ([callbacks2 count], 1u); // callbacks1[0](FALSE); @@ -277,14 +310,14 @@ - (bool)singlePrimaryResponder { // [callbacks2 removeAllObjects]; // // // Case: Both responders report FALSE. -// [manager handleEvent:flutter::testing::keyDownEvent(0x50)]; +// [manager handleEvent:keyDownEvent(0x50)]; // EXPECT_EQ([callbacks1 count], 1u); // EXPECT_EQ([callbacks2 count], 1u); // callbacks1[0](FALSE); // callbacks2[0](FALSE); // EXPECT_EQ([callbacks1 count], 1u); // EXPECT_EQ([callbacks2 count], 1u); -// OCMVerify([owner.nextResponder keyDown:flutter::testing::checkKeyDownEvent(0x50)]); +// OCMVerify([owner.nextResponder keyDown:checkKeyDownEvent(0x50)]); // [callbacks1 removeAllObjects]; // [callbacks2 removeAllObjects]; // @@ -292,25 +325,25 @@ - (bool)singlePrimaryResponder { //} // //- (bool)singleSecondaryResponder { -// NSResponder* owner = flutter::testing::mockOwnerWithDownOnlyNext(); +// NSResponder* owner = mockOwnerWithDownOnlyNext(); // FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc] initWithOwner:owner]; // // __block NSMutableArray* callbacks = // [NSMutableArray array]; -// [manager addPrimaryResponder:flutter::testing::mockPrimaryResponder( +// [manager addPrimaryResponder:mockPrimaryResponder( // ^(FlutterAsyncKeyCallback callback) { // [callbacks addObject:callback]; // })]; // // __block BOOL nextResponse; -// [manager addSecondaryResponder:flutter::testing::mockSecondaryResponder(^() { +// [manager addSecondaryResponder:mockSecondaryResponder(^() { // return nextResponse; // })]; // // // Case: Primary responder responds TRUE. The event shouldn't be handled by // // the secondary responder. // nextResponse = FALSE; -// [manager handleEvent:flutter::testing::keyUpEvent(0x50)]; +// [manager handleEvent:keyUpEvent(0x50)]; // EXPECT_EQ([callbacks count], 1u); // callbacks[0](TRUE); // // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. @@ -319,7 +352,7 @@ - (bool)singlePrimaryResponder { // // Case: Primary responder responds FALSE. The secondary responder returns // // TRUE. // nextResponse = TRUE; -// [manager handleEvent:flutter::testing::keyUpEvent(0x50)]; +// [manager handleEvent:keyUpEvent(0x50)]; // EXPECT_EQ([callbacks count], 1u); // callbacks[0](FALSE); // // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. @@ -327,10 +360,10 @@ - (bool)singlePrimaryResponder { // // // Case: Primary responder responds FALSE. The secondary responder returns FALSE. // nextResponse = FALSE; -// [manager handleEvent:flutter::testing::keyDownEvent(0x50)]; +// [manager handleEvent:keyDownEvent(0x50)]; // EXPECT_EQ([callbacks count], 1u); // callbacks[0](FALSE); -// OCMVerify([owner.nextResponder keyDown:flutter::testing::checkKeyDownEvent(0x50)]); +// OCMVerify([owner.nextResponder keyDown:checkKeyDownEvent(0x50)]); // [callbacks removeAllObjects]; // // return true; @@ -342,7 +375,7 @@ - (bool)singlePrimaryResponder { // // FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc] initWithOwner:owner]; // -// [manager addPrimaryResponder:flutter::testing::mockPrimaryResponder( +// [manager addPrimaryResponder:mockPrimaryResponder( // ^(FlutterAsyncKeyCallback callback) { // callback(FALSE); // })]; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h index 39c571fbe67f1..0f6ce68107163 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h @@ -8,7 +8,7 @@ @required -@property(nonatomic, readonly, nullable) NSResponder *nextResponder; +@property(nonatomic, readonly, nullable) NSResponder* nextResponder; - (BOOL)onTextInputKeyEvent:(nonnull NSEvent*)event; From 0ad5e3ba7ccae12dfcaba11267d0c3e6dfbd36ed Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 9 Mar 2022 15:50:20 -0800 Subject: [PATCH 04/11] All tests --- .../Source/FlutterKeyboardManagerUnittests.mm | 325 +++++++++--------- 1 file changed, 157 insertions(+), 168 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm index d563602359919..7513a8d886f87 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm @@ -64,28 +64,6 @@ id checkKeyDownEvent(unsigned short keyCode) { return owner; } -// id mockPrimaryResponder(KeyCallbackSetter callbackSetter) { -// id mock = -// OCMStrictProtocolMock(@protocol(FlutterKeyPrimaryResponder)); -// OCMStub([mock handleEvent:[OCMArg any] callback:[OCMArg any]]) -// .andDo((^(NSInvocation* invocation) { -// FlutterAsyncKeyCallback callback; -// [invocation getArgument:&callback atIndex:3]; -// callbackSetter(callback); -// })); -// return mock; -// } - -// id mockSecondaryResponder(BoolGetter resultGetter) { -// id mock = -// OCMStrictProtocolMock(@protocol(FlutterKeySecondaryResponder)); -// OCMStub([mock handleKeyEvent:[OCMArg any]]).andDo((^(NSInvocation* invocation) { -// BOOL result = resultGetter(); -// [invocation setReturnValue:&result]; -// })); -// return mock; -// } - } // namespace @interface KeyboardTester : NSObject @@ -114,6 +92,7 @@ - (BOOL)handleTextInputKeyEvent:(NSEvent*)event; @implementation KeyboardTester { AsyncKeyCallbackHandler _embedderHandler; AsyncKeyCallbackHandler _channelHandler; + BOOL _textInputResponse; } - (nonnull instancetype)init { @@ -123,8 +102,9 @@ - (nonnull instancetype)init { } _nextResponder = OCMClassMock([NSResponder class]); - [self respondChannelCallsWith:false]; - [self respondEmbedderCallsWith:false]; + [self respondChannelCallsWith:FALSE]; + [self respondEmbedderCallsWith:FALSE]; + [self respondTextInputWith:FALSE]; id engineMock = OCMStrictClassMock([FlutterEngine class]); OCMStub( // NOLINT(google-objc-avoid-throwing-exception) @@ -150,8 +130,7 @@ - (nonnull instancetype)init { - (void)respondEmbedderCallsWith:(BOOL)response { _embedderHandler = ^(AsyncKeyCallback callback) { - if (callback != nil) - callback(response); + callback(response); }; } @@ -163,8 +142,7 @@ - (void)recordEmbedderCallsTo:(nonnull NSMutableArray*) - (void)respondChannelCallsWith:(BOOL)response { _channelHandler = ^(AsyncKeyCallback callback) { - if (callback != nil) - callback(response); + callback(response); }; } @@ -174,60 +152,68 @@ - (void)recordChannelCallsTo:(nonnull NSMutableArray*)s }; } +- (void)respondTextInputWith:(BOOL)response { + _textInputResponse = response; +} + #pragma mark - Private - (void)handleEmbedderEvent:(const FlutterKeyEvent&)event callback:(nullable FlutterKeyEventCallback)callback userData:(nullable void*)userData { - _embedderHandler(^(BOOL handled) { - callback(handled, userData); - }); + if (callback != nullptr) { + _embedderHandler(^(BOOL handled) { + callback(handled, userData); + }); + } } - (void)handleChannelMessage:(NSString*)channel message:(NSData* _Nullable)message binaryReply:(FlutterBinaryReply _Nullable)callback { - NSDictionary* result = @{ - @"handled" : @false, - }; - NSData* encodedKeyEvent = [[FlutterJSONMessageCodec sharedInstance] encode:result]; - callback(encodedKeyEvent); + _channelHandler(^(BOOL handled) { + NSDictionary* result = @{ + @"handled" : @(handled), + }; + NSData* encodedKeyEvent = [[FlutterJSONMessageCodec sharedInstance] encode:result]; + callback(encodedKeyEvent); + }); } - (BOOL)handleTextInputKeyEvent:(NSEvent*)event { - return NO; + return _textInputResponse; } @end @interface FlutterKeyboardManagerUnittestsObjC : NSObject -// - (bool)nextResponderShouldThrowOnKeyUp; +- (bool)nextResponderShouldThrowOnKeyUp; - (bool)singlePrimaryResponder; -// - (bool)doublePrimaryResponder; -// - (bool)singleSecondaryResponder; -// - (bool)emptyNextResponder; +- (bool)doublePrimaryResponder; + - (bool)textInputPlugin; +- (bool)emptyNextResponder; @end namespace flutter::testing { -// TEST(FlutterKeyboardManagerUnittests, NextResponderShouldThrowOnKeyUp) { -// ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] nextResponderShouldThrowOnKeyUp]); -// } +TEST(FlutterKeyboardManagerUnittests, NextResponderShouldThrowOnKeyUp) { + ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] nextResponderShouldThrowOnKeyUp]); +} TEST(FlutterKeyboardManagerUnittests, SinglePrimaryResponder) { ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] singlePrimaryResponder]); } -// TEST(FlutterKeyboardManagerUnittests, DoublePrimaryResponder) { -// ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] doublePrimaryResponder]); -// } +TEST(FlutterKeyboardManagerUnittests, DoublePrimaryResponder) { + ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] doublePrimaryResponder]); +} -// TEST(FlutterKeyboardManagerUnittests, SingleFinalResponder) { -// ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] singleSecondaryResponder]); -// } +TEST(FlutterKeyboardManagerUnittests, SingleFinalResponder) { + ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] textInputPlugin]); +} -// TEST(FlutterKeyboardManagerUnittests, EmptyNextResponder) { -// ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] emptyNextResponder]); -// } +TEST(FlutterKeyboardManagerUnittests, EmptyNextResponder) { + ASSERT_TRUE([[FlutterKeyboardManagerUnittestsObjC alloc] emptyNextResponder]); +} } // namespace flutter::testing @@ -267,120 +253,123 @@ - (bool)singlePrimaryResponder { return true; } -//- (bool)doublePrimaryResponder { -// NSResponder* owner = mockOwnerWithDownOnlyNext(); -// FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc] initWithOwner:owner]; -// -// __block NSMutableArray* callbacks1 = -// [NSMutableArray array]; -// [manager addPrimaryResponder:mockPrimaryResponder( -// ^(FlutterAsyncKeyCallback callback) { -// [callbacks1 addObject:callback]; -// })]; -// -// __block NSMutableArray* callbacks2 = -// [NSMutableArray array]; -// [manager addPrimaryResponder:mockPrimaryResponder( -// ^(FlutterAsyncKeyCallback callback) { -// [callbacks2 addObject:callback]; -// })]; -// -// // Case: Both responder report TRUE. -// [manager handleEvent:keyUpEvent(0x50)]; -// EXPECT_EQ([callbacks1 count], 1u); -// EXPECT_EQ([callbacks2 count], 1u); -// callbacks1[0](TRUE); -// callbacks2[0](TRUE); -// EXPECT_EQ([callbacks1 count], 1u); -// EXPECT_EQ([callbacks2 count], 1u); -// // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. -// [callbacks1 removeAllObjects]; -// [callbacks2 removeAllObjects]; -// -// // Case: One responder reports TRUE. -// [manager handleEvent:keyUpEvent(0x50)]; -// EXPECT_EQ([callbacks1 count], 1u); -// EXPECT_EQ([callbacks2 count], 1u); -// callbacks1[0](FALSE); -// callbacks2[0](TRUE); -// EXPECT_EQ([callbacks1 count], 1u); -// EXPECT_EQ([callbacks2 count], 1u); -// // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. -// [callbacks1 removeAllObjects]; -// [callbacks2 removeAllObjects]; -// -// // Case: Both responders report FALSE. -// [manager handleEvent:keyDownEvent(0x50)]; -// EXPECT_EQ([callbacks1 count], 1u); -// EXPECT_EQ([callbacks2 count], 1u); -// callbacks1[0](FALSE); -// callbacks2[0](FALSE); -// EXPECT_EQ([callbacks1 count], 1u); -// EXPECT_EQ([callbacks2 count], 1u); -// OCMVerify([owner.nextResponder keyDown:checkKeyDownEvent(0x50)]); -// [callbacks1 removeAllObjects]; -// [callbacks2 removeAllObjects]; -// -// return true; -//} -// -//- (bool)singleSecondaryResponder { -// NSResponder* owner = mockOwnerWithDownOnlyNext(); -// FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc] initWithOwner:owner]; -// -// __block NSMutableArray* callbacks = -// [NSMutableArray array]; -// [manager addPrimaryResponder:mockPrimaryResponder( -// ^(FlutterAsyncKeyCallback callback) { -// [callbacks addObject:callback]; -// })]; -// -// __block BOOL nextResponse; -// [manager addSecondaryResponder:mockSecondaryResponder(^() { -// return nextResponse; -// })]; -// -// // Case: Primary responder responds TRUE. The event shouldn't be handled by -// // the secondary responder. -// nextResponse = FALSE; -// [manager handleEvent:keyUpEvent(0x50)]; -// EXPECT_EQ([callbacks count], 1u); -// callbacks[0](TRUE); -// // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. -// [callbacks removeAllObjects]; -// -// // Case: Primary responder responds FALSE. The secondary responder returns -// // TRUE. -// nextResponse = TRUE; -// [manager handleEvent:keyUpEvent(0x50)]; -// EXPECT_EQ([callbacks count], 1u); -// callbacks[0](FALSE); -// // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. -// [callbacks removeAllObjects]; -// -// // Case: Primary responder responds FALSE. The secondary responder returns FALSE. -// nextResponse = FALSE; -// [manager handleEvent:keyDownEvent(0x50)]; -// EXPECT_EQ([callbacks count], 1u); -// callbacks[0](FALSE); -// OCMVerify([owner.nextResponder keyDown:checkKeyDownEvent(0x50)]); -// [callbacks removeAllObjects]; -// -// return true; -//} -// -//- (bool)emptyNextResponder { -// NSResponder* owner = OCMStrictClassMock([NSResponder class]); -// OCMStub([owner nextResponder]).andReturn(nil); -// -// FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc] initWithOwner:owner]; -// -// [manager addPrimaryResponder:mockPrimaryResponder( -// ^(FlutterAsyncKeyCallback callback) { -// callback(FALSE); -// })]; -// // Passes if no error is thrown. -// return true; -//} +- (bool)doublePrimaryResponder { + KeyboardTester* tester = [[KeyboardTester alloc] init]; + + // Send a down event first so we can send an up event later. + [tester respondEmbedderCallsWith:false]; + [tester respondChannelCallsWith:false]; + [tester.manager handleEvent:keyDownEvent(0x50)]; + + NSMutableArray* embedderCallbacks = + [NSMutableArray array]; + NSMutableArray* channelCallbacks = + [NSMutableArray array]; + [tester recordEmbedderCallsTo:embedderCallbacks]; + [tester recordChannelCallsTo:channelCallbacks]; + + // Case: Both responders report TRUE. + [tester.manager handleEvent:keyUpEvent(0x50)]; + EXPECT_EQ([embedderCallbacks count], 1u); + EXPECT_EQ([channelCallbacks count], 1u); + embedderCallbacks[0](TRUE); + channelCallbacks[0](TRUE); + EXPECT_EQ([embedderCallbacks count], 1u); + EXPECT_EQ([channelCallbacks count], 1u); + // [tester.nextResponder keyUp:] should not be called, otherwise an error will be thrown. + [embedderCallbacks removeAllObjects]; + [channelCallbacks removeAllObjects]; + + // Case: One responder reports TRUE. + [tester respondEmbedderCallsWith:false]; + [tester respondChannelCallsWith:false]; + [tester.manager handleEvent:keyDownEvent(0x50)]; + + [tester recordEmbedderCallsTo:embedderCallbacks]; + [tester recordChannelCallsTo:channelCallbacks]; + [tester.manager handleEvent:keyUpEvent(0x50)]; + EXPECT_EQ([embedderCallbacks count], 1u); + EXPECT_EQ([channelCallbacks count], 1u); + embedderCallbacks[0](FALSE); + channelCallbacks[0](TRUE); + EXPECT_EQ([embedderCallbacks count], 1u); + EXPECT_EQ([channelCallbacks count], 1u); + // [tester.nextResponder keyUp:] should not be called, otherwise an error will be thrown. + [embedderCallbacks removeAllObjects]; + [channelCallbacks removeAllObjects]; + + // Case: Both responders report FALSE. + [tester.manager handleEvent:keyDownEvent(0x53)]; + EXPECT_EQ([embedderCallbacks count], 1u); + EXPECT_EQ([channelCallbacks count], 1u); + embedderCallbacks[0](FALSE); + channelCallbacks[0](FALSE); + EXPECT_EQ([embedderCallbacks count], 1u); + EXPECT_EQ([channelCallbacks count], 1u); + OCMVerify([tester.nextResponder keyDown:checkKeyDownEvent(0x53)]); + [embedderCallbacks removeAllObjects]; + [channelCallbacks removeAllObjects]; + + return true; +} + +- (bool)textInputPlugin { + KeyboardTester* tester = [[KeyboardTester alloc] init]; + + // Send a down event first so we can send an up event later. + [tester respondEmbedderCallsWith:false]; + [tester respondChannelCallsWith:false]; + [tester.manager handleEvent:keyDownEvent(0x50)]; + + NSMutableArray* callbacks = + [NSMutableArray array]; + [tester recordEmbedderCallsTo:callbacks]; + + // Case: Primary responder responds TRUE. The event shouldn't be handled by + // the secondary responder. + [tester respondTextInputWith:FALSE]; + [tester.manager handleEvent:keyUpEvent(0x50)]; + EXPECT_EQ([callbacks count], 1u); + callbacks[0](TRUE); + // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. + [callbacks removeAllObjects]; + + // Send a down event first so we can send an up event later. + [tester respondEmbedderCallsWith:false]; + [tester.manager handleEvent:keyDownEvent(0x50)]; + + // Case: Primary responder responds FALSE. The secondary responder returns + // TRUE. + [tester recordEmbedderCallsTo:callbacks]; + [tester respondTextInputWith:TRUE]; + [tester.manager handleEvent:keyUpEvent(0x50)]; + EXPECT_EQ([callbacks count], 1u); + callbacks[0](FALSE); + // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown. + [callbacks removeAllObjects]; + + // Case: Primary responder responds FALSE. The secondary responder returns FALSE. + [tester respondTextInputWith:FALSE]; + [tester.manager handleEvent:keyDownEvent(0x50)]; + EXPECT_EQ([callbacks count], 1u); + callbacks[0](FALSE); + OCMVerify([tester.nextResponder keyDown:checkKeyDownEvent(0x50)]); + [callbacks removeAllObjects]; + + return true; +} + +- (bool)emptyNextResponder { + KeyboardTester* tester = [[KeyboardTester alloc] init]; + tester.nextResponder = nil; + + [tester respondEmbedderCallsWith:false]; + [tester respondChannelCallsWith:false]; + [tester respondTextInputWith:false]; + [tester.manager handleEvent:keyDownEvent(0x50)]; + + // Passes if no error is thrown. + return true; +} @end From 40d41c0c227dadf7e5a6f3b5c235d55c6a77d9fb Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 9 Mar 2022 15:55:52 -0800 Subject: [PATCH 05/11] Format --- .../macos/framework/Source/FlutterKeyboardManagerUnittests.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm index 7513a8d886f87..9294e4c65c378 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm @@ -190,7 +190,7 @@ @interface FlutterKeyboardManagerUnittestsObjC : NSObject - (bool)nextResponderShouldThrowOnKeyUp; - (bool)singlePrimaryResponder; - (bool)doublePrimaryResponder; - - (bool)textInputPlugin; +- (bool)textInputPlugin; - (bool)emptyNextResponder; @end From e6dc71c9543936b872906546585fc78b0c6bc720 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 9 Mar 2022 16:34:47 -0800 Subject: [PATCH 06/11] Remove secondary; update docs. --- shell/platform/darwin/macos/BUILD.gn | 1 - .../Source/FlutterKeySecondaryResponder.h | 24 --------- .../framework/Source/FlutterKeyboardManager.h | 52 ++++++++----------- .../Source/FlutterKeyboardViewDelegate.h | 18 +++++++ .../framework/Source/FlutterTextInputPlugin.h | 15 +++++- .../Source/FlutterTextInputPlugin.mm | 13 ----- .../Source/FlutterViewController_Internal.h | 1 - 7 files changed, 53 insertions(+), 71 deletions(-) delete mode 100644 shell/platform/darwin/macos/framework/Source/FlutterKeySecondaryResponder.h diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn index 302c9d0287816..f50b4209fed31 100644 --- a/shell/platform/darwin/macos/BUILD.gn +++ b/shell/platform/darwin/macos/BUILD.gn @@ -82,7 +82,6 @@ source_set("flutter_framework_source") { "framework/Source/FlutterIOSurfaceHolder.h", "framework/Source/FlutterIOSurfaceHolder.mm", "framework/Source/FlutterKeyPrimaryResponder.h", - "framework/Source/FlutterKeySecondaryResponder.h", "framework/Source/FlutterKeyboardManager.h", "framework/Source/FlutterKeyboardManager.mm", "framework/Source/FlutterMacOSExternalTexture.h", diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeySecondaryResponder.h b/shell/platform/darwin/macos/framework/Source/FlutterKeySecondaryResponder.h deleted file mode 100644 index e0d33522d2342..0000000000000 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeySecondaryResponder.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -/** - * An interface for a responder that can process a key event and decides whether - * to handle an event synchronously. - * - * To use this class, add it to a |FlutterKeyboardManager| with - * |addSecondaryResponder|. - */ -@protocol FlutterKeySecondaryResponder -/** - * Informs the receiver that the user has interacted with a key. - * - * The return value indicates whether it has handled the given event. - * - * Default implementation returns NO. - */ -@required -- (BOOL)handleKeyEvent:(nonnull NSEvent*)event; -@end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h index f883f2e959ce6..f8a55771bdba1 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h @@ -16,48 +16,40 @@ typedef NSResponder* _NSResponderPtr; typedef _Nullable _NSResponderPtr (^NextResponderProvider)(); /** - * A hub that manages how key events are dispatched to various Flutter key - * responders, and whether the event is propagated to the next NSResponder. + * Processes keyboard events and cooperate with |TextInputPlugin|. * - * This class manages one or more primary responders, as well as zero or more - * secondary responders. + * A keyboard event goes through a few sections, each can choose to handled the + * event, and only unhandled events can move to the next section: * - * An event that is received by |handleEvent| is first dispatched to *all* - * primary responders. Each primary responder responds *asynchronously* with a - * boolean, indicating whether it handles the event. - * - * An event that is not handled by any primary responders is then passed to to - * the first secondary responder (in the chronological order of addition), - * which responds *synchronously* with a boolean, indicating whether it handles - * the event. If not, the event is passed to the next secondary responder, and - * so on. - * - * If no responders handle the event, the event is then handed over to the - * owner's |nextResponder| if not nil, dispatching to method |keyDown|, - * |keyUp|, or |flagsChanged| depending on the event's type. If the - * |nextResponder| is nil, then the event will be propagated no further. - * - * Preventing primary responders from receiving events is not supported, - * because in reality this class will only support 2 hardcoded ones (channel - * and embedder), where the only purpose of supporting two is to support the - * legacy API (channel) during the deprecation window, after which the channel - * responder should be removed. + * - Pre-filtering: Events during IME are sent to the system immediately + * (to be implemented). + * - Keyboard: Dispatch to the embedder responder and the channel responder + * simultaneously. After both responders have responded (asynchronously), the + * event is considered handled if either responder handles. + * - Text input: Events are sent to |TextInputPlugin| and are handled + * synchronously. + * - Next responder: Events are sent to the next responder as specified by + * |viewDelegate|. */ @interface FlutterKeyboardManager : NSObject -// TODO /** - * Create a manager by specifying a weak pointer to the owner view controller. + * Create a keyboard manager. * - * The |viewController.nextResponder| can be nil, but if it isn't, it will be where the - * key events are propagated to if no responders handle the event. + * The |engine| is a weak reference, used for embedder APIs and channel + * messages. + * + * The |viewDelegate| is a weak reference, typically implemented by + * |FlutterViewController|. */ - (nonnull instancetype)initWithEngine:(nonnull FlutterEngine*)engine viewDelegate:(nonnull id)viewDelegate; /** - * Dispatch a key event to all responders, and possibly the next |NSResponder| - * afterwards. + * Processes a key event. + * + * Unhandled events will be dispatched to the text input system, and possibly + * the next responder afterwards. */ - (void)handleEvent:(nonnull NSEvent*)event; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h index 0f6ce68107163..d6ea759c323f8 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h @@ -4,12 +4,30 @@ #import +/** + * An interface for a class that can provides |FlutterKeyboardManager| with + * platform-related features. + * + * This protocol is typically implemented by |FlutterViewController|. + */ @protocol FlutterKeyboardViewDelegate @required +/** + * Get the next responder to dispatch events that the keyboard system + * (including text input) do not handle. + * + * If the |nextResponder| is null, then those events will be discarded. + */ @property(nonatomic, readonly, nullable) NSResponder* nextResponder; +/** + * Dispatch events that are not handled by the keyboard event handlers + * to the text input handler. + * + * This method typically forwards events to |TextInputPlugin.handleKeyEvent|. + */ - (BOOL)onTextInputKeyEvent:(nonnull NSEvent*)event; @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h index b5b8b545d4b6e..57a5aec0e532b 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h @@ -6,7 +6,6 @@ #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeySecondaryResponder.h" @class FlutterTextField; @@ -22,7 +21,7 @@ * When accessibility is on, accessibility bridge creates a NSTextField, i.e. FlutterTextField, * for every text field in the Flutter. This plugin acts as a field editor for those NSTextField[s]. */ -@interface FlutterTextInputPlugin : NSTextView +@interface FlutterTextInputPlugin : NSTextView /** * The NSTextField that currently has this plugin as its field editor. @@ -46,6 +45,18 @@ */ - (BOOL)isFirstResponder; +/** + * Handles key down events received from the view controller, responding YES if + * the event was handled. + * + * Note, the Apple docs suggest that clients should override essentially all the + * mouse and keyboard event-handling methods of NSResponder. However, experimentation + * indicates that only key events are processed by the native layer; Flutter processes + * mouse events. Additionally, processing both keyUp and keyDown results in duplicate + * processing of the same keys. + */ +- (BOOL)handleKeyEvent:(NSEvent*)event; + @end // Private methods made visible for testing diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm index 55f7064b46770..a9485ece992b0 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm @@ -461,19 +461,6 @@ - (NSString*)textAffinityString { : kTextAffinityDownstream; } -#pragma mark - -#pragma mark FlutterKeySecondaryResponder - -/** - * Handles key down events received from the view controller, responding YES if - * the event was handled. - * - * Note, the Apple docs suggest that clients should override essentially all the - * mouse and keyboard event-handling methods of NSResponder. However, experimentation - * indicates that only key events are processed by the native layer; Flutter processes - * mouse events. Additionally, processing both keyUp and keyDown results in duplicate - * processing of the same keys. - */ - (BOOL)handleKeyEvent:(NSEvent*)event { if (event.type == NSEventTypeKeyUp || (event.type == NSEventTypeFlagsChanged && event.modifierFlags < _previouslyPressedFlags)) { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h index fad993b215068..2607242cea4ce 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h @@ -4,7 +4,6 @@ #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeySecondaryResponder.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h" From ff6577fffd1c3d3e0f212ae0043b6526b8847ada Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 9 Mar 2022 16:49:45 -0800 Subject: [PATCH 07/11] Put everything into FlutterKeyboardViewDelegate --- .../framework/Source/FlutterKeyboardManager.h | 6 +----- .../Source/FlutterKeyboardManager.mm | 8 +++----- .../Source/FlutterKeyboardManagerUnittests.mm | 18 ++++++++--------- .../Source/FlutterKeyboardViewDelegate.h | 20 +++++++++++++++++++ .../framework/Source/FlutterViewController.mm | 12 ++++++++++- 5 files changed, 43 insertions(+), 21 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h index f8a55771bdba1..af723a7d2b837 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h @@ -36,14 +36,10 @@ typedef _Nullable _NSResponderPtr (^NextResponderProvider)(); /** * Create a keyboard manager. * - * The |engine| is a weak reference, used for embedder APIs and channel - * messages. - * * The |viewDelegate| is a weak reference, typically implemented by * |FlutterViewController|. */ -- (nonnull instancetype)initWithEngine:(nonnull FlutterEngine*)engine - viewDelegate:(nonnull id)viewDelegate; +- (nonnull instancetype)initWithViewDelegate:(nonnull id)viewDelegate; /** * Processes a key event. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm index f5504e10224dd..fd13981c69206 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm @@ -35,19 +35,17 @@ @implementation FlutterKeyboardManager { NextResponderProvider _getNextResponder; } -- (nonnull instancetype)initWithEngine:(nonnull FlutterEngine*)engine - viewDelegate:(nonnull id)viewDelegate { +- (nonnull instancetype)initWithViewDelegate:(nonnull id)viewDelegate { self = [super init]; if (self != nil) { _viewDelegate = viewDelegate; _primaryResponders = [[NSMutableArray alloc] init]; - __weak FlutterEngine* weakEngine = engine; [self addPrimaryResponder:[[FlutterEmbedderKeyResponder alloc] initWithSendEvent:^(const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* userData) { - [weakEngine sendKeyEvent:event + [_viewDelegate sendKeyEvent:event callback:callback userData:userData]; }]]; @@ -55,7 +53,7 @@ - (nonnull instancetype)initWithEngine:(nonnull FlutterEngine*)engine addPrimaryResponder:[[FlutterChannelKeyResponder alloc] initWithChannel:[FlutterBasicMessageChannel messageChannelWithName:@"flutter/keyevent" - binaryMessenger:engine.binaryMessenger + binaryMessenger:[_viewDelegate getBinaryMessenger] codec:[FlutterJSONMessageCodec sharedInstance]]]]; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm index 9294e4c65c378..ea11e5548a0e2 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm @@ -106,14 +106,8 @@ - (nonnull instancetype)init { [self respondEmbedderCallsWith:FALSE]; [self respondTextInputWith:FALSE]; - id engineMock = OCMStrictClassMock([FlutterEngine class]); - OCMStub( // NOLINT(google-objc-avoid-throwing-exception) - [engineMock binaryMessenger]) - .andReturn(engineMock); - OCMStub([engineMock sendKeyEvent:FlutterKeyEvent {} callback:nil userData:nil]) - .ignoringNonObjectArgs() - .andCall(self, @selector(handleEmbedderEvent:callback:userData:)); - OCMStub([engineMock sendOnChannel:@"flutter/keyevent" + id messengerMock = OCMStrictProtocolMock(@protocol(FlutterBinaryMessenger)); + OCMStub([messengerMock sendOnChannel:@"flutter/keyevent" message:[OCMArg any] binaryReply:[OCMArg any]]) .andCall(self, @selector(handleChannelMessage:message:binaryReply:)); @@ -122,9 +116,13 @@ - (nonnull instancetype)init { OCMStub([viewDelegateMock nextResponder]).andReturn(_nextResponder); OCMStub([viewDelegateMock onTextInputKeyEvent:[OCMArg any]]) .andCall(self, @selector(handleTextInputKeyEvent:)); + OCMStub([viewDelegateMock getBinaryMessenger]) + .andReturn(messengerMock); + OCMStub([viewDelegateMock sendKeyEvent:FlutterKeyEvent {} callback:nil userData:nil]) + .ignoringNonObjectArgs() + .andCall(self, @selector(handleEmbedderEvent:callback:userData:)); - _manager = [[FlutterKeyboardManager alloc] initWithEngine:engineMock - viewDelegate:viewDelegateMock]; + _manager = [[FlutterKeyboardManager alloc] initWithViewDelegate:viewDelegateMock]; return self; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h index d6ea759c323f8..580fe045b419a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h @@ -4,6 +4,8 @@ #import +#import "flutter/shell/platform/embedder/embedder.h" + /** * An interface for a class that can provides |FlutterKeyboardManager| with * platform-related features. @@ -22,6 +24,24 @@ */ @property(nonatomic, readonly, nullable) NSResponder* nextResponder; +/** + * Dispatch events to the framework to be processed by |HardwareKeyboard|. + * + * This method typically forwards events to + * |FlutterEngine.sendKeyEvent:callback:userData:|. + */ +- (void)sendKeyEvent:(const FlutterKeyEvent&)event + callback:(nullable FlutterKeyEventCallback)callback + userData:(nullable void*)userData; + +/** + * Get a binary messenger to send channel messages with. + * + * This method is used to create the key data channel and typically + * forwards to |FlutterEngine.binaryMessenger|. + */ +- (nonnull id)getBinaryMessenger; + /** * Dispatch events that are not handled by the keyboard event handlers * to the text input handler. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index ba2e0a2200ad4..b7caab0758c2e 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -446,7 +446,7 @@ - (void)configureTrackingArea { - (void)initializeKeyboard { __weak FlutterViewController* weakSelf = self; _textInputPlugin = [[FlutterTextInputPlugin alloc] initWithViewController:weakSelf]; - _keyboardManager = [[FlutterKeyboardManager alloc] initWithEngine:_engine viewDelegate:weakSelf]; + _keyboardManager = [[FlutterKeyboardManager alloc] initWithViewDelegate:weakSelf]; } - (void)addInternalPlugins { @@ -661,6 +661,16 @@ - (void)viewDidReshape:(NSView*)view { #pragma mark - FlutterKeyboardViewDelegate +- (void)sendKeyEvent:(const FlutterKeyEvent&)event + callback:(nullable FlutterKeyEventCallback)callback + userData:(nullable void*)userData { + [_engine sendKeyEvent:event callback:callback userData:userData]; +} + +- (id)getBinaryMessenger { + return _engine.binaryMessenger; +} + - (BOOL)onTextInputKeyEvent:(nonnull NSEvent*)event { return [_textInputPlugin handleKeyEvent:event]; } From 54b9f87a15c32dc718c8d7c6d620f4eb42106b43 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 9 Mar 2022 16:51:52 -0800 Subject: [PATCH 08/11] Format --- .../macos/framework/Source/FlutterKeyboardManager.mm | 7 ++++--- .../framework/Source/FlutterKeyboardManagerUnittests.mm | 7 +++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm index fd13981c69206..a7c3eb8657739 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm @@ -46,14 +46,15 @@ - (nonnull instancetype)initWithViewDelegate:(nonnull id Date: Wed, 9 Mar 2022 16:53:45 -0800 Subject: [PATCH 09/11] Add to build. --- shell/platform/darwin/macos/BUILD.gn | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn index f50b4209fed31..2ddb5babbc1d5 100644 --- a/shell/platform/darwin/macos/BUILD.gn +++ b/shell/platform/darwin/macos/BUILD.gn @@ -84,6 +84,7 @@ source_set("flutter_framework_source") { "framework/Source/FlutterKeyPrimaryResponder.h", "framework/Source/FlutterKeyboardManager.h", "framework/Source/FlutterKeyboardManager.mm", + "framework/Source/FlutterKeyboardViewDelegate.h", "framework/Source/FlutterMacOSExternalTexture.h", "framework/Source/FlutterMacOSExternalTexture.h", "framework/Source/FlutterMetalCompositor.h", From 3d2269766671afa5bb8091478ef4d82fbdf4b251 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 9 Mar 2022 16:54:05 -0800 Subject: [PATCH 10/11] Remove from licenses --- ci/licenses_golden/licenses_flutter | 1 - 1 file changed, 1 deletion(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 9c845ccc21be5..3b86652e0ef84 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1443,7 +1443,6 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterFakeKey FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterIndirectScribbleDelegate.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyPrimaryResponder.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeySecondaryResponder.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardManager.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardManager.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardManagerTest.mm From 76f9064b2c6c62ebe2e404b1201c4dbaf3dc3294 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 9 Mar 2022 17:33:06 -0800 Subject: [PATCH 11/11] Licenses --- ci/licenses_golden/licenses_flutter | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 3b86652e0ef84..a07b05d5bd2ca 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1443,10 +1443,10 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterFakeKey FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterIndirectScribbleDelegate.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyPrimaryResponder.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeySecondaryResponder.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardManager.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardManager.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardManagerTest.mm -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterKeyboardViewDelegate.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h @@ -1582,10 +1582,10 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCom FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyPrimaryResponder.h -FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeySecondaryResponder.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManager.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardManagerUnittests.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterKeyboardViewDelegate.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMacOSExternalTexture.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.mm