1 /*
2  * Copyright (C) 2007, 2008, 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2013 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "third_party/blink/renderer/core/css/font_face_cache.h"
28 
29 #include "base/atomic_sequence_num.h"
30 #include "third_party/blink/renderer/core/css/css_segmented_font_face.h"
31 #include "third_party/blink/renderer/core/css/css_value_list.h"
32 #include "third_party/blink/renderer/core/loader/resource/font_resource.h"
33 #include "third_party/blink/renderer/platform/font_family_names.h"
34 #include "third_party/blink/renderer/platform/fonts/font_description.h"
35 #include "third_party/blink/renderer/platform/fonts/font_selection_algorithm.h"
36 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
37 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
38 
39 namespace blink {
40 
FontFaceCache()41 FontFaceCache::FontFaceCache() : version_(0) {}
42 
Add(const StyleRuleFontFace * font_face_rule,FontFace * font_face)43 void FontFaceCache::Add(const StyleRuleFontFace* font_face_rule,
44                         FontFace* font_face) {
45   if (!style_rule_to_font_face_.insert(font_face_rule, font_face).is_new_entry)
46     return;
47   AddFontFace(font_face, true);
48 }
49 
AddFontFace(FontFace * font_face,bool css_connected)50 void FontFaceCache::AddFontFace(FontFace* font_face, bool css_connected) {
51   SegmentedFacesByFamily::AddResult capabilities_result =
52       segmented_faces_.insert(font_face->family(), nullptr);
53 
54   if (capabilities_result.is_new_entry) {
55     capabilities_result.stored_value->value =
56         MakeGarbageCollected<CapabilitiesSet>();
57   }
58 
59   DCHECK(font_face->GetFontSelectionCapabilities().IsValid() &&
60          !font_face->GetFontSelectionCapabilities().IsHashTableDeletedValue());
61 
62   CapabilitiesSet::AddResult segmented_font_face_result =
63       capabilities_result.stored_value->value->insert(
64           font_face->GetFontSelectionCapabilities(), nullptr);
65   if (segmented_font_face_result.is_new_entry) {
66     segmented_font_face_result.stored_value->value =
67         MakeGarbageCollected<CSSSegmentedFontFace>(
68             font_face->GetFontSelectionCapabilities());
69   }
70 
71   segmented_font_face_result.stored_value->value->AddFontFace(font_face,
72                                                               css_connected);
73 
74   if (css_connected)
75     css_connected_font_faces_.insert(font_face);
76 
77   font_selection_query_cache_.erase(font_face->family());
78   IncrementVersion();
79 }
80 
Remove(const StyleRuleFontFace * font_face_rule)81 void FontFaceCache::Remove(const StyleRuleFontFace* font_face_rule) {
82   StyleRuleToFontFace::iterator it =
83       style_rule_to_font_face_.find(font_face_rule);
84   if (it != style_rule_to_font_face_.end()) {
85     RemoveFontFace(it->value.Get(), true);
86     style_rule_to_font_face_.erase(it);
87   }
88 }
89 
RemoveFontFace(FontFace * font_face,bool css_connected)90 void FontFaceCache::RemoveFontFace(FontFace* font_face, bool css_connected) {
91   SegmentedFacesByFamily::iterator segmented_faces_iter =
92       segmented_faces_.find(font_face->family());
93   if (segmented_faces_iter == segmented_faces_.end())
94     return;
95 
96   CapabilitiesSet* family_segmented_faces = segmented_faces_iter->value.Get();
97 
98   CapabilitiesSet::iterator family_segmented_faces_iter =
99       family_segmented_faces->find(font_face->GetFontSelectionCapabilities());
100   if (family_segmented_faces_iter == family_segmented_faces->end())
101     return;
102 
103   CSSSegmentedFontFace* segmented_font_face =
104       family_segmented_faces_iter->value;
105   segmented_font_face->RemoveFontFace(font_face);
106   if (segmented_font_face->IsEmpty()) {
107     family_segmented_faces->erase(family_segmented_faces_iter);
108     if (family_segmented_faces->IsEmpty()) {
109       segmented_faces_.erase(segmented_faces_iter);
110     }
111   }
112 
113   font_selection_query_cache_.erase(font_face->family());
114 
115   if (css_connected)
116     css_connected_font_faces_.erase(font_face);
117 
118   IncrementVersion();
119 }
120 
ClearCSSConnected()121 bool FontFaceCache::ClearCSSConnected() {
122   if (style_rule_to_font_face_.IsEmpty())
123     return false;
124   for (const auto& item : style_rule_to_font_face_)
125     RemoveFontFace(item.value.Get(), true);
126   style_rule_to_font_face_.clear();
127   return true;
128 }
129 
ClearAll()130 void FontFaceCache::ClearAll() {
131   if (segmented_faces_.IsEmpty())
132     return;
133 
134   segmented_faces_.clear();
135   font_selection_query_cache_.clear();
136   style_rule_to_font_face_.clear();
137   css_connected_font_faces_.clear();
138   IncrementVersion();
139 }
140 
IncrementVersion()141 void FontFaceCache::IncrementVersion() {
142   // Versions are guaranteed to be monotonically increasing, but not necessary
143   // sequential within a thread.
144   static base::AtomicSequenceNumber g_version;
145   version_ = g_version.GetNext();
146 }
147 
Get(const FontDescription & font_description,const AtomicString & family)148 CSSSegmentedFontFace* FontFaceCache::Get(
149     const FontDescription& font_description,
150     const AtomicString& family) {
151   if (family.IsEmpty())
152     return nullptr;
153 
154   SegmentedFacesByFamily::iterator segmented_faces_for_family =
155       segmented_faces_.find(family);
156   if (segmented_faces_for_family == segmented_faces_.end() ||
157       segmented_faces_for_family->value->IsEmpty())
158     return nullptr;
159 
160   // TODO(crbug.com/1021568): Prevent `system-ui` from matching. Per spec,
161   // generic family names should not match web fonts unless they are quoted.
162   if (family == font_family_names::kSystemUi)
163     return nullptr;
164 
165   auto family_faces = segmented_faces_for_family->value;
166 
167   // Either add or retrieve a cache entry in the selection query cache for the
168   // specified family.
169   FontSelectionQueryCache::AddResult cache_entry_for_family_add =
170       font_selection_query_cache_.insert(
171           family, MakeGarbageCollected<FontSelectionQueryResult>());
172   auto cache_entry_for_family = cache_entry_for_family_add.stored_value->value;
173 
174   const FontSelectionRequest& request =
175       font_description.GetFontSelectionRequest();
176 
177   FontSelectionQueryResult::AddResult face_entry =
178       cache_entry_for_family->insert(request, nullptr);
179   if (!face_entry.is_new_entry)
180     return face_entry.stored_value->value;
181 
182   // If we don't have a previously cached result for this request, we now need
183   // to iterate over all entries in the CapabilitiesSet for one family and
184   // extract the best CSSSegmentedFontFace from those.
185 
186   // The FontSelectionAlgorithm needs to know the boundaries of stretch, style,
187   // range for all the available faces in order to calculate distances
188   // correctly.
189   FontSelectionCapabilities all_faces_boundaries;
190   for (const auto& item : *family_faces) {
191     all_faces_boundaries.Expand(item.value->GetFontSelectionCapabilities());
192   }
193 
194   FontSelectionAlgorithm font_selection_algorithm(request,
195                                                   all_faces_boundaries);
196   for (const auto& item : *family_faces) {
197     const FontSelectionCapabilities& candidate_key = item.key;
198     CSSSegmentedFontFace* candidate_value = item.value;
199     if (!face_entry.stored_value->value ||
200         font_selection_algorithm.IsBetterMatchForRequest(
201             candidate_key,
202             face_entry.stored_value->value->GetFontSelectionCapabilities())) {
203       face_entry.stored_value->value = candidate_value;
204     }
205   }
206   return face_entry.stored_value->value;
207 }
208 
GetNumSegmentedFacesForTesting()209 size_t FontFaceCache::GetNumSegmentedFacesForTesting() {
210   size_t count = 0;
211   for (auto& family_faces : segmented_faces_) {
212     count += family_faces.value->size();
213   }
214   return count;
215 }
216 
Trace(Visitor * visitor) const217 void FontFaceCache::Trace(Visitor* visitor) const {
218   visitor->Trace(segmented_faces_);
219   visitor->Trace(font_selection_query_cache_);
220   visitor->Trace(style_rule_to_font_face_);
221   visitor->Trace(css_connected_font_faces_);
222 }
223 
224 }  // namespace blink
225