1 // Copyright 2018 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 "third_party/blink/renderer/platform/fonts/win/font_unique_name_lookup_win.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/files/file_path.h"
11 #include "mojo/public/mojom/base/shared_memory.mojom-blink.h"
12 #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
13 #include "third_party/blink/public/mojom/dwrite_font_proxy/dwrite_font_proxy.mojom-blink.h"
14 #include "third_party/blink/public/platform/platform.h"
15 #include "third_party/blink/renderer/platform/instrumentation/histogram.h"
16 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
17 #include "third_party/skia/include/ports/SkTypeface_win.h"
18
19 namespace {
20
21 // These enum values correspond to the
22 // "Blink.Fonts.WindowsUniqueLocalFontInstantiationResult" histogram, new values
23 // can be added, but old values should never be reused.
24 enum WindowsUniqueLocalFontInstantiationResult {
25 kSuccess = 0,
26 kErrorOutsideWindowsFontsDirectory = 1,
27 kErrorOther = 2,
28 kMaxWindowsUniqueLocalFontInstantiationResult = 3
29 };
30
31 } // namespace
32
33 namespace blink {
34
35 FontUniqueNameLookupWin::FontUniqueNameLookupWin() = default;
36
37 FontUniqueNameLookupWin::~FontUniqueNameLookupWin() = default;
38
MatchUniqueName(const String & font_unique_name)39 sk_sp<SkTypeface> FontUniqueNameLookupWin::MatchUniqueName(
40 const String& font_unique_name) {
41 if (lookup_mode_ == blink::mojom::UniqueFontLookupMode::kSingleLookups)
42 return MatchUniqueNameSingleLookup(font_unique_name);
43 return MatchUniqueNameLookupTable(font_unique_name);
44 }
45
MatchUniqueNameSingleLookup(const String & font_unique_name)46 sk_sp<SkTypeface> FontUniqueNameLookupWin::MatchUniqueNameSingleLookup(
47 const String& font_unique_name) {
48 DCHECK(lookup_mode_ == blink::mojom::UniqueFontLookupMode::kSingleLookups);
49 base::FilePath font_file_path;
50 uint32_t ttc_index = 0;
51
52 EnsureServiceConnected();
53
54 bool matching_mojo_success =
55 service_->MatchUniqueFont(font_unique_name, &font_file_path, &ttc_index);
56 DCHECK(matching_mojo_success);
57
58 if (!font_file_path.value().size())
59 return nullptr;
60
61 return InstantiateFromPathAndTtcIndex(font_file_path, ttc_index);
62 }
63
MatchUniqueNameLookupTable(const String & font_unique_name)64 sk_sp<SkTypeface> FontUniqueNameLookupWin::MatchUniqueNameLookupTable(
65 const String& font_unique_name) {
66 DCHECK(lookup_mode_ == blink::mojom::UniqueFontLookupMode::kRetrieveTable);
67
68 if (!IsFontUniqueNameLookupReadyForSyncLookup())
69 return nullptr;
70
71 base::Optional<FontTableMatcher::MatchResult> match_result =
72 font_table_matcher_->MatchName(font_unique_name.Utf8());
73 if (!match_result)
74 return nullptr;
75
76 base::FilePath file_path =
77 base::FilePath::FromUTF8Unsafe(match_result->font_path.c_str());
78 return InstantiateFromPathAndTtcIndex(file_path, match_result->ttc_index);
79 }
80
InstantiateFromPathAndTtcIndex(base::FilePath font_file_path,uint32_t ttc_index)81 sk_sp<SkTypeface> FontUniqueNameLookupWin::InstantiateFromPathAndTtcIndex(
82 base::FilePath font_file_path,
83 uint32_t ttc_index) {
84 // Record here when a locally uniquely matched font could not be
85 // instantiated. One reason could be that the font was outside the
86 // C:\Windows\Fonts directory and thus not accessible due to sandbox
87 // restrictions.
88 sk_sp<SkTypeface> local_typeface = SkTypeface::MakeFromFile(
89 font_file_path.AsUTF8Unsafe().c_str(), ttc_index);
90
91 WindowsUniqueLocalFontInstantiationResult result = kSuccess;
92
93 // There is a chance that some systems have managed to register fonts into the
94 // Windows system font collection outside the C:\Windows\Fonts directory. For
95 // sandboxing reasons, we are unable to access them here. This histogram
96 // serves to quantify how often this case occurs and whether we need and
97 // additional sandbox helper to open the file handle on the browser process
98 // side.
99 if (!local_typeface) {
100 base::FilePath windows_fonts_path(L"C:\\WINDOWS\\FONTS");
101 if (!windows_fonts_path.IsParent(font_file_path))
102 result = kErrorOutsideWindowsFontsDirectory;
103 else
104 result = kErrorOther;
105 }
106
107 DEFINE_THREAD_SAFE_STATIC_LOCAL(
108 EnumerationHistogram, windows_unique_local_font_instantiation_histogram,
109 ("Blink.Fonts.WindowsUniqueLocalFontInstantiationResult",
110 kMaxWindowsUniqueLocalFontInstantiationResult));
111 windows_unique_local_font_instantiation_histogram.Count(result);
112
113 return local_typeface;
114 }
115
IsFontUniqueNameLookupReadyForSyncLookup()116 bool FontUniqueNameLookupWin::IsFontUniqueNameLookupReadyForSyncLookup() {
117 if (!RuntimeEnabledFeatures::FontSrcLocalMatchingEnabled())
118 return true;
119
120 EnsureServiceConnected();
121
122 if (!lookup_mode_.has_value()) {
123 blink::mojom::UniqueFontLookupMode lookup_mode_from_mojo;
124 service_->GetUniqueFontLookupMode(&lookup_mode_from_mojo);
125 lookup_mode_ = lookup_mode_from_mojo;
126 }
127
128 if (lookup_mode_ == blink::mojom::UniqueFontLookupMode::kSingleLookups) {
129 return true;
130 }
131
132 DCHECK(lookup_mode_ == blink::mojom::UniqueFontLookupMode::kRetrieveTable);
133
134 // If we have the table already, we're ready for sync lookups.
135 if (font_table_matcher_.get())
136 return true;
137
138 // We have previously determined via IPC whether the table is sync available.
139 // Return what we found out before.
140 if (sync_available_.has_value())
141 return sync_available_.value();
142
143 // If we haven't asked the browser before, probe synchronously - if the table
144 // is available on the browser side, we can continue with sync operation.
145
146 bool sync_available_from_mojo = false;
147 base::ReadOnlySharedMemoryRegion shared_memory_region;
148 service_->GetUniqueNameLookupTableIfAvailable(&sync_available_from_mojo,
149 &shared_memory_region);
150 sync_available_ = sync_available_from_mojo;
151
152 if (*sync_available_) {
153 // Adopt the shared memory region, do not notify anyone in callbacks as
154 // PrepareFontUniqueNameLookup must not have been called yet. Just return
155 // true from this function.
156 DCHECK_EQ(pending_callbacks_.size(), 0u);
157 ReceiveReadOnlySharedMemoryRegion(std::move(shared_memory_region));
158 }
159
160 // If it wasn't available synchronously LocalFontFaceSource has to call
161 // PrepareFontUniqueNameLookup.
162 return *sync_available_;
163 }
164
EnsureServiceConnected()165 void FontUniqueNameLookupWin::EnsureServiceConnected() {
166 if (service_)
167 return;
168 Platform::Current()->GetBrowserInterfaceBroker()->GetInterface(
169 service_.BindNewPipeAndPassReceiver());
170 }
171
PrepareFontUniqueNameLookup(NotifyFontUniqueNameLookupReady callback)172 void FontUniqueNameLookupWin::PrepareFontUniqueNameLookup(
173 NotifyFontUniqueNameLookupReady callback) {
174 DCHECK(!font_table_matcher_.get());
175 DCHECK(RuntimeEnabledFeatures::FontSrcLocalMatchingEnabled());
176 DCHECK(lookup_mode_ == blink::mojom::UniqueFontLookupMode::kRetrieveTable);
177
178 pending_callbacks_.push_back(std::move(callback));
179
180 // We bind the service on the first call to PrepareFontUniqueNameLookup. After
181 // that we do not need to make additional IPC requests to retrieve the table.
182 // The observing callback was added to the list, so all clients will be
183 // informed when the lookup table has arrived.
184 if (pending_callbacks_.size() > 1)
185 return;
186
187 EnsureServiceConnected();
188
189 service_->GetUniqueNameLookupTable(base::BindOnce(
190 &FontUniqueNameLookupWin::ReceiveReadOnlySharedMemoryRegion,
191 base::Unretained(this)));
192 }
193
ReceiveReadOnlySharedMemoryRegion(base::ReadOnlySharedMemoryRegion shared_memory_region)194 void FontUniqueNameLookupWin::ReceiveReadOnlySharedMemoryRegion(
195 base::ReadOnlySharedMemoryRegion shared_memory_region) {
196 DCHECK(lookup_mode_ == blink::mojom::UniqueFontLookupMode::kRetrieveTable);
197 font_table_matcher_ =
198 std::make_unique<FontTableMatcher>(shared_memory_region.Map());
199 while (!pending_callbacks_.IsEmpty()) {
200 NotifyFontUniqueNameLookupReady callback = pending_callbacks_.TakeFirst();
201 std::move(callback).Run();
202 }
203 }
204
205 } // namespace blink
206