Browse Source

Ladybird+LibWeb+LibGfx: Add option to force use of fontconfig

This allows us to get identical metrics on macOS and Linux. Without
this, Skia will use CoreText on macOS and give us slightly different
text metrics. That causes layout tests to be slightly different on
different platforms, which is a huge headache. So let's not do that.

You can now launch Ladybird and headless-browser with --force-fontconfig
to load fonts through fontconfig. Tests run in this mode by default.
Andreas Kling 11 months ago
parent
commit
dd1b8de671

+ 1 - 1
Ladybird/CMakeLists.txt

@@ -162,7 +162,7 @@ endif()
 if (BUILD_TESTING)
 if (BUILD_TESTING)
     add_test(
     add_test(
         NAME LibWeb
         NAME LibWeb
-        COMMAND $<TARGET_FILE:headless-browser> --run-tests ${LADYBIRD_SOURCE_DIR}/Tests/LibWeb --dump-failed-ref-tests
+        COMMAND $<TARGET_FILE:headless-browser> --run-tests ${LADYBIRD_SOURCE_DIR}/Tests/LibWeb --dump-failed-ref-tests --force-fontconfig
     )
     )
     add_test(
     add_test(
         NAME WPT
         NAME WPT

+ 2 - 0
Ladybird/HelperProcess.cpp

@@ -104,6 +104,8 @@ ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(
         arguments.append("--expose-internals-object"sv);
         arguments.append("--expose-internals-object"sv);
     if (web_content_options.force_cpu_painting == WebView::ForceCPUPainting::Yes)
     if (web_content_options.force_cpu_painting == WebView::ForceCPUPainting::Yes)
         arguments.append("--force-cpu-painting"sv);
         arguments.append("--force-cpu-painting"sv);
+    if (web_content_options.force_fontconfig == WebView::ForceFontconfig::Yes)
+        arguments.append("--force-fontconfig"sv);
     if (auto server = mach_server_name(); server.has_value()) {
     if (auto server = mach_server_name(); server.has_value()) {
         arguments.append("--mach-server-name"sv);
         arguments.append("--mach-server-name"sv);
         arguments.append(server.value());
         arguments.append(server.value());

+ 7 - 0
Ladybird/WebContent/main.cpp

@@ -15,6 +15,7 @@
 #include <LibCore/Process.h>
 #include <LibCore/Process.h>
 #include <LibCore/Resource.h>
 #include <LibCore/Resource.h>
 #include <LibCore/SystemServerTakeover.h>
 #include <LibCore/SystemServerTakeover.h>
+#include <LibGfx/Font/FontDatabase.h>
 #include <LibIPC/ConnectionFromClient.h>
 #include <LibIPC/ConnectionFromClient.h>
 #include <LibJS/Bytecode/Interpreter.h>
 #include <LibJS/Bytecode/Interpreter.h>
 #include <LibMain/Main.h>
 #include <LibMain/Main.h>
@@ -105,6 +106,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
     bool enable_idl_tracing = false;
     bool enable_idl_tracing = false;
     bool enable_http_cache = false;
     bool enable_http_cache = false;
     bool force_cpu_painting = false;
     bool force_cpu_painting = false;
+    bool force_fontconfig = false;
 
 
     Core::ArgsParser args_parser;
     Core::ArgsParser args_parser;
     args_parser.add_option(command_line, "Chrome process command line", "command-line", 0, "command_line");
     args_parser.add_option(command_line, "Chrome process command line", "command-line", 0, "command_line");
@@ -122,6 +124,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
     args_parser.add_option(enable_idl_tracing, "Enable IDL tracing", "enable-idl-tracing");
     args_parser.add_option(enable_idl_tracing, "Enable IDL tracing", "enable-idl-tracing");
     args_parser.add_option(enable_http_cache, "Enable HTTP cache", "enable-http-cache");
     args_parser.add_option(enable_http_cache, "Enable HTTP cache", "enable-http-cache");
     args_parser.add_option(force_cpu_painting, "Force CPU painting", "force-cpu-painting");
     args_parser.add_option(force_cpu_painting, "Force CPU painting", "force-cpu-painting");
+    args_parser.add_option(force_fontconfig, "Force using fontconfig for font loading", "force-fontconfig");
 
 
     args_parser.parse(arguments);
     args_parser.parse(arguments);
 
 
@@ -129,6 +132,10 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
         Core::Process::wait_for_debugger_and_break();
         Core::Process::wait_for_debugger_and_break();
     }
     }
 
 
+    if (force_fontconfig) {
+        Gfx::FontDatabase::the().set_force_fontconfig(true);
+    }
+
     // Layout test mode implies internals object is exposed and the Skia CPU backend is used
     // Layout test mode implies internals object is exposed and the Skia CPU backend is used
     if (is_layout_test_mode) {
     if (is_layout_test_mode) {
         expose_internals_object = true;
         expose_internals_object = true;

+ 1 - 1
Tests/LibWeb/rebaseline-libweb-test

@@ -28,4 +28,4 @@ else
 fi
 fi
 
 
 mkdir -p $expected_dir
 mkdir -p $expected_dir
-$ladybird_headless_binary $mode_flag --layout-test-mode $LADYBIRD_SOURCE_DIR/Tests/LibWeb/$input_dir/$test_name.html > $LADYBIRD_SOURCE_DIR/Tests/LibWeb/$expected_dir/$test_name.txt
+$ladybird_headless_binary $mode_flag --force-fontconfig --layout-test-mode $input_dir/$test_name.html > $expected_dir/$test_name.txt

+ 11 - 0
Userland/Libraries/LibGfx/Font/FontDatabase.cpp

@@ -24,9 +24,20 @@ FontDatabase& FontDatabase::the()
 }
 }
 
 
 struct FontDatabase::Private {
 struct FontDatabase::Private {
+    bool force_fontconfig { false };
     HashMap<FlyString, Vector<NonnullRefPtr<Typeface>>, AK::ASCIICaseInsensitiveFlyStringTraits> typeface_by_family;
     HashMap<FlyString, Vector<NonnullRefPtr<Typeface>>, AK::ASCIICaseInsensitiveFlyStringTraits> typeface_by_family;
 };
 };
 
 
+void FontDatabase::set_force_fontconfig(bool force_fontconfig)
+{
+    m_private->force_fontconfig = force_fontconfig;
+}
+
+bool FontDatabase::should_force_fontconfig() const
+{
+    return m_private->force_fontconfig;
+}
+
 void FontDatabase::load_all_fonts_from_uri(StringView uri)
 void FontDatabase::load_all_fonts_from_uri(StringView uri)
 {
 {
     auto root_or_error = Core::Resource::load_from_uri(uri);
     auto root_or_error = Core::Resource::load_from_uri(uri);

+ 3 - 0
Userland/Libraries/LibGfx/Font/FontDatabase.h

@@ -28,6 +28,9 @@ public:
 
 
     void load_all_fonts_from_uri(StringView);
     void load_all_fonts_from_uri(StringView);
 
 
+    void set_force_fontconfig(bool);
+    [[nodiscard]] bool should_force_fontconfig() const;
+
 private:
 private:
     FontDatabase();
     FontDatabase();
     ~FontDatabase() = default;
     ~FontDatabase() = default;

+ 6 - 5
Userland/Libraries/LibGfx/Font/TypefaceSkia.cpp

@@ -6,16 +6,16 @@
 
 
 #define AK_DONT_REPLACE_STD
 #define AK_DONT_REPLACE_STD
 
 
+#include <LibGfx/Font/FontDatabase.h>
 #include <LibGfx/Font/Typeface.h>
 #include <LibGfx/Font/Typeface.h>
 
 
 #include <core/SkData.h>
 #include <core/SkData.h>
 #include <core/SkFontMgr.h>
 #include <core/SkFontMgr.h>
 #include <core/SkTypeface.h>
 #include <core/SkTypeface.h>
+#include <ports/SkFontMgr_fontconfig.h>
 
 
 #ifdef AK_OS_MACOS
 #ifdef AK_OS_MACOS
 #    include <ports/SkFontMgr_mac_ct.h>
 #    include <ports/SkFontMgr_mac_ct.h>
-#else
-#    include <ports/SkFontMgr_fontconfig.h>
 #endif
 #endif
 
 
 namespace Gfx {
 namespace Gfx {
@@ -26,10 +26,11 @@ RefPtr<SkTypeface> const& Typeface::skia_typeface() const
 {
 {
     if (!s_font_manager) {
     if (!s_font_manager) {
 #ifdef AK_OS_MACOS
 #ifdef AK_OS_MACOS
-        s_font_manager = SkFontMgr_New_CoreText(nullptr);
-#else
-        s_font_manager = SkFontMgr_New_FontConfig(nullptr);
+        if (!Gfx::FontDatabase::the().should_force_fontconfig())
+            s_font_manager = SkFontMgr_New_CoreText(nullptr);
 #endif
 #endif
+        if (!s_font_manager)
+            s_font_manager = SkFontMgr_New_FontConfig(nullptr);
     }
     }
 
 
     if (!m_skia_typeface) {
     if (!m_skia_typeface) {

+ 3 - 0
Userland/Libraries/LibWebView/Application.cpp

@@ -46,6 +46,7 @@ void Application::initialize(Main::Arguments const& arguments, URL::URL new_tab_
     bool enable_http_cache = false;
     bool enable_http_cache = false;
     bool expose_internals_object = false;
     bool expose_internals_object = false;
     bool force_cpu_painting = false;
     bool force_cpu_painting = false;
+    bool force_fontconfig = false;
 
 
     Core::ArgsParser args_parser;
     Core::ArgsParser args_parser;
     args_parser.set_general_help("The Ladybird web browser :^)");
     args_parser.set_general_help("The Ladybird web browser :^)");
@@ -63,6 +64,7 @@ void Application::initialize(Main::Arguments const& arguments, URL::URL new_tab_
     args_parser.add_option(enable_http_cache, "Enable HTTP cache", "enable-http-cache");
     args_parser.add_option(enable_http_cache, "Enable HTTP cache", "enable-http-cache");
     args_parser.add_option(expose_internals_object, "Expose internals object", "expose-internals-object");
     args_parser.add_option(expose_internals_object, "Expose internals object", "expose-internals-object");
     args_parser.add_option(force_cpu_painting, "Force CPU painting", "force-cpu-painting");
     args_parser.add_option(force_cpu_painting, "Force CPU painting", "force-cpu-painting");
+    args_parser.add_option(force_fontconfig, "Force using fontconfig for font loading", "force-fontconfig");
 
 
     create_platform_arguments(args_parser);
     create_platform_arguments(args_parser);
     args_parser.parse(arguments);
     args_parser.parse(arguments);
@@ -99,6 +101,7 @@ void Application::initialize(Main::Arguments const& arguments, URL::URL new_tab_
         .enable_http_cache = enable_http_cache ? EnableHTTPCache::Yes : EnableHTTPCache::No,
         .enable_http_cache = enable_http_cache ? EnableHTTPCache::Yes : EnableHTTPCache::No,
         .expose_internals_object = expose_internals_object ? ExposeInternalsObject::Yes : ExposeInternalsObject::No,
         .expose_internals_object = expose_internals_object ? ExposeInternalsObject::Yes : ExposeInternalsObject::No,
         .force_cpu_painting = force_cpu_painting ? ForceCPUPainting::Yes : ForceCPUPainting::No,
         .force_cpu_painting = force_cpu_painting ? ForceCPUPainting::Yes : ForceCPUPainting::No,
+        .force_fontconfig = force_fontconfig ? ForceFontconfig::Yes : ForceFontconfig::No,
     };
     };
 
 
     create_platform_options(m_chrome_options, m_web_content_options);
     create_platform_options(m_chrome_options, m_web_content_options);

+ 6 - 0
Userland/Libraries/LibWebView/Options.h

@@ -84,6 +84,11 @@ enum class ForceCPUPainting {
     Yes,
     Yes,
 };
 };
 
 
+enum class ForceFontconfig {
+    No,
+    Yes,
+};
+
 struct WebContentOptions {
 struct WebContentOptions {
     String command_line;
     String command_line;
     String executable_path;
     String executable_path;
@@ -95,6 +100,7 @@ struct WebContentOptions {
     EnableHTTPCache enable_http_cache { EnableHTTPCache::No };
     EnableHTTPCache enable_http_cache { EnableHTTPCache::No };
     ExposeInternalsObject expose_internals_object { ExposeInternalsObject::No };
     ExposeInternalsObject expose_internals_object { ExposeInternalsObject::No };
     ForceCPUPainting force_cpu_painting { ForceCPUPainting::No };
     ForceCPUPainting force_cpu_painting { ForceCPUPainting::No };
+    ForceFontconfig force_fontconfig { ForceFontconfig::No };
 };
 };
 
 
 }
 }

+ 3 - 2
vcpkg.json

@@ -3,7 +3,7 @@
   "dependencies": [
   "dependencies": [
     {
     {
       "name": "fontconfig",
       "name": "fontconfig",
-      "platform": "linux | freebsd | openbsd"
+      "platform": "linux | freebsd | openbsd | osx"
     },
     },
     "harfbuzz",
     "harfbuzz",
     "icu",
     "icu",
@@ -34,7 +34,8 @@
       "name": "skia",
       "name": "skia",
       "platform": "osx",
       "platform": "osx",
       "features": [
       "features": [
-        "metal"
+        "metal",
+        "fontconfig"
       ]
       ]
     },
     },
     {
     {