Flutter iOS Embedder
FlutterPlatformViewsTest.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #import <OCMock/OCMock.h>
6 #import <UIKit/UIKit.h>
7 #import <WebKit/WebKit.h>
8 #import <XCTest/XCTest.h>
9 #include "fml/synchronization/count_down_latch.h"
11 
12 #import "flutter/fml/thread.h"
20 
22 
24 __weak static UIView* gMockPlatformView = nil;
25 const float kFloatCompareEpsilon = 0.001;
26 
28 @end
30 
31 - (instancetype)init {
32  self = [super init];
33  if (self) {
34  gMockPlatformView = self;
35  }
36  return self;
37 }
38 
39 - (void)dealloc {
40  gMockPlatformView = nil;
41 }
42 
43 @end
44 
46 @property(nonatomic, strong) UIView* view;
47 @property(nonatomic, assign) BOOL viewCreated;
48 @end
49 
51 
52 - (instancetype)init {
53  if (self = [super init]) {
54  _view = [[FlutterPlatformViewsTestMockPlatformView alloc] init];
55  _viewCreated = NO;
56  }
57  return self;
58 }
59 
60 - (UIView*)view {
61  [self checkViewCreatedOnce];
62  return _view;
63 }
64 
65 - (void)checkViewCreatedOnce {
66  if (self.viewCreated) {
67  abort();
68  }
69  self.viewCreated = YES;
70 }
71 
72 @end
73 
75  : NSObject <FlutterPlatformViewFactory>
76 @end
77 
79 - (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
80  viewIdentifier:(int64_t)viewId
81  arguments:(id _Nullable)args {
83 }
84 
85 @end
86 
88 @property(nonatomic, strong) UIView* view;
89 @property(nonatomic, assign) BOOL viewCreated;
90 @end
91 
93 - (instancetype)init {
94  if (self = [super init]) {
95  _view = [[WKWebView alloc] init];
96  gMockPlatformView = _view;
97  _viewCreated = NO;
98  }
99  return self;
100 }
101 
102 - (UIView*)view {
103  [self checkViewCreatedOnce];
104  return _view;
105 }
106 
107 - (void)checkViewCreatedOnce {
108  if (self.viewCreated) {
109  abort();
110  }
111  self.viewCreated = YES;
112 }
113 @end
114 
116 @end
117 
119 - (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
120  viewIdentifier:(int64_t)viewId
121  arguments:(id _Nullable)args {
122  return [[FlutterPlatformViewsTestMockWebView alloc] init];
123 }
124 @end
125 
127 @end
128 
130 - (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
131  viewIdentifier:(int64_t)viewId
132  arguments:(id _Nullable)args {
133  return nil;
134 }
135 
136 @end
137 
138 namespace flutter {
139 namespace {
140 class FlutterPlatformViewsTestMockPlatformViewDelegate : public PlatformView::Delegate {
141  public:
142  void OnPlatformViewCreated(std::unique_ptr<Surface> surface) override {}
143  void OnPlatformViewDestroyed() override {}
144  void OnPlatformViewScheduleFrame() override {}
145  void OnPlatformViewAddView(int64_t view_id,
146  const ViewportMetrics& viewport_metrics,
147  AddViewCallback callback) override {}
148  void OnPlatformViewRemoveView(int64_t view_id, RemoveViewCallback callback) override {}
149  void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {}
150  void OnPlatformViewSetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics) override {}
151  const flutter::Settings& OnPlatformViewGetSettings() const override { return settings_; }
152  void OnPlatformViewDispatchPlatformMessage(std::unique_ptr<PlatformMessage> message) override {}
153  void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr<PointerDataPacket> packet) override {
154  }
155  void OnPlatformViewDispatchSemanticsAction(int32_t id,
156  SemanticsAction action,
157  fml::MallocMapping args) override {}
158  void OnPlatformViewSetSemanticsEnabled(bool enabled) override {}
159  void OnPlatformViewSetAccessibilityFeatures(int32_t flags) override {}
160  void OnPlatformViewRegisterTexture(std::shared_ptr<Texture> texture) override {}
161  void OnPlatformViewUnregisterTexture(int64_t texture_id) override {}
162  void OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) override {}
163 
164  void LoadDartDeferredLibrary(intptr_t loading_unit_id,
165  std::unique_ptr<const fml::Mapping> snapshot_data,
166  std::unique_ptr<const fml::Mapping> snapshot_instructions) override {
167  }
168  void LoadDartDeferredLibraryError(intptr_t loading_unit_id,
169  const std::string error_message,
170  bool transient) override {}
171  void UpdateAssetResolverByType(std::unique_ptr<flutter::AssetResolver> updated_asset_resolver,
172  flutter::AssetResolver::AssetResolverType type) override {}
173 
174  flutter::Settings settings_;
175 };
176 
177 BOOL BlurRadiusEqualToBlurRadius(CGFloat radius1, CGFloat radius2) {
178  const CGFloat epsilon = 0.01;
179  return std::abs(radius1 - radius2) < epsilon;
180 }
181 
182 } // namespace
183 } // namespace flutter
184 
185 @interface FlutterPlatformViewsTest : XCTestCase
186 @end
187 
188 @implementation FlutterPlatformViewsTest
189 
190 namespace {
191 fml::RefPtr<fml::TaskRunner> GetDefaultTaskRunner() {
192  fml::MessageLoop::EnsureInitializedForCurrentThread();
193  return fml::MessageLoop::GetCurrent().GetTaskRunner();
194 }
195 } // namespace
196 
197 - (void)testFlutterViewOnlyCreateOnceInOneFrame {
198  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
199 
200  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
201  /*platform=*/GetDefaultTaskRunner(),
202  /*raster=*/GetDefaultTaskRunner(),
203  /*ui=*/GetDefaultTaskRunner(),
204  /*io=*/GetDefaultTaskRunner());
205  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
206  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
207  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
208  /*delegate=*/mock_delegate,
209  /*rendering_api=*/mock_delegate.settings_.enable_impeller
212  /*platform_views_controller=*/flutterPlatformViewsController,
213  /*task_runners=*/runners,
214  /*worker_task_runner=*/nil,
215  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
216 
219  flutterPlatformViewsController->RegisterViewFactory(
220  factory, @"MockFlutterPlatformView",
222  FlutterResult result = ^(id result) {
223  };
224  flutterPlatformViewsController->OnMethodCall(
226  methodCallWithMethodName:@"create"
227  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
228  result);
229  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
230  flutterPlatformViewsController->SetFlutterView(flutterView);
231  // Create embedded view params
232  flutter::MutatorsStack stack;
233  // Layer tree always pushes a screen scale factor to the stack
234  SkMatrix screenScaleMatrix =
235  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
236  stack.PushTransform(screenScaleMatrix);
237  // Push a translate matrix
238  SkMatrix translateMatrix = SkMatrix::Translate(100, 100);
239  stack.PushTransform(translateMatrix);
240  SkMatrix finalMatrix;
241  finalMatrix.setConcat(screenScaleMatrix, translateMatrix);
242 
243  auto embeddedViewParams =
244  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
245 
246  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
247 
248  XCTAssertNotNil(gMockPlatformView);
249 
250  flutterPlatformViewsController->Reset();
251 }
252 
253 - (void)testCanCreatePlatformViewWithoutFlutterView {
254  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
255 
256  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
257  /*platform=*/GetDefaultTaskRunner(),
258  /*raster=*/GetDefaultTaskRunner(),
259  /*ui=*/GetDefaultTaskRunner(),
260  /*io=*/GetDefaultTaskRunner());
261  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
262  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
263  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
264  /*delegate=*/mock_delegate,
265  /*rendering_api=*/mock_delegate.settings_.enable_impeller
268  /*platform_views_controller=*/flutterPlatformViewsController,
269  /*task_runners=*/runners,
270  /*worker_task_runner=*/nil,
271  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
272 
275  flutterPlatformViewsController->RegisterViewFactory(
276  factory, @"MockFlutterPlatformView",
278  FlutterResult result = ^(id result) {
279  };
280  flutterPlatformViewsController->OnMethodCall(
282  methodCallWithMethodName:@"create"
283  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
284  result);
285 
286  XCTAssertNotNil(gMockPlatformView);
287 }
288 
289 - (void)testChildClippingViewHitTests {
290  ChildClippingView* childClippingView =
291  [[ChildClippingView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
292  UIView* childView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
293  [childClippingView addSubview:childView];
294 
295  XCTAssertFalse([childClippingView pointInside:CGPointMake(50, 50) withEvent:nil]);
296  XCTAssertFalse([childClippingView pointInside:CGPointMake(99, 100) withEvent:nil]);
297  XCTAssertFalse([childClippingView pointInside:CGPointMake(100, 99) withEvent:nil]);
298  XCTAssertFalse([childClippingView pointInside:CGPointMake(201, 200) withEvent:nil]);
299  XCTAssertFalse([childClippingView pointInside:CGPointMake(200, 201) withEvent:nil]);
300  XCTAssertFalse([childClippingView pointInside:CGPointMake(99, 200) withEvent:nil]);
301  XCTAssertFalse([childClippingView pointInside:CGPointMake(200, 299) withEvent:nil]);
302 
303  XCTAssertTrue([childClippingView pointInside:CGPointMake(150, 150) withEvent:nil]);
304  XCTAssertTrue([childClippingView pointInside:CGPointMake(100, 100) withEvent:nil]);
305  XCTAssertTrue([childClippingView pointInside:CGPointMake(199, 100) withEvent:nil]);
306  XCTAssertTrue([childClippingView pointInside:CGPointMake(100, 199) withEvent:nil]);
307  XCTAssertTrue([childClippingView pointInside:CGPointMake(199, 199) withEvent:nil]);
308 }
309 
310 - (void)testReleasesBackdropFilterSubviewsOnChildClippingViewDealloc {
311  __weak NSMutableArray<UIVisualEffectView*>* weakBackdropFilterSubviews = nil;
312  __weak UIVisualEffectView* weakVisualEffectView1 = nil;
313  __weak UIVisualEffectView* weakVisualEffectView2 = nil;
314 
315  @autoreleasepool {
316  ChildClippingView* clippingView = [[ChildClippingView alloc] initWithFrame:CGRectZero];
317  UIVisualEffectView* visualEffectView1 = [[UIVisualEffectView alloc]
318  initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
319  weakVisualEffectView1 = visualEffectView1;
320  PlatformViewFilter* platformViewFilter1 =
321  [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10)
322  blurRadius:5
323  visualEffectView:visualEffectView1];
324 
325  [clippingView applyBlurBackdropFilters:@[ platformViewFilter1 ]];
326 
327  // Replace the blur filter to validate the original and new UIVisualEffectView are released.
328  UIVisualEffectView* visualEffectView2 = [[UIVisualEffectView alloc]
329  initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
330  weakVisualEffectView2 = visualEffectView2;
331  PlatformViewFilter* platformViewFilter2 =
332  [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10)
333  blurRadius:5
334  visualEffectView:visualEffectView2];
335  [clippingView applyBlurBackdropFilters:@[ platformViewFilter2 ]];
336 
337  weakBackdropFilterSubviews = clippingView.backdropFilterSubviews;
338  XCTAssertNotNil(weakBackdropFilterSubviews);
339  clippingView = nil;
340  }
341  XCTAssertNil(weakBackdropFilterSubviews);
342  XCTAssertNil(weakVisualEffectView1);
343  XCTAssertNil(weakVisualEffectView2);
344 }
345 
346 - (void)testApplyBackdropFilter {
347  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
348 
349  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
350  /*platform=*/GetDefaultTaskRunner(),
351  /*raster=*/GetDefaultTaskRunner(),
352  /*ui=*/GetDefaultTaskRunner(),
353  /*io=*/GetDefaultTaskRunner());
354  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
355  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
356  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
357  /*delegate=*/mock_delegate,
358  /*rendering_api=*/mock_delegate.settings_.enable_impeller
361  /*platform_views_controller=*/flutterPlatformViewsController,
362  /*task_runners=*/runners,
363  /*worker_task_runner=*/nil,
364  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
365 
368  flutterPlatformViewsController->RegisterViewFactory(
369  factory, @"MockFlutterPlatformView",
371  FlutterResult result = ^(id result) {
372  };
373  flutterPlatformViewsController->OnMethodCall(
375  methodCallWithMethodName:@"create"
376  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
377  result);
378 
379  XCTAssertNotNil(gMockPlatformView);
380 
381  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
382  flutterPlatformViewsController->SetFlutterView(flutterView);
383  // Create embedded view params
384  flutter::MutatorsStack stack;
385  // Layer tree always pushes a screen scale factor to the stack
386  CGFloat screenScale = [UIScreen mainScreen].scale;
387  SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale);
388  stack.PushTransform(screenScaleMatrix);
389  // Push a backdrop filter
390  auto filter = std::make_shared<flutter::DlBlurImageFilter>(5, 2, flutter::DlTileMode::kClamp);
391  stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
392 
393  auto embeddedViewParams =
394  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
395 
396  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
397  flutterPlatformViewsController->CompositeWithParams(
398  2, flutterPlatformViewsController->GetCompositionParams(2));
399 
400  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]);
401  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
402  [flutterView addSubview:childClippingView];
403 
404  [flutterView setNeedsLayout];
405  [flutterView layoutIfNeeded];
406 
407  // childClippingView has visual effect view with the correct configurations.
408  NSUInteger numberOfExpectedVisualEffectView = 0;
409  for (UIView* subview in childClippingView.subviews) {
410  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
411  continue;
412  }
413  XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u);
414  if ([self validateOneVisualEffectView:subview
415  expectedFrame:CGRectMake(0, 0, 10, 10)
416  inputRadius:5]) {
417  numberOfExpectedVisualEffectView++;
418  }
419  }
420  XCTAssertEqual(numberOfExpectedVisualEffectView, 1u);
421 }
422 
423 - (void)testApplyBackdropFilterWithCorrectFrame {
424  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
425 
426  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
427  /*platform=*/GetDefaultTaskRunner(),
428  /*raster=*/GetDefaultTaskRunner(),
429  /*ui=*/GetDefaultTaskRunner(),
430  /*io=*/GetDefaultTaskRunner());
431  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
432  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
433  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
434  /*delegate=*/mock_delegate,
435  /*rendering_api=*/mock_delegate.settings_.enable_impeller
438  /*platform_views_controller=*/flutterPlatformViewsController,
439  /*task_runners=*/runners,
440  /*worker_task_runner=*/nil,
441  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
442 
445  flutterPlatformViewsController->RegisterViewFactory(
446  factory, @"MockFlutterPlatformView",
448  FlutterResult result = ^(id result) {
449  };
450  flutterPlatformViewsController->OnMethodCall(
452  methodCallWithMethodName:@"create"
453  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
454  result);
455 
456  XCTAssertNotNil(gMockPlatformView);
457 
458  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
459  flutterPlatformViewsController->SetFlutterView(flutterView);
460  // Create embedded view params
461  flutter::MutatorsStack stack;
462  // Layer tree always pushes a screen scale factor to the stack
463  CGFloat screenScale = [UIScreen mainScreen].scale;
464  SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale);
465  stack.PushTransform(screenScaleMatrix);
466  // Push a backdrop filter
467  auto filter = std::make_shared<flutter::DlBlurImageFilter>(5, 2, flutter::DlTileMode::kClamp);
468  stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 8, screenScale * 8));
469 
470  auto embeddedViewParams =
471  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(5, 10), stack);
472 
473  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
474  flutterPlatformViewsController->CompositeWithParams(
475  2, flutterPlatformViewsController->GetCompositionParams(2));
476 
477  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]);
478  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
479  [flutterView addSubview:childClippingView];
480 
481  [flutterView setNeedsLayout];
482  [flutterView layoutIfNeeded];
483 
484  // childClippingView has visual effect view with the correct configurations.
485  NSUInteger numberOfExpectedVisualEffectView = 0;
486  for (UIView* subview in childClippingView.subviews) {
487  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
488  continue;
489  }
490  XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u);
491  if ([self validateOneVisualEffectView:subview
492  expectedFrame:CGRectMake(0, 0, 5, 8)
493  inputRadius:5]) {
494  numberOfExpectedVisualEffectView++;
495  }
496  }
497  XCTAssertEqual(numberOfExpectedVisualEffectView, 1u);
498 }
499 
500 - (void)testApplyMultipleBackdropFilters {
501  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
502 
503  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
504  /*platform=*/GetDefaultTaskRunner(),
505  /*raster=*/GetDefaultTaskRunner(),
506  /*ui=*/GetDefaultTaskRunner(),
507  /*io=*/GetDefaultTaskRunner());
508  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
509  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
510  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
511  /*delegate=*/mock_delegate,
512  /*rendering_api=*/mock_delegate.settings_.enable_impeller
515  /*platform_views_controller=*/flutterPlatformViewsController,
516  /*task_runners=*/runners,
517  /*worker_task_runner=*/nil,
518  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
519 
522  flutterPlatformViewsController->RegisterViewFactory(
523  factory, @"MockFlutterPlatformView",
525  FlutterResult result = ^(id result) {
526  };
527  flutterPlatformViewsController->OnMethodCall(
529  methodCallWithMethodName:@"create"
530  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
531  result);
532 
533  XCTAssertNotNil(gMockPlatformView);
534 
535  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
536  flutterPlatformViewsController->SetFlutterView(flutterView);
537  // Create embedded view params
538  flutter::MutatorsStack stack;
539  // Layer tree always pushes a screen scale factor to the stack
540  CGFloat screenScale = [UIScreen mainScreen].scale;
541  SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale);
542  stack.PushTransform(screenScaleMatrix);
543  // Push backdrop filters
544  for (int i = 0; i < 50; i++) {
545  auto filter = std::make_shared<flutter::DlBlurImageFilter>(i, 2, flutter::DlTileMode::kClamp);
546  stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
547  }
548 
549  auto embeddedViewParams =
550  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(20, 20), stack);
551 
552  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
553  flutterPlatformViewsController->CompositeWithParams(
554  2, flutterPlatformViewsController->GetCompositionParams(2));
555 
556  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
557  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
558  [flutterView addSubview:childClippingView];
559 
560  [flutterView setNeedsLayout];
561  [flutterView layoutIfNeeded];
562 
563  NSUInteger numberOfExpectedVisualEffectView = 0;
564  for (UIView* subview in childClippingView.subviews) {
565  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
566  continue;
567  }
568  XCTAssertLessThan(numberOfExpectedVisualEffectView, 50u);
569  if ([self validateOneVisualEffectView:subview
570  expectedFrame:CGRectMake(0, 0, 10, 10)
571  inputRadius:(CGFloat)numberOfExpectedVisualEffectView]) {
572  numberOfExpectedVisualEffectView++;
573  }
574  }
575  XCTAssertEqual(numberOfExpectedVisualEffectView, (NSUInteger)numberOfExpectedVisualEffectView);
576 }
577 
578 - (void)testAddBackdropFilters {
579  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
580 
581  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
582  /*platform=*/GetDefaultTaskRunner(),
583  /*raster=*/GetDefaultTaskRunner(),
584  /*ui=*/GetDefaultTaskRunner(),
585  /*io=*/GetDefaultTaskRunner());
586  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
587  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
588  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
589  /*delegate=*/mock_delegate,
590  /*rendering_api=*/mock_delegate.settings_.enable_impeller
593  /*platform_views_controller=*/flutterPlatformViewsController,
594  /*task_runners=*/runners,
595  /*worker_task_runner=*/nil,
596  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
597 
600  flutterPlatformViewsController->RegisterViewFactory(
601  factory, @"MockFlutterPlatformView",
603  FlutterResult result = ^(id result) {
604  };
605  flutterPlatformViewsController->OnMethodCall(
607  methodCallWithMethodName:@"create"
608  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
609  result);
610 
611  XCTAssertNotNil(gMockPlatformView);
612 
613  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
614  flutterPlatformViewsController->SetFlutterView(flutterView);
615  // Create embedded view params
616  flutter::MutatorsStack stack;
617  // Layer tree always pushes a screen scale factor to the stack
618  CGFloat screenScale = [UIScreen mainScreen].scale;
619  SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale);
620  stack.PushTransform(screenScaleMatrix);
621  // Push a backdrop filter
622  auto filter = std::make_shared<flutter::DlBlurImageFilter>(5, 2, flutter::DlTileMode::kClamp);
623  stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
624 
625  auto embeddedViewParams =
626  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
627 
628  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
629  flutterPlatformViewsController->CompositeWithParams(
630  2, flutterPlatformViewsController->GetCompositionParams(2));
631 
632  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]);
633  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
634  [flutterView addSubview:childClippingView];
635 
636  [flutterView setNeedsLayout];
637  [flutterView layoutIfNeeded];
638 
639  NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init];
640  for (UIView* subview in childClippingView.subviews) {
641  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
642  continue;
643  }
644  XCTAssertLessThan(originalVisualEffectViews.count, 1u);
645  if ([self validateOneVisualEffectView:subview
646  expectedFrame:CGRectMake(0, 0, 10, 10)
647  inputRadius:(CGFloat)5]) {
648  [originalVisualEffectViews addObject:subview];
649  }
650  }
651  XCTAssertEqual(originalVisualEffectViews.count, 1u);
652 
653  //
654  // Simulate adding 1 backdrop filter (create a new mutators stack)
655  // Create embedded view params
656  flutter::MutatorsStack stack2;
657  // Layer tree always pushes a screen scale factor to the stack
658  stack2.PushTransform(screenScaleMatrix);
659  // Push backdrop filters
660  for (int i = 0; i < 2; i++) {
661  stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
662  }
663 
664  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
665  SkSize::Make(10, 10), stack2);
666 
667  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
668  flutterPlatformViewsController->CompositeWithParams(
669  2, flutterPlatformViewsController->GetCompositionParams(2));
670 
671  [flutterView setNeedsLayout];
672  [flutterView layoutIfNeeded];
673 
674  NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init];
675  for (UIView* subview in childClippingView.subviews) {
676  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
677  continue;
678  }
679  XCTAssertLessThan(newVisualEffectViews.count, 2u);
680 
681  if ([self validateOneVisualEffectView:subview
682  expectedFrame:CGRectMake(0, 0, 10, 10)
683  inputRadius:(CGFloat)5]) {
684  [newVisualEffectViews addObject:subview];
685  }
686  }
687  XCTAssertEqual(newVisualEffectViews.count, 2u);
688  for (NSUInteger i = 0; i < originalVisualEffectViews.count; i++) {
689  UIView* originalView = originalVisualEffectViews[i];
690  UIView* newView = newVisualEffectViews[i];
691  // Compare reference.
692  XCTAssertEqual(originalView, newView);
693  id mockOrignalView = OCMPartialMock(originalView);
694  OCMReject([mockOrignalView removeFromSuperview]);
695  [mockOrignalView stopMocking];
696  }
697 }
698 
699 - (void)testRemoveBackdropFilters {
700  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
701 
702  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
703  /*platform=*/GetDefaultTaskRunner(),
704  /*raster=*/GetDefaultTaskRunner(),
705  /*ui=*/GetDefaultTaskRunner(),
706  /*io=*/GetDefaultTaskRunner());
707  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
708  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
709  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
710  /*delegate=*/mock_delegate,
711  /*rendering_api=*/mock_delegate.settings_.enable_impeller
714  /*platform_views_controller=*/flutterPlatformViewsController,
715  /*task_runners=*/runners,
716  /*worker_task_runner=*/nil,
717  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
718 
721  flutterPlatformViewsController->RegisterViewFactory(
722  factory, @"MockFlutterPlatformView",
724  FlutterResult result = ^(id result) {
725  };
726  flutterPlatformViewsController->OnMethodCall(
728  methodCallWithMethodName:@"create"
729  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
730  result);
731 
732  XCTAssertNotNil(gMockPlatformView);
733 
734  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
735  flutterPlatformViewsController->SetFlutterView(flutterView);
736  // Create embedded view params
737  flutter::MutatorsStack stack;
738  // Layer tree always pushes a screen scale factor to the stack
739  CGFloat screenScale = [UIScreen mainScreen].scale;
740  SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale);
741  stack.PushTransform(screenScaleMatrix);
742  // Push backdrop filters
743  auto filter = std::make_shared<flutter::DlBlurImageFilter>(5, 2, flutter::DlTileMode::kClamp);
744  for (int i = 0; i < 5; i++) {
745  stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
746  }
747 
748  auto embeddedViewParams =
749  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
750 
751  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
752  flutterPlatformViewsController->CompositeWithParams(
753  2, flutterPlatformViewsController->GetCompositionParams(2));
754 
755  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
756  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
757  [flutterView addSubview:childClippingView];
758 
759  [flutterView setNeedsLayout];
760  [flutterView layoutIfNeeded];
761 
762  NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init];
763  for (UIView* subview in childClippingView.subviews) {
764  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
765  continue;
766  }
767  XCTAssertLessThan(originalVisualEffectViews.count, 5u);
768  if ([self validateOneVisualEffectView:subview
769  expectedFrame:CGRectMake(0, 0, 10, 10)
770  inputRadius:(CGFloat)5]) {
771  [originalVisualEffectViews addObject:subview];
772  }
773  }
774 
775  // Simulate removing 1 backdrop filter (create a new mutators stack)
776  // Create embedded view params
777  flutter::MutatorsStack stack2;
778  // Layer tree always pushes a screen scale factor to the stack
779  stack2.PushTransform(screenScaleMatrix);
780  // Push backdrop filters
781  for (int i = 0; i < 4; i++) {
782  stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
783  }
784 
785  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
786  SkSize::Make(10, 10), stack2);
787 
788  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
789  flutterPlatformViewsController->CompositeWithParams(
790  2, flutterPlatformViewsController->GetCompositionParams(2));
791 
792  [flutterView setNeedsLayout];
793  [flutterView layoutIfNeeded];
794 
795  NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init];
796  for (UIView* subview in childClippingView.subviews) {
797  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
798  continue;
799  }
800  XCTAssertLessThan(newVisualEffectViews.count, 4u);
801  if ([self validateOneVisualEffectView:subview
802  expectedFrame:CGRectMake(0, 0, 10, 10)
803  inputRadius:(CGFloat)5]) {
804  [newVisualEffectViews addObject:subview];
805  }
806  }
807  XCTAssertEqual(newVisualEffectViews.count, 4u);
808 
809  for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) {
810  UIView* newView = newVisualEffectViews[i];
811  id mockNewView = OCMPartialMock(newView);
812  UIView* originalView = originalVisualEffectViews[i];
813  // Compare reference.
814  XCTAssertEqual(originalView, newView);
815  OCMReject([mockNewView removeFromSuperview]);
816  [mockNewView stopMocking];
817  }
818 
819  // Simulate removing all backdrop filters (replace the mutators stack)
820  // Update embedded view params, delete except screenScaleMatrix
821  for (int i = 0; i < 5; i++) {
822  stack2.Pop();
823  }
824  // No backdrop filters in the stack, so no nothing to push
825 
826  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
827  SkSize::Make(10, 10), stack2);
828 
829  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
830  flutterPlatformViewsController->CompositeWithParams(
831  2, flutterPlatformViewsController->GetCompositionParams(2));
832 
833  [flutterView setNeedsLayout];
834  [flutterView layoutIfNeeded];
835 
836  NSUInteger numberOfExpectedVisualEffectView = 0u;
837  for (UIView* subview in childClippingView.subviews) {
838  if ([subview isKindOfClass:[UIVisualEffectView class]]) {
839  numberOfExpectedVisualEffectView++;
840  }
841  }
842  XCTAssertEqual(numberOfExpectedVisualEffectView, 0u);
843 }
844 
845 - (void)testEditBackdropFilters {
846  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
847 
848  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
849  /*platform=*/GetDefaultTaskRunner(),
850  /*raster=*/GetDefaultTaskRunner(),
851  /*ui=*/GetDefaultTaskRunner(),
852  /*io=*/GetDefaultTaskRunner());
853  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
854  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
855  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
856  /*delegate=*/mock_delegate,
857  /*rendering_api=*/mock_delegate.settings_.enable_impeller
860  /*platform_views_controller=*/flutterPlatformViewsController,
861  /*task_runners=*/runners,
862  /*worker_task_runner=*/nil,
863  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
864 
867  flutterPlatformViewsController->RegisterViewFactory(
868  factory, @"MockFlutterPlatformView",
870  FlutterResult result = ^(id result) {
871  };
872  flutterPlatformViewsController->OnMethodCall(
874  methodCallWithMethodName:@"create"
875  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
876  result);
877 
878  XCTAssertNotNil(gMockPlatformView);
879 
880  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
881  flutterPlatformViewsController->SetFlutterView(flutterView);
882  // Create embedded view params
883  flutter::MutatorsStack stack;
884  // Layer tree always pushes a screen scale factor to the stack
885  CGFloat screenScale = [UIScreen mainScreen].scale;
886  SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale);
887  stack.PushTransform(screenScaleMatrix);
888  // Push backdrop filters
889  auto filter = std::make_shared<flutter::DlBlurImageFilter>(5, 2, flutter::DlTileMode::kClamp);
890  for (int i = 0; i < 5; i++) {
891  stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
892  }
893 
894  auto embeddedViewParams =
895  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
896 
897  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
898  flutterPlatformViewsController->CompositeWithParams(
899  2, flutterPlatformViewsController->GetCompositionParams(2));
900 
901  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
902  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
903  [flutterView addSubview:childClippingView];
904 
905  [flutterView setNeedsLayout];
906  [flutterView layoutIfNeeded];
907 
908  NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init];
909  for (UIView* subview in childClippingView.subviews) {
910  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
911  continue;
912  }
913  XCTAssertLessThan(originalVisualEffectViews.count, 5u);
914  if ([self validateOneVisualEffectView:subview
915  expectedFrame:CGRectMake(0, 0, 10, 10)
916  inputRadius:(CGFloat)5]) {
917  [originalVisualEffectViews addObject:subview];
918  }
919  }
920 
921  // Simulate editing 1 backdrop filter in the middle of the stack (create a new mutators stack)
922  // Create embedded view params
923  flutter::MutatorsStack stack2;
924  // Layer tree always pushes a screen scale factor to the stack
925  stack2.PushTransform(screenScaleMatrix);
926  // Push backdrop filters
927  for (int i = 0; i < 5; i++) {
928  if (i == 3) {
929  auto filter2 =
930  std::make_shared<flutter::DlBlurImageFilter>(2, 5, flutter::DlTileMode::kClamp);
931 
932  stack2.PushBackdropFilter(filter2,
933  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
934  continue;
935  }
936 
937  stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
938  }
939 
940  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
941  SkSize::Make(10, 10), stack2);
942 
943  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
944  flutterPlatformViewsController->CompositeWithParams(
945  2, flutterPlatformViewsController->GetCompositionParams(2));
946 
947  [flutterView setNeedsLayout];
948  [flutterView layoutIfNeeded];
949 
950  NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init];
951  for (UIView* subview in childClippingView.subviews) {
952  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
953  continue;
954  }
955  XCTAssertLessThan(newVisualEffectViews.count, 5u);
956  CGFloat expectInputRadius = 5;
957  if (newVisualEffectViews.count == 3) {
958  expectInputRadius = 2;
959  }
960  if ([self validateOneVisualEffectView:subview
961  expectedFrame:CGRectMake(0, 0, 10, 10)
962  inputRadius:(CGFloat)expectInputRadius]) {
963  [newVisualEffectViews addObject:subview];
964  }
965  }
966  XCTAssertEqual(newVisualEffectViews.count, 5u);
967  for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) {
968  UIView* newView = newVisualEffectViews[i];
969  id mockNewView = OCMPartialMock(newView);
970  UIView* originalView = originalVisualEffectViews[i];
971  // Compare reference.
972  XCTAssertEqual(originalView, newView);
973  OCMReject([mockNewView removeFromSuperview]);
974  [mockNewView stopMocking];
975  }
976  [newVisualEffectViews removeAllObjects];
977 
978  // Simulate editing 1 backdrop filter in the beginning of the stack (replace the mutators stack)
979  // Update embedded view params, delete except screenScaleMatrix
980  for (int i = 0; i < 5; i++) {
981  stack2.Pop();
982  }
983  // Push backdrop filters
984  for (int i = 0; i < 5; i++) {
985  if (i == 0) {
986  auto filter2 =
987  std::make_shared<flutter::DlBlurImageFilter>(2, 5, flutter::DlTileMode::kClamp);
988  stack2.PushBackdropFilter(filter2,
989  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
990  continue;
991  }
992 
993  stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
994  }
995 
996  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
997  SkSize::Make(10, 10), stack2);
998 
999  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1000  flutterPlatformViewsController->CompositeWithParams(
1001  2, flutterPlatformViewsController->GetCompositionParams(2));
1002 
1003  [flutterView setNeedsLayout];
1004  [flutterView layoutIfNeeded];
1005 
1006  for (UIView* subview in childClippingView.subviews) {
1007  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
1008  continue;
1009  }
1010  XCTAssertLessThan(newVisualEffectViews.count, 5u);
1011  CGFloat expectInputRadius = 5;
1012  if (newVisualEffectViews.count == 0) {
1013  expectInputRadius = 2;
1014  }
1015  if ([self validateOneVisualEffectView:subview
1016  expectedFrame:CGRectMake(0, 0, 10, 10)
1017  inputRadius:(CGFloat)expectInputRadius]) {
1018  [newVisualEffectViews addObject:subview];
1019  }
1020  }
1021  for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) {
1022  UIView* newView = newVisualEffectViews[i];
1023  id mockNewView = OCMPartialMock(newView);
1024  UIView* originalView = originalVisualEffectViews[i];
1025  // Compare reference.
1026  XCTAssertEqual(originalView, newView);
1027  OCMReject([mockNewView removeFromSuperview]);
1028  [mockNewView stopMocking];
1029  }
1030  [newVisualEffectViews removeAllObjects];
1031 
1032  // Simulate editing 1 backdrop filter in the end of the stack (replace the mutators stack)
1033  // Update embedded view params, delete except screenScaleMatrix
1034  for (int i = 0; i < 5; i++) {
1035  stack2.Pop();
1036  }
1037  // Push backdrop filters
1038  for (int i = 0; i < 5; i++) {
1039  if (i == 4) {
1040  auto filter2 =
1041  std::make_shared<flutter::DlBlurImageFilter>(2, 5, flutter::DlTileMode::kClamp);
1042  stack2.PushBackdropFilter(filter2,
1043  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1044  continue;
1045  }
1046 
1047  stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1048  }
1049 
1050  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
1051  SkSize::Make(10, 10), stack2);
1052 
1053  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1054  flutterPlatformViewsController->CompositeWithParams(
1055  2, flutterPlatformViewsController->GetCompositionParams(2));
1056 
1057  [flutterView setNeedsLayout];
1058  [flutterView layoutIfNeeded];
1059 
1060  for (UIView* subview in childClippingView.subviews) {
1061  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
1062  continue;
1063  }
1064  XCTAssertLessThan(newVisualEffectViews.count, 5u);
1065  CGFloat expectInputRadius = 5;
1066  if (newVisualEffectViews.count == 4) {
1067  expectInputRadius = 2;
1068  }
1069  if ([self validateOneVisualEffectView:subview
1070  expectedFrame:CGRectMake(0, 0, 10, 10)
1071  inputRadius:(CGFloat)expectInputRadius]) {
1072  [newVisualEffectViews addObject:subview];
1073  }
1074  }
1075  XCTAssertEqual(newVisualEffectViews.count, 5u);
1076 
1077  for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) {
1078  UIView* newView = newVisualEffectViews[i];
1079  id mockNewView = OCMPartialMock(newView);
1080  UIView* originalView = originalVisualEffectViews[i];
1081  // Compare reference.
1082  XCTAssertEqual(originalView, newView);
1083  OCMReject([mockNewView removeFromSuperview]);
1084  [mockNewView stopMocking];
1085  }
1086  [newVisualEffectViews removeAllObjects];
1087 
1088  // Simulate editing all backdrop filters in the stack (replace the mutators stack)
1089  // Update embedded view params, delete except screenScaleMatrix
1090  for (int i = 0; i < 5; i++) {
1091  stack2.Pop();
1092  }
1093  // Push backdrop filters
1094  for (int i = 0; i < 5; i++) {
1095  auto filter2 = std::make_shared<flutter::DlBlurImageFilter>(i, 2, flutter::DlTileMode::kClamp);
1096 
1097  stack2.PushBackdropFilter(filter2, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1098  }
1099 
1100  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
1101  SkSize::Make(10, 10), stack2);
1102 
1103  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1104  flutterPlatformViewsController->CompositeWithParams(
1105  2, flutterPlatformViewsController->GetCompositionParams(2));
1106 
1107  [flutterView setNeedsLayout];
1108  [flutterView layoutIfNeeded];
1109 
1110  for (UIView* subview in childClippingView.subviews) {
1111  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
1112  continue;
1113  }
1114  XCTAssertLessThan(newVisualEffectViews.count, 5u);
1115  if ([self validateOneVisualEffectView:subview
1116  expectedFrame:CGRectMake(0, 0, 10, 10)
1117  inputRadius:(CGFloat)newVisualEffectViews.count]) {
1118  [newVisualEffectViews addObject:subview];
1119  }
1120  }
1121  XCTAssertEqual(newVisualEffectViews.count, 5u);
1122 
1123  for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) {
1124  UIView* newView = newVisualEffectViews[i];
1125  id mockNewView = OCMPartialMock(newView);
1126  UIView* originalView = originalVisualEffectViews[i];
1127  // Compare reference.
1128  XCTAssertEqual(originalView, newView);
1129  OCMReject([mockNewView removeFromSuperview]);
1130  [mockNewView stopMocking];
1131  }
1132  [newVisualEffectViews removeAllObjects];
1133 }
1134 
1135 - (void)testApplyBackdropFilterNotDlBlurImageFilter {
1136  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1137 
1138  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1139  /*platform=*/GetDefaultTaskRunner(),
1140  /*raster=*/GetDefaultTaskRunner(),
1141  /*ui=*/GetDefaultTaskRunner(),
1142  /*io=*/GetDefaultTaskRunner());
1143  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
1144  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
1145  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1146  /*delegate=*/mock_delegate,
1147  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1150  /*platform_views_controller=*/flutterPlatformViewsController,
1151  /*task_runners=*/runners,
1152  /*worker_task_runner=*/nil,
1153  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1154 
1157  flutterPlatformViewsController->RegisterViewFactory(
1158  factory, @"MockFlutterPlatformView",
1160  FlutterResult result = ^(id result) {
1161  };
1162  flutterPlatformViewsController->OnMethodCall(
1164  methodCallWithMethodName:@"create"
1165  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1166  result);
1167 
1168  XCTAssertNotNil(gMockPlatformView);
1169 
1170  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
1171  flutterPlatformViewsController->SetFlutterView(flutterView);
1172  // Create embedded view params
1173  flutter::MutatorsStack stack;
1174  // Layer tree always pushes a screen scale factor to the stack
1175  CGFloat screenScale = [UIScreen mainScreen].scale;
1176  SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale);
1177  stack.PushTransform(screenScaleMatrix);
1178  // Push a dilate backdrop filter
1179  auto dilateFilter = std::make_shared<flutter::DlDilateImageFilter>(5, 2);
1180  stack.PushBackdropFilter(dilateFilter, SkRect::MakeEmpty());
1181 
1182  auto embeddedViewParams =
1183  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
1184 
1185  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1186  flutterPlatformViewsController->CompositeWithParams(
1187  2, flutterPlatformViewsController->GetCompositionParams(2));
1188 
1189  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]);
1190  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1191 
1192  [flutterView addSubview:childClippingView];
1193 
1194  [flutterView setNeedsLayout];
1195  [flutterView layoutIfNeeded];
1196 
1197  NSUInteger numberOfExpectedVisualEffectView = 0;
1198  for (UIView* subview in childClippingView.subviews) {
1199  if ([subview isKindOfClass:[UIVisualEffectView class]]) {
1200  numberOfExpectedVisualEffectView++;
1201  }
1202  }
1203  XCTAssertEqual(numberOfExpectedVisualEffectView, 0u);
1204 
1205  // Simulate adding a non-DlBlurImageFilter in the middle of the stack (create a new mutators
1206  // stack) Create embedded view params
1207  flutter::MutatorsStack stack2;
1208  // Layer tree always pushes a screen scale factor to the stack
1209  stack2.PushTransform(screenScaleMatrix);
1210  // Push backdrop filters and dilate filter
1211  auto blurFilter = std::make_shared<flutter::DlBlurImageFilter>(5, 2, flutter::DlTileMode::kClamp);
1212 
1213  for (int i = 0; i < 5; i++) {
1214  if (i == 2) {
1215  stack2.PushBackdropFilter(dilateFilter,
1216  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1217  continue;
1218  }
1219 
1220  stack2.PushBackdropFilter(blurFilter,
1221  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1222  }
1223 
1224  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
1225  SkSize::Make(10, 10), stack2);
1226 
1227  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1228  flutterPlatformViewsController->CompositeWithParams(
1229  2, flutterPlatformViewsController->GetCompositionParams(2));
1230 
1231  [flutterView setNeedsLayout];
1232  [flutterView layoutIfNeeded];
1233 
1234  numberOfExpectedVisualEffectView = 0;
1235  for (UIView* subview in childClippingView.subviews) {
1236  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
1237  continue;
1238  }
1239  XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u);
1240  if ([self validateOneVisualEffectView:subview
1241  expectedFrame:CGRectMake(0, 0, 10, 10)
1242  inputRadius:(CGFloat)5]) {
1243  numberOfExpectedVisualEffectView++;
1244  }
1245  }
1246  XCTAssertEqual(numberOfExpectedVisualEffectView, 4u);
1247 
1248  // Simulate adding a non-DlBlurImageFilter to the beginning of the stack (replace the mutators
1249  // stack) Update embedded view params, delete except screenScaleMatrix
1250  for (int i = 0; i < 5; i++) {
1251  stack2.Pop();
1252  }
1253  // Push backdrop filters and dilate filter
1254  for (int i = 0; i < 5; i++) {
1255  if (i == 0) {
1256  stack2.PushBackdropFilter(dilateFilter,
1257  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1258  continue;
1259  }
1260 
1261  stack2.PushBackdropFilter(blurFilter,
1262  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1263  }
1264 
1265  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
1266  SkSize::Make(10, 10), stack2);
1267 
1268  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1269  flutterPlatformViewsController->CompositeWithParams(
1270  2, flutterPlatformViewsController->GetCompositionParams(2));
1271 
1272  [flutterView setNeedsLayout];
1273  [flutterView layoutIfNeeded];
1274 
1275  numberOfExpectedVisualEffectView = 0;
1276  for (UIView* subview in childClippingView.subviews) {
1277  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
1278  continue;
1279  }
1280  XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u);
1281  if ([self validateOneVisualEffectView:subview
1282  expectedFrame:CGRectMake(0, 0, 10, 10)
1283  inputRadius:(CGFloat)5]) {
1284  numberOfExpectedVisualEffectView++;
1285  }
1286  }
1287  XCTAssertEqual(numberOfExpectedVisualEffectView, 4u);
1288 
1289  // Simulate adding a non-DlBlurImageFilter to the end of the stack (replace the mutators stack)
1290  // Update embedded view params, delete except screenScaleMatrix
1291  for (int i = 0; i < 5; i++) {
1292  stack2.Pop();
1293  }
1294  // Push backdrop filters and dilate filter
1295  for (int i = 0; i < 5; i++) {
1296  if (i == 4) {
1297  stack2.PushBackdropFilter(dilateFilter,
1298  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1299  continue;
1300  }
1301 
1302  stack2.PushBackdropFilter(blurFilter,
1303  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1304  }
1305 
1306  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
1307  SkSize::Make(10, 10), stack2);
1308 
1309  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1310  flutterPlatformViewsController->CompositeWithParams(
1311  2, flutterPlatformViewsController->GetCompositionParams(2));
1312 
1313  [flutterView setNeedsLayout];
1314  [flutterView layoutIfNeeded];
1315 
1316  numberOfExpectedVisualEffectView = 0;
1317  for (UIView* subview in childClippingView.subviews) {
1318  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
1319  continue;
1320  }
1321  XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u);
1322  if ([self validateOneVisualEffectView:subview
1323  expectedFrame:CGRectMake(0, 0, 10, 10)
1324  inputRadius:(CGFloat)5]) {
1325  numberOfExpectedVisualEffectView++;
1326  }
1327  }
1328  XCTAssertEqual(numberOfExpectedVisualEffectView, 4u);
1329 
1330  // Simulate adding only non-DlBlurImageFilter to the stack (replace the mutators stack)
1331  // Update embedded view params, delete except screenScaleMatrix
1332  for (int i = 0; i < 5; i++) {
1333  stack2.Pop();
1334  }
1335  // Push dilate filters
1336  for (int i = 0; i < 5; i++) {
1337  stack2.PushBackdropFilter(dilateFilter,
1338  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1339  }
1340 
1341  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
1342  SkSize::Make(10, 10), stack2);
1343 
1344  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1345  flutterPlatformViewsController->CompositeWithParams(
1346  2, flutterPlatformViewsController->GetCompositionParams(2));
1347 
1348  [flutterView setNeedsLayout];
1349  [flutterView layoutIfNeeded];
1350 
1351  numberOfExpectedVisualEffectView = 0;
1352  for (UIView* subview in childClippingView.subviews) {
1353  if ([subview isKindOfClass:[UIVisualEffectView class]]) {
1354  numberOfExpectedVisualEffectView++;
1355  }
1356  }
1357  XCTAssertEqual(numberOfExpectedVisualEffectView, 0u);
1358 }
1359 
1360 - (void)testApplyBackdropFilterCorrectAPI {
1362  // The gaussianBlur filter is extracted from UIVisualEffectView.
1363  // Each test requires a new PlatformViewFilter
1364  // Valid UIVisualEffectView API
1365  UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc]
1366  initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
1367  PlatformViewFilter* platformViewFilter =
1368  [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10)
1369  blurRadius:5
1370  visualEffectView:visualEffectView];
1371  XCTAssertNotNil(platformViewFilter);
1372 }
1373 
1374 - (void)testApplyBackdropFilterAPIChangedInvalidUIVisualEffectView {
1376  UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc] init];
1377  PlatformViewFilter* platformViewFilter =
1378  [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10)
1379  blurRadius:5
1380  visualEffectView:visualEffectView];
1381  XCTAssertNil(platformViewFilter);
1382 }
1383 
1384 - (void)testApplyBackdropFilterAPIChangedNoGaussianBlurFilter {
1386  UIVisualEffectView* editedUIVisualEffectView = [[UIVisualEffectView alloc]
1387  initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
1388  NSArray* subviews = editedUIVisualEffectView.subviews;
1389  for (UIView* view in subviews) {
1390  if ([NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) {
1391  for (CIFilter* filter in view.layer.filters) {
1392  if ([[filter valueForKey:@"name"] isEqual:@"gaussianBlur"]) {
1393  [filter setValue:@"notGaussianBlur" forKey:@"name"];
1394  break;
1395  }
1396  }
1397  break;
1398  }
1399  }
1400  PlatformViewFilter* platformViewFilter =
1401  [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10)
1402  blurRadius:5
1403  visualEffectView:editedUIVisualEffectView];
1404  XCTAssertNil(platformViewFilter);
1405 }
1406 
1407 - (void)testApplyBackdropFilterAPIChangedInvalidInputRadius {
1409  UIVisualEffectView* editedUIVisualEffectView = [[UIVisualEffectView alloc]
1410  initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
1411  NSArray* subviews = editedUIVisualEffectView.subviews;
1412  for (UIView* view in subviews) {
1413  if ([NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) {
1414  for (CIFilter* filter in view.layer.filters) {
1415  if ([[filter valueForKey:@"name"] isEqual:@"gaussianBlur"]) {
1416  [filter setValue:@"invalidInputRadius" forKey:@"inputRadius"];
1417  break;
1418  }
1419  }
1420  break;
1421  }
1422  }
1423 
1424  PlatformViewFilter* platformViewFilter =
1425  [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10)
1426  blurRadius:5
1427  visualEffectView:editedUIVisualEffectView];
1428  XCTAssertNil(platformViewFilter);
1429 }
1430 
1431 - (void)testBackdropFilterVisualEffectSubviewBackgroundColor {
1432  __weak UIVisualEffectView* weakVisualEffectView;
1433 
1434  @autoreleasepool {
1435  UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc]
1436  initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
1437  weakVisualEffectView = visualEffectView;
1438  PlatformViewFilter* platformViewFilter =
1439  [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10)
1440  blurRadius:5
1441  visualEffectView:visualEffectView];
1442  CGColorRef visualEffectSubviewBackgroundColor = nil;
1443  for (UIView* view in [platformViewFilter backdropFilterView].subviews) {
1444  if ([NSStringFromClass([view class]) hasSuffix:@"VisualEffectSubview"]) {
1445  visualEffectSubviewBackgroundColor = view.layer.backgroundColor;
1446  }
1447  }
1448  XCTAssertTrue(
1449  CGColorEqualToColor(visualEffectSubviewBackgroundColor, UIColor.clearColor.CGColor));
1450  }
1451  XCTAssertNil(weakVisualEffectView);
1452 }
1453 
1454 - (void)testCompositePlatformView {
1455  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1456 
1457  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1458  /*platform=*/GetDefaultTaskRunner(),
1459  /*raster=*/GetDefaultTaskRunner(),
1460  /*ui=*/GetDefaultTaskRunner(),
1461  /*io=*/GetDefaultTaskRunner());
1462  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
1463  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
1464  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1465  /*delegate=*/mock_delegate,
1466  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1469  /*platform_views_controller=*/flutterPlatformViewsController,
1470  /*task_runners=*/runners,
1471  /*worker_task_runner=*/nil,
1472  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1473 
1476  flutterPlatformViewsController->RegisterViewFactory(
1477  factory, @"MockFlutterPlatformView",
1479  FlutterResult result = ^(id result) {
1480  };
1481  flutterPlatformViewsController->OnMethodCall(
1483  methodCallWithMethodName:@"create"
1484  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1485  result);
1486 
1487  XCTAssertNotNil(gMockPlatformView);
1488 
1489  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
1490  flutterPlatformViewsController->SetFlutterView(flutterView);
1491  // Create embedded view params
1492  flutter::MutatorsStack stack;
1493  // Layer tree always pushes a screen scale factor to the stack
1494  SkMatrix screenScaleMatrix =
1495  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
1496  stack.PushTransform(screenScaleMatrix);
1497  // Push a translate matrix
1498  SkMatrix translateMatrix = SkMatrix::Translate(100, 100);
1499  stack.PushTransform(translateMatrix);
1500  SkMatrix finalMatrix;
1501  finalMatrix.setConcat(screenScaleMatrix, translateMatrix);
1502 
1503  auto embeddedViewParams =
1504  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
1505 
1506  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1507  flutterPlatformViewsController->CompositeWithParams(
1508  2, flutterPlatformViewsController->GetCompositionParams(2));
1509 
1510  CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds
1511  toView:flutterView];
1512  XCTAssertTrue(CGRectEqualToRect(platformViewRectInFlutterView, CGRectMake(100, 100, 300, 300)));
1513 }
1514 
1515 - (void)testBackdropFilterCorrectlyPushedAndReset {
1516  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1517 
1518  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1519  /*platform=*/GetDefaultTaskRunner(),
1520  /*raster=*/GetDefaultTaskRunner(),
1521  /*ui=*/GetDefaultTaskRunner(),
1522  /*io=*/GetDefaultTaskRunner());
1523  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
1524  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
1525  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1526  /*delegate=*/mock_delegate,
1527  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1530  /*platform_views_controller=*/flutterPlatformViewsController,
1531  /*task_runners=*/runners,
1532  /*worker_task_runner=*/nil,
1533  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1534 
1537  flutterPlatformViewsController->RegisterViewFactory(
1538  factory, @"MockFlutterPlatformView",
1540  FlutterResult result = ^(id result) {
1541  };
1542  flutterPlatformViewsController->OnMethodCall(
1544  methodCallWithMethodName:@"create"
1545  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1546  result);
1547 
1548  XCTAssertNotNil(gMockPlatformView);
1549 
1550  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
1551  flutterPlatformViewsController->SetFlutterView(flutterView);
1552  // Create embedded view params
1553  flutter::MutatorsStack stack;
1554  // Layer tree always pushes a screen scale factor to the stack
1555  CGFloat screenScale = [UIScreen mainScreen].scale;
1556  SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale);
1557  stack.PushTransform(screenScaleMatrix);
1558 
1559  auto embeddedViewParams =
1560  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
1561 
1562  flutterPlatformViewsController->BeginFrame(SkISize::Make(0, 0));
1563  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1564  flutterPlatformViewsController->PushVisitedPlatformView(2);
1565  auto filter = std::make_shared<flutter::DlBlurImageFilter>(5, 2, flutter::DlTileMode::kClamp);
1566  flutterPlatformViewsController->PushFilterToVisitedPlatformViews(
1567  filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1568  flutterPlatformViewsController->CompositeWithParams(
1569  2, flutterPlatformViewsController->GetCompositionParams(2));
1570 
1571  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]);
1572  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1573  [flutterView addSubview:childClippingView];
1574 
1575  [flutterView setNeedsLayout];
1576  [flutterView layoutIfNeeded];
1577 
1578  // childClippingView has visual effect view with the correct configurations.
1579  NSUInteger numberOfExpectedVisualEffectView = 0;
1580  for (UIView* subview in childClippingView.subviews) {
1581  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
1582  continue;
1583  }
1584  XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u);
1585  if ([self validateOneVisualEffectView:subview
1586  expectedFrame:CGRectMake(0, 0, 10, 10)
1587  inputRadius:5]) {
1588  numberOfExpectedVisualEffectView++;
1589  }
1590  }
1591  XCTAssertEqual(numberOfExpectedVisualEffectView, 1u);
1592 
1593  // New frame, with no filter pushed.
1594  auto embeddedViewParams2 =
1595  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
1596  flutterPlatformViewsController->BeginFrame(SkISize::Make(0, 0));
1597  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2));
1598  flutterPlatformViewsController->CompositeWithParams(
1599  2, flutterPlatformViewsController->GetCompositionParams(2));
1600 
1601  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]);
1602 
1603  [flutterView setNeedsLayout];
1604  [flutterView layoutIfNeeded];
1605 
1606  numberOfExpectedVisualEffectView = 0;
1607  for (UIView* subview in childClippingView.subviews) {
1608  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
1609  continue;
1610  }
1611  numberOfExpectedVisualEffectView++;
1612  }
1613  XCTAssertEqual(numberOfExpectedVisualEffectView, 0u);
1614 }
1615 
1616 - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView {
1617  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1618 
1619  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1620  /*platform=*/GetDefaultTaskRunner(),
1621  /*raster=*/GetDefaultTaskRunner(),
1622  /*ui=*/GetDefaultTaskRunner(),
1623  /*io=*/GetDefaultTaskRunner());
1624  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
1625  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
1626  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1627  /*delegate=*/mock_delegate,
1628  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1631  /*platform_views_controller=*/flutterPlatformViewsController,
1632  /*task_runners=*/runners,
1633  /*worker_task_runner=*/nil,
1634  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1635 
1638  flutterPlatformViewsController->RegisterViewFactory(
1639  factory, @"MockFlutterPlatformView",
1641  FlutterResult result = ^(id result) {
1642  };
1643  flutterPlatformViewsController->OnMethodCall(
1645  methodCallWithMethodName:@"create"
1646  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1647  result);
1648 
1649  XCTAssertNotNil(gMockPlatformView);
1650 
1651  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
1652  flutterPlatformViewsController->SetFlutterView(flutterView);
1653  // Create embedded view params
1654  flutter::MutatorsStack stack;
1655  // Layer tree always pushes a screen scale factor to the stack
1656  SkMatrix screenScaleMatrix =
1657  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
1658  stack.PushTransform(screenScaleMatrix);
1659  // Push a rotate matrix
1660  SkMatrix rotateMatrix;
1661  rotateMatrix.setRotate(10);
1662  stack.PushTransform(rotateMatrix);
1663  SkMatrix finalMatrix;
1664  finalMatrix.setConcat(screenScaleMatrix, rotateMatrix);
1665 
1666  auto embeddedViewParams =
1667  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
1668 
1669  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1670  flutterPlatformViewsController->CompositeWithParams(
1671  2, flutterPlatformViewsController->GetCompositionParams(2));
1672 
1673  CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds
1674  toView:flutterView];
1675  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
1676  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1677  // The childclippingview's frame is set based on flow, but the platform view's frame is set based
1678  // on quartz. Although they should be the same, but we should tolerate small floating point
1679  // errors.
1680  XCTAssertLessThan(fabs(platformViewRectInFlutterView.origin.x - childClippingView.frame.origin.x),
1682  XCTAssertLessThan(fabs(platformViewRectInFlutterView.origin.y - childClippingView.frame.origin.y),
1684  XCTAssertLessThan(
1685  fabs(platformViewRectInFlutterView.size.width - childClippingView.frame.size.width),
1687  XCTAssertLessThan(
1688  fabs(platformViewRectInFlutterView.size.height - childClippingView.frame.size.height),
1690 }
1691 
1692 - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView {
1693  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1694 
1695  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1696  /*platform=*/GetDefaultTaskRunner(),
1697  /*raster=*/GetDefaultTaskRunner(),
1698  /*ui=*/GetDefaultTaskRunner(),
1699  /*io=*/GetDefaultTaskRunner());
1700  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
1701  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
1702  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1703  /*delegate=*/mock_delegate,
1704  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1707  /*platform_views_controller=*/flutterPlatformViewsController,
1708  /*task_runners=*/runners,
1709  /*worker_task_runner=*/nil,
1710  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1711 
1714  flutterPlatformViewsController->RegisterViewFactory(
1715  factory, @"MockFlutterPlatformView",
1717  FlutterResult result = ^(id result) {
1718  };
1719  flutterPlatformViewsController->OnMethodCall(
1721  methodCallWithMethodName:@"create"
1722  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1723  result);
1724 
1725  XCTAssertNotNil(gMockPlatformView);
1726 
1727  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
1728  flutterPlatformViewsController->SetFlutterView(flutterView);
1729  // Create embedded view params.
1730  flutter::MutatorsStack stack;
1731  // Layer tree always pushes a screen scale factor to the stack.
1732  SkMatrix screenScaleMatrix =
1733  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
1734  stack.PushTransform(screenScaleMatrix);
1735  SkMatrix translateMatrix = SkMatrix::Translate(5, 5);
1736  // The platform view's rect for this test will be (5, 5, 10, 10).
1737  stack.PushTransform(translateMatrix);
1738  // Push a clip rect, big enough to contain the entire platform view bound.
1739  SkRect rect = SkRect::MakeXYWH(0, 0, 25, 25);
1740  stack.PushClipRect(rect);
1741  // Push a clip rrect, big enough to contain the entire platform view bound without clipping it.
1742  // Make the origin (-1, -1) so that the top left rounded corner isn't clipping the PlatformView.
1743  SkRect rect_for_rrect = SkRect::MakeXYWH(-1, -1, 25, 25);
1744  SkRRect rrect = SkRRect::MakeRectXY(rect_for_rrect, 1, 1);
1745  stack.PushClipRRect(rrect);
1746 
1747  auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
1748  SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack);
1749 
1750  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1751  flutterPlatformViewsController->CompositeWithParams(
1752  2, flutterPlatformViewsController->GetCompositionParams(2));
1753 
1754  gMockPlatformView.backgroundColor = UIColor.redColor;
1755  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
1756  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1757  [flutterView addSubview:childClippingView];
1758 
1759  [flutterView setNeedsLayout];
1760  [flutterView layoutIfNeeded];
1761  XCTAssertNil(childClippingView.maskView);
1762 }
1763 
1764 - (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView {
1765  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1766 
1767  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1768  /*platform=*/GetDefaultTaskRunner(),
1769  /*raster=*/GetDefaultTaskRunner(),
1770  /*ui=*/GetDefaultTaskRunner(),
1771  /*io=*/GetDefaultTaskRunner());
1772  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
1773  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
1774  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1775  /*delegate=*/mock_delegate,
1776  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1779  /*platform_views_controller=*/flutterPlatformViewsController,
1780  /*task_runners=*/runners,
1781  /*worker_task_runner=*/nil,
1782  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1783 
1786  flutterPlatformViewsController->RegisterViewFactory(
1787  factory, @"MockFlutterPlatformView",
1789  FlutterResult result = ^(id result) {
1790  };
1791  flutterPlatformViewsController->OnMethodCall(
1793  methodCallWithMethodName:@"create"
1794  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1795  result);
1796 
1797  XCTAssertNotNil(gMockPlatformView);
1798 
1799  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
1800  flutterPlatformViewsController->SetFlutterView(flutterView);
1801  // Create embedded view params
1802  flutter::MutatorsStack stack;
1803  // Layer tree always pushes a screen scale factor to the stack.
1804  SkMatrix screenScaleMatrix =
1805  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
1806  stack.PushTransform(screenScaleMatrix);
1807  SkMatrix translateMatrix = SkMatrix::Translate(5, 5);
1808  // The platform view's rect for this test will be (5, 5, 10, 10).
1809  stack.PushTransform(translateMatrix);
1810 
1811  // Push a clip rrect, the rect of the rrect is the same as the PlatformView of the corner should.
1812  // clip the PlatformView.
1813  SkRect rect_for_rrect = SkRect::MakeXYWH(0, 0, 10, 10);
1814  SkRRect rrect = SkRRect::MakeRectXY(rect_for_rrect, 1, 1);
1815  stack.PushClipRRect(rrect);
1816 
1817  auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
1818  SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack);
1819 
1820  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1821  flutterPlatformViewsController->CompositeWithParams(
1822  2, flutterPlatformViewsController->GetCompositionParams(2));
1823 
1824  gMockPlatformView.backgroundColor = UIColor.redColor;
1825  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
1826  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1827  [flutterView addSubview:childClippingView];
1828 
1829  [flutterView setNeedsLayout];
1830  [flutterView layoutIfNeeded];
1831 
1832  XCTAssertNotNil(childClippingView.maskView);
1833 }
1834 
1835 - (void)testClipRect {
1836  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1837 
1838  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1839  /*platform=*/GetDefaultTaskRunner(),
1840  /*raster=*/GetDefaultTaskRunner(),
1841  /*ui=*/GetDefaultTaskRunner(),
1842  /*io=*/GetDefaultTaskRunner());
1843  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
1844  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
1845  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1846  /*delegate=*/mock_delegate,
1847  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1850  /*platform_views_controller=*/flutterPlatformViewsController,
1851  /*task_runners=*/runners,
1852  /*worker_task_runner=*/nil,
1853  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1854 
1857  flutterPlatformViewsController->RegisterViewFactory(
1858  factory, @"MockFlutterPlatformView",
1860  FlutterResult result = ^(id result) {
1861  };
1862  flutterPlatformViewsController->OnMethodCall(
1864  methodCallWithMethodName:@"create"
1865  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1866  result);
1867 
1868  XCTAssertNotNil(gMockPlatformView);
1869 
1870  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
1871  flutterPlatformViewsController->SetFlutterView(flutterView);
1872  // Create embedded view params
1873  flutter::MutatorsStack stack;
1874  // Layer tree always pushes a screen scale factor to the stack
1875  SkMatrix screenScaleMatrix =
1876  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
1877  stack.PushTransform(screenScaleMatrix);
1878  // Push a clip rect
1879  SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3);
1880  stack.PushClipRect(rect);
1881 
1882  auto embeddedViewParams =
1883  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
1884 
1885  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1886  flutterPlatformViewsController->CompositeWithParams(
1887  2, flutterPlatformViewsController->GetCompositionParams(2));
1888 
1889  gMockPlatformView.backgroundColor = UIColor.redColor;
1890  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
1891  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1892  [flutterView addSubview:childClippingView];
1893 
1894  [flutterView setNeedsLayout];
1895  [flutterView layoutIfNeeded];
1896 
1897  CGRect insideClipping = CGRectMake(2, 2, 3, 3);
1898  for (int i = 0; i < 10; i++) {
1899  for (int j = 0; j < 10; j++) {
1900  CGPoint point = CGPointMake(i, j);
1901  int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView];
1902  if (CGRectContainsPoint(insideClipping, point)) {
1903  XCTAssertEqual(alpha, 255);
1904  } else {
1905  XCTAssertEqual(alpha, 0);
1906  }
1907  }
1908  }
1909 }
1910 
1911 - (void)testClipRect_multipleClips {
1912  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1913 
1914  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1915  /*platform=*/GetDefaultTaskRunner(),
1916  /*raster=*/GetDefaultTaskRunner(),
1917  /*ui=*/GetDefaultTaskRunner(),
1918  /*io=*/GetDefaultTaskRunner());
1919  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
1920  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
1921  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1922  /*delegate=*/mock_delegate,
1923  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1926  /*platform_views_controller=*/flutterPlatformViewsController,
1927  /*task_runners=*/runners,
1928  /*worker_task_runner=*/nil,
1929  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1930 
1933  flutterPlatformViewsController->RegisterViewFactory(
1934  factory, @"MockFlutterPlatformView",
1936  FlutterResult result = ^(id result) {
1937  };
1938  flutterPlatformViewsController->OnMethodCall(
1940  methodCallWithMethodName:@"create"
1941  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1942  result);
1943 
1944  XCTAssertNotNil(gMockPlatformView);
1945 
1946  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
1947  flutterPlatformViewsController->SetFlutterView(flutterView);
1948  // Create embedded view params
1949  flutter::MutatorsStack stack;
1950  // Layer tree always pushes a screen scale factor to the stack
1951  SkMatrix screenScaleMatrix =
1952  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
1953  stack.PushTransform(screenScaleMatrix);
1954  // Push a clip rect
1955  SkRect rect1 = SkRect::MakeXYWH(2, 2, 3, 3);
1956  stack.PushClipRect(rect1);
1957  // Push another clip rect
1958  SkRect rect2 = SkRect::MakeXYWH(3, 3, 3, 3);
1959  stack.PushClipRect(rect2);
1960 
1961  auto embeddedViewParams =
1962  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
1963 
1964  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1965  flutterPlatformViewsController->CompositeWithParams(
1966  2, flutterPlatformViewsController->GetCompositionParams(2));
1967 
1968  gMockPlatformView.backgroundColor = UIColor.redColor;
1969  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
1970  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1971  [flutterView addSubview:childClippingView];
1972 
1973  [flutterView setNeedsLayout];
1974  [flutterView layoutIfNeeded];
1975 
1976  /*
1977  clip 1 clip 2
1978  2 3 4 5 6 2 3 4 5 6
1979  2 + - - + 2
1980  3 | | 3 + - - +
1981  4 | | 4 | |
1982  5 + - - + 5 | |
1983  6 6 + - - +
1984 
1985  Result should be the intersection of 2 clips
1986  2 3 4 5 6
1987  2
1988  3 + - +
1989  4 | |
1990  5 + - +
1991  6
1992  */
1993  CGRect insideClipping = CGRectMake(3, 3, 2, 2);
1994  for (int i = 0; i < 10; i++) {
1995  for (int j = 0; j < 10; j++) {
1996  CGPoint point = CGPointMake(i, j);
1997  int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView];
1998  if (CGRectContainsPoint(insideClipping, point)) {
1999  XCTAssertEqual(alpha, 255);
2000  } else {
2001  XCTAssertEqual(alpha, 0);
2002  }
2003  }
2004  }
2005 }
2006 
2007 - (void)testClipRRect {
2008  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2009 
2010  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2011  /*platform=*/GetDefaultTaskRunner(),
2012  /*raster=*/GetDefaultTaskRunner(),
2013  /*ui=*/GetDefaultTaskRunner(),
2014  /*io=*/GetDefaultTaskRunner());
2015  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
2016  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
2017  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2018  /*delegate=*/mock_delegate,
2019  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2022  /*platform_views_controller=*/flutterPlatformViewsController,
2023  /*task_runners=*/runners,
2024  /*worker_task_runner=*/nil,
2025  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2026 
2029  flutterPlatformViewsController->RegisterViewFactory(
2030  factory, @"MockFlutterPlatformView",
2032  FlutterResult result = ^(id result) {
2033  };
2034  flutterPlatformViewsController->OnMethodCall(
2036  methodCallWithMethodName:@"create"
2037  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2038  result);
2039 
2040  XCTAssertNotNil(gMockPlatformView);
2041 
2042  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
2043  flutterPlatformViewsController->SetFlutterView(flutterView);
2044  // Create embedded view params
2045  flutter::MutatorsStack stack;
2046  // Layer tree always pushes a screen scale factor to the stack
2047  SkMatrix screenScaleMatrix =
2048  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
2049  stack.PushTransform(screenScaleMatrix);
2050  // Push a clip rrect
2051  SkRRect rrect = SkRRect::MakeRectXY(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1);
2052  stack.PushClipRRect(rrect);
2053 
2054  auto embeddedViewParams =
2055  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
2056 
2057  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
2058  flutterPlatformViewsController->CompositeWithParams(
2059  2, flutterPlatformViewsController->GetCompositionParams(2));
2060 
2061  gMockPlatformView.backgroundColor = UIColor.redColor;
2062  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
2063  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
2064  [flutterView addSubview:childClippingView];
2065 
2066  [flutterView setNeedsLayout];
2067  [flutterView layoutIfNeeded];
2068 
2069  /*
2070  ClippingMask outterClipping
2071  2 3 4 5 6 7 2 3 4 5 6 7
2072  2 / - - - - \ 2 + - - - - +
2073  3 | | 3 | |
2074  4 | | 4 | |
2075  5 | | 5 | |
2076  6 | | 6 | |
2077  7 \ - - - - / 7 + - - - - +
2078 
2079  innerClipping1 innerClipping2
2080  2 3 4 5 6 7 2 3 4 5 6 7
2081  2 + - - + 2
2082  3 | | 3 + - - - - +
2083  4 | | 4 | |
2084  5 | | 5 | |
2085  6 | | 6 + - - - - +
2086  7 + - - + 7
2087  */
2088  CGRect innerClipping1 = CGRectMake(3, 2, 4, 6);
2089  CGRect innerClipping2 = CGRectMake(2, 3, 6, 4);
2090  CGRect outterClipping = CGRectMake(2, 2, 6, 6);
2091  for (int i = 0; i < 10; i++) {
2092  for (int j = 0; j < 10; j++) {
2093  CGPoint point = CGPointMake(i, j);
2094  int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView];
2095  if (CGRectContainsPoint(innerClipping1, point) ||
2096  CGRectContainsPoint(innerClipping2, point)) {
2097  // Pixels inside either of the 2 inner clippings should be fully opaque.
2098  XCTAssertEqual(alpha, 255);
2099  } else if (CGRectContainsPoint(outterClipping, point)) {
2100  // Corner pixels (i.e. (2, 2), (2, 7), (7, 2) and (7, 7)) should be partially transparent.
2101  XCTAssert(0 < alpha && alpha < 255);
2102  } else {
2103  // Pixels outside outterClipping should be fully transparent.
2104  XCTAssertEqual(alpha, 0);
2105  }
2106  }
2107  }
2108 }
2109 
2110 - (void)testClipRRect_multipleClips {
2111  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2112 
2113  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2114  /*platform=*/GetDefaultTaskRunner(),
2115  /*raster=*/GetDefaultTaskRunner(),
2116  /*ui=*/GetDefaultTaskRunner(),
2117  /*io=*/GetDefaultTaskRunner());
2118  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
2119  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
2120  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2121  /*delegate=*/mock_delegate,
2122  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2125  /*platform_views_controller=*/flutterPlatformViewsController,
2126  /*task_runners=*/runners,
2127  /*worker_task_runner=*/nil,
2128  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2129 
2132  flutterPlatformViewsController->RegisterViewFactory(
2133  factory, @"MockFlutterPlatformView",
2135  FlutterResult result = ^(id result) {
2136  };
2137  flutterPlatformViewsController->OnMethodCall(
2139  methodCallWithMethodName:@"create"
2140  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2141  result);
2142 
2143  XCTAssertNotNil(gMockPlatformView);
2144 
2145  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
2146  flutterPlatformViewsController->SetFlutterView(flutterView);
2147  // Create embedded view params
2148  flutter::MutatorsStack stack;
2149  // Layer tree always pushes a screen scale factor to the stack
2150  SkMatrix screenScaleMatrix =
2151  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
2152  stack.PushTransform(screenScaleMatrix);
2153  // Push a clip rrect
2154  SkRRect rrect = SkRRect::MakeRectXY(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1);
2155  stack.PushClipRRect(rrect);
2156  // Push a clip rect
2157  SkRect rect = SkRect::MakeXYWH(4, 2, 6, 6);
2158  stack.PushClipRect(rect);
2159 
2160  auto embeddedViewParams =
2161  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
2162 
2163  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
2164  flutterPlatformViewsController->CompositeWithParams(
2165  2, flutterPlatformViewsController->GetCompositionParams(2));
2166 
2167  gMockPlatformView.backgroundColor = UIColor.redColor;
2168  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
2169  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
2170  [flutterView addSubview:childClippingView];
2171 
2172  [flutterView setNeedsLayout];
2173  [flutterView layoutIfNeeded];
2174 
2175  /*
2176  clip 1 clip 2
2177  2 3 4 5 6 7 8 9 2 3 4 5 6 7 8 9
2178  2 / - - - - \ 2 + - - - - +
2179  3 | | 3 | |
2180  4 | | 4 | |
2181  5 | | 5 | |
2182  6 | | 6 | |
2183  7 \ - - - - / 7 + - - - - +
2184 
2185  Result should be the intersection of 2 clips
2186  2 3 4 5 6 7 8 9
2187  2 + - - \
2188  3 | |
2189  4 | |
2190  5 | |
2191  6 | |
2192  7 + - - /
2193  */
2194  CGRect clipping = CGRectMake(4, 2, 4, 6);
2195  for (int i = 0; i < 10; i++) {
2196  for (int j = 0; j < 10; j++) {
2197  CGPoint point = CGPointMake(i, j);
2198  int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView];
2199  if (i == 7 && (j == 2 || j == 7)) {
2200  // Upper and lower right corners should be partially transparent.
2201  XCTAssert(0 < alpha && alpha < 255);
2202  } else if (
2203  // left
2204  (i == 4 && j >= 2 && j <= 7) ||
2205  // right
2206  (i == 7 && j >= 2 && j <= 7) ||
2207  // top
2208  (j == 2 && i >= 4 && i <= 7) ||
2209  // bottom
2210  (j == 7 && i >= 4 && i <= 7)) {
2211  // Since we are falling back to software rendering for this case
2212  // The edge pixels can be anti-aliased, so it may not be fully opaque.
2213  XCTAssert(alpha > 127);
2214  } else if ((i == 3 && j >= 1 && j <= 8) || (i == 8 && j >= 1 && j <= 8) ||
2215  (j == 1 && i >= 3 && i <= 8) || (j == 8 && i >= 3 && i <= 8)) {
2216  // Since we are falling back to software rendering for this case
2217  // The edge pixels can be anti-aliased, so it may not be fully transparent.
2218  XCTAssert(alpha < 127);
2219  } else if (CGRectContainsPoint(clipping, point)) {
2220  // Other pixels inside clipping should be fully opaque.
2221  XCTAssertEqual(alpha, 255);
2222  } else {
2223  // Pixels outside clipping should be fully transparent.
2224  XCTAssertEqual(alpha, 0);
2225  }
2226  }
2227  }
2228 }
2229 
2230 - (void)testClipPath {
2231  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2232 
2233  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2234  /*platform=*/GetDefaultTaskRunner(),
2235  /*raster=*/GetDefaultTaskRunner(),
2236  /*ui=*/GetDefaultTaskRunner(),
2237  /*io=*/GetDefaultTaskRunner());
2238  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
2239  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
2240  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2241  /*delegate=*/mock_delegate,
2242  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2245  /*platform_views_controller=*/flutterPlatformViewsController,
2246  /*task_runners=*/runners,
2247  /*worker_task_runner=*/nil,
2248  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2249 
2252  flutterPlatformViewsController->RegisterViewFactory(
2253  factory, @"MockFlutterPlatformView",
2255  FlutterResult result = ^(id result) {
2256  };
2257  flutterPlatformViewsController->OnMethodCall(
2259  methodCallWithMethodName:@"create"
2260  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2261  result);
2262 
2263  XCTAssertNotNil(gMockPlatformView);
2264 
2265  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
2266  flutterPlatformViewsController->SetFlutterView(flutterView);
2267  // Create embedded view params
2268  flutter::MutatorsStack stack;
2269  // Layer tree always pushes a screen scale factor to the stack
2270  SkMatrix screenScaleMatrix =
2271  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
2272  stack.PushTransform(screenScaleMatrix);
2273  // Push a clip path
2274  SkPath path;
2275  path.addRoundRect(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1);
2276  stack.PushClipPath(path);
2277 
2278  auto embeddedViewParams =
2279  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
2280 
2281  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
2282  flutterPlatformViewsController->CompositeWithParams(
2283  2, flutterPlatformViewsController->GetCompositionParams(2));
2284 
2285  gMockPlatformView.backgroundColor = UIColor.redColor;
2286  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
2287  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
2288  [flutterView addSubview:childClippingView];
2289 
2290  [flutterView setNeedsLayout];
2291  [flutterView layoutIfNeeded];
2292 
2293  /*
2294  ClippingMask outterClipping
2295  2 3 4 5 6 7 2 3 4 5 6 7
2296  2 / - - - - \ 2 + - - - - +
2297  3 | | 3 | |
2298  4 | | 4 | |
2299  5 | | 5 | |
2300  6 | | 6 | |
2301  7 \ - - - - / 7 + - - - - +
2302 
2303  innerClipping1 innerClipping2
2304  2 3 4 5 6 7 2 3 4 5 6 7
2305  2 + - - + 2
2306  3 | | 3 + - - - - +
2307  4 | | 4 | |
2308  5 | | 5 | |
2309  6 | | 6 + - - - - +
2310  7 + - - + 7
2311  */
2312  CGRect innerClipping1 = CGRectMake(3, 2, 4, 6);
2313  CGRect innerClipping2 = CGRectMake(2, 3, 6, 4);
2314  CGRect outterClipping = CGRectMake(2, 2, 6, 6);
2315  for (int i = 0; i < 10; i++) {
2316  for (int j = 0; j < 10; j++) {
2317  CGPoint point = CGPointMake(i, j);
2318  int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView];
2319  if (CGRectContainsPoint(innerClipping1, point) ||
2320  CGRectContainsPoint(innerClipping2, point)) {
2321  // Pixels inside either of the 2 inner clippings should be fully opaque.
2322  XCTAssertEqual(alpha, 255);
2323  } else if (CGRectContainsPoint(outterClipping, point)) {
2324  // Corner pixels (i.e. (2, 2), (2, 7), (7, 2) and (7, 7)) should be partially transparent.
2325  XCTAssert(0 < alpha && alpha < 255);
2326  } else {
2327  // Pixels outside outterClipping should be fully transparent.
2328  XCTAssertEqual(alpha, 0);
2329  }
2330  }
2331  }
2332 }
2333 
2334 - (void)testClipPath_multipleClips {
2335  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2336 
2337  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2338  /*platform=*/GetDefaultTaskRunner(),
2339  /*raster=*/GetDefaultTaskRunner(),
2340  /*ui=*/GetDefaultTaskRunner(),
2341  /*io=*/GetDefaultTaskRunner());
2342  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
2343  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
2344  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2345  /*delegate=*/mock_delegate,
2346  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2349  /*platform_views_controller=*/flutterPlatformViewsController,
2350  /*task_runners=*/runners,
2351  /*worker_task_runner=*/nil,
2352  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2353 
2356  flutterPlatformViewsController->RegisterViewFactory(
2357  factory, @"MockFlutterPlatformView",
2359  FlutterResult result = ^(id result) {
2360  };
2361  flutterPlatformViewsController->OnMethodCall(
2363  methodCallWithMethodName:@"create"
2364  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2365  result);
2366 
2367  XCTAssertNotNil(gMockPlatformView);
2368 
2369  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
2370  flutterPlatformViewsController->SetFlutterView(flutterView);
2371  // Create embedded view params
2372  flutter::MutatorsStack stack;
2373  // Layer tree always pushes a screen scale factor to the stack
2374  SkMatrix screenScaleMatrix =
2375  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
2376  stack.PushTransform(screenScaleMatrix);
2377  // Push a clip path
2378  SkPath path;
2379  path.addRoundRect(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1);
2380  stack.PushClipPath(path);
2381  // Push a clip rect
2382  SkRect rect = SkRect::MakeXYWH(4, 2, 6, 6);
2383  stack.PushClipRect(rect);
2384 
2385  auto embeddedViewParams =
2386  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
2387 
2388  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
2389  flutterPlatformViewsController->CompositeWithParams(
2390  2, flutterPlatformViewsController->GetCompositionParams(2));
2391 
2392  gMockPlatformView.backgroundColor = UIColor.redColor;
2393  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
2394  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
2395  [flutterView addSubview:childClippingView];
2396 
2397  [flutterView setNeedsLayout];
2398  [flutterView layoutIfNeeded];
2399 
2400  /*
2401  clip 1 clip 2
2402  2 3 4 5 6 7 8 9 2 3 4 5 6 7 8 9
2403  2 / - - - - \ 2 + - - - - +
2404  3 | | 3 | |
2405  4 | | 4 | |
2406  5 | | 5 | |
2407  6 | | 6 | |
2408  7 \ - - - - / 7 + - - - - +
2409 
2410  Result should be the intersection of 2 clips
2411  2 3 4 5 6 7 8 9
2412  2 + - - \
2413  3 | |
2414  4 | |
2415  5 | |
2416  6 | |
2417  7 + - - /
2418  */
2419  CGRect clipping = CGRectMake(4, 2, 4, 6);
2420  for (int i = 0; i < 10; i++) {
2421  for (int j = 0; j < 10; j++) {
2422  CGPoint point = CGPointMake(i, j);
2423  int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView];
2424  if (i == 7 && (j == 2 || j == 7)) {
2425  // Upper and lower right corners should be partially transparent.
2426  XCTAssert(0 < alpha && alpha < 255);
2427  } else if (
2428  // left
2429  (i == 4 && j >= 2 && j <= 7) ||
2430  // right
2431  (i == 7 && j >= 2 && j <= 7) ||
2432  // top
2433  (j == 2 && i >= 4 && i <= 7) ||
2434  // bottom
2435  (j == 7 && i >= 4 && i <= 7)) {
2436  // Since we are falling back to software rendering for this case
2437  // The edge pixels can be anti-aliased, so it may not be fully opaque.
2438  XCTAssert(alpha > 127);
2439  } else if ((i == 3 && j >= 1 && j <= 8) || (i == 8 && j >= 1 && j <= 8) ||
2440  (j == 1 && i >= 3 && i <= 8) || (j == 8 && i >= 3 && i <= 8)) {
2441  // Since we are falling back to software rendering for this case
2442  // The edge pixels can be anti-aliased, so it may not be fully transparent.
2443  XCTAssert(alpha < 127);
2444  } else if (CGRectContainsPoint(clipping, point)) {
2445  // Other pixels inside clipping should be fully opaque.
2446  XCTAssertEqual(alpha, 255);
2447  } else {
2448  // Pixels outside clipping should be fully transparent.
2449  XCTAssertEqual(alpha, 0);
2450  }
2451  }
2452  }
2453 }
2454 
2455 - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents {
2456  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2457 
2458  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2459  /*platform=*/GetDefaultTaskRunner(),
2460  /*raster=*/GetDefaultTaskRunner(),
2461  /*ui=*/GetDefaultTaskRunner(),
2462  /*io=*/GetDefaultTaskRunner());
2463  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
2464  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
2465  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2466  /*delegate=*/mock_delegate,
2467  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2470  /*platform_views_controller=*/flutterPlatformViewsController,
2471  /*task_runners=*/runners,
2472  /*worker_task_runner=*/nil,
2473  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2474 
2477  flutterPlatformViewsController->RegisterViewFactory(
2478  factory, @"MockFlutterPlatformView",
2480  FlutterResult result = ^(id result) {
2481  };
2482  flutterPlatformViewsController->OnMethodCall(
2484  methodCallWithMethodName:@"create"
2485  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2486  result);
2487 
2488  XCTAssertNotNil(gMockPlatformView);
2489 
2490  // Find touch inteceptor view
2491  UIView* touchInteceptorView = gMockPlatformView;
2492  while (touchInteceptorView != nil &&
2493  ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
2494  touchInteceptorView = touchInteceptorView.superview;
2495  }
2496  XCTAssertNotNil(touchInteceptorView);
2497 
2498  // Find ForwardGestureRecognizer
2499  UIGestureRecognizer* forwardGectureRecognizer = nil;
2500  for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
2501  if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) {
2502  forwardGectureRecognizer = gestureRecognizer;
2503  break;
2504  }
2505  }
2506 
2507  // Before setting flutter view controller, events are not dispatched.
2508  NSSet* touches1 = [[NSSet alloc] init];
2509  id event1 = OCMClassMock([UIEvent class]);
2510  id flutterViewContoller = OCMClassMock([FlutterViewController class]);
2511  [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2512  OCMReject([flutterViewContoller touchesBegan:touches1 withEvent:event1]);
2513 
2514  // Set flutter view controller allows events to be dispatched.
2515  NSSet* touches2 = [[NSSet alloc] init];
2516  id event2 = OCMClassMock([UIEvent class]);
2517  flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller);
2518  [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2];
2519  OCMVerify([flutterViewContoller touchesBegan:touches2 withEvent:event2]);
2520 }
2521 
2522 - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGesturesToBeHandled {
2523  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2524 
2525  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2526  /*platform=*/GetDefaultTaskRunner(),
2527  /*raster=*/GetDefaultTaskRunner(),
2528  /*ui=*/GetDefaultTaskRunner(),
2529  /*io=*/GetDefaultTaskRunner());
2530  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
2531  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
2532  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2533  /*delegate=*/mock_delegate,
2534  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2537  /*platform_views_controller=*/flutterPlatformViewsController,
2538  /*task_runners=*/runners,
2539  /*worker_task_runner=*/nil,
2540  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2541 
2544  flutterPlatformViewsController->RegisterViewFactory(
2545  factory, @"MockFlutterPlatformView",
2547  FlutterResult result = ^(id result) {
2548  };
2549  flutterPlatformViewsController->OnMethodCall(
2551  methodCallWithMethodName:@"create"
2552  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2553  result);
2554 
2555  XCTAssertNotNil(gMockPlatformView);
2556 
2557  // Find touch inteceptor view
2558  UIView* touchInteceptorView = gMockPlatformView;
2559  while (touchInteceptorView != nil &&
2560  ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
2561  touchInteceptorView = touchInteceptorView.superview;
2562  }
2563  XCTAssertNotNil(touchInteceptorView);
2564 
2565  // Find ForwardGestureRecognizer
2566  UIGestureRecognizer* forwardGectureRecognizer = nil;
2567  for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
2568  if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) {
2569  forwardGectureRecognizer = gestureRecognizer;
2570  break;
2571  }
2572  }
2573  id flutterViewContoller = OCMClassMock([FlutterViewController class]);
2574  {
2575  // ***** Sequence 1, finishing touch event with touchEnded ***** //
2576  flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller);
2577 
2578  NSSet* touches1 = [[NSSet alloc] init];
2579  id event1 = OCMClassMock([UIEvent class]);
2580  [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2581  OCMVerify([flutterViewContoller touchesBegan:touches1 withEvent:event1]);
2582 
2583  flutterPlatformViewsController->SetFlutterViewController(nil);
2584 
2585  // Allow the touch events to finish
2586  NSSet* touches2 = [[NSSet alloc] init];
2587  id event2 = OCMClassMock([UIEvent class]);
2588  [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2];
2589  OCMVerify([flutterViewContoller touchesMoved:touches2 withEvent:event2]);
2590 
2591  NSSet* touches3 = [[NSSet alloc] init];
2592  id event3 = OCMClassMock([UIEvent class]);
2593  [forwardGectureRecognizer touchesEnded:touches3 withEvent:event3];
2594  OCMVerify([flutterViewContoller touchesEnded:touches3 withEvent:event3]);
2595 
2596  // Now the 2nd touch sequence should not be allowed.
2597  NSSet* touches4 = [[NSSet alloc] init];
2598  id event4 = OCMClassMock([UIEvent class]);
2599  [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4];
2600  OCMReject([flutterViewContoller touchesBegan:touches4 withEvent:event4]);
2601 
2602  NSSet* touches5 = [[NSSet alloc] init];
2603  id event5 = OCMClassMock([UIEvent class]);
2604  [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5];
2605  OCMReject([flutterViewContoller touchesEnded:touches5 withEvent:event5]);
2606  }
2607 
2608  {
2609  // ***** Sequence 2, finishing touch event with touchCancelled ***** //
2610  flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller);
2611 
2612  NSSet* touches1 = [[NSSet alloc] init];
2613  id event1 = OCMClassMock([UIEvent class]);
2614  [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2615  OCMVerify([flutterViewContoller touchesBegan:touches1 withEvent:event1]);
2616 
2617  flutterPlatformViewsController->SetFlutterViewController(nil);
2618 
2619  // Allow the touch events to finish
2620  NSSet* touches2 = [[NSSet alloc] init];
2621  id event2 = OCMClassMock([UIEvent class]);
2622  [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2];
2623  OCMVerify([flutterViewContoller touchesMoved:touches2 withEvent:event2]);
2624 
2625  NSSet* touches3 = [[NSSet alloc] init];
2626  id event3 = OCMClassMock([UIEvent class]);
2627  [forwardGectureRecognizer touchesCancelled:touches3 withEvent:event3];
2628  OCMVerify([flutterViewContoller forceTouchesCancelled:touches3]);
2629 
2630  // Now the 2nd touch sequence should not be allowed.
2631  NSSet* touches4 = [[NSSet alloc] init];
2632  id event4 = OCMClassMock([UIEvent class]);
2633  [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4];
2634  OCMReject([flutterViewContoller touchesBegan:touches4 withEvent:event4]);
2635 
2636  NSSet* touches5 = [[NSSet alloc] init];
2637  id event5 = OCMClassMock([UIEvent class]);
2638  [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5];
2639  OCMReject([flutterViewContoller touchesEnded:touches5 withEvent:event5]);
2640  }
2641 
2642  flutterPlatformViewsController->Reset();
2643 }
2644 
2645 - (void)
2646  testSetFlutterViewControllerInTheMiddleOfTouchEventAllowsTheNewControllerToHandleSecondTouchSequence {
2647  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2648 
2649  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2650  /*platform=*/GetDefaultTaskRunner(),
2651  /*raster=*/GetDefaultTaskRunner(),
2652  /*ui=*/GetDefaultTaskRunner(),
2653  /*io=*/GetDefaultTaskRunner());
2654  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
2655  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
2656  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2657  /*delegate=*/mock_delegate,
2658  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2661  /*platform_views_controller=*/flutterPlatformViewsController,
2662  /*task_runners=*/runners,
2663  /*worker_task_runner=*/nil,
2664  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2665 
2668  flutterPlatformViewsController->RegisterViewFactory(
2669  factory, @"MockFlutterPlatformView",
2671  FlutterResult result = ^(id result) {
2672  };
2673  flutterPlatformViewsController->OnMethodCall(
2675  methodCallWithMethodName:@"create"
2676  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2677  result);
2678 
2679  XCTAssertNotNil(gMockPlatformView);
2680 
2681  // Find touch inteceptor view
2682  UIView* touchInteceptorView = gMockPlatformView;
2683  while (touchInteceptorView != nil &&
2684  ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
2685  touchInteceptorView = touchInteceptorView.superview;
2686  }
2687  XCTAssertNotNil(touchInteceptorView);
2688 
2689  // Find ForwardGestureRecognizer
2690  UIGestureRecognizer* forwardGectureRecognizer = nil;
2691  for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
2692  if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) {
2693  forwardGectureRecognizer = gestureRecognizer;
2694  break;
2695  }
2696  }
2697  id flutterViewContoller = OCMClassMock([FlutterViewController class]);
2698 
2699  flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller);
2700 
2701  // The touches in this sequence requires 1 touch object, we always create the NSSet with one item.
2702  NSSet* touches1 = [NSSet setWithObject:@1];
2703  id event1 = OCMClassMock([UIEvent class]);
2704  [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2705  OCMVerify([flutterViewContoller touchesBegan:touches1 withEvent:event1]);
2706 
2707  FlutterViewController* flutterViewContoller2 = OCMClassMock([FlutterViewController class]);
2708  flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller2);
2709 
2710  // Touch events should still send to the old FlutterViewController if FlutterViewController
2711  // is updated in between.
2712  NSSet* touches2 = [NSSet setWithObject:@1];
2713  id event2 = OCMClassMock([UIEvent class]);
2714  [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2];
2715  OCMVerify([flutterViewContoller touchesBegan:touches2 withEvent:event2]);
2716  OCMReject([flutterViewContoller2 touchesBegan:touches2 withEvent:event2]);
2717 
2718  NSSet* touches3 = [NSSet setWithObject:@1];
2719  id event3 = OCMClassMock([UIEvent class]);
2720  [forwardGectureRecognizer touchesMoved:touches3 withEvent:event3];
2721  OCMVerify([flutterViewContoller touchesMoved:touches3 withEvent:event3]);
2722  OCMReject([flutterViewContoller2 touchesMoved:touches3 withEvent:event3]);
2723 
2724  NSSet* touches4 = [NSSet setWithObject:@1];
2725  id event4 = OCMClassMock([UIEvent class]);
2726  [forwardGectureRecognizer touchesEnded:touches4 withEvent:event4];
2727  OCMVerify([flutterViewContoller touchesEnded:touches4 withEvent:event4]);
2728  OCMReject([flutterViewContoller2 touchesEnded:touches4 withEvent:event4]);
2729 
2730  NSSet* touches5 = [NSSet setWithObject:@1];
2731  id event5 = OCMClassMock([UIEvent class]);
2732  [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5];
2733  OCMVerify([flutterViewContoller touchesEnded:touches5 withEvent:event5]);
2734  OCMReject([flutterViewContoller2 touchesEnded:touches5 withEvent:event5]);
2735 
2736  // Now the 2nd touch sequence should go to the new FlutterViewController
2737 
2738  NSSet* touches6 = [NSSet setWithObject:@1];
2739  id event6 = OCMClassMock([UIEvent class]);
2740  [forwardGectureRecognizer touchesBegan:touches6 withEvent:event6];
2741  OCMVerify([flutterViewContoller2 touchesBegan:touches6 withEvent:event6]);
2742  OCMReject([flutterViewContoller touchesBegan:touches6 withEvent:event6]);
2743 
2744  // Allow the touch events to finish
2745  NSSet* touches7 = [NSSet setWithObject:@1];
2746  id event7 = OCMClassMock([UIEvent class]);
2747  [forwardGectureRecognizer touchesMoved:touches7 withEvent:event7];
2748  OCMVerify([flutterViewContoller2 touchesMoved:touches7 withEvent:event7]);
2749  OCMReject([flutterViewContoller touchesMoved:touches7 withEvent:event7]);
2750 
2751  NSSet* touches8 = [NSSet setWithObject:@1];
2752  id event8 = OCMClassMock([UIEvent class]);
2753  [forwardGectureRecognizer touchesEnded:touches8 withEvent:event8];
2754  OCMVerify([flutterViewContoller2 touchesEnded:touches8 withEvent:event8]);
2755  OCMReject([flutterViewContoller touchesEnded:touches8 withEvent:event8]);
2756 
2757  flutterPlatformViewsController->Reset();
2758 }
2759 
2760 - (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled {
2761  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2762 
2763  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2764  /*platform=*/GetDefaultTaskRunner(),
2765  /*raster=*/GetDefaultTaskRunner(),
2766  /*ui=*/GetDefaultTaskRunner(),
2767  /*io=*/GetDefaultTaskRunner());
2768  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
2769  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
2770  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2771  /*delegate=*/mock_delegate,
2772  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2775  /*platform_views_controller=*/flutterPlatformViewsController,
2776  /*task_runners=*/runners,
2777  /*worker_task_runner=*/nil,
2778  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2779 
2782  flutterPlatformViewsController->RegisterViewFactory(
2783  factory, @"MockFlutterPlatformView",
2785  FlutterResult result = ^(id result) {
2786  };
2787  flutterPlatformViewsController->OnMethodCall(
2789  methodCallWithMethodName:@"create"
2790  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2791  result);
2792 
2793  XCTAssertNotNil(gMockPlatformView);
2794 
2795  // Find touch inteceptor view
2796  UIView* touchInteceptorView = gMockPlatformView;
2797  while (touchInteceptorView != nil &&
2798  ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
2799  touchInteceptorView = touchInteceptorView.superview;
2800  }
2801  XCTAssertNotNil(touchInteceptorView);
2802 
2803  // Find ForwardGestureRecognizer
2804  UIGestureRecognizer* forwardGectureRecognizer = nil;
2805  for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
2806  if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) {
2807  forwardGectureRecognizer = gestureRecognizer;
2808  break;
2809  }
2810  }
2811  id flutterViewContoller = OCMClassMock([FlutterViewController class]);
2812 
2813  flutterPlatformViewsController->SetFlutterViewController(flutterViewContoller);
2814 
2815  NSSet* touches1 = [NSSet setWithObject:@1];
2816  id event1 = OCMClassMock([UIEvent class]);
2817  [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2818 
2819  [forwardGectureRecognizer touchesCancelled:touches1 withEvent:event1];
2820  OCMVerify([flutterViewContoller forceTouchesCancelled:touches1]);
2821 
2822  flutterPlatformViewsController->Reset();
2823 }
2824 
2825 - (void)
2826  testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldRemoveAndAddBackDelayingRecognizerForWebView {
2827  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2828 
2829  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2830  /*platform=*/GetDefaultTaskRunner(),
2831  /*raster=*/GetDefaultTaskRunner(),
2832  /*ui=*/GetDefaultTaskRunner(),
2833  /*io=*/GetDefaultTaskRunner());
2834  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
2835  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
2836  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2837  /*delegate=*/mock_delegate,
2838  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2841  /*platform_views_controller=*/flutterPlatformViewsController,
2842  /*task_runners=*/runners,
2843  /*worker_task_runner=*/nil,
2844  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2845 
2848  flutterPlatformViewsController->RegisterViewFactory(
2850  FlutterResult result = ^(id result) {
2851  };
2852  flutterPlatformViewsController->OnMethodCall(
2853  [FlutterMethodCall methodCallWithMethodName:@"create"
2854  arguments:@{@"id" : @2, @"viewType" : @"MockWebView"}],
2855  result);
2856 
2857  XCTAssertNotNil(gMockPlatformView);
2858 
2859  // Find touch inteceptor view
2860  UIView* touchInteceptorView = gMockPlatformView;
2861  while (touchInteceptorView != nil &&
2862  ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
2863  touchInteceptorView = touchInteceptorView.superview;
2864  }
2865  XCTAssertNotNil(touchInteceptorView);
2866 
2867  XCTAssert(touchInteceptorView.gestureRecognizers.count == 2);
2868  UIGestureRecognizer* delayingRecognizer = touchInteceptorView.gestureRecognizers[0];
2869  UIGestureRecognizer* forwardingRecognizer = touchInteceptorView.gestureRecognizers[1];
2870 
2871  XCTAssert([delayingRecognizer isKindOfClass:[FlutterDelayingGestureRecognizer class]]);
2872  XCTAssert([forwardingRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]);
2873 
2875 
2876  if (@available(iOS 18.2, *)) {
2877  // Since we remove and add back delayingRecognizer, it would be reordered to the last.
2878  XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], forwardingRecognizer);
2879  XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], delayingRecognizer);
2880  } else {
2881  XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], delayingRecognizer);
2882  XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], forwardingRecognizer);
2883  }
2884 }
2885 
2886 - (void)
2887  testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldNotRemoveAndAddBackDelayingRecognizerForNonWebView {
2888  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2889 
2890  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2891  /*platform=*/GetDefaultTaskRunner(),
2892  /*raster=*/GetDefaultTaskRunner(),
2893  /*ui=*/GetDefaultTaskRunner(),
2894  /*io=*/GetDefaultTaskRunner());
2895  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
2896  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
2897  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2898  /*delegate=*/mock_delegate,
2899  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2902  /*platform_views_controller=*/flutterPlatformViewsController,
2903  /*task_runners=*/runners,
2904  /*worker_task_runner=*/nil,
2905  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2906 
2909  flutterPlatformViewsController->RegisterViewFactory(
2910  factory, @"MockFlutterPlatformView",
2912  FlutterResult result = ^(id result) {
2913  };
2914  flutterPlatformViewsController->OnMethodCall(
2916  methodCallWithMethodName:@"create"
2917  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2918  result);
2919 
2920  XCTAssertNotNil(gMockPlatformView);
2921 
2922  // Find touch inteceptor view
2923  UIView* touchInteceptorView = gMockPlatformView;
2924  while (touchInteceptorView != nil &&
2925  ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
2926  touchInteceptorView = touchInteceptorView.superview;
2927  }
2928  XCTAssertNotNil(touchInteceptorView);
2929 
2930  XCTAssert(touchInteceptorView.gestureRecognizers.count == 2);
2931  UIGestureRecognizer* delayingRecognizer = touchInteceptorView.gestureRecognizers[0];
2932  UIGestureRecognizer* forwardingRecognizer = touchInteceptorView.gestureRecognizers[1];
2933 
2934  XCTAssert([delayingRecognizer isKindOfClass:[FlutterDelayingGestureRecognizer class]]);
2935  XCTAssert([forwardingRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]);
2936 
2938 
2939  XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], delayingRecognizer);
2940  XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], forwardingRecognizer);
2941 }
2942 
2943 - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashing {
2944  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2945 
2946  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2947  /*platform=*/GetDefaultTaskRunner(),
2948  /*raster=*/GetDefaultTaskRunner(),
2949  /*ui=*/GetDefaultTaskRunner(),
2950  /*io=*/GetDefaultTaskRunner());
2951  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
2952  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
2953  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2954  /*delegate=*/mock_delegate,
2955  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2958  /*platform_views_controller=*/flutterPlatformViewsController,
2959  /*task_runners=*/runners,
2960  /*worker_task_runner=*/nil,
2961  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2962 
2965  flutterPlatformViewsController->RegisterViewFactory(
2966  factory, @"MockFlutterPlatformView",
2968  FlutterResult result = ^(id result) {
2969  };
2970  flutterPlatformViewsController->OnMethodCall(
2972  methodCallWithMethodName:@"create"
2973  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2974  result);
2975 
2976  XCTAssertNotNil(gMockPlatformView);
2977 
2978  // Create embedded view params
2979  flutter::MutatorsStack stack;
2980  SkMatrix finalMatrix;
2981 
2982  auto embeddedViewParams_1 =
2983  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2984 
2985  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_1));
2986  flutterPlatformViewsController->CompositeWithParams(
2987  2, flutterPlatformViewsController->GetCompositionParams(2));
2988 
2989  flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
2990  auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
2991  nullptr, framebuffer_info,
2992  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return false; },
2993  [](const flutter::SurfaceFrame& surface_frame) { return true; },
2994  /*frame_size=*/SkISize::Make(800, 600));
2995  XCTAssertFalse(
2996  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
2997 
2998  auto embeddedViewParams_2 =
2999  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3000  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_2));
3001  flutterPlatformViewsController->CompositeWithParams(
3002  2, flutterPlatformViewsController->GetCompositionParams(2));
3003 
3004  auto mock_surface_submit_true = std::make_unique<flutter::SurfaceFrame>(
3005  nullptr, framebuffer_info,
3006  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3007  [](const flutter::SurfaceFrame& surface_frame) { return true; },
3008  /*frame_size=*/SkISize::Make(800, 600));
3009  XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr,
3010  std::move(mock_surface_submit_true)));
3011 }
3012 
3013 - (void)
3014  testFlutterPlatformViewControllerResetDeallocsPlatformViewWhenRootViewsNotBindedToFlutterView {
3015  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3016 
3017  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3018  /*platform=*/GetDefaultTaskRunner(),
3019  /*raster=*/GetDefaultTaskRunner(),
3020  /*ui=*/GetDefaultTaskRunner(),
3021  /*io=*/GetDefaultTaskRunner());
3022  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
3023  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
3024  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3025  /*delegate=*/mock_delegate,
3026  /*rendering_api=*/mock_delegate.settings_.enable_impeller
3029  /*platform_views_controller=*/flutterPlatformViewsController,
3030  /*task_runners=*/runners,
3031  /*worker_task_runner=*/nil,
3032  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3033 
3034  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3035  flutterPlatformViewsController->SetFlutterView(flutterView);
3036 
3039  flutterPlatformViewsController->RegisterViewFactory(
3040  factory, @"MockFlutterPlatformView",
3042  FlutterResult result = ^(id result) {
3043  };
3044  // autorelease pool to trigger an autorelease for all the root_views_ and touch_interceptors_.
3045  @autoreleasepool {
3046  flutterPlatformViewsController->OnMethodCall(
3048  methodCallWithMethodName:@"create"
3049  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
3050  result);
3051 
3052  flutter::MutatorsStack stack;
3053  SkMatrix finalMatrix;
3054  auto embeddedViewParams =
3055  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3056  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
3057 
3058  // Not calling |flutterPlatformViewsController::SubmitFrame| so that the platform views are not
3059  // added to flutter_view_.
3060 
3061  XCTAssertNotNil(gMockPlatformView);
3062  flutterPlatformViewsController->Reset();
3063  }
3064  XCTAssertNil(gMockPlatformView);
3065 }
3066 
3067 - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder {
3068  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3069 
3070  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3071  /*platform=*/GetDefaultTaskRunner(),
3072  /*raster=*/GetDefaultTaskRunner(),
3073  /*ui=*/GetDefaultTaskRunner(),
3074  /*io=*/GetDefaultTaskRunner());
3075  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
3076  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
3077  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3078  /*delegate=*/mock_delegate,
3079  /*rendering_api=*/mock_delegate.settings_.enable_impeller
3082  /*platform_views_controller=*/flutterPlatformViewsController,
3083  /*task_runners=*/runners,
3084  /*worker_task_runner=*/nil,
3085  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3086 
3087  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3088  flutterPlatformViewsController->SetFlutterView(flutterView);
3089 
3092  flutterPlatformViewsController->RegisterViewFactory(
3093  factory, @"MockFlutterPlatformView",
3095  FlutterResult result = ^(id result) {
3096  };
3097 
3098  flutterPlatformViewsController->OnMethodCall(
3100  methodCallWithMethodName:@"create"
3101  arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}],
3102  result);
3103 
3104  // First frame, |EmbeddedViewCount| is not empty after composite.
3105  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
3106  flutter::MutatorsStack stack;
3107  SkMatrix finalMatrix;
3108  auto embeddedViewParams1 =
3109  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3110  flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
3111  flutterPlatformViewsController->CompositeWithParams(
3112  0, flutterPlatformViewsController->GetCompositionParams(0));
3113 
3114  XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL);
3115 
3116  // Second frame, |EmbeddedViewCount| should be empty at the start
3117  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
3118  XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 0UL);
3119 
3120  auto embeddedViewParams2 =
3121  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3122  flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams2));
3123  flutterPlatformViewsController->CompositeWithParams(
3124  0, flutterPlatformViewsController->GetCompositionParams(0));
3125 
3126  XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL);
3127 }
3128 
3129 - (void)
3130  testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithDifferentViewHierarchy {
3131  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3132 
3133  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3134  /*platform=*/GetDefaultTaskRunner(),
3135  /*raster=*/GetDefaultTaskRunner(),
3136  /*ui=*/GetDefaultTaskRunner(),
3137  /*io=*/GetDefaultTaskRunner());
3138  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
3139  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
3140  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3141  /*delegate=*/mock_delegate,
3142  /*rendering_api=*/mock_delegate.settings_.enable_impeller
3145  /*platform_views_controller=*/flutterPlatformViewsController,
3146  /*task_runners=*/runners,
3147  /*worker_task_runner=*/nil,
3148  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3149 
3150  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3151  flutterPlatformViewsController->SetFlutterView(flutterView);
3152 
3155  flutterPlatformViewsController->RegisterViewFactory(
3156  factory, @"MockFlutterPlatformView",
3158  FlutterResult result = ^(id result) {
3159  };
3160  flutterPlatformViewsController->OnMethodCall(
3162  methodCallWithMethodName:@"create"
3163  arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}],
3164  result);
3165  UIView* view1 = gMockPlatformView;
3166 
3167  // This overwrites `gMockPlatformView` to another view.
3168  flutterPlatformViewsController->OnMethodCall(
3170  methodCallWithMethodName:@"create"
3171  arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
3172  result);
3173  UIView* view2 = gMockPlatformView;
3174 
3175  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
3176  flutter::MutatorsStack stack;
3177  SkMatrix finalMatrix;
3178  auto embeddedViewParams1 =
3179  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3180  flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
3181 
3182  auto embeddedViewParams2 =
3183  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(500, 500), stack);
3184  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2));
3185 
3186  // SKSurface is required if the root FlutterView is present.
3187  const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
3188  sk_sp<SkSurface> mock_sk_surface = SkSurfaces::Raster(image_info);
3189  flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3190  auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3191  std::move(mock_sk_surface), framebuffer_info,
3192  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3193  [](const flutter::SurfaceFrame& surface_frame) { return true; },
3194  /*frame_size=*/SkISize::Make(800, 600));
3195 
3196  XCTAssertTrue(
3197  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
3198 
3199  // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view.
3200  UIView* clippingView1 = view1.superview.superview;
3201  UIView* clippingView2 = view2.superview.superview;
3202  XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
3203  [flutterView.subviews indexOfObject:clippingView2],
3204  @"The first clipping view should be added before the second clipping view.");
3205 
3206  // Need to recreate these params since they are `std::move`ed.
3207  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
3208  // Process the second frame in the opposite order.
3209  embeddedViewParams2 =
3210  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(500, 500), stack);
3211  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2));
3212 
3213  embeddedViewParams1 =
3214  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3215  flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
3216 
3217  mock_sk_surface = SkSurfaces::Raster(image_info);
3218  mock_surface = std::make_unique<flutter::SurfaceFrame>(
3219  std::move(mock_sk_surface), framebuffer_info,
3220  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3221  [](const flutter::SurfaceFrame& surface_frame) { return true; },
3222  /*frame_size=*/SkISize::Make(800, 600));
3223  XCTAssertTrue(
3224  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
3225 
3226  XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] >
3227  [flutterView.subviews indexOfObject:clippingView2],
3228  @"The first clipping view should be added after the second clipping view.");
3229 }
3230 
3231 - (void)
3232  testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithSameViewHierarchy {
3233  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3234 
3235  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3236  /*platform=*/GetDefaultTaskRunner(),
3237  /*raster=*/GetDefaultTaskRunner(),
3238  /*ui=*/GetDefaultTaskRunner(),
3239  /*io=*/GetDefaultTaskRunner());
3240  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
3241  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
3242  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3243  /*delegate=*/mock_delegate,
3244  /*rendering_api=*/mock_delegate.settings_.enable_impeller
3247  /*platform_views_controller=*/flutterPlatformViewsController,
3248  /*task_runners=*/runners,
3249  /*worker_task_runner=*/nil,
3250  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3251 
3252  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3253  flutterPlatformViewsController->SetFlutterView(flutterView);
3254 
3257  flutterPlatformViewsController->RegisterViewFactory(
3258  factory, @"MockFlutterPlatformView",
3260  FlutterResult result = ^(id result) {
3261  };
3262  flutterPlatformViewsController->OnMethodCall(
3264  methodCallWithMethodName:@"create"
3265  arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}],
3266  result);
3267  UIView* view1 = gMockPlatformView;
3268 
3269  // This overwrites `gMockPlatformView` to another view.
3270  flutterPlatformViewsController->OnMethodCall(
3272  methodCallWithMethodName:@"create"
3273  arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
3274  result);
3275  UIView* view2 = gMockPlatformView;
3276 
3277  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
3278  flutter::MutatorsStack stack;
3279  SkMatrix finalMatrix;
3280  auto embeddedViewParams1 =
3281  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3282  flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
3283 
3284  auto embeddedViewParams2 =
3285  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(500, 500), stack);
3286  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2));
3287 
3288  // SKSurface is required if the root FlutterView is present.
3289  const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
3290  sk_sp<SkSurface> mock_sk_surface = SkSurfaces::Raster(image_info);
3291  flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3292  auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3293  std::move(mock_sk_surface), framebuffer_info,
3294  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3295  [](const flutter::SurfaceFrame& surface_frame) { return true; },
3296  /*frame_size=*/SkISize::Make(800, 600));
3297 
3298  XCTAssertTrue(
3299  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
3300 
3301  // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view.
3302  UIView* clippingView1 = view1.superview.superview;
3303  UIView* clippingView2 = view2.superview.superview;
3304  XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
3305  [flutterView.subviews indexOfObject:clippingView2],
3306  @"The first clipping view should be added before the second clipping view.");
3307 
3308  // Need to recreate these params since they are `std::move`ed.
3309  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
3310  // Process the second frame in the same order.
3311  embeddedViewParams1 =
3312  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3313  flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
3314 
3315  embeddedViewParams2 =
3316  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(500, 500), stack);
3317  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2));
3318 
3319  mock_sk_surface = SkSurfaces::Raster(image_info);
3320  mock_surface = std::make_unique<flutter::SurfaceFrame>(
3321  std::move(mock_sk_surface), framebuffer_info,
3322  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3323  [](const flutter::SurfaceFrame& surface_frame) { return true; },
3324  /*frame_size=*/SkISize::Make(800, 600));
3325  XCTAssertTrue(
3326  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
3327 
3328  XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
3329  [flutterView.subviews indexOfObject:clippingView2],
3330  @"The first clipping view should be added before the second clipping view.");
3331 }
3332 
3333 - (int)alphaOfPoint:(CGPoint)point onView:(UIView*)view {
3334  unsigned char pixel[4] = {0};
3335 
3336  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
3337 
3338  // Draw the pixel on `point` in the context.
3339  CGContextRef context = CGBitmapContextCreate(
3340  pixel, 1, 1, 8, 4, colorSpace, kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedLast);
3341  CGContextTranslateCTM(context, -point.x, -point.y);
3342  [view.layer renderInContext:context];
3343 
3344  CGContextRelease(context);
3345  CGColorSpaceRelease(colorSpace);
3346  // Get the alpha from the pixel that we just rendered.
3347  return pixel[3];
3348 }
3349 
3350 - (void)testHasFirstResponderInViewHierarchySubtree_viewItselfBecomesFirstResponder {
3351  // For view to become the first responder, it must be a descendant of a UIWindow
3352  UIWindow* window = [[UIWindow alloc] init];
3353  UITextField* textField = [[UITextField alloc] init];
3354  [window addSubview:textField];
3355 
3356  [textField becomeFirstResponder];
3357  XCTAssertTrue(textField.isFirstResponder);
3358  XCTAssertTrue(textField.flt_hasFirstResponderInViewHierarchySubtree);
3359  [textField resignFirstResponder];
3360  XCTAssertFalse(textField.isFirstResponder);
3361  XCTAssertFalse(textField.flt_hasFirstResponderInViewHierarchySubtree);
3362 }
3363 
3364 - (void)testHasFirstResponderInViewHierarchySubtree_descendantViewBecomesFirstResponder {
3365  // For view to become the first responder, it must be a descendant of a UIWindow
3366  UIWindow* window = [[UIWindow alloc] init];
3367  UIView* view = [[UIView alloc] init];
3368  UIView* childView = [[UIView alloc] init];
3369  UITextField* textField = [[UITextField alloc] init];
3370  [window addSubview:view];
3371  [view addSubview:childView];
3372  [childView addSubview:textField];
3373 
3374  [textField becomeFirstResponder];
3375  XCTAssertTrue(textField.isFirstResponder);
3376  XCTAssertTrue(view.flt_hasFirstResponderInViewHierarchySubtree);
3377  [textField resignFirstResponder];
3378  XCTAssertFalse(textField.isFirstResponder);
3379  XCTAssertFalse(view.flt_hasFirstResponderInViewHierarchySubtree);
3380 }
3381 
3382 - (void)testFlutterClippingMaskViewPoolReuseViewsAfterRecycle {
3383  FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2];
3384  FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero];
3385  FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero];
3386  [pool insertViewToPoolIfNeeded:view1];
3387  [pool insertViewToPoolIfNeeded:view2];
3388  CGRect newRect = CGRectMake(0, 0, 10, 10);
3389  FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:newRect];
3390  FlutterClippingMaskView* view4 = [pool getMaskViewWithFrame:newRect];
3391  // view3 and view4 should randomly get either of view1 and view2.
3392  NSSet* set1 = [NSSet setWithObjects:view1, view2, nil];
3393  NSSet* set2 = [NSSet setWithObjects:view3, view4, nil];
3394  XCTAssertEqualObjects(set1, set2);
3395  XCTAssertTrue(CGRectEqualToRect(view3.frame, newRect));
3396  XCTAssertTrue(CGRectEqualToRect(view4.frame, newRect));
3397 }
3398 
3399 - (void)testFlutterClippingMaskViewPoolAllocsNewMaskViewsAfterReachingCapacity {
3400  FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2];
3401  FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero];
3402  FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero];
3403  FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:CGRectZero];
3404  XCTAssertNotEqual(view1, view3);
3405  XCTAssertNotEqual(view2, view3);
3406 }
3407 
3408 - (void)testMaskViewsReleasedWhenPoolIsReleased {
3409  __weak UIView* weakView;
3410  @autoreleasepool {
3411  FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2];
3412  FlutterClippingMaskView* view = [pool getMaskViewWithFrame:CGRectZero];
3413  weakView = view;
3414  XCTAssertNotNil(weakView);
3415  }
3416  XCTAssertNil(weakView);
3417 }
3418 
3419 - (void)testClipMaskViewIsReused {
3420  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3421 
3422  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3423  /*platform=*/GetDefaultTaskRunner(),
3424  /*raster=*/GetDefaultTaskRunner(),
3425  /*ui=*/GetDefaultTaskRunner(),
3426  /*io=*/GetDefaultTaskRunner());
3427  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
3428  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
3429  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3430  /*delegate=*/mock_delegate,
3431  /*rendering_api=*/mock_delegate.settings_.enable_impeller
3434  /*platform_views_controller=*/flutterPlatformViewsController,
3435  /*task_runners=*/runners,
3436  /*worker_task_runner=*/nil,
3437  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3438 
3441  flutterPlatformViewsController->RegisterViewFactory(
3442  factory, @"MockFlutterPlatformView",
3444  FlutterResult result = ^(id result) {
3445  };
3446  flutterPlatformViewsController->OnMethodCall(
3448  methodCallWithMethodName:@"create"
3449  arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
3450  result);
3451 
3452  XCTAssertNotNil(gMockPlatformView);
3453  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
3454  flutterPlatformViewsController->SetFlutterView(flutterView);
3455  // Create embedded view params
3456  flutter::MutatorsStack stack1;
3457  // Layer tree always pushes a screen scale factor to the stack
3458  SkMatrix screenScaleMatrix =
3459  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
3460  stack1.PushTransform(screenScaleMatrix);
3461  // Push a clip rect
3462  SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3);
3463  stack1.PushClipRect(rect);
3464 
3465  auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
3466  screenScaleMatrix, SkSize::Make(10, 10), stack1);
3467 
3468  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1));
3469  flutterPlatformViewsController->CompositeWithParams(
3470  1, flutterPlatformViewsController->GetCompositionParams(1));
3471 
3472  UIView* childClippingView1 = gMockPlatformView.superview.superview;
3473  UIView* maskView1 = childClippingView1.maskView;
3474  XCTAssertNotNil(maskView1);
3475 
3476  // Composite a new frame.
3477  flutterPlatformViewsController->BeginFrame(SkISize::Make(100, 100));
3478  flutter::MutatorsStack stack2;
3479  auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
3480  screenScaleMatrix, SkSize::Make(10, 10), stack2);
3481  auto embeddedViewParams3 = std::make_unique<flutter::EmbeddedViewParams>(
3482  screenScaleMatrix, SkSize::Make(10, 10), stack2);
3483  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams3));
3484  flutterPlatformViewsController->CompositeWithParams(
3485  1, flutterPlatformViewsController->GetCompositionParams(1));
3486 
3487  childClippingView1 = gMockPlatformView.superview.superview;
3488 
3489  // This overrides gMockPlatformView to point to the newly created platform view.
3490  flutterPlatformViewsController->OnMethodCall(
3492  methodCallWithMethodName:@"create"
3493  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
3494  result);
3495 
3496  auto embeddedViewParams4 = std::make_unique<flutter::EmbeddedViewParams>(
3497  screenScaleMatrix, SkSize::Make(10, 10), stack1);
3498  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams4));
3499  flutterPlatformViewsController->CompositeWithParams(
3500  2, flutterPlatformViewsController->GetCompositionParams(2));
3501 
3502  UIView* childClippingView2 = gMockPlatformView.superview.superview;
3503 
3504  UIView* maskView2 = childClippingView2.maskView;
3505  XCTAssertEqual(maskView1, maskView2);
3506  XCTAssertNotNil(childClippingView2.maskView);
3507  XCTAssertNil(childClippingView1.maskView);
3508 }
3509 
3510 - (void)testDifferentClipMaskViewIsUsedForEachView {
3511  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3512 
3513  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3514  /*platform=*/GetDefaultTaskRunner(),
3515  /*raster=*/GetDefaultTaskRunner(),
3516  /*ui=*/GetDefaultTaskRunner(),
3517  /*io=*/GetDefaultTaskRunner());
3518  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
3519  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
3520  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3521  /*delegate=*/mock_delegate,
3522  /*rendering_api=*/mock_delegate.settings_.enable_impeller
3525  /*platform_views_controller=*/flutterPlatformViewsController,
3526  /*task_runners=*/runners,
3527  /*worker_task_runner=*/nil,
3528  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3529 
3532  flutterPlatformViewsController->RegisterViewFactory(
3533  factory, @"MockFlutterPlatformView",
3535  FlutterResult result = ^(id result) {
3536  };
3537 
3538  flutterPlatformViewsController->OnMethodCall(
3540  methodCallWithMethodName:@"create"
3541  arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
3542  result);
3543  UIView* view1 = gMockPlatformView;
3544 
3545  // This overwrites `gMockPlatformView` to another view.
3546  flutterPlatformViewsController->OnMethodCall(
3548  methodCallWithMethodName:@"create"
3549  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
3550  result);
3551  UIView* view2 = gMockPlatformView;
3552 
3553  XCTAssertNotNil(gMockPlatformView);
3554  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
3555  flutterPlatformViewsController->SetFlutterView(flutterView);
3556  // Create embedded view params
3557  flutter::MutatorsStack stack1;
3558  // Layer tree always pushes a screen scale factor to the stack
3559  SkMatrix screenScaleMatrix =
3560  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
3561  stack1.PushTransform(screenScaleMatrix);
3562  // Push a clip rect
3563  SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3);
3564  stack1.PushClipRect(rect);
3565 
3566  auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
3567  screenScaleMatrix, SkSize::Make(10, 10), stack1);
3568 
3569  flutter::MutatorsStack stack2;
3570  stack2.PushClipRect(rect);
3571  auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
3572  screenScaleMatrix, SkSize::Make(10, 10), stack2);
3573 
3574  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1));
3575  flutterPlatformViewsController->CompositeWithParams(
3576  1, flutterPlatformViewsController->GetCompositionParams(1));
3577 
3578  UIView* childClippingView1 = view1.superview.superview;
3579 
3580  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2));
3581  flutterPlatformViewsController->CompositeWithParams(
3582  2, flutterPlatformViewsController->GetCompositionParams(2));
3583 
3584  UIView* childClippingView2 = view2.superview.superview;
3585  UIView* maskView1 = childClippingView1.maskView;
3586  UIView* maskView2 = childClippingView2.maskView;
3587  XCTAssertNotEqual(maskView1, maskView2);
3588 }
3589 
3590 - (void)testMaskViewUsesCAShapeLayerAsTheBackingLayer {
3591  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3592 
3593  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3594  /*platform=*/GetDefaultTaskRunner(),
3595  /*raster=*/GetDefaultTaskRunner(),
3596  /*ui=*/GetDefaultTaskRunner(),
3597  /*io=*/GetDefaultTaskRunner());
3598  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
3599  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
3600  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3601  /*delegate=*/mock_delegate,
3602  /*rendering_api=*/mock_delegate.settings_.enable_impeller
3605  /*platform_views_controller=*/flutterPlatformViewsController,
3606  /*task_runners=*/runners,
3607  /*worker_task_runner=*/nil,
3608  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3609 
3612  flutterPlatformViewsController->RegisterViewFactory(
3613  factory, @"MockFlutterPlatformView",
3615  FlutterResult result = ^(id result) {
3616  };
3617 
3618  flutterPlatformViewsController->OnMethodCall(
3620  methodCallWithMethodName:@"create"
3621  arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
3622  result);
3623 
3624  XCTAssertNotNil(gMockPlatformView);
3625  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
3626  flutterPlatformViewsController->SetFlutterView(flutterView);
3627  // Create embedded view params
3628  flutter::MutatorsStack stack1;
3629  // Layer tree always pushes a screen scale factor to the stack
3630  SkMatrix screenScaleMatrix =
3631  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
3632  stack1.PushTransform(screenScaleMatrix);
3633  // Push a clip rect
3634  SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3);
3635  stack1.PushClipRect(rect);
3636 
3637  auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
3638  screenScaleMatrix, SkSize::Make(10, 10), stack1);
3639 
3640  flutter::MutatorsStack stack2;
3641  stack2.PushClipRect(rect);
3642  auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
3643  screenScaleMatrix, SkSize::Make(10, 10), stack2);
3644 
3645  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1));
3646  flutterPlatformViewsController->CompositeWithParams(
3647  1, flutterPlatformViewsController->GetCompositionParams(1));
3648 
3649  UIView* childClippingView = gMockPlatformView.superview.superview;
3650 
3651  UIView* maskView = childClippingView.maskView;
3652  XCTAssert([maskView.layer isKindOfClass:[CAShapeLayer class]],
3653  @"Mask view must use CAShapeLayer as its backing layer.");
3654 }
3655 
3656 // Return true if a correct visual effect view is found. It also implies all the validation in this
3657 // method passes.
3658 //
3659 // There are two fail states for this method. 1. One of the XCTAssert method failed; or 2. No
3660 // correct visual effect view found.
3661 - (BOOL)validateOneVisualEffectView:(UIView*)visualEffectView
3662  expectedFrame:(CGRect)frame
3663  inputRadius:(CGFloat)inputRadius {
3664  XCTAssertTrue(CGRectEqualToRect(visualEffectView.frame, frame));
3665  for (UIView* view in visualEffectView.subviews) {
3666  if (![NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) {
3667  continue;
3668  }
3669  XCTAssertEqual(view.layer.filters.count, 1u);
3670  NSObject* filter = view.layer.filters.firstObject;
3671 
3672  XCTAssertEqualObjects([filter valueForKey:@"name"], @"gaussianBlur");
3673 
3674  NSObject* inputRadiusInFilter = [filter valueForKey:@"inputRadius"];
3675  XCTAssertTrue([inputRadiusInFilter isKindOfClass:[NSNumber class]] &&
3676  flutter::BlurRadiusEqualToBlurRadius(((NSNumber*)inputRadiusInFilter).floatValue,
3677  inputRadius));
3678  return YES;
3679  }
3680  return NO;
3681 }
3682 
3683 - (void)testDisposingViewInCompositionOrderDoNotCrash {
3684  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3685 
3686  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3687  /*platform=*/GetDefaultTaskRunner(),
3688  /*raster=*/GetDefaultTaskRunner(),
3689  /*ui=*/GetDefaultTaskRunner(),
3690  /*io=*/GetDefaultTaskRunner());
3691  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
3692  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
3693  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3694  /*delegate=*/mock_delegate,
3695  /*rendering_api=*/mock_delegate.settings_.enable_impeller
3698  /*platform_views_controller=*/flutterPlatformViewsController,
3699  /*task_runners=*/runners,
3700  /*worker_task_runner=*/nil,
3701  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3702 
3703  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3704  flutterPlatformViewsController->SetFlutterView(flutterView);
3705 
3708  flutterPlatformViewsController->RegisterViewFactory(
3709  factory, @"MockFlutterPlatformView",
3711  FlutterResult result = ^(id result) {
3712  };
3713 
3714  flutterPlatformViewsController->OnMethodCall(
3716  methodCallWithMethodName:@"create"
3717  arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}],
3718  result);
3719  flutterPlatformViewsController->OnMethodCall(
3721  methodCallWithMethodName:@"create"
3722  arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
3723  result);
3724 
3725  {
3726  // **** First frame, view id 0, 1 in the composition_order_, disposing view 0 is called. **** //
3727  // No view should be disposed, or removed from the composition order.
3728  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
3729  flutter::MutatorsStack stack;
3730  SkMatrix finalMatrix;
3731  auto embeddedViewParams0 =
3732  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3733  flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams0));
3734 
3735  auto embeddedViewParams1 =
3736  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3737  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1));
3738 
3739  XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL);
3740 
3741  XCTestExpectation* expectation = [self expectationWithDescription:@"dispose call ended."];
3742  FlutterResult disposeResult = ^(id result) {
3743  [expectation fulfill];
3744  };
3745 
3746  flutterPlatformViewsController->OnMethodCall(
3747  [FlutterMethodCall methodCallWithMethodName:@"dispose" arguments:@0], disposeResult);
3748  [self waitForExpectationsWithTimeout:30 handler:nil];
3749 
3750  const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
3751  sk_sp<SkSurface> mock_sk_surface = SkSurfaces::Raster(image_info);
3752  flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3753  auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3754  std::move(mock_sk_surface), framebuffer_info,
3755  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3756  [](const flutter::SurfaceFrame& surface_frame) { return true; },
3757  /*frame_size=*/SkISize::Make(800, 600));
3758  XCTAssertTrue(
3759  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
3760 
3761  // Disposing won't remove embedded views until the view is removed from the composition_order_
3762  XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL);
3763  XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(0));
3764  XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(1));
3765  }
3766 
3767  {
3768  // **** Second frame, view id 1 in the composition_order_, no disposing view is called, **** //
3769  // View 0 is removed from the composition order in this frame, hence also disposed.
3770  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
3771  flutter::MutatorsStack stack;
3772  SkMatrix finalMatrix;
3773  auto embeddedViewParams1 =
3774  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3775  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1));
3776 
3777  const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
3778  sk_sp<SkSurface> mock_sk_surface = SkSurfaces::Raster(image_info);
3779  flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3780  auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3781  std::move(mock_sk_surface), framebuffer_info,
3782  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3783  [](const flutter::SurfaceFrame& surface_frame) { return true; },
3784  /*frame_size=*/SkISize::Make(800, 600));
3785  XCTAssertTrue(
3786  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
3787 
3788  // Disposing won't remove embedded views until the view is removed from the composition_order_
3789  XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL);
3790  XCTAssertNil(flutterPlatformViewsController->GetPlatformViewByID(0));
3791  XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(1));
3792  }
3793 }
3794 - (void)testOnlyPlatformViewsAreRemovedWhenReset {
3795  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3796 
3797  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3798  /*platform=*/GetDefaultTaskRunner(),
3799  /*raster=*/GetDefaultTaskRunner(),
3800  /*ui=*/GetDefaultTaskRunner(),
3801  /*io=*/GetDefaultTaskRunner());
3802  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
3803  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
3804  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3805  /*delegate=*/mock_delegate,
3806  /*rendering_api=*/mock_delegate.settings_.enable_impeller
3809  /*platform_views_controller=*/flutterPlatformViewsController,
3810  /*task_runners=*/runners,
3811  /*worker_task_runner=*/nil,
3812  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3813 
3816  flutterPlatformViewsController->RegisterViewFactory(
3817  factory, @"MockFlutterPlatformView",
3819  FlutterResult result = ^(id result) {
3820  };
3821  flutterPlatformViewsController->OnMethodCall(
3823  methodCallWithMethodName:@"create"
3824  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
3825  result);
3826  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3827  flutterPlatformViewsController->SetFlutterView(flutterView);
3828  // Create embedded view params
3829  flutter::MutatorsStack stack;
3830  // Layer tree always pushes a screen scale factor to the stack
3831  SkMatrix screenScaleMatrix =
3832  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
3833  stack.PushTransform(screenScaleMatrix);
3834  // Push a translate matrix
3835  SkMatrix translateMatrix = SkMatrix::Translate(100, 100);
3836  stack.PushTransform(translateMatrix);
3837  SkMatrix finalMatrix;
3838  finalMatrix.setConcat(screenScaleMatrix, translateMatrix);
3839 
3840  auto embeddedViewParams =
3841  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3842 
3843  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
3844 
3845  // SKSurface is required if the root FlutterView is present.
3846  const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
3847  sk_sp<SkSurface> mock_sk_surface = SkSurfaces::Raster(image_info);
3848  flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3849  auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3850  std::move(mock_sk_surface), framebuffer_info,
3851  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3852  [](const flutter::SurfaceFrame& surface_frame) { return true; },
3853  /*frame_size=*/SkISize::Make(800, 600));
3854 
3855  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface));
3856 
3857  UIView* someView = [[UIView alloc] init];
3858  [flutterView addSubview:someView];
3859 
3860  flutterPlatformViewsController->Reset();
3861  XCTAssertEqual(flutterView.subviews.count, 1u);
3862  XCTAssertEqual(flutterView.subviews.firstObject, someView);
3863 }
3864 
3865 - (void)testNilPlatformViewDoesntCrash {
3866  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3867 
3868  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3869  /*platform=*/GetDefaultTaskRunner(),
3870  /*raster=*/GetDefaultTaskRunner(),
3871  /*ui=*/GetDefaultTaskRunner(),
3872  /*io=*/GetDefaultTaskRunner());
3873  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
3874  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
3875  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3876  /*delegate=*/mock_delegate,
3877  /*rendering_api=*/mock_delegate.settings_.enable_impeller
3880  /*platform_views_controller=*/flutterPlatformViewsController,
3881  /*task_runners=*/runners,
3882  /*worker_task_runner=*/nil,
3883  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3884 
3887  flutterPlatformViewsController->RegisterViewFactory(
3888  factory, @"MockFlutterPlatformView",
3890  FlutterResult result = ^(id result) {
3891  };
3892  flutterPlatformViewsController->OnMethodCall(
3894  methodCallWithMethodName:@"create"
3895  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
3896  result);
3897  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3898  flutterPlatformViewsController->SetFlutterView(flutterView);
3899 
3900  // Create embedded view params
3901  flutter::MutatorsStack stack;
3902  // Layer tree always pushes a screen scale factor to the stack
3903  SkMatrix screenScaleMatrix =
3904  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
3905  stack.PushTransform(screenScaleMatrix);
3906  // Push a translate matrix
3907  SkMatrix translateMatrix = SkMatrix::Translate(100, 100);
3908  stack.PushTransform(translateMatrix);
3909  SkMatrix finalMatrix;
3910  finalMatrix.setConcat(screenScaleMatrix, translateMatrix);
3911 
3912  auto embeddedViewParams =
3913  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3914 
3915  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
3916 
3917  // SKSurface is required if the root FlutterView is present.
3918  const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
3919  sk_sp<SkSurface> mock_sk_surface = SkSurfaces::Raster(image_info);
3920  flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3921  auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3922  std::move(mock_sk_surface), framebuffer_info,
3923  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3924  [](const flutter::SurfaceFrame& surface_frame) { return true; },
3925  /*frame_size=*/SkISize::Make(800, 600));
3926 
3927  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface));
3928 
3929  XCTAssertEqual(flutterView.subviews.count, 1u);
3930 }
3931 
3932 - (void)testFlutterTouchInterceptingViewLinksToAccessibilityContainer {
3933  FlutterTouchInterceptingView* touchInteceptorView = [[FlutterTouchInterceptingView alloc] init];
3934  NSObject* container = [[NSObject alloc] init];
3935  [touchInteceptorView setFlutterAccessibilityContainer:container];
3936  XCTAssertEqualObjects([touchInteceptorView accessibilityContainer], container);
3937 }
3938 
3939 - (void)testLayerPool {
3940  // Create an IOSContext and GrDirectContext.
3941  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar"];
3942  [engine run];
3943  XCTAssertTrue([engine iosPlatformView] != nullptr);
3944  auto ios_context = [engine iosPlatformView]->GetIosContext();
3945  auto gr_context = ios_context->GetMainContext();
3946 
3947  auto pool = flutter::OverlayLayerPool{};
3948 
3949  // Add layers to the pool.
3950  pool.CreateLayer(gr_context.get(), ios_context, MTLPixelFormatBGRA8Unorm);
3951  XCTAssertEqual(pool.size(), 1u);
3952  pool.CreateLayer(gr_context.get(), ios_context, MTLPixelFormatBGRA8Unorm);
3953  XCTAssertEqual(pool.size(), 2u);
3954 
3955  // Mark all layers as unused.
3956  pool.RecycleLayers();
3957  XCTAssertEqual(pool.size(), 2u);
3958 
3959  // Free the unused layers. One should remain.
3960  auto unused_layers = pool.RemoveUnusedLayers();
3961  XCTAssertEqual(unused_layers.size(), 2u);
3962  XCTAssertEqual(pool.size(), 1u);
3963 }
3964 
3965 - (void)testFlutterPlatformViewControllerSubmitFramePreservingFrameDamage {
3966  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3967 
3968  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3969  /*platform=*/GetDefaultTaskRunner(),
3970  /*raster=*/GetDefaultTaskRunner(),
3971  /*ui=*/GetDefaultTaskRunner(),
3972  /*io=*/GetDefaultTaskRunner());
3973  auto flutterPlatformViewsController = std::make_shared<flutter::PlatformViewsController>();
3974  flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner());
3975  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3976  /*delegate=*/mock_delegate,
3977  /*rendering_api=*/mock_delegate.settings_.enable_impeller
3980  /*platform_views_controller=*/flutterPlatformViewsController,
3981  /*task_runners=*/runners,
3982  /*worker_task_runner=*/nil,
3983  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3984 
3985  UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3986  flutterPlatformViewsController->SetFlutterView(flutterView);
3987 
3990  flutterPlatformViewsController->RegisterViewFactory(
3991  factory, @"MockFlutterPlatformView",
3993  FlutterResult result = ^(id result) {
3994  };
3995  flutterPlatformViewsController->OnMethodCall(
3997  methodCallWithMethodName:@"create"
3998  arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}],
3999  result);
4000 
4001  // This overwrites `gMockPlatformView` to another view.
4002  flutterPlatformViewsController->OnMethodCall(
4004  methodCallWithMethodName:@"create"
4005  arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
4006  result);
4007 
4008  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
4009  flutter::MutatorsStack stack;
4010  SkMatrix finalMatrix;
4011  auto embeddedViewParams1 =
4012  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
4013  flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
4014 
4015  auto embeddedViewParams2 =
4016  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(500, 500), stack);
4017  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2));
4018 
4019  // SKSurface is required if the root FlutterView is present.
4020  const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
4021  sk_sp<SkSurface> mock_sk_surface = SkSurfaces::Raster(image_info);
4022  flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
4023  std::optional<flutter::SurfaceFrame::SubmitInfo> submit_info;
4024  auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
4025  std::move(mock_sk_surface), framebuffer_info,
4026  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
4027  [&](const flutter::SurfaceFrame& surface_frame) {
4028  submit_info = surface_frame.submit_info();
4029  return true;
4030  },
4031  /*frame_size=*/SkISize::Make(800, 600));
4032  mock_surface->set_submit_info({
4033  .frame_damage = SkIRect::MakeWH(800, 600),
4034  .buffer_damage = SkIRect::MakeWH(400, 600),
4035  });
4036 
4037  XCTAssertTrue(
4038  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
4039 
4040  XCTAssertTrue(submit_info.has_value());
4041  XCTAssertEqual(*submit_info->frame_damage, SkIRect::MakeWH(800, 600));
4042  XCTAssertEqual(*submit_info->buffer_damage, SkIRect::MakeWH(400, 600));
4043 }
4044 
4045 @end
FlutterPlatformViewsTestMockWebView
Definition: FlutterPlatformViewsTest.mm:87
FlutterEngine
Definition: FlutterEngine.h:61
FlutterPlatformViews.h
platform_views_controller.h
FlutterDelayingGestureRecognizer
Definition: FlutterPlatformViews_Internal.h:169
FlutterViewController
Definition: FlutterViewController.h:57
FlutterPlatformViewsTestMockFlutterPlatformView::viewCreated
BOOL viewCreated
Definition: FlutterPlatformViewsTest.mm:47
FlutterPlatformViewsTestMockWebView::viewCreated
BOOL viewCreated
Definition: FlutterPlatformViewsTest.mm:89
FlutterPlatformViewsTestMockPlatformView
Definition: FlutterPlatformViewsTest.mm:27
FlutterEngine_Internal.h
ForwardingGestureRecognizer
Definition: FlutterPlatformViews_Internal.h:196
flutter::OverlayLayerPool::CreateLayer
void CreateLayer(GrDirectContext *gr_context, const std::shared_ptr< IOSContext > &ios_context, MTLPixelFormat pixel_format)
Create a new overlay layer.
Definition: overlay_layer_pool.mm:57
-[ChildClippingView applyBlurBackdropFilters:]
void applyBlurBackdropFilters:(NSArray< PlatformViewFilter * > *filters)
Definition: FlutterPlatformViews_Internal.mm:157
FlutterMacros.h
platform_view
std::unique_ptr< flutter::PlatformViewIOS > platform_view
Definition: FlutterEnginePlatformViewTest.mm:65
-[ChildClippingView backdropFilterSubviews]
NSMutableArray * backdropFilterSubviews()
Definition: FlutterPlatformViews_Internal.mm:181
FlutterPlatformViewsTestMockFlutterPlatformFactory
Definition: FlutterPlatformViewsTest.mm:74
gMockPlatformView
static __weak UIView * gMockPlatformView
Definition: FlutterPlatformViewsTest.mm:24
FlutterMethodCall
Definition: FlutterCodecs.h:220
FlutterPlatformViewsTest
Definition: FlutterPlatformViewsTest.mm:185
FlutterPlatformViewGestureRecognizersBlockingPolicyEager
@ FlutterPlatformViewGestureRecognizersBlockingPolicyEager
Definition: FlutterPlugin.h:261
flutter
Definition: accessibility_bridge.h:28
FlutterPlatformViewsTestMockWebView::view
UIView * view
Definition: FlutterPlatformViewsTest.mm:88
FlutterPlatformViews_Internal.h
FlutterPlatformViewsTestMockFlutterPlatformView::view
UIView * view
Definition: FlutterPlatformViewsTest.mm:46
settings_
flutter::Settings settings_
Definition: FlutterEnginePlatformViewTest.mm:55
FlutterResult
void(^ FlutterResult)(id _Nullable result)
Definition: FlutterChannels.h:194
FlutterClippingMaskViewPool
Definition: FlutterPlatformViews_Internal.h:66
-[FlutterClippingMaskViewPool insertViewToPoolIfNeeded:]
void insertViewToPoolIfNeeded:(FlutterClippingMaskView *maskView)
flutter::IOSRenderingAPI::kMetal
@ kMetal
-[FlutterClippingMaskViewPool getMaskViewWithFrame:]
FlutterClippingMaskView * getMaskViewWithFrame:(CGRect frame)
FlutterPlatformViewFactory-p
Definition: FlutterPlatformViews.h:26
ChildClippingView
Definition: FlutterPlatformViews_Internal.h:123
engine
id engine
Definition: FlutterTextInputPluginTest.mm:89
FlutterPlatformViewsTestNilFlutterPlatformFactory
Definition: FlutterPlatformViewsTest.mm:126
+[PlatformViewFilter resetPreparation]
void resetPreparation()
Definition: FlutterPlatformViews_Internal.mm:78
FlutterPlatformViewsTestMockWebViewFactory
Definition: FlutterPlatformViewsTest.mm:115
platform_view_ios.h
FlutterTouchInterceptingView_Test.h
FlutterPlatformView-p
Definition: FlutterPlatformViews.h:18
PlatformViewFilter
Definition: FlutterPlatformViews_Internal.h:84
texture_id
int64_t texture_id
Definition: texture_registrar_unittests.cc:24
-[FlutterEngine run]
BOOL run()
Definition: FlutterEngine.mm:940
flutter::IOSRenderingAPI::kSoftware
@ kSoftware
FLUTTER_ASSERT_ARC
Definition: FlutterChannelKeyResponder.mm:13
FlutterPlatformViewsTestMockFlutterPlatformView
Definition: FlutterPlatformViewsTest.mm:45
FlutterTouchInterceptingView
Definition: FlutterPlatformViews_Internal.h:138
FlutterViewController.h
kFloatCompareEpsilon
const float kFloatCompareEpsilon
Definition: FlutterPlatformViewsTest.mm:25
flutter::OverlayLayerPool
Storage for Overlay layers across frames.
Definition: overlay_layer_pool.h:53
FlutterClippingMaskView
Definition: FlutterPlatformViews_Internal.h:35