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