OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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/renderer/dwrite_font_proxy_init_win.h" |
| 6 |
| 7 #include <dwrite.h> |
| 8 |
| 9 #include "base/callback.h" |
| 10 #include "base/debug/alias.h" |
| 11 #include "base/win/iat_patch_function.h" |
| 12 #include "base/win/windows_version.h" |
| 13 #include "content/renderer/dwrite_font_proxy_win.h" |
| 14 #include "skia/ext/fontmgr_default_win.h" |
| 15 #include "third_party/WebKit/public/web/win/WebFontRendering.h" |
| 16 #include "third_party/skia/include/ports/SkTypeface_win.h" |
| 17 |
| 18 namespace mswr = Microsoft::WRL; |
| 19 |
| 20 namespace content { |
| 21 |
| 22 namespace { |
| 23 mswr::ComPtr<DWriteFontCollectionProxy> g_font_collection; |
| 24 base::win::IATPatchFunction g_iat_patch_open_sc_manager; |
| 25 base::win::IATPatchFunction g_iat_patch_close_service_handle; |
| 26 base::win::IATPatchFunction g_iat_patch_open_service; |
| 27 base::win::IATPatchFunction g_iat_patch_start_service; |
| 28 base::win::IATPatchFunction g_iat_patch_nt_connect_port; |
| 29 IPC::Sender* g_senderOverride = nullptr; |
| 30 } |
| 31 |
| 32 // These are from ntddk.h |
| 33 #if !defined(STATUS_ACCESS_DENIED) |
| 34 #define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L) |
| 35 #endif |
| 36 |
| 37 typedef LONG NTSTATUS; |
| 38 |
| 39 const uintptr_t kFakeSCMHandle = 0xdead0001; |
| 40 const uintptr_t kFakeServiceHandle = 0xdead0002; |
| 41 |
| 42 SC_HANDLE WINAPI OpenSCManagerWPatch(const wchar_t* machine_name, |
| 43 const wchar_t* database_name, |
| 44 DWORD access_mask) { |
| 45 ::SetLastError(0); |
| 46 return reinterpret_cast<SC_HANDLE>(kFakeSCMHandle); |
| 47 } |
| 48 |
| 49 SC_HANDLE WINAPI OpenServiceWPatch(SC_HANDLE sc_manager, |
| 50 const wchar_t* service_name, |
| 51 DWORD access_mask) { |
| 52 ::SetLastError(0); |
| 53 return reinterpret_cast<SC_HANDLE>(kFakeServiceHandle); |
| 54 } |
| 55 |
| 56 BOOL WINAPI CloseServiceHandlePatch(SC_HANDLE service_handle) { |
| 57 if (service_handle != reinterpret_cast<SC_HANDLE>(kFakeServiceHandle) && |
| 58 service_handle != reinterpret_cast<SC_HANDLE>(kFakeSCMHandle)) |
| 59 CHECK(false); |
| 60 ::SetLastError(0); |
| 61 return TRUE; |
| 62 } |
| 63 |
| 64 BOOL WINAPI StartServiceWPatch(SC_HANDLE service, |
| 65 DWORD args, |
| 66 const wchar_t** arg_vectors) { |
| 67 if (service != reinterpret_cast<SC_HANDLE>(kFakeServiceHandle)) |
| 68 CHECK(false); |
| 69 ::SetLastError(ERROR_ACCESS_DENIED); |
| 70 return FALSE; |
| 71 } |
| 72 |
| 73 NTSTATUS WINAPI NtALpcConnectPortPatch(HANDLE* port_handle, |
| 74 void* port_name, |
| 75 void* object_attribs, |
| 76 void* port_attribs, |
| 77 DWORD flags, |
| 78 void* server_sid, |
| 79 void* message, |
| 80 DWORD* buffer_length, |
| 81 void* out_message_attributes, |
| 82 void* in_message_attributes, |
| 83 void* time_out) { |
| 84 return STATUS_ACCESS_DENIED; |
| 85 } |
| 86 |
| 87 // Directwrite connects to the font cache service to retrieve information about |
| 88 // fonts installed on the system etc. This works well outside the sandbox and |
| 89 // within the sandbox as long as the lpc connection maintained by the current |
| 90 // process with the font cache service remains valid. It appears that there |
| 91 // are cases when this connection is dropped after which directwrite is unable |
| 92 // to connect to the font cache service which causes problems with characters |
| 93 // disappearing. |
| 94 // Directwrite has fallback code to enumerate fonts if it is unable to connect |
| 95 // to the font cache service. We need to intercept the following APIs to |
| 96 // ensure that it does not connect to the font cache service. |
| 97 // NtALpcConnectPort |
| 98 // OpenSCManagerW |
| 99 // OpenServiceW |
| 100 // StartServiceW |
| 101 // CloseServiceHandle. |
| 102 // These are all IAT patched. |
| 103 void PatchServiceManagerCalls() { |
| 104 static bool is_patched = false; |
| 105 if (is_patched) |
| 106 return; |
| 107 const char* service_provider_dll = |
| 108 (base::win::GetVersion() >= base::win::VERSION_WIN8 |
| 109 ? "api-ms-win-service-management-l1-1-0.dll" |
| 110 : "advapi32.dll"); |
| 111 |
| 112 is_patched = true; |
| 113 |
| 114 DWORD patched = |
| 115 g_iat_patch_open_sc_manager.Patch(L"dwrite.dll", service_provider_dll, |
| 116 "OpenSCManagerW", OpenSCManagerWPatch); |
| 117 DCHECK(patched == 0); |
| 118 |
| 119 patched = g_iat_patch_close_service_handle.Patch( |
| 120 L"dwrite.dll", service_provider_dll, "CloseServiceHandle", |
| 121 CloseServiceHandlePatch); |
| 122 DCHECK(patched == 0); |
| 123 |
| 124 patched = g_iat_patch_open_service.Patch(L"dwrite.dll", service_provider_dll, |
| 125 "OpenServiceW", OpenServiceWPatch); |
| 126 DCHECK(patched == 0); |
| 127 |
| 128 patched = g_iat_patch_start_service.Patch( |
| 129 L"dwrite.dll", service_provider_dll, "StartServiceW", StartServiceWPatch); |
| 130 DCHECK(patched == 0); |
| 131 |
| 132 patched = g_iat_patch_nt_connect_port.Patch( |
| 133 L"dwrite.dll", "ntdll.dll", "NtAlpcConnectPort", NtALpcConnectPortPatch); |
| 134 DCHECK(patched == 0); |
| 135 } |
| 136 |
| 137 // Copied from content/common/font_warmup_win.cc |
| 138 // Windows-only DirectWrite support. These warm up the DirectWrite paths |
| 139 // before sandbox lock down to allow Skia access to the Font Manager service. |
| 140 void CreateDirectWriteFactory(IDWriteFactory** factory) { |
| 141 typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc; |
| 142 HMODULE dwrite_dll = LoadLibraryW(L"dwrite.dll"); |
| 143 // TODO(scottmg): Temporary code to track crash in http://crbug.com/387867. |
| 144 if (!dwrite_dll) { |
| 145 DWORD load_library_get_last_error = GetLastError(); |
| 146 base::debug::Alias(&dwrite_dll); |
| 147 base::debug::Alias(&load_library_get_last_error); |
| 148 CHECK(false); |
| 149 } |
| 150 |
| 151 // TODO(kulshin): This shouldn't be necessary, but not having this causes |
| 152 // breakage in content_browsertests, and possibly other high-stress cases. |
| 153 PatchServiceManagerCalls(); |
| 154 |
| 155 DWriteCreateFactoryProc dwrite_create_factory_proc = |
| 156 reinterpret_cast<DWriteCreateFactoryProc>( |
| 157 GetProcAddress(dwrite_dll, "DWriteCreateFactory")); |
| 158 // TODO(scottmg): Temporary code to track crash in http://crbug.com/387867. |
| 159 if (!dwrite_create_factory_proc) { |
| 160 DWORD get_proc_address_get_last_error = GetLastError(); |
| 161 base::debug::Alias(&dwrite_create_factory_proc); |
| 162 base::debug::Alias(&get_proc_address_get_last_error); |
| 163 CHECK(false); |
| 164 } |
| 165 CHECK(SUCCEEDED(dwrite_create_factory_proc( |
| 166 DWRITE_FACTORY_TYPE_ISOLATED, __uuidof(IDWriteFactory), |
| 167 reinterpret_cast<IUnknown**>(factory)))); |
| 168 } |
| 169 |
| 170 HRESULT STDMETHODCALLTYPE StubFontCollection(IDWriteFactory* factory, |
| 171 IDWriteFontCollection** col, |
| 172 BOOL checkUpdates) { |
| 173 DCHECK(g_font_collection != nullptr); |
| 174 g_font_collection.CopyTo(col); |
| 175 return S_OK; |
| 176 } |
| 177 |
| 178 // Copied from content/common/font_warmup_win.cc |
| 179 // TODO(kulshin): eventually, make Skia deal with custom font collections and |
| 180 // get rid of this altogether. |
| 181 void PatchDWriteFactory(IDWriteFactory* factory) { |
| 182 const unsigned int kGetSystemFontCollectionVTableIndex = 3; |
| 183 |
| 184 PROC* vtable = *reinterpret_cast<PROC**>(factory); |
| 185 PROC* function_ptr = &vtable[kGetSystemFontCollectionVTableIndex]; |
| 186 void* stub_function = &StubFontCollection; |
| 187 base::win::ModifyCode(function_ptr, &stub_function, sizeof(PROC)); |
| 188 } |
| 189 |
| 190 void InitializeDWriteFontProxy( |
| 191 const base::Callback<IPC::Sender(void)>& getSender) { |
| 192 mswr::ComPtr<IDWriteFactory> factory; |
| 193 |
| 194 CreateDirectWriteFactory(&factory); |
| 195 |
| 196 if (g_font_collection == nullptr) { |
| 197 mswr::MakeAndInitialize<DWriteFontCollectionProxy>( |
| 198 &g_font_collection, factory.Get(), getSender); |
| 199 } |
| 200 |
| 201 PatchDWriteFactory(factory.Get()); |
| 202 |
| 203 blink::WebFontRendering::setDirectWriteFactory(factory.Get()); |
| 204 SkFontMgr* skiaFontManager = SkFontMgr_New_DirectWrite(factory.Get()); |
| 205 SetDefaultSkiaFactory(skiaFontManager); |
| 206 } |
| 207 |
| 208 void UninitializeDWriteFontProxy() { |
| 209 if (g_font_collection != nullptr) |
| 210 g_font_collection->Unregister(); |
| 211 } |
| 212 |
| 213 void SetDWriteFontProxyTestModeSender(IPC::Sender* sender) { |
| 214 g_senderOverride = sender; |
| 215 } |
| 216 |
| 217 } // namespace content |
OLD | NEW |