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