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