Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / common / dwrite_font_platform_win.cc
blobf2b4bd93b6043f4e0dadd5db27c8b9cc3e4b4540
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/public/common/dwrite_font_platform_win.h"
7 #include <windows.h>
9 #include <dwrite.h>
10 #include <map>
11 #include <string>
12 #include <utility>
13 #include <vector>
14 #include <wrl/implements.h>
15 #include <wrl/wrappers/corewrappers.h>
17 #include "base/command_line.h"
18 #include "base/debug/alias.h"
19 #include "base/debug/crash_logging.h"
20 #include "base/files/file_enumerator.h"
21 #include "base/files/file_path.h"
22 #include "base/files/file_util.h"
23 #include "base/files/memory_mapped_file.h"
24 #include "base/memory/scoped_ptr.h"
25 #include "base/memory/shared_memory.h"
26 #include "base/metrics/field_trial.h"
27 #include "base/metrics/histogram.h"
28 #include "base/path_service.h"
29 #include "base/process/process_handle.h"
30 #include "base/stl_util.h"
31 #include "base/strings/string_number_conversions.h"
32 #include "base/strings/string_util.h"
33 #include "base/strings/utf_string_conversions.h"
34 #include "base/synchronization/lock.h"
35 #include "base/time/time.h"
36 #include "base/win/registry.h"
37 #include "base/win/scoped_comptr.h"
38 #include "content/public/common/content_switches.h"
40 namespace {
42 // Font Cache implementation short story:
43 // Due to our sandboxing restrictions, we cannot connect to Windows font cache
44 // service from Renderer and need to use DirectWrite isolated font loading
45 // mechanism.
46 // DirectWrite needs to be initialized before any of the API could be used.
47 // During initialization DirectWrite loads all font files and populates
48 // internal cache, we refer this phase as enumeration and we are trying
49 // to optimize this phase in our cache approach. Using cache during
50 // initialization will help improve on startup latency in each renderer
51 // instance.
52 // During enumeration DirectWrite reads various fragments from .ttf/.ttc
53 // font files. Our assumption is that these fragments are being read to
54 // cache information such as font families, supported sizes etc.
55 // For reading fragments DirectWrite calls ReadFragment of our FontFileStream
56 // implementation with parameters start_offset and length. We cache these
57 // parameters along with associated data chunk.
58 // Here is small example of how segments are read
59 // start_offset: 0, length: 16
60 // start_offset: 0, length: 12
61 // start_offset: 0, length: 117
62 // For better cache management we collapse segments if they overlap or are
63 // adjacent.
65 namespace mswr = Microsoft::WRL;
67 const char kFontKeyName[] = "font_key_name";
69 // We use this value to determine whether to cache file fragments
70 // or not. In our trials we observed that for some font files
71 // direct write ends up reading almost entire file during enumeration
72 // phase. If we don't use this percentile formula we will end up
73 // increasing significant cache size by caching entire file contents
74 // for some of the font files.
75 const double kMaxPercentileOfFontFileSizeToCache = 0.6;
77 // With current implementation we map entire shared section into memory during
78 // renderer startup. This causes increase in working set of Chrome. As first
79 // step we want to see if caching is really improving any performance for our
80 // users, so we are putting arbitrary limit on cache file size. There are
81 // multiple ways we can tune our working size, like mapping only required part
82 // of section at any given time.
83 const double kArbitraryCacheFileSizeLimit = (30 * 1024 * 1024);
85 // We have chosen current font file length arbitrarily. In our logic
86 // if we don't find file we are looking for in cache we end up loading
87 // that file directly from system fonts folder.
88 const unsigned int kMaxFontFileNameLength = 34;
90 const DWORD kCacheFileVersion = 103;
91 const DWORD kFileSignature = 0x4D4F5243; // CROM
92 const DWORD kMagicCompletionSignature = 0x454E4F44; // DONE
94 const DWORD kUndefinedDWORDS = 36;
96 // Make sure that all structure sizes align with 8 byte boundary otherwise
97 // dr. memory test may complain.
98 #pragma pack(push, 8)
99 // Cache file header, includes signature, completion bits and version.
100 struct CacheFileHeader {
101 CacheFileHeader() {
102 file_signature = kFileSignature;
103 magic_completion_signature = 0;
104 version = kCacheFileVersion;
105 ::ZeroMemory(undefined, sizeof(undefined));
108 DWORD file_signature;
109 DWORD magic_completion_signature;
110 DWORD version;
111 BYTE undefined[kUndefinedDWORDS];
114 // Entry for a particular font file within this cache.
115 struct CacheFileEntry {
116 CacheFileEntry() {
117 file_size = 0;
118 entry_count = 0;
119 ::ZeroMemory(file_name, sizeof(file_name));
122 UINT64 file_size;
123 DWORD entry_count;
124 wchar_t file_name[kMaxFontFileNameLength];
127 // Offsets or data chunks that are cached for particular font file.
128 struct CacheFileOffsetEntry {
129 CacheFileOffsetEntry() {
130 start_offset = 0;
131 length = 0;
134 UINT64 start_offset;
135 UINT64 length;
136 /* BYTE blob_[]; // Place holder for the blob that follows. */
138 #pragma pack(pop)
140 bool ValidateFontCacheHeader(CacheFileHeader* header) {
141 return (header->file_signature == kFileSignature &&
142 header->magic_completion_signature == kMagicCompletionSignature &&
143 header->version == kCacheFileVersion);
146 class FontCacheWriter;
148 // This class implements main interface required for loading custom font
149 // collection as specified by DirectWrite. We also use this class for storing
150 // some state information as this is one of the centralized entity.
151 class FontCollectionLoader
152 : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
153 IDWriteFontCollectionLoader> {
154 public:
155 FontCollectionLoader()
156 : in_collection_building_mode_(false),
157 create_static_cache_(false) {}
159 ~FontCollectionLoader() override;
161 HRESULT RuntimeClassInitialize() {
162 return S_OK;
165 // IDWriteFontCollectionLoader methods.
166 HRESULT STDMETHODCALLTYPE
167 CreateEnumeratorFromKey(IDWriteFactory* factory,
168 void const* key,
169 UINT32 key_size,
170 IDWriteFontFileEnumerator** file_enumerator) override;
172 // Does all the initialization for required loading fonts from registry.
173 static HRESULT Initialize(IDWriteFactory* factory);
175 // Returns font cache map size.
176 UINT32 GetFontMapSize();
178 // Returns font name string when given font index.
179 base::string16 GetFontNameFromKey(UINT32 idx);
181 // Loads internal structure with fonts from registry.
182 bool LoadFontListFromRegistry();
184 // Loads restricted web safe fonts as fallback method to registry fonts.
185 bool LoadRestrictedFontList();
187 // Puts class in collection building mode. In collection building mode
188 // we use static cache if it is available as a look aside buffer.
189 void EnableCollectionBuildingMode(bool enable);
191 // Returns current state of collection building.
192 bool InCollectionBuildingMode();
194 // Loads static cache file.
195 bool LoadCacheFile();
197 // Unloads cache file and related data.
198 void UnloadCacheFile();
200 // Puts class in static cache creating mode. In this mode we record all
201 // direct write requests and store chunks of font data.
202 void EnterStaticCacheMode(const WCHAR* file_name);
204 // Gets out of static cache building mode.
205 void LeaveStaticCacheMode();
207 // Returns if class is currently in static cache building mode.
208 bool IsBuildStaticCacheMode();
210 // Validates cache file for consistency.
211 bool ValidateCacheFile(base::File* file);
213 private:
214 // Structure to represent each chunk within font file that we load in memory.
215 struct CacheTableOffsetEntry {
216 UINT64 start_offset;
217 UINT64 length;
218 BYTE* inside_file_ptr;
221 typedef std::vector<CacheTableOffsetEntry> OffsetVector;
223 // Structure representing each font entry with cache.
224 struct CacheTableEntry {
225 UINT64 file_size;
226 OffsetVector offset_entries;
229 public:
230 // Returns whether file we have particular font entry within cache or not.
231 bool IsFileCached(UINT32 font_key);
232 // Returns cache fragment corresponding to specific font key.
233 void* GetCachedFragment(UINT32 font_key, UINT64 start_offset, UINT64 length);
234 // Returns actual font file size at the time of caching.
235 UINT64 GetCachedFileSize(UINT32 font_key);
237 // Returns instance of font cache writer. This class manages actual font
238 // file format.
239 FontCacheWriter* GetFontCacheWriter();
241 private:
242 // Functions validates and loads cache into internal map.
243 bool ValidateAndLoadCacheMap();
245 mswr::ComPtr<IDWriteFontFileLoader> file_loader_;
247 std::vector<base::string16> reg_fonts_;
248 bool in_collection_building_mode_;
249 bool create_static_cache_;
250 scoped_ptr<base::SharedMemory> cache_;
251 scoped_ptr<FontCacheWriter> cache_writer_;
253 typedef std::map<base::string16, CacheTableEntry*> CacheMap;
254 CacheMap cache_map_;
256 DISALLOW_COPY_AND_ASSIGN(FontCollectionLoader);
259 mswr::ComPtr<FontCollectionLoader> g_font_loader;
260 base::win::ScopedHandle g_shared_font_cache;
262 // Class responsible for handling font cache file format details as well as
263 // tracking various cache region requests by direct write.
264 class FontCacheWriter {
265 public:
266 FontCacheWriter() : count_font_entries_ignored_(0), cookie_counter_(0) {}
268 ~FontCacheWriter() {
269 if (static_cache_.get()) {
270 static_cache_->Close();
274 public:
275 // Holds data related to individual region as requested by direct write.
276 struct CacheRegion {
277 UINT64 start_offset;
278 UINT64 length;
279 const BYTE* ptr;
280 /* BYTE blob_[]; // Place holder for the blob that follows. */
283 // Function to create static font cache file.
284 bool Create(const wchar_t* file_name) {
285 static_cache_.reset(new base::File(base::FilePath(file_name),
286 base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE |
287 base::File::FLAG_EXCLUSIVE_WRITE));
288 if (!static_cache_->IsValid()) {
289 static_cache_.reset();
290 return false;
292 CacheFileHeader header;
294 // At offset 0 write cache version
295 static_cache_->Write(0,
296 reinterpret_cast<const char*>(&header),
297 sizeof(header));
299 static_cache_->Flush();
300 return true;
303 // Closes static font cache file. Also writes completion signature to mark
304 // it as completely written.
305 void Close() {
306 if (static_cache_.get()) {
307 CacheFileHeader header;
308 header.magic_completion_signature = kMagicCompletionSignature;
309 // At offset 0 write cache version
310 int bytes_written = static_cache_->Write(0,
311 reinterpret_cast<const char*>(&header),
312 sizeof(header));
313 DCHECK_NE(bytes_written, -1);
315 UMA_HISTOGRAM_MEMORY_KB("DirectWrite.Fonts.BuildCache.File.Size",
316 static_cache_->GetLength() / 1024);
318 UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.BuildCache.Ignored",
319 count_font_entries_ignored_);
321 static_cache_->Close();
322 static_cache_.reset(NULL);
326 private:
327 typedef std::vector<CacheRegion> RegionVector;
329 // Structure to track various regions requested by direct write for particular
330 // font file.
331 struct FontEntryInternal {
332 FontEntryInternal(const wchar_t* name, UINT64 size)
333 : file_name(name),
334 file_size(size) {
337 base::string16 file_name;
338 UINT64 file_size;
339 RegionVector regions;
342 public:
343 // Starts up new font entry to be tracked, returns cookie to identify this
344 // particular entry.
345 UINT NewFontEntry(const wchar_t* file_name, UINT64 file_size) {
346 base::AutoLock lock(lock_);
347 UINT old_counter = cookie_counter_;
348 FontEntryInternal* font_entry = new FontEntryInternal(file_name, file_size);
349 cookie_map_[cookie_counter_].reset(font_entry);
350 cookie_counter_++;
351 return old_counter;
354 // AddRegion function lets caller add various regions to be cached for
355 // particular font file. Once enumerating that particular font file is done
356 // (based on uniquely identifying cookie) changes could be committed using
357 // CommitFontEntry
358 bool AddRegion(UINT64 cookie, UINT64 start, UINT64 length, const BYTE* ptr) {
359 base::AutoLock lock(lock_);
360 if (cookie_map_.find(cookie) == cookie_map_.end())
361 return false;
362 RegionVector& regions = cookie_map_[cookie].get()->regions;
363 CacheRegion region;
364 region.start_offset = start;
365 region.length = length;
366 region.ptr = ptr;
367 regions.push_back(region);
368 return true;
371 // Function which commits after merging all collected regions into cache file.
372 bool CommitFontEntry(UINT cookie) {
373 base::AutoLock lock(lock_);
374 if (cookie_map_.find(cookie) == cookie_map_.end())
375 return false;
377 // We will skip writing entries beyond allowed limit. Following condition
378 // doesn't enforce hard file size. We need to write complete font entry.
379 int64 length = static_cache_->GetLength();
380 if (length == -1 || length >= kArbitraryCacheFileSizeLimit) {
381 count_font_entries_ignored_++;
382 return false;
385 FontEntryInternal* font_entry = cookie_map_[cookie].get();
386 RegionVector& regions = font_entry->regions;
387 std::sort(regions.begin(), regions.end(), SortCacheRegions);
389 // At this point, we have collected all regions to be cached. These regions
390 // are tuples of start, length, data for particular data segment.
391 // These tuples can overlap.
392 // e.g. (0, 12, data), (0, 117, data), (21, 314, data), (335, 15, data)
393 // In this case as you can see first three segments overlap and
394 // 4th is adjacent. If we cache them individually then we will end up
395 // caching duplicate data, so we merge these segments together to find
396 // superset for the cache. In above example our algorithm should
397 // produce (cache) single segment starting at offset 0 with length 350.
398 RegionVector merged_regions;
399 RegionVector::iterator iter;
400 int idx = 0;
401 for (iter = regions.begin(); iter != regions.end(); iter++) {
402 if (iter == regions.begin()) {
403 merged_regions.push_back(*iter);
404 continue;
406 CacheRegion& base_region = merged_regions[idx];
407 if (IsOverlap(&base_region, &(*iter))) {
408 UINT64 end1 = base_region.start_offset + base_region.length;
409 UINT64 end2 = iter->start_offset + iter->length;
410 if (base_region.start_offset > iter->start_offset) {
411 base_region.start_offset = iter->start_offset;
412 base_region.ptr = iter->ptr;
414 base_region.length = std::max(end1, end2) - base_region.start_offset;
415 } else {
416 merged_regions.push_back(*iter);
417 idx++;
421 UINT64 total_merged_cache_in_bytes = 0;
422 for (iter = merged_regions.begin(); iter != merged_regions.end(); iter++) {
423 total_merged_cache_in_bytes += iter->length;
426 // We want to adjust following parameter based on experiments. But general
427 // logic here is that if we are going to end up caching most of the contents
428 // for a file (e.g. simsunb.ttf > 90%) then we should avoid caching that
429 // file.
430 double percentile = static_cast<double>(total_merged_cache_in_bytes) /
431 font_entry->file_size;
432 if (percentile > kMaxPercentileOfFontFileSizeToCache) {
433 count_font_entries_ignored_++;
434 return false;
437 CacheFileEntry entry;
438 wcsncpy_s(entry.file_name, kMaxFontFileNameLength,
439 font_entry->file_name.c_str(), _TRUNCATE);
440 entry.file_size = font_entry->file_size;
441 entry.entry_count = merged_regions.size();
442 static_cache_->WriteAtCurrentPos(
443 reinterpret_cast<const char*>(&entry),
444 sizeof(entry));
445 for (iter = merged_regions.begin(); iter != merged_regions.end(); iter++) {
446 CacheFileOffsetEntry offset_entry;
447 offset_entry.start_offset = iter->start_offset;
448 offset_entry.length = iter->length;
449 static_cache_->WriteAtCurrentPos(
450 reinterpret_cast<const char*>(&offset_entry),
451 sizeof(offset_entry));
452 static_cache_->WriteAtCurrentPos(
453 reinterpret_cast<const char*>(iter->ptr),
454 iter->length);
456 return true;
459 private:
460 // This is the count of font entries that we reject based on size to be
461 // cached.
462 unsigned int count_font_entries_ignored_;
463 scoped_ptr<base::File> static_cache_;
464 std::map<UINT, scoped_ptr<FontEntryInternal>> cookie_map_;
465 UINT cookie_counter_;
467 // Lock is required to protect internal data structures and access to file,
468 // According to MSDN documentation on ReadFileFragment and based on our
469 // experiments so far, there is possibility of ReadFileFragment getting called
470 // from multiple threads.
471 base::Lock lock_;
473 // Function checks if two regions overlap or are adjacent.
474 bool IsOverlap(CacheRegion* region1, CacheRegion* region2) {
475 return
476 !((region1->start_offset + region1->length) < region2->start_offset ||
477 region1->start_offset > (region2->start_offset + region2->length));
480 // Function to sort cached regions.
481 static bool SortCacheRegions(const CacheRegion& region1,
482 const CacheRegion& region2) {
483 return
484 region1.start_offset == region2.start_offset ?
485 region1.length < region2.length :
486 region1.start_offset < region2.start_offset;
489 DISALLOW_COPY_AND_ASSIGN(FontCacheWriter);
492 // Class implements IDWriteFontFileStream interface as required by direct write.
493 class FontFileStream
494 : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
495 IDWriteFontFileStream> {
496 public:
497 // IDWriteFontFileStream methods.
498 HRESULT STDMETHODCALLTYPE ReadFileFragment(
499 void const** fragment_start,
500 UINT64 file_offset,
501 UINT64 fragment_size,
502 void** context) override {
503 if (cached_data_) {
504 *fragment_start = g_font_loader->GetCachedFragment(font_key_,
505 file_offset,
506 fragment_size);
507 if (*fragment_start == NULL) {
508 DCHECK(false);
510 *context = NULL;
511 return *fragment_start != NULL ? S_OK : E_FAIL;
513 if (!memory_.get() || !memory_->IsValid() ||
514 file_offset >= memory_->length() ||
515 (file_offset + fragment_size) > memory_->length())
516 return E_FAIL;
518 *fragment_start = static_cast<BYTE const*>(memory_->data()) +
519 static_cast<size_t>(file_offset);
520 *context = NULL;
521 if (g_font_loader->IsBuildStaticCacheMode()) {
522 FontCacheWriter* cache_writer = g_font_loader->GetFontCacheWriter();
523 cache_writer->AddRegion(writer_cookie_,
524 file_offset,
525 fragment_size,
526 static_cast<const BYTE*>(*fragment_start));
528 return S_OK;
531 void STDMETHODCALLTYPE ReleaseFileFragment(void* context) override {}
533 HRESULT STDMETHODCALLTYPE GetFileSize(UINT64* file_size) override {
534 if (cached_data_) {
535 *file_size = g_font_loader->GetCachedFileSize(font_key_);
536 return S_OK;
539 if (!memory_.get() || !memory_->IsValid())
540 return E_FAIL;
542 *file_size = memory_->length();
543 return S_OK;
546 HRESULT STDMETHODCALLTYPE GetLastWriteTime(UINT64* last_write_time) override {
547 if (cached_data_) {
548 *last_write_time = 0;
549 return S_OK;
552 if (!memory_.get() || !memory_->IsValid())
553 return E_FAIL;
555 // According to MSDN article http://goo.gl/rrSYzi the "last modified time"
556 // is used by DirectWrite font selection algorithms to determine whether
557 // one font resource is more up to date than another one.
558 // So by returning 0 we are assuming that it will treat all fonts to be
559 // equally up to date.
560 // TODO(shrikant): We should further investigate this.
561 *last_write_time = 0;
562 return S_OK;
565 FontFileStream() : font_key_(0), cached_data_(false) {}
567 HRESULT RuntimeClassInitialize(UINT32 font_key) {
568 if (g_font_loader->InCollectionBuildingMode() &&
569 g_font_loader->IsFileCached(font_key)) {
570 cached_data_ = true;
571 font_key_ = font_key;
572 return S_OK;
575 base::FilePath path;
576 PathService::Get(base::DIR_WINDOWS_FONTS, &path);
577 base::string16 font_key_name(g_font_loader->GetFontNameFromKey(font_key));
578 path = path.Append(font_key_name.c_str());
579 memory_.reset(new base::MemoryMappedFile());
581 // Put some debug information on stack.
582 WCHAR font_name[MAX_PATH];
583 path.value().copy(font_name, arraysize(font_name));
584 base::debug::Alias(font_name);
586 if (!memory_->Initialize(path)) {
587 memory_.reset();
588 return E_FAIL;
591 font_key_ = font_key;
593 base::debug::SetCrashKeyValue(kFontKeyName,
594 base::WideToUTF8(font_key_name));
596 if (g_font_loader->IsBuildStaticCacheMode()) {
597 FontCacheWriter* cache_writer = g_font_loader->GetFontCacheWriter();
598 writer_cookie_ = cache_writer->NewFontEntry(font_key_name.c_str(),
599 memory_->length());
601 return S_OK;
604 ~FontFileStream() override {
605 if (g_font_loader->IsBuildStaticCacheMode()) {
606 FontCacheWriter* cache_writer = g_font_loader->GetFontCacheWriter();
607 cache_writer->CommitFontEntry(writer_cookie_);
611 private:
612 UINT32 font_key_;
613 scoped_ptr<base::MemoryMappedFile> memory_;
614 bool cached_data_;
615 UINT writer_cookie_;
617 DISALLOW_COPY_AND_ASSIGN(FontFileStream);
620 // Implements IDWriteFontFileLoader as required by FontFileLoader.
621 class FontFileLoader
622 : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
623 IDWriteFontFileLoader> {
624 public:
625 // IDWriteFontFileLoader methods.
626 HRESULT STDMETHODCALLTYPE
627 CreateStreamFromKey(void const* ref_key,
628 UINT32 ref_key_size,
629 IDWriteFontFileStream** stream) override {
630 if (ref_key_size != sizeof(UINT32))
631 return E_FAIL;
633 UINT32 font_key = *static_cast<const UINT32*>(ref_key);
634 mswr::ComPtr<FontFileStream> font_stream;
635 HRESULT hr = mswr::MakeAndInitialize<FontFileStream>(&font_stream,
636 font_key);
637 if (SUCCEEDED(hr)) {
638 *stream = font_stream.Detach();
639 return S_OK;
641 return E_FAIL;
644 FontFileLoader() {}
645 ~FontFileLoader() override {}
647 private:
648 DISALLOW_COPY_AND_ASSIGN(FontFileLoader);
651 // Implements IDWriteFontFileEnumerator as required by direct write.
652 class FontFileEnumerator
653 : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
654 IDWriteFontFileEnumerator> {
655 public:
656 // IDWriteFontFileEnumerator methods.
657 HRESULT STDMETHODCALLTYPE MoveNext(BOOL* has_current_file) override {
658 *has_current_file = FALSE;
660 if (current_file_)
661 current_file_.ReleaseAndGetAddressOf();
663 if (font_idx_ < g_font_loader->GetFontMapSize()) {
664 HRESULT hr =
665 factory_->CreateCustomFontFileReference(&font_idx_,
666 sizeof(UINT32),
667 file_loader_.Get(),
668 current_file_.GetAddressOf());
669 DCHECK(SUCCEEDED(hr));
670 *has_current_file = TRUE;
671 font_idx_++;
673 return S_OK;
676 HRESULT STDMETHODCALLTYPE
677 GetCurrentFontFile(IDWriteFontFile** font_file) override {
678 if (!current_file_) {
679 *font_file = NULL;
680 return E_FAIL;
683 *font_file = current_file_.Detach();
684 return S_OK;
687 FontFileEnumerator(const void* keys,
688 UINT32 buffer_size,
689 IDWriteFactory* factory,
690 IDWriteFontFileLoader* file_loader)
691 : factory_(factory), file_loader_(file_loader), font_idx_(0) {}
693 ~FontFileEnumerator() override {}
695 mswr::ComPtr<IDWriteFactory> factory_;
696 mswr::ComPtr<IDWriteFontFile> current_file_;
697 mswr::ComPtr<IDWriteFontFileLoader> file_loader_;
698 UINT32 font_idx_;
700 private:
701 DISALLOW_COPY_AND_ASSIGN(FontFileEnumerator);
704 // IDWriteFontCollectionLoader methods.
705 HRESULT STDMETHODCALLTYPE FontCollectionLoader::CreateEnumeratorFromKey(
706 IDWriteFactory* factory,
707 void const* key,
708 UINT32 key_size,
709 IDWriteFontFileEnumerator** file_enumerator) {
710 *file_enumerator = mswr::Make<FontFileEnumerator>(
711 key, key_size, factory, file_loader_.Get()).Detach();
712 return S_OK;
715 // static
716 HRESULT FontCollectionLoader::Initialize(IDWriteFactory* factory) {
717 DCHECK(g_font_loader == NULL);
719 HRESULT result;
720 result = mswr::MakeAndInitialize<FontCollectionLoader>(&g_font_loader);
721 if (FAILED(result) || !g_font_loader) {
722 DCHECK(false);
723 return E_FAIL;
726 CHECK(g_font_loader->LoadFontListFromRegistry());
728 g_font_loader->file_loader_ = mswr::Make<FontFileLoader>().Detach();
730 factory->RegisterFontFileLoader(g_font_loader->file_loader_.Get());
731 factory->RegisterFontCollectionLoader(g_font_loader.Get());
733 return S_OK;
736 FontCollectionLoader::~FontCollectionLoader() {
737 STLDeleteContainerPairSecondPointers(cache_map_.begin(), cache_map_.end());
740 UINT32 FontCollectionLoader::GetFontMapSize() {
741 return reg_fonts_.size();
744 base::string16 FontCollectionLoader::GetFontNameFromKey(UINT32 idx) {
745 DCHECK(idx < reg_fonts_.size());
746 return reg_fonts_[idx];
749 const base::FilePath::CharType* kFontExtensionsToIgnore[] {
750 FILE_PATH_LITERAL(".FON"), // Bitmap or vector
751 FILE_PATH_LITERAL(".PFM"), // Adobe Type 1
752 FILE_PATH_LITERAL(".PFB"), // Adobe Type 1
755 const wchar_t* kFontsToIgnore[] = {
756 // "Gill Sans Ultra Bold" turns into an Ultra Bold weight "Gill Sans" in
757 // DirectWrite, but most users don't have any other weights. The regular
758 // weight font is named "Gill Sans MT", but that ends up in a different
759 // family with that name. On Mac, there's a "Gill Sans" with various weights,
760 // so CSS authors use { 'font-family': 'Gill Sans', 'Gill Sans MT', ... } and
761 // because of the DirectWrite family futzing, they end up with an Ultra Bold
762 // font, when they just wanted "Gill Sans". Mozilla implemented a more
763 // complicated hack where they effectively rename the Ultra Bold font to
764 // "Gill Sans MT Ultra Bold", but because the Ultra Bold font is so ugly
765 // anyway, we simply ignore it. See
766 // http://www.microsoft.com/typography/fonts/font.aspx?FMID=978 for a picture
767 // of the font, and the file name. We also ignore "Gill Sans Ultra Bold
768 // Condensed".
769 L"gilsanub.ttf",
770 L"gillubcd.ttf",
773 bool FontCollectionLoader::LoadFontListFromRegistry() {
774 const wchar_t kFontsRegistry[] =
775 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
776 CHECK(reg_fonts_.empty());
777 base::win::RegKey regkey;
778 if (regkey.Open(HKEY_LOCAL_MACHINE, kFontsRegistry, KEY_READ) !=
779 ERROR_SUCCESS) {
780 return false;
783 base::FilePath system_font_path;
784 PathService::Get(base::DIR_WINDOWS_FONTS, &system_font_path);
786 base::string16 name;
787 base::string16 value;
788 for (DWORD idx = 0; idx < regkey.GetValueCount(); idx++) {
789 if (regkey.GetValueNameAt(idx, &name) == ERROR_SUCCESS &&
790 regkey.ReadValue(name.c_str(), &value) == ERROR_SUCCESS) {
791 base::FilePath path(value.c_str());
792 // We need to check if path in registry is absolute, if it is then
793 // we check if it is same as DIR_WINDOWS_FONTS otherwise we ignore.
794 bool absolute = path.IsAbsolute();
795 if (absolute &&
796 !base::FilePath::CompareEqualIgnoreCase(system_font_path.value(),
797 path.DirName().value())) {
798 continue;
801 // Ignore if path ends with a separator.
802 if (path.EndsWithSeparator())
803 continue;
805 if (absolute)
806 value = path.BaseName().value();
808 bool should_ignore = false;
809 for (const auto& ignore : kFontsToIgnore) {
810 if (base::FilePath::CompareEqualIgnoreCase(value, ignore)) {
811 should_ignore = true;
812 break;
815 // DirectWrite doesn't support bitmap/vector fonts and Adobe type 1
816 // fonts, we will ignore those font extensions.
817 // MSDN article: http://goo.gl/TfCOA
818 if (!should_ignore) {
819 for (const auto& ignore : kFontExtensionsToIgnore) {
820 if (path.MatchesExtension(ignore)) {
821 should_ignore = true;
822 break;
827 if (!should_ignore)
828 reg_fonts_.push_back(value.c_str());
831 UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Loaded", reg_fonts_.size());
832 UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Ignored",
833 regkey.GetValueCount() - reg_fonts_.size());
834 return true;
837 // This list is mainly based on prefs/prefs_tab_helper.cc kFontDefaults.
838 const wchar_t* kRestrictedFontSet[] = {
839 // These are the "Web Safe" fonts.
840 L"times.ttf", // IDS_STANDARD_FONT_FAMILY
841 L"timesbd.ttf", // IDS_STANDARD_FONT_FAMILY
842 L"timesbi.ttf", // IDS_STANDARD_FONT_FAMILY
843 L"timesi.ttf", // IDS_STANDARD_FONT_FAMILY
844 L"cour.ttf", // IDS_FIXED_FONT_FAMILY
845 L"courbd.ttf", // IDS_FIXED_FONT_FAMILY
846 L"courbi.ttf", // IDS_FIXED_FONT_FAMILY
847 L"couri.ttf", // IDS_FIXED_FONT_FAMILY
848 L"consola.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
849 L"consolab.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
850 L"consolai.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
851 L"consolaz.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
852 L"arial.ttf", // IDS_SANS_SERIF_FONT_FAMILY
853 L"arialbd.ttf", // IDS_SANS_SERIF_FONT_FAMILY
854 L"arialbi.ttf", // IDS_SANS_SERIF_FONT_FAMILY
855 L"ariali.ttf", // IDS_SANS_SERIF_FONT_FAMILY
856 L"comic.ttf", // IDS_CURSIVE_FONT_FAMILY
857 L"comicbd.ttf", // IDS_CURSIVE_FONT_FAMILY
858 L"comici.ttf", // IDS_CURSIVE_FONT_FAMILY
859 L"comicz.ttf", // IDS_CURSIVE_FONT_FAMILY
860 L"impact.ttf", // IDS_FANTASY_FONT_FAMILY
861 L"georgia.ttf",
862 L"georgiab.ttf",
863 L"georgiai.ttf",
864 L"georgiaz.ttf",
865 L"trebuc.ttf",
866 L"trebucbd.ttf",
867 L"trebucbi.ttf",
868 L"trebucit.ttf",
869 L"verdana.ttf",
870 L"verdanab.ttf",
871 L"verdanai.ttf",
872 L"verdanaz.ttf",
873 L"segoeui.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
874 L"segoeuib.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
875 L"segoeuii.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
876 L"msgothic.ttc", // IDS_STANDARD_FONT_FAMILY_JAPANESE
877 L"msmincho.ttc", // IDS_SERIF_FONT_FAMILY_JAPANESE
878 L"gulim.ttc", // IDS_FIXED_FONT_FAMILY_KOREAN
879 L"batang.ttc", // IDS_SERIF_FONT_FAMILY_KOREAN
880 L"simsun.ttc", // IDS_STANDARD_FONT_FAMILY_SIMPLIFIED_HAN
881 L"mingliu.ttc", // IDS_SERIF_FONT_FAMILY_TRADITIONAL_HAN
883 // These are from the Blink fallback list.
884 L"david.ttf", // USCRIPT_HEBREW
885 L"davidbd.ttf", // USCRIPT_HEBREW
886 L"euphemia.ttf", // USCRIPT_CANADIAN_ABORIGINAL
887 L"gautami.ttf", // USCRIPT_TELUGU
888 L"gautamib.ttf", // USCRIPT_TELUGU
889 L"latha.ttf", // USCRIPT_TAMIL
890 L"lathab.ttf", // USCRIPT_TAMIL
891 L"mangal.ttf", // USCRIPT_DEVANAGARI
892 L"mangalb.ttf", // USCRIPT_DEVANAGARI
893 L"monbaiti.ttf", // USCRIPT_MONGOLIAN
894 L"mvboli.ttf", // USCRIPT_THAANA
895 L"plantc.ttf", // USCRIPT_CHEROKEE
896 L"raavi.ttf", // USCRIPT_GURMUKHI
897 L"raavib.ttf", // USCRIPT_GURMUKHI
898 L"shruti.ttf", // USCRIPT_GUJARATI
899 L"shrutib.ttf", // USCRIPT_GUJARATI
900 L"sylfaen.ttf", // USCRIPT_GEORGIAN and USCRIPT_ARMENIAN
901 L"tahoma.ttf", // USCRIPT_ARABIC,
902 L"tahomabd.ttf", // USCRIPT_ARABIC,
903 L"tunga.ttf", // USCRIPT_KANNADA
904 L"tungab.ttf", // USCRIPT_KANNADA
905 L"vrinda.ttf", // USCRIPT_BENGALI
906 L"vrindab.ttf", // USCRIPT_BENGALI
909 bool FontCollectionLoader::LoadRestrictedFontList() {
910 reg_fonts_.clear();
911 reg_fonts_.assign(kRestrictedFontSet,
912 kRestrictedFontSet + _countof(kRestrictedFontSet));
913 return true;
916 void FontCollectionLoader::EnableCollectionBuildingMode(bool enable) {
917 in_collection_building_mode_ = enable;
920 bool FontCollectionLoader::InCollectionBuildingMode() {
921 return in_collection_building_mode_;
924 bool FontCollectionLoader::IsFileCached(UINT32 font_key) {
925 if (!cache_.get() || cache_->memory() == NULL) {
926 return false;
928 CacheMap::iterator iter = cache_map_.find(
929 GetFontNameFromKey(font_key).c_str());
930 return iter != cache_map_.end();;
933 bool FontCollectionLoader::LoadCacheFile() {
934 std::string font_cache_handle_string =
935 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
936 switches::kFontCacheSharedHandle);
937 if (font_cache_handle_string.empty())
938 return false;
940 base::SharedMemoryHandle font_cache_handle = NULL;
941 base::StringToUint(font_cache_handle_string,
942 reinterpret_cast<unsigned int*>(&font_cache_handle));
943 DCHECK(font_cache_handle);
945 base::SharedMemory* shared_mem = new base::SharedMemory(
946 font_cache_handle, true);
947 // Map the cache file into memory.
948 shared_mem->Map(0);
950 cache_.reset(shared_mem);
952 if (base::StartsWith(base::FieldTrialList::FindFullName("LightSpeed"),
953 "PrefetchDWriteFontCache",
954 base::CompareCase::SENSITIVE)) {
955 // Prefetch the cache, to avoid unordered IO when it is used.
956 // PrefetchVirtualMemory() is loaded dynamically because it is only
957 // available from Win8.
958 decltype(PrefetchVirtualMemory)* prefetch_virtual_memory =
959 reinterpret_cast<decltype(PrefetchVirtualMemory)*>(::GetProcAddress(
960 ::GetModuleHandle(L"kernel32.dll"), "PrefetchVirtualMemory"));
961 if (prefetch_virtual_memory != NULL) {
962 WIN32_MEMORY_RANGE_ENTRY memory_range;
963 memory_range.VirtualAddress = shared_mem->memory();
964 memory_range.NumberOfBytes = shared_mem->mapped_size();
965 prefetch_virtual_memory(::GetCurrentProcess(), 1, &memory_range, 0);
969 if (!ValidateAndLoadCacheMap()) {
970 cache_.reset();
971 return false;
974 return true;
977 void FontCollectionLoader::UnloadCacheFile() {
978 cache_.reset();
979 STLDeleteContainerPairSecondPointers(cache_map_.begin(), cache_map_.end());
980 cache_map_.clear();
983 void FontCollectionLoader::EnterStaticCacheMode(const WCHAR* file_name) {
984 cache_writer_.reset(new FontCacheWriter());
985 if (cache_writer_->Create(file_name))
986 create_static_cache_ = true;
989 void FontCollectionLoader::LeaveStaticCacheMode() {
990 cache_writer_->Close();
991 cache_writer_.reset(NULL);
992 create_static_cache_ = false;
995 bool FontCollectionLoader::IsBuildStaticCacheMode() {
996 return create_static_cache_;
999 bool FontCollectionLoader::ValidateAndLoadCacheMap() {
1000 BYTE* mem_file_start = static_cast<BYTE*>(cache_->memory());
1001 BYTE* mem_file_end = mem_file_start + cache_->mapped_size();
1003 BYTE* current_ptr = mem_file_start;
1004 CacheFileHeader* file_header =
1005 reinterpret_cast<CacheFileHeader*>(current_ptr);
1006 if (!ValidateFontCacheHeader(file_header))
1007 return false;
1009 current_ptr = current_ptr + sizeof(CacheFileHeader);
1010 if (current_ptr >= mem_file_end)
1011 return false;
1013 while ((current_ptr + sizeof(CacheFileEntry)) < mem_file_end) {
1014 CacheFileEntry* entry = reinterpret_cast<CacheFileEntry*>(current_ptr);
1015 current_ptr += sizeof(CacheFileEntry);
1016 WCHAR file_name[kMaxFontFileNameLength];
1017 wcsncpy_s(file_name,
1018 kMaxFontFileNameLength,
1019 entry->file_name,
1020 _TRUNCATE);
1021 CacheTableEntry* table_entry = NULL;
1022 CacheMap::iterator iter = cache_map_.find(file_name);
1023 if (iter == cache_map_.end()) {
1024 table_entry = new CacheTableEntry();
1025 cache_map_[file_name] = table_entry;
1026 } else {
1027 table_entry = iter->second;
1029 table_entry->file_size = entry->file_size;
1030 for (DWORD idx = 0;
1031 (current_ptr + sizeof(CacheFileOffsetEntry)) < mem_file_end &&
1032 idx < entry->entry_count;
1033 idx++) {
1034 CacheFileOffsetEntry* offset_entry =
1035 reinterpret_cast<CacheFileOffsetEntry*>(current_ptr);
1036 CacheTableOffsetEntry table_offset_entry;
1037 table_offset_entry.start_offset = offset_entry->start_offset;
1038 table_offset_entry.length = offset_entry->length;
1039 table_offset_entry.inside_file_ptr =
1040 current_ptr + sizeof(CacheFileOffsetEntry);
1041 table_entry->offset_entries.push_back(table_offset_entry);
1042 current_ptr += sizeof(CacheFileOffsetEntry);
1043 current_ptr += offset_entry->length;
1047 return true;
1050 void* FontCollectionLoader::GetCachedFragment(UINT32 font_key,
1051 UINT64 start_offset,
1052 UINT64 length) {
1053 UINT64 just_past_end = start_offset + length;
1054 CacheMap::iterator iter = cache_map_.find(
1055 GetFontNameFromKey(font_key).c_str());
1056 if (iter != cache_map_.end()) {
1057 CacheTableEntry* entry = iter->second;
1058 OffsetVector::iterator offset_iter = entry->offset_entries.begin();
1059 while (offset_iter != entry->offset_entries.end()) {
1060 UINT64 available_just_past_end =
1061 offset_iter->start_offset + offset_iter->length;
1062 if (offset_iter->start_offset <= start_offset &&
1063 just_past_end <= available_just_past_end) {
1064 return offset_iter->inside_file_ptr +
1065 (start_offset - offset_iter->start_offset);
1067 offset_iter++;
1070 return NULL;
1073 UINT64 FontCollectionLoader::GetCachedFileSize(UINT32 font_key) {
1074 CacheMap::iterator iter = cache_map_.find(
1075 GetFontNameFromKey(font_key).c_str());
1076 if (iter != cache_map_.end()) {
1077 return iter->second->file_size;
1079 return 0;
1082 FontCacheWriter* FontCollectionLoader::GetFontCacheWriter() {
1083 return cache_writer_.get();
1086 } // namespace
1088 namespace content {
1090 const char kFontCacheSharedSectionName[] = "ChromeDWriteFontCache";
1092 mswr::ComPtr<IDWriteFontCollection> g_font_collection;
1094 IDWriteFontCollection* GetCustomFontCollection(IDWriteFactory* factory) {
1095 if (g_font_collection.Get() != NULL)
1096 return g_font_collection.Get();
1098 base::TimeTicks start_tick = base::TimeTicks::Now();
1100 FontCollectionLoader::Initialize(factory);
1102 bool cache_file_loaded = g_font_loader->LoadCacheFile();
1104 // Arbitrary threshold to stop loading enormous number of fonts. Usual
1105 // side effect of loading large number of fonts results in renderer getting
1106 // killed as it appears to hang.
1107 const UINT32 kMaxFontThreshold = 1750;
1108 HRESULT hr = E_FAIL;
1109 if (cache_file_loaded ||
1110 g_font_loader->GetFontMapSize() < kMaxFontThreshold) {
1111 g_font_loader->EnableCollectionBuildingMode(true);
1112 hr = factory->CreateCustomFontCollection(
1113 g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf());
1114 g_font_loader->UnloadCacheFile();
1115 g_font_loader->EnableCollectionBuildingMode(false);
1117 bool loading_restricted = false;
1118 if (FAILED(hr) || !g_font_collection.Get()) {
1119 loading_restricted = true;
1120 // We will try here just one more time with restricted font set.
1121 g_font_loader->LoadRestrictedFontList();
1122 hr = factory->CreateCustomFontCollection(
1123 g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf());
1126 base::TimeDelta time_delta = base::TimeTicks::Now() - start_tick;
1127 int64 delta = time_delta.ToInternalValue();
1128 base::debug::Alias(&delta);
1129 UINT32 size = g_font_loader->GetFontMapSize();
1130 base::debug::Alias(&size);
1131 base::debug::Alias(&loading_restricted);
1133 CHECK(SUCCEEDED(hr));
1134 CHECK(g_font_collection.Get() != NULL);
1136 if (cache_file_loaded)
1137 UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime.Cached", time_delta);
1138 else
1139 UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime", time_delta);
1141 base::debug::ClearCrashKey(kFontKeyName);
1143 return g_font_collection.Get();
1146 bool BuildFontCacheInternal(const WCHAR* file_name) {
1147 typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc;
1148 HMODULE dwrite_dll = LoadLibraryW(L"dwrite.dll");
1149 if (!dwrite_dll) {
1150 DWORD load_library_get_last_error = GetLastError();
1151 base::debug::Alias(&dwrite_dll);
1152 base::debug::Alias(&load_library_get_last_error);
1153 CHECK(false);
1156 DWriteCreateFactoryProc dwrite_create_factory_proc =
1157 reinterpret_cast<DWriteCreateFactoryProc>(
1158 GetProcAddress(dwrite_dll, "DWriteCreateFactory"));
1160 if (!dwrite_create_factory_proc) {
1161 DWORD get_proc_address_get_last_error = GetLastError();
1162 base::debug::Alias(&dwrite_create_factory_proc);
1163 base::debug::Alias(&get_proc_address_get_last_error);
1164 CHECK(false);
1167 mswr::ComPtr<IDWriteFactory> factory;
1169 CHECK(SUCCEEDED(
1170 dwrite_create_factory_proc(
1171 DWRITE_FACTORY_TYPE_ISOLATED,
1172 __uuidof(IDWriteFactory),
1173 reinterpret_cast<IUnknown**>(factory.GetAddressOf()))));
1175 base::TimeTicks start_tick = base::TimeTicks::Now();
1177 FontCollectionLoader::Initialize(factory.Get());
1179 g_font_loader->EnterStaticCacheMode(file_name);
1181 mswr::ComPtr<IDWriteFontCollection> font_collection;
1183 HRESULT hr = E_FAIL;
1184 g_font_loader->EnableCollectionBuildingMode(true);
1185 hr = factory->CreateCustomFontCollection(
1186 g_font_loader.Get(), NULL, 0, font_collection.GetAddressOf());
1187 g_font_loader->EnableCollectionBuildingMode(false);
1189 bool loading_restricted = false;
1190 if (FAILED(hr) || !font_collection.Get()) {
1191 loading_restricted = true;
1192 // We will try here just one more time with restricted font set.
1193 g_font_loader->LoadRestrictedFontList();
1194 hr = factory->CreateCustomFontCollection(
1195 g_font_loader.Get(), NULL, 0, font_collection.GetAddressOf());
1198 g_font_loader->LeaveStaticCacheMode();
1200 base::TimeDelta time_delta = base::TimeTicks::Now() - start_tick;
1201 int64 delta = time_delta.ToInternalValue();
1202 base::debug::Alias(&delta);
1203 UINT32 size = g_font_loader->GetFontMapSize();
1204 base::debug::Alias(&size);
1205 base::debug::Alias(&loading_restricted);
1207 CHECK(SUCCEEDED(hr));
1208 CHECK(font_collection.Get() != NULL);
1210 base::debug::ClearCrashKey(kFontKeyName);
1212 return true;
1215 bool ValidateFontCacheFile(base::File* file) {
1216 DCHECK(file != NULL);
1217 CacheFileHeader file_header;
1218 if (file->Read(0, reinterpret_cast<char*>(&file_header), sizeof(file_header))
1219 == -1) {
1220 return false;
1222 return ValidateFontCacheHeader(&file_header);
1225 bool LoadFontCache(const base::FilePath& path) {
1226 scoped_ptr<base::File> file(new base::File(path,
1227 base::File::FLAG_OPEN | base::File::FLAG_READ));
1228 if (!file->IsValid())
1229 return false;
1231 if (!ValidateFontCacheFile(file.get()))
1232 return false;
1234 base::string16 name(base::ASCIIToUTF16(content::kFontCacheSharedSectionName));
1235 name.append(base::UintToString16(base::GetCurrentProcId()));
1236 HANDLE mapping = ::CreateFileMapping(
1237 file->GetPlatformFile(),
1238 NULL,
1239 PAGE_READONLY,
1242 name.c_str());
1243 if (mapping == INVALID_HANDLE_VALUE)
1244 return false;
1246 if (::GetLastError() == ERROR_ALREADY_EXISTS) {
1247 CloseHandle(mapping);
1248 // We crash here, as no one should have created this mapping except Chrome.
1249 CHECK(false);
1250 return false;
1253 DCHECK(!g_shared_font_cache.IsValid());
1254 g_shared_font_cache.Set(mapping);
1256 return true;
1259 bool BuildFontCache(const base::FilePath& file) {
1260 return BuildFontCacheInternal(file.value().c_str());
1263 } // namespace content