Chromium Code Reviews| Index: content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc |
| diff --git a/content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc b/content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..eb297b22562d6ea986703db7f415389e50b0100c |
| --- /dev/null |
| +++ b/content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc |
| @@ -0,0 +1,472 @@ |
| +// 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 <utility> |
| + |
| +#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 "ipc/ipc_sender.h" |
| + |
| +namespace content { |
| + |
| +// 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 |
| +}; |
| + |
| +void LogLoadFamilyResult(DirectWriteLoadFamilyResult result) { |
| + UMA_HISTOGRAM_ENUMERATION("DirectWrite.Fonts.Proxy.LoadFamily", result, |
|
scottmg
2015/11/13 00:51:46
Glad to see these UMA added here! Can we add somet
Ilya Kulshin
2015/11/14 00:25:34
There is the Startup.FirstWebContents.NonEmptyPain
|
| + LOAD_FAMILY_MAX_VALUE); |
| +} |
| + |
| +HRESULT DWriteFontCollectionProxy::FindFamilyName(const WCHAR* family_name, |
| + UINT32* index, |
| + BOOL* exists) { |
| + DCHECK(family_name != nullptr); |
|
scottmg
2015/11/13 00:51:46
DCHECK_NE
Ilya Kulshin
2015/11/14 00:25:34
Unfortunately, I haven't been able to get DCHECK_N
|
| + DCHECK(index != nullptr); |
| + DCHECK(exists != nullptr); |
| + TRACE_EVENT0("dwrite", "FontProxy::FindFamilyName"); |
| + |
| + uint32 family_index = 0; |
| + base::string16 name; |
| + base::WideToUTF16(family_name, wcslen(family_name), &name); |
| + |
| + auto iter = family_names_.find(name); |
| + if (iter != family_names_.end()) { |
| + *index = iter->second; |
| + *exists = iter->second != UINT_MAX; |
| + return S_OK; |
| + } |
| + |
| + if (!sender_.Run()->Send( |
| + new DWriteFontProxyMsg_FindFamily(name, &family_index))) { |
| + 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 != nullptr); |
| + |
| + if (index < families_.size() && families_[index] != nullptr) { |
| + 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 family_count = 0; |
| + if (!sender_.Run()->Send( |
| + new DWriteFontProxyMsg_GetFamilyCount(&family_count))) { |
| + return 0; |
| + } |
| + family_count_ = family_count; |
| + return family_count; |
| +} |
| + |
| +HRESULT DWriteFontCollectionProxy::GetFontFromFontFace( |
| + IDWriteFontFace* font_face, |
| + IDWriteFont** font) { |
| + DCHECK(font_face != nullptr); |
| + DCHECK(font != nullptr); |
| + |
| + for (auto& family : families_) { |
| + if (family != nullptr && family->GetFontFromFontFace(font_face, font)) { |
| + return S_OK; |
| + } |
| + } |
| + return E_FAIL; |
| +} |
| + |
| +HRESULT DWriteFontCollectionProxy::CreateEnumeratorFromKey( |
| + IDWriteFactory* factory, |
| + const void* collection_key, |
| + UINT32 collection_key_size, |
| + IDWriteFontFileEnumerator** font_file_enumerator) { |
| + if (collection_key == nullptr || collection_key_size != sizeof(uint32)) |
| + return E_INVALIDARG; |
| + |
| + TRACE_EVENT0("dwrite", "FontProxy::LoadingFontFiles"); |
| + |
| + const uint32* family_index = reinterpret_cast<const uint32*>(collection_key); |
| + |
| + if (*family_index >= GetFontFamilyCount()) |
| + 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; |
| + if (!sender_.Run()->Send( |
| + new DWriteFontProxyMsg_GetFontFiles(*family_index, &file_names))) { |
| + return E_FAIL; |
| + } |
| + |
| + HRESULT hr = mswr::MakeAndInitialize<FontFileEnumerator>( |
| + font_file_enumerator, factory, this, &file_names); |
| + |
| + if (!SUCCEEDED(hr)) |
| + 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 == nullptr) |
| + return E_FAIL; |
| + |
| + const base::char16* file_name = |
| + reinterpret_cast<const base::char16*>(font_file_reference_key); |
| + DCHECK_EQ(font_file_reference_key_size % sizeof(base::char16), 0u); |
| + uint32 file_name_length = font_file_reference_key_size / sizeof(base::char16); |
| + file_name_length--; // Don't count the terminating null. |
| + |
| + if (file_name[file_name_length] != L'\0') |
| + return E_FAIL; |
| + |
| + TRACE_EVENT0("dwrite", "FontFileEnumerator::CreateStreamFromKey"); |
| + |
| + mswr::ComPtr<IDWriteFontFileStream> stream; |
| + if (!SUCCEEDED(mswr::MakeAndInitialize<FontFileStream>(&stream, file_name))) |
| + return E_FAIL; |
| + *font_file_stream = stream.Detach(); |
| + return S_OK; |
| +} |
| + |
| +HRESULT DWriteFontCollectionProxy::RuntimeClassInitialize( |
| + IDWriteFactory* factory, |
| + const base::Callback<IPC::Sender*(void)>& sender) { |
| + DCHECK(factory != nullptr); |
| + |
| + factory_ = factory; |
| + sender_ = sender; |
| + |
| + 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( |
| + unsigned int family_index, |
| + IDWriteFontCollection** containing_collection) { |
| + TRACE_EVENT0("dwrite", "FontProxy::LoadFamily"); |
| + |
| + uint32 index = family_index; |
| + HRESULT hr = factory_->CreateCustomFontCollection( |
| + this /*collectonLoader*/, reinterpret_cast<const void*>(&index), |
|
scottmg
2015/11/13 00:51:46
collectionLoader
Ilya Kulshin
2015/11/14 00:25:33
Done.
|
| + sizeof(index), containing_collection); |
| + |
| + return SUCCEEDED(hr); |
| +} |
| + |
| +bool DWriteFontCollectionProxy::LoadFamilyNames( |
| + unsigned int family_index, |
| + IDWriteLocalizedStrings** localized_strings) { |
| + TRACE_EVENT0("dwrite", "FontProxy::LoadFamilyNames"); |
| + |
| + std::vector<std::pair<base::string16, base::string16>> strings; |
| + if (!sender_.Run()->Send( |
| + new DWriteFontProxyMsg_GetFamilyNames(family_index, &strings))) { |
| + return false; |
| + } |
| + |
| + HRESULT hr = mswr::MakeAndInitialize<DWriteLocalizedStrings>( |
| + localized_strings, &strings); |
| + |
| + return SUCCEEDED(hr); |
| +} |
| + |
| +bool DWriteFontCollectionProxy::CreateFamily(unsigned int family_index) { |
| + if (family_index < families_.size() && families_[family_index] != nullptr) |
| + 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; |
| +} |
| + |
| +HRESULT DWriteFontFamilyProxy::GetFontCollection( |
| + IDWriteFontCollection** font_collection) { |
| + DCHECK(font_collection != nullptr); |
| + |
| + 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 != nullptr); |
| + |
| + if (index >= GetFontCount()) |
| + return E_INVALIDARG; |
| + if (!LoadFamily()) |
| + return E_FAIL; |
| + |
| + return family_->GetFont(index, font); |
| +} |
| + |
| +HRESULT DWriteFontFamilyProxy::GetFamilyNames(IDWriteLocalizedStrings** names) { |
| + DCHECK(names != nullptr); |
| + |
| + // Prefer the real thing, if available. |
| + if (family_ != nullptr) { |
| + family_names_.Reset(); // Release cached data. |
| + return family_->GetFamilyNames(names); |
| + } |
| + |
| + // If already cached, use the cache. |
| + if (family_names_ != nullptr) { |
| + 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 != nullptr); |
| + |
| + 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 != nullptr); |
| + |
| + if (!LoadFamily()) |
| + return E_FAIL; |
| + |
| + return family_->GetMatchingFonts(weight, stretch, style, matching_fonts); |
| +} |
| + |
| +HRESULT DWriteFontFamilyProxy::RuntimeClassInitialize( |
| + DWriteFontCollectionProxy* collection, |
| + unsigned int index) { |
| + DCHECK(collection != nullptr); |
| + |
| + proxy_collection_ = collection; |
| + family_index_ = index; |
| + return S_OK; |
| +} |
| + |
| +bool DWriteFontFamilyProxy::GetFontFromFontFace(IDWriteFontFace* font_face, |
| + IDWriteFont** font) { |
| + DCHECK(font_face != nullptr); |
| + DCHECK(font != nullptr); |
| + |
| + if (family_ == nullptr) |
| + return false; |
| + |
| + mswr::ComPtr<IDWriteFontCollection> collection; |
| + HRESULT hr = family_->GetFontCollection(&collection); |
| + DCHECK(SUCCEEDED(hr)); |
| + hr = collection->GetFontFromFontFace(font_face, font); |
| + |
| + return SUCCEEDED(hr); |
| +} |
| + |
| +bool DWriteFontFamilyProxy::LoadFamily() { |
| + if (family_ != nullptr) |
| + return true; |
| + |
| + 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); |
| +} |
| + |
| +HRESULT FontFileEnumerator::GetCurrentFontFile(IDWriteFontFile** file) { |
| + DCHECK(file != nullptr); |
| + if (current_file_ >= file_names_.size()) |
| + return E_FAIL; |
| + |
| + TRACE_EVENT0("dwrite", "FontFileEnumerator::GetCurrentFontFile (memmap)"); |
| + return factory_->CreateCustomFontFileReference( |
| + reinterpret_cast<const void*>(file_names_[current_file_].c_str()), |
| + (file_names_[current_file_].size() + 1) * sizeof(base::char16), |
| + loader_.Get() /*IDWriteFontFileLoader*/, file); |
| +} |
| + |
| +HRESULT FontFileEnumerator::MoveNext(BOOL* has_current_file) { |
| + DCHECK(has_current_file); |
| + |
| + TRACE_EVENT0("dwrite", "FontFileEnumerator::MoveNext"); |
| + if (next_file_ >= file_names_.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<base::string16>* file_names) { |
| + factory_ = factory; |
| + loader_ = loader; |
| + file_names_.swap(*file_names); |
| + file_streams_.resize(file_names_.size()); |
| + return S_OK; |
| +} |
| + |
| +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( |
|
scottmg
2015/11/13 00:51:46
Newline.
Ilya Kulshin
2015/11/14 00:25:34
Done.
|
| + const base::string16& file_name) { |
| + data_.Initialize(base::FilePath(file_name)); |
| + if (!data_.IsValid()) |
| + return E_FAIL; |
| + return S_OK; |
| +} |
| + |
| +} // namespace content |