diff options
Diffstat (limited to 'content/common/dwrite_font_platform_win.cc')
-rw-r--r-- | content/common/dwrite_font_platform_win.cc | 1166 |
1 files changed, 0 insertions, 1166 deletions
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 |