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 <list> | |
| 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/strings/utf_string_conversions.h" | |
| 16 #include "base/synchronization/lock.h" | |
| 17 #include "base/sys_byteorder.h" | |
| 11 #include "base/win/iat_patch_function.h" | 18 #include "base/win/iat_patch_function.h" |
| 12 #include "base/win/windows_version.h" | 19 #include "base/win/windows_version.h" |
| 13 #include "content/public/common/dwrite_font_platform_win.h" | 20 #include "content/public/common/dwrite_font_platform_win.h" |
| 21 #include "skia/ext/refptr.h" | |
| 14 #include "third_party/WebKit/public/web/win/WebFontRendering.h" | 22 #include "third_party/WebKit/public/web/win/WebFontRendering.h" |
| 15 #include "third_party/skia/include/core/SkPaint.h" | 23 #include "third_party/skia/include/core/SkPaint.h" |
| 16 #include "third_party/skia/include/ports/SkFontMgr.h" | 24 #include "third_party/skia/include/ports/SkFontMgr.h" |
| 17 #include "third_party/skia/include/ports/SkTypeface_win.h" | 25 #include "third_party/skia/include/ports/SkTypeface_win.h" |
| 18 | 26 |
| 19 namespace content { | 27 namespace content { |
| 20 | 28 |
| 21 namespace { | 29 namespace { |
| 22 | 30 |
| 23 SkFontMgr* g_warmup_fontmgr = nullptr; | 31 SkFontMgr* g_warmup_fontmgr = nullptr; |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 175 | 183 |
| 176 void PatchDWriteFactory(IDWriteFactory* factory) { | 184 void PatchDWriteFactory(IDWriteFactory* factory) { |
| 177 const unsigned int kGetSystemFontCollectionVTableIndex = 3; | 185 const unsigned int kGetSystemFontCollectionVTableIndex = 3; |
| 178 | 186 |
| 179 PROC* vtable = *reinterpret_cast<PROC**>(factory); | 187 PROC* vtable = *reinterpret_cast<PROC**>(factory); |
| 180 PROC* function_ptr = &vtable[kGetSystemFontCollectionVTableIndex]; | 188 PROC* function_ptr = &vtable[kGetSystemFontCollectionVTableIndex]; |
| 181 void* stub_function = &StubFontCollection; | 189 void* stub_function = &StubFontCollection; |
| 182 base::win::ModifyCode(function_ptr, &stub_function, sizeof(PROC)); | 190 base::win::ModifyCode(function_ptr, &stub_function, sizeof(PROC)); |
| 183 } | 191 } |
| 184 | 192 |
| 193 // Magic values for the fake GDI objects. | |
| 194 const uint32_t kFakeDCMagic = 'fkdc'; | |
| 195 const uint32_t kFakeFontMagic = 'fkft'; | |
| 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. We can't | |
| 199 // be sure that this won't be used in a multi-threaded environment so we | |
| 200 // need to ensure a lock is taken before updating the static table of | |
| 201 // issued objects. | |
| 202 class FakeGdiObject : public base::RefCountedThreadSafe<FakeGdiObject> { | |
| 203 public: | |
| 204 // Find a corresponding fake GDI object and verify its magic value. | |
| 205 // The returned object is either nullptr or the new object. | |
| 206 static scoped_refptr<FakeGdiObject> Validate(void* obj, uint32_t magic) { | |
| 207 if (obj) { | |
| 208 base::AutoLock scoped_lock(objects_lock_); | |
| 209 uintptr_t handle = reinterpret_cast<uintptr_t>(obj); | |
| 210 for (const auto& currobj : objects_) { | |
| 211 if ((currobj->magic_ == magic) && (handle == currobj->handle_)) | |
| 212 return currobj; | |
| 213 } | |
| 214 } | |
| 215 | |
| 216 return nullptr; | |
| 217 } | |
| 218 | |
| 219 static scoped_refptr<FakeGdiObject> Create(uint32_t magic) { | |
| 220 base::AutoLock scoped_lock(objects_lock_); | |
| 221 scoped_refptr<FakeGdiObject> object(new FakeGdiObject(magic)); | |
| 222 objects_.push_back(object); | |
| 223 return object; | |
| 224 } | |
| 225 | |
| 226 static bool DeleteObject(void* obj, uint32_t magic) { | |
| 227 uintptr_t handle = reinterpret_cast<uintptr_t>(obj); | |
| 228 base::AutoLock scoped_lock(objects_lock_); | |
| 229 for (auto i = objects_.begin(); i != objects_.end(); ++i) { | |
| 230 const auto& fakeobj = *i; | |
| 231 if ((fakeobj->magic_ == magic) && (fakeobj->handle_ == handle)) { | |
| 232 objects_.erase(i); | |
| 233 return true; | |
| 234 } | |
| 235 } | |
| 236 return false; | |
| 237 } | |
| 238 | |
| 239 void SetTypeface(const skia::RefPtr<SkTypeface>& typeface) { | |
| 240 typeface_ = typeface; | |
| 241 } | |
| 242 | |
| 243 skia::RefPtr<SkTypeface> GetTypeface() { return typeface_; } | |
| 244 | |
| 245 void* GetHandle() { return reinterpret_cast<void*>(handle_); } | |
| 246 | |
| 247 private: | |
| 248 uint32_t magic_; | |
| 249 uintptr_t handle_; | |
| 250 skia::RefPtr<SkTypeface> typeface_; | |
| 251 static uintptr_t curr_handle_; | |
| 252 static std::list<scoped_refptr<FakeGdiObject>> objects_; | |
| 253 static base::Lock objects_lock_; | |
| 254 FakeGdiObject(uint32_t magic) : magic_(magic), handle_(++curr_handle_) {} | |
| 255 ~FakeGdiObject() {} | |
| 256 friend class base::RefCountedThreadSafe<FakeGdiObject>; | |
| 257 | |
| 258 DISALLOW_COPY_AND_ASSIGN(FakeGdiObject); | |
| 259 }; | |
| 260 | |
| 261 uintptr_t FakeGdiObject::curr_handle_ = 0; | |
| 262 std::list<scoped_refptr<FakeGdiObject>> FakeGdiObject::objects_; | |
| 263 base::Lock FakeGdiObject::objects_lock_; | |
| 264 | |
| 265 skia::RefPtr<SkTypeface> GetTypefaceFromLOGFONT(const LOGFONTW* logfont) { | |
| 266 CHECK(g_warmup_fontmgr); | |
| 267 int weight = logfont->lfWeight; | |
| 268 if (weight == FW_DONTCARE) | |
| 269 weight = SkFontStyle::kNormal_Weight; | |
| 270 | |
| 271 SkFontStyle style(weight, logfont->lfWidth, | |
| 272 logfont->lfItalic ? SkFontStyle::kItalic_Slant | |
| 273 : SkFontStyle::kUpright_Slant); | |
| 274 | |
| 275 std::string family_name = base::WideToUTF8(logfont->lfFaceName); | |
| 276 return skia::AdoptRef( | |
| 277 g_warmup_fontmgr->matchFamilyStyle(family_name.c_str(), style)); | |
| 278 } | |
| 279 | |
| 280 HDC WINAPI CreateCompatibleDCPatch(HDC hdc) { | |
| 281 scoped_refptr<FakeGdiObject> ret = FakeGdiObject::Create(kFakeDCMagic); | |
| 282 return static_cast<HDC>(ret->GetHandle()); | |
| 283 } | |
| 284 | |
| 285 HFONT WINAPI CreateFontIndirectWPatch(const LOGFONTW* lplf) { | |
| 286 if (!lplf) | |
| 287 return nullptr; | |
| 288 | |
| 289 skia::RefPtr<SkTypeface> typeface = GetTypefaceFromLOGFONT(lplf); | |
| 290 if (!typeface) | |
| 291 return nullptr; | |
| 292 | |
| 293 scoped_refptr<FakeGdiObject> ret = FakeGdiObject::Create(kFakeFontMagic); | |
| 294 ret->SetTypeface(typeface); | |
| 295 | |
| 296 return static_cast<HFONT>(ret->GetHandle()); | |
| 297 } | |
| 298 | |
| 299 BOOL WINAPI DeleteDCPatch(HDC hdc) { | |
| 300 if (hdc) | |
| 301 return FakeGdiObject::DeleteObject(hdc, kFakeDCMagic); | |
| 302 return FALSE; | |
| 303 } | |
| 304 | |
| 305 BOOL WINAPI DeleteObjectPatch(HGDIOBJ hObject) { | |
| 306 if (hObject) | |
| 307 return FakeGdiObject::DeleteObject(hObject, kFakeFontMagic); | |
| 308 return FALSE; | |
| 309 } | |
| 310 | |
| 311 int WINAPI EnumFontFamiliesExWPatch(HDC hdc, | |
| 312 LPLOGFONTW lpLogfont, | |
| 313 FONTENUMPROCW lpEnumFontFamExProc, | |
| 314 LPARAM lParam, | |
| 315 DWORD dwFlags) { | |
| 316 scoped_refptr<FakeGdiObject> dcobj = | |
| 317 FakeGdiObject::Validate(hdc, kFakeDCMagic); | |
| 318 if (!dcobj) { | |
| 319 LOG(ERROR) << "Invalid DC Object"; | |
| 320 return 1; | |
| 321 } | |
| 322 if (!lpLogfont || !lpEnumFontFamExProc) | |
| 323 return 1; | |
| 324 | |
| 325 skia::RefPtr<SkTypeface> typeface = GetTypefaceFromLOGFONT(lpLogfont); | |
| 326 if (!typeface) | |
| 327 return 1; | |
| 328 | |
| 329 ENUMLOGFONTEXDVW enum_log_font = {0}; | |
| 330 enum_log_font.elfEnumLogfontEx.elfLogFont = *lpLogfont; | |
| 331 // TODO: Fill in the rest of the text metric structure. Not yet needed for | |
| 332 // Flash support but might be in the future. | |
| 333 NEWTEXTMETRICEXW text_metric = {0}; | |
| 334 text_metric.ntmTm.ntmFlags = NTM_PS_OPENTYPE; | |
| 335 | |
| 336 return lpEnumFontFamExProc(&enum_log_font.elfEnumLogfontEx.elfLogFont, | |
| 337 reinterpret_cast<TEXTMETRIC*>(&text_metric), | |
| 338 TRUETYPE_FONTTYPE, lParam); | |
| 339 } | |
| 340 | |
| 341 DWORD WINAPI GetFontDataPatch(HDC hdc, | |
| 342 DWORD dwTable, | |
| 343 DWORD dwOffset, | |
| 344 LPVOID lpvBuffer, | |
| 345 DWORD cbData) { | |
| 346 scoped_refptr<FakeGdiObject> dcobj = | |
| 347 FakeGdiObject::Validate(hdc, kFakeDCMagic); | |
| 348 if (!dcobj) { | |
| 349 LOG(ERROR) << "Invalid DC Object"; | |
| 350 return GDI_ERROR; | |
| 351 } | |
| 352 | |
| 353 skia::RefPtr<SkTypeface> typeface = dcobj->GetTypeface(); | |
| 354 if (!typeface) | |
| 355 return GDI_ERROR; | |
| 356 | |
| 357 if (cbData > INT32_MAX) | |
| 358 return GDI_ERROR; | |
| 359 | |
| 360 // getTableData takes care of lpvBuffer being nullptr. Swap tag to counter | |
|
palmer
2015/09/15 21:13:28
Nit: This is some subtle stuff, and the comment sh
forshaw
2015/09/16 13:18:38
Reworded the comment.
| |
| 361 // effect of the underlying implementation. Also if the buffer pointer | |
| 362 // is nullptr then set length INT_MAX otherwise getTableData returns the | |
| 363 // minimum length between table size and passed length. If the caller passes | |
| 364 // 0 as GetFontData allows it then 0 length is returned which is incorrect. | |
| 365 size_t length = typeface->getTableData( | |
| 366 base::ByteSwap(base::strict_cast<uint32>(dwTable)), dwOffset, | |
| 367 lpvBuffer ? cbData : INT32_MAX, lpvBuffer); | |
| 368 // We can't distinguish between an empty table and an error. | |
| 369 if (length == 0) | |
| 370 return GDI_ERROR; | |
| 371 | |
| 372 return base::checked_cast<DWORD>(length); | |
| 373 } | |
| 374 | |
| 375 HGDIOBJ WINAPI SelectObjectPatch(HDC hdc, HGDIOBJ hgdiobj) { | |
| 376 scoped_refptr<FakeGdiObject> dcobj = | |
| 377 FakeGdiObject::Validate(hdc, kFakeDCMagic); | |
| 378 if (!dcobj) { | |
| 379 LOG(ERROR) << "Invalid DC Object"; | |
| 380 return nullptr; | |
| 381 } | |
| 382 | |
| 383 scoped_refptr<FakeGdiObject> fontobj = | |
| 384 FakeGdiObject::Validate(hgdiobj, kFakeFontMagic); | |
| 385 if (!fontobj) { | |
| 386 LOG(ERROR) << "Invalid Font Object"; | |
| 387 return nullptr; | |
| 388 } | |
| 389 | |
| 390 // Construct a new fake font object to handle the old font if there's one. | |
| 391 scoped_refptr<FakeGdiObject> newfontobj; | |
| 392 skia::RefPtr<SkTypeface> oldfont = dcobj->GetTypeface(); | |
| 393 if (oldfont) { | |
| 394 newfontobj = FakeGdiObject::Create(kFakeFontMagic); | |
| 395 newfontobj->SetTypeface(oldfont); | |
| 396 } | |
| 397 dcobj->SetTypeface(fontobj->GetTypeface()); | |
| 398 | |
| 399 if (newfontobj) | |
| 400 return static_cast<HGDIOBJ>(newfontobj->GetHandle()); | |
| 401 return nullptr; | |
| 402 } | |
| 403 | |
| 404 void DoSingleGdiPatch(base::win::IATPatchFunction& patch, | |
| 405 const base::FilePath& path, | |
| 406 const char* function_name, | |
| 407 void* new_function) { | |
| 408 DWORD error = patch.Patch(path.value().c_str(), "gdi32.dll", function_name, | |
| 409 new_function); | |
| 410 if (error != 0) | |
| 411 LOG(WARNING) << "Failed patching " << function_name << " " << error; | |
| 412 } | |
| 413 | |
| 414 class GdiFontPatchDataImpl : public content::GdiFontPatchData { | |
| 415 public: | |
| 416 GdiFontPatchDataImpl(const base::FilePath& path); | |
| 417 | |
| 418 private: | |
| 419 base::win::IATPatchFunction create_compatible_dc_patch_; | |
| 420 base::win::IATPatchFunction create_font_indirect_patch_; | |
| 421 base::win::IATPatchFunction create_delete_dc_patch_; | |
| 422 base::win::IATPatchFunction create_delete_object_patch_; | |
| 423 base::win::IATPatchFunction create_enum_font_families_patch_; | |
| 424 base::win::IATPatchFunction create_get_font_data_patch_; | |
| 425 base::win::IATPatchFunction create_select_object_patch_; | |
| 426 }; | |
| 427 | |
| 428 GdiFontPatchDataImpl::GdiFontPatchDataImpl(const base::FilePath& path) { | |
| 429 DoSingleGdiPatch(create_compatible_dc_patch_, path, "CreateCompatibleDC", | |
| 430 CreateCompatibleDCPatch); | |
| 431 DoSingleGdiPatch(create_font_indirect_patch_, path, "CreateFontIndirectW", | |
| 432 CreateFontIndirectWPatch); | |
| 433 DoSingleGdiPatch(create_delete_dc_patch_, path, "DeleteDC", DeleteDCPatch); | |
| 434 DoSingleGdiPatch(create_delete_object_patch_, path, "DeleteObject", | |
| 435 DeleteObjectPatch); | |
| 436 DoSingleGdiPatch(create_enum_font_families_patch_, path, | |
| 437 "EnumFontFamiliesExW", EnumFontFamiliesExWPatch); | |
| 438 DoSingleGdiPatch(create_get_font_data_patch_, path, "GetFontData", | |
| 439 GetFontDataPatch); | |
| 440 DoSingleGdiPatch(create_select_object_patch_, path, "SelectObject", | |
| 441 SelectObjectPatch); | |
| 442 } | |
| 443 | |
| 185 } // namespace | 444 } // namespace |
| 186 | 445 |
| 187 void DoPreSandboxWarmupForTypeface(SkTypeface* typeface) { | 446 void DoPreSandboxWarmupForTypeface(SkTypeface* typeface) { |
| 188 SkPaint paint_warmup; | 447 SkPaint paint_warmup; |
| 189 paint_warmup.setTypeface(typeface); | 448 paint_warmup.setTypeface(typeface); |
| 190 wchar_t glyph = L'S'; | 449 wchar_t glyph = L'S'; |
| 191 paint_warmup.measureText(&glyph, 2); | 450 paint_warmup.measureText(&glyph, 2); |
| 192 } | 451 } |
| 193 | 452 |
| 194 SkFontMgr* GetPreSandboxWarmupFontMgr() { | 453 SkFontMgr* GetPreSandboxWarmupFontMgr() { |
| 195 if (!g_warmup_fontmgr) { | 454 if (!g_warmup_fontmgr) { |
| 196 IDWriteFactory* factory; | 455 IDWriteFactory* factory; |
| 197 CreateDirectWriteFactory(&factory); | 456 CreateDirectWriteFactory(&factory); |
| 198 | 457 |
| 199 GetCustomFontCollection(factory); | 458 GetCustomFontCollection(factory); |
| 200 | 459 |
| 201 PatchDWriteFactory(factory); | 460 PatchDWriteFactory(factory); |
| 202 | 461 |
| 203 blink::WebFontRendering::setDirectWriteFactory(factory); | 462 blink::WebFontRendering::setDirectWriteFactory(factory); |
| 204 g_warmup_fontmgr = SkFontMgr_New_DirectWrite(factory); | 463 g_warmup_fontmgr = SkFontMgr_New_DirectWrite(factory); |
| 205 } | 464 } |
| 206 return g_warmup_fontmgr; | 465 return g_warmup_fontmgr; |
| 207 } | 466 } |
| 208 | 467 |
| 468 GdiFontPatchData* PatchGdiFontEnumeration(const base::FilePath& path) { | |
| 469 // We assume the fontmgr is already warmed up before calling this. | |
| 470 DCHECK(g_warmup_fontmgr); | |
| 471 return new GdiFontPatchDataImpl(path); | |
| 472 } | |
| 473 | |
| 474 void SetPreSandboxWarmupFontMgr(SkFontMgr* fontmgr) { | |
| 475 g_warmup_fontmgr = fontmgr; | |
| 476 } | |
| 477 | |
| 209 } // namespace content | 478 } // namespace content |
| OLD | NEW |