| 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)testClipRRect {
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* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
1825 flutterPlatformViewsController->SetFlutterView(mockFlutterView);
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 rrect
1833 SkRRect rrect = SkRRect::MakeRectXY(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1);
1834 stack.PushClipRRect(rrect);
1835
1836 auto embeddedViewParams =
1837 std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
1838
1839 flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1840 flutterPlatformViewsController->CompositeEmbeddedView(2);
1841 gMockPlatformView.backgroundColor = UIColor.redColor;
1842 XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
1843 ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1844 [mockFlutterView addSubview:childClippingView];
1845
1846 [mockFlutterView setNeedsLayout];
1847 [mockFlutterView layoutIfNeeded];
1848
1849 /*
1850 ClippingMask outterClipping
1851 2 3 4 5 6 7 2 3 4 5 6 7
1852 2 / - - - - \ 2 + - - - - +
1853 3 | | 3 | |
1854 4 | | 4 | |
1855 5 | | 5 | |
1856 6 | | 6 | |
1857 7 \ - - - - / 7 + - - - - +
1858
1859 innerClipping1 innerClipping2
1860 2 3 4 5 6 7 2 3 4 5 6 7
1861 2 + - - + 2
1862 3 | | 3 + - - - - +
1863 4 | | 4 | |
1864 5 | | 5 | |
1865 6 | | 6 + - - - - +
1866 7 + - - + 7
1867 */
1868 CGRect innerClipping1 = CGRectMake(3, 2, 4, 6);
1869 CGRect innerClipping2 = CGRectMake(2, 3, 6, 4);
1870 CGRect outterClipping = CGRectMake(2, 2, 6, 6);
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:mockFlutterView];
1875 if (CGRectContainsPoint(innerClipping1, point) ||
1876 CGRectContainsPoint(innerClipping2, point)) {
1877 // Pixels inside either of the 2 inner clippings should be fully opaque.
1878 XCTAssertEqual(alpha, 255);
1879 } else if (CGRectContainsPoint(outterClipping, point)) {
1880 // Corner pixels (i.e. (2, 2), (2, 7), (7, 2) and (7, 7)) should be partially transparent.
1881 XCTAssert(0 < alpha && alpha < 255);
1882 } else {
1883 // Pixels outside outterClipping should be fully transparent.
1884 XCTAssertEqual(alpha, 0);
1885 }
1886 }
1887 }
1888 }
1889
1890 - (void)testClipPath {
1891 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1892 auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
1893 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1894 /*platform=*/thread_task_runner,
1895 /*raster=*/thread_task_runner,
1896 /*ui=*/thread_task_runner,
1897 /*io=*/thread_task_runner);
1898 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
1899 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1900 /*delegate=*/mock_delegate,
1901 /*rendering_api=*/mock_delegate.settings_.enable_impeller
1902 ? flutter::IOSRenderingAPI::kMetal
1903 : flutter::IOSRenderingAPI::kSoftware,
1904 /*platform_views_controller=*/flutterPlatformViewsController,
1905 /*task_runners=*/runners,
1906 /*worker_task_runner=*/nil,
1907 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
1908
1909 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
1910 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
1911 flutterPlatformViewsController->RegisterViewFactory(
1912 factory, @"MockFlutterPlatformView",
1913 FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
1914 FlutterResult result = ^(id result) {
1915 };
1916 flutterPlatformViewsController->OnMethodCall(
1917 [FlutterMethodCall
1918 methodCallWithMethodName:@"create"
1919 arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
1920 result);
1921
1922 XCTAssertNotNil(gMockPlatformView);
1923
1924 UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
1925 flutterPlatformViewsController->SetFlutterView(mockFlutterView);
1926 // Create embedded view params
1927 flutter::MutatorsStack stack;
1928 // Layer tree always pushes a screen scale factor to the stack
1929 SkMatrix screenScaleMatrix =
1930 SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
1931 stack.PushTransform(screenScaleMatrix);
1932 // Push a clip path
1933 SkPath path;
1934 path.addRoundRect(SkRect::MakeXYWH(2, 2, 6, 6), 1, 1);
1935 stack.PushClipPath(path);
1936
1937 auto embeddedViewParams =
1938 std::make_unique<flutter::EmbeddedViewParams>(screenScaleMatrix, SkSize::Make(10, 10), stack);
1939
1940 flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
1941 flutterPlatformViewsController->CompositeEmbeddedView(2);
1942 gMockPlatformView.backgroundColor = UIColor.redColor;
1943 XCTAssertTrue([gMockPlatformView.superview.superview isKindOfClass:ChildClippingView.class]);
1944 ChildClippingView* childClippingView = (ChildClippingView*)gMockPlatformView.superview.superview;
1945 [mockFlutterView addSubview:childClippingView];
1946
1947 [mockFlutterView setNeedsLayout];
1948 [mockFlutterView layoutIfNeeded];
1949
1950 /*
1951 ClippingMask outterClipping
1952 2 3 4 5 6 7 2 3 4 5 6 7
1953 2 / - - - - \ 2 + - - - - +
1954 3 | | 3 | |
1955 4 | | 4 | |
1956 5 | | 5 | |
1957 6 | | 6 | |
1958 7 \ - - - - / 7 + - - - - +
1959
1960 innerClipping1 innerClipping2
1961 2 3 4 5 6 7 2 3 4 5 6 7
1962 2 + - - + 2
1963 3 | | 3 + - - - - +
1964 4 | | 4 | |
1965 5 | | 5 | |
1966 6 | | 6 + - - - - +
1967 7 + - - + 7
1968 */
1969 CGRect innerClipping1 = CGRectMake(3, 2, 4, 6);
1970 CGRect innerClipping2 = CGRectMake(2, 3, 6, 4);
1971 CGRect outterClipping = CGRectMake(2, 2, 6, 6);
1972 for (int i = 0; i < 10; i++) {
1973 for (int j = 0; j < 10; j++) {
1974 CGPoint point = CGPointMake(i, j);
1975 int alpha = [self alphaOfPoint:CGPointMake(i, j) onView:mockFlutterView];
1976 if (CGRectContainsPoint(innerClipping1, point) ||
1977 CGRectContainsPoint(innerClipping2, point)) {
1978 // Pixels inside either of the 2 inner clippings should be fully opaque.
1979 XCTAssertEqual(alpha, 255);
1980 } else if (CGRectContainsPoint(outterClipping, point)) {
1981 // Corner pixels (i.e. (2, 2), (2, 7), (7, 2) and (7, 7)) should be partially transparent.
1982 XCTAssert(0 < alpha && alpha < 255);
1983 } else {
1984 // Pixels outside outterClipping should be fully transparent.
1985 XCTAssertEqual(alpha, 0);
1986 }
1987 }
1988 }
1989 }
1990
1991 - (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents {
1992 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
1993 auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
1994 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
1995 /*platform=*/thread_task_runner,
1996 /*raster=*/thread_task_runner,
1997 /*ui=*/thread_task_runner,
1998 /*io=*/thread_task_runner);
1999 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2000 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2001 /*delegate=*/mock_delegate,
2002 /*rendering_api=*/mock_delegate.settings_.enable_impeller
2003 ? flutter::IOSRenderingAPI::kMetal
2004 : flutter::IOSRenderingAPI::kSoftware,
2005 /*platform_views_controller=*/flutterPlatformViewsController,
2006 /*task_runners=*/runners,
2007 /*worker_task_runner=*/nil,
2008 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2009
2010 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2011 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2012 flutterPlatformViewsController->RegisterViewFactory(
2013 factory, @"MockFlutterPlatformView",
2014 FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
2015 FlutterResult result = ^(id result) {
2016 };
2017 flutterPlatformViewsController->OnMethodCall(
2018 [FlutterMethodCall
2019 methodCallWithMethodName:@"create"
2020 arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2021 result);
2022
2023 XCTAssertNotNil(gMockPlatformView);
2024
2025 // Find touch inteceptor view
2026 UIView* touchInteceptorView = gMockPlatformView;
2027 while (touchInteceptorView != nil &&
2028 ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
2029 touchInteceptorView = touchInteceptorView.superview;
2030 }
2031 XCTAssertNotNil(touchInteceptorView);
2032
2033 // Find ForwardGestureRecognizer
2034 UIGestureRecognizer* forwardGectureRecognizer = nil;
2035 for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
2036 if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) {
2037 forwardGectureRecognizer = gestureRecognizer;
2038 break;
2039 }
2040 }
2041
2042 // Before setting flutter view controller, events are not dispatched.
2043 NSSet* touches1 = [[NSSet alloc] init];
2044 id event1 = OCMClassMock([UIEvent class]);
2045 id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]);
2046 [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2047 OCMReject([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]);
2048
2049 // Set flutter view controller allows events to be dispatched.
2050 NSSet* touches2 = [[NSSet alloc] init];
2051 id event2 = OCMClassMock([UIEvent class]);
2052 flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller);
2053 [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2];
2054 OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]);
2055 }
2056
2057 - (void)testSetFlutterViewControllerInTheMiddleOfTouchEventShouldStillAllowGesturesToBeHandled {
2058 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2059 auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2060 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2061 /*platform=*/thread_task_runner,
2062 /*raster=*/thread_task_runner,
2063 /*ui=*/thread_task_runner,
2064 /*io=*/thread_task_runner);
2065 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2066 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2067 /*delegate=*/mock_delegate,
2068 /*rendering_api=*/mock_delegate.settings_.enable_impeller
2069 ? flutter::IOSRenderingAPI::kMetal
2070 : flutter::IOSRenderingAPI::kSoftware,
2071 /*platform_views_controller=*/flutterPlatformViewsController,
2072 /*task_runners=*/runners,
2073 /*worker_task_runner=*/nil,
2074 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2075
2076 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2077 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2078 flutterPlatformViewsController->RegisterViewFactory(
2079 factory, @"MockFlutterPlatformView",
2080 FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
2081 FlutterResult result = ^(id result) {
2082 };
2083 flutterPlatformViewsController->OnMethodCall(
2084 [FlutterMethodCall
2085 methodCallWithMethodName:@"create"
2086 arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2087 result);
2088
2089 XCTAssertNotNil(gMockPlatformView);
2090
2091 // Find touch inteceptor view
2092 UIView* touchInteceptorView = gMockPlatformView;
2093 while (touchInteceptorView != nil &&
2094 ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
2095 touchInteceptorView = touchInteceptorView.superview;
2096 }
2097 XCTAssertNotNil(touchInteceptorView);
2098
2099 // Find ForwardGestureRecognizer
2100 UIGestureRecognizer* forwardGectureRecognizer = nil;
2101 for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
2102 if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) {
2103 forwardGectureRecognizer = gestureRecognizer;
2104 break;
2105 }
2106 }
2107 id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]);
2108 {
2109 // ***** Sequence 1, finishing touch event with touchEnded ***** //
2110 flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller);
2111
2112 NSSet* touches1 = [[NSSet alloc] init];
2113 id event1 = OCMClassMock([UIEvent class]);
2114 [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2115 OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]);
2116
2117 flutterPlatformViewsController->SetFlutterViewController(nil);
2118
2119 // Allow the touch events to finish
2120 NSSet* touches2 = [[NSSet alloc] init];
2121 id event2 = OCMClassMock([UIEvent class]);
2122 [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2];
2123 OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]);
2124
2125 NSSet* touches3 = [[NSSet alloc] init];
2126 id event3 = OCMClassMock([UIEvent class]);
2127 [forwardGectureRecognizer touchesEnded:touches3 withEvent:event3];
2128 OCMVerify([mockFlutterViewContoller touchesEnded:touches3 withEvent:event3]);
2129
2130 // Now the 2nd touch sequence should not be allowed.
2131 NSSet* touches4 = [[NSSet alloc] init];
2132 id event4 = OCMClassMock([UIEvent class]);
2133 [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4];
2134 OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]);
2135
2136 NSSet* touches5 = [[NSSet alloc] init];
2137 id event5 = OCMClassMock([UIEvent class]);
2138 [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5];
2139 OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]);
2140 }
2141
2142 {
2143 // ***** Sequence 2, finishing touch event with touchCancelled ***** //
2144 flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller);
2145
2146 NSSet* touches1 = [[NSSet alloc] init];
2147 id event1 = OCMClassMock([UIEvent class]);
2148 [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2149 OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]);
2150
2151 flutterPlatformViewsController->SetFlutterViewController(nil);
2152
2153 // Allow the touch events to finish
2154 NSSet* touches2 = [[NSSet alloc] init];
2155 id event2 = OCMClassMock([UIEvent class]);
2156 [forwardGectureRecognizer touchesMoved:touches2 withEvent:event2];
2157 OCMVerify([mockFlutterViewContoller touchesMoved:touches2 withEvent:event2]);
2158
2159 NSSet* touches3 = [[NSSet alloc] init];
2160 id event3 = OCMClassMock([UIEvent class]);
2161 [forwardGectureRecognizer touchesCancelled:touches3 withEvent:event3];
2162 OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches3]);
2163
2164 // Now the 2nd touch sequence should not be allowed.
2165 NSSet* touches4 = [[NSSet alloc] init];
2166 id event4 = OCMClassMock([UIEvent class]);
2167 [forwardGectureRecognizer touchesBegan:touches4 withEvent:event4];
2168 OCMReject([mockFlutterViewContoller touchesBegan:touches4 withEvent:event4]);
2169
2170 NSSet* touches5 = [[NSSet alloc] init];
2171 id event5 = OCMClassMock([UIEvent class]);
2172 [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5];
2173 OCMReject([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]);
2174 }
2175
2176 flutterPlatformViewsController->Reset();
2177 }
2178
2179 - (void)
2180 testSetFlutterViewControllerInTheMiddleOfTouchEventAllowsTheNewControllerToHandleSecondTouchSequence {
2181 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2182 auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2183 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2184 /*platform=*/thread_task_runner,
2185 /*raster=*/thread_task_runner,
2186 /*ui=*/thread_task_runner,
2187 /*io=*/thread_task_runner);
2188 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2189 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2190 /*delegate=*/mock_delegate,
2191 /*rendering_api=*/mock_delegate.settings_.enable_impeller
2192 ? flutter::IOSRenderingAPI::kMetal
2193 : flutter::IOSRenderingAPI::kSoftware,
2194 /*platform_views_controller=*/flutterPlatformViewsController,
2195 /*task_runners=*/runners,
2196 /*worker_task_runner=*/nil,
2197 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2198
2199 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2200 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2201 flutterPlatformViewsController->RegisterViewFactory(
2202 factory, @"MockFlutterPlatformView",
2203 FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
2204 FlutterResult result = ^(id result) {
2205 };
2206 flutterPlatformViewsController->OnMethodCall(
2207 [FlutterMethodCall
2208 methodCallWithMethodName:@"create"
2209 arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2210 result);
2211
2212 XCTAssertNotNil(gMockPlatformView);
2213
2214 // Find touch inteceptor view
2215 UIView* touchInteceptorView = gMockPlatformView;
2216 while (touchInteceptorView != nil &&
2217 ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
2218 touchInteceptorView = touchInteceptorView.superview;
2219 }
2220 XCTAssertNotNil(touchInteceptorView);
2221
2222 // Find ForwardGestureRecognizer
2223 UIGestureRecognizer* forwardGectureRecognizer = nil;
2224 for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
2225 if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) {
2226 forwardGectureRecognizer = gestureRecognizer;
2227 break;
2228 }
2229 }
2230 id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]);
2231
2232 flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller);
2233
2234 // The touches in this sequence requires 1 touch object, we always create the NSSet with one item.
2235 NSSet* touches1 = [NSSet setWithObject:@1];
2236 id event1 = OCMClassMock([UIEvent class]);
2237 [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2238 OCMVerify([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]);
2239
2240 FlutterViewController* mockFlutterViewContoller2 = OCMClassMock([FlutterViewController class]);
2241 flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller2);
2242
2243 // Touch events should still send to the old FlutterViewController if FlutterViewController
2244 // is updated in between.
2245 NSSet* touches2 = [NSSet setWithObject:@1];
2246 id event2 = OCMClassMock([UIEvent class]);
2247 [forwardGectureRecognizer touchesBegan:touches2 withEvent:event2];
2248 OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]);
2249 OCMReject([mockFlutterViewContoller2 touchesBegan:touches2 withEvent:event2]);
2250
2251 NSSet* touches3 = [NSSet setWithObject:@1];
2252 id event3 = OCMClassMock([UIEvent class]);
2253 [forwardGectureRecognizer touchesMoved:touches3 withEvent:event3];
2254 OCMVerify([mockFlutterViewContoller touchesMoved:touches3 withEvent:event3]);
2255 OCMReject([mockFlutterViewContoller2 touchesMoved:touches3 withEvent:event3]);
2256
2257 NSSet* touches4 = [NSSet setWithObject:@1];
2258 id event4 = OCMClassMock([UIEvent class]);
2259 [forwardGectureRecognizer touchesEnded:touches4 withEvent:event4];
2260 OCMVerify([mockFlutterViewContoller touchesEnded:touches4 withEvent:event4]);
2261 OCMReject([mockFlutterViewContoller2 touchesEnded:touches4 withEvent:event4]);
2262
2263 NSSet* touches5 = [NSSet setWithObject:@1];
2264 id event5 = OCMClassMock([UIEvent class]);
2265 [forwardGectureRecognizer touchesEnded:touches5 withEvent:event5];
2266 OCMVerify([mockFlutterViewContoller touchesEnded:touches5 withEvent:event5]);
2267 OCMReject([mockFlutterViewContoller2 touchesEnded:touches5 withEvent:event5]);
2268
2269 // Now the 2nd touch sequence should go to the new FlutterViewController
2270
2271 NSSet* touches6 = [NSSet setWithObject:@1];
2272 id event6 = OCMClassMock([UIEvent class]);
2273 [forwardGectureRecognizer touchesBegan:touches6 withEvent:event6];
2274 OCMVerify([mockFlutterViewContoller2 touchesBegan:touches6 withEvent:event6]);
2275 OCMReject([mockFlutterViewContoller touchesBegan:touches6 withEvent:event6]);
2276
2277 // Allow the touch events to finish
2278 NSSet* touches7 = [NSSet setWithObject:@1];
2279 id event7 = OCMClassMock([UIEvent class]);
2280 [forwardGectureRecognizer touchesMoved:touches7 withEvent:event7];
2281 OCMVerify([mockFlutterViewContoller2 touchesMoved:touches7 withEvent:event7]);
2282 OCMReject([mockFlutterViewContoller touchesMoved:touches7 withEvent:event7]);
2283
2284 NSSet* touches8 = [NSSet setWithObject:@1];
2285 id event8 = OCMClassMock([UIEvent class]);
2286 [forwardGectureRecognizer touchesEnded:touches8 withEvent:event8];
2287 OCMVerify([mockFlutterViewContoller2 touchesEnded:touches8 withEvent:event8]);
2288 OCMReject([mockFlutterViewContoller touchesEnded:touches8 withEvent:event8]);
2289
2290 flutterPlatformViewsController->Reset();
2291 }
2292
2293 - (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled {
2294 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2295 auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2296 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2297 /*platform=*/thread_task_runner,
2298 /*raster=*/thread_task_runner,
2299 /*ui=*/thread_task_runner,
2300 /*io=*/thread_task_runner);
2301 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2302 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2303 /*delegate=*/mock_delegate,
2304 /*rendering_api=*/mock_delegate.settings_.enable_impeller
2305 ? flutter::IOSRenderingAPI::kMetal
2306 : flutter::IOSRenderingAPI::kSoftware,
2307 /*platform_views_controller=*/flutterPlatformViewsController,
2308 /*task_runners=*/runners,
2309 /*worker_task_runner=*/nil,
2310 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2311
2312 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2313 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2314 flutterPlatformViewsController->RegisterViewFactory(
2315 factory, @"MockFlutterPlatformView",
2316 FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
2317 FlutterResult result = ^(id result) {
2318 };
2319 flutterPlatformViewsController->OnMethodCall(
2320 [FlutterMethodCall
2321 methodCallWithMethodName:@"create"
2322 arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2323 result);
2324
2325 XCTAssertNotNil(gMockPlatformView);
2326
2327 // Find touch inteceptor view
2328 UIView* touchInteceptorView = gMockPlatformView;
2329 while (touchInteceptorView != nil &&
2330 ![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
2331 touchInteceptorView = touchInteceptorView.superview;
2332 }
2333 XCTAssertNotNil(touchInteceptorView);
2334
2335 // Find ForwardGestureRecognizer
2336 UIGestureRecognizer* forwardGectureRecognizer = nil;
2337 for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
2338 if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) {
2339 forwardGectureRecognizer = gestureRecognizer;
2340 break;
2341 }
2342 }
2343 id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]);
2344
2345 flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller);
2346
2347 NSSet* touches1 = [NSSet setWithObject:@1];
2348 id event1 = OCMClassMock([UIEvent class]);
2349 [forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
2350
2351 [forwardGectureRecognizer touchesCancelled:touches1 withEvent:event1];
2352 OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches1]);
2353
2354 flutterPlatformViewsController->Reset();
2355 }
2356
2357 - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashing {
2358 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2359 auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2360 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2361 /*platform=*/thread_task_runner,
2362 /*raster=*/thread_task_runner,
2363 /*ui=*/thread_task_runner,
2364 /*io=*/thread_task_runner);
2365 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2366 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2367 /*delegate=*/mock_delegate,
2368 /*rendering_api=*/mock_delegate.settings_.enable_impeller
2369 ? flutter::IOSRenderingAPI::kMetal
2370 : flutter::IOSRenderingAPI::kSoftware,
2371 /*platform_views_controller=*/flutterPlatformViewsController,
2372 /*task_runners=*/runners,
2373 /*worker_task_runner=*/nil,
2374 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2375
2376 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2377 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2378 flutterPlatformViewsController->RegisterViewFactory(
2379 factory, @"MockFlutterPlatformView",
2380 FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
2381 FlutterResult result = ^(id result) {
2382 };
2383 flutterPlatformViewsController->OnMethodCall(
2384 [FlutterMethodCall
2385 methodCallWithMethodName:@"create"
2386 arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2387 result);
2388
2389 XCTAssertNotNil(gMockPlatformView);
2390
2391 // Create embedded view params
2392 flutter::MutatorsStack stack;
2393 SkMatrix finalMatrix;
2394
2395 auto embeddedViewParams_1 =
2396 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2397
2398 flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_1));
2399 flutterPlatformViewsController->CompositeEmbeddedView(2);
2400 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
2401 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
2402 nullptr, framebuffer_info,
2403 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return false; },
2404 /*frame_size=*/SkISize::Make(800, 600));
2405 XCTAssertFalse(
2406 flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
2407
2408 auto embeddedViewParams_2 =
2409 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2410 flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_2));
2411 flutterPlatformViewsController->CompositeEmbeddedView(2);
2412 auto mock_surface_submit_true = std::make_unique<flutter::SurfaceFrame>(
2413 nullptr, framebuffer_info,
2414 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
2415 /*frame_size=*/SkISize::Make(800, 600));
2416 XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr,
2417 std::move(mock_surface_submit_true)));
2418 }
2419
2420 - (void)
2421 testFlutterPlatformViewControllerResetDeallocsPlatformViewWhenRootViewsNotBindedToFlutterView {
2422 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2423 auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2424 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2425 /*platform=*/thread_task_runner,
2426 /*raster=*/thread_task_runner,
2427 /*ui=*/thread_task_runner,
2428 /*io=*/thread_task_runner);
2429 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2430 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2431 /*delegate=*/mock_delegate,
2432 /*rendering_api=*/mock_delegate.settings_.enable_impeller
2433 ? flutter::IOSRenderingAPI::kMetal
2434 : flutter::IOSRenderingAPI::kSoftware,
2435 /*platform_views_controller=*/flutterPlatformViewsController,
2436 /*task_runners=*/runners,
2437 /*worker_task_runner=*/nil,
2438 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2439
2440 UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
2441 flutterPlatformViewsController->SetFlutterView(mockFlutterView);
2442
2443 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2444 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2445 flutterPlatformViewsController->RegisterViewFactory(
2446 factory, @"MockFlutterPlatformView",
2447 FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
2448 FlutterResult result = ^(id result) {
2449 };
2450 // autorelease pool to trigger an autorelease for all the root_views_ and touch_interceptors_.
2451 @autoreleasepool {
2452 flutterPlatformViewsController->OnMethodCall(
2453 [FlutterMethodCall
2454 methodCallWithMethodName:@"create"
2455 arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2456 result);
2457
2458 flutter::MutatorsStack stack;
2459 SkMatrix finalMatrix;
2460 auto embeddedViewParams =
2461 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2462 flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
2463 flutterPlatformViewsController->CompositeEmbeddedView(2);
2464 // Not calling |flutterPlatformViewsController::SubmitFrame| so that the platform views are not
2465 // added to flutter_view_.
2466
2467 XCTAssertNotNil(gMockPlatformView);
2468 flutterPlatformViewsController->Reset();
2469 }
2470 XCTAssertNil(gMockPlatformView);
2471 }
2472
2473 - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder {
2474 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2475 auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2476 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2477 /*platform=*/thread_task_runner,
2478 /*raster=*/thread_task_runner,
2479 /*ui=*/thread_task_runner,
2480 /*io=*/thread_task_runner);
2481 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2482 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2483 /*delegate=*/mock_delegate,
2484 /*rendering_api=*/mock_delegate.settings_.enable_impeller
2485 ? flutter::IOSRenderingAPI::kMetal
2486 : flutter::IOSRenderingAPI::kSoftware,
2487 /*platform_views_controller=*/flutterPlatformViewsController,
2488 /*task_runners=*/runners,
2489 /*worker_task_runner=*/nil,
2490 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2491
2492 UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
2493 flutterPlatformViewsController->SetFlutterView(mockFlutterView);
2494
2495 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2496 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2497 flutterPlatformViewsController->RegisterViewFactory(
2498 factory, @"MockFlutterPlatformView",
2499 FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
2500 FlutterResult result = ^(id result) {
2501 };
2502
2503 flutterPlatformViewsController->OnMethodCall(
2504 [FlutterMethodCall
2505 methodCallWithMethodName:@"create"
2506 arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}],
2507 result);
2508
2509 // First frame, |EmbeddedViewCount| is not empty after composite.
2510 flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
2511 flutter::MutatorsStack stack;
2512 SkMatrix finalMatrix;
2513 auto embeddedViewParams1 =
2514 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2515 flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
2516 flutterPlatformViewsController->CompositeEmbeddedView(0);
2517 XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL);
2518
2519 // Second frame, |EmbeddedViewCount| should be empty at the start
2520 flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
2521 XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 0UL);
2522
2523 auto embeddedViewParams2 =
2524 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2525 flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams2));
2526 flutterPlatformViewsController->CompositeEmbeddedView(0);
2527 XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL);
2528 }
2529
2530 - (void)
2531 testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithDifferentViewHierarchy {
2532 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2533 auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2534 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2535 /*platform=*/thread_task_runner,
2536 /*raster=*/thread_task_runner,
2537 /*ui=*/thread_task_runner,
2538 /*io=*/thread_task_runner);
2539 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2540 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2541 /*delegate=*/mock_delegate,
2542 /*rendering_api=*/mock_delegate.settings_.enable_impeller
2543 ? flutter::IOSRenderingAPI::kMetal
2544 : flutter::IOSRenderingAPI::kSoftware,
2545 /*platform_views_controller=*/flutterPlatformViewsController,
2546 /*task_runners=*/runners,
2547 /*worker_task_runner=*/nil,
2548 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2549
2550 UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
2551 flutterPlatformViewsController->SetFlutterView(mockFlutterView);
2552
2553 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2554 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2555 flutterPlatformViewsController->RegisterViewFactory(
2556 factory, @"MockFlutterPlatformView",
2557 FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
2558 FlutterResult result = ^(id result) {
2559 };
2560 flutterPlatformViewsController->OnMethodCall(
2561 [FlutterMethodCall
2562 methodCallWithMethodName:@"create"
2563 arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}],
2564 result);
2565 UIView* view1 = gMockPlatformView;
2566
2567 // This overwrites `gMockPlatformView` to another view.
2568 flutterPlatformViewsController->OnMethodCall(
2569 [FlutterMethodCall
2570 methodCallWithMethodName:@"create"
2571 arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
2572 result);
2573 UIView* view2 = gMockPlatformView;
2574
2575 flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
2576 flutter::MutatorsStack stack;
2577 SkMatrix finalMatrix;
2578 auto embeddedViewParams1 =
2579 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2580 flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
2581 flutterPlatformViewsController->CompositeEmbeddedView(0);
2582 auto embeddedViewParams2 =
2583 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(500, 500), stack);
2584 flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2));
2585 flutterPlatformViewsController->CompositeEmbeddedView(1);
2586
2587 // SKSurface is required if the root FlutterView is present.
2588 const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
2589 sk_sp<SkSurface> mock_sk_surface = SkSurfaces::Raster(image_info);
2590 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
2591 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
2592 std::move(mock_sk_surface), framebuffer_info,
2593 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
2594 /*frame_size=*/SkISize::Make(800, 600));
2595
2596 XCTAssertTrue(
2597 flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
2598 // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view.
2599 UIView* clippingView1 = view1.superview.superview;
2600 UIView* clippingView2 = view2.superview.superview;
2601 UIView* flutterView = clippingView1.superview;
2602 XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
2603 [flutterView.subviews indexOfObject:clippingView2],
2604 @"The first clipping view should be added before the second clipping view.");
2605
2606 // Need to recreate these params since they are `std::move`ed.
2607 flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
2608 // Process the second frame in the opposite order.
2609 embeddedViewParams2 =
2610 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(500, 500), stack);
2611 flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2));
2612 flutterPlatformViewsController->CompositeEmbeddedView(1);
2613 embeddedViewParams1 =
2614 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2615 flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
2616 flutterPlatformViewsController->CompositeEmbeddedView(0);
2617
2618 mock_sk_surface = SkSurfaces::Raster(image_info);
2619 mock_surface = std::make_unique<flutter::SurfaceFrame>(
2620 std::move(mock_sk_surface), framebuffer_info,
2621 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
2622 /*frame_size=*/SkISize::Make(800, 600));
2623 XCTAssertTrue(
2624 flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
2625 XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] >
2626 [flutterView.subviews indexOfObject:clippingView2],
2627 @"The first clipping view should be added after the second clipping view.");
2628 }
2629
2630 - (void)
2631 testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithSameViewHierarchy {
2632 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2633 auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2634 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2635 /*platform=*/thread_task_runner,
2636 /*raster=*/thread_task_runner,
2637 /*ui=*/thread_task_runner,
2638 /*io=*/thread_task_runner);
2639 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2640 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2641 /*delegate=*/mock_delegate,
2642 /*rendering_api=*/mock_delegate.settings_.enable_impeller
2643 ? flutter::IOSRenderingAPI::kMetal
2644 : flutter::IOSRenderingAPI::kSoftware,
2645 /*platform_views_controller=*/flutterPlatformViewsController,
2646 /*task_runners=*/runners,
2647 /*worker_task_runner=*/nil,
2648 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2649
2650 UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
2651 flutterPlatformViewsController->SetFlutterView(mockFlutterView);
2652
2653 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2654 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2655 flutterPlatformViewsController->RegisterViewFactory(
2656 factory, @"MockFlutterPlatformView",
2657 FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
2658 FlutterResult result = ^(id result) {
2659 };
2660 flutterPlatformViewsController->OnMethodCall(
2661 [FlutterMethodCall
2662 methodCallWithMethodName:@"create"
2663 arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}],
2664 result);
2665 UIView* view1 = gMockPlatformView;
2666
2667 // This overwrites `gMockPlatformView` to another view.
2668 flutterPlatformViewsController->OnMethodCall(
2669 [FlutterMethodCall
2670 methodCallWithMethodName:@"create"
2671 arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
2672 result);
2673 UIView* view2 = gMockPlatformView;
2674
2675 flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
2676 flutter::MutatorsStack stack;
2677 SkMatrix finalMatrix;
2678 auto embeddedViewParams1 =
2679 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2680 flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
2681 flutterPlatformViewsController->CompositeEmbeddedView(0);
2682 auto embeddedViewParams2 =
2683 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(500, 500), stack);
2684 flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2));
2685 flutterPlatformViewsController->CompositeEmbeddedView(1);
2686
2687 // SKSurface is required if the root FlutterView is present.
2688 const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
2689 sk_sp<SkSurface> mock_sk_surface = SkSurfaces::Raster(image_info);
2690 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
2691 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
2692 std::move(mock_sk_surface), framebuffer_info,
2693 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
2694 /*frame_size=*/SkISize::Make(800, 600));
2695
2696 XCTAssertTrue(
2697 flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
2698 // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view.
2699 UIView* clippingView1 = view1.superview.superview;
2700 UIView* clippingView2 = view2.superview.superview;
2701 UIView* flutterView = clippingView1.superview;
2702 XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
2703 [flutterView.subviews indexOfObject:clippingView2],
2704 @"The first clipping view should be added before the second clipping view.");
2705
2706 // Need to recreate these params since they are `std::move`ed.
2707 flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
2708 // Process the second frame in the same order.
2709 embeddedViewParams1 =
2710 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2711 flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
2712 flutterPlatformViewsController->CompositeEmbeddedView(0);
2713 embeddedViewParams2 =
2714 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(500, 500), stack);
2715 flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2));
2716 flutterPlatformViewsController->CompositeEmbeddedView(1);
2717
2718 mock_sk_surface = SkSurfaces::Raster(image_info);
2719 mock_surface = std::make_unique<flutter::SurfaceFrame>(
2720 std::move(mock_sk_surface), framebuffer_info,
2721 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
2722 /*frame_size=*/SkISize::Make(800, 600));
2723 XCTAssertTrue(
2724 flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
2725 XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
2726 [flutterView.subviews indexOfObject:clippingView2],
2727 @"The first clipping view should be added before the second clipping view.");
2728 }
2729
2730 - (void)testThreadMergeAtEndFrame {
2731 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2732 auto thread_task_runner_platform = CreateNewThread("FlutterPlatformViewsTest1");
2733 auto thread_task_runner_other = CreateNewThread("FlutterPlatformViewsTest2");
2734 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2735 /*platform=*/thread_task_runner_platform,
2736 /*raster=*/thread_task_runner_other,
2737 /*ui=*/thread_task_runner_other,
2738 /*io=*/thread_task_runner_other);
2739 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2740 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2741 /*delegate=*/mock_delegate,
2742 /*rendering_api=*/mock_delegate.settings_.enable_impeller
2743 ? flutter::IOSRenderingAPI::kMetal
2744 : flutter::IOSRenderingAPI::kSoftware,
2745 /*platform_views_controller=*/flutterPlatformViewsController,
2746 /*task_runners=*/runners,
2747 /*worker_task_runner=*/nil,
2748 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2749
2750 UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
2751 flutterPlatformViewsController->SetFlutterView(mockFlutterView);
2752
2753 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2754 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2755 flutterPlatformViewsController->RegisterViewFactory(
2756 factory, @"MockFlutterPlatformView",
2757 FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
2758 XCTestExpectation* waitForPlatformView =
2759 [self expectationWithDescription:@"wait for platform view to be created"];
2760 FlutterResult result = ^(id result) {
2761 [waitForPlatformView fulfill];
2762 };
2763
2764 flutterPlatformViewsController->OnMethodCall(
2765 [FlutterMethodCall
2766 methodCallWithMethodName:@"create"
2767 arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2768 result);
2769 [self waitForExpectations:@[ waitForPlatformView ] timeout:30];
2770 XCTAssertNotNil(gMockPlatformView);
2771
2772 flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
2773 SkMatrix finalMatrix;
2774 flutter::MutatorsStack stack;
2775 auto embeddedViewParams =
2776 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
2777 flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
2778
2779 fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger =
2780 fml::MakeRefCounted<fml::RasterThreadMerger>(thread_task_runner_platform->GetTaskQueueId(),
2781 thread_task_runner_other->GetTaskQueueId());
2782 XCTAssertEqual(flutterPlatformViewsController->PostPrerollAction(raster_thread_merger),
2783 flutter::PostPrerollResult::kSkipAndRetryFrame);
2784 XCTAssertFalse(raster_thread_merger->IsMerged());
2785
2786 flutterPlatformViewsController->EndFrame(true, raster_thread_merger);
2787 XCTAssertTrue(raster_thread_merger->IsMerged());
2788
2789 // Unmerge threads before the end of the test
2790 // TaskRunners are required to be unmerged before destruction.
2791 while (raster_thread_merger->DecrementLease() != fml::RasterThreadStatus::kUnmergedNow) {
2792 }
2793 }
2794
2795 - (int)alphaOfPoint:(CGPoint)point onView:(UIView*)view {
2796 unsigned char pixel[4] = {0};
2797
2798 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
2799
2800 // Draw the pixel on `point` in the context.
2801 CGContextRef context = CGBitmapContextCreate(
2802 pixel, 1, 1, 8, 4, colorSpace, kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedLast);
2803 CGContextTranslateCTM(context, -point.x, -point.y);
2804 [view.layer renderInContext:context];
2805
2806 CGContextRelease(context);
2807 CGColorSpaceRelease(colorSpace);
2808 // Get the alpha from the pixel that we just rendered.
2809 return pixel[3];
2810 }
2811
2812 - (void)testHasFirstResponderInViewHierarchySubtree_viewItselfBecomesFirstResponder {
2813 // For view to become the first responder, it must be a descendant of a UIWindow
2814 UIWindow* window = [[UIWindow alloc] init];
2815 UITextField* textField = [[UITextField alloc] init];
2816 [window addSubview:textField];
2817
2818 [textField becomeFirstResponder];
2819 XCTAssertTrue(textField.isFirstResponder);
2820 XCTAssertTrue(textField.flt_hasFirstResponderInViewHierarchySubtree);
2821 [textField resignFirstResponder];
2822 XCTAssertFalse(textField.isFirstResponder);
2823 XCTAssertFalse(textField.flt_hasFirstResponderInViewHierarchySubtree);
2824 }
2825
2826 - (void)testHasFirstResponderInViewHierarchySubtree_descendantViewBecomesFirstResponder {
2827 // For view to become the first responder, it must be a descendant of a UIWindow
2828 UIWindow* window = [[UIWindow alloc] init];
2829 UIView* view = [[UIView alloc] init];
2830 UIView* childView = [[UIView alloc] init];
2831 UITextField* textField = [[UITextField alloc] init];
2832 [window addSubview:view];
2833 [view addSubview:childView];
2834 [childView addSubview:textField];
2835
2836 [textField becomeFirstResponder];
2837 XCTAssertTrue(textField.isFirstResponder);
2838 XCTAssertTrue(view.flt_hasFirstResponderInViewHierarchySubtree);
2839 [textField resignFirstResponder];
2840 XCTAssertFalse(textField.isFirstResponder);
2841 XCTAssertFalse(view.flt_hasFirstResponderInViewHierarchySubtree);
2842 }
2843
2844 - (void)testFlutterClippingMaskViewPoolReuseViewsAfterRecycle {
2845 FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2];
2846 FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero];
2847 FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero];
2848 [pool insertViewToPoolIfNeeded:view1];
2849 [pool insertViewToPoolIfNeeded:view2];
2850 CGRect newRect = CGRectMake(0, 0, 10, 10);
2851 FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:newRect];
2852 FlutterClippingMaskView* view4 = [pool getMaskViewWithFrame:newRect];
2853 // view3 and view4 should randomly get either of view1 and view2.
2854 NSSet* set1 = [NSSet setWithObjects:view1, view2, nil];
2855 NSSet* set2 = [NSSet setWithObjects:view3, view4, nil];
2856 XCTAssertEqualObjects(set1, set2);
2857 XCTAssertTrue(CGRectEqualToRect(view3.frame, newRect));
2858 XCTAssertTrue(CGRectEqualToRect(view4.frame, newRect));
2859 }
2860
2861 - (void)testFlutterClippingMaskViewPoolAllocsNewMaskViewsAfterReachingCapacity {
2862 FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2];
2863 FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero];
2864 FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero];
2865 FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:CGRectZero];
2866 XCTAssertNotEqual(view1, view3);
2867 XCTAssertNotEqual(view2, view3);
2868 }
2869
2870 - (void)testMaskViewsReleasedWhenPoolIsReleased {
2871 __weak UIView* weakView;
2872 @autoreleasepool {
2873 FlutterClippingMaskViewPool* pool = [[FlutterClippingMaskViewPool alloc] initWithCapacity:2];
2874 FlutterClippingMaskView* view = [pool getMaskViewWithFrame:CGRectZero];
2875 weakView = view;
2876 XCTAssertNotNil(weakView);
2877 }
2878 XCTAssertNil(weakView);
2879 }
2880
2881 - (void)testClipMaskViewIsReused {
2882 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2883 auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2884 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2885 /*platform=*/thread_task_runner,
2886 /*raster=*/thread_task_runner,
2887 /*ui=*/thread_task_runner,
2888 /*io=*/thread_task_runner);
2889 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2890 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2891 /*delegate=*/mock_delegate,
2892 /*rendering_api=*/mock_delegate.settings_.enable_impeller
2893 ? flutter::IOSRenderingAPI::kMetal
2894 : flutter::IOSRenderingAPI::kSoftware,
2895 /*platform_views_controller=*/flutterPlatformViewsController,
2896 /*task_runners=*/runners,
2897 /*worker_task_runner=*/nil,
2898 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2899
2900 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2901 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2902 flutterPlatformViewsController->RegisterViewFactory(
2903 factory, @"MockFlutterPlatformView",
2904 FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
2905 FlutterResult result = ^(id result) {
2906 };
2907 flutterPlatformViewsController->OnMethodCall(
2908 [FlutterMethodCall
2909 methodCallWithMethodName:@"create"
2910 arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
2911 result);
2912
2913 XCTAssertNotNil(gMockPlatformView);
2914 UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
2915 flutterPlatformViewsController->SetFlutterView(mockFlutterView);
2916 // Create embedded view params
2917 flutter::MutatorsStack stack1;
2918 // Layer tree always pushes a screen scale factor to the stack
2919 SkMatrix screenScaleMatrix =
2920 SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
2921 stack1.PushTransform(screenScaleMatrix);
2922 // Push a clip rect
2923 SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3);
2924 stack1.PushClipRect(rect);
2925
2926 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
2927 screenScaleMatrix, SkSize::Make(10, 10), stack1);
2928
2929 flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1));
2930 flutterPlatformViewsController->CompositeEmbeddedView(1);
2931 UIView* childClippingView1 = gMockPlatformView.superview.superview;
2932 UIView* maskView1 = childClippingView1.maskView;
2933 XCTAssertNotNil(maskView1);
2934
2935 // Composite a new frame.
2936 flutterPlatformViewsController->BeginFrame(SkISize::Make(100, 100));
2937 flutter::MutatorsStack stack2;
2938 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
2939 screenScaleMatrix, SkSize::Make(10, 10), stack2);
2940 auto embeddedViewParams3 = std::make_unique<flutter::EmbeddedViewParams>(
2941 screenScaleMatrix, SkSize::Make(10, 10), stack2);
2942 flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams3));
2943 flutterPlatformViewsController->CompositeEmbeddedView(1);
2944 childClippingView1 = gMockPlatformView.superview.superview;
2945
2946 // This overrides gMockPlatformView to point to the newly created platform view.
2947 flutterPlatformViewsController->OnMethodCall(
2948 [FlutterMethodCall
2949 methodCallWithMethodName:@"create"
2950 arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2951 result);
2952
2953 auto embeddedViewParams4 = std::make_unique<flutter::EmbeddedViewParams>(
2954 screenScaleMatrix, SkSize::Make(10, 10), stack1);
2955 flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams4));
2956 flutterPlatformViewsController->CompositeEmbeddedView(2);
2957 UIView* childClippingView2 = gMockPlatformView.superview.superview;
2958
2959 UIView* maskView2 = childClippingView2.maskView;
2960 XCTAssertEqual(maskView1, maskView2);
2961 XCTAssertNotNil(childClippingView2.maskView);
2962 XCTAssertNil(childClippingView1.maskView);
2963 }
2964
2965 - (void)testDifferentClipMaskViewIsUsedForEachView {
2966 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2967 auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2968 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2969 /*platform=*/thread_task_runner,
2970 /*raster=*/thread_task_runner,
2971 /*ui=*/thread_task_runner,
2972 /*io=*/thread_task_runner);
2973 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2974 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2975 /*delegate=*/mock_delegate,
2976 /*rendering_api=*/mock_delegate.settings_.enable_impeller
2977 ? flutter::IOSRenderingAPI::kMetal
2978 : flutter::IOSRenderingAPI::kSoftware,
2979 /*platform_views_controller=*/flutterPlatformViewsController,
2980 /*task_runners=*/runners,
2981 /*worker_task_runner=*/nil,
2982 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
2983
2984 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2985 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
2986 flutterPlatformViewsController->RegisterViewFactory(
2987 factory, @"MockFlutterPlatformView",
2988 FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
2989 FlutterResult result = ^(id result) {
2990 };
2991
2992 flutterPlatformViewsController->OnMethodCall(
2993 [FlutterMethodCall
2994 methodCallWithMethodName:@"create"
2995 arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
2996 result);
2997 UIView* view1 = gMockPlatformView;
2998
2999 // This overwrites `gMockPlatformView` to another view.
3000 flutterPlatformViewsController->OnMethodCall(
3001 [FlutterMethodCall
3002 methodCallWithMethodName:@"create"
3003 arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
3004 result);
3005 UIView* view2 = gMockPlatformView;
3006
3007 XCTAssertNotNil(gMockPlatformView);
3008 UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
3009 flutterPlatformViewsController->SetFlutterView(mockFlutterView);
3010 // Create embedded view params
3011 flutter::MutatorsStack stack1;
3012 // Layer tree always pushes a screen scale factor to the stack
3013 SkMatrix screenScaleMatrix =
3014 SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
3015 stack1.PushTransform(screenScaleMatrix);
3016 // Push a clip rect
3017 SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3);
3018 stack1.PushClipRect(rect);
3019
3020 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
3021 screenScaleMatrix, SkSize::Make(10, 10), stack1);
3022
3023 flutter::MutatorsStack stack2;
3024 stack2.PushClipRect(rect);
3025 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
3026 screenScaleMatrix, SkSize::Make(10, 10), stack2);
3027
3028 flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1));
3029 flutterPlatformViewsController->CompositeEmbeddedView(1);
3030 UIView* childClippingView1 = view1.superview.superview;
3031
3032 flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2));
3033 flutterPlatformViewsController->CompositeEmbeddedView(2);
3034 UIView* childClippingView2 = view2.superview.superview;
3035 UIView* maskView1 = childClippingView1.maskView;
3036 UIView* maskView2 = childClippingView2.maskView;
3037 XCTAssertNotEqual(maskView1, maskView2);
3038 }
3039
3040 - (void)testMaskViewUsesCAShapeLayerAsTheBackingLayer {
3041 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3042 auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
3043 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3044 /*platform=*/thread_task_runner,
3045 /*raster=*/thread_task_runner,
3046 /*ui=*/thread_task_runner,
3047 /*io=*/thread_task_runner);
3048 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
3049 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3050 /*delegate=*/mock_delegate,
3051 /*rendering_api=*/mock_delegate.settings_.enable_impeller
3052 ? flutter::IOSRenderingAPI::kMetal
3053 : flutter::IOSRenderingAPI::kSoftware,
3054 /*platform_views_controller=*/flutterPlatformViewsController,
3055 /*task_runners=*/runners,
3056 /*worker_task_runner=*/nil,
3057 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3058
3059 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3060 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3061 flutterPlatformViewsController->RegisterViewFactory(
3062 factory, @"MockFlutterPlatformView",
3063 FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
3064 FlutterResult result = ^(id result) {
3065 };
3066
3067 flutterPlatformViewsController->OnMethodCall(
3068 [FlutterMethodCall
3069 methodCallWithMethodName:@"create"
3070 arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
3071 result);
3072
3073 XCTAssertNotNil(gMockPlatformView);
3074 UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
3075 flutterPlatformViewsController->SetFlutterView(mockFlutterView);
3076 // Create embedded view params
3077 flutter::MutatorsStack stack1;
3078 // Layer tree always pushes a screen scale factor to the stack
3079 SkMatrix screenScaleMatrix =
3080 SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
3081 stack1.PushTransform(screenScaleMatrix);
3082 // Push a clip rect
3083 SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3);
3084 stack1.PushClipRect(rect);
3085
3086 auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
3087 screenScaleMatrix, SkSize::Make(10, 10), stack1);
3088
3089 flutter::MutatorsStack stack2;
3090 stack2.PushClipRect(rect);
3091 auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
3092 screenScaleMatrix, SkSize::Make(10, 10), stack2);
3093
3094 flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1));
3095 flutterPlatformViewsController->CompositeEmbeddedView(1);
3096 UIView* childClippingView = gMockPlatformView.superview.superview;
3097
3098 UIView* maskView = childClippingView.maskView;
3099 XCTAssert([maskView.layer isKindOfClass:[CAShapeLayer class]],
3100 @"Mask view must use CAShapeLayer as its backing layer.");
3101 }
3102
3103 // Return true if a correct visual effect view is found. It also implies all the validation in this
3104 // method passes.
3105 //
3106 // There are two fail states for this method. 1. One of the XCTAssert method failed; or 2. No
3107 // correct visual effect view found.
3108 - (BOOL)validateOneVisualEffectView:(UIView*)visualEffectView
3109 expectedFrame:(CGRect)frame
3110 inputRadius:(CGFloat)inputRadius {
3111 XCTAssertTrue(CGRectEqualToRect(visualEffectView.frame, frame));
3112 for (UIView* view in visualEffectView.subviews) {
3113 if (![NSStringFromClass([view class]) hasSuffix:@"BackdropView"]) {
3114 continue;
3115 }
3116 XCTAssertEqual(view.layer.filters.count, 1u);
3117 NSObject* filter = view.layer.filters.firstObject;
3118
3119 XCTAssertEqualObjects([filter valueForKey:@"name"], @"gaussianBlur");
3120
3121 NSObject* inputRadiusInFilter = [filter valueForKey:@"inputRadius"];
3122 XCTAssertTrue([inputRadiusInFilter isKindOfClass:[NSNumber class]] &&
3123 flutter::BlurRadiusEqualToBlurRadius(((NSNumber*)inputRadiusInFilter).floatValue,
3124 inputRadius));
3125 return YES;
3126 }
3127 return NO;
3128 }
3129
3130 - (void)testDisposingViewInCompositionOrderDoNotCrash {
3131 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3132 auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
3133 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3134 /*platform=*/thread_task_runner,
3135 /*raster=*/thread_task_runner,
3136 /*ui=*/thread_task_runner,
3137 /*io=*/thread_task_runner);
3138 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
3139 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3140 /*delegate=*/mock_delegate,
3141 /*rendering_api=*/mock_delegate.settings_.enable_impeller
3142 ? flutter::IOSRenderingAPI::kMetal
3143 : flutter::IOSRenderingAPI::kSoftware,
3144 /*platform_views_controller=*/flutterPlatformViewsController,
3145 /*task_runners=*/runners,
3146 /*worker_task_runner=*/nil,
3147 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3148
3149 UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3150 flutterPlatformViewsController->SetFlutterView(mockFlutterView);
3151
3152 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3153 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3154 flutterPlatformViewsController->RegisterViewFactory(
3155 factory, @"MockFlutterPlatformView",
3156 FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
3157 FlutterResult result = ^(id result) {
3158 };
3159
3160 flutterPlatformViewsController->OnMethodCall(
3161 [FlutterMethodCall
3162 methodCallWithMethodName:@"create"
3163 arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}],
3164 result);
3165 flutterPlatformViewsController->OnMethodCall(
3166 [FlutterMethodCall
3167 methodCallWithMethodName:@"create"
3168 arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
3169 result);
3170
3171 {
3172 // **** First frame, view id 0, 1 in the composition_order_, disposing view 0 is called. **** //
3173 // No view should be disposed, or removed from the composition order.
3174 flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
3175 flutter::MutatorsStack stack;
3176 SkMatrix finalMatrix;
3177 auto embeddedViewParams0 =
3178 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3179 flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams0));
3180 flutterPlatformViewsController->CompositeEmbeddedView(0);
3181
3182 auto embeddedViewParams1 =
3183 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3184 flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1));
3185 flutterPlatformViewsController->CompositeEmbeddedView(1);
3186 XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL);
3187
3188 XCTestExpectation* expectation = [self expectationWithDescription:@"dispose call ended."];
3189 FlutterResult disposeResult = ^(id result) {
3190 [expectation fulfill];
3191 };
3192
3193 flutterPlatformViewsController->OnMethodCall(
3194 [FlutterMethodCall methodCallWithMethodName:@"dispose" arguments:@0], disposeResult);
3195 [self waitForExpectationsWithTimeout:30 handler:nil];
3196
3197 const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
3198 sk_sp<SkSurface> mock_sk_surface = SkSurfaces::Raster(image_info);
3199 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3200 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3201 std::move(mock_sk_surface), framebuffer_info,
3202 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3203 /*frame_size=*/SkISize::Make(800, 600));
3204 XCTAssertTrue(
3205 flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
3206
3207 // Disposing won't remove embedded views until the view is removed from the composition_order_
3208 XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL);
3209 XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(0));
3210 XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(1));
3211 }
3212
3213 {
3214 // **** Second frame, view id 1 in the composition_order_, no disposing view is called, **** //
3215 // View 0 is removed from the composition order in this frame, hence also disposed.
3216 flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
3217 flutter::MutatorsStack stack;
3218 SkMatrix finalMatrix;
3219 auto embeddedViewParams1 =
3220 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3221 flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1));
3222 flutterPlatformViewsController->CompositeEmbeddedView(1);
3223
3224 const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
3225 sk_sp<SkSurface> mock_sk_surface = SkSurfaces::Raster(image_info);
3226 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3227 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3228 std::move(mock_sk_surface), framebuffer_info,
3229 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3230 /*frame_size=*/SkISize::Make(800, 600));
3231 XCTAssertTrue(
3232 flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
3233
3234 // Disposing won't remove embedded views until the view is removed from the composition_order_
3235 XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL);
3236 XCTAssertNil(flutterPlatformViewsController->GetPlatformViewByID(0));
3237 XCTAssertNotNil(flutterPlatformViewsController->GetPlatformViewByID(1));
3238 }
3239 }
3240 - (void)testOnlyPlatformViewsAreRemovedWhenReset {
3241 flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
3242 auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
3243 flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
3244 /*platform=*/thread_task_runner,
3245 /*raster=*/thread_task_runner,
3246 /*ui=*/thread_task_runner,
3247 /*io=*/thread_task_runner);
3248 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
3249 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
3250 /*delegate=*/mock_delegate,
3251 /*rendering_api=*/mock_delegate.settings_.enable_impeller
3252 ? flutter::IOSRenderingAPI::kMetal
3253 : flutter::IOSRenderingAPI::kSoftware,
3254 /*platform_views_controller=*/flutterPlatformViewsController,
3255 /*task_runners=*/runners,
3256 /*worker_task_runner=*/nil,
3257 /*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
3258
3259 FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
3260 [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init];
3261 flutterPlatformViewsController->RegisterViewFactory(
3262 factory, @"MockFlutterPlatformView",
3263 FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
3264 FlutterResult result = ^(id result) {
3265 };
3266 flutterPlatformViewsController->OnMethodCall(
3267 [FlutterMethodCall
3268 methodCallWithMethodName:@"create"
3269 arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
3270 result);
3271 UIView* mockFlutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
3272 flutterPlatformViewsController->SetFlutterView(mockFlutterView);
3273 // Create embedded view params
3274 flutter::MutatorsStack stack;
3275 // Layer tree always pushes a screen scale factor to the stack
3276 SkMatrix screenScaleMatrix =
3277 SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
3278 stack.PushTransform(screenScaleMatrix);
3279 // Push a translate matrix
3280 SkMatrix translateMatrix = SkMatrix::Translate(100, 100);
3281 stack.PushTransform(translateMatrix);
3282 SkMatrix finalMatrix;
3283 finalMatrix.setConcat(screenScaleMatrix, translateMatrix);
3284
3285 auto embeddedViewParams =
3286 std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
3287
3288 flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams));
3289 flutterPlatformViewsController->CompositeEmbeddedView(2);
3290
3291 // SKSurface is required if the root FlutterView is present.
3292 const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
3293 sk_sp<SkSurface> mock_sk_surface = SkSurfaces::Raster(image_info);
3294 flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
3295 auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
3296 std::move(mock_sk_surface), framebuffer_info,
3297 [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
3298 /*frame_size=*/SkISize::Make(800, 600));
3299
3300 flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface));
3301
3302 UIView* someView = [[UIView alloc] init];
3303 [mockFlutterView addSubview:someView];
3304
3305 flutterPlatformViewsController->Reset();
3306 XCTAssertEqual(mockFlutterView.subviews.count, 1u);
3307 XCTAssertEqual(mockFlutterView.subviews.firstObject, someView);
3308 }
3309
3310 - (void)testFlutterTouchInterceptingViewLinksToAccessibilityContainer {
3311 FlutterTouchInterceptingView* touchInteceptorView = [[FlutterTouchInterceptingView alloc] init];
3312 NSObject* container = [[NSObject alloc] init];
3313 [touchInteceptorView setFlutterAccessibilityContainer:container];
3314 XCTAssertEqualObjects([touchInteceptorView accessibilityContainer], container);
3315 }
3316
3317 @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:986
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