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 |