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/renderer/renderer_font_platform_win.h"
6
7#include <dwrite.h>
8#include <string>
9#include <vector>
10#include <wrl/implements.h>
11#include <wrl/wrappers/corewrappers.h>
12
13#include "base/debug/alias.h"
14#include "base/debug/crash_logging.h"
15#include "base/files/file_enumerator.h"
16#include "base/files/file_path.h"
17#include "base/files/memory_mapped_file.h"
18#include "base/memory/scoped_ptr.h"
19#include "base/memory/scoped_vector.h"
20#include "base/metrics/histogram.h"
21#include "base/path_service.h"
22#include "base/strings/utf_string_conversions.h"
23#include "base/time/time.h"
24#include "base/win/iat_patch_function.h"
25#include "base/win/registry.h"
26#include "base/win/scoped_comptr.h"
27
28namespace {
29
30namespace mswr = Microsoft::WRL;
31namespace mswrw = Microsoft::WRL::Wrappers;
32
33static const char kFontKeyName[] = "font_key_name";
34
35class FontCollectionLoader
36    : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
37                                IDWriteFontCollectionLoader> {
38 public:
39  // IDWriteFontCollectionLoader methods.
40  virtual HRESULT STDMETHODCALLTYPE
41      CreateEnumeratorFromKey(IDWriteFactory* factory,
42                              void const* key,
43                              UINT32 key_size,
44                              IDWriteFontFileEnumerator** file_enumerator);
45
46  static HRESULT Initialize(IDWriteFactory* factory);
47
48  UINT32 GetFontMapSize();
49
50  std::wstring GetFontNameFromKey(UINT32 idx);
51
52  bool LoadFontListFromRegistry();
53  bool LoadRestrictedFontList();
54
55  FontCollectionLoader() {};
56  virtual ~FontCollectionLoader() {};
57
58 private:
59  mswr::ComPtr<IDWriteFontFileLoader> file_loader_;
60
61  std::vector<std::wstring> reg_fonts_;
62};
63
64mswr::ComPtr<FontCollectionLoader> g_font_loader;
65
66class FontFileStream
67    : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
68                                IDWriteFontFileStream> {
69 public:
70  // IDWriteFontFileStream methods.
71  virtual HRESULT STDMETHODCALLTYPE
72  ReadFileFragment(void const** fragment_start,
73                   UINT64 file_offset,
74                   UINT64 fragment_size,
75                   void** context) {
76    if (!memory_.get() || !memory_->IsValid() ||
77        file_offset >= memory_->length() ||
78        (file_offset + fragment_size) > memory_->length())
79      return E_FAIL;
80
81    *fragment_start = static_cast<BYTE const*>(memory_->data()) +
82                      static_cast<size_t>(file_offset);
83    *context = NULL;
84    return S_OK;
85  }
86
87  virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* context) {}
88
89  virtual HRESULT STDMETHODCALLTYPE GetFileSize(UINT64* file_size) {
90    if (!memory_.get() || !memory_->IsValid())
91      return E_FAIL;
92
93    *file_size = memory_->length();
94    return S_OK;
95  }
96
97  virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(UINT64* last_write_time) {
98    if (!memory_.get() || !memory_->IsValid())
99      return E_FAIL;
100
101    // According to MSDN article http://goo.gl/rrSYzi the "last modified time"
102    // is used by DirectWrite font selection algorithms to determine whether
103    // one font resource is more up to date than another one.
104    // So by returning 0 we are assuming that it will treat all fonts to be
105    // equally up to date.
106    // TODO(shrikant): We should further investigate this.
107    *last_write_time = 0;
108    return S_OK;
109  }
110
111  FontFileStream::FontFileStream() : font_key_(0) {
112  };
113
114  HRESULT RuntimeClassInitialize(UINT32 font_key) {
115    base::FilePath path;
116    PathService::Get(base::DIR_WINDOWS_FONTS, &path);
117    std::wstring font_key_name(g_font_loader->GetFontNameFromKey(font_key));
118    path = path.Append(font_key_name.c_str());
119    memory_.reset(new base::MemoryMappedFile());
120
121    // Put some debug information on stack.
122    WCHAR font_name[256];
123    path.value().copy(font_name, arraysize(font_name));
124    base::debug::Alias(font_name);
125
126    if (!memory_->Initialize(path)) {
127      memory_.reset();
128      return E_FAIL;
129    }
130
131    font_key_ = font_key;
132
133    base::debug::SetCrashKeyValue(kFontKeyName,
134                                  base::WideToUTF8(font_key_name));
135    return S_OK;
136  }
137
138  virtual ~FontFileStream() {}
139
140  UINT32 font_key_;
141  scoped_ptr<base::MemoryMappedFile> memory_;
142};
143
144class FontFileLoader
145    : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
146                                IDWriteFontFileLoader> {
147 public:
148  // IDWriteFontFileLoader methods.
149  virtual HRESULT STDMETHODCALLTYPE
150  CreateStreamFromKey(void const* ref_key,
151                      UINT32 ref_key_size,
152                      IDWriteFontFileStream** stream) {
153    if (ref_key_size != sizeof(UINT32))
154      return E_FAIL;
155
156    UINT32 font_key = *static_cast<const UINT32*>(ref_key);
157    mswr::ComPtr<FontFileStream> font_stream;
158    HRESULT hr = mswr::MakeAndInitialize<FontFileStream>(&font_stream,
159                                                         font_key);
160    if (SUCCEEDED(hr)) {
161      *stream = font_stream.Detach();
162      return S_OK;
163    }
164    return E_FAIL;
165  }
166
167  FontFileLoader() {}
168  virtual ~FontFileLoader() {}
169};
170
171class FontFileEnumerator
172    : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>,
173                                IDWriteFontFileEnumerator> {
174 public:
175  // IDWriteFontFileEnumerator methods.
176  virtual HRESULT STDMETHODCALLTYPE MoveNext(BOOL* has_current_file) {
177    *has_current_file = FALSE;
178
179    if (current_file_)
180      current_file_.ReleaseAndGetAddressOf();
181
182    if (font_idx_ < g_font_loader->GetFontMapSize()) {
183      HRESULT hr =
184          factory_->CreateCustomFontFileReference(&font_idx_,
185                                                  sizeof(UINT32),
186                                                  file_loader_.Get(),
187                                                  current_file_.GetAddressOf());
188      DCHECK(SUCCEEDED(hr));
189      *has_current_file = TRUE;
190      font_idx_++;
191    }
192    return S_OK;
193  }
194
195  virtual HRESULT STDMETHODCALLTYPE
196  GetCurrentFontFile(IDWriteFontFile** font_file) {
197    if (!current_file_) {
198      *font_file = NULL;
199      return E_FAIL;
200    }
201
202    *font_file = current_file_.Detach();
203    return S_OK;
204  }
205
206  FontFileEnumerator(const void* keys,
207                     UINT32 buffer_size,
208                     IDWriteFactory* factory,
209                     IDWriteFontFileLoader* file_loader)
210      : factory_(factory), file_loader_(file_loader), font_idx_(0) {}
211
212  virtual ~FontFileEnumerator() {}
213
214  mswr::ComPtr<IDWriteFactory> factory_;
215  mswr::ComPtr<IDWriteFontFile> current_file_;
216  mswr::ComPtr<IDWriteFontFileLoader> file_loader_;
217  UINT32 font_idx_;
218};
219
220// IDWriteFontCollectionLoader methods.
221HRESULT STDMETHODCALLTYPE FontCollectionLoader::CreateEnumeratorFromKey(
222    IDWriteFactory* factory,
223    void const* key,
224    UINT32 key_size,
225    IDWriteFontFileEnumerator** file_enumerator) {
226  *file_enumerator = mswr::Make<FontFileEnumerator>(
227                         key, key_size, factory, file_loader_.Get()).Detach();
228  return S_OK;
229}
230
231// static
232HRESULT FontCollectionLoader::Initialize(IDWriteFactory* factory) {
233  DCHECK(g_font_loader == NULL);
234
235  g_font_loader = mswr::Make<FontCollectionLoader>();
236  if (!g_font_loader) {
237    DCHECK(FALSE);
238    return E_FAIL;
239  }
240
241  CHECK(g_font_loader->LoadFontListFromRegistry());
242
243  g_font_loader->file_loader_ = mswr::Make<FontFileLoader>().Detach();
244
245  factory->RegisterFontFileLoader(g_font_loader->file_loader_.Get());
246  factory->RegisterFontCollectionLoader(g_font_loader.Get());
247
248  return S_OK;
249}
250
251UINT32 FontCollectionLoader::GetFontMapSize() {
252  return reg_fonts_.size();
253}
254
255std::wstring FontCollectionLoader::GetFontNameFromKey(UINT32 idx) {
256  DCHECK(idx < reg_fonts_.size());
257  return reg_fonts_[idx];
258}
259
260bool FontCollectionLoader::LoadFontListFromRegistry() {
261  const wchar_t kFontsRegistry[] =
262      L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
263  CHECK(reg_fonts_.empty());
264  base::win::RegKey regkey;
265  if (regkey.Open(HKEY_LOCAL_MACHINE, kFontsRegistry, KEY_READ) !=
266      ERROR_SUCCESS) {
267    return false;
268  }
269
270  base::FilePath system_font_path;
271  PathService::Get(base::DIR_WINDOWS_FONTS, &system_font_path);
272
273  std::wstring name;
274  std::wstring value;
275  for (DWORD idx = 0; idx < regkey.GetValueCount(); idx++) {
276    if (regkey.GetValueNameAt(idx, &name) == ERROR_SUCCESS &&
277        regkey.ReadValue(name.c_str(), &value) == ERROR_SUCCESS) {
278      base::FilePath path(value.c_str());
279      // We need to check if file name is the only component that exists,
280      // we will ignore all other registry entries.
281      std::vector<base::FilePath::StringType> components;
282      path.GetComponents(&components);
283      if (components.size() == 1 ||
284          base::FilePath::CompareEqualIgnoreCase(system_font_path.value(),
285                                                 path.DirName().value())) {
286        reg_fonts_.push_back(path.BaseName().value());
287      }
288    }
289  }
290  UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Loaded", reg_fonts_.size());
291  UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Ignored",
292                       regkey.GetValueCount() - reg_fonts_.size());
293  return true;
294}
295
296// This list is mainly based on prefs/prefs_tab_helper.cc kFontDefaults.
297const wchar_t* kRestrictedFontSet[] = {
298  // These are the "Web Safe" fonts.
299  L"times.ttf",     // IDS_STANDARD_FONT_FAMILY
300  L"timesbd.ttf",   // IDS_STANDARD_FONT_FAMILY
301  L"timesbi.ttf",   // IDS_STANDARD_FONT_FAMILY
302  L"timesi.ttf",    // IDS_STANDARD_FONT_FAMILY
303  L"cour.ttf",      // IDS_FIXED_FONT_FAMILY
304  L"courbd.ttf",    // IDS_FIXED_FONT_FAMILY
305  L"courbi.ttf",    // IDS_FIXED_FONT_FAMILY
306  L"couri.ttf",     // IDS_FIXED_FONT_FAMILY
307  L"consola.ttf",   // IDS_FIXED_FONT_FAMILY_ALT_WIN
308  L"consolab.ttf",  // IDS_FIXED_FONT_FAMILY_ALT_WIN
309  L"consolai.ttf",  // IDS_FIXED_FONT_FAMILY_ALT_WIN
310  L"consolaz.ttf",  // IDS_FIXED_FONT_FAMILY_ALT_WIN
311  L"arial.ttf",     // IDS_SANS_SERIF_FONT_FAMILY
312  L"arialbd.ttf",   // IDS_SANS_SERIF_FONT_FAMILY
313  L"arialbi.ttf",   // IDS_SANS_SERIF_FONT_FAMILY
314  L"ariali.ttf",    // IDS_SANS_SERIF_FONT_FAMILY
315  L"comic.ttf",     // IDS_CURSIVE_FONT_FAMILY
316  L"comicbd.ttf",   // IDS_CURSIVE_FONT_FAMILY
317  L"comici.ttf",    // IDS_CURSIVE_FONT_FAMILY
318  L"comicz.ttf",    // IDS_CURSIVE_FONT_FAMILY
319  L"impact.ttf",    // IDS_FANTASY_FONT_FAMILY
320  L"segoeui.ttf",   // IDS_PICTOGRAPH_FONT_FAMILY
321  L"segoeuib.ttf",  // IDS_PICTOGRAPH_FONT_FAMILY
322  L"segoeuii.ttf",  // IDS_PICTOGRAPH_FONT_FAMILY
323  L"msgothic.ttc",  // IDS_STANDARD_FONT_FAMILY_JAPANESE
324  L"msmincho.ttc",  // IDS_SERIF_FONT_FAMILY_JAPANESE
325  L"gulim.ttc",     // IDS_FIXED_FONT_FAMILY_KOREAN
326  L"batang.ttc",    // IDS_SERIF_FONT_FAMILY_KOREAN
327  L"simsun.ttc",    // IDS_STANDARD_FONT_FAMILY_SIMPLIFIED_HAN
328  L"mingliu.ttc",   // IDS_SERIF_FONT_FAMILY_TRADITIONAL_HAN
329
330  // These are from the Blink fallback list.
331  L"david.ttf",     // USCRIPT_HEBREW
332  L"davidbd.ttf",   // USCRIPT_HEBREW
333  L"euphemia.ttf",  // USCRIPT_CANADIAN_ABORIGINAL
334  L"gautami.ttf",   // USCRIPT_TELUGU
335  L"gautamib.ttf",  // USCRIPT_TELUGU
336  L"latha.ttf",     // USCRIPT_TAMIL
337  L"lathab.ttf",    // USCRIPT_TAMIL
338  L"mangal.ttf",    // USCRIPT_DEVANAGARI
339  L"mangalb.ttf",   // USCRIPT_DEVANAGARI
340  L"monbaiti.ttf",  // USCRIPT_MONGOLIAN
341  L"mvboli.ttf",    // USCRIPT_THAANA
342  L"plantc.ttf",    // USCRIPT_CHEROKEE
343  L"raavi.ttf",     // USCRIPT_GURMUKHI
344  L"raavib.ttf",    // USCRIPT_GURMUKHI
345  L"shruti.ttf",    // USCRIPT_GUJARATI
346  L"shrutib.ttf",   // USCRIPT_GUJARATI
347  L"sylfaen.ttf",   // USCRIPT_GEORGIAN and USCRIPT_ARMENIAN
348  L"tahoma.ttf",    // USCRIPT_ARABIC,
349  L"tahomabd.ttf",  // USCRIPT_ARABIC,
350  L"tunga.ttf",     // USCRIPT_KANNADA
351  L"tungab.ttf",    // USCRIPT_KANNADA
352  L"vrinda.ttf",    // USCRIPT_BENGALI
353  L"vrindab.ttf",   // USCRIPT_BENGALI
354};
355
356bool FontCollectionLoader::LoadRestrictedFontList() {
357  reg_fonts_.clear();
358  reg_fonts_.assign(kRestrictedFontSet,
359                    kRestrictedFontSet + _countof(kRestrictedFontSet));
360  return true;
361}
362
363}  // namespace
364
365namespace content {
366
367mswr::ComPtr<IDWriteFontCollection> g_font_collection;
368
369IDWriteFontCollection* GetCustomFontCollection(IDWriteFactory* factory) {
370  if (g_font_collection.Get() != NULL)
371    return g_font_collection.Get();
372
373  base::TimeTicks start_tick = base::TimeTicks::Now();
374
375  FontCollectionLoader::Initialize(factory);
376
377  // We try here to put arbitrary limit on max number of fonts that could
378  // be loaded, otherwise we fallback to restricted set of fonts.
379  const UINT32 kMaxFontThreshold = 1750;
380  HRESULT hr = E_FAIL;
381  if (g_font_loader->GetFontMapSize() < kMaxFontThreshold) {
382    hr = factory->CreateCustomFontCollection(
383        g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf());
384  }
385
386  bool loadingRestricted = false;
387  if (FAILED(hr) || !g_font_collection.Get()) {
388    // We will try here just one more time with restricted font set.
389    g_font_loader->LoadRestrictedFontList();
390    hr = factory->CreateCustomFontCollection(
391        g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf());
392  }
393
394  base::TimeDelta time_delta = base::TimeTicks::Now() - start_tick;
395  int64 delta = time_delta.ToInternalValue();
396  base::debug::Alias(&delta);
397  UINT32 size = g_font_loader->GetFontMapSize();
398  base::debug::Alias(&size);
399  base::debug::Alias(&loadingRestricted);
400
401  CHECK(SUCCEEDED(hr));
402  CHECK(g_font_collection.Get() != NULL);
403
404  UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime", time_delta);
405
406  base::debug::ClearCrashKey(kFontKeyName);
407
408  return g_font_collection.Get();
409}
410
411}  // namespace content
412