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 19 namespace content { 20 21 namespace { 22 23 SkFontMgr* g_warmup_fontmgr = NULL; 24 25 base::win::IATPatchFunction g_iat_patch_open_sc_manager; 26 base::win::IATPatchFunction g_iat_patch_close_service_handle; 27 base::win::IATPatchFunction g_iat_patch_open_service; 28 base::win::IATPatchFunction g_iat_patch_start_service; 29 base::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 36 typedef LONG NTSTATUS; 37 38 SC_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 45 SC_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 52 BOOL 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 60 BOOL 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 69 NTSTATUS 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. 99 void 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. 132 void 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 161 HRESULT 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 171 void 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 182 void 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 189 SkFontMgr* 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