Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
window_manager_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
9#include "gtest/gtest.h"
10
11namespace flutter {
12namespace testing {
13
14namespace {
15
16class WindowManagerTest : public WindowsTest {
17 public:
18 WindowManagerTest() = default;
19 virtual ~WindowManagerTest() = default;
20
21 protected:
22 void SetUp() override {
23 auto& context = GetContext();
24 FlutterWindowsEngineBuilder builder(context);
25 ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
26
27 engine_ = builder.Build();
28 ASSERT_TRUE(engine_);
29
30 engine_->SetRootIsolateCreateCallback(context.GetRootIsolateCallback());
31 ASSERT_TRUE(engine_->Run("testWindowController"));
32
33 bool signalled = false;
34 context.AddNativeFunction(
35 "Signal", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
36 isolate_ = flutter::Isolate::Current();
37 signalled = true;
38 }));
39 while (!signalled) {
40 engine_->task_runner()->ProcessTasks();
41 }
42 }
43
44 void TearDown() override { engine_->Stop(); }
45
46 int64_t engine_id() { return reinterpret_cast<int64_t>(engine_.get()); }
47 flutter::Isolate& isolate() { return *isolate_; }
48 RegularWindowCreationRequest* regular_creation_request() {
49 return &regular_creation_request_;
50 }
51 FlutterWindowsEngine* engine() { return engine_.get(); }
52
53 private:
54 std::unique_ptr<FlutterWindowsEngine> engine_;
55 std::optional<flutter::Isolate> isolate_;
56 RegularWindowCreationRequest regular_creation_request_{
57 .preferred_size =
58 {
59 .has_preferred_view_size = true,
60 .preferred_view_width = 800,
61 .preferred_view_height = 600,
62 },
63 };
64
65 FML_DISALLOW_COPY_AND_ASSIGN(WindowManagerTest);
66};
67
68} // namespace
69
70TEST_F(WindowManagerTest, WindowingInitialize) {
71 IsolateScope isolate_scope(isolate());
72
73 static bool received_message = false;
74 WindowingInitRequest init_request{
75 .on_message = [](WindowsMessage* message) { received_message = true; }};
76
77 InternalFlutterWindows_WindowManager_Initialize(engine_id(), &init_request);
78 const int64_t view_id =
80 engine_id(), regular_creation_request());
82 engine_id(), view_id));
83
84 EXPECT_TRUE(received_message);
85}
86
87TEST_F(WindowManagerTest, CreateRegularWindow) {
88 IsolateScope isolate_scope(isolate());
89
90 const int64_t view_id =
92 engine_id(), regular_creation_request());
93 EXPECT_EQ(view_id, 0);
94}
95
96TEST_F(WindowManagerTest, GetWindowHandle) {
97 IsolateScope isolate_scope(isolate());
98
99 const int64_t view_id =
101 engine_id(), regular_creation_request());
102 const HWND window_handle =
104 view_id);
105 EXPECT_NE(window_handle, nullptr);
106}
107
108TEST_F(WindowManagerTest, GetWindowSize) {
109 IsolateScope isolate_scope(isolate());
110
111 const int64_t view_id =
113 engine_id(), regular_creation_request());
114 const HWND window_handle =
116 view_id);
117
120
121 EXPECT_EQ(size.width,
122 regular_creation_request()->preferred_size.preferred_view_width);
123 EXPECT_EQ(size.height,
124 regular_creation_request()->preferred_size.preferred_view_height);
125}
126
127TEST_F(WindowManagerTest, SetWindowSize) {
128 IsolateScope isolate_scope(isolate());
129
130 const int64_t view_id =
132 engine_id(), regular_creation_request());
133 const HWND window_handle =
135 view_id);
136
137 WindowSizeRequest requestedSize{
138
140 .preferred_view_width = 640,
141 .preferred_view_height = 480,
142 };
144 &requestedSize);
145
146 ActualWindowSize actual_size =
148 EXPECT_EQ(actual_size.width, 640);
149 EXPECT_EQ(actual_size.height, 480);
150}
151
152TEST_F(WindowManagerTest, CanConstrainByMinimiumSize) {
153 IsolateScope isolate_scope(isolate());
154
155 const int64_t view_id =
157 engine_id(), regular_creation_request());
158 const HWND window_handle =
160 view_id);
161 WindowConstraints constraints{.has_view_constraints = true,
162 .view_min_width = 900,
163 .view_min_height = 700,
164 .view_max_width = 10000,
165 .view_max_height = 10000};
167 &constraints);
168
169 ActualWindowSize actual_size =
171 EXPECT_EQ(actual_size.width, 900);
172 EXPECT_EQ(actual_size.height, 700);
173}
174
175TEST_F(WindowManagerTest, CanConstrainByMaximumSize) {
176 IsolateScope isolate_scope(isolate());
177
178 const int64_t view_id =
180 engine_id(), regular_creation_request());
181 const HWND window_handle =
183 view_id);
184 WindowConstraints constraints{.has_view_constraints = true,
185 .view_min_width = 0,
186 .view_min_height = 0,
187 .view_max_width = 500,
188 .view_max_height = 500};
190 &constraints);
191
192 ActualWindowSize actual_size =
194 EXPECT_EQ(actual_size.width, 500);
195 EXPECT_EQ(actual_size.height, 500);
196}
197
198TEST_F(WindowManagerTest, CanFullscreenWindow) {
199 IsolateScope isolate_scope(isolate());
200
201 const int64_t view_id =
203 engine_id(), regular_creation_request());
204 const HWND window_handle =
206 view_id);
207
208 FullscreenRequest request{.fullscreen = true, .has_display_id = false};
210
211 int screen_width = GetSystemMetrics(SM_CXSCREEN);
212 int screen_height = GetSystemMetrics(SM_CYSCREEN);
213 ActualWindowSize actual_size =
215 EXPECT_EQ(actual_size.width, screen_width);
216 EXPECT_EQ(actual_size.height, screen_height);
217 EXPECT_TRUE(
219}
220
221TEST_F(WindowManagerTest, CanUnfullscreenWindow) {
222 IsolateScope isolate_scope(isolate());
223
224 const int64_t view_id =
226 engine_id(), regular_creation_request());
227 const HWND window_handle =
229 view_id);
230
231 FullscreenRequest request{.fullscreen = true, .has_display_id = false};
233
234 request.fullscreen = false;
236
237 ActualWindowSize actual_size =
239 EXPECT_EQ(actual_size.width, 800);
240 EXPECT_EQ(actual_size.height, 600);
241 EXPECT_FALSE(
243}
244
245TEST_F(WindowManagerTest, CanSetWindowSizeWhileFullscreen) {
246 IsolateScope isolate_scope(isolate());
247
248 const int64_t view_id =
250 engine_id(), regular_creation_request());
251 const HWND window_handle =
253 view_id);
254
255 FullscreenRequest request{.fullscreen = true, .has_display_id = false};
257
258 WindowSizeRequest requestedSize{
259
261 .preferred_view_width = 500,
262 .preferred_view_height = 500,
263 };
265 &requestedSize);
266
267 request.fullscreen = false;
269
270 ActualWindowSize actual_size =
272 EXPECT_EQ(actual_size.width, 500);
273 EXPECT_EQ(actual_size.height, 500);
274}
275
276TEST_F(WindowManagerTest, CanSetWindowConstraintsWhileFullscreen) {
277 IsolateScope isolate_scope(isolate());
278
279 const int64_t view_id =
281 engine_id(), regular_creation_request());
282 const HWND window_handle =
284 view_id);
285
286 FullscreenRequest request{.fullscreen = true, .has_display_id = false};
288
289 WindowConstraints constraints{.has_view_constraints = true,
290 .view_min_width = 0,
291 .view_min_height = 0,
292 .view_max_width = 500,
293 .view_max_height = 500};
295 &constraints);
296
297 request.fullscreen = false;
299
300 ActualWindowSize actual_size =
302 EXPECT_EQ(actual_size.width, 500);
303 EXPECT_EQ(actual_size.height, 500);
304}
305
306TEST_F(WindowManagerTest, CreateModelessDialogWindow) {
307 IsolateScope isolate_scope(isolate());
308 DialogWindowCreationRequest creation_request{
310 .preferred_view_width = 800,
311 .preferred_view_height = 600},
312 .preferred_constraints = {.has_view_constraints = false},
313 .title = L"Hello World",
314 .parent_or_null = nullptr};
315 const int64_t view_id =
317 engine_id(), &creation_request);
318 EXPECT_EQ(view_id, 0);
319}
320
321TEST_F(WindowManagerTest, CreateModalDialogWindow) {
322 IsolateScope isolate_scope(isolate());
323
324 const int64_t parent_view_id =
326 engine_id(), regular_creation_request());
327 const HWND parent_window_handle =
329 engine_id(), parent_view_id);
330
331 DialogWindowCreationRequest creation_request{
333 {
335 .preferred_view_width = 800,
336 .preferred_view_height = 600,
337 },
338 .preferred_constraints = {.has_view_constraints = false},
339 .title = L"Hello World",
340 .parent_or_null = parent_window_handle};
341
342 const int64_t view_id =
344 engine_id(), &creation_request);
345 EXPECT_EQ(view_id, 1);
346
347 const HWND window_handle =
349 view_id);
350 HostWindow* host_window = HostWindow::GetThisFromHandle(window_handle);
351 EXPECT_EQ(host_window->GetOwnerWindow()->GetWindowHandle(),
352 parent_window_handle);
353}
354
355TEST_F(WindowManagerTest, DialogCanNeverBeFullscreen) {
356 IsolateScope isolate_scope(isolate());
357
358 DialogWindowCreationRequest creation_request{
360 .preferred_view_width = 800,
361 .preferred_view_height = 600},
362 .preferred_constraints = {.has_view_constraints = false},
363 .title = L"Hello World",
364 .parent_or_null = nullptr};
365
366 const int64_t view_id =
368 engine_id(), &creation_request);
369 const HWND window_handle =
371 view_id);
372
373 FullscreenRequest request{.fullscreen = true, .has_display_id = false};
375 EXPECT_FALSE(
377}
378
379TEST_F(WindowManagerTest, CreateTooltipWindow) {
380 IsolateScope isolate_scope(isolate());
381
382 const int64_t parent_view_id =
384 engine_id(), regular_creation_request());
385 const HWND parent_window_handle =
387 engine_id(), parent_view_id);
388
389 auto position_callback = [](const WindowSize& child_size,
390 const WindowRect& parent_rect,
391 const WindowRect& output_rect) -> WindowRect* {
392 WindowRect* rect = static_cast<WindowRect*>(malloc(sizeof(WindowRect)));
393 rect->left = parent_rect.left + 10;
394 rect->top = parent_rect.top + 10;
395 rect->width = child_size.width;
396 rect->height = child_size.height;
397 return rect;
398 };
399
400 TooltipWindowCreationRequest creation_request{
402 .view_min_width = 100,
403 .view_min_height = 50,
404 .view_max_width = 300,
405 .view_max_height = 200},
406 .parent = parent_window_handle,
407 .get_position_callback = position_callback};
408
409 const int64_t tooltip_view_id =
411 engine_id(), &creation_request);
412
413 EXPECT_NE(tooltip_view_id, -1);
414 HWND tooltip_window_handle =
416 engine_id(), tooltip_view_id);
417 EXPECT_NE(tooltip_window_handle, nullptr);
418}
419
420TEST_F(WindowManagerTest, TooltipWindowHasNoActivateStyle) {
421 IsolateScope isolate_scope(isolate());
422
423 const int64_t parent_view_id =
425 engine_id(), regular_creation_request());
426 const HWND parent_window_handle =
428 engine_id(), parent_view_id);
429
430 auto position_callback = [](const WindowSize& child_size,
431 const WindowRect& parent_rect,
432 const WindowRect& output_rect) -> WindowRect* {
433 WindowRect* rect = static_cast<WindowRect*>(malloc(sizeof(WindowRect)));
434 rect->left = parent_rect.left + 10;
435 rect->top = parent_rect.top + 10;
436 rect->width = child_size.width;
437 rect->height = child_size.height;
438 return rect;
439 };
440
441 TooltipWindowCreationRequest creation_request{
443 .view_min_width = 100,
444 .view_min_height = 50,
445 .view_max_width = 300,
446 .view_max_height = 200},
447 .parent = parent_window_handle,
448 .get_position_callback = position_callback};
449
450 const int64_t tooltip_view_id =
452 engine_id(), &creation_request);
453
454 HWND tooltip_window_handle =
456 engine_id(), tooltip_view_id);
457
458 DWORD ex_style = GetWindowLong(tooltip_window_handle, GWL_EXSTYLE);
459 EXPECT_TRUE(ex_style & WS_EX_NOACTIVATE);
460}
461
462TEST_F(WindowManagerTest, TooltipWindowDoesNotStealFocus) {
463 IsolateScope isolate_scope(isolate());
464
465 const int64_t parent_view_id =
467 engine_id(), regular_creation_request());
468 const HWND parent_window_handle =
470 engine_id(), parent_view_id);
471
472 // Give focus to the parent window
473 SetFocus(parent_window_handle);
474 HWND focused_before = GetFocus();
475
476 auto position_callback = [](const WindowSize& child_size,
477 const WindowRect& parent_rect,
478 const WindowRect& output_rect) -> WindowRect* {
479 WindowRect* rect = static_cast<WindowRect*>(malloc(sizeof(WindowRect)));
480 rect->left = parent_rect.left + 10;
481 rect->top = parent_rect.top + 10;
482 rect->width = child_size.width;
483 rect->height = child_size.height;
484 return rect;
485 };
486
487 TooltipWindowCreationRequest creation_request{
489 .view_min_width = 100,
490 .view_min_height = 50,
491 .view_max_width = 300,
492 .view_max_height = 200},
493 .parent = parent_window_handle,
494 .get_position_callback = position_callback};
495
496 const int64_t tooltip_view_id =
498 engine_id(), &creation_request);
499
500 HWND tooltip_window_handle =
502 engine_id(), tooltip_view_id);
503
504 // Verify focus remains with the parent window
505 HWND focused_after = GetFocus();
506 EXPECT_EQ(focused_before, focused_after);
507 EXPECT_NE(focused_after, tooltip_window_handle);
508}
509
510TEST_F(WindowManagerTest, TooltipWindowReturnsNoActivateOnMouseClick) {
511 IsolateScope isolate_scope(isolate());
512
513 const int64_t parent_view_id =
515 engine_id(), regular_creation_request());
516 const HWND parent_window_handle =
518 engine_id(), parent_view_id);
519
520 auto position_callback = [](const WindowSize& child_size,
521 const WindowRect& parent_rect,
522 const WindowRect& output_rect) -> WindowRect* {
523 WindowRect* rect = static_cast<WindowRect*>(malloc(sizeof(WindowRect)));
524 rect->left = parent_rect.left + 10;
525 rect->top = parent_rect.top + 10;
526 rect->width = child_size.width;
527 rect->height = child_size.height;
528 return rect;
529 };
530
531 TooltipWindowCreationRequest creation_request{
533 .view_min_width = 100,
534 .view_min_height = 50,
535 .view_max_width = 300,
536 .view_max_height = 200},
537 .parent = parent_window_handle,
538 .get_position_callback = position_callback};
539
540 const int64_t tooltip_view_id =
542 engine_id(), &creation_request);
543
544 HWND tooltip_window_handle =
546 engine_id(), tooltip_view_id);
547
548 // Send WM_MOUSEACTIVATE message to the tooltip window
549 LRESULT result = SendMessage(tooltip_window_handle, WM_MOUSEACTIVATE,
550 reinterpret_cast<WPARAM>(parent_window_handle),
551 MAKELPARAM(HTCLIENT, WM_LBUTTONDOWN));
552
553 // Verify the tooltip returns MA_NOACTIVATE
554 EXPECT_EQ(result, MA_NOACTIVATE);
555}
556
557TEST_F(WindowManagerTest, TooltipWindowUpdatesPositionOnViewSizeChange) {
558 IsolateScope isolate_scope(isolate());
559
560 const int64_t parent_view_id =
562 engine_id(), regular_creation_request());
563 const HWND parent_window_handle =
565 engine_id(), parent_view_id);
566
567 // Track the child size passed to the callback
568 static int callback_count = 0;
569 static int last_width = 0;
570 static int last_height = 0;
571
572 auto position_callback = [](const WindowSize& child_size,
573 const WindowRect& parent_rect,
574 const WindowRect& output_rect) -> WindowRect* {
575 callback_count++;
576 last_width = child_size.width;
577 last_height = child_size.height;
578
579 // Use malloc since the caller will use free()
580 WindowRect* rect = static_cast<WindowRect*>(malloc(sizeof(WindowRect)));
581 rect->left = parent_rect.left + callback_count * 5;
582 rect->top = parent_rect.top + callback_count * 5;
583 rect->width = child_size.width;
584 rect->height = child_size.height;
585 return rect;
586 };
587
588 TooltipWindowCreationRequest creation_request{
590 .view_min_width = 100,
591 .view_min_height = 50,
592 .view_max_width = 300,
593 .view_max_height = 200},
594 .is_sized_to_content = true,
595 .parent = parent_window_handle,
596 .get_position_callback = position_callback};
597
598 // Reset callback tracking
599 callback_count = 0;
600 last_width = 0;
601 last_height = 0;
602
603 const int64_t tooltip_view_id =
605 engine_id(), &creation_request);
606
607 HWND tooltip_window_handle =
609 engine_id(), tooltip_view_id);
610
611 // Get the view associated with the tooltip window
613 engine()->GetViewFromTopLevelWindow(tooltip_window_handle);
614 ASSERT_NE(view, nullptr);
615
616 // Get initial position
617 RECT initial_rect;
618 GetWindowRect(tooltip_window_handle, &initial_rect);
619 int initial_callback_count = callback_count;
620
621 // Simulate a frame being generated with new dimensions
622 // This should trigger DidUpdateViewSize which calls UpdatePosition
623 view->OnFrameGenerated(150, 100);
624
625 // Process any pending tasks to ensure the callback is executed
626 engine()->task_runner()->ProcessTasks();
627
628 // Verify the callback was called again with the new dimensions
629 EXPECT_GT(callback_count, initial_callback_count);
630 EXPECT_EQ(last_width, 150);
631 EXPECT_EQ(last_height, 100);
632
633 // Get new position and verify it changed
634 RECT new_rect;
635 GetWindowRect(tooltip_window_handle, &new_rect);
636
637 // The position should have changed due to our callback logic
638 // (we offset by callback_count * 5)
639 EXPECT_NE(initial_rect.left, new_rect.left);
640 EXPECT_NE(initial_rect.top, new_rect.top);
641}
642
643} // namespace testing
644} // namespace flutter
HWND GetWindowHandle() const
HostWindow * GetOwnerWindow() const
static HostWindow * GetThisFromHandle(HWND hwnd)
static Isolate Current()
FlutterEngine engine
Definition main.cc:84
FlView * view
const char * message
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
G_BEGIN_DECLS FlutterViewId view_id
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition macros.h:27
TEST_F(DisplayListTest, Defaults)
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
void(* on_message)(WindowsMessage *)
#define CREATE_NATIVE_ENTRY(native_entry)
void InternalFlutterWindows_WindowManager_Initialize(int64_t engine_id, const flutter::WindowingInitRequest *request)
void InternalFlutterWindows_WindowManager_SetWindowConstraints(HWND hwnd, const flutter::WindowConstraints *constraints)
bool InternalFlutterWindows_WindowManager_GetFullscreen(HWND hwnd)
void InternalFlutterWindows_WindowManager_SetWindowSize(HWND hwnd, const flutter::WindowSizeRequest *size)
FlutterViewId InternalFlutterWindows_WindowManager_CreateRegularWindow(int64_t engine_id, const flutter::RegularWindowCreationRequest *request)
FLUTTER_EXPORT FlutterViewId InternalFlutterWindows_WindowManager_CreateDialogWindow(int64_t engine_id, const flutter::DialogWindowCreationRequest *request)
void InternalFlutterWindows_WindowManager_SetFullscreen(HWND hwnd, const flutter::FullscreenRequest *request)
flutter::ActualWindowSize InternalFlutterWindows_WindowManager_GetWindowContentSize(HWND hwnd)
FLUTTER_EXPORT FlutterViewId InternalFlutterWindows_WindowManager_CreateTooltipWindow(int64_t engine_id, const flutter::TooltipWindowCreationRequest *request)
HWND InternalFlutterWindows_WindowManager_GetTopLevelWindowHandle(int64_t engine_id, FlutterViewId view_id)
LONG_PTR LRESULT
#define SendMessage
UINT_PTR WPARAM
unsigned long DWORD