Chromium Code Reviews| 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"; | |
|
gab
2016/04/06 14:25:23
AFAICT this file isn't being cleaned up by this CL
| |
| 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 |