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