gpu: Add memory tracing of GPU transfer buffers.
[chromium-blink-merge.git] / content / common / dwrite_font_platform_win.cc
blob7c6fdd7d1643ae7c6c616b0c1b28a400eb59d467
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/shared_memory.h"
24 #include "base/metrics/histogram.h"
25 #include "base/path_service.h"
26 #include "base/process/process_handle.h"
27 #include "base/stl_util.h"
28 #include "base/strings/string_number_conversions.h"
29 #include "base/strings/utf_string_conversions.h"
30 #include "base/synchronization/lock.h"
31 #include "base/time/time.h"
32 #include "base/win/registry.h"
33 #include "base/win/scoped_comptr.h"
34 #include "content/public/common/content_switches.h"
36 namespace {
38 // Font Cache implementation short story:
39 // Due to our sandboxing restrictions, we cannot connect to Windows font cache
40 // service from Renderer and need to use DirectWrite isolated font loading
41 // mechanism.
42 // DirectWrite needs to be initialized before any of the API could be used.
43 // During initialization DirectWrite loads all font files and populates
44 // internal cache, we refer this phase as enumeration and we are trying
45 // to optimize this phase in our cache approach. Using cache during
46 // initialization will help improve on startup latency in each renderer
47 // instance.
48 // During enumeration DirectWrite reads various fragments from .ttf/.ttc
49 // font files. Our assumption is that these fragments are being read to
50 // cache information such as font families, supported sizes etc.
51 // For reading fragments DirectWrite calls ReadFragment of our FontFileStream
52 // implementation with parameters start_offset and length. We cache these
53 // parameters along with associated data chunk.
54 // Here is small example of how segments are read
55 // start_offset: 0, length: 16
56 // start_offset: 0, length: 12
57 // start_offset: 0, length: 117
58 // For better cache management we collapse segments if they overlap or are
59 // adjacent.
61 namespace mswr = Microsoft::WRL;
63 const char kFontKeyName[] = "font_key_name";
65 // We use this value to determine whether to cache file fragments
66 // or not. In our trials we observed that for some font files
67 // direct write ends up reading almost entire file during enumeration
68 // phase. If we don't use this percentile formula we will end up
69 // increasing significant cache size by caching entire file contents
70 // for some of the font files.
71 const double kMaxPercentileOfFontFileSizeToCache = 0.6;
73 // With current implementation we map entire shared section into memory during
74 // renderer startup. This causes increase in working set of Chrome. As first
75 // step we want to see if caching is really improving any performance for our
76 // users, so we are putting arbitrary limit on cache file size. There are
77 // multiple ways we can tune our working size, like mapping only required part
78 // of section at any given time.
79 const double kArbitraryCacheFileSizeLimit = (30 * 1024 * 1024);
81 // We have chosen current font file length arbitrarily. In our logic
82 // if we don't find file we are looking for in cache we end up loading
83 // that file directly from system fonts folder.
84 const unsigned int kMaxFontFileNameLength = 34;
86 const DWORD kCacheFileVersion = 103;
87 const DWORD kFileSignature = 0x4D4F5243; // CROM
88 const DWORD kMagicCompletionSignature = 0x454E4F44; // DONE
90 const DWORD kUndefinedDWORDS = 36;
92 // Make sure that all structure sizes align with 8 byte boundary otherwise
93 // dr. memory test may complain.
94 #pragma pack(push, 8)
95 // Cache file header, includes signature, completion bits and version.
96 struct CacheFileHeader {
97 CacheFileHeader() {
98 file_signature = kFileSignature;
99 magic_completion_signature = 0;
100 version = kCacheFileVersion;
101 ::ZeroMemory(undefined, sizeof(undefined));
104 DWORD file_signature;
105 DWORD magic_completion_signature;
106 DWORD version;
107 BYTE undefined[kUndefinedDWORDS];
110 // Entry for a particular font file within this cache.
111 struct CacheFileEntry {
112 CacheFileEntry() {
113 file_size = 0;
114 entry_count = 0;
115 ::ZeroMemory(file_name, sizeof(file_name));
118 UINT64 file_size;
119 DWORD entry_count;
120 wchar_t file_name[kMaxFontFileNameLength];
123 // Offsets or data chunks that are cached for particular font file.
124 struct CacheFileOffsetEntry {
125 CacheFileOffsetEntry() {
126 start_offset = 0;
127 length = 0;
130 UINT64 start_offset;
131 UINT64 length;
132 /* BYTE blob_[]; // Place holder for the blob that follows. */
134 #pragma pack(pop)
136 bool ValidateFontCacheHeader(CacheFileHeader* header) {
137 return (header->file_signature == kFileSignature &&
138 header->magic_completion_signature == kMagicCompletionSignature &&
139 header->version == kCacheFileVersion);
142 class FontCacheWriter;
144 // This class implements main interface required for loading custom font
145 // collection as specified by DirectWrite. We also use this class for storing
146 // some state information as this is one of the centralized entity.
147 class FontCollectionLoader
148 : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
149 IDWriteFontCollectionLoader> {
150 public:
151 FontCollectionLoader()
152 : in_collection_building_mode_(false),
153 create_static_cache_(false) {}
155 ~FontCollectionLoader() override;
157 HRESULT RuntimeClassInitialize() {
158 return S_OK;
161 // IDWriteFontCollectionLoader methods.
162 HRESULT STDMETHODCALLTYPE
163 CreateEnumeratorFromKey(IDWriteFactory* factory,
164 void const* key,
165 UINT32 key_size,
166 IDWriteFontFileEnumerator** file_enumerator) override;
168 // Does all the initialization for required loading fonts from registry.
169 static HRESULT Initialize(IDWriteFactory* factory);
171 // Returns font cache map size.
172 UINT32 GetFontMapSize();
174 // Returns font name string when given font index.
175 base::string16 GetFontNameFromKey(UINT32 idx);
177 // Loads internal structure with fonts from registry.
178 bool LoadFontListFromRegistry();
180 // Loads restricted web safe fonts as fallback method to registry fonts.
181 bool LoadRestrictedFontList();
183 // Puts class in collection building mode. In collection building mode
184 // we use static cache if it is available as a look aside buffer.
185 void EnableCollectionBuildingMode(bool enable);
187 // Returns current state of collection building.
188 bool InCollectionBuildingMode();
190 // Loads static cache file.
191 bool LoadCacheFile();
193 // Unloads cache file and related data.
194 void UnloadCacheFile();
196 // Puts class in static cache creating mode. In this mode we record all
197 // direct write requests and store chunks of font data.
198 void EnterStaticCacheMode(const WCHAR* file_name);
200 // Gets out of static cache building mode.
201 void LeaveStaticCacheMode();
203 // Returns if class is currently in static cache building mode.
204 bool IsBuildStaticCacheMode();
206 // Validates cache file for consistency.
207 bool ValidateCacheFile(base::File* file);
209 private:
210 // Structure to represent each chunk within font file that we load in memory.
211 struct CacheTableOffsetEntry {
212 UINT64 start_offset;
213 UINT64 length;
214 BYTE* inside_file_ptr;
217 typedef std::vector<CacheTableOffsetEntry> OffsetVector;
219 // Structure representing each font entry with cache.
220 struct CacheTableEntry {
221 UINT64 file_size;
222 OffsetVector offset_entries;
225 public:
226 // Returns whether file we have particular font entry within cache or not.
227 bool IsFileCached(UINT32 font_key);
228 // Returns cache fragment corresponding to specific font key.
229 void* GetCachedFragment(UINT32 font_key, UINT64 start_offset, UINT64 length);
230 // Returns actual font file size at the time of caching.
231 UINT64 GetCachedFileSize(UINT32 font_key);
233 // Returns instance of font cache writer. This class manages actual font
234 // file format.
235 FontCacheWriter* GetFontCacheWriter();
237 private:
238 // Functions validates and loads cache into internal map.
239 bool ValidateAndLoadCacheMap();
241 mswr::ComPtr<IDWriteFontFileLoader> file_loader_;
243 std::vector<base::string16> reg_fonts_;
244 bool in_collection_building_mode_;
245 bool create_static_cache_;
246 scoped_ptr<base::SharedMemory> cache_;
247 scoped_ptr<FontCacheWriter> cache_writer_;
249 typedef std::map<base::string16, CacheTableEntry*> CacheMap;
250 CacheMap cache_map_;
252 DISALLOW_COPY_AND_ASSIGN(FontCollectionLoader);
255 mswr::ComPtr<FontCollectionLoader> g_font_loader;
256 base::win::ScopedHandle g_shared_font_cache;
258 // Class responsible for handling font cache file format details as well as
259 // tracking various cache region requests by direct write.
260 class FontCacheWriter {
261 public:
262 FontCacheWriter() : count_font_entries_ignored_(0), cookie_counter_(0) {}
264 ~FontCacheWriter() {
265 if (static_cache_.get()) {
266 static_cache_->Close();
270 public:
271 // Holds data related to individual region as requested by direct write.
272 struct CacheRegion {
273 UINT64 start_offset;
274 UINT64 length;
275 const BYTE* ptr;
276 /* BYTE blob_[]; // Place holder for the blob that follows. */
279 // Function to create static font cache file.
280 bool Create(const wchar_t* file_name) {
281 static_cache_.reset(new base::File(base::FilePath(file_name),
282 base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE |
283 base::File::FLAG_EXCLUSIVE_WRITE));
284 if (!static_cache_->IsValid()) {
285 static_cache_.reset();
286 return false;
288 CacheFileHeader header;
290 // At offset 0 write cache version
291 static_cache_->Write(0,
292 reinterpret_cast<const char*>(&header),
293 sizeof(header));
295 static_cache_->Flush();
296 return true;
299 // Closes static font cache file. Also writes completion signature to mark
300 // it as completely written.
301 void Close() {
302 if (static_cache_.get()) {
303 CacheFileHeader header;
304 header.magic_completion_signature = kMagicCompletionSignature;
305 // At offset 0 write cache version
306 int bytes_written = static_cache_->Write(0,
307 reinterpret_cast<const char*>(&header),
308 sizeof(header));
309 DCHECK_NE(bytes_written, -1);
311 UMA_HISTOGRAM_MEMORY_KB("DirectWrite.Fonts.BuildCache.File.Size",
312 static_cache_->GetLength() / 1024);
314 UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.BuildCache.Ignored",
315 count_font_entries_ignored_);
317 static_cache_->Close();
318 static_cache_.reset(NULL);
322 private:
323 typedef std::vector<CacheRegion> RegionVector;
325 // Structure to track various regions requested by direct write for particular
326 // font file.
327 struct FontEntryInternal {
328 FontEntryInternal(const wchar_t* name, UINT64 size)
329 : file_name(name),
330 file_size(size) {
333 base::string16 file_name;
334 UINT64 file_size;
335 RegionVector regions;
338 public:
339 // Starts up new font entry to be tracked, returns cookie to identify this
340 // particular entry.
341 UINT NewFontEntry(const wchar_t* file_name, UINT64 file_size) {
342 base::AutoLock lock(lock_);
343 UINT old_counter = cookie_counter_;
344 FontEntryInternal* font_entry = new FontEntryInternal(file_name, file_size);
345 cookie_map_[cookie_counter_].reset(font_entry);
346 cookie_counter_++;
347 return old_counter;
350 // AddRegion function lets caller add various regions to be cached for
351 // particular font file. Once enumerating that particular font file is done
352 // (based on uniquely identifying cookie) changes could be committed using
353 // CommitFontEntry
354 bool AddRegion(UINT64 cookie, UINT64 start, UINT64 length, const BYTE* ptr) {
355 base::AutoLock lock(lock_);
356 if (cookie_map_.find(cookie) == cookie_map_.end())
357 return false;
358 RegionVector& regions = cookie_map_[cookie].get()->regions;
359 CacheRegion region;
360 region.start_offset = start;
361 region.length = length;
362 region.ptr = ptr;
363 regions.push_back(region);
364 return true;
367 // Function which commits after merging all collected regions into cache file.
368 bool CommitFontEntry(UINT cookie) {
369 base::AutoLock lock(lock_);
370 if (cookie_map_.find(cookie) == cookie_map_.end())
371 return false;
373 // We will skip writing entries beyond allowed limit. Following condition
374 // doesn't enforce hard file size. We need to write complete font entry.
375 int64 length = static_cache_->GetLength();
376 if (length == -1 || length >= kArbitraryCacheFileSizeLimit) {
377 count_font_entries_ignored_++;
378 return false;
381 FontEntryInternal* font_entry = cookie_map_[cookie].get();
382 RegionVector& regions = font_entry->regions;
383 std::sort(regions.begin(), regions.end(), SortCacheRegions);
385 // At this point, we have collected all regions to be cached. These regions
386 // are tuples of start, length, data for particular data segment.
387 // These tuples can overlap.
388 // e.g. (0, 12, data), (0, 117, data), (21, 314, data), (335, 15, data)
389 // In this case as you can see first three segments overlap and
390 // 4th is adjacent. If we cache them individually then we will end up
391 // caching duplicate data, so we merge these segments together to find
392 // superset for the cache. In above example our algorithm should
393 // produce (cache) single segment starting at offset 0 with length 350.
394 RegionVector merged_regions;
395 RegionVector::iterator iter;
396 int idx = 0;
397 for (iter = regions.begin(); iter != regions.end(); iter++) {
398 if (iter == regions.begin()) {
399 merged_regions.push_back(*iter);
400 continue;
402 CacheRegion& base_region = merged_regions[idx];
403 if (IsOverlap(&base_region, &(*iter))) {
404 UINT64 end1 = base_region.start_offset + base_region.length;
405 UINT64 end2 = iter->start_offset + iter->length;
406 if (base_region.start_offset > iter->start_offset) {
407 base_region.start_offset = iter->start_offset;
408 base_region.ptr = iter->ptr;
410 base_region.length = std::max(end1, end2) - base_region.start_offset;
411 } else {
412 merged_regions.push_back(*iter);
413 idx++;
417 UINT64 total_merged_cache_in_bytes = 0;
418 for (iter = merged_regions.begin(); iter != merged_regions.end(); iter++) {
419 total_merged_cache_in_bytes += iter->length;
422 // We want to adjust following parameter based on experiments. But general
423 // logic here is that if we are going to end up caching most of the contents
424 // for a file (e.g. simsunb.ttf > 90%) then we should avoid caching that
425 // file.
426 double percentile = static_cast<double>(total_merged_cache_in_bytes) /
427 font_entry->file_size;
428 if (percentile > kMaxPercentileOfFontFileSizeToCache) {
429 count_font_entries_ignored_++;
430 return false;
433 CacheFileEntry entry;
434 wcsncpy_s(entry.file_name, kMaxFontFileNameLength,
435 font_entry->file_name.c_str(), _TRUNCATE);
436 entry.file_size = font_entry->file_size;
437 entry.entry_count = merged_regions.size();
438 static_cache_->WriteAtCurrentPos(
439 reinterpret_cast<const char*>(&entry),
440 sizeof(entry));
441 for (iter = merged_regions.begin(); iter != merged_regions.end(); iter++) {
442 CacheFileOffsetEntry offset_entry;
443 offset_entry.start_offset = iter->start_offset;
444 offset_entry.length = iter->length;
445 static_cache_->WriteAtCurrentPos(
446 reinterpret_cast<const char*>(&offset_entry),
447 sizeof(offset_entry));
448 static_cache_->WriteAtCurrentPos(
449 reinterpret_cast<const char*>(iter->ptr),
450 iter->length);
452 return true;
455 private:
456 // This is the count of font entries that we reject based on size to be
457 // cached.
458 unsigned int count_font_entries_ignored_;
459 scoped_ptr<base::File> static_cache_;
460 std::map<UINT, scoped_ptr<FontEntryInternal>> cookie_map_;
461 UINT cookie_counter_;
463 // Lock is required to protect internal data structures and access to file,
464 // According to MSDN documentation on ReadFileFragment and based on our
465 // experiments so far, there is possibility of ReadFileFragment getting called
466 // from multiple threads.
467 base::Lock lock_;
469 // Function checks if two regions overlap or are adjacent.
470 bool IsOverlap(CacheRegion* region1, CacheRegion* region2) {
471 return
472 !((region1->start_offset + region1->length) < region2->start_offset ||
473 region1->start_offset > (region2->start_offset + region2->length));
476 // Function to sort cached regions.
477 static bool SortCacheRegions(const CacheRegion& region1,
478 const CacheRegion& region2) {
479 return
480 region1.start_offset == region2.start_offset ?
481 region1.length < region2.length :
482 region1.start_offset < region2.start_offset;
485 DISALLOW_COPY_AND_ASSIGN(FontCacheWriter);
488 // Class implements IDWriteFontFileStream interface as required by direct write.
489 class FontFileStream
490 : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
491 IDWriteFontFileStream> {
492 public:
493 // IDWriteFontFileStream methods.
494 HRESULT STDMETHODCALLTYPE ReadFileFragment(
495 void const** fragment_start,
496 UINT64 file_offset,
497 UINT64 fragment_size,
498 void** context) override {
499 if (cached_data_) {
500 *fragment_start = g_font_loader->GetCachedFragment(font_key_,
501 file_offset,
502 fragment_size);
503 if (*fragment_start == NULL) {
504 DCHECK(false);
506 *context = NULL;
507 return *fragment_start != NULL ? S_OK : E_FAIL;
509 if (!memory_.get() || !memory_->IsValid() ||
510 file_offset >= memory_->length() ||
511 (file_offset + fragment_size) > memory_->length())
512 return E_FAIL;
514 *fragment_start = static_cast<BYTE const*>(memory_->data()) +
515 static_cast<size_t>(file_offset);
516 *context = NULL;
517 if (g_font_loader->IsBuildStaticCacheMode()) {
518 FontCacheWriter* cache_writer = g_font_loader->GetFontCacheWriter();
519 cache_writer->AddRegion(writer_cookie_,
520 file_offset,
521 fragment_size,
522 static_cast<const BYTE*>(*fragment_start));
524 return S_OK;
527 void STDMETHODCALLTYPE ReleaseFileFragment(void* context) override {}
529 HRESULT STDMETHODCALLTYPE GetFileSize(UINT64* file_size) override {
530 if (cached_data_) {
531 *file_size = g_font_loader->GetCachedFileSize(font_key_);
532 return S_OK;
535 if (!memory_.get() || !memory_->IsValid())
536 return E_FAIL;
538 *file_size = memory_->length();
539 return S_OK;
542 HRESULT STDMETHODCALLTYPE GetLastWriteTime(UINT64* last_write_time) override {
543 if (cached_data_) {
544 *last_write_time = 0;
545 return S_OK;
548 if (!memory_.get() || !memory_->IsValid())
549 return E_FAIL;
551 // According to MSDN article http://goo.gl/rrSYzi the "last modified time"
552 // is used by DirectWrite font selection algorithms to determine whether
553 // one font resource is more up to date than another one.
554 // So by returning 0 we are assuming that it will treat all fonts to be
555 // equally up to date.
556 // TODO(shrikant): We should further investigate this.
557 *last_write_time = 0;
558 return S_OK;
561 FontFileStream() : font_key_(0), cached_data_(false) {}
563 HRESULT RuntimeClassInitialize(UINT32 font_key) {
564 if (g_font_loader->InCollectionBuildingMode() &&
565 g_font_loader->IsFileCached(font_key)) {
566 cached_data_ = true;
567 font_key_ = font_key;
568 return S_OK;
571 base::FilePath path;
572 PathService::Get(base::DIR_WINDOWS_FONTS, &path);
573 base::string16 font_key_name(g_font_loader->GetFontNameFromKey(font_key));
574 path = path.Append(font_key_name.c_str());
575 memory_.reset(new base::MemoryMappedFile());
577 // Put some debug information on stack.
578 WCHAR font_name[MAX_PATH];
579 path.value().copy(font_name, arraysize(font_name));
580 base::debug::Alias(font_name);
582 if (!memory_->Initialize(path)) {
583 memory_.reset();
584 return E_FAIL;
587 font_key_ = font_key;
589 base::debug::SetCrashKeyValue(kFontKeyName,
590 base::WideToUTF8(font_key_name));
592 if (g_font_loader->IsBuildStaticCacheMode()) {
593 FontCacheWriter* cache_writer = g_font_loader->GetFontCacheWriter();
594 writer_cookie_ = cache_writer->NewFontEntry(font_key_name.c_str(),
595 memory_->length());
597 return S_OK;
600 ~FontFileStream() override {
601 if (g_font_loader->IsBuildStaticCacheMode()) {
602 FontCacheWriter* cache_writer = g_font_loader->GetFontCacheWriter();
603 cache_writer->CommitFontEntry(writer_cookie_);
607 private:
608 UINT32 font_key_;
609 scoped_ptr<base::MemoryMappedFile> memory_;
610 bool cached_data_;
611 UINT writer_cookie_;
613 DISALLOW_COPY_AND_ASSIGN(FontFileStream);
616 // Implements IDWriteFontFileLoader as required by FontFileLoader.
617 class FontFileLoader
618 : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
619 IDWriteFontFileLoader> {
620 public:
621 // IDWriteFontFileLoader methods.
622 HRESULT STDMETHODCALLTYPE
623 CreateStreamFromKey(void const* ref_key,
624 UINT32 ref_key_size,
625 IDWriteFontFileStream** stream) override {
626 if (ref_key_size != sizeof(UINT32))
627 return E_FAIL;
629 UINT32 font_key = *static_cast<const UINT32*>(ref_key);
630 mswr::ComPtr<FontFileStream> font_stream;
631 HRESULT hr = mswr::MakeAndInitialize<FontFileStream>(&font_stream,
632 font_key);
633 if (SUCCEEDED(hr)) {
634 *stream = font_stream.Detach();
635 return S_OK;
637 return E_FAIL;
640 FontFileLoader() {}
641 ~FontFileLoader() override {}
643 private:
644 DISALLOW_COPY_AND_ASSIGN(FontFileLoader);
647 // Implements IDWriteFontFileEnumerator as required by direct write.
648 class FontFileEnumerator
649 : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
650 IDWriteFontFileEnumerator> {
651 public:
652 // IDWriteFontFileEnumerator methods.
653 HRESULT STDMETHODCALLTYPE MoveNext(BOOL* has_current_file) override {
654 *has_current_file = FALSE;
656 if (current_file_)
657 current_file_.ReleaseAndGetAddressOf();
659 if (font_idx_ < g_font_loader->GetFontMapSize()) {
660 HRESULT hr =
661 factory_->CreateCustomFontFileReference(&font_idx_,
662 sizeof(UINT32),
663 file_loader_.Get(),
664 current_file_.GetAddressOf());
665 DCHECK(SUCCEEDED(hr));
666 *has_current_file = TRUE;
667 font_idx_++;
669 return S_OK;
672 HRESULT STDMETHODCALLTYPE
673 GetCurrentFontFile(IDWriteFontFile** font_file) override {
674 if (!current_file_) {
675 *font_file = NULL;
676 return E_FAIL;
679 *font_file = current_file_.Detach();
680 return S_OK;
683 FontFileEnumerator(const void* keys,
684 UINT32 buffer_size,
685 IDWriteFactory* factory,
686 IDWriteFontFileLoader* file_loader)
687 : factory_(factory), file_loader_(file_loader), font_idx_(0) {}
689 ~FontFileEnumerator() override {}
691 mswr::ComPtr<IDWriteFactory> factory_;
692 mswr::ComPtr<IDWriteFontFile> current_file_;
693 mswr::ComPtr<IDWriteFontFileLoader> file_loader_;
694 UINT32 font_idx_;
696 private:
697 DISALLOW_COPY_AND_ASSIGN(FontFileEnumerator);
700 // IDWriteFontCollectionLoader methods.
701 HRESULT STDMETHODCALLTYPE FontCollectionLoader::CreateEnumeratorFromKey(
702 IDWriteFactory* factory,
703 void const* key,
704 UINT32 key_size,
705 IDWriteFontFileEnumerator** file_enumerator) {
706 *file_enumerator = mswr::Make<FontFileEnumerator>(
707 key, key_size, factory, file_loader_.Get()).Detach();
708 return S_OK;
711 // static
712 HRESULT FontCollectionLoader::Initialize(IDWriteFactory* factory) {
713 DCHECK(g_font_loader == NULL);
715 HRESULT result;
716 result = mswr::MakeAndInitialize<FontCollectionLoader>(&g_font_loader);
717 if (FAILED(result) || !g_font_loader) {
718 DCHECK(false);
719 return E_FAIL;
722 CHECK(g_font_loader->LoadFontListFromRegistry());
724 g_font_loader->file_loader_ = mswr::Make<FontFileLoader>().Detach();
726 factory->RegisterFontFileLoader(g_font_loader->file_loader_.Get());
727 factory->RegisterFontCollectionLoader(g_font_loader.Get());
729 return S_OK;
732 FontCollectionLoader::~FontCollectionLoader() {
733 STLDeleteContainerPairSecondPointers(cache_map_.begin(), cache_map_.end());
736 UINT32 FontCollectionLoader::GetFontMapSize() {
737 return reg_fonts_.size();
740 base::string16 FontCollectionLoader::GetFontNameFromKey(UINT32 idx) {
741 DCHECK(idx < reg_fonts_.size());
742 return reg_fonts_[idx];
745 const base::FilePath::CharType* kFontExtensionsToIgnore[] {
746 FILE_PATH_LITERAL(".FON"), // Bitmap or vector
747 FILE_PATH_LITERAL(".PFM"), // Adobe Type 1
748 FILE_PATH_LITERAL(".PFB"), // Adobe Type 1
751 const wchar_t* kFontsToIgnore[] = {
752 // "Gill Sans Ultra Bold" turns into an Ultra Bold weight "Gill Sans" in
753 // DirectWrite, but most users don't have any other weights. The regular
754 // weight font is named "Gill Sans MT", but that ends up in a different
755 // family with that name. On Mac, there's a "Gill Sans" with various weights,
756 // so CSS authors use { 'font-family': 'Gill Sans', 'Gill Sans MT', ... } and
757 // because of the DirectWrite family futzing, they end up with an Ultra Bold
758 // font, when they just wanted "Gill Sans". Mozilla implemented a more
759 // complicated hack where they effectively rename the Ultra Bold font to
760 // "Gill Sans MT Ultra Bold", but because the Ultra Bold font is so ugly
761 // anyway, we simply ignore it. See
762 // http://www.microsoft.com/typography/fonts/font.aspx?FMID=978 for a picture
763 // of the font, and the file name. We also ignore "Gill Sans Ultra Bold
764 // Condensed".
765 L"gilsanub.ttf",
766 L"gillubcd.ttf",
769 bool FontCollectionLoader::LoadFontListFromRegistry() {
770 const wchar_t kFontsRegistry[] =
771 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
772 CHECK(reg_fonts_.empty());
773 base::win::RegKey regkey;
774 if (regkey.Open(HKEY_LOCAL_MACHINE, kFontsRegistry, KEY_READ) !=
775 ERROR_SUCCESS) {
776 return false;
779 base::FilePath system_font_path;
780 PathService::Get(base::DIR_WINDOWS_FONTS, &system_font_path);
782 base::string16 name;
783 base::string16 value;
784 for (DWORD idx = 0; idx < regkey.GetValueCount(); idx++) {
785 if (regkey.GetValueNameAt(idx, &name) == ERROR_SUCCESS &&
786 regkey.ReadValue(name.c_str(), &value) == ERROR_SUCCESS) {
787 base::FilePath path(value.c_str());
788 // We need to check if path in registry is absolute, if it is then
789 // we check if it is same as DIR_WINDOWS_FONTS otherwise we ignore.
790 bool absolute = path.IsAbsolute();
791 if (absolute &&
792 !base::FilePath::CompareEqualIgnoreCase(system_font_path.value(),
793 path.DirName().value())) {
794 continue;
797 // Ignore if path ends with a separator.
798 if (path.EndsWithSeparator())
799 continue;
801 if (absolute)
802 value = path.BaseName().value();
804 bool should_ignore = false;
805 for (const auto& ignore : kFontsToIgnore) {
806 if (base::FilePath::CompareEqualIgnoreCase(value, ignore)) {
807 should_ignore = true;
808 break;
811 // DirectWrite doesn't support bitmap/vector fonts and Adobe type 1
812 // fonts, we will ignore those font extensions.
813 // MSDN article: http://goo.gl/TfCOA
814 if (!should_ignore) {
815 for (const auto& ignore : kFontExtensionsToIgnore) {
816 if (path.MatchesExtension(ignore)) {
817 should_ignore = true;
818 break;
823 if (!should_ignore)
824 reg_fonts_.push_back(value.c_str());
827 UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Loaded", reg_fonts_.size());
828 UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Ignored",
829 regkey.GetValueCount() - reg_fonts_.size());
830 return true;
833 // This list is mainly based on prefs/prefs_tab_helper.cc kFontDefaults.
834 const wchar_t* kRestrictedFontSet[] = {
835 // These are the "Web Safe" fonts.
836 L"times.ttf", // IDS_STANDARD_FONT_FAMILY
837 L"timesbd.ttf", // IDS_STANDARD_FONT_FAMILY
838 L"timesbi.ttf", // IDS_STANDARD_FONT_FAMILY
839 L"timesi.ttf", // IDS_STANDARD_FONT_FAMILY
840 L"cour.ttf", // IDS_FIXED_FONT_FAMILY
841 L"courbd.ttf", // IDS_FIXED_FONT_FAMILY
842 L"courbi.ttf", // IDS_FIXED_FONT_FAMILY
843 L"couri.ttf", // IDS_FIXED_FONT_FAMILY
844 L"consola.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
845 L"consolab.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
846 L"consolai.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
847 L"consolaz.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
848 L"arial.ttf", // IDS_SANS_SERIF_FONT_FAMILY
849 L"arialbd.ttf", // IDS_SANS_SERIF_FONT_FAMILY
850 L"arialbi.ttf", // IDS_SANS_SERIF_FONT_FAMILY
851 L"ariali.ttf", // IDS_SANS_SERIF_FONT_FAMILY
852 L"comic.ttf", // IDS_CURSIVE_FONT_FAMILY
853 L"comicbd.ttf", // IDS_CURSIVE_FONT_FAMILY
854 L"comici.ttf", // IDS_CURSIVE_FONT_FAMILY
855 L"comicz.ttf", // IDS_CURSIVE_FONT_FAMILY
856 L"impact.ttf", // IDS_FANTASY_FONT_FAMILY
857 L"georgia.ttf",
858 L"georgiab.ttf",
859 L"georgiai.ttf",
860 L"georgiaz.ttf",
861 L"trebuc.ttf",
862 L"trebucbd.ttf",
863 L"trebucbi.ttf",
864 L"trebucit.ttf",
865 L"verdana.ttf",
866 L"verdanab.ttf",
867 L"verdanai.ttf",
868 L"verdanaz.ttf",
869 L"segoeui.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
870 L"segoeuib.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
871 L"segoeuii.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
872 L"msgothic.ttc", // IDS_STANDARD_FONT_FAMILY_JAPANESE
873 L"msmincho.ttc", // IDS_SERIF_FONT_FAMILY_JAPANESE
874 L"gulim.ttc", // IDS_FIXED_FONT_FAMILY_KOREAN
875 L"batang.ttc", // IDS_SERIF_FONT_FAMILY_KOREAN
876 L"simsun.ttc", // IDS_STANDARD_FONT_FAMILY_SIMPLIFIED_HAN
877 L"mingliu.ttc", // IDS_SERIF_FONT_FAMILY_TRADITIONAL_HAN
879 // These are from the Blink fallback list.
880 L"david.ttf", // USCRIPT_HEBREW
881 L"davidbd.ttf", // USCRIPT_HEBREW
882 L"euphemia.ttf", // USCRIPT_CANADIAN_ABORIGINAL
883 L"gautami.ttf", // USCRIPT_TELUGU
884 L"gautamib.ttf", // USCRIPT_TELUGU
885 L"latha.ttf", // USCRIPT_TAMIL
886 L"lathab.ttf", // USCRIPT_TAMIL
887 L"mangal.ttf", // USCRIPT_DEVANAGARI
888 L"mangalb.ttf", // USCRIPT_DEVANAGARI
889 L"monbaiti.ttf", // USCRIPT_MONGOLIAN
890 L"mvboli.ttf", // USCRIPT_THAANA
891 L"plantc.ttf", // USCRIPT_CHEROKEE
892 L"raavi.ttf", // USCRIPT_GURMUKHI
893 L"raavib.ttf", // USCRIPT_GURMUKHI
894 L"shruti.ttf", // USCRIPT_GUJARATI
895 L"shrutib.ttf", // USCRIPT_GUJARATI
896 L"sylfaen.ttf", // USCRIPT_GEORGIAN and USCRIPT_ARMENIAN
897 L"tahoma.ttf", // USCRIPT_ARABIC,
898 L"tahomabd.ttf", // USCRIPT_ARABIC,
899 L"tunga.ttf", // USCRIPT_KANNADA
900 L"tungab.ttf", // USCRIPT_KANNADA
901 L"vrinda.ttf", // USCRIPT_BENGALI
902 L"vrindab.ttf", // USCRIPT_BENGALI
905 bool FontCollectionLoader::LoadRestrictedFontList() {
906 reg_fonts_.clear();
907 reg_fonts_.assign(kRestrictedFontSet,
908 kRestrictedFontSet + _countof(kRestrictedFontSet));
909 return true;
912 void FontCollectionLoader::EnableCollectionBuildingMode(bool enable) {
913 in_collection_building_mode_ = enable;
916 bool FontCollectionLoader::InCollectionBuildingMode() {
917 return in_collection_building_mode_;
920 bool FontCollectionLoader::IsFileCached(UINT32 font_key) {
921 if (!cache_.get() || cache_->memory() == NULL) {
922 return false;
924 CacheMap::iterator iter = cache_map_.find(
925 GetFontNameFromKey(font_key).c_str());
926 return iter != cache_map_.end();;
929 bool FontCollectionLoader::LoadCacheFile() {
930 std::string font_cache_handle_string =
931 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
932 switches::kFontCacheSharedHandle);
933 if (font_cache_handle_string.empty())
934 return false;
936 base::SharedMemoryHandle font_cache_handle = NULL;
937 base::StringToUint(font_cache_handle_string,
938 reinterpret_cast<unsigned int*>(&font_cache_handle));
939 DCHECK(font_cache_handle);
941 base::SharedMemory* shared_mem = new base::SharedMemory(
942 font_cache_handle, true);
943 // Map while file
944 shared_mem->Map(0);
946 cache_.reset(shared_mem);
948 if (!ValidateAndLoadCacheMap()) {
949 cache_.reset();
950 return false;
953 return true;
956 void FontCollectionLoader::UnloadCacheFile() {
957 cache_.reset();
958 STLDeleteContainerPairSecondPointers(cache_map_.begin(), cache_map_.end());
959 cache_map_.clear();
962 void FontCollectionLoader::EnterStaticCacheMode(const WCHAR* file_name) {
963 cache_writer_.reset(new FontCacheWriter());
964 if (cache_writer_->Create(file_name))
965 create_static_cache_ = true;
968 void FontCollectionLoader::LeaveStaticCacheMode() {
969 cache_writer_->Close();
970 cache_writer_.reset(NULL);
971 create_static_cache_ = false;
974 bool FontCollectionLoader::IsBuildStaticCacheMode() {
975 return create_static_cache_;
978 bool FontCollectionLoader::ValidateAndLoadCacheMap() {
979 BYTE* mem_file_start = static_cast<BYTE*>(cache_->memory());
980 BYTE* mem_file_end = mem_file_start + cache_->mapped_size();
982 BYTE* current_ptr = mem_file_start;
983 CacheFileHeader* file_header =
984 reinterpret_cast<CacheFileHeader*>(current_ptr);
985 if (!ValidateFontCacheHeader(file_header))
986 return false;
988 current_ptr = current_ptr + sizeof(CacheFileHeader);
989 if (current_ptr >= mem_file_end)
990 return false;
992 while ((current_ptr + sizeof(CacheFileEntry)) < mem_file_end) {
993 CacheFileEntry* entry = reinterpret_cast<CacheFileEntry*>(current_ptr);
994 current_ptr += sizeof(CacheFileEntry);
995 WCHAR file_name[kMaxFontFileNameLength];
996 wcsncpy_s(file_name,
997 kMaxFontFileNameLength,
998 entry->file_name,
999 _TRUNCATE);
1000 CacheTableEntry* table_entry = NULL;
1001 CacheMap::iterator iter = cache_map_.find(file_name);
1002 if (iter == cache_map_.end()) {
1003 table_entry = new CacheTableEntry();
1004 cache_map_[file_name] = table_entry;
1005 } else {
1006 table_entry = iter->second;
1008 table_entry->file_size = entry->file_size;
1009 for (DWORD idx = 0;
1010 (current_ptr + sizeof(CacheFileOffsetEntry)) < mem_file_end &&
1011 idx < entry->entry_count;
1012 idx++) {
1013 CacheFileOffsetEntry* offset_entry =
1014 reinterpret_cast<CacheFileOffsetEntry*>(current_ptr);
1015 CacheTableOffsetEntry table_offset_entry;
1016 table_offset_entry.start_offset = offset_entry->start_offset;
1017 table_offset_entry.length = offset_entry->length;
1018 table_offset_entry.inside_file_ptr =
1019 current_ptr + sizeof(CacheFileOffsetEntry);
1020 table_entry->offset_entries.push_back(table_offset_entry);
1021 current_ptr += sizeof(CacheFileOffsetEntry);
1022 current_ptr += offset_entry->length;
1026 return true;
1029 void* FontCollectionLoader::GetCachedFragment(UINT32 font_key,
1030 UINT64 start_offset,
1031 UINT64 length) {
1032 UINT64 just_past_end = start_offset + length;
1033 CacheMap::iterator iter = cache_map_.find(
1034 GetFontNameFromKey(font_key).c_str());
1035 if (iter != cache_map_.end()) {
1036 CacheTableEntry* entry = iter->second;
1037 OffsetVector::iterator offset_iter = entry->offset_entries.begin();
1038 while (offset_iter != entry->offset_entries.end()) {
1039 UINT64 available_just_past_end =
1040 offset_iter->start_offset + offset_iter->length;
1041 if (offset_iter->start_offset <= start_offset &&
1042 just_past_end <= available_just_past_end) {
1043 return offset_iter->inside_file_ptr +
1044 (start_offset - offset_iter->start_offset);
1046 offset_iter++;
1049 return NULL;
1052 UINT64 FontCollectionLoader::GetCachedFileSize(UINT32 font_key) {
1053 CacheMap::iterator iter = cache_map_.find(
1054 GetFontNameFromKey(font_key).c_str());
1055 if (iter != cache_map_.end()) {
1056 return iter->second->file_size;
1058 return 0;
1061 FontCacheWriter* FontCollectionLoader::GetFontCacheWriter() {
1062 return cache_writer_.get();
1065 } // namespace
1067 namespace content {
1069 const char kFontCacheSharedSectionName[] = "ChromeDWriteFontCache";
1071 mswr::ComPtr<IDWriteFontCollection> g_font_collection;
1073 IDWriteFontCollection* GetCustomFontCollection(IDWriteFactory* factory) {
1074 if (g_font_collection.Get() != NULL)
1075 return g_font_collection.Get();
1077 base::TimeTicks start_tick = base::TimeTicks::Now();
1079 FontCollectionLoader::Initialize(factory);
1081 bool cache_file_loaded = g_font_loader->LoadCacheFile();
1083 // Arbitrary threshold to stop loading enormous number of fonts. Usual
1084 // side effect of loading large number of fonts results in renderer getting
1085 // killed as it appears to hang.
1086 const UINT32 kMaxFontThreshold = 1750;
1087 HRESULT hr = E_FAIL;
1088 if (cache_file_loaded ||
1089 g_font_loader->GetFontMapSize() < kMaxFontThreshold) {
1090 g_font_loader->EnableCollectionBuildingMode(true);
1091 hr = factory->CreateCustomFontCollection(
1092 g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf());
1093 g_font_loader->UnloadCacheFile();
1094 g_font_loader->EnableCollectionBuildingMode(false);
1096 bool loading_restricted = false;
1097 if (FAILED(hr) || !g_font_collection.Get()) {
1098 loading_restricted = true;
1099 // We will try here just one more time with restricted font set.
1100 g_font_loader->LoadRestrictedFontList();
1101 hr = factory->CreateCustomFontCollection(
1102 g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf());
1105 base::TimeDelta time_delta = base::TimeTicks::Now() - start_tick;
1106 int64 delta = time_delta.ToInternalValue();
1107 base::debug::Alias(&delta);
1108 UINT32 size = g_font_loader->GetFontMapSize();
1109 base::debug::Alias(&size);
1110 base::debug::Alias(&loading_restricted);
1112 CHECK(SUCCEEDED(hr));
1113 CHECK(g_font_collection.Get() != NULL);
1115 if (cache_file_loaded)
1116 UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime.Cached", time_delta);
1117 else
1118 UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime", time_delta);
1120 base::debug::ClearCrashKey(kFontKeyName);
1122 return g_font_collection.Get();
1125 bool BuildFontCacheInternal(const WCHAR* file_name) {
1126 typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc;
1127 HMODULE dwrite_dll = LoadLibraryW(L"dwrite.dll");
1128 if (!dwrite_dll) {
1129 DWORD load_library_get_last_error = GetLastError();
1130 base::debug::Alias(&dwrite_dll);
1131 base::debug::Alias(&load_library_get_last_error);
1132 CHECK(false);
1135 DWriteCreateFactoryProc dwrite_create_factory_proc =
1136 reinterpret_cast<DWriteCreateFactoryProc>(
1137 GetProcAddress(dwrite_dll, "DWriteCreateFactory"));
1139 if (!dwrite_create_factory_proc) {
1140 DWORD get_proc_address_get_last_error = GetLastError();
1141 base::debug::Alias(&dwrite_create_factory_proc);
1142 base::debug::Alias(&get_proc_address_get_last_error);
1143 CHECK(false);
1146 mswr::ComPtr<IDWriteFactory> factory;
1148 CHECK(SUCCEEDED(
1149 dwrite_create_factory_proc(
1150 DWRITE_FACTORY_TYPE_ISOLATED,
1151 __uuidof(IDWriteFactory),
1152 reinterpret_cast<IUnknown**>(factory.GetAddressOf()))));
1154 base::TimeTicks start_tick = base::TimeTicks::Now();
1156 FontCollectionLoader::Initialize(factory.Get());
1158 g_font_loader->EnterStaticCacheMode(file_name);
1160 mswr::ComPtr<IDWriteFontCollection> font_collection;
1162 HRESULT hr = E_FAIL;
1163 g_font_loader->EnableCollectionBuildingMode(true);
1164 hr = factory->CreateCustomFontCollection(
1165 g_font_loader.Get(), NULL, 0, font_collection.GetAddressOf());
1166 g_font_loader->EnableCollectionBuildingMode(false);
1168 bool loading_restricted = false;
1169 if (FAILED(hr) || !font_collection.Get()) {
1170 loading_restricted = true;
1171 // We will try here just one more time with restricted font set.
1172 g_font_loader->LoadRestrictedFontList();
1173 hr = factory->CreateCustomFontCollection(
1174 g_font_loader.Get(), NULL, 0, font_collection.GetAddressOf());
1177 g_font_loader->LeaveStaticCacheMode();
1179 base::TimeDelta time_delta = base::TimeTicks::Now() - start_tick;
1180 int64 delta = time_delta.ToInternalValue();
1181 base::debug::Alias(&delta);
1182 UINT32 size = g_font_loader->GetFontMapSize();
1183 base::debug::Alias(&size);
1184 base::debug::Alias(&loading_restricted);
1186 CHECK(SUCCEEDED(hr));
1187 CHECK(font_collection.Get() != NULL);
1189 base::debug::ClearCrashKey(kFontKeyName);
1191 return true;
1194 bool ValidateFontCacheFile(base::File* file) {
1195 DCHECK(file != NULL);
1196 CacheFileHeader file_header;
1197 if (file->Read(0, reinterpret_cast<char*>(&file_header), sizeof(file_header))
1198 == -1) {
1199 return false;
1201 return ValidateFontCacheHeader(&file_header);
1204 bool LoadFontCache(const base::FilePath& path) {
1205 scoped_ptr<base::File> file(new base::File(path,
1206 base::File::FLAG_OPEN | base::File::FLAG_READ));
1207 if (!file->IsValid())
1208 return false;
1210 if (!ValidateFontCacheFile(file.get()))
1211 return false;
1213 base::string16 name(base::ASCIIToUTF16(content::kFontCacheSharedSectionName));
1214 name.append(base::UintToString16(base::GetCurrentProcId()));
1215 HANDLE mapping = ::CreateFileMapping(
1216 file->GetPlatformFile(),
1217 NULL,
1218 PAGE_READONLY,
1221 name.c_str());
1222 if (mapping == INVALID_HANDLE_VALUE)
1223 return false;
1225 if (::GetLastError() == ERROR_ALREADY_EXISTS) {
1226 CloseHandle(mapping);
1227 // We crash here, as no one should have created this mapping except Chrome.
1228 CHECK(false);
1229 return false;
1232 DCHECK(!g_shared_font_cache.IsValid());
1233 g_shared_font_cache.Set(mapping);
1235 return true;
1238 bool BuildFontCache(const base::FilePath& file) {
1239 return BuildFontCacheInternal(file.value().c_str());
1242 } // namespace content