1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
|
// 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/public/renderer/render_font_warmup_win.h"
#include <dwrite.h>
#include "base/debug/alias.h"
#include "base/logging.h"
#include "base/win/iat_patch_function.h"
#include "base/win/windows_version.h"
#include "content/public/common/dwrite_font_platform_win.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 {
SkFontMgr* g_warmup_fontmgr = NULL;
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;
SC_HANDLE WINAPI OpenSCManagerWPatch(const wchar_t* machine_name,
const wchar_t* database_name,
DWORD access_mask) {
::SetLastError(0);
return reinterpret_cast<SC_HANDLE>(0xdeadbeef);
}
SC_HANDLE WINAPI OpenServiceWPatch(SC_HANDLE sc_manager,
const wchar_t* service_name,
DWORD access_mask) {
::SetLastError(0);
return reinterpret_cast<SC_HANDLE>(0xdeadbabe);
}
BOOL WINAPI CloseServiceHandlePatch(SC_HANDLE service_handle) {
if (service_handle != reinterpret_cast<SC_HANDLE>(0xdeadbabe) &&
service_handle != reinterpret_cast<SC_HANDLE>(0xdeadbeef))
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>(0xdeadbabe))
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;
}
// 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);
}
// 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 != NULL);
*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));
}
} // namespace
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;
}
} // namespace content
|