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_; | |
|
scottmg
2015/09/17 23:24:24
it would pack better on x64 to have magic_ lower d
forshaw
2015/09/18 00:00:28
I could move it, as this class (presumably) has a
| |
| 249 uintptr_t handle_; | |
| 250 skia::RefPtr<SkTypeface> typeface_; | |
| 251 static uintptr_t curr_handle_; | |
|
scottmg
2015/09/17 23:24:25
separate statics from members
forshaw
2015/09/18 00:00:28
Acknowledged.
| |
| 252 static std::list<scoped_refptr<FakeGdiObject>> objects_; | |
|
scottmg
2015/09/17 23:24:24
is a map from handle->FakeGdiObject not sensible f
forshaw
2015/09/18 00:00:28
If I can just use map directly then I guess it sho
| |
| 253 static base::Lock objects_lock_; | |
| 254 FakeGdiObject(uint32_t magic) : magic_(magic), handle_(++curr_handle_) {} | |
| 255 ~FakeGdiObject() {} | |
| 256 friend class base::RefCountedThreadSafe<FakeGdiObject>; | |
|
scottmg
2015/09/17 23:24:24
move friend to first after private:
forshaw
2015/09/18 00:00:28
Acknowledged.
| |
| 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, | |
|
scottmg
2015/09/17 23:24:24
i know they're copied from the declarations, but n
forshaw
2015/09/18 00:00:28
Acknowledged.
| |
| 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| handles lpvBuffer being nullptr. However if it is nullptr | |
| 361 // then set the size to INT32_MAX otherwise |getTableData| will return the | |
| 362 // minimum value between the table entry size and the size passed in. The | |
| 363 // common Windows idiom is to pass 0 as cbSize when passing nullptr, which | |
|
palmer
2015/09/17 21:12:52
Nit: Don't forget to use |...| to distinguish iden
forshaw
2015/09/18 00:00:28
Will do.
| |
| 364 // would in this case result in |getTableData| returning 0 which isn't the | |
| 365 // correct answer for emulating GDI. The dwTable tag must also have its byte | |
| 366 // order swapped to counter the byte order swap which occurs in the method. | |
| 367 size_t length = typeface->getTableData( | |
| 368 base::ByteSwap(base::strict_cast<uint32>(dwTable)), dwOffset, | |
| 369 lpvBuffer ? cbData : INT32_MAX, lpvBuffer); | |
| 370 // We can't distinguish between an empty table and an error. | |
| 371 if (length == 0) | |
| 372 return GDI_ERROR; | |
| 373 | |
| 374 return base::checked_cast<DWORD>(length); | |
| 375 } | |
| 376 | |
| 377 HGDIOBJ WINAPI SelectObjectPatch(HDC hdc, HGDIOBJ hgdiobj) { | |
|
scottmg
2015/09/17 23:24:24
is it never possible for these patches to get call
forshaw
2015/09/18 00:00:28
These patches should only be enabled if the win32k
| |
| 378 scoped_refptr<FakeGdiObject> dcobj = | |
| 379 FakeGdiObject::Validate(hdc, kFakeDCMagic); | |
| 380 if (!dcobj) { | |
| 381 LOG(ERROR) << "Invalid DC Object"; | |
| 382 return nullptr; | |
| 383 } | |
| 384 | |
| 385 scoped_refptr<FakeGdiObject> fontobj = | |
| 386 FakeGdiObject::Validate(hgdiobj, kFakeFontMagic); | |
| 387 if (!fontobj) { | |
| 388 LOG(ERROR) << "Invalid Font Object"; | |
| 389 return nullptr; | |
| 390 } | |
| 391 | |
| 392 // Construct a new fake font object to handle the old font if there's one. | |
| 393 scoped_refptr<FakeGdiObject> newfontobj; | |
| 394 skia::RefPtr<SkTypeface> oldfont = dcobj->GetTypeface(); | |
| 395 if (oldfont) { | |
| 396 newfontobj = FakeGdiObject::Create(kFakeFontMagic); | |
| 397 newfontobj->SetTypeface(oldfont); | |
| 398 } | |
| 399 dcobj->SetTypeface(fontobj->GetTypeface()); | |
| 400 | |
| 401 if (newfontobj) | |
| 402 return static_cast<HGDIOBJ>(newfontobj->GetHandle()); | |
| 403 return nullptr; | |
| 404 } | |
| 405 | |
| 406 void DoSingleGdiPatch(base::win::IATPatchFunction& patch, | |
| 407 const base::FilePath& path, | |
| 408 const char* function_name, | |
| 409 void* new_function) { | |
| 410 DWORD error = patch.Patch(path.value().c_str(), "gdi32.dll", function_name, | |
| 411 new_function); | |
| 412 if (error != 0) | |
| 413 LOG(WARNING) << "Failed patching " << function_name << " " << error; | |
| 414 } | |
| 415 | |
| 416 class GdiFontPatchDataImpl : public content::GdiFontPatchData { | |
| 417 public: | |
| 418 GdiFontPatchDataImpl(const base::FilePath& path); | |
| 419 | |
| 420 private: | |
| 421 base::win::IATPatchFunction create_compatible_dc_patch_; | |
| 422 base::win::IATPatchFunction create_font_indirect_patch_; | |
| 423 base::win::IATPatchFunction create_delete_dc_patch_; | |
| 424 base::win::IATPatchFunction create_delete_object_patch_; | |
| 425 base::win::IATPatchFunction create_enum_font_families_patch_; | |
| 426 base::win::IATPatchFunction create_get_font_data_patch_; | |
| 427 base::win::IATPatchFunction create_select_object_patch_; | |
| 428 }; | |
| 429 | |
| 430 GdiFontPatchDataImpl::GdiFontPatchDataImpl(const base::FilePath& path) { | |
| 431 DoSingleGdiPatch(create_compatible_dc_patch_, path, "CreateCompatibleDC", | |
| 432 CreateCompatibleDCPatch); | |
| 433 DoSingleGdiPatch(create_font_indirect_patch_, path, "CreateFontIndirectW", | |
| 434 CreateFontIndirectWPatch); | |
| 435 DoSingleGdiPatch(create_delete_dc_patch_, path, "DeleteDC", DeleteDCPatch); | |
| 436 DoSingleGdiPatch(create_delete_object_patch_, path, "DeleteObject", | |
| 437 DeleteObjectPatch); | |
| 438 DoSingleGdiPatch(create_enum_font_families_patch_, path, | |
| 439 "EnumFontFamiliesExW", EnumFontFamiliesExWPatch); | |
| 440 DoSingleGdiPatch(create_get_font_data_patch_, path, "GetFontData", | |
| 441 GetFontDataPatch); | |
| 442 DoSingleGdiPatch(create_select_object_patch_, path, "SelectObject", | |
| 443 SelectObjectPatch); | |
| 444 } | |
| 445 | |
| 185 } // namespace | 446 } // namespace |
| 186 | 447 |
| 187 void DoPreSandboxWarmupForTypeface(SkTypeface* typeface) { | 448 void DoPreSandboxWarmupForTypeface(SkTypeface* typeface) { |
| 188 SkPaint paint_warmup; | 449 SkPaint paint_warmup; |
| 189 paint_warmup.setTypeface(typeface); | 450 paint_warmup.setTypeface(typeface); |
| 190 wchar_t glyph = L'S'; | 451 wchar_t glyph = L'S'; |
| 191 paint_warmup.measureText(&glyph, 2); | 452 paint_warmup.measureText(&glyph, 2); |
| 192 } | 453 } |
| 193 | 454 |
| 194 SkFontMgr* GetPreSandboxWarmupFontMgr() { | 455 SkFontMgr* GetPreSandboxWarmupFontMgr() { |
| 195 if (!g_warmup_fontmgr) { | 456 if (!g_warmup_fontmgr) { |
| 196 IDWriteFactory* factory; | 457 IDWriteFactory* factory; |
| 197 CreateDirectWriteFactory(&factory); | 458 CreateDirectWriteFactory(&factory); |
| 198 | 459 |
| 199 GetCustomFontCollection(factory); | 460 GetCustomFontCollection(factory); |
| 200 | 461 |
| 201 PatchDWriteFactory(factory); | 462 PatchDWriteFactory(factory); |
| 202 | 463 |
| 203 blink::WebFontRendering::setDirectWriteFactory(factory); | 464 blink::WebFontRendering::setDirectWriteFactory(factory); |
| 204 g_warmup_fontmgr = SkFontMgr_New_DirectWrite(factory); | 465 g_warmup_fontmgr = SkFontMgr_New_DirectWrite(factory); |
| 205 } | 466 } |
| 206 return g_warmup_fontmgr; | 467 return g_warmup_fontmgr; |
| 207 } | 468 } |
| 208 | 469 |
| 470 GdiFontPatchData* PatchGdiFontEnumeration(const base::FilePath& path) { | |
| 471 // We assume the fontmgr is already warmed up before calling this. | |
| 472 DCHECK(g_warmup_fontmgr); | |
| 473 return new GdiFontPatchDataImpl(path); | |
| 474 } | |
| 475 | |
| 476 void SetPreSandboxWarmupFontMgr(SkFontMgr* fontmgr) { | |
| 477 g_warmup_fontmgr = fontmgr; | |
| 478 } | |
| 479 | |
| 209 } // namespace content | 480 } // namespace content |
| OLD | NEW |