| 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::AnyNumber;
16 using testing::Invoke;
17 using testing::Return;
18
19 namespace flutter {
20 namespace testing {
21
22 namespace {
23 static constexpr int32_t kDefaultPointerDeviceId = 0;
24
25 class MockFlutterWindow : public FlutterWindow {
26 public:
27 MockFlutterWindow(bool reset_view_on_exit = true)
28 : FlutterWindow(800, 600), reset_view_on_exit_(reset_view_on_exit) {
29 ON_CALL(*this, GetDpiScale())
30 .WillByDefault(Return(this->FlutterWindow::GetDpiScale()));
31 }
32 virtual ~MockFlutterWindow() {
33 if (reset_view_on_exit_) {
34 SetView(nullptr);
35 }
36 }
37
38 // Wrapper for GetCurrentDPI() which is a protected method.
39 UINT GetDpi() { return GetCurrentDPI(); }
40
41 // Simulates a WindowProc message from the OS.
42 LRESULT InjectWindowMessage(UINT const message,
43 WPARAM const wparam,
44 LPARAM const lparam) {
45 return HandleMessage(message, wparam, lparam);
46 }
47
48 MOCK_METHOD(void, OnDpiScale, (unsigned int), (override));
49 MOCK_METHOD(void, OnResize, (unsigned int, unsigned int), (override));
50 MOCK_METHOD(void,
51 OnPointerMove,
52 (double, double, FlutterPointerDeviceKind, int32_t, int),
53 (override));
54 MOCK_METHOD(void,
55 OnPointerDown,
56 (double, double, FlutterPointerDeviceKind, int32_t, UINT),
57 (override));
58 MOCK_METHOD(void,
59 OnPointerUp,
60 (double, double, FlutterPointerDeviceKind, int32_t, UINT),
61 (override));
62 MOCK_METHOD(void,
63 OnPointerLeave,
64 (double, double, FlutterPointerDeviceKind, int32_t),
65 (override));
66 MOCK_METHOD(void, OnSetCursor, (), (override));
67 MOCK_METHOD(float, GetScrollOffsetMultiplier, (), (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, GetWindowHandle, (), (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 EXPECT_CALL(win32window, GetDpiScale()).WillOnce(Return(1.0));
138
139 Rect cursor_rect(Point(10, 20), Size(30, 40));
140 EXPECT_CALL(win32window, UpdateCursorRect(cursor_rect)).Times(1);
141
142 win32window.OnCursorRectUpdated(cursor_rect);
143 }
144
145 // Tests that composing rect updates are transformed from Flutter logical
146 // coordinates to device coordinates and passed to the text input manager
147 // when the DPI scale is 150% (144 DPI).
148 TEST(FlutterWindowTest, OnCursorRectUpdatedHighDPI) {
149 MockFlutterWindow win32window;
150 EXPECT_CALL(win32window, GetDpiScale()).WillOnce(Return(1.5));
151
152 Rect expected_cursor_rect(Point(15, 30), Size(45, 60));
153 EXPECT_CALL(win32window, UpdateCursorRect(expected_cursor_rect)).Times(1);
154
155 Rect cursor_rect(Point(10, 20), Size(30, 40));
156 win32window.OnCursorRectUpdated(cursor_rect);
157 }
158
159 TEST(FlutterWindowTest, OnPointerStarSendsDeviceType) {
160 FlutterWindow win32window(100, 100);
161 MockWindowBindingHandlerDelegate delegate;
162 EXPECT_CALL(delegate, OnWindowStateEvent).Times(AnyNumber());
163 win32window.SetView(&delegate);
164
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 EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
264 win32window.SetView(&delegate);
265
266 ON_CALL(win32window, GetScrollOffsetMultiplier())
267 .WillByDefault(Return(120.0f));
268 EXPECT_CALL(win32window, GetScrollOffsetMultiplier()).Times(1);
269
270 EXPECT_CALL(delegate,
271 OnScroll(_, _, 0, 0, 120.0f, kFlutterPointerDeviceKindMouse,
272 kDefaultPointerDeviceId))
273 .Times(1);
274
275 win32window.OnScroll(0.0f, 0.0f, kFlutterPointerDeviceKindMouse,
276 kDefaultPointerDeviceId);
277 }
278
279 TEST(FlutterWindowTest, OnWindowRepaint) {
280 MockFlutterWindow win32window;
281 MockWindowBindingHandlerDelegate delegate;
282 EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
283 win32window.SetView(&delegate);
284
285 EXPECT_CALL(delegate, OnWindowRepaint()).Times(1);
286
287 win32window.InjectWindowMessage(WM_PAINT, 0, 0);
288 }
289
290 TEST(FlutterWindowTest, OnThemeChange) {
291 MockFlutterWindow win32window;
292 MockWindowBindingHandlerDelegate delegate;
293 EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
294 win32window.SetView(&delegate);
295
296 EXPECT_CALL(delegate, OnHighContrastChanged).Times(1);
297
298 win32window.InjectWindowMessage(WM_THEMECHANGED, 0, 0);
299 }
300
301 // The window should return no root accessibility node if
302 // it isn't attached to a view.
303 // Regression test for https://github.com/flutter/flutter/issues/129791
304 TEST(FlutterWindowTest, AccessibilityNodeWithoutView) {
305 MockFlutterWindow win32window;
306
307 EXPECT_EQ(win32window.GetNativeViewAccessible(), nullptr);
308 }
309
310 // Ensure that announcing the alert propagates the message to the alert node.
311 // Different screen readers use different properties for alerts.
312 TEST(FlutterWindowTest, AlertNode) {
313 std::unique_ptr<MockFlutterWindow> win32window =
314 std::make_unique<MockFlutterWindow>();
315 EXPECT_CALL(*win32window.get(), GetAxFragmentRootDelegate())
316 .WillRepeatedly(Return(nullptr));
317 EXPECT_CALL(*win32window.get(), OnWindowStateEvent).Times(AnyNumber());
318 MockFlutterWindowsView view(std::move(win32window));
319 std::wstring message = L"Test alert";
320 EXPECT_CALL(view, NotifyWinEventWrapper(_, ax::mojom::Event::kAlert))
321 .Times(1);
322 view.AnnounceAlert(message);
323
324 IAccessible* alert = view.AlertNode();
325 VARIANT self{.vt = VT_I4, .lVal = CHILDID_SELF};
326 BSTR strptr;
327 alert->get_accName(self, &strptr);
328 EXPECT_EQ(message, strptr);
329
330 alert->get_accDescription(self, &strptr);
331 EXPECT_EQ(message, strptr);
332
333 alert->get_accValue(self, &strptr);
334 EXPECT_EQ(message, strptr);
335
336 VARIANT role;
337 alert->get_accRole(self, &role);
338 EXPECT_EQ(role.vt, VT_I4);
339 EXPECT_EQ(role.lVal, ROLE_SYSTEM_ALERT);
340 }
341
342 TEST(FlutterWindowTest, LifecycleFocusMessages) {
343 MockFlutterWindow win32window;
344 EXPECT_CALL(win32window, GetWindowHandle)
345 .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
346 MockWindowBindingHandlerDelegate delegate;
347
348 WindowStateEvent last_event;
349 EXPECT_CALL(delegate, OnWindowStateEvent)
350 .WillRepeatedly([&last_event](HWND hwnd, WindowStateEvent event) {
351 last_event = event;
352 });
353 EXPECT_CALL(win32window, OnWindowStateEvent)
354 .WillRepeatedly([&](WindowStateEvent event) {
355 win32window.FlutterWindow::OnWindowStateEvent(event);
356 });
357 EXPECT_CALL(win32window, OnResize).Times(AnyNumber());
358
359 win32window.SetView(&delegate);
360
361 win32window.InjectWindowMessage(WM_SIZE, 0, 0);
362 EXPECT_EQ(last_event, WindowStateEvent::kHide);
363
364 win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
365 EXPECT_EQ(last_event, WindowStateEvent::kShow);
366
367 win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
368 EXPECT_EQ(last_event, WindowStateEvent::kFocus);
369
370 win32window.InjectWindowMessage(WM_KILLFOCUS, 0, 0);
371 EXPECT_EQ(last_event, WindowStateEvent::kUnfocus);
372 }
373
374 TEST(FlutterWindowTest, CachedLifecycleMessage) {
375 MockFlutterWindow win32window;
376 EXPECT_CALL(win32window, GetWindowHandle)
377 .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
378 EXPECT_CALL(win32window, OnWindowStateEvent)
379 .WillRepeatedly([&](WindowStateEvent event) {
380 win32window.FlutterWindow::OnWindowStateEvent(event);
381 });
382 EXPECT_CALL(win32window, OnResize).Times(1);
383
384 // Restore
385 win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
386
387 // Focus
388 win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
389
390 MockWindowBindingHandlerDelegate delegate;
391 bool focused = false;
392 bool restored = false;
393 EXPECT_CALL(delegate, OnWindowStateEvent)
394 .WillRepeatedly([&](HWND hwnd, WindowStateEvent event) {
395 if (event == WindowStateEvent::kFocus) {
396 focused = true;
397 } else if (event == WindowStateEvent::kShow) {
398 restored = true;
399 }
400 });
401
402 win32window.SetView(&delegate);
403 EXPECT_TRUE(focused);
404 EXPECT_TRUE(restored);
405 }
406
407 TEST(FlutterWindowTest, PosthumousWindowMessage) {
408 MockWindowBindingHandlerDelegate delegate;
409 int msg_count = 0;
410 HWND hwnd;
411 EXPECT_CALL(delegate, OnWindowStateEvent)
412 .WillRepeatedly([&](HWND hwnd, WindowStateEvent event) { msg_count++; });
413
414 {
415 MockFlutterWindow win32window(false);
416 EXPECT_CALL(win32window, GetWindowHandle).WillRepeatedly([&]() {
417 return win32window.FlutterWindow::GetWindowHandle();
418 });
419 EXPECT_CALL(win32window, OnWindowStateEvent)
420 .WillRepeatedly([&](WindowStateEvent event) {
421 win32window.FlutterWindow::OnWindowStateEvent(event);
422 });
423 EXPECT_CALL(win32window, OnResize).Times(AnyNumber());
424 win32window.SetView(&delegate);
425 win32window.InitializeChild("Title", 1, 1);
426 hwnd = win32window.GetWindowHandle();
427 SendMessage(hwnd, WM_SIZE, 0, MAKEWORD(1, 1));
428 SendMessage(hwnd, WM_SETFOCUS, 0, 0);
429
430 // By setting this to zero before exiting the scope that contains
431 // win32window, and then checking its value afterwards, enforce that the
432 // window receive and process messages from its destructor without
433 // accessing out-of-bounds memory.
434 msg_count = 0;
435 }
436 EXPECT_GE(msg_count, 1);
437 }
438
439 } // namespace testing
440 } // namespace flutter
flutter::WindowStateEvent
WindowStateEvent
An event representing a change in window state that may update the.
Definition: windows_lifecycle_manager.h:24
flutter::WindowStateEvent::kHide
@ kHide
flutter::FlutterWindow::GetDpiScale
virtual float GetDpiScale() override
Definition: flutter_window.cc:167
flutter::FlutterWindow::OnPointerDown
virtual void OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, UINT button)
Definition: flutter_window.cc:213
flutter::testing::MockFlutterWindowsView::MockFlutterWindowsView
MockFlutterWindowsView(std::unique_ptr< WindowBindingHandler > wbh)
Definition: flutter_windows_engine_unittests.cc:621
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:311
flutter::FlutterWindow::OnPointerMove
virtual void OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, int modifiers_state)
Definition: flutter_window.cc:204
flutter::FlutterWindow::OnPointerLeave
virtual void OnPointerLeave(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id)
Definition: flutter_window.cc:239
flutter::FlutterWindow::OnPointerUp
virtual void OnPointerUp(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, UINT button)
Definition: flutter_window.cc:226
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:235
message
Win32Message message
Definition: keyboard_unittests.cc:137
flutter::FlutterWindow
Definition: flutter_window.h:35
flutter::Size
Definition: geometry.h:33
flutter::FlutterWindowsView::FlutterWindowsView
FlutterWindowsView(std::unique_ptr< WindowBindingHandler > window_binding, std::shared_ptr< WindowsProcTable > windows_proc_table=nullptr)
Definition: flutter_windows_view.cc:72