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

Side by Side Diff: content/common/dwrite_font_platform_win.cc

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

Powered by Google App Engine
This is Rietveld 408576698