Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(730)

Unified Diff: content/common/dwrite_font_platform_win.cc

Issue 1845063002: Revert of Remove font cache code (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/child/font_warmup_win.cc ('k') | content/common/dwrite_font_platform_win_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/common/dwrite_font_platform_win.cc
diff --git a/content/common/dwrite_font_platform_win.cc b/content/common/dwrite_font_platform_win.cc
new file mode 100644
index 0000000000000000000000000000000000000000..56140f421aae388d5eb07c3b4eaf3ec0639dbf04
--- /dev/null
+++ b/content/common/dwrite_font_platform_win.cc
@@ -0,0 +1,1282 @@
+// Copyright 2014 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/public/common/dwrite_font_platform_win.h"
+
+#include <windows.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <dwrite.h>
+#include <wrl/implements.h>
+#include <wrl/wrappers/corewrappers.h>
+
+#include <limits>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/debug/alias.h"
+#include "base/debug/crash_logging.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/shared_memory.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/histogram.h"
+#include "base/path_service.h"
+#include "base/process/process_handle.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/lock.h"
+#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
+#include "base/win/registry.h"
+#include "base/win/scoped_comptr.h"
+#include "content/public/common/content_switches.h"
+
+namespace {
+
+// Font Cache implementation short story:
+// Due to our sandboxing restrictions, we cannot connect to Windows font cache
+// service from Renderer and need to use DirectWrite isolated font loading
+// mechanism.
+// DirectWrite needs to be initialized before any of the API could be used.
+// During initialization DirectWrite loads all font files and populates
+// internal cache, we refer this phase as enumeration and we are trying
+// to optimize this phase in our cache approach. Using cache during
+// initialization will help improve on startup latency in each renderer
+// instance.
+// During enumeration DirectWrite reads various fragments from .ttf/.ttc
+// font files. Our assumption is that these fragments are being read to
+// cache information such as font families, supported sizes etc.
+// For reading fragments DirectWrite calls ReadFragment of our FontFileStream
+// implementation with parameters start_offset and length. We cache these
+// parameters along with associated data chunk.
+// Here is small example of how segments are read
+// start_offset: 0, length: 16
+// start_offset: 0, length: 12
+// start_offset: 0, length: 117
+// For better cache management we collapse segments if they overlap or are
+// adjacent.
+
+namespace mswr = Microsoft::WRL;
+
+const char kFontKeyName[] = "font_key_name";
+
+// We use this value to determine whether to cache file fragments
+// or not. In our trials we observed that for some font files
+// direct write ends up reading almost entire file during enumeration
+// phase. If we don't use this percentile formula we will end up
+// increasing significant cache size by caching entire file contents
+// for some of the font files.
+const double kMaxPercentileOfFontFileSizeToCache = 0.6;
+
+// With current implementation we map entire shared section into memory during
+// renderer startup. This causes increase in working set of Chrome. As first
+// step we want to see if caching is really improving any performance for our
+// users, so we are putting arbitrary limit on cache file size. There are
+// multiple ways we can tune our working size, like mapping only required part
+// of section at any given time.
+const double kArbitraryCacheFileSizeLimit = (30 * 1024 * 1024);
+
+// We have chosen current font file length arbitrarily. In our logic
+// if we don't find file we are looking for in cache we end up loading
+// that file directly from system fonts folder.
+const unsigned int kMaxFontFileNameLength = 34;
+
+const DWORD kCacheFileVersion = 103;
+const DWORD kFileSignature = 0x4D4F5243; // CROM
+const DWORD kMagicCompletionSignature = 0x454E4F44; // DONE
+
+const DWORD kUndefinedDWORDS = 36;
+
+// Make sure that all structure sizes align with 8 byte boundary otherwise
+// dr. memory test may complain.
+#pragma pack(push, 8)
+// Cache file header, includes signature, completion bits and version.
+struct CacheFileHeader {
+ CacheFileHeader() {
+ file_signature = kFileSignature;
+ magic_completion_signature = 0;
+ version = kCacheFileVersion;
+ ::ZeroMemory(undefined, sizeof(undefined));
+ }
+
+ DWORD file_signature;
+ DWORD magic_completion_signature;
+ DWORD version;
+ BYTE undefined[kUndefinedDWORDS];
+};
+
+// Entry for a particular font file within this cache.
+struct CacheFileEntry {
+ CacheFileEntry() {
+ file_size = 0;
+ entry_count = 0;
+ ::ZeroMemory(file_name, sizeof(file_name));
+ }
+
+ UINT64 file_size;
+ DWORD entry_count;
+ wchar_t file_name[kMaxFontFileNameLength];
+};
+
+// Offsets or data chunks that are cached for particular font file.
+struct CacheFileOffsetEntry {
+ CacheFileOffsetEntry() {
+ start_offset = 0;
+ length = 0;
+ }
+
+ UINT64 start_offset;
+ UINT64 length;
+ /* BYTE blob_[]; // Place holder for the blob that follows. */
+};
+#pragma pack(pop)
+
+bool ValidateFontCacheHeader(CacheFileHeader* header) {
+ return (header->file_signature == kFileSignature &&
+ header->magic_completion_signature == kMagicCompletionSignature &&
+ header->version == kCacheFileVersion);
+}
+
+class FontCacheWriter;
+
+// This class implements main interface required for loading custom font
+// collection as specified by DirectWrite. We also use this class for storing
+// some state information as this is one of the centralized entity.
+class FontCollectionLoader
+ : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
+ IDWriteFontCollectionLoader> {
+ public:
+ FontCollectionLoader()
+ : in_collection_building_mode_(false),
+ create_static_cache_(false) {}
+
+ ~FontCollectionLoader() override;
+
+ HRESULT RuntimeClassInitialize() {
+ return S_OK;
+ }
+
+ // IDWriteFontCollectionLoader methods.
+ HRESULT STDMETHODCALLTYPE
+ CreateEnumeratorFromKey(IDWriteFactory* factory,
+ void const* key,
+ UINT32 key_size,
+ IDWriteFontFileEnumerator** file_enumerator) override;
+
+ // Does all the initialization for required loading fonts from registry.
+ static HRESULT Initialize(IDWriteFactory* factory);
+
+ // Returns font cache map size.
+ UINT32 GetFontMapSize();
+
+ // Returns font name string when given font index.
+ base::string16 GetFontNameFromKey(UINT32 idx);
+
+ // Loads internal structure with fonts from registry.
+ bool LoadFontListFromRegistry();
+
+ // Loads restricted web safe fonts as fallback method to registry fonts.
+ bool LoadRestrictedFontList();
+
+ // Puts class in collection building mode. In collection building mode
+ // we use static cache if it is available as a look aside buffer.
+ void EnableCollectionBuildingMode(bool enable);
+
+ // Returns current state of collection building.
+ bool InCollectionBuildingMode();
+
+ // Loads static cache file.
+ bool LoadCacheFile();
+
+ // Unloads cache file and related data.
+ void UnloadCacheFile();
+
+ // Puts class in static cache creating mode. In this mode we record all
+ // direct write requests and store chunks of font data.
+ void EnterStaticCacheMode(const WCHAR* file_name);
+
+ // Gets out of static cache building mode.
+ void LeaveStaticCacheMode();
+
+ // Returns if class is currently in static cache building mode.
+ bool IsBuildStaticCacheMode();
+
+ // Validates cache file for consistency.
+ bool ValidateCacheFile(base::File* file);
+
+ private:
+ // Structure to represent each chunk within font file that we load in memory.
+ struct CacheTableOffsetEntry {
+ UINT64 start_offset;
+ UINT64 length;
+ BYTE* inside_file_ptr;
+ };
+
+ typedef std::vector<CacheTableOffsetEntry> OffsetVector;
+
+ // Structure representing each font entry with cache.
+ struct CacheTableEntry {
+ UINT64 file_size;
+ OffsetVector offset_entries;
+ };
+
+ public:
+ // Returns whether file we have particular font entry within cache or not.
+ bool IsFileCached(UINT32 font_key);
+ // Returns cache fragment corresponding to specific font key.
+ void* GetCachedFragment(UINT32 font_key, UINT64 start_offset, UINT64 length);
+ // Returns actual font file size at the time of caching.
+ UINT64 GetCachedFileSize(UINT32 font_key);
+
+ // Returns instance of font cache writer. This class manages actual font
+ // file format.
+ FontCacheWriter* GetFontCacheWriter();
+
+ private:
+ // Functions validates and loads cache into internal map.
+ bool ValidateAndLoadCacheMap();
+
+ mswr::ComPtr<IDWriteFontFileLoader> file_loader_;
+
+ std::vector<base::string16> reg_fonts_;
+ bool in_collection_building_mode_;
+ bool create_static_cache_;
+ scoped_ptr<base::SharedMemory> cache_;
+ scoped_ptr<FontCacheWriter> cache_writer_;
+
+ typedef std::map<base::string16, CacheTableEntry*> CacheMap;
+ CacheMap cache_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(FontCollectionLoader);
+};
+
+mswr::ComPtr<FontCollectionLoader> g_font_loader;
+base::win::ScopedHandle g_shared_font_cache;
+
+// Class responsible for handling font cache file format details as well as
+// tracking various cache region requests by direct write.
+class FontCacheWriter {
+ public:
+ FontCacheWriter() : count_font_entries_ignored_(0), cookie_counter_(0) {}
+
+ ~FontCacheWriter() {
+ if (static_cache_.get()) {
+ static_cache_->Close();
+ }
+ }
+
+ public:
+ // Holds data related to individual region as requested by direct write.
+ struct CacheRegion {
+ UINT64 start_offset;
+ UINT64 length;
+ const BYTE* ptr;
+ /* BYTE blob_[]; // Place holder for the blob that follows. */
+ };
+
+ // Function to create static font cache file.
+ bool Create(const wchar_t* file_name) {
+ static_cache_.reset(new base::File(base::FilePath(file_name),
+ base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE |
+ base::File::FLAG_EXCLUSIVE_WRITE));
+ if (!static_cache_->IsValid()) {
+ static_cache_.reset();
+ return false;
+ }
+ CacheFileHeader header;
+
+ // At offset 0 write cache version
+ static_cache_->Write(0,
+ reinterpret_cast<const char*>(&header),
+ sizeof(header));
+
+ static_cache_->Flush();
+ return true;
+ }
+
+ // Closes static font cache file. Also writes completion signature to mark
+ // it as completely written.
+ void Close() {
+ if (static_cache_.get()) {
+ CacheFileHeader header;
+ header.magic_completion_signature = kMagicCompletionSignature;
+ // At offset 0 write cache version
+ int bytes_written = static_cache_->Write(0,
+ reinterpret_cast<const char*>(&header),
+ sizeof(header));
+ DCHECK_NE(bytes_written, -1);
+
+ UMA_HISTOGRAM_MEMORY_KB("DirectWrite.Fonts.BuildCache.File.Size",
+ static_cache_->GetLength() / 1024);
+
+ UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.BuildCache.Ignored",
+ count_font_entries_ignored_);
+
+ static_cache_->Close();
+ static_cache_.reset(NULL);
+ }
+ }
+
+ private:
+ typedef std::vector<CacheRegion> RegionVector;
+
+ // Structure to track various regions requested by direct write for particular
+ // font file.
+ struct FontEntryInternal {
+ FontEntryInternal(const wchar_t* name, UINT64 size)
+ : file_name(name),
+ file_size(size) {
+ }
+
+ base::string16 file_name;
+ UINT64 file_size;
+ RegionVector regions;
+ };
+
+ public:
+ // Starts up new font entry to be tracked, returns cookie to identify this
+ // particular entry.
+ UINT NewFontEntry(const wchar_t* file_name, UINT64 file_size) {
+ base::AutoLock lock(lock_);
+ UINT old_counter = cookie_counter_;
+ FontEntryInternal* font_entry = new FontEntryInternal(file_name, file_size);
+ cookie_map_[cookie_counter_].reset(font_entry);
+ cookie_counter_++;
+ return old_counter;
+ }
+
+ // AddRegion function lets caller add various regions to be cached for
+ // particular font file. Once enumerating that particular font file is done
+ // (based on uniquely identifying cookie) changes could be committed using
+ // CommitFontEntry
+ bool AddRegion(UINT64 cookie, UINT64 start, UINT64 length, const BYTE* ptr) {
+ base::AutoLock lock(lock_);
+ if (cookie_map_.find(cookie) == cookie_map_.end())
+ return false;
+ RegionVector& regions = cookie_map_[cookie].get()->regions;
+ CacheRegion region;
+ region.start_offset = start;
+ region.length = length;
+ region.ptr = ptr;
+ regions.push_back(region);
+ return true;
+ }
+
+ // Function which commits after merging all collected regions into cache file.
+ bool CommitFontEntry(UINT cookie) {
+ base::AutoLock lock(lock_);
+ if (cookie_map_.find(cookie) == cookie_map_.end())
+ return false;
+
+ // We will skip writing entries beyond allowed limit. Following condition
+ // doesn't enforce hard file size. We need to write complete font entry.
+ int64_t length = static_cache_->GetLength();
+ if (length == -1 || length >= kArbitraryCacheFileSizeLimit) {
+ count_font_entries_ignored_++;
+ return false;
+ }
+
+ FontEntryInternal* font_entry = cookie_map_[cookie].get();
+ RegionVector& regions = font_entry->regions;
+ std::sort(regions.begin(), regions.end(), SortCacheRegions);
+
+ // At this point, we have collected all regions to be cached. These regions
+ // are tuples of start, length, data for particular data segment.
+ // These tuples can overlap.
+ // e.g. (0, 12, data), (0, 117, data), (21, 314, data), (335, 15, data)
+ // In this case as you can see first three segments overlap and
+ // 4th is adjacent. If we cache them individually then we will end up
+ // caching duplicate data, so we merge these segments together to find
+ // superset for the cache. In above example our algorithm should
+ // produce (cache) single segment starting at offset 0 with length 350.
+ RegionVector merged_regions;
+ RegionVector::iterator iter;
+ int idx = 0;
+ for (iter = regions.begin(); iter != regions.end(); iter++) {
+ if (iter == regions.begin()) {
+ merged_regions.push_back(*iter);
+ continue;
+ }
+ CacheRegion& base_region = merged_regions[idx];
+ if (IsOverlap(&base_region, &(*iter))) {
+ UINT64 end1 = base_region.start_offset + base_region.length;
+ UINT64 end2 = iter->start_offset + iter->length;
+ if (base_region.start_offset > iter->start_offset) {
+ base_region.start_offset = iter->start_offset;
+ base_region.ptr = iter->ptr;
+ }
+ base_region.length = std::max(end1, end2) - base_region.start_offset;
+ } else {
+ merged_regions.push_back(*iter);
+ idx++;
+ }
+ }
+
+ UINT64 total_merged_cache_in_bytes = 0;
+ for (iter = merged_regions.begin(); iter != merged_regions.end(); iter++) {
+ total_merged_cache_in_bytes += iter->length;
+ }
+
+ // We want to adjust following parameter based on experiments. But general
+ // logic here is that if we are going to end up caching most of the contents
+ // for a file (e.g. simsunb.ttf > 90%) then we should avoid caching that
+ // file.
+ double percentile = static_cast<double>(total_merged_cache_in_bytes) /
+ font_entry->file_size;
+ if (percentile > kMaxPercentileOfFontFileSizeToCache) {
+ count_font_entries_ignored_++;
+ return false;
+ }
+
+ CacheFileEntry entry;
+ wcsncpy_s(entry.file_name, kMaxFontFileNameLength,
+ font_entry->file_name.c_str(), _TRUNCATE);
+ entry.file_size = font_entry->file_size;
+ entry.entry_count = merged_regions.size();
+ static_cache_->WriteAtCurrentPos(
+ reinterpret_cast<const char*>(&entry),
+ sizeof(entry));
+ for (iter = merged_regions.begin(); iter != merged_regions.end(); iter++) {
+ CacheFileOffsetEntry offset_entry;
+ offset_entry.start_offset = iter->start_offset;
+ offset_entry.length = iter->length;
+ static_cache_->WriteAtCurrentPos(
+ reinterpret_cast<const char*>(&offset_entry),
+ sizeof(offset_entry));
+ static_cache_->WriteAtCurrentPos(
+ reinterpret_cast<const char*>(iter->ptr),
+ iter->length);
+ }
+ return true;
+ }
+
+ private:
+ // This is the count of font entries that we reject based on size to be
+ // cached.
+ unsigned int count_font_entries_ignored_;
+ scoped_ptr<base::File> static_cache_;
+ std::map<UINT, scoped_ptr<FontEntryInternal>> cookie_map_;
+ UINT cookie_counter_;
+
+ // Lock is required to protect internal data structures and access to file,
+ // According to MSDN documentation on ReadFileFragment and based on our
+ // experiments so far, there is possibility of ReadFileFragment getting called
+ // from multiple threads.
+ base::Lock lock_;
+
+ // Function checks if two regions overlap or are adjacent.
+ bool IsOverlap(CacheRegion* region1, CacheRegion* region2) {
+ return
+ !((region1->start_offset + region1->length) < region2->start_offset ||
+ region1->start_offset > (region2->start_offset + region2->length));
+ }
+
+ // Function to sort cached regions.
+ static bool SortCacheRegions(const CacheRegion& region1,
+ const CacheRegion& region2) {
+ return
+ region1.start_offset == region2.start_offset ?
+ region1.length < region2.length :
+ region1.start_offset < region2.start_offset;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(FontCacheWriter);
+};
+
+// Class implements IDWriteFontFileStream interface as required by direct write.
+class FontFileStream
+ : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
+ IDWriteFontFileStream> {
+ public:
+ // IDWriteFontFileStream methods.
+ HRESULT STDMETHODCALLTYPE ReadFileFragment(
+ void const** fragment_start,
+ UINT64 file_offset,
+ UINT64 fragment_size,
+ void** context) override {
+ if (cached_data_) {
+ *fragment_start = g_font_loader->GetCachedFragment(font_key_,
+ file_offset,
+ fragment_size);
+ if (*fragment_start == NULL) {
+ DCHECK(false);
+ }
+ *context = NULL;
+ return *fragment_start != NULL ? S_OK : E_FAIL;
+ }
+ if (!memory_.get() || !memory_->IsValid() ||
+ file_offset >= memory_->length() ||
+ (file_offset + fragment_size) > memory_->length())
+ return E_FAIL;
+
+ *fragment_start = static_cast<BYTE const*>(memory_->data()) +
+ static_cast<size_t>(file_offset);
+ *context = NULL;
+ if (g_font_loader->IsBuildStaticCacheMode()) {
+ FontCacheWriter* cache_writer = g_font_loader->GetFontCacheWriter();
+ cache_writer->AddRegion(writer_cookie_,
+ file_offset,
+ fragment_size,
+ static_cast<const BYTE*>(*fragment_start));
+ }
+ return S_OK;
+ }
+
+ void STDMETHODCALLTYPE ReleaseFileFragment(void* context) override {}
+
+ HRESULT STDMETHODCALLTYPE GetFileSize(UINT64* file_size) override {
+ if (cached_data_) {
+ *file_size = g_font_loader->GetCachedFileSize(font_key_);
+ return S_OK;
+ }
+
+ if (!memory_.get() || !memory_->IsValid())
+ return E_FAIL;
+
+ *file_size = memory_->length();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetLastWriteTime(UINT64* last_write_time) override {
+ if (cached_data_) {
+ *last_write_time = 0;
+ return S_OK;
+ }
+
+ if (!memory_.get() || !memory_->IsValid())
+ return E_FAIL;
+
+ // According to MSDN article http://goo.gl/rrSYzi the "last modified time"
+ // is used by DirectWrite font selection algorithms to determine whether
+ // one font resource is more up to date than another one.
+ // So by returning 0 we are assuming that it will treat all fonts to be
+ // equally up to date.
+ // TODO(shrikant): We should further investigate this.
+ *last_write_time = 0;
+ return S_OK;
+ }
+
+ FontFileStream() : font_key_(0), cached_data_(false) {}
+
+ HRESULT RuntimeClassInitialize(UINT32 font_key) {
+ if (g_font_loader->InCollectionBuildingMode() &&
+ g_font_loader->IsFileCached(font_key)) {
+ cached_data_ = true;
+ font_key_ = font_key;
+ return S_OK;
+ }
+
+ base::FilePath path;
+ PathService::Get(base::DIR_WINDOWS_FONTS, &path);
+ base::string16 font_key_name(g_font_loader->GetFontNameFromKey(font_key));
+ path = path.Append(font_key_name.c_str());
+ memory_.reset(new base::MemoryMappedFile());
+
+ // Put some debug information on stack.
+ WCHAR font_name[MAX_PATH];
+ path.value().copy(font_name, arraysize(font_name));
+ base::debug::Alias(font_name);
+
+ if (!memory_->Initialize(path)) {
+ memory_.reset();
+ return E_FAIL;
+ }
+
+ font_key_ = font_key;
+
+ base::debug::SetCrashKeyValue(kFontKeyName,
+ base::WideToUTF8(font_key_name));
+
+ if (g_font_loader->IsBuildStaticCacheMode()) {
+ FontCacheWriter* cache_writer = g_font_loader->GetFontCacheWriter();
+ writer_cookie_ = cache_writer->NewFontEntry(font_key_name.c_str(),
+ memory_->length());
+ }
+ return S_OK;
+ }
+
+ ~FontFileStream() override {
+ if (g_font_loader->IsBuildStaticCacheMode()) {
+ FontCacheWriter* cache_writer = g_font_loader->GetFontCacheWriter();
+ cache_writer->CommitFontEntry(writer_cookie_);
+ }
+ }
+
+ private:
+ UINT32 font_key_;
+ scoped_ptr<base::MemoryMappedFile> memory_;
+ bool cached_data_;
+ UINT writer_cookie_;
+
+ DISALLOW_COPY_AND_ASSIGN(FontFileStream);
+};
+
+// Implements IDWriteFontFileLoader as required by FontFileLoader.
+class FontFileLoader
+ : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
+ IDWriteFontFileLoader> {
+ public:
+ // IDWriteFontFileLoader methods.
+ HRESULT STDMETHODCALLTYPE
+ CreateStreamFromKey(void const* ref_key,
+ UINT32 ref_key_size,
+ IDWriteFontFileStream** stream) override {
+ if (ref_key_size != sizeof(UINT32))
+ return E_FAIL;
+
+ UINT32 font_key = *static_cast<const UINT32*>(ref_key);
+ mswr::ComPtr<FontFileStream> font_stream;
+ HRESULT hr = mswr::MakeAndInitialize<FontFileStream>(&font_stream,
+ font_key);
+ if (SUCCEEDED(hr)) {
+ *stream = font_stream.Detach();
+ return S_OK;
+ }
+ return E_FAIL;
+ }
+
+ FontFileLoader() {}
+ ~FontFileLoader() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FontFileLoader);
+};
+
+// Implements IDWriteFontFileEnumerator as required by direct write.
+class FontFileEnumerator
+ : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
+ IDWriteFontFileEnumerator> {
+ public:
+ // IDWriteFontFileEnumerator methods.
+ HRESULT STDMETHODCALLTYPE MoveNext(BOOL* has_current_file) override {
+ *has_current_file = FALSE;
+
+ if (current_file_)
+ current_file_.ReleaseAndGetAddressOf();
+
+ if (font_idx_ < g_font_loader->GetFontMapSize()) {
+ HRESULT hr =
+ factory_->CreateCustomFontFileReference(&font_idx_,
+ sizeof(UINT32),
+ file_loader_.Get(),
+ current_file_.GetAddressOf());
+ DCHECK(SUCCEEDED(hr));
+ *has_current_file = TRUE;
+ font_idx_++;
+ }
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ GetCurrentFontFile(IDWriteFontFile** font_file) override {
+ if (!current_file_) {
+ *font_file = NULL;
+ return E_FAIL;
+ }
+
+ *font_file = current_file_.Detach();
+ return S_OK;
+ }
+
+ FontFileEnumerator(const void* keys,
+ UINT32 buffer_size,
+ IDWriteFactory* factory,
+ IDWriteFontFileLoader* file_loader)
+ : factory_(factory), file_loader_(file_loader), font_idx_(0) {}
+
+ ~FontFileEnumerator() override {}
+
+ mswr::ComPtr<IDWriteFactory> factory_;
+ mswr::ComPtr<IDWriteFontFile> current_file_;
+ mswr::ComPtr<IDWriteFontFileLoader> file_loader_;
+ UINT32 font_idx_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FontFileEnumerator);
+};
+
+// IDWriteFontCollectionLoader methods.
+HRESULT STDMETHODCALLTYPE FontCollectionLoader::CreateEnumeratorFromKey(
+ IDWriteFactory* factory,
+ void const* key,
+ UINT32 key_size,
+ IDWriteFontFileEnumerator** file_enumerator) {
+ *file_enumerator = mswr::Make<FontFileEnumerator>(
+ key, key_size, factory, file_loader_.Get()).Detach();
+ return S_OK;
+}
+
+// static
+HRESULT FontCollectionLoader::Initialize(IDWriteFactory* factory) {
+ DCHECK(g_font_loader == NULL);
+
+ HRESULT result;
+ result = mswr::MakeAndInitialize<FontCollectionLoader>(&g_font_loader);
+ if (FAILED(result) || !g_font_loader) {
+ DCHECK(false);
+ return E_FAIL;
+ }
+
+ CHECK(g_font_loader->LoadFontListFromRegistry());
+
+ g_font_loader->file_loader_ = mswr::Make<FontFileLoader>().Detach();
+
+ factory->RegisterFontFileLoader(g_font_loader->file_loader_.Get());
+ factory->RegisterFontCollectionLoader(g_font_loader.Get());
+
+ return S_OK;
+}
+
+FontCollectionLoader::~FontCollectionLoader() {
+ STLDeleteContainerPairSecondPointers(cache_map_.begin(), cache_map_.end());
+}
+
+UINT32 FontCollectionLoader::GetFontMapSize() {
+ return reg_fonts_.size();
+}
+
+base::string16 FontCollectionLoader::GetFontNameFromKey(UINT32 idx) {
+ DCHECK(idx < reg_fonts_.size());
+ return reg_fonts_[idx];
+}
+
+const base::FilePath::CharType* kFontExtensionsToIgnore[] {
+ FILE_PATH_LITERAL(".FON"), // Bitmap or vector
+ FILE_PATH_LITERAL(".PFM"), // Adobe Type 1
+ FILE_PATH_LITERAL(".PFB"), // Adobe Type 1
+};
+
+const wchar_t* kFontsToIgnore[] = {
+ // "Gill Sans Ultra Bold" turns into an Ultra Bold weight "Gill Sans" in
+ // DirectWrite, but most users don't have any other weights. The regular
+ // weight font is named "Gill Sans MT", but that ends up in a different
+ // family with that name. On Mac, there's a "Gill Sans" with various weights,
+ // so CSS authors use { 'font-family': 'Gill Sans', 'Gill Sans MT', ... } and
+ // because of the DirectWrite family futzing, they end up with an Ultra Bold
+ // font, when they just wanted "Gill Sans". Mozilla implemented a more
+ // complicated hack where they effectively rename the Ultra Bold font to
+ // "Gill Sans MT Ultra Bold", but because the Ultra Bold font is so ugly
+ // anyway, we simply ignore it. See
+ // http://www.microsoft.com/typography/fonts/font.aspx?FMID=978 for a picture
+ // of the font, and the file name. We also ignore "Gill Sans Ultra Bold
+ // Condensed".
+ L"gilsanub.ttf",
+ L"gillubcd.ttf",
+};
+
+bool FontCollectionLoader::LoadFontListFromRegistry() {
+ const wchar_t kFontsRegistry[] =
+ L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
+ CHECK(reg_fonts_.empty());
+ base::win::RegKey regkey;
+ if (regkey.Open(HKEY_LOCAL_MACHINE, kFontsRegistry, KEY_READ) !=
+ ERROR_SUCCESS) {
+ return false;
+ }
+
+ base::FilePath system_font_path;
+ PathService::Get(base::DIR_WINDOWS_FONTS, &system_font_path);
+
+ base::string16 name;
+ base::string16 value;
+ for (DWORD idx = 0; idx < regkey.GetValueCount(); idx++) {
+ if (regkey.GetValueNameAt(idx, &name) == ERROR_SUCCESS &&
+ regkey.ReadValue(name.c_str(), &value) == ERROR_SUCCESS) {
+ base::FilePath path(value.c_str());
+ // We need to check if path in registry is absolute, if it is then
+ // we check if it is same as DIR_WINDOWS_FONTS otherwise we ignore.
+ bool absolute = path.IsAbsolute();
+ if (absolute &&
+ !base::FilePath::CompareEqualIgnoreCase(system_font_path.value(),
+ path.DirName().value())) {
+ continue;
+ }
+
+ // Ignore if path ends with a separator.
+ if (path.EndsWithSeparator())
+ continue;
+
+ if (absolute)
+ value = path.BaseName().value();
+
+ bool should_ignore = false;
+ for (const auto& ignore : kFontsToIgnore) {
+ if (base::FilePath::CompareEqualIgnoreCase(value, ignore)) {
+ should_ignore = true;
+ break;
+ }
+ }
+ // DirectWrite doesn't support bitmap/vector fonts and Adobe type 1
+ // fonts, we will ignore those font extensions.
+ // MSDN article: http://goo.gl/TfCOA
+ if (!should_ignore) {
+ for (const auto& ignore : kFontExtensionsToIgnore) {
+ if (path.MatchesExtension(ignore)) {
+ should_ignore = true;
+ break;
+ }
+ }
+ }
+
+ if (!should_ignore)
+ reg_fonts_.push_back(value.c_str());
+ }
+ }
+ UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Loaded", reg_fonts_.size());
+ UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Ignored",
+ regkey.GetValueCount() - reg_fonts_.size());
+ return true;
+}
+
+// This list is mainly based on prefs/prefs_tab_helper.cc kFontDefaults.
+const wchar_t* kRestrictedFontSet[] = {
+ // These are the "Web Safe" fonts.
+ L"times.ttf", // IDS_STANDARD_FONT_FAMILY
+ L"timesbd.ttf", // IDS_STANDARD_FONT_FAMILY
+ L"timesbi.ttf", // IDS_STANDARD_FONT_FAMILY
+ L"timesi.ttf", // IDS_STANDARD_FONT_FAMILY
+ L"cour.ttf", // IDS_FIXED_FONT_FAMILY
+ L"courbd.ttf", // IDS_FIXED_FONT_FAMILY
+ L"courbi.ttf", // IDS_FIXED_FONT_FAMILY
+ L"couri.ttf", // IDS_FIXED_FONT_FAMILY
+ L"consola.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
+ L"consolab.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
+ L"consolai.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
+ L"consolaz.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
+ L"arial.ttf", // IDS_SANS_SERIF_FONT_FAMILY
+ L"arialbd.ttf", // IDS_SANS_SERIF_FONT_FAMILY
+ L"arialbi.ttf", // IDS_SANS_SERIF_FONT_FAMILY
+ L"ariali.ttf", // IDS_SANS_SERIF_FONT_FAMILY
+ L"comic.ttf", // IDS_CURSIVE_FONT_FAMILY
+ L"comicbd.ttf", // IDS_CURSIVE_FONT_FAMILY
+ L"comici.ttf", // IDS_CURSIVE_FONT_FAMILY
+ L"comicz.ttf", // IDS_CURSIVE_FONT_FAMILY
+ L"impact.ttf", // IDS_FANTASY_FONT_FAMILY
+ L"georgia.ttf",
+ L"georgiab.ttf",
+ L"georgiai.ttf",
+ L"georgiaz.ttf",
+ L"trebuc.ttf",
+ L"trebucbd.ttf",
+ L"trebucbi.ttf",
+ L"trebucit.ttf",
+ L"verdana.ttf",
+ L"verdanab.ttf",
+ L"verdanai.ttf",
+ L"verdanaz.ttf",
+ L"segoeui.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
+ L"segoeuib.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
+ L"segoeuii.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
+ L"msgothic.ttc", // IDS_STANDARD_FONT_FAMILY_JAPANESE
+ L"msmincho.ttc", // IDS_SERIF_FONT_FAMILY_JAPANESE
+ L"gulim.ttc", // IDS_FIXED_FONT_FAMILY_KOREAN
+ L"batang.ttc", // IDS_SERIF_FONT_FAMILY_KOREAN
+ L"simsun.ttc", // IDS_STANDARD_FONT_FAMILY_SIMPLIFIED_HAN
+ L"mingliu.ttc", // IDS_SERIF_FONT_FAMILY_TRADITIONAL_HAN
+
+ // These are from the Blink fallback list.
+ L"david.ttf", // USCRIPT_HEBREW
+ L"davidbd.ttf", // USCRIPT_HEBREW
+ L"euphemia.ttf", // USCRIPT_CANADIAN_ABORIGINAL
+ L"gautami.ttf", // USCRIPT_TELUGU
+ L"gautamib.ttf", // USCRIPT_TELUGU
+ L"latha.ttf", // USCRIPT_TAMIL
+ L"lathab.ttf", // USCRIPT_TAMIL
+ L"mangal.ttf", // USCRIPT_DEVANAGARI
+ L"mangalb.ttf", // USCRIPT_DEVANAGARI
+ L"monbaiti.ttf", // USCRIPT_MONGOLIAN
+ L"mvboli.ttf", // USCRIPT_THAANA
+ L"plantc.ttf", // USCRIPT_CHEROKEE
+ L"raavi.ttf", // USCRIPT_GURMUKHI
+ L"raavib.ttf", // USCRIPT_GURMUKHI
+ L"shruti.ttf", // USCRIPT_GUJARATI
+ L"shrutib.ttf", // USCRIPT_GUJARATI
+ L"sylfaen.ttf", // USCRIPT_GEORGIAN and USCRIPT_ARMENIAN
+ L"tahoma.ttf", // USCRIPT_ARABIC,
+ L"tahomabd.ttf", // USCRIPT_ARABIC,
+ L"tunga.ttf", // USCRIPT_KANNADA
+ L"tungab.ttf", // USCRIPT_KANNADA
+ L"vrinda.ttf", // USCRIPT_BENGALI
+ L"vrindab.ttf", // USCRIPT_BENGALI
+};
+
+bool FontCollectionLoader::LoadRestrictedFontList() {
+ reg_fonts_.clear();
+ reg_fonts_.assign(kRestrictedFontSet,
+ kRestrictedFontSet + _countof(kRestrictedFontSet));
+ return true;
+}
+
+void FontCollectionLoader::EnableCollectionBuildingMode(bool enable) {
+ in_collection_building_mode_ = enable;
+}
+
+bool FontCollectionLoader::InCollectionBuildingMode() {
+ return in_collection_building_mode_;
+}
+
+bool FontCollectionLoader::IsFileCached(UINT32 font_key) {
+ if (!cache_.get() || cache_->memory() == NULL) {
+ return false;
+ }
+ CacheMap::iterator iter = cache_map_.find(
+ GetFontNameFromKey(font_key).c_str());
+ return iter != cache_map_.end();
+}
+
+bool FontCollectionLoader::LoadCacheFile() {
+ TRACE_EVENT0("startup", "FontCollectionLoader::LoadCacheFile");
+
+ std::string font_cache_handle_string =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kFontCacheSharedHandle);
+ if (font_cache_handle_string.empty())
+ return false;
+
+ unsigned int handle_uint;
+ base::StringToUint(font_cache_handle_string, &handle_uint);
+ DCHECK(handle_uint);
+ if (handle_uint > static_cast<unsigned int>(std::numeric_limits<long>::max()))
+ return false;
+ base::SharedMemoryHandle font_cache_handle(LongToHandle(handle_uint),
+ base::GetCurrentProcId());
+
+ base::SharedMemory* shared_mem = new base::SharedMemory(
+ font_cache_handle, true);
+ // Map the cache file into memory.
+ shared_mem->Map(0);
+
+ cache_.reset(shared_mem);
+
+ if (base::StartsWith(base::FieldTrialList::FindFullName("LightSpeed"),
+ "PrefetchDWriteFontCache",
+ base::CompareCase::SENSITIVE)) {
+ // Prefetch the cache, to avoid unordered IO when it is used.
+ // PrefetchVirtualMemory() is loaded dynamically because it is only
+ // available from Win8.
+ decltype(PrefetchVirtualMemory)* prefetch_virtual_memory =
+ reinterpret_cast<decltype(PrefetchVirtualMemory)*>(::GetProcAddress(
+ ::GetModuleHandle(L"kernel32.dll"), "PrefetchVirtualMemory"));
+ if (prefetch_virtual_memory != NULL) {
+ WIN32_MEMORY_RANGE_ENTRY memory_range;
+ memory_range.VirtualAddress = shared_mem->memory();
+ memory_range.NumberOfBytes = shared_mem->mapped_size();
+ prefetch_virtual_memory(::GetCurrentProcess(), 1, &memory_range, 0);
+ }
+ }
+
+ if (!ValidateAndLoadCacheMap()) {
+ cache_.reset();
+ return false;
+ }
+
+ return true;
+}
+
+void FontCollectionLoader::UnloadCacheFile() {
+ cache_.reset();
+ STLDeleteContainerPairSecondPointers(cache_map_.begin(), cache_map_.end());
+ cache_map_.clear();
+}
+
+void FontCollectionLoader::EnterStaticCacheMode(const WCHAR* file_name) {
+ cache_writer_.reset(new FontCacheWriter());
+ if (cache_writer_->Create(file_name))
+ create_static_cache_ = true;
+}
+
+void FontCollectionLoader::LeaveStaticCacheMode() {
+ cache_writer_->Close();
+ cache_writer_.reset(NULL);
+ create_static_cache_ = false;
+}
+
+bool FontCollectionLoader::IsBuildStaticCacheMode() {
+ return create_static_cache_;
+}
+
+bool FontCollectionLoader::ValidateAndLoadCacheMap() {
+ BYTE* mem_file_start = static_cast<BYTE*>(cache_->memory());
+ BYTE* mem_file_end = mem_file_start + cache_->mapped_size();
+
+ BYTE* current_ptr = mem_file_start;
+ CacheFileHeader* file_header =
+ reinterpret_cast<CacheFileHeader*>(current_ptr);
+ if (!ValidateFontCacheHeader(file_header))
+ return false;
+
+ current_ptr = current_ptr + sizeof(CacheFileHeader);
+ if (current_ptr >= mem_file_end)
+ return false;
+
+ while ((current_ptr + sizeof(CacheFileEntry)) < mem_file_end) {
+ CacheFileEntry* entry = reinterpret_cast<CacheFileEntry*>(current_ptr);
+ current_ptr += sizeof(CacheFileEntry);
+ WCHAR file_name[kMaxFontFileNameLength];
+ wcsncpy_s(file_name,
+ kMaxFontFileNameLength,
+ entry->file_name,
+ _TRUNCATE);
+ CacheTableEntry* table_entry = NULL;
+ CacheMap::iterator iter = cache_map_.find(file_name);
+ if (iter == cache_map_.end()) {
+ table_entry = new CacheTableEntry();
+ cache_map_[file_name] = table_entry;
+ } else {
+ table_entry = iter->second;
+ }
+ table_entry->file_size = entry->file_size;
+ for (DWORD idx = 0;
+ (current_ptr + sizeof(CacheFileOffsetEntry)) < mem_file_end &&
+ idx < entry->entry_count;
+ idx++) {
+ CacheFileOffsetEntry* offset_entry =
+ reinterpret_cast<CacheFileOffsetEntry*>(current_ptr);
+ CacheTableOffsetEntry table_offset_entry;
+ table_offset_entry.start_offset = offset_entry->start_offset;
+ table_offset_entry.length = offset_entry->length;
+ table_offset_entry.inside_file_ptr =
+ current_ptr + sizeof(CacheFileOffsetEntry);
+ table_entry->offset_entries.push_back(table_offset_entry);
+ current_ptr += sizeof(CacheFileOffsetEntry);
+ current_ptr += offset_entry->length;
+ }
+ }
+
+ return true;
+}
+
+void* FontCollectionLoader::GetCachedFragment(UINT32 font_key,
+ UINT64 start_offset,
+ UINT64 length) {
+ UINT64 just_past_end = start_offset + length;
+ CacheMap::iterator iter = cache_map_.find(
+ GetFontNameFromKey(font_key).c_str());
+ if (iter != cache_map_.end()) {
+ CacheTableEntry* entry = iter->second;
+ OffsetVector::iterator offset_iter = entry->offset_entries.begin();
+ while (offset_iter != entry->offset_entries.end()) {
+ UINT64 available_just_past_end =
+ offset_iter->start_offset + offset_iter->length;
+ if (offset_iter->start_offset <= start_offset &&
+ just_past_end <= available_just_past_end) {
+ return offset_iter->inside_file_ptr +
+ (start_offset - offset_iter->start_offset);
+ }
+ offset_iter++;
+ }
+ }
+ return NULL;
+}
+
+UINT64 FontCollectionLoader::GetCachedFileSize(UINT32 font_key) {
+ CacheMap::iterator iter = cache_map_.find(
+ GetFontNameFromKey(font_key).c_str());
+ if (iter != cache_map_.end()) {
+ return iter->second->file_size;
+ }
+ return 0;
+}
+
+FontCacheWriter* FontCollectionLoader::GetFontCacheWriter() {
+ return cache_writer_.get();
+}
+
+} // namespace
+
+namespace content {
+
+const char kFontCacheSharedSectionName[] = "ChromeDWriteFontCache";
+
+mswr::ComPtr<IDWriteFontCollection> g_font_collection;
+
+IDWriteFontCollection* GetCustomFontCollection(IDWriteFactory* factory) {
+ if (g_font_collection.Get() != NULL)
+ return g_font_collection.Get();
+
+ TRACE_EVENT0("startup", "content::GetCustomFontCollection");
+
+ base::TimeTicks start_tick = base::TimeTicks::Now();
+
+ FontCollectionLoader::Initialize(factory);
+
+ bool cache_file_loaded = g_font_loader->LoadCacheFile();
+
+ // Arbitrary threshold to stop loading enormous number of fonts. Usual
+ // side effect of loading large number of fonts results in renderer getting
+ // killed as it appears to hang.
+ const UINT32 kMaxFontThreshold = 1750;
+ HRESULT hr = E_FAIL;
+ if (cache_file_loaded ||
+ g_font_loader->GetFontMapSize() < kMaxFontThreshold) {
+ g_font_loader->EnableCollectionBuildingMode(true);
+ hr = factory->CreateCustomFontCollection(
+ g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf());
+ g_font_loader->UnloadCacheFile();
+ g_font_loader->EnableCollectionBuildingMode(false);
+ }
+ bool loading_restricted = false;
+ if (FAILED(hr) || !g_font_collection.Get()) {
+ loading_restricted = true;
+ // We will try here just one more time with restricted font set.
+ g_font_loader->LoadRestrictedFontList();
+ hr = factory->CreateCustomFontCollection(
+ g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf());
+ }
+
+ base::TimeDelta time_delta = base::TimeTicks::Now() - start_tick;
+ int64_t delta = time_delta.ToInternalValue();
+ base::debug::Alias(&delta);
+ UINT32 size = g_font_loader->GetFontMapSize();
+ base::debug::Alias(&size);
+ base::debug::Alias(&loading_restricted);
+
+ CHECK(SUCCEEDED(hr));
+ CHECK(g_font_collection.Get() != NULL);
+
+ if (cache_file_loaded)
+ UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime.Cached", time_delta);
+ else
+ UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime", time_delta);
+
+ base::debug::ClearCrashKey(kFontKeyName);
+
+ return g_font_collection.Get();
+}
+
+bool BuildFontCacheInternal(const WCHAR* file_name) {
+ typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc;
+ HMODULE dwrite_dll = LoadLibraryW(L"dwrite.dll");
+ if (!dwrite_dll) {
+ DWORD load_library_get_last_error = GetLastError();
+ base::debug::Alias(&dwrite_dll);
+ base::debug::Alias(&load_library_get_last_error);
+ CHECK(false);
+ }
+
+ DWriteCreateFactoryProc dwrite_create_factory_proc =
+ reinterpret_cast<DWriteCreateFactoryProc>(
+ GetProcAddress(dwrite_dll, "DWriteCreateFactory"));
+
+ if (!dwrite_create_factory_proc) {
+ DWORD get_proc_address_get_last_error = GetLastError();
+ base::debug::Alias(&dwrite_create_factory_proc);
+ base::debug::Alias(&get_proc_address_get_last_error);
+ CHECK(false);
+ }
+
+ mswr::ComPtr<IDWriteFactory> factory;
+
+ CHECK(SUCCEEDED(
+ dwrite_create_factory_proc(
+ DWRITE_FACTORY_TYPE_ISOLATED,
+ __uuidof(IDWriteFactory),
+ reinterpret_cast<IUnknown**>(factory.GetAddressOf()))));
+
+ base::TimeTicks start_tick = base::TimeTicks::Now();
+
+ FontCollectionLoader::Initialize(factory.Get());
+
+ g_font_loader->EnterStaticCacheMode(file_name);
+
+ mswr::ComPtr<IDWriteFontCollection> font_collection;
+
+ HRESULT hr = E_FAIL;
+ g_font_loader->EnableCollectionBuildingMode(true);
+ hr = factory->CreateCustomFontCollection(
+ g_font_loader.Get(), NULL, 0, font_collection.GetAddressOf());
+ g_font_loader->EnableCollectionBuildingMode(false);
+
+ bool loading_restricted = false;
+ if (FAILED(hr) || !font_collection.Get()) {
+ loading_restricted = true;
+ // We will try here just one more time with restricted font set.
+ g_font_loader->LoadRestrictedFontList();
+ hr = factory->CreateCustomFontCollection(
+ g_font_loader.Get(), NULL, 0, font_collection.GetAddressOf());
+ }
+
+ g_font_loader->LeaveStaticCacheMode();
+
+ base::TimeDelta time_delta = base::TimeTicks::Now() - start_tick;
+ int64_t delta = time_delta.ToInternalValue();
+ base::debug::Alias(&delta);
+ UINT32 size = g_font_loader->GetFontMapSize();
+ base::debug::Alias(&size);
+ base::debug::Alias(&loading_restricted);
+
+ CHECK(SUCCEEDED(hr));
+ CHECK(font_collection.Get() != NULL);
+
+ base::debug::ClearCrashKey(kFontKeyName);
+
+ return true;
+}
+
+bool ValidateFontCacheFile(base::File* file) {
+ DCHECK(file != NULL);
+ CacheFileHeader file_header;
+ if (file->Read(0, reinterpret_cast<char*>(&file_header), sizeof(file_header))
+ == -1) {
+ return false;
+ }
+ return ValidateFontCacheHeader(&file_header);
+}
+
+bool LoadFontCache(const base::FilePath& path) {
+ scoped_ptr<base::File> file(new base::File(path,
+ base::File::FLAG_OPEN | base::File::FLAG_READ));
+ if (!file->IsValid())
+ return false;
+
+ if (!ValidateFontCacheFile(file.get()))
+ return false;
+
+ base::string16 name(base::ASCIIToUTF16(content::kFontCacheSharedSectionName));
+ name.append(base::UintToString16(base::GetCurrentProcId()));
+ HANDLE mapping = ::CreateFileMapping(
+ file->GetPlatformFile(),
+ NULL,
+ PAGE_READONLY,
+ 0,
+ 0,
+ name.c_str());
+ if (mapping == INVALID_HANDLE_VALUE)
+ return false;
+
+ if (::GetLastError() == ERROR_ALREADY_EXISTS) {
+ CloseHandle(mapping);
+ // We crash here, as no one should have created this mapping except Chrome.
+ CHECK(false);
+ return false;
+ }
+
+ DCHECK(!g_shared_font_cache.IsValid());
+ g_shared_font_cache.Set(mapping);
+
+ return true;
+}
+
+bool BuildFontCache(const base::FilePath& file) {
+ return BuildFontCacheInternal(file.value().c_str());
+}
+
+bool ShouldUseDirectWriteFontProxyFieldTrial() {
+ return base::StartsWith(
+ base::FieldTrialList::FindFullName("DirectWriteFontProxy"),
+ "UseDirectWriteFontProxy", base::CompareCase::SENSITIVE);
+}
+
+} // namespace content
« no previous file with comments | « content/child/font_warmup_win.cc ('k') | content/common/dwrite_font_platform_win_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698