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