OLD | NEW |
| (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 | |
OLD | NEW |