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 |