1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/public/renderer/render_font_warmup_win.h" 6 7#include <dwrite.h> 8 9#include "base/debug/alias.h" 10#include "base/logging.h" 11#include "base/win/iat_patch_function.h" 12#include "base/win/windows_version.h" 13#include "content/renderer/renderer_font_platform_win.h" 14#include "third_party/WebKit/public/web/win/WebFontRendering.h" 15#include "third_party/skia/include/core/SkPaint.h" 16#include "third_party/skia/include/ports/SkFontMgr.h" 17#include "third_party/skia/include/ports/SkTypeface_win.h" 18 19namespace content { 20 21namespace { 22 23SkFontMgr* g_warmup_fontmgr = NULL; 24 25base::win::IATPatchFunction g_iat_patch_open_sc_manager; 26base::win::IATPatchFunction g_iat_patch_close_service_handle; 27base::win::IATPatchFunction g_iat_patch_open_service; 28base::win::IATPatchFunction g_iat_patch_start_service; 29base::win::IATPatchFunction g_iat_patch_nt_connect_port; 30 31// These are from ntddk.h 32#if !defined(STATUS_ACCESS_DENIED) 33#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L) 34#endif 35 36typedef LONG NTSTATUS; 37 38SC_HANDLE WINAPI OpenSCManagerWPatch(const wchar_t* machine_name, 39 const wchar_t* database_name, 40 DWORD access_mask) { 41 ::SetLastError(0); 42 return reinterpret_cast<SC_HANDLE>(0xdeadbeef); 43} 44 45SC_HANDLE WINAPI OpenServiceWPatch(SC_HANDLE sc_manager, 46 const wchar_t* service_name, 47 DWORD access_mask) { 48 ::SetLastError(0); 49 return reinterpret_cast<SC_HANDLE>(0xdeadbabe); 50} 51 52BOOL WINAPI CloseServiceHandlePatch(SC_HANDLE service_handle) { 53 if (service_handle != reinterpret_cast<SC_HANDLE>(0xdeadbabe) && 54 service_handle != reinterpret_cast<SC_HANDLE>(0xdeadbeef)) 55 CHECK(false); 56 ::SetLastError(0); 57 return TRUE; 58} 59 60BOOL WINAPI StartServiceWPatch(SC_HANDLE service, 61 DWORD args, 62 const wchar_t** arg_vectors) { 63 if (service != reinterpret_cast<SC_HANDLE>(0xdeadbabe)) 64 CHECK(false); 65 ::SetLastError(ERROR_ACCESS_DENIED); 66 return FALSE; 67} 68 69NTSTATUS WINAPI NtALpcConnectPortPatch(HANDLE* port_handle, 70 void* port_name, 71 void* object_attribs, 72 void* port_attribs, 73 DWORD flags, 74 void* server_sid, 75 void* message, 76 DWORD* buffer_length, 77 void* out_message_attributes, 78 void* in_message_attributes, 79 void* time_out) { 80 return STATUS_ACCESS_DENIED; 81} 82 83// Directwrite connects to the font cache service to retrieve information about 84// fonts installed on the system etc. This works well outside the sandbox and 85// within the sandbox as long as the lpc connection maintained by the current 86// process with the font cache service remains valid. It appears that there 87// are cases when this connection is dropped after which directwrite is unable 88// to connect to the font cache service which causes problems with characters 89// disappearing. 90// Directwrite has fallback code to enumerate fonts if it is unable to connect 91// to the font cache service. We need to intercept the following APIs to 92// ensure that it does not connect to the font cache service. 93// NtALpcConnectPort 94// OpenSCManagerW 95// OpenServiceW 96// StartServiceW 97// CloseServiceHandle. 98// These are all IAT patched. 99void PatchServiceManagerCalls() { 100 static bool is_patched = false; 101 if (is_patched) 102 return; 103 const char* service_provider_dll = 104 (base::win::GetVersion() >= base::win::VERSION_WIN8 ? 105 "api-ms-win-service-management-l1-1-0.dll" : "advapi32.dll"); 106 107 is_patched = true; 108 109 DWORD patched = g_iat_patch_open_sc_manager.Patch(L"dwrite.dll", 110 service_provider_dll, "OpenSCManagerW", OpenSCManagerWPatch); 111 DCHECK(patched == 0); 112 113 patched = g_iat_patch_close_service_handle.Patch(L"dwrite.dll", 114 service_provider_dll, "CloseServiceHandle", CloseServiceHandlePatch); 115 DCHECK(patched == 0); 116 117 patched = g_iat_patch_open_service.Patch(L"dwrite.dll", 118 service_provider_dll, "OpenServiceW", OpenServiceWPatch); 119 DCHECK(patched == 0); 120 121 patched = g_iat_patch_start_service.Patch(L"dwrite.dll", 122 service_provider_dll, "StartServiceW", StartServiceWPatch); 123 DCHECK(patched == 0); 124 125 patched = g_iat_patch_nt_connect_port.Patch(L"dwrite.dll", 126 "ntdll.dll", "NtAlpcConnectPort", NtALpcConnectPortPatch); 127 DCHECK(patched == 0); 128} 129 130// Windows-only DirectWrite support. These warm up the DirectWrite paths 131// before sandbox lock down to allow Skia access to the Font Manager service. 132void CreateDirectWriteFactory(IDWriteFactory** factory) { 133 typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc; 134 HMODULE dwrite_dll = LoadLibraryW(L"dwrite.dll"); 135 // TODO(scottmg): Temporary code to track crash in http://crbug.com/387867. 136 if (!dwrite_dll) { 137 DWORD load_library_get_last_error = GetLastError(); 138 base::debug::Alias(&dwrite_dll); 139 base::debug::Alias(&load_library_get_last_error); 140 CHECK(false); 141 } 142 143 PatchServiceManagerCalls(); 144 145 DWriteCreateFactoryProc dwrite_create_factory_proc = 146 reinterpret_cast<DWriteCreateFactoryProc>( 147 GetProcAddress(dwrite_dll, "DWriteCreateFactory")); 148 // TODO(scottmg): Temporary code to track crash in http://crbug.com/387867. 149 if (!dwrite_create_factory_proc) { 150 DWORD get_proc_address_get_last_error = GetLastError(); 151 base::debug::Alias(&dwrite_create_factory_proc); 152 base::debug::Alias(&get_proc_address_get_last_error); 153 CHECK(false); 154 } 155 CHECK(SUCCEEDED( 156 dwrite_create_factory_proc(DWRITE_FACTORY_TYPE_ISOLATED, 157 __uuidof(IDWriteFactory), 158 reinterpret_cast<IUnknown**>(factory)))); 159} 160 161HRESULT STDMETHODCALLTYPE StubFontCollection(IDWriteFactory* factory, 162 IDWriteFontCollection** col, 163 BOOL checkUpdates) { 164 // We always return pre-created font collection from here. 165 IDWriteFontCollection* custom_collection = GetCustomFontCollection(factory); 166 DCHECK(custom_collection != NULL); 167 *col = custom_collection; 168 return S_OK; 169} 170 171void PatchDWriteFactory(IDWriteFactory* factory) { 172 const unsigned int kGetSystemFontCollectionVTableIndex = 3; 173 174 PROC* vtable = *reinterpret_cast<PROC**>(factory); 175 PROC* function_ptr = &vtable[kGetSystemFontCollectionVTableIndex]; 176 void* stub_function = &StubFontCollection; 177 base::win::ModifyCode(function_ptr, &stub_function, sizeof(PROC)); 178} 179 180} // namespace 181 182void DoPreSandboxWarmupForTypeface(SkTypeface* typeface) { 183 SkPaint paint_warmup; 184 paint_warmup.setTypeface(typeface); 185 wchar_t glyph = L'S'; 186 paint_warmup.measureText(&glyph, 2); 187} 188 189SkFontMgr* GetPreSandboxWarmupFontMgr() { 190 if (!g_warmup_fontmgr) { 191 IDWriteFactory* factory; 192 CreateDirectWriteFactory(&factory); 193 194 IDWriteFontCollection* collection = GetCustomFontCollection(factory); 195 196 PatchDWriteFactory(factory); 197 198 blink::WebFontRendering::setDirectWriteFactory(factory); 199 g_warmup_fontmgr = SkFontMgr_New_DirectWrite(factory); 200 } 201 return g_warmup_fontmgr; 202} 203 204} // namespace content 205