diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 9033f121a69e1..14b2dbd8cd4a1 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1036,6 +1036,7 @@ FILE: ../../../flutter/third_party/txt/src/txt/platform_android.cc FILE: ../../../flutter/third_party/txt/src/txt/platform_fuchsia.cc FILE: ../../../flutter/third_party/txt/src/txt/platform_linux.cc FILE: ../../../flutter/third_party/txt/src/txt/platform_mac.mm +FILE: ../../../flutter/third_party/txt/src/txt/platform_windows.cc FILE: ../../../flutter/vulkan/vulkan_application.cc FILE: ../../../flutter/vulkan/vulkan_application.h FILE: ../../../flutter/vulkan/vulkan_backbuffer.cc diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 93c6c1042286d..38ee3f50dcfe1 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -26,6 +26,8 @@ #include "flutter/shell/common/skia_event_tracer_impl.h" #include "flutter/shell/common/switches.h" #include "flutter/shell/common/vsync_waiter.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/writer.h" #include "third_party/dart/runtime/include/dart_tools_api.h" #include "third_party/skia/include/core/SkGraphics.h" #include "third_party/tonic/common/log.h" @@ -33,6 +35,9 @@ namespace flutter { constexpr char kSkiaChannel[] = "flutter/skia"; +constexpr char kSystemChannel[] = "flutter/system"; +constexpr char kTypeKey[] = "type"; +constexpr char kFontChange[] = "fontsChange"; std::unique_ptr Shell::CreateShellOnPlatformThread( DartVMRef vm, @@ -1351,4 +1356,35 @@ fml::Status Shell::WaitForFirstFrame(fml::TimeDelta timeout) { } } +bool Shell::ReloadSystemFonts() { + FML_DCHECK(is_setup_); + FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + if (!engine_) { + return false; + } + engine_->GetFontCollection().GetFontCollection()->SetupDefaultFontManager(); + engine_->GetFontCollection().GetFontCollection()->ClearFontFamilyCache(); + // After system fonts are reloaded, we send a system channel message + // to notify flutter framework. + rapidjson::Document document; + document.SetObject(); + auto& allocator = document.GetAllocator(); + rapidjson::Value message_value; + message_value.SetString(kFontChange, allocator); + document.AddMember(kTypeKey, message_value, allocator); + + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + document.Accept(writer); + std::string message = buffer.GetString(); + fml::RefPtr fontsChangeMessage = + fml::MakeRefCounted( + kSystemChannel, std::vector(message.begin(), message.end()), + nullptr); + + OnPlatformViewDispatchPlatformMessage(fontsChangeMessage); + return true; +} + } // namespace flutter diff --git a/shell/common/shell.h b/shell/common/shell.h index f8f0c67fee2e5..430a4bda17c7d 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -284,6 +284,16 @@ class Shell final : public PlatformView::Delegate, /// fml::Status WaitForFirstFrame(fml::TimeDelta timeout); + //---------------------------------------------------------------------------- + /// @brief Used by embedders to reload the system fonts in + /// FontCollection. + /// It also clears the cached font families and send system + /// channel message to framework to rebuild affected widgets. + /// + /// @return Returns if shell reloads system fonts successfully. + /// + bool ReloadSystemFonts(); + //---------------------------------------------------------------------------- /// @brief Used by embedders to get the last error from the Dart UI /// Isolate, if one exists. diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index e541d908c5586..6887b704ee377 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -144,6 +144,11 @@ bool ShellTest::GetNeedsReportTimings(Shell* shell) { return shell->needs_report_timings_; } +std::shared_ptr ShellTest::GetFontCollection( + Shell* shell) { + return shell->weak_engine_->GetFontCollection().GetFontCollection(); +} + Settings ShellTest::CreateSettingsForFixture() { Settings settings; settings.leak_vm = false; diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 55436a82a29ed..04d653e6079bd 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -51,6 +51,8 @@ class ShellTest : public ThreadTest { static bool GetNeedsReportTimings(Shell* shell); static void SetNeedsReportTimings(Shell* shell, bool value); + std::shared_ptr GetFontCollection(Shell* shell); + // Do not assert |UnreportedTimingsCount| to be positive in any tests. // Otherwise those tests will be flaky as the clearing of unreported timings // is unpredictive. diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 1f85facaf75f0..c1160b92643f9 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -519,6 +519,36 @@ TEST_F(ShellTest, ReportTimingsIsCalledImmediatelyAfterTheFirstFrame) { ASSERT_EQ(timestamps.size(), FrameTiming::kCount); } +TEST_F(ShellTest, ReloadSystemFonts) { + auto settings = CreateSettingsForFixture(); + + fml::MessageLoop::EnsureInitializedForCurrentThread(); + auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); + TaskRunners task_runners("test", task_runner, task_runner, task_runner, + task_runner); + auto shell = CreateShell(std::move(settings), std::move(task_runners)); + + auto fontCollection = GetFontCollection(shell.get()); + std::vector families(1, "Robotofake"); + auto font = + fontCollection->GetMinikinFontCollectionForFamilies(families, "en"); + if (font == nullptr) { + // The system does not have default font. Aborts this test. + return; + } + unsigned int id = font->getId(); + // The result should be cached. + font = fontCollection->GetMinikinFontCollectionForFamilies(families, "en"); + ASSERT_EQ(font->getId(), id); + bool result = shell->ReloadSystemFonts(); + + // The cache is cleared, and FontCollection will be assigned a new id. + font = fontCollection->GetMinikinFontCollectionForFamilies(families, "en"); + ASSERT_NE(font->getId(), id); + ASSERT_TRUE(result); + shell.reset(); +} + TEST_F(ShellTest, WaitForFirstFrame) { auto settings = CreateSettingsForFixture(); std::unique_ptr shell = CreateShell(settings); diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 43599c52fc1ec..72da210079e73 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1279,6 +1279,22 @@ FlutterEngineResult FlutterEngineOnVsync(FLUTTER_API_SYMBOL(FlutterEngine) return kSuccess; } +FlutterEngineResult FlutterEngineReloadSystemFonts( + FLUTTER_API_SYMBOL(FlutterEngine) engine) { + if (engine == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + + TRACE_EVENT0("flutter", "FlutterEngineReloadSystemFonts"); + + if (!reinterpret_cast(engine) + ->ReloadSystemFonts()) { + return LOG_EMBEDDER_ERROR(kInternalInconsistency); + } + + return kSuccess; +} + void FlutterEngineTraceEventDurationBegin(const char* name) { fml::tracing::TraceEvent0("flutter", name); } diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index ee03b66cf10ef..4953066473107 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -1200,6 +1200,17 @@ FlutterEngineResult FlutterEngineOnVsync(FLUTTER_API_SYMBOL(FlutterEngine) uint64_t frame_start_time_nanos, uint64_t frame_target_time_nanos); +//------------------------------------------------------------------------------ +/// @brief Reloads the system fonts in engine. +/// +/// @param[in] engine. A running engine instance. +/// +/// @return The result of the call. +/// +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineReloadSystemFonts( + FLUTTER_API_SYMBOL(FlutterEngine) engine); + //------------------------------------------------------------------------------ /// @brief A profiling utility. Logs a trace duration begin event to the /// timeline. If the timeline is unavailable or disabled, this has diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index 6f675e785dabb..7ec6e771395e6 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -186,6 +186,10 @@ bool EmbedderEngine::OnVsyncEvent(intptr_t baton, frame_target_time); } +bool EmbedderEngine::ReloadSystemFonts() { + return shell_->ReloadSystemFonts(); +} + bool EmbedderEngine::PostRenderThreadTask(fml::closure task) { if (!IsValid()) { return false; diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index 8ef64502f98cc..a99cf50dd3a9f 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -67,6 +67,8 @@ class EmbedderEngine { fml::TimePoint frame_start_time, fml::TimePoint frame_target_time); + bool ReloadSystemFonts(); + bool PostRenderThreadTask(fml::closure task); bool RunTask(const FlutterTask* task); diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 5304a959281d5..48b4c6f45085c 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -235,6 +235,17 @@ TEST(EmbedderTestNoFixture, CanGetCurrentTimeInNanoseconds) { ASSERT_LT((point2 - point1), fml::TimeDelta::FromMilliseconds(1)); } +TEST_F(EmbedderTest, CanReloadSystemFonts) { + auto& context = GetEmbedderContext(); + EmbedderConfigBuilder builder(context); + builder.SetSoftwareRendererConfig(); + auto engine = builder.LaunchEngine(); + ASSERT_TRUE(engine.is_valid()); + + auto result = FlutterEngineReloadSystemFonts(engine.get()); + ASSERT_EQ(result, kSuccess); +} + TEST_F(EmbedderTest, CanCreateOpenGLRenderingEngine) { EmbedderConfigBuilder builder(GetEmbedderContext()); builder.SetOpenGLRendererConfig(SkISize::Make(1, 1)); diff --git a/shell/platform/windows/win32_flutter_window.cc b/shell/platform/windows/win32_flutter_window.cc index 5b2f20c153576..d4e08e390f22e 100644 --- a/shell/platform/windows/win32_flutter_window.cc +++ b/shell/platform/windows/win32_flutter_window.cc @@ -147,6 +147,13 @@ void Win32FlutterWindow::OnClose() { messageloop_running_ = false; } +void Win32FlutterWindow::OnFontChange() { + if (engine_ == nullptr) { + return; + } + FlutterEngineReloadSystemFonts(engine_); +} + // Sends new size information to FlutterEngine. void Win32FlutterWindow::SendWindowMetrics() { if (engine_ == nullptr) { diff --git a/shell/platform/windows/win32_flutter_window.h b/shell/platform/windows/win32_flutter_window.h index c2316a88e0add..138fef8f49279 100644 --- a/shell/platform/windows/win32_flutter_window.h +++ b/shell/platform/windows/win32_flutter_window.h @@ -65,6 +65,9 @@ class Win32FlutterWindow : public Win32Window { // |Win32Window| void OnClose(); + // |Win32Window| + void OnFontChange() override; + // Configures the window instance with an instance of a running Flutter engine // returning a configured FlutterDesktopWindowControllerRef. void SetState(FLUTTER_API_SYMBOL(FlutterEngine) state); diff --git a/shell/platform/windows/win32_window.cc b/shell/platform/windows/win32_window.cc index ad6265109d805..b2e313ef98a12 100644 --- a/shell/platform/windows/win32_window.cc +++ b/shell/platform/windows/win32_window.cc @@ -129,7 +129,9 @@ Win32Window::MessageHandler(HWND hwnd, current_height_ = height; window->HandleResize(width, height); break; - + case WM_FONTCHANGE: + window->OnFontChange(); + break; case WM_MOUSEMOVE: xPos = GET_X_LPARAM(lparam); yPos = GET_Y_LPARAM(lparam); diff --git a/shell/platform/windows/win32_window.h b/shell/platform/windows/win32_window.h index 45819b589ea61..0b5f4eedd8a3c 100644 --- a/shell/platform/windows/win32_window.h +++ b/shell/platform/windows/win32_window.h @@ -97,9 +97,12 @@ class Win32Window { // Called when mouse scrollwheel input occurs. virtual void OnScroll(double delta_x, double delta_y) = 0; - // Called when the user closes the Windows + // Called when the user closes the Windows. virtual void OnClose() = 0; + // Called when the system font change. + virtual void OnFontChange() = 0; + UINT GetCurrentDPI(); UINT GetCurrentWidth(); diff --git a/third_party/txt/BUILD.gn b/third_party/txt/BUILD.gn index c713fbeb0593e..1bcc6988d8b31 100644 --- a/third_party/txt/BUILD.gn +++ b/third_party/txt/BUILD.gn @@ -160,6 +160,8 @@ source_set("txt") { sources += [ "src/txt/platform_linux.cc" ] } else if (is_fuchsia) { sources += [ "src/txt/platform_fuchsia.cc" ] + } else if (is_win) { + sources += [ "src/txt/platform_windows.cc" ] } else { sources += [ "src/txt/platform.cc" ] } diff --git a/third_party/txt/src/txt/platform_windows.cc b/third_party/txt/src/txt/platform_windows.cc new file mode 100644 index 0000000000000..c16e30386252a --- /dev/null +++ b/third_party/txt/src/txt/platform_windows.cc @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/skia/include/ports/SkTypeface_win.h" +#include "txt/platform.h" + +namespace txt { + +std::string GetDefaultFontFamily() { + return "Arial"; +} + +sk_sp GetDefaultFontManager() { + return SkFontMgr_New_DirectWrite(); +} + +} // namespace txt