diff options
author | raymes <raymes@chromium.org> | 2014-11-09 16:27:06 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-11-10 00:27:28 +0000 |
commit | 7ba10bcafcd5cb74990cbae13b2bf2b0ef4d1ab4 (patch) | |
tree | f747ee135c29bc207fbf2899b1f64e71c51bd2db | |
parent | e42d0d199989109375bdcec46f776fe53b4ed000 (diff) | |
download | chromium_src-7ba10bcafcd5cb74990cbae13b2bf2b0ef4d1ab4.zip chromium_src-7ba10bcafcd5cb74990cbae13b2bf2b0ef4d1ab4.tar.gz chromium_src-7ba10bcafcd5cb74990cbae13b2bf2b0ef4d1ab4.tar.bz2 |
Revert of DirectWrite Font Collection Cache Part 1 (patchset #5 id:80001 of https://codereview.chromium.org/693593004/)
Reason for revert:
This broke content_unittests on an XP bot:
https://build.chromium.org/p/chromium.win/builders/XP%20Tests%20%281%29/builds/33702/steps/content_unittests%20on%20Windows-5.1/logs/BuildCacheTest
Original issue's description:
> DirectWrite Font Collection Cache Part 1
>
> This is the first phase of collection cache. This phase only integrates cache changes into existing DirectWrite font code, but doesn't actually trigger cache code. After this checkin there shouldn't be any changes in the way current loading scheme works during renderer startup. (Enumerate and load all fonts from registry during startup.)
>
> Why we need font cache?
> Due to sandbox restrictions renderer cannot communicate to System Font Cache Service. With this CL we implement our own font cache service on browser side. This font cache services scope is limited to help with initial enumeration of all font files that are in the system. This will hopefully speedup renderer load time. Renderer currently loads all font files that are there in the system during initial enumeration.
>
> BUG=406659
> R=cpu,ananta,scottmg
>
> Committed: https://crrev.com/271f94c31cd6b94f27d481008afd43f652bbca7d
> Cr-Commit-Position: refs/heads/master@{#303371}
TBR=ananta@chromium.org,cpu@chromium.org,scottmg@chromium.org,shrikant@chromium.org
NOTREECHECKS=true
NOTRY=true
BUG=406659
Review URL: https://codereview.chromium.org/678083005
Cr-Commit-Position: refs/heads/master@{#303410}
-rw-r--r-- | content/browser/dwrite_font_cache_win.cc | 23 | ||||
-rw-r--r-- | content/common/dwrite_font_platform_win.cc | 1166 | ||||
-rw-r--r-- | content/common/dwrite_font_platform_win.h | 38 | ||||
-rw-r--r-- | content/common/dwrite_font_platform_win_unittest.cc | 64 | ||||
-rw-r--r-- | content/content_browser.gypi | 1 | ||||
-rw-r--r-- | content/content_common.gypi | 3 | ||||
-rw-r--r-- | content/content_renderer.gypi | 2 | ||||
-rw-r--r-- | content/content_tests.gypi | 1 | ||||
-rw-r--r-- | content/public/common/content_switches.cc | 5 | ||||
-rw-r--r-- | content/public/common/content_switches.h | 3 | ||||
-rw-r--r-- | content/public/common/dwrite_font_cache_win.h | 19 | ||||
-rw-r--r-- | content/renderer/render_font_warmup_win.cc | 2 | ||||
-rw-r--r-- | content/renderer/renderer_font_platform_win.cc | 423 | ||||
-rw-r--r-- | content/renderer/renderer_font_platform_win.h | 17 | ||||
-rw-r--r-- | content/test/data/font/dwrite_font_cache_arial.dat | bin | 37667 -> 0 bytes | |||
-rw-r--r-- | content/test/data/font/dwrite_font_cache_corrupt.dat | bin | 37667 -> 0 bytes |
16 files changed, 443 insertions, 1324 deletions
diff --git a/content/browser/dwrite_font_cache_win.cc b/content/browser/dwrite_font_cache_win.cc deleted file mode 100644 index b5e885e..0000000 --- a/content/browser/dwrite_font_cache_win.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/public/common/dwrite_font_cache_win.h" - -#include "base/files/file_path.h" -#include "content/common/dwrite_font_platform_win.h" -#include "content/public/browser/browser_thread.h" - -namespace content { - -bool InitializeFontCache(const base::FilePath& path) { - if (!LoadFontCache(path)) { - content::BrowserThread::PostTask( - content::BrowserThread::FILE, FROM_HERE, base::Bind( - base::IgnoreResult(&content::BuildAndLoadFontCache), - path)); - } - return true; -} - -} // namespace content diff --git a/content/common/dwrite_font_platform_win.cc b/content/common/dwrite_font_platform_win.cc deleted file mode 100644 index 21c2000..0000000 --- a/content/common/dwrite_font_platform_win.cc +++ /dev/null @@ -1,1166 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/common/dwrite_font_platform_win.h" - -#include <dwrite.h> -#include <map> -#include <string> -#include <utility> -#include <vector> -#include <wrl/implements.h> -#include <wrl/wrappers/corewrappers.h> - -#include "base/command_line.h" -#include "base/debug/alias.h" -#include "base/debug/crash_logging.h" -#include "base/files/file_enumerator.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/memory_mapped_file.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/scoped_vector.h" -#include "base/memory/shared_memory.h" -#include "base/metrics/histogram.h" -#include "base/path_service.h" -#include "base/process/process_handle.h" -#include "base/stl_util.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/utf_string_conversions.h" -#include "base/synchronization/lock.h" -#include "base/time/time.h" -#include "base/win/registry.h" -#include "base/win/scoped_comptr.h" -#include "content/public/common/content_switches.h" -#include "content/public/common/dwrite_font_cache_win.h" - -namespace { - -// Font Cache implementation short story: -// Due to our sandboxing restrictions, we cannot connect to Windows font cache -// service from Renderer and need to use DirectWrite isolated font loading -// mechanism. -// DirectWrite needs to be initialized before any of the API could be used. -// During initialization DirectWrite loads all font files and populates -// internal cache, we refer this phase as enumeration and we are trying -// to optimize this phase in our cache approach. Using cache during -// initialization will help improve on startup latency in each renderer -// instance. -// During enumeration DirectWrite reads various fragments from .ttf/.ttc -// font files. Our assumption is that these fragments are being read to -// cache information such as font families, supported sizes etc. -// For reading fragments DirectWrite calls ReadFragment of our FontFileStream -// implementation with parameters start_offset and length. We cache these -// parameters along with associated data chunk. -// Here is small example of how segments are read -// start_offset: 0, length: 16 -// start_offset: 0, length: 12 -// start_offset: 0, length: 117 -// For better cache management we collapse segments if they overlap or are -// adjacent. - -namespace mswr = Microsoft::WRL; - -const char kFontKeyName[] = "font_key_name"; - -// We use this value to determine whether to cache file fragments -// or not. In our trials we observed that for some font files -// direct write ends up reading almost entire file during enumeration -// phase. If we don't use this percentile formula we will end up -// increasing significant cache size by caching entire file contents -// for some of the font files. -const double kMaxPercentileOfFontFileSizeToCache = 0.7; - -// We have chosen current font file length arbitrarily. In our logic -// if we don't find file we are looking for in cache we end up loading -// that file directly from system fonts folder. -const unsigned int kMaxFontFileNameLength = 32; - -const DWORD kCacheFileVersion = 101; -const DWORD kFileSignature = 0x4D4F5243; // CROM -const DWORD kMagicCompletionSignature = 0x454E4F44; // DONE - -const DWORD kUndefinedDWORDS = 32; - -#pragma pack(push, 8) -// Cache file header, includes signature, completion bits and version. -struct CacheFileHeader { - CacheFileHeader() { - file_signature = kFileSignature; - magic_completion_signature = 0; - version = kCacheFileVersion; - ::ZeroMemory(undefined, sizeof(undefined)); - } - - DWORD file_signature; - DWORD magic_completion_signature; - DWORD version; - BYTE undefined[kUndefinedDWORDS]; -}; - -// Entry for a particular font file within this cache. -struct CacheFileEntry { - CacheFileEntry() { - file_size = 0; - entry_count = 0; - ::ZeroMemory(file_name, sizeof(file_name)); - } - - UINT64 file_size; - DWORD entry_count; - wchar_t file_name[kMaxFontFileNameLength]; -}; - -// Offsets or data chunks that are cached for particular font file. -struct CacheFileOffsetEntry { - CacheFileOffsetEntry() { - start_offset = 0; - length = 0; - } - - UINT64 start_offset; - UINT64 length; - /* BYTE blob_[]; // Place holder for the blob that follows. */ -}; -#pragma pack(pop) - -bool ValidateFontCacheHeader(CacheFileHeader* header) { - return (header->file_signature == kFileSignature && - header->magic_completion_signature == kMagicCompletionSignature && - header->version == kCacheFileVersion); -} - -class FontCacheWriter; - -// This class implements main interface required for loading custom font -// collection as specified by DirectWrite. We also use this class for storing -// some state information as this is one of the centralized entity. -class FontCollectionLoader - : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>, - IDWriteFontCollectionLoader> { - public: - FontCollectionLoader() - : in_collection_building_mode_(false), - create_static_cache_(false) {}; - - virtual ~FontCollectionLoader(); - - HRESULT RuntimeClassInitialize() { - return S_OK; - } - - // IDWriteFontCollectionLoader methods. - virtual HRESULT STDMETHODCALLTYPE - CreateEnumeratorFromKey( - IDWriteFactory* factory, - void const* key, - UINT32 key_size, - IDWriteFontFileEnumerator** file_enumerator) override; - - // Does all the initialization for required loading fonts from registry. - static HRESULT Initialize(IDWriteFactory* factory); - - // Returns font cache map size. - UINT32 GetFontMapSize(); - - // Returns font name string when given font index. - base::string16 GetFontNameFromKey(UINT32 idx); - - // Loads internal structure with fonts from registry. - bool LoadFontListFromRegistry(); - - // Loads restricted web safe fonts as fallback method to registry fonts. - bool LoadRestrictedFontList(); - - // Puts class in collection building mode. In collection building mode - // we use static cache if it is available as a look aside buffer. - void EnableCollectionBuildingMode(bool enable); - - // Returns current state of collection building. - bool InCollectionBuildingMode(); - - // Loads static cache file. - bool LoadCacheFile(); - - // Puts class in static cache creating mode. In this mode we record all - // direct write requests and store chunks of font data. - void EnterStaticCacheMode(const WCHAR* file_name); - - // Gets out of static cache building mode. - void LeaveStaticCacheMode(); - - // Returns if class is currently in static cache building mode. - bool IsBuildStaticCacheMode(); - - // Validates cache file for consistency. - bool ValidateCacheFile(base::File* file); - - private: - // Structure to represent each chunk within font file that we load in memory. - struct CacheTableOffsetEntry { - UINT64 start_offset; - UINT64 length; - BYTE* inside_file_ptr; - }; - - typedef std::vector<CacheTableOffsetEntry> OffsetVector; - - // Structure representing each font entry with cache. - struct CacheTableEntry { - UINT64 file_size; - OffsetVector offset_entries; - }; - - public: - // Returns whether file we have particular font entry within cache or not. - bool IsFileCached(UINT32 font_key); - // Returns cache fragment corresponding to specific font key. - void* GetCachedFragment(UINT32 font_key, UINT64 start_offset, UINT64 length); - // Returns actual font file size at the time of caching. - UINT64 GetCachedFileSize(UINT32 font_key); - - // Returns instance of font cache writer. This class manages actual font - // file format. - FontCacheWriter* GetFontCacheWriter(); - - private: - // Functions validates and loads cache into internal map. - bool ValidateAndLoadCacheMap(); - - mswr::ComPtr<IDWriteFontFileLoader> file_loader_; - - std::vector<base::string16> reg_fonts_; - bool in_collection_building_mode_; - bool create_static_cache_; - scoped_ptr<base::SharedMemory> cache_; - scoped_ptr<FontCacheWriter> cache_writer_; - - typedef std::map<base::string16, CacheTableEntry*> CacheMap; - CacheMap cache_map_; - - DISALLOW_COPY_AND_ASSIGN(FontCollectionLoader); -}; - -mswr::ComPtr<FontCollectionLoader> g_font_loader; -base::win::ScopedHandle g_shared_font_cache; - -// Class responsible for handling font cache file format details as well as -// tracking various cache region requests by direct write. -class FontCacheWriter { - public: - FontCacheWriter() : cookie_counter_(0) { - } - - ~FontCacheWriter() { - if (static_cache_.get()) { - static_cache_->Close(); - } - } - - public: - // Holds data related to individual region as requested by direct write. - struct CacheRegion { - UINT64 start_offset; - UINT64 length; - const BYTE* ptr; - /* BYTE blob_[]; // Place holder for the blob that follows. */ - }; - - // Function to create static font cache file. - bool Create(const wchar_t* file_name) { - static_cache_.reset(new base::File(base::FilePath(file_name), - base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_WRITE)); - if (!static_cache_->IsValid()) { - static_cache_.reset(); - // TODO(shrikant): Convert this CHECK to DCHECK post canary. - // We have all fallbacks built in, so if we are not able to create - // static cache, browser can still run with old way of loading all fonts. - CHECK(FALSE); - return false; - } - CacheFileHeader header; - - // At offset 0 write cache version - static_cache_->Write(0, - reinterpret_cast<const char*>(&header), - sizeof(header)); - - static_cache_->Flush(); - return true; - } - - // Closes static font cache file. Also writes completion signature to mark - // it as completely written. - void Close() { - if (static_cache_.get()) { - CacheFileHeader header; - header.magic_completion_signature = kMagicCompletionSignature; - // At offset 0 write cache version - int bytes_written = static_cache_->Write(0, - reinterpret_cast<const char*>(&header), - sizeof(header)); - DCHECK(bytes_written != -1); - static_cache_->Close(); - static_cache_.reset(NULL); - } - } - - private: - typedef std::vector<CacheRegion> RegionVector; - - // Structure to track various regions requested by direct write for particular - // font file. - struct FontEntryInternal { - FontEntryInternal(const wchar_t* name, UINT64 size) - : file_name(name), - file_size(size) { - } - - base::string16 file_name; - UINT64 file_size; - RegionVector regions; - }; - - public: - // Starts up new font entry to be tracked, returns cookie to identify this - // particular entry. - UINT NewFontEntry(const wchar_t* file_name, UINT64 file_size) { - base::AutoLock lock(lock_); - UINT old_counter = cookie_counter_; - FontEntryInternal* font_entry = new FontEntryInternal(file_name, file_size); - cookie_map_[cookie_counter_].reset(font_entry); - cookie_counter_++; - return old_counter; - } - - // AddRegion function lets caller add various regions to be cached for - // particular font file. Once enumerating that particular font file is done - // (based on uniquely identifying cookie) changes could be committed using - // CommitFontEntry - bool AddRegion(UINT64 cookie, UINT64 start, UINT64 length, const BYTE* ptr) { - base::AutoLock lock(lock_); - if (cookie_map_.find(cookie) == cookie_map_.end()) - return false; - RegionVector& regions = cookie_map_[cookie].get()->regions; - CacheRegion region; - region.start_offset = start; - region.length = length; - region.ptr = ptr; - regions.push_back(region); - return true; - } - - // Function which commits after merging all collected regions into cache file. - bool CommitFontEntry(UINT cookie) { - base::AutoLock lock(lock_); - if (cookie_map_.find(cookie) == cookie_map_.end()) - return false; - - FontEntryInternal* font_entry = cookie_map_[cookie].get(); - RegionVector& regions = font_entry->regions; - std::sort(regions.begin(), regions.end(), SortCacheRegions); - - // At this point, we have collected all regions to be cached. These regions - // are tuples of start, length, data for particular data segment. - // These tuples can overlap. - // e.g. (0, 12, data), (0, 117, data), (21, 314, data), (335, 15, data) - // In this case as you can see first three segments overlap and - // 4th is adjacent. If we cache them individually then we will end up - // caching duplicate data, so we merge these segments together to find - // superset for the cache. In above example our algorithm should - // produce (cache) single segment starting at offset 0 with length 350. - RegionVector merged_regions; - RegionVector::iterator iter; - int idx = 0; - for (iter = regions.begin(); iter != regions.end(); iter++) { - if (iter == regions.begin()) { - merged_regions.push_back(*iter); - continue; - } - CacheRegion& base_region = merged_regions[idx]; - if (IsOverlap(&base_region, &(*iter))) { - UINT64 end1 = base_region.start_offset + base_region.length; - UINT64 end2 = iter->start_offset + iter->length; - if (base_region.start_offset > iter->start_offset) { - base_region.start_offset = iter->start_offset; - base_region.ptr = iter->ptr; - } - base_region.length = std::max(end1, end2) - base_region.start_offset; - } else { - merged_regions.push_back(*iter); - idx++; - } - } - - UINT64 total_merged_cache_in_bytes = 0; - for (iter = merged_regions.begin(); iter != merged_regions.end(); iter++) { - total_merged_cache_in_bytes += iter->length; - } - - // We want to adjust following parameter based on experiments. But general - // logic here is that if we are going to end up caching most of the contents - // for a file (e.g. simsunb.ttf > 90%) then we should avoid caching that - // file. - double percentile = static_cast<double>(total_merged_cache_in_bytes) / - font_entry->file_size; - if (percentile > kMaxPercentileOfFontFileSizeToCache) { - return false; - } - - CacheFileEntry entry; - wcsncpy_s(entry.file_name, kMaxFontFileNameLength, - font_entry->file_name.c_str(), _TRUNCATE); - entry.file_size = font_entry->file_size; - entry.entry_count = merged_regions.size(); - static_cache_->WriteAtCurrentPos( - reinterpret_cast<const char*>(&entry), - sizeof(entry)); - for (iter = merged_regions.begin(); iter != merged_regions.end(); iter++) { - CacheFileOffsetEntry offset_entry; - offset_entry.start_offset = iter->start_offset; - offset_entry.length = iter->length; - static_cache_->WriteAtCurrentPos( - reinterpret_cast<const char*>(&offset_entry), - sizeof(offset_entry)); - static_cache_->WriteAtCurrentPos( - reinterpret_cast<const char*>(iter->ptr), - iter->length); - } - return true; - } - - private: - scoped_ptr<base::File> static_cache_; - std::map<UINT, scoped_ptr<FontEntryInternal>> cookie_map_; - UINT cookie_counter_; - - // Lock is required to protect internal data structures and access to file, - // According to MSDN documentation on ReadFileFragment and based on our - // experiments so far, there is possibility of ReadFileFragment getting called - // from multiple threads. - base::Lock lock_; - - // Function checks if two regions overlap or are adjacent. - bool IsOverlap(CacheRegion* region1, CacheRegion* region2) { - return - !((region1->start_offset + region1->length) < region2->start_offset || - region1->start_offset > (region2->start_offset + region2->length)); - } - - // Function to sort cached regions. - static bool SortCacheRegions(const CacheRegion& region1, - const CacheRegion& region2) { - return - region1.start_offset == region2.start_offset ? - region1.length < region2.length : - region1.start_offset < region2.start_offset; - } - - DISALLOW_COPY_AND_ASSIGN(FontCacheWriter); -}; - -// Class implements IDWriteFontFileStream interface as required by direct write. -class FontFileStream - : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>, - IDWriteFontFileStream> { - public: - // IDWriteFontFileStream methods. - virtual HRESULT STDMETHODCALLTYPE ReadFileFragment( - void const** fragment_start, - UINT64 file_offset, - UINT64 fragment_size, - void** context) override { - if (cached_data_) { - *fragment_start = g_font_loader->GetCachedFragment(font_key_, - file_offset, - fragment_size); - if (*fragment_start == NULL) { - DCHECK(false); - } - *context = NULL; - return *fragment_start != NULL ? S_OK : E_FAIL; - } - if (!memory_.get() || !memory_->IsValid() || - file_offset >= memory_->length() || - (file_offset + fragment_size) > memory_->length()) - return E_FAIL; - - *fragment_start = static_cast<BYTE const*>(memory_->data()) + - static_cast<size_t>(file_offset); - *context = NULL; - if (g_font_loader->IsBuildStaticCacheMode()) { - FontCacheWriter* cache_writer = g_font_loader->GetFontCacheWriter(); - cache_writer->AddRegion(writer_cookie_, - file_offset, - fragment_size, - static_cast<const BYTE*>(*fragment_start)); - } - return S_OK; - } - - virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* context) override {} - - virtual HRESULT STDMETHODCALLTYPE GetFileSize(UINT64* file_size) override { - if (cached_data_) { - *file_size = g_font_loader->GetCachedFileSize(font_key_); - return S_OK; - } - - if (!memory_.get() || !memory_->IsValid()) - return E_FAIL; - - *file_size = memory_->length(); - return S_OK; - } - - virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime( - UINT64* last_write_time) override { - if (cached_data_) { - *last_write_time = 0; - return S_OK; - } - - if (!memory_.get() || !memory_->IsValid()) - return E_FAIL; - - // According to MSDN article http://goo.gl/rrSYzi the "last modified time" - // is used by DirectWrite font selection algorithms to determine whether - // one font resource is more up to date than another one. - // So by returning 0 we are assuming that it will treat all fonts to be - // equally up to date. - // TODO(shrikant): We should further investigate this. - *last_write_time = 0; - return S_OK; - } - - FontFileStream::FontFileStream() : font_key_(0), cached_data_(false) { - }; - - HRESULT RuntimeClassInitialize(UINT32 font_key) { - if (g_font_loader->InCollectionBuildingMode() && - g_font_loader->IsFileCached(font_key)) { - cached_data_ = true; - font_key_ = font_key; - return S_OK; - } - - base::FilePath path; - PathService::Get(base::DIR_WINDOWS_FONTS, &path); - base::string16 font_key_name(g_font_loader->GetFontNameFromKey(font_key)); - path = path.Append(font_key_name.c_str()); - memory_.reset(new base::MemoryMappedFile()); - - // Put some debug information on stack. - WCHAR font_name[MAX_PATH]; - path.value().copy(font_name, arraysize(font_name)); - base::debug::Alias(font_name); - - if (!memory_->Initialize(path)) { - memory_.reset(); - return E_FAIL; - } - - font_key_ = font_key; - - base::debug::SetCrashKeyValue(kFontKeyName, - base::WideToUTF8(font_key_name)); - - if (g_font_loader->IsBuildStaticCacheMode()) { - FontCacheWriter* cache_writer = g_font_loader->GetFontCacheWriter(); - writer_cookie_ = cache_writer->NewFontEntry(font_key_name.c_str(), - memory_->length()); - } - return S_OK; - } - - virtual ~FontFileStream() { - if (g_font_loader->IsBuildStaticCacheMode()) { - FontCacheWriter* cache_writer = g_font_loader->GetFontCacheWriter(); - cache_writer->CommitFontEntry(writer_cookie_); - } - } - - private: - UINT32 font_key_; - scoped_ptr<base::MemoryMappedFile> memory_; - bool cached_data_; - UINT writer_cookie_; - - DISALLOW_COPY_AND_ASSIGN(FontFileStream); -}; - -// Implements IDWriteFontFileLoader as required by FontFileLoader. -class FontFileLoader - : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>, - IDWriteFontFileLoader> { - public: - // IDWriteFontFileLoader methods. - virtual HRESULT STDMETHODCALLTYPE - CreateStreamFromKey(void const* ref_key, - UINT32 ref_key_size, - IDWriteFontFileStream** stream) override { - if (ref_key_size != sizeof(UINT32)) - return E_FAIL; - - UINT32 font_key = *static_cast<const UINT32*>(ref_key); - mswr::ComPtr<FontFileStream> font_stream; - HRESULT hr = mswr::MakeAndInitialize<FontFileStream>(&font_stream, - font_key); - if (SUCCEEDED(hr)) { - *stream = font_stream.Detach(); - return S_OK; - } - return E_FAIL; - } - - FontFileLoader() {} - virtual ~FontFileLoader() {} - - private: - - DISALLOW_COPY_AND_ASSIGN(FontFileLoader); -}; - -// Implements IDWriteFontFileEnumerator as required by direct write. -class FontFileEnumerator - : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>, - IDWriteFontFileEnumerator> { - public: - // IDWriteFontFileEnumerator methods. - virtual HRESULT STDMETHODCALLTYPE MoveNext(BOOL* has_current_file) override { - *has_current_file = FALSE; - - if (current_file_) - current_file_.ReleaseAndGetAddressOf(); - - if (font_idx_ < g_font_loader->GetFontMapSize()) { - HRESULT hr = - factory_->CreateCustomFontFileReference(&font_idx_, - sizeof(UINT32), - file_loader_.Get(), - current_file_.GetAddressOf()); - DCHECK(SUCCEEDED(hr)); - *has_current_file = TRUE; - font_idx_++; - } - return S_OK; - } - - virtual HRESULT STDMETHODCALLTYPE - GetCurrentFontFile(IDWriteFontFile** font_file) override { - if (!current_file_) { - *font_file = NULL; - return E_FAIL; - } - - *font_file = current_file_.Detach(); - return S_OK; - } - - FontFileEnumerator(const void* keys, - UINT32 buffer_size, - IDWriteFactory* factory, - IDWriteFontFileLoader* file_loader) - : factory_(factory), file_loader_(file_loader), font_idx_(0) {} - - virtual ~FontFileEnumerator() {} - - mswr::ComPtr<IDWriteFactory> factory_; - mswr::ComPtr<IDWriteFontFile> current_file_; - mswr::ComPtr<IDWriteFontFileLoader> file_loader_; - UINT32 font_idx_; - - private: - - DISALLOW_COPY_AND_ASSIGN(FontFileEnumerator); -}; - -// IDWriteFontCollectionLoader methods. -HRESULT STDMETHODCALLTYPE FontCollectionLoader::CreateEnumeratorFromKey( - IDWriteFactory* factory, - void const* key, - UINT32 key_size, - IDWriteFontFileEnumerator** file_enumerator) { - *file_enumerator = mswr::Make<FontFileEnumerator>( - key, key_size, factory, file_loader_.Get()).Detach(); - return S_OK; -} - -// static -HRESULT FontCollectionLoader::Initialize(IDWriteFactory* factory) { - DCHECK(g_font_loader == NULL); - - HRESULT result; - result = mswr::MakeAndInitialize<FontCollectionLoader>(&g_font_loader); - if (FAILED(result) || !g_font_loader) { - DCHECK(false); - return E_FAIL; - } - - CHECK(g_font_loader->LoadFontListFromRegistry()); - - g_font_loader->file_loader_ = mswr::Make<FontFileLoader>().Detach(); - - factory->RegisterFontFileLoader(g_font_loader->file_loader_.Get()); - factory->RegisterFontCollectionLoader(g_font_loader.Get()); - - return S_OK; -} - -FontCollectionLoader::~FontCollectionLoader() { - STLDeleteContainerPairSecondPointers(cache_map_.begin(), cache_map_.end()); -} - -UINT32 FontCollectionLoader::GetFontMapSize() { - return reg_fonts_.size(); -} - -base::string16 FontCollectionLoader::GetFontNameFromKey(UINT32 idx) { - DCHECK(idx < reg_fonts_.size()); - return reg_fonts_[idx]; -} - -bool FontCollectionLoader::LoadFontListFromRegistry() { - const wchar_t kFontsRegistry[] = - L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"; - CHECK(reg_fonts_.empty()); - base::win::RegKey regkey; - if (regkey.Open(HKEY_LOCAL_MACHINE, kFontsRegistry, KEY_READ) != - ERROR_SUCCESS) { - return false; - } - - base::FilePath system_font_path; - PathService::Get(base::DIR_WINDOWS_FONTS, &system_font_path); - - base::string16 name; - base::string16 value; - for (DWORD idx = 0; idx < regkey.GetValueCount(); idx++) { - if (regkey.GetValueNameAt(idx, &name) == ERROR_SUCCESS && - regkey.ReadValue(name.c_str(), &value) == ERROR_SUCCESS) { - base::FilePath path(value.c_str()); - // We need to check if file name is the only component that exists, - // we will ignore all other registry entries. - std::vector<base::FilePath::StringType> components; - path.GetComponents(&components); - if ((components.size() == 1 && - value.size() < kMaxFontFileNameLength - 1) || - base::FilePath::CompareEqualIgnoreCase(system_font_path.value(), - path.DirName().value())) { - reg_fonts_.push_back(value.c_str()); - } - } - } - UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Loaded", reg_fonts_.size()); - UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Ignored", - regkey.GetValueCount() - reg_fonts_.size()); - return true; -} - -// This list is mainly based on prefs/prefs_tab_helper.cc kFontDefaults. -const wchar_t* kRestrictedFontSet[] = { - // These are the "Web Safe" fonts. - L"times.ttf", // IDS_STANDARD_FONT_FAMILY - L"timesbd.ttf", // IDS_STANDARD_FONT_FAMILY - L"timesbi.ttf", // IDS_STANDARD_FONT_FAMILY - L"timesi.ttf", // IDS_STANDARD_FONT_FAMILY - L"cour.ttf", // IDS_FIXED_FONT_FAMILY - L"courbd.ttf", // IDS_FIXED_FONT_FAMILY - L"courbi.ttf", // IDS_FIXED_FONT_FAMILY - L"couri.ttf", // IDS_FIXED_FONT_FAMILY - L"consola.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN - L"consolab.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN - L"consolai.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN - L"consolaz.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN - L"arial.ttf", // IDS_SANS_SERIF_FONT_FAMILY - L"arialbd.ttf", // IDS_SANS_SERIF_FONT_FAMILY - L"arialbi.ttf", // IDS_SANS_SERIF_FONT_FAMILY - L"ariali.ttf", // IDS_SANS_SERIF_FONT_FAMILY - L"comic.ttf", // IDS_CURSIVE_FONT_FAMILY - L"comicbd.ttf", // IDS_CURSIVE_FONT_FAMILY - L"comici.ttf", // IDS_CURSIVE_FONT_FAMILY - L"comicz.ttf", // IDS_CURSIVE_FONT_FAMILY - L"impact.ttf", // IDS_FANTASY_FONT_FAMILY - L"georgia.ttf", - L"georgiab.ttf", - L"georgiai.ttf", - L"georgiaz.ttf", - L"trebuc.ttf", - L"trebucbd.ttf", - L"trebucbi.ttf", - L"trebucit.ttf", - L"verdana.ttf", - L"verdanab.ttf", - L"verdanai.ttf", - L"verdanaz.ttf", - L"segoeui.ttf", // IDS_PICTOGRAPH_FONT_FAMILY - L"segoeuib.ttf", // IDS_PICTOGRAPH_FONT_FAMILY - L"segoeuii.ttf", // IDS_PICTOGRAPH_FONT_FAMILY - L"msgothic.ttc", // IDS_STANDARD_FONT_FAMILY_JAPANESE - L"msmincho.ttc", // IDS_SERIF_FONT_FAMILY_JAPANESE - L"gulim.ttc", // IDS_FIXED_FONT_FAMILY_KOREAN - L"batang.ttc", // IDS_SERIF_FONT_FAMILY_KOREAN - L"simsun.ttc", // IDS_STANDARD_FONT_FAMILY_SIMPLIFIED_HAN - L"mingliu.ttc", // IDS_SERIF_FONT_FAMILY_TRADITIONAL_HAN - - // These are from the Blink fallback list. - L"david.ttf", // USCRIPT_HEBREW - L"davidbd.ttf", // USCRIPT_HEBREW - L"euphemia.ttf", // USCRIPT_CANADIAN_ABORIGINAL - L"gautami.ttf", // USCRIPT_TELUGU - L"gautamib.ttf", // USCRIPT_TELUGU - L"latha.ttf", // USCRIPT_TAMIL - L"lathab.ttf", // USCRIPT_TAMIL - L"mangal.ttf", // USCRIPT_DEVANAGARI - L"mangalb.ttf", // USCRIPT_DEVANAGARI - L"monbaiti.ttf", // USCRIPT_MONGOLIAN - L"mvboli.ttf", // USCRIPT_THAANA - L"plantc.ttf", // USCRIPT_CHEROKEE - L"raavi.ttf", // USCRIPT_GURMUKHI - L"raavib.ttf", // USCRIPT_GURMUKHI - L"shruti.ttf", // USCRIPT_GUJARATI - L"shrutib.ttf", // USCRIPT_GUJARATI - L"sylfaen.ttf", // USCRIPT_GEORGIAN and USCRIPT_ARMENIAN - L"tahoma.ttf", // USCRIPT_ARABIC, - L"tahomabd.ttf", // USCRIPT_ARABIC, - L"tunga.ttf", // USCRIPT_KANNADA - L"tungab.ttf", // USCRIPT_KANNADA - L"vrinda.ttf", // USCRIPT_BENGALI - L"vrindab.ttf", // USCRIPT_BENGALI -}; - -bool FontCollectionLoader::LoadRestrictedFontList() { - reg_fonts_.clear(); - reg_fonts_.assign(kRestrictedFontSet, - kRestrictedFontSet + _countof(kRestrictedFontSet)); - return true; -} - -void FontCollectionLoader::EnableCollectionBuildingMode(bool enable) { - in_collection_building_mode_ = enable; -} - -bool FontCollectionLoader::InCollectionBuildingMode() { - return in_collection_building_mode_; -} - -bool FontCollectionLoader::IsFileCached(UINT32 font_key) { - if (!cache_.get() || cache_->memory() == NULL) { - return false; - } - CacheMap::iterator iter = cache_map_.find( - GetFontNameFromKey(font_key).c_str()); - return iter != cache_map_.end();; -} - -bool FontCollectionLoader::LoadCacheFile() { - if (!base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kFontCacheSharedMemSuffix)) { - return false; - } - base::SharedMemory* shared_mem = new base::SharedMemory(); - std::string name(content::kFontCacheSharedSectionName); - name.append(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kFontCacheSharedMemSuffix)); - if (!shared_mem->Open(name.c_str(), true)) - return false; - - // Map while file - shared_mem->Map(0); - - cache_.reset(shared_mem); - - if (!ValidateAndLoadCacheMap()) { - cache_.reset(); - return false; - } - - return true; -} - -void FontCollectionLoader::EnterStaticCacheMode(const WCHAR* file_name) { - cache_writer_.reset(new FontCacheWriter()); - cache_writer_->Create(file_name); - create_static_cache_ = true; -} - -void FontCollectionLoader::LeaveStaticCacheMode() { - cache_writer_->Close(); - cache_writer_.reset(NULL); - create_static_cache_ = false; -} - -bool FontCollectionLoader::IsBuildStaticCacheMode() { - return create_static_cache_; -} - -bool FontCollectionLoader::ValidateAndLoadCacheMap() { - BYTE* mem_file_start = static_cast<BYTE*>(cache_->memory()); - BYTE* mem_file_end = mem_file_start + cache_->mapped_size(); - - BYTE* current_ptr = mem_file_start; - CacheFileHeader* file_header = - reinterpret_cast<CacheFileHeader*>(current_ptr); - if (!ValidateFontCacheHeader(file_header)) - return false; - - current_ptr = current_ptr + sizeof(CacheFileHeader); - if (current_ptr >= mem_file_end) - return false; - - while ((current_ptr + sizeof(CacheFileEntry)) < mem_file_end) { - CacheFileEntry* entry = reinterpret_cast<CacheFileEntry*>(current_ptr); - current_ptr += sizeof(CacheFileEntry); - WCHAR file_name[kMaxFontFileNameLength]; - wcsncpy_s(file_name, - kMaxFontFileNameLength, - entry->file_name, - _TRUNCATE); - CacheTableEntry* table_entry = NULL; - CacheMap::iterator iter = cache_map_.find(file_name); - if (iter == cache_map_.end()) { - table_entry = new CacheTableEntry(); - cache_map_[file_name] = table_entry; - } else { - table_entry = iter->second; - } - table_entry->file_size = entry->file_size; - for (DWORD idx = 0; current_ptr < mem_file_end && idx < entry->entry_count; - idx++) { - CacheFileOffsetEntry* offset_entry = - reinterpret_cast<CacheFileOffsetEntry*>(current_ptr); - CacheTableOffsetEntry table_offset_entry; - table_offset_entry.start_offset = offset_entry->start_offset; - table_offset_entry.length = offset_entry->length; - table_offset_entry.inside_file_ptr = - current_ptr + sizeof(CacheFileOffsetEntry); - table_entry->offset_entries.push_back(table_offset_entry); - current_ptr += sizeof(CacheFileOffsetEntry); - current_ptr += offset_entry->length; - } - } - - return true; -} - -void* FontCollectionLoader::GetCachedFragment(UINT32 font_key, - UINT64 start_offset, - UINT64 length) { - UINT64 just_past_end = start_offset + length; - CacheMap::iterator iter = cache_map_.find( - GetFontNameFromKey(font_key).c_str()); - if (iter != cache_map_.end()) { - CacheTableEntry* entry = iter->second; - OffsetVector::iterator offset_iter = entry->offset_entries.begin(); - while (offset_iter != entry->offset_entries.end()) { - UINT64 available_just_past_end = - offset_iter->start_offset + offset_iter->length; - if (offset_iter->start_offset <= start_offset && - just_past_end <= available_just_past_end) { - return offset_iter->inside_file_ptr + - (start_offset - offset_iter->start_offset); - } - offset_iter++; - } - } - return NULL; -} - -UINT64 FontCollectionLoader::GetCachedFileSize(UINT32 font_key) { - CacheMap::iterator iter = cache_map_.find( - GetFontNameFromKey(font_key).c_str()); - if (iter != cache_map_.end()) { - return iter->second->file_size; - } - return 0; -} - -FontCacheWriter* FontCollectionLoader::GetFontCacheWriter() { - return cache_writer_.get(); -} - -} // namespace - -namespace content { - -const char kFontCacheSharedSectionName[] = "ChromeDWriteFontCache"; - -mswr::ComPtr<IDWriteFontCollection> g_font_collection; - -IDWriteFontCollection* GetCustomFontCollection(IDWriteFactory* factory) { - if (g_font_collection.Get() != NULL) - return g_font_collection.Get(); - - base::TimeTicks start_tick = base::TimeTicks::Now(); - - FontCollectionLoader::Initialize(factory); - - g_font_loader->LoadCacheFile(); - - // We try here to put arbitrary limit on max number of fonts that could - // be loaded, otherwise we fallback to restricted set of fonts. - const UINT32 kMaxFontThreshold = 1750; - HRESULT hr = E_FAIL; - if (g_font_loader->GetFontMapSize() < kMaxFontThreshold) { - g_font_loader->EnableCollectionBuildingMode(true); - hr = factory->CreateCustomFontCollection( - g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf()); - g_font_loader->EnableCollectionBuildingMode(false); - } - - bool loading_restricted = false; - if (FAILED(hr) || !g_font_collection.Get()) { - loading_restricted = true; - // We will try here just one more time with restricted font set. - g_font_loader->LoadRestrictedFontList(); - hr = factory->CreateCustomFontCollection( - g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf()); - } - - base::TimeDelta time_delta = base::TimeTicks::Now() - start_tick; - int64 delta = time_delta.ToInternalValue(); - base::debug::Alias(&delta); - UINT32 size = g_font_loader->GetFontMapSize(); - base::debug::Alias(&size); - base::debug::Alias(&loading_restricted); - - CHECK(SUCCEEDED(hr)); - CHECK(g_font_collection.Get() != NULL); - - UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime", time_delta); - - base::debug::ClearCrashKey(kFontKeyName); - - return g_font_collection.Get(); -} - -bool BuildFontCacheInternal(const WCHAR* file_name) { - typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc; - HMODULE dwrite_dll = LoadLibraryW(L"dwrite.dll"); - if (!dwrite_dll) { - DWORD load_library_get_last_error = GetLastError(); - base::debug::Alias(&dwrite_dll); - base::debug::Alias(&load_library_get_last_error); - CHECK(false); - } - - DWriteCreateFactoryProc dwrite_create_factory_proc = - reinterpret_cast<DWriteCreateFactoryProc>( - GetProcAddress(dwrite_dll, "DWriteCreateFactory")); - - if (!dwrite_create_factory_proc) { - DWORD get_proc_address_get_last_error = GetLastError(); - base::debug::Alias(&dwrite_create_factory_proc); - base::debug::Alias(&get_proc_address_get_last_error); - CHECK(false); - } - - mswr::ComPtr<IDWriteFactory> factory; - - CHECK(SUCCEEDED( - dwrite_create_factory_proc( - DWRITE_FACTORY_TYPE_ISOLATED, - __uuidof(IDWriteFactory), - reinterpret_cast<IUnknown**>(factory.GetAddressOf())))); - - base::TimeTicks start_tick = base::TimeTicks::Now(); - - FontCollectionLoader::Initialize(factory.Get()); - - g_font_loader->EnterStaticCacheMode(file_name); - - mswr::ComPtr<IDWriteFontCollection> font_collection; - - // We try here to put arbitrary limit on max number of fonts that could - // be loaded, otherwise we fallback to restricted set of fonts. - const UINT32 kMaxFontThreshold = 1750; - HRESULT hr = E_FAIL; - if (g_font_loader->GetFontMapSize() < kMaxFontThreshold) { - g_font_loader->EnableCollectionBuildingMode(true); - hr = factory->CreateCustomFontCollection( - g_font_loader.Get(), NULL, 0, font_collection.GetAddressOf()); - g_font_loader->EnableCollectionBuildingMode(false); - } - - bool loading_restricted = false; - if (FAILED(hr) || !font_collection.Get()) { - loading_restricted = true; - // We will try here just one more time with restricted font set. - g_font_loader->LoadRestrictedFontList(); - hr = factory->CreateCustomFontCollection( - g_font_loader.Get(), NULL, 0, font_collection.GetAddressOf()); - } - - g_font_loader->LeaveStaticCacheMode(); - - base::TimeDelta time_delta = base::TimeTicks::Now() - start_tick; - int64 delta = time_delta.ToInternalValue(); - base::debug::Alias(&delta); - UINT32 size = g_font_loader->GetFontMapSize(); - base::debug::Alias(&size); - base::debug::Alias(&loading_restricted); - - CHECK(SUCCEEDED(hr)); - CHECK(font_collection.Get() != NULL); - - UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime", time_delta); - - base::debug::ClearCrashKey(kFontKeyName); - - return true; -} - -bool ValidateFontCacheFile(base::File* file) { - DCHECK(file != NULL); - CacheFileHeader file_header; - if (file->Read(0, reinterpret_cast<char*>(&file_header), sizeof(file_header)) - == -1) { - return false; - } - return ValidateFontCacheHeader(&file_header); -} - -bool LoadFontCache(const base::FilePath& path) { - scoped_ptr<base::File> file(new base::File(path, - base::File::FLAG_OPEN | base::File::FLAG_READ)); - if (!file->IsValid()) - return false; - - if (!ValidateFontCacheFile(file.get())) - return false; - - std::string name(content::kFontCacheSharedSectionName); - name.append(base::UintToString(base::GetCurrentProcId())); - HANDLE mapping = ::CreateFileMapping( - file->GetPlatformFile(), - NULL, - PAGE_READONLY, - 0, - 0, - base::ASCIIToWide(name).c_str()); - if (mapping == INVALID_HANDLE_VALUE) - return false; - - if (::GetLastError() == ERROR_ALREADY_EXISTS) { - CloseHandle(mapping); - // We crash here, as no one should have created this mapping except Chrome. - CHECK(false); - return false; - } - - DCHECK(!g_shared_font_cache.IsValid()); - g_shared_font_cache.Set(mapping); - - return true; -} - -// Assumption for this function is that it will get called through a posted task -// on FILE thread. -bool BuildAndLoadFontCache(const base::FilePath& file) { - if (BuildFontCacheInternal(file.value().c_str())) - return LoadFontCache(file); - return false; -} - -} // namespace content diff --git a/content/common/dwrite_font_platform_win.h b/content/common/dwrite_font_platform_win.h deleted file mode 100644 index ea5a52f..0000000 --- a/content/common/dwrite_font_platform_win.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_COMMON_DWRITE_FONT_PLATFORM_WIN_H_ -#define CONTENT_COMMON_DWRITE_FONT_PLATFORM_WIN_H_ - -#include "base/files/file.h" -#include "content/common/content_export.h" - -struct IDWriteFactory; -struct IDWriteFontCollection; - -namespace content { - -// Function returns custom font collection in terms of IDWriteFontCollection. -// This function maintains singleton instance of font collection and returns -// it on repeated calls. -CONTENT_EXPORT IDWriteFontCollection* GetCustomFontCollection( - IDWriteFactory* factory); - -// Build and load is to accomplish both tasks of creating a font cache within -// specified file and then loading it, where loading means creating -// readonly shared memory mapping. As this function need to iterate through all -// available fonts in the system, it may take a while. -CONTENT_EXPORT bool BuildAndLoadFontCache(const base::FilePath& file); - -// Loads font cache from file. This is supposed to be used from browser -// side where loading means creating readonly shared memory file mapping so that -// renderers can read from it. -CONTENT_EXPORT bool LoadFontCache(const base::FilePath& path); - -// Added in header mainly for unittest -CONTENT_EXPORT bool ValidateFontCacheFile(base::File* file); - -} // namespace content - -#endif // CONTENT_COMMON_DWRITE_FONT_PLATFORM_WIN_H_ diff --git a/content/common/dwrite_font_platform_win_unittest.cc b/content/common/dwrite_font_platform_win_unittest.cc deleted file mode 100644 index a2cae0b..0000000 --- a/content/common/dwrite_font_platform_win_unittest.cc +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/common/dwrite_font_platform_win.h" - -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/logging.h" -#include "base/path_service.h" -#include "content/public/common/content_paths.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace content { - -class DWriteFontCacheTest : public testing::Test { - public: - DWriteFontCacheTest() { } - - virtual void SetUp() { - base::FilePath data_path; - ASSERT_TRUE(PathService::Get(content::DIR_TEST_DATA, &data_path)); - arial_cache_path_ = - data_path.AppendASCII("font/dwrite_font_cache_arial.dat"); - - corrupt_cache_path_ = - data_path.AppendASCII("font/dwrite_font_cache_corrupt.dat"); - - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - cache_file_path_ = temp_dir_.path().AppendASCII("dwrite_font_cache.dat"); - } - - protected: - base::FilePath arial_cache_path_; - base::FilePath corrupt_cache_path_; - base::ScopedTempDir temp_dir_; - base::FilePath cache_file_path_; -}; - -TEST_F(DWriteFontCacheTest, BuildCacheTest) { - DLOG(INFO) << __FUNCTION__ << ": " << cache_file_path_.value().c_str(); - EXPECT_TRUE(BuildAndLoadFontCache(cache_file_path_)); - ASSERT_TRUE(base::PathExists(cache_file_path_)); -} - -TEST_F(DWriteFontCacheTest, ValidCacheTest) { - DLOG(INFO) << __FUNCTION__ << ": " << arial_cache_path_.value().c_str(); - scoped_ptr<base::File> file(new base::File(arial_cache_path_, - base::File::FLAG_OPEN | base::File::FLAG_READ)); - ASSERT_TRUE(file->IsValid()); - - EXPECT_TRUE(ValidateFontCacheFile(file.get())); -} - -TEST_F(DWriteFontCacheTest, CorruptCacheTest) { - DLOG(INFO) << __FUNCTION__ << ": " << corrupt_cache_path_.value().c_str(); - scoped_ptr<base::File> file(new base::File(corrupt_cache_path_, - base::File::FLAG_OPEN | base::File::FLAG_READ)); - ASSERT_TRUE(file->IsValid()); - - EXPECT_FALSE(ValidateFontCacheFile(file.get())); -} - -} // content diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 9a7f420..2cf449d 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -572,7 +572,6 @@ 'browser/download/save_package.h', 'browser/download/save_types.cc', 'browser/download/save_types.h', - 'browser/dwrite_font_cache_win.cc', 'browser/fileapi/blob_storage_host.cc', 'browser/fileapi/blob_storage_host.h', 'browser/fileapi/browser_file_system_helper.cc', diff --git a/content/content_common.gypi b/content/content_common.gypi index 77e9f79..6d9a28d 100644 --- a/content/content_common.gypi +++ b/content/content_common.gypi @@ -57,7 +57,6 @@ 'public/common/context_menu_params.h', 'public/common/drop_data.cc', 'public/common/drop_data.h', - 'public/common/dwrite_font_cache_win.h', 'public/common/favicon_url.cc', 'public/common/favicon_url.h', 'public/common/file_chooser_file_info.cc', @@ -215,8 +214,6 @@ 'common/drag_event_source_info.h', 'common/drag_messages.h', 'common/drag_traits.h', - 'common/dwrite_font_platform_win.cc', - 'common/dwrite_font_platform_win.h', 'common/edit_command.h', 'common/file_utilities_messages.h', 'common/fileapi/file_system_messages.h', diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index a6dd936..144f8de 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -346,6 +346,8 @@ 'renderer/renderer_blink_platform_impl.h', 'renderer/renderer_clipboard_client.cc', 'renderer/renderer_clipboard_client.h', + 'renderer/renderer_font_platform_win.cc', + 'renderer/renderer_font_platform_win.h', 'renderer/renderer_main.cc', 'renderer/renderer_main_platform_delegate.h', 'renderer/renderer_main_platform_delegate_android.cc', diff --git a/content/content_tests.gypi b/content/content_tests.gypi index 16f3124..d48ac71 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -670,7 +670,6 @@ 'common/database_connections_unittest.cc', 'common/database_identifier_unittest.cc', 'common/dom_storage/dom_storage_map_unittest.cc', - 'common/dwrite_font_platform_win_unittest.cc', 'common/fileapi/file_system_util_unittest.cc', 'common/gpu/gpu_memory_manager_unittest.cc', 'common/host_discardable_shared_memory_manager_unittest.cc', diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc index 500d98e..03c7578 100644 --- a/content/public/common/content_switches.cc +++ b/content/public/common/content_switches.cc @@ -970,11 +970,6 @@ const char kDisableLegacyIntermediateWindow[] = "disable-legacy-window"; // the kernel. This is only supported on Windows 8 and beyond. const char kEnableWin32kRendererLockDown[] = "enable_win32k_renderer_lockdown"; - -// DirectWrite FontCache is shared by browser to renderers using shared memory. -// This switch allows specifying suffix to shared memory section name to avoid -// clashes between different instances of Chrome. -const char kFontCacheSharedMemSuffix[] = "font-cache-shared-mem-suffix"; #endif #if defined(ENABLE_PLUGINS) diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h index b4b4bb5..360aded 100644 --- a/content/public/common/content_switches.h +++ b/content/public/common/content_switches.h @@ -290,9 +290,6 @@ CONTENT_EXPORT extern const char kDisableLegacyIntermediateWindow[]; // This switch will be removed when we enable the win32K lockdown process // mitigation. CONTENT_EXPORT extern const char kEnableWin32kRendererLockDown[]; -// Switch to uniquely identify names shared memory section for font cache -// across chromium flavors. -CONTENT_EXPORT extern const char kFontCacheSharedMemSuffix[]; #endif #if defined(ENABLE_PLUGINS) diff --git a/content/public/common/dwrite_font_cache_win.h b/content/public/common/dwrite_font_cache_win.h deleted file mode 100644 index 73b1bb8..0000000 --- a/content/public/common/dwrite_font_cache_win.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_PUBLIC_COMMON_DWRITE_FONT_CACHE_WIN_H_ -#define CONTENT_PUBLIC_COMMON_DWRITE_FONT_CACHE_WIN_H_ - -#include "base/files/file.h" -#include "content/common/content_export.h" - -namespace content { - -CONTENT_EXPORT extern const char kFontCacheSharedSectionName[]; - -CONTENT_EXPORT bool InitializeFontCache(const base::FilePath& path); - -} // namespace content - -#endif // CONTENT_PUBLIC_COMMON_DWRITE_FONT_CACHE_WIN_H_ diff --git a/content/renderer/render_font_warmup_win.cc b/content/renderer/render_font_warmup_win.cc index b41923e..90bbab2 100644 --- a/content/renderer/render_font_warmup_win.cc +++ b/content/renderer/render_font_warmup_win.cc @@ -10,7 +10,7 @@ #include "base/logging.h" #include "base/win/iat_patch_function.h" #include "base/win/windows_version.h" -#include "content/common/dwrite_font_platform_win.h" +#include "content/renderer/renderer_font_platform_win.h" #include "third_party/WebKit/public/web/win/WebFontRendering.h" #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/ports/SkFontMgr.h" diff --git a/content/renderer/renderer_font_platform_win.cc b/content/renderer/renderer_font_platform_win.cc new file mode 100644 index 0000000..380bbd4 --- /dev/null +++ b/content/renderer/renderer_font_platform_win.cc @@ -0,0 +1,423 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/renderer_font_platform_win.h" + +#include <dwrite.h> +#include <string> +#include <vector> +#include <wrl/implements.h> +#include <wrl/wrappers/corewrappers.h> + +#include "base/debug/alias.h" +#include "base/debug/crash_logging.h" +#include "base/files/file_enumerator.h" +#include "base/files/file_path.h" +#include "base/files/memory_mapped_file.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/metrics/histogram.h" +#include "base/path_service.h" +#include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" +#include "base/win/iat_patch_function.h" +#include "base/win/registry.h" +#include "base/win/scoped_comptr.h" + +namespace { + +namespace mswr = Microsoft::WRL; +namespace mswrw = Microsoft::WRL::Wrappers; + +static const char kFontKeyName[] = "font_key_name"; + +class FontCollectionLoader + : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>, + IDWriteFontCollectionLoader> { + public: + // IDWriteFontCollectionLoader methods. + virtual HRESULT STDMETHODCALLTYPE + CreateEnumeratorFromKey(IDWriteFactory* factory, + void const* key, + UINT32 key_size, + IDWriteFontFileEnumerator** file_enumerator); + + static HRESULT Initialize(IDWriteFactory* factory); + + UINT32 GetFontMapSize(); + + std::wstring GetFontNameFromKey(UINT32 idx); + + bool LoadFontListFromRegistry(); + bool LoadRestrictedFontList(); + + FontCollectionLoader() {}; + virtual ~FontCollectionLoader() {}; + + private: + mswr::ComPtr<IDWriteFontFileLoader> file_loader_; + + std::vector<std::wstring> reg_fonts_; +}; + +mswr::ComPtr<FontCollectionLoader> g_font_loader; + +class FontFileStream + : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>, + IDWriteFontFileStream> { + public: + // IDWriteFontFileStream methods. + virtual HRESULT STDMETHODCALLTYPE + ReadFileFragment(void const** fragment_start, + UINT64 file_offset, + UINT64 fragment_size, + void** context) { + if (!memory_.get() || !memory_->IsValid() || + file_offset >= memory_->length() || + (file_offset + fragment_size) > memory_->length()) + return E_FAIL; + + *fragment_start = static_cast<BYTE const*>(memory_->data()) + + static_cast<size_t>(file_offset); + *context = NULL; + return S_OK; + } + + virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* context) {} + + virtual HRESULT STDMETHODCALLTYPE GetFileSize(UINT64* file_size) { + if (!memory_.get() || !memory_->IsValid()) + return E_FAIL; + + *file_size = memory_->length(); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(UINT64* last_write_time) { + if (!memory_.get() || !memory_->IsValid()) + return E_FAIL; + + // According to MSDN article http://goo.gl/rrSYzi the "last modified time" + // is used by DirectWrite font selection algorithms to determine whether + // one font resource is more up to date than another one. + // So by returning 0 we are assuming that it will treat all fonts to be + // equally up to date. + // TODO(shrikant): We should further investigate this. + *last_write_time = 0; + return S_OK; + } + + FontFileStream::FontFileStream() : font_key_(0) { + }; + + HRESULT RuntimeClassInitialize(UINT32 font_key) { + base::FilePath path; + PathService::Get(base::DIR_WINDOWS_FONTS, &path); + std::wstring font_key_name(g_font_loader->GetFontNameFromKey(font_key)); + path = path.Append(font_key_name.c_str()); + memory_.reset(new base::MemoryMappedFile()); + + // Put some debug information on stack. + WCHAR font_name[256]; + path.value().copy(font_name, arraysize(font_name)); + base::debug::Alias(font_name); + + if (!memory_->Initialize(path)) { + memory_.reset(); + return E_FAIL; + } + + font_key_ = font_key; + + base::debug::SetCrashKeyValue(kFontKeyName, + base::WideToUTF8(font_key_name)); + return S_OK; + } + + virtual ~FontFileStream() {} + + UINT32 font_key_; + scoped_ptr<base::MemoryMappedFile> memory_; +}; + +class FontFileLoader + : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>, + IDWriteFontFileLoader> { + public: + // IDWriteFontFileLoader methods. + virtual HRESULT STDMETHODCALLTYPE + CreateStreamFromKey(void const* ref_key, + UINT32 ref_key_size, + IDWriteFontFileStream** stream) { + if (ref_key_size != sizeof(UINT32)) + return E_FAIL; + + UINT32 font_key = *static_cast<const UINT32*>(ref_key); + mswr::ComPtr<FontFileStream> font_stream; + HRESULT hr = mswr::MakeAndInitialize<FontFileStream>(&font_stream, + font_key); + if (SUCCEEDED(hr)) { + *stream = font_stream.Detach(); + return S_OK; + } + return E_FAIL; + } + + FontFileLoader() {} + virtual ~FontFileLoader() {} +}; + +class FontFileEnumerator + : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>, + IDWriteFontFileEnumerator> { + public: + // IDWriteFontFileEnumerator methods. + virtual HRESULT STDMETHODCALLTYPE MoveNext(BOOL* has_current_file) { + *has_current_file = FALSE; + + if (current_file_) + current_file_.ReleaseAndGetAddressOf(); + + if (font_idx_ < g_font_loader->GetFontMapSize()) { + HRESULT hr = + factory_->CreateCustomFontFileReference(&font_idx_, + sizeof(UINT32), + file_loader_.Get(), + current_file_.GetAddressOf()); + DCHECK(SUCCEEDED(hr)); + *has_current_file = TRUE; + font_idx_++; + } + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE + GetCurrentFontFile(IDWriteFontFile** font_file) { + if (!current_file_) { + *font_file = NULL; + return E_FAIL; + } + + *font_file = current_file_.Detach(); + return S_OK; + } + + FontFileEnumerator(const void* keys, + UINT32 buffer_size, + IDWriteFactory* factory, + IDWriteFontFileLoader* file_loader) + : factory_(factory), file_loader_(file_loader), font_idx_(0) {} + + virtual ~FontFileEnumerator() {} + + mswr::ComPtr<IDWriteFactory> factory_; + mswr::ComPtr<IDWriteFontFile> current_file_; + mswr::ComPtr<IDWriteFontFileLoader> file_loader_; + UINT32 font_idx_; +}; + +// IDWriteFontCollectionLoader methods. +HRESULT STDMETHODCALLTYPE FontCollectionLoader::CreateEnumeratorFromKey( + IDWriteFactory* factory, + void const* key, + UINT32 key_size, + IDWriteFontFileEnumerator** file_enumerator) { + *file_enumerator = mswr::Make<FontFileEnumerator>( + key, key_size, factory, file_loader_.Get()).Detach(); + return S_OK; +} + +// static +HRESULT FontCollectionLoader::Initialize(IDWriteFactory* factory) { + DCHECK(g_font_loader == NULL); + + g_font_loader = mswr::Make<FontCollectionLoader>(); + if (!g_font_loader) { + DCHECK(FALSE); + return E_FAIL; + } + + CHECK(g_font_loader->LoadFontListFromRegistry()); + + g_font_loader->file_loader_ = mswr::Make<FontFileLoader>().Detach(); + + factory->RegisterFontFileLoader(g_font_loader->file_loader_.Get()); + factory->RegisterFontCollectionLoader(g_font_loader.Get()); + + return S_OK; +} + +UINT32 FontCollectionLoader::GetFontMapSize() { + return reg_fonts_.size(); +} + +std::wstring FontCollectionLoader::GetFontNameFromKey(UINT32 idx) { + DCHECK(idx < reg_fonts_.size()); + return reg_fonts_[idx]; +} + +bool FontCollectionLoader::LoadFontListFromRegistry() { + const wchar_t kFontsRegistry[] = + L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"; + CHECK(reg_fonts_.empty()); + base::win::RegKey regkey; + if (regkey.Open(HKEY_LOCAL_MACHINE, kFontsRegistry, KEY_READ) != + ERROR_SUCCESS) { + return false; + } + + base::FilePath system_font_path; + PathService::Get(base::DIR_WINDOWS_FONTS, &system_font_path); + + std::wstring name; + std::wstring value; + for (DWORD idx = 0; idx < regkey.GetValueCount(); idx++) { + if (regkey.GetValueNameAt(idx, &name) == ERROR_SUCCESS && + regkey.ReadValue(name.c_str(), &value) == ERROR_SUCCESS) { + base::FilePath path(value.c_str()); + // We need to check if file name is the only component that exists, + // we will ignore all other registry entries. + std::vector<base::FilePath::StringType> components; + path.GetComponents(&components); + if (components.size() == 1 || + base::FilePath::CompareEqualIgnoreCase(system_font_path.value(), + path.DirName().value())) { + reg_fonts_.push_back(path.BaseName().value()); + } + } + } + UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Loaded", reg_fonts_.size()); + UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Ignored", + regkey.GetValueCount() - reg_fonts_.size()); + return true; +} + +// This list is mainly based on prefs/prefs_tab_helper.cc kFontDefaults. +const wchar_t* kRestrictedFontSet[] = { + // These are the "Web Safe" fonts. + L"times.ttf", // IDS_STANDARD_FONT_FAMILY + L"timesbd.ttf", // IDS_STANDARD_FONT_FAMILY + L"timesbi.ttf", // IDS_STANDARD_FONT_FAMILY + L"timesi.ttf", // IDS_STANDARD_FONT_FAMILY + L"cour.ttf", // IDS_FIXED_FONT_FAMILY + L"courbd.ttf", // IDS_FIXED_FONT_FAMILY + L"courbi.ttf", // IDS_FIXED_FONT_FAMILY + L"couri.ttf", // IDS_FIXED_FONT_FAMILY + L"consola.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN + L"consolab.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN + L"consolai.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN + L"consolaz.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN + L"arial.ttf", // IDS_SANS_SERIF_FONT_FAMILY + L"arialbd.ttf", // IDS_SANS_SERIF_FONT_FAMILY + L"arialbi.ttf", // IDS_SANS_SERIF_FONT_FAMILY + L"ariali.ttf", // IDS_SANS_SERIF_FONT_FAMILY + L"comic.ttf", // IDS_CURSIVE_FONT_FAMILY + L"comicbd.ttf", // IDS_CURSIVE_FONT_FAMILY + L"comici.ttf", // IDS_CURSIVE_FONT_FAMILY + L"comicz.ttf", // IDS_CURSIVE_FONT_FAMILY + L"impact.ttf", // IDS_FANTASY_FONT_FAMILY + L"georgia.ttf", + L"georgiab.ttf", + L"georgiai.ttf", + L"georgiaz.ttf", + L"trebuc.ttf", + L"trebucbd.ttf", + L"trebucbi.ttf", + L"trebucit.ttf", + L"verdana.ttf", + L"verdanab.ttf", + L"verdanai.ttf", + L"verdanaz.ttf", + L"segoeui.ttf", // IDS_PICTOGRAPH_FONT_FAMILY + L"segoeuib.ttf", // IDS_PICTOGRAPH_FONT_FAMILY + L"segoeuii.ttf", // IDS_PICTOGRAPH_FONT_FAMILY + L"msgothic.ttc", // IDS_STANDARD_FONT_FAMILY_JAPANESE + L"msmincho.ttc", // IDS_SERIF_FONT_FAMILY_JAPANESE + L"gulim.ttc", // IDS_FIXED_FONT_FAMILY_KOREAN + L"batang.ttc", // IDS_SERIF_FONT_FAMILY_KOREAN + L"simsun.ttc", // IDS_STANDARD_FONT_FAMILY_SIMPLIFIED_HAN + L"mingliu.ttc", // IDS_SERIF_FONT_FAMILY_TRADITIONAL_HAN + + // These are from the Blink fallback list. + L"david.ttf", // USCRIPT_HEBREW + L"davidbd.ttf", // USCRIPT_HEBREW + L"euphemia.ttf", // USCRIPT_CANADIAN_ABORIGINAL + L"gautami.ttf", // USCRIPT_TELUGU + L"gautamib.ttf", // USCRIPT_TELUGU + L"latha.ttf", // USCRIPT_TAMIL + L"lathab.ttf", // USCRIPT_TAMIL + L"mangal.ttf", // USCRIPT_DEVANAGARI + L"mangalb.ttf", // USCRIPT_DEVANAGARI + L"monbaiti.ttf", // USCRIPT_MONGOLIAN + L"mvboli.ttf", // USCRIPT_THAANA + L"plantc.ttf", // USCRIPT_CHEROKEE + L"raavi.ttf", // USCRIPT_GURMUKHI + L"raavib.ttf", // USCRIPT_GURMUKHI + L"shruti.ttf", // USCRIPT_GUJARATI + L"shrutib.ttf", // USCRIPT_GUJARATI + L"sylfaen.ttf", // USCRIPT_GEORGIAN and USCRIPT_ARMENIAN + L"tahoma.ttf", // USCRIPT_ARABIC, + L"tahomabd.ttf", // USCRIPT_ARABIC, + L"tunga.ttf", // USCRIPT_KANNADA + L"tungab.ttf", // USCRIPT_KANNADA + L"vrinda.ttf", // USCRIPT_BENGALI + L"vrindab.ttf", // USCRIPT_BENGALI +}; + +bool FontCollectionLoader::LoadRestrictedFontList() { + reg_fonts_.clear(); + reg_fonts_.assign(kRestrictedFontSet, + kRestrictedFontSet + _countof(kRestrictedFontSet)); + return true; +} + +} // namespace + +namespace content { + +mswr::ComPtr<IDWriteFontCollection> g_font_collection; + +IDWriteFontCollection* GetCustomFontCollection(IDWriteFactory* factory) { + if (g_font_collection.Get() != NULL) + return g_font_collection.Get(); + + base::TimeTicks start_tick = base::TimeTicks::Now(); + + FontCollectionLoader::Initialize(factory); + + // We try here to put arbitrary limit on max number of fonts that could + // be loaded, otherwise we fallback to restricted set of fonts. + const UINT32 kMaxFontThreshold = 1750; + HRESULT hr = E_FAIL; + if (g_font_loader->GetFontMapSize() < kMaxFontThreshold) { + hr = factory->CreateCustomFontCollection( + g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf()); + } + + bool loadingRestricted = false; + if (FAILED(hr) || !g_font_collection.Get()) { + // We will try here just one more time with restricted font set. + g_font_loader->LoadRestrictedFontList(); + hr = factory->CreateCustomFontCollection( + g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf()); + } + + base::TimeDelta time_delta = base::TimeTicks::Now() - start_tick; + int64 delta = time_delta.ToInternalValue(); + base::debug::Alias(&delta); + UINT32 size = g_font_loader->GetFontMapSize(); + base::debug::Alias(&size); + base::debug::Alias(&loadingRestricted); + + CHECK(SUCCEEDED(hr)); + CHECK(g_font_collection.Get() != NULL); + + UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime", time_delta); + + base::debug::ClearCrashKey(kFontKeyName); + + return g_font_collection.Get(); +} + +} // namespace content diff --git a/content/renderer/renderer_font_platform_win.h b/content/renderer/renderer_font_platform_win.h new file mode 100644 index 0000000..a04901b --- /dev/null +++ b/content/renderer/renderer_font_platform_win.h @@ -0,0 +1,17 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_RENDERER_RENDERER_FONT_PLATFORM_WIN_H_ +#define CHROME_RENDERER_RENDERER_FONT_PLATFORM_WIN_H_ + +struct IDWriteFactory; +struct IDWriteFontCollection; + +namespace content { + +IDWriteFontCollection* GetCustomFontCollection(IDWriteFactory* factory); + +} // namespace content + +#endif // CHROME_RENDERER_RENDERER_FONT_PLATFORM_WIN_H_ diff --git a/content/test/data/font/dwrite_font_cache_arial.dat b/content/test/data/font/dwrite_font_cache_arial.dat Binary files differdeleted file mode 100644 index c749e2b..0000000 --- a/content/test/data/font/dwrite_font_cache_arial.dat +++ /dev/null diff --git a/content/test/data/font/dwrite_font_cache_corrupt.dat b/content/test/data/font/dwrite_font_cache_corrupt.dat Binary files differdeleted file mode 100644 index 40c9d8c..0000000 --- a/content/test/data/font/dwrite_font_cache_corrupt.dat +++ /dev/null |