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 LRESULT InjectPointerMessageWithClientPoint(MockFlutterWindow& window,
87  UINT message,
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 
110 class 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 
130 class FlutterWindowTest : public WindowsTest {};
131 
132 } // namespace
133 
134 TEST_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 
141 TEST_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).
160 TEST_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).
173 TEST_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 
184 TEST_F(FlutterWindowTest, OnPointerStarSendsDeviceType) {
185  std::unique_ptr<FlutterWindowsEngine> engine =
186  FlutterWindowsEngineBuilder{GetContext()}.Build();
187  FlutterWindow win32window(100, 100, engine->display_manager());
188  MockWindowBindingHandlerDelegate delegate;
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,
210  kFlutterPointerButtonMousePrimary, 0, 0))
211  .Times(1);
212  EXPECT_CALL(delegate,
213  OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
214  kDefaultPointerDeviceId,
215  kFlutterPointerButtonMousePrimary, 0, 0))
216  .Times(1);
217  EXPECT_CALL(delegate,
218  OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
219  kDefaultPointerDeviceId,
220  kFlutterPointerButtonMousePrimary, 0, 0))
221  .Times(1);
222 
223  // Up
224  EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
225  kDefaultPointerDeviceId,
226  kFlutterPointerButtonMousePrimary))
227  .Times(1);
228  EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
229  kDefaultPointerDeviceId,
230  kFlutterPointerButtonMousePrimary))
231  .Times(1);
232  EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,
233  kDefaultPointerDeviceId,
234  kFlutterPointerButtonMousePrimary))
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 
285 TEST_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);
312  MockWindowBindingHandlerDelegate delegate;
313 
314  win32window.SetView(&delegate);
315 
316  EXPECT_CALL(delegate,
317  OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
318  kDefaultPointerDeviceId,
319  kFlutterPointerButtonMousePrimary, 720, 0))
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 
328 TEST_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);
352  MockWindowBindingHandlerDelegate delegate;
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 
365 TEST_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);
388  MockWindowBindingHandlerDelegate delegate;
389  win32window.SetView(&delegate);
390 
391  EXPECT_CALL(delegate, OnPointerUp(25, 30, kFlutterPointerDeviceKindStylus,
392  kDefaultPointerDeviceId,
393  kFlutterPointerButtonMousePrimary))
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 
402 TEST_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);
425  MockWindowBindingHandlerDelegate delegate;
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 
438 TEST_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);
466  MockWindowBindingHandlerDelegate delegate;
467  win32window.SetView(&delegate);
468 
469  // First establish the pointer with WM_POINTERDOWN
470  EXPECT_CALL(delegate,
471  OnPointerDown(45, 50, kFlutterPointerDeviceKindStylus, 0,
472  kFlutterPointerButtonMousePrimary, 0, 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 
490 TEST_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);
504  MockWindowBindingHandlerDelegate delegate;
505  win32window.SetView(&delegate);
506 
507  EXPECT_CALL(delegate, OnPointerDown(45, 50, kFlutterPointerDeviceKindMouse,
508  kDefaultPointerDeviceId,
509  kFlutterPointerButtonMousePrimary, 0, 0))
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 
518 TEST_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);
532  MockWindowBindingHandlerDelegate delegate;
533  win32window.SetView(&delegate);
534 
535  EXPECT_CALL(delegate, OnPointerDown(55, 60, kFlutterPointerDeviceKindTouch,
536  kDefaultPointerDeviceId,
537  kFlutterPointerButtonMousePrimary, 0, 0))
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 
546 TEST_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);
563  MockWindowBindingHandlerDelegate delegate;
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,
585  kFlutterPointerButtonMousePrimary, 0, 0))
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.
597 TEST_F(FlutterWindowTest, OnScrollCallsGetScrollOffsetMultiplier) {
598  MockFlutterWindow win32window;
599  MockWindowBindingHandlerDelegate delegate;
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 
617 TEST_F(FlutterWindowTest, OnWindowRepaint) {
618  MockFlutterWindow win32window;
619  MockWindowBindingHandlerDelegate delegate;
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 
628 TEST_F(FlutterWindowTest, OnThemeChange) {
629  MockFlutterWindow win32window;
630  MockWindowBindingHandlerDelegate delegate;
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
642 TEST_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.
650 TEST_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 
682 TEST_F(FlutterWindowTest, LifecycleFocusMessages) {
683  MockFlutterWindow win32window;
684  EXPECT_CALL(win32window, GetWindowHandle)
685  .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
686  MockWindowBindingHandlerDelegate delegate;
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),
708  Eq(FlutterViewFocusDirection::kUndefined)))
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),
714  Eq(FlutterViewFocusDirection::kUndefined)))
715  .Times(1);
716  win32window.InjectWindowMessage(WM_KILLFOCUS, 0, 0);
717  EXPECT_EQ(last_event, WindowStateEvent::kUnfocus);
718 }
719 
720 TEST_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 
736  MockWindowBindingHandlerDelegate delegate;
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),
749  Eq(FlutterViewFocusDirection::kUndefined)))
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)
Win32Message message
TEST_F(AccessibilityPluginTest, DirectAnnounceCall)
WindowStateEvent
An event representing a change in window state that may update the.
constexpr FlutterViewId kImplicitViewId