blob: e15f8ab264b00bce4fbf1feca33b62e8e42ea4a8 [file] [log] [blame]
// 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 <dwrite.h>
#include <dwrite_2.h>
#include <dwrite_3.h>
#include <wrl.h>
#include <memory>
#include <string>
#include <vector>
#include "base/cancelable_callback.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/memory/singleton.h"
#include "base/optional.h"
#include "base/synchronization/waitable_event.h"
#include "base/time/time.h"
#include "content/common/content_export.h"
#include "third_party/blink/public/common/font_unique_name_lookup/font_unique_name_table.pb.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. A method ScheduleBuildFontUniqueNameTable() is
// provided to schedule building the font unique name lookup
// structure. EnsureFontUniqueNameTable() can be called on any thread to wait
// for the lookup table to be ready. After that, DuplicateMemoryRegion() can be
// used to retrieve the lookup structure. Thread-safe when used as described
// below.
class CONTENT_EXPORT DWriteFontLookupTableBuilder {
static DWriteFontLookupTableBuilder* GetInstance();
// Retrieve the prepared memory region if it is available.
// EnsureFontUniqueNameTable() must be checked before.
base::ReadOnlySharedMemoryRegion DuplicateMemoryRegion();
// Wait for the internal WaitableEvent to be signaled if needed and return
// true if the font unique name lookup table was successfully
// constructed. Call only after ScheduleBuildFontUniqueNameTable().
bool EnsureFontUniqueNameTable();
// Returns whether the indexing has completed and the shared memory region is
// immediately ready without any sync operations.
bool FontUniqueNameTableReady();
// If needed, i.e. if we're on pre-Windows 10, posts a task to load from cache
// or build (if cache not available) the unique name table index, should only
// be called once at browser startup, after that, use
// EnsureFontUniqueNameTable() and DuplicatedMemoryRegion() to retrieve the
// lookup structure buffer.
void SchedulePrepareFontUniqueNameTableIfNeeded();
enum class SlowDownMode { kDelayEachTask, kHangOneTask, kNoSlowdown };
// Slow down each family indexing step for testing the internal timeout,
// either with a single hung task or by delaying each indexing step. At the
// same time, configure a new timeout value for testing, overriding the
// default timeout.
void SetSlowDownIndexingForTestingWithTimeout(SlowDownMode slowdown_mode,
base::TimeDelta new_timeout);
// 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();
// Signals hang_event_for_testing_ which is used in testing hanging one of the
// font name retrieval tasks.
void ResumeFromHangForTesting();
// Computes a hash to determine whether cache contents needed to be updated,
// consisting of font names and their file paths read from the registry (not
// from disk), The DWrite.dll's product version and the Chrome version, as a
// safety mechanism to refresh the cache for every release. Exposed as a
// public method to be able to run the hash function in a test.
std::string ComputePersistenceHash();
// Configures the cache directory in which to store the serialized font table
// lookup structure. Use only in testing. Normally the directory name is
// retrieved from ContentBrowserClient.
void SetCacheDirectoryForTesting(base::FilePath cache_directory);
// Configures whether the cache should be used. Needed for testing to test
// repeated rebuilding of the font table lookup structure.
void SetCachingEnabledForTesting(bool caching_enabled);
// Disables DCHECKs that ensure DWriteFontLookupTableBuilder is only run pre
// Windows 10, used for testing only to allow running the tests on Windows 10.
void OverrideDWriteVersionChecksForTesting();
friend class base::NoDestructor<DWriteFontLookupTableBuilder>;
struct FontFileWithUniqueNames {
FontFileWithUniqueNames(blink::FontUniqueNameTable_UniqueFont&& font,
std::vector<std::string>&& names);
DWriteFontLookupTableBuilder::FontFileWithUniqueNames&& other);
FontFileWithUniqueNames(const FontFileWithUniqueNames&) = delete;
FontFileWithUniqueNames& operator=(const FontFileWithUniqueNames&) = delete;
blink::FontUniqueNameTable_UniqueFont font_entry;
std::vector<std::string> extracted_names;
using FamilyResult = std::vector<FontFileWithUniqueNames>;
// Try to find a serialized lookup table from the cache directory specified at
// construction and load it into memory.
bool LoadFromFile();
// Serialize the current lookup table into a file in the cache directory
// specified at construction time.
bool PersistToFile();
// Load from cache or construct the font unique name lookup table. If the
// cache is up to date, do not schedule a run to scan all Windows-enumerated
// fonts.
void PrepareFontUniqueNameTable();
// Helper function to perform DWrite operations to retrieve path names, full
// font name and PostScript name for a font specified by collection + family
// index.
static FamilyResult ExtractPathAndNamesFromFamily(
Microsoft::WRL::ComPtr<IDWriteFontCollection> collection,
uint32_t family_index,
base::TimeTicks start_time,
SlowDownMode slow_down_mode,
base::WaitableEvent* hang_event_for_testing,
base::TimeDelta indexing_timeout);
// Callback from scheduled tasks to add the retrieved font names to the
// protobuf.
void AppendFamilyResultAndFinalizeIfNeeded(const FamilyResult& family_result);
// Sort the results that were collected into the protobuf structure and signal
// that font unique name lookup table construction is complete. Serializes the
// constructed protobuf to disk.
void FinalizeFontTable();
void OnTimeout();
bool IsFontUniqueNameTableValid();
void InitializeDirectWrite();
base::FilePath TableCacheFilePath();
// Returns true if IDWriteFactory3 is available, which means that we can
// access IDWriteFontSet API which provides direct lookup by PostScript name
// and full font name, in which case we do not need to build this table.
bool HasDWriteUniqueFontLookups();
base::TimeDelta IndexingTimeout();
// Protobuf structure temporarily used and shared during table construction.
std::unique_ptr<blink::FontUniqueNameTable> font_unique_name_table_;
base::MappedReadOnlyRegion font_table_memory_;
base::WaitableEvent font_table_built_;
bool direct_write_initialized_ = false;
base::TimeDelta font_indexing_timeout_;
Microsoft::WRL::ComPtr<IDWriteFontCollection> collection_;
Microsoft::WRL::ComPtr<IDWriteFactory2> factory2_;
Microsoft::WRL::ComPtr<IDWriteFactory3> factory3_;
SlowDownMode slow_down_mode_for_testing_ = SlowDownMode::kNoSlowdown;
uint32_t outstanding_family_results_ = 0;
base::TimeTicks start_time_table_ready_;
base::TimeTicks start_time_table_build_;
base::FilePath cache_directory_;
std::string persistence_hash_;
bool caching_enabled_ = true;
base::Optional<base::WaitableEvent> hang_event_for_testing_;
base::CancelableOnceCallback<void()> timeout_callback_;
} // namespace content