Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(208)

Side by Side Diff: content/common/font_warmup_win.cc

Issue 1642213002: Move font_warmup_win, fix webrtc dep. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « content/common/font_warmup_win.h ('k') | content/common/font_warmup_win_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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/common/font_warmup_win.h"
6
7 #include <dwrite.h>
8 #include <stdint.h>
9 #include <map>
10
11 #include "base/debug/alias.h"
12 #include "base/files/file_path.h"
13 #include "base/lazy_instance.h"
14 #include "base/logging.h"
15 #include "base/macros.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/numerics/safe_conversions.h"
18 #include "base/numerics/safe_math.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/synchronization/lock.h"
21 #include "base/sys_byteorder.h"
22 #include "base/trace_event/trace_event.h"
23 #include "base/win/iat_patch_function.h"
24 #include "base/win/windows_version.h"
25 #include "content/public/common/dwrite_font_platform_win.h"
26 #include "ppapi/shared_impl/proxy_lock.h"
27 #include "skia/ext/fontmgr_default_win.h"
28 #include "skia/ext/refptr.h"
29 #include "third_party/WebKit/public/web/win/WebFontRendering.h"
30 #include "third_party/skia/include/core/SkPaint.h"
31 #include "third_party/skia/include/ports/SkFontMgr.h"
32 #include "third_party/skia/include/ports/SkTypeface_win.h"
33 #include "ui/gfx/hud_font.h"
34
35 namespace content {
36
37 namespace {
38
39 // The Skia font manager, used for the life of the process (leaked at the end).
40 SkFontMgr* g_warmup_fontmgr = nullptr;
41
42 base::win::IATPatchFunction g_iat_patch_open_sc_manager;
43 base::win::IATPatchFunction g_iat_patch_close_service_handle;
44 base::win::IATPatchFunction g_iat_patch_open_service;
45 base::win::IATPatchFunction g_iat_patch_start_service;
46 base::win::IATPatchFunction g_iat_patch_nt_connect_port;
47
48 // These are from ntddk.h
49 #if !defined(STATUS_ACCESS_DENIED)
50 #define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
51 #endif
52
53 typedef LONG NTSTATUS;
54
55 const uintptr_t kFakeSCMHandle = 0xdead0001;
56 const uintptr_t kFakeServiceHandle = 0xdead0002;
57
58 SC_HANDLE WINAPI OpenSCManagerWPatch(const wchar_t* machine_name,
59 const wchar_t* database_name,
60 DWORD access_mask) {
61 ::SetLastError(0);
62 return reinterpret_cast<SC_HANDLE>(kFakeSCMHandle);
63 }
64
65 SC_HANDLE WINAPI OpenServiceWPatch(SC_HANDLE sc_manager,
66 const wchar_t* service_name,
67 DWORD access_mask) {
68 ::SetLastError(0);
69 return reinterpret_cast<SC_HANDLE>(kFakeServiceHandle);
70 }
71
72 BOOL WINAPI CloseServiceHandlePatch(SC_HANDLE service_handle) {
73 if (service_handle != reinterpret_cast<SC_HANDLE>(kFakeServiceHandle) &&
74 service_handle != reinterpret_cast<SC_HANDLE>(kFakeSCMHandle))
75 CHECK(false);
76 ::SetLastError(0);
77 return TRUE;
78 }
79
80 BOOL WINAPI StartServiceWPatch(SC_HANDLE service,
81 DWORD args,
82 const wchar_t** arg_vectors) {
83 if (service != reinterpret_cast<SC_HANDLE>(kFakeServiceHandle))
84 CHECK(false);
85 ::SetLastError(ERROR_ACCESS_DENIED);
86 return FALSE;
87 }
88
89 NTSTATUS WINAPI NtALpcConnectPortPatch(HANDLE* port_handle,
90 void* port_name,
91 void* object_attribs,
92 void* port_attribs,
93 DWORD flags,
94 void* server_sid,
95 void* message,
96 DWORD* buffer_length,
97 void* out_message_attributes,
98 void* in_message_attributes,
99 void* time_out) {
100 return STATUS_ACCESS_DENIED;
101 }
102
103 // Windows-only DirectWrite support. These warm up the DirectWrite paths
104 // before sandbox lock down to allow Skia access to the Font Manager service.
105 void CreateDirectWriteFactory(IDWriteFactory** factory) {
106 typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc;
107 HMODULE dwrite_dll = LoadLibraryW(L"dwrite.dll");
108 // TODO(scottmg): Temporary code to track crash in http://crbug.com/387867.
109 if (!dwrite_dll) {
110 DWORD load_library_get_last_error = GetLastError();
111 base::debug::Alias(&dwrite_dll);
112 base::debug::Alias(&load_library_get_last_error);
113 CHECK(false);
114 }
115
116 PatchServiceManagerCalls();
117
118 DWriteCreateFactoryProc dwrite_create_factory_proc =
119 reinterpret_cast<DWriteCreateFactoryProc>(
120 GetProcAddress(dwrite_dll, "DWriteCreateFactory"));
121 // TODO(scottmg): Temporary code to track crash in http://crbug.com/387867.
122 if (!dwrite_create_factory_proc) {
123 DWORD get_proc_address_get_last_error = GetLastError();
124 base::debug::Alias(&dwrite_create_factory_proc);
125 base::debug::Alias(&get_proc_address_get_last_error);
126 CHECK(false);
127 }
128 CHECK(SUCCEEDED(dwrite_create_factory_proc(
129 DWRITE_FACTORY_TYPE_ISOLATED, __uuidof(IDWriteFactory),
130 reinterpret_cast<IUnknown**>(factory))));
131 }
132
133 HRESULT STDMETHODCALLTYPE StubFontCollection(IDWriteFactory* factory,
134 IDWriteFontCollection** col,
135 BOOL checkUpdates) {
136 // We always return pre-created font collection from here.
137 IDWriteFontCollection* custom_collection = GetCustomFontCollection(factory);
138 DCHECK(custom_collection != nullptr);
139 *col = custom_collection;
140 return S_OK;
141 }
142
143 void PatchDWriteFactory(IDWriteFactory* factory) {
144 const unsigned int kGetSystemFontCollectionVTableIndex = 3;
145
146 PROC* vtable = *reinterpret_cast<PROC**>(factory);
147 PROC* function_ptr = &vtable[kGetSystemFontCollectionVTableIndex];
148 void* stub_function = &StubFontCollection;
149 base::win::ModifyCode(function_ptr, &stub_function, sizeof(PROC));
150 }
151
152 // Class to fake out a DC or a Font object. Maintains a reference to a
153 // SkTypeFace to emulate the simple operation of a DC and Font.
154 class FakeGdiObject : public base::RefCountedThreadSafe<FakeGdiObject> {
155 public:
156 FakeGdiObject(uint32_t magic, void* handle)
157 : handle_(handle), magic_(magic) {}
158
159 void set_typeface(const skia::RefPtr<SkTypeface>& typeface) {
160 typeface_ = typeface;
161 }
162
163 skia::RefPtr<SkTypeface> typeface() { return typeface_; }
164 void* handle() { return handle_; }
165 uint32_t magic() { return magic_; }
166
167 private:
168 friend class base::RefCountedThreadSafe<FakeGdiObject>;
169 ~FakeGdiObject() {}
170
171 void* handle_;
172 uint32_t magic_;
173 skia::RefPtr<SkTypeface> typeface_;
174
175 DISALLOW_COPY_AND_ASSIGN(FakeGdiObject);
176 };
177
178 // This class acts as a factory for creating new fake GDI objects. It also maps
179 // the new instances of the FakeGdiObject class to an incrementing handle value
180 // which is passed to the caller of the emulated GDI function for later
181 // reference. We can't be sure that this won't be used in a multi-threaded
182 // environment so we need to ensure a lock is taken before accessing the map of
183 // issued objects.
184 class FakeGdiObjectFactory {
185 public:
186 FakeGdiObjectFactory() : curr_handle_(0) {}
187
188 // Find a corresponding fake GDI object and verify its magic value.
189 // The returned value is either nullptr or the validated object.
190 scoped_refptr<FakeGdiObject> Validate(void* obj, uint32_t magic) {
191 if (obj) {
192 base::AutoLock scoped_lock(objects_lock_);
193 auto handle_entry = objects_.find(obj);
194 if (handle_entry != objects_.end() &&
195 handle_entry->second->magic() == magic) {
196 return handle_entry->second;
197 }
198 }
199 return nullptr;
200 }
201
202 scoped_refptr<FakeGdiObject> Create(uint32_t magic) {
203 base::AutoLock scoped_lock(objects_lock_);
204 curr_handle_++;
205 // We don't support wrapping the fake handle value.
206 void* handle = reinterpret_cast<void*>(curr_handle_.ValueOrDie());
207 scoped_refptr<FakeGdiObject> object(new FakeGdiObject(magic, handle));
208 objects_[handle] = object;
209 return object;
210 }
211
212 bool DeleteObject(void* obj, uint32_t magic) {
213 base::AutoLock scoped_lock(objects_lock_);
214 auto handle_entry = objects_.find(obj);
215 if (handle_entry != objects_.end() &&
216 handle_entry->second->magic() == magic) {
217 objects_.erase(handle_entry);
218 return true;
219 }
220 return false;
221 }
222
223 size_t GetObjectCount() {
224 base::AutoLock scoped_lock(objects_lock_);
225 return objects_.size();
226 }
227
228 void ResetObjectHandles() {
229 base::AutoLock scoped_lock(objects_lock_);
230 curr_handle_ = 0;
231 objects_.clear();
232 }
233
234 private:
235 base::CheckedNumeric<uintptr_t> curr_handle_;
236 std::map<void*, scoped_refptr<FakeGdiObject>> objects_;
237 base::Lock objects_lock_;
238
239 DISALLOW_COPY_AND_ASSIGN(FakeGdiObjectFactory);
240 };
241
242 base::LazyInstance<FakeGdiObjectFactory>::Leaky g_fake_gdi_object_factory =
243 LAZY_INSTANCE_INITIALIZER;
244
245 // Magic values for the fake GDI objects.
246 const uint32_t kFakeDCMagic = 'fkdc';
247 const uint32_t kFakeFontMagic = 'fkft';
248
249 skia::RefPtr<SkTypeface> GetTypefaceFromLOGFONT(const LOGFONTW* log_font) {
250 CHECK(g_warmup_fontmgr);
251 int weight = log_font->lfWeight;
252 if (weight == FW_DONTCARE)
253 weight = SkFontStyle::kNormal_Weight;
254
255 SkFontStyle style(weight, log_font->lfWidth,
256 log_font->lfItalic ? SkFontStyle::kItalic_Slant
257 : SkFontStyle::kUpright_Slant);
258
259 std::string family_name = base::WideToUTF8(log_font->lfFaceName);
260 ppapi::ProxyAutoLock lock; // Needed for DirectWrite font proxy.
261 return skia::AdoptRef(
262 g_warmup_fontmgr->matchFamilyStyle(family_name.c_str(), style));
263 }
264
265 HDC WINAPI CreateCompatibleDCPatch(HDC dc_handle) {
266 scoped_refptr<FakeGdiObject> ret =
267 g_fake_gdi_object_factory.Get().Create(kFakeDCMagic);
268 return static_cast<HDC>(ret->handle());
269 }
270
271 HFONT WINAPI CreateFontIndirectWPatch(const LOGFONTW* log_font) {
272 if (!log_font)
273 return nullptr;
274
275 skia::RefPtr<SkTypeface> typeface = GetTypefaceFromLOGFONT(log_font);
276 if (!typeface)
277 return nullptr;
278
279 scoped_refptr<FakeGdiObject> ret =
280 g_fake_gdi_object_factory.Get().Create(kFakeFontMagic);
281 ret->set_typeface(typeface);
282
283 return static_cast<HFONT>(ret->handle());
284 }
285
286 BOOL WINAPI DeleteDCPatch(HDC dc_handle) {
287 return g_fake_gdi_object_factory.Get().DeleteObject(dc_handle, kFakeDCMagic);
288 }
289
290 BOOL WINAPI DeleteObjectPatch(HGDIOBJ object_handle) {
291 return g_fake_gdi_object_factory.Get().DeleteObject(object_handle,
292 kFakeFontMagic);
293 }
294
295 int WINAPI EnumFontFamiliesExWPatch(HDC dc_handle,
296 LPLOGFONTW log_font,
297 FONTENUMPROCW enum_callback,
298 LPARAM callback_param,
299 DWORD flags) {
300 scoped_refptr<FakeGdiObject> dc_obj =
301 g_fake_gdi_object_factory.Get().Validate(dc_handle, kFakeDCMagic);
302 if (!dc_obj)
303 return 1;
304
305 if (!log_font || !enum_callback)
306 return 1;
307
308 skia::RefPtr<SkTypeface> typeface = GetTypefaceFromLOGFONT(log_font);
309 if (!typeface)
310 return 1;
311
312 ENUMLOGFONTEXDVW enum_log_font = {};
313 enum_log_font.elfEnumLogfontEx.elfLogFont = *log_font;
314 // TODO: Fill in the rest of the text metric structure. Not yet needed for
315 // Flash support but might be in the future.
316 NEWTEXTMETRICEXW text_metric = {};
317 text_metric.ntmTm.ntmFlags = NTM_PS_OPENTYPE;
318
319 return enum_callback(&enum_log_font.elfEnumLogfontEx.elfLogFont,
320 reinterpret_cast<TEXTMETRIC*>(&text_metric),
321 TRUETYPE_FONTTYPE, callback_param);
322 }
323
324 DWORD WINAPI GetFontDataPatch(HDC dc_handle,
325 DWORD table_tag,
326 DWORD table_offset,
327 LPVOID buffer,
328 DWORD buffer_length) {
329 scoped_refptr<FakeGdiObject> dc_obj =
330 g_fake_gdi_object_factory.Get().Validate(dc_handle, kFakeDCMagic);
331 if (!dc_obj)
332 return GDI_ERROR;
333
334 skia::RefPtr<SkTypeface> typeface = dc_obj->typeface();
335 if (!typeface)
336 return GDI_ERROR;
337
338 // |getTableData| handles |buffer| being nullptr. However if it is nullptr
339 // then set the size to INT32_MAX otherwise |getTableData| will return the
340 // minimum value between the table entry size and the size passed in. The
341 // common Windows idiom is to pass 0 as |buffer_length| when passing nullptr,
342 // which would in this case result in |getTableData| returning 0 which isn't
343 // the correct answer for emulating GDI. |table_tag| must also have its
344 // byte order swapped to counter the swap which occurs in the called method.
345 size_t length = typeface->getTableData(
346 base::ByteSwap(base::strict_cast<uint32_t>(table_tag)), table_offset,
347 buffer ? buffer_length : INT32_MAX, buffer);
348 // We can't distinguish between an empty table and an error.
349 if (length == 0)
350 return GDI_ERROR;
351
352 return base::checked_cast<DWORD>(length);
353 }
354
355 HGDIOBJ WINAPI SelectObjectPatch(HDC dc_handle, HGDIOBJ object_handle) {
356 scoped_refptr<FakeGdiObject> dc_obj =
357 g_fake_gdi_object_factory.Get().Validate(dc_handle, kFakeDCMagic);
358 if (!dc_obj)
359 return nullptr;
360
361 scoped_refptr<FakeGdiObject> font_obj =
362 g_fake_gdi_object_factory.Get().Validate(object_handle, kFakeFontMagic);
363 if (!font_obj)
364 return nullptr;
365
366 // Construct a new fake font object to handle the old font if there's one.
367 scoped_refptr<FakeGdiObject> new_font_obj;
368 skia::RefPtr<SkTypeface> old_typeface = dc_obj->typeface();
369 if (old_typeface) {
370 new_font_obj = g_fake_gdi_object_factory.Get().Create(kFakeFontMagic);
371 new_font_obj->set_typeface(old_typeface);
372 }
373 dc_obj->set_typeface(font_obj->typeface());
374
375 if (new_font_obj)
376 return static_cast<HGDIOBJ>(new_font_obj->handle());
377 return nullptr;
378 }
379
380 void DoSingleGdiPatch(base::win::IATPatchFunction& patch,
381 const base::FilePath& path,
382 const char* function_name,
383 void* new_function) {
384 patch.Patch(path.value().c_str(), "gdi32.dll", function_name, new_function);
385 }
386
387 class GdiFontPatchDataImpl : public content::GdiFontPatchData {
388 public:
389 GdiFontPatchDataImpl(const base::FilePath& path);
390
391 private:
392 base::win::IATPatchFunction create_compatible_dc_patch_;
393 base::win::IATPatchFunction create_font_indirect_patch_;
394 base::win::IATPatchFunction create_delete_dc_patch_;
395 base::win::IATPatchFunction create_delete_object_patch_;
396 base::win::IATPatchFunction create_enum_font_families_patch_;
397 base::win::IATPatchFunction create_get_font_data_patch_;
398 base::win::IATPatchFunction create_select_object_patch_;
399 };
400
401 GdiFontPatchDataImpl::GdiFontPatchDataImpl(const base::FilePath& path) {
402 DoSingleGdiPatch(create_compatible_dc_patch_, path, "CreateCompatibleDC",
403 CreateCompatibleDCPatch);
404 DoSingleGdiPatch(create_font_indirect_patch_, path, "CreateFontIndirectW",
405 CreateFontIndirectWPatch);
406 DoSingleGdiPatch(create_delete_dc_patch_, path, "DeleteDC", DeleteDCPatch);
407 DoSingleGdiPatch(create_delete_object_patch_, path, "DeleteObject",
408 DeleteObjectPatch);
409 DoSingleGdiPatch(create_enum_font_families_patch_, path,
410 "EnumFontFamiliesExW", EnumFontFamiliesExWPatch);
411 DoSingleGdiPatch(create_get_font_data_patch_, path, "GetFontData",
412 GetFontDataPatch);
413 DoSingleGdiPatch(create_select_object_patch_, path, "SelectObject",
414 SelectObjectPatch);
415 }
416
417 } // namespace
418
419 // Directwrite connects to the font cache service to retrieve information about
420 // fonts installed on the system etc. This works well outside the sandbox and
421 // within the sandbox as long as the lpc connection maintained by the current
422 // process with the font cache service remains valid. It appears that there
423 // are cases when this connection is dropped after which directwrite is unable
424 // to connect to the font cache service which causes problems with characters
425 // disappearing.
426 // Directwrite has fallback code to enumerate fonts if it is unable to connect
427 // to the font cache service. We need to intercept the following APIs to
428 // ensure that it does not connect to the font cache service.
429 // NtALpcConnectPort
430 // OpenSCManagerW
431 // OpenServiceW
432 // StartServiceW
433 // CloseServiceHandle.
434 // These are all IAT patched.
435 void PatchServiceManagerCalls() {
436 static bool is_patched = false;
437 if (is_patched)
438 return;
439 const char* service_provider_dll =
440 (base::win::GetVersion() >= base::win::VERSION_WIN8
441 ? "api-ms-win-service-management-l1-1-0.dll"
442 : "advapi32.dll");
443
444 is_patched = true;
445
446 DWORD patched =
447 g_iat_patch_open_sc_manager.Patch(L"dwrite.dll", service_provider_dll,
448 "OpenSCManagerW", OpenSCManagerWPatch);
449 DCHECK(patched == 0);
450
451 patched = g_iat_patch_close_service_handle.Patch(
452 L"dwrite.dll", service_provider_dll, "CloseServiceHandle",
453 CloseServiceHandlePatch);
454 DCHECK(patched == 0);
455
456 patched = g_iat_patch_open_service.Patch(L"dwrite.dll", service_provider_dll,
457 "OpenServiceW", OpenServiceWPatch);
458 DCHECK(patched == 0);
459
460 patched = g_iat_patch_start_service.Patch(
461 L"dwrite.dll", service_provider_dll, "StartServiceW", StartServiceWPatch);
462 DCHECK(patched == 0);
463
464 patched = g_iat_patch_nt_connect_port.Patch(
465 L"dwrite.dll", "ntdll.dll", "NtAlpcConnectPort", NtALpcConnectPortPatch);
466 DCHECK(patched == 0);
467 }
468
469 void DoPreSandboxWarmupForTypeface(SkTypeface* typeface) {
470 SkPaint paint_warmup;
471 paint_warmup.setTypeface(typeface);
472 wchar_t glyph = L'S';
473 paint_warmup.measureText(&glyph, 2);
474 }
475
476 SkFontMgr* GetPreSandboxWarmupFontMgr() {
477 if (!g_warmup_fontmgr) {
478 IDWriteFactory* factory;
479 CreateDirectWriteFactory(&factory);
480
481 GetCustomFontCollection(factory);
482
483 PatchDWriteFactory(factory);
484
485 blink::WebFontRendering::setDirectWriteFactory(factory);
486 g_warmup_fontmgr = SkFontMgr_New_DirectWrite(factory);
487 }
488 return g_warmup_fontmgr;
489 }
490
491 GdiFontPatchData* PatchGdiFontEnumeration(const base::FilePath& path) {
492 if (ShouldUseDirectWriteFontProxyFieldTrial() && !g_warmup_fontmgr)
493 g_warmup_fontmgr = SkFontMgr_New_DirectWrite();
494 // If not using the font proxy, we assume |g_warmup_fontmgr| is already
495 // initialized before this function is called.
496 DCHECK(g_warmup_fontmgr);
497 return new GdiFontPatchDataImpl(path);
498 }
499
500 size_t GetEmulatedGdiHandleCountForTesting() {
501 return g_fake_gdi_object_factory.Get().GetObjectCount();
502 }
503
504 void ResetEmulatedGdiHandlesForTesting() {
505 g_fake_gdi_object_factory.Get().ResetObjectHandles();
506 }
507
508 void SetPreSandboxWarmupFontMgrForTesting(SkFontMgr* fontmgr) {
509 g_warmup_fontmgr = fontmgr;
510 }
511
512 void WarmupDirectWrite() {
513 TRACE_EVENT0("startup", "content::WarmupDirectWrite");
514
515 // The objects used here are intentionally not freed as we want the Skia
516 // code to use these objects after warmup.
517 SetDefaultSkiaFactory(GetPreSandboxWarmupFontMgr());
518
519 // We need to warm up *some* font for DirectWrite. We also need to pass one
520 // down for the CC HUD code, so use the same one here. Note that we don't use
521 // a monospace as would be nice in an attempt to avoid a small startup time
522 // regression, see http://crbug.com/463613.
523 skia::RefPtr<SkTypeface> hud_typeface = skia::AdoptRef(
524 GetPreSandboxWarmupFontMgr()->legacyCreateTypeface("Times New Roman", 0));
525 DoPreSandboxWarmupForTypeface(hud_typeface.get());
526 gfx::SetHudTypeface(hud_typeface);
527 }
528
529 } // namespace content
OLDNEW
« no previous file with comments | « content/common/font_warmup_win.h ('k') | content/common/font_warmup_win_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698