2 * Copyright (C) 2016 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "CSSFontFaceSet.h"
29 #include "CSSFontFaceSource.h"
30 #include "CSSFontFamily.h"
31 #include "CSSFontSelector.h"
32 #include "CSSParser.h"
33 #include "CSSPrimitiveValue.h"
34 #include "CSSSegmentedFontFace.h"
35 #include "CSSValueList.h"
36 #include "CSSValuePool.h"
37 #include "FontCache.h"
38 #include "StyleProperties.h"
42 CSSFontFaceSet::CSSFontFaceSet()
46 CSSFontFaceSet::~CSSFontFaceSet()
48 for (auto& face : m_faces)
49 face->removeClient(*this);
51 for (auto& pair : m_locallyInstalledFacesLookupTable) {
52 for (auto& face : pair.value)
53 face->removeClient(*this);
57 void CSSFontFaceSet::addClient(CSSFontFaceSetClient& client)
59 m_clients.add(&client);
62 void CSSFontFaceSet::removeClient(CSSFontFaceSetClient& client)
64 ASSERT(m_clients.contains(&client));
65 m_clients.remove(&client);
68 void CSSFontFaceSet::incrementActiveCount()
71 if (m_activeCount == 1) {
72 m_status = Status::Loading;
73 for (auto* client : m_clients)
74 client->startedLoading();
78 void CSSFontFaceSet::decrementActiveCount()
82 m_status = Status::Loaded;
83 for (auto* client : m_clients)
84 client->completedLoading();
88 bool CSSFontFaceSet::hasFace(const CSSFontFace& face) const
90 for (auto& myFace : m_faces) {
91 if (myFace.ptr() == &face)
98 void CSSFontFaceSet::registerLocalFontFacesForFamily(const String& familyName)
100 ASSERT(!m_locallyInstalledFacesLookupTable.contains(familyName));
102 Vector<FontTraitsMask> traitsMasks = FontCache::singleton().getTraitsInFamily(familyName);
103 if (traitsMasks.isEmpty())
106 Vector<Ref<CSSFontFace>> faces;
107 for (auto mask : traitsMasks) {
108 Ref<CSSFontFace> face = CSSFontFace::create(nullptr, nullptr, nullptr, true);
110 Ref<CSSValueList> familyList = CSSValueList::createCommaSeparated();
111 familyList->append(CSSValuePool::singleton().createFontFamilyValue(familyName));
112 face->setFamilies(familyList.get());
113 face->setTraitsMask(mask);
114 face->adoptSource(std::make_unique<CSSFontFaceSource>(face.get(), familyName));
115 ASSERT(!face->allSourcesFailed());
116 faces.append(WTFMove(face));
118 m_locallyInstalledFacesLookupTable.add(familyName, WTFMove(faces));
121 String CSSFontFaceSet::familyNameFromPrimitive(const CSSPrimitiveValue& value)
123 if (value.isFontFamily())
124 return value.fontFamily().familyName;
125 if (!value.isValueID())
128 // We need to use the raw text for all the generic family types, since @font-face is a way of actually
129 // defining what font to use for those types.
130 switch (value.getValueID()) {
133 case CSSValueSansSerif:
134 return sansSerifFamily;
135 case CSSValueCursive:
136 return cursiveFamily;
137 case CSSValueFantasy:
138 return fantasyFamily;
139 case CSSValueMonospace:
140 return monospaceFamily;
141 case CSSValueWebkitPictograph:
142 return pictographFamily;
148 void CSSFontFaceSet::addToFacesLookupTable(CSSFontFace& face)
150 if (!face.families())
153 for (auto& item : *face.families()) {
154 String familyName = CSSFontFaceSet::familyNameFromPrimitive(downcast<CSSPrimitiveValue>(item.get()));
155 if (familyName.isEmpty())
158 auto addResult = m_facesLookupTable.add(familyName, Vector<Ref<CSSFontFace>>());
159 auto& familyFontFaces = addResult.iterator->value;
160 if (addResult.isNewEntry) {
161 // m_locallyInstalledFontFaces grows without bound, eventually encorporating every font installed on the system.
162 // This is by design.
163 registerLocalFontFacesForFamily(familyName);
164 familyFontFaces = { };
167 familyFontFaces.append(face);
171 void CSSFontFaceSet::add(CSSFontFace& face)
173 ASSERT(!hasFace(face));
175 for (auto* client : m_clients)
176 client->fontModified();
178 face.addClient(*this);
181 if (face.cssConnection())
182 m_faces.insert(m_facesPartitionIndex++, face);
184 m_faces.append(face);
186 addToFacesLookupTable(face);
188 if (face.status() == CSSFontFace::Status::Loading || face.status() == CSSFontFace::Status::TimedOut)
189 incrementActiveCount();
192 void CSSFontFaceSet::removeFromFacesLookupTable(const CSSFontFace& face, const CSSValueList& familiesToSearchFor)
194 for (auto& item : familiesToSearchFor) {
195 String familyName = CSSFontFaceSet::familyNameFromPrimitive(downcast<CSSPrimitiveValue>(item.get()));
196 if (familyName.isEmpty())
199 auto iterator = m_facesLookupTable.find(familyName);
200 ASSERT(iterator != m_facesLookupTable.end());
202 for (size_t i = 0; i < iterator->value.size(); ++i) {
203 if (iterator->value[i].ptr() == &face) {
205 iterator->value.remove(i);
209 ASSERT_UNUSED(found, found);
210 if (!iterator->value.size())
211 m_facesLookupTable.remove(iterator);
215 void CSSFontFaceSet::remove(const CSSFontFace& face)
219 for (auto* client : m_clients)
220 client->fontModified();
223 removeFromFacesLookupTable(face, *face.families());
225 for (size_t i = 0; i < m_faces.size(); ++i) {
226 if (m_faces[i].ptr() == &face) {
227 if (i < m_facesPartitionIndex)
228 --m_facesPartitionIndex;
229 m_faces[i]->removeClient(*this);
231 if (face.status() == CSSFontFace::Status::Loading || face.status() == CSSFontFace::Status::TimedOut)
232 decrementActiveCount();
236 ASSERT_NOT_REACHED();
239 void CSSFontFaceSet::clear()
242 m_facesLookupTable.clear();
243 m_locallyInstalledFacesLookupTable.clear();
247 CSSFontFace& CSSFontFaceSet::operator[](size_t i)
249 ASSERT(i < faceCount());
253 static Optional<FontTraitsMask> computeFontTraitsMask(MutableStyleProperties& style)
255 RefPtr<CSSValue> styleValue = style.getPropertyCSSValue(CSSPropertyFontStyle).get();
257 styleValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal).ptr();
259 FontTraitsMask styleMask;
260 if (auto styleMaskOptional = CSSFontFace::calculateStyleMask(*styleValue))
261 styleMask = styleMaskOptional.value();
265 RefPtr<CSSValue> weightValue = style.getPropertyCSSValue(CSSPropertyFontWeight).get();
267 weightValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal).ptr();
269 FontTraitsMask weightMask;
270 if (auto weightMaskOptional = CSSFontFace::calculateWeightMask(*weightValue))
271 weightMask = weightMaskOptional.value();
275 return static_cast<FontTraitsMask>(static_cast<unsigned>(styleMask) | static_cast<unsigned>(weightMask));
278 Vector<std::reference_wrapper<CSSFontFace>> CSSFontFaceSet::matchingFaces(const String& font, const String&, ExceptionCode& ec)
280 Vector<std::reference_wrapper<CSSFontFace>> result;
281 Ref<MutableStyleProperties> style = MutableStyleProperties::create();
282 auto parseResult = CSSParser::parseValue(style.ptr(), CSSPropertyFont, font, true, CSSStrictMode, nullptr);
283 if (parseResult == CSSParser::ParseResult::Error) {
288 FontTraitsMask fontTraitsMask;
289 if (auto maskOptional = computeFontTraitsMask(style.get()))
290 fontTraitsMask = maskOptional.value();
296 RefPtr<CSSValue> family = style->getPropertyCSSValue(CSSPropertyFontFamily);
297 if (!is<CSSValueList>(family.get())) {
301 CSSValueList& familyList = downcast<CSSValueList>(*family);
303 HashSet<AtomicString> uniqueFamilies;
304 for (auto& family : familyList) {
305 const CSSPrimitiveValue& primitive = downcast<CSSPrimitiveValue>(family.get());
306 if (!primitive.isFontFamily())
308 uniqueFamilies.add(primitive.fontFamily().familyName);
311 for (auto& family : uniqueFamilies) {
312 CSSSegmentedFontFace* faces = getFontFace(fontTraitsMask, family);
315 for (auto& constituentFace : faces->constituentFaces())
316 result.append(constituentFace.get());
322 bool CSSFontFaceSet::check(const String& font, const String& text, ExceptionCode& ec)
324 auto matchingFaces = this->matchingFaces(font, text, ec);
328 for (auto& face : matchingFaces) {
329 if (face.get().status() == CSSFontFace::Status::Pending)
335 static bool fontFaceComparator(FontTraitsMask desiredTraitsMaskForComparison, const CSSFontFace& first, const CSSFontFace& second)
337 FontTraitsMask firstTraitsMask = first.traitsMask();
338 FontTraitsMask secondTraitsMask = second.traitsMask();
340 bool firstHasDesiredStyle = firstTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
341 bool secondHasDesiredStyle = secondTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
343 if (firstHasDesiredStyle != secondHasDesiredStyle)
344 return firstHasDesiredStyle;
346 if ((desiredTraitsMaskForComparison & FontStyleItalicMask) && !first.isLocalFallback() && !second.isLocalFallback()) {
347 // Prefer a font that has indicated that it can only support italics to a font that claims to support
348 // all styles. The specialized font is more likely to be the one the author wants used.
349 bool firstRequiresItalics = (firstTraitsMask & FontStyleItalicMask) && !(firstTraitsMask & FontStyleNormalMask);
350 bool secondRequiresItalics = (secondTraitsMask & FontStyleItalicMask) && !(secondTraitsMask & FontStyleNormalMask);
351 if (firstRequiresItalics != secondRequiresItalics)
352 return firstRequiresItalics;
355 if (secondTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
357 if (firstTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
360 // http://www.w3.org/TR/2011/WD-css3-fonts-20111004/#font-matching-algorithm says :
361 // - If the desired weight is less than 400, weights below the desired weight are checked in descending order followed by weights above the desired weight in ascending order until a match is found.
362 // - If the desired weight is greater than 500, weights above the desired weight are checked in ascending order followed by weights below the desired weight in descending order until a match is found.
363 // - If the desired weight is 400, 500 is checked first and then the rule for desired weights less than 400 is used.
364 // - If the desired weight is 500, 400 is checked first and then the rule for desired weights less than 400 is used.
366 static const unsigned fallbackRuleSets = 9;
367 static const unsigned rulesPerSet = 8;
368 static const FontTraitsMask weightFallbackRuleSets[fallbackRuleSets][rulesPerSet] = {
369 { FontWeight200Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
370 { FontWeight100Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
371 { FontWeight200Mask, FontWeight100Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
372 { FontWeight500Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
373 { FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
374 { FontWeight700Mask, FontWeight800Mask, FontWeight900Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
375 { FontWeight800Mask, FontWeight900Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
376 { FontWeight900Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
377 { FontWeight800Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask }
380 unsigned ruleSetIndex = 0;
381 for (; !(desiredTraitsMaskForComparison & (1 << (FontWeight100Bit + ruleSetIndex))); ruleSetIndex++) { }
383 const FontTraitsMask* weightFallbackRule = weightFallbackRuleSets[ruleSetIndex];
384 for (unsigned i = 0; i < rulesPerSet; ++i) {
385 if (secondTraitsMask & weightFallbackRule[i])
387 if (firstTraitsMask & weightFallbackRule[i])
394 CSSSegmentedFontFace* CSSFontFaceSet::getFontFace(FontTraitsMask traitsMask, const AtomicString& family)
396 auto iterator = m_facesLookupTable.find(family);
397 if (iterator == m_facesLookupTable.end())
399 auto& familyFontFaces = iterator->value;
401 auto& segmentedFontFaceCache = m_cache.add(family, HashMap<unsigned, RefPtr<CSSSegmentedFontFace>>()).iterator->value;
403 auto& face = segmentedFontFaceCache.add(traitsMask, nullptr).iterator->value;
407 face = CSSSegmentedFontFace::create();
409 Vector<std::reference_wrapper<CSSFontFace>, 32> candidateFontFaces;
410 for (int i = familyFontFaces.size() - 1; i >= 0; --i) {
411 CSSFontFace& candidate = familyFontFaces[i];
412 unsigned candidateTraitsMask = candidate.traitsMask();
413 if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask))
415 candidateFontFaces.append(candidate);
418 auto localIterator = m_locallyInstalledFacesLookupTable.find(family);
419 if (localIterator != m_locallyInstalledFacesLookupTable.end()) {
420 for (auto& candidate : localIterator->value) {
421 unsigned candidateTraitsMask = candidate->traitsMask();
422 if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask))
424 candidateFontFaces.append(candidate);
428 std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), [traitsMask](const CSSFontFace& first, const CSSFontFace& second) {
429 return fontFaceComparator(traitsMask, first, second);
431 for (auto& candidate : candidateFontFaces)
432 face->appendFontFace(candidate.get());
437 void CSSFontFaceSet::fontStateChanged(CSSFontFace& face, CSSFontFace::Status oldState, CSSFontFace::Status newState)
439 ASSERT(hasFace(face));
440 if (oldState == CSSFontFace::Status::Pending) {
441 ASSERT(newState == CSSFontFace::Status::Loading);
442 incrementActiveCount();
444 if (newState == CSSFontFace::Status::Success || newState == CSSFontFace::Status::Failure) {
445 ASSERT(oldState == CSSFontFace::Status::Loading || oldState == CSSFontFace::Status::TimedOut);
446 for (auto* client : m_clients)
447 client->faceFinished(face, newState);
448 decrementActiveCount();
452 void CSSFontFaceSet::fontPropertyChanged(CSSFontFace& face, CSSValueList* oldFamilies)
457 removeFromFacesLookupTable(face, *oldFamilies);
458 addToFacesLookupTable(face);
461 for (auto* client : m_clients)
462 client->fontModified();