diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index f73dac77ae003..e10405e0f0dc5 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -43655,7 +43655,6 @@ ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterOverl ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm + ../../../flutter/LICENSE @@ -43713,8 +43712,12 @@ ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/availability ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/connection_collection.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/connection_collection.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/connection_collection_test.mm + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h + ../../../flutter/LICENSE @@ -46559,7 +46562,6 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterOverlay FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -46617,8 +46619,12 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/availability_v FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/connection_collection.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/connection_collection.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/connection_collection_test.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index f3e3389a3df2f..f63d7e1f7f4ef 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -77,7 +77,6 @@ source_set("flutter_framework_source_arc") { "framework/Source/FlutterMetalLayer.mm", "framework/Source/FlutterOverlayView.h", "framework/Source/FlutterOverlayView.mm", - "framework/Source/FlutterPlatformViews.mm", "framework/Source/FlutterPlatformViews_Internal.h", "framework/Source/FlutterPlatformViews_Internal.mm", "framework/Source/FlutterPluginAppLifeCycleDelegate.mm", @@ -110,8 +109,12 @@ source_set("flutter_framework_source_arc") { "framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.mm", "framework/Source/connection_collection.h", "framework/Source/connection_collection.mm", + "framework/Source/overlay_layer_pool.h", + "framework/Source/overlay_layer_pool.mm", "framework/Source/platform_message_response_darwin.h", "framework/Source/platform_message_response_darwin.mm", + "framework/Source/platform_views_controller.h", + "framework/Source/platform_views_controller.mm", "framework/Source/profiler_metrics_ios.h", "framework/Source/profiler_metrics_ios.mm", "framework/Source/vsync_waiter_ios.h", diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 669eb9a35a39f..71a0372048774 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -123,7 +123,7 @@ @implementation FlutterEngine { fml::WeakNSObject _viewController; fml::scoped_nsobject _publisher; - std::shared_ptr _platformViewsController; + std::shared_ptr _platformViewsController; flutter::IOSRenderingAPI _renderingApi; std::shared_ptr _profiler_metrics; std::shared_ptr _profiler; @@ -271,7 +271,7 @@ - (void)setUpApplicationLifecycleNotifications:(NSNotificationCenter*)center { - (void)recreatePlatformViewController { _renderingApi = flutter::GetRenderingAPIForProcess(FlutterView.forceSoftwareRendering); - _platformViewsController.reset(new flutter::FlutterPlatformViewsController()); + _platformViewsController.reset(new flutter::PlatformViewsController()); } - (flutter::IOSRenderingAPI)platformViewsRenderingAPI { @@ -495,7 +495,7 @@ - (FlutterViewController*)viewController { - (FlutterPlatformPlugin*)platformPlugin { return _platformPlugin.get(); } -- (std::shared_ptr&)platformViewsController { +- (std::shared_ptr&)platformViewsController { return _platformViewsController; } - (FlutterTextInputPlugin*)textInputPlugin { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h index 03c1e6ef34d7e..489dfd38c4d87 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h @@ -48,7 +48,7 @@ extern NSString* const kFlutterEngineWillDealloc; base64Encode:(bool)base64Encode; - (FlutterPlatformPlugin*)platformPlugin; -- (std::shared_ptr&)platformViewsController; +- (std::shared_ptr&)platformViewsController; - (FlutterTextInputPlugin*)textInputPlugin; - (FlutterRestorationPlugin*)restorationPlugin; - (void)launchEngine:(nullable NSString*)entrypoint diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 16e51ff0cd1d0..05e999777b097 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -6,6 +6,7 @@ #import #import #include "fml/synchronization/count_down_latch.h" +#include "shell/platform/darwin/ios/framework/Source/platform_views_controller.h" #import "flutter/fml/thread.h" #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" @@ -121,6 +122,11 @@ void UpdateAssetResolverByType(std::unique_ptr updated_a flutter::Settings settings_; }; +BOOL BlurRadiusEqualToBlurRadius(CGFloat radius1, CGFloat radius2) { + const CGFloat epsilon = 0.01; + return std::abs(radius1 - radius2) < epsilon; +} + } // namespace } // namespace flutter @@ -144,7 +150,7 @@ - (void)testFlutterViewOnlyCreateOnceInOneFrame { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -200,7 +206,7 @@ - (void)testCanCreatePlatformViewWithoutFlutterView { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -293,7 +299,7 @@ - (void)testApplyBackdropFilter { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -370,7 +376,7 @@ - (void)testApplyBackdropFilterWithCorrectFrame { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -447,7 +453,7 @@ - (void)testApplyMultipleBackdropFilters { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -525,7 +531,7 @@ - (void)testAddBackdropFilters { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -646,7 +652,7 @@ - (void)testRemoveBackdropFilters { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -792,7 +798,7 @@ - (void)testEditBackdropFilters { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -1082,7 +1088,7 @@ - (void)testApplyBackdropFilterNotDlBlurImageFilter { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -1401,7 +1407,7 @@ - (void)testCompositePlatformView { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -1462,7 +1468,7 @@ - (void)testBackdropFilterCorrectlyPushedAndReset { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -1563,7 +1569,7 @@ - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -1639,7 +1645,7 @@ - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -1711,7 +1717,7 @@ - (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -1782,7 +1788,7 @@ - (void)testClipRect { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -1858,7 +1864,7 @@ - (void)testClipRRect { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -1961,7 +1967,7 @@ - (void)testClipPath { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -2065,7 +2071,7 @@ - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -2132,7 +2138,7 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -2256,7 +2262,7 @@ - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGestu /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -2370,7 +2376,7 @@ - (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -2435,7 +2441,7 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -2506,7 +2512,7 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -2559,7 +2565,7 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -2622,7 +2628,7 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -2724,7 +2730,7 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -2911,7 +2917,7 @@ - (void)testClipMaskViewIsReused { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -3002,7 +3008,7 @@ - (void)testDifferentClipMaskViewIsUsedForEachView { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -3082,7 +3088,7 @@ - (void)testMaskViewUsesCAShapeLayerAsTheBackingLayer { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -3175,7 +3181,7 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -3286,7 +3292,7 @@ - (void)testOnlyPlatformViewsAreRemovedWhenReset { /*raster=*/GetDefaultTaskRunner(), /*ui=*/GetDefaultTaskRunner(), /*io=*/GetDefaultTaskRunner()); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -3364,7 +3370,7 @@ - (void)testLayerPool { auto ios_context = [engine iosPlatformView]->GetIosContext(); auto gr_context = ios_context->GetMainContext(); - auto pool = flutter::FlutterPlatformViewLayerPool{}; + auto pool = flutter::OverlayLayerPool{}; // Add layers to the pool. pool.CreateLayer(gr_context.get(), ios_context, MTLPixelFormatBGRA8Unorm); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index e87d1cbb15cfb..b42c267074d4f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -19,6 +19,7 @@ #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewResponder.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.h" #import "flutter/shell/platform/darwin/ios/ios_context.h" @class FlutterTouchInterceptingView; @@ -129,363 +130,6 @@ - (NSMutableArray*)backdropFilterSubviews; @end -namespace flutter { -// Converts a SkMatrix to CATransform3D. -// Certain fields are ignored in CATransform3D since SkMatrix is 3x3 and CATransform3D is 4x4. -CATransform3D GetCATransform3DFromSkMatrix(const SkMatrix& matrix); - -// Reset the anchor of `layer` to match the transform operation from flow. -// The position of the `layer` should be unchanged after resetting the anchor. -void ResetAnchor(CALayer* layer); - -CGRect GetCGRectFromSkRect(const SkRect& clipSkRect); -BOOL BlurRadiusEqualToBlurRadius(CGFloat radius1, CGFloat radius2); - -class IOSSurface; - -struct FlutterPlatformViewLayer { - FlutterPlatformViewLayer(const fml::scoped_nsobject& overlay_view, - const fml::scoped_nsobject& overlay_view_wrapper, - std::unique_ptr ios_surface, - std::unique_ptr surface); - - ~FlutterPlatformViewLayer(); - - fml::scoped_nsobject overlay_view; - fml::scoped_nsobject overlay_view_wrapper; - std::unique_ptr ios_surface; - std::unique_ptr surface; - - // Whether a frame for this layer was submitted. - bool did_submit_last_frame; - - // The GrContext that is currently used by the overlay surfaces. - // We track this to know when the GrContext for the Flutter app has changed - // so we can update the overlay with the new context. - GrDirectContext* gr_context; - - void UpdateViewState(UIView* flutter_view, SkRect rect, int64_t view_id, int64_t overlay_id); -}; - -/// @brief Storage for Overlay layers across frames. -/// -/// Note: this class does not synchronize access to its layers or any layer removal. As it -/// is currently used, layers must be created on the platform thread but other methods of -/// it are called on the raster thread. This is safe as overlay layers are only ever added -/// while the raster thread is latched. -class FlutterPlatformViewLayerPool { - public: - FlutterPlatformViewLayerPool() = default; - - ~FlutterPlatformViewLayerPool() = default; - - /// @brief Gets a layer from the pool if available. - /// - /// The layer is marked as used until [RecycleLayers] is called. - std::shared_ptr GetNextLayer(); - - /// @brief Create a new overlay layer. - /// - /// This method can only be called on the Platform thread. - void CreateLayer(GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - MTLPixelFormat pixel_format); - - /// @brief Removes unused layers from the pool. Returns the unused layers. - std::vector> RemoveUnusedLayers(); - - /// @brief Marks the layers in the pool as available for reuse. - void RecycleLayers(); - - /// @brief The count of layers currently in the pool. - size_t size() const; - - private: - // The index of the entry in the layers_ vector that determines the beginning of the unused - // layers. For example, consider the following vector: - // _____ - // | 0 | - /// |---| - /// | 1 | <-- available_layer_index_ - /// |---| - /// | 2 | - /// |---| - /// - /// This indicates that entries starting from 1 can be reused meanwhile the entry at position 0 - /// cannot be reused. - size_t available_layer_index_ = 0; - std::vector> layers_; - - FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewLayerPool); -}; - -class FlutterPlatformViewsController { - public: - FlutterPlatformViewsController(); - - ~FlutterPlatformViewsController(); - - fml::WeakPtr GetWeakPtr(); - - void SetTaskRunner(const fml::RefPtr& platform_task_runner); - - void SetFlutterView(UIView* flutter_view) __attribute__((cf_audited_transfer)); - - void SetFlutterViewController(UIViewController* flutter_view_controller) - __attribute__((cf_audited_transfer)); - - UIViewController* getFlutterViewController() - __attribute__((cf_audited_transfer)); - - void RegisterViewFactory( - NSObject* factory, - NSString* factoryId, - FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy) - __attribute__((cf_audited_transfer)); - - // Called at the beginning of each frame. - void BeginFrame(SkISize frame_size); - - // Indicates that we don't compisite any platform views or overlays during this frame. - // Also reverts the composition_order_ to its original state at the beginning of the frame. - void CancelFrame(); - - // Runs on the raster thread. - void PrerollCompositeEmbeddedView(int64_t view_id, - std::unique_ptr params); - - size_t EmbeddedViewCount() const; - - size_t LayerPoolSize() const; - - // Returns the `FlutterPlatformView`'s `view` object associated with the view_id. - // - // If the `FlutterPlatformViewsController` does not contain any `FlutterPlatformView` object or - // a `FlutterPlatformView` object associated with the view_id cannot be found, the method - // returns nil. - UIView* GetPlatformViewByID(int64_t view_id); - - // Returns the `FlutterTouchInterceptingView` with the view_id. - // - // If the `FlutterPlatformViewsController` does not contain any `FlutterPlatformView` object or - // a `FlutterPlatformView` object associated with the view_id cannot be found, the method - // returns nil. - FlutterTouchInterceptingView* GetFlutterTouchInterceptingViewByID(int64_t view_id); - - // Runs on the raster thread. - PostPrerollResult PostPrerollAction( - const fml::RefPtr& raster_thread_merger); - - // Runs on the raster thread. - void EndFrame(bool should_resubmit_frame, - const fml::RefPtr& raster_thread_merger); - - // Return the Canvas for the overlay slice for the given platform view. - // - // Runs on the raster thread. - DlCanvas* CompositeEmbeddedView(int64_t view_id); - - // Discards all platform views instances and auxiliary resources. - // - // Runs on the raster thread. - void Reset(); - - // Encode rendering for the Flutter overlay views and queue up perform platform view mutations. - // - // Runs on the raster thread. - bool SubmitFrame(GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - std::unique_ptr frame); - - void OnMethodCall(FlutterMethodCall* call, FlutterResult result) - __attribute__((cf_audited_transfer)); - - // Returns the platform view id if the platform view (or any of its descendant view) is the first - // responder. Returns -1 if no such platform view is found. - long FindFirstResponderPlatformViewId(); - - // Pushes backdrop filter mutation to the mutator stack of each visited platform view. - void PushFilterToVisitedPlatformViews(const std::shared_ptr& filter, - const SkRect& filter_rect); - - // Pushes the view id of a visted platform view to the list of visied platform views. - void PushVisitedPlatformView(int64_t view_id) { visited_platform_views_.push_back(view_id); } - - // Visible for testing. - void CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params); - - const EmbeddedViewParams& GetCompositionParams(int64_t view_id) const { - return current_composition_params_.find(view_id)->second; - } - - private: - struct LayerData { - SkRect rect; - int64_t view_id; - int64_t overlay_id; - std::shared_ptr layer; - }; - - using LayersMap = std::unordered_map; - - // Update the buffers and mutate the platform views in CATransaction. - // - // Runs on the platform thread. - void PerformSubmit(const LayersMap& platform_view_layers, - std::unordered_map& current_composition_params, - const std::unordered_set& views_to_recomposite, - const std::vector& composition_order, - const std::vector>& unused_layers, - const std::vector>& surface_frames); - - /// @brief Populate any missing overlay layers. - /// - /// This requires posting a task to the platform thread and blocking on its completion. - void CreateMissingOverlays(GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - size_t required_overlay_layers); - - void OnCreate(FlutterMethodCall* call, FlutterResult result) __attribute__((cf_audited_transfer)); - void OnDispose(FlutterMethodCall* call, FlutterResult result) - __attribute__((cf_audited_transfer)); - void OnAcceptGesture(FlutterMethodCall* call, FlutterResult result) - __attribute__((cf_audited_transfer)); - void OnRejectGesture(FlutterMethodCall* call, FlutterResult result) - __attribute__((cf_audited_transfer)); - - /// @brief Return all views to be disposed on the platform thread. - std::vector GetViewsToDispose(); - - void ClipViewSetMaskView(UIView* clipView) __attribute__((cf_audited_transfer)); - - // Applies the mutators in the mutators_stack to the UIView chain that was constructed by - // `ReconstructClipViewsChain` - // - // Clips are applied to the `embedded_view`'s super view(|ChildClippingView|) using a - // |FlutterClippingMaskView|. Transforms are applied to `embedded_view` - // - // The `bounding_rect` is the final bounding rect of the PlatformView - // (EmbeddedViewParams::finalBoundingRect). If a clip mutator's rect contains the final bounding - // rect of the PlatformView, the clip mutator is not applied for performance optimization. - void ApplyMutators(const MutatorsStack& mutators_stack, - UIView* embedded_view, - const SkRect& bounding_rect) __attribute__((cf_audited_transfer)); - - std::shared_ptr GetExistingLayer(); - - // Runs on the platform thread. - void CreateLayer(GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - MTLPixelFormat pixel_format); - - // Removes overlay views and platform views that aren't needed in the current frame. - // Must run on the platform thread. - void RemoveUnusedLayers( - const std::vector>& unused_layers, - const std::vector& composition_order); - - // Appends the overlay views and platform view and sets their z index based on the composition - // order. - void BringLayersIntoView(const LayersMap& layer_map, - const std::vector& composition_order); - - // Resets the state of the frame. - void ResetFrameState(); - - // The pool of reusable view layers. The pool allows to recycle layer in each frame. - std::unique_ptr layer_pool_; - - // The platform view's |EmbedderViewSlice| keyed off the view id, which contains any subsequent - // operation until the next platform view or the end of the last leaf node in the layer tree. - // - // The Slices are deleted by the FlutterPlatformViewsController.reset(). - std::unordered_map> slices_; - - fml::scoped_nsobject channel_; - fml::scoped_nsobject flutter_view_; - fml::scoped_nsobject> flutter_view_controller_; - fml::scoped_nsobject mask_view_pool_; - std::unordered_map>> - factories_; - - // The FlutterPlatformViewGestureRecognizersBlockingPolicy for each type of platform view. - std::unordered_map - gesture_recognizers_blocking_policies_; - - /// The size of the current onscreen surface in physical pixels. - SkISize frame_size_; - - /// The task runner for posting tasks to the platform thread. - fml::RefPtr platform_task_runner_; - - /// Each of the following structs stores part of the platform view hierarchy according to its - /// ID. - /// - /// This data must only be accessed on the platform thread. - struct PlatformViewData { - fml::scoped_nsobject> view; - fml::scoped_nsobject touch_interceptor; - fml::scoped_nsobject root_view; - }; - - /// This data must only be accessed on the platform thread. - std::unordered_map platform_views_; - - /// The composition parameters for each platform view. - /// - /// This state is only modified on the raster thread. - std::unordered_map current_composition_params_; - - /// Method channel `OnDispose` calls adds the views to be disposed to this set to be disposed on - /// the next frame. - /// - /// This state is modified on both the platform and raster thread. - std::unordered_set views_to_dispose_; - - /// view IDs in composition order. - /// - /// This state is only modified on the raster thread. - std::vector composition_order_; - - /// platform view IDs visited during layer tree composition. - /// - /// This state is only modified on the raster thread. - std::vector visited_platform_views_; - - /// Only composite platform views in this set. - /// - /// This state is only modified on the raster thread. - std::unordered_set views_to_recomposite_; - -#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG - /// A set to keep track of embedded views that do not have (0, 0) origin. - /// An insertion triggers a warning message about non-zero origin logged on the debug console. - /// See https://github.com/flutter/flutter/issues/109700 for details. - std::unordered_set non_zero_origin_views_; -#endif - - /// @brief The composition order from the previous thread. - /// - /// Only accessed from the platform thread. - std::vector previous_composition_order_; - - /// Whether the previous frame had any platform views in active composition order. - /// - /// This state is tracked so that the first frame after removing the last platform view - /// runs through the platform view rendering code path, giving us a chance to remove the - /// platform view from the UIView hierarchy. - /// - /// Only accessed from the raster thread. - bool had_platform_views_ = false; - - // WeakPtrFactory must be the last member. - std::unique_ptr> weak_factory_; - - FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); -}; - -} // namespace flutter - // A UIView that is used as the parent for embedded UIViews. // // This view has 2 roles: @@ -494,7 +138,7 @@ class FlutterPlatformViewsController { @interface FlutterTouchInterceptingView : UIView - (instancetype)initWithEmbeddedView:(UIView*)embeddedView platformViewsController: - (fml::WeakPtr)platformViewsController + (fml::WeakPtr)platformViewsController gestureRecognizersBlockingPolicy: (FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm index 83d01ec2c6901..2a90c27a6a415 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -11,38 +11,11 @@ FLUTTER_ASSERT_ARC static constexpr int kMaxPointsInVerb = 4; -static constexpr NSUInteger kFlutterClippingMaskViewPoolCapacity = 5; -namespace flutter { - -FlutterPlatformViewLayer::FlutterPlatformViewLayer( - const fml::scoped_nsobject& overlay_view, - const fml::scoped_nsobject& overlay_view_wrapper, - std::unique_ptr ios_surface, - std::unique_ptr surface) - : overlay_view(overlay_view), - overlay_view_wrapper(overlay_view_wrapper), - ios_surface(std::move(ios_surface)), - surface(std::move(surface)){}; - -FlutterPlatformViewLayer::~FlutterPlatformViewLayer() = default; - -FlutterPlatformViewsController::FlutterPlatformViewsController() - : layer_pool_(std::make_unique()), - weak_factory_(std::make_unique>(this)) { - mask_view_pool_.reset( - [[FlutterClippingMaskViewPool alloc] initWithCapacity:kFlutterClippingMaskViewPoolCapacity]); -}; - -FlutterPlatformViewsController::~FlutterPlatformViewsController() = default; - -void FlutterPlatformViewsController::SetTaskRunner( - const fml::RefPtr& platform_task_runner) { - platform_task_runner_ = platform_task_runner; -} - -fml::WeakPtr FlutterPlatformViewsController::GetWeakPtr() { - return weak_factory_->GetWeakPtr(); +namespace { +CGRect GetCGRectFromSkRect(const SkRect& clipSkRect) { + return CGRectMake(clipSkRect.fLeft, clipSkRect.fTop, clipSkRect.fRight - clipSkRect.fLeft, + clipSkRect.fBottom - clipSkRect.fTop); } CATransform3D GetCATransform3DFromSkMatrix(const SkMatrix& matrix) { @@ -59,24 +32,7 @@ CATransform3D GetCATransform3DFromSkMatrix(const SkMatrix& matrix) { transform.m24 = matrix.getPerspY(); return transform; } - -void ResetAnchor(CALayer* layer) { - // Flow uses (0, 0) to apply transform matrix so we need to match that in Quartz. - layer.anchorPoint = CGPointZero; - layer.position = CGPointZero; -} - -CGRect GetCGRectFromSkRect(const SkRect& clipSkRect) { - return CGRectMake(clipSkRect.fLeft, clipSkRect.fTop, clipSkRect.fRight - clipSkRect.fLeft, - clipSkRect.fBottom - clipSkRect.fTop); -} - -BOOL BlurRadiusEqualToBlurRadius(CGFloat radius1, CGFloat radius2) { - const CGFloat epsilon = 0.01; - return radius1 - radius2 < epsilon; -} - -} // namespace flutter +} // namespace @interface PlatformViewFilter () @@ -291,11 +247,11 @@ - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event { } - (void)clipRect:(const SkRect&)clipSkRect matrix:(const SkMatrix&)matrix { - CGRect clipRect = flutter::GetCGRectFromSkRect(clipSkRect); + CGRect clipRect = GetCGRectFromSkRect(clipSkRect); CGPathRef path = CGPathCreateWithRect(clipRect, nil); // The `matrix` is based on the physical pixels, convert it to UIKit points. CATransform3D matrixInPoints = - CATransform3DConcat(flutter::GetCATransform3DFromSkMatrix(matrix), _reverseScreenScale); + CATransform3DConcat(GetCATransform3DFromSkMatrix(matrix), _reverseScreenScale); [self addTransformedPath:path matrix:matrixInPoints]; } @@ -311,7 +267,7 @@ - (void)clipRRect:(const SkRRect&)clipSkRRect matrix:(const SkMatrix&)matrix { } case SkRRect::kOval_Type: case SkRRect::kSimple_Type: { - CGRect clipRect = flutter::GetCGRectFromSkRect(clipSkRRect.rect()); + CGRect clipRect = GetCGRectFromSkRect(clipSkRRect.rect()); pathRef = CGPathCreateWithRoundedRect(clipRect, clipSkRRect.getSimpleRadii().x(), clipSkRRect.getSimpleRadii().y(), nil); break; @@ -361,7 +317,7 @@ - (void)clipRRect:(const SkRRect&)clipSkRRect matrix:(const SkMatrix&)matrix { } // The `matrix` is based on the physical pixels, convert it to UIKit points. CATransform3D matrixInPoints = - CATransform3DConcat(flutter::GetCATransform3DFromSkMatrix(matrix), _reverseScreenScale); + CATransform3DConcat(GetCATransform3DFromSkMatrix(matrix), _reverseScreenScale); // TODO(cyanglaz): iOS does not seem to support hard edge on CAShapeLayer. It clearly stated that // the CAShaperLayer will be drawn antialiased. Need to figure out a way to do the hard edge // clipping on iOS. @@ -429,7 +385,7 @@ - (void)clipPath:(const SkPath&)path matrix:(const SkMatrix&)matrix { } // The `matrix` is based on the physical pixels, convert it to UIKit points. CATransform3D matrixInPoints = - CATransform3DConcat(flutter::GetCATransform3DFromSkMatrix(matrix), _reverseScreenScale); + CATransform3DConcat(GetCATransform3DFromSkMatrix(matrix), _reverseScreenScale); [self addTransformedPath:pathRef matrix:matrixInPoints]; } @@ -491,3 +447,272 @@ - (void)insertViewToPoolIfNeeded:(FlutterClippingMaskView*)maskView { } @end + +@implementation UIView (FirstResponder) +- (BOOL)flt_hasFirstResponderInViewHierarchySubtree { + if (self.isFirstResponder) { + return YES; + } + for (UIView* subview in self.subviews) { + if (subview.flt_hasFirstResponderInViewHierarchySubtree) { + return YES; + } + } + return NO; +} +@end + +// This recognizer delays touch events from being dispatched to the responder chain until it failed +// recognizing a gesture. +// +// We only fail this recognizer when asked to do so by the Flutter framework (which does so by +// invoking an acceptGesture method on the platform_views channel). And this is how we allow the +// Flutter framework to delay or prevent the embedded view from getting a touch sequence. +@interface FlutterDelayingGestureRecognizer : UIGestureRecognizer + +// Indicates that if the `FlutterDelayingGestureRecognizer`'s state should be set to +// `UIGestureRecognizerStateEnded` during next `touchesEnded` call. +@property(nonatomic) BOOL shouldEndInNextTouchesEnded; + +// Indicates that the `FlutterDelayingGestureRecognizer`'s `touchesEnded` has been invoked without +// setting the state to `UIGestureRecognizerStateEnded`. +@property(nonatomic) BOOL touchedEndedWithoutBlocking; + +@property(nonatomic, readonly) UIGestureRecognizer* forwardingRecognizer; + +- (instancetype)initWithTarget:(id)target + action:(SEL)action + forwardingRecognizer:(UIGestureRecognizer*)forwardingRecognizer; +@end + +// While the FlutterDelayingGestureRecognizer is preventing touches from hitting the responder chain +// the touch events are not arriving to the FlutterView (and thus not arriving to the Flutter +// framework). We use this gesture recognizer to dispatch the events directly to the FlutterView +// while during this phase. +// +// If the Flutter framework decides to dispatch events to the embedded view, we fail the +// FlutterDelayingGestureRecognizer which sends the events up the responder chain. But since the +// events are handled by the embedded view they are not delivered to the Flutter framework in this +// phase as well. So during this phase as well the ForwardingGestureRecognizer dispatched the events +// directly to the FlutterView. +@interface ForwardingGestureRecognizer : UIGestureRecognizer +- (instancetype)initWithTarget:(id)target + platformViewsController: + (fml::WeakPtr)platformViewsController; +@end + +@interface FlutterTouchInterceptingView () +@property(nonatomic, weak, readonly) UIView* embeddedView; +@property(nonatomic, readonly) FlutterDelayingGestureRecognizer* delayingRecognizer; +@property(nonatomic, readonly) FlutterPlatformViewGestureRecognizersBlockingPolicy blockingPolicy; +@end + +@implementation FlutterTouchInterceptingView +- (instancetype)initWithEmbeddedView:(UIView*)embeddedView + platformViewsController: + (fml::WeakPtr)platformViewsController + gestureRecognizersBlockingPolicy: + (FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy { + self = [super initWithFrame:embeddedView.frame]; + if (self) { + self.multipleTouchEnabled = YES; + _embeddedView = embeddedView; + embeddedView.autoresizingMask = + (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + + [self addSubview:embeddedView]; + + ForwardingGestureRecognizer* forwardingRecognizer = + [[ForwardingGestureRecognizer alloc] initWithTarget:self + platformViewsController:platformViewsController]; + + _delayingRecognizer = + [[FlutterDelayingGestureRecognizer alloc] initWithTarget:self + action:nil + forwardingRecognizer:forwardingRecognizer]; + _blockingPolicy = blockingPolicy; + + [self addGestureRecognizer:_delayingRecognizer]; + [self addGestureRecognizer:forwardingRecognizer]; + } + return self; +} + +- (void)releaseGesture { + self.delayingRecognizer.state = UIGestureRecognizerStateFailed; +} + +- (void)blockGesture { + switch (_blockingPolicy) { + case FlutterPlatformViewGestureRecognizersBlockingPolicyEager: + // We block all other gesture recognizers immediately in this policy. + self.delayingRecognizer.state = UIGestureRecognizerStateEnded; + break; + case FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded: + if (self.delayingRecognizer.touchedEndedWithoutBlocking) { + // If touchesEnded of the `DelayingGesureRecognizer` has been already invoked, + // we want to set the state of the `DelayingGesureRecognizer` to + // `UIGestureRecognizerStateEnded` as soon as possible. + self.delayingRecognizer.state = UIGestureRecognizerStateEnded; + } else { + // If touchesEnded of the `DelayingGesureRecognizer` has not been invoked, + // We will set a flag to notify the `DelayingGesureRecognizer` to set the state to + // `UIGestureRecognizerStateEnded` when touchesEnded is called. + self.delayingRecognizer.shouldEndInNextTouchesEnded = YES; + } + break; + default: + break; + } +} + +// We want the intercepting view to consume the touches and not pass the touches up to the parent +// view. Make the touch event method not call super will not pass the touches up to the parent view. +// Hence we overide the touch event methods and do nothing. +- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { +} + +- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { +} + +- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { +} + +- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { +} + +- (id)accessibilityContainer { + return self.flutterAccessibilityContainer; +} + +@end + +@implementation FlutterDelayingGestureRecognizer + +- (instancetype)initWithTarget:(id)target + action:(SEL)action + forwardingRecognizer:(UIGestureRecognizer*)forwardingRecognizer { + self = [super initWithTarget:target action:action]; + if (self) { + self.delaysTouchesBegan = YES; + self.delaysTouchesEnded = YES; + self.delegate = self; + _shouldEndInNextTouchesEnded = NO; + _touchedEndedWithoutBlocking = NO; + _forwardingRecognizer = forwardingRecognizer; + } + return self; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer + shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer { + // The forwarding gesture recognizer should always get all touch events, so it should not be + // required to fail by any other gesture recognizer. + return otherGestureRecognizer != _forwardingRecognizer && otherGestureRecognizer != self; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer + shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer { + return otherGestureRecognizer == self; +} + +- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { + self.touchedEndedWithoutBlocking = NO; + [super touchesBegan:touches withEvent:event]; +} + +- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { + if (self.shouldEndInNextTouchesEnded) { + self.state = UIGestureRecognizerStateEnded; + self.shouldEndInNextTouchesEnded = NO; + } else { + self.touchedEndedWithoutBlocking = YES; + } + [super touchesEnded:touches withEvent:event]; +} + +- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { + self.state = UIGestureRecognizerStateFailed; +} +@end + +@implementation ForwardingGestureRecognizer { + // Weak reference to PlatformViewsController. The PlatformViewsController has + // a reference to the FlutterViewController, where we can dispatch pointer events to. + // + // The lifecycle of PlatformViewsController is bind to FlutterEngine, which should always + // outlives the FlutterViewController. And ForwardingGestureRecognizer is owned by a subview of + // FlutterView, so the ForwardingGestureRecognizer never out lives FlutterViewController. + // Therefore, `_platformViewsController` should never be nullptr. + 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 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 + platformViewsController: + (fml::WeakPtr)platformViewsController { + self = [super initWithTarget:target action:nil]; + if (self) { + self.delegate = self; + FML_DCHECK(platformViewsController.get() != nullptr); + _platformViewsController = std::move(platformViewsController); + _currentTouchPointersCount = 0; + } + return self; +} + +- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { + FML_DCHECK(_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.reset(_platformViewsController->GetFlutterViewController()); + } + [_flutterViewController.get() touchesBegan:touches withEvent:event]; + _currentTouchPointersCount += touches.count; +} + +- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { + [_flutterViewController.get() touchesMoved:touches withEvent:event]; +} + +- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)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 + // 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); + } +} + +- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { + // In the event of platform view is removed, iOS generates a "stationary" change type instead of + // "cancelled" change type. + // Flutter needs all the cancelled touches to be "cancelled" change types in order to correctly + // handle gesture sequence. + // We always override the change type to "cancelled". + [_flutterViewController.get() forceTouchesCancelled:touches]; + _currentTouchPointersCount -= touches.count; + if (_currentTouchPointersCount == 0) { + self.state = UIGestureRecognizerStateFailed; + _flutterViewController.reset(nil); + } +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer + shouldRecognizeSimultaneouslyWithGestureRecognizer: + (UIGestureRecognizer*)otherGestureRecognizer { + return YES; +} +@end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterView.h b/shell/platform/darwin/ios/framework/Source/FlutterView.h index 53e3bf330be6d..53dd50e45d6a8 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterView.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterView.h @@ -18,7 +18,7 @@ - (flutter::Rasterizer::Screenshot)takeScreenshot:(flutter::Rasterizer::ScreenshotType)type asBase64Encoded:(BOOL)base64Encode; -- (std::shared_ptr&)platformViewsController; +- (std::shared_ptr&)platformViewsController; /** * A callback that is called when iOS queries accessibility information of the Flutter view. diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 60bff2c482a8c..ba42b71c3e61b 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -2305,7 +2305,7 @@ - (BOOL)prefersStatusBarHidden { #pragma mark - Platform views -- (std::shared_ptr&)platformViewsController { +- (std::shared_ptr&)platformViewsController { return [_engine.get() platformViewsController]; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h index f86cb98bd7578..c41533ff9c041 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h @@ -16,7 +16,7 @@ #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewResponder.h" namespace flutter { -class FlutterPlatformViewsController; +class PlatformViewsController; } FLUTTER_DARWIN_EXPORT @@ -58,7 +58,7 @@ typedef void (^FlutterKeyboardAnimationCallback)(fml::TimePoint); @property(nonatomic, assign, readwrite) BOOL prefersStatusBarHidden; - (fml::WeakNSObject)getWeakNSObject; -- (std::shared_ptr&)platformViewsController; +- (std::shared_ptr&)platformViewsController; - (FlutterRestorationPlugin*)restorationPlugin; // Accepts keypress events, and then calls |nextAction| if the event was not diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewTest.mm index 4a514f8e20fdd..098c0f3eb9ead 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewTest.mm @@ -14,12 +14,12 @@ @interface FakeDelegate : NSObject @end @implementation FakeDelegate { - std::shared_ptr _platformViewsController; + std::shared_ptr _platformViewsController; } - (instancetype)init { _callbackCalled = NO; - _platformViewsController = std::shared_ptr(nullptr); + _platformViewsController = std::shared_ptr(nullptr); return self; } @@ -28,7 +28,7 @@ - (instancetype)init { return {}; } -- (std::shared_ptr&)platformViewsController { +- (std::shared_ptr&)platformViewsController { return _platformViewsController; } diff --git a/shell/platform/darwin/ios/framework/Source/SemanticsObjectTestMocks.h b/shell/platform/darwin/ios/framework/Source/SemanticsObjectTestMocks.h index 9813d435a1f83..4ccd57b1bbf6b 100644 --- a/shell/platform/darwin/ios/framework/Source/SemanticsObjectTestMocks.h +++ b/shell/platform/darwin/ios/framework/Source/SemanticsObjectTestMocks.h @@ -43,7 +43,7 @@ class MockAccessibilityBridge : public AccessibilityBridgeIos { } void AccessibilityObjectDidBecomeFocused(int32_t id) override {} void AccessibilityObjectDidLoseFocus(int32_t id) override {} - std::shared_ptr GetPlatformViewsController() const override { + std::shared_ptr GetPlatformViewsController() const override { return nil; } std::vector observations; @@ -74,7 +74,7 @@ class MockAccessibilityBridgeNoWindow : public AccessibilityBridgeIos { } void AccessibilityObjectDidBecomeFocused(int32_t id) override {} void AccessibilityObjectDidLoseFocus(int32_t id) override {} - std::shared_ptr GetPlatformViewsController() const override { + std::shared_ptr GetPlatformViewsController() const override { return nil; } std::vector observations; diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h index c117b83593f10..4086120000072 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h @@ -51,7 +51,7 @@ class AccessibilityBridge final : public AccessibilityBridgeIos { AccessibilityBridge(FlutterViewController* view_controller, PlatformViewIOS* platform_view, - std::shared_ptr platform_views_controller, + std::shared_ptr platform_views_controller, std::unique_ptr ios_delegate = nullptr); ~AccessibilityBridge(); @@ -73,7 +73,7 @@ class AccessibilityBridge final : public AccessibilityBridgeIos { fml::WeakPtr GetWeakPtr(); - std::shared_ptr GetPlatformViewsController() const override { + std::shared_ptr GetPlatformViewsController() const override { return platform_views_controller_; }; @@ -92,7 +92,7 @@ class AccessibilityBridge final : public AccessibilityBridgeIos { FlutterViewController* view_controller_; PlatformViewIOS* platform_view_; - const std::shared_ptr platform_views_controller_; + const std::shared_ptr platform_views_controller_; // If the this id is kSemanticObjectIdInvalid, it means either nothing has // been focused or the focus is currently outside of the flutter application // (i.e. the status bar or keyboard) diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index 7868c593f0732..7162b5c28c760 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -42,7 +42,7 @@ void PostAccessibilityNotification(UIAccessibilityNotifications notification, AccessibilityBridge::AccessibilityBridge( FlutterViewController* view_controller, PlatformViewIOS* platform_view, - std::shared_ptr platform_views_controller, + std::shared_ptr platform_views_controller, std::unique_ptr ios_delegate) : view_controller_(view_controller), platform_view_(platform_view), diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_ios.h b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_ios.h index 9b6d5ef0a1922..0da3646a2e802 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_ios.h +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_ios.h @@ -14,7 +14,7 @@ @class UIView; namespace flutter { -class FlutterPlatformViewsController; +class PlatformViewsController; /// Interface that represents an accessibility bridge for iOS. class AccessibilityBridgeIos { @@ -39,7 +39,7 @@ class AccessibilityBridgeIos { * The input id is the uid of the newly focused SemanticObject. */ virtual void AccessibilityObjectDidLoseFocus(int32_t id) = 0; - virtual std::shared_ptr GetPlatformViewsController() const = 0; + virtual std::shared_ptr GetPlatformViewsController() const = 0; }; } // namespace flutter diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm index 81b79a386ea70..f89c02febf616 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm @@ -279,8 +279,7 @@ - (void)testSemanticsDeallocated { /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = - std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -341,8 +340,7 @@ - (void)testSemanticsDeallocatedWithoutLoadingView { /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = - std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -389,7 +387,7 @@ - (void)testReplacedSemanticsDoesNotCleanupChildren { /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -486,7 +484,7 @@ - (void)testScrollableSemanticsDeallocated { /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -561,7 +559,7 @@ - (void)testBridgeReplacesSemanticsNode { /*ui=*/thread_task_runner, /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(thread_task_runner); auto platform_view = std::make_unique( /*delegate=*/mock_delegate, @@ -2181,8 +2179,7 @@ - (void)testPlatformViewDestructorDoesNotCallSemanticsAPIs { /*is_gpu_disabled_sync_switch=*/std::make_shared()); id mockFlutterViewController = OCMClassMock([FlutterViewController class]); - auto flutterPlatformViewsController = - std::make_shared(); + auto flutterPlatformViewsController = std::make_shared(); flutterPlatformViewsController->SetTaskRunner(thread_task_runner); OCMStub([mockFlutterViewController platformViewsController]) diff --git a/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.h b/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.h new file mode 100644 index 0000000000000..bd04aa81a8bf5 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.h @@ -0,0 +1,102 @@ +// 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. + +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_OVERLAY_LAYER_POOL_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_OVERLAY_LAYER_POOL_H_ + +#include +#include + +#import + +#include "flow/surface.h" +#include "fml/platform/darwin/scoped_nsobject.h" + +#import "flutter/shell/platform/darwin/ios/ios_context.h" + +namespace flutter { + +class IOSSurface; + +/// @brief State holder for a Flutter overlay layer. +struct OverlayLayer { + OverlayLayer(const fml::scoped_nsobject& overlay_view, + const fml::scoped_nsobject& overlay_view_wrapper, + std::unique_ptr ios_surface, + std::unique_ptr surface); + + ~OverlayLayer() = default; + + fml::scoped_nsobject overlay_view; + fml::scoped_nsobject overlay_view_wrapper; + std::unique_ptr ios_surface; + std::unique_ptr surface; + + // Whether a frame for this layer was submitted. + bool did_submit_last_frame; + + // The GrContext that is currently used by the overlay surfaces. + // We track this to know when the GrContext for the Flutter app has changed + // so we can update the overlay with the new context. + GrDirectContext* gr_context; + + void UpdateViewState(UIView* flutter_view, SkRect rect, int64_t view_id, int64_t overlay_id); +}; + +/// @brief Storage for Overlay layers across frames. +/// +/// Note: this class does not synchronize access to its layers or any layer removal. As it +/// is currently used, layers must be created on the platform thread but other methods of +/// it are called on the raster thread. This is safe as overlay layers are only ever added +/// while the raster thread is latched. +class OverlayLayerPool { + public: + OverlayLayerPool() = default; + + ~OverlayLayerPool() = default; + + /// @brief Gets a layer from the pool if available. + /// + /// The layer is marked as used until [RecycleLayers] is called. + std::shared_ptr GetNextLayer(); + + /// @brief Create a new overlay layer. + /// + /// This method can only be called on the Platform thread. + void CreateLayer(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format); + + /// @brief Removes unused layers from the pool. Returns the unused layers. + std::vector> RemoveUnusedLayers(); + + /// @brief Marks the layers in the pool as available for reuse. + void RecycleLayers(); + + /// @brief The count of layers currently in the pool. + size_t size() const; + + private: + OverlayLayerPool(const OverlayLayerPool&) = delete; + OverlayLayerPool& operator=(const OverlayLayerPool&) = delete; + + // The index of the entry in the layers_ vector that determines the beginning of the unused + // layers. For example, consider the following vector: + // _____ + // | 0 | + /// |---| + /// | 1 | <-- available_layer_index_ + /// |---| + /// | 2 | + /// |---| + /// + /// This indicates that entries starting from 1 can be reused meanwhile the entry at position 0 + /// cannot be reused. + size_t available_layer_index_ = 0; + std::vector> layers_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_OVERLAY_LAYER_POOL_H_ diff --git a/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.mm b/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.mm new file mode 100644 index 0000000000000..4d63a595fcc4f --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.mm @@ -0,0 +1,136 @@ +// 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. + +#include "flutter/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.h" + +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h" +#import "flutter/shell/platform/darwin/ios/ios_surface.h" + +namespace flutter { + +OverlayLayer::OverlayLayer(const fml::scoped_nsobject& overlay_view, + const fml::scoped_nsobject& overlay_view_wrapper, + std::unique_ptr ios_surface, + std::unique_ptr surface) + : overlay_view(overlay_view), + overlay_view_wrapper(overlay_view_wrapper), + ios_surface(std::move(ios_surface)), + surface(std::move(surface)){}; + +void OverlayLayer::UpdateViewState(UIView* flutter_view, + SkRect rect, + int64_t view_id, + int64_t overlay_id) { + UIView* overlay_view_wrapper = this->overlay_view_wrapper.get(); + auto screenScale = [UIScreen mainScreen].scale; + // Set the size of the overlay view wrapper. + // This wrapper view masks the overlay view. + overlay_view_wrapper.frame = CGRectMake(rect.x() / screenScale, rect.y() / screenScale, + rect.width() / screenScale, rect.height() / screenScale); + // Set a unique view identifier, so the overlay_view_wrapper can be identified in XCUITests. + overlay_view_wrapper.accessibilityIdentifier = + [NSString stringWithFormat:@"platform_view[%lld].overlay[%lld]", view_id, overlay_id]; + + UIView* overlay_view = this->overlay_view.get(); + // Set the size of the overlay view. + // This size is equal to the device screen size. + overlay_view.frame = [flutter_view convertRect:flutter_view.bounds toView:overlay_view_wrapper]; + // Set a unique view identifier, so the overlay_view can be identified in XCUITests. + overlay_view.accessibilityIdentifier = + [NSString stringWithFormat:@"platform_view[%lld].overlay_view[%lld]", view_id, overlay_id]; +} + +// OverlayLayerPool +//////////////////////////////////////////////////////// + +std::shared_ptr OverlayLayerPool::GetNextLayer() { + std::shared_ptr result; + if (available_layer_index_ < layers_.size()) { + result = layers_[available_layer_index_]; + available_layer_index_++; + } + + return result; +} + +void OverlayLayerPool::CreateLayer(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format) { + FML_DCHECK([[NSThread currentThread] isMainThread]); + std::shared_ptr layer; + fml::scoped_nsobject overlay_view; + fml::scoped_nsobject overlay_view_wrapper; + + bool impeller_enabled = !!ios_context->GetImpellerContext(); + if (!gr_context && !impeller_enabled) { + overlay_view.reset([[FlutterOverlayView alloc] init]); + overlay_view_wrapper.reset([[FlutterOverlayView alloc] init]); + + auto ca_layer = fml::scoped_nsobject{[overlay_view.get() layer]}; + std::unique_ptr ios_surface = IOSSurface::Create(ios_context, ca_layer); + std::unique_ptr surface = ios_surface->CreateGPUSurface(); + + layer = std::make_shared(std::move(overlay_view), std::move(overlay_view_wrapper), + std::move(ios_surface), std::move(surface)); + } else { + CGFloat screenScale = [UIScreen mainScreen].scale; + overlay_view.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale + pixelFormat:pixel_format]); + overlay_view_wrapper.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale + pixelFormat:pixel_format]); + + auto ca_layer = fml::scoped_nsobject{[overlay_view.get() layer]}; + std::unique_ptr ios_surface = IOSSurface::Create(ios_context, ca_layer); + std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); + + layer = std::make_shared(std::move(overlay_view), std::move(overlay_view_wrapper), + std::move(ios_surface), std::move(surface)); + layer->gr_context = gr_context; + } + // The overlay view wrapper masks the overlay view. + // This is required to keep the backing surface size unchanged between frames. + // + // Otherwise, changing the size of the overlay would require a new surface, + // which can be very expensive. + // + // This is the case of an animation in which the overlay size is changing in every frame. + // + // +------------------------+ + // | overlay_view | + // | +--------------+ | +--------------+ + // | | wrapper | | == mask => | overlay_view | + // | +--------------+ | +--------------+ + // +------------------------+ + layer->overlay_view_wrapper.get().clipsToBounds = YES; + [layer->overlay_view_wrapper.get() addSubview:layer->overlay_view]; + + layers_.push_back(layer); +} + +void OverlayLayerPool::RecycleLayers() { + available_layer_index_ = 0; +} + +std::vector> OverlayLayerPool::RemoveUnusedLayers() { + std::vector> results; + for (size_t i = available_layer_index_; i < layers_.size(); i++) { + results.push_back(layers_[i]); + } + // Leave at least one overlay layer, to work around cases where scrolling + // platform views under an app bar continually adds and removes an + // overlay layer. This logic could be removed if https://github.com/flutter/flutter/issues/150646 + // is fixed. + static constexpr size_t kLeakLayerCount = 1; + size_t erase_offset = std::max(available_layer_index_, kLeakLayerCount); + if (erase_offset < layers_.size()) { + layers_.erase(layers_.begin() + erase_offset, layers_.end()); + } + return results; +} + +size_t OverlayLayerPool::size() const { + return layers_.size(); +} + +} // namespace flutter diff --git a/shell/platform/darwin/ios/framework/Source/platform_views_controller.h b/shell/platform/darwin/ios/framework/Source/platform_views_controller.h new file mode 100644 index 0000000000000..551792ef8d47b --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/platform_views_controller.h @@ -0,0 +1,324 @@ +// 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. + +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PLATFORM_VIEWS_CONTROLLER_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PLATFORM_VIEWS_CONTROLLER_H_ + +#include + +#include "flutter/flow/surface.h" +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/fml/task_runner.h" +#include "flutter/fml/trace_event.h" +#include "impeller/base/thread_safety.h" +#include "third_party/skia/include/core/SkRect.h" + +#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewResponder.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/overlay_layer_pool.h" +#import "flutter/shell/platform/darwin/ios/ios_context.h" + +@class FlutterTouchInterceptingView; +@class FlutterClippingMaskViewPool; + +namespace flutter { + +/// @brief Composites Flutter UI and overlay layers alongside embedded UIViews. +class PlatformViewsController { + public: + PlatformViewsController(); + + ~PlatformViewsController() = default; + + /// @brief Retrieve a weak pointer to this controller. + fml::WeakPtr GetWeakPtr(); + + /// @brief Set the platform task runner used to post rendering tasks. + void SetTaskRunner(const fml::RefPtr& platform_task_runner); + + /// @brief Set the flutter view. + void SetFlutterView(UIView* flutter_view) __attribute__((cf_audited_transfer)); + + /// @brief Set the flutter view controller. + void SetFlutterViewController(UIViewController* flutter_view_controller) + __attribute__((cf_audited_transfer)); + + /// @brief Retrieve the view controller. + UIViewController* GetFlutterViewController() + __attribute__((cf_audited_transfer)); + + /// @brief set the factory used to construct embedded UI Views. + void RegisterViewFactory( + NSObject* factory, + NSString* factoryId, + FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy) + __attribute__((cf_audited_transfer)); + + /// @brief Mark the beginning of a frame and record the size of the onscreen. + void BeginFrame(SkISize frame_size); + + /// @brief Cancel the current frame, indicating that no platform views are composited. + /// + /// Additionally, reverts the composition order to its original state at the beginning of the + /// frame. + void CancelFrame(); + + /// @brief Record a platform view in the layer tree to be rendered, along with the positioning and + /// mutator parameters. + /// + /// Called from the raster thread. + void PrerollCompositeEmbeddedView(int64_t view_id, + std::unique_ptr params); + + /// @brief Returns the`FlutterTouchInterceptingView` with the provided view_id. + /// + /// Returns nil if there is no platform view with the provided id. Called + /// from the platform thread. + FlutterTouchInterceptingView* GetFlutterTouchInterceptingViewByID(int64_t view_id); + + /// @brief Determine if thread merging is required after prerolling platform views. + /// + /// Called from the raster thread. + PostPrerollResult PostPrerollAction( + const fml::RefPtr& raster_thread_merger); + + /// @brief Mark the end of a compositor frame. + /// + /// May determine changes are required to the thread merging state. + /// Called from the raster thread. + void EndFrame(bool should_resubmit_frame, + const fml::RefPtr& raster_thread_merger); + + /// @brief Returns the Canvas for the overlay slice for the given platform view. + /// + /// Called from the raster thread. + DlCanvas* CompositeEmbeddedView(int64_t view_id); + + /// @brief Discards all platform views instances and auxiliary resources. + /// + /// Called from the raster thread. + void Reset(); + + /// @brief Encode rendering for the Flutter overlay views and queue up perform platform view + /// mutations. + /// + /// Called from the raster thread. + bool SubmitFrame(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + std::unique_ptr frame); + + /// @brief Handler for platform view message channels. + void OnMethodCall(FlutterMethodCall* call, FlutterResult result) + __attribute__((cf_audited_transfer)); + + /// @brief Returns the platform view id if the platform view (or any of its descendant view) is + /// the first responder. + /// + /// Returns -1 if no such platform view is found. + long FindFirstResponderPlatformViewId(); + + /// @brief Pushes backdrop filter mutation to the mutator stack of each visited platform view. + void PushFilterToVisitedPlatformViews(const std::shared_ptr& filter, + const SkRect& filter_rect); + + /// @brief Pushes the view id of a visted platform view to the list of visied platform views. + void PushVisitedPlatformView(int64_t view_id) { visited_platform_views_.push_back(view_id); } + + // visible for testing. + size_t EmbeddedViewCount() const; + + // visible for testing. + size_t LayerPoolSize() const; + + // visible for testing. + // Returns the `FlutterPlatformView`'s `view` object associated with the view_id. + // + // If the `PlatformViewsController` does not contain any `FlutterPlatformView` object or + // a `FlutterPlatformView` object associated with the view_id cannot be found, the method + // returns nil. + UIView* GetPlatformViewByID(int64_t view_id); + + // Visible for testing. + void CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params); + + // Visible for testing. + const EmbeddedViewParams& GetCompositionParams(int64_t view_id) const { + return current_composition_params_.find(view_id)->second; + } + + private: + PlatformViewsController(const PlatformViewsController&) = delete; + PlatformViewsController& operator=(const PlatformViewsController&) = delete; + + struct LayerData { + SkRect rect; + int64_t view_id; + int64_t overlay_id; + std::shared_ptr layer; + }; + + using LayersMap = std::unordered_map; + + // Update the buffers and mutate the platform views in CATransaction. + // + // Runs on the platform thread. + void PerformSubmit(const LayersMap& platform_view_layers, + std::unordered_map& current_composition_params, + const std::unordered_set& views_to_recomposite, + const std::vector& composition_order, + const std::vector>& unused_layers, + const std::vector>& surface_frames); + + /// @brief Populate any missing overlay layers. + /// + /// This requires posting a task to the platform thread and blocking on its completion. + void CreateMissingOverlays(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + size_t required_overlay_layers); + + void OnCreate(FlutterMethodCall* call, FlutterResult result) __attribute__((cf_audited_transfer)); + + void OnDispose(FlutterMethodCall* call, FlutterResult result) + __attribute__((cf_audited_transfer)); + + void OnAcceptGesture(FlutterMethodCall* call, FlutterResult result) + __attribute__((cf_audited_transfer)); + + void OnRejectGesture(FlutterMethodCall* call, FlutterResult result) + __attribute__((cf_audited_transfer)); + + /// @brief Return all views to be disposed on the platform thread. + std::vector GetViewsToDispose(); + + void ClipViewSetMaskView(UIView* clipView) __attribute__((cf_audited_transfer)); + + // Applies the mutators in the mutators_stack to the UIView chain that was constructed by + // `ReconstructClipViewsChain` + // + // Clips are applied to the `embedded_view`'s super view(|ChildClippingView|) using a + // |FlutterClippingMaskView|. Transforms are applied to `embedded_view` + // + // The `bounding_rect` is the final bounding rect of the PlatformView + // (EmbeddedViewParams::finalBoundingRect). If a clip mutator's rect contains the final bounding + // rect of the PlatformView, the clip mutator is not applied for performance optimization. + void ApplyMutators(const MutatorsStack& mutators_stack, + UIView* embedded_view, + const SkRect& bounding_rect) __attribute__((cf_audited_transfer)); + + std::shared_ptr GetExistingLayer(); + + // Runs on the platform thread. + void CreateLayer(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format); + + // Removes overlay views and platform views that aren't needed in the current frame. + // Must run on the platform thread. + void RemoveUnusedLayers(const std::vector>& unused_layers, + const std::vector& composition_order); + + // Appends the overlay views and platform view and sets their z index based on the composition + // order. + void BringLayersIntoView(const LayersMap& layer_map, + const std::vector& composition_order); + + // Resets the state of the frame. + void ResetFrameState(); + + // The pool of reusable view layers. The pool allows to recycle layer in each frame. + std::unique_ptr layer_pool_; + + // The platform view's |EmbedderViewSlice| keyed off the view id, which contains any subsequent + // operation until the next platform view or the end of the last leaf node in the layer tree. + // + // The Slices are deleted by the PlatformViewsController.reset(). + std::unordered_map> slices_; + + fml::scoped_nsobject channel_; + fml::scoped_nsobject flutter_view_; + fml::scoped_nsobject> flutter_view_controller_; + fml::scoped_nsobject mask_view_pool_; + std::unordered_map>> + factories_; + + // The FlutterPlatformViewGestureRecognizersBlockingPolicy for each type of platform view. + std::unordered_map + gesture_recognizers_blocking_policies_; + + /// The size of the current onscreen surface in physical pixels. + SkISize frame_size_; + + /// The task runner for posting tasks to the platform thread. + fml::RefPtr platform_task_runner_; + + /// Each of the following structs stores part of the platform view hierarchy according to its + /// ID. + /// + /// This data must only be accessed on the platform thread. + struct PlatformViewData { + fml::scoped_nsobject> view; + fml::scoped_nsobject touch_interceptor; + fml::scoped_nsobject root_view; + }; + + /// This data must only be accessed on the platform thread. + std::unordered_map platform_views_; + + /// The composition parameters for each platform view. + /// + /// This state is only modified on the raster thread. + std::unordered_map current_composition_params_; + + /// Method channel `OnDispose` calls adds the views to be disposed to this set to be disposed on + /// the next frame. + /// + /// This state is modified on both the platform and raster thread. + std::unordered_set views_to_dispose_; + + /// view IDs in composition order. + /// + /// This state is only modified on the raster thread. + std::vector composition_order_; + + /// platform view IDs visited during layer tree composition. + /// + /// This state is only modified on the raster thread. + std::vector visited_platform_views_; + + /// Only composite platform views in this set. + /// + /// This state is only modified on the raster thread. + std::unordered_set views_to_recomposite_; + +#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG + /// A set to keep track of embedded views that do not have (0, 0) origin. + /// An insertion triggers a warning message about non-zero origin logged on the debug console. + /// See https://github.com/flutter/flutter/issues/109700 for details. + std::unordered_set non_zero_origin_views_; +#endif + + /// @brief The composition order from the previous thread. + /// + /// Only accessed from the platform thread. + std::vector previous_composition_order_; + + /// Whether the previous frame had any platform views in active composition order. + /// + /// This state is tracked so that the first frame after removing the last platform view + /// runs through the platform view rendering code path, giving us a chance to remove the + /// platform view from the UIView hierarchy. + /// + /// Only accessed from the raster thread. + bool had_platform_views_ = false; + + // WeakPtrFactory must be the last member. + std::unique_ptr> weak_factory_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PLATFORM_VIEWS_CONTROLLER_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/platform_views_controller.mm similarity index 58% rename from shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm rename to shell/platform/darwin/ios/framework/Source/platform_views_controller.mm index 5509c2245fdf6..2627268961997 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/platform_views_controller.mm @@ -2,22 +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/ios/framework/Source/FlutterPlatformViews_Internal.h" - -#include -#include +#import "shell/platform/darwin/ios/framework/Source/platform_views_controller.h" #include "flow/surface_frame.h" #include "flutter/flow/view_slicer.h" #include "flutter/fml/make_copyable.h" -#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "fml/synchronization/count_down_latch.h" + #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" #import "flutter/shell/platform/darwin/ios/ios_surface.h" -#include "fml/logging.h" -#include "fml/synchronization/count_down_latch.h" -FLUTTER_ASSERT_ARC +namespace { #ifdef FML_OS_IOS_SIMULATOR // The number of frames the rasterizer task runner will continue @@ -27,19 +23,39 @@ static const int kDefaultMergedLeaseDuration = 10; #endif // FML_OS_IOS_SIMULATOR -@implementation UIView (FirstResponder) -- (BOOL)flt_hasFirstResponderInViewHierarchySubtree { - if (self.isFirstResponder) { - return YES; - } - for (UIView* subview in self.subviews) { - if (subview.flt_hasFirstResponderInViewHierarchySubtree) { - return YES; - } - } - return NO; +static constexpr NSUInteger kFlutterClippingMaskViewPoolCapacity = 5; + +// Converts a SkMatrix to CATransform3D. +// +// Certain fields are ignored in CATransform3D since SkMatrix is 3x3 and CATransform3D is 4x4. +CATransform3D GetCATransform3DFromSkMatrix(const SkMatrix& matrix) { + // Skia only supports 2D transform so we don't map z. + CATransform3D transform = CATransform3DIdentity; + transform.m11 = matrix.getScaleX(); + transform.m21 = matrix.getSkewX(); + transform.m41 = matrix.getTranslateX(); + transform.m14 = matrix.getPerspX(); + + transform.m12 = matrix.getSkewY(); + transform.m22 = matrix.getScaleY(); + transform.m42 = matrix.getTranslateY(); + transform.m24 = matrix.getPerspY(); + return transform; +} + +// Reset the anchor of `layer` to match the transform operation from flow. +// +// The position of the `layer` should be unchanged after resetting the anchor. +void ResetAnchor(CALayer* layer) { + // Flow uses (0, 0) to apply transform matrix so we need to match that in Quartz. + layer.anchorPoint = CGPointZero; + layer.position = CGPointZero; +} + +CGRect GetCGRectFromSkRect(const SkRect& clipSkRect) { + return CGRectMake(clipSkRect.fLeft, clipSkRect.fTop, clipSkRect.fRight - clipSkRect.fLeft, + clipSkRect.fBottom - clipSkRect.fTop); } -@end // Determines if the `clip_rect` from a clipRect mutator contains the // `platformview_boundingrect`. @@ -49,9 +65,9 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree { // // `platformview_boundingrect` is the final bounding rect of the PlatformView in the coordinate // space where the PlatformView is displayed. -static bool ClipRectContainsPlatformViewBoundingRect(const SkRect& clip_rect, - const SkRect& platformview_boundingrect, - const SkMatrix& transform_matrix) { +bool ClipRectContainsPlatformViewBoundingRect(const SkRect& clip_rect, + const SkRect& platformview_boundingrect, + const SkMatrix& transform_matrix) { SkRect transformed_rect = transform_matrix.mapRect(clip_rect); return transformed_rect.contains(platformview_boundingrect); } @@ -64,9 +80,9 @@ static bool ClipRectContainsPlatformViewBoundingRect(const SkRect& clip_rect, // // `platformview_boundingrect` is the final bounding rect of the PlatformView in the coordinate // space where the PlatformView is displayed. -static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, - const SkRect& platformview_boundingrect, - const SkMatrix& transform_matrix) { +bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, + const SkRect& platformview_boundingrect, + const SkMatrix& transform_matrix) { SkVector upper_left = clip_rrect.radii(SkRRect::Corner::kUpperLeft_Corner); SkVector upper_right = clip_rrect.radii(SkRRect::Corner::kUpperRight_Corner); SkVector lower_right = clip_rrect.radii(SkRRect::Corner::kLowerRight_Corner); @@ -89,116 +105,43 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, return transformed_rrect.contains(platformview_boundingrect); } +} // namespace + namespace flutter { + // Becomes NO if Apple's API changes and blurred backdrop filters cannot be applied. BOOL canApplyBlurBackdrop = YES; -std::shared_ptr FlutterPlatformViewLayerPool::GetNextLayer() { - std::shared_ptr result; - if (available_layer_index_ < layers_.size()) { - result = layers_[available_layer_index_]; - available_layer_index_++; - } - - return result; -} - -void FlutterPlatformViewLayerPool::CreateLayer(GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - MTLPixelFormat pixel_format) { - FML_DCHECK([[NSThread currentThread] isMainThread]); - std::shared_ptr layer; - fml::scoped_nsobject overlay_view; - fml::scoped_nsobject overlay_view_wrapper; - - bool impeller_enabled = !!ios_context->GetImpellerContext(); - if (!gr_context && !impeller_enabled) { - overlay_view.reset([[FlutterOverlayView alloc] init]); - overlay_view_wrapper.reset([[FlutterOverlayView alloc] init]); - - auto ca_layer = fml::scoped_nsobject{[overlay_view.get() layer]}; - std::unique_ptr ios_surface = IOSSurface::Create(ios_context, ca_layer); - std::unique_ptr surface = ios_surface->CreateGPUSurface(); - - layer = std::make_shared(std::move(overlay_view), - std::move(overlay_view_wrapper), - std::move(ios_surface), std::move(surface)); - } else { - CGFloat screenScale = [UIScreen mainScreen].scale; - overlay_view.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale - pixelFormat:pixel_format]); - overlay_view_wrapper.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale - pixelFormat:pixel_format]); - - auto ca_layer = fml::scoped_nsobject{[overlay_view.get() layer]}; - std::unique_ptr ios_surface = IOSSurface::Create(ios_context, ca_layer); - std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - - layer = std::make_shared(std::move(overlay_view), - std::move(overlay_view_wrapper), - std::move(ios_surface), std::move(surface)); - layer->gr_context = gr_context; - } - // The overlay view wrapper masks the overlay view. - // This is required to keep the backing surface size unchanged between frames. - // - // Otherwise, changing the size of the overlay would require a new surface, - // which can be very expensive. - // - // This is the case of an animation in which the overlay size is changing in every frame. - // - // +------------------------+ - // | overlay_view | - // | +--------------+ | +--------------+ - // | | wrapper | | == mask => | overlay_view | - // | +--------------+ | +--------------+ - // +------------------------+ - layer->overlay_view_wrapper.get().clipsToBounds = YES; - [layer->overlay_view_wrapper.get() addSubview:layer->overlay_view]; - - layers_.push_back(layer); -} - -void FlutterPlatformViewLayerPool::RecycleLayers() { - available_layer_index_ = 0; -} +PlatformViewsController::PlatformViewsController() + : layer_pool_(std::make_unique()), + weak_factory_(std::make_unique>(this)) { + mask_view_pool_.reset( + [[FlutterClippingMaskViewPool alloc] initWithCapacity:kFlutterClippingMaskViewPoolCapacity]); +}; -std::vector> -FlutterPlatformViewLayerPool::RemoveUnusedLayers() { - std::vector> results; - for (size_t i = available_layer_index_; i < layers_.size(); i++) { - results.push_back(layers_[i]); - } - // Leave at least one overlay layer, to work around cases where scrolling - // platform views under an app bar continually adds and removes an - // overlay layer. This logic could be removed if https://github.com/flutter/flutter/issues/150646 - // is fixed. - static constexpr size_t kLeakLayerCount = 1; - size_t erase_offset = std::max(available_layer_index_, kLeakLayerCount); - if (erase_offset < layers_.size()) { - layers_.erase(layers_.begin() + erase_offset, layers_.end()); - } - return results; +void PlatformViewsController::SetTaskRunner( + const fml::RefPtr& platform_task_runner) { + platform_task_runner_ = platform_task_runner; } -size_t FlutterPlatformViewLayerPool::size() const { - return layers_.size(); +fml::WeakPtr PlatformViewsController::GetWeakPtr() { + return weak_factory_->GetWeakPtr(); } -void FlutterPlatformViewsController::SetFlutterView(UIView* flutter_view) { +void PlatformViewsController::SetFlutterView(UIView* flutter_view) { flutter_view_.reset(flutter_view); } -void FlutterPlatformViewsController::SetFlutterViewController( +void PlatformViewsController::SetFlutterViewController( UIViewController* flutter_view_controller) { flutter_view_controller_.reset(flutter_view_controller); } -UIViewController* FlutterPlatformViewsController::getFlutterViewController() { +UIViewController* PlatformViewsController::GetFlutterViewController() { return flutter_view_controller_.get(); } -void FlutterPlatformViewsController::OnMethodCall(FlutterMethodCall* call, FlutterResult result) { +void PlatformViewsController::OnMethodCall(FlutterMethodCall* call, FlutterResult result) { if ([[call method] isEqualToString:@"create"]) { OnCreate(call, result); } else if ([[call method] isEqualToString:@"dispose"]) { @@ -212,7 +155,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } } -void FlutterPlatformViewsController::OnCreate(FlutterMethodCall* call, FlutterResult result) { +void PlatformViewsController::OnCreate(FlutterMethodCall* call, FlutterResult result) { NSDictionary* args = [call arguments]; int64_t viewId = [args[@"id"] longLongValue]; @@ -279,7 +222,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, result(nil); } -void FlutterPlatformViewsController::OnDispose(FlutterMethodCall* call, FlutterResult result) { +void PlatformViewsController::OnDispose(FlutterMethodCall* call, FlutterResult result) { NSNumber* arg = [call arguments]; int64_t viewId = [arg longLongValue]; @@ -294,8 +237,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, result(nil); } -void FlutterPlatformViewsController::OnAcceptGesture(FlutterMethodCall* call, - FlutterResult result) { +void PlatformViewsController::OnAcceptGesture(FlutterMethodCall* call, FlutterResult result) { NSDictionary* args = [call arguments]; int64_t viewId = [args[@"id"] longLongValue]; @@ -312,8 +254,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, result(nil); } -void FlutterPlatformViewsController::OnRejectGesture(FlutterMethodCall* call, - FlutterResult result) { +void PlatformViewsController::OnRejectGesture(FlutterMethodCall* call, FlutterResult result) { NSDictionary* args = [call arguments]; int64_t viewId = [args[@"id"] longLongValue]; @@ -330,7 +271,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, result(nil); } -void FlutterPlatformViewsController::RegisterViewFactory( +void PlatformViewsController::RegisterViewFactory( NSObject* factory, NSString* factoryId, FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy) { @@ -340,16 +281,16 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, gesture_recognizers_blocking_policies_[idString] = gestureRecognizerBlockingPolicy; } -void FlutterPlatformViewsController::BeginFrame(SkISize frame_size) { +void PlatformViewsController::BeginFrame(SkISize frame_size) { ResetFrameState(); frame_size_ = frame_size; } -void FlutterPlatformViewsController::CancelFrame() { +void PlatformViewsController::CancelFrame() { ResetFrameState(); } -PostPrerollResult FlutterPlatformViewsController::PostPrerollAction( +PostPrerollResult PlatformViewsController::PostPrerollAction( const fml::RefPtr& raster_thread_merger) { // TODO(jonahwilliams): remove this once Software backend is removed for iOS Sim. #ifdef FML_OS_IOS_SIMULATOR @@ -379,7 +320,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, #endif // FML_OS_IOS_SIMULATOR } -void FlutterPlatformViewsController::EndFrame( +void PlatformViewsController::EndFrame( bool should_resubmit_frame, const fml::RefPtr& raster_thread_merger) { #if FML_OS_IOS_SIMULATOR @@ -389,7 +330,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, #endif // FML_OS_IOS_SIMULATOR } -void FlutterPlatformViewsController::PushFilterToVisitedPlatformViews( +void PlatformViewsController::PushFilterToVisitedPlatformViews( const std::shared_ptr& filter, const SkRect& filter_rect) { for (int64_t id : visited_platform_views_) { @@ -399,7 +340,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } } -void FlutterPlatformViewsController::PrerollCompositeEmbeddedView( +void PlatformViewsController::PrerollCompositeEmbeddedView( int64_t view_id, std::unique_ptr params) { SkRect view_bounds = SkRect::Make(frame_size_); @@ -418,19 +359,19 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, views_to_recomposite_.insert(view_id); } -size_t FlutterPlatformViewsController::EmbeddedViewCount() const { +size_t PlatformViewsController::EmbeddedViewCount() const { return composition_order_.size(); } -size_t FlutterPlatformViewsController::LayerPoolSize() const { +size_t PlatformViewsController::LayerPoolSize() const { return layer_pool_->size(); } -UIView* FlutterPlatformViewsController::GetPlatformViewByID(int64_t view_id) { +UIView* PlatformViewsController::GetPlatformViewByID(int64_t view_id) { return [GetFlutterTouchInterceptingViewByID(view_id) embeddedView]; } -FlutterTouchInterceptingView* FlutterPlatformViewsController::GetFlutterTouchInterceptingViewByID( +FlutterTouchInterceptingView* PlatformViewsController::GetFlutterTouchInterceptingViewByID( int64_t view_id) { if (platform_views_.empty()) { return nil; @@ -438,7 +379,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, return platform_views_[view_id].touch_interceptor.get(); } -long FlutterPlatformViewsController::FindFirstResponderPlatformViewId() { +long PlatformViewsController::FindFirstResponderPlatformViewId() { for (auto const& [id, platform_view_data] : platform_views_) { UIView* root_view = (UIView*)platform_view_data.root_view.get(); if (root_view.flt_hasFirstResponderInViewHierarchySubtree) { @@ -448,7 +389,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, return -1; } -void FlutterPlatformViewsController::ClipViewSetMaskView(UIView* clipView) { +void PlatformViewsController::ClipViewSetMaskView(UIView* clipView) { FML_DCHECK([[NSThread currentThread] isMainThread]); if (clipView.maskView) { return; @@ -462,9 +403,9 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // This method is only called when the `embedded_view` needs to be re-composited at the current // frame. See: `CompositeWithParams` for details. -void FlutterPlatformViewsController::ApplyMutators(const MutatorsStack& mutators_stack, - UIView* embedded_view, - const SkRect& bounding_rect) { +void PlatformViewsController::ApplyMutators(const MutatorsStack& mutators_stack, + UIView* embedded_view, + const SkRect& bounding_rect) { if (flutter_view_ == nullptr) { return; } @@ -525,8 +466,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, if (!canApplyBlurBackdrop || !(*iter)->GetFilterMutation().GetFilter().asBlur()) { break; } - CGRect filterRect = - flutter::GetCGRectFromSkRect((*iter)->GetFilterMutation().GetFilterRect()); + CGRect filterRect = GetCGRectFromSkRect((*iter)->GetFilterMutation().GetFilterRect()); // `filterRect` is in global coordinates. We need to convert to local space. filterRect = CGRectApplyAffineTransform( filterRect, CGAffineTransformMakeScale(1 / screenScale, 1 / screenScale)); @@ -579,7 +519,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // the mask view, whose origin is always (0,0) to the flutter_view. transformMatrix.postTranslate(-clipView.frame.origin.x, -clipView.frame.origin.y); - embedded_view.layer.transform = flutter::GetCATransform3DFromSkMatrix(transformMatrix); + embedded_view.layer.transform = GetCATransform3DFromSkMatrix(transformMatrix); } // Composite the PlatformView with `view_id`. @@ -590,8 +530,8 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Note that `views_to_recomposite_` does not represent all the views in the view hierarchy, // if a PlatformView does not change its composition parameter from last frame, it is not // included in the `views_to_recomposite_`. -void FlutterPlatformViewsController::CompositeWithParams(int64_t view_id, - const EmbeddedViewParams& params) { +void PlatformViewsController::CompositeWithParams(int64_t view_id, + const EmbeddedViewParams& params) { CGRect frame = CGRectMake(0, 0, params.sizePoints().width(), params.sizePoints().height()); FlutterTouchInterceptingView* touchInterceptor = platform_views_[view_id].touch_interceptor.get(); #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG @@ -629,11 +569,11 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, ApplyMutators(mutatorStack, touchInterceptor, rect); } -DlCanvas* FlutterPlatformViewsController::CompositeEmbeddedView(int64_t view_id) { +DlCanvas* PlatformViewsController::CompositeEmbeddedView(int64_t view_id) { return slices_[view_id]->canvas(); } -void FlutterPlatformViewsController::Reset() { +void PlatformViewsController::Reset() { // Reset will only be called from the raster thread or a merged raster/platform thread. // platform_views_ must only be modified on the platform thread, and any operations that // read or modify platform views should occur there. @@ -653,17 +593,17 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, visited_platform_views_.clear(); } -bool FlutterPlatformViewsController::SubmitFrame(GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - std::unique_ptr background_frame) { - TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame"); +bool PlatformViewsController::SubmitFrame(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + std::unique_ptr background_frame) { + TRACE_EVENT0("flutter", "PlatformViewsController::SubmitFrame"); // No platform views to render; we're done. if (flutter_view_ == nullptr || (composition_order_.empty() && !had_platform_views_)) { had_platform_views_ = false; return background_frame->Submit(); } - had_platform_views_ = true; + had_platform_views_ = !composition_order_.empty(); bool did_encode = true; LayersMap platform_view_layers; @@ -698,7 +638,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, if (overlay == overlay_layers.end()) { continue; } - std::shared_ptr layer = GetExistingLayer(); + std::shared_ptr layer = GetExistingLayer(); if (!layer) { continue; } @@ -737,8 +677,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, surface_frames.push_back(std::move(background_frame)); // Mark all layers as available, so they can be used in the next frame. - std::vector> unused_layers = - layer_pool_->RemoveUnusedLayers(); + std::vector> unused_layers = layer_pool_->RemoveUnusedLayers(); layer_pool_->RecycleLayers(); auto task = [&, // @@ -763,11 +702,10 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, return did_encode; } -void FlutterPlatformViewsController::CreateMissingOverlays( - GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - size_t required_overlay_layers) { - TRACE_EVENT0("flutter", "FlutterPlatformViewsController::CreateMissingLayers"); +void PlatformViewsController::CreateMissingOverlays(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + size_t required_overlay_layers) { + TRACE_EVENT0("flutter", "PlatformViewsController::CreateMissingLayers"); if (required_overlay_layers <= layer_pool_->size()) { return; @@ -792,14 +730,14 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } /// Update the buffers and mutate the platform views in CATransaction on the platform thread. -void FlutterPlatformViewsController::PerformSubmit( +void PlatformViewsController::PerformSubmit( const LayersMap& platform_view_layers, std::unordered_map& current_composition_params, const std::unordered_set& views_to_recomposite, const std::vector& composition_order, - const std::vector>& unused_layers, + const std::vector>& unused_layers, const std::vector>& surface_frames) { - TRACE_EVENT0("flutter", "FlutterPlatformViewsController::PerformSubmit"); + TRACE_EVENT0("flutter", "PlatformViewsController::PerformSubmit"); FML_DCHECK([[NSThread currentThread] isMainThread]); [CATransaction begin]; @@ -838,9 +776,8 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, [CATransaction commit]; } -void FlutterPlatformViewsController::BringLayersIntoView( - const LayersMap& layer_map, - const std::vector& composition_order) { +void PlatformViewsController::BringLayersIntoView(const LayersMap& layer_map, + const std::vector& composition_order) { FML_DCHECK(flutter_view_); UIView* flutter_view = flutter_view_.get(); @@ -876,43 +813,20 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } } -std::shared_ptr FlutterPlatformViewsController::GetExistingLayer() { +std::shared_ptr PlatformViewsController::GetExistingLayer() { return layer_pool_->GetNextLayer(); } -void FlutterPlatformViewsController::CreateLayer(GrDirectContext* gr_context, - const std::shared_ptr& ios_context, - MTLPixelFormat pixel_format) { +void PlatformViewsController::CreateLayer(GrDirectContext* gr_context, + const std::shared_ptr& ios_context, + MTLPixelFormat pixel_format) { layer_pool_->CreateLayer(gr_context, ios_context, pixel_format); } -void FlutterPlatformViewLayer::UpdateViewState(UIView* flutter_view, - SkRect rect, - int64_t view_id, - int64_t overlay_id) { - UIView* overlay_view_wrapper = this->overlay_view_wrapper.get(); - auto screenScale = [UIScreen mainScreen].scale; - // Set the size of the overlay view wrapper. - // This wrapper view masks the overlay view. - overlay_view_wrapper.frame = CGRectMake(rect.x() / screenScale, rect.y() / screenScale, - rect.width() / screenScale, rect.height() / screenScale); - // Set a unique view identifier, so the overlay_view_wrapper can be identified in XCUITests. - overlay_view_wrapper.accessibilityIdentifier = - [NSString stringWithFormat:@"platform_view[%lld].overlay[%lld]", view_id, overlay_id]; - - UIView* overlay_view = this->overlay_view.get(); - // Set the size of the overlay view. - // This size is equal to the device screen size. - overlay_view.frame = [flutter_view convertRect:flutter_view.bounds toView:overlay_view_wrapper]; - // Set a unique view identifier, so the overlay_view can be identified in XCUITests. - overlay_view.accessibilityIdentifier = - [NSString stringWithFormat:@"platform_view[%lld].overlay_view[%lld]", view_id, overlay_id]; -} - -void FlutterPlatformViewsController::RemoveUnusedLayers( - const std::vector>& unused_layers, +void PlatformViewsController::RemoveUnusedLayers( + const std::vector>& unused_layers, const std::vector& composition_order) { - for (const std::shared_ptr& layer : unused_layers) { + for (const std::shared_ptr& layer : unused_layers) { [layer->overlay_view_wrapper removeFromSuperview]; } @@ -929,7 +843,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, } } -std::vector FlutterPlatformViewsController::GetViewsToDispose() { +std::vector PlatformViewsController::GetViewsToDispose() { std::vector views; if (views_to_dispose_.empty()) { return views; @@ -953,264 +867,10 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, return views; } -void FlutterPlatformViewsController::ResetFrameState() { +void PlatformViewsController::ResetFrameState() { slices_.clear(); composition_order_.clear(); visited_platform_views_.clear(); } } // namespace flutter - -// This recognizers delays touch events from being dispatched to the responder chain until it failed -// recognizing a gesture. -// -// We only fail this recognizer when asked to do so by the Flutter framework (which does so by -// invoking an acceptGesture method on the platform_views channel). And this is how we allow the -// Flutter framework to delay or prevent the embedded view from getting a touch sequence. -@interface DelayingGestureRecognizer : UIGestureRecognizer - -// Indicates that if the `DelayingGestureRecognizer`'s state should be set to -// `UIGestureRecognizerStateEnded` during next `touchesEnded` call. -@property(nonatomic) BOOL shouldEndInNextTouchesEnded; - -// Indicates that the `DelayingGestureRecognizer`'s `touchesEnded` has been invoked without -// setting the state to `UIGestureRecognizerStateEnded`. -@property(nonatomic) BOOL touchedEndedWithoutBlocking; - -@property(nonatomic, readonly) UIGestureRecognizer* forwardingRecognizer; - -- (instancetype)initWithTarget:(id)target - action:(SEL)action - forwardingRecognizer:(UIGestureRecognizer*)forwardingRecognizer; -@end - -// While the DelayingGestureRecognizer is preventing touches from hitting the responder chain -// the touch events are not arriving to the FlutterView (and thus not arriving to the Flutter -// framework). We use this gesture recognizer to dispatch the events directly to the FlutterView -// while during this phase. -// -// If the Flutter framework decides to dispatch events to the embedded view, we fail the -// DelayingGestureRecognizer which sends the events up the responder chain. But since the events -// are handled by the embedded view they are not delivered to the Flutter framework in this phase -// as well. So during this phase as well the ForwardingGestureRecognizer dispatched the events -// directly to the FlutterView. -@interface ForwardingGestureRecognizer : UIGestureRecognizer -- (instancetype)initWithTarget:(id)target - platformViewsController: - (fml::WeakPtr)platformViewsController; -@end - -@interface FlutterTouchInterceptingView () -@property(nonatomic, weak, readonly) UIView* embeddedView; -@property(nonatomic, readonly) DelayingGestureRecognizer* delayingRecognizer; -@property(nonatomic, readonly) FlutterPlatformViewGestureRecognizersBlockingPolicy blockingPolicy; -@end - -@implementation FlutterTouchInterceptingView -- (instancetype)initWithEmbeddedView:(UIView*)embeddedView - platformViewsController: - (fml::WeakPtr)platformViewsController - gestureRecognizersBlockingPolicy: - (FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy { - self = [super initWithFrame:embeddedView.frame]; - if (self) { - self.multipleTouchEnabled = YES; - _embeddedView = embeddedView; - embeddedView.autoresizingMask = - (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); - - [self addSubview:embeddedView]; - - ForwardingGestureRecognizer* forwardingRecognizer = - [[ForwardingGestureRecognizer alloc] initWithTarget:self - platformViewsController:platformViewsController]; - - _delayingRecognizer = [[DelayingGestureRecognizer alloc] initWithTarget:self - action:nil - forwardingRecognizer:forwardingRecognizer]; - _blockingPolicy = blockingPolicy; - - [self addGestureRecognizer:_delayingRecognizer]; - [self addGestureRecognizer:forwardingRecognizer]; - } - return self; -} - -- (void)releaseGesture { - self.delayingRecognizer.state = UIGestureRecognizerStateFailed; -} - -- (void)blockGesture { - switch (_blockingPolicy) { - case FlutterPlatformViewGestureRecognizersBlockingPolicyEager: - // We block all other gesture recognizers immediately in this policy. - self.delayingRecognizer.state = UIGestureRecognizerStateEnded; - break; - case FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded: - if (self.delayingRecognizer.touchedEndedWithoutBlocking) { - // If touchesEnded of the `DelayingGesureRecognizer` has been already invoked, - // we want to set the state of the `DelayingGesureRecognizer` to - // `UIGestureRecognizerStateEnded` as soon as possible. - self.delayingRecognizer.state = UIGestureRecognizerStateEnded; - } else { - // If touchesEnded of the `DelayingGesureRecognizer` has not been invoked, - // We will set a flag to notify the `DelayingGesureRecognizer` to set the state to - // `UIGestureRecognizerStateEnded` when touchesEnded is called. - self.delayingRecognizer.shouldEndInNextTouchesEnded = YES; - } - break; - default: - break; - } -} - -// We want the intercepting view to consume the touches and not pass the touches up to the parent -// view. Make the touch event method not call super will not pass the touches up to the parent view. -// Hence we overide the touch event methods and do nothing. -- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { -} - -- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { -} - -- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { -} - -- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { -} - -- (id)accessibilityContainer { - return self.flutterAccessibilityContainer; -} - -@end - -@implementation DelayingGestureRecognizer - -- (instancetype)initWithTarget:(id)target - action:(SEL)action - forwardingRecognizer:(UIGestureRecognizer*)forwardingRecognizer { - self = [super initWithTarget:target action:action]; - if (self) { - self.delaysTouchesBegan = YES; - self.delaysTouchesEnded = YES; - self.delegate = self; - _shouldEndInNextTouchesEnded = NO; - _touchedEndedWithoutBlocking = NO; - _forwardingRecognizer = forwardingRecognizer; - } - return self; -} - -- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer - shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer { - // The forwarding gesture recognizer should always get all touch events, so it should not be - // required to fail by any other gesture recognizer. - return otherGestureRecognizer != _forwardingRecognizer && otherGestureRecognizer != self; -} - -- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer - shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer { - return otherGestureRecognizer == self; -} - -- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { - self.touchedEndedWithoutBlocking = NO; - [super touchesBegan:touches withEvent:event]; -} - -- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { - if (self.shouldEndInNextTouchesEnded) { - self.state = UIGestureRecognizerStateEnded; - self.shouldEndInNextTouchesEnded = NO; - } else { - self.touchedEndedWithoutBlocking = YES; - } - [super touchesEnded:touches withEvent:event]; -} - -- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { - self.state = UIGestureRecognizerStateFailed; -} -@end - -@implementation ForwardingGestureRecognizer { - // Weak reference to FlutterPlatformViewsController. The FlutterPlatformViewsController has - // a reference to the FlutterViewController, where we can dispatch pointer events to. - // - // The lifecycle of FlutterPlatformViewsController is bind to FlutterEngine, which should always - // outlives the FlutterViewController. And ForwardingGestureRecognizer is owned by a subview of - // FlutterView, so the ForwardingGestureRecognizer never out lives FlutterViewController. - // Therefore, `_platformViewsController` should never be nullptr. - 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 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 - platformViewsController: - (fml::WeakPtr)platformViewsController { - self = [super initWithTarget:target action:nil]; - if (self) { - self.delegate = self; - FML_DCHECK(platformViewsController.get() != nullptr); - _platformViewsController = std::move(platformViewsController); - _currentTouchPointersCount = 0; - } - return self; -} - -- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { - FML_DCHECK(_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.reset(_platformViewsController->getFlutterViewController()); - } - [_flutterViewController.get() touchesBegan:touches withEvent:event]; - _currentTouchPointersCount += touches.count; -} - -- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { - [_flutterViewController.get() touchesMoved:touches withEvent:event]; -} - -- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)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 - // 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); - } -} - -- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { - // In the event of platform view is removed, iOS generates a "stationary" change type instead of - // "cancelled" change type. - // Flutter needs all the cancelled touches to be "cancelled" change types in order to correctly - // handle gesture sequence. - // We always override the change type to "cancelled". - [_flutterViewController.get() forceTouchesCancelled:touches]; - _currentTouchPointersCount -= touches.count; - if (_currentTouchPointersCount == 0) { - self.state = UIGestureRecognizerStateFailed; - _flutterViewController.reset(nil); - } -} - -- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer - shouldRecognizeSimultaneouslyWithGestureRecognizer: - (UIGestureRecognizer*)otherGestureRecognizer { - return YES; -} -@end diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.h b/shell/platform/darwin/ios/ios_external_view_embedder.h index cc7ec59b34800..a1ce91b637930 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.h +++ b/shell/platform/darwin/ios/ios_external_view_embedder.h @@ -12,16 +12,15 @@ namespace flutter { class IOSExternalViewEmbedder : public ExternalViewEmbedder { public: - IOSExternalViewEmbedder(const std::shared_ptr& - platform_views_controller, - const std::shared_ptr& context); + IOSExternalViewEmbedder( + const std::shared_ptr& platform_views_controller, + const std::shared_ptr& context); // |ExternalViewEmbedder| virtual ~IOSExternalViewEmbedder() override; private: - const std::shared_ptr& - platform_views_controller_; + const std::shared_ptr& platform_views_controller_; std::shared_ptr ios_context_; // |ExternalViewEmbedder| diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index 6cd0430481251..b7c1333d91bf5 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -12,7 +12,7 @@ namespace flutter { IOSExternalViewEmbedder::IOSExternalViewEmbedder( - const std::shared_ptr& platform_views_controller, + const std::shared_ptr& platform_views_controller, const std::shared_ptr& context) : platform_views_controller_(platform_views_controller), ios_context_(context) { FML_CHECK(ios_context_); diff --git a/shell/platform/darwin/ios/platform_view_ios.h b/shell/platform/darwin/ios/platform_view_ios.h index fed1adfba1899..299015d785074 100644 --- a/shell/platform/darwin/ios/platform_view_ios.h +++ b/shell/platform/darwin/ios/platform_view_ios.h @@ -42,13 +42,13 @@ class PlatformViewIOS final : public PlatformView { public: PlatformViewIOS(PlatformView::Delegate& delegate, const std::shared_ptr& context, - const std::shared_ptr& platform_views_controller, + const std::shared_ptr& platform_views_controller, const flutter::TaskRunners& task_runners); explicit PlatformViewIOS( PlatformView::Delegate& delegate, IOSRenderingAPI rendering_api, - const std::shared_ptr& platform_views_controller, + const std::shared_ptr& platform_views_controller, const flutter::TaskRunners& task_runners, const std::shared_ptr& worker_task_runner, const std::shared_ptr& is_gpu_disabled_sync_switch); @@ -139,7 +139,7 @@ class PlatformViewIOS final : public PlatformView { std::mutex ios_surface_mutex_; std::unique_ptr ios_surface_; std::shared_ptr ios_context_; - const std::shared_ptr& platform_views_controller_; + const std::shared_ptr& platform_views_controller_; AccessibilityBridgeManager accessibility_bridge_; fml::scoped_nsprotocol text_input_plugin_; ScopedObserver dealloc_view_controller_observer_; diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index 4cda02ca7f3e6..1e58148319e84 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -42,7 +42,7 @@ PlatformViewIOS::PlatformViewIOS( PlatformView::Delegate& delegate, const std::shared_ptr& context, - const std::shared_ptr& platform_views_controller, + const std::shared_ptr& platform_views_controller, const flutter::TaskRunners& task_runners) : PlatformView(delegate, task_runners), ios_context_(context), @@ -54,7 +54,7 @@ new PlatformMessageHandlerIos(task_runners.GetPlatformTaskRunner())) {} PlatformViewIOS::PlatformViewIOS( PlatformView::Delegate& delegate, IOSRenderingAPI rendering_api, - const std::shared_ptr& platform_views_controller, + const std::shared_ptr& platform_views_controller, const flutter::TaskRunners& task_runners, const std::shared_ptr& worker_task_runner, const std::shared_ptr& is_gpu_disabled_sync_switch)