| // Copyright 2015 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/child/dwrite_font_proxy/dwrite_font_proxy_win.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <utility> |
| |
| #include "base/debug/crash_logging.h" |
| #include "base/feature_list.h" |
| #include "base/logging.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/win/scoped_handle.h" |
| #include "content/child/dwrite_font_proxy/dwrite_localized_strings_win.h" |
| #include "content/common/dwrite_font_proxy_messages.h" |
| #include "content/public/child/child_thread.h" |
| #include "ipc/ipc_platform_file.h" |
| #include "ipc/ipc_sender.h" |
| |
| namespace mswr = Microsoft::WRL; |
| |
| namespace content { |
| |
| namespace { |
| |
| // This enum is used to define the buckets for an enumerated UMA histogram. |
| // Hence, |
| // (a) existing enumerated constants should never be deleted or reordered, and |
| // (b) new constants should only be appended at the end of the enumeration. |
| enum DirectWriteLoadFamilyResult { |
| LOAD_FAMILY_SUCCESS_SINGLE_FAMILY = 0, |
| LOAD_FAMILY_SUCCESS_MATCHED_FAMILY = 1, |
| LOAD_FAMILY_ERROR_MULTIPLE_FAMILIES = 2, |
| LOAD_FAMILY_ERROR_NO_FAMILIES = 3, |
| LOAD_FAMILY_ERROR_NO_COLLECTION = 4, |
| |
| LOAD_FAMILY_MAX_VALUE |
| }; |
| |
| // This enum is used to define the buckets for an enumerated UMA histogram. |
| // Hence, |
| // (a) existing enumerated constants should never be deleted or reordered, and |
| // (b) new constants should only be appended at the end of the enumeration. |
| enum FontProxyError { |
| FIND_FAMILY_SEND_FAILED = 0, |
| GET_FAMILY_COUNT_SEND_FAILED = 1, |
| COLLECTION_KEY_INVALID = 2, |
| FAMILY_INDEX_OUT_OF_RANGE = 3, |
| GET_FONT_FILES_SEND_FAILED = 4, |
| MAPPED_FILE_FAILED = 5, |
| DUPLICATE_HANDLE_FAILED = 6, |
| |
| FONT_PROXY_ERROR_MAX_VALUE |
| }; |
| |
| const char kFontKeyName[] = "font_key_name"; |
| |
| void LogLoadFamilyResult(DirectWriteLoadFamilyResult result) { |
| UMA_HISTOGRAM_ENUMERATION("DirectWrite.Fonts.Proxy.LoadFamilyResult", result, |
| LOAD_FAMILY_MAX_VALUE); |
| } |
| |
| void LogFamilyCount(uint32_t count) { |
| UMA_HISTOGRAM_COUNTS_1000("DirectWrite.Fonts.Proxy.FamilyCount", count); |
| } |
| |
| void LogFontProxyError(FontProxyError error) { |
| UMA_HISTOGRAM_ENUMERATION("DirectWrite.Fonts.Proxy.FontProxyError", error, |
| FONT_PROXY_ERROR_MAX_VALUE); |
| } |
| |
| } // namespace |
| |
| DWriteFontCollectionProxy::DWriteFontCollectionProxy() = default; |
| |
| DWriteFontCollectionProxy::~DWriteFontCollectionProxy() = default; |
| |
| HRESULT DWriteFontCollectionProxy::FindFamilyName(const WCHAR* family_name, |
| UINT32* index, |
| BOOL* exists) { |
| DCHECK(family_name); |
| DCHECK(index); |
| DCHECK(exists); |
| TRACE_EVENT0("dwrite", "FontProxy::FindFamilyName"); |
| |
| uint32_t family_index = 0; |
| base::string16 name(family_name); |
| |
| auto iter = family_names_.find(name); |
| if (iter != family_names_.end()) { |
| *index = iter->second; |
| *exists = iter->second != UINT_MAX; |
| return S_OK; |
| } |
| |
| if (!GetSender()->Send( |
| new DWriteFontProxyMsg_FindFamily(name, &family_index))) { |
| LogFontProxyError(FIND_FAMILY_SEND_FAILED); |
| return E_FAIL; |
| } |
| |
| if (family_index != UINT32_MAX) { |
| if (!CreateFamily(family_index)) |
| return E_FAIL; |
| *exists = TRUE; |
| *index = family_index; |
| families_[family_index]->SetName(name); |
| } else { |
| *exists = FALSE; |
| *index = UINT32_MAX; |
| } |
| |
| family_names_[name] = *index; |
| return S_OK; |
| } |
| |
| HRESULT DWriteFontCollectionProxy::GetFontFamily( |
| UINT32 index, |
| IDWriteFontFamily** font_family) { |
| DCHECK(font_family); |
| |
| if (index < families_.size() && families_[index]) { |
| families_[index].CopyTo(font_family); |
| return S_OK; |
| } |
| |
| if (!CreateFamily(index)) |
| return E_FAIL; |
| |
| families_[index].CopyTo(font_family); |
| return S_OK; |
| } |
| |
| UINT32 DWriteFontCollectionProxy::GetFontFamilyCount() { |
| if (family_count_ != UINT_MAX) |
| return family_count_; |
| |
| TRACE_EVENT0("dwrite", "FontProxy::GetFontFamilyCount"); |
| |
| uint32_t family_count = 0; |
| if (!GetSender()->Send( |
| new DWriteFontProxyMsg_GetFamilyCount(&family_count))) { |
| LogFontProxyError(GET_FAMILY_COUNT_SEND_FAILED); |
| return 0; |
| } |
| |
| LogFamilyCount(family_count); |
| family_count_ = family_count; |
| return family_count; |
| } |
| |
| HRESULT DWriteFontCollectionProxy::GetFontFromFontFace( |
| IDWriteFontFace* font_face, |
| IDWriteFont** font) { |
| DCHECK(font_face); |
| DCHECK(font); |
| |
| for (const auto& family : families_) { |
| if (family && family->GetFontFromFontFace(font_face, font)) { |
| return S_OK; |
| } |
| } |
| // If the font came from our collection, at least one family should match |
| DCHECK(false); |
| |
| return E_FAIL; |
| } |
| |
| HRESULT DWriteFontCollectionProxy::CreateEnumeratorFromKey( |
| IDWriteFactory* factory, |
| const void* collection_key, |
| UINT32 collection_key_size, |
| IDWriteFontFileEnumerator** font_file_enumerator) { |
| if (!collection_key || collection_key_size != sizeof(uint32_t)) { |
| LogFontProxyError(COLLECTION_KEY_INVALID); |
| return E_INVALIDARG; |
| } |
| |
| TRACE_EVENT0("dwrite", "FontProxy::LoadingFontFiles"); |
| |
| const uint32_t* family_index = |
| reinterpret_cast<const uint32_t*>(collection_key); |
| |
| if (*family_index >= GetFontFamilyCount()) { |
| LogFontProxyError(FAMILY_INDEX_OUT_OF_RANGE); |
| return E_INVALIDARG; |
| } |
| |
| // If we already loaded the family we should reuse the existing collection. |
| DCHECK(!families_[*family_index]->IsLoaded()); |
| |
| std::vector<base::string16> file_names; |
| std::vector<IPC::PlatformFileForTransit> file_handles; |
| if (!GetSender()->Send(new DWriteFontProxyMsg_GetFontFiles( |
| *family_index, &file_names, &file_handles))) { |
| LogFontProxyError(GET_FONT_FILES_SEND_FAILED); |
| return E_FAIL; |
| } |
| |
| std::vector<HANDLE> handles; |
| file_handles.reserve(file_names.size() + file_handles.size()); |
| for (const base::string16& file_name : file_names) { |
| // This leaks the handles, since they are used as the reference key to |
| // CreateStreamFromKey, and DirectWrite requires the reference keys to |
| // remain valid for the lifetime of the loader. The loader is the font |
| // collection proxy, which remains alive for the lifetime of the renderer. |
| HANDLE handle = |
| CreateFile(file_name.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, |
| OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
| if (handle != INVALID_HANDLE_VALUE) |
| handles.push_back(handle); |
| } |
| for (const IPC::PlatformFileForTransit& file_handle : file_handles) { |
| handles.push_back(IPC::PlatformFileForTransitToPlatformFile(file_handle)); |
| } |
| |
| HRESULT hr = mswr::MakeAndInitialize<FontFileEnumerator>( |
| font_file_enumerator, factory, this, &handles); |
| |
| if (!SUCCEEDED(hr)) { |
| DCHECK(false); |
| return E_FAIL; |
| } |
| |
| return S_OK; |
| } |
| |
| HRESULT DWriteFontCollectionProxy::CreateStreamFromKey( |
| const void* font_file_reference_key, |
| UINT32 font_file_reference_key_size, |
| IDWriteFontFileStream** font_file_stream) { |
| if (font_file_reference_key_size != sizeof(HANDLE)) { |
| return E_FAIL; |
| } |
| |
| TRACE_EVENT0("dwrite", "FontFileEnumerator::CreateStreamFromKey"); |
| |
| HANDLE file_handle = |
| *reinterpret_cast<const HANDLE*>(font_file_reference_key); |
| |
| if (file_handle == NULL || file_handle == INVALID_HANDLE_VALUE) { |
| DCHECK(false); |
| return E_FAIL; |
| } |
| |
| mswr::ComPtr<FontFileStream> stream; |
| if (!SUCCEEDED( |
| mswr::MakeAndInitialize<FontFileStream>(&stream, file_handle))) { |
| DCHECK(false); |
| return E_FAIL; |
| } |
| *font_file_stream = stream.Detach(); |
| return S_OK; |
| } |
| |
| HRESULT DWriteFontCollectionProxy::RuntimeClassInitialize( |
| IDWriteFactory* factory, |
| IPC::Sender* sender_override) { |
| DCHECK(factory); |
| |
| factory_ = factory; |
| sender_override_ = sender_override; |
| |
| HRESULT hr = factory->RegisterFontCollectionLoader(this); |
| DCHECK(SUCCEEDED(hr)); |
| hr = factory_->RegisterFontFileLoader(this); |
| DCHECK(SUCCEEDED(hr)); |
| return S_OK; |
| } |
| |
| void DWriteFontCollectionProxy::Unregister() { |
| factory_->UnregisterFontCollectionLoader(this); |
| factory_->UnregisterFontFileLoader(this); |
| } |
| |
| bool DWriteFontCollectionProxy::LoadFamily( |
| UINT32 family_index, |
| IDWriteFontCollection** containing_collection) { |
| TRACE_EVENT0("dwrite", "FontProxy::LoadFamily"); |
| |
| uint32_t index = family_index; |
| // CreateCustomFontCollection ends up calling |
| // DWriteFontCollectionProxy::CreateEnumeratorFromKey. |
| HRESULT hr = factory_->CreateCustomFontCollection( |
| this /*collectionLoader*/, reinterpret_cast<const void*>(&index), |
| sizeof(index), containing_collection); |
| |
| return SUCCEEDED(hr); |
| } |
| |
| bool DWriteFontCollectionProxy::GetFontFamily(UINT32 family_index, |
| const base::string16& family_name, |
| IDWriteFontFamily** font_family) { |
| DCHECK(font_family); |
| DCHECK(!family_name.empty()); |
| if (!CreateFamily(family_index)) |
| return false; |
| |
| mswr::ComPtr<DWriteFontFamilyProxy>& family = families_[family_index]; |
| if (!family->IsLoaded() || family->GetName().empty()) |
| family->SetName(family_name); |
| |
| family.CopyTo(font_family); |
| return true; |
| } |
| |
| bool DWriteFontCollectionProxy::LoadFamilyNames( |
| UINT32 family_index, |
| IDWriteLocalizedStrings** localized_strings) { |
| TRACE_EVENT0("dwrite", "FontProxy::LoadFamilyNames"); |
| |
| std::vector<std::pair<base::string16, base::string16>> strings; |
| if (!GetSender()->Send( |
| new DWriteFontProxyMsg_GetFamilyNames(family_index, &strings))) { |
| return false; |
| } |
| |
| HRESULT hr = mswr::MakeAndInitialize<DWriteLocalizedStrings>( |
| localized_strings, &strings); |
| |
| return SUCCEEDED(hr); |
| } |
| |
| bool DWriteFontCollectionProxy::CreateFamily(UINT32 family_index) { |
| if (family_index < families_.size() && families_[family_index]) |
| return true; |
| |
| UINT32 family_count = GetFontFamilyCount(); |
| if (family_index >= family_count) { |
| return false; |
| } |
| |
| if (families_.size() < family_count) |
| families_.resize(family_count); |
| |
| mswr::ComPtr<DWriteFontFamilyProxy> family; |
| HRESULT hr = mswr::MakeAndInitialize<DWriteFontFamilyProxy>(&family, this, |
| family_index); |
| DCHECK(SUCCEEDED(hr)); |
| DCHECK_LT(family_index, families_.size()); |
| |
| families_[family_index] = family; |
| return true; |
| } |
| |
| IPC::Sender* DWriteFontCollectionProxy::GetSender() { |
| return sender_override_ ? sender_override_ : ChildThread::Get(); |
| } |
| |
| DWriteFontFamilyProxy::DWriteFontFamilyProxy() = default; |
| |
| DWriteFontFamilyProxy::~DWriteFontFamilyProxy() = default; |
| |
| HRESULT DWriteFontFamilyProxy::GetFontCollection( |
| IDWriteFontCollection** font_collection) { |
| DCHECK(font_collection); |
| |
| proxy_collection_.CopyTo(font_collection); |
| return S_OK; |
| } |
| |
| UINT32 DWriteFontFamilyProxy::GetFontCount() { |
| // We could conceivably proxy just the font count. However, calling |
| // GetFontCount is almost certain to be followed by a series of GetFont |
| // calls which will need to load all the fonts anyway, so we might as |
| // well save an IPC here. |
| if (!LoadFamily()) |
| return 0; |
| |
| return family_->GetFontCount(); |
| } |
| |
| HRESULT DWriteFontFamilyProxy::GetFont(UINT32 index, IDWriteFont** font) { |
| DCHECK(font); |
| |
| if (index >= GetFontCount()) { |
| return E_INVALIDARG; |
| } |
| if (!LoadFamily()) |
| return E_FAIL; |
| |
| return family_->GetFont(index, font); |
| } |
| |
| HRESULT DWriteFontFamilyProxy::GetFamilyNames(IDWriteLocalizedStrings** names) { |
| DCHECK(names); |
| |
| // Prefer the real thing, if available. |
| if (family_) { |
| family_names_.Reset(); // Release cached data. |
| return family_->GetFamilyNames(names); |
| } |
| |
| // If already cached, use the cache. |
| if (family_names_) { |
| family_names_.CopyTo(names); |
| return S_OK; |
| } |
| |
| TRACE_EVENT0("dwrite", "FontProxy::GetFamilyNames"); |
| |
| // Otherwise, do the IPC. |
| if (!proxy_collection_->LoadFamilyNames(family_index_, &family_names_)) |
| return E_FAIL; |
| |
| family_names_.CopyTo(names); |
| return S_OK; |
| } |
| |
| HRESULT DWriteFontFamilyProxy::GetFirstMatchingFont( |
| DWRITE_FONT_WEIGHT weight, |
| DWRITE_FONT_STRETCH stretch, |
| DWRITE_FONT_STYLE style, |
| IDWriteFont** matching_font) { |
| DCHECK(matching_font); |
| |
| if (!LoadFamily()) |
| return E_FAIL; |
| |
| return family_->GetFirstMatchingFont(weight, stretch, style, matching_font); |
| } |
| |
| HRESULT DWriteFontFamilyProxy::GetMatchingFonts( |
| DWRITE_FONT_WEIGHT weight, |
| DWRITE_FONT_STRETCH stretch, |
| DWRITE_FONT_STYLE style, |
| IDWriteFontList** matching_fonts) { |
| DCHECK(matching_fonts); |
| |
| if (!LoadFamily()) |
| return E_FAIL; |
| |
| return family_->GetMatchingFonts(weight, stretch, style, matching_fonts); |
| } |
| |
| HRESULT DWriteFontFamilyProxy::RuntimeClassInitialize( |
| DWriteFontCollectionProxy* collection, |
| UINT32 index) { |
| DCHECK(collection); |
| |
| proxy_collection_ = collection; |
| family_index_ = index; |
| return S_OK; |
| } |
| |
| bool DWriteFontFamilyProxy::GetFontFromFontFace(IDWriteFontFace* font_face, |
| IDWriteFont** font) { |
| DCHECK(font_face); |
| DCHECK(font); |
| |
| if (!family_) |
| return false; |
| |
| mswr::ComPtr<IDWriteFontCollection> collection; |
| HRESULT hr = family_->GetFontCollection(&collection); |
| DCHECK(SUCCEEDED(hr)); |
| hr = collection->GetFontFromFontFace(font_face, font); |
| |
| return SUCCEEDED(hr); |
| } |
| |
| void DWriteFontFamilyProxy::SetName(const base::string16& family_name) { |
| family_name_.assign(family_name); |
| } |
| |
| const base::string16& DWriteFontFamilyProxy::GetName() { |
| return family_name_; |
| } |
| |
| bool DWriteFontFamilyProxy::IsLoaded() { |
| return family_ != nullptr; |
| } |
| |
| bool DWriteFontFamilyProxy::LoadFamily() { |
| if (family_) |
| return true; |
| |
| SCOPED_UMA_HISTOGRAM_TIMER("DirectWrite.Fonts.Proxy.LoadFamilyTime"); |
| |
| base::debug::ScopedCrashKey crash_key(kFontKeyName, |
| base::WideToUTF8(family_name_)); |
| |
| mswr::ComPtr<IDWriteFontCollection> collection; |
| if (!proxy_collection_->LoadFamily(family_index_, &collection)) { |
| LogLoadFamilyResult(LOAD_FAMILY_ERROR_NO_COLLECTION); |
| return false; |
| } |
| |
| UINT32 family_count = collection->GetFontFamilyCount(); |
| |
| HRESULT hr; |
| if (family_count > 1) { |
| // Some fonts are packaged in a single file containing multiple families. In |
| // such a case we can find the right family by family name. |
| DCHECK(!family_name_.empty()); |
| UINT32 family_index = 0; |
| BOOL found = FALSE; |
| hr = |
| collection->FindFamilyName(family_name_.c_str(), &family_index, &found); |
| if (SUCCEEDED(hr) && found) { |
| hr = collection->GetFontFamily(family_index, &family_); |
| LogLoadFamilyResult(LOAD_FAMILY_SUCCESS_MATCHED_FAMILY); |
| return SUCCEEDED(hr); |
| } |
| } |
| |
| DCHECK_LE(family_count, 1u); |
| |
| if (family_count == 0) { |
| // This is really strange, we successfully loaded no fonts?! |
| LogLoadFamilyResult(LOAD_FAMILY_ERROR_NO_FAMILIES); |
| return false; |
| } |
| |
| LogLoadFamilyResult(family_count == 1 ? LOAD_FAMILY_SUCCESS_SINGLE_FAMILY |
| : LOAD_FAMILY_ERROR_MULTIPLE_FAMILIES); |
| |
| hr = collection->GetFontFamily(0, &family_); |
| |
| return SUCCEEDED(hr); |
| } |
| |
| FontFileEnumerator::FontFileEnumerator() = default; |
| |
| FontFileEnumerator::~FontFileEnumerator() = default; |
| |
| HRESULT FontFileEnumerator::GetCurrentFontFile(IDWriteFontFile** file) { |
| DCHECK(file); |
| if (current_file_ >= files_.size()) { |
| return E_FAIL; |
| } |
| |
| TRACE_EVENT0("dwrite", "FontFileEnumerator::GetCurrentFontFile"); |
| |
| // CreateCustomFontFileReference ends up calling |
| // DWriteFontCollectionProxy::CreateStreamFromKey. |
| HRESULT hr = factory_->CreateCustomFontFileReference( |
| reinterpret_cast<const void*>(&files_[current_file_]), |
| sizeof(files_[current_file_]), loader_.Get() /*IDWriteFontFileLoader*/, |
| file); |
| DCHECK(SUCCEEDED(hr)); |
| return hr; |
| } |
| |
| HRESULT FontFileEnumerator::MoveNext(BOOL* has_current_file) { |
| DCHECK(has_current_file); |
| |
| TRACE_EVENT0("dwrite", "FontFileEnumerator::MoveNext"); |
| if (next_file_ >= files_.size()) { |
| *has_current_file = FALSE; |
| current_file_ = UINT_MAX; |
| return S_OK; |
| } |
| |
| current_file_ = next_file_; |
| ++next_file_; |
| *has_current_file = TRUE; |
| return S_OK; |
| } |
| |
| HRESULT FontFileEnumerator::RuntimeClassInitialize( |
| IDWriteFactory* factory, |
| IDWriteFontFileLoader* loader, |
| std::vector<HANDLE>* files) { |
| factory_ = factory; |
| loader_ = loader; |
| files_.swap(*files); |
| return S_OK; |
| } |
| |
| FontFileStream::FontFileStream() = default; |
| |
| FontFileStream::~FontFileStream() = default; |
| |
| HRESULT FontFileStream::GetFileSize(UINT64* file_size) { |
| *file_size = data_.length(); |
| return S_OK; |
| } |
| |
| HRESULT FontFileStream::GetLastWriteTime(UINT64* last_write_time) { |
| *last_write_time = 0; |
| return S_OK; |
| } |
| |
| HRESULT FontFileStream::ReadFileFragment(const void** fragment_start, |
| UINT64 fragment_offset, |
| UINT64 fragment_size, |
| void** fragment_context) { |
| if (fragment_offset + fragment_size < fragment_offset) |
| return E_FAIL; |
| if (fragment_offset + fragment_size > data_.length()) |
| return E_FAIL; |
| *fragment_start = data_.data() + fragment_offset; |
| *fragment_context = nullptr; |
| return S_OK; |
| } |
| |
| HRESULT FontFileStream::RuntimeClassInitialize(HANDLE handle) { |
| // Duplicate the original handle so we can reopen the file after the memory |
| // mapped section closes it. |
| HANDLE duplicate_handle; |
| if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), |
| &duplicate_handle, 0 /* dwDesiredAccess */, |
| false /* bInheritHandle */, DUPLICATE_SAME_ACCESS)) { |
| LogFontProxyError(DUPLICATE_HANDLE_FAILED); |
| return E_FAIL; |
| } |
| |
| data_.Initialize(base::File(duplicate_handle)); |
| if (!data_.IsValid()) { |
| LogFontProxyError(MAPPED_FILE_FAILED); |
| return E_FAIL; |
| } |
| return S_OK; |
| } |
| |
| } // namespace content |