| 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
5 #include "flutter/fml/macros.h"
6 #include "flutter/shell/platform/windows/testing/flutter_window_test.h"
7 #include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h"
8 #include "flutter/shell/platform/windows/testing/mock_window_binding_handler_delegate.h"
9 #include "flutter/shell/platform/windows/testing/wm_builders.h"
10
11 #include "gmock/gmock.h"
12 #include "gtest/gtest.h"
13
14 using testing::_;
15 using testing::Invoke;
16 using testing::Return;
17
18 namespace flutter {
19 namespace testing {
20
21 namespace {
22 static constexpr int32_t kDefaultPointerDeviceId = 0;
23
24 class MockFlutterWindow : public FlutterWindow {
25 public:
26 MockFlutterWindow(bool reset_view_on_exit = true)
27 : FlutterWindow(800, 600), reset_view_on_exit_(reset_view_on_exit) {
28 ON_CALL(*this, GetDpiScale())
29 .WillByDefault(Return(this->FlutterWindow::GetDpiScale()));
30 }
31 virtual ~MockFlutterWindow() {
32 if (reset_view_on_exit_) {
33 SetView(nullptr);
34 }
35 }
36
37 // Wrapper for GetCurrentDPI() which is a protected method.
38 UINT GetDpi() { return GetCurrentDPI(); }
39
40 // Simulates a WindowProc message from the OS.
41 LRESULT InjectWindowMessage(UINT const message,
42 WPARAM const wparam,
43 LPARAM const lparam) {
44 return HandleMessage(message, wparam, lparam);
45 }
46
47 MOCK_METHOD(void, OnDpiScale, (unsigned int), (override));
48 MOCK_METHOD(void, OnResize, (unsigned int, unsigned int), (override));
49 MOCK_METHOD(void,
50 OnPointerMove,
51 (double, double, FlutterPointerDeviceKind, int32_t, int),
52 (override));
53 MOCK_METHOD(void,
54 OnPointerDown,
55 (double, double, FlutterPointerDeviceKind, int32_t, UINT),
56 (override));
57 MOCK_METHOD(void,
58 OnPointerUp,
59 (double, double, FlutterPointerDeviceKind, int32_t, UINT),
60 (override));
61 MOCK_METHOD(void,
62 OnPointerLeave,
63 (double, double, FlutterPointerDeviceKind, int32_t),
64 (override));
65 MOCK_METHOD(void, OnSetCursor, (), (override));
66 MOCK_METHOD(float, GetScrollOffsetMultiplier, (), (override));
67 MOCK_METHOD(bool, GetHighContrastEnabled, (), (override));
68 MOCK_METHOD(float, GetDpiScale, (), (override));
69 MOCK_METHOD(bool, IsVisible, (), (override));
70 MOCK_METHOD(void, UpdateCursorRect, (const Rect&), (override));
71 MOCK_METHOD(void, OnResetImeComposing, (), (override));
72 MOCK_METHOD(UINT, Win32DispatchMessage, (UINT, WPARAM, LPARAM), (override));
73 MOCK_METHOD(BOOL, Win32PeekMessage, (LPMSG, UINT, UINT, UINT), (override));
74 MOCK_METHOD(uint32_t, Win32MapVkToChar, (uint32_t), (override));
75 MOCK_METHOD(HWND, GetPlatformWindow, (), (override));
76 MOCK_METHOD(ui::AXFragmentRootDelegateWin*,
77 GetAxFragmentRootDelegate,
78 (),
79 (override));
80 MOCK_METHOD(void, OnWindowStateEvent, (WindowStateEvent), (override));
81
82 protected:
83 // |KeyboardManager::WindowDelegate|
84 LRESULT Win32DefWindowProc(HWND hWnd,
85 UINT Msg,
86 WPARAM wParam,
87 LPARAM lParam) override {
88 return kWmResultDefault;
89 }
90
91 private:
92 bool reset_view_on_exit_;
93 FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindow);
94 };
95
96 class MockFlutterWindowsView : public FlutterWindowsView {
97 public:
98 MockFlutterWindowsView(std::unique_ptr<WindowBindingHandler> window_binding)
99 : FlutterWindowsView(std::move(window_binding)) {}
100 ~MockFlutterWindowsView() {}
101
102 MOCK_METHOD(void,
103 NotifyWinEventWrapper,
104 (ui::AXPlatformNodeWin*, ax::mojom::Event),
105 (override));
106
107 private:
108 FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView);
109 };
110
111 } // namespace
112
113 TEST(FlutterWindowTest, CreateDestroy) {
114 FlutterWindowTest window(800, 600);
115 ASSERT_TRUE(TRUE);
116 }
117
118 TEST(FlutterWindowTest, OnBitmapSurfaceUpdated) {
119 FlutterWindow win32window(100, 100);
120 int old_handle_count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
121
122 constexpr size_t row_bytes = 100 * 4;
123 constexpr size_t height = 100;
124 std::array<char, row_bytes * height> allocation;
125 win32window.OnBitmapSurfaceUpdated(allocation.data(), row_bytes, height);
126
127 int new_handle_count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
128 // Check GDI resources leak
129 EXPECT_EQ(old_handle_count, new_handle_count);
130 }
131
132 // Tests that composing rect updates are transformed from Flutter logical
133 // coordinates to device coordinates and passed to the text input manager
134 // when the DPI scale is 100% (96 DPI).
135 TEST(FlutterWindowTest, OnCursorRectUpdatedRegularDPI) {
136 MockFlutterWindow win32window;
137 ON_CALL(win32window, GetDpiScale()).WillByDefault(Return(1.0));
138 EXPECT_CALL(win32window, GetDpiScale()).Times(1);
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(FlutterWindowTest, OnCursorRectUpdatedHighDPI) {
150 MockFlutterWindow win32window;
151 ON_CALL(win32window, GetDpiScale()).WillByDefault(Return(1.5));
152 EXPECT_CALL(win32window, GetDpiScale()).Times(1);
153
154 Rect expected_cursor_rect(Point(15, 30), Size(45, 60));
155 EXPECT_CALL(win32window, UpdateCursorRect(expected_cursor_rect)).Times(1);
156
157 Rect cursor_rect(Point(10, 20), Size(30, 40));
158 win32window.OnCursorRectUpdated(cursor_rect);
159 }
160
161 TEST(FlutterWindowTest, OnPointerStarSendsDeviceType) {
162 FlutterWindow win32window(100, 100);
163 MockWindowBindingHandlerDelegate delegate;
164 win32window.SetView(&delegate);
165 // Move
166 EXPECT_CALL(delegate,
167 OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
168 kDefaultPointerDeviceId, 0))
169 .Times(1);
170 EXPECT_CALL(delegate,
171 OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
172 kDefaultPointerDeviceId, 0))
173 .Times(1);
174 EXPECT_CALL(delegate,
175 OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
176 kDefaultPointerDeviceId, 0))
177 .Times(1);
178
179 // Down
180 EXPECT_CALL(
181 delegate,
182 OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
183 kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
184 .Times(1);
185 EXPECT_CALL(
186 delegate,
187 OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
188 kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
189 .Times(1);
190 EXPECT_CALL(
191 delegate,
192 OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
193 kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
194 .Times(1);
195
196 // Up
197 EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
198 kDefaultPointerDeviceId,
199 kFlutterPointerButtonMousePrimary))
200 .Times(1);
201 EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
202 kDefaultPointerDeviceId,
203 kFlutterPointerButtonMousePrimary))
204 .Times(1);
205 EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,
206 kDefaultPointerDeviceId,
207 kFlutterPointerButtonMousePrimary))
208 .Times(1);
209
210 // Leave
211 EXPECT_CALL(delegate,
212 OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindMouse,
213 kDefaultPointerDeviceId))
214 .Times(1);
215 EXPECT_CALL(delegate,
216 OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindTouch,
217 kDefaultPointerDeviceId))
218 .Times(1);
219 EXPECT_CALL(delegate,
220 OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindStylus,
221 kDefaultPointerDeviceId))
222 .Times(1);
223
224 win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
225 kDefaultPointerDeviceId, 0);
226 win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
227 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
228 win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
229 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
230 win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindMouse,
231 kDefaultPointerDeviceId);
232
233 // Touch
234 win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
235 kDefaultPointerDeviceId, 0);
236 win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
237 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
238 win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
239 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
240 win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindTouch,
241 kDefaultPointerDeviceId);
242
243 // Pen
244 win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
245 kDefaultPointerDeviceId, 0);
246 win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
247 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
248 win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,
249 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
250 win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindStylus,
251 kDefaultPointerDeviceId);
252
253 // Destruction of win32window sends a HIDE update. In situ, the window is
254 // owned by the delegate, and so is destructed first. Not so here.
255 win32window.SetView(nullptr);
256 }
257
258 // Tests that calls to OnScroll in turn calls GetScrollOffsetMultiplier
259 // for mapping scroll ticks to pixels.
260 TEST(FlutterWindowTest, OnScrollCallsGetScrollOffsetMultiplier) {
261 MockFlutterWindow win32window;
262 MockWindowBindingHandlerDelegate delegate;
263 win32window.SetView(&delegate);
264
265 ON_CALL(win32window, GetScrollOffsetMultiplier())
266 .WillByDefault(Return(120.0f));
267 EXPECT_CALL(win32window, GetScrollOffsetMultiplier()).Times(1);
268
269 EXPECT_CALL(delegate,
270 OnScroll(_, _, 0, 0, 120.0f, kFlutterPointerDeviceKindMouse,
271 kDefaultPointerDeviceId))
272 .Times(1);
273
274 win32window.OnScroll(0.0f, 0.0f, kFlutterPointerDeviceKindMouse,
275 kDefaultPointerDeviceId);
276 }
277
278 TEST(FlutterWindowTest, OnWindowRepaint) {
279 MockFlutterWindow win32window;
280 MockWindowBindingHandlerDelegate delegate;
281 win32window.SetView(&delegate);
282
283 EXPECT_CALL(delegate, OnWindowRepaint()).Times(1);
284
285 win32window.InjectWindowMessage(WM_PAINT, 0, 0);
286 }
287
288 TEST(FlutterWindowTest, OnThemeChange) {
289 MockFlutterWindow win32window;
290 MockWindowBindingHandlerDelegate delegate;
291 win32window.SetView(&delegate);
292
293 ON_CALL(win32window, GetHighContrastEnabled()).WillByDefault(Return(true));
294 EXPECT_CALL(delegate, UpdateHighContrastEnabled(true)).Times(1);
295
296 win32window.InjectWindowMessage(WM_THEMECHANGED, 0, 0);
297 }
298
299 // The window should return no root accessibility node if
300 // it isn't attached to a view.
301 // Regression test for https://github.com/flutter/flutter/issues/129791
302 TEST(FlutterWindowTest, AccessibilityNodeWithoutView) {
303 MockFlutterWindow win32window;
304
305 EXPECT_EQ(win32window.GetNativeViewAccessible(), nullptr);
306 }
307
308 TEST(FlutterWindowTest, InitialAccessibilityFeatures) {
309 MockFlutterWindow win32window;
310 MockWindowBindingHandlerDelegate delegate;
311 win32window.SetView(&delegate);
312
313 ON_CALL(win32window, GetHighContrastEnabled()).WillByDefault(Return(true));
314 EXPECT_CALL(delegate, UpdateHighContrastEnabled(true)).Times(1);
315
316 win32window.SendInitialAccessibilityFeatures();
317 }
318
319 // Ensure that announcing the alert propagates the message to the alert node.
320 // Different screen readers use different properties for alerts.
321 TEST(FlutterWindowTest, AlertNode) {
322 std::unique_ptr<MockFlutterWindow> win32window =
323 std::make_unique<MockFlutterWindow>();
324 ON_CALL(*win32window, GetPlatformWindow()).WillByDefault(Return(nullptr));
325 ON_CALL(*win32window, GetAxFragmentRootDelegate())
326 .WillByDefault(Return(nullptr));
327 MockFlutterWindowsView view(std::move(win32window));
328 std::wstring message = L"Test alert";
329 EXPECT_CALL(view, NotifyWinEventWrapper(_, ax::mojom::Event::kAlert))
330 .Times(1);
331 view.AnnounceAlert(message);
332
333 IAccessible* alert = view.AlertNode();
334 VARIANT self{.vt = VT_I4, .lVal = CHILDID_SELF};
335 BSTR strptr;
336 alert->get_accName(self, &strptr);
337 EXPECT_EQ(message, strptr);
338
339 alert->get_accDescription(self, &strptr);
340 EXPECT_EQ(message, strptr);
341
342 alert->get_accValue(self, &strptr);
343 EXPECT_EQ(message, strptr);
344
345 VARIANT role;
346 alert->get_accRole(self, &role);
347 EXPECT_EQ(role.vt, VT_I4);
348 EXPECT_EQ(role.lVal, ROLE_SYSTEM_ALERT);
349 }
350
351 TEST(FlutterWindowTest, LifecycleFocusMessages) {
352 MockFlutterWindow win32window;
353 ON_CALL(win32window, GetPlatformWindow).WillByDefault([]() {
354 return reinterpret_cast<HWND>(1);
355 });
356 MockWindowBindingHandlerDelegate delegate;
357 win32window.SetView(&delegate);
358
359 WindowStateEvent last_event;
360 ON_CALL(delegate, OnWindowStateEvent)
361 .WillByDefault([&last_event](HWND hwnd, WindowStateEvent event) {
362 last_event = event;
363 });
364 ON_CALL(win32window, OnWindowStateEvent)
365 .WillByDefault([&](WindowStateEvent event) {
366 win32window.FlutterWindow::OnWindowStateEvent(event);
367 });
368
369 win32window.InjectWindowMessage(WM_SIZE, 0, 0);
370 EXPECT_EQ(last_event, WindowStateEvent::kHide);
371
372 win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
373 EXPECT_EQ(last_event, WindowStateEvent::kShow);
374
375 win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
376 EXPECT_EQ(last_event, WindowStateEvent::kFocus);
377
378 win32window.InjectWindowMessage(WM_KILLFOCUS, 0, 0);
379 EXPECT_EQ(last_event, WindowStateEvent::kUnfocus);
380 }
381
382 TEST(FlutterWindowTest, CachedLifecycleMessage) {
383 MockFlutterWindow win32window;
384 ON_CALL(win32window, GetPlatformWindow).WillByDefault([]() {
385 return reinterpret_cast<HWND>(1);
386 });
387 ON_CALL(win32window, OnWindowStateEvent)
388 .WillByDefault([&](WindowStateEvent event) {
389 win32window.FlutterWindow::OnWindowStateEvent(event);
390 });
391
392 // Restore
393 win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
394
395 // Focus
396 win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
397
398 MockWindowBindingHandlerDelegate delegate;
399 bool focused = false;
400 bool restored = false;
401 ON_CALL(delegate, OnWindowStateEvent)
402 .WillByDefault([&](HWND hwnd, WindowStateEvent event) {
403 if (event == WindowStateEvent::kFocus) {
404 focused = true;
405 } else if (event == WindowStateEvent::kShow) {
406 restored = true;
407 }
408 });
409
410 win32window.SetView(&delegate);
411 EXPECT_TRUE(focused);
412 EXPECT_TRUE(restored);
413 }
414
415 TEST(FlutterWindowTest, PosthumousWindowMessage) {
416 MockWindowBindingHandlerDelegate delegate;
417 int msg_count = 0;
418 HWND hwnd;
419 ON_CALL(delegate, OnWindowStateEvent)
420 .WillByDefault([&](HWND hwnd, WindowStateEvent event) { msg_count++; });
421
422 {
423 MockFlutterWindow win32window(false);
424 ON_CALL(win32window, GetPlatformWindow).WillByDefault([&]() {
425 return win32window.FlutterWindow::GetPlatformWindow();
426 });
427 ON_CALL(win32window, OnWindowStateEvent)
428 .WillByDefault([&](WindowStateEvent event) {
429 win32window.FlutterWindow::OnWindowStateEvent(event);
430 });
431 win32window.SetView(&delegate);
432 win32window.InitializeChild("Title", 1, 1);
433 hwnd = win32window.GetPlatformWindow();
434 SendMessage(hwnd, WM_SIZE, 0, MAKEWORD(1, 1));
435 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
436
437 // By setting this to zero before exiting the scope that contains
438 // win32window, and then checking its value afterwards, enforce that the
439 // window receive and process messages from its destructor without
440 // accessing out-of-bounds memory.
441 msg_count = 0;
442 }
443 EXPECT_GE(msg_count, 1);
444 }
445
446 } // namespace testing
447 } // namespace flutter
flutter::WindowStateEvent
WindowStateEvent
An event representing a change in window state that may update the.
Definition: windows_lifecycle_manager.h:24
flutter::FlutterWindowsView::FlutterWindowsView
FlutterWindowsView(std::unique_ptr< WindowBindingHandler > window_binding)
Definition: flutter_windows_view.cc:41
flutter::WindowStateEvent::kHide
@ kHide
flutter::FlutterWindow::GetDpiScale
virtual float GetDpiScale() override
Definition: flutter_window.cc:175
flutter::FlutterWindow::OnPointerDown
virtual void OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, UINT button)
Definition: flutter_window.cc:227
flutter::testing::MockFlutterWindowsView::MockFlutterWindowsView
MockFlutterWindowsView(std::unique_ptr< WindowBindingHandler > wbh)
Definition: flutter_windows_engine_unittests.cc:605
flutter::Rect
Definition: geometry.h:56
flutter::FlutterWindow::OnBitmapSurfaceUpdated
virtual bool OnBitmapSurfaceUpdated(const void *allocation, size_t row_bytes, size_t height) override
Definition: flutter_window.cc:325
flutter::FlutterWindow::OnPointerMove
virtual void OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, int modifiers_state)
Definition: flutter_window.cc:218
flutter::FlutterWindow::OnPointerLeave
virtual void OnPointerLeave(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id)
Definition: flutter_window.cc:253
flutter::FlutterWindow::OnPointerUp
virtual void OnPointerUp(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, UINT button)
Definition: flutter_window.cc:240
flutter::WindowStateEvent::kFocus
@ kFocus
flutter::FlutterWindow::SetView
virtual void SetView(WindowBindingHandlerDelegate *view) override
Definition: flutter_window.cc:156
flutter::WindowStateEvent::kShow
@ kShow
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::testing::MockFlutterWindowsView::MOCK_METHOD
MOCK_METHOD(void, NotifyWinEventWrapper,(ui::AXPlatformNodeWin *, ax::mojom::Event),(override))
flutter::Point
Definition: geometry.h:13
flutter::WindowStateEvent::kUnfocus
@ kUnfocus
flutter::testing::TEST
TEST(AccessibilityBridgeWindows, GetParent)
Definition: accessibility_bridge_windows_unittests.cc:233
message
Win32Message message
Definition: keyboard_unittests.cc:137
flutter::FlutterWindow
Definition: flutter_window.h:35
flutter::Size
Definition: geometry.h:33