Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
flutter_windows_engine_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 <thread>
7
25#include "gmock/gmock.h"
26#include "gtest/gtest.h"
27
28// winbase.h defines GetCurrentTime as a macro.
29#undef GetCurrentTime
30
31namespace {
32// Process the next win32 message if there is one. This can be used to
33// pump the Windows platform thread task runner.
34void PumpMessage() {
35 ::MSG msg;
36 if (::GetMessage(&msg, nullptr, 0, 0)) {
37 ::TranslateMessage(&msg);
39 }
40}
41} // namespace
42
43namespace flutter {
44namespace testing {
45
46using ::testing::_;
47using ::testing::DoAll;
48using ::testing::NiceMock;
49using ::testing::Return;
50using ::testing::SetArgPointee;
51
53
54// The engine can be run without any views.
56 FlutterWindowsEngineBuilder builder{GetContext()};
57 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
58
59 EngineModifier modifier(engine.get());
60 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
61
62 ASSERT_TRUE(engine->Run());
63 ASSERT_EQ(engine->view(kImplicitViewId), nullptr);
64 ASSERT_EQ(engine->view(123), nullptr);
65}
66
67TEST_F(FlutterWindowsEngineTest, TaskRunnerDelayedTask) {
68 bool finished = false;
69 auto runner = std::make_unique<TaskRunner>(
70 [] {
71 return static_cast<uint64_t>(
73 },
74 [&](const FlutterTask*) { finished = true; });
75 runner->PostFlutterTask(
77 static_cast<uint64_t>((fml::TimePoint::Now().ToEpochDelta() +
79 .ToNanoseconds()));
81 while (!finished) {
82 PumpMessage();
83 }
84 auto duration = fml::TimePoint::Now() - start;
85 EXPECT_GE(duration, fml::TimeDelta::FromMilliseconds(50));
86}
87
88// https://github.com/flutter/flutter/issues/173843)
89TEST_F(FlutterWindowsEngineTest, TaskRunnerDoesNotDeadlock) {
90 auto runner = std::make_unique<TaskRunner>(
91 [] {
92 return static_cast<uint64_t>(
94 },
95 [&](const FlutterTask*) {});
96
97 struct RunnerHolder {
98 void PostTaskLoop() {
99 runner->PostTask([this] { PostTaskLoop(); });
100 }
101 std::unique_ptr<TaskRunner> runner;
102 };
103
104 RunnerHolder container{.runner = std::move(runner)};
105 // Spam flutter tasks.
106 container.PostTaskLoop();
107
108 const LPCWSTR class_name = L"FlutterTestWindowClass";
109 WNDCLASS wc = {0};
110 wc.lpfnWndProc = DefWindowProc;
111 wc.lpszClassName = class_name;
112 RegisterClass(&wc);
113
114 HWND window;
115 container.runner->PostTask([&] {
116 window = CreateWindowEx(0, class_name, L"Empty Window", WS_OVERLAPPEDWINDOW,
117 CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, nullptr,
118 nullptr, nullptr, nullptr);
119 ShowWindow(window, SW_SHOW);
120 });
121
122 while (true) {
123 ::MSG msg;
124 if (::GetMessage(&msg, nullptr, 0, 0)) {
125 if (msg.message == WM_PAINT) {
126 break;
127 }
128 ::TranslateMessage(&msg);
129 ::DispatchMessage(&msg);
130 }
131 }
132
133 DestroyWindow(window);
134 UnregisterClassW(class_name, nullptr);
135}
136
137TEST_F(FlutterWindowsEngineTest, RunDoesExpectedInitialization) {
138 FlutterWindowsEngineBuilder builder{GetContext()};
139 builder.AddDartEntrypointArgument("arg1");
140 builder.AddDartEntrypointArgument("arg2");
141
142 auto windows_proc_table = std::make_shared<MockWindowsProcTable>();
143
144 HMONITOR mock_monitor = reinterpret_cast<HMONITOR>(1);
145
146 MONITORINFOEXW monitor_info = {};
147 monitor_info.cbSize = sizeof(MONITORINFOEXW);
148 monitor_info.rcMonitor = {0, 0, 1920, 1080};
149 monitor_info.rcWork = {0, 0, 1920, 1080};
150 monitor_info.dwFlags = MONITORINFOF_PRIMARY;
151 wcscpy_s(monitor_info.szDevice, L"\\\\.\\DISPLAY1");
152
153 EXPECT_CALL(*windows_proc_table, GetMonitorInfoW(mock_monitor, _))
154 .WillRepeatedly(DoAll(SetArgPointee<1>(monitor_info), Return(TRUE)));
155
156 EXPECT_CALL(*windows_proc_table, EnumDisplayMonitors(nullptr, nullptr, _, _))
157 .WillRepeatedly([&](HDC hdc, LPCRECT lprcClip, MONITORENUMPROC lpfnEnum,
158 LPARAM dwData) {
159 lpfnEnum(mock_monitor, nullptr, &monitor_info.rcMonitor, dwData);
160 return TRUE;
161 });
162
163 EXPECT_CALL(*windows_proc_table, GetDpiForMonitor(mock_monitor, _))
164 .WillRepeatedly(Return(96));
165
166 // Mock locale information
167 EXPECT_CALL(*windows_proc_table, GetThreadPreferredUILanguages(_, _, _, _))
168 .WillRepeatedly(
169 [](DWORD flags, PULONG count, PZZWSTR languages, PULONG length) {
170 // We need to mock the locale information twice because the first
171 // call is to get the size and the second call is to fill the
172 // buffer.
173 if (languages == nullptr) {
174 // First call is to get the size
175 *count = 1; // One language
176 *length = 10; // "fr-FR\0\0" (double null-terminated)
177 return TRUE;
178 } else {
179 // Second call is to fill the buffer
180 *count = 1;
181 // Fill with "fr-FR\0\0" (double null-terminated)
182 wchar_t* lang_buffer = languages;
183 wcscpy(lang_buffer, L"fr-FR");
184 // Move past the first null terminator to add the second
185 lang_buffer += wcslen(L"fr-FR") + 1;
186 *lang_buffer = L'\0';
187 return TRUE;
188 }
189 });
190
191 builder.SetWindowsProcTable(windows_proc_table);
192
193 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
194 EngineModifier modifier(engine.get());
195
196 // The engine should be run with expected configuration values.
197 bool run_called = false;
198 modifier.embedder_api().Run = MOCK_ENGINE_PROC(
199 Run, ([&run_called, engine_instance = engine.get()](
200 size_t version, const FlutterRendererConfig* config,
201 const FlutterProjectArgs* args, void* user_data,
202 FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
203 run_called = true;
204 *engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(1);
205
206 EXPECT_EQ(version, FLUTTER_ENGINE_VERSION);
207 EXPECT_NE(config, nullptr);
208 // We have an EGL manager, so this should be using OpenGL.
209 EXPECT_EQ(config->type, kOpenGL);
210 EXPECT_EQ(user_data, engine_instance);
211 // Spot-check arguments.
212 EXPECT_NE(args->assets_path, nullptr);
213 EXPECT_NE(args->icu_data_path, nullptr);
214 EXPECT_EQ(args->dart_entrypoint_argc, 2U);
215 EXPECT_EQ(strcmp(args->dart_entrypoint_argv[0], "arg1"), 0);
216 EXPECT_EQ(strcmp(args->dart_entrypoint_argv[1], "arg2"), 0);
217 EXPECT_NE(args->platform_message_callback, nullptr);
218 EXPECT_NE(args->custom_task_runners, nullptr);
219 EXPECT_NE(args->custom_task_runners->thread_priority_setter, nullptr);
220 EXPECT_EQ(args->custom_dart_entrypoint, nullptr);
221 EXPECT_NE(args->vsync_callback, nullptr);
222 EXPECT_EQ(args->update_semantics_callback, nullptr);
223 EXPECT_NE(args->update_semantics_callback2, nullptr);
224 EXPECT_EQ(args->update_semantics_node_callback, nullptr);
225 EXPECT_EQ(args->update_semantics_custom_action_callback, nullptr);
226 EXPECT_NE(args->view_focus_change_request_callback, nullptr);
227
228 args->custom_task_runners->thread_priority_setter(
230 EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
231 THREAD_PRIORITY_ABOVE_NORMAL);
232 return kSuccess;
233 }));
234 // Accessibility updates must do nothing when the embedder engine is mocked
236 UpdateAccessibilityFeatures,
238 FlutterAccessibilityFeature flags) { return kSuccess; });
239
240 // It should send locale info.
241 bool update_locales_called = false;
243 UpdateLocales,
244 ([&update_locales_called](auto engine, const FlutterLocale** locales,
245 size_t locales_count) {
246 update_locales_called = true;
247
248 EXPECT_GT(locales_count, 0);
249 EXPECT_NE(locales, nullptr);
250
251 return kSuccess;
252 }));
253
254 // And it should send initial settings info.
255 bool settings_message_sent = false;
257 SendPlatformMessage,
258 ([&settings_message_sent](auto engine, auto message) {
259 if (std::string(message->channel) == std::string("flutter/settings")) {
260 settings_message_sent = true;
261 }
262
263 return kSuccess;
264 }));
265
266 // And it should send display info.
267 bool notify_display_update_called = false;
268
270 NotifyDisplayUpdate,
271 ([&notify_display_update_called](
273 const FlutterEngineDisplaysUpdateType update_type,
274 const FlutterEngineDisplay* embedder_displays,
275 size_t display_count) {
276 EXPECT_EQ(update_type, kFlutterEngineDisplaysUpdateTypeStartup);
277 notify_display_update_called = true;
278 return kSuccess;
279 }));
280
281 // Set the EGL manager to !nullptr to test ANGLE rendering.
282 modifier.SetEGLManager(std::make_unique<egl::MockManager>());
283
284 engine->Run();
285
286 EXPECT_TRUE(run_called);
287 EXPECT_TRUE(update_locales_called);
288 EXPECT_TRUE(settings_message_sent);
289 EXPECT_TRUE(notify_display_update_called);
290
291 // Ensure that deallocation doesn't call the actual Shutdown with the bogus
292 // engine pointer that the overridden Run returned.
293 modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; };
294 modifier.ReleaseEGLManager();
295}
296
297TEST_F(FlutterWindowsEngineTest, ConfiguresFrameVsync) {
298 FlutterWindowsEngineBuilder builder{GetContext()};
299 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
300 EngineModifier modifier(engine.get());
301 bool on_vsync_called = false;
302
303 modifier.embedder_api().GetCurrentTime =
304 MOCK_ENGINE_PROC(GetCurrentTime, ([]() -> uint64_t { return 1; }));
306 OnVsync,
307 ([&on_vsync_called, engine_instance = engine.get()](
308 FLUTTER_API_SYMBOL(FlutterEngine) engine, intptr_t baton,
309 uint64_t frame_start_time_nanos, uint64_t frame_target_time_nanos) {
310 EXPECT_EQ(baton, 1);
311 EXPECT_EQ(frame_start_time_nanos, 16600000);
312 EXPECT_EQ(frame_target_time_nanos, 33200000);
313 on_vsync_called = true;
314 return kSuccess;
315 }));
316 modifier.SetStartTime(0);
317 modifier.SetFrameInterval(16600000);
318
319 engine->OnVsync(1);
320
321 EXPECT_TRUE(on_vsync_called);
322}
323
324TEST_F(FlutterWindowsEngineTest, RunWithoutANGLEUsesSoftware) {
325 FlutterWindowsEngineBuilder builder{GetContext()};
326 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
327 EngineModifier modifier(engine.get());
328
330 MOCK_ENGINE_PROC(NotifyDisplayUpdate,
331 ([engine_instance = engine.get()](
333 const FlutterEngineDisplaysUpdateType update_type,
334 const FlutterEngineDisplay* embedder_displays,
335 size_t display_count) { return kSuccess; }));
336
337 // The engine should be run with expected configuration values.
338 bool run_called = false;
339 modifier.embedder_api().Run = MOCK_ENGINE_PROC(
340 Run, ([&run_called, engine_instance = engine.get()](
341 size_t version, const FlutterRendererConfig* config,
342 const FlutterProjectArgs* args, void* user_data,
343 FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
344 run_called = true;
345 *engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(1);
346 // We don't have an EGL Manager, so we should be using software.
347 EXPECT_EQ(config->type, kSoftware);
348 return kSuccess;
349 }));
350 // Accessibility updates must do nothing when the embedder engine is mocked
352 UpdateAccessibilityFeatures,
354 FlutterAccessibilityFeature flags) { return kSuccess; });
355
356 // Stub out UpdateLocales and SendPlatformMessage as we don't have a fully
357 // initialized engine instance.
359 UpdateLocales, ([](auto engine, const FlutterLocale** locales,
360 size_t locales_count) { return kSuccess; }));
362 MOCK_ENGINE_PROC(SendPlatformMessage,
363 ([](auto engine, auto message) { return kSuccess; }));
364
365 // Set the EGL manager to nullptr to test software fallback path.
366 modifier.SetEGLManager(nullptr);
367
368 engine->Run();
369
370 EXPECT_TRUE(run_called);
371
372 // Ensure that deallocation doesn't call the actual Shutdown with the bogus
373 // engine pointer that the overridden Run returned.
374 modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; };
375}
376
377TEST_F(FlutterWindowsEngineTest, RunWithoutANGLEOnImpellerFailsToStart) {
378 FlutterWindowsEngineBuilder builder{GetContext()};
379 builder.SetSwitches({"--enable-impeller=true"});
380 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
381 EngineModifier modifier(engine.get());
382
384 MOCK_ENGINE_PROC(NotifyDisplayUpdate,
385 ([engine_instance = engine.get()](
387 const FlutterEngineDisplaysUpdateType update_type,
388 const FlutterEngineDisplay* embedder_displays,
389 size_t display_count) { return kSuccess; }));
390
391 // Accessibility updates must do nothing when the embedder engine is mocked
393 UpdateAccessibilityFeatures,
395 FlutterAccessibilityFeature flags) { return kSuccess; });
396
397 // Stub out UpdateLocales and SendPlatformMessage as we don't have a fully
398 // initialized engine instance.
400 UpdateLocales, ([](auto engine, const FlutterLocale** locales,
401 size_t locales_count) { return kSuccess; }));
403 MOCK_ENGINE_PROC(SendPlatformMessage,
404 ([](auto engine, auto message) { return kSuccess; }));
405
406 // Set the EGL manager to nullptr to test software fallback path.
407 modifier.SetEGLManager(nullptr);
408
409 EXPECT_FALSE(engine->Run());
410}
411
412TEST_F(FlutterWindowsEngineTest, RunWithImpellerEnablesSDFs) {
413 FlutterWindowsEngineBuilder builder{GetContext()};
414 builder.SetSwitches({"--enable-impeller=true"});
415 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
416 EngineModifier modifier(engine.get());
417
419 MOCK_ENGINE_PROC(NotifyDisplayUpdate,
420 ([engine_instance = engine.get()](
422 const FlutterEngineDisplaysUpdateType update_type,
423 const FlutterEngineDisplay* embedder_displays,
424 size_t display_count) { return kSuccess; }));
425
427 UpdateAccessibilityFeatures,
429 FlutterAccessibilityFeature flags) { return kSuccess; });
430
432 UpdateLocales, ([](auto engine, const FlutterLocale** locales,
433 size_t locales_count) { return kSuccess; }));
434
436 MOCK_ENGINE_PROC(SendPlatformMessage,
437 ([](auto engine, auto message) { return kSuccess; }));
438
439 bool run_called = false;
440 modifier.embedder_api().Run = MOCK_ENGINE_PROC(
441 Run, ([&run_called, engine_instance = engine.get()](
442 size_t version, const FlutterRendererConfig* config,
443 const FlutterProjectArgs* args, void* user_data,
444 FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
445 run_called = true;
446 *engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(1);
447
448 bool has_sdf_switch = false;
449 for (int i = 0; i < args->command_line_argc; ++i) {
450 if (strcmp(args->command_line_argv[i], "--impeller-use-sdfs=true") ==
451 0) {
452 has_sdf_switch = true;
453 break;
454 }
455 }
456 EXPECT_TRUE(has_sdf_switch);
457 return kSuccess;
458 }));
459
460 // Set the EGL manager to !nullptr to test ANGLE rendering.
461 modifier.SetEGLManager(std::make_unique<egl::MockManager>());
462
463 engine->Run();
464
465 EXPECT_TRUE(run_called);
466
467 modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; };
468 modifier.ReleaseEGLManager();
469}
470
471TEST_F(FlutterWindowsEngineTest, SendPlatformMessageWithoutResponse) {
472 FlutterWindowsEngineBuilder builder{GetContext()};
473 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
474 EngineModifier modifier(engine.get());
475
476 const char* channel = "test";
477 const std::vector<uint8_t> test_message = {1, 2, 3, 4};
478
479 // Without a response, SendPlatformMessage should be a simple pass-through.
480 bool called = false;
482 SendPlatformMessage, ([&called, test_message](auto engine, auto message) {
483 called = true;
484 EXPECT_STREQ(message->channel, "test");
485 EXPECT_EQ(message->message_size, test_message.size());
486 EXPECT_EQ(memcmp(message->message, test_message.data(),
487 message->message_size),
488 0);
489 EXPECT_EQ(message->response_handle, nullptr);
490 return kSuccess;
491 }));
492
493 engine->SendPlatformMessage(channel, test_message.data(), test_message.size(),
494 nullptr, nullptr);
495 EXPECT_TRUE(called);
496}
497
498TEST_F(FlutterWindowsEngineTest, PlatformMessageRoundTrip) {
499 FlutterWindowsEngineBuilder builder{GetContext()};
500 builder.SetDartEntrypoint("hiPlatformChannels");
501
502 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
503 EngineModifier modifier(engine.get());
504 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
505
506 auto binary_messenger =
507 std::make_unique<BinaryMessengerImpl>(engine->messenger());
508
509 engine->Run();
510 bool did_call_callback = false;
511 bool did_call_reply = false;
512 bool did_call_dart_reply = false;
513 std::string channel = "hi";
514 binary_messenger->SetMessageHandler(
515 channel,
516 [&did_call_callback, &did_call_dart_reply](
517 const uint8_t* message, size_t message_size, BinaryReply reply) {
518 if (message_size == 5) {
519 EXPECT_EQ(message[0], static_cast<uint8_t>('h'));
520 char response[] = {'b', 'y', 'e'};
521 reply(reinterpret_cast<uint8_t*>(response), 3);
522 did_call_callback = true;
523 } else {
524 EXPECT_EQ(message_size, 3);
525 EXPECT_EQ(message[0], static_cast<uint8_t>('b'));
526 did_call_dart_reply = true;
527 }
528 });
529 char payload[] = {'h', 'e', 'l', 'l', 'o'};
530 binary_messenger->Send(
531 channel, reinterpret_cast<uint8_t*>(payload), 5,
532 [&did_call_reply](const uint8_t* reply, size_t reply_size) {
533 EXPECT_EQ(reply_size, 5);
534 EXPECT_EQ(reply[0], static_cast<uint8_t>('h'));
535 did_call_reply = true;
536 });
537 // Rely on timeout mechanism in CI.
538 while (!did_call_callback || !did_call_reply || !did_call_dart_reply) {
539 engine->task_runner()->ProcessTasks();
540 }
541}
542
543TEST_F(FlutterWindowsEngineTest, PlatformMessageRespondOnDifferentThread) {
544 FlutterWindowsEngineBuilder builder{GetContext()};
545 builder.SetDartEntrypoint("hiPlatformChannels");
546
547 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
548
549 EngineModifier modifier(engine.get());
550 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
551
552 auto binary_messenger =
553 std::make_unique<BinaryMessengerImpl>(engine->messenger());
554
555 engine->Run();
556 bool did_call_callback = false;
557 bool did_call_reply = false;
558 bool did_call_dart_reply = false;
559 std::string channel = "hi";
560 std::unique_ptr<std::thread> reply_thread;
561 binary_messenger->SetMessageHandler(
562 channel,
563 [&did_call_callback, &did_call_dart_reply, &reply_thread](
564 const uint8_t* message, size_t message_size, BinaryReply reply) {
565 if (message_size == 5) {
566 EXPECT_EQ(message[0], static_cast<uint8_t>('h'));
567 reply_thread.reset(new std::thread([reply = std::move(reply)]() {
568 char response[] = {'b', 'y', 'e'};
569 reply(reinterpret_cast<uint8_t*>(response), 3);
570 }));
571 did_call_callback = true;
572 } else {
573 EXPECT_EQ(message_size, 3);
574 EXPECT_EQ(message[0], static_cast<uint8_t>('b'));
575 did_call_dart_reply = true;
576 }
577 });
578 char payload[] = {'h', 'e', 'l', 'l', 'o'};
579 binary_messenger->Send(
580 channel, reinterpret_cast<uint8_t*>(payload), 5,
581 [&did_call_reply](const uint8_t* reply, size_t reply_size) {
582 EXPECT_EQ(reply_size, 5);
583 EXPECT_EQ(reply[0], static_cast<uint8_t>('h'));
584 did_call_reply = true;
585 });
586 // Rely on timeout mechanism in CI.
587 while (!did_call_callback || !did_call_reply || !did_call_dart_reply) {
588 engine->task_runner()->ProcessTasks();
589 }
590 ASSERT_TRUE(reply_thread);
591 reply_thread->join();
592}
593
594TEST_F(FlutterWindowsEngineTest, SendPlatformMessageWithResponse) {
595 FlutterWindowsEngineBuilder builder{GetContext()};
596 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
597 EngineModifier modifier(engine.get());
598
599 const char* channel = "test";
600 const std::vector<uint8_t> test_message = {1, 2, 3, 4};
601 auto* dummy_response_handle =
602 reinterpret_cast<FlutterPlatformMessageResponseHandle*>(5);
603 const FlutterDesktopBinaryReply reply_handler = [](auto... args) {};
604 void* reply_user_data = reinterpret_cast<void*>(6);
605
606 // When a response is requested, a handle should be created, passed as part
607 // of the message, and then released.
608 bool create_response_handle_called = false;
611 PlatformMessageCreateResponseHandle,
612 ([&create_response_handle_called, &reply_handler, reply_user_data,
613 dummy_response_handle](auto engine, auto reply, auto user_data,
614 auto response_handle) {
615 create_response_handle_called = true;
616 EXPECT_EQ(reply, reply_handler);
617 EXPECT_EQ(user_data, reply_user_data);
618 EXPECT_NE(response_handle, nullptr);
619 *response_handle = dummy_response_handle;
620 return kSuccess;
621 }));
622 bool release_response_handle_called = false;
625 PlatformMessageReleaseResponseHandle,
626 ([&release_response_handle_called, dummy_response_handle](
627 auto engine, auto response_handle) {
628 release_response_handle_called = true;
629 EXPECT_EQ(response_handle, dummy_response_handle);
630 return kSuccess;
631 }));
632 bool send_message_called = false;
634 SendPlatformMessage, ([&send_message_called, test_message,
635 dummy_response_handle](auto engine, auto message) {
636 send_message_called = true;
637 EXPECT_STREQ(message->channel, "test");
638 EXPECT_EQ(message->message_size, test_message.size());
639 EXPECT_EQ(memcmp(message->message, test_message.data(),
640 message->message_size),
641 0);
642 EXPECT_EQ(message->response_handle, dummy_response_handle);
643 return kSuccess;
644 }));
645
646 engine->SendPlatformMessage(channel, test_message.data(), test_message.size(),
647 reply_handler, reply_user_data);
648 EXPECT_TRUE(create_response_handle_called);
649 EXPECT_TRUE(release_response_handle_called);
650 EXPECT_TRUE(send_message_called);
651}
652
654 FlutterWindowsEngineBuilder builder{GetContext()};
655 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
656 EngineModifier modifier(engine.get());
657
658 bool called = false;
659 std::string message = "Hello";
661 SendSemanticsAction, ([&called, &message](auto engine, auto info) {
662 called = true;
663 EXPECT_EQ(info->view_id, 456);
664 EXPECT_EQ(info->node_id, 42);
665 EXPECT_EQ(info->action, kFlutterSemanticsActionDismiss);
666 EXPECT_EQ(memcmp(info->data, message.c_str(), message.size()), 0);
667 EXPECT_EQ(info->data_length, message.size());
668 return kSuccess;
669 }));
670
671 auto data = fml::MallocMapping::Copy(message.c_str(), message.size());
672 engine->DispatchSemanticsAction(456, 42, kFlutterSemanticsActionDismiss,
673 std::move(data));
674 EXPECT_TRUE(called);
675}
676
677TEST_F(FlutterWindowsEngineTest, SetsThreadPriority) {
679 EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
680 THREAD_PRIORITY_BELOW_NORMAL);
681
683 EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
684 THREAD_PRIORITY_ABOVE_NORMAL);
685
687 EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
688 THREAD_PRIORITY_ABOVE_NORMAL);
689
690 // FlutterThreadPriority::kNormal does not change thread priority, reset to 0
691 // here.
692 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
693
695 EXPECT_EQ(GetThreadPriority(GetCurrentThread()), THREAD_PRIORITY_NORMAL);
696}
697
698TEST_F(FlutterWindowsEngineTest, AddPluginRegistrarDestructionCallback) {
699 FlutterWindowsEngineBuilder builder{GetContext()};
700 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
701 EngineModifier modifier(engine.get());
702
704 std::make_shared<MockKeyResponseController>());
705
706 engine->Run();
707
708 // Verify that destruction handlers don't overwrite each other.
709 int result1 = 0;
710 int result2 = 0;
711 engine->AddPluginRegistrarDestructionCallback(
713 auto result = reinterpret_cast<int*>(ref);
714 *result = 1;
715 },
716 reinterpret_cast<FlutterDesktopPluginRegistrarRef>(&result1));
717 engine->AddPluginRegistrarDestructionCallback(
719 auto result = reinterpret_cast<int*>(ref);
720 *result = 2;
721 },
722 reinterpret_cast<FlutterDesktopPluginRegistrarRef>(&result2));
723
724 engine->Stop();
725 EXPECT_EQ(result1, 1);
726 EXPECT_EQ(result2, 2);
727}
728
730 FlutterWindowsEngineBuilder builder{GetContext()};
731 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
732 EngineModifier modifier(engine.get());
733
734 bool called = false;
735 modifier.embedder_api().ScheduleFrame =
736 MOCK_ENGINE_PROC(ScheduleFrame, ([&called](auto engine) {
737 called = true;
738 return kSuccess;
739 }));
740
741 engine->ScheduleFrame();
742 EXPECT_TRUE(called);
743}
744
745TEST_F(FlutterWindowsEngineTest, SetNextFrameCallback) {
746 FlutterWindowsEngineBuilder builder{GetContext()};
747 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
748 EngineModifier modifier(engine.get());
749
750 bool called = false;
752 SetNextFrameCallback, ([&called](auto engine, auto callback, auto data) {
753 called = true;
754 return kSuccess;
755 }));
756
757 engine->SetNextFrameCallback([]() {});
758 EXPECT_TRUE(called);
759}
760
761TEST_F(FlutterWindowsEngineTest, GetExecutableName) {
762 FlutterWindowsEngineBuilder builder{GetContext()};
763 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
764 EXPECT_EQ(engine->GetExecutableName(), "flutter_windows_unittests.exe");
765}
766
767// Ensure that after setting or resetting the high contrast feature,
768// the corresponding status flag can be retrieved from the engine.
769TEST_F(FlutterWindowsEngineTest, UpdateHighContrastFeature) {
770 auto windows_proc_table = std::make_shared<MockWindowsProcTable>();
771 EXPECT_CALL(*windows_proc_table, GetHighContrastEnabled)
772 .WillOnce(Return(true))
773 .WillOnce(Return(false));
774
775 FlutterWindowsEngineBuilder builder{GetContext()};
776 builder.SetWindowsProcTable(windows_proc_table);
777 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
778 EngineModifier modifier(engine.get());
779
780 std::optional<FlutterAccessibilityFeature> engine_flags;
782 UpdateAccessibilityFeatures, ([&engine_flags](auto engine, auto flags) {
783 engine_flags = flags;
784 return kSuccess;
785 }));
787 SendPlatformMessage,
788 [](auto engine, const auto message) { return kSuccess; });
789
790 // 1: High contrast is enabled.
791 engine->UpdateHighContrastMode();
792
793 EXPECT_TRUE(engine->high_contrast_enabled());
794 EXPECT_TRUE(engine_flags.has_value());
795 EXPECT_TRUE(
796 engine_flags.value() &
798
799 // 2: High contrast is disabled.
800 engine_flags.reset();
801 engine->UpdateHighContrastMode();
802
803 EXPECT_FALSE(engine->high_contrast_enabled());
804 EXPECT_TRUE(engine_flags.has_value());
805 EXPECT_FALSE(
806 engine_flags.value() &
808}
809
810TEST_F(FlutterWindowsEngineTest, PostRasterThreadTask) {
811 FlutterWindowsEngineBuilder builder{GetContext()};
812 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
813 EngineModifier modifier(engine.get());
814
816 PostRenderThreadTask, ([](auto engine, auto callback, auto context) {
818 return kSuccess;
819 }));
820
821 bool called = false;
822 engine->PostRasterThreadTask([&called]() { called = true; });
823
824 EXPECT_TRUE(called);
825}
826
827class MockFlutterWindowsView : public FlutterWindowsView {
828 public:
830 std::unique_ptr<WindowBindingHandler> wbh)
832 engine,
833 std::move(wbh),
834 false,
835 BoxConstraints()) {}
837
839 NotifyWinEventWrapper,
840 (ui::AXPlatformNodeWin*, ax::mojom::Event),
841 (override));
842 MOCK_METHOD(HWND, GetWindowHandle, (), (const, override));
843 MOCK_METHOD(bool, Focus, (), (override));
844
845 private:
846 FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView);
847};
848
849// Verify the view is notified of accessibility announcements.
850TEST_F(FlutterWindowsEngineTest, AccessibilityAnnouncement) {
851 auto& context = GetContext();
853 builder.SetDartEntrypoint("sendAccessibilityAnnouncement");
854
855 bool done = false;
856 auto native_entry =
857 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
858 context.AddNativeFunction("Signal", native_entry);
859
860 EnginePtr engine{builder.RunHeadless()};
861 ASSERT_NE(engine, nullptr);
862
863 ui::AXPlatformNodeDelegateBase parent_delegate;
864 AlertPlatformNodeDelegate delegate{parent_delegate};
865
866 auto window_binding_handler =
867 std::make_unique<NiceMock<MockWindowBindingHandler>>();
868 EXPECT_CALL(*window_binding_handler, GetAlertDelegate)
869 .WillOnce(Return(&delegate));
870
871 auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
872 MockFlutterWindowsView view{windows_engine,
873 std::move(window_binding_handler)};
874 EngineModifier modifier{windows_engine};
875 modifier.SetImplicitView(&view);
876
877 windows_engine->UpdateSemanticsEnabled(true);
878
879 EXPECT_CALL(view, NotifyWinEventWrapper).Times(1);
880
881 // Rely on timeout mechanism in CI.
882 while (!done) {
883 windows_engine->task_runner()->ProcessTasks();
884 }
885}
886
887// Verify the app can send accessibility announcements while in headless mode.
888TEST_F(FlutterWindowsEngineTest, AccessibilityAnnouncementHeadless) {
889 auto& context = GetContext();
891 builder.SetDartEntrypoint("sendAccessibilityAnnouncement");
892
893 bool done = false;
894 auto native_entry =
895 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
896 context.AddNativeFunction("Signal", native_entry);
897
898 EnginePtr engine{builder.RunHeadless()};
899 ASSERT_NE(engine, nullptr);
900
901 auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
902 windows_engine->UpdateSemanticsEnabled(true);
903
904 // Rely on timeout mechanism in CI.
905 while (!done) {
906 windows_engine->task_runner()->ProcessTasks();
907 }
908}
909
910// Verify the engine does not crash if it receives an accessibility event
911// it does not support yet.
912TEST_F(FlutterWindowsEngineTest, AccessibilityTooltip) {
913 fml::testing::LogCapture log_capture;
914
915 auto& context = GetContext();
917 builder.SetDartEntrypoint("sendAccessibilityTooltipEvent");
918
919 bool done = false;
920 auto native_entry =
921 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
922 context.AddNativeFunction("Signal", native_entry);
923
924 ViewControllerPtr controller{builder.Run()};
925 ASSERT_NE(controller, nullptr);
926
927 auto engine = FlutterDesktopViewControllerGetEngine(controller.get());
928 auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine);
929 windows_engine->UpdateSemanticsEnabled(true);
930
931 // Rely on timeout mechanism in CI.
932 while (!done) {
933 windows_engine->task_runner()->ProcessTasks();
934 }
935
936 // Verify no error was logged.
937 // Regression test for:
938 // https://github.com/flutter/flutter/issues/144274
939 EXPECT_EQ(log_capture.str().find("tooltip"), std::string::npos);
940}
941
943 public:
947
949 void,
950 Quit,
951 (std::optional<HWND>, std::optional<WPARAM>, std::optional<LPARAM>, UINT),
952 (override));
953 MOCK_METHOD(void, DispatchMessage, (HWND, UINT, WPARAM, LPARAM), (override));
954 MOCK_METHOD(bool, IsLastWindowOfProcess, (), (override));
956
963
964 std::function<void()> begin_processing_callback = nullptr;
965};
966
968 FlutterWindowsEngineBuilder builder{GetContext()};
969 builder.SetDartEntrypoint("exitTestExit");
970 bool finished = false;
971
972 auto engine = builder.Build();
973 auto window_binding_handler =
974 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
975 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
976
977 EngineModifier modifier(engine.get());
978 modifier.SetImplicitView(&view);
979 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
980 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
981 EXPECT_CALL(*handler, Quit)
982 .WillOnce([&finished](std::optional<HWND> hwnd,
983 std::optional<WPARAM> wparam,
984 std::optional<LPARAM> lparam,
985 UINT exit_code) { finished = exit_code == 0; });
986 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillRepeatedly(Return(true));
987 modifier.SetLifecycleManager(std::move(handler));
988
989 engine->lifecycle_manager()->BeginProcessingExit();
990
991 engine->Run();
992
993 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
994 0);
995
996 // The test will only succeed when this while loop exits. Otherwise it will
997 // timeout.
998 while (!finished) {
999 engine->task_runner()->ProcessTasks();
1000 }
1001}
1002
1004 FlutterWindowsEngineBuilder builder{GetContext()};
1005 builder.SetDartEntrypoint("exitTestCancel");
1006 bool did_call = false;
1007
1008 auto engine = builder.Build();
1009 auto window_binding_handler =
1010 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1011 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1012
1013 EngineModifier modifier(engine.get());
1014 modifier.SetImplicitView(&view);
1015 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1016 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1017 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillRepeatedly(Return(true));
1018 EXPECT_CALL(*handler, Quit).Times(0);
1019 modifier.SetLifecycleManager(std::move(handler));
1020 engine->lifecycle_manager()->BeginProcessingExit();
1021
1022 auto binary_messenger =
1023 std::make_unique<BinaryMessengerImpl>(engine->messenger());
1024 binary_messenger->SetMessageHandler(
1025 "flutter/platform", [&did_call](const uint8_t* message,
1026 size_t message_size, BinaryReply reply) {
1027 std::string contents(message, message + message_size);
1028 EXPECT_NE(contents.find("\"method\":\"System.exitApplication\""),
1029 std::string::npos);
1030 EXPECT_NE(contents.find("\"type\":\"required\""), std::string::npos);
1031 EXPECT_NE(contents.find("\"exitCode\":0"), std::string::npos);
1032 did_call = true;
1033 char response[] = "";
1034 reply(reinterpret_cast<uint8_t*>(response), 0);
1035 });
1036
1037 engine->Run();
1038
1039 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1040 0);
1041
1042 while (!did_call) {
1043 engine->task_runner()->ProcessTasks();
1044 }
1045}
1046
1047// Flutter consumes the first WM_CLOSE message to allow the app to cancel the
1048// exit. If the app does not cancel the exit, Flutter synthesizes a second
1049// WM_CLOSE message.
1050TEST_F(FlutterWindowsEngineTest, TestExitSecondCloseMessage) {
1051 FlutterWindowsEngineBuilder builder{GetContext()};
1052 builder.SetDartEntrypoint("exitTestExit");
1053 bool second_close = false;
1054
1055 auto engine = builder.Build();
1056 auto window_binding_handler =
1057 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1058 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1059
1060 EngineModifier modifier(engine.get());
1061 modifier.SetImplicitView(&view);
1062 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1063 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1064 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(true));
1065 EXPECT_CALL(*handler, Quit)
1066 .WillOnce([handler_ptr = handler.get()](
1067 std::optional<HWND> hwnd, std::optional<WPARAM> wparam,
1068 std::optional<LPARAM> lparam, UINT exit_code) {
1069 handler_ptr->WindowsLifecycleManager::Quit(hwnd, wparam, lparam,
1070 exit_code);
1071 });
1072 EXPECT_CALL(*handler, DispatchMessage)
1073 .WillRepeatedly(
1074 [&engine](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
1075 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1076 hwnd, msg, wparam, lparam);
1077 });
1078 modifier.SetLifecycleManager(std::move(handler));
1079 engine->lifecycle_manager()->BeginProcessingExit();
1080
1081 engine->Run();
1082
1083 // This delegate will be registered after the lifecycle manager, so it will be
1084 // called only when a message is not consumed by the lifecycle manager. This
1085 // should be called on the second, synthesized WM_CLOSE message that the
1086 // lifecycle manager posts.
1087 engine->window_proc_delegate_manager()->RegisterTopLevelWindowProcDelegate(
1088 [](HWND hwnd, UINT message, WPARAM wpar, LPARAM lpar, void* user_data,
1089 LRESULT* result) {
1090 switch (message) {
1091 case WM_CLOSE: {
1092 bool* called = reinterpret_cast<bool*>(user_data);
1093 *called = true;
1094 return true;
1095 }
1096 }
1097 return false;
1098 },
1099 reinterpret_cast<void*>(&second_close));
1100
1101 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1102 0);
1103
1104 while (!second_close) {
1105 engine->task_runner()->ProcessTasks();
1106 }
1107}
1108
1109TEST_F(FlutterWindowsEngineTest, TestExitCloseMultiWindow) {
1110 FlutterWindowsEngineBuilder builder{GetContext()};
1111 builder.SetDartEntrypoint("exitTestExit");
1112 bool finished = false;
1113
1114 auto engine = builder.Build();
1115 auto window_binding_handler =
1116 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1117 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1118
1119 EngineModifier modifier(engine.get());
1120 modifier.SetImplicitView(&view);
1121 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1122 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1123 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce([&finished]() {
1124 finished = true;
1125 return false;
1126 });
1127 // Quit should not be called when there is more than one window.
1128 EXPECT_CALL(*handler, Quit).Times(0);
1129 modifier.SetLifecycleManager(std::move(handler));
1130 engine->lifecycle_manager()->BeginProcessingExit();
1131
1132 engine->Run();
1133
1134 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1135 0);
1136
1137 while (!finished) {
1138 engine->task_runner()->ProcessTasks();
1139 }
1140}
1141
1142TEST_F(FlutterWindowsEngineTest, LifecycleManagerDisabledByDefault) {
1143 FlutterWindowsEngineBuilder builder{GetContext()};
1144
1145 auto engine = builder.Build();
1146 auto window_binding_handler =
1147 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1148 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1149
1150 EngineModifier modifier(engine.get());
1151 modifier.SetImplicitView(&view);
1152 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1153 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1154 EXPECT_CALL(*handler, IsLastWindowOfProcess).Times(0);
1155 modifier.SetLifecycleManager(std::move(handler));
1156
1157 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1158 0);
1159}
1160
1161TEST_F(FlutterWindowsEngineTest, EnableApplicationLifecycle) {
1162 FlutterWindowsEngineBuilder builder{GetContext()};
1163
1164 auto engine = builder.Build();
1165 auto window_binding_handler =
1166 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1167 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1168
1169 EngineModifier modifier(engine.get());
1170 modifier.SetImplicitView(&view);
1171 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1172 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1173 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(false));
1174 modifier.SetLifecycleManager(std::move(handler));
1175 engine->lifecycle_manager()->BeginProcessingExit();
1176
1177 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1178 0);
1179}
1180
1181TEST_F(FlutterWindowsEngineTest, ApplicationLifecycleExternalWindow) {
1182 FlutterWindowsEngineBuilder builder{GetContext()};
1183
1184 auto engine = builder.Build();
1185 auto window_binding_handler =
1186 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1187 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1188
1189 EngineModifier modifier(engine.get());
1190 modifier.SetImplicitView(&view);
1191 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1192 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1193 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(false));
1194 modifier.SetLifecycleManager(std::move(handler));
1195 engine->lifecycle_manager()->BeginProcessingExit();
1196
1197 engine->lifecycle_manager()->ExternalWindowMessage(0, WM_CLOSE, 0, 0);
1198}
1199
1200TEST_F(FlutterWindowsEngineTest, LifecycleStateTransition) {
1201 FlutterWindowsEngineBuilder builder{GetContext()};
1202
1203 auto engine = builder.Build();
1204 auto window_binding_handler =
1205 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1206 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1207
1208 EngineModifier modifier(engine.get());
1209 modifier.SetImplicitView(&view);
1210 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1211 engine->Run();
1212
1213 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1214 (HWND)1, WM_SIZE, SIZE_RESTORED, 0);
1215
1216 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1217 PumpMessage();
1218 }
1219
1220 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1222
1223 engine->lifecycle_manager()->OnWindowStateEvent((HWND)1,
1225
1226 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1227 PumpMessage();
1228 }
1229
1230 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1232
1233 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1234 (HWND)1, WM_SIZE, SIZE_MINIMIZED, 0);
1235
1236 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1237 PumpMessage();
1238 }
1239
1240 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1242
1243 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1244 (HWND)1, WM_SIZE, SIZE_RESTORED, 0);
1245
1246 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1247 PumpMessage();
1248 }
1249
1250 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1252}
1253
1254TEST_F(FlutterWindowsEngineTest, ExternalWindowMessage) {
1255 FlutterWindowsEngineBuilder builder{GetContext()};
1256
1257 auto engine = builder.Build();
1258 auto window_binding_handler =
1259 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1260 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1261
1262 EngineModifier modifier(engine.get());
1263 modifier.SetImplicitView(&view);
1264 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1265 // Sets lifecycle state to resumed.
1266 engine->Run();
1267
1268 // Ensure HWND(1) is in the set of visible windows before hiding it.
1269 engine->ProcessExternalWindowMessage(reinterpret_cast<HWND>(1), WM_SHOWWINDOW,
1270 TRUE, NULL);
1271 engine->ProcessExternalWindowMessage(reinterpret_cast<HWND>(1), WM_SHOWWINDOW,
1272 FALSE, NULL);
1273
1274 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1275 PumpMessage();
1276 }
1277
1278 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1280}
1281
1283 FlutterWindowsEngineBuilder builder{GetContext()};
1284 HWND outer = reinterpret_cast<HWND>(1);
1285 HWND inner = reinterpret_cast<HWND>(2);
1286
1287 auto engine = builder.Build();
1288 auto window_binding_handler =
1289 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1290 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1291 ON_CALL(view, GetWindowHandle).WillByDefault([=]() { return inner; });
1292
1293 EngineModifier modifier(engine.get());
1294 modifier.SetImplicitView(&view);
1295 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1296 // Sets lifecycle state to resumed.
1297 engine->Run();
1298
1299 // Show both top-level and Flutter window.
1300 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1301 outer, WM_SHOWWINDOW, TRUE, NULL);
1302 view.OnWindowStateEvent(inner, WindowStateEvent::kShow);
1303 view.OnWindowStateEvent(inner, WindowStateEvent::kFocus);
1304
1305 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1306 PumpMessage();
1307 }
1308
1309 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1311
1312 // Hide Flutter window, but not top level window.
1313 view.OnWindowStateEvent(inner, WindowStateEvent::kHide);
1314
1315 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1316 PumpMessage();
1317 }
1318
1319 // The top-level window is still visible, so we ought not enter hidden state.
1320 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1322}
1323
1324TEST_F(FlutterWindowsEngineTest, EnableLifecycleState) {
1325 FlutterWindowsEngineBuilder builder{GetContext()};
1326 builder.SetDartEntrypoint("enableLifecycleTest");
1327 bool finished = false;
1328
1329 auto engine = builder.Build();
1330 auto window_binding_handler =
1331 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1332 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1333
1334 EngineModifier modifier(engine.get());
1335 modifier.SetImplicitView(&view);
1336 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1337 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1338 EXPECT_CALL(*handler, SetLifecycleState)
1339 .WillRepeatedly([handler_ptr = handler.get()](AppLifecycleState state) {
1340 handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
1341 });
1342 modifier.SetLifecycleManager(std::move(handler));
1343
1344 auto binary_messenger =
1345 std::make_unique<BinaryMessengerImpl>(engine->messenger());
1346 // Mark the test only as completed on receiving an inactive state message.
1347 binary_messenger->SetMessageHandler(
1348 "flutter/unittest", [&finished](const uint8_t* message,
1349 size_t message_size, BinaryReply reply) {
1350 std::string contents(message, message + message_size);
1351 EXPECT_NE(contents.find("AppLifecycleState.inactive"),
1352 std::string::npos);
1353 finished = true;
1354 char response[] = "";
1355 reply(reinterpret_cast<uint8_t*>(response), 0);
1356 });
1357
1358 engine->Run();
1359
1360 // Test that setting the state before enabling lifecycle does nothing.
1361 HWND hwnd = reinterpret_cast<HWND>(1);
1362 view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1363 view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
1364 EXPECT_FALSE(finished);
1365
1366 // Test that we can set the state afterwards.
1367
1368 engine->lifecycle_manager()->BeginProcessingLifecycle();
1369 view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1370
1371 while (!finished) {
1372 engine->task_runner()->ProcessTasks();
1373 }
1374}
1375
1376TEST_F(FlutterWindowsEngineTest, LifecycleStateToFrom) {
1377 FlutterWindowsEngineBuilder builder{GetContext()};
1378 builder.SetDartEntrypoint("enableLifecycleToFrom");
1379 bool enabled_lifecycle = false;
1380 bool dart_responded = false;
1381
1382 auto engine = builder.Build();
1383 auto window_binding_handler =
1384 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1385 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1386
1387 EngineModifier modifier(engine.get());
1388 modifier.SetImplicitView(&view);
1389 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1390 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1391 EXPECT_CALL(*handler, SetLifecycleState)
1392 .WillRepeatedly([handler_ptr = handler.get()](AppLifecycleState state) {
1393 handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
1394 });
1395 handler->begin_processing_callback = [&]() { enabled_lifecycle = true; };
1396 modifier.SetLifecycleManager(std::move(handler));
1397
1398 auto binary_messenger =
1399 std::make_unique<BinaryMessengerImpl>(engine->messenger());
1400 binary_messenger->SetMessageHandler(
1401 "flutter/unittest",
1402 [&](const uint8_t* message, size_t message_size, BinaryReply reply) {
1403 std::string contents(message, message + message_size);
1404 EXPECT_NE(contents.find("AppLifecycleState."), std::string::npos);
1405 dart_responded = true;
1406 char response[] = "";
1407 reply(reinterpret_cast<uint8_t*>(response), 0);
1408 });
1409
1410 engine->Run();
1411
1412 while (!enabled_lifecycle) {
1413 engine->task_runner()->ProcessTasks();
1414 }
1415
1416 HWND hwnd = reinterpret_cast<HWND>(1);
1417 view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1418 view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
1419
1420 while (!dart_responded) {
1421 engine->task_runner()->ProcessTasks();
1422 }
1423}
1424
1426 FlutterWindowsEngineBuilder builder{GetContext()};
1427 builder.SetDartEntrypoint("enableLifecycleToFrom");
1428
1429 auto engine = builder.Build();
1430 auto window_binding_handler =
1431 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1432 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1433
1434 EngineModifier modifier(engine.get());
1435 modifier.SetImplicitView(&view);
1436 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1437
1438 bool lifecycle_began = false;
1439 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1440 handler->begin_processing_callback = [&]() { lifecycle_began = true; };
1441 modifier.SetLifecycleManager(std::move(handler));
1442
1443 engine->Run();
1444
1445 while (!lifecycle_began) {
1446 engine->task_runner()->ProcessTasks();
1447 }
1448}
1449
1450TEST_F(FlutterWindowsEngineTest, ReceivePlatformViewMessage) {
1451 FlutterWindowsEngineBuilder builder{GetContext()};
1452 builder.SetDartEntrypoint("sendCreatePlatformViewMethod");
1453 auto engine = builder.Build();
1454
1455 EngineModifier modifier{engine.get()};
1456 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1457
1458 bool received_call = false;
1459
1460 auto manager = std::make_unique<MockPlatformViewManager>(engine.get());
1461 EXPECT_CALL(*manager, AddPlatformView)
1462 .WillOnce([&](PlatformViewId id, std::string_view type_name) {
1463 received_call = true;
1464 return true;
1465 });
1466 modifier.SetPlatformViewPlugin(std::move(manager));
1467
1468 engine->Run();
1469
1470 while (!received_call) {
1471 engine->task_runner()->ProcessTasks();
1472 }
1473}
1474
1475TEST_F(FlutterWindowsEngineTest, AddViewFailureDoesNotHang) {
1476 FlutterWindowsEngineBuilder builder{GetContext()};
1477 auto engine = builder.Build();
1478
1479 EngineModifier modifier{engine.get()};
1480
1481 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1482 modifier.embedder_api().AddView = MOCK_ENGINE_PROC(
1483 AddView,
1485 const FlutterAddViewInfo* info) { return kInternalInconsistency; });
1486
1487 ASSERT_TRUE(engine->Run());
1488
1489 // Create the first view. This is the implicit view and isn't added to the
1490 // engine.
1491 auto implicit_window = std::make_unique<NiceMock<MockWindowBindingHandler>>();
1492
1493 std::unique_ptr<FlutterWindowsView> implicit_view =
1494 engine->CreateView(std::move(implicit_window),
1495 /*is_sized_to_content=*/false, BoxConstraints());
1496
1497 EXPECT_TRUE(implicit_view);
1498
1499 // Create a second view. The embedder attempts to add it to the engine.
1500 auto second_window = std::make_unique<NiceMock<MockWindowBindingHandler>>();
1501
1502 EXPECT_DEBUG_DEATH(
1503 engine->CreateView(std::move(second_window),
1504 /*is_sized_to_content=*/false, BoxConstraints()),
1505 "FlutterEngineAddView returned an unexpected result");
1506}
1507
1508TEST_F(FlutterWindowsEngineTest, RemoveViewFailureDoesNotHang) {
1509 FlutterWindowsEngineBuilder builder{GetContext()};
1510 builder.SetDartEntrypoint("sendCreatePlatformViewMethod");
1511 auto engine = builder.Build();
1512
1513 EngineModifier modifier{engine.get()};
1514
1515 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1516 modifier.embedder_api().RemoveView = MOCK_ENGINE_PROC(
1517 RemoveView,
1519 const FlutterRemoveViewInfo* info) { return kInternalInconsistency; });
1520
1521 ASSERT_TRUE(engine->Run());
1522 EXPECT_DEBUG_DEATH(engine->RemoveView(123),
1523 "FlutterEngineRemoveView returned an unexpected result");
1524}
1525
1527 auto& context = GetContext();
1529 builder.SetDartEntrypoint("mergedUIThread");
1530 builder.SetUIThreadPolicy(FlutterDesktopUIThreadPolicy::RunOnPlatformThread);
1531
1532 std::optional<std::thread::id> ui_thread_id;
1533
1534 auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1535 ui_thread_id = std::this_thread::get_id();
1536 });
1537 context.AddNativeFunction("Signal", native_entry);
1538
1539 EnginePtr engine{builder.RunHeadless()};
1540 while (!ui_thread_id) {
1541 PumpMessage();
1542 }
1543 ASSERT_EQ(*ui_thread_id, std::this_thread::get_id());
1544}
1545
1546TEST_F(FlutterWindowsEngineTest, OnViewFocusChangeRequest) {
1547 FlutterWindowsEngineBuilder builder{GetContext()};
1548 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
1549 auto window_binding_handler =
1550 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1551 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1552
1553 EngineModifier modifier(engine.get());
1554 modifier.SetImplicitView(&view);
1555
1557 request.view_id = kImplicitViewId;
1558
1559 EXPECT_CALL(view, Focus()).WillOnce(Return(true));
1560 modifier.OnViewFocusChangeRequest(&request);
1561}
1562
1563TEST_F(FlutterWindowsEngineTest, UpdateSemanticsMultiView) {
1564 auto& context = GetContext();
1566 builder.SetDartEntrypoint("sendSemanticsTreeInfo");
1567
1568 // Setup: a signal for when we have send out all of our semantics updates
1569 bool done = false;
1570 auto native_entry =
1571 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
1572 context.AddNativeFunction("Signal", native_entry);
1573
1574 // Setup: Create the engine and two views + enable semantics
1575 EnginePtr engine{builder.RunHeadless()};
1576 ASSERT_NE(engine, nullptr);
1577
1578 auto window_binding_handler1 =
1579 std::make_unique<NiceMock<MockWindowBindingHandler>>();
1580 auto window_binding_handler2 =
1581 std::make_unique<NiceMock<MockWindowBindingHandler>>();
1582
1583 // The following mocks are required by
1584 // FlutterWindowsView::CreateWindowMetricsEvent so that we create a valid
1585 // view.
1586 EXPECT_CALL(*window_binding_handler1, GetPhysicalWindowBounds)
1587 .WillRepeatedly(testing::Return(PhysicalWindowBounds{100, 100}));
1588 EXPECT_CALL(*window_binding_handler1, GetDpiScale)
1589 .WillRepeatedly(testing::Return(96.0));
1590 EXPECT_CALL(*window_binding_handler2, GetPhysicalWindowBounds)
1591 .WillRepeatedly(testing::Return(PhysicalWindowBounds{200, 200}));
1592 EXPECT_CALL(*window_binding_handler2, GetDpiScale)
1593 .WillRepeatedly(testing::Return(96.0));
1594
1595 auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
1596 EngineModifier modifier{windows_engine};
1597 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1598
1599 // We want to avoid adding an implicit view as the first view
1600 modifier.SetNextViewId(kImplicitViewId + 1);
1601
1602 auto view1 = windows_engine->CreateView(std::move(window_binding_handler1),
1603 /*is_sized_to_content=*/false,
1604 BoxConstraints());
1605 auto view2 = windows_engine->CreateView(std::move(window_binding_handler2),
1606 /*is_sized_to_content=*/false,
1607 BoxConstraints());
1608
1609 // Act: UpdateSemanticsEnabled will trigger the semantics updates
1610 // to get sent.
1611 windows_engine->UpdateSemanticsEnabled(true);
1612
1613 while (!done) {
1614 windows_engine->task_runner()->ProcessTasks();
1615 }
1616
1617 auto accessibility_bridge1 = view1->accessibility_bridge().lock();
1618 auto accessibility_bridge2 = view2->accessibility_bridge().lock();
1619
1620 // Expect: that the semantics trees are updated with their
1621 // respective nodes.
1622 while (
1623 !accessibility_bridge1->GetPlatformNodeFromTree(view1->view_id() + 1)) {
1624 windows_engine->task_runner()->ProcessTasks();
1625 }
1626
1627 while (
1628 !accessibility_bridge2->GetPlatformNodeFromTree(view2->view_id() + 1)) {
1629 windows_engine->task_runner()->ProcessTasks();
1630 }
1631
1632 // Rely on timeout mechanism in CI.
1633 auto tree1 = accessibility_bridge1->GetTree();
1634 auto tree2 = accessibility_bridge2->GetTree();
1635 EXPECT_NE(tree1->GetFromId(view1->view_id() + 1), nullptr);
1636 EXPECT_NE(tree2->GetFromId(view2->view_id() + 1), nullptr);
1637}
1638
1639} // namespace testing
1640} // namespace flutter
void SetLifecycleManager(std::unique_ptr< WindowsLifecycleManager > &&handler)
void SetFrameInterval(uint64_t frame_interval_nanos)
void OnViewFocusChangeRequest(const FlutterViewFocusChangeRequest *request)
void SetEGLManager(std::unique_ptr< egl::Manager > egl_manager)
void SetStartTime(uint64_t start_time_nanos)
Reset the start_time field that is used to align vsync events.
FlutterEngineProcTable & embedder_api()
void SetImplicitView(FlutterWindowsView *view)
virtual void Quit(std::optional< HWND > window, std::optional< WPARAM > wparam, std::optional< LPARAM > lparam, UINT exit_code)
virtual void SetLifecycleState(AppLifecycleState state)
void SetSwitches(std::vector< std::string > switches)
void SetWindowsProcTable(std::shared_ptr< WindowsProcTable > windows_proc_table)
MOCK_METHOD(bool, Focus,(),(override))
MOCK_METHOD(void, NotifyWinEventWrapper,(ui::AXPlatformNodeWin *, ax::mojom::Event),(override))
MockFlutterWindowsView(FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > wbh)
MOCK_METHOD(HWND, GetWindowHandle,(),(const, override))
MOCK_METHOD(void, DispatchMessage,(HWND, UINT, WPARAM, LPARAM),(override))
MOCK_METHOD(void, Quit,(std::optional< HWND >, std::optional< WPARAM >, std::optional< LPARAM >, UINT),(override))
MOCK_METHOD(void, SetLifecycleState,(AppLifecycleState),(override))
MOCK_METHOD(bool, IsLastWindowOfProcess,(),(override))
void SetDartEntrypoint(std::string_view entrypoint)
static MallocMapping Copy(const T *begin, const T *end)
Definition mapping.h:162
static constexpr TimeDelta FromMilliseconds(int64_t millis)
Definition time_delta.h:46
constexpr int64_t ToNanoseconds() const
Definition time_delta.h:61
constexpr TimeDelta ToEpochDelta() const
Definition time_point.h:52
static TimePoint Now()
Definition time_point.cc:49
std::string type_name
#define FLUTTER_API_SYMBOL(symbol)
Definition embedder.h:67
@ kOpenGL
Definition embedder.h:80
FlutterAccessibilityFeature
Definition embedder.h:91
@ kFlutterAccessibilityFeatureHighContrast
Request that UI be rendered with darker colors.
Definition embedder.h:105
@ kInternalInconsistency
Definition embedder.h:76
FlutterEngineDisplaysUpdateType
Definition embedder.h:2373
@ kFlutterEngineDisplaysUpdateTypeStartup
Definition embedder.h:2379
@ kBackground
Suitable for threads that shouldn't disrupt high priority work.
Definition embedder.h:378
@ kDisplay
Suitable for threads which generate data for the display.
Definition embedder.h:382
@ kNormal
Default priority level.
Definition embedder.h:380
@ kRaster
Suitable for thread which raster data.
Definition embedder.h:384
@ kFlutterSemanticsActionDismiss
A request that the node should be dismissed.
Definition embedder.h:167
#define FLUTTER_ENGINE_VERSION
Definition embedder.h:70
GLFWwindow * window
Definition main.cc:60
FlutterEngine engine
Definition main.cc:84
FlView * view
const char * message
return TRUE
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
const gchar * channel
const gchar FlBinaryMessengerMessageHandler handler
void(* FlutterDesktopBinaryReply)(const uint8_t *data, size_t data_size, void *user_data)
@ RunOnPlatformThread
int64_t PlatformViewId
FlutterDesktopBinaryReply callback
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition macros.h:27
size_t length
TEST_F(DisplayListTest, Defaults)
std::unique_ptr< FlutterDesktopEngine, EngineDeleter > EnginePtr
std::unique_ptr< FlutterDesktopViewController, ViewControllerDeleter > ViewControllerPtr
void MockEmbedderApiForKeyboard(EngineModifier &modifier, std::shared_ptr< MockKeyResponseController > response_controller)
constexpr int64_t kImplicitViewId
UINT GetDpiForMonitor(HMONITOR monitor)
Definition dpi_utils.cc:132
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 manager
static void DispatchSemanticsAction(JNIEnv *env, jobject jcaller, jlong shell_holder, jint id, jint action, jobject args, jint args_position)
std::function< void(const uint8_t *reply, size_t reply_size)> BinaryReply
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
static void WindowsPlatformThreadPrioritySetter(FlutterThreadPriority priority)
static void ScheduleFrame(JNIEnv *env, jobject jcaller, jlong shell_holder)
Definition ref_ptr.h:261
#define MOCK_ENGINE_PROC(proc, mock_impl)
std::shared_ptr< ContextGLES > context
FlutterEngineScheduleFrameFnPtr ScheduleFrame
Definition embedder.h:3805
FlutterEnginePlatformMessageReleaseResponseHandleFnPtr PlatformMessageReleaseResponseHandle
Definition embedder.h:3781
FlutterEngineUpdateLocalesFnPtr UpdateLocales
Definition embedder.h:3798
FlutterEngineRunsAOTCompiledDartCodeFnPtr RunsAOTCompiledDartCode
Definition embedder.h:3799
FlutterEngineSetNextFrameCallbackFnPtr SetNextFrameCallback
Definition embedder.h:3806
FlutterEngineShutdownFnPtr Shutdown
Definition embedder.h:3770
FlutterEngineOnVsyncFnPtr OnVsync
Definition embedder.h:3790
FlutterEngineGetCurrentTimeFnPtr GetCurrentTime
Definition embedder.h:3796
FlutterEngineNotifyDisplayUpdateFnPtr NotifyDisplayUpdate
Definition embedder.h:3804
FlutterEngineSendPlatformMessageFnPtr SendPlatformMessage
Definition embedder.h:3777
FlutterEnginePlatformMessageCreateResponseHandleFnPtr PlatformMessageCreateResponseHandle
Definition embedder.h:3779
FlutterEngineRunFnPtr Run
Definition embedder.h:3769
FlutterEngineUpdateAccessibilityFeaturesFnPtr UpdateAccessibilityFeatures
Definition embedder.h:3788
FlutterEnginePostRenderThreadTaskFnPtr PostRenderThreadTask
Definition embedder.h:3795
FlutterEngineSendSemanticsActionFnPtr SendSemanticsAction
Definition embedder.h:3810
FlutterRendererType type
Definition embedder.h:1038
FlutterViewId view_id
The identifier of the view that received the focus event.
Definition embedder.h:1257
std::string str() const
Definition logging.cc:102
FlutterDesktopEngineRef FlutterDesktopViewControllerGetEngine(FlutterDesktopViewControllerRef controller)
const size_t start
#define CREATE_NATIVE_ENTRY(native_entry)
struct tagMSG MSG
LONG_PTR LRESULT
unsigned int UINT
LONG_PTR LPARAM
#define GetCurrentTime()
#define GetMessage
UINT_PTR WPARAM
#define DispatchMessage
unsigned long DWORD