OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/common/font_warmup_win.h" | 5 #include "content/common/font_warmup_win.h" |
6 | 6 |
7 #include <dwrite.h> | 7 #include <dwrite.h> |
| 8 #include <map> |
8 | 9 |
9 #include "base/debug/alias.h" | 10 #include "base/debug/alias.h" |
| 11 #include "base/files/file_path.h" |
| 12 #include "base/lazy_instance.h" |
10 #include "base/logging.h" | 13 #include "base/logging.h" |
| 14 #include "base/memory/ref_counted.h" |
| 15 #include "base/numerics/safe_conversions.h" |
| 16 #include "base/numerics/safe_math.h" |
| 17 #include "base/strings/utf_string_conversions.h" |
| 18 #include "base/synchronization/lock.h" |
| 19 #include "base/sys_byteorder.h" |
11 #include "base/win/iat_patch_function.h" | 20 #include "base/win/iat_patch_function.h" |
12 #include "base/win/windows_version.h" | 21 #include "base/win/windows_version.h" |
13 #include "content/public/common/dwrite_font_platform_win.h" | 22 #include "content/public/common/dwrite_font_platform_win.h" |
14 #include "skia/ext/fontmgr_default_win.h" | 23 #include "skia/ext/fontmgr_default_win.h" |
15 #include "skia/ext/refptr.h" | 24 #include "skia/ext/refptr.h" |
16 #include "third_party/WebKit/public/web/win/WebFontRendering.h" | 25 #include "third_party/WebKit/public/web/win/WebFontRendering.h" |
17 #include "third_party/skia/include/core/SkPaint.h" | 26 #include "third_party/skia/include/core/SkPaint.h" |
18 #include "third_party/skia/include/ports/SkFontMgr.h" | 27 #include "third_party/skia/include/ports/SkFontMgr.h" |
19 #include "third_party/skia/include/ports/SkTypeface_win.h" | 28 #include "third_party/skia/include/ports/SkTypeface_win.h" |
20 #include "ui/gfx/hud_font.h" | 29 #include "ui/gfx/hud_font.h" |
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
178 | 187 |
179 void PatchDWriteFactory(IDWriteFactory* factory) { | 188 void PatchDWriteFactory(IDWriteFactory* factory) { |
180 const unsigned int kGetSystemFontCollectionVTableIndex = 3; | 189 const unsigned int kGetSystemFontCollectionVTableIndex = 3; |
181 | 190 |
182 PROC* vtable = *reinterpret_cast<PROC**>(factory); | 191 PROC* vtable = *reinterpret_cast<PROC**>(factory); |
183 PROC* function_ptr = &vtable[kGetSystemFontCollectionVTableIndex]; | 192 PROC* function_ptr = &vtable[kGetSystemFontCollectionVTableIndex]; |
184 void* stub_function = &StubFontCollection; | 193 void* stub_function = &StubFontCollection; |
185 base::win::ModifyCode(function_ptr, &stub_function, sizeof(PROC)); | 194 base::win::ModifyCode(function_ptr, &stub_function, sizeof(PROC)); |
186 } | 195 } |
187 | 196 |
| 197 // Class to fake out a DC or a Font object. Maintains a reference to a |
| 198 // SkTypeFace to emulate the simple operation of a DC and Font. |
| 199 class FakeGdiObject : public base::RefCountedThreadSafe<FakeGdiObject> { |
| 200 public: |
| 201 FakeGdiObject(uint32_t magic, void* handle) |
| 202 : magic_(magic), handle_(handle) {} |
| 203 ~FakeGdiObject() {} |
| 204 |
| 205 void set_typeface(const skia::RefPtr<SkTypeface>& typeface) { |
| 206 typeface_ = typeface; |
| 207 } |
| 208 |
| 209 skia::RefPtr<SkTypeface> typeface() { return typeface_; } |
| 210 void* handle() { return handle_; } |
| 211 uint32_t magic() { return magic_; } |
| 212 |
| 213 private: |
| 214 friend class base::RefCountedThreadSafe<FakeGdiObject>; |
| 215 |
| 216 void* handle_; |
| 217 uint32_t magic_; |
| 218 skia::RefPtr<SkTypeface> typeface_; |
| 219 |
| 220 DISALLOW_COPY_AND_ASSIGN(FakeGdiObject); |
| 221 }; |
| 222 |
| 223 // This class acts as a factory for creating new fake GDI objects. It also maps |
| 224 // the new instances of the FakeGdiObject class to an incrementing handle value |
| 225 // which is passed to the caller of the emulated GDI function for later |
| 226 // reference. We can't be sure that this won't be used in a multi-threaded |
| 227 // environment so we need to ensure a lock is taken before accessing the map of |
| 228 // issued objects. |
| 229 class FakeGdiObjectFactory { |
| 230 public: |
| 231 FakeGdiObjectFactory() : curr_handle_(0) {} |
| 232 |
| 233 // Find a corresponding fake GDI object and verify its magic value. |
| 234 // The returned value is either nullptr or the validated object. |
| 235 scoped_refptr<FakeGdiObject> Validate(void* obj, uint32_t magic) { |
| 236 if (obj) { |
| 237 base::AutoLock scoped_lock(objects_lock_); |
| 238 auto handle_entry = objects_.find(obj); |
| 239 if (handle_entry != objects_.end() && |
| 240 handle_entry->second->magic() == magic) { |
| 241 return handle_entry->second; |
| 242 } |
| 243 } |
| 244 return nullptr; |
| 245 } |
| 246 |
| 247 scoped_refptr<FakeGdiObject> Create(uint32_t magic) { |
| 248 base::AutoLock scoped_lock(objects_lock_); |
| 249 curr_handle_++; |
| 250 // We don't support wrapping the fake handle value. |
| 251 void* handle = reinterpret_cast<void*>(curr_handle_.ValueOrDie()); |
| 252 scoped_refptr<FakeGdiObject> object(new FakeGdiObject(magic, handle)); |
| 253 objects_[handle] = object; |
| 254 return object; |
| 255 } |
| 256 |
| 257 bool DeleteObject(void* obj, uint32_t magic) { |
| 258 base::AutoLock scoped_lock(objects_lock_); |
| 259 auto handle_entry = objects_.find(obj); |
| 260 if (handle_entry != objects_.end() && |
| 261 handle_entry->second->magic() == magic) { |
| 262 objects_.erase(handle_entry); |
| 263 return true; |
| 264 } |
| 265 return false; |
| 266 } |
| 267 |
| 268 size_t GetObjectCount() { |
| 269 base::AutoLock scoped_lock(objects_lock_); |
| 270 return objects_.size(); |
| 271 } |
| 272 |
| 273 void ResetObjectHandles() { |
| 274 base::AutoLock scoped_lock(objects_lock_); |
| 275 curr_handle_ = 0; |
| 276 objects_.clear(); |
| 277 } |
| 278 |
| 279 private: |
| 280 base::CheckedNumeric<uintptr_t> curr_handle_; |
| 281 std::map<void*, scoped_refptr<FakeGdiObject>> objects_; |
| 282 base::Lock objects_lock_; |
| 283 |
| 284 DISALLOW_COPY_AND_ASSIGN(FakeGdiObjectFactory); |
| 285 }; |
| 286 |
| 287 base::LazyInstance<FakeGdiObjectFactory>::Leaky g_fake_gdi_object_factory = |
| 288 LAZY_INSTANCE_INITIALIZER; |
| 289 |
| 290 // Magic values for the fake GDI objects. |
| 291 const uint32_t kFakeDCMagic = 'fkdc'; |
| 292 const uint32_t kFakeFontMagic = 'fkft'; |
| 293 |
| 294 skia::RefPtr<SkTypeface> GetTypefaceFromLOGFONT(const LOGFONTW* log_font) { |
| 295 CHECK(g_warmup_fontmgr); |
| 296 int weight = log_font->lfWeight; |
| 297 if (weight == FW_DONTCARE) |
| 298 weight = SkFontStyle::kNormal_Weight; |
| 299 |
| 300 SkFontStyle style(weight, log_font->lfWidth, |
| 301 log_font->lfItalic ? SkFontStyle::kItalic_Slant |
| 302 : SkFontStyle::kUpright_Slant); |
| 303 |
| 304 std::string family_name = base::WideToUTF8(log_font->lfFaceName); |
| 305 return skia::AdoptRef( |
| 306 g_warmup_fontmgr->matchFamilyStyle(family_name.c_str(), style)); |
| 307 } |
| 308 |
| 309 HDC WINAPI CreateCompatibleDCPatch(HDC dc_handle) { |
| 310 scoped_refptr<FakeGdiObject> ret = |
| 311 g_fake_gdi_object_factory.Get().Create(kFakeDCMagic); |
| 312 return static_cast<HDC>(ret->handle()); |
| 313 } |
| 314 |
| 315 HFONT WINAPI CreateFontIndirectWPatch(const LOGFONTW* log_font) { |
| 316 if (!log_font) |
| 317 return nullptr; |
| 318 |
| 319 skia::RefPtr<SkTypeface> typeface = GetTypefaceFromLOGFONT(log_font); |
| 320 if (!typeface) |
| 321 return nullptr; |
| 322 |
| 323 scoped_refptr<FakeGdiObject> ret = |
| 324 g_fake_gdi_object_factory.Get().Create(kFakeFontMagic); |
| 325 ret->set_typeface(typeface); |
| 326 |
| 327 return static_cast<HFONT>(ret->handle()); |
| 328 } |
| 329 |
| 330 BOOL WINAPI DeleteDCPatch(HDC dc_handle) { |
| 331 return g_fake_gdi_object_factory.Get().DeleteObject(dc_handle, kFakeDCMagic); |
| 332 } |
| 333 |
| 334 BOOL WINAPI DeleteObjectPatch(HGDIOBJ object_handle) { |
| 335 return g_fake_gdi_object_factory.Get().DeleteObject(object_handle, |
| 336 kFakeFontMagic); |
| 337 } |
| 338 |
| 339 int WINAPI EnumFontFamiliesExWPatch(HDC dc_handle, |
| 340 LPLOGFONTW log_font, |
| 341 FONTENUMPROCW enum_callback, |
| 342 LPARAM callback_param, |
| 343 DWORD flags) { |
| 344 scoped_refptr<FakeGdiObject> dc_obj = |
| 345 g_fake_gdi_object_factory.Get().Validate(dc_handle, kFakeDCMagic); |
| 346 if (!dc_obj) |
| 347 return 1; |
| 348 |
| 349 if (!log_font || !enum_callback) |
| 350 return 1; |
| 351 |
| 352 skia::RefPtr<SkTypeface> typeface = GetTypefaceFromLOGFONT(log_font); |
| 353 if (!typeface) |
| 354 return 1; |
| 355 |
| 356 ENUMLOGFONTEXDVW enum_log_font = {0}; |
| 357 enum_log_font.elfEnumLogfontEx.elfLogFont = *log_font; |
| 358 // TODO: Fill in the rest of the text metric structure. Not yet needed for |
| 359 // Flash support but might be in the future. |
| 360 NEWTEXTMETRICEXW text_metric = {0}; |
| 361 text_metric.ntmTm.ntmFlags = NTM_PS_OPENTYPE; |
| 362 |
| 363 return enum_callback(&enum_log_font.elfEnumLogfontEx.elfLogFont, |
| 364 reinterpret_cast<TEXTMETRIC*>(&text_metric), |
| 365 TRUETYPE_FONTTYPE, callback_param); |
| 366 } |
| 367 |
| 368 DWORD WINAPI GetFontDataPatch(HDC dc_handle, |
| 369 DWORD table_tag, |
| 370 DWORD table_offset, |
| 371 LPVOID buffer, |
| 372 DWORD buffer_length) { |
| 373 scoped_refptr<FakeGdiObject> dc_obj = |
| 374 g_fake_gdi_object_factory.Get().Validate(dc_handle, kFakeDCMagic); |
| 375 if (!dc_obj) |
| 376 return GDI_ERROR; |
| 377 |
| 378 skia::RefPtr<SkTypeface> typeface = dc_obj->typeface(); |
| 379 if (!typeface) |
| 380 return GDI_ERROR; |
| 381 |
| 382 // |getTableData| handles |buffer| being nullptr. However if it is nullptr |
| 383 // then set the size to INT32_MAX otherwise |getTableData| will return the |
| 384 // minimum value between the table entry size and the size passed in. The |
| 385 // common Windows idiom is to pass 0 as |buffer_length| when passing nullptr, |
| 386 // which would in this case result in |getTableData| returning 0 which isn't |
| 387 // the correct answer for emulating GDI. |table_tag| must also have its |
| 388 // byte order swapped to counter the swap which occurs in the called method. |
| 389 size_t length = typeface->getTableData( |
| 390 base::ByteSwap(base::strict_cast<uint32_t>(table_tag)), table_offset, |
| 391 buffer ? buffer_length : INT32_MAX, buffer); |
| 392 // We can't distinguish between an empty table and an error. |
| 393 if (length == 0) |
| 394 return GDI_ERROR; |
| 395 |
| 396 return base::checked_cast<DWORD>(length); |
| 397 } |
| 398 |
| 399 HGDIOBJ WINAPI SelectObjectPatch(HDC dc_handle, HGDIOBJ object_handle) { |
| 400 scoped_refptr<FakeGdiObject> dc_obj = |
| 401 g_fake_gdi_object_factory.Get().Validate(dc_handle, kFakeDCMagic); |
| 402 if (!dc_obj) |
| 403 return nullptr; |
| 404 |
| 405 scoped_refptr<FakeGdiObject> font_obj = |
| 406 g_fake_gdi_object_factory.Get().Validate(object_handle, kFakeFontMagic); |
| 407 if (!font_obj) |
| 408 return nullptr; |
| 409 |
| 410 // Construct a new fake font object to handle the old font if there's one. |
| 411 scoped_refptr<FakeGdiObject> new_font_obj; |
| 412 skia::RefPtr<SkTypeface> old_typeface = dc_obj->typeface(); |
| 413 if (old_typeface) { |
| 414 new_font_obj = g_fake_gdi_object_factory.Get().Create(kFakeFontMagic); |
| 415 new_font_obj->set_typeface(old_typeface); |
| 416 } |
| 417 dc_obj->set_typeface(font_obj->typeface()); |
| 418 |
| 419 if (new_font_obj) |
| 420 return static_cast<HGDIOBJ>(new_font_obj->handle()); |
| 421 return nullptr; |
| 422 } |
| 423 |
| 424 void DoSingleGdiPatch(base::win::IATPatchFunction& patch, |
| 425 const base::FilePath& path, |
| 426 const char* function_name, |
| 427 void* new_function) { |
| 428 patch.Patch(path.value().c_str(), "gdi32.dll", function_name, new_function); |
| 429 } |
| 430 |
| 431 class GdiFontPatchDataImpl : public content::GdiFontPatchData { |
| 432 public: |
| 433 GdiFontPatchDataImpl(const base::FilePath& path); |
| 434 |
| 435 private: |
| 436 base::win::IATPatchFunction create_compatible_dc_patch_; |
| 437 base::win::IATPatchFunction create_font_indirect_patch_; |
| 438 base::win::IATPatchFunction create_delete_dc_patch_; |
| 439 base::win::IATPatchFunction create_delete_object_patch_; |
| 440 base::win::IATPatchFunction create_enum_font_families_patch_; |
| 441 base::win::IATPatchFunction create_get_font_data_patch_; |
| 442 base::win::IATPatchFunction create_select_object_patch_; |
| 443 }; |
| 444 |
| 445 GdiFontPatchDataImpl::GdiFontPatchDataImpl(const base::FilePath& path) { |
| 446 DoSingleGdiPatch(create_compatible_dc_patch_, path, "CreateCompatibleDC", |
| 447 CreateCompatibleDCPatch); |
| 448 DoSingleGdiPatch(create_font_indirect_patch_, path, "CreateFontIndirectW", |
| 449 CreateFontIndirectWPatch); |
| 450 DoSingleGdiPatch(create_delete_dc_patch_, path, "DeleteDC", DeleteDCPatch); |
| 451 DoSingleGdiPatch(create_delete_object_patch_, path, "DeleteObject", |
| 452 DeleteObjectPatch); |
| 453 DoSingleGdiPatch(create_enum_font_families_patch_, path, |
| 454 "EnumFontFamiliesExW", EnumFontFamiliesExWPatch); |
| 455 DoSingleGdiPatch(create_get_font_data_patch_, path, "GetFontData", |
| 456 GetFontDataPatch); |
| 457 DoSingleGdiPatch(create_select_object_patch_, path, "SelectObject", |
| 458 SelectObjectPatch); |
| 459 } |
| 460 |
188 } // namespace | 461 } // namespace |
189 | 462 |
190 void DoPreSandboxWarmupForTypeface(SkTypeface* typeface) { | 463 void DoPreSandboxWarmupForTypeface(SkTypeface* typeface) { |
191 SkPaint paint_warmup; | 464 SkPaint paint_warmup; |
192 paint_warmup.setTypeface(typeface); | 465 paint_warmup.setTypeface(typeface); |
193 wchar_t glyph = L'S'; | 466 wchar_t glyph = L'S'; |
194 paint_warmup.measureText(&glyph, 2); | 467 paint_warmup.measureText(&glyph, 2); |
195 } | 468 } |
196 | 469 |
197 SkFontMgr* GetPreSandboxWarmupFontMgr() { | 470 SkFontMgr* GetPreSandboxWarmupFontMgr() { |
198 if (!g_warmup_fontmgr) { | 471 if (!g_warmup_fontmgr) { |
199 IDWriteFactory* factory; | 472 IDWriteFactory* factory; |
200 CreateDirectWriteFactory(&factory); | 473 CreateDirectWriteFactory(&factory); |
201 | 474 |
202 GetCustomFontCollection(factory); | 475 GetCustomFontCollection(factory); |
203 | 476 |
204 PatchDWriteFactory(factory); | 477 PatchDWriteFactory(factory); |
205 | 478 |
206 blink::WebFontRendering::setDirectWriteFactory(factory); | 479 blink::WebFontRendering::setDirectWriteFactory(factory); |
207 g_warmup_fontmgr = SkFontMgr_New_DirectWrite(factory); | 480 g_warmup_fontmgr = SkFontMgr_New_DirectWrite(factory); |
208 } | 481 } |
209 return g_warmup_fontmgr; | 482 return g_warmup_fontmgr; |
210 } | 483 } |
211 | 484 |
| 485 GdiFontPatchData* PatchGdiFontEnumeration(const base::FilePath& path) { |
| 486 // We assume the fontmgr is already warmed up before calling this. |
| 487 DCHECK(g_warmup_fontmgr); |
| 488 return new GdiFontPatchDataImpl(path); |
| 489 } |
| 490 |
| 491 size_t GetEmulatedGdiHandleCountForTesting() { |
| 492 return g_fake_gdi_object_factory.Get().GetObjectCount(); |
| 493 } |
| 494 |
| 495 void ResetEmulatedGdiHandlesForTesting() { |
| 496 g_fake_gdi_object_factory.Get().ResetObjectHandles(); |
| 497 } |
| 498 |
| 499 void SetPreSandboxWarmupFontMgrForTesting(SkFontMgr* fontmgr) { |
| 500 g_warmup_fontmgr = fontmgr; |
| 501 } |
| 502 |
212 void WarmupDirectWrite() { | 503 void WarmupDirectWrite() { |
213 // The objects used here are intentionally not freed as we want the Skia | 504 // The objects used here are intentionally not freed as we want the Skia |
214 // code to use these objects after warmup. | 505 // code to use these objects after warmup. |
215 SetDefaultSkiaFactory(GetPreSandboxWarmupFontMgr()); | 506 SetDefaultSkiaFactory(GetPreSandboxWarmupFontMgr()); |
216 | 507 |
217 // We need to warm up *some* font for DirectWrite. We also need to pass one | 508 // We need to warm up *some* font for DirectWrite. We also need to pass one |
218 // down for the CC HUD code, so use the same one here. Note that we don't use | 509 // down for the CC HUD code, so use the same one here. Note that we don't use |
219 // a monospace as would be nice in an attempt to avoid a small startup time | 510 // a monospace as would be nice in an attempt to avoid a small startup time |
220 // regression, see http://crbug.com/463613. | 511 // regression, see http://crbug.com/463613. |
221 skia::RefPtr<SkTypeface> hud_typeface = skia::AdoptRef( | 512 skia::RefPtr<SkTypeface> hud_typeface = skia::AdoptRef( |
222 GetPreSandboxWarmupFontMgr()->legacyCreateTypeface("Times New Roman", 0)); | 513 GetPreSandboxWarmupFontMgr()->legacyCreateTypeface("Times New Roman", 0)); |
223 DoPreSandboxWarmupForTypeface(hud_typeface.get()); | 514 DoPreSandboxWarmupForTypeface(hud_typeface.get()); |
224 gfx::SetHudTypeface(hud_typeface); | 515 gfx::SetHudTypeface(hud_typeface); |
225 } | 516 } |
226 | 517 |
227 } // namespace content | 518 } // namespace content |
OLD | NEW |