Flutter Windows Embedder
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 
8 #include "flutter/fml/logging.h"
9 #include "flutter/fml/macros.h"
10 #include "flutter/shell/platform/embedder/embedder.h"
11 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
14 #include "flutter/shell/platform/windows/testing/egl/mock_manager.h"
15 #include "flutter/shell/platform/windows/testing/engine_modifier.h"
16 #include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h"
17 #include "flutter/shell/platform/windows/testing/mock_platform_view_manager.h"
18 #include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h"
19 #include "flutter/shell/platform/windows/testing/mock_windows_proc_table.h"
20 #include "flutter/shell/platform/windows/testing/test_keyboard.h"
21 #include "flutter/shell/platform/windows/testing/windows_test.h"
22 #include "flutter/shell/platform/windows/testing/windows_test_config_builder.h"
23 #include "flutter/third_party/accessibility/ax/platform/ax_platform_node_win.h"
24 #include "fml/synchronization/waitable_event.h"
25 #include "gmock/gmock.h"
26 #include "gtest/gtest.h"
27 
28 // winbase.h defines GetCurrentTime as a macro.
29 #undef GetCurrentTime
30 
31 namespace {
32 // Process the next win32 message if there is one. This can be used to
33 // pump the Windows platform thread task runner.
34 void PumpMessage() {
35  ::MSG msg;
36  if (::GetMessage(&msg, nullptr, 0, 0)) {
37  ::TranslateMessage(&msg);
38  ::DispatchMessage(&msg);
39  }
40 }
41 } // namespace
42 
43 namespace flutter {
44 namespace testing {
45 
46 using ::testing::_;
47 using ::testing::DoAll;
48 using ::testing::NiceMock;
49 using ::testing::Return;
50 using ::testing::SetArgPointee;
51 
52 class FlutterWindowsEngineTest : public WindowsTest {};
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 
67 TEST_F(FlutterWindowsEngineTest, TaskRunnerDelayedTask) {
68  bool finished = false;
69  auto runner = std::make_unique<TaskRunner>(
70  [] {
71  return static_cast<uint64_t>(
72  fml::TimePoint::Now().ToEpochDelta().ToNanoseconds());
73  },
74  [&](const FlutterTask*) { finished = true; });
75  runner->PostFlutterTask(
76  FlutterTask{},
77  static_cast<uint64_t>((fml::TimePoint::Now().ToEpochDelta() +
78  fml::TimeDelta::FromMilliseconds(50))
79  .ToNanoseconds()));
80  auto start = fml::TimePoint::Now();
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)
89 TEST_F(FlutterWindowsEngineTest, TaskRunnerDoesNotDeadlock) {
90  auto runner = std::make_unique<TaskRunner>(
91  [] {
92  return static_cast<uint64_t>(
93  fml::TimePoint::Now().ToEpochDelta().ToNanoseconds());
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 
137 TEST_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(
229  FlutterThreadPriority::kRaster);
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
235  modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
236  UpdateAccessibilityFeatures,
237  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
238  FlutterAccessibilityFeature flags) { return kSuccess; });
239 
240  // It should send locale info.
241  bool update_locales_called = false;
242  modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC(
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;
256  modifier.embedder_api().SendPlatformMessage = MOCK_ENGINE_PROC(
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 
269  modifier.embedder_api().NotifyDisplayUpdate = MOCK_ENGINE_PROC(
270  NotifyDisplayUpdate,
271  ([&notify_display_update_called](
272  FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
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 
297 TEST_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; }));
305  modifier.embedder_api().OnVsync = MOCK_ENGINE_PROC(
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 
324 TEST_F(FlutterWindowsEngineTest, RunWithoutANGLEUsesSoftware) {
325  FlutterWindowsEngineBuilder builder{GetContext()};
326  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
327  EngineModifier modifier(engine.get());
328 
329  modifier.embedder_api().NotifyDisplayUpdate =
330  MOCK_ENGINE_PROC(NotifyDisplayUpdate,
331  ([engine_instance = engine.get()](
332  FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
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
351  modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
352  UpdateAccessibilityFeatures,
353  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
354  FlutterAccessibilityFeature flags) { return kSuccess; });
355 
356  // Stub out UpdateLocales and SendPlatformMessage as we don't have a fully
357  // initialized engine instance.
358  modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC(
359  UpdateLocales, ([](auto engine, const FlutterLocale** locales,
360  size_t locales_count) { return kSuccess; }));
361  modifier.embedder_api().SendPlatformMessage =
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 
377 TEST_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 
383  modifier.embedder_api().NotifyDisplayUpdate =
384  MOCK_ENGINE_PROC(NotifyDisplayUpdate,
385  ([engine_instance = engine.get()](
386  FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
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
392  modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
393  UpdateAccessibilityFeatures,
394  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
395  FlutterAccessibilityFeature flags) { return kSuccess; });
396 
397  // Stub out UpdateLocales and SendPlatformMessage as we don't have a fully
398  // initialized engine instance.
399  modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC(
400  UpdateLocales, ([](auto engine, const FlutterLocale** locales,
401  size_t locales_count) { return kSuccess; }));
402  modifier.embedder_api().SendPlatformMessage =
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 
412 TEST_F(FlutterWindowsEngineTest, SendPlatformMessageWithoutResponse) {
413  FlutterWindowsEngineBuilder builder{GetContext()};
414  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
415  EngineModifier modifier(engine.get());
416 
417  const char* channel = "test";
418  const std::vector<uint8_t> test_message = {1, 2, 3, 4};
419 
420  // Without a response, SendPlatformMessage should be a simple pass-through.
421  bool called = false;
422  modifier.embedder_api().SendPlatformMessage = MOCK_ENGINE_PROC(
423  SendPlatformMessage, ([&called, test_message](auto engine, auto message) {
424  called = true;
425  EXPECT_STREQ(message->channel, "test");
426  EXPECT_EQ(message->message_size, test_message.size());
427  EXPECT_EQ(memcmp(message->message, test_message.data(),
428  message->message_size),
429  0);
430  EXPECT_EQ(message->response_handle, nullptr);
431  return kSuccess;
432  }));
433 
434  engine->SendPlatformMessage(channel, test_message.data(), test_message.size(),
435  nullptr, nullptr);
436  EXPECT_TRUE(called);
437 }
438 
439 TEST_F(FlutterWindowsEngineTest, PlatformMessageRoundTrip) {
440  FlutterWindowsEngineBuilder builder{GetContext()};
441  builder.SetDartEntrypoint("hiPlatformChannels");
442 
443  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
444  EngineModifier modifier(engine.get());
445  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
446 
447  auto binary_messenger =
448  std::make_unique<BinaryMessengerImpl>(engine->messenger());
449 
450  engine->Run();
451  bool did_call_callback = false;
452  bool did_call_reply = false;
453  bool did_call_dart_reply = false;
454  std::string channel = "hi";
455  binary_messenger->SetMessageHandler(
456  channel,
457  [&did_call_callback, &did_call_dart_reply](
458  const uint8_t* message, size_t message_size, BinaryReply reply) {
459  if (message_size == 5) {
460  EXPECT_EQ(message[0], static_cast<uint8_t>('h'));
461  char response[] = {'b', 'y', 'e'};
462  reply(reinterpret_cast<uint8_t*>(response), 3);
463  did_call_callback = true;
464  } else {
465  EXPECT_EQ(message_size, 3);
466  EXPECT_EQ(message[0], static_cast<uint8_t>('b'));
467  did_call_dart_reply = true;
468  }
469  });
470  char payload[] = {'h', 'e', 'l', 'l', 'o'};
471  binary_messenger->Send(
472  channel, reinterpret_cast<uint8_t*>(payload), 5,
473  [&did_call_reply](const uint8_t* reply, size_t reply_size) {
474  EXPECT_EQ(reply_size, 5);
475  EXPECT_EQ(reply[0], static_cast<uint8_t>('h'));
476  did_call_reply = true;
477  });
478  // Rely on timeout mechanism in CI.
479  while (!did_call_callback || !did_call_reply || !did_call_dart_reply) {
480  engine->task_runner()->ProcessTasks();
481  }
482 }
483 
484 TEST_F(FlutterWindowsEngineTest, PlatformMessageRespondOnDifferentThread) {
485  FlutterWindowsEngineBuilder builder{GetContext()};
486  builder.SetDartEntrypoint("hiPlatformChannels");
487 
488  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
489 
490  EngineModifier modifier(engine.get());
491  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
492 
493  auto binary_messenger =
494  std::make_unique<BinaryMessengerImpl>(engine->messenger());
495 
496  engine->Run();
497  bool did_call_callback = false;
498  bool did_call_reply = false;
499  bool did_call_dart_reply = false;
500  std::string channel = "hi";
501  std::unique_ptr<std::thread> reply_thread;
502  binary_messenger->SetMessageHandler(
503  channel,
504  [&did_call_callback, &did_call_dart_reply, &reply_thread](
505  const uint8_t* message, size_t message_size, BinaryReply reply) {
506  if (message_size == 5) {
507  EXPECT_EQ(message[0], static_cast<uint8_t>('h'));
508  reply_thread.reset(new std::thread([reply = std::move(reply)]() {
509  char response[] = {'b', 'y', 'e'};
510  reply(reinterpret_cast<uint8_t*>(response), 3);
511  }));
512  did_call_callback = true;
513  } else {
514  EXPECT_EQ(message_size, 3);
515  EXPECT_EQ(message[0], static_cast<uint8_t>('b'));
516  did_call_dart_reply = true;
517  }
518  });
519  char payload[] = {'h', 'e', 'l', 'l', 'o'};
520  binary_messenger->Send(
521  channel, reinterpret_cast<uint8_t*>(payload), 5,
522  [&did_call_reply](const uint8_t* reply, size_t reply_size) {
523  EXPECT_EQ(reply_size, 5);
524  EXPECT_EQ(reply[0], static_cast<uint8_t>('h'));
525  did_call_reply = true;
526  });
527  // Rely on timeout mechanism in CI.
528  while (!did_call_callback || !did_call_reply || !did_call_dart_reply) {
529  engine->task_runner()->ProcessTasks();
530  }
531  ASSERT_TRUE(reply_thread);
532  reply_thread->join();
533 }
534 
535 TEST_F(FlutterWindowsEngineTest, SendPlatformMessageWithResponse) {
536  FlutterWindowsEngineBuilder builder{GetContext()};
537  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
538  EngineModifier modifier(engine.get());
539 
540  const char* channel = "test";
541  const std::vector<uint8_t> test_message = {1, 2, 3, 4};
542  auto* dummy_response_handle =
543  reinterpret_cast<FlutterPlatformMessageResponseHandle*>(5);
544  const FlutterDesktopBinaryReply reply_handler = [](auto... args) {};
545  void* reply_user_data = reinterpret_cast<void*>(6);
546 
547  // When a response is requested, a handle should be created, passed as part
548  // of the message, and then released.
549  bool create_response_handle_called = false;
550  modifier.embedder_api().PlatformMessageCreateResponseHandle =
551  MOCK_ENGINE_PROC(
552  PlatformMessageCreateResponseHandle,
553  ([&create_response_handle_called, &reply_handler, reply_user_data,
554  dummy_response_handle](auto engine, auto reply, auto user_data,
555  auto response_handle) {
556  create_response_handle_called = true;
557  EXPECT_EQ(reply, reply_handler);
558  EXPECT_EQ(user_data, reply_user_data);
559  EXPECT_NE(response_handle, nullptr);
560  *response_handle = dummy_response_handle;
561  return kSuccess;
562  }));
563  bool release_response_handle_called = false;
564  modifier.embedder_api().PlatformMessageReleaseResponseHandle =
565  MOCK_ENGINE_PROC(
566  PlatformMessageReleaseResponseHandle,
567  ([&release_response_handle_called, dummy_response_handle](
568  auto engine, auto response_handle) {
569  release_response_handle_called = true;
570  EXPECT_EQ(response_handle, dummy_response_handle);
571  return kSuccess;
572  }));
573  bool send_message_called = false;
574  modifier.embedder_api().SendPlatformMessage = MOCK_ENGINE_PROC(
575  SendPlatformMessage, ([&send_message_called, test_message,
576  dummy_response_handle](auto engine, auto message) {
577  send_message_called = true;
578  EXPECT_STREQ(message->channel, "test");
579  EXPECT_EQ(message->message_size, test_message.size());
580  EXPECT_EQ(memcmp(message->message, test_message.data(),
581  message->message_size),
582  0);
583  EXPECT_EQ(message->response_handle, dummy_response_handle);
584  return kSuccess;
585  }));
586 
587  engine->SendPlatformMessage(channel, test_message.data(), test_message.size(),
588  reply_handler, reply_user_data);
589  EXPECT_TRUE(create_response_handle_called);
590  EXPECT_TRUE(release_response_handle_called);
591  EXPECT_TRUE(send_message_called);
592 }
593 
594 TEST_F(FlutterWindowsEngineTest, DispatchSemanticsAction) {
595  FlutterWindowsEngineBuilder builder{GetContext()};
596  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
597  EngineModifier modifier(engine.get());
598 
599  bool called = false;
600  std::string message = "Hello";
601  modifier.embedder_api().SendSemanticsAction = MOCK_ENGINE_PROC(
602  SendSemanticsAction, ([&called, &message](auto engine, auto info) {
603  called = true;
604  EXPECT_EQ(info->view_id, 456);
605  EXPECT_EQ(info->node_id, 42);
606  EXPECT_EQ(info->action, kFlutterSemanticsActionDismiss);
607  EXPECT_EQ(memcmp(info->data, message.c_str(), message.size()), 0);
608  EXPECT_EQ(info->data_length, message.size());
609  return kSuccess;
610  }));
611 
612  auto data = fml::MallocMapping::Copy(message.c_str(), message.size());
613  engine->DispatchSemanticsAction(456, 42, kFlutterSemanticsActionDismiss,
614  std::move(data));
615  EXPECT_TRUE(called);
616 }
617 
618 TEST_F(FlutterWindowsEngineTest, SetsThreadPriority) {
619  WindowsPlatformThreadPrioritySetter(FlutterThreadPriority::kBackground);
620  EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
621  THREAD_PRIORITY_BELOW_NORMAL);
622 
623  WindowsPlatformThreadPrioritySetter(FlutterThreadPriority::kDisplay);
624  EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
625  THREAD_PRIORITY_ABOVE_NORMAL);
626 
627  WindowsPlatformThreadPrioritySetter(FlutterThreadPriority::kRaster);
628  EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
629  THREAD_PRIORITY_ABOVE_NORMAL);
630 
631  // FlutterThreadPriority::kNormal does not change thread priority, reset to 0
632  // here.
633  SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
634 
635  WindowsPlatformThreadPrioritySetter(FlutterThreadPriority::kNormal);
636  EXPECT_EQ(GetThreadPriority(GetCurrentThread()), THREAD_PRIORITY_NORMAL);
637 }
638 
639 TEST_F(FlutterWindowsEngineTest, AddPluginRegistrarDestructionCallback) {
640  FlutterWindowsEngineBuilder builder{GetContext()};
641  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
642  EngineModifier modifier(engine.get());
643 
644  MockEmbedderApiForKeyboard(modifier,
645  std::make_shared<MockKeyResponseController>());
646 
647  engine->Run();
648 
649  // Verify that destruction handlers don't overwrite each other.
650  int result1 = 0;
651  int result2 = 0;
652  engine->AddPluginRegistrarDestructionCallback(
654  auto result = reinterpret_cast<int*>(ref);
655  *result = 1;
656  },
657  reinterpret_cast<FlutterDesktopPluginRegistrarRef>(&result1));
658  engine->AddPluginRegistrarDestructionCallback(
660  auto result = reinterpret_cast<int*>(ref);
661  *result = 2;
662  },
663  reinterpret_cast<FlutterDesktopPluginRegistrarRef>(&result2));
664 
665  engine->Stop();
666  EXPECT_EQ(result1, 1);
667  EXPECT_EQ(result2, 2);
668 }
669 
671  FlutterWindowsEngineBuilder builder{GetContext()};
672  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
673  EngineModifier modifier(engine.get());
674 
675  bool called = false;
676  modifier.embedder_api().ScheduleFrame =
677  MOCK_ENGINE_PROC(ScheduleFrame, ([&called](auto engine) {
678  called = true;
679  return kSuccess;
680  }));
681 
682  engine->ScheduleFrame();
683  EXPECT_TRUE(called);
684 }
685 
686 TEST_F(FlutterWindowsEngineTest, SetNextFrameCallback) {
687  FlutterWindowsEngineBuilder builder{GetContext()};
688  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
689  EngineModifier modifier(engine.get());
690 
691  bool called = false;
692  modifier.embedder_api().SetNextFrameCallback = MOCK_ENGINE_PROC(
693  SetNextFrameCallback, ([&called](auto engine, auto callback, auto data) {
694  called = true;
695  return kSuccess;
696  }));
697 
698  engine->SetNextFrameCallback([]() {});
699  EXPECT_TRUE(called);
700 }
701 
702 TEST_F(FlutterWindowsEngineTest, GetExecutableName) {
703  FlutterWindowsEngineBuilder builder{GetContext()};
704  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
705  EXPECT_EQ(engine->GetExecutableName(), "flutter_windows_unittests.exe");
706 }
707 
708 // Ensure that after setting or resetting the high contrast feature,
709 // the corresponding status flag can be retrieved from the engine.
710 TEST_F(FlutterWindowsEngineTest, UpdateHighContrastFeature) {
711  auto windows_proc_table = std::make_shared<MockWindowsProcTable>();
712  EXPECT_CALL(*windows_proc_table, GetHighContrastEnabled)
713  .WillOnce(Return(true))
714  .WillOnce(Return(false));
715 
716  FlutterWindowsEngineBuilder builder{GetContext()};
717  builder.SetWindowsProcTable(windows_proc_table);
718  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
719  EngineModifier modifier(engine.get());
720 
721  std::optional<FlutterAccessibilityFeature> engine_flags;
722  modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
723  UpdateAccessibilityFeatures, ([&engine_flags](auto engine, auto flags) {
724  engine_flags = flags;
725  return kSuccess;
726  }));
727  modifier.embedder_api().SendPlatformMessage = MOCK_ENGINE_PROC(
728  SendPlatformMessage,
729  [](auto engine, const auto message) { return kSuccess; });
730 
731  // 1: High contrast is enabled.
732  engine->UpdateHighContrastMode();
733 
734  EXPECT_TRUE(engine->high_contrast_enabled());
735  EXPECT_TRUE(engine_flags.has_value());
736  EXPECT_TRUE(
737  engine_flags.value() &
738  FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast);
739 
740  // 2: High contrast is disabled.
741  engine_flags.reset();
742  engine->UpdateHighContrastMode();
743 
744  EXPECT_FALSE(engine->high_contrast_enabled());
745  EXPECT_TRUE(engine_flags.has_value());
746  EXPECT_FALSE(
747  engine_flags.value() &
748  FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast);
749 }
750 
751 TEST_F(FlutterWindowsEngineTest, PostRasterThreadTask) {
752  FlutterWindowsEngineBuilder builder{GetContext()};
753  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
754  EngineModifier modifier(engine.get());
755 
756  modifier.embedder_api().PostRenderThreadTask = MOCK_ENGINE_PROC(
757  PostRenderThreadTask, ([](auto engine, auto callback, auto context) {
758  callback(context);
759  return kSuccess;
760  }));
761 
762  bool called = false;
763  engine->PostRasterThreadTask([&called]() { called = true; });
764 
765  EXPECT_TRUE(called);
766 }
767 
768 class MockFlutterWindowsView : public FlutterWindowsView {
769  public:
771  std::unique_ptr<WindowBindingHandler> wbh)
772  : FlutterWindowsView(kImplicitViewId, engine, std::move(wbh)) {}
774 
776  NotifyWinEventWrapper,
777  (ui::AXPlatformNodeWin*, ax::mojom::Event),
778  (override));
779  MOCK_METHOD(HWND, GetWindowHandle, (), (const, override));
780  MOCK_METHOD(bool, Focus, (), (override));
781 
782  private:
783  FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView);
784 };
785 
786 // Verify the view is notified of accessibility announcements.
787 TEST_F(FlutterWindowsEngineTest, AccessibilityAnnouncement) {
788  auto& context = GetContext();
789  WindowsConfigBuilder builder{context};
790  builder.SetDartEntrypoint("sendAccessibilityAnnouncement");
791 
792  bool done = false;
793  auto native_entry =
794  CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
795  context.AddNativeFunction("Signal", native_entry);
796 
797  EnginePtr engine{builder.RunHeadless()};
798  ASSERT_NE(engine, nullptr);
799 
800  ui::AXPlatformNodeDelegateBase parent_delegate;
801  AlertPlatformNodeDelegate delegate{parent_delegate};
802 
803  auto window_binding_handler =
804  std::make_unique<NiceMock<MockWindowBindingHandler>>();
805  EXPECT_CALL(*window_binding_handler, GetAlertDelegate)
806  .WillOnce(Return(&delegate));
807 
808  auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
809  MockFlutterWindowsView view{windows_engine,
810  std::move(window_binding_handler)};
811  EngineModifier modifier{windows_engine};
812  modifier.SetImplicitView(&view);
813 
814  windows_engine->UpdateSemanticsEnabled(true);
815 
816  EXPECT_CALL(view, NotifyWinEventWrapper).Times(1);
817 
818  // Rely on timeout mechanism in CI.
819  while (!done) {
820  windows_engine->task_runner()->ProcessTasks();
821  }
822 }
823 
824 // Verify the app can send accessibility announcements while in headless mode.
825 TEST_F(FlutterWindowsEngineTest, AccessibilityAnnouncementHeadless) {
826  auto& context = GetContext();
827  WindowsConfigBuilder builder{context};
828  builder.SetDartEntrypoint("sendAccessibilityAnnouncement");
829 
830  bool done = false;
831  auto native_entry =
832  CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
833  context.AddNativeFunction("Signal", native_entry);
834 
835  EnginePtr engine{builder.RunHeadless()};
836  ASSERT_NE(engine, nullptr);
837 
838  auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
839  windows_engine->UpdateSemanticsEnabled(true);
840 
841  // Rely on timeout mechanism in CI.
842  while (!done) {
843  windows_engine->task_runner()->ProcessTasks();
844  }
845 }
846 
847 // Verify the engine does not crash if it receives an accessibility event
848 // it does not support yet.
849 TEST_F(FlutterWindowsEngineTest, AccessibilityTooltip) {
850  fml::testing::LogCapture log_capture;
851 
852  auto& context = GetContext();
853  WindowsConfigBuilder builder{context};
854  builder.SetDartEntrypoint("sendAccessibilityTooltipEvent");
855 
856  bool done = false;
857  auto native_entry =
858  CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
859  context.AddNativeFunction("Signal", native_entry);
860 
861  ViewControllerPtr controller{builder.Run()};
862  ASSERT_NE(controller, nullptr);
863 
864  auto engine = FlutterDesktopViewControllerGetEngine(controller.get());
865  auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine);
866  windows_engine->UpdateSemanticsEnabled(true);
867 
868  // Rely on timeout mechanism in CI.
869  while (!done) {
870  windows_engine->task_runner()->ProcessTasks();
871  }
872 
873  // Verify no error was logged.
874  // Regression test for:
875  // https://github.com/flutter/flutter/issues/144274
876  EXPECT_EQ(log_capture.str().find("tooltip"), std::string::npos);
877 }
878 
880  public:
882  : WindowsLifecycleManager(engine) {}
884 
886  void,
887  Quit,
888  (std::optional<HWND>, std::optional<WPARAM>, std::optional<LPARAM>, UINT),
889  (override));
890  MOCK_METHOD(void, DispatchMessage, (HWND, UINT, WPARAM, LPARAM), (override));
891  MOCK_METHOD(bool, IsLastWindowOfProcess, (), (override));
893 
894  void BeginProcessingLifecycle() override {
898  }
899  }
900 
901  std::function<void()> begin_processing_callback = nullptr;
902 };
903 
905  FlutterWindowsEngineBuilder builder{GetContext()};
906  builder.SetDartEntrypoint("exitTestExit");
907  bool finished = false;
908 
909  auto engine = builder.Build();
910  auto window_binding_handler =
911  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
912  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
913 
914  EngineModifier modifier(engine.get());
915  modifier.SetImplicitView(&view);
916  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
917  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
918  EXPECT_CALL(*handler, Quit)
919  .WillOnce([&finished](std::optional<HWND> hwnd,
920  std::optional<WPARAM> wparam,
921  std::optional<LPARAM> lparam,
922  UINT exit_code) { finished = exit_code == 0; });
923  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillRepeatedly(Return(true));
924  modifier.SetLifecycleManager(std::move(handler));
925 
926  engine->lifecycle_manager()->BeginProcessingExit();
927 
928  engine->Run();
929 
930  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
931  0);
932 
933  // The test will only succeed when this while loop exits. Otherwise it will
934  // timeout.
935  while (!finished) {
936  engine->task_runner()->ProcessTasks();
937  }
938 }
939 
941  FlutterWindowsEngineBuilder builder{GetContext()};
942  builder.SetDartEntrypoint("exitTestCancel");
943  bool did_call = false;
944 
945  auto engine = builder.Build();
946  auto window_binding_handler =
947  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
948  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
949 
950  EngineModifier modifier(engine.get());
951  modifier.SetImplicitView(&view);
952  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
953  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
954  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillRepeatedly(Return(true));
955  EXPECT_CALL(*handler, Quit).Times(0);
956  modifier.SetLifecycleManager(std::move(handler));
957  engine->lifecycle_manager()->BeginProcessingExit();
958 
959  auto binary_messenger =
960  std::make_unique<BinaryMessengerImpl>(engine->messenger());
961  binary_messenger->SetMessageHandler(
962  "flutter/platform", [&did_call](const uint8_t* message,
963  size_t message_size, BinaryReply reply) {
964  std::string contents(message, message + message_size);
965  EXPECT_NE(contents.find("\"method\":\"System.exitApplication\""),
966  std::string::npos);
967  EXPECT_NE(contents.find("\"type\":\"required\""), std::string::npos);
968  EXPECT_NE(contents.find("\"exitCode\":0"), std::string::npos);
969  did_call = true;
970  char response[] = "";
971  reply(reinterpret_cast<uint8_t*>(response), 0);
972  });
973 
974  engine->Run();
975 
976  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
977  0);
978 
979  while (!did_call) {
980  engine->task_runner()->ProcessTasks();
981  }
982 }
983 
984 // Flutter consumes the first WM_CLOSE message to allow the app to cancel the
985 // exit. If the app does not cancel the exit, Flutter synthesizes a second
986 // WM_CLOSE message.
987 TEST_F(FlutterWindowsEngineTest, TestExitSecondCloseMessage) {
988  FlutterWindowsEngineBuilder builder{GetContext()};
989  builder.SetDartEntrypoint("exitTestExit");
990  bool second_close = false;
991 
992  auto engine = builder.Build();
993  auto window_binding_handler =
994  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
995  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
996 
997  EngineModifier modifier(engine.get());
998  modifier.SetImplicitView(&view);
999  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1000  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1001  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(true));
1002  EXPECT_CALL(*handler, Quit)
1003  .WillOnce([handler_ptr = handler.get()](
1004  std::optional<HWND> hwnd, std::optional<WPARAM> wparam,
1005  std::optional<LPARAM> lparam, UINT exit_code) {
1006  handler_ptr->WindowsLifecycleManager::Quit(hwnd, wparam, lparam,
1007  exit_code);
1008  });
1009  EXPECT_CALL(*handler, DispatchMessage)
1010  .WillRepeatedly(
1011  [&engine](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
1012  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1013  hwnd, msg, wparam, lparam);
1014  });
1015  modifier.SetLifecycleManager(std::move(handler));
1016  engine->lifecycle_manager()->BeginProcessingExit();
1017 
1018  engine->Run();
1019 
1020  // This delegate will be registered after the lifecycle manager, so it will be
1021  // called only when a message is not consumed by the lifecycle manager. This
1022  // should be called on the second, synthesized WM_CLOSE message that the
1023  // lifecycle manager posts.
1024  engine->window_proc_delegate_manager()->RegisterTopLevelWindowProcDelegate(
1025  [](HWND hwnd, UINT message, WPARAM wpar, LPARAM lpar, void* user_data,
1026  LRESULT* result) {
1027  switch (message) {
1028  case WM_CLOSE: {
1029  bool* called = reinterpret_cast<bool*>(user_data);
1030  *called = true;
1031  return true;
1032  }
1033  }
1034  return false;
1035  },
1036  reinterpret_cast<void*>(&second_close));
1037 
1038  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1039  0);
1040 
1041  while (!second_close) {
1042  engine->task_runner()->ProcessTasks();
1043  }
1044 }
1045 
1046 TEST_F(FlutterWindowsEngineTest, TestExitCloseMultiWindow) {
1047  FlutterWindowsEngineBuilder builder{GetContext()};
1048  builder.SetDartEntrypoint("exitTestExit");
1049  bool finished = false;
1050 
1051  auto engine = builder.Build();
1052  auto window_binding_handler =
1053  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1054  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1055 
1056  EngineModifier modifier(engine.get());
1057  modifier.SetImplicitView(&view);
1058  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1059  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1060  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce([&finished]() {
1061  finished = true;
1062  return false;
1063  });
1064  // Quit should not be called when there is more than one window.
1065  EXPECT_CALL(*handler, Quit).Times(0);
1066  modifier.SetLifecycleManager(std::move(handler));
1067  engine->lifecycle_manager()->BeginProcessingExit();
1068 
1069  engine->Run();
1070 
1071  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1072  0);
1073 
1074  while (!finished) {
1075  engine->task_runner()->ProcessTasks();
1076  }
1077 }
1078 
1079 TEST_F(FlutterWindowsEngineTest, LifecycleManagerDisabledByDefault) {
1080  FlutterWindowsEngineBuilder builder{GetContext()};
1081 
1082  auto engine = builder.Build();
1083  auto window_binding_handler =
1084  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1085  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1086 
1087  EngineModifier modifier(engine.get());
1088  modifier.SetImplicitView(&view);
1089  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1090  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1091  EXPECT_CALL(*handler, IsLastWindowOfProcess).Times(0);
1092  modifier.SetLifecycleManager(std::move(handler));
1093 
1094  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1095  0);
1096 }
1097 
1098 TEST_F(FlutterWindowsEngineTest, EnableApplicationLifecycle) {
1099  FlutterWindowsEngineBuilder builder{GetContext()};
1100 
1101  auto engine = builder.Build();
1102  auto window_binding_handler =
1103  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1104  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1105 
1106  EngineModifier modifier(engine.get());
1107  modifier.SetImplicitView(&view);
1108  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1109  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1110  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(false));
1111  modifier.SetLifecycleManager(std::move(handler));
1112  engine->lifecycle_manager()->BeginProcessingExit();
1113 
1114  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1115  0);
1116 }
1117 
1118 TEST_F(FlutterWindowsEngineTest, ApplicationLifecycleExternalWindow) {
1119  FlutterWindowsEngineBuilder builder{GetContext()};
1120 
1121  auto engine = builder.Build();
1122  auto window_binding_handler =
1123  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1124  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1125 
1126  EngineModifier modifier(engine.get());
1127  modifier.SetImplicitView(&view);
1128  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1129  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1130  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(false));
1131  modifier.SetLifecycleManager(std::move(handler));
1132  engine->lifecycle_manager()->BeginProcessingExit();
1133 
1134  engine->lifecycle_manager()->ExternalWindowMessage(0, WM_CLOSE, 0, 0);
1135 }
1136 
1137 TEST_F(FlutterWindowsEngineTest, LifecycleStateTransition) {
1138  FlutterWindowsEngineBuilder builder{GetContext()};
1139 
1140  auto engine = builder.Build();
1141  auto window_binding_handler =
1142  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1143  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1144 
1145  EngineModifier modifier(engine.get());
1146  modifier.SetImplicitView(&view);
1147  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1148  engine->Run();
1149 
1150  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1151  (HWND)1, WM_SIZE, SIZE_RESTORED, 0);
1152 
1153  while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1154  PumpMessage();
1155  }
1156 
1157  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1159 
1160  engine->lifecycle_manager()->OnWindowStateEvent((HWND)1,
1162 
1163  while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1164  PumpMessage();
1165  }
1166 
1167  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1169 
1170  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1171  (HWND)1, WM_SIZE, SIZE_MINIMIZED, 0);
1172 
1173  while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1174  PumpMessage();
1175  }
1176 
1177  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1179 
1180  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1181  (HWND)1, WM_SIZE, SIZE_RESTORED, 0);
1182 
1183  while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1184  PumpMessage();
1185  }
1186 
1187  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1189 }
1190 
1191 TEST_F(FlutterWindowsEngineTest, ExternalWindowMessage) {
1192  FlutterWindowsEngineBuilder builder{GetContext()};
1193 
1194  auto engine = builder.Build();
1195  auto window_binding_handler =
1196  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1197  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1198 
1199  EngineModifier modifier(engine.get());
1200  modifier.SetImplicitView(&view);
1201  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1202  // Sets lifecycle state to resumed.
1203  engine->Run();
1204 
1205  // Ensure HWND(1) is in the set of visible windows before hiding it.
1206  engine->ProcessExternalWindowMessage(reinterpret_cast<HWND>(1), WM_SHOWWINDOW,
1207  TRUE, NULL);
1208  engine->ProcessExternalWindowMessage(reinterpret_cast<HWND>(1), WM_SHOWWINDOW,
1209  FALSE, NULL);
1210 
1211  while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1212  PumpMessage();
1213  }
1214 
1215  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1217 }
1218 
1219 TEST_F(FlutterWindowsEngineTest, InnerWindowHidden) {
1220  FlutterWindowsEngineBuilder builder{GetContext()};
1221  HWND outer = reinterpret_cast<HWND>(1);
1222  HWND inner = reinterpret_cast<HWND>(2);
1223 
1224  auto engine = builder.Build();
1225  auto window_binding_handler =
1226  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1227  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1228  ON_CALL(view, GetWindowHandle).WillByDefault([=]() { return inner; });
1229 
1230  EngineModifier modifier(engine.get());
1231  modifier.SetImplicitView(&view);
1232  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1233  // Sets lifecycle state to resumed.
1234  engine->Run();
1235 
1236  // Show both top-level and Flutter window.
1237  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1238  outer, WM_SHOWWINDOW, TRUE, NULL);
1239  view.OnWindowStateEvent(inner, WindowStateEvent::kShow);
1240  view.OnWindowStateEvent(inner, WindowStateEvent::kFocus);
1241 
1242  while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1243  PumpMessage();
1244  }
1245 
1246  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1248 
1249  // Hide Flutter window, but not top level window.
1250  view.OnWindowStateEvent(inner, WindowStateEvent::kHide);
1251 
1252  while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1253  PumpMessage();
1254  }
1255 
1256  // The top-level window is still visible, so we ought not enter hidden state.
1257  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1259 }
1260 
1261 TEST_F(FlutterWindowsEngineTest, EnableLifecycleState) {
1262  FlutterWindowsEngineBuilder builder{GetContext()};
1263  builder.SetDartEntrypoint("enableLifecycleTest");
1264  bool finished = false;
1265 
1266  auto engine = builder.Build();
1267  auto window_binding_handler =
1268  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1269  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1270 
1271  EngineModifier modifier(engine.get());
1272  modifier.SetImplicitView(&view);
1273  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1274  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1275  EXPECT_CALL(*handler, SetLifecycleState)
1276  .WillRepeatedly([handler_ptr = handler.get()](AppLifecycleState state) {
1277  handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
1278  });
1279  modifier.SetLifecycleManager(std::move(handler));
1280 
1281  auto binary_messenger =
1282  std::make_unique<BinaryMessengerImpl>(engine->messenger());
1283  // Mark the test only as completed on receiving an inactive state message.
1284  binary_messenger->SetMessageHandler(
1285  "flutter/unittest", [&finished](const uint8_t* message,
1286  size_t message_size, BinaryReply reply) {
1287  std::string contents(message, message + message_size);
1288  EXPECT_NE(contents.find("AppLifecycleState.inactive"),
1289  std::string::npos);
1290  finished = true;
1291  char response[] = "";
1292  reply(reinterpret_cast<uint8_t*>(response), 0);
1293  });
1294 
1295  engine->Run();
1296 
1297  // Test that setting the state before enabling lifecycle does nothing.
1298  HWND hwnd = reinterpret_cast<HWND>(1);
1299  view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1300  view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
1301  EXPECT_FALSE(finished);
1302 
1303  // Test that we can set the state afterwards.
1304 
1305  engine->lifecycle_manager()->BeginProcessingLifecycle();
1306  view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1307 
1308  while (!finished) {
1309  engine->task_runner()->ProcessTasks();
1310  }
1311 }
1312 
1313 TEST_F(FlutterWindowsEngineTest, LifecycleStateToFrom) {
1314  FlutterWindowsEngineBuilder builder{GetContext()};
1315  builder.SetDartEntrypoint("enableLifecycleToFrom");
1316  bool enabled_lifecycle = false;
1317  bool dart_responded = false;
1318 
1319  auto engine = builder.Build();
1320  auto window_binding_handler =
1321  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1322  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1323 
1324  EngineModifier modifier(engine.get());
1325  modifier.SetImplicitView(&view);
1326  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1327  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1328  EXPECT_CALL(*handler, SetLifecycleState)
1329  .WillRepeatedly([handler_ptr = handler.get()](AppLifecycleState state) {
1330  handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
1331  });
1332  handler->begin_processing_callback = [&]() { enabled_lifecycle = true; };
1333  modifier.SetLifecycleManager(std::move(handler));
1334 
1335  auto binary_messenger =
1336  std::make_unique<BinaryMessengerImpl>(engine->messenger());
1337  binary_messenger->SetMessageHandler(
1338  "flutter/unittest",
1339  [&](const uint8_t* message, size_t message_size, BinaryReply reply) {
1340  std::string contents(message, message + message_size);
1341  EXPECT_NE(contents.find("AppLifecycleState."), std::string::npos);
1342  dart_responded = true;
1343  char response[] = "";
1344  reply(reinterpret_cast<uint8_t*>(response), 0);
1345  });
1346 
1347  engine->Run();
1348 
1349  while (!enabled_lifecycle) {
1350  engine->task_runner()->ProcessTasks();
1351  }
1352 
1353  HWND hwnd = reinterpret_cast<HWND>(1);
1354  view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1355  view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
1356 
1357  while (!dart_responded) {
1358  engine->task_runner()->ProcessTasks();
1359  }
1360 }
1361 
1362 TEST_F(FlutterWindowsEngineTest, ChannelListenedTo) {
1363  FlutterWindowsEngineBuilder builder{GetContext()};
1364  builder.SetDartEntrypoint("enableLifecycleToFrom");
1365 
1366  auto engine = builder.Build();
1367  auto window_binding_handler =
1368  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1369  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1370 
1371  EngineModifier modifier(engine.get());
1372  modifier.SetImplicitView(&view);
1373  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1374 
1375  bool lifecycle_began = false;
1376  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1377  handler->begin_processing_callback = [&]() { lifecycle_began = true; };
1378  modifier.SetLifecycleManager(std::move(handler));
1379 
1380  engine->Run();
1381 
1382  while (!lifecycle_began) {
1383  engine->task_runner()->ProcessTasks();
1384  }
1385 }
1386 
1387 TEST_F(FlutterWindowsEngineTest, ReceivePlatformViewMessage) {
1388  FlutterWindowsEngineBuilder builder{GetContext()};
1389  builder.SetDartEntrypoint("sendCreatePlatformViewMethod");
1390  auto engine = builder.Build();
1391 
1392  EngineModifier modifier{engine.get()};
1393  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1394 
1395  bool received_call = false;
1396 
1397  auto manager = std::make_unique<MockPlatformViewManager>(engine.get());
1398  EXPECT_CALL(*manager, AddPlatformView)
1399  .WillOnce([&](PlatformViewId id, std::string_view type_name) {
1400  received_call = true;
1401  return true;
1402  });
1403  modifier.SetPlatformViewPlugin(std::move(manager));
1404 
1405  engine->Run();
1406 
1407  while (!received_call) {
1408  engine->task_runner()->ProcessTasks();
1409  }
1410 }
1411 
1412 TEST_F(FlutterWindowsEngineTest, AddViewFailureDoesNotHang) {
1413  FlutterWindowsEngineBuilder builder{GetContext()};
1414  auto engine = builder.Build();
1415 
1416  EngineModifier modifier{engine.get()};
1417 
1418  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1419  modifier.embedder_api().AddView = MOCK_ENGINE_PROC(
1420  AddView,
1421  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
1422  const FlutterAddViewInfo* info) { return kInternalInconsistency; });
1423 
1424  ASSERT_TRUE(engine->Run());
1425 
1426  // Create the first view. This is the implicit view and isn't added to the
1427  // engine.
1428  auto implicit_window = std::make_unique<NiceMock<MockWindowBindingHandler>>();
1429 
1430  std::unique_ptr<FlutterWindowsView> implicit_view =
1431  engine->CreateView(std::move(implicit_window));
1432 
1433  EXPECT_TRUE(implicit_view);
1434 
1435  // Create a second view. The embedder attempts to add it to the engine.
1436  auto second_window = std::make_unique<NiceMock<MockWindowBindingHandler>>();
1437 
1438  EXPECT_DEBUG_DEATH(engine->CreateView(std::move(second_window)),
1439  "FlutterEngineAddView returned an unexpected result");
1440 }
1441 
1442 TEST_F(FlutterWindowsEngineTest, RemoveViewFailureDoesNotHang) {
1443  FlutterWindowsEngineBuilder builder{GetContext()};
1444  builder.SetDartEntrypoint("sendCreatePlatformViewMethod");
1445  auto engine = builder.Build();
1446 
1447  EngineModifier modifier{engine.get()};
1448 
1449  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1450  modifier.embedder_api().RemoveView = MOCK_ENGINE_PROC(
1451  RemoveView,
1452  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
1453  const FlutterRemoveViewInfo* info) { return kInternalInconsistency; });
1454 
1455  ASSERT_TRUE(engine->Run());
1456  EXPECT_DEBUG_DEATH(engine->RemoveView(123),
1457  "FlutterEngineRemoveView returned an unexpected result");
1458 }
1459 
1461  auto& context = GetContext();
1462  WindowsConfigBuilder builder{context};
1463  builder.SetDartEntrypoint("mergedUIThread");
1464  builder.SetUIThreadPolicy(FlutterDesktopUIThreadPolicy::RunOnPlatformThread);
1465 
1466  std::optional<std::thread::id> ui_thread_id;
1467 
1468  auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1469  ui_thread_id = std::this_thread::get_id();
1470  });
1471  context.AddNativeFunction("Signal", native_entry);
1472 
1473  EnginePtr engine{builder.RunHeadless()};
1474  while (!ui_thread_id) {
1475  PumpMessage();
1476  }
1477  ASSERT_EQ(*ui_thread_id, std::this_thread::get_id());
1478 }
1479 
1480 TEST_F(FlutterWindowsEngineTest, OnViewFocusChangeRequest) {
1481  FlutterWindowsEngineBuilder builder{GetContext()};
1482  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
1483  auto window_binding_handler =
1484  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1485  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1486 
1487  EngineModifier modifier(engine.get());
1488  modifier.SetImplicitView(&view);
1489 
1490  FlutterViewFocusChangeRequest request;
1491  request.view_id = kImplicitViewId;
1492 
1493  EXPECT_CALL(view, Focus()).WillOnce(Return(true));
1494  modifier.OnViewFocusChangeRequest(&request);
1495 }
1496 
1497 TEST_F(FlutterWindowsEngineTest, UpdateSemanticsMultiView) {
1498  auto& context = GetContext();
1499  WindowsConfigBuilder builder{context};
1500  builder.SetDartEntrypoint("sendSemanticsTreeInfo");
1501 
1502  // Setup: a signal for when we have send out all of our semantics updates
1503  bool done = false;
1504  auto native_entry =
1505  CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
1506  context.AddNativeFunction("Signal", native_entry);
1507 
1508  // Setup: Create the engine and two views + enable semantics
1509  EnginePtr engine{builder.RunHeadless()};
1510  ASSERT_NE(engine, nullptr);
1511 
1512  auto window_binding_handler1 =
1513  std::make_unique<NiceMock<MockWindowBindingHandler>>();
1514  auto window_binding_handler2 =
1515  std::make_unique<NiceMock<MockWindowBindingHandler>>();
1516 
1517  // The following mocks are required by
1518  // FlutterWindowsView::CreateWindowMetricsEvent so that we create a valid
1519  // view.
1520  EXPECT_CALL(*window_binding_handler1, GetPhysicalWindowBounds)
1521  .WillRepeatedly(testing::Return(PhysicalWindowBounds{100, 100}));
1522  EXPECT_CALL(*window_binding_handler1, GetDpiScale)
1523  .WillRepeatedly(testing::Return(96.0));
1524  EXPECT_CALL(*window_binding_handler2, GetPhysicalWindowBounds)
1525  .WillRepeatedly(testing::Return(PhysicalWindowBounds{200, 200}));
1526  EXPECT_CALL(*window_binding_handler2, GetDpiScale)
1527  .WillRepeatedly(testing::Return(96.0));
1528 
1529  auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
1530  EngineModifier modifier{windows_engine};
1531  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1532 
1533  // We want to avoid adding an implicit view as the first view
1534  modifier.SetNextViewId(kImplicitViewId + 1);
1535 
1536  auto view1 = windows_engine->CreateView(std::move(window_binding_handler1));
1537  auto view2 = windows_engine->CreateView(std::move(window_binding_handler2));
1538 
1539  // Act: UpdateSemanticsEnabled will trigger the semantics updates
1540  // to get sent.
1541  windows_engine->UpdateSemanticsEnabled(true);
1542 
1543  while (!done) {
1544  windows_engine->task_runner()->ProcessTasks();
1545  }
1546 
1547  auto accessibility_bridge1 = view1->accessibility_bridge().lock();
1548  auto accessibility_bridge2 = view2->accessibility_bridge().lock();
1549 
1550  // Expect: that the semantics trees are updated with their
1551  // respective nodes.
1552  while (
1553  !accessibility_bridge1->GetPlatformNodeFromTree(view1->view_id() + 1)) {
1554  windows_engine->task_runner()->ProcessTasks();
1555  }
1556 
1557  while (
1558  !accessibility_bridge2->GetPlatformNodeFromTree(view2->view_id() + 1)) {
1559  windows_engine->task_runner()->ProcessTasks();
1560  }
1561 
1562  // Rely on timeout mechanism in CI.
1563  auto tree1 = accessibility_bridge1->GetTree();
1564  auto tree2 = accessibility_bridge2->GetTree();
1565  EXPECT_NE(tree1->GetFromId(view1->view_id() + 1), nullptr);
1566  EXPECT_NE(tree2->GetFromId(view2->view_id() + 1), nullptr);
1567 }
1568 
1569 } // namespace testing
1570 } // namespace flutter
std::unique_ptr< FlutterWindowsView > CreateView(std::unique_ptr< WindowBindingHandler > window)
virtual void Quit(std::optional< HWND > window, std::optional< WPARAM > wparam, std::optional< LPARAM > lparam, UINT exit_code)
virtual void DispatchMessage(HWND window, UINT msg, WPARAM wparam, LPARAM lparam)
virtual void SetLifecycleState(AppLifecycleState state)
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(* FlutterDesktopBinaryReply)(const uint8_t *data, size_t data_size, void *user_data)
FlutterDesktopEngineRef FlutterDesktopViewControllerGetEngine(FlutterDesktopViewControllerRef ref)
@ RunOnPlatformThread
int64_t PlatformViewId
FlutterDesktopBinaryReply callback
Win32Message message
TEST_F(CompositorOpenGLTest, CreateBackingStore)
UINT GetDpiForMonitor(HMONITOR monitor)
Definition: dpi_utils.cc:132
std::function< void(const uint8_t *reply, size_t reply_size)> BinaryReply
static void WindowsPlatformThreadPrioritySetter(FlutterThreadPriority priority)
constexpr FlutterViewId kImplicitViewId