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