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