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