Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
flutter_window_unittests.cc
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.
11
12#include "gmock/gmock.h"
13#include "gtest/gtest.h"
14
15namespace flutter {
16namespace testing {
17
18using ::testing::_;
19using ::testing::AnyNumber;
20using ::testing::Eq;
21using ::testing::Invoke;
22using ::testing::Return;
23
24namespace {
25static constexpr int32_t kDefaultPointerDeviceId = 0;
26
27class MockFlutterWindow : public FlutterWindow {
28 public:
29 explicit MockFlutterWindow(bool reset_view_on_exit = true)
30 : reset_view_on_exit_(reset_view_on_exit) {
31 ON_CALL(*this, GetDpiScale())
32 .WillByDefault(Return(this->FlutterWindow::GetDpiScale()));
33 }
34
35 // Used for injecting a proc_table to override calls to the windows API
36 MockFlutterWindow(int width,
37 int height,
38 std::shared_ptr<WindowsProcTable> proc_table = nullptr)
39 : FlutterWindow(width, height, nullptr, std::move(proc_table)) {}
40
41 virtual ~MockFlutterWindow() {
42 if (reset_view_on_exit_) {
43 SetView(nullptr);
44 }
45 }
46
47 // Wrapper for GetCurrentDPI() which is a protected method.
48 UINT GetDpi() { return GetCurrentDPI(); }
49 // Simulates a WindowProc message from the OS.
50 LRESULT InjectWindowMessage(UINT const message,
51 WPARAM const wparam,
52 LPARAM const lparam) {
53 return HandleMessage(message, wparam, lparam);
54 }
55
56 MOCK_METHOD(void, OnDpiScale, (unsigned int), (override));
57 MOCK_METHOD(void, OnResize, (unsigned int, unsigned int), (override));
58 MOCK_METHOD(float, GetScrollOffsetMultiplier, (), (override));
59 MOCK_METHOD(float, GetDpiScale, (), (override));
60 MOCK_METHOD(void, UpdateCursorRect, (const Rect&), (override));
61 MOCK_METHOD(void, OnResetImeComposing, (), (override));
62 MOCK_METHOD(UINT, Win32DispatchMessage, (UINT, WPARAM, LPARAM), (override));
63 MOCK_METHOD(BOOL, Win32PeekMessage, (LPMSG, UINT, UINT, UINT), (override));
64 MOCK_METHOD(uint32_t, Win32MapVkToChar, (uint32_t), (override));
65 MOCK_METHOD(HWND, GetWindowHandle, (), (override));
67 GetAxFragmentRootDelegate,
68 (),
69 (override));
70 MOCK_METHOD(void, OnWindowStateEvent, (WindowStateEvent), (override));
71
72 protected:
73 // |KeyboardManager::WindowDelegate|
74 LRESULT Win32DefWindowProc(HWND hWnd,
75 UINT Msg,
76 WPARAM wParam,
77 LPARAM lParam) override {
78 return kWmResultDefault;
79 }
80
81 private:
82 bool reset_view_on_exit_;
83 FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindow);
84};
85
86LRESULT InjectPointerMessageWithClientPoint(MockFlutterWindow& window,
88 WPARAM wparam,
89 int x,
90 int y) {
91 HWND flutter_window = window.FlutterWindow::GetWindowHandle();
92 HWND parent_window =
93 CreateWindowEx(0, L"STATIC", L"", WS_POPUP, 100, 100, 300, 300, nullptr,
94 nullptr, GetModuleHandle(nullptr), nullptr);
95 EXPECT_NE(parent_window, nullptr);
96 EXPECT_NE(SetParent(flutter_window, parent_window), nullptr);
97 EXPECT_TRUE(SetWindowPos(flutter_window, nullptr, 0, 0, 100, 100,
98 SWP_NOZORDER | SWP_NOACTIVATE));
99
100 POINT point = {x, y};
101 EXPECT_TRUE(ClientToScreen(flutter_window, &point));
102 LRESULT result =
103 window.InjectWindowMessage(message, wparam, MAKELPARAM(point.x, point.y));
104
105 EXPECT_NE(SetParent(flutter_window, HWND_MESSAGE), nullptr);
106 DestroyWindow(parent_window);
107 return result;
108}
109
110class MockFlutterWindowsView : public FlutterWindowsView {
111 public:
112 MockFlutterWindowsView(FlutterWindowsEngine* engine,
113 std::unique_ptr<WindowBindingHandler> window_binding)
115 engine,
116 std::move(window_binding),
117 false,
118 BoxConstraints()) {}
120
121 MOCK_METHOD(void,
122 NotifyWinEventWrapper,
123 (ui::AXPlatformNodeWin*, ax::mojom::Event),
124 (override));
125
126 private:
127 FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView);
128};
129
130class FlutterWindowTest : public WindowsTest {};
131
132} // namespace
133
134TEST_F(FlutterWindowTest, CreateDestroy) {
135 std::unique_ptr<FlutterWindowsEngine> engine =
136 FlutterWindowsEngineBuilder{GetContext()}.Build();
137 FlutterWindow window(800, 600, engine->display_manager());
138 ASSERT_TRUE(TRUE);
139}
140
141TEST_F(FlutterWindowTest, OnBitmapSurfaceUpdated) {
142 std::unique_ptr<FlutterWindowsEngine> engine =
143 FlutterWindowsEngineBuilder{GetContext()}.Build();
144 FlutterWindow win32window(100, 100, engine->display_manager());
145 int old_handle_count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
146
147 constexpr size_t row_bytes = 100 * 4;
148 constexpr size_t height = 100;
149 std::array<char, row_bytes * height> allocation;
150 win32window.OnBitmapSurfaceUpdated(allocation.data(), row_bytes, height);
151
152 int new_handle_count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
153 // Check GDI resources leak
154 EXPECT_EQ(old_handle_count, new_handle_count);
155}
156
157// Tests that composing rect updates are transformed from Flutter logical
158// coordinates to device coordinates and passed to the text input manager
159// when the DPI scale is 100% (96 DPI).
160TEST_F(FlutterWindowTest, OnCursorRectUpdatedRegularDPI) {
161 MockFlutterWindow win32window;
162 EXPECT_CALL(win32window, GetDpiScale()).WillOnce(Return(1.0));
163
164 Rect cursor_rect(Point(10, 20), Size(30, 40));
165 EXPECT_CALL(win32window, UpdateCursorRect(cursor_rect)).Times(1);
166
167 win32window.OnCursorRectUpdated(cursor_rect);
168}
169
170// Tests that composing rect updates are transformed from Flutter logical
171// coordinates to device coordinates and passed to the text input manager
172// when the DPI scale is 150% (144 DPI).
173TEST_F(FlutterWindowTest, OnCursorRectUpdatedHighDPI) {
174 MockFlutterWindow win32window;
175 EXPECT_CALL(win32window, GetDpiScale()).WillOnce(Return(1.5));
176
177 Rect expected_cursor_rect(Point(15, 30), Size(45, 60));
178 EXPECT_CALL(win32window, UpdateCursorRect(expected_cursor_rect)).Times(1);
179
180 Rect cursor_rect(Point(10, 20), Size(30, 40));
181 win32window.OnCursorRectUpdated(cursor_rect);
182}
183
184TEST_F(FlutterWindowTest, OnPointerStarSendsDeviceType) {
185 std::unique_ptr<FlutterWindowsEngine> engine =
186 FlutterWindowsEngineBuilder{GetContext()}.Build();
187 FlutterWindow win32window(100, 100, engine->display_manager());
189 EXPECT_CALL(delegate, OnWindowStateEvent).Times(AnyNumber());
190 win32window.SetView(&delegate);
191
192 // Move
193 EXPECT_CALL(delegate,
194 OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
195 kDefaultPointerDeviceId, 0, 0, 0))
196 .Times(1);
197 EXPECT_CALL(delegate,
198 OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
199 kDefaultPointerDeviceId, 0, 0, 0))
200 .Times(1);
201 EXPECT_CALL(delegate,
202 OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
203 kDefaultPointerDeviceId, 0, 0, 0))
204 .Times(1);
205
206 // Down
207 EXPECT_CALL(delegate,
208 OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
209 kDefaultPointerDeviceId,
211 .Times(1);
212 EXPECT_CALL(delegate,
213 OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
214 kDefaultPointerDeviceId,
216 .Times(1);
217 EXPECT_CALL(delegate,
218 OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
219 kDefaultPointerDeviceId,
221 .Times(1);
222
223 // Up
224 EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
225 kDefaultPointerDeviceId,
227 .Times(1);
228 EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
229 kDefaultPointerDeviceId,
231 .Times(1);
232 EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,
233 kDefaultPointerDeviceId,
235 .Times(1);
236
237 // Leave
238 EXPECT_CALL(delegate,
239 OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindMouse,
240 kDefaultPointerDeviceId))
241 .Times(1);
242 EXPECT_CALL(delegate,
243 OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindTouch,
244 kDefaultPointerDeviceId))
245 .Times(1);
246 EXPECT_CALL(delegate,
247 OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindStylus,
248 kDefaultPointerDeviceId))
249 .Times(1);
250
251 win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
252 kDefaultPointerDeviceId, 0, 0, 0);
253 win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
254 kDefaultPointerDeviceId, WM_LBUTTONDOWN, 0, 0);
255 win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
256 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
257 win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindMouse,
258 kDefaultPointerDeviceId);
259
260 // Touch
261 win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
262 kDefaultPointerDeviceId, 0, 0, 0);
263 win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
264 kDefaultPointerDeviceId, WM_LBUTTONDOWN, 0, 0);
265 win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
266 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
267 win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindTouch,
268 kDefaultPointerDeviceId);
269
270 // Pen
271 win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
272 kDefaultPointerDeviceId, 0, 0, 0);
273 win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
274 kDefaultPointerDeviceId, WM_LBUTTONDOWN, 0, 0);
275 win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,
276 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
277 win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindStylus,
278 kDefaultPointerDeviceId);
279
280 // Destruction of win32window sends a HIDE update. In situ, the window is
281 // owned by the delegate, and so is destructed first. Not so here.
282 win32window.SetView(nullptr);
283}
284
285TEST_F(FlutterWindowTest, OnStylusPointerDown) {
286 auto mock_proc_table = std::make_shared<MockWindowsProcTable>();
287
288 // Set up expectations for the mock
289 EXPECT_CALL(*mock_proc_table, GetPointerInfo(_, _))
290 .WillRepeatedly([](UINT32 pointer_id, POINTER_INFO* pointer_info) {
291 if (pointer_info != nullptr) {
292 pointer_info->pointerType = PT_PEN;
293 pointer_info->pointerId = pointer_id;
294 pointer_info->pointerFlags = POINTER_FLAG_INCONTACT;
295 }
296 return TRUE;
297 });
298
299 EXPECT_CALL(*mock_proc_table, GetPointerPenInfo(_, _))
300 .WillRepeatedly([](UINT32 pointer_id, POINTER_PEN_INFO* pen_info) {
301 if (pen_info != nullptr) {
302 pen_info->pressure = 720; // Non-zero pressure for contact events
303 pen_info->rotation = 0;
304 }
305 return TRUE;
306 });
307
308 POINTER_INFO test_pointer_info = {};
309 BOOL result = mock_proc_table->GetPointerInfo(1, &test_pointer_info);
310
311 MockFlutterWindow win32window(100, 100, mock_proc_table);
313
314 win32window.SetView(&delegate);
315
316 EXPECT_CALL(delegate,
317 OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
318 kDefaultPointerDeviceId,
320 .Times(1);
321
322 UINT32 pointerId = 1;
323 WPARAM wparam = static_cast<WPARAM>(pointerId);
324 InjectPointerMessageWithClientPoint(win32window, WM_POINTERDOWN, wparam, 10,
325 10);
326}
327
328TEST_F(FlutterWindowTest, OnStylusPointerMove) {
329 auto mock_proc_table = std::make_shared<MockWindowsProcTable>();
330
331 EXPECT_CALL(*mock_proc_table, GetPointerInfo(_, _))
332 .WillRepeatedly([](UINT32 pointer_id, POINTER_INFO* pointer_info) {
333 if (pointer_info != nullptr) {
334 pointer_info->pointerType = PT_PEN;
335 pointer_info->pointerId = pointer_id;
336 pointer_info->pointerFlags =
337 POINTER_FLAG_INCONTACT | POINTER_FLAG_UPDATE;
338 }
339 return TRUE;
340 });
341
342 EXPECT_CALL(*mock_proc_table, GetPointerPenInfo(_, _))
343 .WillRepeatedly([](UINT32 pointer_id, POINTER_PEN_INFO* pen_info) {
344 if (pen_info != nullptr) {
345 pen_info->pressure = 720; // Non-zero pressure for contact events
346 pen_info->rotation = 10; // This is PRE-transformation to radians.
347 }
348 return TRUE;
349 });
350
351 MockFlutterWindow win32window(100, 100, mock_proc_table);
353 win32window.SetView(&delegate);
354
355 EXPECT_CALL(delegate, OnPointerMove(15, 20, kFlutterPointerDeviceKindStylus,
356 kDefaultPointerDeviceId, 10, 720, 0))
357 .Times(1);
358
359 UINT32 pointerId = 1;
360 WPARAM wparam = static_cast<WPARAM>(pointerId);
361 InjectPointerMessageWithClientPoint(win32window, WM_POINTERUPDATE, wparam, 15,
362 20);
363}
364
365TEST_F(FlutterWindowTest, OnStylusPointerUp) {
366 auto mock_proc_table = std::make_shared<MockWindowsProcTable>();
367
368 EXPECT_CALL(*mock_proc_table, GetPointerInfo(_, _))
369 .WillRepeatedly([](UINT32 pointer_id, POINTER_INFO* pointer_info) {
370 if (pointer_info != nullptr) {
371 pointer_info->pointerType = PT_PEN;
372 pointer_info->pointerId = pointer_id;
373 pointer_info->pointerFlags = POINTER_FLAG_UP;
374 }
375 return TRUE;
376 });
377
378 EXPECT_CALL(*mock_proc_table, GetPointerPenInfo(_, _))
379 .WillRepeatedly([](UINT32 pointer_id, POINTER_PEN_INFO* pen_info) {
380 if (pen_info != nullptr) {
381 pen_info->pressure = 720;
382 pen_info->rotation = 0;
383 }
384 return TRUE;
385 });
386
387 MockFlutterWindow win32window(100, 100, mock_proc_table);
389 win32window.SetView(&delegate);
390
391 EXPECT_CALL(delegate, OnPointerUp(25, 30, kFlutterPointerDeviceKindStylus,
392 kDefaultPointerDeviceId,
394 .Times(1);
395
396 UINT32 pointerId = 1;
397 WPARAM wparam = static_cast<WPARAM>(pointerId);
398 InjectPointerMessageWithClientPoint(win32window, WM_POINTERUP, wparam, 25,
399 30);
400}
401
402TEST_F(FlutterWindowTest, OnStylusPointerLeave) {
403 auto mock_proc_table = std::make_shared<MockWindowsProcTable>();
404
405 EXPECT_CALL(*mock_proc_table, GetPointerInfo(_, _))
406 .WillRepeatedly([](UINT32 pointer_id, POINTER_INFO* pointer_info) {
407 if (pointer_info != nullptr) {
408 pointer_info->pointerType = PT_PEN;
409 pointer_info->pointerId = pointer_id;
410 pointer_info->pointerFlags = POINTER_FLAG_UP;
411 }
412 return TRUE;
413 });
414
415 EXPECT_CALL(*mock_proc_table, GetPointerPenInfo(_, _))
416 .WillRepeatedly([](UINT32 pointer_id, POINTER_PEN_INFO* pen_info) {
417 if (pen_info != nullptr) {
418 pen_info->pressure = 720;
419 pen_info->rotation = 0;
420 }
421 return TRUE;
422 });
423
424 MockFlutterWindow win32window(100, 100, mock_proc_table);
426 win32window.SetView(&delegate);
427
428 EXPECT_CALL(delegate, OnPointerLeave(35, 40, kFlutterPointerDeviceKindStylus,
429 kDefaultPointerDeviceId))
430 .Times(1);
431
432 UINT32 pointerId = 1;
433 WPARAM wparam = static_cast<WPARAM>(pointerId);
434 InjectPointerMessageWithClientPoint(win32window, WM_POINTERLEAVE, wparam, 35,
435 40);
436}
437
438TEST_F(FlutterWindowTest, OnStylusPointerHover) {
439 // Test that WM_POINTERUPDATE without POINTER_FLAG_INCONTACT (hover) is
440 // handled
441 auto mock_proc_table = std::make_shared<MockWindowsProcTable>();
442
443 EXPECT_CALL(*mock_proc_table, GetPointerInfo(_, _))
444 .WillRepeatedly([](UINT32 pointer_id, POINTER_INFO* pointer_info) {
445 if (pointer_info != nullptr) {
446 pointer_info->pointerId = 1;
447 pointer_info->pointerType = PT_PEN;
448 // No POINTER_FLAG_INCONTACT - this simulates a hover event
449 pointer_info->pointerFlags = POINTER_FLAG_UPDATE;
450 pointer_info->ptPixelLocation.x = 45;
451 pointer_info->ptPixelLocation.y = 50;
452 }
453 return TRUE;
454 });
455
456 EXPECT_CALL(*mock_proc_table, GetPointerPenInfo(_, _))
457 .WillRepeatedly([](UINT32 pointer_id, POINTER_PEN_INFO* pen_info) {
458 if (pen_info != nullptr) {
459 pen_info->pressure = 0;
460 pen_info->rotation = 0;
461 }
462 return TRUE;
463 });
464
465 MockFlutterWindow win32window(100, 100, mock_proc_table);
467 win32window.SetView(&delegate);
468
469 // First establish the pointer with WM_POINTERDOWN
470 EXPECT_CALL(delegate,
471 OnPointerDown(45, 50, kFlutterPointerDeviceKindStylus, 0,
473 .Times(1);
474
475 UINT32 pointerId = 1;
476 WPARAM wparam = static_cast<WPARAM>(pointerId);
477 InjectPointerMessageWithClientPoint(win32window, WM_POINTERDOWN, wparam, 45,
478 50);
479
480 // Now expect OnPointerMove to be called for hover events
481 EXPECT_CALL(delegate, OnPointerMove(45, 50, kFlutterPointerDeviceKindStylus,
482 0, 0, 0, 0))
483 .Times(1);
484
485 // Inject WM_POINTERUPDATE message (hover event)
486 InjectPointerMessageWithClientPoint(win32window, WM_POINTERUPDATE, wparam, 45,
487 50);
488}
489
490TEST_F(FlutterWindowTest, OnMousePointerDown) {
491 auto mock_proc_table = std::make_shared<MockWindowsProcTable>();
492
493 EXPECT_CALL(*mock_proc_table, GetPointerInfo(_, _))
494 .WillRepeatedly([](UINT32 pointer_id, POINTER_INFO* pointer_info) {
495 if (pointer_info != nullptr) {
496 pointer_info->pointerType = PT_MOUSE;
497 pointer_info->pointerId = pointer_id;
498 pointer_info->pointerFlags = POINTER_FLAG_INCONTACT;
499 }
500 return TRUE;
501 });
502
503 MockFlutterWindow win32window(100, 100, mock_proc_table);
505 win32window.SetView(&delegate);
506
507 EXPECT_CALL(delegate, OnPointerDown(45, 50, kFlutterPointerDeviceKindMouse,
508 kDefaultPointerDeviceId,
510 .Times(1);
511
512 UINT32 pointerId = 1;
513 WPARAM wparam = static_cast<WPARAM>(pointerId);
514 InjectPointerMessageWithClientPoint(win32window, WM_POINTERDOWN, wparam, 45,
515 50);
516}
517
518TEST_F(FlutterWindowTest, OnTouchPointerDown) {
519 auto mock_proc_table = std::make_shared<MockWindowsProcTable>();
520
521 EXPECT_CALL(*mock_proc_table, GetPointerInfo(_, _))
522 .WillRepeatedly([](UINT32 pointer_id, POINTER_INFO* pointer_info) {
523 if (pointer_info != nullptr) {
524 pointer_info->pointerType = PT_TOUCH;
525 pointer_info->pointerId = pointer_id;
526 pointer_info->pointerFlags = POINTER_FLAG_INCONTACT;
527 }
528 return TRUE;
529 });
530
531 MockFlutterWindow win32window(100, 100, mock_proc_table);
533 win32window.SetView(&delegate);
534
535 EXPECT_CALL(delegate, OnPointerDown(55, 60, kFlutterPointerDeviceKindTouch,
536 kDefaultPointerDeviceId,
538 .Times(1);
539
540 UINT32 pointerId = 1;
541 WPARAM wparam = static_cast<WPARAM>(pointerId);
542 InjectPointerMessageWithClientPoint(win32window, WM_POINTERDOWN, wparam, 55,
543 60);
544}
545
546TEST_F(FlutterWindowTest, PointerMessageScreenCoordinatesAreConvertedToClient) {
547 constexpr int kClientX = 15;
548 constexpr int kClientY = 20;
549
550 auto mock_proc_table = std::make_shared<MockWindowsProcTable>();
551
552 EXPECT_CALL(*mock_proc_table, GetPointerInfo(_, _))
553 .WillRepeatedly([](UINT32 pointer_id, POINTER_INFO* pointer_info) {
554 if (pointer_info != nullptr) {
555 pointer_info->pointerType = PT_TOUCH;
556 pointer_info->pointerId = pointer_id;
557 pointer_info->pointerFlags = POINTER_FLAG_INCONTACT;
558 }
559 return TRUE;
560 });
561
562 MockFlutterWindow win32window(100, 100, mock_proc_table);
564 EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
565 EXPECT_CALL(delegate, OnWindowStateEvent).Times(AnyNumber());
566 win32window.SetView(&delegate);
567
568 HWND parent_window =
569 CreateWindowEx(0, L"STATIC", L"", WS_POPUP, 100, 100, 300, 300, nullptr,
570 nullptr, GetModuleHandle(nullptr), nullptr);
571 ASSERT_NE(parent_window, nullptr);
572
573 HWND flutter_window = win32window.FlutterWindow::GetWindowHandle();
574 ASSERT_NE(flutter_window, nullptr);
575 ASSERT_NE(SetParent(flutter_window, parent_window), nullptr);
576 ASSERT_TRUE(SetWindowPos(flutter_window, nullptr, 25, 35, 100, 100,
577 SWP_NOZORDER | SWP_NOACTIVATE));
578
579 POINT pointer_point = {kClientX, kClientY};
580 ASSERT_TRUE(ClientToScreen(flutter_window, &pointer_point));
581
582 EXPECT_CALL(delegate,
583 OnPointerDown(kClientX, kClientY, kFlutterPointerDeviceKindTouch,
584 kDefaultPointerDeviceId,
586 .Times(1);
587
588 win32window.InjectWindowMessage(WM_POINTERDOWN, /*wparam=*/1,
589 MAKELPARAM(pointer_point.x, pointer_point.y));
590
591 EXPECT_NE(SetParent(flutter_window, HWND_MESSAGE), nullptr);
592 DestroyWindow(parent_window);
593}
594
595// Tests that calls to OnScroll in turn calls GetScrollOffsetMultiplier
596// for mapping scroll ticks to pixels.
597TEST_F(FlutterWindowTest, OnScrollCallsGetScrollOffsetMultiplier) {
598 MockFlutterWindow win32window;
600 EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
601 win32window.SetView(&delegate);
602
603 EXPECT_CALL(win32window, GetWindowHandle).WillOnce([&win32window]() {
604 return win32window.FlutterWindow::GetWindowHandle();
605 });
606 EXPECT_CALL(win32window, GetScrollOffsetMultiplier).WillOnce(Return(120.0f));
607
608 EXPECT_CALL(delegate,
609 OnScroll(_, _, 0, 0, 120.0f, kFlutterPointerDeviceKindMouse,
610 kDefaultPointerDeviceId))
611 .Times(1);
612
613 win32window.OnScroll(0.0f, 0.0f, kFlutterPointerDeviceKindMouse,
614 kDefaultPointerDeviceId);
615}
616
617TEST_F(FlutterWindowTest, OnWindowRepaint) {
618 MockFlutterWindow win32window;
620 EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
621 win32window.SetView(&delegate);
622
623 EXPECT_CALL(delegate, OnWindowRepaint()).Times(1);
624
625 win32window.InjectWindowMessage(WM_PAINT, 0, 0);
626}
627
628TEST_F(FlutterWindowTest, OnThemeChange) {
629 MockFlutterWindow win32window;
631 EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
632 win32window.SetView(&delegate);
633
634 EXPECT_CALL(delegate, OnHighContrastChanged).Times(1);
635
636 win32window.InjectWindowMessage(WM_THEMECHANGED, 0, 0);
637}
638
639// The window should return no root accessibility node if
640// it isn't attached to a view.
641// Regression test for https://github.com/flutter/flutter/issues/129791
642TEST_F(FlutterWindowTest, AccessibilityNodeWithoutView) {
643 MockFlutterWindow win32window;
644
645 EXPECT_EQ(win32window.GetNativeViewAccessible(), nullptr);
646}
647
648// Ensure that announcing the alert propagates the message to the alert node.
649// Different screen readers use different properties for alerts.
650TEST_F(FlutterWindowTest, AlertNode) {
651 std::unique_ptr<FlutterWindowsEngine> engine =
652 FlutterWindowsEngineBuilder{GetContext()}.Build();
653 auto win32window = std::make_unique<MockFlutterWindow>();
654 EXPECT_CALL(*win32window.get(), GetAxFragmentRootDelegate())
655 .WillRepeatedly(Return(nullptr));
656 EXPECT_CALL(*win32window.get(), OnWindowStateEvent).Times(AnyNumber());
657 EXPECT_CALL(*win32window.get(), GetWindowHandle).Times(AnyNumber());
658 MockFlutterWindowsView view{engine.get(), std::move(win32window)};
659 std::wstring message = L"Test alert";
660 EXPECT_CALL(view, NotifyWinEventWrapper(_, ax::mojom::Event::kAlert))
661 .Times(1);
662 view.AnnounceAlert(message);
663
664 IAccessible* alert = view.AlertNode();
665 VARIANT self{.vt = VT_I4, .lVal = CHILDID_SELF};
666 BSTR strptr;
667 alert->get_accName(self, &strptr);
668 EXPECT_EQ(message, strptr);
669
670 alert->get_accDescription(self, &strptr);
671 EXPECT_EQ(message, strptr);
672
673 alert->get_accValue(self, &strptr);
674 EXPECT_EQ(message, strptr);
675
676 VARIANT role;
677 alert->get_accRole(self, &role);
678 EXPECT_EQ(role.vt, VT_I4);
679 EXPECT_EQ(role.lVal, ROLE_SYSTEM_ALERT);
680}
681
682TEST_F(FlutterWindowTest, LifecycleFocusMessages) {
683 MockFlutterWindow win32window;
684 EXPECT_CALL(win32window, GetWindowHandle)
685 .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
687
688 WindowStateEvent last_event;
689 EXPECT_CALL(delegate, OnWindowStateEvent)
690 .WillRepeatedly([&last_event](HWND hwnd, WindowStateEvent event) {
691 last_event = event;
692 });
693 EXPECT_CALL(win32window, OnWindowStateEvent)
694 .WillRepeatedly([&](WindowStateEvent event) {
695 win32window.FlutterWindow::OnWindowStateEvent(event);
696 });
697 EXPECT_CALL(win32window, OnResize).Times(AnyNumber());
698
699 win32window.SetView(&delegate);
700
701 win32window.InjectWindowMessage(WM_SIZE, 0, 0);
702 EXPECT_EQ(last_event, WindowStateEvent::kHide);
703
704 win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
705 EXPECT_EQ(last_event, WindowStateEvent::kShow);
706
707 EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kFocused),
709 .Times(1);
710 win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
711 EXPECT_EQ(last_event, WindowStateEvent::kFocus);
712
713 EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kUnfocused),
715 .Times(1);
716 win32window.InjectWindowMessage(WM_KILLFOCUS, 0, 0);
717 EXPECT_EQ(last_event, WindowStateEvent::kUnfocus);
718}
719
720TEST_F(FlutterWindowTest, CachedLifecycleMessage) {
721 MockFlutterWindow win32window;
722 EXPECT_CALL(win32window, GetWindowHandle)
723 .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
724 EXPECT_CALL(win32window, OnWindowStateEvent)
725 .WillRepeatedly([&](WindowStateEvent event) {
726 win32window.FlutterWindow::OnWindowStateEvent(event);
727 });
728 EXPECT_CALL(win32window, OnResize).Times(1);
729
730 // Restore
731 win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
732
733 // Focus
734 win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
735
737 bool focused = false;
738 bool restored = false;
739 EXPECT_CALL(delegate, OnWindowStateEvent)
740 .WillRepeatedly([&](HWND hwnd, WindowStateEvent event) {
741 if (event == WindowStateEvent::kFocus) {
742 focused = true;
743 } else if (event == WindowStateEvent::kShow) {
744 restored = true;
745 }
746 });
747
748 EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kFocused),
750 .Times(1);
751 win32window.SetView(&delegate);
752 EXPECT_TRUE(focused);
753 EXPECT_TRUE(restored);
754}
755
756} // namespace testing
757} // namespace flutter
virtual void OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, uint32_t rotation, uint32_t pressure, int modifiers_state)
virtual void OnPointerLeave(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id)
virtual bool OnBitmapSurfaceUpdated(const void *allocation, size_t row_bytes, size_t height) override
virtual void OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, UINT button, uint32_t rotation, uint32_t pressure)
virtual void OnPointerUp(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, UINT button)
virtual void SetView(WindowBindingHandlerDelegate *view) override
virtual float GetDpiScale() override
FlutterWindowsView(FlutterViewId view_id, FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > window_binding, bool is_sized_to_content, const BoxConstraints &box_constraints, FlutterWindowsViewSizingDelegate *sizing_delegate=nullptr, std::shared_ptr< WindowsProcTable > windows_proc_table=nullptr)
MOCK_METHOD(void, NotifyWinEventWrapper,(ui::AXPlatformNodeWin *, ax::mojom::Event),(override))
MockFlutterWindowsView(FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > wbh)
int32_t x
@ kUnfocused
Specifies that a view does not have platform focus.
Definition embedder.h:1221
@ kFocused
Specifies that a view has platform focus.
Definition embedder.h:1224
@ kUndefined
Definition embedder.h:1205
@ kFlutterPointerButtonMousePrimary
Definition embedder.h:1319
@ kFlutterPointerDeviceKindTouch
Definition embedder.h:1311
@ kFlutterPointerDeviceKindStylus
Definition embedder.h:1312
@ kFlutterPointerDeviceKindMouse
Definition embedder.h:1310
GLFWwindow * window
Definition main.cc:60
FlutterEngine engine
Definition main.cc:84
FlView * view
const char * message
return TRUE
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition macros.h:27
double y
TEST_F(DisplayListTest, Defaults)
constexpr LRESULT kWmResultDefault
Definition wm_builders.h:15
constexpr int64_t kImplicitViewId
WindowStateEvent
An event representing a change in window state that may update the.
Definition ref_ptr.h:261
int32_t height
int32_t width
int BOOL
struct tagMSG * LPMSG
LONG_PTR LRESULT
unsigned int UINT
LONG_PTR LPARAM
UINT_PTR WPARAM