| Flutter iOS Embedder |

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