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