Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
36 changes: 36 additions & 0 deletions shell/common/shell.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,18 @@
#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"

namespace flutter {

constexpr char kSkiaChannel[] = "flutter/skia";
constexpr char kSystemChannel[] = "flutter/system";
constexpr char kTypeKey[] = "type";
constexpr char kFontChange[] = "fontsChange";

std::unique_ptr<Shell> Shell::CreateShellOnPlatformThread(
DartVMRef vm,
Expand Down Expand Up @@ -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<rapidjson::StringBuffer> writer(buffer);
document.Accept(writer);
std::string message = buffer.GetString();
fml::RefPtr<PlatformMessage> fontsChangeMessage =
fml::MakeRefCounted<flutter::PlatformMessage>(
kSystemChannel, std::vector<uint8_t>(message.begin(), message.end()),
nullptr);

OnPlatformViewDispatchPlatformMessage(fontsChangeMessage);
return true;
}

} // namespace flutter
10 changes: 10 additions & 0 deletions shell/common/shell.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions shell/common/shell_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ bool ShellTest::GetNeedsReportTimings(Shell* shell) {
return shell->needs_report_timings_;
}

std::shared_ptr<txt::FontCollection> ShellTest::GetFontCollection(
Shell* shell) {
return shell->weak_engine_->GetFontCollection().GetFontCollection();
}

Settings ShellTest::CreateSettingsForFixture() {
Settings settings;
settings.leak_vm = false;
Expand Down
2 changes: 2 additions & 0 deletions shell/common/shell_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class ShellTest : public ThreadTest {
static bool GetNeedsReportTimings(Shell* shell);
static void SetNeedsReportTimings(Shell* shell, bool value);

std::shared_ptr<txt::FontCollection> 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.
Expand Down
30 changes: 30 additions & 0 deletions shell/common/shell_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,36 @@ TEST_F(ShellTest, ReportTimingsIsCalledImmediatelyAfterTheFirstFrame) {
ASSERT_EQ(timestamps.size(), FrameTiming::kCount);
}

TEST_F(ShellTest, ReloadSystemFonts) {
Copy link
Contributor Author

@chunhtai chunhtai Sep 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This tests the actual functionality

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<std::string> 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> shell = CreateShell(settings);
Expand Down
16 changes: 16 additions & 0 deletions shell/platform/embedder/embedder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<flutter::EmbedderEngine*>(engine)
->ReloadSystemFonts()) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency);
}

return kSuccess;
}

void FlutterEngineTraceEventDurationBegin(const char* name) {
fml::tracing::TraceEvent0("flutter", name);
}
Expand Down
11 changes: 11 additions & 0 deletions shell/platform/embedder/embedder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions shell/platform/embedder/embedder_engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions shell/platform/embedder/embedder_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
11 changes: 11 additions & 0 deletions shell/platform/embedder/tests/embedder_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,17 @@ TEST(EmbedderTestNoFixture, CanGetCurrentTimeInNanoseconds) {
ASSERT_LT((point2 - point1), fml::TimeDelta::FromMilliseconds(1));
}

TEST_F(EmbedderTest, CanReloadSystemFonts) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test make sure the hook from embedding -> embedding_engine -> shell works. It does not test for functionality.

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));
Expand Down
7 changes: 7 additions & 0 deletions shell/platform/windows/win32_flutter_window.cc
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ void Win32FlutterWindow::OnClose() {
messageloop_running_ = false;
}

void Win32FlutterWindow::OnFontChange() {
Copy link
Contributor Author

@chunhtai chunhtai Sep 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kpozin Here is the current design for system font api.
When system font changes:

  1. Use FlutterEngineReloadSystemFonts in embedder.h to reload system font and clear caches
  2. send system channel message {"type": "fontsChange"} to "flutter/system" flutter framework to rebuild the widget.

Noted that you will probably need to implement fuchsia version of loading default fonts.
see: third_party/txt/src/txt/platform_windows.cc
FlutterEngineReloadSystemFonts relys on this function to retrieve the latest system fonts. If there is any caching happen under this layer, the FlutterEngineReloadSystemFonts will still load the old value.

Let me know if this looks ok to you.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this seems fine to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for confirming, I will let you know if anything change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kpozin a little update
you will only need to call this embedder api

FlutterEngineResult FlutterEngineReloadSystemFonts(

I make it so it will send the system message as part of the api.
However, you will still need to implement fuchsia version of loading default fonts.
https://github.com/flutter/engine/blob/master/third_party/txt/src/txt/platform_fuchsia.cc

if (engine_ == nullptr) {
return;
}
FlutterEngineReloadSystemFonts(engine_);
}

// Sends new size information to FlutterEngine.
void Win32FlutterWindow::SendWindowMetrics() {
if (engine_ == nullptr) {
Expand Down
3 changes: 3 additions & 0 deletions shell/platform/windows/win32_flutter_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion shell/platform/windows/win32_window.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 4 additions & 1 deletion shell/platform/windows/win32_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 2 additions & 0 deletions third_party/txt/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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" ]
}
Expand Down
18 changes: 18 additions & 0 deletions third_party/txt/src/txt/platform_windows.cc
Original file line number Diff line number Diff line change
@@ -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";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change seems unrelated. Is this improving font fallback handling? If so, it would be great to split this out into its own change as part of #39915

Also, I'm surprised to see a file being added to third_party. What is the source for txt, and why isn't this an upstream change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is essential for reloading system fonts. The original GetDefaultFontFamily in third_party/txt/src/txt/platform.cc uses SkFontMgr::RefDefault() which will cache the result and there is no way to clear it. When a new system font is installed, it will not load the latest system fonts but return previous cached. We have to implement the windows version of it.

There is no upstream for txt. The txt's source lives in here. One thing that worries me is that this might be migrated to skia library at some point @jason-simmons Do you think it is ok to change it now?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - go ahead and modify this in libtxt. if we later migrate to Skia's text shaper then we will need to port this feature over to the integration with Skia.

}

sk_sp<SkFontMgr> GetDefaultFontManager() {
return SkFontMgr_New_DirectWrite();
}

} // namespace txt