summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkulshin <kulshin@chromium.org>2015-12-14 15:12:54 -0800
committerCommit bot <commit-bot@chromium.org>2015-12-14 23:14:21 +0000
commit4039fd5b32b74b01866ca4c1ce73998f19320de1 (patch)
treec149e90f0e6d10d7aed46a3671bec2ee47f86007
parentc16ff6353c0ef641aadf2d86c8840e3f75a1749e (diff)
downloadchromium_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}
-rw-r--r--content/browser/renderer_host/dwrite_font_proxy_message_filter_win.cc345
-rw-r--r--content/browser/renderer_host/dwrite_font_proxy_message_filter_win.h65
-rw-r--r--content/browser/renderer_host/dwrite_font_proxy_message_filter_win_unittest.cc145
-rw-r--r--content/child/dwrite_font_proxy/OWNERS3
-rw-r--r--content/child/dwrite_font_proxy/dwrite_font_proxy_init_win.cc118
-rw-r--r--content/child/dwrite_font_proxy/dwrite_font_proxy_init_win.h30
-rw-r--r--content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc514
-rw-r--r--content/child/dwrite_font_proxy/dwrite_font_proxy_win.h202
-rw-r--r--content/child/dwrite_font_proxy/dwrite_font_proxy_win_unittest.cc378
-rw-r--r--content/child/dwrite_font_proxy/dwrite_localized_strings_win.cc91
-rw-r--r--content/child/dwrite_font_proxy/dwrite_localized_strings_win.h57
-rw-r--r--content/common/content_message_generator.h4
-rw-r--r--content/common/dwrite_font_proxy_messages.h39
-rw-r--r--content/common/font_warmup_win.cc100
-rw-r--r--content/common/font_warmup_win.h18
-rw-r--r--content/content_browser.gypi2
-rw-r--r--content/content_child.gypi6
-rw-r--r--content/content_common.gypi1
-rw-r--r--content/content_tests.gypi4
-rw-r--r--content/test/dwrite_font_fake_sender_win.cc174
-rw-r--r--content/test/dwrite_font_fake_sender_win.h160
-rw-r--r--ipc/ipc_message_start.h1
-rw-r--r--tools/metrics/histograms/histograms.xml43
-rw-r--r--ui/gfx/win/direct_write.cc32
-rw-r--r--ui/gfx/win/direct_write.h5
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", &times_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