Commit 56c3f510 authored by Dominik Röttsches's avatar Dominik Röttsches Committed by Commit Bot

Move font unique name lookup table construction to own singleton class

Preparation for background scheduling of font unique name table
construction at browser startup. In a forthcoming CL, threading
primitives are added to support scheduling the font lookup table
construction at startup. For now, move the existing code to a separate
singleton class.

Bug: 889864
Change-Id: Idaa0bdf4cd7128e42cdc36d409eab37ae83c1d1a
Reviewed-on: https://chromium-review.googlesource.com/c/1458186
Commit-Queue: Dominik Röttsches <drott@chromium.org>
Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Cr-Commit-Position: refs/heads/master@{#630099}
parent ad1c576f
...@@ -1319,6 +1319,8 @@ jumbo_source_set("browser") { ...@@ -1319,6 +1319,8 @@ jumbo_source_set("browser") {
"renderer_host/display_util.h", "renderer_host/display_util.h",
"renderer_host/dwrite_font_file_util_win.cc", "renderer_host/dwrite_font_file_util_win.cc",
"renderer_host/dwrite_font_file_util_win.h", "renderer_host/dwrite_font_file_util_win.h",
"renderer_host/dwrite_font_lookup_table_builder_win.cc",
"renderer_host/dwrite_font_lookup_table_builder_win.h",
"renderer_host/dwrite_font_proxy_impl_win.cc", "renderer_host/dwrite_font_proxy_impl_win.cc",
"renderer_host/dwrite_font_proxy_impl_win.h", "renderer_host/dwrite_font_proxy_impl_win.h",
"renderer_host/dwrite_font_uma_logging_win.cc", "renderer_host/dwrite_font_uma_logging_win.cc",
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_RENDERER_HOST_DWRITE_FONT_LOOKUP_TABLE_BUILDER_WIN_H_
#define CONTENT_BROWSER_RENDERER_HOST_DWRITE_FONT_LOOKUP_TABLE_BUILDER_WIN_H_
#include <dwrite.h>
#include <dwrite_2.h>
#include <wrl.h>
#include "base/macros.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/memory/singleton.h"
#include "base/synchronization/waitable_event.h"
#include "content/common/content_export.h"
namespace base {
template <typename T>
class NoDestructor;
}
namespace content {
// Singleton class which encapsulates building the font unique name table lookup
// once, then serving the built table as a ReadOnlySharedMemoryRegion. Receives
// requests for accessing this table from DWriteFontProxyImpl after Mojo IPC
// calls from the renderer.
class CONTENT_EXPORT DWriteFontLookupTableBuilder {
public:
static DWriteFontLookupTableBuilder* GetInstance();
void SetSlowDownIndexingForTesting(bool);
// Needed to trigger rebuilding the lookup table, when testing using
// slowed-down indexing. Otherwise, the test methods would use the already
// cached lookup table.
void ResetLookupTableForTesting();
// Retrieve the prepared memory region if it is available.
// EnsureFontUniqueNameTable() should be checked before. This method hits an
// assertion otherwise.
base::ReadOnlySharedMemoryRegion DuplicatedMemoryRegion();
// Wait for the internal WaitableEvent to be signaled if needed and return
// true if the font unique name lookup table was successfully constructed.
bool EnsureFontUniqueNameTable();
private:
friend class base::NoDestructor<DWriteFontLookupTableBuilder>;
void BuildFontUniqueNameTable();
bool IsFontUniqueNameTableValid();
// Checks if the unique font table has been built already, and if not, builds
// it by enumerating fonts from the collection, extracting their file
// locations, ttc indices and names.
void InitializeDirectWrite();
DWriteFontLookupTableBuilder();
~DWriteFontLookupTableBuilder();
// This can only be replaced from the construction sequence. Once
// font_table_built_ is signaled, it can be read from everywhere.
base::MappedReadOnlyRegion font_table_memory_;
bool direct_write_initialized_ = false;
Microsoft::WRL::ComPtr<IDWriteFontCollection> collection_;
Microsoft::WRL::ComPtr<IDWriteFactory2> factory2_;
bool slow_down_indexing_for_testing_ = false;
DISALLOW_COPY_AND_ASSIGN(DWriteFontLookupTableBuilder);
};
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_DWRITE_FONT_LOOKUP_TABLE_BUILDER_WIN_H_
// Copyright 2019 The Chromium 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 "content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h"
#include <string>
#include <utility>
#include <vector>
#include "base/files/file.h"
#include "base/test/scoped_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/font_unique_name_lookup/font_table_matcher.h"
namespace content {
namespace {
std::vector<std::pair<std::string, uint32_t>> expected_test_fonts = {
{u8"CambriaMath", 1},
{u8"Ming-Lt-HKSCS-ExtB", 2},
{u8"NSimSun", 1},
{u8"calibri-bolditalic", 0}};
class DWriteFontLookupTableBuilderTest : public testing::Test {
public:
DWriteFontLookupTableBuilderTest() = default;
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
};
} // namespace
// Run a test similar to DWriteFontProxyImplUnitTest, TestFindUniqueFont but
// without going through Mojo and running it on the DWRiteFontLookupTableBuilder
// class directly.
TEST_F(DWriteFontLookupTableBuilderTest, TestFindUniqueFontDirect) {
DWriteFontLookupTableBuilder* font_lookup_table_builder =
DWriteFontLookupTableBuilder::GetInstance();
font_lookup_table_builder->EnsureFontUniqueNameTable();
base::ReadOnlySharedMemoryRegion font_table_memory =
font_lookup_table_builder->DuplicatedMemoryRegion();
blink::FontTableMatcher font_table_matcher(font_table_memory.Map());
for (auto& test_font_name_index : expected_test_fonts) {
base::Optional<blink::FontTableMatcher::MatchResult> match_result =
font_table_matcher.MatchName(test_font_name_index.first);
CHECK(match_result) << "No font matched for font name: "
<< test_font_name_index.first;
base::File unique_font_file(
base::FilePath::FromUTF8Unsafe(match_result->font_path),
base::File::FLAG_OPEN | base::File::FLAG_READ);
CHECK(unique_font_file.IsValid());
CHECK_GT(unique_font_file.GetLength(), 0);
CHECK_EQ(test_font_name_index.second, match_result->ttc_index);
}
}
TEST_F(DWriteFontLookupTableBuilderTest, TestTimeout) {
DWriteFontLookupTableBuilder* font_lookup_table_builder =
DWriteFontLookupTableBuilder::GetInstance();
font_lookup_table_builder->ResetLookupTableForTesting();
font_lookup_table_builder->SetSlowDownIndexingForTesting(true);
font_lookup_table_builder->EnsureFontUniqueNameTable();
base::ReadOnlySharedMemoryRegion font_table_memory =
font_lookup_table_builder->DuplicatedMemoryRegion();
blink::FontTableMatcher font_table_matcher(font_table_memory.Map());
for (auto& test_font_name_index : expected_test_fonts) {
base::Optional<blink::FontTableMatcher::MatchResult> match_result =
font_table_matcher.MatchName(test_font_name_index.first);
CHECK(!match_result);
}
// Need to reset the table again so that it can be rebuilt successfully
// without the artificial timeout when running the next test.
font_lookup_table_builder->ResetLookupTableForTesting();
}
} // namespace content
...@@ -111,54 +111,10 @@ bool CheckRequiredStylesPresent(IDWriteFontCollection* collection, ...@@ -111,54 +111,10 @@ bool CheckRequiredStylesPresent(IDWriteFontCollection* collection,
return true; return true;
} }
// The unresponsive renderer timeout is 30 seconds (kDefaultCommitTimeout). As a
// starting point, let's set the max time for indexing fonts to a third of that,
// 10 seconds and record a UMA histogram for how long indexing usually
// takes. Once we have UMA data, we can look into reducing this timeout. This
// timeout is meant to cover pathological cases of font indexing where a Windows
// installation has an unusually large collection of fonts. In practice,
// building the unique font name table should not take longer than tens of
// milliseconds (~26 ms on a developer machine, Windows 10, default fonts).
const base::TimeDelta kFontIndexingTimeout = base::TimeDelta::FromSeconds(10);
const base::TimeDelta kIndexingSlowDownForTesting =
base::TimeDelta::FromMilliseconds(1200);
bool extract_case_folded_localized_strings(
IDWriteLocalizedStrings* dwrite_localized_strings,
std::vector<std::string>* localized_strings) {
if (!dwrite_localized_strings->GetCount())
return false;
localized_strings->clear();
localized_strings->reserve(dwrite_localized_strings->GetCount());
for (UINT32 j = 0; j < dwrite_localized_strings->GetCount(); ++j) {
UINT32 length;
HRESULT hr = dwrite_localized_strings->GetStringLength(j, &length);
if (FAILED(hr))
continue;
std::wstring localized_name;
localized_name.resize(length + 1);
hr = dwrite_localized_strings->GetString(j, &localized_name[0], length + 1);
if (FAILED(hr)) {
continue;
}
localized_name.resize(length);
// The documentation for the API call does not specify an encoding but the
// results are wchar_t and FireFox considers them UTF-16, as seen here:
// https://dxr.mozilla.org/mozilla-central/source/gfx/thebes/gfxDWriteFontList.cpp#90
// so we'll assume that.
localized_strings->push_back(base::UTF16ToUTF8(
base::i18n::FoldCase(base::string16(localized_name))));
}
return true;
}
} // namespace } // namespace
DWriteFontProxyImpl::DWriteFontProxyImpl() DWriteFontProxyImpl::DWriteFontProxyImpl()
: windows_fonts_path_(GetWindowsFontsPath()), : windows_fonts_path_(GetWindowsFontsPath()) {}
slow_down_indexing_for_testing_(false) {}
DWriteFontProxyImpl::~DWriteFontProxyImpl() = default; DWriteFontProxyImpl::~DWriteFontProxyImpl() = default;
...@@ -174,10 +130,6 @@ void DWriteFontProxyImpl::SetWindowsFontsPathForTesting(base::string16 path) { ...@@ -174,10 +130,6 @@ void DWriteFontProxyImpl::SetWindowsFontsPathForTesting(base::string16 path) {
windows_fonts_path_.swap(path); windows_fonts_path_.swap(path);
} }
void DWriteFontProxyImpl::SetSlowDownIndexingForTesting(bool slow_down) {
slow_down_indexing_for_testing_ = slow_down;
}
void DWriteFontProxyImpl::FindFamily(const base::string16& family_name, void DWriteFontProxyImpl::FindFamily(const base::string16& family_name,
FindFamilyCallback callback) { FindFamilyCallback callback) {
InitializeDirectWrite(); InitializeDirectWrite();
...@@ -439,171 +391,17 @@ void DWriteFontProxyImpl::MapCharacters( ...@@ -439,171 +391,17 @@ void DWriteFontProxyImpl::MapCharacters(
DCHECK_GT(result->mapped_length, 0u); DCHECK_GT(result->mapped_length, 0u);
} }
bool DWriteFontProxyImpl::IsFontUniqueNameTableValid() {
return font_unique_name_table_memory_.IsValid() &&
font_unique_name_table_memory_.mapping.size();
}
bool DWriteFontProxyImpl::EnsureFontUniqueNameTable() {
if (IsFontUniqueNameTableValid())
return true;
base::TimeTicks time_ticks = base::TimeTicks::Now();
blink::FontUniqueNameTable font_unique_name_table;
bool timed_out = false;
// The stored_for_platform_version_identifier proto field is used for
// persisting the table to disk and identifiying whether and update to the
// table is needed when loading it back. This functionality is not used on
// Windows, hence setting it to the empty string is sufficient.
font_unique_name_table.set_stored_for_platform_version_identifier("");
for (UINT32 family_index = 0;
family_index < collection_->GetFontFamilyCount(); ++family_index) {
if (base::TimeTicks::Now() - time_ticks > kFontIndexingTimeout) {
timed_out = true;
break;
}
mswr::ComPtr<IDWriteFontFamily> family;
HRESULT hr = collection_->GetFontFamily(family_index, &family);
if (FAILED(hr))
return false;
UINT32 font_count = family->GetFontCount();
for (UINT32 font_index = 0; font_index < font_count; ++font_index) {
mswr::ComPtr<IDWriteFont> font;
hr = family->GetFont(font_index, &font);
if (FAILED(hr)) {
if (IsLastResortFallbackFont(family_index))
LogMessageFilterError(
MessageFilterError::LAST_RESORT_FONT_GET_FONT_FAILED);
return false;
}
if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE)
continue;
std::set<base::string16> path_set;
std::set<base::string16> custom_font_path_set;
uint32_t ttc_index = 0;
if (!AddFilesForFont(font.Get(), windows_fonts_path_, &path_set,
&custom_font_path_set, &ttc_index)) {
if (IsLastResortFallbackFont(family_index))
LogMessageFilterError(
MessageFilterError::LAST_RESORT_FONT_ADD_FILES_FAILED);
// It's possible to not be able to retrieve a font file for a font that
// is in the system font collection, see https://crbug.com/922183. If we
// were not able to retrieve a file for a registered font, we do not
// need to add it to the map.
continue;
}
// After having received clarification from Microsoft, the API is designed
// for allowing multiple files to be returned, if MS was to support a file
// format like Type1 fonts with this API, but for now only ever returns 1
// font file as only TrueType / OpenType fonts are supported.
CHECK_EQ(path_set.size() + custom_font_path_set.size(), 1u);
// If this font is placed in a custom font path location, we pass it to
// Blink, and we'll track with UMA there if such a font path is matched
// and used. If this happens more than very rarely, we will need to add an
// out-of-process loading mechanism for loading those uniquely matched
// font files.
base::FilePath file_path(path_set.size() ? *path_set.begin()
: *custom_font_path_set.begin());
CHECK(!file_path.empty());
// Add file entry to map.
blink::FontUniqueNameTable_UniqueFont* added_unique_font =
font_unique_name_table.add_fonts();
added_unique_font->set_file_path(file_path.AsUTF8Unsafe());
added_unique_font->set_ttc_index(ttc_index);
int added_font_index = font_unique_name_table.fonts_size() - 1;
auto extract_and_append_names =
[&font_unique_name_table, &hr, &font, &added_font_index](
DWRITE_INFORMATIONAL_STRING_ID font_info_string_id) {
// Now get names, and make them point to the added font.
IDWriteLocalizedStrings* font_id_keyed_names;
BOOL has_id_keyed_names;
hr = font->GetInformationalStrings(
font_info_string_id, &font_id_keyed_names, &has_id_keyed_names);
if (FAILED(hr) || !has_id_keyed_names)
return;
std::vector<std::string> extracted_names;
extract_case_folded_localized_strings(font_id_keyed_names,
&extracted_names);
for (auto& extracted_name : extracted_names) {
blink::FontUniqueNameTable_UniqueNameToFontMapping* name_mapping =
font_unique_name_table.add_name_map();
name_mapping->set_font_name(extracted_name);
name_mapping->set_font_index(added_font_index);
}
};
extract_and_append_names(DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME);
extract_and_append_names(DWRITE_INFORMATIONAL_STRING_FULL_NAME);
}
if (UNLIKELY(slow_down_indexing_for_testing_))
base::PlatformThread::Sleep(kIndexingSlowDownForTesting);
}
if (timed_out) {
LOG(ERROR) << "Creating unique font lookup table timed out, emptying "
"partial table.";
font_unique_name_table.clear_fonts();
font_unique_name_table.clear_name_map();
}
// Sort names for using binary search on this proto in FontTableMatcher.
std::sort(font_unique_name_table.mutable_name_map()->begin(),
font_unique_name_table.mutable_name_map()->end(),
[](const blink::FontUniqueNameTable_UniqueNameToFontMapping& a,
const blink::FontUniqueNameTable_UniqueNameToFontMapping& b) {
return a.font_name() < b.font_name();
});
font_unique_name_table_memory_ = base::ReadOnlySharedMemoryRegion::Create(
font_unique_name_table.ByteSizeLong());
if (!IsFontUniqueNameTableValid())
return false;
if (!font_unique_name_table.SerializeToArray(
font_unique_name_table_memory_.mapping.memory(),
font_unique_name_table_memory_.mapping.size())) {
font_unique_name_table_memory_ = base::MappedReadOnlyRegion();
return false;
}
UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.Proxy.LookupTableBuildTime",
base::TimeTicks::Now() - time_ticks);
// The size is usually tens of kilobytes, ~50kb on a standard Windows 10
// installation, 1MB should be a more than high enough upper limit.
UMA_HISTOGRAM_CUSTOM_COUNTS(
"DirectWrite.Fonts.Proxy.LookupTableSize",
font_unique_name_table_memory_.mapping.size() / 1024, 1, 1000, 50);
return true;
}
void DWriteFontProxyImpl::GetUniqueNameLookupTable( void DWriteFontProxyImpl::GetUniqueNameLookupTable(
GetUniqueNameLookupTableCallback callback) { GetUniqueNameLookupTableCallback callback) {
InitializeDirectWrite(); InitializeDirectWrite();
callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun( callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
std::move(callback), base::ReadOnlySharedMemoryRegion()); std::move(callback), base::ReadOnlySharedMemoryRegion());
if (!collection_)
return;
if (!EnsureFontUniqueNameTable()) if (!DWriteFontLookupTableBuilder::GetInstance()->EnsureFontUniqueNameTable())
return; return;
std::move(callback).Run(font_unique_name_table_memory_.region.Duplicate()); std::move(callback).Run(
DWriteFontLookupTableBuilder::GetInstance()->DuplicatedMemoryRegion());
} }
void DWriteFontProxyImpl::InitializeDirectWrite() { void DWriteFontProxyImpl::InitializeDirectWrite() {
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "base/memory/read_only_shared_memory_region.h" #include "base/memory/read_only_shared_memory_region.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "content/public/browser/browser_message_filter.h" #include "content/public/browser/browser_message_filter.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
...@@ -41,7 +42,6 @@ class CONTENT_EXPORT DWriteFontProxyImpl ...@@ -41,7 +42,6 @@ class CONTENT_EXPORT DWriteFontProxyImpl
const service_manager::BindSourceInfo& source_info); const service_manager::BindSourceInfo& source_info);
void SetWindowsFontsPathForTesting(base::string16 path); void SetWindowsFontsPathForTesting(base::string16 path);
void SetSlowDownIndexingForTesting(bool);
protected: protected:
// blink::mojom::DWriteFontProxy: // blink::mojom::DWriteFontProxy:
...@@ -65,20 +65,14 @@ class CONTENT_EXPORT DWriteFontProxyImpl ...@@ -65,20 +65,14 @@ class CONTENT_EXPORT DWriteFontProxyImpl
void InitializeDirectWrite(); void InitializeDirectWrite();
private: private:
// Checks if the unique font table has been built already, and if not, builds
// it by enumerating fonts from the collection, extracting their file
// locations, ttc indices and names.
bool EnsureFontUniqueNameTable();
bool IsLastResortFallbackFont(uint32_t font_index); bool IsLastResortFallbackFont(uint32_t font_index);
private: private:
bool IsFontUniqueNameTableValid();
bool direct_write_initialized_ = false; bool direct_write_initialized_ = false;
Microsoft::WRL::ComPtr<IDWriteFontCollection> collection_; Microsoft::WRL::ComPtr<IDWriteFontCollection> collection_;
Microsoft::WRL::ComPtr<IDWriteFactory2> factory2_; Microsoft::WRL::ComPtr<IDWriteFactory2> factory2_;
Microsoft::WRL::ComPtr<IDWriteFontFallback> font_fallback_; Microsoft::WRL::ComPtr<IDWriteFontFallback> font_fallback_;
base::string16 windows_fonts_path_; base::string16 windows_fonts_path_;
bool slow_down_indexing_for_testing_;
base::MappedReadOnlyRegion font_unique_name_table_memory_; base::MappedReadOnlyRegion font_unique_name_table_memory_;
// Temp code to help track down crbug.com/561873 // Temp code to help track down crbug.com/561873
......
...@@ -210,18 +210,6 @@ TEST_F(DWriteFontProxyImplUnitTest, TestFindUniqueFont) { ...@@ -210,18 +210,6 @@ TEST_F(DWriteFontProxyImplUnitTest, TestFindUniqueFont) {
} }
} }
TEST_F(DWriteFontProxyImplUnitTest, TestFontIndexingTimeout) {
impl_.SetSlowDownIndexingForTesting(true);
base::ReadOnlySharedMemoryRegion font_table_memory;
dwrite_font_proxy().GetUniqueNameLookupTable(&font_table_memory);
blink::FontTableMatcher font_table_matcher(font_table_memory.Map());
for (auto& test_font_name_index : expected_test_fonts) {
base::Optional<blink::FontTableMatcher::MatchResult> match_result =
font_table_matcher.MatchName(test_font_name_index.first);
CHECK(!match_result);
}
}
} // namespace } // namespace
......
...@@ -1546,6 +1546,7 @@ test("content_unittests") { ...@@ -1546,6 +1546,7 @@ test("content_unittests") {
"../browser/presentation/presentation_service_impl_unittest.cc", "../browser/presentation/presentation_service_impl_unittest.cc",
"../browser/renderer_host/clipboard_host_impl_unittest.cc", "../browser/renderer_host/clipboard_host_impl_unittest.cc",
"../browser/renderer_host/cursor_manager_unittest.cc", "../browser/renderer_host/cursor_manager_unittest.cc",
"../browser/renderer_host/dwrite_font_lookup_table_builder_win_unittest.cc",
"../browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc", "../browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc",
"../browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc", "../browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc",
"../browser/renderer_host/frame_token_message_queue_unittest.cc", "../browser/renderer_host/frame_token_message_queue_unittest.cc",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment