Home | History | Annotate | Download | only in renderer
      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
      3 // found in the LICENSE file.
      4 
      5 #include "content/public/renderer/render_font_warmup_win.h"
      6 
      7 #include <dwrite.h>
      8 
      9 #include "base/logging.h"
     10 #include "base/win/iat_patch_function.h"
     11 #include "base/win/windows_version.h"
     12 #include "third_party/WebKit/public/web/win/WebFontRendering.h"
     13 #include "third_party/skia/include/core/SkPaint.h"
     14 #include "third_party/skia/include/ports/SkFontMgr.h"
     15 #include "third_party/skia/include/ports/SkTypeface_win.h"
     16 
     17 namespace content {
     18 
     19 namespace {
     20 
     21 SkFontMgr* g_warmup_fontmgr = NULL;
     22 
     23 base::win::IATPatchFunction g_iat_patch_open_sc_manager;
     24 base::win::IATPatchFunction g_iat_patch_close_service_handle;
     25 base::win::IATPatchFunction g_iat_patch_open_service;
     26 base::win::IATPatchFunction g_iat_patch_start_service;
     27 base::win::IATPatchFunction g_iat_patch_nt_connect_port;
     28 
     29 // These are from ntddk.h
     30 #if !defined(STATUS_ACCESS_DENIED)
     31 #define STATUS_ACCESS_DENIED   ((NTSTATUS)0xC0000022L)
     32 #endif
     33 
     34 typedef LONG NTSTATUS;
     35 
     36 SC_HANDLE WINAPI OpenSCManagerWPatch(const wchar_t* machine_name,
     37                                      const wchar_t* database_name,
     38                                      DWORD access_mask) {
     39   ::SetLastError(0);
     40   return reinterpret_cast<SC_HANDLE>(0xdeadbeef);
     41 }
     42 
     43 SC_HANDLE WINAPI OpenServiceWPatch(SC_HANDLE sc_manager,
     44                                    const wchar_t* service_name,
     45                                    DWORD access_mask) {
     46   ::SetLastError(0);
     47   return reinterpret_cast<SC_HANDLE>(0xdeadbabe);
     48 }
     49 
     50 BOOL WINAPI CloseServiceHandlePatch(SC_HANDLE service_handle) {
     51   if (service_handle != reinterpret_cast<SC_HANDLE>(0xdeadbabe) &&
     52       service_handle != reinterpret_cast<SC_HANDLE>(0xdeadbeef))
     53     CHECK(false);
     54   ::SetLastError(0);
     55   return TRUE;
     56 }
     57 
     58 BOOL WINAPI StartServiceWPatch(SC_HANDLE service,
     59                                DWORD args,
     60                                const wchar_t** arg_vectors) {
     61   if (service != reinterpret_cast<SC_HANDLE>(0xdeadbabe))
     62     CHECK(false);
     63   ::SetLastError(ERROR_ACCESS_DENIED);
     64   return FALSE;
     65 }
     66 
     67 NTSTATUS WINAPI NtALpcConnectPortPatch(HANDLE* port_handle,
     68                                        void* port_name,
     69                                        void* object_attribs,
     70                                        void* port_attribs,
     71                                        DWORD flags,
     72                                        void* server_sid,
     73                                        void* message,
     74                                        DWORD* buffer_length,
     75                                        void* out_message_attributes,
     76                                        void* in_message_attributes,
     77                                        void* time_out) {
     78   return STATUS_ACCESS_DENIED;
     79 }
     80 
     81 // Directwrite connects to the font cache service to retrieve information about
     82 // fonts installed on the system etc. This works well outside the sandbox and
     83 // within the sandbox as long as the lpc connection maintained by the current
     84 // process with the font cache service remains valid. It appears that there
     85 // are cases when this connection is dropped after which directwrite is unable
     86 // to connect to the font cache service which causes problems with characters
     87 // disappearing.
     88 // Directwrite has fallback code to enumerate fonts if it is unable to connect
     89 // to the font cache service. We need to intercept the following APIs to
     90 // ensure that it does not connect to the font cache service.
     91 // NtALpcConnectPort
     92 // OpenSCManagerW
     93 // OpenServiceW
     94 // StartServiceW
     95 // CloseServiceHandle.
     96 // These are all IAT patched.
     97 void PatchServiceManagerCalls() {
     98   static bool is_patched = false;
     99   if (is_patched)
    100     return;
    101   const char* service_provider_dll =
    102       (base::win::GetVersion() >= base::win::VERSION_WIN8 ?
    103           "api-ms-win-service-management-l1-1-0.dll" : "advapi32.dll");
    104 
    105   is_patched = true;
    106 
    107   DWORD patched = g_iat_patch_open_sc_manager.Patch(L"dwrite.dll",
    108       service_provider_dll, "OpenSCManagerW", OpenSCManagerWPatch);
    109   DCHECK(patched == 0);
    110 
    111   patched = g_iat_patch_close_service_handle.Patch(L"dwrite.dll",
    112       service_provider_dll, "CloseServiceHandle", CloseServiceHandlePatch);
    113   DCHECK(patched == 0);
    114 
    115   patched = g_iat_patch_open_service.Patch(L"dwrite.dll",
    116       service_provider_dll, "OpenServiceW", OpenServiceWPatch);
    117   DCHECK(patched == 0);
    118 
    119   patched = g_iat_patch_start_service.Patch(L"dwrite.dll",
    120       service_provider_dll, "StartServiceW", StartServiceWPatch);
    121   DCHECK(patched == 0);
    122 
    123   patched = g_iat_patch_nt_connect_port.Patch(L"dwrite.dll",
    124       "ntdll.dll", "NtAlpcConnectPort", NtALpcConnectPortPatch);
    125   DCHECK(patched == 0);
    126 }
    127 
    128 
    129 // Windows-only DirectWrite support. These warm up the DirectWrite paths
    130 // before sandbox lock down to allow Skia access to the Font Manager service.
    131 void CreateDirectWriteFactory(IDWriteFactory** factory) {
    132   typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc;
    133   PatchServiceManagerCalls();
    134 
    135   DWriteCreateFactoryProc dwrite_create_factory_proc =
    136       reinterpret_cast<DWriteCreateFactoryProc>(
    137           GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory"));
    138   CHECK(dwrite_create_factory_proc);
    139   CHECK(SUCCEEDED(
    140       dwrite_create_factory_proc(DWRITE_FACTORY_TYPE_ISOLATED,
    141                                  __uuidof(IDWriteFactory),
    142                                  reinterpret_cast<IUnknown**>(factory))));
    143 }
    144 
    145 }  // namespace
    146 
    147 void DoPreSandboxWarmupForTypeface(SkTypeface* typeface) {
    148   SkPaint paint_warmup;
    149   paint_warmup.setTypeface(typeface);
    150   wchar_t glyph = L'S';
    151   paint_warmup.measureText(&glyph, 2);
    152 }
    153 
    154 SkFontMgr* GetPreSandboxWarmupFontMgr() {
    155   if (!g_warmup_fontmgr) {
    156     IDWriteFactory* factory;
    157     CreateDirectWriteFactory(&factory);
    158     blink::WebFontRendering::setDirectWriteFactory(factory);
    159     g_warmup_fontmgr = SkFontMgr_New_DirectWrite(factory);
    160   }
    161   return g_warmup_fontmgr;
    162 }
    163 
    164 }  // namespace content
    165