| 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 if (@available(iOS 18.2, *)) {
3201 // Since we remove and add back delayingRecognizer, it would be reordered to the last.
3202 XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], forwardingRecognizer);
3203 XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], delayingRecognizer);
3204 } else {
3205 XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], delayingRecognizer);
3206 XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], forwardingRecognizer);
3207 }
3208 }
3209
3210 - (void)
3211 testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldRemoveAndAddBackDelayingRecognizerForWrapperWebView {
3212 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3213
3214 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3215 /*platform=*/GetDefaultTaskRunner(),
3216 /*raster=*/GetDefaultTaskRunner(),
3217 /*ui=*/GetDefaultTaskRunner(),
3218 /*io=*/GetDefaultTaskRunner());
3219 FlutterPlatformViewsController* flutterPlatformViewsController =
3220 [[FlutterPlatformViewsController alloc] init];
3221 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3222 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3223 /*delegate=*/mock_delegate,
3224 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3225 /*platform_views_controller=*/flutterPlatformViewsController,
3226 /*task_runners=*/runners,
3227 /*worker_task_runner=*/nil,
3228 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3229
3230 FlutterPlatformViewsTestMockWrapperWebViewFactory* factory =
3231 [[FlutterPlatformViewsTestMockWrapperWebViewFactory alloc] init];
3232 [flutterPlatformViewsController
3233 registerViewFactory:factory
3234 withId:@"MockWrapperWebView"
3235 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3236 FlutterResult result = ^(id result) {
3237 };
3238 [flutterPlatformViewsController
3239 onMethodCall:[FlutterMethodCall
3240 methodCallWithMethodName:@"create"
3241 arguments:@{@"id" : @2, @"viewType" : @"MockWrapperWebView"}]
3242 result:result];
3243
3244 XCTAssertNotNil(gMockPlatformView);
3245
3246 // Find touch inteceptor view
3247 UIView* touchInteceptorView = gMockPlatformView;
3248 while (touchInteceptorView != nil &&
3249 ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
3250 touchInteceptorView = touchInteceptorView.superview;
3251 }
3252 XCTAssertNotNil(touchInteceptorView);
3253
3254 XCTAssert(touchInteceptorView.gestureRecognizers.count == 2);
3255 UIGestureRecognizer* delayingRecognizer = touchInteceptorView.gestureRecognizers[0];
3256 UIGestureRecognizer* forwardingRecognizer = touchInteceptorView.gestureRecognizers[1];
3257
3258 XCTAssert([delayingRecognizer isKindOfClass:[FlutterDelayingGestureRecognizer class]]);
3259 XCTAssert([forwardingRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]);
3260
3261 [(FlutterTouchInterceptingView*)touchInteceptorView blockGesture];
3262
3263 if (@available(iOS 18.2, *)) {
3264 // Since we remove and add back delayingRecognizer, it would be reordered to the last.
3265 XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], forwardingRecognizer);
3266 XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], delayingRecognizer);
3267 } else {
3268 XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], delayingRecognizer);
3269 XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], forwardingRecognizer);
3270 }
3271 }
3272
3273 - (void)
3274 testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldNotRemoveAndAddBackDelayingRecognizerForNestedWrapperWebView {
3275 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3276
3277 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3278 /*platform=*/GetDefaultTaskRunner(),
3279 /*raster=*/GetDefaultTaskRunner(),
3280 /*ui=*/GetDefaultTaskRunner(),
3281 /*io=*/GetDefaultTaskRunner());
3282 FlutterPlatformViewsController* flutterPlatformViewsController =
3283 [[FlutterPlatformViewsController alloc] init];
3284 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3285 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3286 /*delegate=*/mock_delegate,
3287 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3288 /*platform_views_controller=*/flutterPlatformViewsController,
3289 /*task_runners=*/runners,
3290 /*worker_task_runner=*/nil,
3291 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3292
3293 FlutterPlatformViewsTestMockNestedWrapperWebViewFactory* factory =
3294 [[FlutterPlatformViewsTestMockNestedWrapperWebViewFactory alloc] init];
3295 [flutterPlatformViewsController
3296 registerViewFactory:factory
3297 withId:@"MockNestedWrapperWebView"
3298 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3299 FlutterResult result = ^(id result) {
3300 };
3301 [flutterPlatformViewsController
3302 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3303 arguments:@{
3304 @"id" : @2,
3305 @"viewType" : @"MockNestedWrapperWebView"
3306 }]
3307 result:result];
3308
3309 XCTAssertNotNil(gMockPlatformView);
3310
3311 // Find touch inteceptor view
3312 UIView* touchInteceptorView = gMockPlatformView;
3313 while (touchInteceptorView != nil &&
3314 ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
3315 touchInteceptorView = touchInteceptorView.superview;
3316 }
3317 XCTAssertNotNil(touchInteceptorView);
3318
3319 XCTAssert(touchInteceptorView.gestureRecognizers.count == 2);
3320 UIGestureRecognizer* delayingRecognizer = touchInteceptorView.gestureRecognizers[0];
3321 UIGestureRecognizer* forwardingRecognizer = touchInteceptorView.gestureRecognizers[1];
3322
3323 XCTAssert([delayingRecognizer isKindOfClass:[FlutterDelayingGestureRecognizer class]]);
3324 XCTAssert([forwardingRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]);
3325
3326 [(FlutterTouchInterceptingView*)touchInteceptorView blockGesture];
3327
3328 XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], delayingRecognizer);
3329 XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], forwardingRecognizer);
3330 }
3331
3332 - (void)
3333 testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldNotRemoveAndAddBackDelayingRecognizerForNonWebView {
3334 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3335
3336 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3337 /*platform=*/GetDefaultTaskRunner(),
3338 /*raster=*/GetDefaultTaskRunner(),
3339 /*ui=*/GetDefaultTaskRunner(),
3340 /*io=*/GetDefaultTaskRunner());
3341 FlutterPlatformViewsController* flutterPlatformViewsController =
3342 [[FlutterPlatformViewsController alloc] init];
3343 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3344 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3345 /*delegate=*/mock_delegate,
3346 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3347 /*platform_views_controller=*/flutterPlatformViewsController,
3348 /*task_runners=*/runners,
3349 /*worker_task_runner=*/nil,
3350 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3351
3352 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3353 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3354 [flutterPlatformViewsController
3355 registerViewFactory:factory
3356 withId:@"MockFlutterPlatformView"
3357 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3358 FlutterResult result = ^(id result) {
3359 };
3360 [flutterPlatformViewsController
3361 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3362 arguments:@{
3363 @"id" : @2,
3364 @"viewType" : @"MockFlutterPlatformView"
3365 }]
3366 result:result];
3367
3368 XCTAssertNotNil(gMockPlatformView);
3369
3370 // Find touch inteceptor view
3371 UIView* touchInteceptorView = gMockPlatformView;
3372 while (touchInteceptorView != nil &&
3373 ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
3374 touchInteceptorView = touchInteceptorView.superview;
3375 }
3376 XCTAssertNotNil(touchInteceptorView);
3377
3378 XCTAssert(touchInteceptorView.gestureRecognizers.count == 2);
3379 UIGestureRecognizer* delayingRecognizer = touchInteceptorView.gestureRecognizers[0];
3380 UIGestureRecognizer* forwardingRecognizer = touchInteceptorView.gestureRecognizers[1];
3381
3382 XCTAssert([delayingRecognizer isKindOfClass:[FlutterDelayingGestureRecognizer class]]);
3383 XCTAssert([forwardingRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]);
3384
3385 [(FlutterTouchInterceptingView*)touchInteceptorView blockGesture];
3386
3387 XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], delayingRecognizer);
3388 XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], forwardingRecognizer);
3389 }
3390
3391 - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashing {
3392 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3393
3394 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3395 /*platform=*/GetDefaultTaskRunner(),
3396 /*raster=*/GetDefaultTaskRunner(),
3397 /*ui=*/GetDefaultTaskRunner(),
3398 /*io=*/GetDefaultTaskRunner());
3399 FlutterPlatformViewsController* flutterPlatformViewsController =
3400 [[FlutterPlatformViewsController alloc] init];
3401 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3402 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3403 /*delegate=*/mock_delegate,
3404 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3405 /*platform_views_controller=*/flutterPlatformViewsController,
3406 /*task_runners=*/runners,
3407 /*worker_task_runner=*/nil,
3408 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3409
3410 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3411 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3412 [flutterPlatformViewsController
3413 registerViewFactory:factory
3414 withId:@"MockFlutterPlatformView"
3415 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3416 FlutterResult result = ^(id result) {
3417 };
3418 [flutterPlatformViewsController
3419 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3420 arguments:@{
3421 @"id" : @2,
3422 @"viewType" : @"MockFlutterPlatformView"
3423 }]
3424 result:result];
3425
3426 XCTAssertNotNil(gMockPlatformView);
3427
3428 // Create embedded view params
3429 flutter::MutatorsStack stack;
3430 flutter::DlMatrix finalMatrix;
3431
3432 auto embeddedViewParams_1 = std::make_unique<flutter::EmbeddedViewParams>(
3433 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3434
3435 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
3436 withParams:std::move(embeddedViewParams_1)];
3437 [flutterPlatformViewsController
3438 compositeView:2
3439 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
3440
3441 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3442 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3443 nullptr, framebuffer_info,
3444 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return false; },
3445 [](const flutter::SurfaceFrame& surface_frame) { return true; },
3446 /*frame_size=*/SkISize::Make(800, 600));
3447 XCTAssertFalse([flutterPlatformViewsController
3448 submitFrame:std::move(mock_surface)
3449 withIosContext:std::make_shared<flutter::IOSContextNoop>()]);
3450
3451 auto embeddedViewParams_2 = std::make_unique<flutter::EmbeddedViewParams>(
3452 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3453 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
3454 withParams:std::move(embeddedViewParams_2)];
3455 [flutterPlatformViewsController
3456 compositeView:2
3457 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
3458
3459 auto mock_surface_submit_true = std::make_unique<flutter::SurfaceFrame>(
3460 nullptr, framebuffer_info,
3461 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3462 [](const flutter::SurfaceFrame& surface_frame) { return true; },
3463 /*frame_size=*/SkISize::Make(800, 600));
3464 XCTAssertTrue([flutterPlatformViewsController
3465 submitFrame:std::move(mock_surface_submit_true)
3466 withIosContext:std::make_shared<flutter::IOSContextNoop>()]);
3467 }
3468
3469 - (void)
3470 testFlutterPlatformViewControllerResetDeallocsPlatformViewWhenRootViewsNotBindedToFlutterView {
3471 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3472
3473 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3474 /*platform=*/GetDefaultTaskRunner(),
3475 /*raster=*/GetDefaultTaskRunner(),
3476 /*ui=*/GetDefaultTaskRunner(),
3477 /*io=*/GetDefaultTaskRunner());
3478 FlutterPlatformViewsController* flutterPlatformViewsController =
3479 [[FlutterPlatformViewsController alloc] init];
3480 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3481 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3482 /*delegate=*/mock_delegate,
3483 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3484 /*platform_views_controller=*/flutterPlatformViewsController,
3485 /*task_runners=*/runners,
3486 /*worker_task_runner=*/nil,
3487 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3488
3489 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3490 flutterPlatformViewsController.flutterView = flutterView;
3491
3492 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3493 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3494 [flutterPlatformViewsController
3495 registerViewFactory:factory
3496 withId:@"MockFlutterPlatformView"
3497 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3498 FlutterResult result = ^(id result) {
3499 };
3500 // autorelease pool to trigger an autorelease for all the root_views_ and touch_interceptors_.
3501 @autoreleasepool {
3502 [flutterPlatformViewsController
3503 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3504 arguments:@{
3505 @"id" : @2,
3506 @"viewType" : @"MockFlutterPlatformView"
3507 }]
3508 result:result];
3509
3510 flutter::MutatorsStack stack;
3511 flutter::DlMatrix finalMatrix;
3512 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
3513 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3514 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
3515 withParams:std::move(embeddedViewParams)];
3516
3517 // Not calling |[flutterPlatformViewsController submitFrame:withIosContext:]| so that
3518 // the platform views are not added to flutter_view_.
3519
3520 XCTAssertNotNil(gMockPlatformView);
3521 [flutterPlatformViewsController reset];
3522 }
3523 XCTAssertNil(gMockPlatformView);
3524 }
3525
3526 - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder {
3527 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3528
3529 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3530 /*platform=*/GetDefaultTaskRunner(),
3531 /*raster=*/GetDefaultTaskRunner(),
3532 /*ui=*/GetDefaultTaskRunner(),
3533 /*io=*/GetDefaultTaskRunner());
3534 FlutterPlatformViewsController* flutterPlatformViewsController =
3535 [[FlutterPlatformViewsController alloc] init];
3536 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3537 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3538 /*delegate=*/mock_delegate,
3539 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3540 /*platform_views_controller=*/flutterPlatformViewsController,
3541 /*task_runners=*/runners,
3542 /*worker_task_runner=*/nil,
3543 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3544
3545 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3546 flutterPlatformViewsController.flutterView = flutterView;
3547
3548 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3549 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3550 [flutterPlatformViewsController
3551 registerViewFactory:factory
3552 withId:@"MockFlutterPlatformView"
3553 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3554 FlutterResult result = ^(id result) {
3555 };
3556
3557 [flutterPlatformViewsController
3558 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3559 arguments:@{
3560 @"id" : @0,
3561 @"viewType" : @"MockFlutterPlatformView"
3562 }]
3563 result:result];
3564
3565 // First frame, |embeddedViewCount| is not empty after composite.
3566 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
3567 flutter::MutatorsStack stack;
3568 flutter::DlMatrix finalMatrix;
3569 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
3570 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3571 [flutterPlatformViewsController prerollCompositeEmbeddedView:0
3572 withParams:std::move(embeddedViewParams1)];
3573 [flutterPlatformViewsController
3574 compositeView:0
3575 withParams:[flutterPlatformViewsController compositionParamsForView:0]];
3576
3577 XCTAssertEqual(flutterPlatformViewsController.embeddedViewCount, 1UL);
3578
3579 // Second frame, |embeddedViewCount| should be empty at the start
3580 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
3581 XCTAssertEqual(flutterPlatformViewsController.embeddedViewCount, 0UL);
3582
3583 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
3584 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3585 [flutterPlatformViewsController prerollCompositeEmbeddedView:0
3586 withParams:std::move(embeddedViewParams2)];
3587 [flutterPlatformViewsController
3588 compositeView:0
3589 withParams:[flutterPlatformViewsController compositionParamsForView:0]];
3590
3591 XCTAssertEqual(flutterPlatformViewsController.embeddedViewCount, 1UL);
3592 }
3593
3594 - (void)
3595 testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithDifferentViewHierarchy {
3596 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3597
3598 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3599 /*platform=*/GetDefaultTaskRunner(),
3600 /*raster=*/GetDefaultTaskRunner(),
3601 /*ui=*/GetDefaultTaskRunner(),
3602 /*io=*/GetDefaultTaskRunner());
3603 FlutterPlatformViewsController* flutterPlatformViewsController =
3604 [[FlutterPlatformViewsController alloc] init];
3605 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3606 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3607 /*delegate=*/mock_delegate,
3608 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3609 /*platform_views_controller=*/flutterPlatformViewsController,
3610 /*task_runners=*/runners,
3611 /*worker_task_runner=*/nil,
3612 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3613
3614 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3615 flutterPlatformViewsController.flutterView = flutterView;
3616
3617 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3618 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3619 [flutterPlatformViewsController
3620 registerViewFactory:factory
3621 withId:@"MockFlutterPlatformView"
3622 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3623 FlutterResult result = ^(id result) {
3624 };
3625 [flutterPlatformViewsController
3626 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3627 arguments:@{
3628 @"id" : @0,
3629 @"viewType" : @"MockFlutterPlatformView"
3630 }]
3631 result:result];
3632 UIView* view1 = gMockPlatformView;
3633
3634 // This overwrites `gMockPlatformView` to another view.
3635 [flutterPlatformViewsController
3636 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3637 arguments:@{
3638 @"id" : @1,
3639 @"viewType" : @"MockFlutterPlatformView"
3640 }]
3641 result:result];
3642 UIView* view2 = gMockPlatformView;
3643
3644 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
3645 flutter::MutatorsStack stack;
3646 flutter::DlMatrix finalMatrix;
3647 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
3648 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3649 [flutterPlatformViewsController prerollCompositeEmbeddedView:0
3650 withParams:std::move(embeddedViewParams1)];
3651
3652 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
3653 flutter::ToSkMatrix(finalMatrix), SkSize::Make(500, 500), stack);
3654 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
3655 withParams:std::move(embeddedViewParams2)];
3656
3657 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3658 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3659 nullptr, framebuffer_info,
3660 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3661 [](const flutter::SurfaceFrame& surface_frame) { return true; },
3662 /*frame_size=*/SkISize::Make(800, 600), nullptr, /*display_list_fallback=*/true);
3663 XCTAssertTrue([flutterPlatformViewsController
3664 submitFrame:std::move(mock_surface)
3665 withIosContext:std::make_shared<flutter::IOSContextNoop>()]);
3666
3667 // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view.
3668 UIView* clippingView1 = view1.superview.superview;
3669 UIView* clippingView2 = view2.superview.superview;
3670 XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
3671 [flutterView.subviews indexOfObject:clippingView2],
3672 @"The first clipping view should be added before the second clipping view.");
3673
3674 // Need to recreate these params since they are `std::move`ed.
3675 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
3676 // Process the second frame in the opposite order.
3677 embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
3678 flutter::ToSkMatrix(finalMatrix), SkSize::Make(500, 500), stack);
3679 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
3680 withParams:std::move(embeddedViewParams2)];
3681
3682 embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
3683 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3684 [flutterPlatformViewsController prerollCompositeEmbeddedView:0
3685 withParams:std::move(embeddedViewParams1)];
3686
3687 mock_surface = std::make_unique<flutter::SurfaceFrame>(
3688 nullptr, framebuffer_info,
3689 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3690 [](const flutter::SurfaceFrame& surface_frame) { return true; },
3691 /*frame_size=*/SkISize::Make(800, 600), nullptr, /*display_list_fallback=*/true);
3692 XCTAssertTrue([flutterPlatformViewsController
3693 submitFrame:std::move(mock_surface)
3694 withIosContext:std::make_shared<flutter::IOSContextNoop>()]);
3695
3696 XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] >
3697 [flutterView.subviews indexOfObject:clippingView2],
3698 @"The first clipping view should be added after the second clipping view.");
3699 }
3700
3701 - (void)
3702 testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithSameViewHierarchy {
3703 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3704
3705 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3706 /*platform=*/GetDefaultTaskRunner(),
3707 /*raster=*/GetDefaultTaskRunner(),
3708 /*ui=*/GetDefaultTaskRunner(),
3709 /*io=*/GetDefaultTaskRunner());
3710 FlutterPlatformViewsController* flutterPlatformViewsController =
3711 [[FlutterPlatformViewsController alloc] init];
3712 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3713 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3714 /*delegate=*/mock_delegate,
3715 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3716 /*platform_views_controller=*/flutterPlatformViewsController,
3717 /*task_runners=*/runners,
3718 /*worker_task_runner=*/nil,
3719 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3720
3721 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3722 flutterPlatformViewsController.flutterView = flutterView;
3723
3724 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3725 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3726 [flutterPlatformViewsController
3727 registerViewFactory:factory
3728 withId:@"MockFlutterPlatformView"
3729 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3730 FlutterResult result = ^(id result) {
3731 };
3732 [flutterPlatformViewsController
3733 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3734 arguments:@{
3735 @"id" : @0,
3736 @"viewType" : @"MockFlutterPlatformView"
3737 }]
3738 result:result];
3739 UIView* view1 = gMockPlatformView;
3740
3741 // This overwrites `gMockPlatformView` to another view.
3742 [flutterPlatformViewsController
3743 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3744 arguments:@{
3745 @"id" : @1,
3746 @"viewType" : @"MockFlutterPlatformView"
3747 }]
3748 result:result];
3749 UIView* view2 = gMockPlatformView;
3750
3751 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
3752 flutter::MutatorsStack stack;
3753 flutter::DlMatrix finalMatrix;
3754 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
3755 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3756 [flutterPlatformViewsController prerollCompositeEmbeddedView:0
3757 withParams:std::move(embeddedViewParams1)];
3758
3759 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
3760 flutter::ToSkMatrix(finalMatrix), SkSize::Make(500, 500), stack);
3761 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
3762 withParams:std::move(embeddedViewParams2)];
3763
3764 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3765 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3766 nullptr, framebuffer_info,
3767 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3768 [](const flutter::SurfaceFrame& surface_frame) { return true; },
3769 /*frame_size=*/SkISize::Make(800, 600), nullptr, /*display_list_fallback=*/true);
3770 XCTAssertTrue([flutterPlatformViewsController
3771 submitFrame:std::move(mock_surface)
3772 withIosContext:std::make_shared<flutter::IOSContextNoop>()]);
3773
3774 // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view.
3775 UIView* clippingView1 = view1.superview.superview;
3776 UIView* clippingView2 = view2.superview.superview;
3777 XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
3778 [flutterView.subviews indexOfObject:clippingView2],
3779 @"The first clipping view should be added before the second clipping view.");
3780
3781 // Need to recreate these params since they are `std::move`ed.
3782 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
3783 // Process the second frame in the same order.
3784 embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
3785 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
3786 [flutterPlatformViewsController prerollCompositeEmbeddedView:0
3787 withParams:std::move(embeddedViewParams1)];
3788
3789 embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
3790 flutter::ToSkMatrix(finalMatrix), SkSize::Make(500, 500), stack);
3791 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
3792 withParams:std::move(embeddedViewParams2)];
3793
3794 mock_surface = std::make_unique<flutter::SurfaceFrame>(
3795 nullptr, framebuffer_info,
3796 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3797 [](const flutter::SurfaceFrame& surface_frame) { return true; },
3798 /*frame_size=*/SkISize::Make(800, 600), nullptr, /*display_list_fallback=*/true);
3799 XCTAssertTrue([flutterPlatformViewsController
3800 submitFrame:std::move(mock_surface)
3801 withIosContext:std::make_shared<flutter::IOSContextNoop>()]);
3802
3803 XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
3804 [flutterView.subviews indexOfObject:clippingView2],
3805 @"The first clipping view should be added before the second clipping view.");
3806 }
3807
3808 - (int)alphaOfPoint:(CGPoint)point onView:(UIView*)view {
3809 unsigned char pixel[4] = {0};
3810
3811 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
3812
3813 // Draw the pixel on `point` in the context.
3814 CGContextRef context = CGBitmapContextCreate(
3815 pixel, 1, 1, 8, 4, colorSpace, kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedLast);
3816 CGContextTranslateCTM(context, -point.x, -point.y);
3817 [view.layer renderInContext:context];
3818
3819 CGContextRelease(context);
3820 CGColorSpaceRelease(colorSpace);
3821 // Get the alpha from the pixel that we just rendered.
3822 return pixel[3];
3823 }
3824
3825 - (void)testHasFirstResponderInViewHierarchySubtree_viewItselfBecomesFirstResponder {
3826 // For view to become the first responder, it must be a descendant of a UIWindow
3827 UIWindow* window = [[UIWindow alloc] init];
3828 UITextField* textField = [[UITextField alloc] init];
3829 [window addSubview:textField];
3830
3831 [textField becomeFirstResponder];
3832 XCTAssertTrue(textField.isFirstResponder);
3833 XCTAssertTrue(textField.flt_hasFirstResponderInViewHierarchySubtree);
3834 [textField resignFirstResponder];
3835 XCTAssertFalse(textField.isFirstResponder);
3836 XCTAssertFalse(textField.flt_hasFirstResponderInViewHierarchySubtree);
3837 }
3838
3839 - (void)testHasFirstResponderInViewHierarchySubtree_descendantViewBecomesFirstResponder {
3840 // For view to become the first responder, it must be a descendant of a UIWindow
3841 UIWindow* window = [[UIWindow alloc] init];
3842 UIView* view = [[UIView alloc] init];
3843 UIView* childView = [[UIView alloc] init];
3844 UITextField* textField = [[UITextField alloc] init];
3845 [window addSubview:view];
3846 [view addSubview:childView];
3847 [childView addSubview:textField];
3848
3849 [textField becomeFirstResponder];
3850 XCTAssertTrue(textField.isFirstResponder);
3851 XCTAssertTrue(view.flt_hasFirstResponderInViewHierarchySubtree);
3852 [textField resignFirstResponder];
3853 XCTAssertFalse(textField.isFirstResponder);
3854 XCTAssertFalse(view.flt_hasFirstResponderInViewHierarchySubtree);
3855 }
3856
3857 - (void)testFlutterClippingMaskViewPoolReuseViewsAfterRecycle {
3858 FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2];
3859 FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero];
3860 FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero];
3861 [pool insertViewToPoolIfNeeded:view1];
3862 [pool insertViewToPoolIfNeeded:view2];
3863 CGRect newRect = CGRectMake(0, 0, 10, 10);
3864 FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:newRect];
3865 FlutterClippingMaskView* view4 = [pool getMaskViewWithFrame:newRect];
3866 // view3 and view4 should randomly get either of view1 and view2.
3867 NSSet* set1 = [NSSet setWithObjects:view1, view2, nil];
3868 NSSet* set2 = [NSSet setWithObjects:view3, view4, nil];
3869 XCTAssertEqualObjects(set1, set2);
3870 XCTAssertTrue(CGRectEqualToRect(view3.frame, newRect));
3871 XCTAssertTrue(CGRectEqualToRect(view4.frame, newRect));
3872 }
3873
3874 - (void)testFlutterClippingMaskViewPoolAllocsNewMaskViewsAfterReachingCapacity {
3875 FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2];
3876 FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero];
3877 FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero];
3878 FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:CGRectZero];
3879 XCTAssertNotEqual(view1, view3);
3880 XCTAssertNotEqual(view2, view3);
3881 }
3882
3883 - (void)testMaskViewsReleasedWhenPoolIsReleased {
3884 __weak UIView* weakView;
3885 @autoreleasepool {
3886 FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2];
3887 FlutterClippingMaskView* view = [pool getMaskViewWithFrame:CGRectZero];
3888 weakView = view;
3889 XCTAssertNotNil(weakView);
3890 }
3891 XCTAssertNil(weakView);
3892 }
3893
3894 - (void)testClipMaskViewIsReused {
3895 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3896
3897 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3898 /*platform=*/GetDefaultTaskRunner(),
3899 /*raster=*/GetDefaultTaskRunner(),
3900 /*ui=*/GetDefaultTaskRunner(),
3901 /*io=*/GetDefaultTaskRunner());
3902 FlutterPlatformViewsController* flutterPlatformViewsController =
3903 [[FlutterPlatformViewsController alloc] init];
3904 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
3905 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3906 /*delegate=*/mock_delegate,
3907 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
3908 /*platform_views_controller=*/flutterPlatformViewsController,
3909 /*task_runners=*/runners,
3910 /*worker_task_runner=*/nil,
3911 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3912
3913 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3914 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3915 [flutterPlatformViewsController
3916 registerViewFactory:factory
3917 withId:@"MockFlutterPlatformView"
3918 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
3919 FlutterResult result = ^(id result) {
3920 };
3921 [flutterPlatformViewsController
3922 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3923 arguments:@{
3924 @"id" : @1,
3925 @"viewType" : @"MockFlutterPlatformView"
3926 }]
3927 result:result];
3928
3929 XCTAssertNotNil(gMockPlatformView);
3930 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
3931 flutterPlatformViewsController.flutterView = flutterView;
3932 // Create embedded view params
3933 flutter::MutatorsStack stack1;
3934 // Layer tree always pushes a screen scale factor to the stack
3935 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
3936 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
3937 stack1.PushTransform(screenScaleMatrix);
3938 // Push a clip rect
3939 flutter::DlRect rect = flutter::DlRect::MakeXYWH(2, 2, 3, 3);
3940 stack1.PushClipRect(rect);
3941
3942 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
3943 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack1);
3944
3945 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
3946 withParams:std::move(embeddedViewParams1)];
3947 [flutterPlatformViewsController
3948 compositeView:1
3949 withParams:[flutterPlatformViewsController compositionParamsForView:1]];
3950
3951 UIView* childClippingView1 = gMockPlatformView.superview.superview;
3952 UIView* maskView1 = childClippingView1.maskView;
3953 XCTAssertNotNil(maskView1);
3954
3955 // Composite a new frame.
3956 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(100, 100)];
3957 flutter::MutatorsStack stack2;
3958 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
3959 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
3960 auto embeddedViewParams3 = std::make_unique<flutter::EmbeddedViewParams>(
3961 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
3962 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
3963 withParams:std::move(embeddedViewParams3)];
3964 [flutterPlatformViewsController
3965 compositeView:1
3966 withParams:[flutterPlatformViewsController compositionParamsForView:1]];
3967
3968 childClippingView1 = gMockPlatformView.superview.superview;
3969
3970 // This overrides gMockPlatformView to point to the newly created platform view.
3971 [flutterPlatformViewsController
3972 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
3973 arguments:@{
3974 @"id" : @2,
3975 @"viewType" : @"MockFlutterPlatformView"
3976 }]
3977 result:result];
3978
3979 auto embeddedViewParams4 = std::make_unique<flutter::EmbeddedViewParams>(
3980 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack1);
3981 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
3982 withParams:std::move(embeddedViewParams4)];
3983 [flutterPlatformViewsController
3984 compositeView:2
3985 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
3986
3987 UIView* childClippingView2 = gMockPlatformView.superview.superview;
3988
3989 UIView* maskView2 = childClippingView2.maskView;
3990 XCTAssertEqual(maskView1, maskView2);
3991 XCTAssertNotNil(childClippingView2.maskView);
3992 XCTAssertNil(childClippingView1.maskView);
3993 }
3994
3995 - (void)testDifferentClipMaskViewIsUsedForEachView {
3996 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3997
3998 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3999 /*platform=*/GetDefaultTaskRunner(),
4000 /*raster=*/GetDefaultTaskRunner(),
4001 /*ui=*/GetDefaultTaskRunner(),
4002 /*io=*/GetDefaultTaskRunner());
4003 FlutterPlatformViewsController* flutterPlatformViewsController =
4004 [[FlutterPlatformViewsController alloc] init];
4005 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
4006 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
4007 /*delegate=*/mock_delegate,
4008 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
4009 /*platform_views_controller=*/flutterPlatformViewsController,
4010 /*task_runners=*/runners,
4011 /*worker_task_runner=*/nil,
4012 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
4013
4014 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
4015 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
4016 [flutterPlatformViewsController
4017 registerViewFactory:factory
4018 withId:@"MockFlutterPlatformView"
4019 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
4020 FlutterResult result = ^(id result) {
4021 };
4022
4023 [flutterPlatformViewsController
4024 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4025 arguments:@{
4026 @"id" : @1,
4027 @"viewType" : @"MockFlutterPlatformView"
4028 }]
4029 result:result];
4030 UIView* view1 = gMockPlatformView;
4031
4032 // This overwrites `gMockPlatformView` to another view.
4033 [flutterPlatformViewsController
4034 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4035 arguments:@{
4036 @"id" : @2,
4037 @"viewType" : @"MockFlutterPlatformView"
4038 }]
4039 result:result];
4040 UIView* view2 = gMockPlatformView;
4041
4042 XCTAssertNotNil(gMockPlatformView);
4043 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
4044 flutterPlatformViewsController.flutterView = flutterView;
4045 // Create embedded view params
4046 flutter::MutatorsStack stack1;
4047 // Layer tree always pushes a screen scale factor to the stack
4048 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
4049 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
4050 stack1.PushTransform(screenScaleMatrix);
4051 // Push a clip rect
4052 flutter::DlRect rect = flutter::DlRect::MakeXYWH(2, 2, 3, 3);
4053 stack1.PushClipRect(rect);
4054
4055 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
4056 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack1);
4057
4058 flutter::MutatorsStack stack2;
4059 stack2.PushClipRect(rect);
4060 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
4061 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
4062
4063 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
4064 withParams:std::move(embeddedViewParams1)];
4065 [flutterPlatformViewsController
4066 compositeView:1
4067 withParams:[flutterPlatformViewsController compositionParamsForView:1]];
4068
4069 UIView* childClippingView1 = view1.superview.superview;
4070
4071 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
4072 withParams:std::move(embeddedViewParams2)];
4073 [flutterPlatformViewsController
4074 compositeView:2
4075 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
4076
4077 UIView* childClippingView2 = view2.superview.superview;
4078 UIView* maskView1 = childClippingView1.maskView;
4079 UIView* maskView2 = childClippingView2.maskView;
4080 XCTAssertNotEqual(maskView1, maskView2);
4081 }
4082
4083 - (void)testMaskViewUsesCAShapeLayerAsTheBackingLayer {
4084 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
4085
4086 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
4087 /*platform=*/GetDefaultTaskRunner(),
4088 /*raster=*/GetDefaultTaskRunner(),
4089 /*ui=*/GetDefaultTaskRunner(),
4090 /*io=*/GetDefaultTaskRunner());
4091 FlutterPlatformViewsController* flutterPlatformViewsController =
4092 [[FlutterPlatformViewsController alloc] init];
4093 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
4094 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
4095 /*delegate=*/mock_delegate,
4096 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
4097 /*platform_views_controller=*/flutterPlatformViewsController,
4098 /*task_runners=*/runners,
4099 /*worker_task_runner=*/nil,
4100 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
4101
4102 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
4103 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
4104 [flutterPlatformViewsController
4105 registerViewFactory:factory
4106 withId:@"MockFlutterPlatformView"
4107 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
4108 FlutterResult result = ^(id result) {
4109 };
4110
4111 [flutterPlatformViewsController
4112 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4113 arguments:@{
4114 @"id" : @1,
4115 @"viewType" : @"MockFlutterPlatformView"
4116 }]
4117 result:result];
4118
4119 XCTAssertNotNil(gMockPlatformView);
4120 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
4121 flutterPlatformViewsController.flutterView = flutterView;
4122 // Create embedded view params
4123 flutter::MutatorsStack stack1;
4124 // Layer tree always pushes a screen scale factor to the stack
4125 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
4126 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
4127 stack1.PushTransform(screenScaleMatrix);
4128 // Push a clip rect
4129 flutter::DlRect rect = flutter::DlRect::MakeXYWH(2, 2, 3, 3);
4130 stack1.PushClipRect(rect);
4131
4132 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
4133 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack1);
4134
4135 flutter::MutatorsStack stack2;
4136 stack2.PushClipRect(rect);
4137 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
4138 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack2);
4139
4140 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
4141 withParams:std::move(embeddedViewParams1)];
4142 [flutterPlatformViewsController
4143 compositeView:1
4144 withParams:[flutterPlatformViewsController compositionParamsForView:1]];
4145
4146 UIView* childClippingView = gMockPlatformView.superview.superview;
4147
4148 UIView* maskView = childClippingView.maskView;
4149 XCTAssert([maskView.layer isKindOfClass:[CAShapeLayer class]],
4150 @"Mask view must use CAShapeLayer as its backing layer.");
4151 }
4152
4153 // Return true if a correct visual effect view is found. It also implies all the validation in this
4154 // method passes.
4155 //
4156 // There are two fail states for this method. 1. One of the XCTAssert method failed; or 2. No
4157 // correct visual effect view found.
4158 - (BOOL)validateOneVisualEffectView:(UIView*)visualEffectView
4159 expectedFrame:(CGRect)frame
4160 inputRadius:(CGFloat)inputRadius {
4161 XCTAssertTrue(CGRectEqualToRect(visualEffectView.frame, frame));
4162 for (UIView* view in visualEffectView.subviews) {
4163 if (![NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) {
4164 continue;
4165 }
4166 XCTAssertEqual(view.layer.filters.count, 1u);
4167 NSObject* filter = view.layer.filters.firstObject;
4168
4169 XCTAssertEqualObjects([filter valueForKey:@"name"], @"gaussianBlur");
4170
4171 NSObject* inputRadiusInFilter = [filter valueForKey:@"inputRadius"];
4172 XCTAssertTrue([inputRadiusInFilter isKindOfClass:[NSNumber class]] &&
4173 flutter::BlurRadiusEqualToBlurRadius(((NSNumber*)inputRadiusInFilter).floatValue,
4174 inputRadius));
4175 return YES;
4176 }
4177 return NO;
4178 }
4179
4180 - (void)testDisposingViewInCompositionOrderDoNotCrash {
4181 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
4182
4183 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
4184 /*platform=*/GetDefaultTaskRunner(),
4185 /*raster=*/GetDefaultTaskRunner(),
4186 /*ui=*/GetDefaultTaskRunner(),
4187 /*io=*/GetDefaultTaskRunner());
4188 FlutterPlatformViewsController* flutterPlatformViewsController =
4189 [[FlutterPlatformViewsController alloc] init];
4190 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
4191 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
4192 /*delegate=*/mock_delegate,
4193 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
4194 /*platform_views_controller=*/flutterPlatformViewsController,
4195 /*task_runners=*/runners,
4196 /*worker_task_runner=*/nil,
4197 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
4198
4199 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
4200 flutterPlatformViewsController.flutterView = flutterView;
4201
4202 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
4203 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
4204 [flutterPlatformViewsController
4205 registerViewFactory:factory
4206 withId:@"MockFlutterPlatformView"
4207 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
4208 FlutterResult result = ^(id result) {
4209 };
4210
4211 [flutterPlatformViewsController
4212 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4213 arguments:@{
4214 @"id" : @0,
4215 @"viewType" : @"MockFlutterPlatformView"
4216 }]
4217 result:result];
4218 [flutterPlatformViewsController
4219 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4220 arguments:@{
4221 @"id" : @1,
4222 @"viewType" : @"MockFlutterPlatformView"
4223 }]
4224 result:result];
4225
4226 {
4227 // **** First frame, view id 0, 1 in the composition_order_, disposing view 0 is called. **** //
4228 // No view should be disposed, or removed from the composition order.
4229 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
4230 flutter::MutatorsStack stack;
4231 flutter::DlMatrix finalMatrix;
4232 auto embeddedViewParams0 = std::make_unique<flutter::EmbeddedViewParams>(
4233 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
4234 [flutterPlatformViewsController prerollCompositeEmbeddedView:0
4235 withParams:std::move(embeddedViewParams0)];
4236
4237 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
4238 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
4239 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
4240 withParams:std::move(embeddedViewParams1)];
4241
4242 XCTAssertEqual(flutterPlatformViewsController.embeddedViewCount, 2UL);
4243
4244 XCTestExpectation* expectation = [self expectationWithDescription:@"dispose call ended."];
4245 FlutterResult disposeResult = ^(id result) {
4246 [expectation fulfill];
4247 };
4248
4249 [flutterPlatformViewsController
4250 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"dispose" arguments:@0]
4251 result:disposeResult];
4252 [self waitForExpectationsWithTimeout:30 handler:nil];
4253
4254 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
4255 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
4256 nullptr, framebuffer_info,
4257 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
4258 [](const flutter::SurfaceFrame& surface_frame) { return true; },
4259 /*frame_size=*/SkISize::Make(800, 600), nullptr,
4260 /*display_list_fallback=*/true);
4261 XCTAssertTrue([flutterPlatformViewsController
4262 submitFrame:std::move(mock_surface)
4263 withIosContext:std::make_shared<flutter::IOSContextNoop>()]);
4264
4265 // Disposing won't remove embedded views until the view is removed from the composition_order_
4266 XCTAssertEqual(flutterPlatformViewsController.embeddedViewCount, 2UL);
4267 XCTAssertNotNil([flutterPlatformViewsController platformViewForId:0]);
4268 XCTAssertNotNil([flutterPlatformViewsController platformViewForId:1]);
4269 }
4270
4271 {
4272 // **** Second frame, view id 1 in the composition_order_, no disposing view is called, **** //
4273 // View 0 is removed from the composition order in this frame, hence also disposed.
4274 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
4275 flutter::MutatorsStack stack;
4276 flutter::DlMatrix finalMatrix;
4277 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
4278 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
4279 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
4280 withParams:std::move(embeddedViewParams1)];
4281
4282 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
4283 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
4284 nullptr, framebuffer_info,
4285 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
4286 [](const flutter::SurfaceFrame& surface_frame) { return true; },
4287 /*frame_size=*/SkISize::Make(800, 600), nullptr, /*display_list_fallback=*/true);
4288 XCTAssertTrue([flutterPlatformViewsController
4289 submitFrame:std::move(mock_surface)
4290 withIosContext:std::make_shared<flutter::IOSContextNoop>()]);
4291
4292 // Disposing won't remove embedded views until the view is removed from the composition_order_
4293 XCTAssertEqual(flutterPlatformViewsController.embeddedViewCount, 1UL);
4294 XCTAssertNil([flutterPlatformViewsController platformViewForId:0]);
4295 XCTAssertNotNil([flutterPlatformViewsController platformViewForId:1]);
4296 }
4297 }
4298 - (void)testOnlyPlatformViewsAreRemovedWhenReset {
4299 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
4300
4301 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
4302 /*platform=*/GetDefaultTaskRunner(),
4303 /*raster=*/GetDefaultTaskRunner(),
4304 /*ui=*/GetDefaultTaskRunner(),
4305 /*io=*/GetDefaultTaskRunner());
4306 FlutterPlatformViewsController* flutterPlatformViewsController =
4307 [[FlutterPlatformViewsController alloc] init];
4308 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
4309 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
4310 /*delegate=*/mock_delegate,
4311 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
4312 /*platform_views_controller=*/flutterPlatformViewsController,
4313 /*task_runners=*/runners,
4314 /*worker_task_runner=*/nil,
4315 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
4316
4317 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
4318 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
4319 [flutterPlatformViewsController
4320 registerViewFactory:factory
4321 withId:@"MockFlutterPlatformView"
4322 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
4323 FlutterResult result = ^(id result) {
4324 };
4325 [flutterPlatformViewsController
4326 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4327 arguments:@{
4328 @"id" : @2,
4329 @"viewType" : @"MockFlutterPlatformView"
4330 }]
4331 result:result];
4332 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
4333 flutterPlatformViewsController.flutterView = flutterView;
4334 // Create embedded view params
4335 flutter::MutatorsStack stack;
4336 // Layer tree always pushes a screen scale factor to the stack
4337 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
4338 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
4339 stack.PushTransform(screenScaleMatrix);
4340 // Push a translate matrix
4341 flutter::DlMatrix translateMatrix = flutter::DlMatrix::MakeTranslation({100, 100});
4342 stack.PushTransform(translateMatrix);
4343 flutter::DlMatrix finalMatrix = screenScaleMatrix * translateMatrix;
4344
4345 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
4346 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
4347
4348 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
4349 withParams:std::move(embeddedViewParams)];
4350
4351 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
4352 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
4353 nullptr, framebuffer_info,
4354 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
4355 [](const flutter::SurfaceFrame& surface_frame) { return true; },
4356 /*frame_size=*/SkISize::Make(800, 600), nullptr, /*display_list_fallback=*/true);
4357 [flutterPlatformViewsController submitFrame:std::move(mock_surface)
4358 withIosContext:std::make_shared<flutter::IOSContextNoop>()];
4359
4360 UIView* someView = [[UIView alloc] init];
4361 [flutterView addSubview:someView];
4362
4363 [flutterPlatformViewsController reset];
4364 XCTAssertEqual(flutterView.subviews.count, 1u);
4365 XCTAssertEqual(flutterView.subviews.firstObject, someView);
4366 }
4367
4368 - (void)testResetClearsPreviousCompositionOrder {
4369 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
4370
4371 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
4372 /*platform=*/GetDefaultTaskRunner(),
4373 /*raster=*/GetDefaultTaskRunner(),
4374 /*ui=*/GetDefaultTaskRunner(),
4375 /*io=*/GetDefaultTaskRunner());
4376 FlutterPlatformViewsController* flutterPlatformViewsController =
4377 [[FlutterPlatformViewsController alloc] init];
4378 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
4379 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
4380 /*delegate=*/mock_delegate,
4381 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
4382 /*platform_views_controller=*/flutterPlatformViewsController,
4383 /*task_runners=*/runners,
4384 /*worker_task_runner=*/nil,
4385 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
4386
4387 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
4388 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
4389 [flutterPlatformViewsController
4390 registerViewFactory:factory
4391 withId:@"MockFlutterPlatformView"
4392 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
4393 FlutterResult result = ^(id result) {
4394 };
4395 [flutterPlatformViewsController
4396 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4397 arguments:@{
4398 @"id" : @2,
4399 @"viewType" : @"MockFlutterPlatformView"
4400 }]
4401 result:result];
4402 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
4403 flutterPlatformViewsController.flutterView = flutterView;
4404 // Create embedded view params
4405 flutter::MutatorsStack stack;
4406 // Layer tree always pushes a screen scale factor to the stack
4407 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
4408 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
4409 stack.PushTransform(screenScaleMatrix);
4410 // Push a translate matrix
4411 flutter::DlMatrix translateMatrix = flutter::DlMatrix::MakeTranslation({100, 100});
4412 stack.PushTransform(translateMatrix);
4413 flutter::DlMatrix finalMatrix = screenScaleMatrix * translateMatrix;
4414
4415 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
4416 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
4417
4418 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
4419 withParams:std::move(embeddedViewParams)];
4420
4421 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
4422 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
4423 nullptr, framebuffer_info,
4424 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
4425 [](const flutter::SurfaceFrame& surface_frame) { return true; },
4426 /*frame_size=*/SkISize::Make(800, 600), nullptr, /*display_list_fallback=*/true);
4427 [flutterPlatformViewsController submitFrame:std::move(mock_surface)
4428 withIosContext:std::make_shared<flutter::IOSContextNoop>()];
4429
4430 // The above code should result in previousCompositionOrder having one viewId in it
4431 XCTAssertEqual(flutterPlatformViewsController.previousCompositionOrder.size(), 1ul);
4432
4433 // reset should clear previousCompositionOrder
4434 [flutterPlatformViewsController reset];
4435
4436 // previousCompositionOrder should now be empty
4437 XCTAssertEqual(flutterPlatformViewsController.previousCompositionOrder.size(), 0ul);
4438 }
4439
4440 - (void)testNilPlatformViewDoesntCrash {
4441 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
4442
4443 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
4444 /*platform=*/GetDefaultTaskRunner(),
4445 /*raster=*/GetDefaultTaskRunner(),
4446 /*ui=*/GetDefaultTaskRunner(),
4447 /*io=*/GetDefaultTaskRunner());
4448 FlutterPlatformViewsController* flutterPlatformViewsController =
4449 [[FlutterPlatformViewsController alloc] init];
4450 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
4451 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
4452 /*delegate=*/mock_delegate,
4453 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
4454 /*platform_views_controller=*/flutterPlatformViewsController,
4455 /*task_runners=*/runners,
4456 /*worker_task_runner=*/nil,
4457 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
4458
4459 FlutterPlatformViewsTestNilFlutterPlatformFactory* factory =
4460 [[FlutterPlatformViewsTestNilFlutterPlatformFactory alloc] init];
4461 [flutterPlatformViewsController
4462 registerViewFactory:factory
4463 withId:@"MockFlutterPlatformView"
4464 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
4465 FlutterResult result = ^(id result) {
4466 };
4467 [flutterPlatformViewsController
4468 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4469 arguments:@{
4470 @"id" : @2,
4471 @"viewType" : @"MockFlutterPlatformView"
4472 }]
4473 result:result];
4474 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
4475 flutterPlatformViewsController.flutterView = flutterView;
4476
4477 // Create embedded view params
4478 flutter::MutatorsStack stack;
4479 // Layer tree always pushes a screen scale factor to the stack
4480 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
4481 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
4482 stack.PushTransform(screenScaleMatrix);
4483 // Push a translate matrix
4484 flutter::DlMatrix translateMatrix = flutter::DlMatrix::MakeTranslation({100, 100});
4485 stack.PushTransform(translateMatrix);
4486 flutter::DlMatrix finalMatrix = screenScaleMatrix * translateMatrix;
4487
4488 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
4489 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
4490
4491 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
4492 withParams:std::move(embeddedViewParams)];
4493
4494 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
4495 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
4496 nullptr, framebuffer_info,
4497 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
4498 [](const flutter::SurfaceFrame& surface_frame) { return true; },
4499 /*frame_size=*/SkISize::Make(800, 600), nullptr, /*display_list_fallback=*/true);
4500 [flutterPlatformViewsController submitFrame:std::move(mock_surface)
4501 withIosContext:std::make_shared<flutter::IOSContextNoop>()];
4502
4503 XCTAssertEqual(flutterView.subviews.count, 1u);
4504 }
4505
4506 - (void)testFlutterTouchInterceptingViewLinksToAccessibilityContainer {
4507 FlutterTouchInterceptingView* touchInteceptorView = [[FlutterTouchInterceptingView alloc] init];
4508 NSObject* container = [[NSObject alloc] init];
4509 [touchInteceptorView setFlutterAccessibilityContainer:container];
4510 XCTAssertEqualObjects([touchInteceptorView accessibilityContainer], container);
4511 }
4512
4513 - (void)testLayerPool {
4514 // Create an IOSContext.
4515 FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar"];
4516 [engine run];
4517 XCTAssertTrue(engine.platformView != nullptr);
4518 auto ios_context = engine.platformView->GetIosContext();
4519
4520 auto pool = flutter::OverlayLayerPool{};
4521
4522 // Add layers to the pool.
4523 pool.CreateLayer(ios_context, MTLPixelFormatBGRA8Unorm);
4524 XCTAssertEqual(pool.size(), 1u);
4525 pool.CreateLayer(ios_context, MTLPixelFormatBGRA8Unorm);
4526 XCTAssertEqual(pool.size(), 2u);
4527
4528 // Mark all layers as unused.
4529 pool.RecycleLayers();
4530 XCTAssertEqual(pool.size(), 2u);
4531
4532 // Free the unused layers. One should remain.
4533 auto unused_layers = pool.RemoveUnusedLayers();
4534 XCTAssertEqual(unused_layers.size(), 2u);
4535 XCTAssertEqual(pool.size(), 1u);
4536 }
4537
4538 - (void)testFlutterPlatformViewControllerSubmitFramePreservingFrameDamage {
4539 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
4540
4541 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
4542 /*platform=*/GetDefaultTaskRunner(),
4543 /*raster=*/GetDefaultTaskRunner(),
4544 /*ui=*/GetDefaultTaskRunner(),
4545 /*io=*/GetDefaultTaskRunner());
4546 FlutterPlatformViewsController* flutterPlatformViewsController =
4547 [[FlutterPlatformViewsController alloc] init];
4548 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
4549 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
4550 /*delegate=*/mock_delegate,
4551 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
4552 /*platform_views_controller=*/flutterPlatformViewsController,
4553 /*task_runners=*/runners,
4554 /*worker_task_runner=*/nil,
4555 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
4556
4557 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
4558 flutterPlatformViewsController.flutterView = flutterView;
4559
4560 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
4561 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
4562 [flutterPlatformViewsController
4563 registerViewFactory:factory
4564 withId:@"MockFlutterPlatformView"
4565 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
4566 FlutterResult result = ^(id result) {
4567 };
4568 [flutterPlatformViewsController
4569 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4570 arguments:@{
4571 @"id" : @0,
4572 @"viewType" : @"MockFlutterPlatformView"
4573 }]
4574 result:result];
4575
4576 // This overwrites `gMockPlatformView` to another view.
4577 [flutterPlatformViewsController
4578 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4579 arguments:@{
4580 @"id" : @1,
4581 @"viewType" : @"MockFlutterPlatformView"
4582 }]
4583 result:result];
4584
4585 [flutterPlatformViewsController beginFrameWithSize:SkISize::Make(300, 300)];
4586 flutter::MutatorsStack stack;
4587 flutter::DlMatrix finalMatrix;
4588 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
4589 flutter::ToSkMatrix(finalMatrix), SkSize::Make(300, 300), stack);
4590 [flutterPlatformViewsController prerollCompositeEmbeddedView:0
4591 withParams:std::move(embeddedViewParams1)];
4592
4593 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
4594 flutter::ToSkMatrix(finalMatrix), SkSize::Make(500, 500), stack);
4595 [flutterPlatformViewsController prerollCompositeEmbeddedView:1
4596 withParams:std::move(embeddedViewParams2)];
4597
4598 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
4599 std::optional<flutter::SurfaceFrame::SubmitInfo> submit_info;
4600 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
4601 nullptr, framebuffer_info,
4602 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
4603 [&](const flutter::SurfaceFrame& surface_frame) {
4604 submit_info = surface_frame.submit_info();
4605 return true;
4606 },
4607 /*frame_size=*/SkISize::Make(800, 600), nullptr,
4608 /*display_list_fallback=*/true);
4609 mock_surface->set_submit_info({
4610 .frame_damage = SkIRect::MakeWH(800, 600),
4611 .buffer_damage = SkIRect::MakeWH(400, 600),
4612 });
4613
4614 [flutterPlatformViewsController submitFrame:std::move(mock_surface)
4615 withIosContext:std::make_shared<flutter::IOSContextNoop>()];
4616
4617 XCTAssertTrue(submit_info.has_value());
4618 XCTAssertEqual(*submit_info->frame_damage, SkIRect::MakeWH(800, 600));
4619 XCTAssertEqual(*submit_info->buffer_damage, SkIRect::MakeWH(400, 600));
4620 }
4621
4622 - (void)testClipSuperellipse {
4623 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
4624
4625 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
4626 /*platform=*/GetDefaultTaskRunner(),
4627 /*raster=*/GetDefaultTaskRunner(),
4628 /*ui=*/GetDefaultTaskRunner(),
4629 /*io=*/GetDefaultTaskRunner());
4630 FlutterPlatformViewsController* flutterPlatformViewsController =
4631 [[FlutterPlatformViewsController alloc] init];
4632 flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
4633 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
4634 /*delegate=*/mock_delegate,
4635 /*rendering_api=*/flutter::IOSRenderingAPI::kMetal,
4636 /*platform_views_controller=*/flutterPlatformViewsController,
4637 /*task_runners=*/runners,
4638 /*worker_task_runner=*/nil,
4639 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
4640
4641 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
4642 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
4643 [flutterPlatformViewsController
4644 registerViewFactory:factory
4645 withId:@"MockFlutterPlatformView"
4646 gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
4647 FlutterResult result = ^(id result) {
4648 };
4649 [flutterPlatformViewsController
4650 onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"create"
4651 arguments:@{
4652 @"id" : @2,
4653 @"viewType" : @"MockFlutterPlatformView"
4654 }]
4655 result:result];
4656
4657 XCTAssertNotNil(gMockPlatformView);
4658
4659 UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
4660 flutterPlatformViewsController.flutterView = flutterView;
4661 // Create embedded view params
4662 flutter::MutatorsStack stack;
4663 // Layer tree always pushes a screen scale factor to the stack
4664 flutter::DlScalar screenScale = [UIScreen mainScreen].scale;
4665 flutter::DlMatrix screenScaleMatrix = flutter::DlMatrix::MakeScale({screenScale, screenScale, 1});
4666 stack.PushTransform(screenScaleMatrix);
4667 // Push a clip superellipse
4668 flutter::DlRect rect = flutter::DlRect::MakeXYWH(3, 3, 5, 5);
4669 stack.PushClipRSE(flutter::DlRoundSuperellipse::MakeOval(rect));
4670
4671 auto embeddedViewParams = std::make_unique<flutter::EmbeddedViewParams>(
4672 flutter::ToSkMatrix(screenScaleMatrix), SkSize::Make(10, 10), stack);
4673
4674 [flutterPlatformViewsController prerollCompositeEmbeddedView:2
4675 withParams:std::move(embeddedViewParams)];
4676 [flutterPlatformViewsController
4677 compositeView:2
4678 withParams:[flutterPlatformViewsController compositionParamsForView:2]];
4679
4680 gMockPlatformView.backgroundColor = UIColor.redColor;
4681 XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
4682 ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
4683 [flutterView addSubview:childClippingView];
4684
4685 [flutterView setNeedsLayout];
4686 [flutterView layoutIfNeeded];
4687
4688 CGPoint corners[] = {CGPointMake(rect.GetLeft(), rect.GetTop()),
4689 CGPointMake(rect.GetRight(), rect.GetTop()),
4690 CGPointMake(rect.GetLeft(), rect.GetBottom()),
4691 CGPointMake(rect.GetRight(), rect.GetBottom())};
4692 for (auto point : corners) {
4693 int alpha = [self alphaOfPoint:point onView:flutterView];
4694 XCTAssertNotEqual(alpha, 255);
4695 }
4696 CGPoint center =
4697 CGPointMake(rect.GetLeft() + rect.GetWidth() / 2, rect.GetTop() + rect.GetHeight() / 2);
4698 int alpha = [self alphaOfPoint:center onView:flutterView];
4699 XCTAssertEqual(alpha, 255);
4700 }
4701
4702 @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:643
FlutterTouchInterceptingView
Definition: FlutterPlatformViews.mm:513
ForwardingGestureRecognizer
Definition: FlutterPlatformViews.mm:692
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