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

Unified Diff: content/common/font_warmup_win.cc

Issue 1327673002: Added GDI font emulation support for Flash. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@warmup_direct_write
Patch Set: Changed comment and used common check Created 5 years, 3 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/common/font_warmup_win.cc
diff --git a/content/common/font_warmup_win.cc b/content/common/font_warmup_win.cc
index d848db39000f8f72fae757e1f49307b62399d213..c54c220595da4418f6704e5a2fb7b0633db584e9 100644
--- a/content/common/font_warmup_win.cc
+++ b/content/common/font_warmup_win.cc
@@ -5,12 +5,20 @@
#include "content/common/font_warmup_win.h"
#include <dwrite.h>
+#include <list>
#include "base/debug/alias.h"
+#include "base/files/file_path.h"
#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/lock.h"
+#include "base/sys_byteorder.h"
#include "base/win/iat_patch_function.h"
#include "base/win/windows_version.h"
#include "content/public/common/dwrite_font_platform_win.h"
+#include "skia/ext/refptr.h"
#include "third_party/WebKit/public/web/win/WebFontRendering.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/ports/SkFontMgr.h"
@@ -182,6 +190,259 @@ void PatchDWriteFactory(IDWriteFactory* factory) {
base::win::ModifyCode(function_ptr, &stub_function, sizeof(PROC));
}
+// Magic values for the fake GDI objects.
+const uint32_t kFakeDCMagic = 'fkdc';
+const uint32_t kFakeFontMagic = 'fkft';
+
+// Class to fake out a DC or a Font object. Maintains a reference to a
+// SkTypeFace to emulate the simple operation of a DC and Font. We can't
+// be sure that this won't be used in a multi-threaded environment so we
+// need to ensure a lock is taken before updating the static table of
+// issued objects.
+class FakeGdiObject : public base::RefCountedThreadSafe<FakeGdiObject> {
+ public:
+ // Find a corresponding fake GDI object and verify its magic value.
+ // The returned object is either nullptr or the new object.
+ static scoped_refptr<FakeGdiObject> Validate(void* obj, uint32_t magic) {
+ if (obj) {
+ base::AutoLock scoped_lock(objects_lock_);
+ uintptr_t handle = reinterpret_cast<uintptr_t>(obj);
+ for (const auto& currobj : objects_) {
+ if ((currobj->magic_ == magic) && (handle == currobj->handle_))
+ return currobj;
+ }
+ }
+
+ return nullptr;
+ }
+
+ static scoped_refptr<FakeGdiObject> Create(uint32_t magic) {
+ base::AutoLock scoped_lock(objects_lock_);
+ scoped_refptr<FakeGdiObject> object(new FakeGdiObject(magic));
+ objects_.push_back(object);
+ return object;
+ }
+
+ static bool DeleteObject(void* obj, uint32_t magic) {
+ uintptr_t handle = reinterpret_cast<uintptr_t>(obj);
+ base::AutoLock scoped_lock(objects_lock_);
+ for (auto i = objects_.begin(); i != objects_.end(); ++i) {
+ const auto& fakeobj = *i;
+ if ((fakeobj->magic_ == magic) && (fakeobj->handle_ == handle)) {
+ objects_.erase(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void SetTypeface(const skia::RefPtr<SkTypeface>& typeface) {
+ typeface_ = typeface;
+ }
+
+ skia::RefPtr<SkTypeface> GetTypeface() { return typeface_; }
+
+ void* GetHandle() { return reinterpret_cast<void*>(handle_); }
+
+ private:
+ uint32_t magic_;
scottmg 2015/09/17 23:24:24 it would pack better on x64 to have magic_ lower d
forshaw 2015/09/18 00:00:28 I could move it, as this class (presumably) has a
+ uintptr_t handle_;
+ skia::RefPtr<SkTypeface> typeface_;
+ static uintptr_t curr_handle_;
scottmg 2015/09/17 23:24:25 separate statics from members
forshaw 2015/09/18 00:00:28 Acknowledged.
+ static std::list<scoped_refptr<FakeGdiObject>> objects_;
scottmg 2015/09/17 23:24:24 is a map from handle->FakeGdiObject not sensible f
forshaw 2015/09/18 00:00:28 If I can just use map directly then I guess it sho
+ static base::Lock objects_lock_;
+ FakeGdiObject(uint32_t magic) : magic_(magic), handle_(++curr_handle_) {}
+ ~FakeGdiObject() {}
+ friend class base::RefCountedThreadSafe<FakeGdiObject>;
scottmg 2015/09/17 23:24:24 move friend to first after private:
forshaw 2015/09/18 00:00:28 Acknowledged.
+
+ DISALLOW_COPY_AND_ASSIGN(FakeGdiObject);
+};
+
+uintptr_t FakeGdiObject::curr_handle_ = 0;
+std::list<scoped_refptr<FakeGdiObject>> FakeGdiObject::objects_;
+base::Lock FakeGdiObject::objects_lock_;
+
+skia::RefPtr<SkTypeface> GetTypefaceFromLOGFONT(const LOGFONTW* logfont) {
+ CHECK(g_warmup_fontmgr);
+ int weight = logfont->lfWeight;
+ if (weight == FW_DONTCARE)
+ weight = SkFontStyle::kNormal_Weight;
+
+ SkFontStyle style(weight, logfont->lfWidth,
+ logfont->lfItalic ? SkFontStyle::kItalic_Slant
+ : SkFontStyle::kUpright_Slant);
+
+ std::string family_name = base::WideToUTF8(logfont->lfFaceName);
+ return skia::AdoptRef(
+ g_warmup_fontmgr->matchFamilyStyle(family_name.c_str(), style));
+}
+
+HDC WINAPI CreateCompatibleDCPatch(HDC hdc) {
+ scoped_refptr<FakeGdiObject> ret = FakeGdiObject::Create(kFakeDCMagic);
+ return static_cast<HDC>(ret->GetHandle());
+}
+
+HFONT WINAPI CreateFontIndirectWPatch(const LOGFONTW* lplf) {
+ if (!lplf)
+ return nullptr;
+
+ skia::RefPtr<SkTypeface> typeface = GetTypefaceFromLOGFONT(lplf);
+ if (!typeface)
+ return nullptr;
+
+ scoped_refptr<FakeGdiObject> ret = FakeGdiObject::Create(kFakeFontMagic);
+ ret->SetTypeface(typeface);
+
+ return static_cast<HFONT>(ret->GetHandle());
+}
+
+BOOL WINAPI DeleteDCPatch(HDC hdc) {
+ if (hdc)
+ return FakeGdiObject::DeleteObject(hdc, kFakeDCMagic);
+ return FALSE;
+}
+
+BOOL WINAPI DeleteObjectPatch(HGDIOBJ hObject) {
+ if (hObject)
+ return FakeGdiObject::DeleteObject(hObject, kFakeFontMagic);
+ return FALSE;
+}
+
+int WINAPI EnumFontFamiliesExWPatch(HDC hdc,
+ LPLOGFONTW lpLogfont,
scottmg 2015/09/17 23:24:24 i know they're copied from the declarations, but n
forshaw 2015/09/18 00:00:28 Acknowledged.
+ FONTENUMPROCW lpEnumFontFamExProc,
+ LPARAM lParam,
+ DWORD dwFlags) {
+ scoped_refptr<FakeGdiObject> dcobj =
+ FakeGdiObject::Validate(hdc, kFakeDCMagic);
+ if (!dcobj) {
+ LOG(ERROR) << "Invalid DC Object";
+ return 1;
+ }
+ if (!lpLogfont || !lpEnumFontFamExProc)
+ return 1;
+
+ skia::RefPtr<SkTypeface> typeface = GetTypefaceFromLOGFONT(lpLogfont);
+ if (!typeface)
+ return 1;
+
+ ENUMLOGFONTEXDVW enum_log_font = {0};
+ enum_log_font.elfEnumLogfontEx.elfLogFont = *lpLogfont;
+ // TODO: Fill in the rest of the text metric structure. Not yet needed for
+ // Flash support but might be in the future.
+ NEWTEXTMETRICEXW text_metric = {0};
+ text_metric.ntmTm.ntmFlags = NTM_PS_OPENTYPE;
+
+ return lpEnumFontFamExProc(&enum_log_font.elfEnumLogfontEx.elfLogFont,
+ reinterpret_cast<TEXTMETRIC*>(&text_metric),
+ TRUETYPE_FONTTYPE, lParam);
+}
+
+DWORD WINAPI GetFontDataPatch(HDC hdc,
+ DWORD dwTable,
+ DWORD dwOffset,
+ LPVOID lpvBuffer,
+ DWORD cbData) {
+ scoped_refptr<FakeGdiObject> dcobj =
+ FakeGdiObject::Validate(hdc, kFakeDCMagic);
+ if (!dcobj) {
+ LOG(ERROR) << "Invalid DC Object";
+ return GDI_ERROR;
+ }
+
+ skia::RefPtr<SkTypeface> typeface = dcobj->GetTypeface();
+ if (!typeface)
+ return GDI_ERROR;
+
+ if (cbData > INT32_MAX)
+ return GDI_ERROR;
+
+ // |getTableData| handles lpvBuffer being nullptr. However if it is nullptr
+ // then set the size to INT32_MAX otherwise |getTableData| will return the
+ // minimum value between the table entry size and the size passed in. The
+ // common Windows idiom is to pass 0 as cbSize when passing nullptr, which
palmer 2015/09/17 21:12:52 Nit: Don't forget to use |...| to distinguish iden
forshaw 2015/09/18 00:00:28 Will do.
+ // would in this case result in |getTableData| returning 0 which isn't the
+ // correct answer for emulating GDI. The dwTable tag must also have its byte
+ // order swapped to counter the byte order swap which occurs in the method.
+ size_t length = typeface->getTableData(
+ base::ByteSwap(base::strict_cast<uint32>(dwTable)), dwOffset,
+ lpvBuffer ? cbData : INT32_MAX, lpvBuffer);
+ // We can't distinguish between an empty table and an error.
+ if (length == 0)
+ return GDI_ERROR;
+
+ return base::checked_cast<DWORD>(length);
+}
+
+HGDIOBJ WINAPI SelectObjectPatch(HDC hdc, HGDIOBJ hgdiobj) {
scottmg 2015/09/17 23:24:24 is it never possible for these patches to get call
forshaw 2015/09/18 00:00:28 These patches should only be enabled if the win32k
+ scoped_refptr<FakeGdiObject> dcobj =
+ FakeGdiObject::Validate(hdc, kFakeDCMagic);
+ if (!dcobj) {
+ LOG(ERROR) << "Invalid DC Object";
+ return nullptr;
+ }
+
+ scoped_refptr<FakeGdiObject> fontobj =
+ FakeGdiObject::Validate(hgdiobj, kFakeFontMagic);
+ if (!fontobj) {
+ LOG(ERROR) << "Invalid Font Object";
+ return nullptr;
+ }
+
+ // Construct a new fake font object to handle the old font if there's one.
+ scoped_refptr<FakeGdiObject> newfontobj;
+ skia::RefPtr<SkTypeface> oldfont = dcobj->GetTypeface();
+ if (oldfont) {
+ newfontobj = FakeGdiObject::Create(kFakeFontMagic);
+ newfontobj->SetTypeface(oldfont);
+ }
+ dcobj->SetTypeface(fontobj->GetTypeface());
+
+ if (newfontobj)
+ return static_cast<HGDIOBJ>(newfontobj->GetHandle());
+ return nullptr;
+}
+
+void DoSingleGdiPatch(base::win::IATPatchFunction& patch,
+ const base::FilePath& path,
+ const char* function_name,
+ void* new_function) {
+ DWORD error = patch.Patch(path.value().c_str(), "gdi32.dll", function_name,
+ new_function);
+ if (error != 0)
+ LOG(WARNING) << "Failed patching " << function_name << " " << error;
+}
+
+class GdiFontPatchDataImpl : public content::GdiFontPatchData {
+ public:
+ GdiFontPatchDataImpl(const base::FilePath& path);
+
+ private:
+ base::win::IATPatchFunction create_compatible_dc_patch_;
+ base::win::IATPatchFunction create_font_indirect_patch_;
+ base::win::IATPatchFunction create_delete_dc_patch_;
+ base::win::IATPatchFunction create_delete_object_patch_;
+ base::win::IATPatchFunction create_enum_font_families_patch_;
+ base::win::IATPatchFunction create_get_font_data_patch_;
+ base::win::IATPatchFunction create_select_object_patch_;
+};
+
+GdiFontPatchDataImpl::GdiFontPatchDataImpl(const base::FilePath& path) {
+ DoSingleGdiPatch(create_compatible_dc_patch_, path, "CreateCompatibleDC",
+ CreateCompatibleDCPatch);
+ DoSingleGdiPatch(create_font_indirect_patch_, path, "CreateFontIndirectW",
+ CreateFontIndirectWPatch);
+ DoSingleGdiPatch(create_delete_dc_patch_, path, "DeleteDC", DeleteDCPatch);
+ DoSingleGdiPatch(create_delete_object_patch_, path, "DeleteObject",
+ DeleteObjectPatch);
+ DoSingleGdiPatch(create_enum_font_families_patch_, path,
+ "EnumFontFamiliesExW", EnumFontFamiliesExWPatch);
+ DoSingleGdiPatch(create_get_font_data_patch_, path, "GetFontData",
+ GetFontDataPatch);
+ DoSingleGdiPatch(create_select_object_patch_, path, "SelectObject",
+ SelectObjectPatch);
+}
+
} // namespace
void DoPreSandboxWarmupForTypeface(SkTypeface* typeface) {
@@ -206,4 +467,14 @@ SkFontMgr* GetPreSandboxWarmupFontMgr() {
return g_warmup_fontmgr;
}
+GdiFontPatchData* PatchGdiFontEnumeration(const base::FilePath& path) {
+ // We assume the fontmgr is already warmed up before calling this.
+ DCHECK(g_warmup_fontmgr);
+ return new GdiFontPatchDataImpl(path);
+}
+
+void SetPreSandboxWarmupFontMgr(SkFontMgr* fontmgr) {
+ g_warmup_fontmgr = fontmgr;
+}
+
} // namespace content

Powered by Google App Engine
This is Rietveld 408576698