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 |