summaryrefslogtreecommitdiff
path: root/chromium/content/browser/renderer_host/dwrite_font_file_util_win.cc
blob: 855b57c5f3db394ceb19ecf314e09d5fc26fe9c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/renderer_host/dwrite_font_file_util_win.h"

#include <shlobj.h>
#include <wrl.h>
#include <vector>

#include "base/i18n/case_conversion.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "base/trace_event/trace_event.h"
#include "content/browser/renderer_host/dwrite_font_uma_logging_win.h"

namespace content {

bool FontFilePathAndTtcIndex(IDWriteFont* font,
                             base::string16& file_path,
                             uint32_t& ttc_index) {
  Microsoft::WRL::ComPtr<IDWriteFontFace> font_face;
  HRESULT hr;
  hr = font->CreateFontFace(&font_face);
  if (FAILED(hr)) {
    base::UmaHistogramSparse("DirectWrite.Fonts.Proxy.CreateFontFaceResult",
                             hr);
    LogMessageFilterError(
        MessageFilterError::ADD_FILES_FOR_FONT_CREATE_FACE_FAILED);
    return false;
  }
  return FontFilePathAndTtcIndex(font_face.Get(), file_path, ttc_index);
}

bool FontFilePathAndTtcIndex(IDWriteFontFace* font_face,
                             base::string16& file_path,
                             uint32_t& ttc_index) {
  TRACE_EVENT0("dwrite,fonts",
               "dwrite_font_file_util::FontFilePathAndTtcIndex");
  UINT32 file_count;
  HRESULT hr;
  hr = font_face->GetFiles(&file_count, nullptr);
  if (FAILED(hr)) {
    LogMessageFilterError(
        MessageFilterError::ADD_FILES_FOR_FONT_GET_FILE_COUNT_FAILED);
    return false;
  }

  // We've learned from the DirectWrite team at MS that the number of font files
  // retrieved per IDWriteFontFile can only ever be 1. Other font formats such
  // as Type 1, which represent one font in multiple files, are currently not
  // supported in the API (as of December 2018, Windows 10). In Chrome we do not
  // plan to support Type 1 fonts, or generally other font formats different
  // from OpenType, hence no need to loop over file_count or retrieve multiple
  // files.
  DCHECK_EQ(file_count, 1u);
  if (file_count > 1) {
    LogMessageFilterError(
        MessageFilterError::GET_FILE_COUNT_INVALID_NUMBER_OF_FILES);
    return false;
  }

  Microsoft::WRL::ComPtr<IDWriteFontFile> font_file;
  hr = font_face->GetFiles(&file_count, &font_file);
  if (FAILED(hr)) {
    LogMessageFilterError(
        MessageFilterError::ADD_FILES_FOR_FONT_GET_FILES_FAILED);
    return false;
  }

  Microsoft::WRL::ComPtr<IDWriteFontFileLoader> loader;
  hr = font_file->GetLoader(&loader);
  if (FAILED(hr)) {
    LogMessageFilterError(
        MessageFilterError::ADD_FILES_FOR_FONT_GET_LOADER_FAILED);
    return false;
  }

  Microsoft::WRL::ComPtr<IDWriteLocalFontFileLoader> local_loader;
  hr = loader.As(&local_loader);

  if (hr == E_NOINTERFACE) {
    // We could get here if the system font collection contains fonts that
    // are backed by something other than files in the system fonts folder.
    // I don't think that is actually possible, so for now we'll just
    // ignore it (result will be that we'll be unable to match any styles
    // for this font, forcing blink/skia to fall back to whatever font is
    // next). If we get telemetry indicating that this case actually
    // happens, we can implement this by exposing the loader via ipc. That
    // will likely be by loading the font data into shared memory, although
    // we could proxy the stream reads directly instead.
    LogLoaderType(DirectWriteFontLoaderType::OTHER_LOADER);
    DCHECK(false);
    return false;
  } else if (FAILED(hr)) {
    LogMessageFilterError(MessageFilterError::ADD_FILES_FOR_FONT_QI_FAILED);
    return false;
  }

  const void* key;
  UINT32 key_size;
  hr = font_file->GetReferenceKey(&key, &key_size);
  if (FAILED(hr)) {
    LogMessageFilterError(
        MessageFilterError::ADD_LOCAL_FILE_GET_REFERENCE_KEY_FAILED);
    return false;
  }

  UINT32 path_length = 0;
  hr = local_loader->GetFilePathLengthFromKey(key, key_size, &path_length);
  if (FAILED(hr)) {
    LogMessageFilterError(
        MessageFilterError::ADD_LOCAL_FILE_GET_PATH_LENGTH_FAILED);
    return false;
  }
  base::string16 retrieve_file_path;
  retrieve_file_path.resize(
      ++path_length);  // Reserve space for the null terminator.
  hr = local_loader->GetFilePathFromKey(key, key_size, &retrieve_file_path[0],
                                        path_length);
  if (FAILED(hr)) {
    LogMessageFilterError(MessageFilterError::ADD_LOCAL_FILE_GET_PATH_FAILED);
    return false;
  }
  // No need for the null-terminator in base::string16.
  retrieve_file_path.resize(--path_length);

  uint32_t retrieve_ttc_index = font_face->GetIndex();
  if (FAILED(hr)) {
    return false;
  }

  file_path = retrieve_file_path;
  ttc_index = retrieve_ttc_index;

  return true;
}

bool AddFilesForFont(IDWriteFont* font,
                     const base::string16& windows_fonts_path,
                     std::set<base::string16>* path_set,
                     std::set<base::string16>* custom_font_path_set,
                     uint32_t* ttc_index) {
  base::string16 file_path;
  if (!FontFilePathAndTtcIndex(font, file_path, *ttc_index)) {
    return false;
  }

  base::string16 file_path_folded = base::i18n::FoldCase(file_path);

  if (!file_path_folded.size())
    return false;

  if (!base::StartsWith(file_path_folded, windows_fonts_path,
                        base::CompareCase::SENSITIVE)) {
    LogLoaderType(DirectWriteFontLoaderType::FILE_OUTSIDE_SANDBOX);
    custom_font_path_set->insert(file_path);
  } else {
    LogLoaderType(DirectWriteFontLoaderType::FILE_SYSTEM_FONT_DIR);
    path_set->insert(file_path);
  }
  return true;
}

base::string16 GetWindowsFontsPath() {
  std::vector<base::char16> font_path_chars;
  // SHGetSpecialFolderPath requires at least MAX_PATH characters.
  font_path_chars.resize(MAX_PATH);
  BOOL result = SHGetSpecialFolderPath(nullptr /* hwndOwner - reserved */,
                                       font_path_chars.data(), CSIDL_FONTS,
                                       FALSE /* fCreate */);
  DCHECK(result);
  return base::i18n::FoldCase(font_path_chars.data());
}

}  // namespace content