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