Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1697)

Unified Diff: content/browser/renderer_host/dwrite_font_proxy_message_filter_win.cc

Issue 1378353006: Implementation of dwrite font proxy and removal of dwrite font cache (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Merge to head Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/renderer_host/dwrite_font_proxy_message_filter_win.cc
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 0000000000000000000000000000000000000000..7f78c0a7238c1cc72dfba7c4d6ac34058aa46943
--- /dev/null
+++ b/content/browser/renderer_host/dwrite_font_proxy_message_filter_win.cc
@@ -0,0 +1,350 @@
+// 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/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 "content/shell/common/shell_switches.h"
+#include "ipc/ipc_message_macros.h"
+#include "ui/gfx/win/direct_write.h"
+
+namespace mswr = Microsoft::WRL;
+
+namespace content {
+
+// This enum is used to define the buckets for an enumerated UMA histogram.
+// Hence,
+// (a) existing enumerated constants should never be deleted or reordered, and
+// (b) new constants should only be appended at the end of the enumeration.
+enum DWriteFontLoaderType {
+ kFileSystemFontDir = 0,
+ kFileOutsideSandbox = 1,
+ kOtherLoader = 2,
+
+ kFontLoaderTypeMaxValue
+};
+
+DWriteFontProxyMessageFilter::DWriteFontProxyMessageFilter()
+ : BrowserMessageFilter(DWriteFontProxyMsgStart),
+ postTaskAndReply_(&BrowserThread::PostTaskAndReply) {
+ Init();
+}
+
+DWriteFontProxyMessageFilter::DWriteFontProxyMessageFilter(
+ PostTaskAndReplyFn postTaskAndReply)
jam 2015/11/03 00:18:54 i dont think you need this extra complexity with t
Ilya Kulshin 2015/11/03 18:56:21 Yes, that's exactly what I needed. Thanks for the
+ : BrowserMessageFilter(DWriteFontProxyMsgStart),
+ postTaskAndReply_(postTaskAndReply) {
+ Init();
+}
+
+void DWriteFontProxyMessageFilter::Init() {
+ mswr::ComPtr<IDWriteFactory> factory;
+ gfx::win::GetDWriteFactory(&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));
+
+ std::vector<base::char16> fontPathChars;
+ // SHGetSpecialFolderPath requires at least MAX_PATH characters.
+ fontPathChars.resize(MAX_PATH);
+ DCHECK(SHGetSpecialFolderPath(NULL /* hwndOwner - reserved */,
+ fontPathChars.data(), CSIDL_FONTS,
+ FALSE /* fCreate */));
+ windowsFontsPath_ = base::i18n::FoldCase(fontPathChars.data());
+}
+
+bool DWriteFontProxyMessageFilter::OnMessageReceived(
+ const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(DWriteFontProxyMessageFilter, message)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(DWriteFontProxyMsg_FindFamily, OnFindFamily)
+ IPC_MESSAGE_HANDLER(DWriteFontProxyMsg_GetFamilyCount, OnGetFamilyCount)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(DWriteFontProxyMsg_GetFamilyNames,
+ OnGetFamilyNames)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(DWriteFontProxyMsg_GetFontFiles,
+ OnGetFontFiles)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void DWriteFontProxyMessageFilter::OnFindFamily(
+ const base::string16& familyName,
+ IPC::Message* reply_message) {
+ TRACE_EVENT0("dwrite", "FontProxyHost::OnFindFamily");
+ postTaskAndReply_(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&DWriteFontProxyMessageFilter::DoFindFamily, this, familyName,
+ reply_message),
+ base::Bind(base::IgnoreResult(&DWriteFontProxyMessageFilter::Send), this,
jam 2015/11/03 00:18:54 no need to use PostTaskAndReply. a messagefilter c
Ilya Kulshin 2015/11/03 18:56:21 Done.
+ reply_message));
+}
+
+void DWriteFontProxyMessageFilter::DoFindFamily(
+ const base::string16& familyName,
+ IPC::Message* reply_message) {
+ DCHECK(collection_ != nullptr);
+ if (collection_ != nullptr) {
+ TRACE_EVENT0("dwrite", "FontProxyHost::DoFindFamily");
+ BOOL exists = FALSE;
+ uint32 index;
+ HRESULT hr =
+ collection_->FindFamilyName(familyName.data(), &index, &exists);
+ if (SUCCEEDED(hr)) {
+ DWriteFontProxyMsg_FindFamily::WriteReplyParams(reply_message, index);
+ return;
+ }
+ }
+ DWriteFontProxyMsg_FindFamily::WriteReplyParams(reply_message, UINT32_MAX);
jam 2015/11/03 00:18:54 no need to use IPC_DELAY_REPLY here since you alwa
Ilya Kulshin 2015/11/03 18:56:21 This is still a callback into direct write, and I'
jam 2015/11/09 16:32:08 I'm not sure what you mean. All I'm saying is don'
+}
+
+void DWriteFontProxyMessageFilter::OnGetFamilyCount(uint32* count) {
+ TRACE_EVENT0("dwrite", "FontProxyHost::OnGetFamilyCount");
+ DCHECK(collection_ != nullptr);
+ if (collection_ == nullptr)
+ *count = 0;
+ else
+ *count = collection_->GetFontFamilyCount();
+}
+
+void DWriteFontProxyMessageFilter::OnGetFamilyNames(
+ uint32 familyIndex,
+ IPC::Message* reply_message) {
+ TRACE_EVENT0("dwrite", "FontProxyHost::OnGetFamilyNames");
+ postTaskAndReply_(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&DWriteFontProxyMessageFilter::DoGetFamilyNames, this,
+ familyIndex, reply_message),
+ base::Bind(base::IgnoreResult(&DWriteFontProxyMessageFilter::Send), this,
+ reply_message));
+}
+
+void DWriteFontProxyMessageFilter::DoGetFamilyNames(
+ uint32 familyIndex,
+ IPC::Message* reply_message) {
+ DCHECK(collection_ != nullptr);
+ if (collection_ == nullptr)
+ return;
+
+ TRACE_EVENT0("dwrite", "FontProxyHost::DoGetFamilyNames");
+
+ mswr::ComPtr<IDWriteFontFamily> family;
+ HRESULT hr = collection_->GetFontFamily(familyIndex, &family);
+ if (!SUCCEEDED(hr))
+ return;
+
+ mswr::ComPtr<IDWriteLocalizedStrings> localizedNames;
+ hr = family->GetFamilyNames(&localizedNames);
+ if (!SUCCEEDED(hr))
+ return;
+
+ unsigned int stringCount = localizedNames->GetCount();
+
+ std::vector<base::char16> locale;
+ std::vector<base::char16> name;
+ std::vector<std::pair<base::string16, base::string16>> familyNames;
+ for (unsigned int index = 0; index < stringCount; index++) {
+ uint32 length = 0;
+ hr = localizedNames->GetLocaleNameLength(index, &length);
+ if (!SUCCEEDED(hr))
+ return;
+ length++; // Reserve space for the null terminator.
+ locale.resize(length);
+ hr = localizedNames->GetLocaleName(index, locale.data(), length);
+ if (!SUCCEEDED(hr))
+ return;
+ DCHECK(locale[length - 1] == L'\0');
+
+ length = 0;
+ hr = localizedNames->GetStringLength(index, &length);
+ if (!SUCCEEDED(hr))
+ return;
+ length++; // Reserve space for the null terminator.
+ name.resize(length);
+ hr = localizedNames->GetString(index, name.data(), length);
+ if (!SUCCEEDED(hr))
+ return;
+ DCHECK(name[length - 1] == L'\0');
+
+ // Would be great to use emplace_back instead.
+ familyNames.push_back(std::pair<base::string16, base::string16>(
+ base::string16(locale.data()), base::string16(name.data())));
+ }
+ DWriteFontProxyMsg_GetFamilyNames::WriteReplyParams(reply_message,
+ familyNames);
+}
+
+void DWriteFontProxyMessageFilter::OnGetFontFiles(uint32 familyIndex,
+ IPC::Message* reply_message) {
+ TRACE_EVENT0("dwrite", "FontProxyHost::OnGetFontFiles");
+ postTaskAndReply_(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&DWriteFontProxyMessageFilter::DoGetFontFiles, this,
+ familyIndex, reply_message),
+ base::Bind(base::IgnoreResult(&DWriteFontProxyMessageFilter::Send), this,
+ reply_message));
+}
+
+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",
+};
+
+void DWriteFontProxyMessageFilter::DoGetFontFiles(uint32 familyIndex,
+ IPC::Message* reply_message) {
+ DCHECK(collection_ != nullptr);
+ if (collection_ == nullptr)
+ return;
+
+ TRACE_EVENT0("dwrite", "FontProxyHost::DoGetFontFiles");
+
+ mswr::ComPtr<IDWriteFontFamily> family;
+ HRESULT hr = collection_->GetFontFamily(familyIndex, &family);
+ if (!SUCCEEDED(hr))
+ return;
+
+ uint32 fontCount = family->GetFontCount();
+
+ std::set<base::string16> pathSet;
+ std::vector<base::char16> filePathChars;
+ // 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 (unsigned int fontIndex = 0; fontIndex < fontCount; fontIndex++) {
+ mswr::ComPtr<IDWriteFont> font;
+ hr = family->GetFont(fontIndex, &font);
+ if (!SUCCEEDED(hr))
+ return;
+
+ mswr::ComPtr<IDWriteFontFace> fontFace;
+ hr = font->CreateFontFace(&fontFace);
+ if (!SUCCEEDED(hr))
+ return;
+
+ uint32 fileCount;
+ hr = fontFace->GetFiles(&fileCount, nullptr);
+ if (!SUCCEEDED(hr))
+ return;
+
+ std::vector<mswr::ComPtr<IDWriteFontFile>> fontFiles;
+ fontFiles.resize(fileCount);
+ hr = fontFace->GetFiles(
+ &fileCount, reinterpret_cast<IDWriteFontFile**>(fontFiles.data()));
+ if (!SUCCEEDED(hr))
+ return;
+
+ for (unsigned int fileIndex = 0; fileIndex < fileCount; fileIndex++) {
+ mswr::ComPtr<IDWriteFontFileLoader> loader;
+ hr = fontFiles[fileIndex]->GetLoader(&loader);
+ if (!SUCCEEDED(hr))
+ return;
+
+ mswr::ComPtr<IDWriteLocalFontFileLoader> localLoader;
+ hr = loader.CopyTo(localLoader.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.
+ UMA_HISTOGRAM_ENUMERATION("DWriteFontProxy.LoaderType", kOtherLoader,
+ kFontLoaderTypeMaxValue);
+ DCHECK(false);
+ return;
+ } else if (!SUCCEEDED(hr)) {
+ return;
+ }
+
+ const void* key;
+ uint32 keySize;
+ hr = fontFiles[fileIndex]->GetReferenceKey(&key, &keySize);
+ if (!SUCCEEDED(hr))
+ return;
+
+ uint32 pathLength = 0;
+ localLoader->GetFilePathLengthFromKey(key, keySize, &pathLength);
+ pathLength++; // Reserve space for the null terminator.
+ filePathChars.resize(pathLength);
+ hr = localLoader->GetFilePathFromKey(key, keySize, filePathChars.data(),
+ pathLength);
+ if (!SUCCEEDED(hr))
+ return;
+
+ base::string16 filePath = base::i18n::FoldCase(filePathChars.data());
+ if (!base::StartsWith(filePath, windowsFontsPath_,
+ 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.
+ UMA_HISTOGRAM_ENUMERATION("DWriteFontProxy.LoaderType",
+ kFileOutsideSandbox, kFontLoaderTypeMaxValue);
+ DCHECK(false);
+ return;
+ }
+
+ // Refer to comments in kFontsToIgnore for this block.
+ for (const auto& ignore : kFontsToIgnore) {
+ // Ok to do ascii comparison since the strings we are looking for are
+ // all ascii.
+ if (base::EndsWith(filePath, 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.
+ continue;
+ }
+ }
+ UMA_HISTOGRAM_ENUMERATION("DWriteFontProxy.LoaderType",
+ kFileSystemFontDir, kFontLoaderTypeMaxValue);
+ pathSet.insert(filePath);
+ }
+ }
+
+ std::vector<base::string16> filePaths;
+ filePaths.assign(pathSet.begin(), pathSet.end());
+ DWriteFontProxyMsg_GetFontFiles::WriteReplyParams(reply_message, filePaths);
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698