| 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 |