| Flutter iOS Embedder |

FlutterPlatformViewsTest.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h"
6
7 #import <OCMock/OCMock.h>
8 #import <UIKit/UIKit.h>
9 #import <WebKit/WebKit.h>
10 #import <XCTest/XCTest.h>
11
12 #include <memory>
13
14 #include "flutter/display_list/effects/dl_image_filters.h"
15 #include "flutter/fml/synchronization/count_down_latch.h"
16 #include "flutter/fml/thread.h"
17 #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
18 #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
19 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h"
20 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsController.h"
21 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h"
22 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTouchInterceptingView_Test.h"
23 #include "flutter/shell/platform/darwin/ios/ios_context_noop.h"
24 #include "flutter/shell/platform/darwin/ios/platform_view_ios.h"
25
26 FLUTTER_ASSERT_ARC
27
28 @class FlutterPlatformViewsTestMockPlatformView;
29 __weak static UIView* gMockPlatformView = nil;
30 const float kFloatCompareEpsilon = 0.001;
31
32 @interface FlutterPlatformViewsTestMockPlatformView : UIView
33 @end
34 @implementation FlutterPlatformViewsTestMockPlatformView
35
36 - (instancetype)init {
37 self = [super init];
38 if (self) {
39 gMockPlatformView = self;
40 }
41 return self;
42 }
43
44 - (void)dealloc {
45 gMockPlatformView = nil;
46 }
47
48 @end
49
50 @interface FlutterPlatformViewsTestMockFlutterPlatformView : NSObject <FlutterPlatformView>
51 @property(nonatomic, strong) UIView* view;
52 @property(nonatomic, assign) BOOL viewCreated;
53 @end
54
55 @implementation FlutterPlatformViewsTestMockFlutterPlatformView
56
57 - (instancetype)init {
58 if (self = [super init]) {
59 _view = [[FlutterPlatformViewsTestMockPlatformView alloc] init];
60 _viewCreated = NO;
61 }
62 return self;
63 }
64
65 - (UIView*)view {
66 [self checkViewCreatedOnce];
67 return _view;
68 }
69
70 - (void)checkViewCreatedOnce {
71 if (self.viewCreated) {
72 abort();
73 }
74 self.viewCreated = YES;
75 }
76
77 - (void)dealloc {
78 gMockPlatformView = nil;
79 }
80 @end
81
82 @interface FlutterPlatformViewsTestMockFlutterPlatformFactory
83 : NSObject <FlutterPlatformViewFactory>
84 @end
85
86 @implementation FlutterPlatformViewsTestMockFlutterPlatformFactory
87 - (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
88 viewIdentifier:(int64_t)viewId
89 arguments:(id _Nullable)args {
90 return [[FlutterPlatformViewsTestMockFlutterPlatformView alloc] init];
91 }
92
93 @end
94
95 @interface FlutterPlatformViewsTestMockWebView : NSObject <FlutterPlatformView>
96 @property(nonatomic, strong) UIView* view;
97 @property(nonatomic, assign) BOOL viewCreated;
98 @end
99
100 @implementation FlutterPlatformViewsTestMockWebView
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
127 @interface FlutterPlatformViewsTestMockWebViewFactory : NSObject <FlutterPlatformViewFactory>
128 @end
129
130 @implementation FlutterPlatformViewsTestMockWebViewFactory
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
138 @interface FlutterPlatformViewsTestNilFlutterPlatformFactory : NSObject <FlutterPlatformViewFactory>
139 @end
140
141 @implementation FlutterPlatformViewsTestNilFlutterPlatformFactory
142 - (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
143 viewIdentifier:(int64_t)viewId
144 arguments:(id _Nullable)args {
145 return nil;
146 }
147
148 @end
149
150 @interface FlutterPlatformViewsTestMockWrapperWebView : NSObject <FlutterPlatformView>
151 @property(nonatomic, strong) UIView* view;
152 @property(nonatomic, assign) BOOL viewCreated;
153 @end
154
155 @implementation FlutterPlatformViewsTestMockWrapperWebView
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
183 @interface FlutterPlatformViewsTestMockWrapperWebViewFactory : NSObject <FlutterPlatformViewFactory>
184 @end
185
186 @implementation FlutterPlatformViewsTestMockWrapperWebViewFactory
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
194 @interface FlutterPlatformViewsTestMockNestedWrapperWebView : NSObject <FlutterPlatformView>
195 @property(nonatomic, strong) UIView* view;
196 @property(nonatomic, assign) BOOL viewCreated;
197 @end
198
199 @implementation FlutterPlatformViewsTestMockNestedWrapperWebView
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
225 @interface FlutterPlatformViewsTestMockNestedWrapperWebViewFactory
226 : NSObject <FlutterPlatformViewFactory>
227 @end
228
229 @implementation FlutterPlatformViewsTestMockNestedWrapperWebViewFactory
230 - (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
231 viewIdentifier:(int64_t)viewId
232 arguments:(id _Nullable)args {
233 return [[FlutterPlatformViewsTestMockNestedWrapperWebView alloc] init];
234 }
235 @end
236
237 namespace flutter {
238 namespace {
239 class FlutterPlatformViewsTestMockPlatformViewDelegate : public PlatformView::Delegate {
240 public:
241 void OnPlatformViewCreated(std::unique_ptr<Surface> surface) override {}
242 void OnPlatformViewDestroyed() override {}
243 void OnPlatformViewScheduleFrame() override {}
244 void OnPlatformViewAddView(int64_t view_id,
245 const ViewportMetrics& viewport_metrics,
246 AddViewCallback callback) override {}
247 void OnPlatformViewRemoveView(int64_t view_id, RemoveViewCallback callback) override {}
248 void 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,
273 flutter::AssetResolver::AssetResolverType type) override {}
274
275 flutter::Settings settings_;
276 };
277
278 BOOL 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
291 namespace {
292 fml::RefPtr<fml::TaskRunner> GetDefaultTaskRunner() {
293 fml::MessageLoop::EnsureInitializedForCurrentThread();
294 return fml::MessageLoop::GetCurrent().GetTaskRunner();
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
317 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
318 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
319 [flutterPlatformViewsController
320 registerViewFactory:factory
321 withId:@"MockFlutterPlatformView"
322 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
323 FlutterResult result = ^(id result) {
324 };
325 [flutterPlatformViewsController
326 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
327 arguments:@{
328 @"id" : @2,
329 @"viewType" : @"MockFlutterPlatformView"
330 }]
331 result:result];
332 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
333 flutterPlatformViewsController.flutterView = flutterView;
334 // Create embedded view params
335 flutter::MutatorsStack stack;
336 // Layer tree always pushes a screen scale factor to the stack
337 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 = std::make_unique<flutter::EmbeddedViewParams>(
346 flutter::ToSkMatrix(finalMatrix), SkSize::Make(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
375 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
376 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
377 [flutterPlatformViewsController
378 registerViewFactory:factory
379 withId:@"MockFlutterPlatformView"
380 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
381 FlutterResult result = ^(id result) {
382 };
383 [flutterPlatformViewsController
384 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
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
470 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
471 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
472 [flutterPlatformViewsController
473 registerViewFactory:factory
474 withId:@"MockFlutterPlatformView"
475 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
476 FlutterResult result = ^(id result) {
477 };
478 [flutterPlatformViewsController
479 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
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
491 flutter::MutatorsStack stack;
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
497 auto filter = flutter::DlBlurImageFilter::Make(5, 2, flutter::DlTileMode::kClamp);
498 stack.PushBackdropFilter(filter,
499 flutter::DlRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
500
501 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
502 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack);
503
504 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
505 withParams:std::move(embeddedViewParams)];
506 [flutterPlatformViewsController
507 compositeView:2
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
552 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
553 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
554 [flutterPlatformViewsController
555 registerViewFactory:factory
556 withId:@"MockFlutterPlatformView"
557 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
558 FlutterResult result = ^(id result) {
559 };
560 [flutterPlatformViewsController
561 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
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
573 flutter::MutatorsStack stack;
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
579 auto filter = flutter::DlBlurImageFilter::Make(5, 2, flutter::DlTileMode::kClamp);
580 stack.PushBackdropFilter(filter,
581 flutter::DlRect::MakeXYWH(0, 0, screenScale * 8, screenScale * 8));
582
583 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
584 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(5, 10), stack);
585
586 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
587 withParams:std::move(embeddedViewParams)];
588 [flutterPlatformViewsController
589 compositeView:2
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
634 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
635 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
636 [flutterPlatformViewsController
637 registerViewFactory:factory
638 withId:@"MockFlutterPlatformView"
639 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
640 FlutterResult result = ^(id result) {
641 };
642 [flutterPlatformViewsController
643 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
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
655 flutter::MutatorsStack stack;
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++) {
662 auto filter = flutter::DlBlurImageFilter::Make(i, 2, flutter::DlTileMode::kClamp);
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 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(20, 20), stack);
669
670 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
671 withParams:std::move(embeddedViewParams)];
672 [flutterPlatformViewsController
673 compositeView:2
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
717 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
718 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
719 [flutterPlatformViewsController
720 registerViewFactory:factory
721 withId:@"MockFlutterPlatformView"
722 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
723 FlutterResult result = ^(id result) {
724 };
725 [flutterPlatformViewsController
726 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
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
738 flutter::MutatorsStack stack;
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
744 auto filter = flutter::DlBlurImageFilter::Make(5, 2, flutter::DlTileMode::kClamp);
745 stack.PushBackdropFilter(filter,
746 flutter::DlRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10));
747
748 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
749 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack);
750
751 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
752 withParams:std::move(embeddedViewParams)];
753 [flutterPlatformViewsController
754 compositeView:2
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
781 flutter::MutatorsStack stack2;
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 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
792
793 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
794 withParams:std::move(embeddedViewParams)];
795 [flutterPlatformViewsController
796 compositeView:2
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
846 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
847 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
848 [flutterPlatformViewsController
849 registerViewFactory:factory
850 withId:@"MockFlutterPlatformView"
851 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
852 FlutterResult result = ^(id result) {
853 };
854 [flutterPlatformViewsController
855 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
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
867 flutter::MutatorsStack stack;
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
873 auto filter = flutter::DlBlurImageFilter::Make(5, 2, flutter::DlTileMode::kClamp);
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 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack);
881
882 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
883 withParams:std::move(embeddedViewParams)];
884 [flutterPlatformViewsController
885 compositeView:2
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
910 flutter::MutatorsStack stack2;
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 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
921
922 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
923 withParams:std::move(embeddedViewParams)];
924 [flutterPlatformViewsController
925 compositeView:2
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 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
964
965 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
966 withParams:std::move(embeddedViewParams)];
967 [flutterPlatformViewsController
968 compositeView:2
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
1002 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
1003 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
1004 [flutterPlatformViewsController
1005 registerViewFactory:factory
1006 withId:@"MockFlutterPlatformView"
1007 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
1008 FlutterResult result = ^(id result) {
1009 };
1010 [flutterPlatformViewsController
1011 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
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
1023 flutter::MutatorsStack stack;
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
1029 auto filter = flutter::DlBlurImageFilter::Make(5, 2, flutter::DlTileMode::kClamp);
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 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack);
1037
1038 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
1039 withParams:std::move(embeddedViewParams)];
1040 [flutterPlatformViewsController
1041 compositeView:2
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
1066 flutter::MutatorsStack stack2;
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) {
1072 auto filter2 = flutter::DlBlurImageFilter::Make(2, 5, flutter::DlTileMode::kClamp);
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 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
1085
1086 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
1087 withParams:std::move(embeddedViewParams)];
1088 [flutterPlatformViewsController
1089 compositeView:2
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) {
1131 auto filter2 = flutter::DlBlurImageFilter::Make(2, 5, flutter::DlTileMode::kClamp);
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 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
1143
1144 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
1145 withParams:std::move(embeddedViewParams)];
1146 [flutterPlatformViewsController
1147 compositeView:2
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) {
1187 auto filter2 = flutter::DlBlurImageFilter::Make(2, 5, flutter::DlTileMode::kClamp);
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 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
1199
1200 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
1201 withParams:std::move(embeddedViewParams)];
1202 [flutterPlatformViewsController
1203 compositeView:2
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++) {
1244 auto filter2 = flutter::DlBlurImageFilter::Make(i, 2, flutter::DlTileMode::kClamp);
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 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
1252
1253 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
1254 withParams:std::move(embeddedViewParams)];
1255 [flutterPlatformViewsController
1256 compositeView:2
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
1306 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
1307 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
1308 [flutterPlatformViewsController
1309 registerViewFactory:factory
1310 withId:@"MockFlutterPlatformView"
1311 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
1312 FlutterResult result = ^(id result) {
1313 };
1314 [flutterPlatformViewsController
1315 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
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
1327 flutter::MutatorsStack stack;
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 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack);
1338
1339 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
1340 withParams:std::move(embeddedViewParams)];
1341 [flutterPlatformViewsController
1342 compositeView:2
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
1363 flutter::MutatorsStack stack2;
1364 // Layer tree always pushes a screen scale factor to the stack
1365 stack2.PushTransform(screenScaleMatrix);
1366 // Push backdrop filters and dilate filter
1367 auto blurFilter = flutter::DlBlurImageFilter::Make(5, 2, flutter::DlTileMode::kClamp);
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 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
1382
1383 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
1384 withParams:std::move(embeddedViewParams)];
1385 [flutterPlatformViewsController
1386 compositeView:2
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 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
1425
1426 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
1427 withParams:std::move(embeddedViewParams)];
1428 [flutterPlatformViewsController
1429 compositeView:2
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 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
1468
1469 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
1470 withParams:std::move(embeddedViewParams)];
1471 [flutterPlatformViewsController
1472 compositeView:2
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 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
1505
1506 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
1507 withParams:std::move(embeddedViewParams)];
1508 [flutterPlatformViewsController
1509 compositeView:2
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 {
1525 [PlatformViewFilter resetPreparation];
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 {
1539 [PlatformViewFilter resetPreparation];
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 {
1549 [PlatformViewFilter resetPreparation];
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 {
1572 [PlatformViewFilter resetPreparation];
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
1637 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
1638 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
1639 [flutterPlatformViewsController
1640 registerViewFactory:factory
1641 withId:@"MockFlutterPlatformView"
1642 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
1643 FlutterResult result = ^(id result) {
1644 };
1645 [flutterPlatformViewsController
1646 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
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
1658 flutter::MutatorsStack stack;
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 = std::make_unique<flutter::EmbeddedViewParams>(
1669 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
1670
1671 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
1672 withParams:std::move(embeddedViewParams)];
1673 [flutterPlatformViewsController
1674 compositeView:2
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
1701 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
1702 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
1703 [flutterPlatformViewsController
1704 registerViewFactory:factory
1705 withId:@"MockFlutterPlatformView"
1706 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
1707 FlutterResult result = ^(id result) {
1708 };
1709 [flutterPlatformViewsController
1710 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
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
1722 flutter::MutatorsStack stack;
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 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack);
1730
1731 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(0, 0)];
1732 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
1733 withParams:std::move(embeddedViewParams)];
1734 [flutterPlatformViewsController pushVisitedPlatformViewId:2];
1735 auto filter = flutter::DlBlurImageFilter::Make(5, 2, flutter::DlTileMode::kClamp);
1736 [flutterPlatformViewsController
1737 pushFilterToVisitedPlatformViews:filter
1738 withRect:SkRect::MakeXYWH(0, 0, screenScale * 10, screenScale * 10)];
1739 [flutterPlatformViewsController
1740 compositeView:2
1741 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
1742
1743 XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]);
1744 ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1745 [flutterView addSubview:childClippingView];
1746
1747 [flutterView setNeedsLayout];
1748 [flutterView layoutIfNeeded];
1749
1750 // childClippingView has visual effect view with the correct configurations.
1751 NSUInteger numberOfExpectedVisualEffectView = 0;
1752 for (UIView* subview in childClippingView.subviews) {
1753 if (![subview isKindOfClass:[UIVisualEffectView class]]) {
1754 continue;
1755 }
1756 XCTAssertLessThan(numberOfExpectedVisualEffectView, 1u);
1757 if ([self validateOneVisualEffectView:subview
1758 expectedFrame:CGRectMake(0, 0, 10, 10)
1759 inputRadius:5]) {
1760 numberOfExpectedVisualEffectView++;
1761 }
1762 }
1763 XCTAssertEqual(numberOfExpectedVisualEffectView, 1u);
1764
1765 // New frame, with no filter pushed.
1766 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
1767 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack);
1768 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(0, 0)];
1769 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
1770 withParams:std::move(embeddedViewParams2)];
1771 [flutterPlatformViewsController
1772 compositeView:2
1773 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
1774
1775 XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:[ChildClippingView class]]);
1776
1777 [flutterView setNeedsLayout];
1778 [flutterView layoutIfNeeded];
1779
1780 numberOfExpectedVisualEffectView = 0;
1781 for (UIView* subview in childClippingView.subviews) {
1782 if (![subview isKindOfClass:[UIVisualEffectView class]]) {
1783 continue;
1784 }
1785 numberOfExpectedVisualEffectView++;
1786 }
1787 XCTAssertEqual(numberOfExpectedVisualEffectView, 0u);
1788 }
1789
1790 - (void)testChildClippingViewShouldBeTheBoundingRectOfPlatformView {
1791 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1792
1793 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1794 /*platform=*/GetDefaultTaskRunner(),
1795 /*raster=*/GetDefaultTaskRunner(),
1796 /*ui=*/GetDefaultTaskRunner(),
1797 /*io=*/GetDefaultTaskRunner());
1798 FlutterPlatformViewsController* flutterPlatformViewsController =
1799 [[FlutterPlatformViewsController alloc] init];
1800 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
1801 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1802 /*delegate=*/mock_delegate,
1803 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
1804 /*platform_views_controller=*/flutterPlatformViewsController,
1805 /*task_runners=*/runners,
1806 /*worker_task_runner=*/nil,
1807 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1808
1809 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
1810 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
1811 [flutterPlatformViewsController
1812 registerViewFactory:factory
1813 withId:@"MockFlutterPlatformView"
1814 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
1815 FlutterResult result = ^(id result) {
1816 };
1817 [flutterPlatformViewsController
1818 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
1819 arguments:@{
1820 @"id" : @2,
1821 @"viewType" : @"MockFlutterPlatformView"
1822 }]
1823 result:result];
1824
1825 XCTAssertNotNil(gMockPlatformView);
1826
1827 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
1828 flutterPlatformViewsController.flutterView = flutterView;
1829 // Create embedded view params
1830 flutter::MutatorsStack stack;
1831 // Layer tree always pushes a screen scale factor to the stack
1832 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
1833 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
1834 stack.PushTransform(screenScaleMatrix);
1835 // Push a rotate matrix
1836 flutter::DlMatrix rotateMatrix = flutter::DlMatrix::MakeRotationZ(flutter::DlDegrees(10));
1837 stack.PushTransform(rotateMatrix);
1838 flutter::DlMatrix finalMatrix = screenScaleMatrix * rotateMatrix;
1839
1840 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
1841 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
1842
1843 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
1844 withParams:std::move(embeddedViewParams)];
1845 [flutterPlatformViewsController
1846 compositeView:2
1847 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
1848
1849 CGRect platformViewRectInFlutterView = [gMockPlatformView convertRect:gMockPlatformView.bounds
1850 toView:flutterView];
1851 XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
1852 ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1853 // The childclippingview's frame is set based on flow, but the platform view's frame is set based
1854 // on quartz. Although they should be the same, but we should tolerate small floating point
1855 // errors.
1856 XCTAssertLessThan(fabs(platformViewRectInFlutterView.origin.x - childClippingView.frame.origin.x),
1857 kFloatCompareEpsilon);
1858 XCTAssertLessThan(fabs(platformViewRectInFlutterView.origin.y - childClippingView.frame.origin.y),
1859 kFloatCompareEpsilon);
1860 XCTAssertLessThan(
1861 fabs(platformViewRectInFlutterView.size.width - childClippingView.frame.size.width),
1862 kFloatCompareEpsilon);
1863 XCTAssertLessThan(
1864 fabs(platformViewRectInFlutterView.size.height - childClippingView.frame.size.height),
1865 kFloatCompareEpsilon);
1866 }
1867
1868 - (void)testClipsDoNotInterceptWithPlatformViewShouldNotAddMaskView {
1869 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1870
1871 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1872 /*platform=*/GetDefaultTaskRunner(),
1873 /*raster=*/GetDefaultTaskRunner(),
1874 /*ui=*/GetDefaultTaskRunner(),
1875 /*io=*/GetDefaultTaskRunner());
1876 FlutterPlatformViewsController* flutterPlatformViewsController =
1877 [[FlutterPlatformViewsController alloc] init];
1878 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
1879 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1880 /*delegate=*/mock_delegate,
1881 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
1882 /*platform_views_controller=*/flutterPlatformViewsController,
1883 /*task_runners=*/runners,
1884 /*worker_task_runner=*/nil,
1885 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1886
1887 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
1888 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
1889 [flutterPlatformViewsController
1890 registerViewFactory:factory
1891 withId:@"MockFlutterPlatformView"
1892 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
1893 FlutterResult result = ^(id result) {
1894 };
1895 [flutterPlatformViewsController
1896 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
1897 arguments:@{
1898 @"id" : @2,
1899 @"viewType" : @"MockFlutterPlatformView"
1900 }]
1901 result:result];
1902
1903 XCTAssertNotNil(gMockPlatformView);
1904
1905 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
1906 flutterPlatformViewsController.flutterView = flutterView;
1907 // Create embedded view params.
1908 flutter::MutatorsStack stack;
1909 // Layer tree always pushes a screen scale factor to the stack.
1910 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
1911 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
1912 stack.PushTransform(screenScaleMatrix);
1913 flutter::DlMatrix translateMatrix = flutter::DlMatrix::MakeTranslation({5, 5});
1914 // The platform view's rect for this test will be (5, 5, 10, 10).
1915 stack.PushTransform(translateMatrix);
1916 // Push a clip rect, big enough to contain the entire platform view bound.
1917 flutter::DlRect rect = flutter::DlRect::MakeXYWH(0, 0, 25, 25);
1918 stack.PushClipRect(rect);
1919 // Push a clip rrect, big enough to contain the entire platform view bound without clipping it.
1920 // Make the origin (-1, -1) so that the top left rounded corner isn't clipping the PlatformView.
1921 flutter::DlRect rect_for_rrect = flutter::DlRect::MakeXYWH(-1, -1, 25, 25);
1922 flutter::DlRoundRect rrect = flutter::DlRoundRect::MakeRectXY(rect_for_rrect, 1, 1);
1923 stack.PushClipRRect(rrect);
1924
1925 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
1926 flutter::ToSkMatrix(screenScaleMatrix * translateMatrix), SkSize::Make(5, 5), stack);
1927
1928 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
1929 withParams:std::move(embeddedViewParams)];
1930 [flutterPlatformViewsController
1931 compositeView:2
1932 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
1933
1934 gMockPlatformView.backgroundColor = UIColor.redColor;
1935 XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
1936 ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1937 [flutterView addSubview:childClippingView];
1938
1939 [flutterView setNeedsLayout];
1940 [flutterView layoutIfNeeded];
1941 XCTAssertNil(childClippingView.maskView);
1942 }
1943
1944 - (void)testClipRRectOnlyHasCornersInterceptWithPlatformViewShouldAddMaskView {
1945 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1946
1947 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1948 /*platform=*/GetDefaultTaskRunner(),
1949 /*raster=*/GetDefaultTaskRunner(),
1950 /*ui=*/GetDefaultTaskRunner(),
1951 /*io=*/GetDefaultTaskRunner());
1952 FlutterPlatformViewsController* flutterPlatformViewsController =
1953 [[FlutterPlatformViewsController alloc] init];
1954 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
1955 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1956 /*delegate=*/mock_delegate,
1957 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
1958 /*platform_views_controller=*/flutterPlatformViewsController,
1959 /*task_runners=*/runners,
1960 /*worker_task_runner=*/nil,
1961 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1962
1963 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
1964 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
1965 [flutterPlatformViewsController
1966 registerViewFactory:factory
1967 withId:@"MockFlutterPlatformView"
1968 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
1969 FlutterResult result = ^(id result) {
1970 };
1971 [flutterPlatformViewsController
1972 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
1973 arguments:@{
1974 @"id" : @2,
1975 @"viewType" : @"MockFlutterPlatformView"
1976 }]
1977 result:result];
1978
1979 XCTAssertNotNil(gMockPlatformView);
1980
1981 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
1982 flutterPlatformViewsController.flutterView = flutterView;
1983 // Create embedded view params
1984 flutter::MutatorsStack stack;
1985 // Layer tree always pushes a screen scale factor to the stack.
1986 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
1987 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
1988 stack.PushTransform(screenScaleMatrix);
1989 flutter::DlMatrix translateMatrix = flutter::DlMatrix::MakeTranslation({5, 5});
1990 // The platform view's rect for this test will be (5, 5, 10, 10).
1991 stack.PushTransform(translateMatrix);
1992
1993 // Push a clip rrect, the rect of the rrect is the same as the PlatformView of the corner should.
1994 // clip the PlatformView.
1995 flutter::DlRect rect_for_rrect = flutter::DlRect::MakeXYWH(0, 0, 10, 10);
1996 flutter::DlRoundRect rrect = flutter::DlRoundRect::MakeRectXY(rect_for_rrect, 1, 1);
1997 stack.PushClipRRect(rrect);
1998
1999 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
2000 flutter::ToSkMatrix(screenScaleMatrix * translateMatrix), SkSize::Make(5, 5), stack);
2001
2002 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
2003 withParams:std::move(embeddedViewParams)];
2004 [flutterPlatformViewsController
2005 compositeView:2
2006 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
2007
2008 gMockPlatformView.backgroundColor = UIColor.redColor;
2009 XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
2010 ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
2011 [flutterView addSubview:childClippingView];
2012
2013 [flutterView setNeedsLayout];
2014 [flutterView layoutIfNeeded];
2015
2016 XCTAssertNotNil(childClippingView.maskView);
2017 }
2018
2019 - (void)testClipRect {
2020 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2021
2022 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2023 /*platform=*/GetDefaultTaskRunner(),
2024 /*raster=*/GetDefaultTaskRunner(),
2025 /*ui=*/GetDefaultTaskRunner(),
2026 /*io=*/GetDefaultTaskRunner());
2027 FlutterPlatformViewsController* flutterPlatformViewsController =
2028 [[FlutterPlatformViewsController alloc] init];
2029 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
2030 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2031 /*delegate=*/mock_delegate,
2032 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
2033 /*platform_views_controller=*/flutterPlatformViewsController,
2034 /*task_runners=*/runners,
2035 /*worker_task_runner=*/nil,
2036 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2037
2038 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2039 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2040 [flutterPlatformViewsController
2041 registerViewFactory:factory
2042 withId:@"MockFlutterPlatformView"
2043 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
2044 FlutterResult result = ^(id result) {
2045 };
2046 [flutterPlatformViewsController
2047 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
2048 arguments:@{
2049 @"id" : @2,
2050 @"viewType" : @"MockFlutterPlatformView"
2051 }]
2052 result:result];
2053
2054 XCTAssertNotNil(gMockPlatformView);
2055
2056 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
2057 flutterPlatformViewsController.flutterView = flutterView;
2058 // Create embedded view params
2059 flutter::MutatorsStack stack;
2060 // Layer tree always pushes a screen scale factor to the stack
2061 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
2062 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
2063 stack.PushTransform(screenScaleMatrix);
2064 // Push a clip rect
2065 flutter::DlRect rect = flutter::DlRect::MakeXYWH(2, 2, 3, 3);
2066 stack.PushClipRect(rect);
2067
2068 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
2069 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack);
2070
2071 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
2072 withParams:std::move(embeddedViewParams)];
2073 [flutterPlatformViewsController
2074 compositeView:2
2075 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
2076
2077 gMockPlatformView.backgroundColor = UIColor.redColor;
2078 XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
2079 ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
2080 [flutterView addSubview:childClippingView];
2081
2082 [flutterView setNeedsLayout];
2083 [flutterView layoutIfNeeded];
2084
2085 CGRect insideClipping = CGRectMake(2, 2, 3, 3);
2086 for (int i = 0; i < 10; i++) {
2087 for (int j = 0; j < 10; j++) {
2088 CGPoint point = CGPointMake(i, j);
2089 int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView];
2090 if (CGRectContainsPoint(insideClipping, point)) {
2091 XCTAssertEqual(alpha, 255);
2092 } else {
2093 XCTAssertEqual(alpha, 0);
2094 }
2095 }
2096 }
2097 }
2098
2099 - (void)testClipRect_multipleClips {
2100 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2101
2102 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2103 /*platform=*/GetDefaultTaskRunner(),
2104 /*raster=*/GetDefaultTaskRunner(),
2105 /*ui=*/GetDefaultTaskRunner(),
2106 /*io=*/GetDefaultTaskRunner());
2107 FlutterPlatformViewsController* flutterPlatformViewsController =
2108 [[FlutterPlatformViewsController alloc] init];
2109 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
2110 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2111 /*delegate=*/mock_delegate,
2112 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
2113 /*platform_views_controller=*/flutterPlatformViewsController,
2114 /*task_runners=*/runners,
2115 /*worker_task_runner=*/nil,
2116 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2117
2118 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2119 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2120 [flutterPlatformViewsController
2121 registerViewFactory:factory
2122 withId:@"MockFlutterPlatformView"
2123 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
2124 FlutterResult result = ^(id result) {
2125 };
2126 [flutterPlatformViewsController
2127 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
2128 arguments:@{
2129 @"id" : @2,
2130 @"viewType" : @"MockFlutterPlatformView"
2131 }]
2132 result:result];
2133
2134 XCTAssertNotNil(gMockPlatformView);
2135
2136 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
2137 flutterPlatformViewsController.flutterView = flutterView;
2138 // Create embedded view params
2139 flutter::MutatorsStack stack;
2140 // Layer tree always pushes a screen scale factor to the stack
2141 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
2142 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
2143 stack.PushTransform(screenScaleMatrix);
2144 // Push a clip rect
2145 flutter::DlRect rect1 = flutter::DlRect::MakeXYWH(2, 2, 3, 3);
2146 stack.PushClipRect(rect1);
2147 // Push another clip rect
2148 flutter::DlRect rect2 = flutter::DlRect::MakeXYWH(3, 3, 3, 3);
2149 stack.PushClipRect(rect2);
2150
2151 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
2152 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack);
2153
2154 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
2155 withParams:std::move(embeddedViewParams)];
2156 [flutterPlatformViewsController
2157 compositeView:2
2158 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
2159
2160 gMockPlatformView.backgroundColor = UIColor.redColor;
2161 XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
2162 ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
2163 [flutterView addSubview:childClippingView];
2164
2165 [flutterView setNeedsLayout];
2166 [flutterView layoutIfNeeded];
2167
2168 /*
2169 clip 1 clip 2
2170 2 3 4 5 6 2 3 4 5 6
2171 2 + - - + 2
2172 3 | | 3 + - - +
2173 4 | | 4 | |
2174 5 + - - + 5 | |
2175 6 6 + - - +
2176
2177 Result should be the intersection of 2 clips
2178 2 3 4 5 6
2179 2
2180 3 + - +
2181 4 | |
2182 5 + - +
2183 6
2184 */
2185 CGRect insideClipping = CGRectMake(3, 3, 2, 2);
2186 for (int i = 0; i < 10; i++) {
2187 for (int j = 0; j < 10; j++) {
2188 CGPoint point = CGPointMake(i, j);
2189 int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView];
2190 if (CGRectContainsPoint(insideClipping, point)) {
2191 XCTAssertEqual(alpha, 255);
2192 } else {
2193 XCTAssertEqual(alpha, 0);
2194 }
2195 }
2196 }
2197 }
2198
2199 - (void)testClipRRect {
2200 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2201
2202 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2203 /*platform=*/GetDefaultTaskRunner(),
2204 /*raster=*/GetDefaultTaskRunner(),
2205 /*ui=*/GetDefaultTaskRunner(),
2206 /*io=*/GetDefaultTaskRunner());
2207 FlutterPlatformViewsController* flutterPlatformViewsController =
2208 [[FlutterPlatformViewsController alloc] init];
2209 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
2210 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2211 /*delegate=*/mock_delegate,
2212 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
2213 /*platform_views_controller=*/flutterPlatformViewsController,
2214 /*task_runners=*/runners,
2215 /*worker_task_runner=*/nil,
2216 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2217
2218 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2219 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2220 [flutterPlatformViewsController
2221 registerViewFactory:factory
2222 withId:@"MockFlutterPlatformView"
2223 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
2224 FlutterResult result = ^(id result) {
2225 };
2226 [flutterPlatformViewsController
2227 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
2228 arguments:@{
2229 @"id" : @2,
2230 @"viewType" : @"MockFlutterPlatformView"
2231 }]
2232 result:result];
2233
2234 XCTAssertNotNil(gMockPlatformView);
2235
2236 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
2237 flutterPlatformViewsController.flutterView = flutterView;
2238 // Create embedded view params
2239 flutter::MutatorsStack stack;
2240 // Layer tree always pushes a screen scale factor to the stack
2241 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
2242 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
2243 stack.PushTransform(screenScaleMatrix);
2244 // Push a clip rrect
2245 flutter::DlRoundRect rrect =
2246 flutter::DlRoundRect::MakeRectXY(flutter::DlRect::MakeXYWH(2, 2, 6, 6), 1, 1);
2247 stack.PushClipRRect(rrect);
2248
2249 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
2250 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack);
2251
2252 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
2253 withParams:std::move(embeddedViewParams)];
2254 [flutterPlatformViewsController
2255 compositeView:2
2256 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
2257
2258 gMockPlatformView.backgroundColor = UIColor.redColor;
2259 XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
2260 ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
2261 [flutterView addSubview:childClippingView];
2262
2263 [flutterView setNeedsLayout];
2264 [flutterView layoutIfNeeded];
2265
2266 /*
2267 ClippingMask outterClipping
2268 2 3 4 5 6 7 2 3 4 5 6 7
2269 2 / - - - - \ 2 + - - - - +
2270 3 | | 3 | |
2271 4 | | 4 | |
2272 5 | | 5 | |
2273 6 | | 6 | |
2274 7 \ - - - - / 7 + - - - - +
2275
2276 innerClipping1 innerClipping2
2277 2 3 4 5 6 7 2 3 4 5 6 7
2278 2 + - - + 2
2279 3 | | 3 + - - - - +
2280 4 | | 4 | |
2281 5 | | 5 | |
2282 6 | | 6 + - - - - +
2283 7 + - - + 7
2284 */
2285 CGRect innerClipping1 = CGRectMake(3, 2, 4, 6);
2286 CGRect innerClipping2 = CGRectMake(2, 3, 6, 4);
2287 CGRect outterClipping = CGRectMake(2, 2, 6, 6);
2288 for (int i = 0; i < 10; i++) {
2289 for (int j = 0; j < 10; j++) {
2290 CGPoint point = CGPointMake(i, j);
2291 int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView];
2292 if (CGRectContainsPoint(innerClipping1, point) ||
2293 CGRectContainsPoint(innerClipping2, point)) {
2294 // Pixels inside either of the 2 inner clippings should be fully opaque.
2295 XCTAssertEqual(alpha, 255);
2296 } else if (CGRectContainsPoint(outterClipping, point)) {
2297 // Corner pixels (i.e. (2, 2), (2, 7), (7, 2) and (7, 7)) should be partially transparent.
2298 XCTAssert(0 < alpha && alpha < 255);
2299 } else {
2300 // Pixels outside outterClipping should be fully transparent.
2301 XCTAssertEqual(alpha, 0);
2302 }
2303 }
2304 }
2305 }
2306
2307 - (void)testClipRRect_multipleClips {
2308 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2309
2310 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2311 /*platform=*/GetDefaultTaskRunner(),
2312 /*raster=*/GetDefaultTaskRunner(),
2313 /*ui=*/GetDefaultTaskRunner(),
2314 /*io=*/GetDefaultTaskRunner());
2315 FlutterPlatformViewsController* flutterPlatformViewsController =
2316 [[FlutterPlatformViewsController alloc] init];
2317 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
2318 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2319 /*delegate=*/mock_delegate,
2320 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
2321 /*platform_views_controller=*/flutterPlatformViewsController,
2322 /*task_runners=*/runners,
2323 /*worker_task_runner=*/nil,
2324 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2325
2326 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2327 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2328 [flutterPlatformViewsController
2329 registerViewFactory:factory
2330 withId:@"MockFlutterPlatformView"
2331 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
2332 FlutterResult result = ^(id result) {
2333 };
2334 [flutterPlatformViewsController
2335 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
2336 arguments:@{
2337 @"id" : @2,
2338 @"viewType" : @"MockFlutterPlatformView"
2339 }]
2340 result:result];
2341
2342 XCTAssertNotNil(gMockPlatformView);
2343
2344 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
2345 flutterPlatformViewsController.flutterView = flutterView;
2346 // Create embedded view params
2347 flutter::MutatorsStack stack;
2348 // Layer tree always pushes a screen scale factor to the stack
2349 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
2350 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
2351 stack.PushTransform(screenScaleMatrix);
2352 // Push a clip rrect
2353 flutter::DlRoundRect rrect =
2354 flutter::DlRoundRect::MakeRectXY(flutter::DlRect::MakeXYWH(2, 2, 6, 6), 1, 1);
2355 stack.PushClipRRect(rrect);
2356 // Push a clip rect
2357 flutter::DlRect rect = flutter::DlRect::MakeXYWH(4, 2, 6, 6);
2358 stack.PushClipRect(rect);
2359
2360 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
2361 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack);
2362
2363 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
2364 withParams:std::move(embeddedViewParams)];
2365 [flutterPlatformViewsController
2366 compositeView:2
2367 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
2368
2369 gMockPlatformView.backgroundColor = UIColor.redColor;
2370 XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
2371 ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
2372 [flutterView addSubview:childClippingView];
2373
2374 [flutterView setNeedsLayout];
2375 [flutterView layoutIfNeeded];
2376
2377 /*
2378 clip 1 clip 2
2379 2 3 4 5 6 7 8 9 2 3 4 5 6 7 8 9
2380 2 / - - - - \ 2 + - - - - +
2381 3 | | 3 | |
2382 4 | | 4 | |
2383 5 | | 5 | |
2384 6 | | 6 | |
2385 7 \ - - - - / 7 + - - - - +
2386
2387 Result should be the intersection of 2 clips
2388 2 3 4 5 6 7 8 9
2389 2 + - - \
2390 3 | |
2391 4 | |
2392 5 | |
2393 6 | |
2394 7 + - - /
2395 */
2396 CGRect clipping = CGRectMake(4, 2, 4, 6);
2397 for (int i = 0; i < 10; i++) {
2398 for (int j = 0; j < 10; j++) {
2399 CGPoint point = CGPointMake(i, j);
2400 int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView];
2401 if (i == 7 && (j == 2 || j == 7)) {
2402 // Upper and lower right corners should be partially transparent.
2403 XCTAssert(0 < alpha && alpha < 255);
2404 } else if (
2405 // left
2406 (i == 4 && j >= 2 && j <= 7) ||
2407 // right
2408 (i == 7 && j >= 2 && j <= 7) ||
2409 // top
2410 (j == 2 && i >= 4 && i <= 7) ||
2411 // bottom
2412 (j == 7 && i >= 4 && i <= 7)) {
2413 // Since we are falling back to software rendering for this case
2414 // The edge pixels can be anti-aliased, so it may not be fully opaque.
2415 XCTAssert(alpha > 127);
2416 } else if ((i == 3 && j >= 1 && j <= 8) || (i == 8 && j >= 1 && j <= 8) ||
2417 (j == 1 && i >= 3 && i <= 8) || (j == 8 && i >= 3 && i <= 8)) {
2418 // Since we are falling back to software rendering for this case
2419 // The edge pixels can be anti-aliased, so it may not be fully transparent.
2420 XCTAssert(alpha < 127);
2421 } else if (CGRectContainsPoint(clipping, point)) {
2422 // Other pixels inside clipping should be fully opaque.
2423 XCTAssertEqual(alpha, 255);
2424 } else {
2425 // Pixels outside clipping should be fully transparent.
2426 XCTAssertEqual(alpha, 0);
2427 }
2428 }
2429 }
2430 }
2431
2432 - (void)testClipPath {
2433 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2434
2435 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2436 /*platform=*/GetDefaultTaskRunner(),
2437 /*raster=*/GetDefaultTaskRunner(),
2438 /*ui=*/GetDefaultTaskRunner(),
2439 /*io=*/GetDefaultTaskRunner());
2440 FlutterPlatformViewsController* flutterPlatformViewsController =
2441 [[FlutterPlatformViewsController alloc] init];
2442 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
2443 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2444 /*delegate=*/mock_delegate,
2445 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
2446 /*platform_views_controller=*/flutterPlatformViewsController,
2447 /*task_runners=*/runners,
2448 /*worker_task_runner=*/nil,
2449 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2450
2451 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2452 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2453 [flutterPlatformViewsController
2454 registerViewFactory:factory
2455 withId:@"MockFlutterPlatformView"
2456 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
2457 FlutterResult result = ^(id result) {
2458 };
2459 [flutterPlatformViewsController
2460 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
2461 arguments:@{
2462 @"id" : @2,
2463 @"viewType" : @"MockFlutterPlatformView"
2464 }]
2465 result:result];
2466
2467 XCTAssertNotNil(gMockPlatformView);
2468
2469 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
2470 flutterPlatformViewsController.flutterView = flutterView;
2471 // Create embedded view params
2472 flutter::MutatorsStack stack;
2473 // Layer tree always pushes a screen scale factor to the stack
2474 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
2475 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
2476 stack.PushTransform(screenScaleMatrix);
2477 // Push a clip path
2478 flutter::DlPath path =
2479 flutter::DlPath::MakeRoundRectXY(flutter::DlRect::MakeXYWH(2, 2, 6, 6), 1, 1);
2480 stack.PushClipPath(path);
2481
2482 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
2483 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack);
2484
2485 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
2486 withParams:std::move(embeddedViewParams)];
2487 [flutterPlatformViewsController
2488 compositeView:2
2489 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
2490
2491 gMockPlatformView.backgroundColor = UIColor.redColor;
2492 XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
2493 ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
2494 [flutterView addSubview:childClippingView];
2495
2496 [flutterView setNeedsLayout];
2497 [flutterView layoutIfNeeded];
2498
2499 /*
2500 ClippingMask outterClipping
2501 2 3 4 5 6 7 2 3 4 5 6 7
2502 2 / - - - - \ 2 + - - - - +
2503 3 | | 3 | |
2504 4 | | 4 | |
2505 5 | | 5 | |
2506 6 | | 6 | |
2507 7 \ - - - - / 7 + - - - - +
2508
2509 innerClipping1 innerClipping2
2510 2 3 4 5 6 7 2 3 4 5 6 7
2511 2 + - - + 2
2512 3 | | 3 + - - - - +
2513 4 | | 4 | |
2514 5 | | 5 | |
2515 6 | | 6 + - - - - +
2516 7 + - - + 7
2517 */
2518 CGRect innerClipping1 = CGRectMake(3, 2, 4, 6);
2519 CGRect innerClipping2 = CGRectMake(2, 3, 6, 4);
2520 CGRect outterClipping = CGRectMake(2, 2, 6, 6);
2521 for (int i = 0; i < 10; i++) {
2522 for (int j = 0; j < 10; j++) {
2523 CGPoint point = CGPointMake(i, j);
2524 int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView];
2525 if (CGRectContainsPoint(innerClipping1, point) ||
2526 CGRectContainsPoint(innerClipping2, point)) {
2527 // Pixels inside either of the 2 inner clippings should be fully opaque.
2528 XCTAssertEqual(alpha, 255);
2529 } else if (CGRectContainsPoint(outterClipping, point)) {
2530 // Corner pixels (i.e. (2, 2), (2, 7), (7, 2) and (7, 7)) should be partially transparent.
2531 XCTAssert(0 < alpha && alpha < 255);
2532 } else {
2533 // Pixels outside outterClipping should be fully transparent.
2534 XCTAssertEqual(alpha, 0);
2535 }
2536 }
2537 }
2538 }
2539
2540 - (void)testClipPath_multipleClips {
2541 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2542
2543 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2544 /*platform=*/GetDefaultTaskRunner(),
2545 /*raster=*/GetDefaultTaskRunner(),
2546 /*ui=*/GetDefaultTaskRunner(),
2547 /*io=*/GetDefaultTaskRunner());
2548 FlutterPlatformViewsController* flutterPlatformViewsController =
2549 [[FlutterPlatformViewsController alloc] init];
2550 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
2551 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2552 /*delegate=*/mock_delegate,
2553 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
2554 /*platform_views_controller=*/flutterPlatformViewsController,
2555 /*task_runners=*/runners,
2556 /*worker_task_runner=*/nil,
2557 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2558
2559 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2560 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2561 [flutterPlatformViewsController
2562 registerViewFactory:factory
2563 withId:@"MockFlutterPlatformView"
2564 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
2565 FlutterResult result = ^(id result) {
2566 };
2567 [flutterPlatformViewsController
2568 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
2569 arguments:@{
2570 @"id" : @2,
2571 @"viewType" : @"MockFlutterPlatformView"
2572 }]
2573 result:result];
2574
2575 XCTAssertNotNil(gMockPlatformView);
2576
2577 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
2578 flutterPlatformViewsController.flutterView = flutterView;
2579 // Create embedded view params
2580 flutter::MutatorsStack stack;
2581 // Layer tree always pushes a screen scale factor to the stack
2582 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
2583 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
2584 stack.PushTransform(screenScaleMatrix);
2585 // Push a clip path
2586 flutter::DlPath path =
2587 flutter::DlPath::MakeRoundRectXY(flutter::DlRect::MakeXYWH(2, 2, 6, 6), 1, 1);
2588 stack.PushClipPath(path);
2589 // Push a clip rect
2590 flutter::DlRect rect = flutter::DlRect::MakeXYWH(4, 2, 6, 6);
2591 stack.PushClipRect(rect);
2592
2593 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
2594 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack);
2595
2596 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
2597 withParams:std::move(embeddedViewParams)];
2598 [flutterPlatformViewsController
2599 compositeView:2
2600 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
2601
2602 gMockPlatformView.backgroundColor = UIColor.redColor;
2603 XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
2604 ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
2605 [flutterView addSubview:childClippingView];
2606
2607 [flutterView setNeedsLayout];
2608 [flutterView layoutIfNeeded];
2609
2610 /*
2611 clip 1 clip 2
2612 2 3 4 5 6 7 8 9 2 3 4 5 6 7 8 9
2613 2 / - - - - \ 2 + - - - - +
2614 3 | | 3 | |
2615 4 | | 4 | |
2616 5 | | 5 | |
2617 6 | | 6 | |
2618 7 \ - - - - / 7 + - - - - +
2619
2620 Result should be the intersection of 2 clips
2621 2 3 4 5 6 7 8 9
2622 2 + - - \
2623 3 | |
2624 4 | |
2625 5 | |
2626 6 | |
2627 7 + - - /
2628 */
2629 CGRect clipping = CGRectMake(4, 2, 4, 6);
2630 for (int i = 0; i < 10; i++) {
2631 for (int j = 0; j < 10; j++) {
2632 CGPoint point = CGPointMake(i, j);
2633 int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:flutterView];
2634 if (i == 7 && (j == 2 || j == 7)) {
2635 // Upper and lower right corners should be partially transparent.
2636 XCTAssert(0 < alpha && alpha < 255);
2637 } else if (
2638 // left
2639 (i == 4 && j >= 2 && j <= 7) ||
2640 // right
2641 (i == 7 && j >= 2 && j <= 7) ||
2642 // top
2643 (j == 2 && i >= 4 && i <= 7) ||
2644 // bottom
2645 (j == 7 && i >= 4 && i <= 7)) {
2646 // Since we are falling back to software rendering for this case
2647 // The edge pixels can be anti-aliased, so it may not be fully opaque.
2648 XCTAssert(alpha > 127);
2649 } else if ((i == 3 && j >= 1 && j <= 8) || (i == 8 && j >= 1 && j <= 8) ||
2650 (j == 1 && i >= 3 && i <= 8) || (j == 8 && i >= 3 && i <= 8)) {
2651 // Since we are falling back to software rendering for this case
2652 // The edge pixels can be anti-aliased, so it may not be fully transparent.
2653 XCTAssert(alpha < 127);
2654 } else if (CGRectContainsPoint(clipping, point)) {
2655 // Other pixels inside clipping should be fully opaque.
2656 XCTAssertEqual(alpha, 255);
2657 } else {
2658 // Pixels outside clipping should be fully transparent.
2659 XCTAssertEqual(alpha, 0);
2660 }
2661 }
2662 }
2663 }
2664
2665 - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents {
2666 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2667
2668 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2669 /*platform=*/GetDefaultTaskRunner(),
2670 /*raster=*/GetDefaultTaskRunner(),
2671 /*ui=*/GetDefaultTaskRunner(),
2672 /*io=*/GetDefaultTaskRunner());
2673 FlutterPlatformViewsController* flutterPlatformViewsController =
2674 [[FlutterPlatformViewsController alloc] init];
2675 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
2676 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2677 /*delegate=*/mock_delegate,
2678 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
2679 /*platform_views_controller=*/flutterPlatformViewsController,
2680 /*task_runners=*/runners,
2681 /*worker_task_runner=*/nil,
2682 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2683
2684 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2685 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2686 [flutterPlatformViewsController
2687 registerViewFactory:factory
2688 withId:@"MockFlutterPlatformView"
2689 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
2690 FlutterResult result = ^(id result) {
2691 };
2692 [flutterPlatformViewsController
2693 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
2694 arguments:@{
2695 @"id" : @2,
2696 @"viewType" : @"MockFlutterPlatformView"
2697 }]
2698 result:result];
2699
2700 XCTAssertNotNil(gMockPlatformView);
2701
2702 // Find touch inteceptor view
2703 UIView* touchInteceptorView = gMockPlatformView;
2704 while (touchInteceptorView != nil &&
2705 ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
2706 touchInteceptorView = touchInteceptorView.superview;
2707 }
2708 XCTAssertNotNil(touchInteceptorView);
2709
2710 // Find ForwardGestureRecognizer
2711 UIGestureRecognizer* forwardGectureRecognizer = nil;
2712 for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
2713 if ([gestureRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]) {
2714 forwardGectureRecognizer = gestureRecognizer;
2715 break;
2716 }
2717 }
2718
2719 // Before setting flutter view controller, events are not dispatched.
2720 NSSet* touches1 = [[NSSet alloc] init];
2721 id event1 = OCMClassMock([UIEvent class]);
2722 id flutterViewController = OCMClassMock([FlutterViewController class]);
2723 [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2724 OCMReject([flutterViewController touchesBegan:touches1 withEvent:event1]);
2725
2726 // Set flutter view controller allows events to be dispatched.
2727 NSSet* touches2 = [[NSSet alloc] init];
2728 id event2 = OCMClassMock([UIEvent class]);
2729 flutterPlatformViewsController.flutterViewController = flutterViewController;
2730 [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2];
2731 OCMVerify([flutterViewController touchesBegan:touches2 withEvent:event2]);
2732 }
2733
2734 - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGesturesToBeHandled {
2735 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2736
2737 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2738 /*platform=*/GetDefaultTaskRunner(),
2739 /*raster=*/GetDefaultTaskRunner(),
2740 /*ui=*/GetDefaultTaskRunner(),
2741 /*io=*/GetDefaultTaskRunner());
2742 FlutterPlatformViewsController* flutterPlatformViewsController =
2743 [[FlutterPlatformViewsController alloc] init];
2744 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
2745 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2746 /*delegate=*/mock_delegate,
2747 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
2748 /*platform_views_controller=*/flutterPlatformViewsController,
2749 /*task_runners=*/runners,
2750 /*worker_task_runner=*/nil,
2751 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2752
2753 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2754 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2755 [flutterPlatformViewsController
2756 registerViewFactory:factory
2757 withId:@"MockFlutterPlatformView"
2758 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
2759 FlutterResult result = ^(id result) {
2760 };
2761 [flutterPlatformViewsController
2762 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
2763 arguments:@{
2764 @"id" : @2,
2765 @"viewType" : @"MockFlutterPlatformView"
2766 }]
2767 result:result];
2768
2769 XCTAssertNotNil(gMockPlatformView);
2770
2771 // Find touch inteceptor view
2772 UIView* touchInteceptorView = gMockPlatformView;
2773 while (touchInteceptorView != nil &&
2774 ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
2775 touchInteceptorView = touchInteceptorView.superview;
2776 }
2777 XCTAssertNotNil(touchInteceptorView);
2778
2779 // Find ForwardGestureRecognizer
2780 UIGestureRecognizer* forwardGectureRecognizer = nil;
2781 for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
2782 if ([gestureRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]) {
2783 forwardGectureRecognizer = gestureRecognizer;
2784 break;
2785 }
2786 }
2787 id flutterViewController = OCMClassMock([FlutterViewController class]);
2788 {
2789 // ***** Sequence 1, finishing touch event with touchEnded ***** //
2790 flutterPlatformViewsController.flutterViewController = flutterViewController;
2791
2792 NSSet* touches1 = [[NSSet alloc] init];
2793 id event1 = OCMClassMock([UIEvent class]);
2794 [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2795 OCMVerify([flutterViewController touchesBegan:touches1 withEvent:event1]);
2796
2797 flutterPlatformViewsController.flutterViewController = nil;
2798
2799 // Allow the touch events to finish
2800 NSSet* touches2 = [[NSSet alloc] init];
2801 id event2 = OCMClassMock([UIEvent class]);
2802 [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2];
2803 OCMVerify([flutterViewController touchesMoved:touches2 withEvent:event2]);
2804
2805 NSSet* touches3 = [[NSSet alloc] init];
2806 id event3 = OCMClassMock([UIEvent class]);
2807 [forwardGectureRecognizer touchesEnded:touches3 withEvent:event3];
2808 OCMVerify([flutterViewController touchesEnded:touches3 withEvent:event3]);
2809
2810 // Now the 2nd touch sequence should not be allowed.
2811 NSSet* touches4 = [[NSSet alloc] init];
2812 id event4 = OCMClassMock([UIEvent class]);
2813 [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4];
2814 OCMReject([flutterViewController touchesBegan:touches4 withEvent:event4]);
2815
2816 NSSet* touches5 = [[NSSet alloc] init];
2817 id event5 = OCMClassMock([UIEvent class]);
2818 [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5];
2819 OCMReject([flutterViewController touchesEnded:touches5 withEvent:event5]);
2820 }
2821
2822 {
2823 // ***** Sequence 2, finishing touch event with touchCancelled ***** //
2824 flutterPlatformViewsController.flutterViewController = flutterViewController;
2825
2826 NSSet* touches1 = [[NSSet alloc] init];
2827 id event1 = OCMClassMock([UIEvent class]);
2828 [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2829 OCMVerify([flutterViewController touchesBegan:touches1 withEvent:event1]);
2830
2831 flutterPlatformViewsController.flutterViewController = nil;
2832
2833 // Allow the touch events to finish
2834 NSSet* touches2 = [[NSSet alloc] init];
2835 id event2 = OCMClassMock([UIEvent class]);
2836 [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2];
2837 OCMVerify([flutterViewController touchesMoved:touches2 withEvent:event2]);
2838
2839 NSSet* touches3 = [[NSSet alloc] init];
2840 id event3 = OCMClassMock([UIEvent class]);
2841 [forwardGectureRecognizer touchesCancelled:touches3 withEvent:event3];
2842 OCMVerify([flutterViewController forceTouchesCancelled:touches3]);
2843
2844 // Now the 2nd touch sequence should not be allowed.
2845 NSSet* touches4 = [[NSSet alloc] init];
2846 id event4 = OCMClassMock([UIEvent class]);
2847 [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4];
2848 OCMReject([flutterViewController touchesBegan:touches4 withEvent:event4]);
2849
2850 NSSet* touches5 = [[NSSet alloc] init];
2851 id event5 = OCMClassMock([UIEvent class]);
2852 [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5];
2853 OCMReject([flutterViewController touchesEnded:touches5 withEvent:event5]);
2854 }
2855
2856 [flutterPlatformViewsController reset];
2857 }
2858
2859 - (void)
2860 testSetFlutterViewControllerInTheMiddleOfTouchEventAllowsTheNewControllerToHandleSecondTouchSequence {
2861 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2862
2863 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2864 /*platform=*/GetDefaultTaskRunner(),
2865 /*raster=*/GetDefaultTaskRunner(),
2866 /*ui=*/GetDefaultTaskRunner(),
2867 /*io=*/GetDefaultTaskRunner());
2868 FlutterPlatformViewsController* flutterPlatformViewsController =
2869 [[FlutterPlatformViewsController alloc] init];
2870 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
2871 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2872 /*delegate=*/mock_delegate,
2873 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
2874 /*platform_views_controller=*/flutterPlatformViewsController,
2875 /*task_runners=*/runners,
2876 /*worker_task_runner=*/nil,
2877 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2878
2879 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2880 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2881 [flutterPlatformViewsController
2882 registerViewFactory:factory
2883 withId:@"MockFlutterPlatformView"
2884 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
2885 FlutterResult result = ^(id result) {
2886 };
2887 [flutterPlatformViewsController
2888 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
2889 arguments:@{
2890 @"id" : @2,
2891 @"viewType" : @"MockFlutterPlatformView"
2892 }]
2893 result:result];
2894
2895 XCTAssertNotNil(gMockPlatformView);
2896
2897 // Find touch inteceptor view
2898 UIView* touchInteceptorView = gMockPlatformView;
2899 while (touchInteceptorView != nil &&
2900 ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
2901 touchInteceptorView = touchInteceptorView.superview;
2902 }
2903 XCTAssertNotNil(touchInteceptorView);
2904
2905 // Find ForwardGestureRecognizer
2906 UIGestureRecognizer* forwardGectureRecognizer = nil;
2907 for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
2908 if ([gestureRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]) {
2909 forwardGectureRecognizer = gestureRecognizer;
2910 break;
2911 }
2912 }
2913 id flutterViewController = OCMClassMock([FlutterViewController class]);
2914 flutterPlatformViewsController.flutterViewController = flutterViewController;
2915
2916 // The touches in this sequence requires 1 touch object, we always create the NSSet with one item.
2917 NSSet* touches1 = [NSSet setWithObject:@1];
2918 id event1 = OCMClassMock([UIEvent class]);
2919 [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2920 OCMVerify([flutterViewController touchesBegan:touches1 withEvent:event1]);
2921
2922 FlutterViewController* flutterViewController2 = OCMClassMock([FlutterViewController class]);
2923 flutterPlatformViewsController.flutterViewController = flutterViewController2;
2924
2925 // Touch events should still send to the old FlutterViewController if FlutterViewController
2926 // is updated in between.
2927 NSSet* touches2 = [NSSet setWithObject:@1];
2928 id event2 = OCMClassMock([UIEvent class]);
2929 [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2];
2930 OCMVerify([flutterViewController touchesBegan:touches2 withEvent:event2]);
2931 OCMReject([flutterViewController2 touchesBegan:touches2 withEvent:event2]);
2932
2933 NSSet* touches3 = [NSSet setWithObject:@1];
2934 id event3 = OCMClassMock([UIEvent class]);
2935 [forwardGectureRecognizer touchesMoved:touches3 withEvent:event3];
2936 OCMVerify([flutterViewController touchesMoved:touches3 withEvent:event3]);
2937 OCMReject([flutterViewController2 touchesMoved:touches3 withEvent:event3]);
2938
2939 NSSet* touches4 = [NSSet setWithObject:@1];
2940 id event4 = OCMClassMock([UIEvent class]);
2941 [forwardGectureRecognizer touchesEnded:touches4 withEvent:event4];
2942 OCMVerify([flutterViewController touchesEnded:touches4 withEvent:event4]);
2943 OCMReject([flutterViewController2 touchesEnded:touches4 withEvent:event4]);
2944
2945 NSSet* touches5 = [NSSet setWithObject:@1];
2946 id event5 = OCMClassMock([UIEvent class]);
2947 [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5];
2948 OCMVerify([flutterViewController touchesEnded:touches5 withEvent:event5]);
2949 OCMReject([flutterViewController2 touchesEnded:touches5 withEvent:event5]);
2950
2951 // Now the 2nd touch sequence should go to the new FlutterViewController
2952
2953 NSSet* touches6 = [NSSet setWithObject:@1];
2954 id event6 = OCMClassMock([UIEvent class]);
2955 [forwardGectureRecognizer touchesBegan:touches6 withEvent:event6];
2956 OCMVerify([flutterViewController2 touchesBegan:touches6 withEvent:event6]);
2957 OCMReject([flutterViewController touchesBegan:touches6 withEvent:event6]);
2958
2959 // Allow the touch events to finish
2960 NSSet* touches7 = [NSSet setWithObject:@1];
2961 id event7 = OCMClassMock([UIEvent class]);
2962 [forwardGectureRecognizer touchesMoved:touches7 withEvent:event7];
2963 OCMVerify([flutterViewController2 touchesMoved:touches7 withEvent:event7]);
2964 OCMReject([flutterViewController touchesMoved:touches7 withEvent:event7]);
2965
2966 NSSet* touches8 = [NSSet setWithObject:@1];
2967 id event8 = OCMClassMock([UIEvent class]);
2968 [forwardGectureRecognizer touchesEnded:touches8 withEvent:event8];
2969 OCMVerify([flutterViewController2 touchesEnded:touches8 withEvent:event8]);
2970 OCMReject([flutterViewController touchesEnded:touches8 withEvent:event8]);
2971
2972 [flutterPlatformViewsController reset];
2973 }
2974
2975 - (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled {
2976 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2977
2978 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2979 /*platform=*/GetDefaultTaskRunner(),
2980 /*raster=*/GetDefaultTaskRunner(),
2981 /*ui=*/GetDefaultTaskRunner(),
2982 /*io=*/GetDefaultTaskRunner());
2983 FlutterPlatformViewsController* flutterPlatformViewsController =
2984 [[FlutterPlatformViewsController alloc] init];
2985 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
2986 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2987 /*delegate=*/mock_delegate,
2988 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
2989 /*platform_views_controller=*/flutterPlatformViewsController,
2990 /*task_runners=*/runners,
2991 /*worker_task_runner=*/nil,
2992 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2993
2994 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2995 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2996 [flutterPlatformViewsController
2997 registerViewFactory:factory
2998 withId:@"MockFlutterPlatformView"
2999 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3000 FlutterResult result = ^(id result) {
3001 };
3002 [flutterPlatformViewsController
3003 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3004 arguments:@{
3005 @"id" : @2,
3006 @"viewType" : @"MockFlutterPlatformView"
3007 }]
3008 result:result];
3009
3010 XCTAssertNotNil(gMockPlatformView);
3011
3012 // Find touch inteceptor view
3013 UIView* touchInteceptorView = gMockPlatformView;
3014 while (touchInteceptorView != nil &&
3015 ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
3016 touchInteceptorView = touchInteceptorView.superview;
3017 }
3018 XCTAssertNotNil(touchInteceptorView);
3019
3020 // Find ForwardGestureRecognizer
3021 UIGestureRecognizer* forwardGectureRecognizer = nil;
3022 for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
3023 if ([gestureRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]) {
3024 forwardGectureRecognizer = gestureRecognizer;
3025 break;
3026 }
3027 }
3028 id flutterViewController = OCMClassMock([FlutterViewController class]);
3029 flutterPlatformViewsController.flutterViewController = flutterViewController;
3030
3031 NSSet* touches1 = [NSSet setWithObject:@1];
3032 id event1 = OCMClassMock([UIEvent class]);
3033 [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
3034
3035 [forwardGectureRecognizer touchesCancelled:touches1 withEvent:event1];
3036 OCMVerify([flutterViewController forceTouchesCancelled:touches1]);
3037
3038 [flutterPlatformViewsController reset];
3039 }
3040
3041 - (void)testFlutterPlatformViewTouchesEndedOrTouchesCancelledEventDoesNotFailTheGestureRecognizer {
3042 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3043
3044 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3045 /*platform=*/GetDefaultTaskRunner(),
3046 /*raster=*/GetDefaultTaskRunner(),
3047 /*ui=*/GetDefaultTaskRunner(),
3048 /*io=*/GetDefaultTaskRunner());
3049 FlutterPlatformViewsController* flutterPlatformViewsController =
3050 [[FlutterPlatformViewsController alloc] init];
3051 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3052 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3053 /*delegate=*/mock_delegate,
3054 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3055 /*platform_views_controller=*/flutterPlatformViewsController,
3056 /*task_runners=*/runners,
3057 /*worker_task_runner=*/nil,
3058 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3059
3060 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3061 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3062 [flutterPlatformViewsController
3063 registerViewFactory:factory
3064 withId:@"MockFlutterPlatformView"
3065 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3066 FlutterResult result = ^(id result) {
3067 };
3068 [flutterPlatformViewsController
3069 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3070 arguments:@{
3071 @"id" : @2,
3072 @"viewType" : @"MockFlutterPlatformView"
3073 }]
3074 result:result];
3075
3076 XCTAssertNotNil(gMockPlatformView);
3077
3078 // Find touch inteceptor view
3079 UIView* touchInteceptorView = gMockPlatformView;
3080 while (touchInteceptorView != nil &&
3081 ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
3082 touchInteceptorView = touchInteceptorView.superview;
3083 }
3084 XCTAssertNotNil(touchInteceptorView);
3085
3086 // Find ForwardGestureRecognizer
3087 __block UIGestureRecognizer* forwardGestureRecognizer = nil;
3088 for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
3089 if ([gestureRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]) {
3090 forwardGestureRecognizer = gestureRecognizer;
3091 break;
3092 }
3093 }
3094 id flutterViewController = OCMClassMock([FlutterViewController class]);
3095 flutterPlatformViewsController.flutterViewController = flutterViewController;
3096
3097 NSSet* touches1 = [NSSet setWithObject:@1];
3098 id event1 = OCMClassMock([UIEvent class]);
3099 XCTAssert(forwardGestureRecognizer.state == UIGestureRecognizerStatePossible,
3100 @"Forwarding gesture recognizer must start with possible state.");
3101 [forwardGestureRecognizer touchesBegan:touches1 withEvent:event1];
3102 [forwardGestureRecognizer touchesEnded:touches1 withEvent:event1];
3103 XCTAssert(forwardGestureRecognizer.state == UIGestureRecognizerStateFailed,
3104 @"Forwarding gesture recognizer must end with failed state.");
3105
3106 XCTestExpectation* touchEndedExpectation =
3107 [self expectationWithDescription:@"Wait for gesture recognizer's state change."];
3108 dispatch_async(dispatch_get_main_queue(), ^{
3109 // Re-query forward gesture recognizer since it's recreated.
3110 for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
3111 if ([gestureRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]) {
3112 forwardGestureRecognizer = gestureRecognizer;
3113 break;
3114 }
3115 }
3116 XCTAssert(forwardGestureRecognizer.state == UIGestureRecognizerStatePossible,
3117 @"Forwarding gesture recognizer must be reset to possible state.");
3118 [touchEndedExpectation fulfill];
3119 });
3120 [self waitForExpectationsWithTimeout:30 handler:nil];
3121
3122 XCTAssert(forwardGestureRecognizer.state == UIGestureRecognizerStatePossible,
3123 @"Forwarding gesture recognizer must start with possible state.");
3124 [forwardGestureRecognizer touchesBegan:touches1 withEvent:event1];
3125 [forwardGestureRecognizer touchesCancelled:touches1 withEvent:event1];
3126 XCTAssert(forwardGestureRecognizer.state == UIGestureRecognizerStateFailed,
3127 @"Forwarding gesture recognizer must end with failed state.");
3128 XCTestExpectation* touchCancelledExpectation =
3129 [self expectationWithDescription:@"Wait for gesture recognizer's state change."];
3130 dispatch_async(dispatch_get_main_queue(), ^{
3131 // Re-query forward gesture recognizer since it's recreated.
3132 for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
3133 if ([gestureRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]) {
3134 forwardGestureRecognizer = gestureRecognizer;
3135 break;
3136 }
3137 }
3138 XCTAssert(forwardGestureRecognizer.state == UIGestureRecognizerStatePossible,
3139 @"Forwarding gesture recognizer must be reset to possible state.");
3140 [touchCancelledExpectation fulfill];
3141 });
3142 [self waitForExpectationsWithTimeout:30 handler:nil];
3143
3144 [flutterPlatformViewsController reset];
3145 }
3146
3147 - (void)
3148 testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldRemoveAndAddBackDelayingRecognizerForWebView {
3149 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3150
3151 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3152 /*platform=*/GetDefaultTaskRunner(),
3153 /*raster=*/GetDefaultTaskRunner(),
3154 /*ui=*/GetDefaultTaskRunner(),
3155 /*io=*/GetDefaultTaskRunner());
3156 FlutterPlatformViewsController* flutterPlatformViewsController =
3157 [[FlutterPlatformViewsController alloc] init];
3158 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3159 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3160 /*delegate=*/mock_delegate,
3161 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3162 /*platform_views_controller=*/flutterPlatformViewsController,
3163 /*task_runners=*/runners,
3164 /*worker_task_runner=*/nil,
3165 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3166
3167 FlutterPlatformViewsTestMockWebViewFactory* factory =
3168 [[FlutterPlatformViewsTestMockWebViewFactory alloc] init];
3169 [flutterPlatformViewsController
3170 registerViewFactory:factory
3171 withId:@"MockWebView"
3172 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3173 FlutterResult result = ^(id result) {
3174 };
3175 [flutterPlatformViewsController
3176 onMethodCall:[FlutterMethodCall
3177 methodCallWithMethodName:@"create"
3178 arguments:@{@"id" : @2, @"viewType" : @"MockWebView"}]
3179 result:result];
3180
3181 XCTAssertNotNil(gMockPlatformView);
3182
3183 // Find touch inteceptor view
3184 UIView* touchInteceptorView = gMockPlatformView;
3185 while (touchInteceptorView != nil &&
3186 ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
3187 touchInteceptorView = touchInteceptorView.superview;
3188 }
3189 XCTAssertNotNil(touchInteceptorView);
3190
3191 XCTAssert(touchInteceptorView.gestureRecognizers.count == 2);
3192 UIGestureRecognizer* delayingRecognizer = touchInteceptorView.gestureRecognizers[0];
3193 UIGestureRecognizer* forwardingRecognizer = touchInteceptorView.gestureRecognizers[1];
3194
3195 XCTAssert([delayingRecognizer isKindOfClass:[FlutterDelayingGestureRecognizer class]]);
3196 XCTAssert([forwardingRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]);
3197
3198 [(FlutterTouchInterceptingView*)touchInteceptorView blockGesture];
3199
3200 BOOL shouldReAddDelayingRecognizer = NO;
3201 if (@available(iOS 26.0, *)) {
3202 // TODO(hellohuanlin): find a solution for iOS 26,
3203 // https://github.com/flutter/flutter/issues/175099.
3204 } else if (@available(iOS 18.2, *)) {
3205 shouldReAddDelayingRecognizer = YES;
3206 }
3207 if (shouldReAddDelayingRecognizer) {
3208 // Since we remove and add back delayingRecognizer, it would be reordered to the last.
3209 XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], forwardingRecognizer);
3210 XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], delayingRecognizer);
3211 } else {
3212 XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], delayingRecognizer);
3213 XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], forwardingRecognizer);
3214 }
3215 }
3216
3217 - (void)
3218 testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldRemoveAndAddBackDelayingRecognizerForWrapperWebView {
3219 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3220
3221 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3222 /*platform=*/GetDefaultTaskRunner(),
3223 /*raster=*/GetDefaultTaskRunner(),
3224 /*ui=*/GetDefaultTaskRunner(),
3225 /*io=*/GetDefaultTaskRunner());
3226 FlutterPlatformViewsController* flutterPlatformViewsController =
3227 [[FlutterPlatformViewsController alloc] init];
3228 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3229 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3230 /*delegate=*/mock_delegate,
3231 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3232 /*platform_views_controller=*/flutterPlatformViewsController,
3233 /*task_runners=*/runners,
3234 /*worker_task_runner=*/nil,
3235 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3236
3237 FlutterPlatformViewsTestMockWrapperWebViewFactory* factory =
3238 [[FlutterPlatformViewsTestMockWrapperWebViewFactory alloc] init];
3239 [flutterPlatformViewsController
3240 registerViewFactory:factory
3241 withId:@"MockWrapperWebView"
3242 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3243 FlutterResult result = ^(id result) {
3244 };
3245 [flutterPlatformViewsController
3246 onMethodCall:[FlutterMethodCall
3247 methodCallWithMethodName:@"create"
3248 arguments:@{@"id" : @2, @"viewType" : @"MockWrapperWebView"}]
3249 result:result];
3250
3251 XCTAssertNotNil(gMockPlatformView);
3252
3253 // Find touch inteceptor view
3254 UIView* touchInteceptorView = gMockPlatformView;
3255 while (touchInteceptorView != nil &&
3256 ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
3257 touchInteceptorView = touchInteceptorView.superview;
3258 }
3259 XCTAssertNotNil(touchInteceptorView);
3260
3261 XCTAssert(touchInteceptorView.gestureRecognizers.count == 2);
3262 UIGestureRecognizer* delayingRecognizer = touchInteceptorView.gestureRecognizers[0];
3263 UIGestureRecognizer* forwardingRecognizer = touchInteceptorView.gestureRecognizers[1];
3264
3265 XCTAssert([delayingRecognizer isKindOfClass:[FlutterDelayingGestureRecognizer class]]);
3266 XCTAssert([forwardingRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]);
3267
3268 [(FlutterTouchInterceptingView*)touchInteceptorView blockGesture];
3269
3270 BOOL shouldReAddDelayingRecognizer = NO;
3271 if (@available(iOS 26.0, *)) {
3272 // TODO(hellohuanlin): find a solution for iOS 26,
3273 // https://github.com/flutter/flutter/issues/175099.
3274 } else if (@available(iOS 18.2, *)) {
3275 shouldReAddDelayingRecognizer = YES;
3276 }
3277 if (shouldReAddDelayingRecognizer) {
3278 // Since we remove and add back delayingRecognizer, it would be reordered to the last.
3279 XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], forwardingRecognizer);
3280 XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], delayingRecognizer);
3281 } else {
3282 XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], delayingRecognizer);
3283 XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], forwardingRecognizer);
3284 }
3285 }
3286
3287 - (void)
3288 testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldNotRemoveAndAddBackDelayingRecognizerForNestedWrapperWebView {
3289 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3290
3291 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3292 /*platform=*/GetDefaultTaskRunner(),
3293 /*raster=*/GetDefaultTaskRunner(),
3294 /*ui=*/GetDefaultTaskRunner(),
3295 /*io=*/GetDefaultTaskRunner());
3296 FlutterPlatformViewsController* flutterPlatformViewsController =
3297 [[FlutterPlatformViewsController alloc] init];
3298 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3299 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3300 /*delegate=*/mock_delegate,
3301 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3302 /*platform_views_controller=*/flutterPlatformViewsController,
3303 /*task_runners=*/runners,
3304 /*worker_task_runner=*/nil,
3305 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3306
3307 FlutterPlatformViewsTestMockNestedWrapperWebViewFactory* factory =
3308 [[FlutterPlatformViewsTestMockNestedWrapperWebViewFactory alloc] init];
3309 [flutterPlatformViewsController
3310 registerViewFactory:factory
3311 withId:@"MockNestedWrapperWebView"
3312 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3313 FlutterResult result = ^(id result) {
3314 };
3315 [flutterPlatformViewsController
3316 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3317 arguments:@{
3318 @"id" : @2,
3319 @"viewType" : @"MockNestedWrapperWebView"
3320 }]
3321 result:result];
3322
3323 XCTAssertNotNil(gMockPlatformView);
3324
3325 // Find touch inteceptor view
3326 UIView* touchInteceptorView = gMockPlatformView;
3327 while (touchInteceptorView != nil &&
3328 ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
3329 touchInteceptorView = touchInteceptorView.superview;
3330 }
3331 XCTAssertNotNil(touchInteceptorView);
3332
3333 XCTAssert(touchInteceptorView.gestureRecognizers.count == 2);
3334 UIGestureRecognizer* delayingRecognizer = touchInteceptorView.gestureRecognizers[0];
3335 UIGestureRecognizer* forwardingRecognizer = touchInteceptorView.gestureRecognizers[1];
3336
3337 XCTAssert([delayingRecognizer isKindOfClass:[FlutterDelayingGestureRecognizer class]]);
3338 XCTAssert([forwardingRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]);
3339
3340 [(FlutterTouchInterceptingView*)touchInteceptorView blockGesture];
3341
3342 XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], delayingRecognizer);
3343 XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], forwardingRecognizer);
3344 }
3345
3346 - (void)
3347 testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldNotRemoveAndAddBackDelayingRecognizerForNonWebView {
3348 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3349
3350 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3351 /*platform=*/GetDefaultTaskRunner(),
3352 /*raster=*/GetDefaultTaskRunner(),
3353 /*ui=*/GetDefaultTaskRunner(),
3354 /*io=*/GetDefaultTaskRunner());
3355 FlutterPlatformViewsController* flutterPlatformViewsController =
3356 [[FlutterPlatformViewsController alloc] init];
3357 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3358 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3359 /*delegate=*/mock_delegate,
3360 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3361 /*platform_views_controller=*/flutterPlatformViewsController,
3362 /*task_runners=*/runners,
3363 /*worker_task_runner=*/nil,
3364 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3365
3366 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3367 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3368 [flutterPlatformViewsController
3369 registerViewFactory:factory
3370 withId:@"MockFlutterPlatformView"
3371 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3372 FlutterResult result = ^(id result) {
3373 };
3374 [flutterPlatformViewsController
3375 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3376 arguments:@{
3377 @"id" : @2,
3378 @"viewType" : @"MockFlutterPlatformView"
3379 }]
3380 result:result];
3381
3382 XCTAssertNotNil(gMockPlatformView);
3383
3384 // Find touch inteceptor view
3385 UIView* touchInteceptorView = gMockPlatformView;
3386 while (touchInteceptorView != nil &&
3387 ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
3388 touchInteceptorView = touchInteceptorView.superview;
3389 }
3390 XCTAssertNotNil(touchInteceptorView);
3391
3392 XCTAssert(touchInteceptorView.gestureRecognizers.count == 2);
3393 UIGestureRecognizer* delayingRecognizer = touchInteceptorView.gestureRecognizers[0];
3394 UIGestureRecognizer* forwardingRecognizer = touchInteceptorView.gestureRecognizers[1];
3395
3396 XCTAssert([delayingRecognizer isKindOfClass:[FlutterDelayingGestureRecognizer class]]);
3397 XCTAssert([forwardingRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]);
3398
3399 [(FlutterTouchInterceptingView*)touchInteceptorView blockGesture];
3400
3401 XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], delayingRecognizer);
3402 XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], forwardingRecognizer);
3403 }
3404
3405 - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashing {
3406 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3407
3408 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3409 /*platform=*/GetDefaultTaskRunner(),
3410 /*raster=*/GetDefaultTaskRunner(),
3411 /*ui=*/GetDefaultTaskRunner(),
3412 /*io=*/GetDefaultTaskRunner());
3413 FlutterPlatformViewsController* flutterPlatformViewsController =
3414 [[FlutterPlatformViewsController alloc] init];
3415 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3416 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3417 /*delegate=*/mock_delegate,
3418 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3419 /*platform_views_controller=*/flutterPlatformViewsController,
3420 /*task_runners=*/runners,
3421 /*worker_task_runner=*/nil,
3422 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3423
3424 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3425 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3426 [flutterPlatformViewsController
3427 registerViewFactory:factory
3428 withId:@"MockFlutterPlatformView"
3429 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3430 FlutterResult result = ^(id result) {
3431 };
3432 [flutterPlatformViewsController
3433 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3434 arguments:@{
3435 @"id" : @2,
3436 @"viewType" : @"MockFlutterPlatformView"
3437 }]
3438 result:result];
3439
3440 XCTAssertNotNil(gMockPlatformView);
3441
3442 // Create embedded view params
3443 flutter::MutatorsStack stack;
3444 flutter::DlMatrix finalMatrix;
3445
3446 auto embeddedViewParams_1 = std::make_unique<flutter::EmbeddedViewParams>(
3447 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3448
3449 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
3450 withParams:std::move(embeddedViewParams_1)];
3451 [flutterPlatformViewsController
3452 compositeView:2
3453 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
3454
3455 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3456 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3457 nullptr, framebuffer_info,
3458 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return false; },
3459 [](const flutter::SurfaceFrame& surface_frame) { return true; },
3460 /*frame_size=*/SkISize::Make(800, 600));
3461 XCTAssertFalse([flutterPlatformViewsController
3462 submitFrame:std::move(mock_surface)
3463 withIosContext:std::make_shared<flutter::IOSContextNoop>()]);
3464
3465 auto embeddedViewParams_2 = std::make_unique<flutter::EmbeddedViewParams>(
3466 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3467 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
3468 withParams:std::move(embeddedViewParams_2)];
3469 [flutterPlatformViewsController
3470 compositeView:2
3471 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
3472
3473 auto mock_surface_submit_true = std::make_unique<flutter::SurfaceFrame>(
3474 nullptr, framebuffer_info,
3475 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3476 [](const flutter::SurfaceFrame& surface_frame) { return true; },
3477 /*frame_size=*/SkISize::Make(800, 600));
3478 XCTAssertTrue([flutterPlatformViewsController
3479 submitFrame:std::move(mock_surface_submit_true)
3480 withIosContext:std::make_shared<flutter::IOSContextNoop>()]);
3481 }
3482
3483 - (void)
3484 testFlutterPlatformViewControllerResetDeallocsPlatformViewWhenRootViewsNotBindedToFlutterView {
3485 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3486
3487 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3488 /*platform=*/GetDefaultTaskRunner(),
3489 /*raster=*/GetDefaultTaskRunner(),
3490 /*ui=*/GetDefaultTaskRunner(),
3491 /*io=*/GetDefaultTaskRunner());
3492 FlutterPlatformViewsController* flutterPlatformViewsController =
3493 [[FlutterPlatformViewsController alloc] init];
3494 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3495 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3496 /*delegate=*/mock_delegate,
3497 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3498 /*platform_views_controller=*/flutterPlatformViewsController,
3499 /*task_runners=*/runners,
3500 /*worker_task_runner=*/nil,
3501 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3502
3503 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3504 flutterPlatformViewsController.flutterView = flutterView;
3505
3506 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3507 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3508 [flutterPlatformViewsController
3509 registerViewFactory:factory
3510 withId:@"MockFlutterPlatformView"
3511 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3512 FlutterResult result = ^(id result) {
3513 };
3514 // autorelease pool to trigger an autorelease for all the root_views_ and touch_interceptors_.
3515 @autoreleasepool {
3516 [flutterPlatformViewsController
3517 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3518 arguments:@{
3519 @"id" : @2,
3520 @"viewType" : @"MockFlutterPlatformView"
3521 }]
3522 result:result];
3523
3524 flutter::MutatorsStack stack;
3525 flutter::DlMatrix finalMatrix;
3526 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
3527 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3528 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
3529 withParams:std::move(embeddedViewParams)];
3530
3531 // Not calling |[flutterPlatformViewsController submitFrame:withIosContext:]| so that
3532 // the platform views are not added to flutter_view_.
3533
3534 XCTAssertNotNil(gMockPlatformView);
3535 [flutterPlatformViewsController reset];
3536 }
3537 XCTAssertNil(gMockPlatformView);
3538 }
3539
3540 - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder {
3541 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3542
3543 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3544 /*platform=*/GetDefaultTaskRunner(),
3545 /*raster=*/GetDefaultTaskRunner(),
3546 /*ui=*/GetDefaultTaskRunner(),
3547 /*io=*/GetDefaultTaskRunner());
3548 FlutterPlatformViewsController* flutterPlatformViewsController =
3549 [[FlutterPlatformViewsController alloc] init];
3550 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3551 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3552 /*delegate=*/mock_delegate,
3553 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3554 /*platform_views_controller=*/flutterPlatformViewsController,
3555 /*task_runners=*/runners,
3556 /*worker_task_runner=*/nil,
3557 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3558
3559 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3560 flutterPlatformViewsController.flutterView = flutterView;
3561
3562 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3563 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3564 [flutterPlatformViewsController
3565 registerViewFactory:factory
3566 withId:@"MockFlutterPlatformView"
3567 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3568 FlutterResult result = ^(id result) {
3569 };
3570
3571 [flutterPlatformViewsController
3572 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3573 arguments:@{
3574 @"id" : @0,
3575 @"viewType" : @"MockFlutterPlatformView"
3576 }]
3577 result:result];
3578
3579 // First frame, |embeddedViewCount| is not empty after composite.
3580 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
3581 flutter::MutatorsStack stack;
3582 flutter::DlMatrix finalMatrix;
3583 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
3584 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3585 [flutterPlatformViewsController prerollCompositeEmbeddedView:0
3586 withParams:std::move(embeddedViewParams1)];
3587 [flutterPlatformViewsController
3588 compositeView:0
3589 withParams:[flutterPlatformViewsController compositionParamsForView:0]];
3590
3591 XCTAssertEqual(flutterPlatformViewsController.embeddedViewCount, 1UL);
3592
3593 // Second frame, |embeddedViewCount| should be empty at the start
3594 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
3595 XCTAssertEqual(flutterPlatformViewsController.embeddedViewCount, 0UL);
3596
3597 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
3598 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3599 [flutterPlatformViewsController prerollCompositeEmbeddedView:0
3600 withParams:std::move(embeddedViewParams2)];
3601 [flutterPlatformViewsController
3602 compositeView:0
3603 withParams:[flutterPlatformViewsController compositionParamsForView:0]];
3604
3605 XCTAssertEqual(flutterPlatformViewsController.embeddedViewCount, 1UL);
3606 }
3607
3608 - (void)
3609 testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithDifferentViewHierarchy {
3610 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3611
3612 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3613 /*platform=*/GetDefaultTaskRunner(),
3614 /*raster=*/GetDefaultTaskRunner(),
3615 /*ui=*/GetDefaultTaskRunner(),
3616 /*io=*/GetDefaultTaskRunner());
3617 FlutterPlatformViewsController* flutterPlatformViewsController =
3618 [[FlutterPlatformViewsController alloc] init];
3619 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3620 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3621 /*delegate=*/mock_delegate,
3622 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3623 /*platform_views_controller=*/flutterPlatformViewsController,
3624 /*task_runners=*/runners,
3625 /*worker_task_runner=*/nil,
3626 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3627
3628 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3629 flutterPlatformViewsController.flutterView = flutterView;
3630
3631 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3632 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3633 [flutterPlatformViewsController
3634 registerViewFactory:factory
3635 withId:@"MockFlutterPlatformView"
3636 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3637 FlutterResult result = ^(id result) {
3638 };
3639 [flutterPlatformViewsController
3640 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3641 arguments:@{
3642 @"id" : @0,
3643 @"viewType" : @"MockFlutterPlatformView"
3644 }]
3645 result:result];
3646 UIView* view1 = gMockPlatformView;
3647
3648 // This overwrites `gMockPlatformView` to another view.
3649 [flutterPlatformViewsController
3650 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3651 arguments:@{
3652 @"id" : @1,
3653 @"viewType" : @"MockFlutterPlatformView"
3654 }]
3655 result:result];
3656 UIView* view2 = gMockPlatformView;
3657
3658 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
3659 flutter::MutatorsStack stack;
3660 flutter::DlMatrix finalMatrix;
3661 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
3662 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3663 [flutterPlatformViewsController prerollCompositeEmbeddedView:0
3664 withParams:std::move(embeddedViewParams1)];
3665
3666 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
3667 flutter::ToSkMatrix(finalMatrix), SkSize::Make(500, 500), stack);
3668 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
3669 withParams:std::move(embeddedViewParams2)];
3670
3671 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3672 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3673 nullptr, framebuffer_info,
3674 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3675 [](const flutter::SurfaceFrame& surface_frame) { return true; },
3676 /*frame_size=*/SkISize::Make(800, 600), nullptr, /*display_list_fallback=*/true);
3677 XCTAssertTrue([flutterPlatformViewsController
3678 submitFrame:std::move(mock_surface)
3679 withIosContext:std::make_shared<flutter::IOSContextNoop>()]);
3680
3681 // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view.
3682 UIView* clippingView1 = view1.superview.superview;
3683 UIView* clippingView2 = view2.superview.superview;
3684 XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
3685 [flutterView.subviews indexOfObject:clippingView2],
3686 @"The first clipping view should be added before the second clipping view.");
3687
3688 // Need to recreate these params since they are `std::move`ed.
3689 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
3690 // Process the second frame in the opposite order.
3691 embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
3692 flutter::ToSkMatrix(finalMatrix), SkSize::Make(500, 500), stack);
3693 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
3694 withParams:std::move(embeddedViewParams2)];
3695
3696 embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
3697 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3698 [flutterPlatformViewsController prerollCompositeEmbeddedView:0
3699 withParams:std::move(embeddedViewParams1)];
3700
3701 mock_surface = std::make_unique<flutter::SurfaceFrame>(
3702 nullptr, framebuffer_info,
3703 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3704 [](const flutter::SurfaceFrame& surface_frame) { return true; },
3705 /*frame_size=*/SkISize::Make(800, 600), nullptr, /*display_list_fallback=*/true);
3706 XCTAssertTrue([flutterPlatformViewsController
3707 submitFrame:std::move(mock_surface)
3708 withIosContext:std::make_shared<flutter::IOSContextNoop>()]);
3709
3710 XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] >
3711 [flutterView.subviews indexOfObject:clippingView2],
3712 @"The first clipping view should be added after the second clipping view.");
3713 }
3714
3715 - (void)
3716 testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithSameViewHierarchy {
3717 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3718
3719 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3720 /*platform=*/GetDefaultTaskRunner(),
3721 /*raster=*/GetDefaultTaskRunner(),
3722 /*ui=*/GetDefaultTaskRunner(),
3723 /*io=*/GetDefaultTaskRunner());
3724 FlutterPlatformViewsController* flutterPlatformViewsController =
3725 [[FlutterPlatformViewsController alloc] init];
3726 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3727 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3728 /*delegate=*/mock_delegate,
3729 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3730 /*platform_views_controller=*/flutterPlatformViewsController,
3731 /*task_runners=*/runners,
3732 /*worker_task_runner=*/nil,
3733 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3734
3735 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3736 flutterPlatformViewsController.flutterView = flutterView;
3737
3738 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3739 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3740 [flutterPlatformViewsController
3741 registerViewFactory:factory
3742 withId:@"MockFlutterPlatformView"
3743 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3744 FlutterResult result = ^(id result) {
3745 };
3746 [flutterPlatformViewsController
3747 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3748 arguments:@{
3749 @"id" : @0,
3750 @"viewType" : @"MockFlutterPlatformView"
3751 }]
3752 result:result];
3753 UIView* view1 = gMockPlatformView;
3754
3755 // This overwrites `gMockPlatformView` to another view.
3756 [flutterPlatformViewsController
3757 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3758 arguments:@{
3759 @"id" : @1,
3760 @"viewType" : @"MockFlutterPlatformView"
3761 }]
3762 result:result];
3763 UIView* view2 = gMockPlatformView;
3764
3765 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
3766 flutter::MutatorsStack stack;
3767 flutter::DlMatrix finalMatrix;
3768 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
3769 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3770 [flutterPlatformViewsController prerollCompositeEmbeddedView:0
3771 withParams:std::move(embeddedViewParams1)];
3772
3773 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
3774 flutter::ToSkMatrix(finalMatrix), SkSize::Make(500, 500), stack);
3775 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
3776 withParams:std::move(embeddedViewParams2)];
3777
3778 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3779 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3780 nullptr, framebuffer_info,
3781 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3782 [](const flutter::SurfaceFrame& surface_frame) { return true; },
3783 /*frame_size=*/SkISize::Make(800, 600), nullptr, /*display_list_fallback=*/true);
3784 XCTAssertTrue([flutterPlatformViewsController
3785 submitFrame:std::move(mock_surface)
3786 withIosContext:std::make_shared<flutter::IOSContextNoop>()]);
3787
3788 // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view.
3789 UIView* clippingView1 = view1.superview.superview;
3790 UIView* clippingView2 = view2.superview.superview;
3791 XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
3792 [flutterView.subviews indexOfObject:clippingView2],
3793 @"The first clipping view should be added before the second clipping view.");
3794
3795 // Need to recreate these params since they are `std::move`ed.
3796 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
3797 // Process the second frame in the same order.
3798 embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
3799 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3800 [flutterPlatformViewsController prerollCompositeEmbeddedView:0
3801 withParams:std::move(embeddedViewParams1)];
3802
3803 embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
3804 flutter::ToSkMatrix(finalMatrix), SkSize::Make(500, 500), stack);
3805 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
3806 withParams:std::move(embeddedViewParams2)];
3807
3808 mock_surface = std::make_unique<flutter::SurfaceFrame>(
3809 nullptr, framebuffer_info,
3810 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3811 [](const flutter::SurfaceFrame& surface_frame) { return true; },
3812 /*frame_size=*/SkISize::Make(800, 600), nullptr, /*display_list_fallback=*/true);
3813 XCTAssertTrue([flutterPlatformViewsController
3814 submitFrame:std::move(mock_surface)
3815 withIosContext:std::make_shared<flutter::IOSContextNoop>()]);
3816
3817 XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
3818 [flutterView.subviews indexOfObject:clippingView2],
3819 @"The first clipping view should be added before the second clipping view.");
3820 }
3821
3822 - (int)alphaOfPoint:(CGPoint)point onView:(UIView*)view {
3823 unsigned char pixel[4] = {0};
3824
3825 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
3826
3827 // Draw the pixel on `point` in the context.
3828 CGContextRef context = CGBitmapContextCreate(
3829 pixel, 1, 1, 8, 4, colorSpace, kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedLast);
3830 CGContextTranslateCTM(context, -point.x, -point.y);
3831 [view.layer renderInContext:context];
3832
3833 CGContextRelease(context);
3834 CGColorSpaceRelease(colorSpace);
3835 // Get the alpha from the pixel that we just rendered.
3836 return pixel[3];
3837 }
3838
3839 - (void)testHasFirstResponderInViewHierarchySubtree_viewItselfBecomesFirstResponder {
3840 // For view to become the first responder, it must be a descendant of a UIWindow
3841 UIWindow* window = [[UIWindow alloc] init];
3842 UITextField* textField = [[UITextField alloc] init];
3843 [window addSubview:textField];
3844
3845 [textField becomeFirstResponder];
3846 XCTAssertTrue(textField.isFirstResponder);
3847 XCTAssertTrue(textField.flt_hasFirstResponderInViewHierarchySubtree);
3848 [textField resignFirstResponder];
3849 XCTAssertFalse(textField.isFirstResponder);
3850 XCTAssertFalse(textField.flt_hasFirstResponderInViewHierarchySubtree);
3851 }
3852
3853 - (void)testHasFirstResponderInViewHierarchySubtree_descendantViewBecomesFirstResponder {
3854 // For view to become the first responder, it must be a descendant of a UIWindow
3855 UIWindow* window = [[UIWindow alloc] init];
3856 UIView* view = [[UIView alloc] init];
3857 UIView* childView = [[UIView alloc] init];
3858 UITextField* textField = [[UITextField alloc] init];
3859 [window addSubview:view];
3860 [view addSubview:childView];
3861 [childView addSubview:textField];
3862
3863 [textField becomeFirstResponder];
3864 XCTAssertTrue(textField.isFirstResponder);
3865 XCTAssertTrue(view.flt_hasFirstResponderInViewHierarchySubtree);
3866 [textField resignFirstResponder];
3867 XCTAssertFalse(textField.isFirstResponder);
3868 XCTAssertFalse(view.flt_hasFirstResponderInViewHierarchySubtree);
3869 }
3870
3871 - (void)testFlutterClippingMaskViewPoolReuseViewsAfterRecycle {
3872 FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2];
3873 FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero];
3874 FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero];
3875 [pool insertViewToPoolIfNeeded:view1];
3876 [pool insertViewToPoolIfNeeded:view2];
3877 CGRect newRect = CGRectMake(0, 0, 10, 10);
3878 FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:newRect];
3879 FlutterClippingMaskView* view4 = [pool getMaskViewWithFrame:newRect];
3880 // view3 and view4 should randomly get either of view1 and view2.
3881 NSSet* set1 = [NSSet setWithObjects:view1, view2, nil];
3882 NSSet* set2 = [NSSet setWithObjects:view3, view4, nil];
3883 XCTAssertEqualObjects(set1, set2);
3884 XCTAssertTrue(CGRectEqualToRect(view3.frame, newRect));
3885 XCTAssertTrue(CGRectEqualToRect(view4.frame, newRect));
3886 }
3887
3888 - (void)testFlutterClippingMaskViewPoolAllocsNewMaskViewsAfterReachingCapacity {
3889 FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2];
3890 FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero];
3891 FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero];
3892 FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:CGRectZero];
3893 XCTAssertNotEqual(view1, view3);
3894 XCTAssertNotEqual(view2, view3);
3895 }
3896
3897 - (void)testMaskViewsReleasedWhenPoolIsReleased {
3898 __weak UIView* weakView;
3899 @autoreleasepool {
3900 FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2];
3901 FlutterClippingMaskView* view = [pool getMaskViewWithFrame:CGRectZero];
3902 weakView = view;
3903 XCTAssertNotNil(weakView);
3904 }
3905 XCTAssertNil(weakView);
3906 }
3907
3908 - (void)testClipMaskViewIsReused {
3909 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3910
3911 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3912 /*platform=*/GetDefaultTaskRunner(),
3913 /*raster=*/GetDefaultTaskRunner(),
3914 /*ui=*/GetDefaultTaskRunner(),
3915 /*io=*/GetDefaultTaskRunner());
3916 FlutterPlatformViewsController* flutterPlatformViewsController =
3917 [[FlutterPlatformViewsController alloc] init];
3918 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3919 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3920 /*delegate=*/mock_delegate,
3921 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3922 /*platform_views_controller=*/flutterPlatformViewsController,
3923 /*task_runners=*/runners,
3924 /*worker_task_runner=*/nil,
3925 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3926
3927 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3928 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3929 [flutterPlatformViewsController
3930 registerViewFactory:factory
3931 withId:@"MockFlutterPlatformView"
3932 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3933 FlutterResult result = ^(id result) {
3934 };
3935 [flutterPlatformViewsController
3936 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3937 arguments:@{
3938 @"id" : @1,
3939 @"viewType" : @"MockFlutterPlatformView"
3940 }]
3941 result:result];
3942
3943 XCTAssertNotNil(gMockPlatformView);
3944 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
3945 flutterPlatformViewsController.flutterView = flutterView;
3946 // Create embedded view params
3947 flutter::MutatorsStack stack1;
3948 // Layer tree always pushes a screen scale factor to the stack
3949 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
3950 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
3951 stack1.PushTransform(screenScaleMatrix);
3952 // Push a clip rect
3953 flutter::DlRect rect = flutter::DlRect::MakeXYWH(2, 2, 3, 3);
3954 stack1.PushClipRect(rect);
3955
3956 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
3957 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack1);
3958
3959 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
3960 withParams:std::move(embeddedViewParams1)];
3961 [flutterPlatformViewsController
3962 compositeView:1
3963 withParams:[flutterPlatformViewsController compositionParamsForView:1]];
3964
3965 UIView* childClippingView1 = gMockPlatformView.superview.superview;
3966 UIView* maskView1 = childClippingView1.maskView;
3967 XCTAssertNotNil(maskView1);
3968
3969 // Composite a new frame.
3970 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(100, 100)];
3971 flutter::MutatorsStack stack2;
3972 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
3973 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
3974 auto embeddedViewParams3 = std::make_unique<flutter::EmbeddedViewParams>(
3975 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
3976 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
3977 withParams:std::move(embeddedViewParams3)];
3978 [flutterPlatformViewsController
3979 compositeView:1
3980 withParams:[flutterPlatformViewsController compositionParamsForView:1]];
3981
3982 childClippingView1 = gMockPlatformView.superview.superview;
3983
3984 // This overrides gMockPlatformView to point to the newly created platform view.
3985 [flutterPlatformViewsController
3986 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3987 arguments:@{
3988 @"id" : @2,
3989 @"viewType" : @"MockFlutterPlatformView"
3990 }]
3991 result:result];
3992
3993 auto embeddedViewParams4 = std::make_unique<flutter::EmbeddedViewParams>(
3994 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack1);
3995 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
3996 withParams:std::move(embeddedViewParams4)];
3997 [flutterPlatformViewsController
3998 compositeView:2
3999 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
4000
4001 UIView* childClippingView2 = gMockPlatformView.superview.superview;
4002
4003 UIView* maskView2 = childClippingView2.maskView;
4004 XCTAssertEqual(maskView1, maskView2);
4005 XCTAssertNotNil(childClippingView2.maskView);
4006 XCTAssertNil(childClippingView1.maskView);
4007 }
4008
4009 - (void)testDifferentClipMaskViewIsUsedForEachView {
4010 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
4011
4012 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
4013 /*platform=*/GetDefaultTaskRunner(),
4014 /*raster=*/GetDefaultTaskRunner(),
4015 /*ui=*/GetDefaultTaskRunner(),
4016 /*io=*/GetDefaultTaskRunner());
4017 FlutterPlatformViewsController* flutterPlatformViewsController =
4018 [[FlutterPlatformViewsController alloc] init];
4019 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
4020 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
4021 /*delegate=*/mock_delegate,
4022 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
4023 /*platform_views_controller=*/flutterPlatformViewsController,
4024 /*task_runners=*/runners,
4025 /*worker_task_runner=*/nil,
4026 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
4027
4028 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
4029 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
4030 [flutterPlatformViewsController
4031 registerViewFactory:factory
4032 withId:@"MockFlutterPlatformView"
4033 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
4034 FlutterResult result = ^(id result) {
4035 };
4036
4037 [flutterPlatformViewsController
4038 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4039 arguments:@{
4040 @"id" : @1,
4041 @"viewType" : @"MockFlutterPlatformView"
4042 }]
4043 result:result];
4044 UIView* view1 = gMockPlatformView;
4045
4046 // This overwrites `gMockPlatformView` to another view.
4047 [flutterPlatformViewsController
4048 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4049 arguments:@{
4050 @"id" : @2,
4051 @"viewType" : @"MockFlutterPlatformView"
4052 }]
4053 result:result];
4054 UIView* view2 = gMockPlatformView;
4055
4056 XCTAssertNotNil(gMockPlatformView);
4057 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
4058 flutterPlatformViewsController.flutterView = flutterView;
4059 // Create embedded view params
4060 flutter::MutatorsStack stack1;
4061 // Layer tree always pushes a screen scale factor to the stack
4062 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
4063 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
4064 stack1.PushTransform(screenScaleMatrix);
4065 // Push a clip rect
4066 flutter::DlRect rect = flutter::DlRect::MakeXYWH(2, 2, 3, 3);
4067 stack1.PushClipRect(rect);
4068
4069 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
4070 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack1);
4071
4072 flutter::MutatorsStack stack2;
4073 stack2.PushClipRect(rect);
4074 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
4075 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
4076
4077 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
4078 withParams:std::move(embeddedViewParams1)];
4079 [flutterPlatformViewsController
4080 compositeView:1
4081 withParams:[flutterPlatformViewsController compositionParamsForView:1]];
4082
4083 UIView* childClippingView1 = view1.superview.superview;
4084
4085 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
4086 withParams:std::move(embeddedViewParams2)];
4087 [flutterPlatformViewsController
4088 compositeView:2
4089 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
4090
4091 UIView* childClippingView2 = view2.superview.superview;
4092 UIView* maskView1 = childClippingView1.maskView;
4093 UIView* maskView2 = childClippingView2.maskView;
4094 XCTAssertNotEqual(maskView1, maskView2);
4095 }
4096
4097 - (void)testMaskViewUsesCAShapeLayerAsTheBackingLayer {
4098 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
4099
4100 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
4101 /*platform=*/GetDefaultTaskRunner(),
4102 /*raster=*/GetDefaultTaskRunner(),
4103 /*ui=*/GetDefaultTaskRunner(),
4104 /*io=*/GetDefaultTaskRunner());
4105 FlutterPlatformViewsController* flutterPlatformViewsController =
4106 [[FlutterPlatformViewsController alloc] init];
4107 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
4108 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
4109 /*delegate=*/mock_delegate,
4110 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
4111 /*platform_views_controller=*/flutterPlatformViewsController,
4112 /*task_runners=*/runners,
4113 /*worker_task_runner=*/nil,
4114 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
4115
4116 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
4117 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
4118 [flutterPlatformViewsController
4119 registerViewFactory:factory
4120 withId:@"MockFlutterPlatformView"
4121 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
4122 FlutterResult result = ^(id result) {
4123 };
4124
4125 [flutterPlatformViewsController
4126 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4127 arguments:@{
4128 @"id" : @1,
4129 @"viewType" : @"MockFlutterPlatformView"
4130 }]
4131 result:result];
4132
4133 XCTAssertNotNil(gMockPlatformView);
4134 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
4135 flutterPlatformViewsController.flutterView = flutterView;
4136 // Create embedded view params
4137 flutter::MutatorsStack stack1;
4138 // Layer tree always pushes a screen scale factor to the stack
4139 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
4140 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
4141 stack1.PushTransform(screenScaleMatrix);
4142 // Push a clip rect
4143 flutter::DlRect rect = flutter::DlRect::MakeXYWH(2, 2, 3, 3);
4144 stack1.PushClipRect(rect);
4145
4146 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
4147 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack1);
4148
4149 flutter::MutatorsStack stack2;
4150 stack2.PushClipRect(rect);
4151 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
4152 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
4153
4154 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
4155 withParams:std::move(embeddedViewParams1)];
4156 [flutterPlatformViewsController
4157 compositeView:1
4158 withParams:[flutterPlatformViewsController compositionParamsForView:1]];
4159
4160 UIView* childClippingView = gMockPlatformView.superview.superview;
4161
4162 UIView* maskView = childClippingView.maskView;
4163 XCTAssert([maskView.layer isKindOfClass:[CAShapeLayer class]],
4164 @"Mask view must use CAShapeLayer as its backing layer.");
4165 }
4166
4167 // Return true if a correct visual effect view is found. It also implies all the validation in this
4168 // method passes.
4169 //
4170 // There are two fail states for this method. 1. One of the XCTAssert method failed; or 2. No
4171 // correct visual effect view found.
4172 - (BOOL)validateOneVisualEffectView:(UIView*)visualEffectView
4173 expectedFrame:(CGRect)frame
4174 inputRadius:(CGFloat)inputRadius {
4175 XCTAssertTrue(CGRectEqualToRect(visualEffectView.frame, frame));
4176 for (UIView* view in visualEffectView.subviews) {
4177 if (![NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) {
4178 continue;
4179 }
4180 XCTAssertEqual(view.layer.filters.count, 1u);
4181 NSObject* filter = view.layer.filters.firstObject;
4182
4183 XCTAssertEqualObjects([filter valueForKey:@"name"], @"gaussianBlur");
4184
4185 NSObject* inputRadiusInFilter = [filter valueForKey:@"inputRadius"];
4186 XCTAssertTrue([inputRadiusInFilter isKindOfClass:[NSNumber class]] &&
4187 flutter::BlurRadiusEqualToBlurRadius(((NSNumber*)inputRadiusInFilter).floatValue,
4188 inputRadius));
4189 return YES;
4190 }
4191 return NO;
4192 }
4193
4194 - (void)testDisposingViewInCompositionOrderDoNotCrash {
4195 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
4196
4197 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
4198 /*platform=*/GetDefaultTaskRunner(),
4199 /*raster=*/GetDefaultTaskRunner(),
4200 /*ui=*/GetDefaultTaskRunner(),
4201 /*io=*/GetDefaultTaskRunner());
4202 FlutterPlatformViewsController* flutterPlatformViewsController =
4203 [[FlutterPlatformViewsController alloc] init];
4204 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
4205 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
4206 /*delegate=*/mock_delegate,
4207 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
4208 /*platform_views_controller=*/flutterPlatformViewsController,
4209 /*task_runners=*/runners,
4210 /*worker_task_runner=*/nil,
4211 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
4212
4213 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
4214 flutterPlatformViewsController.flutterView = flutterView;
4215
4216 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
4217 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
4218 [flutterPlatformViewsController
4219 registerViewFactory:factory
4220 withId:@"MockFlutterPlatformView"
4221 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
4222 FlutterResult result = ^(id result) {
4223 };
4224
4225 [flutterPlatformViewsController
4226 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4227 arguments:@{
4228 @"id" : @0,
4229 @"viewType" : @"MockFlutterPlatformView"
4230 }]
4231 result:result];
4232 [flutterPlatformViewsController
4233 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4234 arguments:@{
4235 @"id" : @1,
4236 @"viewType" : @"MockFlutterPlatformView"
4237 }]
4238 result:result];
4239
4240 {
4241 // **** First frame, view id 0, 1 in the composition_order_, disposing view 0 is called. **** //
4242 // No view should be disposed, or removed from the composition order.
4243 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
4244 flutter::MutatorsStack stack;
4245 flutter::DlMatrix finalMatrix;
4246 auto embeddedViewParams0 = std::make_unique<flutter::EmbeddedViewParams>(
4247 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
4248 [flutterPlatformViewsController prerollCompositeEmbeddedView:0
4249 withParams:std::move(embeddedViewParams0)];
4250
4251 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
4252 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
4253 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
4254 withParams:std::move(embeddedViewParams1)];
4255
4256 XCTAssertEqual(flutterPlatformViewsController.embeddedViewCount, 2UL);
4257
4258 XCTestExpectation* expectation = [self expectationWithDescription:@"dispose call ended."];
4259 FlutterResult disposeResult = ^(id result) {
4260 [expectation fulfill];
4261 };
4262
4263 [flutterPlatformViewsController
4264 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"dispose" arguments:@0]
4265 result:disposeResult];
4266 [self waitForExpectationsWithTimeout:30 handler:nil];
4267
4268 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
4269 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
4270 nullptr, framebuffer_info,
4271 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
4272 [](const flutter::SurfaceFrame& surface_frame) { return true; },
4273 /*frame_size=*/SkISize::Make(800, 600), nullptr,
4274 /*display_list_fallback=*/true);
4275 XCTAssertTrue([flutterPlatformViewsController
4276 submitFrame:std::move(mock_surface)
4277 withIosContext:std::make_shared<flutter::IOSContextNoop>()]);
4278
4279 // Disposing won't remove embedded views until the view is removed from the composition_order_
4280 XCTAssertEqual(flutterPlatformViewsController.embeddedViewCount, 2UL);
4281 XCTAssertNotNil([flutterPlatformViewsController platformViewForId:0]);
4282 XCTAssertNotNil([flutterPlatformViewsController platformViewForId:1]);
4283 }
4284
4285 {
4286 // **** Second frame, view id 1 in the composition_order_, no disposing view is called, **** //
4287 // View 0 is removed from the composition order in this frame, hence also disposed.
4288 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
4289 flutter::MutatorsStack stack;
4290 flutter::DlMatrix finalMatrix;
4291 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
4292 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
4293 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
4294 withParams:std::move(embeddedViewParams1)];
4295
4296 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
4297 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
4298 nullptr, framebuffer_info,
4299 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
4300 [](const flutter::SurfaceFrame& surface_frame) { return true; },
4301 /*frame_size=*/SkISize::Make(800, 600), nullptr, /*display_list_fallback=*/true);
4302 XCTAssertTrue([flutterPlatformViewsController
4303 submitFrame:std::move(mock_surface)
4304 withIosContext:std::make_shared<flutter::IOSContextNoop>()]);
4305
4306 // Disposing won't remove embedded views until the view is removed from the composition_order_
4307 XCTAssertEqual(flutterPlatformViewsController.embeddedViewCount, 1UL);
4308 XCTAssertNil([flutterPlatformViewsController platformViewForId:0]);
4309 XCTAssertNotNil([flutterPlatformViewsController platformViewForId:1]);
4310 }
4311 }
4312 - (void)testOnlyPlatformViewsAreRemovedWhenReset {
4313 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
4314
4315 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
4316 /*platform=*/GetDefaultTaskRunner(),
4317 /*raster=*/GetDefaultTaskRunner(),
4318 /*ui=*/GetDefaultTaskRunner(),
4319 /*io=*/GetDefaultTaskRunner());
4320 FlutterPlatformViewsController* flutterPlatformViewsController =
4321 [[FlutterPlatformViewsController alloc] init];
4322 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
4323 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
4324 /*delegate=*/mock_delegate,
4325 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
4326 /*platform_views_controller=*/flutterPlatformViewsController,
4327 /*task_runners=*/runners,
4328 /*worker_task_runner=*/nil,
4329 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
4330
4331 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
4332 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
4333 [flutterPlatformViewsController
4334 registerViewFactory:factory
4335 withId:@"MockFlutterPlatformView"
4336 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
4337 FlutterResult result = ^(id result) {
4338 };
4339 [flutterPlatformViewsController
4340 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4341 arguments:@{
4342 @"id" : @2,
4343 @"viewType" : @"MockFlutterPlatformView"
4344 }]
4345 result:result];
4346 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
4347 flutterPlatformViewsController.flutterView = flutterView;
4348 // Create embedded view params
4349 flutter::MutatorsStack stack;
4350 // Layer tree always pushes a screen scale factor to the stack
4351 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
4352 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
4353 stack.PushTransform(screenScaleMatrix);
4354 // Push a translate matrix
4355 flutter::DlMatrix translateMatrix = flutter::DlMatrix::MakeTranslation({100, 100});
4356 stack.PushTransform(translateMatrix);
4357 flutter::DlMatrix finalMatrix = screenScaleMatrix * translateMatrix;
4358
4359 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
4360 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
4361
4362 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
4363 withParams:std::move(embeddedViewParams)];
4364
4365 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
4366 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
4367 nullptr, framebuffer_info,
4368 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
4369 [](const flutter::SurfaceFrame& surface_frame) { return true; },
4370 /*frame_size=*/SkISize::Make(800, 600), nullptr, /*display_list_fallback=*/true);
4371 [flutterPlatformViewsController submitFrame:std::move(mock_surface)
4372 withIosContext:std::make_shared<flutter::IOSContextNoop>()];
4373
4374 UIView* someView = [[UIView alloc] init];
4375 [flutterView addSubview:someView];
4376
4377 [flutterPlatformViewsController reset];
4378 XCTAssertEqual(flutterView.subviews.count, 1u);
4379 XCTAssertEqual(flutterView.subviews.firstObject, someView);
4380 }
4381
4382 - (void)testResetClearsPreviousCompositionOrder {
4383 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
4384
4385 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
4386 /*platform=*/GetDefaultTaskRunner(),
4387 /*raster=*/GetDefaultTaskRunner(),
4388 /*ui=*/GetDefaultTaskRunner(),
4389 /*io=*/GetDefaultTaskRunner());
4390 FlutterPlatformViewsController* flutterPlatformViewsController =
4391 [[FlutterPlatformViewsController alloc] init];
4392 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
4393 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
4394 /*delegate=*/mock_delegate,
4395 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
4396 /*platform_views_controller=*/flutterPlatformViewsController,
4397 /*task_runners=*/runners,
4398 /*worker_task_runner=*/nil,
4399 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
4400
4401 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
4402 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
4403 [flutterPlatformViewsController
4404 registerViewFactory:factory
4405 withId:@"MockFlutterPlatformView"
4406 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
4407 FlutterResult result = ^(id result) {
4408 };
4409 [flutterPlatformViewsController
4410 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4411 arguments:@{
4412 @"id" : @2,
4413 @"viewType" : @"MockFlutterPlatformView"
4414 }]
4415 result:result];
4416 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
4417 flutterPlatformViewsController.flutterView = flutterView;
4418 // Create embedded view params
4419 flutter::MutatorsStack stack;
4420 // Layer tree always pushes a screen scale factor to the stack
4421 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
4422 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
4423 stack.PushTransform(screenScaleMatrix);
4424 // Push a translate matrix
4425 flutter::DlMatrix translateMatrix = flutter::DlMatrix::MakeTranslation({100, 100});
4426 stack.PushTransform(translateMatrix);
4427 flutter::DlMatrix finalMatrix = screenScaleMatrix * translateMatrix;
4428
4429 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
4430 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
4431
4432 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
4433 withParams:std::move(embeddedViewParams)];
4434
4435 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
4436 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
4437 nullptr, framebuffer_info,
4438 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
4439 [](const flutter::SurfaceFrame& surface_frame) { return true; },
4440 /*frame_size=*/SkISize::Make(800, 600), nullptr, /*display_list_fallback=*/true);
4441 [flutterPlatformViewsController submitFrame:std::move(mock_surface)
4442 withIosContext:std::make_shared<flutter::IOSContextNoop>()];
4443
4444 // The above code should result in previousCompositionOrder having one viewId in it
4445 XCTAssertEqual(flutterPlatformViewsController.previousCompositionOrder.size(), 1ul);
4446
4447 // reset should clear previousCompositionOrder
4448 [flutterPlatformViewsController reset];
4449
4450 // previousCompositionOrder should now be empty
4451 XCTAssertEqual(flutterPlatformViewsController.previousCompositionOrder.size(), 0ul);
4452 }
4453
4454 - (void)testNilPlatformViewDoesntCrash {
4455 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
4456
4457 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
4458 /*platform=*/GetDefaultTaskRunner(),
4459 /*raster=*/GetDefaultTaskRunner(),
4460 /*ui=*/GetDefaultTaskRunner(),
4461 /*io=*/GetDefaultTaskRunner());
4462 FlutterPlatformViewsController* flutterPlatformViewsController =
4463 [[FlutterPlatformViewsController alloc] init];
4464 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
4465 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
4466 /*delegate=*/mock_delegate,
4467 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
4468 /*platform_views_controller=*/flutterPlatformViewsController,
4469 /*task_runners=*/runners,
4470 /*worker_task_runner=*/nil,
4471 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
4472
4473 FlutterPlatformViewsTestNilFlutterPlatformFactory* factory =
4474 [[FlutterPlatformViewsTestNilFlutterPlatformFactory alloc] init];
4475 [flutterPlatformViewsController
4476 registerViewFactory:factory
4477 withId:@"MockFlutterPlatformView"
4478 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
4479 FlutterResult result = ^(id result) {
4480 };
4481 [flutterPlatformViewsController
4482 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4483 arguments:@{
4484 @"id" : @2,
4485 @"viewType" : @"MockFlutterPlatformView"
4486 }]
4487 result:result];
4488 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
4489 flutterPlatformViewsController.flutterView = flutterView;
4490
4491 // Create embedded view params
4492 flutter::MutatorsStack stack;
4493 // Layer tree always pushes a screen scale factor to the stack
4494 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
4495 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
4496 stack.PushTransform(screenScaleMatrix);
4497 // Push a translate matrix
4498 flutter::DlMatrix translateMatrix = flutter::DlMatrix::MakeTranslation({100, 100});
4499 stack.PushTransform(translateMatrix);
4500 flutter::DlMatrix finalMatrix = screenScaleMatrix * translateMatrix;
4501
4502 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
4503 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
4504
4505 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
4506 withParams:std::move(embeddedViewParams)];
4507
4508 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
4509 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
4510 nullptr, framebuffer_info,
4511 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
4512 [](const flutter::SurfaceFrame& surface_frame) { return true; },
4513 /*frame_size=*/SkISize::Make(800, 600), nullptr, /*display_list_fallback=*/true);
4514 [flutterPlatformViewsController submitFrame:std::move(mock_surface)
4515 withIosContext:std::make_shared<flutter::IOSContextNoop>()];
4516
4517 XCTAssertEqual(flutterView.subviews.count, 1u);
4518 }
4519
4520 - (void)testFlutterTouchInterceptingViewLinksToAccessibilityContainer {
4521 FlutterTouchInterceptingView* touchInteceptorView = [[FlutterTouchInterceptingView alloc] init];
4522 NSObject* container = [[NSObject alloc] init];
4523 [touchInteceptorView setFlutterAccessibilityContainer:container];
4524 XCTAssertEqualObjects([touchInteceptorView accessibilityContainer], container);
4525 }
4526
4527 - (void)testLayerPool {
4528 // Create an IOSContext.
4529 FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar"];
4530 [engine run];
4531 XCTAssertTrue(engine.platformView != nullptr);
4532 auto ios_context = engine.platformView->GetIosContext();
4533
4534 auto pool = flutter::OverlayLayerPool{};
4535
4536 // Add layers to the pool.
4537 pool.CreateLayer(ios_context, MTLPixelFormatBGRA8Unorm);
4538 XCTAssertEqual(pool.size(), 1u);
4539 pool.CreateLayer(ios_context, MTLPixelFormatBGRA8Unorm);
4540 XCTAssertEqual(pool.size(), 2u);
4541
4542 // Mark all layers as unused.
4543 pool.RecycleLayers();
4544 XCTAssertEqual(pool.size(), 2u);
4545
4546 // Free the unused layers. One should remain.
4547 auto unused_layers = pool.RemoveUnusedLayers();
4548 XCTAssertEqual(unused_layers.size(), 2u);
4549 XCTAssertEqual(pool.size(), 1u);
4550 }
4551
4552 - (void)testFlutterPlatformViewControllerSubmitFramePreservingFrameDamage {
4553 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
4554
4555 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
4556 /*platform=*/GetDefaultTaskRunner(),
4557 /*raster=*/GetDefaultTaskRunner(),
4558 /*ui=*/GetDefaultTaskRunner(),
4559 /*io=*/GetDefaultTaskRunner());
4560 FlutterPlatformViewsController* flutterPlatformViewsController =
4561 [[FlutterPlatformViewsController alloc] init];
4562 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
4563 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
4564 /*delegate=*/mock_delegate,
4565 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
4566 /*platform_views_controller=*/flutterPlatformViewsController,
4567 /*task_runners=*/runners,
4568 /*worker_task_runner=*/nil,
4569 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
4570
4571 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
4572 flutterPlatformViewsController.flutterView = flutterView;
4573
4574 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
4575 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
4576 [flutterPlatformViewsController
4577 registerViewFactory:factory
4578 withId:@"MockFlutterPlatformView"
4579 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
4580 FlutterResult result = ^(id result) {
4581 };
4582 [flutterPlatformViewsController
4583 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4584 arguments:@{
4585 @"id" : @0,
4586 @"viewType" : @"MockFlutterPlatformView"
4587 }]
4588 result:result];
4589
4590 // This overwrites `gMockPlatformView` to another view.
4591 [flutterPlatformViewsController
4592 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4593 arguments:@{
4594 @"id" : @1,
4595 @"viewType" : @"MockFlutterPlatformView"
4596 }]
4597 result:result];
4598
4599 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
4600 flutter::MutatorsStack stack;
4601 flutter::DlMatrix finalMatrix;
4602 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
4603 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
4604 [flutterPlatformViewsController prerollCompositeEmbeddedView:0
4605 withParams:std::move(embeddedViewParams1)];
4606
4607 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
4608 flutter::ToSkMatrix(finalMatrix), SkSize::Make(500, 500), stack);
4609 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
4610 withParams:std::move(embeddedViewParams2)];
4611
4612 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
4613 std::optional<flutter::SurfaceFrame::SubmitInfo> submit_info;
4614 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
4615 nullptr, framebuffer_info,
4616 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
4617 [&](const flutter::SurfaceFrame& surface_frame) {
4618 submit_info = surface_frame.submit_info();
4619 return true;
4620 },
4621 /*frame_size=*/SkISize::Make(800, 600), nullptr,
4622 /*display_list_fallback=*/true);
4623 mock_surface->set_submit_info({
4624 .frame_damage = SkIRect::MakeWH(800, 600),
4625 .buffer_damage = SkIRect::MakeWH(400, 600),
4626 });
4627
4628 [flutterPlatformViewsController submitFrame:std::move(mock_surface)
4629 withIosContext:std::make_shared<flutter::IOSContextNoop>()];
4630
4631 XCTAssertTrue(submit_info.has_value());
4632 XCTAssertEqual(*submit_info->frame_damage, SkIRect::MakeWH(800, 600));
4633 XCTAssertEqual(*submit_info->buffer_damage, SkIRect::MakeWH(400, 600));
4634 }
4635
4636 - (void)testClipSuperellipse {
4637 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
4638
4639 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
4640 /*platform=*/GetDefaultTaskRunner(),
4641 /*raster=*/GetDefaultTaskRunner(),
4642 /*ui=*/GetDefaultTaskRunner(),
4643 /*io=*/GetDefaultTaskRunner());
4644 FlutterPlatformViewsController* flutterPlatformViewsController =
4645 [[FlutterPlatformViewsController alloc] init];
4646 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
4647 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
4648 /*delegate=*/mock_delegate,
4649 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
4650 /*platform_views_controller=*/flutterPlatformViewsController,
4651 /*task_runners=*/runners,
4652 /*worker_task_runner=*/nil,
4653 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
4654
4655 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
4656 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
4657 [flutterPlatformViewsController
4658 registerViewFactory:factory
4659 withId:@"MockFlutterPlatformView"
4660 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
4661 FlutterResult result = ^(id result) {
4662 };
4663 [flutterPlatformViewsController
4664 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4665 arguments:@{
4666 @"id" : @2,
4667 @"viewType" : @"MockFlutterPlatformView"
4668 }]
4669 result:result];
4670
4671 XCTAssertNotNil(gMockPlatformView);
4672
4673 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
4674 flutterPlatformViewsController.flutterView = flutterView;
4675 // Create embedded view params
4676 flutter::MutatorsStack stack;
4677 // Layer tree always pushes a screen scale factor to the stack
4678 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
4679 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
4680 stack.PushTransform(screenScaleMatrix);
4681 // Push a clip superellipse
4682 flutter::DlRect rect = flutter::DlRect::MakeXYWH(3, 3, 5, 5);
4683 stack.PushClipRSE(flutter::DlRoundSuperellipse::MakeOval(rect));
4684
4685 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
4686 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack);
4687
4688 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
4689 withParams:std::move(embeddedViewParams)];
4690 [flutterPlatformViewsController
4691 compositeView:2
4692 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
4693
4694 gMockPlatformView.backgroundColor = UIColor.redColor;
4695 XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
4696 ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
4697 [flutterView addSubview:childClippingView];
4698
4699 [flutterView setNeedsLayout];
4700 [flutterView layoutIfNeeded];
4701
4702 CGPoint corners[] = {CGPointMake(rect.GetLeft(), rect.GetTop()),
4703 CGPointMake(rect.GetRight(), rect.GetTop()),
4704 CGPointMake(rect.GetLeft(), rect.GetBottom()),
4705 CGPointMake(rect.GetRight(), rect.GetBottom())};
4706 for (auto point : corners) {
4707 int alpha = [self alphaOfPoint:point onView:flutterView];
4708 XCTAssertNotEqual(alpha, 255);
4709 }
4710 CGPoint center =
4711 CGPointMake(rect.GetLeft() + rect.GetWidth() / 2, rect.GetTop() + rect.GetHeight() / 2);
4712 int alpha = [self alphaOfPoint:center onView:flutterView];
4713 XCTAssertEqual(alpha, 255);
4714 }
4715
4716 @end
FlutterResult
void(^ FlutterResult)(id _Nullable result)
Definition: FlutterChannels.h:194
FlutterEngine_Test.h
settings_
flutter::Settings settings_
Definition: FlutterEnginePlatformViewTest.mm:58
platform_view
std::unique_ptr< flutter::PlatformViewIOS > platform_view
Definition: FlutterEnginePlatformViewTest.mm:68
FlutterMacros.h
FlutterPlatformViews.h
FlutterPlatformViews_Internal.h
FlutterPlatformViewsController.h
gMockPlatformView
static __weak UIView * gMockPlatformView
Definition: FlutterPlatformViewsTest.mm:29
kFloatCompareEpsilon
const float kFloatCompareEpsilon
Definition: FlutterPlatformViewsTest.mm:30
engine
id engine
Definition: FlutterTextInputPluginTest.mm:136
FlutterTouchInterceptingView_Test.h
FlutterViewController.h
ChildClippingView
Definition: FlutterPlatformViews.mm:178
-[ChildClippingView backdropFilterSubviews]
NSMutableArray * backdropFilterSubviews()
Definition: FlutterPlatformViews.mm:216
-[ChildClippingView applyBlurBackdropFilters:]
void applyBlurBackdropFilters:(NSArray< PlatformViewFilter * > *filters)
Definition: FlutterPlatformViews.mm:192
FlutterClippingMaskView
Definition: FlutterPlatformViews.mm:241
FlutterClippingMaskViewPool
Definition: FlutterPlatformViews.mm:456
-[FlutterClippingMaskViewPool getMaskViewWithFrame:]
FlutterClippingMaskView * getMaskViewWithFrame:(CGRect frame)
-[FlutterClippingMaskViewPool insertViewToPoolIfNeeded:]
void insertViewToPoolIfNeeded:(FlutterClippingMaskView *maskView)
FlutterDelayingGestureRecognizer
Definition: FlutterPlatformViews.mm:647
FlutterTouchInterceptingView
Definition: FlutterPlatformViews.mm:513
ForwardingGestureRecognizer
Definition: FlutterPlatformViews.mm:696
PlatformViewFilter
Definition: FlutterPlatformViews.mm:86
+[PlatformViewFilter resetPreparation]
void resetPreparation()
Definition: FlutterPlatformViews.mm:113
flutter::OverlayLayerPool
Storage for Overlay layers across frames.
Definition: overlay_layer_pool.h:47
flutter::OverlayLayerPool::CreateLayer
void CreateLayer(const std::shared_ptr< IOSContext > &ios_context, MTLPixelFormat pixel_format)
Create a new overlay layer.
Definition: overlay_layer_pool.mm:55
FlutterEngine
Definition: FlutterEngine.h:61
-[FlutterEngine run]
BOOL run()
Definition: FlutterEngine.mm:896
FlutterMethodCall
Definition: FlutterCodecs.h:221
+[FlutterMethodCall methodCallWithMethodName:arguments:]
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
FlutterPlatformViewsController
Definition: FlutterPlatformViewsController.h:31
-[FlutterPlatformViewsController compositionParamsForView:]
const flutter::EmbeddedViewParams & compositionParamsForView:(int64_t viewId)
-[FlutterPlatformViewsController pushVisitedPlatformViewId:]
void pushVisitedPlatformViewId:(int64_t viewId)
Pushes the view id of a visted platform view to the list of visied platform views.
Definition: FlutterPlatformViewsController.mm:952
-[FlutterPlatformViewsController reset]
void reset()
Discards all platform views instances and auxiliary resources.
Definition: FlutterPlatformViewsController.mm:646
-[FlutterPlatformViewsController registerViewFactory:withId:gestureRecognizersBlockingPolicy:]
void registerViewFactory:withId:gestureRecognizersBlockingPolicy:(NSObject< FlutterPlatformViewFactory > *factory,[withId] NSString *factoryId,[gestureRecognizersBlockingPolicy] FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy)
set the factory used to construct embedded UI Views.
Definition: FlutterPlatformViewsController.mm:397
-[FlutterPlatformViewsController previousCompositionOrder]
std::vector< int64_t > & previousCompositionOrder()
FlutterPlatformViewsController::taskRunner
const fml::RefPtr< fml::TaskRunner > & taskRunner
The task runner used to post rendering tasks to the platform thread.
Definition: FlutterPlatformViewsController.h:35
FlutterPlatformViewsController::flutterViewController
UIViewController< FlutterViewResponder > *_Nullable flutterViewController
The flutter view controller.
Definition: FlutterPlatformViewsController.h:41
-[FlutterPlatformViewsController compositeView:withParams:]
void compositeView:withParams:(int64_t viewId,[withParams] const flutter::EmbeddedViewParams &params)
-[FlutterPlatformViewsController embeddedViewCount]
size_t embeddedViewCount()
FlutterPlatformViewsController::flutterView
UIView *_Nullable flutterView
The flutter view.
Definition: FlutterPlatformViewsController.h:38
-[FlutterPlatformViewsController onMethodCall:result:]
void onMethodCall:result:(FlutterMethodCall *call,[result] FlutterResult result)
Handler for platform view message channels.
Definition: FlutterPlatformViewsController.mm:270
FlutterPlatformViewsTest
Definition: FlutterPlatformViewsTest.mm:287
FlutterPlatformViewsTestMockFlutterPlatformFactory
Definition: FlutterPlatformViewsTest.mm:83
FlutterPlatformViewsTestMockFlutterPlatformView
Definition: FlutterPlatformViewsTest.mm:50
FlutterPlatformViewsTestMockFlutterPlatformView::view
UIView * view
Definition: FlutterPlatformViewsTest.mm:51
FlutterPlatformViewsTestMockFlutterPlatformView::viewCreated
BOOL viewCreated
Definition: FlutterPlatformViewsTest.mm:52
FlutterPlatformViewsTestMockNestedWrapperWebViewFactory
Definition: FlutterPlatformViewsTest.mm:226
FlutterPlatformViewsTestMockNestedWrapperWebView
Definition: FlutterPlatformViewsTest.mm:194
FlutterPlatformViewsTestMockNestedWrapperWebView::view
UIView * view
Definition: FlutterPlatformViewsTest.mm:195
FlutterPlatformViewsTestMockNestedWrapperWebView::viewCreated
BOOL viewCreated
Definition: FlutterPlatformViewsTest.mm:196
FlutterPlatformViewsTestMockPlatformView
Definition: FlutterPlatformViewsTest.mm:33
FlutterPlatformViewsTestMockWebViewFactory
Definition: FlutterPlatformViewsTest.mm:127
FlutterPlatformViewsTestMockWebView
Definition: FlutterPlatformViewsTest.mm:95
FlutterPlatformViewsTestMockWebView::viewCreated
BOOL viewCreated
Definition: FlutterPlatformViewsTest.mm:97
FlutterPlatformViewsTestMockWebView::view
UIView * view
Definition: FlutterPlatformViewsTest.mm:96
FlutterPlatformViewsTestMockWrapperWebViewFactory
Definition: FlutterPlatformViewsTest.mm:183
FlutterPlatformViewsTestMockWrapperWebView
Definition: FlutterPlatformViewsTest.mm:150
FlutterPlatformViewsTestMockWrapperWebView::view
UIView * view
Definition: FlutterPlatformViewsTest.mm:151
FlutterPlatformViewsTestMockWrapperWebView::viewCreated
BOOL viewCreated
Definition: FlutterPlatformViewsTest.mm:152
FlutterPlatformViewsTestNilFlutterPlatformFactory
Definition: FlutterPlatformViewsTest.mm:138
FlutterViewController
Definition: FlutterViewController.h:57
ios_context_noop.h
FLUTTER_ASSERT_ARC
Definition: FlutterChannelKeyResponder.mm:13
flutter
Definition: accessibility_bridge.h:26
flutter::IOSRenderingAPI::kMetal
@ kMetal
platform_view_ios.h
FlutterPlatformView-p
Definition: FlutterPlatformViews.h:18
FlutterPlatformViewFactory-p
Definition: FlutterPlatformViews.h:26
texture_id
int64_t texture_id
Definition: texture_registrar_unittests.cc:24