| Flutter iOS Embedder |

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