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