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 |