1 | /*
|
---|
2 | * Copyright (C) 2016 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 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 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 "config.h"
|
---|
27 | #include "CSSFontFaceSet.h"
|
---|
28 |
|
---|
29 | #include "CSSFontFaceSource.h"
|
---|
30 | #include "CSSFontFamily.h"
|
---|
31 | #include "CSSFontSelector.h"
|
---|
32 | #include "CSSFontStyleValue.h"
|
---|
33 | #include "CSSParser.h"
|
---|
34 | #include "CSSPrimitiveValue.h"
|
---|
35 | #include "CSSPropertyParserHelpers.h"
|
---|
36 | #include "CSSPropertyParserWorkerSafe.h"
|
---|
37 | #include "CSSSegmentedFontFace.h"
|
---|
38 | #include "CSSValueList.h"
|
---|
39 | #include "CSSValuePool.h"
|
---|
40 | #include "FontCache.h"
|
---|
41 | #include "StyleBuilderConverter.h"
|
---|
42 | #include "StyleProperties.h"
|
---|
43 |
|
---|
44 | namespace WebCore {
|
---|
45 |
|
---|
46 | CSSFontFaceSet::CSSFontFaceSet(CSSFontSelector* owningFontSelector)
|
---|
47 | : m_owningFontSelector(owningFontSelector)
|
---|
48 | {
|
---|
49 | }
|
---|
50 |
|
---|
51 | CSSFontFaceSet::~CSSFontFaceSet()
|
---|
52 | {
|
---|
53 | for (auto& face : m_faces)
|
---|
54 | face->removeClient(*this);
|
---|
55 |
|
---|
56 | for (auto& pair : m_locallyInstalledFacesLookupTable) {
|
---|
57 | for (auto& face : pair.value)
|
---|
58 | face->removeClient(*this);
|
---|
59 | }
|
---|
60 | }
|
---|
61 |
|
---|
62 | void CSSFontFaceSet::addFontModifiedObserver(const FontModifiedObserver& fontModifiedObserver)
|
---|
63 | {
|
---|
64 | auto result = m_fontModifiedObservers.add(fontModifiedObserver);
|
---|
65 | ASSERT_UNUSED(result, result.isNewEntry);
|
---|
66 | }
|
---|
67 |
|
---|
68 | void CSSFontFaceSet::addFontEventClient(const FontEventClient& fontEventClient)
|
---|
69 | {
|
---|
70 | auto result = m_fontEventClients.add(fontEventClient);
|
---|
71 | ASSERT_UNUSED(result, result.isNewEntry);
|
---|
72 | }
|
---|
73 |
|
---|
74 | void CSSFontFaceSet::incrementActiveCount()
|
---|
75 | {
|
---|
76 | ++m_activeCount;
|
---|
77 | if (m_activeCount == 1) {
|
---|
78 | m_status = Status::Loading;
|
---|
79 | m_fontEventClients.forEach([] (auto& client) {
|
---|
80 | client.startedLoading();
|
---|
81 | });
|
---|
82 | }
|
---|
83 | }
|
---|
84 |
|
---|
85 | void CSSFontFaceSet::decrementActiveCount()
|
---|
86 | {
|
---|
87 | --m_activeCount;
|
---|
88 | if (!m_activeCount) {
|
---|
89 | m_status = Status::Loaded;
|
---|
90 | m_fontEventClients.forEach([] (auto& client) {
|
---|
91 | client.completedLoading();
|
---|
92 | });
|
---|
93 | }
|
---|
94 | }
|
---|
95 |
|
---|
96 | bool CSSFontFaceSet::hasFace(const CSSFontFace& face) const
|
---|
97 | {
|
---|
98 | for (auto& myFace : m_faces) {
|
---|
99 | if (myFace.ptr() == &face)
|
---|
100 | return true;
|
---|
101 | }
|
---|
102 |
|
---|
103 | return false;
|
---|
104 | }
|
---|
105 |
|
---|
106 | // Calling updateStyleIfNeeded() might delete |this|.
|
---|
107 | void CSSFontFaceSet::updateStyleIfNeeded()
|
---|
108 | {
|
---|
109 | if (m_owningFontSelector)
|
---|
110 | m_owningFontSelector->updateStyleIfNeeded();
|
---|
111 | }
|
---|
112 |
|
---|
113 | void CSSFontFaceSet::ensureLocalFontFacesForFamilyRegistered(const String& familyName)
|
---|
114 | {
|
---|
115 | ASSERT(m_owningFontSelector);
|
---|
116 | if (m_locallyInstalledFacesLookupTable.contains(familyName))
|
---|
117 | return;
|
---|
118 |
|
---|
119 | if (!m_owningFontSelector->scriptExecutionContext())
|
---|
120 | return;
|
---|
121 | AllowUserInstalledFonts allowUserInstalledFonts = m_owningFontSelector->scriptExecutionContext()->settingsValues().shouldAllowUserInstalledFonts ? AllowUserInstalledFonts::Yes : AllowUserInstalledFonts::No;
|
---|
122 | Vector<FontSelectionCapabilities> capabilities = FontCache::forCurrentThread().getFontSelectionCapabilitiesInFamily(AtomString { familyName }, allowUserInstalledFonts);
|
---|
123 | if (capabilities.isEmpty())
|
---|
124 | return;
|
---|
125 |
|
---|
126 | Vector<Ref<CSSFontFace>> faces;
|
---|
127 | for (auto item : capabilities) {
|
---|
128 | auto face = CSSFontFace::create(*m_owningFontSelector, nullptr, nullptr, true);
|
---|
129 |
|
---|
130 | Ref<CSSValueList> familyList = CSSValueList::createCommaSeparated();
|
---|
131 | familyList->append(m_owningFontSelector->scriptExecutionContext()->cssValuePool().createFontFamilyValue(familyName));
|
---|
132 | face->setFamilies(familyList.get());
|
---|
133 | face->setFontSelectionCapabilities(item);
|
---|
134 | face->adoptSource(makeUnique<CSSFontFaceSource>(face.get(), familyName));
|
---|
135 | ASSERT(!face->computeFailureState());
|
---|
136 | faces.append(WTFMove(face));
|
---|
137 | }
|
---|
138 | m_locallyInstalledFacesLookupTable.add(familyName, WTFMove(faces));
|
---|
139 | }
|
---|
140 |
|
---|
141 | String CSSFontFaceSet::familyNameFromPrimitive(const CSSPrimitiveValue& value)
|
---|
142 | {
|
---|
143 | if (value.isFontFamily())
|
---|
144 | return value.fontFamily().familyName;
|
---|
145 | if (!value.isValueID())
|
---|
146 | return { };
|
---|
147 |
|
---|
148 | // We need to use the raw text for all the generic family types, since @font-face is a way of actually
|
---|
149 | // defining what font to use for those types.
|
---|
150 | switch (value.valueID()) {
|
---|
151 | case CSSValueSerif:
|
---|
152 | return serifFamily.get();
|
---|
153 | case CSSValueSansSerif:
|
---|
154 | return sansSerifFamily.get();
|
---|
155 | case CSSValueCursive:
|
---|
156 | return cursiveFamily.get();
|
---|
157 | case CSSValueFantasy:
|
---|
158 | return fantasyFamily.get();
|
---|
159 | case CSSValueMonospace:
|
---|
160 | return monospaceFamily.get();
|
---|
161 | case CSSValueWebkitPictograph:
|
---|
162 | return pictographFamily.get();
|
---|
163 | case CSSValueSystemUi:
|
---|
164 | return systemUiFamily.get();
|
---|
165 | default:
|
---|
166 | return { };
|
---|
167 | }
|
---|
168 | }
|
---|
169 |
|
---|
170 | void CSSFontFaceSet::addToFacesLookupTable(CSSFontFace& face)
|
---|
171 | {
|
---|
172 | if (!face.families()) {
|
---|
173 | // If the font has failed, there's no point in actually adding it to m_facesLookupTable,
|
---|
174 | // because no font requests can actually use it for anything. So, let's just ... not add it.
|
---|
175 | return;
|
---|
176 | }
|
---|
177 | auto families = face.families().value();
|
---|
178 |
|
---|
179 | for (auto& item : *families) {
|
---|
180 | String familyName = CSSFontFaceSet::familyNameFromPrimitive(downcast<CSSPrimitiveValue>(item.get()));
|
---|
181 | if (familyName.isEmpty())
|
---|
182 | continue;
|
---|
183 |
|
---|
184 | auto addResult = m_facesLookupTable.add(familyName, Vector<Ref<CSSFontFace>>());
|
---|
185 | auto& familyFontFaces = addResult.iterator->value;
|
---|
186 | if (addResult.isNewEntry) {
|
---|
187 | // m_locallyInstalledFontFaces grows without bound, eventually encorporating every font installed on the system.
|
---|
188 | // This is by design.
|
---|
189 | if (m_owningFontSelector)
|
---|
190 | ensureLocalFontFacesForFamilyRegistered(familyName);
|
---|
191 | familyFontFaces = { };
|
---|
192 | }
|
---|
193 |
|
---|
194 | familyFontFaces.append(face);
|
---|
195 | }
|
---|
196 | }
|
---|
197 |
|
---|
198 | void CSSFontFaceSet::add(CSSFontFace& face)
|
---|
199 | {
|
---|
200 | ASSERT(!hasFace(face));
|
---|
201 |
|
---|
202 | m_fontModifiedObservers.forEach([] (auto& observer) {
|
---|
203 | observer();
|
---|
204 | });
|
---|
205 |
|
---|
206 | face.addClient(*this);
|
---|
207 | m_cache.clear();
|
---|
208 |
|
---|
209 | if (face.cssConnection())
|
---|
210 | m_faces.insert(m_facesPartitionIndex++, face);
|
---|
211 | else
|
---|
212 | m_faces.append(face);
|
---|
213 |
|
---|
214 | addToFacesLookupTable(face);
|
---|
215 |
|
---|
216 | if (face.status() == CSSFontFace::Status::Loading || face.status() == CSSFontFace::Status::TimedOut)
|
---|
217 | incrementActiveCount();
|
---|
218 |
|
---|
219 | if (face.cssConnection()) {
|
---|
220 | ASSERT(!m_constituentCSSConnections.contains(face.cssConnection()));
|
---|
221 | m_constituentCSSConnections.add(face.cssConnection(), &face);
|
---|
222 | }
|
---|
223 | }
|
---|
224 |
|
---|
225 | void CSSFontFaceSet::removeFromFacesLookupTable(const CSSFontFace& face, const CSSValueList& familiesToSearchFor)
|
---|
226 | {
|
---|
227 | for (auto& item : familiesToSearchFor) {
|
---|
228 | String familyName = CSSFontFaceSet::familyNameFromPrimitive(downcast<CSSPrimitiveValue>(item.get()));
|
---|
229 | if (familyName.isEmpty())
|
---|
230 | continue;
|
---|
231 |
|
---|
232 | auto iterator = m_facesLookupTable.find(familyName);
|
---|
233 | if (iterator == m_facesLookupTable.end()) {
|
---|
234 | // The font may have failed even before addToFacesLookupTable() was called on it,
|
---|
235 | // which means we never added it (because there's no point in adding a failed font).
|
---|
236 | // So, if it was never added, removing it is free! Woohoo!
|
---|
237 | return;
|
---|
238 | }
|
---|
239 | bool found = false;
|
---|
240 | for (size_t i = 0; i < iterator->value.size(); ++i) {
|
---|
241 | if (iterator->value[i].ptr() == &face) {
|
---|
242 | found = true;
|
---|
243 | iterator->value.remove(i);
|
---|
244 | break;
|
---|
245 | }
|
---|
246 | }
|
---|
247 | ASSERT_UNUSED(found, found);
|
---|
248 | if (!iterator->value.size())
|
---|
249 | m_facesLookupTable.remove(iterator);
|
---|
250 | }
|
---|
251 | }
|
---|
252 |
|
---|
253 | void CSSFontFaceSet::remove(const CSSFontFace& face)
|
---|
254 | {
|
---|
255 | Ref protect { face };
|
---|
256 |
|
---|
257 | m_cache.clear();
|
---|
258 |
|
---|
259 | m_fontModifiedObservers.forEach([] (auto& observer) {
|
---|
260 | observer();
|
---|
261 | });
|
---|
262 |
|
---|
263 | if (face.families())
|
---|
264 | removeFromFacesLookupTable(face, *face.families().value());
|
---|
265 |
|
---|
266 | if (face.cssConnection()) {
|
---|
267 | ASSERT(m_constituentCSSConnections.get(face.cssConnection()) == &face);
|
---|
268 | m_constituentCSSConnections.remove(face.cssConnection());
|
---|
269 | }
|
---|
270 |
|
---|
271 | for (size_t i = 0; i < m_faces.size(); ++i) {
|
---|
272 | if (m_faces[i].ptr() == &face) {
|
---|
273 | if (i < m_facesPartitionIndex)
|
---|
274 | --m_facesPartitionIndex;
|
---|
275 | m_faces[i]->removeClient(*this);
|
---|
276 | m_faces.remove(i);
|
---|
277 | if (face.status() == CSSFontFace::Status::Loading || face.status() == CSSFontFace::Status::TimedOut)
|
---|
278 | decrementActiveCount();
|
---|
279 | return;
|
---|
280 | }
|
---|
281 | }
|
---|
282 | ASSERT_NOT_REACHED();
|
---|
283 | }
|
---|
284 |
|
---|
285 | CSSFontFace* CSSFontFaceSet::lookUpByCSSConnection(StyleRuleFontFace& target)
|
---|
286 | {
|
---|
287 | return m_constituentCSSConnections.get(&target);
|
---|
288 | }
|
---|
289 |
|
---|
290 | void CSSFontFaceSet::purge()
|
---|
291 | {
|
---|
292 | Vector<Ref<CSSFontFace>> toRemove;
|
---|
293 | for (auto& face : m_faces) {
|
---|
294 | if (face->purgeable())
|
---|
295 | toRemove.append(face.copyRef());
|
---|
296 | }
|
---|
297 |
|
---|
298 | for (auto& item : toRemove)
|
---|
299 | remove(item.get());
|
---|
300 | }
|
---|
301 |
|
---|
302 | void CSSFontFaceSet::emptyCaches()
|
---|
303 | {
|
---|
304 | m_cache.clear();
|
---|
305 | }
|
---|
306 |
|
---|
307 | void CSSFontFaceSet::clear()
|
---|
308 | {
|
---|
309 | for (auto& face : m_faces)
|
---|
310 | face->removeClient(*this);
|
---|
311 | m_faces.clear();
|
---|
312 | m_facesLookupTable.clear();
|
---|
313 | m_locallyInstalledFacesLookupTable.clear();
|
---|
314 | m_cache.clear();
|
---|
315 | m_constituentCSSConnections.clear();
|
---|
316 | m_facesPartitionIndex = 0;
|
---|
317 | m_status = Status::Loaded;
|
---|
318 | }
|
---|
319 |
|
---|
320 | CSSFontFace& CSSFontFaceSet::operator[](size_t i)
|
---|
321 | {
|
---|
322 | ASSERT(i < faceCount());
|
---|
323 | return m_faces[i];
|
---|
324 | }
|
---|
325 |
|
---|
326 | static FontSelectionRequest computeFontSelectionRequest(CSSPropertyParserHelpers::FontRaw& font)
|
---|
327 | {
|
---|
328 | auto weightSelectionValue = font.weight
|
---|
329 | ? WTF::switchOn(*font.weight, [&] (CSSValueID keyword) {
|
---|
330 | switch (keyword) {
|
---|
331 | case CSSValueNormal:
|
---|
332 | return normalWeightValue();
|
---|
333 | case CSSValueBold:
|
---|
334 | case CSSValueBolder:
|
---|
335 | return boldWeightValue();
|
---|
336 | case CSSValueLighter:
|
---|
337 | return lightWeightValue();
|
---|
338 | default:
|
---|
339 | ASSERT_NOT_REACHED();
|
---|
340 | return normalWeightValue();
|
---|
341 | }
|
---|
342 | }, [&] (double weight) {
|
---|
343 | return FontSelectionValue::clampFloat(weight);
|
---|
344 | }) : normalWeightValue();
|
---|
345 |
|
---|
346 | // Because this is a FontRaw, we know we should be able to dereference stretchSelectionValue as
|
---|
347 | // consumeFontStretchKeywordValueRaw only returns results valid to pass to fontStretchValue.
|
---|
348 | auto stretchSelectionValue = fontStretchValue(font.stretch.value_or(CSSValueNormal));
|
---|
349 | ASSERT(stretchSelectionValue);
|
---|
350 |
|
---|
351 | auto styleKeyword = font.style ? font.style->style : CSSValueNormal;
|
---|
352 | auto styleSelectionValue = [&] () -> std::optional<FontSelectionValue> {
|
---|
353 | if (styleKeyword == CSSValueNormal)
|
---|
354 | return std::nullopt;
|
---|
355 | if (styleKeyword == CSSValueItalic)
|
---|
356 | return italicValue();
|
---|
357 | ASSERT(font.style && styleKeyword == CSSValueOblique);
|
---|
358 | float degrees = 0;
|
---|
359 | if (font.style->angle)
|
---|
360 | degrees = static_cast<float>(CSSPrimitiveValue::computeDegrees(font.style->angle->type, font.style->angle->value));
|
---|
361 | return FontSelectionValue(degrees);
|
---|
362 | }();
|
---|
363 |
|
---|
364 | return { weightSelectionValue, *stretchSelectionValue, styleSelectionValue };
|
---|
365 | }
|
---|
366 |
|
---|
367 | using CodePointsMap = HashSet<UChar32, DefaultHash<UChar32>, WTF::UnsignedWithZeroKeyHashTraits<UChar32>>;
|
---|
368 | static CodePointsMap codePointsFromString(StringView stringView)
|
---|
369 | {
|
---|
370 | CodePointsMap result;
|
---|
371 | auto graphemeClusters = stringView.graphemeClusters();
|
---|
372 | for (auto cluster : graphemeClusters) {
|
---|
373 | ASSERT(cluster.length() > 0);
|
---|
374 | UChar32 character = 0;
|
---|
375 | if (cluster.is8Bit())
|
---|
376 | character = cluster[0];
|
---|
377 | else
|
---|
378 | U16_GET(cluster.characters16(), 0, 0, cluster.length(), character);
|
---|
379 | result.add(character);
|
---|
380 | }
|
---|
381 | return result;
|
---|
382 | }
|
---|
383 |
|
---|
384 | ExceptionOr<Vector<std::reference_wrapper<CSSFontFace>>> CSSFontFaceSet::matchingFacesExcludingPreinstalledFonts(const String& fontShorthand, const String& string)
|
---|
385 | {
|
---|
386 | auto font = CSSPropertyParserWorkerSafe::parseFont(fontShorthand, HTMLStandardMode);
|
---|
387 | if (!font)
|
---|
388 | return Exception { SyntaxError };
|
---|
389 |
|
---|
390 | HashSet<AtomString> uniqueFamilies;
|
---|
391 | Vector<AtomString> familyOrder;
|
---|
392 | for (auto& familyRaw : font->family) {
|
---|
393 | AtomString familyAtom;
|
---|
394 | WTF::switchOn(familyRaw, [&] (CSSValueID familyKeyword) {
|
---|
395 | if (familyKeyword != CSSValueWebkitBody)
|
---|
396 | familyAtom = familyNamesData->at(CSSPropertyParserHelpers::genericFontFamilyIndex(familyKeyword));
|
---|
397 | else {
|
---|
398 | ASSERT(m_owningFontSelector && m_owningFontSelector->scriptExecutionContext());
|
---|
399 | familyAtom = AtomString { m_owningFontSelector->scriptExecutionContext()->settingsValues().fontGenericFamilies.standardFontFamily() };
|
---|
400 | }
|
---|
401 | }, [&] (const AtomString& familyString) {
|
---|
402 | familyAtom = familyString;
|
---|
403 | });
|
---|
404 |
|
---|
405 | if (!familyAtom.isEmpty() && uniqueFamilies.add(familyAtom).isNewEntry)
|
---|
406 | familyOrder.append(familyAtom);
|
---|
407 | }
|
---|
408 |
|
---|
409 | HashSet<CSSFontFace*> resultConstituents;
|
---|
410 | auto request = computeFontSelectionRequest(*font);
|
---|
411 | for (auto codePoint : codePointsFromString(string)) {
|
---|
412 | bool found = false;
|
---|
413 | for (auto& family : familyOrder) {
|
---|
414 | auto* faces = fontFace(request, family);
|
---|
415 | if (!faces)
|
---|
416 | continue;
|
---|
417 | for (auto& constituentFace : faces->constituentFaces()) {
|
---|
418 | if (constituentFace->isLocalFallback())
|
---|
419 | continue;
|
---|
420 | if (constituentFace->rangesMatchCodePoint(codePoint)) {
|
---|
421 | resultConstituents.add(constituentFace.ptr());
|
---|
422 | found = true;
|
---|
423 | break;
|
---|
424 | }
|
---|
425 | }
|
---|
426 | if (found)
|
---|
427 | break;
|
---|
428 | }
|
---|
429 | }
|
---|
430 |
|
---|
431 | return WTF::map(resultConstituents, [](auto* constituent) -> std::reference_wrapper<CSSFontFace> {
|
---|
432 | return *constituent;
|
---|
433 | });
|
---|
434 | }
|
---|
435 |
|
---|
436 | ExceptionOr<bool> CSSFontFaceSet::check(const String& font, const String& text)
|
---|
437 | {
|
---|
438 | auto matchingFaces = this->matchingFacesExcludingPreinstalledFonts(font, text);
|
---|
439 | if (matchingFaces.hasException())
|
---|
440 | return matchingFaces.releaseException();
|
---|
441 |
|
---|
442 | for (auto& face : matchingFaces.releaseReturnValue()) {
|
---|
443 | if (face.get().status() == CSSFontFace::Status::Pending
|
---|
444 | || face.get().status() == CSSFontFace::Status::Loading)
|
---|
445 | return false;
|
---|
446 | }
|
---|
447 | return true;
|
---|
448 | }
|
---|
449 |
|
---|
450 | CSSSegmentedFontFace* CSSFontFaceSet::fontFace(FontSelectionRequest request, const AtomString& family)
|
---|
451 | {
|
---|
452 | auto iterator = m_facesLookupTable.find(family);
|
---|
453 | if (iterator == m_facesLookupTable.end())
|
---|
454 | return nullptr;
|
---|
455 | auto& familyFontFaces = iterator->value;
|
---|
456 |
|
---|
457 | auto& segmentedFontFaceCache = m_cache.add(family, FontSelectionHashMap()).iterator->value;
|
---|
458 |
|
---|
459 | auto& face = segmentedFontFaceCache.add(request, nullptr).iterator->value;
|
---|
460 | if (face)
|
---|
461 | return face.get();
|
---|
462 |
|
---|
463 | face = CSSSegmentedFontFace::create();
|
---|
464 |
|
---|
465 | Vector<std::reference_wrapper<CSSFontFace>, 32> candidateFontFaces;
|
---|
466 | for (int i = familyFontFaces.size() - 1; i >= 0; --i) {
|
---|
467 | CSSFontFace& candidate = familyFontFaces[i];
|
---|
468 | if (auto capabilities = candidate.fontSelectionCapabilities()) {
|
---|
469 | if (!isItalic(request.slope) && isItalic(capabilities->slope.minimum))
|
---|
470 | continue;
|
---|
471 | candidateFontFaces.append(candidate);
|
---|
472 | }
|
---|
473 | }
|
---|
474 |
|
---|
475 | auto localIterator = m_locallyInstalledFacesLookupTable.find(family);
|
---|
476 | if (localIterator != m_locallyInstalledFacesLookupTable.end()) {
|
---|
477 | for (auto& candidate : localIterator->value) {
|
---|
478 | if (auto capabilities = candidate->fontSelectionCapabilities()) {
|
---|
479 | if (!isItalic(request.slope) && isItalic(capabilities->slope.minimum))
|
---|
480 | continue;
|
---|
481 | candidateFontFaces.append(candidate);
|
---|
482 | }
|
---|
483 | }
|
---|
484 | }
|
---|
485 |
|
---|
486 | if (!candidateFontFaces.isEmpty()) {
|
---|
487 | auto capabilities = candidateFontFaces.map([](auto& face) {
|
---|
488 | return *face.get().fontSelectionCapabilities();
|
---|
489 | });
|
---|
490 | FontSelectionAlgorithm fontSelectionAlgorithm(request, capabilities);
|
---|
491 | std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), [&fontSelectionAlgorithm](const CSSFontFace& first, const CSSFontFace& second) {
|
---|
492 | auto firstCapabilities = first.fontSelectionCapabilities();
|
---|
493 | auto secondCapabilities = second.fontSelectionCapabilities();
|
---|
494 |
|
---|
495 | auto stretchDistanceFirst = fontSelectionAlgorithm.stretchDistance(*firstCapabilities).distance;
|
---|
496 | auto stretchDistanceSecond = fontSelectionAlgorithm.stretchDistance(*secondCapabilities).distance;
|
---|
497 | if (stretchDistanceFirst < stretchDistanceSecond)
|
---|
498 | return true;
|
---|
499 | if (stretchDistanceFirst > stretchDistanceSecond)
|
---|
500 | return false;
|
---|
501 |
|
---|
502 | auto styleDistanceFirst = fontSelectionAlgorithm.styleDistance(*firstCapabilities).distance;
|
---|
503 | auto styleDistanceSecond = fontSelectionAlgorithm.styleDistance(*secondCapabilities).distance;
|
---|
504 | if (styleDistanceFirst < styleDistanceSecond)
|
---|
505 | return true;
|
---|
506 | if (styleDistanceFirst > styleDistanceSecond)
|
---|
507 | return false;
|
---|
508 |
|
---|
509 | auto weightDistanceFirst = fontSelectionAlgorithm.weightDistance(*firstCapabilities).distance;
|
---|
510 | auto weightDistanceSecond = fontSelectionAlgorithm.weightDistance(*secondCapabilities).distance;
|
---|
511 | if (weightDistanceFirst < weightDistanceSecond)
|
---|
512 | return true;
|
---|
513 | return false;
|
---|
514 | });
|
---|
515 | CSSFontFace* previousCandidate = nullptr;
|
---|
516 | for (auto& candidate : candidateFontFaces) {
|
---|
517 | if (&candidate.get() == previousCandidate)
|
---|
518 | continue;
|
---|
519 | previousCandidate = &candidate.get();
|
---|
520 | face->appendFontFace(candidate.get());
|
---|
521 | }
|
---|
522 | }
|
---|
523 |
|
---|
524 | return face.get();
|
---|
525 | }
|
---|
526 |
|
---|
527 | void CSSFontFaceSet::fontStateChanged(CSSFontFace& face, CSSFontFace::Status oldState, CSSFontFace::Status newState)
|
---|
528 | {
|
---|
529 | ASSERT(hasFace(face));
|
---|
530 | if (oldState == CSSFontFace::Status::Pending) {
|
---|
531 | ASSERT(newState == CSSFontFace::Status::Loading);
|
---|
532 | incrementActiveCount();
|
---|
533 | }
|
---|
534 | if (newState == CSSFontFace::Status::Success || newState == CSSFontFace::Status::Failure) {
|
---|
535 | ASSERT(oldState == CSSFontFace::Status::Loading || oldState == CSSFontFace::Status::TimedOut);
|
---|
536 | m_fontEventClients.forEach([&] (auto& client) {
|
---|
537 | client.faceFinished(face, newState);
|
---|
538 | });
|
---|
539 | decrementActiveCount();
|
---|
540 | }
|
---|
541 | }
|
---|
542 |
|
---|
543 | void CSSFontFaceSet::fontPropertyChanged(CSSFontFace& face, CSSValueList* oldFamilies)
|
---|
544 | {
|
---|
545 | m_cache.clear();
|
---|
546 |
|
---|
547 | if (oldFamilies) {
|
---|
548 | removeFromFacesLookupTable(face, *oldFamilies);
|
---|
549 | addToFacesLookupTable(face);
|
---|
550 | }
|
---|
551 |
|
---|
552 | m_fontModifiedObservers.forEach([] (auto& observer) {
|
---|
553 | observer();
|
---|
554 | });
|
---|
555 | }
|
---|
556 |
|
---|
557 | }
|
---|