diff options
author | kulshin <kulshin@chromium.org> | 2015-12-14 15:12:54 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-12-14 23:14:21 +0000 |
commit | 4039fd5b32b74b01866ca4c1ce73998f19320de1 (patch) | |
tree | c149e90f0e6d10d7aed46a3671bec2ee47f86007 | |
parent | c16ff6353c0ef641aadf2d86c8840e3f75a1749e (diff) | |
download | chromium_src-4039fd5b32b74b01866ca4c1ce73998f19320de1.zip chromium_src-4039fd5b32b74b01866ca4c1ce73998f19320de1.tar.gz chromium_src-4039fd5b32b74b01866ca4c1ce73998f19320de1.tar.bz2 |
Create direct write font proxy classes and unit tests.
BUG=525142
Committed: https://crrev.com/42290cb91a66566637a1715737b1961a8480b934
Cr-Commit-Position: refs/heads/master@{#363785}
Committed: https://crrev.com/772bb2e55ce932909dc9ac934eabb4c47dd638d4
Cr-Commit-Position: refs/heads/master@{#364445}
Review URL: https://codereview.chromium.org/1438603002
Cr-Commit-Position: refs/heads/master@{#365122}
25 files changed, 2474 insertions, 63 deletions
diff --git a/content/browser/renderer_host/dwrite_font_proxy_message_filter_win.cc b/content/browser/renderer_host/dwrite_font_proxy_message_filter_win.cc new file mode 100644 index 0000000..fedda0f --- /dev/null +++ b/content/browser/renderer_host/dwrite_font_proxy_message_filter_win.cc @@ -0,0 +1,345 @@ +// 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/browser/renderer_host/dwrite_font_proxy_message_filter_win.h" + +#include <dwrite.h> +#include <shlobj.h> + +#include <set> +#include <utility> + +#include "base/callback_helpers.h" +#include "base/i18n/case_conversion.h" +#include "base/logging.h" +#include "base/metrics/histogram_macros.h" +#include "base/strings/string16.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "content/common/dwrite_font_proxy_messages.h" +#include "ipc/ipc_message_macros.h" +#include "ui/gfx/win/direct_write.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 DirectWriteFontLoaderType { + FILE_SYSTEM_FONT_DIR = 0, + FILE_OUTSIDE_SANDBOX = 1, + OTHER_LOADER = 2, + + FONT_LOADER_TYPE_MAX_VALUE +}; + +void LogLoaderType(DirectWriteFontLoaderType loader_type) { + UMA_HISTOGRAM_ENUMERATION("DirectWrite.Fonts.Proxy.LoaderType", loader_type, + FONT_LOADER_TYPE_MAX_VALUE); +} + +const wchar_t* kFontsToIgnore[] = { + // "Gill Sans Ultra Bold" turns into an Ultra Bold weight "Gill Sans" in + // DirectWrite, but most users don't have any other weights. The regular + // weight font is named "Gill Sans MT", but that ends up in a different + // family with that name. On Mac, there's a "Gill Sans" with various + // weights, so CSS authors use { 'font-family': 'Gill Sans', + // 'Gill Sans MT', ... } and because of the DirectWrite family futzing, + // they end up with an Ultra Bold font, when they just wanted "Gill Sans". + // Mozilla implemented a more complicated hack where they effectively + // rename the Ultra Bold font to "Gill Sans MT Ultra Bold", but because the + // Ultra Bold font is so ugly anyway, we simply ignore it. See + // http://www.microsoft.com/typography/fonts/font.aspx?FMID=978 for a + // picture of the font, and the file name. We also ignore "Gill Sans Ultra + // Bold Condensed". + L"gilsanub.ttf", L"gillubcd.ttf", +}; + +base::string16 GetWindowsFontsPath() { + std::vector<base::char16> font_path_chars; + // SHGetSpecialFolderPath requires at least MAX_PATH characters. + font_path_chars.resize(MAX_PATH); + BOOL result = SHGetSpecialFolderPath(nullptr /* hwndOwner - reserved */, + font_path_chars.data(), CSIDL_FONTS, + FALSE /* fCreate */); + DCHECK(result); + return base::i18n::FoldCase(font_path_chars.data()); +} + +} // namespace + +DWriteFontProxyMessageFilter::DWriteFontProxyMessageFilter() + : BrowserMessageFilter(DWriteFontProxyMsgStart), + windows_fonts_path_(GetWindowsFontsPath()) {} + +DWriteFontProxyMessageFilter::~DWriteFontProxyMessageFilter() = default; + +bool DWriteFontProxyMessageFilter::OnMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(DWriteFontProxyMessageFilter, message) + IPC_MESSAGE_HANDLER(DWriteFontProxyMsg_FindFamily, OnFindFamily) + IPC_MESSAGE_HANDLER(DWriteFontProxyMsg_GetFamilyCount, OnGetFamilyCount) + IPC_MESSAGE_HANDLER(DWriteFontProxyMsg_GetFamilyNames, OnGetFamilyNames) + IPC_MESSAGE_HANDLER(DWriteFontProxyMsg_GetFontFiles, OnGetFontFiles) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void DWriteFontProxyMessageFilter::OverrideThreadForMessage( + const IPC::Message& message, + content::BrowserThread::ID* thread) { + if (IPC_MESSAGE_CLASS(message) == DWriteFontProxyMsgStart) + *thread = BrowserThread::FILE; +} + +void DWriteFontProxyMessageFilter::OnFindFamily( + const base::string16& family_name, + UINT32* family_index) { + InitializeDirectWrite(); + TRACE_EVENT0("dwrite", "FontProxyHost::OnFindFamily"); + DCHECK(collection_); + *family_index = UINT32_MAX; + if (collection_) { + BOOL exists = FALSE; + UINT32 index = UINT32_MAX; + HRESULT hr = + collection_->FindFamilyName(family_name.data(), &index, &exists); + if (SUCCEEDED(hr) && exists) + *family_index = index; + } +} + +void DWriteFontProxyMessageFilter::OnGetFamilyCount(UINT32* count) { + InitializeDirectWrite(); + TRACE_EVENT0("dwrite", "FontProxyHost::OnGetFamilyCount"); + DCHECK(collection_); + if (!collection_) + *count = 0; + else + *count = collection_->GetFontFamilyCount(); +} + +void DWriteFontProxyMessageFilter::OnGetFamilyNames( + UINT32 family_index, + std::vector<DWriteStringPair>* family_names) { + InitializeDirectWrite(); + TRACE_EVENT0("dwrite", "FontProxyHost::OnGetFamilyNames"); + DCHECK(collection_); + if (!collection_) + return; + + TRACE_EVENT0("dwrite", "FontProxyHost::DoGetFamilyNames"); + + mswr::ComPtr<IDWriteFontFamily> family; + HRESULT hr = collection_->GetFontFamily(family_index, &family); + if (!SUCCEEDED(hr)) + return; + + mswr::ComPtr<IDWriteLocalizedStrings> localized_names; + hr = family->GetFamilyNames(&localized_names); + if (!SUCCEEDED(hr)) + return; + + size_t string_count = localized_names->GetCount(); + + std::vector<base::char16> locale; + std::vector<base::char16> name; + for (size_t index = 0; index < string_count; ++index) { + UINT32 length = 0; + hr = localized_names->GetLocaleNameLength(index, &length); + if (!SUCCEEDED(hr)) + return; + ++length; // Reserve space for the null terminator. + locale.resize(length); + hr = localized_names->GetLocaleName(index, locale.data(), length); + if (!SUCCEEDED(hr)) + return; + DCHECK_EQ(L'\0', locale[length - 1]); + + length = 0; + hr = localized_names->GetStringLength(index, &length); + if (!SUCCEEDED(hr)) + return; + ++length; // Reserve space for the null terminator. + name.resize(length); + hr = localized_names->GetString(index, name.data(), length); + if (!SUCCEEDED(hr)) + return; + DCHECK_EQ(L'\0', name[length - 1]); + + // Would be great to use emplace_back instead. + family_names->push_back(std::pair<base::string16, base::string16>( + base::string16(locale.data()), base::string16(name.data()))); + } +} + +void DWriteFontProxyMessageFilter::OnGetFontFiles( + uint32_t family_index, + std::vector<base::string16>* file_paths) { + InitializeDirectWrite(); + TRACE_EVENT0("dwrite", "FontProxyHost::OnGetFontFiles"); + DCHECK(collection_); + if (!collection_) + return; + + mswr::ComPtr<IDWriteFontFamily> family; + HRESULT hr = collection_->GetFontFamily(family_index, &family); + if (!SUCCEEDED(hr)) + return; + + UINT32 font_count = family->GetFontCount(); + + std::set<base::string16> path_set; + // Iterate through all the fonts in the family, and all the files for those + // fonts. If anything goes wrong, bail on the entire family to avoid having + // a partially-loaded font family. + for (UINT32 font_index = 0; font_index < font_count; ++font_index) { + mswr::ComPtr<IDWriteFont> font; + hr = family->GetFont(font_index, &font); + if (!SUCCEEDED(hr)) + return; + + AddFilesForFont(&path_set, font.Get()); + } + + file_paths->assign(path_set.begin(), path_set.end()); +} + +void DWriteFontProxyMessageFilter::InitializeDirectWrite() { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + if (direct_write_initialized_) + return; + direct_write_initialized_ = true; + + mswr::ComPtr<IDWriteFactory> factory; + gfx::win::CreateDWriteFactory(&factory); + if (factory == nullptr) { + // We won't be able to load fonts, but we should still return messages so + // renderers don't hang if they for some reason send us a font message. + return; + } + + HRESULT hr = factory->GetSystemFontCollection(&collection_); + DCHECK(SUCCEEDED(hr)); +} + +bool DWriteFontProxyMessageFilter::AddFilesForFont( + std::set<base::string16>* path_set, + IDWriteFont* font) { + mswr::ComPtr<IDWriteFontFace> font_face; + HRESULT hr; + hr = font->CreateFontFace(&font_face); + if (!SUCCEEDED(hr)) + return false; + + UINT32 file_count; + hr = font_face->GetFiles(&file_count, nullptr); + if (!SUCCEEDED(hr)) + return false; + + std::vector<mswr::ComPtr<IDWriteFontFile>> font_files; + font_files.resize(file_count); + hr = font_face->GetFiles( + &file_count, reinterpret_cast<IDWriteFontFile**>(font_files.data())); + if (!SUCCEEDED(hr)) + return false; + + for (unsigned int file_index = 0; file_index < file_count; ++file_index) { + mswr::ComPtr<IDWriteFontFileLoader> loader; + hr = font_files[file_index]->GetLoader(&loader); + if (!SUCCEEDED(hr)) + return false; + + mswr::ComPtr<IDWriteLocalFontFileLoader> local_loader; + hr = loader.CopyTo(local_loader.GetAddressOf()); // QueryInterface. + + if (hr == E_NOINTERFACE) { + // We could get here if the system font collection contains fonts that + // are backed by something other than files in the system fonts folder. + // I don't think that is actually possible, so for now we'll just + // ignore it (result will be that we'll be unable to match any styles + // for this font, forcing blink/skia to fall back to whatever font is + // next). If we get telemetry indicating that this case actually + // happens, we can implement this by exposing the loader via ipc. That + // will likely by loading the font data into shared memory, although we + // could proxy the stream reads directly instead. + LogLoaderType(OTHER_LOADER); + DCHECK(false); + return false; + } else if (!SUCCEEDED(hr)) { + return false; + } + + if (!AddLocalFile(path_set, local_loader.Get(), + font_files[file_index].Get())) { + return false; + } + } + return true; +} + +bool DWriteFontProxyMessageFilter::AddLocalFile( + std::set<base::string16>* path_set, + IDWriteLocalFontFileLoader* local_loader, + IDWriteFontFile* font_file) { + HRESULT hr; + const void* key; + UINT32 key_size; + hr = font_file->GetReferenceKey(&key, &key_size); + if (!SUCCEEDED(hr)) + return false; + + UINT32 path_length = 0; + hr = local_loader->GetFilePathLengthFromKey(key, key_size, &path_length); + if (!SUCCEEDED(hr)) + return false; + ++path_length; // Reserve space for the null terminator. + std::vector<base::char16> file_path_chars; + file_path_chars.resize(path_length); + hr = local_loader->GetFilePathFromKey(key, key_size, file_path_chars.data(), + path_length); + if (!SUCCEEDED(hr)) + return false; + + base::string16 file_path = base::i18n::FoldCase(file_path_chars.data()); + if (!base::StartsWith(file_path, windows_fonts_path_, + base::CompareCase::SENSITIVE)) { + // Skip loading fonts from outside the system fonts directory, since + // these families will not be accessible to the renderer process. If + // this turns out to be a common case, we can either grant the renderer + // access to these files (not sure if this is actually possible), or + // load the file data ourselves and hand it to the renderer. + LogLoaderType(FILE_OUTSIDE_SANDBOX); + NOTREACHED(); // Not yet implemented. + return false; + } + + // Refer to comments in kFontsToIgnore for this block. + for (const auto& file_to_ignore : kFontsToIgnore) { + // Ok to do ascii comparison since the strings we are looking for are + // all ascii. + if (base::EndsWith(file_path, file_to_ignore, + base::CompareCase::INSENSITIVE_ASCII)) { + // Unlike most other cases in this function, we do not abort loading + // the entire family, since we want to specifically ignore particular + // font styles and load the rest of the family if it exists. The + // renderer can deal with a family with zero files if that ends up + // being the case. + return true; + } + } + + LogLoaderType(FILE_SYSTEM_FONT_DIR); + path_set->insert(file_path); + return true; +} + +} // namespace content diff --git a/content/browser/renderer_host/dwrite_font_proxy_message_filter_win.h b/content/browser/renderer_host/dwrite_font_proxy_message_filter_win.h new file mode 100644 index 0000000..25ef9b6 --- /dev/null +++ b/content/browser/renderer_host/dwrite_font_proxy_message_filter_win.h @@ -0,0 +1,65 @@ +// 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. + +#ifndef CONTENT_BROWSER_RENDERER_HOST_DWRITE_FONT_PROXY_MESSAGE_FILTER_WIN_H_ +#define CONTENT_BROWSER_RENDERER_HOST_DWRITE_FONT_PROXY_MESSAGE_FILTER_WIN_H_ + +#include <dwrite.h> +#include <wrl.h> +#include <set> +#include <utility> +#include <vector> + +#include "base/location.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "content/common/content_export.h" +#include "content/public/browser/browser_message_filter.h" +#include "content/public/browser/browser_thread.h" + +namespace content { + +// Implements a message filter that handles the dwrite font proxy messages. +// If DWrite is enabled, calls into the system font collection to obtain +// results. Otherwise, acts as if the system collection contains no fonts. +class CONTENT_EXPORT DWriteFontProxyMessageFilter + : public BrowserMessageFilter { + public: + DWriteFontProxyMessageFilter(); + + // BrowserMessageFilter: + bool OnMessageReceived(const IPC::Message& message) override; + void OverrideThreadForMessage(const IPC::Message& message, + content::BrowserThread::ID* thread) override; + + protected: + ~DWriteFontProxyMessageFilter() override; + + void OnFindFamily(const base::string16& family_name, UINT32* family_index); + void OnGetFamilyCount(UINT32* count); + void OnGetFamilyNames( + UINT32 family_index, + std::vector<std::pair<base::string16, base::string16>>* family_names); + void OnGetFontFiles(UINT32 family_index, + std::vector<base::string16>* file_paths); + + void InitializeDirectWrite(); + + private: + bool AddFilesForFont(std::set<base::string16>* path_set, IDWriteFont* font); + bool AddLocalFile(std::set<base::string16>* path_set, + IDWriteLocalFontFileLoader* local_loader, + IDWriteFontFile* font_file); + + private: + bool direct_write_initialized_ = false; + Microsoft::WRL::ComPtr<IDWriteFontCollection> collection_; + base::string16 windows_fonts_path_; + + DISALLOW_COPY_AND_ASSIGN(DWriteFontProxyMessageFilter); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_DWRITE_FONT_PROXY_MESSAGE_FILTER_WIN_H_ diff --git a/content/browser/renderer_host/dwrite_font_proxy_message_filter_win_unittest.cc b/content/browser/renderer_host/dwrite_font_proxy_message_filter_win_unittest.cc new file mode 100644 index 0000000..65359e9 --- /dev/null +++ b/content/browser/renderer_host/dwrite_font_proxy_message_filter_win_unittest.cc @@ -0,0 +1,145 @@ +// 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/browser/renderer_host/dwrite_font_proxy_message_filter_win.h" + +#include <dwrite.h> + +#include "base/memory/ref_counted.h" +#include "base/run_loop.h" +#include "base/test/test_simple_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "content/common/dwrite_font_proxy_messages.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "ipc/ipc_message_macros.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/win/direct_write.h" + +namespace mswr = Microsoft::WRL; + +namespace content { + +namespace { + +class FilterWithFakeSender : public DWriteFontProxyMessageFilter { + public: + bool Send(IPC::Message* msg) override { + EXPECT_EQ(nullptr, reply_message_.get()); + reply_message_.reset(msg); + return true; + } + + IPC::Message* GetReply() { return reply_message_.get(); } + + void ResetReply() { reply_message_.reset(nullptr); } + + private: + ~FilterWithFakeSender() override = default; + + scoped_ptr<IPC::Message> reply_message_; +}; + +class DWriteFontProxyMessageFilterUnitTest : public testing::Test { + public: + DWriteFontProxyMessageFilterUnitTest() { + filter_ = new FilterWithFakeSender(); + } + + void Send(IPC::SyncMessage* message) { + std::unique_ptr<IPC::SyncMessage> deleter(message); + scoped_ptr<IPC::MessageReplyDeserializer> serializer( + message->GetReplyDeserializer()); + filter_->OnMessageReceived(*message); + base::RunLoop().RunUntilIdle(); + ASSERT_NE(nullptr, filter_->GetReply()); + serializer->SerializeOutputParameters(*(filter_->GetReply())); + } + + scoped_refptr<FilterWithFakeSender> filter_; + content::TestBrowserThreadBundle thread_bundle_; +}; + +TEST_F(DWriteFontProxyMessageFilterUnitTest, GetFamilyCount) { + if (!gfx::win::ShouldUseDirectWrite()) + return; + UINT32 family_count = 0; + Send(new DWriteFontProxyMsg_GetFamilyCount(&family_count)); + EXPECT_NE(0u, family_count); // Assume there's some fonts on the test system. +} + +TEST_F(DWriteFontProxyMessageFilterUnitTest, FindFamily) { + if (!gfx::win::ShouldUseDirectWrite()) + return; + UINT32 arial_index = 0; + Send(new DWriteFontProxyMsg_FindFamily(L"Arial", &arial_index)); + EXPECT_NE(UINT_MAX, arial_index); + + filter_->ResetReply(); + UINT32 times_index = 0; + Send(new DWriteFontProxyMsg_FindFamily(L"Times New Roman", ×_index)); + EXPECT_NE(UINT_MAX, times_index); + EXPECT_NE(arial_index, times_index); + + filter_->ResetReply(); + UINT32 unknown_index = 0; + Send(new DWriteFontProxyMsg_FindFamily(L"Not a font family", &unknown_index)); + EXPECT_EQ(UINT_MAX, unknown_index); +} + +TEST_F(DWriteFontProxyMessageFilterUnitTest, GetFamilyNames) { + if (!gfx::win::ShouldUseDirectWrite()) + return; + UINT32 arial_index = 0; + Send(new DWriteFontProxyMsg_FindFamily(L"Arial", &arial_index)); + filter_->ResetReply(); + + std::vector<DWriteStringPair> names; + Send(new DWriteFontProxyMsg_GetFamilyNames(arial_index, &names)); + + EXPECT_LT(0u, names.size()); + for (const auto& pair : names) { + EXPECT_STRNE(L"", pair.first.c_str()); + EXPECT_STRNE(L"", pair.second.c_str()); + } +} + +TEST_F(DWriteFontProxyMessageFilterUnitTest, GetFamilyNamesIndexOutOfBounds) { + if (!gfx::win::ShouldUseDirectWrite()) + return; + std::vector<DWriteStringPair> names; + UINT32 invalid_index = 1000000; + Send(new DWriteFontProxyMsg_GetFamilyNames(invalid_index, &names)); + + EXPECT_EQ(0u, names.size()); +} + +TEST_F(DWriteFontProxyMessageFilterUnitTest, GetFontFiles) { + if (!gfx::win::ShouldUseDirectWrite()) + return; + UINT32 arial_index = 0; + Send(new DWriteFontProxyMsg_FindFamily(L"Arial", &arial_index)); + filter_->ResetReply(); + + std::vector<base::string16> files; + Send(new DWriteFontProxyMsg_GetFontFiles(arial_index, &files)); + + EXPECT_LT(0u, files.size()); + for (const base::string16& file : files) { + EXPECT_STRNE(L"", file.c_str()); + } +} + +TEST_F(DWriteFontProxyMessageFilterUnitTest, GetFontFilesIndexOutOfBounds) { + if (!gfx::win::ShouldUseDirectWrite()) + return; + std::vector<base::string16> files; + UINT32 invalid_index = 1000000; + Send(new DWriteFontProxyMsg_GetFontFiles(invalid_index, &files)); + + EXPECT_EQ(0u, files.size()); +} + +} // namespace + +} // namespace content diff --git a/content/child/dwrite_font_proxy/OWNERS b/content/child/dwrite_font_proxy/OWNERS new file mode 100644 index 0000000..54db994 --- /dev/null +++ b/content/child/dwrite_font_proxy/OWNERS @@ -0,0 +1,3 @@ +kulshin@chromium.org +jschuh@chromium.org +scottmg@chromium.org
\ No newline at end of file diff --git a/content/child/dwrite_font_proxy/dwrite_font_proxy_init_win.cc b/content/child/dwrite_font_proxy/dwrite_font_proxy_init_win.cc new file mode 100644 index 0000000..7ec2a42 --- /dev/null +++ b/content/child/dwrite_font_proxy/dwrite_font_proxy_init_win.cc @@ -0,0 +1,118 @@ +// 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_init_win.h" + +#include <dwrite.h> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/debug/alias.h" +#include "base/win/iat_patch_function.h" +#include "base/win/windows_version.h" +#include "content/child/dwrite_font_proxy/dwrite_font_proxy_win.h" +#include "content/common/font_warmup_win.h" +#include "skia/ext/fontmgr_default_win.h" +#include "third_party/WebKit/public/web/win/WebFontRendering.h" +#include "third_party/skia/include/ports/SkTypeface_win.h" + +namespace mswr = Microsoft::WRL; + +namespace content { + +namespace { + +mswr::ComPtr<DWriteFontCollectionProxy> g_font_collection; +IPC::Sender* g_sender_override = nullptr; + +// Windows-only DirectWrite support. These warm up the DirectWrite paths +// before sandbox lock down to allow Skia access to the Font Manager service. +void CreateDirectWriteFactory(IDWriteFactory** factory) { + typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc; + HMODULE dwrite_dll = LoadLibraryW(L"dwrite.dll"); + // TODO(scottmg): Temporary code to track crash in http://crbug.com/387867. + 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); + } + + // This shouldn't be necessary, but not having this causes breakage in + // content_browsertests, and possibly other high-stress cases. + PatchServiceManagerCalls(); + + DWriteCreateFactoryProc dwrite_create_factory_proc = + reinterpret_cast<DWriteCreateFactoryProc>( + GetProcAddress(dwrite_dll, "DWriteCreateFactory")); + // TODO(scottmg): Temporary code to track crash in http://crbug.com/387867. + 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); + } + CHECK(SUCCEEDED(dwrite_create_factory_proc( + DWRITE_FACTORY_TYPE_ISOLATED, __uuidof(IDWriteFactory), + reinterpret_cast<IUnknown**>(factory)))); +} + +HRESULT STDMETHODCALLTYPE StubFontCollection(IDWriteFactory* factory, + IDWriteFontCollection** col, + BOOL checkUpdates) { + DCHECK(g_font_collection); + g_font_collection.CopyTo(col); + return S_OK; +} + +// Copied from content/common/font_warmup_win.cc +void PatchDWriteFactory(IDWriteFactory* factory) { + const unsigned int kGetSystemFontCollectionVTableIndex = 3; + + PROC* vtable = *reinterpret_cast<PROC**>(factory); + PROC* function_ptr = &vtable[kGetSystemFontCollectionVTableIndex]; + void* stub_function = &StubFontCollection; + base::win::ModifyCode(function_ptr, &stub_function, sizeof(PROC)); +} + +// Needed as a function for Bind() +IPC::Sender* GetSenderOverride() { + return g_sender_override; +} + +} // namespace + +void InitializeDWriteFontProxy( + const base::Callback<IPC::Sender*(void)>& sender) { + mswr::ComPtr<IDWriteFactory> factory; + + CreateDirectWriteFactory(&factory); + + if (!g_font_collection) { + if (g_sender_override) { + mswr::MakeAndInitialize<DWriteFontCollectionProxy>( + &g_font_collection, factory.Get(), base::Bind(&GetSenderOverride)); + } else { + mswr::MakeAndInitialize<DWriteFontCollectionProxy>(&g_font_collection, + factory.Get(), sender); + } + } + + PatchDWriteFactory(factory.Get()); + + blink::WebFontRendering::setDirectWriteFactory(factory.Get()); + SkFontMgr* skia_font_manager = SkFontMgr_New_DirectWrite(factory.Get()); + SetDefaultSkiaFactory(skia_font_manager); +} + +void UninitializeDWriteFontProxy() { + if (g_font_collection) + g_font_collection->Unregister(); +} + +void SetDWriteFontProxySenderForTesting(IPC::Sender* sender) { + g_sender_override = sender; +} + +} // namespace content diff --git a/content/child/dwrite_font_proxy/dwrite_font_proxy_init_win.h b/content/child/dwrite_font_proxy/dwrite_font_proxy_init_win.h new file mode 100644 index 0000000..e346cb0 --- /dev/null +++ b/content/child/dwrite_font_proxy/dwrite_font_proxy_init_win.h @@ -0,0 +1,30 @@ +// 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. + +#ifndef CONTENT_CHILD_DWRITE_FONT_PROXY_DWRITE_FONT_PROXY_INIT_WIN_H_ +#define CONTENT_CHILD_DWRITE_FONT_PROXY_DWRITE_FONT_PROXY_INIT_WIN_H_ + +#include "base/callback.h" +#include "content/common/content_export.h" +#include "ipc/ipc_sender.h" + +namespace content { + +// Initializes the dwrite font proxy, using the specified callback to obtain +// the sender to be used for sending IPC messages to the browser process. +CONTENT_EXPORT void InitializeDWriteFontProxy( + const base::Callback<IPC::Sender*(void)>& sender); + +// Uninitialize the dwrite font proxy. This is safe to call even if the proxy +// has not been initialized. After this, calls to load fonts may fail. +CONTENT_EXPORT void UninitializeDWriteFontProxy(); + +// Configures the dwrite font proxy to use the specified sender. This can be +// useful in tests which use a fake render thread which is unable to process +// font IPC messages. This should only be called when running as a test. +CONTENT_EXPORT void SetDWriteFontProxySenderForTesting(IPC::Sender* sender); + +} // namespace content + +#endif // CONTENT_CHILD_DWRITE_FONT_PROXY_DWRITE_FONT_PROXY_INIT_WIN_H_ 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 0000000..0462d711 --- /dev/null +++ b/content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc @@ -0,0 +1,514 @@ +// 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/debug/crash_logging.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 "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 +}; + +const char kFontKeyName[] = "font_key_name"; + +void LogLoadFamilyResult(DirectWriteLoadFamilyResult result) { + UMA_HISTOGRAM_ENUMERATION("DirectWrite.Fonts.Proxy.LoadFamily", result, + LOAD_FAMILY_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 (!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); + + 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 (!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); + DCHECK(font); + + for (const auto& family : families_) { + if (family && 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 || collection_key_size != sizeof(uint32_t)) + return E_INVALIDARG; + + TRACE_EVENT0("dwrite", "FontProxy::LoadingFontFiles"); + + const uint32_t* family_index = + reinterpret_cast<const uint32_t*>(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) + 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); + size_t file_name_size = + static_cast<size_t>(font_file_reference_key_size) / sizeof(base::char16); + + if (file_name_size == 0 || file_name[file_name_size - 1] != 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); + + 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( + 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::LoadFamilyNames( + UINT32 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(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; +} + +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); +} + +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_ >= file_names_.size()) + return E_FAIL; + + TRACE_EVENT0("dwrite", "FontFileEnumerator::GetCurrentFontFile (memmap)"); + // CreateCustomFontFileReference ends up calling + // DWriteFontCollectionProxy::CreateStreamFromKey. + return factory_->CreateCustomFontFileReference( + reinterpret_cast<const void*>(file_names_[current_file_].c_str()), + (file_names_[current_file_].length() + 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; +} + +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( + const base::string16& file_name) { + data_.Initialize(base::FilePath(file_name)); + if (!data_.IsValid()) + return E_FAIL; + return S_OK; +} + +} // namespace content diff --git a/content/child/dwrite_font_proxy/dwrite_font_proxy_win.h b/content/child/dwrite_font_proxy/dwrite_font_proxy_win.h new file mode 100644 index 0000000..bdee58f --- /dev/null +++ b/content/child/dwrite_font_proxy/dwrite_font_proxy_win.h @@ -0,0 +1,202 @@ +// 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. + +#ifndef CONTENT_CHILD_DWRITE_FONT_PROXY_DWRITE_FONT_PROXY_WIN_H_ +#define CONTENT_CHILD_DWRITE_FONT_PROXY_DWRITE_FONT_PROXY_WIN_H_ + +#include <dwrite.h> +#include <wrl.h> + +#include <map> +#include <vector> + +#include "base/callback.h" +#include "base/files/memory_mapped_file.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "content/common/content_export.h" + +namespace IPC { +class Sender; +} + +namespace content { + +class DWriteFontFamilyProxy; + +// Implements a DirectWrite font collection that uses IPC to the browser to do +// font enumeration. If a matching family is found, it will be loaded locally +// into a custom font collection. +// This is needed because the sandbox interferes with DirectWrite's +// communication with the system font service. +class CONTENT_EXPORT DWriteFontCollectionProxy + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + IDWriteFontCollection, + IDWriteFontCollectionLoader, + IDWriteFontFileLoader> { + public: + DWriteFontCollectionProxy(); + ~DWriteFontCollectionProxy() override; + + // IDWriteFontCollection: + HRESULT STDMETHODCALLTYPE FindFamilyName(const WCHAR* family_name, + UINT32* index, + BOOL* exists) override; + HRESULT STDMETHODCALLTYPE + GetFontFamily(UINT32 index, IDWriteFontFamily** font_family) override; + UINT32 STDMETHODCALLTYPE GetFontFamilyCount() override; + HRESULT STDMETHODCALLTYPE GetFontFromFontFace(IDWriteFontFace* font_face, + IDWriteFont** font) override; + + // IDWriteFontCollectionLoader: + HRESULT STDMETHODCALLTYPE CreateEnumeratorFromKey( + IDWriteFactory* factory, + const void* collection_key, + UINT32 collection_key_size, + IDWriteFontFileEnumerator** font_file_enumerator) override; + + // IDWriteFontFileLoader: + HRESULT STDMETHODCALLTYPE + CreateStreamFromKey(const void* font_file_reference_key, + UINT32 font_file_reference_key_size, + IDWriteFontFileStream** font_file_stream) override; + + HRESULT STDMETHODCALLTYPE + RuntimeClassInitialize(IDWriteFactory* factory, + const base::Callback<IPC::Sender*(void)>& sender); + + void Unregister(); + + bool LoadFamily(UINT32 family_index, + IDWriteFontCollection** containing_collection); + + bool LoadFamilyNames(UINT32 family_index, IDWriteLocalizedStrings** strings); + + bool CreateFamily(UINT32 family_index); + + private: + Microsoft::WRL::ComPtr<IDWriteFactory> factory_; + std::vector<Microsoft::WRL::ComPtr<DWriteFontFamilyProxy>> families_; + std::map<base::string16, UINT32> family_names_; + UINT32 family_count_ = UINT_MAX; + base::Callback<IPC::Sender*(void)> sender_; + + DISALLOW_ASSIGN(DWriteFontCollectionProxy); +}; + +// Implements the DirectWrite font family interface. This class is just a +// stub, until something calls a method that requires actual font data. At that +// point this will load the font files into a custom collection and +// subsequently calls will be proxied to the resulting DirectWrite object. +class CONTENT_EXPORT DWriteFontFamilyProxy + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + IDWriteFontFamily> { + public: + DWriteFontFamilyProxy(); + ~DWriteFontFamilyProxy() override; + + // IDWriteFontFamily: + HRESULT STDMETHODCALLTYPE + GetFontCollection(IDWriteFontCollection** font_collection) override; + UINT32 STDMETHODCALLTYPE GetFontCount() override; + HRESULT STDMETHODCALLTYPE GetFont(UINT32 index, IDWriteFont** font) override; + HRESULT STDMETHODCALLTYPE + GetFamilyNames(IDWriteLocalizedStrings** names) override; + HRESULT STDMETHODCALLTYPE + GetFirstMatchingFont(DWRITE_FONT_WEIGHT weight, + DWRITE_FONT_STRETCH stretch, + DWRITE_FONT_STYLE style, + IDWriteFont** matching_font) override; + HRESULT STDMETHODCALLTYPE + GetMatchingFonts(DWRITE_FONT_WEIGHT weight, + DWRITE_FONT_STRETCH stretch, + DWRITE_FONT_STYLE style, + IDWriteFontList** matching_fonts) override; + + HRESULT STDMETHODCALLTYPE + RuntimeClassInitialize(DWriteFontCollectionProxy* collection, UINT32 index); + + bool GetFontFromFontFace(IDWriteFontFace* font_face, IDWriteFont** font); + + void SetName(const base::string16& family_name); + + bool IsLoaded(); + + protected: + bool LoadFamily(); + + private: + UINT32 family_index_; + base::string16 family_name_; + Microsoft::WRL::ComPtr<DWriteFontCollectionProxy> proxy_collection_; + Microsoft::WRL::ComPtr<IDWriteFontFamily> family_; + Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> family_names_; + + DISALLOW_ASSIGN(DWriteFontFamilyProxy); +}; + +// Implements the DirectWrite font file enumerator interface, backed by a list +// of font files. +class CONTENT_EXPORT FontFileEnumerator + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + IDWriteFontFileEnumerator> { + public: + FontFileEnumerator(); + ~FontFileEnumerator() override; + + // IDWriteFontFileEnumerator: + HRESULT STDMETHODCALLTYPE GetCurrentFontFile(IDWriteFontFile** file) override; + HRESULT STDMETHODCALLTYPE MoveNext(BOOL* has_current_file) override; + + HRESULT STDMETHODCALLTYPE + RuntimeClassInitialize(IDWriteFactory* factory, + IDWriteFontFileLoader* loader, + std::vector<base::string16>* file_names); + + private: + Microsoft::WRL::ComPtr<IDWriteFactory> factory_; + Microsoft::WRL::ComPtr<IDWriteFontFileLoader> loader_; + std::vector<base::string16> file_names_; + std::vector<Microsoft::WRL::ComPtr<IDWriteFontFileStream>> file_streams_; + UINT32 next_file_ = 0; + UINT32 current_file_ = UINT_MAX; + + DISALLOW_ASSIGN(FontFileEnumerator); +}; + +// Implements the DirectWrite font file stream interface that maps the file to +// be loaded as a memory mapped file, and subsequently returns pointers into +// the mapped memory block. +// TODO(kulshin): confirm that using custom streams is actually an improvement +class CONTENT_EXPORT FontFileStream + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + IDWriteFontFileStream> { + public: + FontFileStream(); + ~FontFileStream() override; + + // IDWriteFontFileStream: + HRESULT STDMETHODCALLTYPE GetFileSize(UINT64* file_size) override; + HRESULT STDMETHODCALLTYPE GetLastWriteTime(UINT64* last_write_time) override; + HRESULT STDMETHODCALLTYPE ReadFileFragment(const void** fragment_start, + UINT64 file_offset, + UINT64 fragment_size, + void** fragment_context) override; + void STDMETHODCALLTYPE ReleaseFileFragment(void* fragment_context) override {} + + HRESULT STDMETHODCALLTYPE + RuntimeClassInitialize(const base::string16& file_name); + + private: + base::MemoryMappedFile data_; + + DISALLOW_ASSIGN(FontFileStream); +}; + +} // namespace content +#endif // CONTENT_CHILD_DWRITE_FONT_PROXY_DWRITE_FONT_PROXY_WIN_H_ diff --git a/content/child/dwrite_font_proxy/dwrite_font_proxy_win_unittest.cc b/content/child/dwrite_font_proxy/dwrite_font_proxy_win_unittest.cc new file mode 100644 index 0000000..93811eb --- /dev/null +++ b/content/child/dwrite_font_proxy/dwrite_font_proxy_win_unittest.cc @@ -0,0 +1,378 @@ +// Copyright (c) 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 <dwrite.h> +#include <shlobj.h> +#include <wrl.h> + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "content/common/dwrite_font_proxy_messages.h" +#include "content/common/view_messages.h" +#include "content/test/dwrite_font_fake_sender_win.h" +#include "ipc/ipc_message_macros.h" +#include "ipc/ipc_sender.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mswr = Microsoft::WRL; + +namespace content { + +namespace { + +void CreateDWriteFactory(IUnknown** factory) { + using DWriteCreateFactoryProc = decltype(DWriteCreateFactory)*; + HMODULE dwrite_dll = LoadLibraryW(L"dwrite.dll"); + if (!dwrite_dll) + return; + + DWriteCreateFactoryProc dwrite_create_factory_proc = + reinterpret_cast<DWriteCreateFactoryProc>( + GetProcAddress(dwrite_dll, "DWriteCreateFactory")); + if (!dwrite_create_factory_proc) + return; + + dwrite_create_factory_proc(DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), factory); +} + +class DWriteFontProxyUnitTest : public testing::Test { + public: + DWriteFontProxyUnitTest() { + if (!factory) + return; + fake_collection_ = new FakeFontCollection(); + SetupFonts(fake_collection_.get()); + mswr::MakeAndInitialize<DWriteFontCollectionProxy>( + &collection_, factory.Get(), + base::Bind(&FakeFontCollection::GetTrackingSender, fake_collection_)); + } + + ~DWriteFontProxyUnitTest() override { + if (collection_) + collection_->Unregister(); + } + + static void SetupFonts(FakeFontCollection* fonts) { + fonts->AddFont(L"Aardvark") + .AddFamilyName(L"en-us", L"Aardvark") + .AddFamilyName(L"de-de", L"Erdferkel") + .AddFilePath(L"X:\\Nonexistent\\Folder\\Aardvark.ttf"); + FakeFont& arial = + fonts->AddFont(L"Arial").AddFamilyName(L"en-us", L"Arial"); + for (auto& path : arial_font_files) + arial.AddFilePath(path); + fonts->AddFont(L"Times New Roman") + .AddFamilyName(L"en-us", L"Times New Roman") + .AddFilePath(L"X:\\Nonexistent\\Folder\\Times.ttf"); + } + + static void SetUpTestCase() { + CreateDWriteFactory(&factory); + + std::vector<base::char16> font_path; + font_path.resize(MAX_PATH); + SHGetSpecialFolderPath(nullptr /* hwndOwner - reserved */, font_path.data(), + CSIDL_FONTS, FALSE /* fCreate*/); + base::string16 arial; + arial.append(font_path.data()).append(L"\\arial.ttf"); + base::string16 arialbd; + arialbd.append(font_path.data()).append(L"\\arialbd.ttf"); + arial_font_files.push_back(arial); + arial_font_files.push_back(arialbd); + } + + protected: + scoped_refptr<FakeFontCollection> fake_collection_; + mswr::ComPtr<DWriteFontCollectionProxy> collection_; + + static std::vector<base::string16> arial_font_files; + static mswr::ComPtr<IDWriteFactory> factory; +}; +std::vector<base::string16> DWriteFontProxyUnitTest::arial_font_files; +mswr::ComPtr<IDWriteFactory> DWriteFontProxyUnitTest::factory; + +TEST_F(DWriteFontProxyUnitTest, GetFontFamilyCount) { + if (!factory) + return; + + UINT32 family_count = collection_->GetFontFamilyCount(); + + EXPECT_EQ(3, family_count); + ASSERT_EQ(1, fake_collection_->MessageCount()); + EXPECT_EQ(DWriteFontProxyMsg_GetFamilyCount::ID, + fake_collection_->GetMessage(0)->type()); + + // Calling again should not cause another message to be sent. + family_count = collection_->GetFontFamilyCount(); + EXPECT_EQ(3, family_count); + ASSERT_EQ(1, fake_collection_->MessageCount()); +} + +TEST_F(DWriteFontProxyUnitTest, FindFamilyNameShouldFindFamily) { + HRESULT hr; + if (!factory) + return; + + UINT32 index = UINT_MAX; + BOOL exists = FALSE; + hr = collection_->FindFamilyName(L"Arial", &index, &exists); + + EXPECT_EQ(S_OK, hr); + EXPECT_EQ(1, index); + EXPECT_TRUE(exists); + ASSERT_EQ(2, fake_collection_->MessageCount()); + EXPECT_EQ(DWriteFontProxyMsg_FindFamily::ID, + fake_collection_->GetMessage(0)->type()); + EXPECT_EQ(DWriteFontProxyMsg_GetFamilyCount::ID, + fake_collection_->GetMessage(1)->type()); +} + +TEST_F(DWriteFontProxyUnitTest, FindFamilyNameShouldReturnUINTMAXWhenNotFound) { + HRESULT hr; + if (!factory) + return; + + UINT32 index = UINT_MAX; + BOOL exists = FALSE; + hr = collection_->FindFamilyName(L"Not a font", &index, &exists); + + EXPECT_EQ(S_OK, hr); + EXPECT_EQ(UINT32_MAX, index); + EXPECT_FALSE(exists); + ASSERT_EQ(1, fake_collection_->MessageCount()); + EXPECT_EQ(DWriteFontProxyMsg_FindFamily::ID, + fake_collection_->GetMessage(0)->type()); +} + +TEST_F(DWriteFontProxyUnitTest, FindFamilyNameShouldNotSendDuplicateIPC) { + HRESULT hr; + if (!factory) + return; + + UINT32 index = UINT_MAX; + BOOL exists = FALSE; + hr = collection_->FindFamilyName(L"Arial", &index, &exists); + ASSERT_EQ(S_OK, hr); + ASSERT_EQ(2, fake_collection_->MessageCount()); + + hr = collection_->FindFamilyName(L"Arial", &index, &exists); + + EXPECT_EQ(S_OK, hr); + EXPECT_EQ(2, fake_collection_->MessageCount()); +} + +TEST_F(DWriteFontProxyUnitTest, GetFontFamilyShouldCreateFamily) { + HRESULT hr; + if (!factory) + return; + + UINT32 index = UINT_MAX; + BOOL exists = FALSE; + hr = collection_->FindFamilyName(L"Arial", &index, &exists); + ASSERT_EQ(2, fake_collection_->MessageCount()); + + mswr::ComPtr<IDWriteFontFamily> family; + hr = collection_->GetFontFamily(2, &family); + + EXPECT_EQ(S_OK, hr); + EXPECT_EQ(2, fake_collection_->MessageCount()); + EXPECT_NE(nullptr, family.Get()); +} + +void CheckLocale(const base::string16& locale_name, + const base::string16& expected_value, + IDWriteLocalizedStrings* strings) { + UINT32 locale_index = 0; + BOOL locale_exists = FALSE; + strings->FindLocaleName(locale_name.data(), &locale_index, &locale_exists); + EXPECT_TRUE(locale_exists); + + UINT32 name_length = 0; + strings->GetLocaleNameLength(locale_index, &name_length); + EXPECT_EQ(locale_name.size(), name_length); + base::string16 actual_name; + name_length++; + actual_name.resize(name_length); + strings->GetLocaleName(locale_index, const_cast<WCHAR*>(actual_name.data()), + name_length); + EXPECT_STREQ(locale_name.c_str(), actual_name.c_str()); + + UINT32 string_length = 0; + strings->GetStringLength(locale_index, &string_length); + EXPECT_EQ(expected_value.size(), string_length); + base::string16 actual_value; + string_length++; + actual_value.resize(string_length); + strings->GetString(locale_index, const_cast<WCHAR*>(actual_value.data()), + string_length); + EXPECT_STREQ(expected_value.c_str(), actual_value.c_str()); +} + +TEST_F(DWriteFontProxyUnitTest, GetFamilyNames) { + HRESULT hr; + if (!factory) + return; + + UINT32 index = UINT_MAX; + BOOL exists = FALSE; + hr = collection_->FindFamilyName(L"Aardvark", &index, &exists); + ASSERT_EQ(2, fake_collection_->MessageCount()); + + mswr::ComPtr<IDWriteFontFamily> family; + hr = collection_->GetFontFamily(index, &family); + EXPECT_EQ(S_OK, hr); + EXPECT_EQ(2, fake_collection_->MessageCount()); + + mswr::ComPtr<IDWriteLocalizedStrings> names; + hr = family->GetFamilyNames(&names); + EXPECT_EQ(S_OK, hr); + EXPECT_EQ(3, fake_collection_->MessageCount()); + EXPECT_EQ(DWriteFontProxyMsg_GetFamilyNames::ID, + fake_collection_->GetMessage(2)->type()); + + EXPECT_EQ(2, names->GetCount()); + UINT32 locale_index = 0; + BOOL locale_exists = FALSE; + hr = names->FindLocaleName(L"fr-fr", &locale_index, &locale_exists); + EXPECT_EQ(S_OK, hr); + EXPECT_FALSE(locale_exists); + + CheckLocale(L"en-us", L"Aardvark", names.Get()); + CheckLocale(L"de-de", L"Erdferkel", names.Get()); + + base::string16 unused; + unused.resize(25); + hr = names->GetLocaleName(15234, const_cast<WCHAR*>(unused.data()), + unused.size() - 1); + EXPECT_FALSE(SUCCEEDED(hr)); +} + +TEST_F(DWriteFontProxyUnitTest, GetFontCollection) { + HRESULT hr; + if (!factory) + return; + + UINT32 index = UINT_MAX; + BOOL exists = FALSE; + hr = collection_->FindFamilyName(L"Arial", &index, &exists); + ASSERT_EQ(2, fake_collection_->MessageCount()); + + mswr::ComPtr<IDWriteFontFamily> family; + hr = collection_->GetFontFamily(2, &family); + EXPECT_EQ(S_OK, hr); + EXPECT_EQ(2, fake_collection_->MessageCount()); + + mswr::ComPtr<IDWriteFontCollection> returned_collection; + hr = family->GetFontCollection(&returned_collection); + EXPECT_EQ(S_OK, hr); + EXPECT_EQ(2, fake_collection_->MessageCount()); + EXPECT_EQ(collection_.Get(), returned_collection.Get()); +} + +TEST_F(DWriteFontProxyUnitTest, GetFamilyNamesShouldNotIPCAfterLoadingFamily) { + HRESULT hr; + if (!factory) + return; + + UINT32 index = UINT_MAX; + BOOL exists = FALSE; + collection_->FindFamilyName(L"Arial", &index, &exists); + mswr::ComPtr<IDWriteFontFamily> family; + collection_->GetFontFamily(index, &family); + family->GetFontCount(); + EXPECT_EQ(fake_collection_->MessageCount(), 3); + + mswr::ComPtr<IDWriteLocalizedStrings> names; + hr = family->GetFamilyNames(&names); + EXPECT_EQ(S_OK, hr); + EXPECT_EQ(3, fake_collection_->MessageCount()); +} + +TEST_F(DWriteFontProxyUnitTest, + GetFontFamilyShouldNotCreateFamilyWhenIndexIsInvalid) { + HRESULT hr; + if (!factory) + return; + + UINT32 index = UINT_MAX; + BOOL exists = FALSE; + hr = collection_->FindFamilyName(L"Arial", &index, &exists); + ASSERT_EQ(2, fake_collection_->MessageCount()); + + mswr::ComPtr<IDWriteFontFamily> family; + hr = collection_->GetFontFamily(1654, &family); + + EXPECT_FALSE(SUCCEEDED(hr)); + EXPECT_EQ(2, fake_collection_->MessageCount()); +} + +TEST_F(DWriteFontProxyUnitTest, LoadingFontFamily) { + HRESULT hr; + if (!factory) + return; + + UINT32 index = UINT_MAX; + BOOL exists = FALSE; + collection_->FindFamilyName(L"Arial", &index, &exists); + mswr::ComPtr<IDWriteFontFamily> family; + collection_->GetFontFamily(index, &family); + ASSERT_EQ(2, fake_collection_->MessageCount()); + + UINT32 font_count = family->GetFontCount(); + EXPECT_LT(0u, font_count); + EXPECT_EQ(3, fake_collection_->MessageCount()); + EXPECT_EQ(DWriteFontProxyMsg_GetFontFiles::ID, + fake_collection_->GetMessage(2)->type()); + mswr::ComPtr<IDWriteFont> font; + hr = family->GetFirstMatchingFont(DWRITE_FONT_WEIGHT_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, + DWRITE_FONT_STYLE_NORMAL, &font); + EXPECT_EQ(S_OK, hr); + EXPECT_EQ(3, fake_collection_->MessageCount()); + mswr::ComPtr<IDWriteFont> font2; + hr = family->GetFont(0, &font2); + EXPECT_EQ(S_OK, hr); + EXPECT_EQ(3, fake_collection_->MessageCount()); + mswr::ComPtr<IDWriteFontList> matching_fonts; + hr = family->GetMatchingFonts(DWRITE_FONT_WEIGHT_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, + DWRITE_FONT_STYLE_NORMAL, &matching_fonts); + EXPECT_EQ(S_OK, hr); + EXPECT_EQ(3, fake_collection_->MessageCount()); + EXPECT_NE(nullptr, matching_fonts.Get()); +} + +TEST_F(DWriteFontProxyUnitTest, GetFontFromFontFaceShouldFindFont) { + HRESULT hr; + if (!factory) + return; + + UINT32 index = UINT_MAX; + BOOL exists = FALSE; + collection_->FindFamilyName(L"Arial", &index, &exists); + mswr::ComPtr<IDWriteFontFamily> family; + collection_->GetFontFamily(index, &family); + + mswr::ComPtr<IDWriteFont> font; + family->GetFirstMatchingFont(DWRITE_FONT_WEIGHT_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, + DWRITE_FONT_STYLE_NORMAL, &font); + + mswr::ComPtr<IDWriteFontFace> font_face; + hr = font->CreateFontFace(&font_face); + ASSERT_TRUE(SUCCEEDED(hr)); + ASSERT_EQ(3, fake_collection_->MessageCount()); + + mswr::ComPtr<IDWriteFont> found_font; + collection_->GetFontFromFontFace(font_face.Get(), &found_font); + EXPECT_NE(nullptr, found_font.Get()); + EXPECT_EQ(3, fake_collection_->MessageCount()); +} + +} // namespace + +} // namespace content diff --git a/content/child/dwrite_font_proxy/dwrite_localized_strings_win.cc b/content/child/dwrite_font_proxy/dwrite_localized_strings_win.cc new file mode 100644 index 0000000..27f8fa4 --- /dev/null +++ b/content/child/dwrite_font_proxy/dwrite_localized_strings_win.cc @@ -0,0 +1,91 @@ +// 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_localized_strings_win.h" + +#include "base/logging.h" + +namespace content { + +DWriteLocalizedStrings::DWriteLocalizedStrings() = default; + +DWriteLocalizedStrings::~DWriteLocalizedStrings() = default; + +HRESULT DWriteLocalizedStrings::FindLocaleName(const WCHAR* locale_name, + UINT32* index, + BOOL* exists) { + for (size_t n = 0; n < strings_.size(); ++n) { + if (_wcsicmp(strings_[n].first.data(), locale_name) == 0) { + *index = n; + *exists = TRUE; + return S_OK; + } + } + + *index = UINT_MAX; + *exists = FALSE; + return S_OK; +} + +UINT32 DWriteLocalizedStrings::GetCount() { + return strings_.size(); +} + +HRESULT DWriteLocalizedStrings::GetLocaleName(UINT32 index, + WCHAR* locale_name, + UINT32 size) { + if (index >= strings_.size()) + return E_INVALIDARG; + // string16::size does not count the null terminator as part of the string, + // but GetLocaleName requires the caller to reserve space for the null + // terminator, so we need to ensure |size| is greater than the count of + // characters. + if (size <= strings_[index].first.size()) + return E_INVALIDARG; + wcsncpy(locale_name, strings_[index].first.c_str(), size); + return S_OK; +} + +HRESULT DWriteLocalizedStrings::GetLocaleNameLength(UINT32 index, + UINT32* length) { + if (index >= strings_.size()) + return E_INVALIDARG; + // Oddly, GetLocaleNameLength requires the length to not count the null + // terminator, even though GetLocaleName requires the output to be null + // terminated. + *length = strings_[index].first.size(); + return S_OK; +} + +HRESULT DWriteLocalizedStrings::GetString(UINT32 index, + WCHAR* string_buffer, + UINT32 size) { + if (index >= strings_.size()) + return E_INVALIDARG; + // string16::size does not count the null terminator as part of the string, + // but GetString requires the caller to reserve space for the null terminator, + // so we need to ensure |size| is greater than the count of characters. + if (size <= strings_[index].second.size()) + return E_INVALIDARG; + wcsncpy(string_buffer, strings_[index].second.c_str(), size); + return S_OK; +} + +HRESULT DWriteLocalizedStrings::GetStringLength(UINT32 index, UINT32* length) { + if (index >= strings_.size()) + return E_INVALIDARG; + // Oddly, GetStringLength requires the length to not count the null + // terminator, even though GetString requires the output to be null + // terminated. + *length = strings_[index].second.size(); + return S_OK; +} + +HRESULT DWriteLocalizedStrings::RuntimeClassInitialize( + std::vector<std::pair<base::string16, base::string16>>* strings) { + strings_.swap(*strings); + return S_OK; +} + +} // namespace content diff --git a/content/child/dwrite_font_proxy/dwrite_localized_strings_win.h b/content/child/dwrite_font_proxy/dwrite_localized_strings_win.h new file mode 100644 index 0000000..796f435 --- /dev/null +++ b/content/child/dwrite_font_proxy/dwrite_localized_strings_win.h @@ -0,0 +1,57 @@ +// 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. + +#ifndef CONTENT_CHILD_DWRITE_FONT_PROXY_DWRITE_LOCALIZED_STRINGS_WIN_H_ +#define CONTENT_CHILD_DWRITE_FONT_PROXY_DWRITE_LOCALIZED_STRINGS_WIN_H_ + +#include <dwrite.h> +#include <wrl.h> +#include <utility> +#include <vector> + +#include "base/macros.h" +#include "base/strings/string16.h" + +namespace content { + +// Impements IDWriteLocalizedStrings, backed by a vector of string pairs. +class DWriteLocalizedStrings + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + IDWriteLocalizedStrings> { + public: + DWriteLocalizedStrings(); + ~DWriteLocalizedStrings() override; + + // IDWriteLocalizedStrings: + HRESULT STDMETHODCALLTYPE FindLocaleName(const WCHAR* locale_name, + UINT32* index, + BOOL* exists) override; + UINT32 STDMETHODCALLTYPE GetCount() override; + HRESULT STDMETHODCALLTYPE GetLocaleName(UINT32 index, + WCHAR* locale_name, + UINT32 size) override; + HRESULT STDMETHODCALLTYPE GetLocaleNameLength(UINT32 index, + UINT32* length) override; + HRESULT STDMETHODCALLTYPE GetString(UINT32 index, + WCHAR* string_buffer, + UINT32 size) override; + HRESULT STDMETHODCALLTYPE GetStringLength(UINT32 index, + UINT32* length) override; + + HRESULT STDMETHODCALLTYPE RuntimeClassInitialize( + std::vector<std::pair<base::string16, base::string16>>* strings); + + private: + // List of strings. First element of each pair is the locale, and the second + // element is the associated value. Use a vector because the expected number + // of pairs is small (typically 1-2, rarely up to a few dozen?) and we need + // index-based access. + std::vector<std::pair<base::string16, base::string16>> strings_; + + DISALLOW_ASSIGN(DWriteLocalizedStrings); +}; + +} // namespace content +#endif // CONTENT_CHILD_DWRITE_FONT_PROXY_DWRITE_LOCALIZED_STRINGS_WIN_H_ diff --git a/content/common/content_message_generator.h b/content/common/content_message_generator.h index a3b31ad..9210587 100644 --- a/content/common/content_message_generator.h +++ b/content/common/content_message_generator.h @@ -74,3 +74,7 @@ #include "content/common/media/media_player_messages_android.h" #include "content/common/media/media_session_messages_android.h" #endif // defined(OS_ANDROID) + +#if defined(OS_WIN) +#include "content/common/dwrite_font_proxy_messages.h" +#endif // defined(OS_WIN) diff --git a/content/common/dwrite_font_proxy_messages.h b/content/common/dwrite_font_proxy_messages.h new file mode 100644 index 0000000..84cb751 --- /dev/null +++ b/content/common/dwrite_font_proxy_messages.h @@ -0,0 +1,39 @@ +// 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 <utility> +#include <vector> + +#include "base/strings/string16.h" +#include "content/common/content_export.h" +#include "ipc/ipc_message_macros.h" + +#undef IPC_MESSAGE_EXPORT +#define IPC_MESSAGE_EXPORT CONTENT_EXPORT +#define IPC_MESSAGE_START DWriteFontProxyMsgStart + +// The macros can't handle a complex template declaration, so we typedef it. +typedef std::pair<base::string16, base::string16> DWriteStringPair; + +// Locates the index of the specified font family within the system collection. +IPC_SYNC_MESSAGE_CONTROL1_1(DWriteFontProxyMsg_FindFamily, + base::string16 /* family_name */, + uint32_t /* out index */) + +// Returns the number of font families in the system collection. +IPC_SYNC_MESSAGE_CONTROL0_1(DWriteFontProxyMsg_GetFamilyCount, + uint32_t /* out count */) + +// Returns the list of locale and family name pairs for the font family at the +// specified index. +IPC_SYNC_MESSAGE_CONTROL1_1( + DWriteFontProxyMsg_GetFamilyNames, + uint32_t /* family_index */, + std::vector<DWriteStringPair> /* out family_names */) + +// Returns the list of font file paths in the system font directory that contain +// font data for the font family at the specified index. +IPC_SYNC_MESSAGE_CONTROL1_1(DWriteFontProxyMsg_GetFontFiles, + uint32_t /* family_index */, + std::vector<base::string16> /* out file_paths */) diff --git a/content/common/font_warmup_win.cc b/content/common/font_warmup_win.cc index 6a059d0..ddc1889 100644 --- a/content/common/font_warmup_win.cc +++ b/content/common/font_warmup_win.cc @@ -96,56 +96,6 @@ NTSTATUS WINAPI NtALpcConnectPortPatch(HANDLE* port_handle, return STATUS_ACCESS_DENIED; } -// Directwrite connects to the font cache service to retrieve information about -// fonts installed on the system etc. This works well outside the sandbox and -// within the sandbox as long as the lpc connection maintained by the current -// process with the font cache service remains valid. It appears that there -// are cases when this connection is dropped after which directwrite is unable -// to connect to the font cache service which causes problems with characters -// disappearing. -// Directwrite has fallback code to enumerate fonts if it is unable to connect -// to the font cache service. We need to intercept the following APIs to -// ensure that it does not connect to the font cache service. -// NtALpcConnectPort -// OpenSCManagerW -// OpenServiceW -// StartServiceW -// CloseServiceHandle. -// These are all IAT patched. -void PatchServiceManagerCalls() { - static bool is_patched = false; - if (is_patched) - return; - const char* service_provider_dll = - (base::win::GetVersion() >= base::win::VERSION_WIN8 - ? "api-ms-win-service-management-l1-1-0.dll" - : "advapi32.dll"); - - is_patched = true; - - DWORD patched = - g_iat_patch_open_sc_manager.Patch(L"dwrite.dll", service_provider_dll, - "OpenSCManagerW", OpenSCManagerWPatch); - DCHECK(patched == 0); - - patched = g_iat_patch_close_service_handle.Patch( - L"dwrite.dll", service_provider_dll, "CloseServiceHandle", - CloseServiceHandlePatch); - DCHECK(patched == 0); - - patched = g_iat_patch_open_service.Patch(L"dwrite.dll", service_provider_dll, - "OpenServiceW", OpenServiceWPatch); - DCHECK(patched == 0); - - patched = g_iat_patch_start_service.Patch( - L"dwrite.dll", service_provider_dll, "StartServiceW", StartServiceWPatch); - DCHECK(patched == 0); - - patched = g_iat_patch_nt_connect_port.Patch( - L"dwrite.dll", "ntdll.dll", "NtAlpcConnectPort", NtALpcConnectPortPatch); - DCHECK(patched == 0); -} - // Windows-only DirectWrite support. These warm up the DirectWrite paths // before sandbox lock down to allow Skia access to the Font Manager service. void CreateDirectWriteFactory(IDWriteFactory** factory) { @@ -461,6 +411,56 @@ GdiFontPatchDataImpl::GdiFontPatchDataImpl(const base::FilePath& path) { } // namespace +// Directwrite connects to the font cache service to retrieve information about +// fonts installed on the system etc. This works well outside the sandbox and +// within the sandbox as long as the lpc connection maintained by the current +// process with the font cache service remains valid. It appears that there +// are cases when this connection is dropped after which directwrite is unable +// to connect to the font cache service which causes problems with characters +// disappearing. +// Directwrite has fallback code to enumerate fonts if it is unable to connect +// to the font cache service. We need to intercept the following APIs to +// ensure that it does not connect to the font cache service. +// NtALpcConnectPort +// OpenSCManagerW +// OpenServiceW +// StartServiceW +// CloseServiceHandle. +// These are all IAT patched. +void PatchServiceManagerCalls() { + static bool is_patched = false; + if (is_patched) + return; + const char* service_provider_dll = + (base::win::GetVersion() >= base::win::VERSION_WIN8 + ? "api-ms-win-service-management-l1-1-0.dll" + : "advapi32.dll"); + + is_patched = true; + + DWORD patched = + g_iat_patch_open_sc_manager.Patch(L"dwrite.dll", service_provider_dll, + "OpenSCManagerW", OpenSCManagerWPatch); + DCHECK(patched == 0); + + patched = g_iat_patch_close_service_handle.Patch( + L"dwrite.dll", service_provider_dll, "CloseServiceHandle", + CloseServiceHandlePatch); + DCHECK(patched == 0); + + patched = g_iat_patch_open_service.Patch(L"dwrite.dll", service_provider_dll, + "OpenServiceW", OpenServiceWPatch); + DCHECK(patched == 0); + + patched = g_iat_patch_start_service.Patch( + L"dwrite.dll", service_provider_dll, "StartServiceW", StartServiceWPatch); + DCHECK(patched == 0); + + patched = g_iat_patch_nt_connect_port.Patch( + L"dwrite.dll", "ntdll.dll", "NtAlpcConnectPort", NtALpcConnectPortPatch); + DCHECK(patched == 0); +} + void DoPreSandboxWarmupForTypeface(SkTypeface* typeface) { SkPaint paint_warmup; paint_warmup.setTypeface(typeface); diff --git a/content/common/font_warmup_win.h b/content/common/font_warmup_win.h index 7a0e9b5..587b731 100644 --- a/content/common/font_warmup_win.h +++ b/content/common/font_warmup_win.h @@ -50,6 +50,24 @@ CONTENT_EXPORT void SetPreSandboxWarmupFontMgrForTesting(SkFontMgr* fontmgr); // Warmup the direct write font manager for content processes. CONTENT_EXPORT void WarmupDirectWrite(); +// Directwrite connects to the font cache service to retrieve information about +// fonts installed on the system etc. This works well outside the sandbox and +// within the sandbox as long as the lpc connection maintained by the current +// process with the font cache service remains valid. It appears that there +// are cases when this connection is dropped after which directwrite is unable +// to connect to the font cache service which causes problems with characters +// disappearing. +// Directwrite has fallback code to enumerate fonts if it is unable to connect +// to the font cache service. We need to intercept the following APIs to +// ensure that it does not connect to the font cache service. +// NtALpcConnectPort +// OpenSCManagerW +// OpenServiceW +// StartServiceW +// CloseServiceHandle. +// These are all IAT patched. +CONTENT_EXPORT void PatchServiceManagerCalls(); + } // namespace content #endif // CONTENT_COMMON_FONT_WARMUP_WIN_H_ diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 6cea7d4..2fba878 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -1126,6 +1126,8 @@ 'browser/renderer_host/delegated_frame_evictor.h', 'browser/renderer_host/dip_util.cc', 'browser/renderer_host/dip_util.h', + 'browser/renderer_host/dwrite_font_proxy_message_filter_win.cc', + 'browser/renderer_host/dwrite_font_proxy_message_filter_win.h', 'browser/renderer_host/event_with_latency_info.h', 'browser/renderer_host/file_utilities_message_filter.cc', 'browser/renderer_host/file_utilities_message_filter.h', diff --git a/content/content_child.gypi b/content/content_child.gypi index 792e2fe..81c0a05 100644 --- a/content/content_child.gypi +++ b/content/content_child.gypi @@ -82,6 +82,12 @@ 'child/database_util.h', 'child/db_message_filter.cc', 'child/db_message_filter.h', + 'child/dwrite_font_proxy/dwrite_font_proxy_init_win.cc', + 'child/dwrite_font_proxy/dwrite_font_proxy_init_win.h', + 'child/dwrite_font_proxy/dwrite_font_proxy_win.cc', + 'child/dwrite_font_proxy/dwrite_font_proxy_win.h', + 'child/dwrite_font_proxy/dwrite_localized_strings_win.cc', + 'child/dwrite_font_proxy/dwrite_localized_strings_win.h', 'child/file_info_util.cc', 'child/file_info_util.h', 'child/fileapi/file_system_dispatcher.cc', diff --git a/content/content_common.gypi b/content/content_common.gypi index d1fb8a7..463b8cd 100644 --- a/content/content_common.gypi +++ b/content/content_common.gypi @@ -249,6 +249,7 @@ 'common/drag_messages.h', 'common/drag_traits.h', 'common/dwrite_font_platform_win.cc', + 'common/dwrite_font_proxy_messages.h', 'common/edit_command.h', 'common/file_utilities_messages.h', 'common/fileapi/file_system_messages.h', diff --git a/content/content_tests.gypi b/content/content_tests.gypi index 8fc67f5..cf3427c 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -116,6 +116,8 @@ 'test/content_browser_sanity_checker.h', 'test/content_test_suite.cc', 'test/content_test_suite.h', + 'test/dwrite_font_fake_sender_win.h', + 'test/dwrite_font_fake_sender_win.cc', 'test/fake_compositor_dependencies.cc', 'test/fake_compositor_dependencies.h', 'test/fake_plugin_service.cc', @@ -533,6 +535,7 @@ 'browser/quota/usage_tracker_unittest.cc', 'browser/renderer_host/begin_frame_observer_proxy_unittest.cc', 'browser/renderer_host/clipboard_message_filter_unittest.cc', + 'browser/renderer_host/dwrite_font_proxy_message_filter_win_unittest.cc', 'browser/renderer_host/event_with_latency_info_unittest.cc', 'browser/renderer_host/input/gesture_event_queue_unittest.cc', 'browser/renderer_host/input/input_router_impl_unittest.cc', @@ -624,6 +627,7 @@ 'child/blink_platform_impl_unittest.cc', 'child/blob_storage/blob_consolidation_unittest.cc', 'child/blob_storage/blob_transport_controller_unittest.cc', + 'child/dwrite_font_proxy/dwrite_font_proxy_win_unittest.cc', 'child/fileapi/webfilewriter_base_unittest.cc', 'child/indexed_db/indexed_db_dispatcher_unittest.cc', 'child/indexed_db/mock_webidbcallbacks.cc', diff --git a/content/test/dwrite_font_fake_sender_win.cc b/content/test/dwrite_font_fake_sender_win.cc new file mode 100644 index 0000000..83203af --- /dev/null +++ b/content/test/dwrite_font_fake_sender_win.cc @@ -0,0 +1,174 @@ +// 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/test/dwrite_font_fake_sender_win.h" + +#include <shlobj.h> + +#include "content/common/dwrite_font_proxy_messages.h" + +namespace content { + +void AddFamily(const base::string16& font_path, + const base::string16& family_name, + const base::string16& base_family_name, + FakeFontCollection* collection) { + collection->AddFont(family_name) + .AddFamilyName(L"en-us", family_name) + .AddFilePath(font_path + L"\\" + base_family_name + L".ttf") + .AddFilePath(font_path + L"\\" + base_family_name + L"bd.ttf") + .AddFilePath(font_path + L"\\" + base_family_name + L"bi.ttf") + .AddFilePath(font_path + L"\\" + base_family_name + L"i.ttf"); +} + +IPC::Sender* CreateFakeCollectionSender() { + std::vector<base::char16> font_path_chars; + font_path_chars.resize(MAX_PATH); + SHGetSpecialFolderPath(nullptr /* hwndOwner - reserved */, + font_path_chars.data(), CSIDL_FONTS, + FALSE /* fCreate */); + base::string16 font_path(font_path_chars.data()); + scoped_refptr<FakeFontCollection> fake_collection = new FakeFontCollection(); + AddFamily(font_path, L"Arial", L"arial", fake_collection.get()); + AddFamily(font_path, L"Courier New", L"cour", fake_collection.get()); + AddFamily(font_path, L"Times New Roman", L"times", fake_collection.get()); + return fake_collection->GetSender(); +} + +FakeFont::FakeFont(const base::string16& name) : font_name_(name) {} + +FakeFont::~FakeFont() = default; + +FakeFontCollection::FakeFontCollection() = default; + +FakeFont& FakeFontCollection::AddFont(const base::string16& font_name) { + fonts_.emplace_back(font_name); + return fonts_.back(); +} + +size_t FakeFontCollection::MessageCount() { + return messages_.size(); +} + +IPC::Message* FakeFontCollection::GetMessage(size_t index) { + return messages_[index].get(); +} + +IPC::Sender* FakeFontCollection::GetSender() { + return new FakeSender(this, false /* track_messages */); +} + +IPC::Sender* FakeFontCollection::GetTrackingSender() { + return new FakeSender(this, true /* track_messages */); +} + +FakeFontCollection::ReplySender::ReplySender(FakeFontCollection* collection) + : collection_(collection) {} + +FakeFontCollection::ReplySender::~ReplySender() = default; + +scoped_ptr<IPC::Message>& FakeFontCollection::ReplySender::OnMessageReceived( + const IPC::Message& msg) { + reply_.reset(); + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(ReplySender, msg) + IPC_MESSAGE_HANDLER(DWriteFontProxyMsg_FindFamily, OnFindFamily) + IPC_MESSAGE_HANDLER(DWriteFontProxyMsg_GetFamilyCount, OnGetFamilyCount) + IPC_MESSAGE_HANDLER(DWriteFontProxyMsg_GetFamilyNames, OnGetFamilyNames) + IPC_MESSAGE_HANDLER(DWriteFontProxyMsg_GetFontFiles, OnGetFontFiles) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return reply_; +} + +bool FakeFontCollection::ReplySender::Send(IPC::Message* msg) { + reply_.reset(msg); + return true; +} + +void FakeFontCollection::ReplySender::OnFindFamily( + const base::string16& family_name, + uint32_t* index) { + collection_->OnFindFamily(family_name, index); +} + +void FakeFontCollection::ReplySender::OnGetFamilyCount(uint32_t* count) { + collection_->OnGetFamilyCount(count); +} + +void FakeFontCollection::ReplySender::OnGetFamilyNames( + uint32_t family_index, + std::vector<DWriteStringPair>* family_names) { + collection_->OnGetFamilyNames(family_index, family_names); +} + +void FakeFontCollection::ReplySender::OnGetFontFiles( + uint32_t family_index, + std::vector<base::string16>* file_paths) { + collection_->OnGetFontFiles(family_index, file_paths); +} + +FakeFontCollection::FakeSender::FakeSender(FakeFontCollection* collection, + bool track_messages) + : track_messages_(track_messages), collection_(collection) {} + +FakeFontCollection::FakeSender::~FakeSender() = default; + +bool FakeFontCollection::FakeSender::Send(IPC::Message* message) { + std::unique_ptr<IPC::Message> incoming_message; + if (track_messages_) + collection_->messages_.emplace_back(message); + else + incoming_message.reset(message); // Ensure message is deleted. + std::unique_ptr<ReplySender> sender = collection_->GetReplySender(); + scoped_ptr<IPC::Message> reply; + reply.swap(sender->OnMessageReceived(*message)); + + IPC::SyncMessage* sync_message = reinterpret_cast<IPC::SyncMessage*>(message); + scoped_ptr<IPC::MessageReplyDeserializer> serializer( + sync_message->GetReplyDeserializer()); + serializer->SerializeOutputParameters(*(reply.get())); + return true; +} + +void FakeFontCollection::OnFindFamily(const base::string16& family_name, + uint32_t* index) { + for (size_t n = 0; n < fonts_.size(); n++) { + if (_wcsicmp(family_name.data(), fonts_[n].font_name_.data()) == 0) { + *index = n; + return; + } + } + *index = UINT32_MAX; +} + +void FakeFontCollection::OnGetFamilyCount(uint32_t* count) { + *count = fonts_.size(); +} + +void FakeFontCollection::OnGetFamilyNames( + uint32_t family_index, + std::vector<DWriteStringPair>* family_names) { + if (family_index >= fonts_.size()) + return; + *family_names = fonts_[family_index].family_names_; +} + +void FakeFontCollection::OnGetFontFiles( + uint32_t family_index, + std::vector<base::string16>* file_paths) { + if (family_index >= fonts_.size()) + return; + *file_paths = fonts_[family_index].file_paths_; +} + +std::unique_ptr<FakeFontCollection::ReplySender> +FakeFontCollection::GetReplySender() { + return std::unique_ptr<FakeFontCollection::ReplySender>( + new FakeFontCollection::ReplySender(this)); +} + +FakeFontCollection::~FakeFontCollection() = default; + +} // namespace content diff --git a/content/test/dwrite_font_fake_sender_win.h b/content/test/dwrite_font_fake_sender_win.h new file mode 100644 index 0000000..d6f438a --- /dev/null +++ b/content/test/dwrite_font_fake_sender_win.h @@ -0,0 +1,160 @@ +// Copyright (c) 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. + +#ifndef CONTENT_TEST_DWRITE_FONT_FAKE_SENDER_WIN_H_ +#define CONTENT_TEST_DWRITE_FONT_FAKE_SENDER_WIN_H_ + +#include <wrl.h> + +#include <utility> +#include <vector> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/strings/string16.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_sender.h" + +namespace content { + +class FakeFontCollection; + +// Creates a new FakeFontCollection, seeded with some basic data, and returns a +// Sender that can be used to interact with the collection. +IPC::Sender* CreateFakeCollectionSender(); + +// Helper class for describing a font object. Use FakeFontCollection instead. +class FakeFont { + public: + explicit FakeFont(const base::string16& name); + + ~FakeFont(); + + FakeFont& AddFilePath(const base::string16& file_path) { + file_paths_.push_back(file_path); + return *this; + } + + FakeFont& AddFamilyName(const base::string16& locale, + const base::string16& family_name) { + family_names_.emplace_back(locale, family_name); + return *this; + } + + private: + friend FakeFontCollection; + base::string16 font_name_; + std::vector<base::string16> file_paths_; + std::vector<std::pair<base::string16, base::string16>> family_names_; + + DISALLOW_ASSIGN(FakeFont); +}; + +// Implements a font collection that supports interaction through sending IPC +// messages from dwrite_font_proxy_messages.h. +// Usage: +// Create a new FakeFontCollection. +// Call AddFont() to add fonts. +// For each font, call methods on the font to configure the font. +// Note: the indices of the fonts will correspond to the order they were +// added. The collection will not sort or reorder fonts in any way. +// Call GetSender()/GetTrackingSender() to obtain an IPC::Sender. +// Call Send() with DWriteFontProxyMsg_* to interact with the collection. +// Call MessageCount()/GetMessage() to inspect the messages that were sent. +// +// The internal code flow for GetSender()/Send() is as follows: +// GetSender() returns a new FakeSender, pointing back to the collection. +// FakeSender::Send() will create a new ReplySender and call +// ReplySender::OnMessageReceived() +// ReplySender::OnMessageReceived() contains the message map, which will +// internally call ReplySender::On*() and ReplySender::Send() +// ReplySender::Send() will save the reply message, to be used later. +// FakeSender::Send() will retrieve the reply message and save the output. +class FakeFontCollection : public base::RefCounted<FakeFontCollection> { + public: + FakeFontCollection(); + + FakeFont& AddFont(const base::string16& font_name); + + size_t MessageCount(); + + IPC::Message* GetMessage(size_t index); + + IPC::Sender* GetSender(); + + // Like GetSender(), but will keep track of all sent messages for inspection. + IPC::Sender* GetTrackingSender(); + + protected: + // This class handles sending the reply message back to the "renderer" + class ReplySender : public IPC::Sender { + public: + explicit ReplySender(FakeFontCollection* collection); + + ~ReplySender() override; + + scoped_ptr<IPC::Message>& OnMessageReceived(const IPC::Message& msg); + + bool Send(IPC::Message* msg) override; + + private: + void OnFindFamily(const base::string16& family_name, uint32_t* index); + + void OnGetFamilyCount(uint32_t* count); + + void OnGetFamilyNames( + uint32_t family_index, + std::vector<std::pair<base::string16, base::string16>>* family_names); + void OnGetFontFiles(uint32 family_index, + std::vector<base::string16>* file_paths_); + + private: + scoped_refptr<FakeFontCollection> collection_; + scoped_ptr<IPC::Message> reply_; + + DISALLOW_COPY_AND_ASSIGN(ReplySender); + }; + + // This class can be used by the "renderer" to send messages to the "browser" + class FakeSender : public IPC::Sender { + public: + FakeSender(FakeFontCollection* collection, bool track_messages); + + ~FakeSender() override; + + bool Send(IPC::Message* message) override; + + private: + bool track_messages_; + scoped_refptr<FakeFontCollection> collection_; + + DISALLOW_COPY_AND_ASSIGN(FakeSender); + }; + + void OnFindFamily(const base::string16& family_name, uint32_t* index); + + void OnGetFamilyCount(uint32_t* count); + + void OnGetFamilyNames( + uint32_t family_index, + std::vector<std::pair<base::string16, base::string16>>* family_names); + + void OnGetFontFiles(uint32 family_index, + std::vector<base::string16>* file_paths); + + std::unique_ptr<ReplySender> GetReplySender(); + + private: + friend class base::RefCounted<FakeFontCollection>; + ~FakeFontCollection(); + + std::vector<FakeFont> fonts_; + std::vector<std::unique_ptr<IPC::Message>> messages_; + + DISALLOW_COPY_AND_ASSIGN(FakeFontCollection); +}; + +} // namespace content + +#endif // CONTENT_TEST_DWRITE_FONT_FAKE_SENDER_WIN_H_ diff --git a/ipc/ipc_message_start.h b/ipc/ipc_message_start.h index 65f731e..b0942bd 100644 --- a/ipc/ipc_message_start.h +++ b/ipc/ipc_message_start.h @@ -134,6 +134,7 @@ enum IPCMessageStart { ArcInstanceHostMsgStart, DistillerMsgStart, StartupMetricMsgStart, + DWriteFontProxyMsgStart, LastIPCMsgStart // Must come last. }; diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 21544ac..e7b935b 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml @@ -7372,6 +7372,34 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries. </summary> </histogram> +<histogram name="DirectWrite.Fonts.Proxy.LoaderType" + enum="DirectWriteFontLoaderType"> + <owner>kulshin@chromium.org</owner> + <summary> + The codepath that was used to load a font family. This is logged in the + browser every time a renderer attempts to load a font family, once per font + file. + </summary> +</histogram> + +<histogram name="DirectWrite.Fonts.Proxy.LoadFamilyResult" + enum="DirectWriteLoadFamilyResult"> + <owner>kulshin@chromium.org</owner> + <summary> + The outcome of attempting to load a font family in the renderer (success vs + failure and number of families). This is logged in the renderer once per + family that is loaded. + </summary> +</histogram> + +<histogram name="DirectWrite.Fonts.Proxy.LoadFamilyTime" units="milliseconds"> + <owner>kulshin@chromium.org</owner> + <summary> + The time taken to load a font family, excluding glyph data. This is logged + in the renderer once per family that is loaded. + </summary> +</histogram> + <histogram name="DisabledExtension.ExtensionWipedStatus" enum="BooleanWiped"> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <summary>Whether an extension has been wiped out.</summary> @@ -58960,6 +58988,21 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries. <int value="1" label="Failed"/> </enum> +<enum name="DirectWriteFontLoaderType" type="int"> + <int value="0" label="File: system font directory"/> + <int value="1" label="File: sandbox whitelisted"/> + <int value="2" label="File: outside sandbox whitelist"/> + <int value="3" label="Other"/> +</enum> + +<enum name="DirectWriteLoadFamilyResult" type="int"> + <int value="0" label="Success: single family"/> + <int value="1" label="Success: matched from collection"/> + <int value="2" label="Error: multiple families"/> + <int value="3" label="Error: no families"/> + <int value="4" label="Error: failed to create colleciton"/> +</enum> + <enum name="DistillableType" type="int"> <int value="0" label="Not distillable"/> <int value="1" label="Non-mobile-friendly Distillable"/> diff --git a/ui/gfx/win/direct_write.cc b/ui/gfx/win/direct_write.cc index 38c926a..9c8250b 100644 --- a/ui/gfx/win/direct_write.cc +++ b/ui/gfx/win/direct_write.cc @@ -4,8 +4,6 @@ #include "ui/gfx/win/direct_write.h" -#include <dwrite.h> - #include "base/basictypes.h" #include "base/command_line.h" #include "base/metrics/field_trial.h" @@ -57,12 +55,7 @@ bool ShouldUseDirectWrite() { return group_name != "Disabled"; } -void MaybeInitializeDirectWrite() { - static bool tried_dwrite_initialize = false; - if (tried_dwrite_initialize) - return; - tried_dwrite_initialize = true; - +void CreateDWriteFactory(IDWriteFactory** factory) { if (!ShouldUseDirectWrite() || base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableDirectWriteForUI)) { @@ -81,13 +74,26 @@ void MaybeInitializeDirectWrite() { if (!dwrite_create_factory_proc) return; + // Failure to create the DirectWrite factory indicates a corrupt dll. + base::win::ScopedComPtr<IUnknown> factory_unknown; + if (FAILED(dwrite_create_factory_proc(DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + factory_unknown.Receive()))) { + return; + } + factory_unknown.QueryInterface<IDWriteFactory>(factory); +} + +void MaybeInitializeDirectWrite() { + static bool tried_dwrite_initialize = false; + if (tried_dwrite_initialize) + return; + tried_dwrite_initialize = true; + base::win::ScopedComPtr<IDWriteFactory> factory; + CreateDWriteFactory(factory.Receive()); - // Failure to create the DirectWrite factory indicates a corrupt dll. - if (FAILED(dwrite_create_factory_proc( - DWRITE_FACTORY_TYPE_SHARED, - __uuidof(IDWriteFactory), - reinterpret_cast<IUnknown**>(factory.Receive())))) + if (factory == nullptr) return; // The skia call to create a new DirectWrite font manager instance can fail diff --git a/ui/gfx/win/direct_write.h b/ui/gfx/win/direct_write.h index f16e45c..ec22a00 100644 --- a/ui/gfx/win/direct_write.h +++ b/ui/gfx/win/direct_write.h @@ -5,6 +5,8 @@ #ifndef UI_GFX_WIN_DIRECT_WRITE_H_ #define UI_GFX_WIN_DIRECT_WRITE_H_ +#include <dwrite.h> + #include "ui/gfx/gfx_export.h" namespace gfx { @@ -18,6 +20,9 @@ GFX_EXPORT void MaybeInitializeDirectWrite(); // Returns true if we are using DirectWrite for font metrics and rendering. GFX_EXPORT bool IsDirectWriteEnabled(); +// Creates a DirectWrite factory, if using DirectWrite. +GFX_EXPORT void CreateDWriteFactory(IDWriteFactory** factory); + } // namespace win } // namespace gfx |