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