Flutter Engine
The Flutter Engine
FlutterMutatorViewTest.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 "flutter/shell/platform/darwin/macos/framework/Source/FlutterMutatorView.h"
7#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h"
8
9#include "third_party/googletest/googletest/include/gtest/gtest.h"
10
11#import "flutter/shell/platform/darwin/macos/framework/Source/NSView+ClipsToBounds.h"
12
14
15@property(readonly, nonatomic, nonnull) NSMutableArray<NSView*>* pathClipViews;
16@property(readonly, nonatomic, nullable) NSView* platformViewContainer;
17
18@end
19
20static constexpr float kMaxErr = 1e-10;
21
22namespace {
23void ApplyFlutterLayer(FlutterMutatorView* view,
25 const std::vector<FlutterPlatformViewMutation>& mutations) {
26 flutter::PlatformViewLayer layer(0, // identifier
27 mutations,
28 // Offset is ignored by mutator view, the bounding rect is
29 // determined by width and transform.
30 FlutterPoint{0, 0}, // offset
31 size);
32
33 [view applyFlutterLayer:&layer];
34}
35
36// Expect that each element within two CATransform3Ds is within an error bound.
37//
38// In order to avoid architecture-specific floating point differences we don't check for exact
39// equality using, for example, CATransform3DEqualToTransform.
40void ExpectTransform3DEqual(const CATransform3D& t, const CATransform3D& u) {
41 EXPECT_NEAR(t.m11, u.m11, kMaxErr);
42 EXPECT_NEAR(t.m12, u.m12, kMaxErr);
43 EXPECT_NEAR(t.m13, u.m13, kMaxErr);
44 EXPECT_NEAR(t.m14, u.m14, kMaxErr);
45
46 EXPECT_NEAR(t.m21, u.m21, kMaxErr);
47 EXPECT_NEAR(t.m22, u.m22, kMaxErr);
48 EXPECT_NEAR(t.m23, u.m23, kMaxErr);
49 EXPECT_NEAR(t.m24, u.m24, kMaxErr);
50
51 EXPECT_NEAR(t.m31, u.m31, kMaxErr);
52 EXPECT_NEAR(t.m32, u.m32, kMaxErr);
53 EXPECT_NEAR(t.m33, u.m33, kMaxErr);
54 EXPECT_NEAR(t.m34, u.m34, kMaxErr);
55
56 EXPECT_NEAR(t.m41, u.m41, kMaxErr);
57 EXPECT_NEAR(t.m42, u.m42, kMaxErr);
58 EXPECT_NEAR(t.m43, u.m43, kMaxErr);
59 EXPECT_NEAR(t.m44, u.m44, kMaxErr);
60}
61} // namespace
62
63TEST(FlutterMutatorViewTest, BasicFrameIsCorrect) {
64 NSView* platformView = [[NSView alloc] init];
65 FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
66
67 EXPECT_EQ(mutatorView.platformView, platformView);
68
69 std::vector<FlutterPlatformViewMutation> mutations{
70 {
72 .transformation =
74 .scaleX = 1,
75 .transX = 100,
76 .scaleY = 1,
77 .transY = 50,
78 },
79 },
80 };
81
82 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
83
84 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(100, 50, 30, 20)));
85 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
86 EXPECT_EQ(mutatorView.pathClipViews.count, 0ull);
87 EXPECT_NE(mutatorView.platformViewContainer, nil);
88}
89
90TEST(FlutterMutatorViewTest, ClipsToBounds) {
91 NSView* platformView = [[NSView alloc] init];
92 FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
93 EXPECT_TRUE(mutatorView.clipsToBounds);
94}
95
96TEST(FlutterMutatorViewTest, TransformedFrameIsCorrect) {
97 NSView* platformView = [[NSView alloc] init];
98 FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
99 NSView* mutatorViewParent = [[NSView alloc] init];
100 mutatorViewParent.wantsLayer = YES;
101 mutatorViewParent.layer.contentsScale = 2.0;
102 [mutatorViewParent addSubview:mutatorView];
103
104 std::vector<FlutterPlatformViewMutation> mutations{
105 {
107 .transformation =
109 .scaleX = 2,
110 .scaleY = 2,
111 },
112 },
113 {
115 .transformation =
117 .scaleX = 1,
118 .transX = 100,
119 .scaleY = 1,
120 .transY = 50,
121 },
122 },
123 {
125 .transformation =
127 .scaleX = 1.5,
128 .transX = -7.5,
129 .scaleY = 1.5,
130 .transY = -5,
131 },
132 },
133 };
134
135 // PlatformView size form engine comes in physical pixels
136 ApplyFlutterLayer(mutatorView, FlutterSize{30 * 2, 20 * 2}, mutations);
137 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(92.5, 45, 45, 30)));
138 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
139
140 ExpectTransform3DEqual(mutatorView.platformViewContainer.layer.sublayerTransform,
141 CATransform3DMakeScale(1.5, 1.5, 1));
142}
143
144TEST(FlutterMutatorViewTest, FrameWithLooseClipIsCorrect) {
145 NSView* platformView = [[NSView alloc] init];
146 FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
147
148 EXPECT_EQ(mutatorView.platformView, platformView);
149
150 std::vector<FlutterPlatformViewMutation> mutations{
151 {
153 .clip_rect = FlutterRect{80, 40, 200, 100},
154 },
155 {
157 .transformation =
159 .scaleX = 1,
160 .transX = 100,
161 .scaleY = 1,
162 .transY = 50,
163 },
164 },
165 };
166
167 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
168
169 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(100, 50, 30, 20)));
170 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
171}
172
173TEST(FlutterMutatorViewTest, FrameWithTightClipIsCorrect) {
174 NSView* platformView = [[NSView alloc] init];
175 FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
176
177 EXPECT_EQ(mutatorView.platformView, platformView);
178
179 std::vector<FlutterPlatformViewMutation> mutations{
180 {
182 .clip_rect = FlutterRect{80, 40, 200, 100},
183 },
184 {
186 .clip_rect = FlutterRect{110, 55, 120, 65},
187 },
188 {
190 .transformation =
192 .scaleX = 1,
193 .transX = 100,
194 .scaleY = 1,
195 .transY = 50,
196 },
197 },
198 };
199
200 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
201
202 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 55, 10, 10)));
204 CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, -5, 30, 20)));
205 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
206}
207
208TEST(FlutterMutatorViewTest, FrameWithTightClipAndTransformIsCorrect) {
209 NSView* platformView = [[NSView alloc] init];
210 FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
211 NSView* mutatorViewParent = [[NSView alloc] init];
212 mutatorViewParent.wantsLayer = YES;
213 mutatorViewParent.layer.contentsScale = 2.0;
214 [mutatorViewParent addSubview:mutatorView];
215
216 std::vector<FlutterPlatformViewMutation> mutations{
217 {
219 .transformation =
221 .scaleX = 2,
222 .scaleY = 2,
223 },
224 },
225 {
227 .clip_rect = FlutterRect{80, 40, 200, 100},
228 },
229 {
231 .clip_rect = FlutterRect{110, 55, 120, 65},
232 },
233 {
235 .transformation =
237 .scaleX = 1,
238 .transX = 100,
239 .scaleY = 1,
240 .transY = 50,
241 },
242 },
243 {
245 .transformation =
247 .scaleX = 1.5,
248 .transX = -7.5,
249 .scaleY = 1.5,
250 .transY = -5,
251 },
252 },
253 };
254
255 ApplyFlutterLayer(mutatorView, FlutterSize{30 * 2, 20 * 2}, mutations);
256
257 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 55, 10, 10)));
259 CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-17.5, -10, 45, 30)));
260 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
261}
262
263// Rounded rectangle without hitting the corner
264TEST(FlutterMutatorViewTest, RoundRectClipsToSimpleRectangle) {
265 NSView* platformView = [[NSView alloc] init];
266 FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
267
268 std::vector<FlutterPlatformViewMutation> mutations{
269 {
271 .clip_rounded_rect =
273 .rect = FlutterRect{110, 30, 120, 90},
274 .upper_left_corner_radius = FlutterSize{10, 10},
275 .upper_right_corner_radius = FlutterSize{10, 10},
276 .lower_right_corner_radius = FlutterSize{10, 10},
277 .lower_left_corner_radius = FlutterSize{10, 10},
278 },
279 },
280 {
282 .transformation =
284 .scaleX = 1,
285 .transX = 100,
286 .scaleY = 1,
287 .transY = 50,
288 },
289 },
290 };
291
292 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
293
294 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 50, 10, 20)));
296 CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, 0, 30, 20)));
297 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
298 EXPECT_EQ(mutatorView.pathClipViews.count, 0ul);
299}
300
301// Ensure that the mutator view, clip views, and container all use a flipped y axis. The transforms
302// sent from the framework assume this, and so aside from the consistency with every other embedder,
303// we can avoid a lot of extra math.
304TEST(FlutterMutatorViewTest, ViewsSetIsFlipped) {
305 NSView* platformView = [[NSView alloc] init];
306 FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
307
308 std::vector<FlutterPlatformViewMutation> mutations{
309 {
311 .clip_rounded_rect =
313 .rect = FlutterRect{110, 60, 150, 150},
314 .upper_left_corner_radius = FlutterSize{10, 10},
315 .upper_right_corner_radius = FlutterSize{10, 10},
316 .lower_right_corner_radius = FlutterSize{10, 10},
317 .lower_left_corner_radius = FlutterSize{10, 10},
318 },
319 },
320 {
322 .transformation =
324 .scaleX = 1,
325 .transX = 100,
326 .scaleY = 1,
327 .transY = 50,
328 },
329 },
330 };
331
332 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
333
334 EXPECT_TRUE(mutatorView.isFlipped);
335 ASSERT_EQ(mutatorView.pathClipViews.count, 1ul);
336 EXPECT_TRUE(mutatorView.pathClipViews.firstObject.isFlipped);
337 EXPECT_TRUE(mutatorView.platformViewContainer.isFlipped);
338}
339
340TEST(FlutterMutatorViewTest, RectsClipsToPathWhenRotated) {
341 NSView* platformView = [[NSView alloc] init];
342 FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
343 std::vector<FlutterPlatformViewMutation> mutations{
344 {
346 // Roation M_PI / 8
347 .transformation =
349 .scaleX = 0.9238795325112867,
350 .skewX = -0.3826834323650898,
351 .skewY = 0.3826834323650898,
352 .scaleY = 0.9238795325112867,
353 },
354 },
355 {
357 .clip_rect = FlutterRect{110, 60, 150, 150},
358 },
359 {
361 .transformation =
363 .scaleX = 1,
364 .transX = 100,
365 .scaleY = 1,
366 .transY = 50,
367 },
368 },
369 };
370 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
371 EXPECT_EQ(mutatorView.pathClipViews.count, 1ul);
372 EXPECT_NEAR(mutatorView.platformViewContainer.frame.size.width, 35.370054622640396, kMaxErr);
373 EXPECT_NEAR(mutatorView.platformViewContainer.frame.size.height, 29.958093621178421, kMaxErr);
374}
375
376TEST(FlutterMutatorViewTest, RoundRectClipsToPath) {
377 NSView* platformView = [[NSView alloc] init];
378 FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
379
380 std::vector<FlutterPlatformViewMutation> mutations{
381 {
383 .clip_rounded_rect =
385 .rect = FlutterRect{110, 60, 150, 150},
386 .upper_left_corner_radius = FlutterSize{10, 10},
387 .upper_right_corner_radius = FlutterSize{10, 10},
388 .lower_right_corner_radius = FlutterSize{10, 10},
389 .lower_left_corner_radius = FlutterSize{10, 10},
390 },
391 },
392 {
394 .transformation =
396 .scaleX = 1,
397 .transX = 100,
398 .scaleY = 1,
399 .transY = 50,
400 },
401 },
402 };
403
404 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
405
406 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 60, 20, 10)));
408 CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, -10, 30, 20)));
409 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
410 EXPECT_EQ(mutatorView.pathClipViews.count, 1ul);
411 ExpectTransform3DEqual(mutatorView.pathClipViews.firstObject.layer.mask.transform,
412 CATransform3DMakeTranslation(-100, -50, 0));
413}
414
415TEST(FlutterMutatorViewTest, PathClipViewsAreAddedAndRemoved) {
416 NSView* platformView = [[NSView alloc] init];
417 FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
418
419 std::vector<FlutterPlatformViewMutation> mutations{
420 {
422 .transformation =
424 .scaleX = 1,
425 .transX = 100,
426 .scaleY = 1,
427 .transY = 50,
428 },
429 },
430 };
431
432 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
433
434 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(100, 50, 30, 20)));
435 EXPECT_EQ(mutatorView.pathClipViews.count, 0ull);
436
437 std::vector<FlutterPlatformViewMutation> mutations2{
438 {
440 .clip_rounded_rect =
442 .rect = FlutterRect{110, 60, 150, 150},
443 .upper_left_corner_radius = FlutterSize{10, 10},
444 .upper_right_corner_radius = FlutterSize{10, 10},
445 .lower_right_corner_radius = FlutterSize{10, 10},
446 .lower_left_corner_radius = FlutterSize{10, 10},
447 },
448 },
449 {
451 .transformation =
453 .scaleX = 1,
454 .transX = 100,
455 .scaleY = 1,
456 .transY = 50,
457 },
458 },
459 };
460
461 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations2);
462
463 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 60, 20, 10)));
465 CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, -10, 30, 20)));
466 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
467 EXPECT_EQ(mutatorView.pathClipViews.count, 1ul);
468
469 EXPECT_EQ(platformView.superview, mutatorView.platformViewContainer);
470 EXPECT_EQ(mutatorView.platformViewContainer.superview, mutatorView.pathClipViews[0]);
471 EXPECT_EQ(mutatorView.pathClipViews[0].superview, mutatorView);
472
473 std::vector<FlutterPlatformViewMutation> mutations3{
474 {
476 .clip_rounded_rect =
478 .rect = FlutterRect{110, 55, 150, 150},
479 .upper_left_corner_radius = FlutterSize{10, 10},
480 .upper_right_corner_radius = FlutterSize{10, 10},
481 .lower_right_corner_radius = FlutterSize{10, 10},
482 .lower_left_corner_radius = FlutterSize{10, 10},
483 },
484 },
485 {
487 .clip_rounded_rect =
489 .rect = FlutterRect{30, 30, 120, 65},
490 .upper_left_corner_radius = FlutterSize{10, 10},
491 .upper_right_corner_radius = FlutterSize{10, 10},
492 .lower_right_corner_radius = FlutterSize{10, 10},
493 .lower_left_corner_radius = FlutterSize{10, 10},
494 },
495 },
496 {
498 .transformation =
500 .scaleX = 1,
501 .transX = 100,
502 .scaleY = 1,
503 .transY = 50,
504 },
505 },
506 };
507
508 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations3);
509
510 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 55, 10, 10)));
512 CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, -5, 30, 20)));
513 EXPECT_EQ(mutatorView.pathClipViews.count, 2ul);
514
515 EXPECT_EQ(platformView.superview, mutatorView.platformViewContainer);
516 EXPECT_EQ(mutatorView.platformViewContainer.superview, mutatorView.pathClipViews[1]);
517 EXPECT_EQ(mutatorView.pathClipViews[1].superview, mutatorView.pathClipViews[0]);
518 EXPECT_EQ(mutatorView.pathClipViews[0].superview, mutatorView);
519
520 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations2);
521
522 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(110, 60, 20, 10)));
524 CGRectEqualToRect(mutatorView.subviews.firstObject.frame, CGRectMake(-10, -10, 30, 20)));
525 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 30, 20)));
526 EXPECT_EQ(mutatorView.pathClipViews.count, 1ul);
527
528 EXPECT_EQ(platformView.superview, mutatorView.platformViewContainer);
529 EXPECT_EQ(mutatorView.platformViewContainer.superview, mutatorView.pathClipViews[0]);
530 EXPECT_EQ(mutatorView.pathClipViews[0].superview, mutatorView);
531
532 ApplyFlutterLayer(mutatorView, FlutterSize{30, 20}, mutations);
533
534 EXPECT_TRUE(CGRectEqualToRect(mutatorView.frame, CGRectMake(100, 50, 30, 20)));
535 EXPECT_EQ(mutatorView.pathClipViews.count, 0ull);
536}
537
538TEST(FlutterMutatorViewTest, HitTestIgnoreRegion) {
539 NSView* platformView = [[NSView alloc] init];
540 FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
541 ApplyFlutterLayer(mutatorView, FlutterSize{100, 100}, {});
542 EXPECT_EQ([mutatorView hitTest:NSMakePoint(10, 10)], platformView);
543 EXPECT_EQ([mutatorView hitTest:NSMakePoint(50, 10)], platformView);
544
545 [mutatorView resetHitTestRegion];
546 [mutatorView addHitTestIgnoreRegion:CGRectMake(0, 0, 50, 50)];
547 [mutatorView addHitTestIgnoreRegion:CGRectMake(50, 50, 50, 50)];
548
549 EXPECT_EQ([mutatorView hitTest:NSMakePoint(10, 10)], nil);
550 EXPECT_EQ([mutatorView hitTest:NSMakePoint(49, 10)], nil);
551 EXPECT_EQ([mutatorView hitTest:NSMakePoint(10, 49)], nil);
552 EXPECT_EQ([mutatorView hitTest:NSMakePoint(50, 50)], nil);
553 EXPECT_EQ([mutatorView hitTest:NSMakePoint(50, 10)], platformView);
554 EXPECT_EQ([mutatorView hitTest:NSMakePoint(10, 50)], platformView);
555
556 [mutatorView resetHitTestRegion];
557 EXPECT_EQ([mutatorView hitTest:NSMakePoint(10, 10)], platformView);
558 EXPECT_EQ([mutatorView hitTest:NSMakePoint(49, 10)], platformView);
559 EXPECT_EQ([mutatorView hitTest:NSMakePoint(10, 49)], platformView);
560 EXPECT_EQ([mutatorView hitTest:NSMakePoint(50, 50)], platformView);
561 EXPECT_EQ([mutatorView hitTest:NSMakePoint(50, 10)], platformView);
562 EXPECT_EQ([mutatorView hitTest:NSMakePoint(10, 50)], platformView);
563}
564
565TEST(FlutterMutatorViewTest, ReparentingPlatformView) {
566 NSView* platformView = [[NSView alloc] init];
567 FlutterMutatorView* mutatorView = [[FlutterMutatorView alloc] initWithPlatformView:platformView];
568 ApplyFlutterLayer(mutatorView, FlutterSize{100, 100}, {});
569 EXPECT_TRUE(platformView.superview == mutatorView.platformViewContainer);
570 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(0, 0, 100, 100)));
571
572 // Reparent platform view and replace it with placeholder (mimicking WKWebKit going full screen)
573 NSView* newParent = [[NSView alloc] init];
574 [newParent addSubview:platformView];
575 platformView.frame = CGRectMake(10, 10, 200, 200);
576
577 NSView* placeholderView = [[NSView alloc] init];
578 [mutatorView.platformViewContainer addSubview:placeholderView];
579 ApplyFlutterLayer(mutatorView, FlutterSize{100, 100}, {});
580
581 // Platform view should not be touched but the replacement view should be properly positioned.
582 EXPECT_TRUE(platformView.superview == newParent);
583 EXPECT_TRUE(CGRectEqualToRect(platformView.frame, CGRectMake(10, 10, 200, 200)));
584 EXPECT_TRUE(CGRectEqualToRect(placeholderView.frame, CGRectMake(0, 0, 100, 100)));
585}
586
587@interface FlutterCursorCoordinatorTest : NSObject
588
589@end
590
591@implementation FlutterCursorCoordinatorTest
593 id flutterView = OCMClassMock([FlutterView class]);
594 FlutterCursorCoordinator* coordinator =
595 [[FlutterCursorCoordinator alloc] initWithFlutterView:flutterView];
596 {
597 id platformView = OCMClassMock([NSView class]);
598 OCMStub([flutterView cursorUpdate:[OCMArg any]]);
599 id mutatorView = OCMStrictClassMock([FlutterMutatorView class]);
600 OCMStub([mutatorView platformView]).andReturn(platformView);
601 CGPoint location = NSMakePoint(50, 50);
602 OCMStub([mutatorView convertPoint:location fromView:[OCMArg any]]).andReturn(location);
603 NSEvent* event = [NSEvent mouseEventWithType:NSEventTypeMouseMoved
604 location:location
605 modifierFlags:0
606 timestamp:0
607 windowNumber:0
608 context:nil
609 eventNumber:0
610 clickCount:0
611 pressure:0];
612 [coordinator processMouseMoveEvent:event
613 forMutatorView:mutatorView
614 overlayRegion:{CGRectMake(0, 0, 100, 100)}];
615 OCMVerify([flutterView cursorUpdate:event]);
616 }
617 {
618 id platformView = OCMClassMock([NSView class]);
619 // Make sure once event is handled the coordinator will not send cursorUpdate again.
620 OCMReject([flutterView cursorUpdate:[OCMArg any]]);
621 id mutatorView = OCMStrictClassMock([FlutterMutatorView class]);
622 OCMStub([mutatorView platformView]).andReturn(platformView);
623 CGPoint location = NSMakePoint(50, 50);
624 OCMStub([mutatorView convertPoint:location fromView:[OCMArg any]]).andReturn(location);
625 NSEvent* event = [NSEvent mouseEventWithType:NSEventTypeMouseMoved
626 location:location
627 modifierFlags:0
628 timestamp:0
629 windowNumber:0
630 context:nil
631 eventNumber:0
632 clickCount:0
633 pressure:0];
634 [coordinator processMouseMoveEvent:event
635 forMutatorView:mutatorView
636 overlayRegion:{CGRectMake(0, 0, 100, 100)}];
637 }
638 EXPECT_TRUE(coordinator.cleanupScheduled);
639 [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
640 EXPECT_FALSE(coordinator.cleanupScheduled);
641}
642
644 id flutterView = OCMClassMock([FlutterView class]);
645 OCMReject([flutterView cursorUpdate:[OCMArg any]]);
646 FlutterCursorCoordinator* coordinator =
647 [[FlutterCursorCoordinator alloc] initWithFlutterView:flutterView];
648 id platformViewWindow = OCMClassMock([NSWindow class]);
649 {
650 id platformView = OCMClassMock([NSView class]);
651 OCMStub([platformViewWindow invalidateCursorRectsForView:platformView]);
652 OCMStub([platformView window]).andReturn(platformViewWindow);
653 OCMStub([flutterView cursorUpdate:[OCMArg any]]);
654 id mutatorView = OCMStrictClassMock([FlutterMutatorView class]);
655 OCMStub([mutatorView platformView]).andReturn(platformView);
656 CGPoint location = NSMakePoint(150, 150);
657 OCMStub([mutatorView convertPoint:location fromView:[OCMArg any]]).andReturn(location);
658 NSEvent* event = [NSEvent mouseEventWithType:NSEventTypeMouseMoved
659 location:location
660 modifierFlags:0
661 timestamp:0
662 windowNumber:0
663 context:nil
664 eventNumber:0
665 clickCount:0
666 pressure:0];
667 [coordinator processMouseMoveEvent:event
668 forMutatorView:mutatorView
669 overlayRegion:{CGRectMake(0, 0, 100, 100)}];
670 OCMVerify([platformViewWindow invalidateCursorRectsForView:platformView]);
671 }
672 {
673 // Make sure this is not called again for subsequent invocation during same run loop turn.
674 OCMReject([platformViewWindow invalidateCursorRectsForView:[OCMArg any]]);
675
676 id platformView = OCMClassMock([NSView class]);
677 OCMStub([platformViewWindow invalidateCursorRectsForView:platformView]);
678 OCMStub([platformView window]).andReturn(platformViewWindow);
679 OCMStub([flutterView cursorUpdate:[OCMArg any]]);
680 id mutatorView = OCMStrictClassMock([FlutterMutatorView class]);
681 OCMStub([mutatorView platformView]).andReturn(platformView);
682 CGPoint location = NSMakePoint(150, 150);
683 OCMStub([mutatorView convertPoint:location fromView:[OCMArg any]]).andReturn(location);
684 NSEvent* event = [NSEvent mouseEventWithType:NSEventTypeMouseMoved
685 location:location
686 modifierFlags:0
687 timestamp:0
688 windowNumber:0
689 context:nil
690 eventNumber:0
691 clickCount:0
692 pressure:0];
693 [coordinator processMouseMoveEvent:event
694 forMutatorView:mutatorView
695 overlayRegion:{CGRectMake(0, 0, 100, 100)}];
696 }
697 EXPECT_TRUE(coordinator.cleanupScheduled);
698 [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
699 EXPECT_FALSE(coordinator.cleanupScheduled);
700
701 // Check that invalidateCursorRectsForView is called again
702 platformViewWindow = OCMClassMock([NSWindow class]);
703 {
704 id platformView = OCMClassMock([NSView class]);
705 OCMStub([platformViewWindow invalidateCursorRectsForView:platformView]);
706 OCMStub([platformView window]).andReturn(platformViewWindow);
707 OCMStub([flutterView cursorUpdate:[OCMArg any]]);
708 id mutatorView = OCMStrictClassMock([FlutterMutatorView class]);
709 OCMStub([mutatorView platformView]).andReturn(platformView);
710 CGPoint location = NSMakePoint(150, 150);
711 OCMStub([mutatorView convertPoint:location fromView:[OCMArg any]]).andReturn(location);
712 NSEvent* event = [NSEvent mouseEventWithType:NSEventTypeMouseMoved
713 location:location
714 modifierFlags:0
715 timestamp:0
716 windowNumber:0
717 context:nil
718 eventNumber:0
719 clickCount:0
720 pressure:0];
721 [coordinator processMouseMoveEvent:event
722 forMutatorView:mutatorView
723 overlayRegion:{CGRectMake(0, 0, 100, 100)}];
724 OCMVerify([platformViewWindow invalidateCursorRectsForView:platformView]);
725 }
726 [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
727}
728@end
729
730TEST(FlutterMutatorViewTest, CursorCoordinator) {
731 [[[FlutterCursorCoordinatorTest alloc] init] testCoordinatorEventWithinFlutterContent];
732 [[[FlutterCursorCoordinatorTest alloc] init] testCoordinatorEventOutsideFlutterContent];
733}
static constexpr float kMaxErr
TEST(FlutterMutatorViewTest, BasicFrameIsCorrect)
Represents a platform view layer, including all mutations.
@ kFlutterPlatformViewMutationTypeClipRoundedRect
Definition: embedder.h:1697
@ kFlutterPlatformViewMutationTypeClipRect
Definition: embedder.h:1694
@ kFlutterPlatformViewMutationTypeTransformation
Definition: embedder.h:1700
GLFWwindow * window
Definition: main.cc:45
FlKeyEvent * event
void processMouseMoveEvent:forMutatorView:overlayRegion:(NSEvent *event, [forMutatorView] FlutterMutatorView *view, [overlayRegion] const std::vector< CGRect > &region)
NSMutableArray * pathClipViews()
NSView * platformView
Returns wrapped platform view.
void resetHitTestRegion()
Resets hit hit testing region for this mutator view.
void applyFlutterLayer:(nonnull const flutter::PlatformViewLayer *layer)
CGFloat contentsScale()
Returns the scale factor to translate logical pixels to physical pixels for this view.
void addHitTestIgnoreRegion:(CGRect region)
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
SIT bool any(const Vec< 1, T > &x)
Definition: SkVx.h:530
A structure to represent a 2D point.
Definition: embedder.h:445
A structure to represent a rectangle.
Definition: embedder.h:437
A structure to represent a rounded rectangle.
Definition: embedder.h:451
FlutterRect rect
Definition: embedder.h:452
A structure to represent the width and height.
Definition: embedder.h:423
double scaleX
horizontal scale factor
Definition: embedder.h:279
#define EXPECT_TRUE(handle)
Definition: unit_test.h:678