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 <XCTest/XCTest.h>
8 
17 
19 
22 const float kFloatCompareEpsilon = 0.001;
23 
25 @end
27 
28 - (instancetype)init {
29  self = [super init];
30  if (self) {
31  gMockPlatformView = self;
32  }
33  return self;
34 }
35 
36 - (void)dealloc {
37  gMockPlatformView = nil;
38 }
39 
40 @end
41 
43 @property(nonatomic, strong) UIView* view;
44 @property(nonatomic, assign) BOOL viewCreated;
45 @end
46 
48 
49 - (instancetype)init {
50  if (self = [super init]) {
51  _view = [[FlutterPlatformViewsTestMockPlatformView alloc] init];
52  _viewCreated = NO;
53  }
54  return self;
55 }
56 
57 - (UIView*)view {
58  [self checkViewCreatedOnce];
59  return _view;
60 }
61 
62 - (void)checkViewCreatedOnce {
63  if (self.viewCreated) {
64  abort();
65  }
66  self.viewCreated = YES;
67 }
68 
69 @end
70 
72  : NSObject <FlutterPlatformViewFactory>
73 @end
74 
76 - (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
77  viewIdentifier:(int64_t)viewId
78  arguments:(id _Nullable)args {
80 }
81 
82 @end
83 
84 namespace flutter {
85 namespace {
86 class FlutterPlatformViewsTestMockPlatformViewDelegate : public PlatformView::Delegate {
87  public:
88  void OnPlatformViewCreated(std::unique_ptr<Surface> surface) override {}
89  void OnPlatformViewDestroyed() override {}
90  void OnPlatformViewScheduleFrame() override {}
91  void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {}
92  void OnPlatformViewSetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics) override {}
93  const flutter::Settings& OnPlatformViewGetSettings() const override { return settings_; }
94  void OnPlatformViewDispatchPlatformMessage(std::unique_ptr<PlatformMessage> message) override {}
95  void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr<PointerDataPacket> packet) override {
96  }
97  void OnPlatformViewDispatchSemanticsAction(int32_t id,
98  SemanticsAction action,
99  fml::MallocMapping args) override {}
100  void OnPlatformViewSetSemanticsEnabled(bool enabled) override {}
101  void OnPlatformViewSetAccessibilityFeatures(int32_t flags) override {}
102  void OnPlatformViewRegisterTexture(std::shared_ptr<Texture> texture) override {}
103  void OnPlatformViewUnregisterTexture(int64_t texture_id) override {}
104  void OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) override {}
105 
106  void LoadDartDeferredLibrary(intptr_t loading_unit_id,
107  std::unique_ptr<const fml::Mapping> snapshot_data,
108  std::unique_ptr<const fml::Mapping> snapshot_instructions) override {
109  }
110  void LoadDartDeferredLibraryError(intptr_t loading_unit_id,
111  const std::string error_message,
112  bool transient) override {}
113  void UpdateAssetResolverByType(std::unique_ptr<flutter::AssetResolver> updated_asset_resolver,
114  flutter::AssetResolver::AssetResolverType type) override {}
115 
116  flutter::Settings settings_;
117 };
118 
119 } // namespace
120 } // namespace flutter
121 
122 namespace {
123 fml::RefPtr<fml::TaskRunner> CreateNewThread(const std::string& name) {
124  auto thread = std::make_unique<fml::Thread>(name);
125  auto runner = thread->GetTaskRunner();
126  return runner;
127 }
128 } // namespace
129 
130 @interface FlutterPlatformViewsTest : XCTestCase
131 @end
132 
133 @implementation FlutterPlatformViewsTest
134 
135 - (void)testFlutterViewOnlyCreateOnceInOneFrame {
136  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
137  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
138  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
139  /*platform=*/thread_task_runner,
140  /*raster=*/thread_task_runner,
141  /*ui=*/thread_task_runner,
142  /*io=*/thread_task_runner);
143  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
144  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
145  /*delegate=*/mock_delegate,
146  /*rendering_api=*/mock_delegate.settings_.enable_impeller
149  /*platform_views_controller=*/flutterPlatformViewsController,
150  /*task_runners=*/runners,
151  /*worker_task_runner=*/nil,
152  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
153 
156  flutterPlatformViewsController->RegisterViewFactory(
157  factory, @"MockFlutterPlatformView",
159  FlutterResult result = ^(id result) {
160  };
161  flutterPlatformViewsController->OnMethodCall(
163  methodCallWithMethodName:@"create"
164  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
165  result);
166  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
167  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
168  // Create embedded view params
169  flutter::MutatorsStack stack;
170  // Layer tree always pushes a screen scale factor to the stack
171  SkMatrix screenScaleMatrix =
172  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
173  stack.PushTransform(screenScaleMatrix);
174  // Push a translate matrix
175  SkMatrix translateMatrix = SkMatrix::Translate(100, 100);
176  stack.PushTransform(translateMatrix);
177  SkMatrix finalMatrix;
178  finalMatrix.setConcat(screenScaleMatrix, translateMatrix);
179 
180  auto embeddedViewParams =
181  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
182 
183  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
184  flutterPlatformViewsController->CompositeEmbeddedView(2);
185 
186  flutterPlatformViewsController->GetPlatformViewRect(2);
187 
188  XCTAssertNotNil(gMockPlatformView);
189 
190  flutterPlatformViewsController->Reset();
191 }
192 
193 - (void)testCanCreatePlatformViewWithoutFlutterView {
194  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
195  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
196  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
197  /*platform=*/thread_task_runner,
198  /*raster=*/thread_task_runner,
199  /*ui=*/thread_task_runner,
200  /*io=*/thread_task_runner);
201  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
202  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
203  /*delegate=*/mock_delegate,
204  /*rendering_api=*/mock_delegate.settings_.enable_impeller
207  /*platform_views_controller=*/flutterPlatformViewsController,
208  /*task_runners=*/runners,
209  /*worker_task_runner=*/nil,
210  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
211 
214  flutterPlatformViewsController->RegisterViewFactory(
215  factory, @"MockFlutterPlatformView",
217  FlutterResult result = ^(id result) {
218  };
219  flutterPlatformViewsController->OnMethodCall(
221  methodCallWithMethodName:@"create"
222  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
223  result);
224 
225  XCTAssertNotNil(gMockPlatformView);
226 }
227 
228 - (void)testChildClippingViewHitTests {
229  ChildClippingView* childClippingView =
230  [[ChildClippingView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
231  UIView* childView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
232  [childClippingView addSubview:childView];
233 
234  XCTAssertFalse([childClippingView pointInside:CGPointMake(50, 50) withEvent:nil]);
235  XCTAssertFalse([childClippingView pointInside:CGPointMake(99, 100) withEvent:nil]);
236  XCTAssertFalse([childClippingView pointInside:CGPointMake(100, 99) withEvent:nil]);
237  XCTAssertFalse([childClippingView pointInside:CGPointMake(201, 200) withEvent:nil]);
238  XCTAssertFalse([childClippingView pointInside:CGPointMake(200, 201) withEvent:nil]);
239  XCTAssertFalse([childClippingView pointInside:CGPointMake(99, 200) withEvent:nil]);
240  XCTAssertFalse([childClippingView pointInside:CGPointMake(200, 299) withEvent:nil]);
241 
242  XCTAssertTrue([childClippingView pointInside:CGPointMake(150, 150) withEvent:nil]);
243  XCTAssertTrue([childClippingView pointInside:CGPointMake(100, 100) withEvent:nil]);
244  XCTAssertTrue([childClippingView pointInside:CGPointMake(199, 100) withEvent:nil]);
245  XCTAssertTrue([childClippingView pointInside:CGPointMake(100, 199) withEvent:nil]);
246  XCTAssertTrue([childClippingView pointInside:CGPointMake(199, 199) withEvent:nil]);
247 }
248 
249 - (void)testReleasesBackdropFilterSubviewsOnChildClippingViewDealloc {
250  __weak NSMutableArray<UIVisualEffectView*>* weakBackdropFilterSubviews = nil;
251  @autoreleasepool {
252  ChildClippingView* clipping_view = [[ChildClippingView alloc] initWithFrame:CGRectZero];
253  weakBackdropFilterSubviews = clipping_view.backdropFilterSubviews;
254  XCTAssertNotNil(weakBackdropFilterSubviews);
255  clipping_view = nil;
256  }
257  XCTAssertNil(weakBackdropFilterSubviews);
258 }
259 
260 - (void)testApplyBackdropFilter {
261  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
262  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
263  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
264  /*platform=*/thread_task_runner,
265  /*raster=*/thread_task_runner,
266  /*ui=*/thread_task_runner,
267  /*io=*/thread_task_runner);
268  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
269  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
270  /*delegate=*/mock_delegate,
271  /*rendering_api=*/mock_delegate.settings_.enable_impeller
274  /*platform_views_controller=*/flutterPlatformViewsController,
275  /*task_runners=*/runners,
276  /*worker_task_runner=*/nil,
277  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
278 
281  flutterPlatformViewsController->RegisterViewFactory(
282  factory, @"MockFlutterPlatformView",
284  FlutterResult result = ^(id result) {
285  };
286  flutterPlatformViewsController->OnMethodCall(
288  methodCallWithMethodName:@"create"
289  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
290  result);
291 
292  XCTAssertNotNil(gMockPlatformView);
293 
294  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
295  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
296  // Create embedded view params
297  flutter::MutatorsStack stack;
298  // Layer tree always pushes a screen scale factor to the stack
299  CGFloat screenScale = [UIScreen mainScreen].scale;
300  SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale);
301  stack.PushTransform(screenScaleMatrix);
302  // Push a backdrop filter
303  auto filter = std::make_shared<flutter::DlBlurImageFilter>(5, 2, flutter::DlTileMode::kClamp);
304  stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
305 
306  auto embeddedViewParams =
307  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
308 
309  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
310  flutterPlatformViewsController->CompositeEmbeddedView(2);
311  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]);
312  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
313  [mockFlutterView addSubview:childClippingView];
314 
315  [mockFlutterView setNeedsLayout];
316  [mockFlutterView layoutIfNeeded];
317 
318  // childClippingView has visual effect view with the correct configurations.
319  NSUInteger numberOfExpectedVisualEffectView = 0;
320  for (UIView* subview in childClippingView.subviews) {
321  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
322  continue;
323  }
324  XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u);
325  if ([self validateOneVisualEffectView:subview
326  expectedFrame:CGRectMake(0, 0, 10, 10)
327  inputRadius:5]) {
328  numberOfExpectedVisualEffectView++;
329  }
330  }
331  XCTAssertEqual(numberOfExpectedVisualEffectView, 1u);
332 }
333 
334 - (void)testApplyBackdropFilterWithCorrectFrame {
335  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
336  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
337  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
338  /*platform=*/thread_task_runner,
339  /*raster=*/thread_task_runner,
340  /*ui=*/thread_task_runner,
341  /*io=*/thread_task_runner);
342  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
343  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
344  /*delegate=*/mock_delegate,
345  /*rendering_api=*/mock_delegate.settings_.enable_impeller
348  /*platform_views_controller=*/flutterPlatformViewsController,
349  /*task_runners=*/runners,
350  /*worker_task_runner=*/nil,
351  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
352 
355  flutterPlatformViewsController->RegisterViewFactory(
356  factory, @"MockFlutterPlatformView",
358  FlutterResult result = ^(id result) {
359  };
360  flutterPlatformViewsController->OnMethodCall(
362  methodCallWithMethodName:@"create"
363  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
364  result);
365 
366  XCTAssertNotNil(gMockPlatformView);
367 
368  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
369  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
370  // Create embedded view params
371  flutter::MutatorsStack stack;
372  // Layer tree always pushes a screen scale factor to the stack
373  CGFloat screenScale = [UIScreen mainScreen].scale;
374  SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale);
375  stack.PushTransform(screenScaleMatrix);
376  // Push a backdrop filter
377  auto filter = std::make_shared<flutter::DlBlurImageFilter>(5, 2, flutter::DlTileMode::kClamp);
378  stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 8, screenScale * 8));
379 
380  auto embeddedViewParams =
381  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(5, 10), stack);
382 
383  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
384  flutterPlatformViewsController->CompositeEmbeddedView(2);
385  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]);
386  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
387  [mockFlutterView addSubview:childClippingView];
388 
389  [mockFlutterView setNeedsLayout];
390  [mockFlutterView layoutIfNeeded];
391 
392  // childClippingView has visual effect view with the correct configurations.
393  NSUInteger numberOfExpectedVisualEffectView = 0;
394  for (UIView* subview in childClippingView.subviews) {
395  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
396  continue;
397  }
398  XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u);
399  if ([self validateOneVisualEffectView:subview
400  expectedFrame:CGRectMake(0, 0, 5, 8)
401  inputRadius:5]) {
402  numberOfExpectedVisualEffectView++;
403  }
404  }
405  XCTAssertEqual(numberOfExpectedVisualEffectView, 1u);
406 }
407 
408 - (void)testApplyMultipleBackdropFilters {
409  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
410  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
411  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
412  /*platform=*/thread_task_runner,
413  /*raster=*/thread_task_runner,
414  /*ui=*/thread_task_runner,
415  /*io=*/thread_task_runner);
416  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
417  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
418  /*delegate=*/mock_delegate,
419  /*rendering_api=*/mock_delegate.settings_.enable_impeller
422  /*platform_views_controller=*/flutterPlatformViewsController,
423  /*task_runners=*/runners,
424  /*worker_task_runner=*/nil,
425  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
426 
429  flutterPlatformViewsController->RegisterViewFactory(
430  factory, @"MockFlutterPlatformView",
432  FlutterResult result = ^(id result) {
433  };
434  flutterPlatformViewsController->OnMethodCall(
436  methodCallWithMethodName:@"create"
437  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
438  result);
439 
440  XCTAssertNotNil(gMockPlatformView);
441 
442  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
443  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
444  // Create embedded view params
445  flutter::MutatorsStack stack;
446  // Layer tree always pushes a screen scale factor to the stack
447  CGFloat screenScale = [UIScreen mainScreen].scale;
448  SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale);
449  stack.PushTransform(screenScaleMatrix);
450  // Push backdrop filters
451  for (int i = 0; i < 50; i++) {
452  auto filter = std::make_shared<flutter::DlBlurImageFilter>(i, 2, flutter::DlTileMode::kClamp);
453  stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
454  }
455 
456  auto embeddedViewParams =
457  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(20, 20), stack);
458 
459  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
460  flutterPlatformViewsController->CompositeEmbeddedView(2);
461  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
462  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
463  [mockFlutterView addSubview:childClippingView];
464 
465  [mockFlutterView setNeedsLayout];
466  [mockFlutterView layoutIfNeeded];
467 
468  NSUInteger numberOfExpectedVisualEffectView = 0;
469  for (UIView* subview in childClippingView.subviews) {
470  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
471  continue;
472  }
473  XCTAssertLessThan(numberOfExpectedVisualEffectView, 50u);
474  if ([self validateOneVisualEffectView:subview
475  expectedFrame:CGRectMake(0, 0, 10, 10)
476  inputRadius:(CGFloat)numberOfExpectedVisualEffectView]) {
477  numberOfExpectedVisualEffectView++;
478  }
479  }
480  XCTAssertEqual(numberOfExpectedVisualEffectView, (NSUInteger)numberOfExpectedVisualEffectView);
481 }
482 
483 - (void)testAddBackdropFilters {
484  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
485  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
486  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
487  /*platform=*/thread_task_runner,
488  /*raster=*/thread_task_runner,
489  /*ui=*/thread_task_runner,
490  /*io=*/thread_task_runner);
491  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
492  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
493  /*delegate=*/mock_delegate,
494  /*rendering_api=*/mock_delegate.settings_.enable_impeller
497  /*platform_views_controller=*/flutterPlatformViewsController,
498  /*task_runners=*/runners,
499  /*worker_task_runner=*/nil,
500  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
501 
504  flutterPlatformViewsController->RegisterViewFactory(
505  factory, @"MockFlutterPlatformView",
507  FlutterResult result = ^(id result) {
508  };
509  flutterPlatformViewsController->OnMethodCall(
511  methodCallWithMethodName:@"create"
512  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
513  result);
514 
515  XCTAssertNotNil(gMockPlatformView);
516 
517  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
518  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
519  // Create embedded view params
520  flutter::MutatorsStack stack;
521  // Layer tree always pushes a screen scale factor to the stack
522  CGFloat screenScale = [UIScreen mainScreen].scale;
523  SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale);
524  stack.PushTransform(screenScaleMatrix);
525  // Push a backdrop filter
526  auto filter = std::make_shared<flutter::DlBlurImageFilter>(5, 2, flutter::DlTileMode::kClamp);
527  stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
528 
529  auto embeddedViewParams =
530  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
531 
532  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
533  flutterPlatformViewsController->CompositeEmbeddedView(2);
534  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]);
535  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
536  [mockFlutterView addSubview:childClippingView];
537 
538  [mockFlutterView setNeedsLayout];
539  [mockFlutterView layoutIfNeeded];
540 
541  NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init];
542  for (UIView* subview in childClippingView.subviews) {
543  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
544  continue;
545  }
546  XCTAssertLessThan(originalVisualEffectViews.count, 1u);
547  if ([self validateOneVisualEffectView:subview
548  expectedFrame:CGRectMake(0, 0, 10, 10)
549  inputRadius:(CGFloat)5]) {
550  [originalVisualEffectViews addObject:subview];
551  }
552  }
553  XCTAssertEqual(originalVisualEffectViews.count, 1u);
554 
555  //
556  // Simulate adding 1 backdrop filter (create a new mutators stack)
557  // Create embedded view params
558  flutter::MutatorsStack stack2;
559  // Layer tree always pushes a screen scale factor to the stack
560  stack2.PushTransform(screenScaleMatrix);
561  // Push backdrop filters
562  for (int i = 0; i < 2; i++) {
563  stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
564  }
565 
566  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
567  SkSize::Make(10, 10), stack2);
568 
569  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
570  flutterPlatformViewsController->CompositeEmbeddedView(2);
571  [mockFlutterView setNeedsLayout];
572  [mockFlutterView layoutIfNeeded];
573 
574  NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init];
575  for (UIView* subview in childClippingView.subviews) {
576  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
577  continue;
578  }
579  XCTAssertLessThan(newVisualEffectViews.count, 2u);
580 
581  if ([self validateOneVisualEffectView:subview
582  expectedFrame:CGRectMake(0, 0, 10, 10)
583  inputRadius:(CGFloat)5]) {
584  [newVisualEffectViews addObject:subview];
585  }
586  }
587  XCTAssertEqual(newVisualEffectViews.count, 2u);
588  for (NSUInteger i = 0; i < originalVisualEffectViews.count; i++) {
589  UIView* originalView = originalVisualEffectViews[i];
590  UIView* newView = newVisualEffectViews[i];
591  // Compare reference.
592  XCTAssertEqual(originalView, newView);
593  id mockOrignalView = OCMPartialMock(originalView);
594  OCMReject([mockOrignalView removeFromSuperview]);
595  }
596 }
597 
598 - (void)testRemoveBackdropFilters {
599  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
600  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
601  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
602  /*platform=*/thread_task_runner,
603  /*raster=*/thread_task_runner,
604  /*ui=*/thread_task_runner,
605  /*io=*/thread_task_runner);
606  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
607  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
608  /*delegate=*/mock_delegate,
609  /*rendering_api=*/mock_delegate.settings_.enable_impeller
612  /*platform_views_controller=*/flutterPlatformViewsController,
613  /*task_runners=*/runners,
614  /*worker_task_runner=*/nil,
615  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
616 
619  flutterPlatformViewsController->RegisterViewFactory(
620  factory, @"MockFlutterPlatformView",
622  FlutterResult result = ^(id result) {
623  };
624  flutterPlatformViewsController->OnMethodCall(
626  methodCallWithMethodName:@"create"
627  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
628  result);
629 
630  XCTAssertNotNil(gMockPlatformView);
631 
632  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
633  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
634  // Create embedded view params
635  flutter::MutatorsStack stack;
636  // Layer tree always pushes a screen scale factor to the stack
637  CGFloat screenScale = [UIScreen mainScreen].scale;
638  SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale);
639  stack.PushTransform(screenScaleMatrix);
640  // Push backdrop filters
641  auto filter = std::make_shared<flutter::DlBlurImageFilter>(5, 2, flutter::DlTileMode::kClamp);
642  for (int i = 0; i < 5; i++) {
643  stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
644  }
645 
646  auto embeddedViewParams =
647  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
648 
649  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
650  flutterPlatformViewsController->CompositeEmbeddedView(2);
651  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
652  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
653  [mockFlutterView addSubview:childClippingView];
654 
655  [mockFlutterView setNeedsLayout];
656  [mockFlutterView layoutIfNeeded];
657 
658  NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init];
659  for (UIView* subview in childClippingView.subviews) {
660  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
661  continue;
662  }
663  XCTAssertLessThan(originalVisualEffectViews.count, 5u);
664  if ([self validateOneVisualEffectView:subview
665  expectedFrame:CGRectMake(0, 0, 10, 10)
666  inputRadius:(CGFloat)5]) {
667  [originalVisualEffectViews addObject:subview];
668  }
669  }
670 
671  // Simulate removing 1 backdrop filter (create a new mutators stack)
672  // Create embedded view params
673  flutter::MutatorsStack stack2;
674  // Layer tree always pushes a screen scale factor to the stack
675  stack2.PushTransform(screenScaleMatrix);
676  // Push backdrop filters
677  for (int i = 0; i < 4; i++) {
678  stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
679  }
680 
681  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
682  SkSize::Make(10, 10), stack2);
683 
684  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
685  flutterPlatformViewsController->CompositeEmbeddedView(2);
686  [mockFlutterView setNeedsLayout];
687  [mockFlutterView layoutIfNeeded];
688 
689  NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init];
690  for (UIView* subview in childClippingView.subviews) {
691  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
692  continue;
693  }
694  XCTAssertLessThan(newVisualEffectViews.count, 4u);
695  if ([self validateOneVisualEffectView:subview
696  expectedFrame:CGRectMake(0, 0, 10, 10)
697  inputRadius:(CGFloat)5]) {
698  [newVisualEffectViews addObject:subview];
699  }
700  }
701  XCTAssertEqual(newVisualEffectViews.count, 4u);
702 
703  for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) {
704  UIView* newView = newVisualEffectViews[i];
705  id mockNewView = OCMPartialMock(newView);
706  UIView* originalView = originalVisualEffectViews[i];
707  // Compare reference.
708  XCTAssertEqual(originalView, newView);
709  OCMReject([mockNewView removeFromSuperview]);
710  [mockNewView stopMocking];
711  }
712 
713  // Simulate removing all backdrop filters (replace the mutators stack)
714  // Update embedded view params, delete except screenScaleMatrix
715  for (int i = 0; i < 5; i++) {
716  stack2.Pop();
717  }
718  // No backdrop filters in the stack, so no nothing to push
719 
720  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
721  SkSize::Make(10, 10), stack2);
722 
723  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
724  flutterPlatformViewsController->CompositeEmbeddedView(2);
725  [mockFlutterView setNeedsLayout];
726  [mockFlutterView layoutIfNeeded];
727 
728  NSUInteger numberOfExpectedVisualEffectView = 0u;
729  for (UIView* subview in childClippingView.subviews) {
730  if ([subview isKindOfClass:[UIVisualEffectView class]]) {
731  numberOfExpectedVisualEffectView++;
732  }
733  }
734  XCTAssertEqual(numberOfExpectedVisualEffectView, 0u);
735 }
736 
737 - (void)testEditBackdropFilters {
738  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
739  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
740  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
741  /*platform=*/thread_task_runner,
742  /*raster=*/thread_task_runner,
743  /*ui=*/thread_task_runner,
744  /*io=*/thread_task_runner);
745  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
746  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
747  /*delegate=*/mock_delegate,
748  /*rendering_api=*/mock_delegate.settings_.enable_impeller
751  /*platform_views_controller=*/flutterPlatformViewsController,
752  /*task_runners=*/runners,
753  /*worker_task_runner=*/nil,
754  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
755 
758  flutterPlatformViewsController->RegisterViewFactory(
759  factory, @"MockFlutterPlatformView",
761  FlutterResult result = ^(id result) {
762  };
763  flutterPlatformViewsController->OnMethodCall(
765  methodCallWithMethodName:@"create"
766  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
767  result);
768 
769  XCTAssertNotNil(gMockPlatformView);
770 
771  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
772  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
773  // Create embedded view params
774  flutter::MutatorsStack stack;
775  // Layer tree always pushes a screen scale factor to the stack
776  CGFloat screenScale = [UIScreen mainScreen].scale;
777  SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale);
778  stack.PushTransform(screenScaleMatrix);
779  // Push backdrop filters
780  auto filter = std::make_shared<flutter::DlBlurImageFilter>(5, 2, flutter::DlTileMode::kClamp);
781  for (int i = 0; i < 5; i++) {
782  stack.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
783  }
784 
785  auto embeddedViewParams =
786  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
787 
788  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
789  flutterPlatformViewsController->CompositeEmbeddedView(2);
790  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
791  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
792  [mockFlutterView addSubview:childClippingView];
793 
794  [mockFlutterView setNeedsLayout];
795  [mockFlutterView layoutIfNeeded];
796 
797  NSMutableArray* originalVisualEffectViews = [[NSMutableArray alloc] init];
798  for (UIView* subview in childClippingView.subviews) {
799  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
800  continue;
801  }
802  XCTAssertLessThan(originalVisualEffectViews.count, 5u);
803  if ([self validateOneVisualEffectView:subview
804  expectedFrame:CGRectMake(0, 0, 10, 10)
805  inputRadius:(CGFloat)5]) {
806  [originalVisualEffectViews addObject:subview];
807  }
808  }
809 
810  // Simulate editing 1 backdrop filter in the middle of the stack (create a new mutators stack)
811  // Create embedded view params
812  flutter::MutatorsStack stack2;
813  // Layer tree always pushes a screen scale factor to the stack
814  stack2.PushTransform(screenScaleMatrix);
815  // Push backdrop filters
816  for (int i = 0; i < 5; i++) {
817  if (i == 3) {
818  auto filter2 =
819  std::make_shared<flutter::DlBlurImageFilter>(2, 5, flutter::DlTileMode::kClamp);
820 
821  stack2.PushBackdropFilter(filter2,
822  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
823  continue;
824  }
825 
826  stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
827  }
828 
829  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
830  SkSize::Make(10, 10), stack2);
831 
832  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
833  flutterPlatformViewsController->CompositeEmbeddedView(2);
834  [mockFlutterView setNeedsLayout];
835  [mockFlutterView layoutIfNeeded];
836 
837  NSMutableArray* newVisualEffectViews = [[NSMutableArray alloc] init];
838  for (UIView* subview in childClippingView.subviews) {
839  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
840  continue;
841  }
842  XCTAssertLessThan(newVisualEffectViews.count, 5u);
843  CGFloat expectInputRadius = 5;
844  if (newVisualEffectViews.count == 3) {
845  expectInputRadius = 2;
846  }
847  if ([self validateOneVisualEffectView:subview
848  expectedFrame:CGRectMake(0, 0, 10, 10)
849  inputRadius:(CGFloat)expectInputRadius]) {
850  [newVisualEffectViews addObject:subview];
851  }
852  }
853  XCTAssertEqual(newVisualEffectViews.count, 5u);
854  for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) {
855  UIView* newView = newVisualEffectViews[i];
856  id mockNewView = OCMPartialMock(newView);
857  UIView* originalView = originalVisualEffectViews[i];
858  // Compare reference.
859  XCTAssertEqual(originalView, newView);
860  OCMReject([mockNewView removeFromSuperview]);
861  [mockNewView stopMocking];
862  }
863  [newVisualEffectViews removeAllObjects];
864 
865  // Simulate editing 1 backdrop filter in the beginning of the stack (replace the mutators stack)
866  // Update embedded view params, delete except screenScaleMatrix
867  for (int i = 0; i < 5; i++) {
868  stack2.Pop();
869  }
870  // Push backdrop filters
871  for (int i = 0; i < 5; i++) {
872  if (i == 0) {
873  auto filter2 =
874  std::make_shared<flutter::DlBlurImageFilter>(2, 5, flutter::DlTileMode::kClamp);
875  stack2.PushBackdropFilter(filter2,
876  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
877  continue;
878  }
879 
880  stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
881  }
882 
883  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
884  SkSize::Make(10, 10), stack2);
885 
886  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
887  flutterPlatformViewsController->CompositeEmbeddedView(2);
888  [mockFlutterView setNeedsLayout];
889  [mockFlutterView layoutIfNeeded];
890 
891  for (UIView* subview in childClippingView.subviews) {
892  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
893  continue;
894  }
895  XCTAssertLessThan(newVisualEffectViews.count, 5u);
896  CGFloat expectInputRadius = 5;
897  if (newVisualEffectViews.count == 0) {
898  expectInputRadius = 2;
899  }
900  if ([self validateOneVisualEffectView:subview
901  expectedFrame:CGRectMake(0, 0, 10, 10)
902  inputRadius:(CGFloat)expectInputRadius]) {
903  [newVisualEffectViews addObject:subview];
904  }
905  }
906  for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) {
907  UIView* newView = newVisualEffectViews[i];
908  id mockNewView = OCMPartialMock(newView);
909  UIView* originalView = originalVisualEffectViews[i];
910  // Compare reference.
911  XCTAssertEqual(originalView, newView);
912  OCMReject([mockNewView removeFromSuperview]);
913  [mockNewView stopMocking];
914  }
915  [newVisualEffectViews removeAllObjects];
916 
917  // Simulate editing 1 backdrop filter in the end of the stack (replace the mutators stack)
918  // Update embedded view params, delete except screenScaleMatrix
919  for (int i = 0; i < 5; i++) {
920  stack2.Pop();
921  }
922  // Push backdrop filters
923  for (int i = 0; i < 5; i++) {
924  if (i == 4) {
925  auto filter2 =
926  std::make_shared<flutter::DlBlurImageFilter>(2, 5, flutter::DlTileMode::kClamp);
927  stack2.PushBackdropFilter(filter2,
928  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
929  continue;
930  }
931 
932  stack2.PushBackdropFilter(filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
933  }
934 
935  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
936  SkSize::Make(10, 10), stack2);
937 
938  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
939  flutterPlatformViewsController->CompositeEmbeddedView(2);
940  [mockFlutterView setNeedsLayout];
941  [mockFlutterView layoutIfNeeded];
942 
943  for (UIView* subview in childClippingView.subviews) {
944  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
945  continue;
946  }
947  XCTAssertLessThan(newVisualEffectViews.count, 5u);
948  CGFloat expectInputRadius = 5;
949  if (newVisualEffectViews.count == 4) {
950  expectInputRadius = 2;
951  }
952  if ([self validateOneVisualEffectView:subview
953  expectedFrame:CGRectMake(0, 0, 10, 10)
954  inputRadius:(CGFloat)expectInputRadius]) {
955  [newVisualEffectViews addObject:subview];
956  }
957  }
958  XCTAssertEqual(newVisualEffectViews.count, 5u);
959 
960  for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) {
961  UIView* newView = newVisualEffectViews[i];
962  id mockNewView = OCMPartialMock(newView);
963  UIView* originalView = originalVisualEffectViews[i];
964  // Compare reference.
965  XCTAssertEqual(originalView, newView);
966  OCMReject([mockNewView removeFromSuperview]);
967  [mockNewView stopMocking];
968  }
969  [newVisualEffectViews removeAllObjects];
970 
971  // Simulate editing all backdrop filters in the stack (replace the mutators stack)
972  // Update embedded view params, delete except screenScaleMatrix
973  for (int i = 0; i < 5; i++) {
974  stack2.Pop();
975  }
976  // Push backdrop filters
977  for (int i = 0; i < 5; i++) {
978  auto filter2 = std::make_shared<flutter::DlBlurImageFilter>(i, 2, flutter::DlTileMode::kClamp);
979 
980  stack2.PushBackdropFilter(filter2, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
981  }
982 
983  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
984  SkSize::Make(10, 10), stack2);
985 
986  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
987  flutterPlatformViewsController->CompositeEmbeddedView(2);
988  [mockFlutterView setNeedsLayout];
989  [mockFlutterView layoutIfNeeded];
990 
991  for (UIView* subview in childClippingView.subviews) {
992  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
993  continue;
994  }
995  XCTAssertLessThan(newVisualEffectViews.count, 5u);
996  if ([self validateOneVisualEffectView:subview
997  expectedFrame:CGRectMake(0, 0, 10, 10)
998  inputRadius:(CGFloat)newVisualEffectViews.count]) {
999  [newVisualEffectViews addObject:subview];
1000  }
1001  }
1002  XCTAssertEqual(newVisualEffectViews.count, 5u);
1003 
1004  for (NSUInteger i = 0; i < newVisualEffectViews.count; i++) {
1005  UIView* newView = newVisualEffectViews[i];
1006  id mockNewView = OCMPartialMock(newView);
1007  UIView* originalView = originalVisualEffectViews[i];
1008  // Compare reference.
1009  XCTAssertEqual(originalView, newView);
1010  OCMReject([mockNewView removeFromSuperview]);
1011  [mockNewView stopMocking];
1012  }
1013  [newVisualEffectViews removeAllObjects];
1014 }
1015 
1016 - (void)testApplyBackdropFilterNotDlBlurImageFilter {
1017  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1018  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
1019  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1020  /*platform=*/thread_task_runner,
1021  /*raster=*/thread_task_runner,
1022  /*ui=*/thread_task_runner,
1023  /*io=*/thread_task_runner);
1024  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
1025  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1026  /*delegate=*/mock_delegate,
1027  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1030  /*platform_views_controller=*/flutterPlatformViewsController,
1031  /*task_runners=*/runners,
1032  /*worker_task_runner=*/nil,
1033  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1034 
1037  flutterPlatformViewsController->RegisterViewFactory(
1038  factory, @"MockFlutterPlatformView",
1040  FlutterResult result = ^(id result) {
1041  };
1042  flutterPlatformViewsController->OnMethodCall(
1044  methodCallWithMethodName:@"create"
1045  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1046  result);
1047 
1048  XCTAssertNotNil(gMockPlatformView);
1049 
1050  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
1051  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
1052  // Create embedded view params
1053  flutter::MutatorsStack stack;
1054  // Layer tree always pushes a screen scale factor to the stack
1055  CGFloat screenScale = [UIScreen mainScreen].scale;
1056  SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale);
1057  stack.PushTransform(screenScaleMatrix);
1058  // Push a dilate backdrop filter
1059  auto dilateFilter = std::make_shared<flutter::DlDilateImageFilter>(5, 2);
1060  stack.PushBackdropFilter(dilateFilter, SkRect::MakeEmpty());
1061 
1062  auto embeddedViewParams =
1063  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
1064 
1065  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1066  flutterPlatformViewsController->CompositeEmbeddedView(2);
1067  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]);
1068  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1069 
1070  [mockFlutterView addSubview:childClippingView];
1071 
1072  [mockFlutterView setNeedsLayout];
1073  [mockFlutterView layoutIfNeeded];
1074 
1075  NSUInteger numberOfExpectedVisualEffectView = 0;
1076  for (UIView* subview in childClippingView.subviews) {
1077  if ([subview isKindOfClass:[UIVisualEffectView class]]) {
1078  numberOfExpectedVisualEffectView++;
1079  }
1080  }
1081  XCTAssertEqual(numberOfExpectedVisualEffectView, 0u);
1082 
1083  // Simulate adding a non-DlBlurImageFilter in the middle of the stack (create a new mutators
1084  // stack) Create embedded view params
1085  flutter::MutatorsStack stack2;
1086  // Layer tree always pushes a screen scale factor to the stack
1087  stack2.PushTransform(screenScaleMatrix);
1088  // Push backdrop filters and dilate filter
1089  auto blurFilter = std::make_shared<flutter::DlBlurImageFilter>(5, 2, flutter::DlTileMode::kClamp);
1090 
1091  for (int i = 0; i < 5; i++) {
1092  if (i == 2) {
1093  stack2.PushBackdropFilter(dilateFilter,
1094  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1095  continue;
1096  }
1097 
1098  stack2.PushBackdropFilter(blurFilter,
1099  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1100  }
1101 
1102  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
1103  SkSize::Make(10, 10), stack2);
1104 
1105  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1106  flutterPlatformViewsController->CompositeEmbeddedView(2);
1107  [mockFlutterView setNeedsLayout];
1108  [mockFlutterView layoutIfNeeded];
1109 
1110  numberOfExpectedVisualEffectView = 0;
1111  for (UIView* subview in childClippingView.subviews) {
1112  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
1113  continue;
1114  }
1115  XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u);
1116  if ([self validateOneVisualEffectView:subview
1117  expectedFrame:CGRectMake(0, 0, 10, 10)
1118  inputRadius:(CGFloat)5]) {
1119  numberOfExpectedVisualEffectView++;
1120  }
1121  }
1122  XCTAssertEqual(numberOfExpectedVisualEffectView, 4u);
1123 
1124  // Simulate adding a non-DlBlurImageFilter to the beginning of the stack (replace the mutators
1125  // stack) Update embedded view params, delete except screenScaleMatrix
1126  for (int i = 0; i < 5; i++) {
1127  stack2.Pop();
1128  }
1129  // Push backdrop filters and dilate filter
1130  for (int i = 0; i < 5; i++) {
1131  if (i == 0) {
1132  stack2.PushBackdropFilter(dilateFilter,
1133  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1134  continue;
1135  }
1136 
1137  stack2.PushBackdropFilter(blurFilter,
1138  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1139  }
1140 
1141  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
1142  SkSize::Make(10, 10), stack2);
1143 
1144  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1145  flutterPlatformViewsController->CompositeEmbeddedView(2);
1146  [mockFlutterView setNeedsLayout];
1147  [mockFlutterView layoutIfNeeded];
1148 
1149  numberOfExpectedVisualEffectView = 0;
1150  for (UIView* subview in childClippingView.subviews) {
1151  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
1152  continue;
1153  }
1154  XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u);
1155  if ([self validateOneVisualEffectView:subview
1156  expectedFrame:CGRectMake(0, 0, 10, 10)
1157  inputRadius:(CGFloat)5]) {
1158  numberOfExpectedVisualEffectView++;
1159  }
1160  }
1161  XCTAssertEqual(numberOfExpectedVisualEffectView, 4u);
1162 
1163  // Simulate adding a non-DlBlurImageFilter to the end of the stack (replace the mutators stack)
1164  // Update embedded view params, delete except screenScaleMatrix
1165  for (int i = 0; i < 5; i++) {
1166  stack2.Pop();
1167  }
1168  // Push backdrop filters and dilate filter
1169  for (int i = 0; i < 5; i++) {
1170  if (i == 4) {
1171  stack2.PushBackdropFilter(dilateFilter,
1172  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1173  continue;
1174  }
1175 
1176  stack2.PushBackdropFilter(blurFilter,
1177  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1178  }
1179 
1180  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
1181  SkSize::Make(10, 10), stack2);
1182 
1183  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1184  flutterPlatformViewsController->CompositeEmbeddedView(2);
1185  [mockFlutterView setNeedsLayout];
1186  [mockFlutterView layoutIfNeeded];
1187 
1188  numberOfExpectedVisualEffectView = 0;
1189  for (UIView* subview in childClippingView.subviews) {
1190  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
1191  continue;
1192  }
1193  XCTAssertLessThan(numberOfExpectedVisualEffectView, 4u);
1194  if ([self validateOneVisualEffectView:subview
1195  expectedFrame:CGRectMake(0, 0, 10, 10)
1196  inputRadius:(CGFloat)5]) {
1197  numberOfExpectedVisualEffectView++;
1198  }
1199  }
1200  XCTAssertEqual(numberOfExpectedVisualEffectView, 4u);
1201 
1202  // Simulate adding only non-DlBlurImageFilter to the stack (replace the mutators stack)
1203  // Update embedded view params, delete except screenScaleMatrix
1204  for (int i = 0; i < 5; i++) {
1205  stack2.Pop();
1206  }
1207  // Push dilate filters
1208  for (int i = 0; i < 5; i++) {
1209  stack2.PushBackdropFilter(dilateFilter,
1210  SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1211  }
1212 
1213  embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix,
1214  SkSize::Make(10, 10), stack2);
1215 
1216  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1217  flutterPlatformViewsController->CompositeEmbeddedView(2);
1218  [mockFlutterView setNeedsLayout];
1219  [mockFlutterView layoutIfNeeded];
1220 
1221  numberOfExpectedVisualEffectView = 0;
1222  for (UIView* subview in childClippingView.subviews) {
1223  if ([subview isKindOfClass:[UIVisualEffectView class]]) {
1224  numberOfExpectedVisualEffectView++;
1225  }
1226  }
1227  XCTAssertEqual(numberOfExpectedVisualEffectView, 0u);
1228 }
1229 
1230 - (void)testApplyBackdropFilterCorrectAPI {
1232  // The gaussianBlur filter is extracted from UIVisualEffectView.
1233  // Each test requires a new PlatformViewFilter
1234  // Valid UIVisualEffectView API
1235  UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc]
1236  initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
1237  PlatformViewFilter* platformViewFilter =
1238  [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10)
1239  blurRadius:5
1240  visualEffectView:visualEffectView];
1241  XCTAssertNotNil(platformViewFilter);
1242 }
1243 
1244 - (void)testApplyBackdropFilterAPIChangedInvalidUIVisualEffectView {
1246  UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc] init];
1247  PlatformViewFilter* platformViewFilter =
1248  [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10)
1249  blurRadius:5
1250  visualEffectView:visualEffectView];
1251  XCTAssertNil(platformViewFilter);
1252 }
1253 
1254 - (void)testApplyBackdropFilterAPIChangedNoGaussianBlurFilter {
1256  UIVisualEffectView* editedUIVisualEffectView = [[UIVisualEffectView alloc]
1257  initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
1258  NSArray* subviews = editedUIVisualEffectView.subviews;
1259  for (UIView* view in subviews) {
1260  if ([NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) {
1261  for (CIFilter* filter in view.layer.filters) {
1262  if ([[filter valueForKey:@"name"] isEqual:@"gaussianBlur"]) {
1263  [filter setValue:@"notGaussianBlur" forKey:@"name"];
1264  break;
1265  }
1266  }
1267  break;
1268  }
1269  }
1270  PlatformViewFilter* platformViewFilter =
1271  [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10)
1272  blurRadius:5
1273  visualEffectView:editedUIVisualEffectView];
1274  XCTAssertNil(platformViewFilter);
1275 }
1276 
1277 - (void)testApplyBackdropFilterAPIChangedInvalidInputRadius {
1279  UIVisualEffectView* editedUIVisualEffectView = [[UIVisualEffectView alloc]
1280  initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
1281  NSArray* subviews = editedUIVisualEffectView.subviews;
1282  for (UIView* view in subviews) {
1283  if ([NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) {
1284  for (CIFilter* filter in view.layer.filters) {
1285  if ([[filter valueForKey:@"name"] isEqual:@"gaussianBlur"]) {
1286  [filter setValue:@"invalidInputRadius" forKey:@"inputRadius"];
1287  break;
1288  }
1289  }
1290  break;
1291  }
1292  }
1293 
1294  PlatformViewFilter* platformViewFilter =
1295  [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10)
1296  blurRadius:5
1297  visualEffectView:editedUIVisualEffectView];
1298  XCTAssertNil(platformViewFilter);
1299 }
1300 
1301 - (void)testBackdropFilterVisualEffectSubviewBackgroundColor {
1302  UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc]
1303  initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
1304  PlatformViewFilter* platformViewFilter =
1305  [[PlatformViewFilter alloc] initWithFrame:CGRectMake(0, 0, 10, 10)
1306  blurRadius:5
1307  visualEffectView:visualEffectView];
1308  CGColorRef visualEffectSubviewBackgroundColor = nil;
1309  for (UIView* view in [platformViewFilter backdropFilterView].subviews) {
1310  if ([NSStringFromClass([view class]) hasSuffix:@"VisualEffectSubview"]) {
1311  visualEffectSubviewBackgroundColor = view.layer.backgroundColor;
1312  }
1313  }
1314  XCTAssertTrue(
1315  CGColorEqualToColor(visualEffectSubviewBackgroundColor, UIColor.clearColor.CGColor));
1316 }
1317 
1318 - (void)testCompositePlatformView {
1319  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1320  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
1321  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1322  /*platform=*/thread_task_runner,
1323  /*raster=*/thread_task_runner,
1324  /*ui=*/thread_task_runner,
1325  /*io=*/thread_task_runner);
1326  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
1327  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1328  /*delegate=*/mock_delegate,
1329  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1332  /*platform_views_controller=*/flutterPlatformViewsController,
1333  /*task_runners=*/runners,
1334  /*worker_task_runner=*/nil,
1335  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1336 
1339  flutterPlatformViewsController->RegisterViewFactory(
1340  factory, @"MockFlutterPlatformView",
1342  FlutterResult result = ^(id result) {
1343  };
1344  flutterPlatformViewsController->OnMethodCall(
1346  methodCallWithMethodName:@"create"
1347  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1348  result);
1349 
1350  XCTAssertNotNil(gMockPlatformView);
1351 
1352  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
1353  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
1354  // Create embedded view params
1355  flutter::MutatorsStack stack;
1356  // Layer tree always pushes a screen scale factor to the stack
1357  SkMatrix screenScaleMatrix =
1358  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
1359  stack.PushTransform(screenScaleMatrix);
1360  // Push a translate matrix
1361  SkMatrix translateMatrix = SkMatrix::Translate(100, 100);
1362  stack.PushTransform(translateMatrix);
1363  SkMatrix finalMatrix;
1364  finalMatrix.setConcat(screenScaleMatrix, translateMatrix);
1365 
1366  auto embeddedViewParams =
1367  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
1368 
1369  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1370  flutterPlatformViewsController->CompositeEmbeddedView(2);
1371  CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds
1372  toView:mockFlutterView];
1373  XCTAssertTrue(CGRectEqualToRect(platformViewRectInFlutterView, CGRectMake(100, 100, 300, 300)));
1374 }
1375 
1376 - (void)testBackdropFilterCorrectlyPushedAndReset {
1377  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1378  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
1379  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1380  /*platform=*/thread_task_runner,
1381  /*raster=*/thread_task_runner,
1382  /*ui=*/thread_task_runner,
1383  /*io=*/thread_task_runner);
1384  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
1385  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1386  /*delegate=*/mock_delegate,
1387  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1390  /*platform_views_controller=*/flutterPlatformViewsController,
1391  /*task_runners=*/runners,
1392  /*worker_task_runner=*/nil,
1393  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1394 
1397  flutterPlatformViewsController->RegisterViewFactory(
1398  factory, @"MockFlutterPlatformView",
1400  FlutterResult result = ^(id result) {
1401  };
1402  flutterPlatformViewsController->OnMethodCall(
1404  methodCallWithMethodName:@"create"
1405  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1406  result);
1407 
1408  XCTAssertNotNil(gMockPlatformView);
1409 
1410  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
1411  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
1412  // Create embedded view params
1413  flutter::MutatorsStack stack;
1414  // Layer tree always pushes a screen scale factor to the stack
1415  CGFloat screenScale = [UIScreen mainScreen].scale;
1416  SkMatrix screenScaleMatrix = SkMatrix::Scale(screenScale, screenScale);
1417  stack.PushTransform(screenScaleMatrix);
1418 
1419  auto embeddedViewParams =
1420  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
1421 
1422  flutterPlatformViewsController->BeginFrame(SkISize::Make(0, 0));
1423  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1424  flutterPlatformViewsController->PushVisitedPlatformView(2);
1425  auto filter = std::make_shared<flutter::DlBlurImageFilter>(5, 2, flutter::DlTileMode::kClamp);
1426  flutterPlatformViewsController->PushFilterToVisitedPlatformViews(
1427  filter, SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
1428  flutterPlatformViewsController->CompositeEmbeddedView(2);
1429  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]);
1430  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1431  [mockFlutterView addSubview:childClippingView];
1432 
1433  [mockFlutterView setNeedsLayout];
1434  [mockFlutterView layoutIfNeeded];
1435 
1436  // childClippingView has visual effect view with the correct configurations.
1437  NSUInteger numberOfExpectedVisualEffectView = 0;
1438  for (UIView* subview in childClippingView.subviews) {
1439  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
1440  continue;
1441  }
1442  XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u);
1443  if ([self validateOneVisualEffectView:subview
1444  expectedFrame:CGRectMake(0, 0, 10, 10)
1445  inputRadius:5]) {
1446  numberOfExpectedVisualEffectView++;
1447  }
1448  }
1449  XCTAssertEqual(numberOfExpectedVisualEffectView, 1u);
1450 
1451  // New frame, with no filter pushed.
1452  auto embeddedViewParams2 =
1453  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
1454  flutterPlatformViewsController->BeginFrame(SkISize::Make(0, 0));
1455  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2));
1456  flutterPlatformViewsController->CompositeEmbeddedView(2);
1457  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]);
1458 
1459  [mockFlutterView setNeedsLayout];
1460  [mockFlutterView layoutIfNeeded];
1461 
1462  numberOfExpectedVisualEffectView = 0;
1463  for (UIView* subview in childClippingView.subviews) {
1464  if (![subview isKindOfClass:[UIVisualEffectView class]]) {
1465  continue;
1466  }
1467  numberOfExpectedVisualEffectView++;
1468  }
1469  XCTAssertEqual(numberOfExpectedVisualEffectView, 0u);
1470 }
1471 
1472 - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView {
1473  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1474  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
1475  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1476  /*platform=*/thread_task_runner,
1477  /*raster=*/thread_task_runner,
1478  /*ui=*/thread_task_runner,
1479  /*io=*/thread_task_runner);
1480  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
1481  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1482  /*delegate=*/mock_delegate,
1483  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1486  /*platform_views_controller=*/flutterPlatformViewsController,
1487  /*task_runners=*/runners,
1488  /*worker_task_runner=*/nil,
1489  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1490 
1493  flutterPlatformViewsController->RegisterViewFactory(
1494  factory, @"MockFlutterPlatformView",
1496  FlutterResult result = ^(id result) {
1497  };
1498  flutterPlatformViewsController->OnMethodCall(
1500  methodCallWithMethodName:@"create"
1501  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1502  result);
1503 
1504  XCTAssertNotNil(gMockPlatformView);
1505 
1506  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
1507  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
1508  // Create embedded view params
1509  flutter::MutatorsStack stack;
1510  // Layer tree always pushes a screen scale factor to the stack
1511  SkMatrix screenScaleMatrix =
1512  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
1513  stack.PushTransform(screenScaleMatrix);
1514  // Push a rotate matrix
1515  SkMatrix rotateMatrix;
1516  rotateMatrix.setRotate(10);
1517  stack.PushTransform(rotateMatrix);
1518  SkMatrix finalMatrix;
1519  finalMatrix.setConcat(screenScaleMatrix, rotateMatrix);
1520 
1521  auto embeddedViewParams =
1522  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
1523 
1524  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1525  flutterPlatformViewsController->CompositeEmbeddedView(2);
1526  CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds
1527  toView:mockFlutterView];
1528  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
1529  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1530  // The childclippingview's frame is set based on flow, but the platform view's frame is set based
1531  // on quartz. Although they should be the same, but we should tolerate small floating point
1532  // errors.
1533  XCTAssertLessThan(fabs(platformViewRectInFlutterView.origin.x - childClippingView.frame.origin.x),
1535  XCTAssertLessThan(fabs(platformViewRectInFlutterView.origin.y - childClippingView.frame.origin.y),
1537  XCTAssertLessThan(
1538  fabs(platformViewRectInFlutterView.size.width - childClippingView.frame.size.width),
1540  XCTAssertLessThan(
1541  fabs(platformViewRectInFlutterView.size.height - childClippingView.frame.size.height),
1543 }
1544 
1545 - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView {
1546  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1547  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
1548  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1549  /*platform=*/thread_task_runner,
1550  /*raster=*/thread_task_runner,
1551  /*ui=*/thread_task_runner,
1552  /*io=*/thread_task_runner);
1553  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
1554  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1555  /*delegate=*/mock_delegate,
1556  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1559  /*platform_views_controller=*/flutterPlatformViewsController,
1560  /*task_runners=*/runners,
1561  /*worker_task_runner=*/nil,
1562  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1563 
1566  flutterPlatformViewsController->RegisterViewFactory(
1567  factory, @"MockFlutterPlatformView",
1569  FlutterResult result = ^(id result) {
1570  };
1571  flutterPlatformViewsController->OnMethodCall(
1573  methodCallWithMethodName:@"create"
1574  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1575  result);
1576 
1577  XCTAssertNotNil(gMockPlatformView);
1578 
1579  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
1580  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
1581  // Create embedded view params.
1582  flutter::MutatorsStack stack;
1583  // Layer tree always pushes a screen scale factor to the stack.
1584  SkMatrix screenScaleMatrix =
1585  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
1586  stack.PushTransform(screenScaleMatrix);
1587  SkMatrix translateMatrix = SkMatrix::Translate(5, 5);
1588  // The platform view's rect for this test will be (5, 5, 10, 10).
1589  stack.PushTransform(translateMatrix);
1590  // Push a clip rect, big enough to contain the entire platform view bound.
1591  SkRect rect = SkRect::MakeXYWH(0, 0, 25, 25);
1592  stack.PushClipRect(rect);
1593  // Push a clip rrect, big enough to contain the entire platform view bound without clipping it.
1594  // Make the origin (-1, -1) so that the top left rounded corner isn't clipping the PlatformView.
1595  SkRect rect_for_rrect = SkRect::MakeXYWH(-1, -1, 25, 25);
1596  SkRRect rrect = SkRRect::MakeRectXY(rect_for_rrect, 1, 1);
1597  stack.PushClipRRect(rrect);
1598 
1599  auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
1600  SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack);
1601 
1602  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1603  flutterPlatformViewsController->CompositeEmbeddedView(2);
1604  gMockPlatformView.backgroundColor = UIColor.redColor;
1605  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
1606  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1607  [mockFlutterView addSubview:childClippingView];
1608 
1609  [mockFlutterView setNeedsLayout];
1610  [mockFlutterView layoutIfNeeded];
1611  XCTAssertNil(childClippingView.maskView);
1612 }
1613 
1614 - (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView {
1615  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1616  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
1617  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1618  /*platform=*/thread_task_runner,
1619  /*raster=*/thread_task_runner,
1620  /*ui=*/thread_task_runner,
1621  /*io=*/thread_task_runner);
1622  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
1623  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1624  /*delegate=*/mock_delegate,
1625  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1628  /*platform_views_controller=*/flutterPlatformViewsController,
1629  /*task_runners=*/runners,
1630  /*worker_task_runner=*/nil,
1631  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1632 
1635  flutterPlatformViewsController->RegisterViewFactory(
1636  factory, @"MockFlutterPlatformView",
1638  FlutterResult result = ^(id result) {
1639  };
1640  flutterPlatformViewsController->OnMethodCall(
1642  methodCallWithMethodName:@"create"
1643  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1644  result);
1645 
1646  XCTAssertNotNil(gMockPlatformView);
1647 
1648  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
1649  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
1650  // Create embedded view params
1651  flutter::MutatorsStack stack;
1652  // Layer tree always pushes a screen scale factor to the stack.
1653  SkMatrix screenScaleMatrix =
1654  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
1655  stack.PushTransform(screenScaleMatrix);
1656  SkMatrix translateMatrix = SkMatrix::Translate(5, 5);
1657  // The platform view's rect for this test will be (5, 5, 10, 10).
1658  stack.PushTransform(translateMatrix);
1659 
1660  // Push a clip rrect, the rect of the rrect is the same as the PlatformView of the corner should.
1661  // clip the PlatformView.
1662  SkRect rect_for_rrect = SkRect::MakeXYWH(0, 0, 10, 10);
1663  SkRRect rrect = SkRRect::MakeRectXY(rect_for_rrect, 1, 1);
1664  stack.PushClipRRect(rrect);
1665 
1666  auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
1667  SkMatrix::Concat(screenScaleMatrix, translateMatrix), SkSize::Make(5, 5), stack);
1668 
1669  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1670  flutterPlatformViewsController->CompositeEmbeddedView(2);
1671  gMockPlatformView.backgroundColor = UIColor.redColor;
1672  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
1673  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1674  [mockFlutterView addSubview:childClippingView];
1675 
1676  [mockFlutterView setNeedsLayout];
1677  [mockFlutterView layoutIfNeeded];
1678 
1679  XCTAssertNotNil(childClippingView.maskView);
1680 }
1681 
1682 - (void)testClipRect {
1683  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1684  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
1685  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1686  /*platform=*/thread_task_runner,
1687  /*raster=*/thread_task_runner,
1688  /*ui=*/thread_task_runner,
1689  /*io=*/thread_task_runner);
1690  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
1691  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1692  /*delegate=*/mock_delegate,
1693  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1696  /*platform_views_controller=*/flutterPlatformViewsController,
1697  /*task_runners=*/runners,
1698  /*worker_task_runner=*/nil,
1699  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1700 
1703  flutterPlatformViewsController->RegisterViewFactory(
1704  factory, @"MockFlutterPlatformView",
1706  FlutterResult result = ^(id result) {
1707  };
1708  flutterPlatformViewsController->OnMethodCall(
1710  methodCallWithMethodName:@"create"
1711  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1712  result);
1713 
1714  XCTAssertNotNil(gMockPlatformView);
1715 
1716  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
1717  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
1718  // Create embedded view params
1719  flutter::MutatorsStack stack;
1720  // Layer tree always pushes a screen scale factor to the stack
1721  SkMatrix screenScaleMatrix =
1722  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
1723  stack.PushTransform(screenScaleMatrix);
1724  // Push a clip rect
1725  SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3);
1726  stack.PushClipRect(rect);
1727 
1728  auto embeddedViewParams =
1729  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
1730 
1731  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1732  flutterPlatformViewsController->CompositeEmbeddedView(2);
1733  gMockPlatformView.backgroundColor = UIColor.redColor;
1734  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
1735  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1736  [mockFlutterView addSubview:childClippingView];
1737 
1738  [mockFlutterView setNeedsLayout];
1739  [mockFlutterView layoutIfNeeded];
1740 
1741  for (int i = 0; i < 10; i++) {
1742  for (int j = 0; j < 10; j++) {
1743  CGPoint point = CGPointMake(i, j);
1744  int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView];
1745  // Edges of the clipping might have a semi transparent pixel, we only check the pixels that
1746  // are fully inside the clipped area.
1747  CGRect insideClipping = CGRectMake(3, 3, 1, 1);
1748  if (CGRectContainsPoint(insideClipping, point)) {
1749  XCTAssertEqual(alpha, 255);
1750  } else {
1751  XCTAssertLessThan(alpha, 255);
1752  }
1753  }
1754  }
1755 }
1756 
1757 - (void)testClipRRect {
1758  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1759  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
1760  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1761  /*platform=*/thread_task_runner,
1762  /*raster=*/thread_task_runner,
1763  /*ui=*/thread_task_runner,
1764  /*io=*/thread_task_runner);
1765  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
1766  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1767  /*delegate=*/mock_delegate,
1768  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1771  /*platform_views_controller=*/flutterPlatformViewsController,
1772  /*task_runners=*/runners,
1773  /*worker_task_runner=*/nil,
1774  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1775 
1778  flutterPlatformViewsController->RegisterViewFactory(
1779  factory, @"MockFlutterPlatformView",
1781  FlutterResult result = ^(id result) {
1782  };
1783  flutterPlatformViewsController->OnMethodCall(
1785  methodCallWithMethodName:@"create"
1786  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1787  result);
1788 
1789  XCTAssertNotNil(gMockPlatformView);
1790 
1791  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
1792  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
1793  // Create embedded view params
1794  flutter::MutatorsStack stack;
1795  // Layer tree always pushes a screen scale factor to the stack
1796  SkMatrix screenScaleMatrix =
1797  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
1798  stack.PushTransform(screenScaleMatrix);
1799  // Push a clip rrect
1800  SkRRect rrect = SkRRect::MakeRectXY(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1);
1801  stack.PushClipRRect(rrect);
1802 
1803  auto embeddedViewParams =
1804  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
1805 
1806  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1807  flutterPlatformViewsController->CompositeEmbeddedView(2);
1808  gMockPlatformView.backgroundColor = UIColor.redColor;
1809  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
1810  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1811  [mockFlutterView addSubview:childClippingView];
1812 
1813  [mockFlutterView setNeedsLayout];
1814  [mockFlutterView layoutIfNeeded];
1815 
1816  for (int i = 0; i < 10; i++) {
1817  for (int j = 0; j < 10; j++) {
1818  CGPoint point = CGPointMake(i, j);
1819  int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView];
1820  // Edges of the clipping might have a semi transparent pixel, we only check the pixels that
1821  // are fully inside the clipped area.
1822  CGRect insideClipping = CGRectMake(3, 3, 4, 4);
1823  if (CGRectContainsPoint(insideClipping, point)) {
1824  XCTAssertEqual(alpha, 255);
1825  } else {
1826  XCTAssertLessThan(alpha, 255);
1827  }
1828  }
1829  }
1830 }
1831 
1832 - (void)testClipPath {
1833  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1834  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
1835  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1836  /*platform=*/thread_task_runner,
1837  /*raster=*/thread_task_runner,
1838  /*ui=*/thread_task_runner,
1839  /*io=*/thread_task_runner);
1840  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
1841  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1842  /*delegate=*/mock_delegate,
1843  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1846  /*platform_views_controller=*/flutterPlatformViewsController,
1847  /*task_runners=*/runners,
1848  /*worker_task_runner=*/nil,
1849  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1850 
1853  flutterPlatformViewsController->RegisterViewFactory(
1854  factory, @"MockFlutterPlatformView",
1856  FlutterResult result = ^(id result) {
1857  };
1858  flutterPlatformViewsController->OnMethodCall(
1860  methodCallWithMethodName:@"create"
1861  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1862  result);
1863 
1864  XCTAssertNotNil(gMockPlatformView);
1865 
1866  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
1867  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
1868  // Create embedded view params
1869  flutter::MutatorsStack stack;
1870  // Layer tree always pushes a screen scale factor to the stack
1871  SkMatrix screenScaleMatrix =
1872  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
1873  stack.PushTransform(screenScaleMatrix);
1874  // Push a clip path
1875  SkPath path;
1876  path.addRoundRect(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1);
1877  stack.PushClipPath(path);
1878 
1879  auto embeddedViewParams =
1880  std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
1881 
1882  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1883  flutterPlatformViewsController->CompositeEmbeddedView(2);
1884  gMockPlatformView.backgroundColor = UIColor.redColor;
1885  XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
1886  ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1887  [mockFlutterView addSubview:childClippingView];
1888 
1889  [mockFlutterView setNeedsLayout];
1890  [mockFlutterView layoutIfNeeded];
1891 
1892  for (int i = 0; i < 10; i++) {
1893  for (int j = 0; j < 10; j++) {
1894  CGPoint point = CGPointMake(i, j);
1895  int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView];
1896  // Edges of the clipping might have a semi transparent pixel, we only check the pixels that
1897  // are fully inside the clipped area.
1898  CGRect insideClipping = CGRectMake(3, 3, 4, 4);
1899  if (CGRectContainsPoint(insideClipping, point)) {
1900  XCTAssertEqual(alpha, 255);
1901  } else {
1902  XCTAssertLessThan(alpha, 255);
1903  }
1904  }
1905  }
1906 }
1907 
1908 - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents {
1909  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1910  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
1911  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1912  /*platform=*/thread_task_runner,
1913  /*raster=*/thread_task_runner,
1914  /*ui=*/thread_task_runner,
1915  /*io=*/thread_task_runner);
1916  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
1917  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1918  /*delegate=*/mock_delegate,
1919  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1922  /*platform_views_controller=*/flutterPlatformViewsController,
1923  /*task_runners=*/runners,
1924  /*worker_task_runner=*/nil,
1925  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1926 
1929  flutterPlatformViewsController->RegisterViewFactory(
1930  factory, @"MockFlutterPlatformView",
1932  FlutterResult result = ^(id result) {
1933  };
1934  flutterPlatformViewsController->OnMethodCall(
1936  methodCallWithMethodName:@"create"
1937  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1938  result);
1939 
1940  XCTAssertNotNil(gMockPlatformView);
1941 
1942  // Find touch inteceptor view
1943  UIView* touchInteceptorView = gMockPlatformView;
1944  while (touchInteceptorView != nil &&
1945  ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
1946  touchInteceptorView = touchInteceptorView.superview;
1947  }
1948  XCTAssertNotNil(touchInteceptorView);
1949 
1950  // Find ForwardGestureRecognizer
1951  UIGestureRecognizer* forwardGectureRecognizer = nil;
1952  for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
1953  if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) {
1954  forwardGectureRecognizer = gestureRecognizer;
1955  break;
1956  }
1957  }
1958 
1959  // Before setting flutter view controller, events are not dispatched.
1960  NSSet* touches1 = [[NSSet alloc] init];
1961  id event1 = OCMClassMock([UIEvent class]);
1962  id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]);
1963  [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
1964  OCMReject([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]);
1965 
1966  // Set flutter view controller allows events to be dispatched.
1967  NSSet* touches2 = [[NSSet alloc] init];
1968  id event2 = OCMClassMock([UIEvent class]);
1969  flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller);
1970  [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2];
1971  OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]);
1972 }
1973 
1974 - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGesturesToBeHandled {
1975  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1976  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
1977  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1978  /*platform=*/thread_task_runner,
1979  /*raster=*/thread_task_runner,
1980  /*ui=*/thread_task_runner,
1981  /*io=*/thread_task_runner);
1982  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
1983  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1984  /*delegate=*/mock_delegate,
1985  /*rendering_api=*/mock_delegate.settings_.enable_impeller
1988  /*platform_views_controller=*/flutterPlatformViewsController,
1989  /*task_runners=*/runners,
1990  /*worker_task_runner=*/nil,
1991  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1992 
1995  flutterPlatformViewsController->RegisterViewFactory(
1996  factory, @"MockFlutterPlatformView",
1998  FlutterResult result = ^(id result) {
1999  };
2000  flutterPlatformViewsController->OnMethodCall(
2002  methodCallWithMethodName:@"create"
2003  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2004  result);
2005 
2006  XCTAssertNotNil(gMockPlatformView);
2007 
2008  // Find touch inteceptor view
2009  UIView* touchInteceptorView = gMockPlatformView;
2010  while (touchInteceptorView != nil &&
2011  ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
2012  touchInteceptorView = touchInteceptorView.superview;
2013  }
2014  XCTAssertNotNil(touchInteceptorView);
2015 
2016  // Find ForwardGestureRecognizer
2017  UIGestureRecognizer* forwardGectureRecognizer = nil;
2018  for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
2019  if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) {
2020  forwardGectureRecognizer = gestureRecognizer;
2021  break;
2022  }
2023  }
2024  id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]);
2025  {
2026  // ***** Sequence 1, finishing touch event with touchEnded ***** //
2027  flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller);
2028 
2029  NSSet* touches1 = [[NSSet alloc] init];
2030  id event1 = OCMClassMock([UIEvent class]);
2031  [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2032  OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]);
2033 
2034  flutterPlatformViewsController->SetFlutterViewController(nil);
2035 
2036  // Allow the touch events to finish
2037  NSSet* touches2 = [[NSSet alloc] init];
2038  id event2 = OCMClassMock([UIEvent class]);
2039  [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2];
2040  OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]);
2041 
2042  NSSet* touches3 = [[NSSet alloc] init];
2043  id event3 = OCMClassMock([UIEvent class]);
2044  [forwardGectureRecognizer touchesEnded:touches3 withEvent:event3];
2045  OCMVerify([mockFlutterViewContoller touchesEnded:touches3 withEvent:event3]);
2046 
2047  // Now the 2nd touch sequence should not be allowed.
2048  NSSet* touches4 = [[NSSet alloc] init];
2049  id event4 = OCMClassMock([UIEvent class]);
2050  [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4];
2051  OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]);
2052 
2053  NSSet* touches5 = [[NSSet alloc] init];
2054  id event5 = OCMClassMock([UIEvent class]);
2055  [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5];
2056  OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]);
2057  }
2058 
2059  {
2060  // ***** Sequence 2, finishing touch event with touchCancelled ***** //
2061  flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller);
2062 
2063  NSSet* touches1 = [[NSSet alloc] init];
2064  id event1 = OCMClassMock([UIEvent class]);
2065  [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2066  OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]);
2067 
2068  flutterPlatformViewsController->SetFlutterViewController(nil);
2069 
2070  // Allow the touch events to finish
2071  NSSet* touches2 = [[NSSet alloc] init];
2072  id event2 = OCMClassMock([UIEvent class]);
2073  [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2];
2074  OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]);
2075 
2076  NSSet* touches3 = [[NSSet alloc] init];
2077  id event3 = OCMClassMock([UIEvent class]);
2078  [forwardGectureRecognizer touchesCancelled:touches3 withEvent:event3];
2079  OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches3]);
2080 
2081  // Now the 2nd touch sequence should not be allowed.
2082  NSSet* touches4 = [[NSSet alloc] init];
2083  id event4 = OCMClassMock([UIEvent class]);
2084  [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4];
2085  OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]);
2086 
2087  NSSet* touches5 = [[NSSet alloc] init];
2088  id event5 = OCMClassMock([UIEvent class]);
2089  [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5];
2090  OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]);
2091  }
2092 
2093  flutterPlatformViewsController->Reset();
2094 }
2095 
2096 - (void)
2097  testSetFlutterViewControllerInTheMiddleOfTouchEventAllowsTheNewControllerToHandleSecondTouchSequence {
2098  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2099  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2100  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2101  /*platform=*/thread_task_runner,
2102  /*raster=*/thread_task_runner,
2103  /*ui=*/thread_task_runner,
2104  /*io=*/thread_task_runner);
2105  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2106  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2107  /*delegate=*/mock_delegate,
2108  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2111  /*platform_views_controller=*/flutterPlatformViewsController,
2112  /*task_runners=*/runners,
2113  /*worker_task_runner=*/nil,
2114  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2115 
2118  flutterPlatformViewsController->RegisterViewFactory(
2119  factory, @"MockFlutterPlatformView",
2121  FlutterResult result = ^(id result) {
2122  };
2123  flutterPlatformViewsController->OnMethodCall(
2125  methodCallWithMethodName:@"create"
2126  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2127  result);
2128 
2129  XCTAssertNotNil(gMockPlatformView);
2130 
2131  // Find touch inteceptor view
2132  UIView* touchInteceptorView = gMockPlatformView;
2133  while (touchInteceptorView != nil &&
2134  ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
2135  touchInteceptorView = touchInteceptorView.superview;
2136  }
2137  XCTAssertNotNil(touchInteceptorView);
2138 
2139  // Find ForwardGestureRecognizer
2140  UIGestureRecognizer* forwardGectureRecognizer = nil;
2141  for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
2142  if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) {
2143  forwardGectureRecognizer = gestureRecognizer;
2144  break;
2145  }
2146  }
2147  id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]);
2148 
2149  flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller);
2150 
2151  // The touches in this sequence requires 1 touch object, we always create the NSSet with one item.
2152  NSSet* touches1 = [NSSet setWithObject:@1];
2153  id event1 = OCMClassMock([UIEvent class]);
2154  [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2155  OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]);
2156 
2157  UIViewController* mockFlutterViewContoller2 = OCMClassMock([UIViewController class]);
2158  flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller2);
2159 
2160  // Touch events should still send to the old FlutterViewController if FlutterViewController
2161  // is updated in between.
2162  NSSet* touches2 = [NSSet setWithObject:@1];
2163  id event2 = OCMClassMock([UIEvent class]);
2164  [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2];
2165  OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]);
2166  OCMReject([mockFlutterViewContoller2 touchesBegan:touches2 withEvent:event2]);
2167 
2168  NSSet* touches3 = [NSSet setWithObject:@1];
2169  id event3 = OCMClassMock([UIEvent class]);
2170  [forwardGectureRecognizer touchesMoved:touches3 withEvent:event3];
2171  OCMVerify([mockFlutterViewContoller touchesMoved:touches3 withEvent:event3]);
2172  OCMReject([mockFlutterViewContoller2 touchesMoved:touches3 withEvent:event3]);
2173 
2174  NSSet* touches4 = [NSSet setWithObject:@1];
2175  id event4 = OCMClassMock([UIEvent class]);
2176  [forwardGectureRecognizer touchesEnded:touches4 withEvent:event4];
2177  OCMVerify([mockFlutterViewContoller touchesEnded:touches4 withEvent:event4]);
2178  OCMReject([mockFlutterViewContoller2 touchesEnded:touches4 withEvent:event4]);
2179 
2180  NSSet* touches5 = [NSSet setWithObject:@1];
2181  id event5 = OCMClassMock([UIEvent class]);
2182  [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5];
2183  OCMVerify([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]);
2184  OCMReject([mockFlutterViewContoller2 touchesEnded:touches5 withEvent:event5]);
2185 
2186  // Now the 2nd touch sequence should go to the new FlutterViewController
2187 
2188  NSSet* touches6 = [NSSet setWithObject:@1];
2189  id event6 = OCMClassMock([UIEvent class]);
2190  [forwardGectureRecognizer touchesBegan:touches6 withEvent:event6];
2191  OCMVerify([mockFlutterViewContoller2 touchesBegan:touches6 withEvent:event6]);
2192  OCMReject([mockFlutterViewContoller touchesBegan:touches6 withEvent:event6]);
2193 
2194  // Allow the touch events to finish
2195  NSSet* touches7 = [NSSet setWithObject:@1];
2196  id event7 = OCMClassMock([UIEvent class]);
2197  [forwardGectureRecognizer touchesMoved:touches7 withEvent:event7];
2198  OCMVerify([mockFlutterViewContoller2 touchesMoved:touches7 withEvent:event7]);
2199  OCMReject([mockFlutterViewContoller touchesMoved:touches7 withEvent:event7]);
2200 
2201  NSSet* touches8 = [NSSet setWithObject:@1];
2202  id event8 = OCMClassMock([UIEvent class]);
2203  [forwardGectureRecognizer touchesEnded:touches8 withEvent:event8];
2204  OCMVerify([mockFlutterViewContoller2 touchesEnded:touches8 withEvent:event8]);
2205  OCMReject([mockFlutterViewContoller touchesEnded:touches8 withEvent:event8]);
2206 
2207  flutterPlatformViewsController->Reset();
2208 }
2209 
2210 - (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled {
2211  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2212  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2213  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2214  /*platform=*/thread_task_runner,
2215  /*raster=*/thread_task_runner,
2216  /*ui=*/thread_task_runner,
2217  /*io=*/thread_task_runner);
2218  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2219  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2220  /*delegate=*/mock_delegate,
2221  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2224  /*platform_views_controller=*/flutterPlatformViewsController,
2225  /*task_runners=*/runners,
2226  /*worker_task_runner=*/nil,
2227  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2228 
2231  flutterPlatformViewsController->RegisterViewFactory(
2232  factory, @"MockFlutterPlatformView",
2234  FlutterResult result = ^(id result) {
2235  };
2236  flutterPlatformViewsController->OnMethodCall(
2238  methodCallWithMethodName:@"create"
2239  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2240  result);
2241 
2242  XCTAssertNotNil(gMockPlatformView);
2243 
2244  // Find touch inteceptor view
2245  UIView* touchInteceptorView = gMockPlatformView;
2246  while (touchInteceptorView != nil &&
2247  ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
2248  touchInteceptorView = touchInteceptorView.superview;
2249  }
2250  XCTAssertNotNil(touchInteceptorView);
2251 
2252  // Find ForwardGestureRecognizer
2253  UIGestureRecognizer* forwardGectureRecognizer = nil;
2254  for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
2255  if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) {
2256  forwardGectureRecognizer = gestureRecognizer;
2257  break;
2258  }
2259  }
2260  id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]);
2261 
2262  flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller);
2263 
2264  NSSet* touches1 = [NSSet setWithObject:@1];
2265  id event1 = OCMClassMock([UIEvent class]);
2266  [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2267 
2268  [forwardGectureRecognizer touchesCancelled:touches1 withEvent:event1];
2269  OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches1]);
2270 
2271  flutterPlatformViewsController->Reset();
2272 }
2273 
2274 - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashing {
2275  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2276  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2277  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2278  /*platform=*/thread_task_runner,
2279  /*raster=*/thread_task_runner,
2280  /*ui=*/thread_task_runner,
2281  /*io=*/thread_task_runner);
2282  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2283  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2284  /*delegate=*/mock_delegate,
2285  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2288  /*platform_views_controller=*/flutterPlatformViewsController,
2289  /*task_runners=*/runners,
2290  /*worker_task_runner=*/nil,
2291  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2292 
2295  flutterPlatformViewsController->RegisterViewFactory(
2296  factory, @"MockFlutterPlatformView",
2298  FlutterResult result = ^(id result) {
2299  };
2300  flutterPlatformViewsController->OnMethodCall(
2302  methodCallWithMethodName:@"create"
2303  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2304  result);
2305 
2306  XCTAssertNotNil(gMockPlatformView);
2307 
2308  // Create embedded view params
2309  flutter::MutatorsStack stack;
2310  SkMatrix finalMatrix;
2311 
2312  auto embeddedViewParams_1 =
2313  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2314 
2315  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_1));
2316  flutterPlatformViewsController->CompositeEmbeddedView(2);
2317  flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
2318  auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
2319  nullptr, framebuffer_info,
2320  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return false; },
2321  /*frame_size=*/SkISize::Make(800, 600));
2322  XCTAssertFalse(
2323  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
2324 
2325  auto embeddedViewParams_2 =
2326  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2327  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_2));
2328  flutterPlatformViewsController->CompositeEmbeddedView(2);
2329  auto mock_surface_submit_true = std::make_unique<flutter::SurfaceFrame>(
2330  nullptr, framebuffer_info,
2331  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
2332  /*frame_size=*/SkISize::Make(800, 600));
2333  XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr,
2334  std::move(mock_surface_submit_true)));
2335 }
2336 
2337 - (void)
2338  testFlutterPlatformViewControllerResetDeallocsPlatformViewWhenRootViewsNotBindedToFlutterView {
2339  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2340  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2341  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2342  /*platform=*/thread_task_runner,
2343  /*raster=*/thread_task_runner,
2344  /*ui=*/thread_task_runner,
2345  /*io=*/thread_task_runner);
2346  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2347  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2348  /*delegate=*/mock_delegate,
2349  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2352  /*platform_views_controller=*/flutterPlatformViewsController,
2353  /*task_runners=*/runners,
2354  /*worker_task_runner=*/nil,
2355  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2356 
2357  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
2358  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
2359 
2362  flutterPlatformViewsController->RegisterViewFactory(
2363  factory, @"MockFlutterPlatformView",
2365  FlutterResult result = ^(id result) {
2366  };
2367  // autorelease pool to trigger an autorelease for all the root_views_ and touch_interceptors_.
2368  @autoreleasepool {
2369  flutterPlatformViewsController->OnMethodCall(
2371  methodCallWithMethodName:@"create"
2372  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2373  result);
2374 
2375  flutter::MutatorsStack stack;
2376  SkMatrix finalMatrix;
2377  auto embeddedViewParams =
2378  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2379  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
2380  flutterPlatformViewsController->CompositeEmbeddedView(2);
2381  // Not calling |flutterPlatformViewsController::SubmitFrame| so that the platform views are not
2382  // added to flutter_view_.
2383 
2384  XCTAssertNotNil(gMockPlatformView);
2385  flutterPlatformViewsController->Reset();
2386  }
2387  XCTAssertNil(gMockPlatformView);
2388 }
2389 
2390 - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder {
2391  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2392  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2393  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2394  /*platform=*/thread_task_runner,
2395  /*raster=*/thread_task_runner,
2396  /*ui=*/thread_task_runner,
2397  /*io=*/thread_task_runner);
2398  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2399  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2400  /*delegate=*/mock_delegate,
2401  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2404  /*platform_views_controller=*/flutterPlatformViewsController,
2405  /*task_runners=*/runners,
2406  /*worker_task_runner=*/nil,
2407  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2408 
2409  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
2410  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
2411 
2414  flutterPlatformViewsController->RegisterViewFactory(
2415  factory, @"MockFlutterPlatformView",
2417  FlutterResult result = ^(id result) {
2418  };
2419 
2420  flutterPlatformViewsController->OnMethodCall(
2422  methodCallWithMethodName:@"create"
2423  arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}],
2424  result);
2425 
2426  // First frame, |EmbeddedViewCount| is not empty after composite.
2427  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
2428  flutter::MutatorsStack stack;
2429  SkMatrix finalMatrix;
2430  auto embeddedViewParams1 =
2431  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2432  flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
2433  flutterPlatformViewsController->CompositeEmbeddedView(0);
2434  XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL);
2435 
2436  // Second frame, |EmbeddedViewCount| should be empty at the start
2437  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
2438  XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 0UL);
2439 
2440  auto embeddedViewParams2 =
2441  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2442  flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams2));
2443  flutterPlatformViewsController->CompositeEmbeddedView(0);
2444  XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL);
2445 }
2446 
2447 - (void)
2448  testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithDifferentViewHierarchy {
2449  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2450  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2451  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2452  /*platform=*/thread_task_runner,
2453  /*raster=*/thread_task_runner,
2454  /*ui=*/thread_task_runner,
2455  /*io=*/thread_task_runner);
2456  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2457  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2458  /*delegate=*/mock_delegate,
2459  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2462  /*platform_views_controller=*/flutterPlatformViewsController,
2463  /*task_runners=*/runners,
2464  /*worker_task_runner=*/nil,
2465  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2466 
2467  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
2468  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
2469 
2472  flutterPlatformViewsController->RegisterViewFactory(
2473  factory, @"MockFlutterPlatformView",
2475  FlutterResult result = ^(id result) {
2476  };
2477  flutterPlatformViewsController->OnMethodCall(
2479  methodCallWithMethodName:@"create"
2480  arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}],
2481  result);
2482  UIView* view1 = gMockPlatformView;
2483 
2484  // This overwrites `gMockPlatformView` to another view.
2485  flutterPlatformViewsController->OnMethodCall(
2487  methodCallWithMethodName:@"create"
2488  arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
2489  result);
2490  UIView* view2 = gMockPlatformView;
2491 
2492  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
2493  flutter::MutatorsStack stack;
2494  SkMatrix finalMatrix;
2495  auto embeddedViewParams1 =
2496  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2497  flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
2498  flutterPlatformViewsController->CompositeEmbeddedView(0);
2499  auto embeddedViewParams2 =
2500  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(500, 500), stack);
2501  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2));
2502  flutterPlatformViewsController->CompositeEmbeddedView(1);
2503 
2504  // SKSurface is required if the root FlutterView is present.
2505  const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
2506  sk_sp<SkSurface> mock_sk_surface = SkSurfaces::Raster(image_info);
2507  flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
2508  auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
2509  std::move(mock_sk_surface), framebuffer_info,
2510  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
2511  /*frame_size=*/SkISize::Make(800, 600));
2512 
2513  XCTAssertTrue(
2514  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
2515  // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view.
2516  UIView* clippingView1 = view1.superview.superview;
2517  UIView* clippingView2 = view2.superview.superview;
2518  UIView* flutterView = clippingView1.superview;
2519  XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
2520  [flutterView.subviews indexOfObject:clippingView2],
2521  @"The first clipping view should be added before the second clipping view.");
2522 
2523  // Need to recreate these params since they are `std::move`ed.
2524  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
2525  // Process the second frame in the opposite order.
2526  embeddedViewParams2 =
2527  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(500, 500), stack);
2528  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2));
2529  flutterPlatformViewsController->CompositeEmbeddedView(1);
2530  embeddedViewParams1 =
2531  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2532  flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
2533  flutterPlatformViewsController->CompositeEmbeddedView(0);
2534 
2535  mock_sk_surface = SkSurfaces::Raster(image_info);
2536  mock_surface = std::make_unique<flutter::SurfaceFrame>(
2537  std::move(mock_sk_surface), framebuffer_info,
2538  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
2539  /*frame_size=*/SkISize::Make(800, 600));
2540  XCTAssertTrue(
2541  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
2542  XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] >
2543  [flutterView.subviews indexOfObject:clippingView2],
2544  @"The first clipping view should be added after the second clipping view.");
2545 }
2546 
2547 - (void)
2548  testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithSameViewHierarchy {
2549  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2550  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2551  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2552  /*platform=*/thread_task_runner,
2553  /*raster=*/thread_task_runner,
2554  /*ui=*/thread_task_runner,
2555  /*io=*/thread_task_runner);
2556  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2557  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2558  /*delegate=*/mock_delegate,
2559  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2562  /*platform_views_controller=*/flutterPlatformViewsController,
2563  /*task_runners=*/runners,
2564  /*worker_task_runner=*/nil,
2565  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2566 
2567  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
2568  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
2569 
2572  flutterPlatformViewsController->RegisterViewFactory(
2573  factory, @"MockFlutterPlatformView",
2575  FlutterResult result = ^(id result) {
2576  };
2577  flutterPlatformViewsController->OnMethodCall(
2579  methodCallWithMethodName:@"create"
2580  arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}],
2581  result);
2582  UIView* view1 = gMockPlatformView;
2583 
2584  // This overwrites `gMockPlatformView` to another view.
2585  flutterPlatformViewsController->OnMethodCall(
2587  methodCallWithMethodName:@"create"
2588  arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
2589  result);
2590  UIView* view2 = gMockPlatformView;
2591 
2592  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
2593  flutter::MutatorsStack stack;
2594  SkMatrix finalMatrix;
2595  auto embeddedViewParams1 =
2596  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2597  flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
2598  flutterPlatformViewsController->CompositeEmbeddedView(0);
2599  auto embeddedViewParams2 =
2600  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(500, 500), stack);
2601  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2));
2602  flutterPlatformViewsController->CompositeEmbeddedView(1);
2603 
2604  // SKSurface is required if the root FlutterView is present.
2605  const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
2606  sk_sp<SkSurface> mock_sk_surface = SkSurfaces::Raster(image_info);
2607  flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
2608  auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
2609  std::move(mock_sk_surface), framebuffer_info,
2610  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
2611  /*frame_size=*/SkISize::Make(800, 600));
2612 
2613  XCTAssertTrue(
2614  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
2615  // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view.
2616  UIView* clippingView1 = view1.superview.superview;
2617  UIView* clippingView2 = view2.superview.superview;
2618  UIView* flutterView = clippingView1.superview;
2619  XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
2620  [flutterView.subviews indexOfObject:clippingView2],
2621  @"The first clipping view should be added before the second clipping view.");
2622 
2623  // Need to recreate these params since they are `std::move`ed.
2624  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
2625  // Process the second frame in the same order.
2626  embeddedViewParams1 =
2627  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2628  flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
2629  flutterPlatformViewsController->CompositeEmbeddedView(0);
2630  embeddedViewParams2 =
2631  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(500, 500), stack);
2632  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2));
2633  flutterPlatformViewsController->CompositeEmbeddedView(1);
2634 
2635  mock_sk_surface = SkSurfaces::Raster(image_info);
2636  mock_surface = std::make_unique<flutter::SurfaceFrame>(
2637  std::move(mock_sk_surface), framebuffer_info,
2638  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
2639  /*frame_size=*/SkISize::Make(800, 600));
2640  XCTAssertTrue(
2641  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
2642  XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
2643  [flutterView.subviews indexOfObject:clippingView2],
2644  @"The first clipping view should be added before the second clipping view.");
2645 }
2646 
2647 - (void)testThreadMergeAtEndFrame {
2648  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2649  auto thread_task_runner_platform = CreateNewThread("FlutterPlatformViewsTest1");
2650  auto thread_task_runner_other = CreateNewThread("FlutterPlatformViewsTest2");
2651  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2652  /*platform=*/thread_task_runner_platform,
2653  /*raster=*/thread_task_runner_other,
2654  /*ui=*/thread_task_runner_other,
2655  /*io=*/thread_task_runner_other);
2656  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2657  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2658  /*delegate=*/mock_delegate,
2659  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2662  /*platform_views_controller=*/flutterPlatformViewsController,
2663  /*task_runners=*/runners,
2664  /*worker_task_runner=*/nil,
2665  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2666 
2667  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
2668  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
2669 
2672  flutterPlatformViewsController->RegisterViewFactory(
2673  factory, @"MockFlutterPlatformView",
2675  XCTestExpectation* waitForPlatformView =
2676  [self expectationWithDescription:@"wait for platform view to be created"];
2677  FlutterResult result = ^(id result) {
2678  [waitForPlatformView fulfill];
2679  };
2680 
2681  flutterPlatformViewsController->OnMethodCall(
2683  methodCallWithMethodName:@"create"
2684  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2685  result);
2686  [self waitForExpectations:@[ waitForPlatformView ] timeout:30];
2687  XCTAssertNotNil(gMockPlatformView);
2688 
2689  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
2690  SkMatrix finalMatrix;
2691  flutter::MutatorsStack stack;
2692  auto embeddedViewParams =
2693  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2694  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
2695 
2696  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger =
2697  fml::MakeRefCounted<fml::RasterThreadMerger>(thread_task_runner_platform->GetTaskQueueId(),
2698  thread_task_runner_other->GetTaskQueueId());
2699  XCTAssertEqual(flutterPlatformViewsController->PostPrerollAction(raster_thread_merger),
2700  flutter::PostPrerollResult::kSkipAndRetryFrame);
2701  XCTAssertFalse(raster_thread_merger->IsMerged());
2702 
2703  flutterPlatformViewsController->EndFrame(true, raster_thread_merger);
2704  XCTAssertTrue(raster_thread_merger->IsMerged());
2705 
2706  // Unmerge threads before the end of the test
2707  // TaskRunners are required to be unmerged before destruction.
2708  while (raster_thread_merger->DecrementLease() != fml::RasterThreadStatus::kUnmergedNow) {
2709  }
2710 }
2711 
2712 - (int)alphaOfPoint:(CGPoint)point onView:(UIView*)view {
2713  unsigned char pixel[4] = {0};
2714 
2715  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
2716 
2717  // Draw the pixel on `point` in the context.
2718  CGContextRef context = CGBitmapContextCreate(
2719  pixel, 1, 1, 8, 4, colorSpace, kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedLast);
2720  CGContextTranslateCTM(context, -point.x, -point.y);
2721  [view.layer renderInContext:context];
2722 
2723  CGContextRelease(context);
2724  CGColorSpaceRelease(colorSpace);
2725  // Get the alpha from the pixel that we just rendered.
2726  return pixel[3];
2727 }
2728 
2729 - (void)testHasFirstResponderInViewHierarchySubtree_viewItselfBecomesFirstResponder {
2730  // For view to become the first responder, it must be a descendant of a UIWindow
2731  UIWindow* window = [[UIWindow alloc] init];
2732  UITextField* textField = [[UITextField alloc] init];
2733  [window addSubview:textField];
2734 
2735  [textField becomeFirstResponder];
2736  XCTAssertTrue(textField.isFirstResponder);
2737  XCTAssertTrue(textField.flt_hasFirstResponderInViewHierarchySubtree);
2738  [textField resignFirstResponder];
2739  XCTAssertFalse(textField.isFirstResponder);
2740  XCTAssertFalse(textField.flt_hasFirstResponderInViewHierarchySubtree);
2741 }
2742 
2743 - (void)testHasFirstResponderInViewHierarchySubtree_descendantViewBecomesFirstResponder {
2744  // For view to become the first responder, it must be a descendant of a UIWindow
2745  UIWindow* window = [[UIWindow alloc] init];
2746  UIView* view = [[UIView alloc] init];
2747  UIView* childView = [[UIView alloc] init];
2748  UITextField* textField = [[UITextField alloc] init];
2749  [window addSubview:view];
2750  [view addSubview:childView];
2751  [childView addSubview:textField];
2752 
2753  [textField becomeFirstResponder];
2754  XCTAssertTrue(textField.isFirstResponder);
2755  XCTAssertTrue(view.flt_hasFirstResponderInViewHierarchySubtree);
2756  [textField resignFirstResponder];
2757  XCTAssertFalse(textField.isFirstResponder);
2758  XCTAssertFalse(view.flt_hasFirstResponderInViewHierarchySubtree);
2759 }
2760 
2761 - (void)testFlutterClippingMaskViewPoolReuseViewsAfterRecycle {
2762  FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2];
2763  FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero];
2764  FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero];
2765  [pool insertViewToPoolIfNeeded:view1];
2766  [pool insertViewToPoolIfNeeded:view2];
2767  CGRect newRect = CGRectMake(0, 0, 10, 10);
2768  FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:newRect];
2769  FlutterClippingMaskView* view4 = [pool getMaskViewWithFrame:newRect];
2770  // view3 and view4 should randomly get either of view1 and view2.
2771  NSSet* set1 = [NSSet setWithObjects:view1, view2, nil];
2772  NSSet* set2 = [NSSet setWithObjects:view3, view4, nil];
2773  XCTAssertEqualObjects(set1, set2);
2774  XCTAssertTrue(CGRectEqualToRect(view3.frame, newRect));
2775  XCTAssertTrue(CGRectEqualToRect(view4.frame, newRect));
2776 }
2777 
2778 - (void)testFlutterClippingMaskViewPoolAllocsNewMaskViewsAfterReachingCapacity {
2779  FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2];
2780  FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero];
2781  FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero];
2782  FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:CGRectZero];
2783  XCTAssertNotEqual(view1, view3);
2784  XCTAssertNotEqual(view2, view3);
2785 }
2786 
2787 - (void)testMaskViewsReleasedWhenPoolIsReleased {
2788  __weak UIView* weakView;
2789  @autoreleasepool {
2790  FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2];
2791  FlutterClippingMaskView* view = [pool getMaskViewWithFrame:CGRectZero];
2792  weakView = view;
2793  XCTAssertNotNil(weakView);
2794  }
2795  XCTAssertNil(weakView);
2796 }
2797 
2798 - (void)testClipMaskViewIsReused {
2799  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2800  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2801  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2802  /*platform=*/thread_task_runner,
2803  /*raster=*/thread_task_runner,
2804  /*ui=*/thread_task_runner,
2805  /*io=*/thread_task_runner);
2806  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2807  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2808  /*delegate=*/mock_delegate,
2809  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2812  /*platform_views_controller=*/flutterPlatformViewsController,
2813  /*task_runners=*/runners,
2814  /*worker_task_runner=*/nil,
2815  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2816 
2819  flutterPlatformViewsController->RegisterViewFactory(
2820  factory, @"MockFlutterPlatformView",
2822  FlutterResult result = ^(id result) {
2823  };
2824  flutterPlatformViewsController->OnMethodCall(
2826  methodCallWithMethodName:@"create"
2827  arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
2828  result);
2829 
2830  XCTAssertNotNil(gMockPlatformView);
2831  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
2832  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
2833  // Create embedded view params
2834  flutter::MutatorsStack stack1;
2835  // Layer tree always pushes a screen scale factor to the stack
2836  SkMatrix screenScaleMatrix =
2837  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
2838  stack1.PushTransform(screenScaleMatrix);
2839  // Push a clip rect
2840  SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3);
2841  stack1.PushClipRect(rect);
2842 
2843  auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
2844  screenScaleMatrix, SkSize::Make(10, 10), stack1);
2845 
2846  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1));
2847  flutterPlatformViewsController->CompositeEmbeddedView(1);
2848  UIView* childClippingView1 = gMockPlatformView.superview.superview;
2849  UIView* maskView1 = childClippingView1.maskView;
2850  XCTAssertNotNil(maskView1);
2851 
2852  // Composite a new frame.
2853  flutterPlatformViewsController->BeginFrame(SkISize::Make(100, 100));
2854  flutter::MutatorsStack stack2;
2855  auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
2856  screenScaleMatrix, SkSize::Make(10, 10), stack2);
2857  auto embeddedViewParams3 = std::make_unique<flutter::EmbeddedViewParams>(
2858  screenScaleMatrix, SkSize::Make(10, 10), stack2);
2859  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams3));
2860  flutterPlatformViewsController->CompositeEmbeddedView(1);
2861  childClippingView1 = gMockPlatformView.superview.superview;
2862 
2863  // This overrides gMockPlatformView to point to the newly created platform view.
2864  flutterPlatformViewsController->OnMethodCall(
2866  methodCallWithMethodName:@"create"
2867  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2868  result);
2869 
2870  auto embeddedViewParams4 = std::make_unique<flutter::EmbeddedViewParams>(
2871  screenScaleMatrix, SkSize::Make(10, 10), stack1);
2872  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams4));
2873  flutterPlatformViewsController->CompositeEmbeddedView(2);
2874  UIView* childClippingView2 = gMockPlatformView.superview.superview;
2875 
2876  UIView* maskView2 = childClippingView2.maskView;
2877  XCTAssertEqual(maskView1, maskView2);
2878  XCTAssertNotNil(childClippingView2.maskView);
2879  XCTAssertNil(childClippingView1.maskView);
2880 }
2881 
2882 - (void)testDifferentClipMaskViewIsUsedForEachView {
2883  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2884  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2885  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2886  /*platform=*/thread_task_runner,
2887  /*raster=*/thread_task_runner,
2888  /*ui=*/thread_task_runner,
2889  /*io=*/thread_task_runner);
2890  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2891  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2892  /*delegate=*/mock_delegate,
2893  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2896  /*platform_views_controller=*/flutterPlatformViewsController,
2897  /*task_runners=*/runners,
2898  /*worker_task_runner=*/nil,
2899  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2900 
2903  flutterPlatformViewsController->RegisterViewFactory(
2904  factory, @"MockFlutterPlatformView",
2906  FlutterResult result = ^(id result) {
2907  };
2908 
2909  flutterPlatformViewsController->OnMethodCall(
2911  methodCallWithMethodName:@"create"
2912  arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
2913  result);
2914  UIView* view1 = gMockPlatformView;
2915 
2916  // This overwrites `gMockPlatformView` to another view.
2917  flutterPlatformViewsController->OnMethodCall(
2919  methodCallWithMethodName:@"create"
2920  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2921  result);
2922  UIView* view2 = gMockPlatformView;
2923 
2924  XCTAssertNotNil(gMockPlatformView);
2925  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
2926  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
2927  // Create embedded view params
2928  flutter::MutatorsStack stack1;
2929  // Layer tree always pushes a screen scale factor to the stack
2930  SkMatrix screenScaleMatrix =
2931  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
2932  stack1.PushTransform(screenScaleMatrix);
2933  // Push a clip rect
2934  SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3);
2935  stack1.PushClipRect(rect);
2936 
2937  auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
2938  screenScaleMatrix, SkSize::Make(10, 10), stack1);
2939 
2940  flutter::MutatorsStack stack2;
2941  stack2.PushClipRect(rect);
2942  auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
2943  screenScaleMatrix, SkSize::Make(10, 10), stack2);
2944 
2945  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1));
2946  flutterPlatformViewsController->CompositeEmbeddedView(1);
2947  UIView* childClippingView1 = view1.superview.superview;
2948 
2949  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2));
2950  flutterPlatformViewsController->CompositeEmbeddedView(2);
2951  UIView* childClippingView2 = view2.superview.superview;
2952  UIView* maskView1 = childClippingView1.maskView;
2953  UIView* maskView2 = childClippingView2.maskView;
2954  XCTAssertNotEqual(maskView1, maskView2);
2955 }
2956 
2957 // Return true if a correct visual effect view is found. It also implies all the validation in this
2958 // method passes.
2959 //
2960 // There are two fail states for this method. 1. One of the XCTAssert method failed; or 2. No
2961 // correct visual effect view found.
2962 - (BOOL)validateOneVisualEffectView:(UIView*)visualEffectView
2963  expectedFrame:(CGRect)frame
2964  inputRadius:(CGFloat)inputRadius {
2965  XCTAssertTrue(CGRectEqualToRect(visualEffectView.frame, frame));
2966  for (UIView* view in visualEffectView.subviews) {
2967  if (![NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) {
2968  continue;
2969  }
2970  XCTAssertEqual(view.layer.filters.count, 1u);
2971  NSObject* filter = view.layer.filters.firstObject;
2972 
2973  XCTAssertEqualObjects([filter valueForKey:@"name"], @"gaussianBlur");
2974 
2975  NSObject* inputRadiusInFilter = [filter valueForKey:@"inputRadius"];
2976  XCTAssertTrue([inputRadiusInFilter isKindOfClass:[NSNumber class]] &&
2977  flutter::BlurRadiusEqualToBlurRadius(((NSNumber*)inputRadiusInFilter).floatValue,
2978  inputRadius));
2979  return YES;
2980  }
2981  return NO;
2982 }
2983 
2984 - (void)testDisposingViewInCompositionOrderDoNotCrash {
2985  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2986  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2987  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2988  /*platform=*/thread_task_runner,
2989  /*raster=*/thread_task_runner,
2990  /*ui=*/thread_task_runner,
2991  /*io=*/thread_task_runner);
2992  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2993  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2994  /*delegate=*/mock_delegate,
2995  /*rendering_api=*/mock_delegate.settings_.enable_impeller
2998  /*platform_views_controller=*/flutterPlatformViewsController,
2999  /*task_runners=*/runners,
3000  /*worker_task_runner=*/nil,
3001  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3002 
3003  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3004  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
3005 
3008  flutterPlatformViewsController->RegisterViewFactory(
3009  factory, @"MockFlutterPlatformView",
3011  FlutterResult result = ^(id result) {
3012  };
3013 
3014  flutterPlatformViewsController->OnMethodCall(
3016  methodCallWithMethodName:@"create"
3017  arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}],
3018  result);
3019  flutterPlatformViewsController->OnMethodCall(
3021  methodCallWithMethodName:@"create"
3022  arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
3023  result);
3024 
3025  {
3026  // **** First frame, view id 0, 1 in the composition_order_, disposing view 0 is called. **** //
3027  // No view should be disposed, or removed from the composition order.
3028  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
3029  flutter::MutatorsStack stack;
3030  SkMatrix finalMatrix;
3031  auto embeddedViewParams0 =
3032  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3033  flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams0));
3034  flutterPlatformViewsController->CompositeEmbeddedView(0);
3035 
3036  auto embeddedViewParams1 =
3037  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3038  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1));
3039  flutterPlatformViewsController->CompositeEmbeddedView(1);
3040  XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL);
3041 
3042  XCTestExpectation* expectation = [self expectationWithDescription:@"dispose call ended."];
3043  FlutterResult disposeResult = ^(id result) {
3044  [expectation fulfill];
3045  };
3046 
3047  flutterPlatformViewsController->OnMethodCall(
3048  [FlutterMethodCall methodCallWithMethodName:@"dispose" arguments:@0], disposeResult);
3049  [self waitForExpectationsWithTimeout:30 handler:nil];
3050 
3051  const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
3052  sk_sp<SkSurface> mock_sk_surface = SkSurfaces::Raster(image_info);
3053  flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3054  auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3055  std::move(mock_sk_surface), framebuffer_info,
3056  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3057  /*frame_size=*/SkISize::Make(800, 600));
3058  XCTAssertTrue(
3059  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
3060 
3061  // Disposing won't remove embedded views until the view is removed from the composition_order_
3062  XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL);
3063  XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(0));
3064  XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(1));
3065  }
3066 
3067  {
3068  // **** Second frame, view id 1 in the composition_order_, no disposing view is called, **** //
3069  // View 0 is removed from the composition order in this frame, hence also disposed.
3070  flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
3071  flutter::MutatorsStack stack;
3072  SkMatrix finalMatrix;
3073  auto embeddedViewParams1 =
3074  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3075  flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1));
3076  flutterPlatformViewsController->CompositeEmbeddedView(1);
3077 
3078  const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
3079  sk_sp<SkSurface> mock_sk_surface = SkSurfaces::Raster(image_info);
3080  flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3081  auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3082  std::move(mock_sk_surface), framebuffer_info,
3083  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3084  /*frame_size=*/SkISize::Make(800, 600));
3085  XCTAssertTrue(
3086  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
3087 
3088  // Disposing won't remove embedded views until the view is removed from the composition_order_
3089  XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL);
3090  XCTAssertNil(flutterPlatformViewsController->GetPlatformViewByID(0));
3091  XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(1));
3092  }
3093 }
3094 - (void)testOnlyPlatformViewsAreRemovedWhenReset {
3095  flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3096  auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
3097  flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3098  /*platform=*/thread_task_runner,
3099  /*raster=*/thread_task_runner,
3100  /*ui=*/thread_task_runner,
3101  /*io=*/thread_task_runner);
3102  auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
3103  auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3104  /*delegate=*/mock_delegate,
3105  /*rendering_api=*/mock_delegate.settings_.enable_impeller
3108  /*platform_views_controller=*/flutterPlatformViewsController,
3109  /*task_runners=*/runners,
3110  /*worker_task_runner=*/nil,
3111  /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3112 
3115  flutterPlatformViewsController->RegisterViewFactory(
3116  factory, @"MockFlutterPlatformView",
3118  FlutterResult result = ^(id result) {
3119  };
3120  flutterPlatformViewsController->OnMethodCall(
3122  methodCallWithMethodName:@"create"
3123  arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
3124  result);
3125  UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3126  flutterPlatformViewsController->SetFlutterView(mockFlutterView);
3127  // Create embedded view params
3128  flutter::MutatorsStack stack;
3129  // Layer tree always pushes a screen scale factor to the stack
3130  SkMatrix screenScaleMatrix =
3131  SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
3132  stack.PushTransform(screenScaleMatrix);
3133  // Push a translate matrix
3134  SkMatrix translateMatrix = SkMatrix::Translate(100, 100);
3135  stack.PushTransform(translateMatrix);
3136  SkMatrix finalMatrix;
3137  finalMatrix.setConcat(screenScaleMatrix, translateMatrix);
3138 
3139  auto embeddedViewParams =
3140  std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3141 
3142  flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
3143  flutterPlatformViewsController->CompositeEmbeddedView(2);
3144 
3145  // SKSurface is required if the root FlutterView is present.
3146  const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
3147  sk_sp<SkSurface> mock_sk_surface = SkSurfaces::Raster(image_info);
3148  flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3149  auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3150  std::move(mock_sk_surface), framebuffer_info,
3151  [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3152  /*frame_size=*/SkISize::Make(800, 600));
3153 
3154  flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface));
3155 
3156  UIView* someView = [[UIView alloc] init];
3157  [mockFlutterView addSubview:someView];
3158 
3159  flutterPlatformViewsController->Reset();
3160  XCTAssertEqual(mockFlutterView.subviews.count, 1u);
3161  XCTAssertEqual(mockFlutterView.subviews.firstObject, someView);
3162 }
3163 
3164 - (void)testFlutterTouchInterceptingViewLinksToAccessibilityContainer {
3165  FlutterTouchInterceptingView* touchInteceptorView = [[FlutterTouchInterceptingView alloc] init];
3166  NSObject* container = [[NSObject alloc] init];
3167  [touchInteceptorView setFlutterAccessibilityContainer:container];
3168  XCTAssertEqualObjects([touchInteceptorView accessibilityContainer], container);
3169 }
3170 
3171 @end
gMockPlatformView
static __weak FlutterPlatformViewsTestMockPlatformView * gMockPlatformView
Definition: FlutterPlatformViewsTest.mm:21
FlutterPlatformViews.h
FlutterViewController
Definition: FlutterViewController.h:56
FlutterPlatformViewsTestMockFlutterPlatformView::viewCreated
BOOL viewCreated
Definition: FlutterPlatformViewsTest.mm:44
FlutterPlatformViewsTestMockPlatformView
Definition: FlutterPlatformViewsTest.mm:24
flutter::BlurRadiusEqualToBlurRadius
BOOL BlurRadiusEqualToBlurRadius(CGFloat radius1, CGFloat radius2)
Definition: FlutterPlatformViews_Internal.mm:67
FLUTTER_ASSERT_ARC::CreateNewThread
fml::RefPtr< fml::TaskRunner > CreateNewThread(const std::string &name)
Definition: VsyncWaiterIosTest.mm:16
FlutterMacros.h
platform_view
std::unique_ptr< flutter::PlatformViewIOS > platform_view
Definition: FlutterEnginePlatformViewTest.mm:61
-[ChildClippingView backdropFilterSubviews]
NSMutableArray * backdropFilterSubviews()
Definition: FlutterPlatformViews_Internal.mm:237
FlutterPlatformViewsTestMockFlutterPlatformFactory
Definition: FlutterPlatformViewsTest.mm:71
-[FlutterTouchInterceptingView setFlutterAccessibilityContainer:]
void setFlutterAccessibilityContainer:(NSObject *flutterAccessibilityContainer)
Definition: FlutterPlatformViews.mm:1074
FlutterMethodCall
Definition: FlutterCodecs.h:220
FlutterPlatformViewsTest
Definition: FlutterPlatformViewsTest.mm:130
FlutterPlatformViewGestureRecognizersBlockingPolicyEager
@ FlutterPlatformViewGestureRecognizersBlockingPolicyEager
Definition: FlutterPlugin.h:261
flutter
Definition: accessibility_bridge.h:28
FlutterBinaryMessenger.h
FlutterPlatformViews_Internal.h
FlutterPlatformViewsTestMockFlutterPlatformView::view
UIView * view
Definition: FlutterPlatformViewsTest.mm:43
settings_
flutter::Settings settings_
Definition: FlutterEnginePlatformViewTest.mm:51
FlutterResult
void(^ FlutterResult)(id _Nullable result)
Definition: FlutterChannels.h:194
FlutterClippingMaskViewPool
Definition: FlutterPlatformViews_Internal.h:60
-[FlutterClippingMaskViewPool insertViewToPoolIfNeeded:]
void insertViewToPoolIfNeeded:(FlutterClippingMaskView *maskView)
flutter::IOSRenderingAPI::kMetal
@ kMetal
-[FlutterClippingMaskViewPool getMaskViewWithFrame:]
FlutterClippingMaskView * getMaskViewWithFrame:(CGRect frame)
ChildClippingView
Definition: FlutterPlatformViews_Internal.h:117
FlutterViewController_Internal.h
+[PlatformViewFilter resetPreparation]
void resetPreparation()
Definition: FlutterPlatformViews_Internal.mm:114
FlutterTouchInterceptingView
Definition: FlutterPlatformViews.mm:988
platform_view_ios.h
FlutterTouchInterceptingView_Test.h
FlutterPlatformView-p
Definition: FlutterPlatformViews.h:18
PlatformViewFilter
Definition: FlutterPlatformViews_Internal.h:78
texture_id
int64_t texture_id
Definition: texture_registrar_unittests.cc:24
flutter::IOSRenderingAPI::kSoftware
@ kSoftware
FLUTTER_ASSERT_ARC
Definition: VsyncWaiterIosTest.mm:15
FlutterPlatformViewsTestMockFlutterPlatformView
Definition: FlutterPlatformViewsTest.mm:42
FlutterViewController.h
kFloatCompareEpsilon
const float kFloatCompareEpsilon
Definition: FlutterPlatformViewsTest.mm:22
FlutterClippingMaskView
Definition: FlutterPlatformViews_Internal.h:29