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