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