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/logging.h" 10 #include "base/win/iat_patch_function.h" 11 #include "base/win/windows_version.h" 12 #include "third_party/WebKit/public/web/win/WebFontRendering.h" 13 #include "third_party/skia/include/core/SkPaint.h" 14 #include "third_party/skia/include/ports/SkFontMgr.h" 15 #include "third_party/skia/include/ports/SkTypeface_win.h" 16 17 namespace content { 18 19 namespace { 20 21 SkFontMgr* g_warmup_fontmgr = NULL; 22 23 base::win::IATPatchFunction g_iat_patch_open_sc_manager; 24 base::win::IATPatchFunction g_iat_patch_close_service_handle; 25 base::win::IATPatchFunction g_iat_patch_open_service; 26 base::win::IATPatchFunction g_iat_patch_start_service; 27 base::win::IATPatchFunction g_iat_patch_nt_connect_port; 28 29 // These are from ntddk.h 30 #if !defined(STATUS_ACCESS_DENIED) 31 #define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L) 32 #endif 33 34 typedef LONG NTSTATUS; 35 36 SC_HANDLE WINAPI OpenSCManagerWPatch(const wchar_t* machine_name, 37 const wchar_t* database_name, 38 DWORD access_mask) { 39 ::SetLastError(0); 40 return reinterpret_cast<SC_HANDLE>(0xdeadbeef); 41 } 42 43 SC_HANDLE WINAPI OpenServiceWPatch(SC_HANDLE sc_manager, 44 const wchar_t* service_name, 45 DWORD access_mask) { 46 ::SetLastError(0); 47 return reinterpret_cast<SC_HANDLE>(0xdeadbabe); 48 } 49 50 BOOL WINAPI CloseServiceHandlePatch(SC_HANDLE service_handle) { 51 if (service_handle != reinterpret_cast<SC_HANDLE>(0xdeadbabe) && 52 service_handle != reinterpret_cast<SC_HANDLE>(0xdeadbeef)) 53 CHECK(false); 54 ::SetLastError(0); 55 return TRUE; 56 } 57 58 BOOL WINAPI StartServiceWPatch(SC_HANDLE service, 59 DWORD args, 60 const wchar_t** arg_vectors) { 61 if (service != reinterpret_cast<SC_HANDLE>(0xdeadbabe)) 62 CHECK(false); 63 ::SetLastError(ERROR_ACCESS_DENIED); 64 return FALSE; 65 } 66 67 NTSTATUS WINAPI NtALpcConnectPortPatch(HANDLE* port_handle, 68 void* port_name, 69 void* object_attribs, 70 void* port_attribs, 71 DWORD flags, 72 void* server_sid, 73 void* message, 74 DWORD* buffer_length, 75 void* out_message_attributes, 76 void* in_message_attributes, 77 void* time_out) { 78 return STATUS_ACCESS_DENIED; 79 } 80 81 // Directwrite connects to the font cache service to retrieve information about 82 // fonts installed on the system etc. This works well outside the sandbox and 83 // within the sandbox as long as the lpc connection maintained by the current 84 // process with the font cache service remains valid. It appears that there 85 // are cases when this connection is dropped after which directwrite is unable 86 // to connect to the font cache service which causes problems with characters 87 // disappearing. 88 // Directwrite has fallback code to enumerate fonts if it is unable to connect 89 // to the font cache service. We need to intercept the following APIs to 90 // ensure that it does not connect to the font cache service. 91 // NtALpcConnectPort 92 // OpenSCManagerW 93 // OpenServiceW 94 // StartServiceW 95 // CloseServiceHandle. 96 // These are all IAT patched. 97 void PatchServiceManagerCalls() { 98 static bool is_patched = false; 99 if (is_patched) 100 return; 101 const char* service_provider_dll = 102 (base::win::GetVersion() >= base::win::VERSION_WIN8 ? 103 "api-ms-win-service-management-l1-1-0.dll" : "advapi32.dll"); 104 105 is_patched = true; 106 107 DWORD patched = g_iat_patch_open_sc_manager.Patch(L"dwrite.dll", 108 service_provider_dll, "OpenSCManagerW", OpenSCManagerWPatch); 109 DCHECK(patched == 0); 110 111 patched = g_iat_patch_close_service_handle.Patch(L"dwrite.dll", 112 service_provider_dll, "CloseServiceHandle", CloseServiceHandlePatch); 113 DCHECK(patched == 0); 114 115 patched = g_iat_patch_open_service.Patch(L"dwrite.dll", 116 service_provider_dll, "OpenServiceW", OpenServiceWPatch); 117 DCHECK(patched == 0); 118 119 patched = g_iat_patch_start_service.Patch(L"dwrite.dll", 120 service_provider_dll, "StartServiceW", StartServiceWPatch); 121 DCHECK(patched == 0); 122 123 patched = g_iat_patch_nt_connect_port.Patch(L"dwrite.dll", 124 "ntdll.dll", "NtAlpcConnectPort", NtALpcConnectPortPatch); 125 DCHECK(patched == 0); 126 } 127 128 129 // Windows-only DirectWrite support. These warm up the DirectWrite paths 130 // before sandbox lock down to allow Skia access to the Font Manager service. 131 void CreateDirectWriteFactory(IDWriteFactory** factory) { 132 typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc; 133 PatchServiceManagerCalls(); 134 135 DWriteCreateFactoryProc dwrite_create_factory_proc = 136 reinterpret_cast<DWriteCreateFactoryProc>( 137 GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory")); 138 CHECK(dwrite_create_factory_proc); 139 CHECK(SUCCEEDED( 140 dwrite_create_factory_proc(DWRITE_FACTORY_TYPE_ISOLATED, 141 __uuidof(IDWriteFactory), 142 reinterpret_cast<IUnknown**>(factory)))); 143 } 144 145 } // namespace 146 147 void DoPreSandboxWarmupForTypeface(SkTypeface* typeface) { 148 SkPaint paint_warmup; 149 paint_warmup.setTypeface(typeface); 150 wchar_t glyph = L'S'; 151 paint_warmup.measureText(&glyph, 2); 152 } 153 154 SkFontMgr* GetPreSandboxWarmupFontMgr() { 155 if (!g_warmup_fontmgr) { 156 IDWriteFactory* factory; 157 CreateDirectWriteFactory(&factory); 158 blink::WebFontRendering::setDirectWriteFactory(factory); 159 g_warmup_fontmgr = SkFontMgr_New_DirectWrite(factory); 160 } 161 return g_warmup_fontmgr; 162 } 163 164 } // namespace content 165