1 /*
2 * Copyright (C) 2008 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "third_party/blink/renderer/core/css/css_segmented_font_face.h"
27
28 #include "base/bind.h"
29 #include "base/callback.h"
30 #include "third_party/blink/renderer/core/css/css_font_face.h"
31 #include "third_party/blink/renderer/core/css/css_font_selector.h"
32 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
33 #include "third_party/blink/renderer/platform/fonts/font_description.h"
34 #include "third_party/blink/renderer/platform/fonts/font_face_creation_params.h"
35 #include "third_party/blink/renderer/platform/fonts/segmented_font_data.h"
36 #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
37
38 // See comment below in CSSSegmentedFontFace::GetFontData - the cache from
39 // CSSSegmentedFontFace (which represents a group of @font-face declarations
40 // with identical FontSelectionCapabilities but differing by unicode-range) to
41 // FontData/SegmentedFontData, (i.e. the actual font blobs that can be used for
42 // shaping and painting retrieved from a CSSFontFaceSource) is usually small
43 // (less than a dozen, up to tens) for non-animation-cases, but grows fast to
44 // thousands when animating variable font parameters. Set a limit until we start
45 // dropping cache entries in animation scenarios.
46 static constexpr size_t kFontDataTableMaxSize = 250;
47
48 namespace blink {
49
CSSSegmentedFontFace(FontSelectionCapabilities font_selection_capabilities)50 CSSSegmentedFontFace::CSSSegmentedFontFace(
51 FontSelectionCapabilities font_selection_capabilities)
52 : font_selection_capabilities_(font_selection_capabilities),
53 font_data_table_(kFontDataTableMaxSize),
54 font_faces_(MakeGarbageCollected<FontFaceList>()),
55 approximate_character_count_(0) {}
56
57 CSSSegmentedFontFace::~CSSSegmentedFontFace() = default;
58
PruneTable()59 void CSSSegmentedFontFace::PruneTable() {
60 // Make sure the glyph page tree prunes out all uses of this custom font.
61 if (!font_data_table_.size())
62 return;
63
64 font_data_table_.Clear();
65 }
66
IsValid() const67 bool CSSSegmentedFontFace::IsValid() const {
68 // Valid if at least one font face is valid.
69 return font_faces_->ForEachUntilTrue(
70 WTF::BindRepeating([](Member<FontFace> font_face) -> bool {
71 if (font_face->CssFontFace()->IsValid())
72 return true;
73 return false;
74 }));
75 }
76
FontFaceInvalidated()77 void CSSSegmentedFontFace::FontFaceInvalidated() {
78 PruneTable();
79 }
80
AddFontFace(FontFace * font_face,bool css_connected)81 void CSSSegmentedFontFace::AddFontFace(FontFace* font_face,
82 bool css_connected) {
83 PruneTable();
84 font_face->CssFontFace()->AddSegmentedFontFace(this);
85 font_faces_->Insert(font_face, css_connected);
86 }
87
RemoveFontFace(FontFace * font_face)88 void CSSSegmentedFontFace::RemoveFontFace(FontFace* font_face) {
89 if (!font_faces_->Erase(font_face))
90 return;
91
92 PruneTable();
93 font_face->CssFontFace()->RemoveSegmentedFontFace(this);
94 }
95
GetFontData(const FontDescription & font_description)96 scoped_refptr<FontData> CSSSegmentedFontFace::GetFontData(
97 const FontDescription& font_description) {
98 if (!IsValid())
99 return nullptr;
100
101 const FontSelectionRequest& font_selection_request =
102 font_description.GetFontSelectionRequest();
103 bool is_unique_match = false;
104 FontCacheKey key = font_description.CacheKey(
105 FontFaceCreationParams(), is_unique_match, font_selection_request);
106
107 // font_data_table_ caches FontData and SegmentedFontData instances, which
108 // provide SimpleFontData objects containing FontPlatformData objects. In the
109 // case of variable font animations, the variable instance SkTypeface is
110 // contained in these FontPlatformData objects. In other words, this cache
111 // stores the recently used variable font instances during a variable font
112 // animation. The cache reflects in how many different sizes, synthetic styles
113 // (bold / italic synthetic versions), or for variable fonts, in how many
114 // variable instances (stretch/style/weightand font-variation-setings
115 // variations) the font is instantiated. In non animation scenarios, there is
116 // usually only a small number of FontData/SegmentedFontData instances created
117 // per CSSSegmentedFontFace. Whereas in variable font animations, this number
118 // grows rapidly.
119 scoped_refptr<SegmentedFontData>* cached_font_data =
120 font_data_table_.Get(key);
121 if (cached_font_data && (*cached_font_data) &&
122 (*cached_font_data)->NumFaces())
123 return *cached_font_data;
124
125 scoped_refptr<SegmentedFontData> created_font_data;
126 created_font_data = SegmentedFontData::Create();
127
128 FontDescription requested_font_description(font_description);
129 if (!font_selection_capabilities_.HasRange()) {
130 requested_font_description.SetSyntheticBold(
131 font_selection_capabilities_.weight.maximum < BoldThreshold() &&
132 font_selection_request.weight >= BoldThreshold());
133 requested_font_description.SetSyntheticItalic(
134 font_selection_capabilities_.slope.maximum == NormalSlopeValue() &&
135 font_selection_request.slope == ItalicSlopeValue());
136 }
137
138 font_faces_->ForEachReverse(WTF::BindRepeating(
139 [](const FontDescription& requested_font_description,
140 scoped_refptr<SegmentedFontData> created_font_data,
141 Member<FontFace> font_face) {
142 if (!font_face->CssFontFace()->IsValid())
143 return;
144 if (scoped_refptr<SimpleFontData> face_font_data =
145 font_face->CssFontFace()->GetFontData(
146 requested_font_description)) {
147 DCHECK(!face_font_data->IsSegmented());
148 if (face_font_data->IsCustomFont()) {
149 created_font_data->AppendFace(base::AdoptRef(
150 new FontDataForRangeSet(std::move(face_font_data),
151 font_face->CssFontFace()->Ranges())));
152 } else {
153 created_font_data->AppendFace(
154 base::AdoptRef(new FontDataForRangeSetFromCache(
155 std::move(face_font_data),
156 font_face->CssFontFace()->Ranges())));
157 }
158 }
159 },
160 requested_font_description, created_font_data));
161
162 if (created_font_data->NumFaces()) {
163 scoped_refptr<SegmentedFontData> put_to_cache(created_font_data);
164 font_data_table_.Put(std::move(key), std::move(put_to_cache));
165 // No release, we have a reference to an object in the cache which should
166 // retain the ref count it has.
167 return created_font_data;
168 }
169
170 return nullptr;
171 }
172
WillUseFontData(const FontDescription & font_description,const String & text)173 void CSSSegmentedFontFace::WillUseFontData(
174 const FontDescription& font_description,
175 const String& text) {
176 approximate_character_count_ += text.length();
177 font_faces_->ForEachReverseUntilTrue(WTF::BindRepeating(
178 [](const FontDescription& font_description, const String& text,
179 Member<FontFace> font_face) -> bool {
180 if (font_face->LoadStatus() != FontFace::kUnloaded)
181 return true;
182 if (font_face->CssFontFace()->MaybeLoadFont(font_description, text))
183 return true;
184 return false;
185 },
186 font_description, text));
187 }
188
WillUseRange(const blink::FontDescription & font_description,const blink::FontDataForRangeSet & range_set)189 void CSSSegmentedFontFace::WillUseRange(
190 const blink::FontDescription& font_description,
191 const blink::FontDataForRangeSet& range_set) {
192 // Iterating backwards since later defined unicode-range faces override
193 // previously defined ones, according to the CSS3 fonts module.
194 // https://drafts.csswg.org/css-fonts/#composite-fonts
195 font_faces_->ForEachReverseUntilTrue(WTF::BindRepeating(
196 [](const blink::FontDescription& font_description,
197 const blink::FontDataForRangeSet& range_set,
198 Member<FontFace> font_face) -> bool {
199 CSSFontFace* css_font_face = font_face->CssFontFace();
200 if (css_font_face->MaybeLoadFont(font_description, range_set))
201 return true;
202 return false;
203 },
204 font_description, range_set));
205 }
206
CheckFont(const String & text) const207 bool CSSSegmentedFontFace::CheckFont(const String& text) const {
208 return font_faces_->ForEachUntilFalse(WTF::BindRepeating(
209 [](const String& text, Member<FontFace> font_face) -> bool {
210 if (font_face->LoadStatus() != FontFace::kLoaded &&
211 font_face->CssFontFace()->Ranges()->IntersectsWith(text))
212 return false;
213 return true;
214 },
215 text));
216 }
217
Match(const String & text,HeapVector<Member<FontFace>> * faces) const218 void CSSSegmentedFontFace::Match(const String& text,
219 HeapVector<Member<FontFace>>* faces) const {
220 // WTF::BindRepeating requires WrapPersistent around |faces|, which is fine,
221 // because the wrap's lifetime is contained to this function.
222 font_faces_->ForEach(WTF::BindRepeating(
223 [](const String& text, HeapVector<Member<FontFace>>* faces,
224 Member<FontFace> font_face) {
225 if (font_face->CssFontFace()->Ranges()->IntersectsWith(text))
226 faces->push_back(font_face);
227 },
228 text, WrapPersistent(faces)));
229 }
230
Trace(Visitor * visitor) const231 void CSSSegmentedFontFace::Trace(Visitor* visitor) const {
232 visitor->Trace(font_faces_);
233 }
234
IsEmpty() const235 bool FontFaceList::IsEmpty() const {
236 return css_connected_face_.IsEmpty() && non_css_connected_face_.IsEmpty();
237 }
238
Insert(FontFace * font_face,bool css_connected)239 void FontFaceList::Insert(FontFace* font_face, bool css_connected) {
240 if (css_connected) {
241 css_connected_face_.insert(font_face);
242 } else {
243 non_css_connected_face_.insert(font_face);
244 }
245 }
246
Erase(FontFace * font_face)247 bool FontFaceList::Erase(FontFace* font_face) {
248 FontFaceListPart::iterator it = css_connected_face_.find(font_face);
249 if (it != css_connected_face_.end()) {
250 css_connected_face_.erase(it);
251 return true;
252 }
253 it = non_css_connected_face_.find(font_face);
254 if (it != non_css_connected_face_.end()) {
255 non_css_connected_face_.erase(it);
256 return true;
257 }
258 return false;
259 }
260
261 // A callback that will be fed into |ForEach*UntilTrue|.
262 // |callback| wants the |ForEach*UntilFalse| operation to stop iterating on
263 // false, so its return value has to be negated in order for it to work with
264 // |ForEach*UntilTrue|.
NegatingCallback(const base::RepeatingCallback<bool (Member<FontFace>)> & callback,Member<FontFace> font_face)265 static bool NegatingCallback(
266 const base::RepeatingCallback<bool(Member<FontFace>)>& callback,
267 Member<FontFace> font_face) {
268 return !callback.Run(font_face);
269 }
270
271 // A callback that will be fed into |ForEach*UntilTrue|, when we want it to
272 // always continue iterating, so false has to be always returned.
FalseReturningCallback(const base::RepeatingCallback<void (Member<FontFace>)> & callback,Member<FontFace> font_face)273 static bool FalseReturningCallback(
274 const base::RepeatingCallback<void(Member<FontFace>)>& callback,
275 Member<FontFace> font_face) {
276 callback.Run(font_face);
277 return false;
278 }
279
ForEachUntilTrue(const base::RepeatingCallback<bool (Member<FontFace>)> & callback) const280 bool FontFaceList::ForEachUntilTrue(
281 const base::RepeatingCallback<bool(Member<FontFace>)>& callback) const {
282 for (Member<FontFace> font_face : css_connected_face_) {
283 if (callback.Run(font_face))
284 return true;
285 }
286 for (Member<FontFace> font_face : non_css_connected_face_) {
287 if (callback.Run(font_face))
288 return true;
289 }
290 return false;
291 }
292
ForEachUntilFalse(const base::RepeatingCallback<bool (Member<FontFace>)> & callback) const293 bool FontFaceList::ForEachUntilFalse(
294 const base::RepeatingCallback<bool(Member<FontFace>)>& callback) const {
295 base::RepeatingCallback<bool(Member<FontFace>)> negating_callback =
296 WTF::BindRepeating(NegatingCallback, callback);
297 // When |callback| returns |false|, |ForEachUntilTrue(negating_callback)| will
298 // stop iterating and return |true|, so negate it.
299 return !ForEachUntilTrue(negating_callback);
300 }
301
ForEach(const base::RepeatingCallback<void (Member<FontFace>)> & func) const302 void FontFaceList::ForEach(
303 const base::RepeatingCallback<void(Member<FontFace>)>& func) const {
304 base::RepeatingCallback<bool(Member<FontFace>)> false_returning_callback =
305 WTF::BindRepeating(FalseReturningCallback, func);
306 ForEachUntilTrue(false_returning_callback);
307 }
308
ForEachReverseUntilTrue(const base::RepeatingCallback<bool (Member<FontFace>)> & func) const309 bool FontFaceList::ForEachReverseUntilTrue(
310 const base::RepeatingCallback<bool(Member<FontFace>)>& func) const {
311 for (auto it = non_css_connected_face_.rbegin();
312 it != non_css_connected_face_.rend(); ++it) {
313 if (func.Run(*it))
314 return true;
315 }
316 for (auto it = css_connected_face_.rbegin(); it != css_connected_face_.rend();
317 ++it) {
318 if (func.Run(*it))
319 return true;
320 }
321 return false;
322 }
323
ForEachReverseUntilFalse(const base::RepeatingCallback<bool (Member<FontFace>)> & callback) const324 bool FontFaceList::ForEachReverseUntilFalse(
325 const base::RepeatingCallback<bool(Member<FontFace>)>& callback) const {
326 base::RepeatingCallback<bool(Member<FontFace>)> negating_callback =
327 WTF::BindRepeating(NegatingCallback, callback);
328 // When |callback| returns |false|,
329 // |ForEachReverseUntilTrue(negating_callback)| will stop iterating and return
330 // |true|, so negate it.
331 return !ForEachReverseUntilTrue(negating_callback);
332 }
333
ForEachReverse(const base::RepeatingCallback<void (Member<FontFace>)> & callback) const334 void FontFaceList::ForEachReverse(
335 const base::RepeatingCallback<void(Member<FontFace>)>& callback) const {
336 base::RepeatingCallback<bool(Member<FontFace>)> false_returning_callback =
337 WTF::BindRepeating(FalseReturningCallback, callback);
338 ForEachReverseUntilTrue(false_returning_callback);
339 }
340
Trace(Visitor * visitor) const341 void FontFaceList::Trace(Visitor* visitor) const {
342 visitor->Trace(css_connected_face_);
343 visitor->Trace(non_css_connected_face_);
344 }
345
346 } // namespace blink
347