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