| Flutter iOS Embedder |

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