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