Flutter Engine
 
Loading...
Searching...
No Matches
flutter_window_unittests.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
12
13#include "gmock/gmock.h"
14#include "gtest/gtest.h"
15
16namespace flutter {
17namespace testing {
18
19using ::testing::_;
20using ::testing::AnyNumber;
21using ::testing::Eq;
22using ::testing::Invoke;
23using ::testing::Return;
24
25namespace {
26static constexpr int32_t kDefaultPointerDeviceId = 0;
27
28class MockFlutterWindow : public FlutterWindow {
29 public:
30 MockFlutterWindow(bool reset_view_on_exit = true)
31 : reset_view_on_exit_(reset_view_on_exit) {
32 ON_CALL(*this, GetDpiScale())
33 .WillByDefault(Return(this->FlutterWindow::GetDpiScale()));
34 }
35 virtual ~MockFlutterWindow() {
36 if (reset_view_on_exit_) {
37 SetView(nullptr);
38 }
39 }
40
41 // Wrapper for GetCurrentDPI() which is a protected method.
42 UINT GetDpi() { return GetCurrentDPI(); }
43
44 // Simulates a WindowProc message from the OS.
45 LRESULT InjectWindowMessage(UINT const message,
46 WPARAM const wparam,
47 LPARAM const lparam) {
48 return HandleMessage(message, wparam, lparam);
49 }
50
51 MOCK_METHOD(void, OnDpiScale, (unsigned int), (override));
52 MOCK_METHOD(void, OnResize, (unsigned int, unsigned int), (override));
53 MOCK_METHOD(void,
54 OnPointerMove,
55 (double, double, FlutterPointerDeviceKind, int32_t, int),
56 (override));
57 MOCK_METHOD(void,
58 OnPointerDown,
59 (double, double, FlutterPointerDeviceKind, int32_t, UINT),
60 (override));
61 MOCK_METHOD(void,
62 OnPointerUp,
63 (double, double, FlutterPointerDeviceKind, int32_t, UINT),
64 (override));
65 MOCK_METHOD(void,
66 OnPointerLeave,
67 (double, double, FlutterPointerDeviceKind, int32_t),
68 (override));
69 MOCK_METHOD(float, GetScrollOffsetMultiplier, (), (override));
70 MOCK_METHOD(float, GetDpiScale, (), (override));
71 MOCK_METHOD(void, UpdateCursorRect, (const Rect&), (override));
72 MOCK_METHOD(void, OnResetImeComposing, (), (override));
73 MOCK_METHOD(UINT, Win32DispatchMessage, (UINT, WPARAM, LPARAM), (override));
74 MOCK_METHOD(BOOL, Win32PeekMessage, (LPMSG, UINT, UINT, UINT), (override));
75 MOCK_METHOD(uint32_t, Win32MapVkToChar, (uint32_t), (override));
76 MOCK_METHOD(HWND, GetWindowHandle, (), (override));
78 GetAxFragmentRootDelegate,
79 (),
80 (override));
81 MOCK_METHOD(void, OnWindowStateEvent, (WindowStateEvent), (override));
82
83 protected:
84 // |KeyboardManager::WindowDelegate|
85 LRESULT Win32DefWindowProc(HWND hWnd,
86 UINT Msg,
87 WPARAM wParam,
88 LPARAM lParam) override {
89 return kWmResultDefault;
90 }
91
92 private:
93 bool reset_view_on_exit_;
94 FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindow);
95};
96
97class MockFlutterWindowsView : public FlutterWindowsView {
98 public:
99 MockFlutterWindowsView(FlutterWindowsEngine* engine,
100 std::unique_ptr<WindowBindingHandler> window_binding)
101 : FlutterWindowsView(kImplicitViewId, engine, std::move(window_binding)) {
102 }
104
105 MOCK_METHOD(void,
106 NotifyWinEventWrapper,
107 (ui::AXPlatformNodeWin*, ax::mojom::Event),
108 (override));
109
110 private:
111 FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView);
112};
113
114class FlutterWindowTest : public WindowsTest {};
115
116} // namespace
117
118TEST_F(FlutterWindowTest, CreateDestroy) {
119 std::unique_ptr<FlutterWindowsEngine> engine =
120 FlutterWindowsEngineBuilder{GetContext()}.Build();
121 FlutterWindow window(800, 600, engine->display_manager());
122 ASSERT_TRUE(TRUE);
123}
124
125TEST_F(FlutterWindowTest, OnBitmapSurfaceUpdated) {
126 std::unique_ptr<FlutterWindowsEngine> engine =
127 FlutterWindowsEngineBuilder{GetContext()}.Build();
128 FlutterWindow win32window(100, 100, engine->display_manager());
129 int old_handle_count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
130
131 constexpr size_t row_bytes = 100 * 4;
132 constexpr size_t height = 100;
133 std::array<char, row_bytes * height> allocation;
134 win32window.OnBitmapSurfaceUpdated(allocation.data(), row_bytes, height);
135
136 int new_handle_count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
137 // Check GDI resources leak
138 EXPECT_EQ(old_handle_count, new_handle_count);
139}
140
141// Tests that composing rect updates are transformed from Flutter logical
142// coordinates to device coordinates and passed to the text input manager
143// when the DPI scale is 100% (96 DPI).
144TEST_F(FlutterWindowTest, OnCursorRectUpdatedRegularDPI) {
145 MockFlutterWindow win32window;
146 EXPECT_CALL(win32window, GetDpiScale()).WillOnce(Return(1.0));
147
148 Rect cursor_rect(Point(10, 20), Size(30, 40));
149 EXPECT_CALL(win32window, UpdateCursorRect(cursor_rect)).Times(1);
150
151 win32window.OnCursorRectUpdated(cursor_rect);
152}
153
154// Tests that composing rect updates are transformed from Flutter logical
155// coordinates to device coordinates and passed to the text input manager
156// when the DPI scale is 150% (144 DPI).
157TEST_F(FlutterWindowTest, OnCursorRectUpdatedHighDPI) {
158 MockFlutterWindow win32window;
159 EXPECT_CALL(win32window, GetDpiScale()).WillOnce(Return(1.5));
160
161 Rect expected_cursor_rect(Point(15, 30), Size(45, 60));
162 EXPECT_CALL(win32window, UpdateCursorRect(expected_cursor_rect)).Times(1);
163
164 Rect cursor_rect(Point(10, 20), Size(30, 40));
165 win32window.OnCursorRectUpdated(cursor_rect);
166}
167
168TEST_F(FlutterWindowTest, OnPointerStarSendsDeviceType) {
169 std::unique_ptr<FlutterWindowsEngine> engine =
170 FlutterWindowsEngineBuilder{GetContext()}.Build();
171 FlutterWindow win32window(100, 100, engine->display_manager());
173 EXPECT_CALL(delegate, OnWindowStateEvent).Times(AnyNumber());
174 win32window.SetView(&delegate);
175
176 // Move
177 EXPECT_CALL(delegate,
178 OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
179 kDefaultPointerDeviceId, 0))
180 .Times(1);
181 EXPECT_CALL(delegate,
182 OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
183 kDefaultPointerDeviceId, 0))
184 .Times(1);
185 EXPECT_CALL(delegate,
186 OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
187 kDefaultPointerDeviceId, 0))
188 .Times(1);
189
190 // Down
191 EXPECT_CALL(
192 delegate,
193 OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
194 kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
195 .Times(1);
196 EXPECT_CALL(
197 delegate,
198 OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
199 kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
200 .Times(1);
201 EXPECT_CALL(
202 delegate,
203 OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
204 kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
205 .Times(1);
206
207 // Up
208 EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
209 kDefaultPointerDeviceId,
211 .Times(1);
212 EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
213 kDefaultPointerDeviceId,
215 .Times(1);
216 EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,
217 kDefaultPointerDeviceId,
219 .Times(1);
220
221 // Leave
222 EXPECT_CALL(delegate,
223 OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindMouse,
224 kDefaultPointerDeviceId))
225 .Times(1);
226 EXPECT_CALL(delegate,
227 OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindTouch,
228 kDefaultPointerDeviceId))
229 .Times(1);
230 EXPECT_CALL(delegate,
231 OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindStylus,
232 kDefaultPointerDeviceId))
233 .Times(1);
234
235 win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
236 kDefaultPointerDeviceId, 0);
237 win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
238 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
239 win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
240 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
241 win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindMouse,
242 kDefaultPointerDeviceId);
243
244 // Touch
245 win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
246 kDefaultPointerDeviceId, 0);
247 win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
248 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
249 win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
250 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
251 win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindTouch,
252 kDefaultPointerDeviceId);
253
254 // Pen
255 win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
256 kDefaultPointerDeviceId, 0);
257 win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
258 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
259 win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,
260 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
261 win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindStylus,
262 kDefaultPointerDeviceId);
263
264 // Destruction of win32window sends a HIDE update. In situ, the window is
265 // owned by the delegate, and so is destructed first. Not so here.
266 win32window.SetView(nullptr);
267}
268
269// Tests that calls to OnScroll in turn calls GetScrollOffsetMultiplier
270// for mapping scroll ticks to pixels.
271TEST_F(FlutterWindowTest, OnScrollCallsGetScrollOffsetMultiplier) {
272 MockFlutterWindow win32window;
274 EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
275 win32window.SetView(&delegate);
276
277 EXPECT_CALL(win32window, GetWindowHandle).WillOnce([&win32window]() {
278 return win32window.FlutterWindow::GetWindowHandle();
279 });
280 EXPECT_CALL(win32window, GetScrollOffsetMultiplier).WillOnce(Return(120.0f));
281
282 EXPECT_CALL(delegate,
283 OnScroll(_, _, 0, 0, 120.0f, kFlutterPointerDeviceKindMouse,
284 kDefaultPointerDeviceId))
285 .Times(1);
286
287 win32window.OnScroll(0.0f, 0.0f, kFlutterPointerDeviceKindMouse,
288 kDefaultPointerDeviceId);
289}
290
291TEST_F(FlutterWindowTest, OnWindowRepaint) {
292 MockFlutterWindow win32window;
294 EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
295 win32window.SetView(&delegate);
296
297 EXPECT_CALL(delegate, OnWindowRepaint()).Times(1);
298
299 win32window.InjectWindowMessage(WM_PAINT, 0, 0);
300}
301
302TEST_F(FlutterWindowTest, OnThemeChange) {
303 MockFlutterWindow win32window;
305 EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
306 win32window.SetView(&delegate);
307
308 EXPECT_CALL(delegate, OnHighContrastChanged).Times(1);
309
310 win32window.InjectWindowMessage(WM_THEMECHANGED, 0, 0);
311}
312
313// The window should return no root accessibility node if
314// it isn't attached to a view.
315// Regression test for https://github.com/flutter/flutter/issues/129791
316TEST_F(FlutterWindowTest, AccessibilityNodeWithoutView) {
317 MockFlutterWindow win32window;
318
319 EXPECT_EQ(win32window.GetNativeViewAccessible(), nullptr);
320}
321
322// Ensure that announcing the alert propagates the message to the alert node.
323// Different screen readers use different properties for alerts.
324TEST_F(FlutterWindowTest, AlertNode) {
325 std::unique_ptr<FlutterWindowsEngine> engine =
326 FlutterWindowsEngineBuilder{GetContext()}.Build();
327 auto win32window = std::make_unique<MockFlutterWindow>();
328 EXPECT_CALL(*win32window.get(), GetAxFragmentRootDelegate())
329 .WillRepeatedly(Return(nullptr));
330 EXPECT_CALL(*win32window.get(), OnWindowStateEvent).Times(AnyNumber());
331 EXPECT_CALL(*win32window.get(), GetWindowHandle).Times(AnyNumber());
332 MockFlutterWindowsView view{engine.get(), std::move(win32window)};
333 std::wstring message = L"Test alert";
334 EXPECT_CALL(view, NotifyWinEventWrapper(_, ax::mojom::Event::kAlert))
335 .Times(1);
336 view.AnnounceAlert(message);
337
338 IAccessible* alert = view.AlertNode();
339 VARIANT self{.vt = VT_I4, .lVal = CHILDID_SELF};
340 BSTR strptr;
341 alert->get_accName(self, &strptr);
342 EXPECT_EQ(message, strptr);
343
344 alert->get_accDescription(self, &strptr);
345 EXPECT_EQ(message, strptr);
346
347 alert->get_accValue(self, &strptr);
348 EXPECT_EQ(message, strptr);
349
350 VARIANT role;
351 alert->get_accRole(self, &role);
352 EXPECT_EQ(role.vt, VT_I4);
353 EXPECT_EQ(role.lVal, ROLE_SYSTEM_ALERT);
354}
355
356TEST_F(FlutterWindowTest, LifecycleFocusMessages) {
357 MockFlutterWindow win32window;
358 EXPECT_CALL(win32window, GetWindowHandle)
359 .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
361
362 WindowStateEvent last_event;
363 EXPECT_CALL(delegate, OnWindowStateEvent)
364 .WillRepeatedly([&last_event](HWND hwnd, WindowStateEvent event) {
365 last_event = event;
366 });
367 EXPECT_CALL(win32window, OnWindowStateEvent)
368 .WillRepeatedly([&](WindowStateEvent event) {
369 win32window.FlutterWindow::OnWindowStateEvent(event);
370 });
371 EXPECT_CALL(win32window, OnResize).Times(AnyNumber());
372
373 win32window.SetView(&delegate);
374
375 win32window.InjectWindowMessage(WM_SIZE, 0, 0);
376 EXPECT_EQ(last_event, WindowStateEvent::kHide);
377
378 win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
379 EXPECT_EQ(last_event, WindowStateEvent::kShow);
380
381 EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kFocused),
383 .Times(1);
384 win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
385 EXPECT_EQ(last_event, WindowStateEvent::kFocus);
386
387 EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kUnfocused),
389 .Times(1);
390 win32window.InjectWindowMessage(WM_KILLFOCUS, 0, 0);
391 EXPECT_EQ(last_event, WindowStateEvent::kUnfocus);
392}
393
394TEST_F(FlutterWindowTest, CachedLifecycleMessage) {
395 MockFlutterWindow win32window;
396 EXPECT_CALL(win32window, GetWindowHandle)
397 .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
398 EXPECT_CALL(win32window, OnWindowStateEvent)
399 .WillRepeatedly([&](WindowStateEvent event) {
400 win32window.FlutterWindow::OnWindowStateEvent(event);
401 });
402 EXPECT_CALL(win32window, OnResize).Times(1);
403
404 // Restore
405 win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
406
407 // Focus
408 win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
409
411 bool focused = false;
412 bool restored = false;
413 EXPECT_CALL(delegate, OnWindowStateEvent)
414 .WillRepeatedly([&](HWND hwnd, WindowStateEvent event) {
415 if (event == WindowStateEvent::kFocus) {
416 focused = true;
417 } else if (event == WindowStateEvent::kShow) {
418 restored = true;
419 }
420 });
421
422 EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kFocused),
424 .Times(1);
425 win32window.SetView(&delegate);
426 EXPECT_TRUE(focused);
427 EXPECT_TRUE(restored);
428}
429
430} // namespace testing
431} // namespace flutter
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 OnPointerUp(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, UINT button)
virtual void OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, UINT button)
virtual void OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, int modifiers_state)
virtual void SetView(WindowBindingHandlerDelegate *view) override
virtual float GetDpiScale() override
FlutterWindowsView(FlutterViewId view_id, FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > window_binding, 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)
@ kUnfocused
Specifies that a view does not have platform focus.
Definition embedder.h:1191
@ kFocused
Specifies that a view has platform focus.
Definition embedder.h:1194
@ kUndefined
Definition embedder.h:1175
@ kFlutterPointerButtonMousePrimary
Definition embedder.h:1289
FlutterPointerDeviceKind
The device type that created a pointer event.
Definition embedder.h:1279
@ kFlutterPointerDeviceKindTouch
Definition embedder.h:1281
@ kFlutterPointerDeviceKindStylus
Definition embedder.h:1282
@ kFlutterPointerDeviceKindMouse
Definition embedder.h:1280
GLFWwindow * window
Definition main.cc:60
FlutterEngine engine
Definition main.cc:84
FlView * view
return TRUE
G_BEGIN_DECLS GBytes * message
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition macros.h:27
TEST_F(DisplayListTest, Defaults)
constexpr LRESULT kWmResultDefault
Definition wm_builders.h:15
constexpr int64_t kImplicitViewId
WindowStateEvent
An event representing a change in window state that may update the.
Definition ref_ptr.h:261
int32_t height
int BOOL
struct tagMSG * LPMSG
LONG_PTR LRESULT
unsigned int UINT
LONG_PTR LPARAM
UINT_PTR WPARAM