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