Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 9f207a3

Browse files
Add initial settings message to Windows embedding (#22323)
Sends the flutter/settings update message to the engine after starting it. For now values other than 24-hour time preference are hard-coded, but dark mode support can be added later. Fixes flutter/flutter#65590
1 parent a92adb0 commit 9f207a3

File tree

8 files changed

+153
-8
lines changed

8 files changed

+153
-8
lines changed

shell/platform/embedder/test_utils/proc_table_replacement.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
// FlutterEngineProcTable entries (by using statics) to facilitate mocking in
99
// tests of code built on top of the embedder API.
1010
//
11-
// This should *ONLY* be used in unit tests as it is leaky by design.
11+
// This should *ONLY* be used in unit tests as it is leaky by design. Because it
12+
// uses statics for the lambdas, tests using this macro are generally not safe
13+
// to run multiple times (e.g., using gtest_repeat).
1214
//
1315
// |proc| should be the name of an entry in FlutterEngineProcTable, such as
1416
// "initialize". |mock_impl| should be a lamba that replaces its implementation,

shell/platform/windows/flutter_windows_engine.cc

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@
88
#include <iostream>
99
#include <sstream>
1010

11+
#include "flutter/shell/platform/common/cpp/client_wrapper/binary_messenger_impl.h"
12+
#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h"
13+
#include "flutter/shell/platform/common/cpp/json_message_codec.h"
1114
#include "flutter/shell/platform/common/cpp/path_utils.h"
1215
#include "flutter/shell/platform/windows/flutter_windows_view.h"
1316
#include "flutter/shell/platform/windows/string_conversion.h"
1417
#include "flutter/shell/platform/windows/system_utils.h"
18+
#include "third_party/rapidjson/include/rapidjson/document.h"
1519

1620
namespace flutter {
1721

@@ -114,10 +118,19 @@ FlutterWindowsEngine::FlutterWindowsEngine(const FlutterProjectBundle& project)
114118
plugin_registrar_ = std::make_unique<FlutterDesktopPluginRegistrar>();
115119
plugin_registrar_->engine = this;
116120

121+
messenger_wrapper_ = std::make_unique<BinaryMessengerImpl>(messenger_.get());
117122
message_dispatcher_ =
118123
std::make_unique<IncomingMessageDispatcher>(messenger_.get());
119124
window_proc_delegate_manager_ =
120125
std::make_unique<Win32WindowProcDelegateManager>();
126+
127+
// Set up internal channels.
128+
// TODO: Replace this with an embedder.h API. See
129+
// https://github.com/flutter/flutter/issues/71099
130+
settings_channel_ =
131+
std::make_unique<BasicMessageChannel<rapidjson::Document>>(
132+
messenger_wrapper_.get(), "flutter/settings",
133+
&JsonMessageCodec::GetInstance());
121134
}
122135

123136
FlutterWindowsEngine::~FlutterWindowsEngine() {
@@ -327,7 +340,15 @@ void FlutterWindowsEngine::SendSystemSettings() {
327340
embedder_api_.UpdateLocales(engine_, flutter_locale_list.data(),
328341
flutter_locale_list.size());
329342

330-
// TODO: Send 'flutter/settings' channel settings here as well.
343+
rapidjson::Document settings(rapidjson::kObjectType);
344+
auto& allocator = settings.GetAllocator();
345+
settings.AddMember("alwaysUse24HourFormat",
346+
Prefer24HourTime(GetUserTimeFormat()), allocator);
347+
settings.AddMember("textScaleFactor", 1.0, allocator);
348+
// TODO: Implement dark mode support.
349+
// https://github.com/flutter/flutter/issues/54612
350+
settings.AddMember("platformBrightness", "light", allocator);
351+
settings_channel_->Send(settings);
331352
}
332353

333354
} // namespace flutter

shell/platform/windows/flutter_windows_engine.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@
1010
#include <optional>
1111
#include <vector>
1212

13+
#include "flutter/shell/platform/common/cpp/client_wrapper/binary_messenger_impl.h"
14+
#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/basic_message_channel.h"
1315
#include "flutter/shell/platform/common/cpp/incoming_message_dispatcher.h"
1416
#include "flutter/shell/platform/embedder/embedder.h"
1517
#include "flutter/shell/platform/windows/flutter_project_bundle.h"
1618
#include "flutter/shell/platform/windows/public/flutter_windows.h"
1719
#include "flutter/shell/platform/windows/win32_task_runner.h"
1820
#include "flutter/shell/platform/windows/win32_window_proc_delegate_manager.h"
1921
#include "flutter/shell/platform/windows/window_state.h"
22+
#include "third_party/rapidjson/include/rapidjson/document.h"
2023

2124
namespace flutter {
2225

@@ -134,16 +137,22 @@ class FlutterWindowsEngine {
134137
// The plugin messenger handle given to API clients.
135138
std::unique_ptr<FlutterDesktopMessenger> messenger_;
136139

140+
// A wrapper around messenger_ for interacting with client_wrapper-level APIs.
141+
std::unique_ptr<BinaryMessengerImpl> messenger_wrapper_;
142+
137143
// Message dispatch manager for messages from engine_.
138144
std::unique_ptr<IncomingMessageDispatcher> message_dispatcher_;
139145

140146
// The plugin registrar handle given to API clients.
141147
std::unique_ptr<FlutterDesktopPluginRegistrar> plugin_registrar_;
142148

149+
// The MethodChannel used for communication with the Flutter engine.
150+
std::unique_ptr<BasicMessageChannel<rapidjson::Document>> settings_channel_;
151+
143152
// A callback to be called when the engine (and thus the plugin registrar)
144153
// is being destroyed.
145154
FlutterDesktopOnPluginRegistrarDestroyed
146-
plugin_registrar_destruction_callback_;
155+
plugin_registrar_destruction_callback_ = nullptr;
147156

148157
// The manager for WindowProc delegate registration and callbacks.
149158
std::unique_ptr<Win32WindowProcDelegateManager> window_proc_delegate_manager_;

shell/platform/windows/flutter_windows_engine_unittests.cc

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,81 @@ std::unique_ptr<FlutterWindowsEngine> GetTestEngine() {
2020
properties.icu_data_path = L"C:\\foo\\icudtl.dat";
2121
properties.aot_library_path = L"C:\\foo\\aot.so";
2222
FlutterProjectBundle project(properties);
23-
return std::make_unique<FlutterWindowsEngine>(project);
23+
auto engine = std::make_unique<FlutterWindowsEngine>(project);
24+
25+
EngineEmbedderApiModifier modifier(engine.get());
26+
// Force the non-AOT path unless overridden by the test.
27+
modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
28+
29+
return engine;
2430
}
2531
} // namespace
2632

33+
TEST(FlutterWindowsEngine, RunDoesExpectedInitialization) {
34+
std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
35+
EngineEmbedderApiModifier modifier(engine.get());
36+
37+
// The engine should be run with expected configuration values.
38+
bool run_called = false;
39+
modifier.embedder_api().Run = MOCK_ENGINE_PROC(
40+
Run, ([&run_called, engine_instance = engine.get()](
41+
size_t version, const FlutterRendererConfig* config,
42+
const FlutterProjectArgs* args, void* user_data,
43+
FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
44+
run_called = true;
45+
*engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(1);
46+
47+
EXPECT_EQ(version, FLUTTER_ENGINE_VERSION);
48+
EXPECT_NE(config, nullptr);
49+
EXPECT_EQ(user_data, engine_instance);
50+
// Spot-check arguments.
51+
EXPECT_STREQ(args->assets_path, "C:\\foo\\flutter_assets");
52+
EXPECT_STREQ(args->icu_data_path, "C:\\foo\\icudtl.dat");
53+
EXPECT_EQ(args->dart_entrypoint_argc, 0);
54+
EXPECT_NE(args->platform_message_callback, nullptr);
55+
EXPECT_NE(args->custom_task_runners, nullptr);
56+
EXPECT_EQ(args->custom_dart_entrypoint, nullptr);
57+
58+
return kSuccess;
59+
}));
60+
61+
// It should send locale info.
62+
bool update_locales_called = false;
63+
modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC(
64+
UpdateLocales,
65+
([&update_locales_called](auto engine, const FlutterLocale** locales,
66+
size_t locales_count) {
67+
update_locales_called = true;
68+
69+
EXPECT_GT(locales_count, 0);
70+
EXPECT_NE(locales, nullptr);
71+
72+
return kSuccess;
73+
}));
74+
75+
// And it should send initial settings info.
76+
bool settings_message_sent = false;
77+
modifier.embedder_api().SendPlatformMessage = MOCK_ENGINE_PROC(
78+
SendPlatformMessage,
79+
([&settings_message_sent](auto engine, auto message) {
80+
if (std::string(message->channel) == std::string("flutter/settings")) {
81+
settings_message_sent = true;
82+
}
83+
84+
return kSuccess;
85+
}));
86+
87+
engine->RunWithEntrypoint(nullptr);
88+
89+
EXPECT_TRUE(run_called);
90+
EXPECT_TRUE(update_locales_called);
91+
EXPECT_TRUE(settings_message_sent);
92+
93+
// Ensure that deallocation doesn't call the actual Shutdown with the bogus
94+
// engine pointer that the overridden Run returned.
95+
modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; };
96+
}
97+
2798
TEST(FlutterWindowsEngine, SendPlatformMessageWithoutResponse) {
2899
std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
29100
EngineEmbedderApiModifier modifier(engine.get());

shell/platform/windows/system_utils.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ std::vector<std::wstring> GetPreferredLanguages();
3131
// Parses a Windows language name into its components.
3232
LanguageInfo ParseLanguageName(std::wstring language_name);
3333

34+
// Returns the user's system time format string.
35+
std::wstring GetUserTimeFormat();
36+
37+
// Returns true if the time_format is set to use 24 hour time.
38+
bool Prefer24HourTime(std::wstring time_format);
39+
3440
} // namespace flutter
3541

3642
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_SYSTEM_UTILS_H_

shell/platform/windows/system_utils_unittests.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,22 @@ TEST(SystemUtils, ParseLanguageNameWithThreeCharacterLanguage) {
7171
EXPECT_TRUE(info.script.empty());
7272
}
7373

74+
TEST(SystemUtils, GetUserTimeFormat) {
75+
// The value varies based on machine; just ensure that something is returned.
76+
EXPECT_FALSE(GetUserTimeFormat().empty());
77+
}
78+
79+
TEST(SystemUtils, Prefer24HourTimeHandlesEmptyFormat) {
80+
EXPECT_FALSE(Prefer24HourTime(L""));
81+
}
82+
83+
TEST(SystemUtils, Prefer24HourTimeHandles12Hour) {
84+
EXPECT_FALSE(Prefer24HourTime(L"h:mm:ss tt"));
85+
}
86+
87+
TEST(SystemUtils, Prefer24HourTimeHandles24Hour) {
88+
EXPECT_TRUE(Prefer24HourTime(L"HH:mm:ss"));
89+
}
90+
7491
} // namespace testing
7592
} // namespace flutter

shell/platform/windows/system_utils_win32.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,20 @@ LanguageInfo ParseLanguageName(std::wstring language_name) {
9090
return info;
9191
}
9292

93+
std::wstring GetUserTimeFormat() {
94+
// Rather than do the call-allocate-call-free dance, just use a sufficiently
95+
// large buffer to handle any reasonable time format string.
96+
const int kBufferSize = 100;
97+
wchar_t buffer[kBufferSize];
98+
if (::GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_STIMEFORMAT, buffer,
99+
kBufferSize) == 0) {
100+
return std::wstring();
101+
}
102+
return std::wstring(buffer, kBufferSize);
103+
}
104+
105+
bool Prefer24HourTime(std::wstring time_format) {
106+
return time_format.find(L"H") != std::wstring::npos;
107+
}
108+
93109
} // namespace flutter

testing/run_tests.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,11 @@ def RunEngineExecutable(build_dir, executable_name, filter, flags=[], cwd=buildr
9797
def RunCCTests(build_dir, filter):
9898
print("Running Engine Unit-tests.")
9999

100-
shuffle_flags = [
100+
# Not all of the engine unit tests are designed to be run more than once.
101+
non_repeatable_shuffle_flags = [
101102
"--gtest_shuffle",
103+
]
104+
shuffle_flags = non_repeatable_shuffle_flags + [
102105
"--gtest_repeat=2",
103106
]
104107

@@ -115,7 +118,7 @@ def RunCCTests(build_dir, filter):
115118
RunEngineExecutable(build_dir, 'embedder_unittests', filter, shuffle_flags)
116119
RunEngineExecutable(build_dir, 'embedder_proctable_unittests', filter, shuffle_flags)
117120
else:
118-
RunEngineExecutable(build_dir, 'flutter_windows_unittests', filter, shuffle_flags)
121+
RunEngineExecutable(build_dir, 'flutter_windows_unittests', filter, non_repeatable_shuffle_flags)
119122

120123
RunEngineExecutable(build_dir, 'client_wrapper_windows_unittests', filter, shuffle_flags)
121124

@@ -150,14 +153,14 @@ def RunCCTests(build_dir, filter):
150153
# These unit-tests are Objective-C and can only run on Darwin.
151154
if IsMac():
152155
RunEngineExecutable(build_dir, 'flutter_channels_unittests', filter, shuffle_flags)
153-
RunEngineExecutable(build_dir, 'flutter_desktop_darwin_unittests', filter, shuffle_flags)
156+
RunEngineExecutable(build_dir, 'flutter_desktop_darwin_unittests', filter, non_repeatable_shuffle_flags)
154157

155158
# https://github.com/flutter/flutter/issues/36296
156159
if IsLinux():
157160
RunEngineExecutable(build_dir, 'txt_unittests', filter, shuffle_flags)
158161

159162
if IsLinux():
160-
RunEngineExecutable(build_dir, 'flutter_linux_unittests', filter, shuffle_flags)
163+
RunEngineExecutable(build_dir, 'flutter_linux_unittests', filter, non_repeatable_shuffle_flags)
161164

162165

163166
def RunEngineBenchmarks(build_dir, filter):

0 commit comments

Comments
 (0)