Flutter Windows Embedder
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.
4 #include "flutter/fml/macros.h"
6 #include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h"
7 #include "flutter/shell/platform/windows/testing/mock_window_binding_handler_delegate.h"
8 #include "flutter/shell/platform/windows/testing/mock_windows_proc_table.h"
9 #include "flutter/shell/platform/windows/testing/windows_test.h"
10 #include "flutter/shell/platform/windows/testing/wm_builders.h"
11 
12 #include "gmock/gmock.h"
13 #include "gtest/gtest.h"
14 
15 namespace flutter {
16 namespace testing {
17 
18 using ::testing::_;
19 using ::testing::AnyNumber;
20 using ::testing::Eq;
21 using ::testing::Invoke;
22 using ::testing::Return;
23 
24 namespace {
25 static constexpr int32_t kDefaultPointerDeviceId = 0;
26 
27 class 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));
66  MOCK_METHOD(ui::AXFragmentRootDelegateWin*,
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 
86 class MockFlutterWindowsView : public FlutterWindowsView {
87  public:
88  MockFlutterWindowsView(FlutterWindowsEngine* engine,
89  std::unique_ptr<WindowBindingHandler> window_binding)
91  engine,
92  std::move(window_binding),
93  false,
94  BoxConstraints()) {}
96 
97  MOCK_METHOD(void,
98  NotifyWinEventWrapper,
99  (ui::AXPlatformNodeWin*, ax::mojom::Event),
100  (override));
101 
102  private:
103  FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView);
104 };
105 
106 class FlutterWindowTest : public WindowsTest {};
107 
108 } // namespace
109 
110 TEST_F(FlutterWindowTest, CreateDestroy) {
111  std::unique_ptr<FlutterWindowsEngine> engine =
112  FlutterWindowsEngineBuilder{GetContext()}.Build();
113  FlutterWindow window(800, 600, engine->display_manager());
114  ASSERT_TRUE(TRUE);
115 }
116 
117 TEST_F(FlutterWindowTest, OnBitmapSurfaceUpdated) {
118  std::unique_ptr<FlutterWindowsEngine> engine =
119  FlutterWindowsEngineBuilder{GetContext()}.Build();
120  FlutterWindow win32window(100, 100, engine->display_manager());
121  int old_handle_count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
122 
123  constexpr size_t row_bytes = 100 * 4;
124  constexpr size_t height = 100;
125  std::array<char, row_bytes * height> allocation;
126  win32window.OnBitmapSurfaceUpdated(allocation.data(), row_bytes, height);
127 
128  int new_handle_count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
129  // Check GDI resources leak
130  EXPECT_EQ(old_handle_count, new_handle_count);
131 }
132 
133 // Tests that composing rect updates are transformed from Flutter logical
134 // coordinates to device coordinates and passed to the text input manager
135 // when the DPI scale is 100% (96 DPI).
136 TEST_F(FlutterWindowTest, OnCursorRectUpdatedRegularDPI) {
137  MockFlutterWindow win32window;
138  EXPECT_CALL(win32window, GetDpiScale()).WillOnce(Return(1.0));
139 
140  Rect cursor_rect(Point(10, 20), Size(30, 40));
141  EXPECT_CALL(win32window, UpdateCursorRect(cursor_rect)).Times(1);
142 
143  win32window.OnCursorRectUpdated(cursor_rect);
144 }
145 
146 // Tests that composing rect updates are transformed from Flutter logical
147 // coordinates to device coordinates and passed to the text input manager
148 // when the DPI scale is 150% (144 DPI).
149 TEST_F(FlutterWindowTest, OnCursorRectUpdatedHighDPI) {
150  MockFlutterWindow win32window;
151  EXPECT_CALL(win32window, GetDpiScale()).WillOnce(Return(1.5));
152 
153  Rect expected_cursor_rect(Point(15, 30), Size(45, 60));
154  EXPECT_CALL(win32window, UpdateCursorRect(expected_cursor_rect)).Times(1);
155 
156  Rect cursor_rect(Point(10, 20), Size(30, 40));
157  win32window.OnCursorRectUpdated(cursor_rect);
158 }
159 
160 TEST_F(FlutterWindowTest, OnPointerStarSendsDeviceType) {
161  std::unique_ptr<FlutterWindowsEngine> engine =
162  FlutterWindowsEngineBuilder{GetContext()}.Build();
163  FlutterWindow win32window(100, 100, engine->display_manager());
164  MockWindowBindingHandlerDelegate delegate;
165  EXPECT_CALL(delegate, OnWindowStateEvent).Times(AnyNumber());
166  win32window.SetView(&delegate);
167 
168  // Move
169  EXPECT_CALL(delegate,
170  OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
171  kDefaultPointerDeviceId, 0, 0, 0))
172  .Times(1);
173  EXPECT_CALL(delegate,
174  OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
175  kDefaultPointerDeviceId, 0, 0, 0))
176  .Times(1);
177  EXPECT_CALL(delegate,
178  OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
179  kDefaultPointerDeviceId, 0, 0, 0))
180  .Times(1);
181 
182  // Down
183  EXPECT_CALL(delegate,
184  OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
185  kDefaultPointerDeviceId,
186  kFlutterPointerButtonMousePrimary, 0, 0))
187  .Times(1);
188  EXPECT_CALL(delegate,
189  OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
190  kDefaultPointerDeviceId,
191  kFlutterPointerButtonMousePrimary, 0, 0))
192  .Times(1);
193  EXPECT_CALL(delegate,
194  OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
195  kDefaultPointerDeviceId,
196  kFlutterPointerButtonMousePrimary, 0, 0))
197  .Times(1);
198 
199  // Up
200  EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
201  kDefaultPointerDeviceId,
202  kFlutterPointerButtonMousePrimary))
203  .Times(1);
204  EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
205  kDefaultPointerDeviceId,
206  kFlutterPointerButtonMousePrimary))
207  .Times(1);
208  EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,
209  kDefaultPointerDeviceId,
210  kFlutterPointerButtonMousePrimary))
211  .Times(1);
212 
213  // Leave
214  EXPECT_CALL(delegate,
215  OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindMouse,
216  kDefaultPointerDeviceId))
217  .Times(1);
218  EXPECT_CALL(delegate,
219  OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindTouch,
220  kDefaultPointerDeviceId))
221  .Times(1);
222  EXPECT_CALL(delegate,
223  OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindStylus,
224  kDefaultPointerDeviceId))
225  .Times(1);
226 
227  win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
228  kDefaultPointerDeviceId, 0, 0, 0);
229  win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
230  kDefaultPointerDeviceId, WM_LBUTTONDOWN, 0, 0);
231  win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
232  kDefaultPointerDeviceId, WM_LBUTTONDOWN);
233  win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindMouse,
234  kDefaultPointerDeviceId);
235 
236  // Touch
237  win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
238  kDefaultPointerDeviceId, 0, 0, 0);
239  win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
240  kDefaultPointerDeviceId, WM_LBUTTONDOWN, 0, 0);
241  win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
242  kDefaultPointerDeviceId, WM_LBUTTONDOWN);
243  win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindTouch,
244  kDefaultPointerDeviceId);
245 
246  // Pen
247  win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
248  kDefaultPointerDeviceId, 0, 0, 0);
249  win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
250  kDefaultPointerDeviceId, WM_LBUTTONDOWN, 0, 0);
251  win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,
252  kDefaultPointerDeviceId, WM_LBUTTONDOWN);
253  win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindStylus,
254  kDefaultPointerDeviceId);
255 
256  // Destruction of win32window sends a HIDE update. In situ, the window is
257  // owned by the delegate, and so is destructed first. Not so here.
258  win32window.SetView(nullptr);
259 }
260 
261 TEST_F(FlutterWindowTest, OnStylusPointerDown) {
262  auto mock_proc_table = std::make_shared<MockWindowsProcTable>();
263 
264  // Set up expectations for the mock
265  EXPECT_CALL(*mock_proc_table, GetPointerInfo(_, _))
266  .WillRepeatedly([](UINT32 pointer_id, POINTER_INFO* pointer_info) {
267  if (pointer_info != nullptr) {
268  pointer_info->pointerType = PT_PEN;
269  pointer_info->pointerId = pointer_id;
270  pointer_info->pointerFlags = POINTER_FLAG_INCONTACT;
271  }
272  return TRUE;
273  });
274 
275  EXPECT_CALL(*mock_proc_table, GetPointerPenInfo(_, _))
276  .WillRepeatedly([](UINT32 pointer_id, POINTER_PEN_INFO* pen_info) {
277  if (pen_info != nullptr) {
278  pen_info->pressure = 720; // Non-zero pressure for contact events
279  pen_info->rotation = 0;
280  }
281  return TRUE;
282  });
283 
284  POINTER_INFO test_pointer_info = {};
285  BOOL result = mock_proc_table->GetPointerInfo(1, &test_pointer_info);
286 
287  MockFlutterWindow win32window(100, 100, mock_proc_table);
288  MockWindowBindingHandlerDelegate delegate;
289 
290  win32window.SetView(&delegate);
291 
292  EXPECT_CALL(delegate,
293  OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
294  kDefaultPointerDeviceId,
295  kFlutterPointerButtonMousePrimary, 720, 0))
296  .Times(1);
297 
298  UINT32 pointerId = 1;
299  WPARAM wparam = static_cast<WPARAM>(pointerId);
300  LPARAM lparam = MAKELPARAM(10, 10);
301 
302  win32window.InjectWindowMessage(WM_POINTERDOWN, wparam, lparam);
303 }
304 
305 TEST_F(FlutterWindowTest, OnStylusPointerMove) {
306  auto mock_proc_table = std::make_shared<MockWindowsProcTable>();
307 
308  EXPECT_CALL(*mock_proc_table, GetPointerInfo(_, _))
309  .WillRepeatedly([](UINT32 pointer_id, POINTER_INFO* pointer_info) {
310  if (pointer_info != nullptr) {
311  pointer_info->pointerType = PT_PEN;
312  pointer_info->pointerId = pointer_id;
313  pointer_info->pointerFlags =
314  POINTER_FLAG_INCONTACT | POINTER_FLAG_UPDATE;
315  }
316  return TRUE;
317  });
318 
319  EXPECT_CALL(*mock_proc_table, GetPointerPenInfo(_, _))
320  .WillRepeatedly([](UINT32 pointer_id, POINTER_PEN_INFO* pen_info) {
321  if (pen_info != nullptr) {
322  pen_info->pressure = 720; // Non-zero pressure for contact events
323  pen_info->rotation = 10; // This is PRE-transformation to radians.
324  }
325  return TRUE;
326  });
327 
328  MockFlutterWindow win32window(100, 100, mock_proc_table);
329  MockWindowBindingHandlerDelegate delegate;
330  win32window.SetView(&delegate);
331 
332  EXPECT_CALL(delegate, OnPointerMove(15, 20, kFlutterPointerDeviceKindStylus,
333  kDefaultPointerDeviceId, 10, 720, 0))
334  .Times(1);
335 
336  UINT32 pointerId = 1;
337  WPARAM wparam = static_cast<WPARAM>(pointerId);
338  LPARAM lparam = MAKELPARAM(15, 20);
339 
340  win32window.InjectWindowMessage(WM_POINTERUPDATE, wparam, lparam);
341 }
342 
343 TEST_F(FlutterWindowTest, OnStylusPointerUp) {
344  auto mock_proc_table = std::make_shared<MockWindowsProcTable>();
345 
346  EXPECT_CALL(*mock_proc_table, GetPointerInfo(_, _))
347  .WillRepeatedly([](UINT32 pointer_id, POINTER_INFO* pointer_info) {
348  if (pointer_info != nullptr) {
349  pointer_info->pointerType = PT_PEN;
350  pointer_info->pointerId = pointer_id;
351  pointer_info->pointerFlags = POINTER_FLAG_UP;
352  }
353  return TRUE;
354  });
355 
356  EXPECT_CALL(*mock_proc_table, GetPointerPenInfo(_, _))
357  .WillRepeatedly([](UINT32 pointer_id, POINTER_PEN_INFO* pen_info) {
358  if (pen_info != nullptr) {
359  pen_info->pressure = 720;
360  pen_info->rotation = 0;
361  }
362  return TRUE;
363  });
364 
365  MockFlutterWindow win32window(100, 100, mock_proc_table);
366  MockWindowBindingHandlerDelegate delegate;
367  win32window.SetView(&delegate);
368 
369  EXPECT_CALL(delegate, OnPointerUp(25, 30, kFlutterPointerDeviceKindStylus,
370  kDefaultPointerDeviceId,
371  kFlutterPointerButtonMousePrimary))
372  .Times(1);
373 
374  UINT32 pointerId = 1;
375  WPARAM wparam = static_cast<WPARAM>(pointerId);
376  LPARAM lparam = MAKELPARAM(25, 30);
377 
378  win32window.InjectWindowMessage(WM_POINTERUP, wparam, lparam);
379 }
380 
381 TEST_F(FlutterWindowTest, OnStylusPointerLeave) {
382  auto mock_proc_table = std::make_shared<MockWindowsProcTable>();
383 
384  EXPECT_CALL(*mock_proc_table, GetPointerInfo(_, _))
385  .WillRepeatedly([](UINT32 pointer_id, POINTER_INFO* pointer_info) {
386  if (pointer_info != nullptr) {
387  pointer_info->pointerType = PT_PEN;
388  pointer_info->pointerId = pointer_id;
389  pointer_info->pointerFlags = POINTER_FLAG_UP;
390  }
391  return TRUE;
392  });
393 
394  EXPECT_CALL(*mock_proc_table, GetPointerPenInfo(_, _))
395  .WillRepeatedly([](UINT32 pointer_id, POINTER_PEN_INFO* pen_info) {
396  if (pen_info != nullptr) {
397  pen_info->pressure = 720;
398  pen_info->rotation = 0;
399  }
400  return TRUE;
401  });
402 
403  MockFlutterWindow win32window(100, 100, mock_proc_table);
404  MockWindowBindingHandlerDelegate delegate;
405  win32window.SetView(&delegate);
406 
407  EXPECT_CALL(delegate, OnPointerLeave(35, 40, kFlutterPointerDeviceKindStylus,
408  kDefaultPointerDeviceId))
409  .Times(1);
410 
411  UINT32 pointerId = 1;
412  WPARAM wparam = static_cast<WPARAM>(pointerId);
413  LPARAM lparam = MAKELPARAM(35, 40);
414 
415  win32window.InjectWindowMessage(WM_POINTERLEAVE, wparam, lparam);
416 }
417 
418 TEST_F(FlutterWindowTest, OnStylusPointerHover) {
419  // Test that WM_POINTERUPDATE without POINTER_FLAG_INCONTACT (hover) is
420  // handled
421  auto mock_proc_table = std::make_shared<MockWindowsProcTable>();
422 
423  EXPECT_CALL(*mock_proc_table, GetPointerInfo(_, _))
424  .WillRepeatedly([](UINT32 pointer_id, POINTER_INFO* pointer_info) {
425  if (pointer_info != nullptr) {
426  pointer_info->pointerId = 1;
427  pointer_info->pointerType = PT_PEN;
428  // No POINTER_FLAG_INCONTACT - this simulates a hover event
429  pointer_info->pointerFlags = POINTER_FLAG_UPDATE;
430  pointer_info->ptPixelLocation.x = 45;
431  pointer_info->ptPixelLocation.y = 50;
432  }
433  return TRUE;
434  });
435 
436  EXPECT_CALL(*mock_proc_table, GetPointerPenInfo(_, _))
437  .WillRepeatedly([](UINT32 pointer_id, POINTER_PEN_INFO* pen_info) {
438  if (pen_info != nullptr) {
439  pen_info->pressure = 0;
440  pen_info->rotation = 0;
441  }
442  return TRUE;
443  });
444 
445  MockFlutterWindow win32window(100, 100, mock_proc_table);
446  MockWindowBindingHandlerDelegate delegate;
447  win32window.SetView(&delegate);
448 
449  // First establish the pointer with WM_POINTERDOWN
450  EXPECT_CALL(delegate,
451  OnPointerDown(45, 50, kFlutterPointerDeviceKindStylus, 0,
452  kFlutterPointerButtonMousePrimary, 0, 0))
453  .Times(1);
454 
455  UINT32 pointerId = 1;
456  WPARAM wparam = static_cast<WPARAM>(pointerId);
457  LPARAM lparam = MAKELPARAM(45, 50);
458  win32window.InjectWindowMessage(WM_POINTERDOWN, wparam, lparam);
459 
460  // Now expect OnPointerMove to be called for hover events
461  EXPECT_CALL(delegate, OnPointerMove(45, 50, kFlutterPointerDeviceKindStylus,
462  0, 0, 0, 0))
463  .Times(1);
464 
465  // Inject WM_POINTERUPDATE message (hover event)
466  win32window.InjectWindowMessage(WM_POINTERUPDATE, wparam, lparam);
467 }
468 
469 TEST_F(FlutterWindowTest, OnMousePointerDown) {
470  auto mock_proc_table = std::make_shared<MockWindowsProcTable>();
471 
472  EXPECT_CALL(*mock_proc_table, GetPointerInfo(_, _))
473  .WillRepeatedly([](UINT32 pointer_id, POINTER_INFO* pointer_info) {
474  if (pointer_info != nullptr) {
475  pointer_info->pointerType = PT_MOUSE;
476  pointer_info->pointerId = pointer_id;
477  pointer_info->pointerFlags = POINTER_FLAG_INCONTACT;
478  }
479  return TRUE;
480  });
481 
482  MockFlutterWindow win32window(100, 100, mock_proc_table);
483  MockWindowBindingHandlerDelegate delegate;
484  win32window.SetView(&delegate);
485 
486  EXPECT_CALL(delegate, OnPointerDown(45, 50, kFlutterPointerDeviceKindMouse,
487  kDefaultPointerDeviceId,
488  kFlutterPointerButtonMousePrimary, 0, 0))
489  .Times(1);
490 
491  UINT32 pointerId = 1;
492  WPARAM wparam = static_cast<WPARAM>(pointerId);
493  LPARAM lparam = MAKELPARAM(45, 50);
494 
495  win32window.InjectWindowMessage(WM_POINTERDOWN, wparam, lparam);
496 }
497 
498 TEST_F(FlutterWindowTest, OnTouchPointerDown) {
499  auto mock_proc_table = std::make_shared<MockWindowsProcTable>();
500 
501  EXPECT_CALL(*mock_proc_table, GetPointerInfo(_, _))
502  .WillRepeatedly([](UINT32 pointer_id, POINTER_INFO* pointer_info) {
503  if (pointer_info != nullptr) {
504  pointer_info->pointerType = PT_TOUCH;
505  pointer_info->pointerId = pointer_id;
506  pointer_info->pointerFlags = POINTER_FLAG_INCONTACT;
507  }
508  return TRUE;
509  });
510 
511  MockFlutterWindow win32window(100, 100, mock_proc_table);
512  MockWindowBindingHandlerDelegate delegate;
513  win32window.SetView(&delegate);
514 
515  EXPECT_CALL(delegate, OnPointerDown(55, 60, kFlutterPointerDeviceKindTouch,
516  kDefaultPointerDeviceId,
517  kFlutterPointerButtonMousePrimary, 0, 0))
518  .Times(1);
519 
520  UINT32 pointerId = 1;
521  WPARAM wparam = static_cast<WPARAM>(pointerId);
522  LPARAM lparam = MAKELPARAM(55, 60);
523 
524  win32window.InjectWindowMessage(WM_POINTERDOWN, wparam, lparam);
525 }
526 
527 // Tests that calls to OnScroll in turn calls GetScrollOffsetMultiplier
528 // for mapping scroll ticks to pixels.
529 TEST_F(FlutterWindowTest, OnScrollCallsGetScrollOffsetMultiplier) {
530  MockFlutterWindow win32window;
531  MockWindowBindingHandlerDelegate delegate;
532  EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
533  win32window.SetView(&delegate);
534 
535  EXPECT_CALL(win32window, GetWindowHandle).WillOnce([&win32window]() {
536  return win32window.FlutterWindow::GetWindowHandle();
537  });
538  EXPECT_CALL(win32window, GetScrollOffsetMultiplier).WillOnce(Return(120.0f));
539 
540  EXPECT_CALL(delegate,
541  OnScroll(_, _, 0, 0, 120.0f, kFlutterPointerDeviceKindMouse,
542  kDefaultPointerDeviceId))
543  .Times(1);
544 
545  win32window.OnScroll(0.0f, 0.0f, kFlutterPointerDeviceKindMouse,
546  kDefaultPointerDeviceId);
547 }
548 
549 TEST_F(FlutterWindowTest, OnWindowRepaint) {
550  MockFlutterWindow win32window;
551  MockWindowBindingHandlerDelegate delegate;
552  EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
553  win32window.SetView(&delegate);
554 
555  EXPECT_CALL(delegate, OnWindowRepaint()).Times(1);
556 
557  win32window.InjectWindowMessage(WM_PAINT, 0, 0);
558 }
559 
560 TEST_F(FlutterWindowTest, OnThemeChange) {
561  MockFlutterWindow win32window;
562  MockWindowBindingHandlerDelegate delegate;
563  EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
564  win32window.SetView(&delegate);
565 
566  EXPECT_CALL(delegate, OnHighContrastChanged).Times(1);
567 
568  win32window.InjectWindowMessage(WM_THEMECHANGED, 0, 0);
569 }
570 
571 // The window should return no root accessibility node if
572 // it isn't attached to a view.
573 // Regression test for https://github.com/flutter/flutter/issues/129791
574 TEST_F(FlutterWindowTest, AccessibilityNodeWithoutView) {
575  MockFlutterWindow win32window;
576 
577  EXPECT_EQ(win32window.GetNativeViewAccessible(), nullptr);
578 }
579 
580 // Ensure that announcing the alert propagates the message to the alert node.
581 // Different screen readers use different properties for alerts.
582 TEST_F(FlutterWindowTest, AlertNode) {
583  std::unique_ptr<FlutterWindowsEngine> engine =
584  FlutterWindowsEngineBuilder{GetContext()}.Build();
585  auto win32window = std::make_unique<MockFlutterWindow>();
586  EXPECT_CALL(*win32window.get(), GetAxFragmentRootDelegate())
587  .WillRepeatedly(Return(nullptr));
588  EXPECT_CALL(*win32window.get(), OnWindowStateEvent).Times(AnyNumber());
589  EXPECT_CALL(*win32window.get(), GetWindowHandle).Times(AnyNumber());
590  MockFlutterWindowsView view{engine.get(), std::move(win32window)};
591  std::wstring message = L"Test alert";
592  EXPECT_CALL(view, NotifyWinEventWrapper(_, ax::mojom::Event::kAlert))
593  .Times(1);
594  view.AnnounceAlert(message);
595 
596  IAccessible* alert = view.AlertNode();
597  VARIANT self{.vt = VT_I4, .lVal = CHILDID_SELF};
598  BSTR strptr;
599  alert->get_accName(self, &strptr);
600  EXPECT_EQ(message, strptr);
601 
602  alert->get_accDescription(self, &strptr);
603  EXPECT_EQ(message, strptr);
604 
605  alert->get_accValue(self, &strptr);
606  EXPECT_EQ(message, strptr);
607 
608  VARIANT role;
609  alert->get_accRole(self, &role);
610  EXPECT_EQ(role.vt, VT_I4);
611  EXPECT_EQ(role.lVal, ROLE_SYSTEM_ALERT);
612 }
613 
614 TEST_F(FlutterWindowTest, LifecycleFocusMessages) {
615  MockFlutterWindow win32window;
616  EXPECT_CALL(win32window, GetWindowHandle)
617  .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
618  MockWindowBindingHandlerDelegate delegate;
619 
620  WindowStateEvent last_event;
621  EXPECT_CALL(delegate, OnWindowStateEvent)
622  .WillRepeatedly([&last_event](HWND hwnd, WindowStateEvent event) {
623  last_event = event;
624  });
625  EXPECT_CALL(win32window, OnWindowStateEvent)
626  .WillRepeatedly([&](WindowStateEvent event) {
627  win32window.FlutterWindow::OnWindowStateEvent(event);
628  });
629  EXPECT_CALL(win32window, OnResize).Times(AnyNumber());
630 
631  win32window.SetView(&delegate);
632 
633  win32window.InjectWindowMessage(WM_SIZE, 0, 0);
634  EXPECT_EQ(last_event, WindowStateEvent::kHide);
635 
636  win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
637  EXPECT_EQ(last_event, WindowStateEvent::kShow);
638 
639  EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kFocused),
640  Eq(FlutterViewFocusDirection::kUndefined)))
641  .Times(1);
642  win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
643  EXPECT_EQ(last_event, WindowStateEvent::kFocus);
644 
645  EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kUnfocused),
646  Eq(FlutterViewFocusDirection::kUndefined)))
647  .Times(1);
648  win32window.InjectWindowMessage(WM_KILLFOCUS, 0, 0);
649  EXPECT_EQ(last_event, WindowStateEvent::kUnfocus);
650 }
651 
652 TEST_F(FlutterWindowTest, CachedLifecycleMessage) {
653  MockFlutterWindow win32window;
654  EXPECT_CALL(win32window, GetWindowHandle)
655  .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
656  EXPECT_CALL(win32window, OnWindowStateEvent)
657  .WillRepeatedly([&](WindowStateEvent event) {
658  win32window.FlutterWindow::OnWindowStateEvent(event);
659  });
660  EXPECT_CALL(win32window, OnResize).Times(1);
661 
662  // Restore
663  win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
664 
665  // Focus
666  win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
667 
668  MockWindowBindingHandlerDelegate delegate;
669  bool focused = false;
670  bool restored = false;
671  EXPECT_CALL(delegate, OnWindowStateEvent)
672  .WillRepeatedly([&](HWND hwnd, WindowStateEvent event) {
673  if (event == WindowStateEvent::kFocus) {
674  focused = true;
675  } else if (event == WindowStateEvent::kShow) {
676  restored = true;
677  }
678  });
679 
680  EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kFocused),
681  Eq(FlutterViewFocusDirection::kUndefined)))
682  .Times(1);
683  win32window.SetView(&delegate);
684  EXPECT_TRUE(focused);
685  EXPECT_TRUE(restored);
686 }
687 
688 } // namespace testing
689 } // 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)
Win32Message message
TEST_F(AccessibilityPluginTest, DirectAnnounceCall)
WindowStateEvent
An event representing a change in window state that may update the.
constexpr FlutterViewId kImplicitViewId