diff options
Diffstat (limited to 'chromium/content/common/font_warmup_win.cc')
-rw-r--r-- | chromium/content/common/font_warmup_win.cc | 526 |
1 files changed, 0 insertions, 526 deletions
diff --git a/chromium/content/common/font_warmup_win.cc b/chromium/content/common/font_warmup_win.cc deleted file mode 100644 index cdd1d0f2dba..00000000000 --- a/chromium/content/common/font_warmup_win.cc +++ /dev/null @@ -1,526 +0,0 @@ -// Copyright 2014 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/common/font_warmup_win.h" - -#include <dwrite.h> -#include <stdint.h> -#include <map> - -#include "base/debug/alias.h" -#include "base/files/file_path.h" -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/numerics/safe_conversions.h" -#include "base/numerics/safe_math.h" -#include "base/strings/utf_string_conversions.h" -#include "base/synchronization/lock.h" -#include "base/sys_byteorder.h" -#include "base/trace_event/trace_event.h" -#include "base/win/iat_patch_function.h" -#include "base/win/windows_version.h" -#include "content/public/common/dwrite_font_platform_win.h" -#include "ppapi/shared_impl/proxy_lock.h" -#include "skia/ext/fontmgr_default_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" -#include "third_party/skia/include/ports/SkTypeface_win.h" - -namespace content { - -namespace { - -// The Skia font manager, used for the life of the process (leaked at the end). -SkFontMgr* g_warmup_fontmgr = nullptr; - -base::win::IATPatchFunction g_iat_patch_open_sc_manager; -base::win::IATPatchFunction g_iat_patch_close_service_handle; -base::win::IATPatchFunction g_iat_patch_open_service; -base::win::IATPatchFunction g_iat_patch_start_service; -base::win::IATPatchFunction g_iat_patch_nt_connect_port; - -// These are from ntddk.h -#if !defined(STATUS_ACCESS_DENIED) -#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L) -#endif - -typedef LONG NTSTATUS; - -const uintptr_t kFakeSCMHandle = 0xdead0001; -const uintptr_t kFakeServiceHandle = 0xdead0002; - -SC_HANDLE WINAPI OpenSCManagerWPatch(const wchar_t* machine_name, - const wchar_t* database_name, - DWORD access_mask) { - ::SetLastError(0); - return reinterpret_cast<SC_HANDLE>(kFakeSCMHandle); -} - -SC_HANDLE WINAPI OpenServiceWPatch(SC_HANDLE sc_manager, - const wchar_t* service_name, - DWORD access_mask) { - ::SetLastError(0); - return reinterpret_cast<SC_HANDLE>(kFakeServiceHandle); -} - -BOOL WINAPI CloseServiceHandlePatch(SC_HANDLE service_handle) { - if (service_handle != reinterpret_cast<SC_HANDLE>(kFakeServiceHandle) && - service_handle != reinterpret_cast<SC_HANDLE>(kFakeSCMHandle)) - CHECK(false); - ::SetLastError(0); - return TRUE; -} - -BOOL WINAPI StartServiceWPatch(SC_HANDLE service, - DWORD args, - const wchar_t** arg_vectors) { - if (service != reinterpret_cast<SC_HANDLE>(kFakeServiceHandle)) - CHECK(false); - ::SetLastError(ERROR_ACCESS_DENIED); - return FALSE; -} - -NTSTATUS WINAPI NtALpcConnectPortPatch(HANDLE* port_handle, - void* port_name, - void* object_attribs, - void* port_attribs, - DWORD flags, - void* server_sid, - void* message, - DWORD* buffer_length, - void* out_message_attributes, - void* in_message_attributes, - void* time_out) { - return STATUS_ACCESS_DENIED; -} - -// 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); - } - - 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) { - // We always return pre-created font collection from here. - IDWriteFontCollection* custom_collection = GetCustomFontCollection(factory); - DCHECK(custom_collection != nullptr); - *col = custom_collection; - return S_OK; -} - -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)); -} - -// 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. -class FakeGdiObject : public base::RefCountedThreadSafe<FakeGdiObject> { - public: - FakeGdiObject(uint32_t magic, void* handle) - : handle_(handle), magic_(magic) {} - - void set_typeface(const skia::RefPtr<SkTypeface>& typeface) { - typeface_ = typeface; - } - - skia::RefPtr<SkTypeface> typeface() { return typeface_; } - void* handle() { return handle_; } - uint32_t magic() { return magic_; } - - private: - friend class base::RefCountedThreadSafe<FakeGdiObject>; - ~FakeGdiObject() {} - - void* handle_; - uint32_t magic_; - skia::RefPtr<SkTypeface> typeface_; - - DISALLOW_COPY_AND_ASSIGN(FakeGdiObject); -}; - -// This class acts as a factory for creating new fake GDI objects. It also maps -// the new instances of the FakeGdiObject class to an incrementing handle value -// which is passed to the caller of the emulated GDI function for later -// reference. 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 accessing the map of -// issued objects. -class FakeGdiObjectFactory { - public: - FakeGdiObjectFactory() : curr_handle_(0) {} - - // Find a corresponding fake GDI object and verify its magic value. - // The returned value is either nullptr or the validated object. - scoped_refptr<FakeGdiObject> Validate(void* obj, uint32_t magic) { - if (obj) { - base::AutoLock scoped_lock(objects_lock_); - auto handle_entry = objects_.find(obj); - if (handle_entry != objects_.end() && - handle_entry->second->magic() == magic) { - return handle_entry->second; - } - } - return nullptr; - } - - scoped_refptr<FakeGdiObject> Create(uint32_t magic) { - base::AutoLock scoped_lock(objects_lock_); - curr_handle_++; - // We don't support wrapping the fake handle value. - void* handle = reinterpret_cast<void*>(curr_handle_.ValueOrDie()); - scoped_refptr<FakeGdiObject> object(new FakeGdiObject(magic, handle)); - objects_[handle] = object; - return object; - } - - bool DeleteObject(void* obj, uint32_t magic) { - base::AutoLock scoped_lock(objects_lock_); - auto handle_entry = objects_.find(obj); - if (handle_entry != objects_.end() && - handle_entry->second->magic() == magic) { - objects_.erase(handle_entry); - return true; - } - return false; - } - - size_t GetObjectCount() { - base::AutoLock scoped_lock(objects_lock_); - return objects_.size(); - } - - void ResetObjectHandles() { - base::AutoLock scoped_lock(objects_lock_); - curr_handle_ = 0; - objects_.clear(); - } - - private: - base::CheckedNumeric<uintptr_t> curr_handle_; - std::map<void*, scoped_refptr<FakeGdiObject>> objects_; - base::Lock objects_lock_; - - DISALLOW_COPY_AND_ASSIGN(FakeGdiObjectFactory); -}; - -base::LazyInstance<FakeGdiObjectFactory>::Leaky g_fake_gdi_object_factory = - LAZY_INSTANCE_INITIALIZER; - -// Magic values for the fake GDI objects. -const uint32_t kFakeDCMagic = 'fkdc'; -const uint32_t kFakeFontMagic = 'fkft'; - -skia::RefPtr<SkTypeface> GetTypefaceFromLOGFONT(const LOGFONTW* log_font) { - CHECK(g_warmup_fontmgr); - int weight = log_font->lfWeight; - if (weight == FW_DONTCARE) - weight = SkFontStyle::kNormal_Weight; - - SkFontStyle style(weight, log_font->lfWidth, - log_font->lfItalic ? SkFontStyle::kItalic_Slant - : SkFontStyle::kUpright_Slant); - - std::string family_name = base::WideToUTF8(log_font->lfFaceName); - ppapi::ProxyAutoLock lock; // Needed for DirectWrite font proxy. - return skia::AdoptRef( - g_warmup_fontmgr->matchFamilyStyle(family_name.c_str(), style)); -} - -HDC WINAPI CreateCompatibleDCPatch(HDC dc_handle) { - scoped_refptr<FakeGdiObject> ret = - g_fake_gdi_object_factory.Get().Create(kFakeDCMagic); - return static_cast<HDC>(ret->handle()); -} - -HFONT WINAPI CreateFontIndirectWPatch(const LOGFONTW* log_font) { - if (!log_font) - return nullptr; - - skia::RefPtr<SkTypeface> typeface = GetTypefaceFromLOGFONT(log_font); - if (!typeface) - return nullptr; - - scoped_refptr<FakeGdiObject> ret = - g_fake_gdi_object_factory.Get().Create(kFakeFontMagic); - ret->set_typeface(typeface); - - return static_cast<HFONT>(ret->handle()); -} - -BOOL WINAPI DeleteDCPatch(HDC dc_handle) { - return g_fake_gdi_object_factory.Get().DeleteObject(dc_handle, kFakeDCMagic); -} - -BOOL WINAPI DeleteObjectPatch(HGDIOBJ object_handle) { - return g_fake_gdi_object_factory.Get().DeleteObject(object_handle, - kFakeFontMagic); -} - -int WINAPI EnumFontFamiliesExWPatch(HDC dc_handle, - LPLOGFONTW log_font, - FONTENUMPROCW enum_callback, - LPARAM callback_param, - DWORD flags) { - scoped_refptr<FakeGdiObject> dc_obj = - g_fake_gdi_object_factory.Get().Validate(dc_handle, kFakeDCMagic); - if (!dc_obj) - return 1; - - if (!log_font || !enum_callback) - return 1; - - skia::RefPtr<SkTypeface> typeface = GetTypefaceFromLOGFONT(log_font); - if (!typeface) - return 1; - - ENUMLOGFONTEXDVW enum_log_font = {}; - enum_log_font.elfEnumLogfontEx.elfLogFont = *log_font; - // 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 = {}; - text_metric.ntmTm.ntmFlags = NTM_PS_OPENTYPE; - - return enum_callback(&enum_log_font.elfEnumLogfontEx.elfLogFont, - reinterpret_cast<TEXTMETRIC*>(&text_metric), - TRUETYPE_FONTTYPE, callback_param); -} - -DWORD WINAPI GetFontDataPatch(HDC dc_handle, - DWORD table_tag, - DWORD table_offset, - LPVOID buffer, - DWORD buffer_length) { - scoped_refptr<FakeGdiObject> dc_obj = - g_fake_gdi_object_factory.Get().Validate(dc_handle, kFakeDCMagic); - if (!dc_obj) - return GDI_ERROR; - - skia::RefPtr<SkTypeface> typeface = dc_obj->typeface(); - if (!typeface) - return GDI_ERROR; - - // |getTableData| handles |buffer| 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 |buffer_length| when passing nullptr, - // which would in this case result in |getTableData| returning 0 which isn't - // the correct answer for emulating GDI. |table_tag| must also have its - // byte order swapped to counter the swap which occurs in the called method. - size_t length = typeface->getTableData( - base::ByteSwap(base::strict_cast<uint32_t>(table_tag)), table_offset, - buffer ? buffer_length : INT32_MAX, buffer); - // 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 dc_handle, HGDIOBJ object_handle) { - scoped_refptr<FakeGdiObject> dc_obj = - g_fake_gdi_object_factory.Get().Validate(dc_handle, kFakeDCMagic); - if (!dc_obj) - return nullptr; - - scoped_refptr<FakeGdiObject> font_obj = - g_fake_gdi_object_factory.Get().Validate(object_handle, kFakeFontMagic); - if (!font_obj) - return nullptr; - - // Construct a new fake font object to handle the old font if there's one. - scoped_refptr<FakeGdiObject> new_font_obj; - skia::RefPtr<SkTypeface> old_typeface = dc_obj->typeface(); - if (old_typeface) { - new_font_obj = g_fake_gdi_object_factory.Get().Create(kFakeFontMagic); - new_font_obj->set_typeface(old_typeface); - } - dc_obj->set_typeface(font_obj->typeface()); - - if (new_font_obj) - return static_cast<HGDIOBJ>(new_font_obj->handle()); - return nullptr; -} - -void DoSingleGdiPatch(base::win::IATPatchFunction& patch, - const base::FilePath& path, - const char* function_name, - void* new_function) { - patch.Patch(path.value().c_str(), "gdi32.dll", function_name, new_function); -} - -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 - -// 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); - wchar_t glyph = L'S'; - paint_warmup.measureText(&glyph, 2); -} - -SkFontMgr* GetPreSandboxWarmupFontMgr() { - if (!g_warmup_fontmgr) { - IDWriteFactory* factory; - CreateDirectWriteFactory(&factory); - - GetCustomFontCollection(factory); - - PatchDWriteFactory(factory); - - blink::WebFontRendering::setDirectWriteFactory(factory); - g_warmup_fontmgr = SkFontMgr_New_DirectWrite(factory); - } - return g_warmup_fontmgr; -} - -GdiFontPatchData* PatchGdiFontEnumeration(const base::FilePath& path) { - if (ShouldUseDirectWriteFontProxyFieldTrial() && !g_warmup_fontmgr) - g_warmup_fontmgr = SkFontMgr_New_DirectWrite(); - // If not using the font proxy, we assume |g_warmup_fontmgr| is already - // initialized before this function is called. - DCHECK(g_warmup_fontmgr); - return new GdiFontPatchDataImpl(path); -} - -size_t GetEmulatedGdiHandleCountForTesting() { - return g_fake_gdi_object_factory.Get().GetObjectCount(); -} - -void ResetEmulatedGdiHandlesForTesting() { - g_fake_gdi_object_factory.Get().ResetObjectHandles(); -} - -void SetPreSandboxWarmupFontMgrForTesting(SkFontMgr* fontmgr) { - g_warmup_fontmgr = fontmgr; -} - -void WarmupDirectWrite() { - TRACE_EVENT0("startup", "content::WarmupDirectWrite"); - - // The objects used here are intentionally not freed as we want the Skia - // code to use these objects after warmup. - SetDefaultSkiaFactory(GetPreSandboxWarmupFontMgr()); - - // We need to warm up *some* font for DirectWrite. Note that we don't use - // a monospace as would be nice in an attempt to avoid a small startup time - // regression, see http://crbug.com/463613. - skia::RefPtr<SkTypeface> hud_typeface = skia::AdoptRef( - GetPreSandboxWarmupFontMgr()->legacyCreateTypeface("Times New Roman", 0)); - DoPreSandboxWarmupForTypeface(hud_typeface.get()); -} - -} // namespace content |