Blink roll 25b6bd3a7a131ffe68d809546ad1a20707915cdc:3a503f41ae42e5b79cfcd2ff10e65afde...
[chromium-blink-merge.git] / content / common / dwrite_font_platform_win.cc
blob7479c6717c02ff97f57f6dcaf06ea7cd3203ac35
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 <dwrite.h>
8 #include <map>
9 #include <string>
10 #include <utility>
11 #include <vector>
12 #include <wrl/implements.h>
13 #include <wrl/wrappers/corewrappers.h>
15 #include "base/command_line.h"
16 #include "base/debug/alias.h"
17 #include "base/debug/crash_logging.h"
18 #include "base/files/file_enumerator.h"
19 #include "base/files/file_path.h"
20 #include "base/files/file_util.h"
21 #include "base/files/memory_mapped_file.h"
22 #include "base/memory/scoped_ptr.h"
23 #include "base/memory/scoped_vector.h"
24 #include "base/memory/shared_memory.h"
25 #include "base/metrics/histogram.h"
26 #include "base/path_service.h"
27 #include "base/process/process_handle.h"
28 #include "base/stl_util.h"
29 #include "base/strings/string_number_conversions.h"
30 #include "base/strings/utf_string_conversions.h"
31 #include "base/synchronization/lock.h"
32 #include "base/time/time.h"
33 #include "base/win/registry.h"
34 #include "base/win/scoped_comptr.h"
35 #include "content/public/common/content_switches.h"
37 namespace {
39 // Font Cache implementation short story:
40 // Due to our sandboxing restrictions, we cannot connect to Windows font cache
41 // service from Renderer and need to use DirectWrite isolated font loading
42 // mechanism.
43 // DirectWrite needs to be initialized before any of the API could be used.
44 // During initialization DirectWrite loads all font files and populates
45 // internal cache, we refer this phase as enumeration and we are trying
46 // to optimize this phase in our cache approach. Using cache during
47 // initialization will help improve on startup latency in each renderer
48 // instance.
49 // During enumeration DirectWrite reads various fragments from .ttf/.ttc
50 // font files. Our assumption is that these fragments are being read to
51 // cache information such as font families, supported sizes etc.
52 // For reading fragments DirectWrite calls ReadFragment of our FontFileStream
53 // implementation with parameters start_offset and length. We cache these
54 // parameters along with associated data chunk.
55 // Here is small example of how segments are read
56 // start_offset: 0, length: 16
57 // start_offset: 0, length: 12
58 // start_offset: 0, length: 117
59 // For better cache management we collapse segments if they overlap or are
60 // adjacent.
62 namespace mswr = Microsoft::WRL;
64 const char kFontKeyName[] = "font_key_name";
66 // We use this value to determine whether to cache file fragments
67 // or not. In our trials we observed that for some font files
68 // direct write ends up reading almost entire file during enumeration
69 // phase. If we don't use this percentile formula we will end up
70 // increasing significant cache size by caching entire file contents
71 // for some of the font files.
72 const double kMaxPercentileOfFontFileSizeToCache = 0.5;
74 // With current implementation we map entire shared section into memory during
75 // renderer startup. This causes increase in working set of Chrome. As first
76 // step we want to see if caching is really improving any performance for our
77 // users, so we are putting arbitrary limit on cache file size. There are
78 // multiple ways we can tune our working size, like mapping only required part
79 // of section at any given time.
80 const double kArbitraryCacheFileSizeLimit = (20 * 1024 * 1024);
82 // We have chosen current font file length arbitrarily. In our logic
83 // if we don't find file we are looking for in cache we end up loading
84 // that file directly from system fonts folder.
85 const unsigned int kMaxFontFileNameLength = 34;
87 const DWORD kCacheFileVersion = 101;
88 const DWORD kFileSignature = 0x4D4F5243; // CROM
89 const DWORD kMagicCompletionSignature = 0x454E4F44; // DONE
91 const DWORD kUndefinedDWORDS = 36;
93 // Make sure that all structure sizes align with 8 byte boundary otherwise
94 // dr. memory test may complain.
95 #pragma pack(push, 8)
96 // Cache file header, includes signature, completion bits and version.
97 struct CacheFileHeader {
98 CacheFileHeader() {
99 file_signature = kFileSignature;
100 magic_completion_signature = 0;
101 version = kCacheFileVersion;
102 ::ZeroMemory(undefined, sizeof(undefined));
105 DWORD file_signature;
106 DWORD magic_completion_signature;
107 DWORD version;
108 BYTE undefined[kUndefinedDWORDS];
111 // Entry for a particular font file within this cache.
112 struct CacheFileEntry {
113 CacheFileEntry() {
114 file_size = 0;
115 entry_count = 0;
116 ::ZeroMemory(file_name, sizeof(file_name));
119 UINT64 file_size;
120 DWORD entry_count;
121 wchar_t file_name[kMaxFontFileNameLength];
124 // Offsets or data chunks that are cached for particular font file.
125 struct CacheFileOffsetEntry {
126 CacheFileOffsetEntry() {
127 start_offset = 0;
128 length = 0;
131 UINT64 start_offset;
132 UINT64 length;
133 /* BYTE blob_[]; // Place holder for the blob that follows. */
135 #pragma pack(pop)
137 bool ValidateFontCacheHeader(CacheFileHeader* header) {
138 return (header->file_signature == kFileSignature &&
139 header->magic_completion_signature == kMagicCompletionSignature &&
140 header->version == kCacheFileVersion);
143 class FontCacheWriter;
145 // This class implements main interface required for loading custom font
146 // collection as specified by DirectWrite. We also use this class for storing
147 // some state information as this is one of the centralized entity.
148 class FontCollectionLoader
149 : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
150 IDWriteFontCollectionLoader> {
151 public:
152 FontCollectionLoader()
153 : in_collection_building_mode_(false),
154 create_static_cache_(false) {};
156 virtual ~FontCollectionLoader();
158 HRESULT RuntimeClassInitialize() {
159 return S_OK;
162 // IDWriteFontCollectionLoader methods.
163 virtual HRESULT STDMETHODCALLTYPE
164 CreateEnumeratorFromKey(
165 IDWriteFactory* factory,
166 void const* key,
167 UINT32 key_size,
168 IDWriteFontFileEnumerator** file_enumerator) override;
170 // Does all the initialization for required loading fonts from registry.
171 static HRESULT Initialize(IDWriteFactory* factory);
173 // Returns font cache map size.
174 UINT32 GetFontMapSize();
176 // Returns font name string when given font index.
177 base::string16 GetFontNameFromKey(UINT32 idx);
179 // Loads internal structure with fonts from registry.
180 bool LoadFontListFromRegistry();
182 // Loads restricted web safe fonts as fallback method to registry fonts.
183 bool LoadRestrictedFontList();
185 // Puts class in collection building mode. In collection building mode
186 // we use static cache if it is available as a look aside buffer.
187 void EnableCollectionBuildingMode(bool enable);
189 // Returns current state of collection building.
190 bool InCollectionBuildingMode();
192 // Loads static cache file.
193 bool LoadCacheFile();
195 // Puts class in static cache creating mode. In this mode we record all
196 // direct write requests and store chunks of font data.
197 void EnterStaticCacheMode(const WCHAR* file_name);
199 // Gets out of static cache building mode.
200 void LeaveStaticCacheMode();
202 // Returns if class is currently in static cache building mode.
203 bool IsBuildStaticCacheMode();
205 // Validates cache file for consistency.
206 bool ValidateCacheFile(base::File* file);
208 private:
209 // Structure to represent each chunk within font file that we load in memory.
210 struct CacheTableOffsetEntry {
211 UINT64 start_offset;
212 UINT64 length;
213 BYTE* inside_file_ptr;
216 typedef std::vector<CacheTableOffsetEntry> OffsetVector;
218 // Structure representing each font entry with cache.
219 struct CacheTableEntry {
220 UINT64 file_size;
221 OffsetVector offset_entries;
224 public:
225 // Returns whether file we have particular font entry within cache or not.
226 bool IsFileCached(UINT32 font_key);
227 // Returns cache fragment corresponding to specific font key.
228 void* GetCachedFragment(UINT32 font_key, UINT64 start_offset, UINT64 length);
229 // Returns actual font file size at the time of caching.
230 UINT64 GetCachedFileSize(UINT32 font_key);
232 // Returns instance of font cache writer. This class manages actual font
233 // file format.
234 FontCacheWriter* GetFontCacheWriter();
236 private:
237 // Functions validates and loads cache into internal map.
238 bool ValidateAndLoadCacheMap();
240 mswr::ComPtr<IDWriteFontFileLoader> file_loader_;
242 std::vector<base::string16> reg_fonts_;
243 bool in_collection_building_mode_;
244 bool create_static_cache_;
245 scoped_ptr<base::SharedMemory> cache_;
246 scoped_ptr<FontCacheWriter> cache_writer_;
248 typedef std::map<base::string16, CacheTableEntry*> CacheMap;
249 CacheMap cache_map_;
251 DISALLOW_COPY_AND_ASSIGN(FontCollectionLoader);
254 mswr::ComPtr<FontCollectionLoader> g_font_loader;
255 base::win::ScopedHandle g_shared_font_cache;
257 // Class responsible for handling font cache file format details as well as
258 // tracking various cache region requests by direct write.
259 class FontCacheWriter {
260 public:
261 FontCacheWriter()
262 : cookie_counter_(0),
263 count_font_entries_ignored_(0) {
266 ~FontCacheWriter() {
267 if (static_cache_.get()) {
268 static_cache_->Close();
272 public:
273 // Holds data related to individual region as requested by direct write.
274 struct CacheRegion {
275 UINT64 start_offset;
276 UINT64 length;
277 const BYTE* ptr;
278 /* BYTE blob_[]; // Place holder for the blob that follows. */
281 // Function to create static font cache file.
282 bool Create(const wchar_t* file_name) {
283 static_cache_.reset(new base::File(base::FilePath(file_name),
284 base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE |
285 base::File::FLAG_EXCLUSIVE_WRITE));
286 if (!static_cache_->IsValid()) {
287 static_cache_.reset();
288 return false;
290 CacheFileHeader header;
292 // At offset 0 write cache version
293 static_cache_->Write(0,
294 reinterpret_cast<const char*>(&header),
295 sizeof(header));
297 static_cache_->Flush();
298 return true;
301 // Closes static font cache file. Also writes completion signature to mark
302 // it as completely written.
303 void Close() {
304 if (static_cache_.get()) {
305 CacheFileHeader header;
306 header.magic_completion_signature = kMagicCompletionSignature;
307 // At offset 0 write cache version
308 int bytes_written = static_cache_->Write(0,
309 reinterpret_cast<const char*>(&header),
310 sizeof(header));
311 DCHECK(bytes_written != -1);
313 UMA_HISTOGRAM_MEMORY_KB("DirectWrite.Fonts.BuildCache.File.Size",
314 static_cache_->GetLength() / 1024);
316 UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.BuildCache.Ignored",
317 count_font_entries_ignored_);
319 static_cache_->Close();
320 static_cache_.reset(NULL);
324 private:
325 typedef std::vector<CacheRegion> RegionVector;
327 // Structure to track various regions requested by direct write for particular
328 // font file.
329 struct FontEntryInternal {
330 FontEntryInternal(const wchar_t* name, UINT64 size)
331 : file_name(name),
332 file_size(size) {
335 base::string16 file_name;
336 UINT64 file_size;
337 RegionVector regions;
340 public:
341 // Starts up new font entry to be tracked, returns cookie to identify this
342 // particular entry.
343 UINT NewFontEntry(const wchar_t* file_name, UINT64 file_size) {
344 base::AutoLock lock(lock_);
345 UINT old_counter = cookie_counter_;
346 FontEntryInternal* font_entry = new FontEntryInternal(file_name, file_size);
347 cookie_map_[cookie_counter_].reset(font_entry);
348 cookie_counter_++;
349 return old_counter;
352 // AddRegion function lets caller add various regions to be cached for
353 // particular font file. Once enumerating that particular font file is done
354 // (based on uniquely identifying cookie) changes could be committed using
355 // CommitFontEntry
356 bool AddRegion(UINT64 cookie, UINT64 start, UINT64 length, const BYTE* ptr) {
357 base::AutoLock lock(lock_);
358 if (cookie_map_.find(cookie) == cookie_map_.end())
359 return false;
360 RegionVector& regions = cookie_map_[cookie].get()->regions;
361 CacheRegion region;
362 region.start_offset = start;
363 region.length = length;
364 region.ptr = ptr;
365 regions.push_back(region);
366 return true;
369 // Function which commits after merging all collected regions into cache file.
370 bool CommitFontEntry(UINT cookie) {
371 base::AutoLock lock(lock_);
372 if (cookie_map_.find(cookie) == cookie_map_.end())
373 return false;
375 // We will skip writing entries beyond allowed limit. Following condition
376 // doesn't enforce hard file size. We need to write complete font entry.
377 int64 length = static_cache_->GetLength();
378 if (length == -1 || length >= kArbitraryCacheFileSizeLimit) {
379 count_font_entries_ignored_++;
380 return false;
383 FontEntryInternal* font_entry = cookie_map_[cookie].get();
384 RegionVector& regions = font_entry->regions;
385 std::sort(regions.begin(), regions.end(), SortCacheRegions);
387 // At this point, we have collected all regions to be cached. These regions
388 // are tuples of start, length, data for particular data segment.
389 // These tuples can overlap.
390 // e.g. (0, 12, data), (0, 117, data), (21, 314, data), (335, 15, data)
391 // In this case as you can see first three segments overlap and
392 // 4th is adjacent. If we cache them individually then we will end up
393 // caching duplicate data, so we merge these segments together to find
394 // superset for the cache. In above example our algorithm should
395 // produce (cache) single segment starting at offset 0 with length 350.
396 RegionVector merged_regions;
397 RegionVector::iterator iter;
398 int idx = 0;
399 for (iter = regions.begin(); iter != regions.end(); iter++) {
400 if (iter == regions.begin()) {
401 merged_regions.push_back(*iter);
402 continue;
404 CacheRegion& base_region = merged_regions[idx];
405 if (IsOverlap(&base_region, &(*iter))) {
406 UINT64 end1 = base_region.start_offset + base_region.length;
407 UINT64 end2 = iter->start_offset + iter->length;
408 if (base_region.start_offset > iter->start_offset) {
409 base_region.start_offset = iter->start_offset;
410 base_region.ptr = iter->ptr;
412 base_region.length = std::max(end1, end2) - base_region.start_offset;
413 } else {
414 merged_regions.push_back(*iter);
415 idx++;
419 UINT64 total_merged_cache_in_bytes = 0;
420 for (iter = merged_regions.begin(); iter != merged_regions.end(); iter++) {
421 total_merged_cache_in_bytes += iter->length;
424 // We want to adjust following parameter based on experiments. But general
425 // logic here is that if we are going to end up caching most of the contents
426 // for a file (e.g. simsunb.ttf > 90%) then we should avoid caching that
427 // file.
428 double percentile = static_cast<double>(total_merged_cache_in_bytes) /
429 font_entry->file_size;
430 if (percentile > kMaxPercentileOfFontFileSizeToCache) {
431 count_font_entries_ignored_++;
432 return false;
435 CacheFileEntry entry;
436 wcsncpy_s(entry.file_name, kMaxFontFileNameLength,
437 font_entry->file_name.c_str(), _TRUNCATE);
438 entry.file_size = font_entry->file_size;
439 entry.entry_count = merged_regions.size();
440 static_cache_->WriteAtCurrentPos(
441 reinterpret_cast<const char*>(&entry),
442 sizeof(entry));
443 for (iter = merged_regions.begin(); iter != merged_regions.end(); iter++) {
444 CacheFileOffsetEntry offset_entry;
445 offset_entry.start_offset = iter->start_offset;
446 offset_entry.length = iter->length;
447 static_cache_->WriteAtCurrentPos(
448 reinterpret_cast<const char*>(&offset_entry),
449 sizeof(offset_entry));
450 static_cache_->WriteAtCurrentPos(
451 reinterpret_cast<const char*>(iter->ptr),
452 iter->length);
454 return true;
457 private:
458 // This is the count of font entries that we reject based on size to be
459 // cached.
460 unsigned int count_font_entries_ignored_;
461 scoped_ptr<base::File> static_cache_;
462 std::map<UINT, scoped_ptr<FontEntryInternal>> cookie_map_;
463 UINT cookie_counter_;
465 // Lock is required to protect internal data structures and access to file,
466 // According to MSDN documentation on ReadFileFragment and based on our
467 // experiments so far, there is possibility of ReadFileFragment getting called
468 // from multiple threads.
469 base::Lock lock_;
471 // Function checks if two regions overlap or are adjacent.
472 bool IsOverlap(CacheRegion* region1, CacheRegion* region2) {
473 return
474 !((region1->start_offset + region1->length) < region2->start_offset ||
475 region1->start_offset > (region2->start_offset + region2->length));
478 // Function to sort cached regions.
479 static bool SortCacheRegions(const CacheRegion& region1,
480 const CacheRegion& region2) {
481 return
482 region1.start_offset == region2.start_offset ?
483 region1.length < region2.length :
484 region1.start_offset < region2.start_offset;
487 DISALLOW_COPY_AND_ASSIGN(FontCacheWriter);
490 // Class implements IDWriteFontFileStream interface as required by direct write.
491 class FontFileStream
492 : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
493 IDWriteFontFileStream> {
494 public:
495 // IDWriteFontFileStream methods.
496 virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(
497 void const** fragment_start,
498 UINT64 file_offset,
499 UINT64 fragment_size,
500 void** context) override {
501 if (cached_data_) {
502 *fragment_start = g_font_loader->GetCachedFragment(font_key_,
503 file_offset,
504 fragment_size);
505 if (*fragment_start == NULL) {
506 DCHECK(false);
508 *context = NULL;
509 return *fragment_start != NULL ? S_OK : E_FAIL;
511 if (!memory_.get() || !memory_->IsValid() ||
512 file_offset >= memory_->length() ||
513 (file_offset + fragment_size) > memory_->length())
514 return E_FAIL;
516 *fragment_start = static_cast<BYTE const*>(memory_->data()) +
517 static_cast<size_t>(file_offset);
518 *context = NULL;
519 if (g_font_loader->IsBuildStaticCacheMode()) {
520 FontCacheWriter* cache_writer = g_font_loader->GetFontCacheWriter();
521 cache_writer->AddRegion(writer_cookie_,
522 file_offset,
523 fragment_size,
524 static_cast<const BYTE*>(*fragment_start));
526 return S_OK;
529 virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* context) override {}
531 virtual HRESULT STDMETHODCALLTYPE GetFileSize(UINT64* file_size) override {
532 if (cached_data_) {
533 *file_size = g_font_loader->GetCachedFileSize(font_key_);
534 return S_OK;
537 if (!memory_.get() || !memory_->IsValid())
538 return E_FAIL;
540 *file_size = memory_->length();
541 return S_OK;
544 virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(
545 UINT64* last_write_time) override {
546 if (cached_data_) {
547 *last_write_time = 0;
548 return S_OK;
551 if (!memory_.get() || !memory_->IsValid())
552 return E_FAIL;
554 // According to MSDN article http://goo.gl/rrSYzi the "last modified time"
555 // is used by DirectWrite font selection algorithms to determine whether
556 // one font resource is more up to date than another one.
557 // So by returning 0 we are assuming that it will treat all fonts to be
558 // equally up to date.
559 // TODO(shrikant): We should further investigate this.
560 *last_write_time = 0;
561 return S_OK;
564 FontFileStream::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 virtual ~FontFileStream() {
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 virtual 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 virtual ~FontFileLoader() {}
647 private:
649 DISALLOW_COPY_AND_ASSIGN(FontFileLoader);
652 // Implements IDWriteFontFileEnumerator as required by direct write.
653 class FontFileEnumerator
654 : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
655 IDWriteFontFileEnumerator> {
656 public:
657 // IDWriteFontFileEnumerator methods.
658 virtual HRESULT STDMETHODCALLTYPE MoveNext(BOOL* has_current_file) override {
659 *has_current_file = FALSE;
661 if (current_file_)
662 current_file_.ReleaseAndGetAddressOf();
664 if (font_idx_ < g_font_loader->GetFontMapSize()) {
665 HRESULT hr =
666 factory_->CreateCustomFontFileReference(&font_idx_,
667 sizeof(UINT32),
668 file_loader_.Get(),
669 current_file_.GetAddressOf());
670 DCHECK(SUCCEEDED(hr));
671 *has_current_file = TRUE;
672 font_idx_++;
674 return S_OK;
677 virtual HRESULT STDMETHODCALLTYPE
678 GetCurrentFontFile(IDWriteFontFile** font_file) override {
679 if (!current_file_) {
680 *font_file = NULL;
681 return E_FAIL;
684 *font_file = current_file_.Detach();
685 return S_OK;
688 FontFileEnumerator(const void* keys,
689 UINT32 buffer_size,
690 IDWriteFactory* factory,
691 IDWriteFontFileLoader* file_loader)
692 : factory_(factory), file_loader_(file_loader), font_idx_(0) {}
694 virtual ~FontFileEnumerator() {}
696 mswr::ComPtr<IDWriteFactory> factory_;
697 mswr::ComPtr<IDWriteFontFile> current_file_;
698 mswr::ComPtr<IDWriteFontFileLoader> file_loader_;
699 UINT32 font_idx_;
701 private:
703 DISALLOW_COPY_AND_ASSIGN(FontFileEnumerator);
706 // IDWriteFontCollectionLoader methods.
707 HRESULT STDMETHODCALLTYPE FontCollectionLoader::CreateEnumeratorFromKey(
708 IDWriteFactory* factory,
709 void const* key,
710 UINT32 key_size,
711 IDWriteFontFileEnumerator** file_enumerator) {
712 *file_enumerator = mswr::Make<FontFileEnumerator>(
713 key, key_size, factory, file_loader_.Get()).Detach();
714 return S_OK;
717 // static
718 HRESULT FontCollectionLoader::Initialize(IDWriteFactory* factory) {
719 DCHECK(g_font_loader == NULL);
721 HRESULT result;
722 result = mswr::MakeAndInitialize<FontCollectionLoader>(&g_font_loader);
723 if (FAILED(result) || !g_font_loader) {
724 DCHECK(false);
725 return E_FAIL;
728 CHECK(g_font_loader->LoadFontListFromRegistry());
730 g_font_loader->file_loader_ = mswr::Make<FontFileLoader>().Detach();
732 factory->RegisterFontFileLoader(g_font_loader->file_loader_.Get());
733 factory->RegisterFontCollectionLoader(g_font_loader.Get());
735 return S_OK;
738 FontCollectionLoader::~FontCollectionLoader() {
739 STLDeleteContainerPairSecondPointers(cache_map_.begin(), cache_map_.end());
742 UINT32 FontCollectionLoader::GetFontMapSize() {
743 return reg_fonts_.size();
746 base::string16 FontCollectionLoader::GetFontNameFromKey(UINT32 idx) {
747 DCHECK(idx < reg_fonts_.size());
748 return reg_fonts_[idx];
751 bool FontCollectionLoader::LoadFontListFromRegistry() {
752 const wchar_t kFontsRegistry[] =
753 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
754 CHECK(reg_fonts_.empty());
755 base::win::RegKey regkey;
756 if (regkey.Open(HKEY_LOCAL_MACHINE, kFontsRegistry, KEY_READ) !=
757 ERROR_SUCCESS) {
758 return false;
761 base::FilePath system_font_path;
762 PathService::Get(base::DIR_WINDOWS_FONTS, &system_font_path);
764 base::string16 name;
765 base::string16 value;
766 for (DWORD idx = 0; idx < regkey.GetValueCount(); idx++) {
767 if (regkey.GetValueNameAt(idx, &name) == ERROR_SUCCESS &&
768 regkey.ReadValue(name.c_str(), &value) == ERROR_SUCCESS) {
769 base::FilePath path(value.c_str());
770 // We need to check if file name is the only component that exists,
771 // we will ignore all other registry entries.
772 std::vector<base::FilePath::StringType> components;
773 path.GetComponents(&components);
774 if ((components.size() == 1 &&
775 value.size() < kMaxFontFileNameLength - 1) ||
776 base::FilePath::CompareEqualIgnoreCase(system_font_path.value(),
777 path.DirName().value())) {
778 reg_fonts_.push_back(value.c_str());
782 UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Loaded", reg_fonts_.size());
783 UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Ignored",
784 regkey.GetValueCount() - reg_fonts_.size());
785 return true;
788 // This list is mainly based on prefs/prefs_tab_helper.cc kFontDefaults.
789 const wchar_t* kRestrictedFontSet[] = {
790 // These are the "Web Safe" fonts.
791 L"times.ttf", // IDS_STANDARD_FONT_FAMILY
792 L"timesbd.ttf", // IDS_STANDARD_FONT_FAMILY
793 L"timesbi.ttf", // IDS_STANDARD_FONT_FAMILY
794 L"timesi.ttf", // IDS_STANDARD_FONT_FAMILY
795 L"cour.ttf", // IDS_FIXED_FONT_FAMILY
796 L"courbd.ttf", // IDS_FIXED_FONT_FAMILY
797 L"courbi.ttf", // IDS_FIXED_FONT_FAMILY
798 L"couri.ttf", // IDS_FIXED_FONT_FAMILY
799 L"consola.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
800 L"consolab.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
801 L"consolai.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
802 L"consolaz.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
803 L"arial.ttf", // IDS_SANS_SERIF_FONT_FAMILY
804 L"arialbd.ttf", // IDS_SANS_SERIF_FONT_FAMILY
805 L"arialbi.ttf", // IDS_SANS_SERIF_FONT_FAMILY
806 L"ariali.ttf", // IDS_SANS_SERIF_FONT_FAMILY
807 L"comic.ttf", // IDS_CURSIVE_FONT_FAMILY
808 L"comicbd.ttf", // IDS_CURSIVE_FONT_FAMILY
809 L"comici.ttf", // IDS_CURSIVE_FONT_FAMILY
810 L"comicz.ttf", // IDS_CURSIVE_FONT_FAMILY
811 L"impact.ttf", // IDS_FANTASY_FONT_FAMILY
812 L"georgia.ttf",
813 L"georgiab.ttf",
814 L"georgiai.ttf",
815 L"georgiaz.ttf",
816 L"trebuc.ttf",
817 L"trebucbd.ttf",
818 L"trebucbi.ttf",
819 L"trebucit.ttf",
820 L"verdana.ttf",
821 L"verdanab.ttf",
822 L"verdanai.ttf",
823 L"verdanaz.ttf",
824 L"segoeui.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
825 L"segoeuib.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
826 L"segoeuii.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
827 L"msgothic.ttc", // IDS_STANDARD_FONT_FAMILY_JAPANESE
828 L"msmincho.ttc", // IDS_SERIF_FONT_FAMILY_JAPANESE
829 L"gulim.ttc", // IDS_FIXED_FONT_FAMILY_KOREAN
830 L"batang.ttc", // IDS_SERIF_FONT_FAMILY_KOREAN
831 L"simsun.ttc", // IDS_STANDARD_FONT_FAMILY_SIMPLIFIED_HAN
832 L"mingliu.ttc", // IDS_SERIF_FONT_FAMILY_TRADITIONAL_HAN
834 // These are from the Blink fallback list.
835 L"david.ttf", // USCRIPT_HEBREW
836 L"davidbd.ttf", // USCRIPT_HEBREW
837 L"euphemia.ttf", // USCRIPT_CANADIAN_ABORIGINAL
838 L"gautami.ttf", // USCRIPT_TELUGU
839 L"gautamib.ttf", // USCRIPT_TELUGU
840 L"latha.ttf", // USCRIPT_TAMIL
841 L"lathab.ttf", // USCRIPT_TAMIL
842 L"mangal.ttf", // USCRIPT_DEVANAGARI
843 L"mangalb.ttf", // USCRIPT_DEVANAGARI
844 L"monbaiti.ttf", // USCRIPT_MONGOLIAN
845 L"mvboli.ttf", // USCRIPT_THAANA
846 L"plantc.ttf", // USCRIPT_CHEROKEE
847 L"raavi.ttf", // USCRIPT_GURMUKHI
848 L"raavib.ttf", // USCRIPT_GURMUKHI
849 L"shruti.ttf", // USCRIPT_GUJARATI
850 L"shrutib.ttf", // USCRIPT_GUJARATI
851 L"sylfaen.ttf", // USCRIPT_GEORGIAN and USCRIPT_ARMENIAN
852 L"tahoma.ttf", // USCRIPT_ARABIC,
853 L"tahomabd.ttf", // USCRIPT_ARABIC,
854 L"tunga.ttf", // USCRIPT_KANNADA
855 L"tungab.ttf", // USCRIPT_KANNADA
856 L"vrinda.ttf", // USCRIPT_BENGALI
857 L"vrindab.ttf", // USCRIPT_BENGALI
860 bool FontCollectionLoader::LoadRestrictedFontList() {
861 reg_fonts_.clear();
862 reg_fonts_.assign(kRestrictedFontSet,
863 kRestrictedFontSet + _countof(kRestrictedFontSet));
864 return true;
867 void FontCollectionLoader::EnableCollectionBuildingMode(bool enable) {
868 in_collection_building_mode_ = enable;
871 bool FontCollectionLoader::InCollectionBuildingMode() {
872 return in_collection_building_mode_;
875 bool FontCollectionLoader::IsFileCached(UINT32 font_key) {
876 if (!cache_.get() || cache_->memory() == NULL) {
877 return false;
879 CacheMap::iterator iter = cache_map_.find(
880 GetFontNameFromKey(font_key).c_str());
881 return iter != cache_map_.end();;
884 bool FontCollectionLoader::LoadCacheFile() {
885 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
886 switches::kFontCacheSharedMemSuffix)) {
887 return false;
889 base::SharedMemory* shared_mem = new base::SharedMemory();
890 std::string name(content::kFontCacheSharedSectionName);
891 name.append(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
892 switches::kFontCacheSharedMemSuffix));
893 if (!shared_mem->Open(name.c_str(), true))
894 return false;
896 // Map while file
897 shared_mem->Map(0);
899 cache_.reset(shared_mem);
901 if (!ValidateAndLoadCacheMap()) {
902 cache_.reset();
903 return false;
906 return true;
909 void FontCollectionLoader::EnterStaticCacheMode(const WCHAR* file_name) {
910 cache_writer_.reset(new FontCacheWriter());
911 if (cache_writer_->Create(file_name))
912 create_static_cache_ = true;
915 void FontCollectionLoader::LeaveStaticCacheMode() {
916 cache_writer_->Close();
917 cache_writer_.reset(NULL);
918 create_static_cache_ = false;
921 bool FontCollectionLoader::IsBuildStaticCacheMode() {
922 return create_static_cache_;
925 bool FontCollectionLoader::ValidateAndLoadCacheMap() {
926 BYTE* mem_file_start = static_cast<BYTE*>(cache_->memory());
927 BYTE* mem_file_end = mem_file_start + cache_->mapped_size();
929 BYTE* current_ptr = mem_file_start;
930 CacheFileHeader* file_header =
931 reinterpret_cast<CacheFileHeader*>(current_ptr);
932 if (!ValidateFontCacheHeader(file_header))
933 return false;
935 current_ptr = current_ptr + sizeof(CacheFileHeader);
936 if (current_ptr >= mem_file_end)
937 return false;
939 while ((current_ptr + sizeof(CacheFileEntry)) < mem_file_end) {
940 CacheFileEntry* entry = reinterpret_cast<CacheFileEntry*>(current_ptr);
941 current_ptr += sizeof(CacheFileEntry);
942 WCHAR file_name[kMaxFontFileNameLength];
943 wcsncpy_s(file_name,
944 kMaxFontFileNameLength,
945 entry->file_name,
946 _TRUNCATE);
947 CacheTableEntry* table_entry = NULL;
948 CacheMap::iterator iter = cache_map_.find(file_name);
949 if (iter == cache_map_.end()) {
950 table_entry = new CacheTableEntry();
951 cache_map_[file_name] = table_entry;
952 } else {
953 table_entry = iter->second;
955 table_entry->file_size = entry->file_size;
956 for (DWORD idx = 0;
957 (current_ptr + sizeof(CacheFileOffsetEntry)) < mem_file_end &&
958 idx < entry->entry_count;
959 idx++) {
960 CacheFileOffsetEntry* offset_entry =
961 reinterpret_cast<CacheFileOffsetEntry*>(current_ptr);
962 CacheTableOffsetEntry table_offset_entry;
963 table_offset_entry.start_offset = offset_entry->start_offset;
964 table_offset_entry.length = offset_entry->length;
965 table_offset_entry.inside_file_ptr =
966 current_ptr + sizeof(CacheFileOffsetEntry);
967 table_entry->offset_entries.push_back(table_offset_entry);
968 current_ptr += sizeof(CacheFileOffsetEntry);
969 current_ptr += offset_entry->length;
973 return true;
976 void* FontCollectionLoader::GetCachedFragment(UINT32 font_key,
977 UINT64 start_offset,
978 UINT64 length) {
979 UINT64 just_past_end = start_offset + length;
980 CacheMap::iterator iter = cache_map_.find(
981 GetFontNameFromKey(font_key).c_str());
982 if (iter != cache_map_.end()) {
983 CacheTableEntry* entry = iter->second;
984 OffsetVector::iterator offset_iter = entry->offset_entries.begin();
985 while (offset_iter != entry->offset_entries.end()) {
986 UINT64 available_just_past_end =
987 offset_iter->start_offset + offset_iter->length;
988 if (offset_iter->start_offset <= start_offset &&
989 just_past_end <= available_just_past_end) {
990 return offset_iter->inside_file_ptr +
991 (start_offset - offset_iter->start_offset);
993 offset_iter++;
996 return NULL;
999 UINT64 FontCollectionLoader::GetCachedFileSize(UINT32 font_key) {
1000 CacheMap::iterator iter = cache_map_.find(
1001 GetFontNameFromKey(font_key).c_str());
1002 if (iter != cache_map_.end()) {
1003 return iter->second->file_size;
1005 return 0;
1008 FontCacheWriter* FontCollectionLoader::GetFontCacheWriter() {
1009 return cache_writer_.get();
1012 } // namespace
1014 namespace content {
1016 const char kFontCacheSharedSectionName[] = "ChromeDWriteFontCache";
1018 mswr::ComPtr<IDWriteFontCollection> g_font_collection;
1020 IDWriteFontCollection* GetCustomFontCollection(IDWriteFactory* factory) {
1021 if (g_font_collection.Get() != NULL)
1022 return g_font_collection.Get();
1024 base::TimeTicks start_tick = base::TimeTicks::Now();
1026 FontCollectionLoader::Initialize(factory);
1028 bool cache_file_loaded = g_font_loader->LoadCacheFile();
1030 // We try here to put arbitrary limit on max number of fonts that could
1031 // be loaded, otherwise we fallback to restricted set of fonts.
1032 const UINT32 kMaxFontThreshold = 1750;
1033 HRESULT hr = E_FAIL;
1034 if (g_font_loader->GetFontMapSize() < kMaxFontThreshold) {
1035 g_font_loader->EnableCollectionBuildingMode(true);
1036 hr = factory->CreateCustomFontCollection(
1037 g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf());
1038 g_font_loader->EnableCollectionBuildingMode(false);
1041 bool loading_restricted = false;
1042 if (FAILED(hr) || !g_font_collection.Get()) {
1043 loading_restricted = true;
1044 // We will try here just one more time with restricted font set.
1045 g_font_loader->LoadRestrictedFontList();
1046 hr = factory->CreateCustomFontCollection(
1047 g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf());
1050 base::TimeDelta time_delta = base::TimeTicks::Now() - start_tick;
1051 int64 delta = time_delta.ToInternalValue();
1052 base::debug::Alias(&delta);
1053 UINT32 size = g_font_loader->GetFontMapSize();
1054 base::debug::Alias(&size);
1055 base::debug::Alias(&loading_restricted);
1057 CHECK(SUCCEEDED(hr));
1058 CHECK(g_font_collection.Get() != NULL);
1060 if (cache_file_loaded)
1061 UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime.Cached", time_delta);
1062 else
1063 UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime", time_delta);
1065 base::debug::ClearCrashKey(kFontKeyName);
1067 return g_font_collection.Get();
1070 bool BuildFontCacheInternal(const WCHAR* file_name) {
1071 typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc;
1072 HMODULE dwrite_dll = LoadLibraryW(L"dwrite.dll");
1073 if (!dwrite_dll) {
1074 DWORD load_library_get_last_error = GetLastError();
1075 base::debug::Alias(&dwrite_dll);
1076 base::debug::Alias(&load_library_get_last_error);
1077 CHECK(false);
1080 DWriteCreateFactoryProc dwrite_create_factory_proc =
1081 reinterpret_cast<DWriteCreateFactoryProc>(
1082 GetProcAddress(dwrite_dll, "DWriteCreateFactory"));
1084 if (!dwrite_create_factory_proc) {
1085 DWORD get_proc_address_get_last_error = GetLastError();
1086 base::debug::Alias(&dwrite_create_factory_proc);
1087 base::debug::Alias(&get_proc_address_get_last_error);
1088 CHECK(false);
1091 mswr::ComPtr<IDWriteFactory> factory;
1093 CHECK(SUCCEEDED(
1094 dwrite_create_factory_proc(
1095 DWRITE_FACTORY_TYPE_ISOLATED,
1096 __uuidof(IDWriteFactory),
1097 reinterpret_cast<IUnknown**>(factory.GetAddressOf()))));
1099 base::TimeTicks start_tick = base::TimeTicks::Now();
1101 FontCollectionLoader::Initialize(factory.Get());
1103 g_font_loader->EnterStaticCacheMode(file_name);
1105 mswr::ComPtr<IDWriteFontCollection> font_collection;
1107 // We try here to put arbitrary limit on max number of fonts that could
1108 // be loaded, otherwise we fallback to restricted set of fonts.
1109 const UINT32 kMaxFontThreshold = 1750;
1110 HRESULT hr = E_FAIL;
1111 if (g_font_loader->GetFontMapSize() < kMaxFontThreshold) {
1112 g_font_loader->EnableCollectionBuildingMode(true);
1113 hr = factory->CreateCustomFontCollection(
1114 g_font_loader.Get(), NULL, 0, font_collection.GetAddressOf());
1115 g_font_loader->EnableCollectionBuildingMode(false);
1118 bool loading_restricted = false;
1119 if (FAILED(hr) || !font_collection.Get()) {
1120 loading_restricted = true;
1121 // We will try here just one more time with restricted font set.
1122 g_font_loader->LoadRestrictedFontList();
1123 hr = factory->CreateCustomFontCollection(
1124 g_font_loader.Get(), NULL, 0, font_collection.GetAddressOf());
1127 g_font_loader->LeaveStaticCacheMode();
1129 base::TimeDelta time_delta = base::TimeTicks::Now() - start_tick;
1130 int64 delta = time_delta.ToInternalValue();
1131 base::debug::Alias(&delta);
1132 UINT32 size = g_font_loader->GetFontMapSize();
1133 base::debug::Alias(&size);
1134 base::debug::Alias(&loading_restricted);
1136 CHECK(SUCCEEDED(hr));
1137 CHECK(font_collection.Get() != NULL);
1139 base::debug::ClearCrashKey(kFontKeyName);
1141 return true;
1144 bool ValidateFontCacheFile(base::File* file) {
1145 DCHECK(file != NULL);
1146 CacheFileHeader file_header;
1147 if (file->Read(0, reinterpret_cast<char*>(&file_header), sizeof(file_header))
1148 == -1) {
1149 return false;
1151 return ValidateFontCacheHeader(&file_header);
1154 bool LoadFontCache(const base::FilePath& path) {
1155 scoped_ptr<base::File> file(new base::File(path,
1156 base::File::FLAG_OPEN | base::File::FLAG_READ));
1157 if (!file->IsValid())
1158 return false;
1160 if (!ValidateFontCacheFile(file.get()))
1161 return false;
1163 std::string name(content::kFontCacheSharedSectionName);
1164 name.append(base::UintToString(base::GetCurrentProcId()));
1165 HANDLE mapping = ::CreateFileMapping(
1166 file->GetPlatformFile(),
1167 NULL,
1168 PAGE_READONLY,
1171 base::ASCIIToWide(name).c_str());
1172 if (mapping == INVALID_HANDLE_VALUE)
1173 return false;
1175 if (::GetLastError() == ERROR_ALREADY_EXISTS) {
1176 CloseHandle(mapping);
1177 // We crash here, as no one should have created this mapping except Chrome.
1178 CHECK(false);
1179 return false;
1182 DCHECK(!g_shared_font_cache.IsValid());
1183 g_shared_font_cache.Set(mapping);
1185 return true;
1188 bool BuildFontCache(const base::FilePath& file) {
1189 return BuildFontCacheInternal(file.value().c_str());
1192 } // namespace content