[Font Loading] Crash when a single load request causes multiple fonts to fail loading
[WebKit-https.git] / Source / WebCore / css / CSSFontFaceSet.cpp
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 "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"
39
40 namespace WebCore {
41
42 CSSFontFaceSet::CSSFontFaceSet()
43 {
44 }
45
46 CSSFontFaceSet::~CSSFontFaceSet()
47 {
48     for (auto& face : m_faces)
49         face->removeClient(*this);
50
51     for (auto& pair : m_locallyInstalledFacesLookupTable) {
52         for (auto& face : pair.value)
53             face->removeClient(*this);
54     }
55 }
56
57 void CSSFontFaceSet::addClient(CSSFontFaceSetClient& client)
58 {
59     m_clients.add(&client);
60 }
61
62 void CSSFontFaceSet::removeClient(CSSFontFaceSetClient& client)
63 {
64     ASSERT(m_clients.contains(&client));
65     m_clients.remove(&client);
66 }
67
68 void CSSFontFaceSet::incrementActiveCount()
69 {
70     ++m_activeCount;
71     if (m_activeCount == 1) {
72         m_status = Status::Loading;
73         for (auto* client : m_clients)
74             client->startedLoading();
75     }
76 }
77
78 void CSSFontFaceSet::decrementActiveCount()
79 {
80     --m_activeCount;
81     if (!m_activeCount) {
82         m_status = Status::Loaded;
83         for (auto* client : m_clients)
84             client->completedLoading();
85     }
86 }
87
88 bool CSSFontFaceSet::hasFace(const CSSFontFace& face) const
89 {
90     for (auto& myFace : m_faces) {
91         if (myFace.ptr() == &face)
92             return true;
93     }
94
95     return false;
96 }
97
98 void CSSFontFaceSet::registerLocalFontFacesForFamily(const String& familyName)
99 {
100     ASSERT(!m_locallyInstalledFacesLookupTable.contains(familyName));
101
102     Vector<FontTraitsMask> traitsMasks = FontCache::singleton().getTraitsInFamily(familyName);
103     if (traitsMasks.isEmpty())
104         return;
105
106     Vector<Ref<CSSFontFace>> faces;
107     for (auto mask : traitsMasks) {
108         Ref<CSSFontFace> face = CSSFontFace::create(nullptr, nullptr, nullptr, true);
109         
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));
117     }
118     m_locallyInstalledFacesLookupTable.add(familyName, WTFMove(faces));
119 }
120
121 String CSSFontFaceSet::familyNameFromPrimitive(const CSSPrimitiveValue& value)
122 {
123     if (value.isFontFamily())
124         return value.fontFamily().familyName;
125     if (!value.isValueID())
126         return { };
127
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()) {
131     case CSSValueSerif:
132         return serifFamily;
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;
143     default:
144         return { };
145     }
146 }
147
148 void CSSFontFaceSet::addToFacesLookupTable(CSSFontFace& face)
149 {
150     if (!face.families())
151         return;
152
153     for (auto& item : *face.families()) {
154         String familyName = CSSFontFaceSet::familyNameFromPrimitive(downcast<CSSPrimitiveValue>(item.get()));
155         if (familyName.isEmpty())
156             continue;
157
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 = { };
165         }
166
167         familyFontFaces.append(face);
168     }
169 }
170
171 void CSSFontFaceSet::add(CSSFontFace& face)
172 {
173     ASSERT(!hasFace(face));
174
175     for (auto* client : m_clients)
176         client->fontModified();
177
178     face.addClient(*this);
179     m_cache.clear();
180
181     if (face.cssConnection())
182         m_faces.insert(m_facesPartitionIndex++, face);
183     else
184         m_faces.append(face);
185
186     addToFacesLookupTable(face);
187
188     if (face.status() == CSSFontFace::Status::Loading || face.status() == CSSFontFace::Status::TimedOut)
189         incrementActiveCount();
190 }
191
192 void CSSFontFaceSet::removeFromFacesLookupTable(const CSSFontFace& face, const CSSValueList& familiesToSearchFor)
193 {
194     for (auto& item : familiesToSearchFor) {
195         String familyName = CSSFontFaceSet::familyNameFromPrimitive(downcast<CSSPrimitiveValue>(item.get()));
196         if (familyName.isEmpty())
197             continue;
198
199         auto iterator = m_facesLookupTable.find(familyName);
200         ASSERT(iterator != m_facesLookupTable.end());
201         bool found = false;
202         for (size_t i = 0; i < iterator->value.size(); ++i) {
203             if (iterator->value[i].ptr() == &face) {
204                 found = true;
205                 iterator->value.remove(i);
206                 break;
207             }
208         }
209         ASSERT_UNUSED(found, found);
210         if (!iterator->value.size())
211             m_facesLookupTable.remove(iterator);
212     }
213 }
214
215 void CSSFontFaceSet::remove(const CSSFontFace& face)
216 {
217     m_cache.clear();
218
219     for (auto* client : m_clients)
220         client->fontModified();
221
222     if (face.families())
223         removeFromFacesLookupTable(face, *face.families());
224
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);
230             m_faces.remove(i);
231             if (face.status() == CSSFontFace::Status::Loading || face.status() == CSSFontFace::Status::TimedOut)
232                 decrementActiveCount();
233             return;
234         }
235     }
236     ASSERT_NOT_REACHED();
237 }
238
239 void CSSFontFaceSet::clear()
240 {
241     m_faces.clear();
242     m_facesLookupTable.clear();
243     m_locallyInstalledFacesLookupTable.clear();
244     m_cache.clear();
245 }
246
247 CSSFontFace& CSSFontFaceSet::operator[](size_t i)
248 {
249     ASSERT(i < faceCount());
250     return m_faces[i];
251 }
252
253 static Optional<FontTraitsMask> computeFontTraitsMask(MutableStyleProperties& style)
254 {
255     RefPtr<CSSValue> styleValue = style.getPropertyCSSValue(CSSPropertyFontStyle).get();
256     if (!styleValue)
257         styleValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal).ptr();
258
259     FontTraitsMask styleMask;
260     if (auto styleMaskOptional = CSSFontFace::calculateStyleMask(*styleValue))
261         styleMask = styleMaskOptional.value();
262     else
263         return Nullopt;
264
265     RefPtr<CSSValue> weightValue = style.getPropertyCSSValue(CSSPropertyFontWeight).get();
266     if (!weightValue)
267         weightValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal).ptr();
268
269     FontTraitsMask weightMask;
270     if (auto weightMaskOptional = CSSFontFace::calculateWeightMask(*weightValue))
271         weightMask = weightMaskOptional.value();
272     else
273         return Nullopt;
274
275     return static_cast<FontTraitsMask>(static_cast<unsigned>(styleMask) | static_cast<unsigned>(weightMask));
276 }
277
278 Vector<std::reference_wrapper<CSSFontFace>> CSSFontFaceSet::matchingFaces(const String& font, const String&, ExceptionCode& ec)
279 {
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) {
284         ec = SYNTAX_ERR;
285         return result;
286     }
287
288     FontTraitsMask fontTraitsMask;
289     if (auto maskOptional = computeFontTraitsMask(style.get()))
290         fontTraitsMask = maskOptional.value();
291     else {
292         ec = SYNTAX_ERR;
293         return result;
294     }
295
296     RefPtr<CSSValue> family = style->getPropertyCSSValue(CSSPropertyFontFamily);
297     if (!is<CSSValueList>(family.get())) {
298         ec = SYNTAX_ERR;
299         return result;
300     }
301     CSSValueList& familyList = downcast<CSSValueList>(*family);
302
303     HashSet<AtomicString> uniqueFamilies;
304     for (auto& family : familyList) {
305         const CSSPrimitiveValue& primitive = downcast<CSSPrimitiveValue>(family.get());
306         if (!primitive.isFontFamily())
307             continue;
308         uniqueFamilies.add(primitive.fontFamily().familyName);
309     }
310
311     for (auto& family : uniqueFamilies) {
312         CSSSegmentedFontFace* faces = getFontFace(fontTraitsMask, family);
313         if (!faces)
314             continue;
315         for (auto& constituentFace : faces->constituentFaces())
316             result.append(constituentFace.get());
317     }
318
319     return result;
320 }
321
322 bool CSSFontFaceSet::check(const String& font, const String& text, ExceptionCode& ec)
323 {
324     auto matchingFaces = this->matchingFaces(font, text, ec);
325     if (ec)
326         return false;
327
328     for (auto& face : matchingFaces) {
329         if (face.get().status() == CSSFontFace::Status::Pending)
330             return false;
331     }
332     return true;
333 }
334
335 static bool fontFaceComparator(FontTraitsMask desiredTraitsMaskForComparison, const CSSFontFace& first, const CSSFontFace& second)
336 {
337     FontTraitsMask firstTraitsMask = first.traitsMask();
338     FontTraitsMask secondTraitsMask = second.traitsMask();
339
340     bool firstHasDesiredStyle = firstTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
341     bool secondHasDesiredStyle = secondTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
342
343     if (firstHasDesiredStyle != secondHasDesiredStyle)
344         return firstHasDesiredStyle;
345
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;
353     }
354
355     if (secondTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
356         return false;
357     if (firstTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
358         return true;
359
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.
365
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 }
378     };
379
380     unsigned ruleSetIndex = 0;
381     for (; !(desiredTraitsMaskForComparison & (1 << (FontWeight100Bit + ruleSetIndex))); ruleSetIndex++) { }
382
383     const FontTraitsMask* weightFallbackRule = weightFallbackRuleSets[ruleSetIndex];
384     for (unsigned i = 0; i < rulesPerSet; ++i) {
385         if (secondTraitsMask & weightFallbackRule[i])
386             return false;
387         if (firstTraitsMask & weightFallbackRule[i])
388             return true;
389     }
390
391     return false;
392 }
393
394 CSSSegmentedFontFace* CSSFontFaceSet::getFontFace(FontTraitsMask traitsMask, const AtomicString& family)
395 {
396     auto iterator = m_facesLookupTable.find(family);
397     if (iterator == m_facesLookupTable.end())
398         return nullptr;
399     auto& familyFontFaces = iterator->value;
400
401     auto& segmentedFontFaceCache = m_cache.add(family, HashMap<unsigned, RefPtr<CSSSegmentedFontFace>>()).iterator->value;
402
403     auto& face = segmentedFontFaceCache.add(traitsMask, nullptr).iterator->value;
404     if (face)
405         return face.get();
406
407     face = CSSSegmentedFontFace::create();
408
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))
414             continue;
415         candidateFontFaces.append(candidate);
416     }
417
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))
423                 continue;
424             candidateFontFaces.append(candidate);
425         }
426     }
427
428     std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), [traitsMask](const CSSFontFace& first, const CSSFontFace& second) {
429         return fontFaceComparator(traitsMask, first, second);
430     });
431     for (auto& candidate : candidateFontFaces)
432         face->appendFontFace(candidate.get());
433
434     return face.get();
435 }
436
437 void CSSFontFaceSet::fontStateChanged(CSSFontFace& face, CSSFontFace::Status oldState, CSSFontFace::Status newState)
438 {
439     ASSERT(hasFace(face));
440     if (oldState == CSSFontFace::Status::Pending) {
441         ASSERT(newState == CSSFontFace::Status::Loading);
442         incrementActiveCount();
443     }
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();
449     }
450 }
451
452 void CSSFontFaceSet::fontPropertyChanged(CSSFontFace& face, CSSValueList* oldFamilies)
453 {
454     m_cache.clear();
455
456     if (oldFamilies) {
457         removeFromFacesLookupTable(face, *oldFamilies);
458         addToFacesLookupTable(face);
459     }
460
461     for (auto* client : m_clients)
462         client->fontModified();
463 }
464
465 }