From 87ba06a5778bea7a585a428589b10806c799f281 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 9 Nov 2020 12:42:09 -0800 Subject: [PATCH 01/12] Let touch events go through --- .../framework/Source/FlutterPlatformViews.mm | 37 +++++- .../Source/FlutterPlatformViewsTest.mm | 115 ++++++++++++++++++ 2 files changed, 148 insertions(+), 4 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 16da52e578f9b..d299b3677c6f2 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -876,6 +876,13 @@ @implementation ForwardingGestureRecognizer { fml::WeakPtr _platformViewsController; // Counting the pointers that has started in one touch sequence. NSInteger _currentTouchPointersCount; + // We can't dispatch events to the framework without this back pointer. + // This is a weak reference, the ForwardingGestureRecognizer is owned by the + // FlutterTouchInterceptingView which is strong referenced only by the FlutterView, + // which is strongly referenced by the FlutterViewController. + // So this is safe as when FlutterView is deallocated the reference to ForwardingGestureRecognizer + // will go away. + UIViewController* _flutterViewController; } - (instancetype)initWithTarget:(id)target @@ -892,16 +899,31 @@ - (instancetype)initWithTarget:(id)target } - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { - [_platformViewsController->getFlutterViewController() touchesBegan:touches withEvent:event]; + if (!_flutterViewController) { + // We try to get a reference to `_flutterViewController` before handling gestures. + // Once `_flutterViewController` is available, we keep this weak reference at least + // until the end of the touch event. + _flutterViewController = _platformViewsController->getFlutterViewController(); + if (!_flutterViewController) { + return; + } + } + [_flutterViewController touchesBegan:touches withEvent:event]; _currentTouchPointersCount += touches.count; } - (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { - [_platformViewsController->getFlutterViewController() touchesMoved:touches withEvent:event]; + if (!_flutterViewController) { + return; + } + [_flutterViewController touchesMoved:touches withEvent:event]; } - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { - [_platformViewsController->getFlutterViewController() touchesEnded:touches withEvent:event]; + if (!_flutterViewController) { + return; + } + [_flutterViewController touchesEnded:touches withEvent:event]; _currentTouchPointersCount -= touches.count; // Touches in one touch sequence are sent to the touchesEnded method separately if different // fingers stop touching the screen at different time. So one touchesEnded method triggering does @@ -910,12 +932,19 @@ - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { if (_currentTouchPointersCount == 0) { self.state = UIGestureRecognizerStateFailed; } + // Reset _flutterViewController if the engine asked to remove reference. + _flutterViewController = _platformViewsController->getFlutterViewController(); } - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { - [_platformViewsController->getFlutterViewController() touchesCancelled:touches withEvent:event]; + if (!_flutterViewController) { + return; + } + [_flutterViewController touchesCancelled:touches withEvent:event]; _currentTouchPointersCount = 0; self.state = UIGestureRecognizerStateFailed; + // Reset _flutterViewController if the engine asked to remove reference. + _flutterViewController = _platformViewsController->getFlutterViewController(); } - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index f2ec1a7f1b399..27defd3266ba2 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -596,6 +596,121 @@ - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { flutterPlatformViewsController->Reset(); } +- (void)testSetFlutterViewControllerToNilInTheMiddleOfTouchEventShouldStillAllowGesturesToBeHandled { + flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; + auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, + /*platform=*/thread_task_runner, + /*raster=*/thread_task_runner, + /*ui=*/thread_task_runner, + /*io=*/thread_task_runner); + auto surface_factory = flutter::IOSSurfaceFactory::Create(flutter::IOSRenderingAPI::kSoftware); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, + /*ios_surface_factory=*/surface_factory, + /*task_runners=*/runners); + + auto flutterPlatformViewsController = + std::make_shared(surface_factory); + surface_factory->SetPlatformViewsController(flutterPlatformViewsController); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + + XCTAssertNotNil(gMockPlatformView); + + // Find touch inteceptor view + UIView* touchInteceptorView = gMockPlatformView; + while (touchInteceptorView != nil && + ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) { + touchInteceptorView = touchInteceptorView.superview; + } + XCTAssertNotNil(touchInteceptorView); + + // Find ForwardGestureRecognizer + UIGestureRecognizer* forwardGectureRecognizer = nil; + for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) { + if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) { + forwardGectureRecognizer = gestureRecognizer; + break; + } + } + UIViewController* mockFlutterViewContoller = OCMClassMock([UIViewController class]); + { + // ***** Sequence 1, finishing touch event with touchEnded ***** // + + flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + + NSSet* touches1 = OCMClassMock([NSSet class]); + UIEvent* event1 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; + OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + + flutterPlatformViewsController->SetFlutterViewController(nil); + + // Allow the touch event to finish + NSSet* touches2 = OCMClassMock([NSSet class]); + UIEvent* event2 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2]; + OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]); + + NSSet* touches3 = OCMClassMock([NSSet class]); + UIEvent* event3 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesEnded:touches3 withEvent:event3]; + OCMVerify([mockFlutterViewContoller touchesEnded:touches3 withEvent:event3]); + + // Now the 2nd touch event should not be allowed. + NSSet* touches4 = OCMClassMock([NSSet class]); + UIEvent* event4 = OCMClassMock([UIEvent class]); + mockFlutterViewContoller = OCMClassMock([UIViewController class]); + [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4]; + OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]); + } + + { + // ***** Sequence 2, finishing touch event with touchCancelled ***** // + flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + + NSSet* touches1 = OCMClassMock([NSSet class]); + UIEvent* event1 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; + OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + + flutterPlatformViewsController->SetFlutterViewController(nil); + + // Allow the touch event to finish + NSSet* touches2 = OCMClassMock([NSSet class]); + UIEvent* event2 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2]; + OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]); + + NSSet* touches3 = OCMClassMock([NSSet class]); + UIEvent* event3 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesCancelled:touches3 withEvent:event3]; + OCMVerify([mockFlutterViewContoller touchesCancelled:touches3 withEvent:event3]); + + // Now the 2nd touch event should not be allowed. + NSSet* touches4 = OCMClassMock([NSSet class]); + UIEvent* event4 = OCMClassMock([UIEvent class]); + mockFlutterViewContoller = OCMClassMock([UIViewController class]); + [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4]; + OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]); + } + + flutterPlatformViewsController->Reset(); +} + - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashing { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); From dd80223eb97076dc8adb6d7f215299799c3e4f6a Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 9 Nov 2020 12:42:26 -0800 Subject: [PATCH 02/12] format --- .../darwin/ios/framework/Source/FlutterPlatformViewsTest.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 27defd3266ba2..b1cda6b5cf720 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -596,7 +596,8 @@ - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { flutterPlatformViewsController->Reset(); } -- (void)testSetFlutterViewControllerToNilInTheMiddleOfTouchEventShouldStillAllowGesturesToBeHandled { +- (void) + testSetFlutterViewControllerToNilInTheMiddleOfTouchEventShouldStillAllowGesturesToBeHandled { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); flutter::TaskRunners runners(/*label=*/self.name.UTF8String, From c1529b354bd006276d437754f40cb932be9c1e2e Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 9 Nov 2020 16:29:45 -0800 Subject: [PATCH 03/12] review --- .../framework/Source/FlutterPlatformViews.mm | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 672b6754c4842..3402838782b95 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -895,30 +895,16 @@ - (instancetype)initWithTarget:(id)target } - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { - if (!_flutterViewController) { - // We try to get a reference to `_flutterViewController` before handling gestures. - // Once `_flutterViewController` is available, we keep this weak reference at least - // until the end of the touch event. - _flutterViewController = _platformViewsController->getFlutterViewController(); - if (!_flutterViewController) { - return; - } - } + _flutterViewController = _platformViewsController->getFlutterViewController(); [_flutterViewController touchesBegan:touches withEvent:event]; _currentTouchPointersCount += touches.count; } - (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { - if (!_flutterViewController) { - return; - } [_flutterViewController touchesMoved:touches withEvent:event]; } - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { - if (!_flutterViewController) { - return; - } [_flutterViewController touchesEnded:touches withEvent:event]; _currentTouchPointersCount -= touches.count; // Touches in one touch sequence are sent to the touchesEnded method separately if different @@ -933,14 +919,9 @@ - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { } - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { - if (!_flutterViewController) { - return; - } [_flutterViewController touchesCancelled:touches withEvent:event]; _currentTouchPointersCount = 0; self.state = UIGestureRecognizerStateFailed; - // Reset _flutterViewController if the engine asked to remove reference. - _flutterViewController = _platformViewsController->getFlutterViewController(); } - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer From d6433a36d8dc0a8b0f9289ec93ce9dfd92b110c0 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 9 Nov 2020 19:16:40 -0800 Subject: [PATCH 04/12] only update flutter view controller if the touchBegan starts a new sequence --- .../framework/Source/FlutterPlatformViews.mm | 6 +- .../Source/FlutterPlatformViewsTest.mm | 90 +++++++++++++++++-- .../IosUnitTests.xcodeproj/project.pbxproj | 2 + 3 files changed, 89 insertions(+), 9 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 3402838782b95..4d0d10db00eac 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -895,7 +895,9 @@ - (instancetype)initWithTarget:(id)target } - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { - _flutterViewController = _platformViewsController->getFlutterViewController(); + if (_currentTouchPointersCount == 0) { + _flutterViewController = _platformViewsController->getFlutterViewController(); + } [_flutterViewController touchesBegan:touches withEvent:event]; _currentTouchPointersCount += touches.count; } @@ -914,8 +916,6 @@ - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { if (_currentTouchPointersCount == 0) { self.state = UIGestureRecognizerStateFailed; } - // Reset _flutterViewController if the engine asked to remove reference. - _flutterViewController = _platformViewsController->getFlutterViewController(); } - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 32cf421970bc5..c585a82d6e7ea 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -596,8 +596,7 @@ - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { flutterPlatformViewsController->Reset(); } -- (void) - testSetFlutterViewControllerToNilInTheMiddleOfTouchEventShouldStillAllowGesturesToBeHandled { +- (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGesturesToBeHandled { flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); flutter::TaskRunners runners(/*label=*/self.name.UTF8String, @@ -660,7 +659,7 @@ - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { flutterPlatformViewsController->SetFlutterViewController(nil); - // Allow the touch event to finish + // Allow the touch events to finish NSSet* touches2 = OCMClassMock([NSSet class]); UIEvent* event2 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2]; @@ -671,12 +670,17 @@ - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { [forwardGectureRecognizer touchesEnded:touches3 withEvent:event3]; OCMVerify([mockFlutterViewContoller touchesEnded:touches3 withEvent:event3]); - // Now the 2nd touch event should not be allowed. + // Now the 2nd touch sequence should not be allowed. NSSet* touches4 = OCMClassMock([NSSet class]); UIEvent* event4 = OCMClassMock([UIEvent class]); mockFlutterViewContoller = OCMClassMock([UIViewController class]); [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4]; OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]); + + NSSet* touches5 = OCMClassMock([NSSet class]); + UIEvent* event5 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; + OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); } { @@ -690,7 +694,7 @@ - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { flutterPlatformViewsController->SetFlutterViewController(nil); - // Allow the touch event to finish + // Allow the touch events to finish NSSet* touches2 = OCMClassMock([NSSet class]); UIEvent* event2 = OCMClassMock([UIEvent class]); [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2]; @@ -701,12 +705,86 @@ - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { [forwardGectureRecognizer touchesCancelled:touches3 withEvent:event3]; OCMVerify([mockFlutterViewContoller touchesCancelled:touches3 withEvent:event3]); - // Now the 2nd touch event should not be allowed. + // Now the 2nd touch sequence should not be allowed. NSSet* touches4 = OCMClassMock([NSSet class]); UIEvent* event4 = OCMClassMock([UIEvent class]); mockFlutterViewContoller = OCMClassMock([UIViewController class]); [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4]; OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]); + + NSSet* touches5 = OCMClassMock([NSSet class]); + UIEvent* event5 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; + OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); + } + + { + // ***** Sequence 3, multile touches in one sequence with setting flutter view controllers in + // between ***** // + flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller); + + NSSet* touches1 = OCMClassMock([NSSet class]); + OCMStub([touches1 count]).andReturn(1); + UIEvent* event1 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1]; + OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]); + + UIViewController* mockFlutterViewContoller2 = OCMClassMock([UIViewController class]); + flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller2); + + // Touch events should still send to the old FlutterViewController if FlutterViewController + // is updated in between. + NSSet* touches2 = OCMClassMock([NSSet class]); + OCMStub([touches2 count]).andReturn(1); + UIEvent* event2 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2]; + OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]); + OCMReject([mockFlutterViewContoller2 touchesBegan:touches2 withEvent:event2]); + + NSSet* touches3 = OCMClassMock([NSSet class]); + OCMStub([touches3 count]).andReturn(1); + UIEvent* event3 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesMoved:touches3 withEvent:event3]; + OCMVerify([mockFlutterViewContoller touchesMoved:touches3 withEvent:event3]); + OCMReject([mockFlutterViewContoller2 touchesMoved:touches3 withEvent:event3]); + + NSSet* touches4 = OCMClassMock([NSSet class]); + OCMStub([touches4 count]).andReturn(1); + UIEvent* event4 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesEnded:touches4 withEvent:event4]; + OCMVerify([mockFlutterViewContoller touchesEnded:touches4 withEvent:event4]); + OCMReject([mockFlutterViewContoller2 touchesEnded:touches4 withEvent:event4]); + + NSSet* touches5 = OCMClassMock([NSSet class]); + OCMStub([touches5 count]).andReturn(1); + UIEvent* event5 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5]; + OCMVerify([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]); + OCMReject([mockFlutterViewContoller2 touchesEnded:touches5 withEvent:event5]); + + // Now the 2nd touch sequence should go to the new FlutterViewController + + NSSet* touches6 = OCMClassMock([NSSet class]); + OCMStub([touches6 count]).andReturn(1); + UIEvent* event6 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesBegan:touches6 withEvent:event6]; + OCMVerify([mockFlutterViewContoller2 touchesBegan:touches6 withEvent:event6]); + OCMReject([mockFlutterViewContoller touchesBegan:touches6 withEvent:event6]); + + // Allow the touch events to finish + NSSet* touches7 = OCMClassMock([NSSet class]); + OCMStub([touches7 count]).andReturn(1); + UIEvent* event7 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesMoved:touches7 withEvent:event7]; + OCMVerify([mockFlutterViewContoller2 touchesMoved:touches7 withEvent:event7]); + OCMReject([mockFlutterViewContoller touchesMoved:touches7 withEvent:event7]); + + NSSet* touches8 = OCMClassMock([NSSet class]); + OCMStub([touches8 count]).andReturn(1); + UIEvent* event8 = OCMClassMock([UIEvent class]); + [forwardGectureRecognizer touchesEnded:touches8 withEvent:event8]; + OCMVerify([mockFlutterViewContoller2 touchesEnded:touches8 withEvent:event8]); + OCMReject([mockFlutterViewContoller touchesEnded:touches8 withEvent:event8]); } flutterPlatformViewsController->Reset(); diff --git a/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj b/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj index ea69e52363610..63c04f78e9285 100644 --- a/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj +++ b/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj @@ -64,6 +64,7 @@ 0D6AB6C922BB05E200EEE540 /* IosUnitTestsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = IosUnitTestsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 0D6AB6CF22BB05E200EEE540 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0D6AB73E22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = FlutterEngineConfig.xcconfig; sourceTree = ""; }; + 68FF0A70255A3B4E00165B60 /* FlutterPlatformViewsTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterPlatformViewsTest.mm; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -96,6 +97,7 @@ 0AC2331224BA71D300A85907 /* FlutterEnginePlatformViewTest.mm */, 0AC2331924BA71D300A85907 /* FlutterPluginAppLifeCycleDelegateTest.m */, 0AC2332124BA71D300A85907 /* FlutterViewControllerTest.mm */, + 68FF0A70255A3B4E00165B60 /* FlutterPlatformViewsTest.mm */, ); name = Source; path = ../../../shell/platform/darwin/ios/framework/Source; From 48472f40d3e7ec9ad3add6541bd893e00d2f63b6 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Wed, 11 Nov 2020 09:46:47 -0800 Subject: [PATCH 05/12] add comments --- .../darwin/ios/framework/Source/FlutterPlatformViews.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 4d0d10db00eac..0857107c6881c 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -896,6 +896,8 @@ - (instancetype)initWithTarget:(id)target - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { if (_currentTouchPointersCount == 0) { + // At the start of each gesture sequence, we reset the `_flutterViewController`, + // so that all the touch events in the same sequence are forwarded to the same `_flutterViewController`. _flutterViewController = _platformViewsController->getFlutterViewController(); } [_flutterViewController touchesBegan:touches withEvent:event]; From 366196d85f1cffd0b9c6226bd5c2eb87f1a579f8 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Wed, 11 Nov 2020 16:51:56 -0800 Subject: [PATCH 06/12] review --- .../framework/Source/FlutterPlatformViews.mm | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 0857107c6881c..399c2f1834708 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -873,12 +873,10 @@ @implementation ForwardingGestureRecognizer { // Counting the pointers that has started in one touch sequence. NSInteger _currentTouchPointersCount; // We can't dispatch events to the framework without this back pointer. - // This is a weak reference, the ForwardingGestureRecognizer is owned by the - // FlutterTouchInterceptingView which is strong referenced only by the FlutterView, - // which is strongly referenced by the FlutterViewController. - // So this is safe as when FlutterView is deallocated the reference to ForwardingGestureRecognizer - // will go away. - UIViewController* _flutterViewController; + // This gesture recognizer retains the `FlutterViewController` until the + // end of a gesture sequence, that is all the touches in touchesBegan are concluded + // with |touchesCancelled| or |touchesEnded|. + fml::scoped_nsobject _flutterViewController; } - (instancetype)initWithTarget:(id)target @@ -895,21 +893,28 @@ - (instancetype)initWithTarget:(id)target } - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { + FML_DCHECK(_currentTouchPointersCount >= 0); + if (_currentTouchPointersCount < 0) { + // _currentTouchPointersCount should never be less than 0. + // To not breaking production code, we reset the count to 0 if a negative value + // is reached. + _currentTouchPointersCount = 0; + } if (_currentTouchPointersCount == 0) { // At the start of each gesture sequence, we reset the `_flutterViewController`, // so that all the touch events in the same sequence are forwarded to the same `_flutterViewController`. - _flutterViewController = _platformViewsController->getFlutterViewController(); + _flutterViewController.reset([_platformViewsController->getFlutterViewController() retain]); } - [_flutterViewController touchesBegan:touches withEvent:event]; + [_flutterViewController.get() touchesBegan:touches withEvent:event]; _currentTouchPointersCount += touches.count; } - (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { - [_flutterViewController touchesMoved:touches withEvent:event]; + [_flutterViewController.get() touchesMoved:touches withEvent:event]; } - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { - [_flutterViewController touchesEnded:touches withEvent:event]; + [_flutterViewController.get() touchesEnded:touches withEvent:event]; _currentTouchPointersCount -= touches.count; // Touches in one touch sequence are sent to the touchesEnded method separately if different // fingers stop touching the screen at different time. So one touchesEnded method triggering does @@ -917,13 +922,21 @@ - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { // UIGestureRecognizerStateFailed when all the touches in the current touch sequence is ended. if (_currentTouchPointersCount == 0) { self.state = UIGestureRecognizerStateFailed; + _flutterViewController.reset(nil); } } - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { - [_flutterViewController touchesCancelled:touches withEvent:event]; - _currentTouchPointersCount = 0; - self.state = UIGestureRecognizerStateFailed; + [_flutterViewController.get() touchesCancelled:touches withEvent:event]; + _currentTouchPointersCount -= touches.count; + // Touches in one touch sequence are sent to the touchesEnded method separately if different + // fingers stop touching the screen at different time. So one touchesEnded method triggering does + // not necessarially mean the touch sequence has ended. We Only set the state to + // UIGestureRecognizerStateFailed when all the touches in the current touch sequence is ended. + if (_currentTouchPointersCount == 0) { + self.state = UIGestureRecognizerStateFailed; + _flutterViewController.reset(nil); + } } - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer From b096e048f0113c47910544d298c4283ba84a8c26 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Wed, 11 Nov 2020 16:52:03 -0800 Subject: [PATCH 07/12] review --- .../darwin/ios/framework/Source/FlutterPlatformViews.mm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 399c2f1834708..02ea21edf9710 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -929,10 +929,6 @@ - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { [_flutterViewController.get() touchesCancelled:touches withEvent:event]; _currentTouchPointersCount -= touches.count; - // Touches in one touch sequence are sent to the touchesEnded method separately if different - // fingers stop touching the screen at different time. So one touchesEnded method triggering does - // not necessarially mean the touch sequence has ended. We Only set the state to - // UIGestureRecognizerStateFailed when all the touches in the current touch sequence is ended. if (_currentTouchPointersCount == 0) { self.state = UIGestureRecognizerStateFailed; _flutterViewController.reset(nil); From fc7d934772e401100029be43d4e2f96f380ad6fc Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Wed, 11 Nov 2020 16:52:18 -0800 Subject: [PATCH 08/12] format --- .../darwin/ios/framework/Source/FlutterPlatformViews.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 02ea21edf9710..54f9b8af1029d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -902,7 +902,8 @@ - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { } if (_currentTouchPointersCount == 0) { // At the start of each gesture sequence, we reset the `_flutterViewController`, - // so that all the touch events in the same sequence are forwarded to the same `_flutterViewController`. + // so that all the touch events in the same sequence are forwarded to the same + // `_flutterViewController`. _flutterViewController.reset([_platformViewsController->getFlutterViewController() retain]); } [_flutterViewController.get() touchesBegan:touches withEvent:event]; From b349a698cf62dfe48e7065bc57c113e35ff7ac0b Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Wed, 11 Nov 2020 17:08:36 -0800 Subject: [PATCH 09/12] review --- .../darwin/ios/framework/Source/FlutterPlatformViews.mm | 6 ------ 1 file changed, 6 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 54f9b8af1029d..78e69ddba0061 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -894,12 +894,6 @@ - (instancetype)initWithTarget:(id)target - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { FML_DCHECK(_currentTouchPointersCount >= 0); - if (_currentTouchPointersCount < 0) { - // _currentTouchPointersCount should never be less than 0. - // To not breaking production code, we reset the count to 0 if a negative value - // is reached. - _currentTouchPointersCount = 0; - } if (_currentTouchPointersCount == 0) { // At the start of each gesture sequence, we reset the `_flutterViewController`, // so that all the touch events in the same sequence are forwarded to the same From 9ee26e384f714ed7ad756c6dffd68faf07815381 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 13 Nov 2020 10:00:19 -0800 Subject: [PATCH 10/12] remove platformviewstest reference in xcode --- testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj b/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj index 63c04f78e9285..ea69e52363610 100644 --- a/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj +++ b/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj @@ -64,7 +64,6 @@ 0D6AB6C922BB05E200EEE540 /* IosUnitTestsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = IosUnitTestsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 0D6AB6CF22BB05E200EEE540 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 0D6AB73E22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = FlutterEngineConfig.xcconfig; sourceTree = ""; }; - 68FF0A70255A3B4E00165B60 /* FlutterPlatformViewsTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterPlatformViewsTest.mm; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -97,7 +96,6 @@ 0AC2331224BA71D300A85907 /* FlutterEnginePlatformViewTest.mm */, 0AC2331924BA71D300A85907 /* FlutterPluginAppLifeCycleDelegateTest.m */, 0AC2332124BA71D300A85907 /* FlutterViewControllerTest.mm */, - 68FF0A70255A3B4E00165B60 /* FlutterPlatformViewsTest.mm */, ); name = Source; path = ../../../shell/platform/darwin/ios/framework/Source; From f93bc2176c72f38a8ae090df390ad2f07c7a7559 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 13 Nov 2020 14:26:36 -0800 Subject: [PATCH 11/12] rebase test code --- .../darwin/ios/framework/Source/FlutterPlatformViewsTest.mm | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 1ca8709863ef5..fb9694a70c267 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -576,16 +576,12 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto surface_factory = flutter::IOSSurfaceFactory::Create(flutter::IOSRenderingAPI::kSoftware); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, - /*ios_surface_factory=*/surface_factory, /*task_runners=*/runners); - auto flutterPlatformViewsController = - std::make_shared(surface_factory); - surface_factory->SetPlatformViewsController(flutterPlatformViewsController); + auto flutterPlatformViewsController = std::make_shared(); FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = [[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease]; From bca3d180e7dd5ada994ab95e4b1a3c910ab86484 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 13 Nov 2020 14:33:16 -0800 Subject: [PATCH 12/12] fix the previous rebase --- .../darwin/ios/framework/Source/FlutterPlatformViewsTest.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index fb9694a70c267..0334720337049 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -576,13 +576,13 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu /*raster=*/thread_task_runner, /*ui=*/thread_task_runner, /*io=*/thread_task_runner); + auto flutterPlatformViewsController = std::make_shared(); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, /*task_runners=*/runners); - auto flutterPlatformViewsController = std::make_shared(); - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = [[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease]; flutterPlatformViewsController->RegisterViewFactory(