diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 70fdf7936bf8d..51349cb70e1ff 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -18,6 +18,8 @@ #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #import "flutter/shell/platform/darwin/ios/ios_surface.h" +static const NSUInteger kFlutterClippingMaskViewPoolCapacity = 5; + @implementation UIView (FirstResponder) - (BOOL)flt_hasFirstResponderInViewHierarchySubtree { if (self.isFirstResponder) { @@ -445,8 +447,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, clipView.maskView = [mask_view_pool_.get() getMaskViewWithFrame:frame]; } -// 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) { @@ -461,10 +461,12 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, NSMutableArray* blurFilters = [[[NSMutableArray alloc] init] autorelease]; FML_DCHECK(!clipView.maskView || [clipView.maskView isKindOfClass:[FlutterClippingMaskView class]]); - if (clipView.maskView) { - [mask_view_pool_.get() insertViewToPoolIfNeeded:(FlutterClippingMaskView*)(clipView.maskView)]; - clipView.maskView = nil; + if (mask_view_pool_.get() == nil) { + mask_view_pool_.reset([[FlutterClippingMaskViewPool alloc] + initWithCapacity:kFlutterClippingMaskViewPoolCapacity]); } + [mask_view_pool_.get() recycleMaskViews]; + clipView.maskView = nil; CGFloat screenScale = [UIScreen mainScreen].scale; auto iter = mutators_stack.Begin(); while (iter != mutators_stack.End()) { @@ -568,14 +570,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, embedded_view.layer.transform = flutter::GetCATransform3DFromSkMatrix(transformMatrix); } -// Composite the PlatformView with `view_id`. -// -// Every frame, during the paint traversal of the layer tree, this method is called for all -// the PlatformViews in `views_to_recomposite_`. -// -// 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) { CGRect frame = CGRectMake(0, 0, params.sizePoints().width(), params.sizePoints().height()); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 6f205b88c5d2f..670206331b356 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -2649,8 +2649,7 @@ - (void)testFlutterClippingMaskViewPoolReuseViewsAfterRecycle { [[[FlutterClippingMaskViewPool alloc] initWithCapacity:2] autorelease]; FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero]; FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero]; - [pool insertViewToPoolIfNeeded:view1]; - [pool insertViewToPoolIfNeeded:view2]; + [pool recycleMaskViews]; CGRect newRect = CGRectMake(0, 0, 10, 10); FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:newRect]; FlutterClippingMaskView* view4 = [pool getMaskViewWithFrame:newRect]; @@ -2728,6 +2727,10 @@ - (void)testClipMaskViewIsReused { auto embeddedViewParams1 = std::make_unique( screenScaleMatrix, SkSize::Make(10, 10), stack1); + flutter::MutatorsStack stack2; + auto embeddedViewParams2 = std::make_unique( + screenScaleMatrix, SkSize::Make(10, 10), stack2); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); flutterPlatformViewsController->CompositeEmbeddedView(1); UIView* childClippingView1 = gMockPlatformView.superview.superview; @@ -2735,10 +2738,6 @@ - (void)testClipMaskViewIsReused { XCTAssertNotNil(maskView1); // Composite a new frame. - flutterPlatformViewsController->BeginFrame(SkISize::Make(100, 100)); - flutter::MutatorsStack stack2; - auto embeddedViewParams2 = std::make_unique( - screenScaleMatrix, SkSize::Make(10, 10), stack2); auto embeddedViewParams3 = std::make_unique( screenScaleMatrix, SkSize::Make(10, 10), stack2); flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams3)); @@ -2764,77 +2763,6 @@ - (void)testClipMaskViewIsReused { XCTAssertNil(childClippingView1.maskView); } -- (void)testDifferentClipMaskViewIsUsedForEachView { - flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); - flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread_task_runner, - /*raster=*/thread_task_runner, - /*ui=*/thread_task_runner, - /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], - result); - UIView* view1 = gMockPlatformView; - - // This overwrites `gMockPlatformView` to another view. - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - UIView* view2 = gMockPlatformView; - - XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)] autorelease]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack1; - // Layer tree always pushes a screen scale factor to the stack - SkMatrix screenScaleMatrix = - SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); - stack1.PushTransform(screenScaleMatrix); - // Push a clip rect - SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); - stack1.PushClipRect(rect); - - auto embeddedViewParams1 = std::make_unique( - screenScaleMatrix, SkSize::Make(10, 10), stack1); - - flutter::MutatorsStack stack2; - stack2.PushClipRect(rect); - auto embeddedViewParams2 = std::make_unique( - screenScaleMatrix, SkSize::Make(10, 10), stack2); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(1); - UIView* childClippingView1 = view1.superview.superview; - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - UIView* childClippingView2 = view2.superview.superview; - UIView* maskView1 = childClippingView1.maskView; - UIView* maskView2 = childClippingView2.maskView; - XCTAssertNotEqual(maskView1, maskView2); -} - // Return true if a correct visual effect view is found. It also implies all the validation in this // method passes. // diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 55e9d5a8fc46c..8447d19ca2df9 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -56,7 +56,7 @@ // in the pool. If there are none available, a new FlutterClippingMaskView is constructed. If the // capacity is reached, the newly constructed FlutterClippingMaskView is not added to the pool. // -// Call |insertViewToPoolIfNeeded:| to return a maskView to the pool. +// Call |recycleMaskViews| to mark all the FlutterClippingMaskViews in the pool available. @interface FlutterClippingMaskViewPool : NSObject // Initialize the pool with `capacity`. When the `capacity` is reached, a FlutterClippingMaskView is @@ -66,8 +66,8 @@ // Reuse a maskView from the pool, or allocate a new one. - (FlutterClippingMaskView*)getMaskViewWithFrame:(CGRect)frame; -// Insert the `maskView` into the pool. -- (void)insertViewToPoolIfNeeded:(FlutterClippingMaskView*)maskView; +// Mark all the maskViews available. +- (void)recycleMaskViews; @end @@ -291,12 +291,20 @@ class FlutterPlatformViewsController { int CountClips(const MutatorsStack& mutators_stack); void ClipViewSetMaskView(UIView* clipView); - // 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` + // Clips are applied to the super view with a CALayer mask. Transforms are applied to the + // current view that's at the head of the chain. For example the following mutators stack [T_1, + // C_2, T_3, T_4, C_5, T_6] where T denotes a transform and C denotes a clip, will result in the + // following UIView tree: + // + // C_2 -> C_5 -> PLATFORM_VIEW + // (PLATFORM_VIEW is a subview of C_5 which is a subview of C_2) + // + // T_1 is applied to C_2, T_3 and T_4 are applied to C_5, and T_6 is applied to PLATFORM_VIEW. + // + // After each clip operation, we update the head to the super view of the current head. // // The `bounding_rect` is the final bounding rect of the PlatformView // (EmbeddedViewParams::finalBoundingRect). If a clip mutator's rect contains the final bounding @@ -304,7 +312,6 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view, const SkRect& bounding_rect); - void CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params); // Allocates a new FlutterPlatformViewLayer if needed, draws the pixels within the rect from diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm index ebcfcad0ddfc1..89e1687795248 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -9,7 +9,6 @@ #import "flutter/shell/platform/darwin/ios/ios_surface.h" static int kMaxPointsInVerb = 4; -static const NSUInteger kFlutterClippingMaskViewPoolCapacity = 5; namespace flutter { @@ -27,10 +26,7 @@ FlutterPlatformViewsController::FlutterPlatformViewsController() : layer_pool_(std::make_unique()), - weak_factory_(std::make_unique>(this)) { - mask_view_pool_.reset( - [[FlutterClippingMaskViewPool alloc] initWithCapacity:kFlutterClippingMaskViewPoolCapacity]); -}; + weak_factory_(std::make_unique>(this)){}; FlutterPlatformViewsController::~FlutterPlatformViewsController() = default; @@ -462,10 +458,9 @@ @interface FlutterClippingMaskViewPool () // The maximum number of `FlutterClippingMaskView` the pool can contain. // This prevents the pool to grow infinately and limits the maximum memory a pool can use. @property(assign, nonatomic) NSUInteger capacity; - -// The pool contains the views that are available to use. -// The number of items in the pool must not excceds `capacity`. -@property(retain, nonatomic) NSMutableSet* pool; +@property(retain, nonatomic) NSMutableArray* pool; +// The index points to the first available FlutterClippingMaskView in the `pool`. +@property(assign, nonatomic) NSUInteger availableIndex; @end @@ -473,42 +468,48 @@ @implementation FlutterClippingMaskViewPool : NSObject - (instancetype)initWithCapacity:(NSInteger)capacity { if (self = [super init]) { - // Most of cases, there are only one PlatformView in the scene. - // Thus init with the capacity of 1. - _pool = [[NSMutableSet alloc] initWithCapacity:1]; + _pool = [[NSMutableArray alloc] initWithCapacity:capacity]; _capacity = capacity; + _availableIndex = 0; } return self; } - (FlutterClippingMaskView*)getMaskViewWithFrame:(CGRect)frame { - FML_DCHECK(self.pool.count <= self.capacity); + FML_DCHECK(self.availableIndex <= self.capacity); FlutterClippingMaskView* maskView; - if (self.pool.count == 0) { - // The pool is empty, alloc a new one. + if (self.availableIndex == self.capacity) { + // The pool is full, alloc a new one. maskView = [[[FlutterClippingMaskView alloc] initWithFrame:frame screenScale:[UIScreen mainScreen].scale] autorelease]; return maskView; } - maskView = [self.pool anyObject]; - maskView.frame = frame; - [maskView reset]; - [self.pool removeObject:maskView]; + + if (self.availableIndex >= self.pool.count) { + // The pool doesn't have enough maskViews, alloc a new one and add to the pool. + maskView = + [[[FlutterClippingMaskView alloc] initWithFrame:frame + screenScale:[UIScreen mainScreen].scale] autorelease]; + [self.pool addObject:maskView]; + FML_DCHECK(self.pool.count <= self.capacity); + } else { + // Reuse a maskView from the pool. + maskView = [self.pool objectAtIndex:self.availableIndex]; + maskView.frame = frame; + [maskView reset]; + } + self.availableIndex++; return maskView; } -- (void)insertViewToPoolIfNeeded:(FlutterClippingMaskView*)maskView { - FML_DCHECK(![self.pool containsObject:maskView]); - FML_DCHECK(self.pool.count <= self.capacity); - if (self.pool.count == self.capacity) { - return; - } - [self.pool addObject:maskView]; +- (void)recycleMaskViews { + self.availableIndex = 0; } - (void)dealloc { [_pool release]; + _pool = nil; [super dealloc]; } diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index 4e69202318806..0fcb3c83f3e1f 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -57,9 +57,6 @@ 684FFF8D29F9C10700281002 /* golden_platform_view_large_cliprrect_iPhone SE (3rd generation)_16.2_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 684FFF7929F9C10600281002 /* golden_platform_view_large_cliprrect_iPhone SE (3rd generation)_16.2_simulator.png */; }; 684FFF8E29F9C10700281002 /* golden_spawn_engine_works_iPhone SE (3rd generation)_16.2_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 684FFF7A29F9C10700281002 /* golden_spawn_engine_works_iPhone SE (3rd generation)_16.2_simulator.png */; }; 684FFF8F29F9C10700281002 /* golden_platform_view_with_other_backdrop_filter_iPhone SE (3rd generation)_16.2_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 684FFF7B29F9C10700281002 /* golden_platform_view_with_other_backdrop_filter_iPhone SE (3rd generation)_16.2_simulator.png */; }; - 6860CE252A01B2FF00B68EC5 /* golden_two_platform_view_clip_rrect_iPhone SE (3rd generation)_16.2_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6860CE222A01B2FF00B68EC5 /* golden_two_platform_view_clip_rrect_iPhone SE (3rd generation)_16.2_simulator.png */; }; - 6860CE262A01B2FF00B68EC5 /* golden_two_platform_view_clip_rect_iPhone SE (3rd generation)_16.2_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6860CE232A01B2FF00B68EC5 /* golden_two_platform_view_clip_rect_iPhone SE (3rd generation)_16.2_simulator.png */; }; - 6860CE272A01B2FF00B68EC5 /* golden_two_platform_view_clip_path_iPhone SE (3rd generation)_16.2_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6860CE242A01B2FF00B68EC5 /* golden_two_platform_view_clip_path_iPhone SE (3rd generation)_16.2_simulator.png */; }; 68A5B63423EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68A5B63323EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m */; }; 68D4017D2564859300ECD91A /* ContinuousTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = 68D4017C2564859300ECD91A /* ContinuousTexture.m */; }; F26F15B8268B6B5600EC54D3 /* iPadGestureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F26F15B7268B6B5500EC54D3 /* iPadGestureTests.m */; }; @@ -177,9 +174,6 @@ 684FFF7929F9C10600281002 /* golden_platform_view_large_cliprrect_iPhone SE (3rd generation)_16.2_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_large_cliprrect_iPhone SE (3rd generation)_16.2_simulator.png"; sourceTree = ""; }; 684FFF7A29F9C10700281002 /* golden_spawn_engine_works_iPhone SE (3rd generation)_16.2_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_spawn_engine_works_iPhone SE (3rd generation)_16.2_simulator.png"; sourceTree = ""; }; 684FFF7B29F9C10700281002 /* golden_platform_view_with_other_backdrop_filter_iPhone SE (3rd generation)_16.2_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_with_other_backdrop_filter_iPhone SE (3rd generation)_16.2_simulator.png"; sourceTree = ""; }; - 6860CE222A01B2FF00B68EC5 /* golden_two_platform_view_clip_rrect_iPhone SE (3rd generation)_16.2_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_two_platform_view_clip_rrect_iPhone SE (3rd generation)_16.2_simulator.png"; sourceTree = ""; }; - 6860CE232A01B2FF00B68EC5 /* golden_two_platform_view_clip_rect_iPhone SE (3rd generation)_16.2_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_two_platform_view_clip_rect_iPhone SE (3rd generation)_16.2_simulator.png"; sourceTree = ""; }; - 6860CE242A01B2FF00B68EC5 /* golden_two_platform_view_clip_path_iPhone SE (3rd generation)_16.2_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_two_platform_view_clip_path_iPhone SE (3rd generation)_16.2_simulator.png"; sourceTree = ""; }; 68A5B63323EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewGestureRecognizerTests.m; sourceTree = ""; }; 68D4017B2564859300ECD91A /* ContinuousTexture.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContinuousTexture.h; sourceTree = ""; }; 68D4017C2564859300ECD91A /* ContinuousTexture.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContinuousTexture.m; sourceTree = ""; }; @@ -308,9 +302,6 @@ F7B464DC2759D02B00079189 /* Goldens */ = { isa = PBXGroup; children = ( - 6860CE242A01B2FF00B68EC5 /* golden_two_platform_view_clip_path_iPhone SE (3rd generation)_16.2_simulator.png */, - 6860CE232A01B2FF00B68EC5 /* golden_two_platform_view_clip_rect_iPhone SE (3rd generation)_16.2_simulator.png */, - 6860CE222A01B2FF00B68EC5 /* golden_two_platform_view_clip_rrect_iPhone SE (3rd generation)_16.2_simulator.png */, 684FFF7229F9C10500281002 /* golden_bogus_font_text_iPhone SE (3rd generation)_16.2_simulator.png */, 684FFF7029F9C10500281002 /* golden_non_full_screen_flutter_view_platform_view_iPhone SE (3rd generation)_16.2_simulator.png */, 684FFF7729F9C10600281002 /* golden_platform_view_clippath_iPhone SE (3rd generation)_16.2_simulator.png */, @@ -465,9 +456,7 @@ 684FFF7D29F9C10700281002 /* golden_platform_view_cliprect_with_transform_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 684FFF8829F9C10700281002 /* golden_platform_view_cliprect_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 684FFF8529F9C10700281002 /* golden_platform_view_cliprrect_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, - 6860CE252A01B2FF00B68EC5 /* golden_two_platform_view_clip_rrect_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 684FFF8F29F9C10700281002 /* golden_platform_view_with_other_backdrop_filter_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, - 6860CE262A01B2FF00B68EC5 /* golden_two_platform_view_clip_rect_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 684FFF8A29F9C10700281002 /* golden_platform_view_clippath_with_transform_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 684FFF8429F9C10700281002 /* golden_non_full_screen_flutter_view_platform_view_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 684FFF7C29F9C10700281002 /* golden_platform_view_cliprrect_with_transform_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, @@ -480,7 +469,6 @@ 684FFF8D29F9C10700281002 /* golden_platform_view_large_cliprrect_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 684FFF8329F9C10700281002 /* golden_platform_view_transform_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 684FFF8B29F9C10700281002 /* golden_platform_view_clippath_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, - 6860CE272A01B2FF00B68EC5 /* golden_two_platform_view_clip_path_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, 684FFF8129F9C10700281002 /* golden_platform_view_large_cliprrect_with_transform_iPhone SE (3rd generation)_16.2_simulator.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 4dca96551a3b7..28c84f8e6896f 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -69,10 +69,7 @@ - (BOOL)application:(UIApplication*)application @"--spawn-engine-works" : @"spawn_engine_works", @"--pointer-events" : @"pointer_events", @"--platform-view-scrolling-under-widget" : @"platform_view_scrolling_under_widget", - @"--platform-view-cliprect-after-moved" : @"platform_view_cliprect_after_moved", - @"--two-platform-view-clip-rect" : @"two_platform_view_clip_rect", - @"--two-platform-view-clip-rrect" : @"two_platform_view_clip_rrect", - @"--two-platform-view-clip-path" : @"two_platform_view_clip_path", + @"--platform-view-cliprect-after-moved" : @"platform_view_cliprect_after_moved" }; __block NSString* flutterViewControllerTestName = nil; [launchArgsMap diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m index 3bcff696844a0..6ecb07a7c7c5d 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenTestManager.m @@ -47,9 +47,6 @@ - (instancetype)initWithLaunchArg:(NSString*)launchArg { @"--bogus-font-text" : @"bogus_font_text", @"--spawn-engine-works" : @"spawn_engine_works", @"--platform-view-cliprect-after-moved" : @"platform_view_cliprect_after_moved", - @"--two-platform-view-clip-rect" : @"two_platform_view_clip_rect", - @"--two-platform-view-clip-rrect" : @"two_platform_view_clip_rrect", - @"--two-platform-view-clip-path" : @"two_platform_view_clip_path", }; }); _identifier = launchArgsMap[launchArg]; diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m index cd28f98781440..47d49f3c7779c 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewUITests.m @@ -254,60 +254,6 @@ - (void)testPlatformView { @end -@interface TwoPlatformViewClipRectTests : GoldenPlatformViewTests - -@end - -@implementation TwoPlatformViewClipRectTests - -- (instancetype)initWithInvocation:(NSInvocation*)invocation { - GoldenTestManager* manager = - [[GoldenTestManager alloc] initWithLaunchArg:@"--two-platform-view-clip-rect"]; - return [super initWithManager:manager invocation:invocation]; -} - -- (void)testPlatformView { - [self checkPlatformViewGolden]; -} - -@end - -@interface TwoPlatformViewClipRRectTests : GoldenPlatformViewTests - -@end - -@implementation TwoPlatformViewClipRRectTests - -- (instancetype)initWithInvocation:(NSInvocation*)invocation { - GoldenTestManager* manager = - [[GoldenTestManager alloc] initWithLaunchArg:@"--two-platform-view-clip-rrect"]; - return [super initWithManager:manager invocation:invocation]; -} - -- (void)testPlatformView { - [self checkPlatformViewGolden]; -} - -@end - -@interface TwoPlatformViewClipPathTests : GoldenPlatformViewTests - -@end - -@implementation TwoPlatformViewClipPathTests - -- (instancetype)initWithInvocation:(NSInvocation*)invocation { - GoldenTestManager* manager = - [[GoldenTestManager alloc] initWithLaunchArg:@"--two-platform-view-clip-path"]; - return [super initWithManager:manager invocation:invocation]; -} - -- (void)testPlatformView { - [self checkPlatformViewGolden]; -} - -@end - @interface PlatformViewMutationTransformTests : GoldenPlatformViewTests @end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_two_platform_view_clip_path_iPhone SE (3rd generation)_16.2_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_two_platform_view_clip_path_iPhone SE (3rd generation)_16.2_simulator.png deleted file mode 100644 index 71caaac361f03..0000000000000 Binary files a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_two_platform_view_clip_path_iPhone SE (3rd generation)_16.2_simulator.png and /dev/null differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_two_platform_view_clip_rect_iPhone SE (3rd generation)_16.2_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_two_platform_view_clip_rect_iPhone SE (3rd generation)_16.2_simulator.png deleted file mode 100644 index d90daed50411a..0000000000000 Binary files a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_two_platform_view_clip_rect_iPhone SE (3rd generation)_16.2_simulator.png and /dev/null differ diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_two_platform_view_clip_rrect_iPhone SE (3rd generation)_16.2_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_two_platform_view_clip_rrect_iPhone SE (3rd generation)_16.2_simulator.png deleted file mode 100644 index 09d187affdd99..0000000000000 Binary files a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_two_platform_view_clip_rrect_iPhone SE (3rd generation)_16.2_simulator.png and /dev/null differ diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 741ed0c854cef..75ccd395206be 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -112,6 +112,7 @@ class PlatformViewNoOverlayIntersectionScenario extends Scenario } } + /// A platform view that is larger than the display size. /// This is only applicable on Android while using virtual displays. /// Related issue: https://github.com/flutter/flutter/issues/28978. @@ -575,7 +576,7 @@ class PlatformViewClipRectAfterMovedScenario extends Scenario with _BasePlatform ..pushClipRect(const Rect.fromLTRB(100, 100, 400, 400)); addPlatformView( - _numberOfFrames == 10? 10000: id, + _numberOfFrames == 10? 10000:id, dispatcher: view.platformDispatcher, sceneBuilder: builder, ); @@ -638,6 +639,7 @@ class PlatformViewClipRRectScenario extends PlatformViewScenario { } } + /// Platform view with clip rrect. /// The bounding rect of the rrect is the same as PlatformView and only the corner radii clips the PlatformView. class PlatformViewLargeClipRRectScenario extends PlatformViewScenario { @@ -876,187 +878,6 @@ class PlatformViewClipPathWithTransformScenario extends PlatformViewScenario { } } -/// Two platform views, both have clip rects -class TwoPlatformViewClipRect extends Scenario - with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - TwoPlatformViewClipRect( - super.view, { - required this.firstId, - required this.secondId, - }); - - /// The platform view identifier to use for the first platform view. - final int firstId; - - /// The platform view identifier to use for the second platform view. - final int secondId; - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - builder.pushOffset(0, 600); - builder.pushClipRect(const Rect.fromLTRB(100, 100, 400, 400)); - - addPlatformView( - firstId, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - text: 'platform view 1', - ); - - builder.pop(); - builder.pop(); - - // Use a different rect to differentiate from the 1st clip rect. - builder.pushClipRect(const Rect.fromLTRB(100, 100, 300, 300)); - - addPlatformView( - secondId, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - text: 'platform view 2', - ); - - builder.pop(); - final Scene scene = builder.build(); - view.render(scene); - scene.dispose(); - } -} - -/// Two platform views, both have clip rrects -class TwoPlatformViewClipRRect extends Scenario - with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - TwoPlatformViewClipRRect( - super.view, { - required this.firstId, - required this.secondId, - }); - - /// The platform view identifier to use for the first platform view. - final int firstId; - - /// The platform view identifier to use for the second platform view. - final int secondId; - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - builder.pushOffset(0, 600); - builder.pushClipRRect( - RRect.fromLTRBAndCorners( - 0, - 0, - 500, - 500, - topLeft: const Radius.circular(15), - topRight: const Radius.circular(50), - bottomLeft: const Radius.circular(50), - ), - ); - - addPlatformView( - firstId, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - text: 'platform view 1', - ); - - builder.pop(); - builder.pop(); - - // Use a different rrect to differentiate from the 1st clip rrect. - builder.pushClipRRect( - RRect.fromLTRBAndCorners( - 0, - 0, - 500, - 500, - topLeft: const Radius.circular(100), - topRight: const Radius.circular(50), - bottomLeft: const Radius.circular(50), - ), - ); - - addPlatformView( - secondId, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - text: 'platform view 2', - ); - - builder.pop(); - final Scene scene = builder.build(); - view.render(scene); - scene.dispose(); - } -} - -/// Two platform views, both have clip path -class TwoPlatformViewClipPath extends Scenario - with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - TwoPlatformViewClipPath( - super.view, { - required this.firstId, - required this.secondId, - }); - - /// The platform view identifier to use for the first platform view. - final int firstId; - - /// The platform view identifier to use for the second platform view. - final int secondId; - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - builder.pushOffset(0, 600); - final Path path = Path() - ..moveTo(100, 100) - ..quadraticBezierTo(50, 250, 100, 400) - ..lineTo(350, 400) - ..cubicTo(400, 300, 300, 200, 350, 100) - ..close(); - - builder.pushClipPath(path); - - addPlatformView( - firstId, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - text: 'platform view 1', - ); - - builder.pop(); - builder.pop(); - - // Use a different path to differentiate from the 1st clip path. - final Path path2 = Path() - ..moveTo(100, 100) - ..quadraticBezierTo(100, 150, 100, 400) - ..lineTo(350, 350) - ..cubicTo(400, 300, 300, 200, 350, 200) - ..close(); - - builder.pushClipPath(path2); - - addPlatformView( - secondId, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - text: 'platform view 2', - ); - - builder.pop(); - final Scene scene = builder.build(); - view.render(scene); - scene.dispose(); - } -} - /// Platform view with transform. class PlatformViewTransformScenario extends PlatformViewScenario { /// Constructs a platform view with transform scenario. @@ -1223,10 +1044,10 @@ class PlatformViewForOverlappingPlatformViewsScenario extends Scenario /// Creates the PlatformViewForOverlappingPlatformViewsScenario. PlatformViewForOverlappingPlatformViewsScenario( - super.view, { - required this.foregroundId, - required this.backgroundId, - }) { + super.view, { + required this.foregroundId, + required this.backgroundId, + }) { _nextFrame = _firstFrame; } @@ -1330,7 +1151,7 @@ class PlatformViewForOverlappingPlatformViewsScenario extends Scenario view.platformDispatcher.sendPlatformMessage( 'flutter/platform_views', message.buffer.asByteData(), - (ByteData? response) {}, + (ByteData? response) {}, ); } } @@ -1539,7 +1360,7 @@ class PlatformViewScrollingUnderWidget extends Scenario super.view, { required int firstPlatformViewId, required int lastPlatformViewId, - }) : _firstPlatformViewId = firstPlatformViewId, + }) : _firstPlatformViewId = firstPlatformViewId, _lastPlatformViewId = lastPlatformViewId; final int _firstPlatformViewId; @@ -1629,6 +1450,7 @@ void addPlatformView( } final String platformViewKey = '$viewType-$id'; + if (_createdPlatformViews.containsKey(platformViewKey)) { addPlatformViewToSceneBuilder( id, @@ -1651,6 +1473,7 @@ void addPlatformView( const int valueString = 7; const int valueUint8List = 8; const int valueMap = 13; + final Uint8List message = Uint8List.fromList([ valueString, ..._encodeString('create'), diff --git a/testing/scenario_app/lib/src/scenarios.dart b/testing/scenario_app/lib/src/scenarios.dart index e18f3a8009525..ff71e609e0cdd 100644 --- a/testing/scenario_app/lib/src/scenarios.dart +++ b/testing/scenario_app/lib/src/scenarios.dart @@ -53,9 +53,6 @@ Map _scenarios = { 'platform_view_gesture_reject_after_touches_ended': (FlutterView view) => PlatformViewForTouchIOSScenario(view, id: _viewId++, accept: false, rejectUntilTouchesEnded: true), 'platform_view_gesture_accept_with_overlapping_platform_views': (FlutterView view) => PlatformViewForOverlappingPlatformViewsScenario(view, foregroundId: _viewId++, backgroundId: _viewId++), 'platform_view_scrolling_under_widget':(FlutterView view) => PlatformViewScrollingUnderWidget(view, firstPlatformViewId: _viewId++, lastPlatformViewId: _viewId+=16), - 'two_platform_view_clip_rect': (FlutterView view) => TwoPlatformViewClipRect(view, firstId: _viewId++, secondId: _viewId++), - 'two_platform_view_clip_rrect': (FlutterView view) => TwoPlatformViewClipRRect(view, firstId: _viewId++, secondId: _viewId++), - 'two_platform_view_clip_path': (FlutterView view) => TwoPlatformViewClipPath(view, firstId: _viewId++, secondId: _viewId++), 'tap_status_bar': (FlutterView view) => TouchesScenario(view), 'initial_route_reply': (FlutterView view) => InitialRouteReply(view), 'platform_view_with_continuous_texture': (FlutterView view) => PlatformViewWithContinuousTexture(view, id: _viewId++),