Parse font-display
[WebKit-https.git] / Source / WebCore / css / CSSFontFace.cpp
1 /*
2  * Copyright (C) 2007, 2008, 2011, 2013 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 "CSSFontFace.h"
28
29 #include "CSSFontFaceSource.h"
30 #include "CSSFontFaceSrcValue.h"
31 #include "CSSFontFeatureValue.h"
32 #include "CSSFontSelector.h"
33 #include "CSSFontStyleRangeValue.h"
34 #include "CSSPrimitiveValueMappings.h"
35 #include "CSSUnicodeRangeValue.h"
36 #include "CSSValue.h"
37 #include "CSSValueList.h"
38 #include "Document.h"
39 #include "Font.h"
40 #include "FontCache.h"
41 #include "FontDescription.h"
42 #include "FontFace.h"
43 #include "FontVariantBuilder.h"
44 #include "Settings.h"
45 #include "StyleBuilderConverter.h"
46 #include "StyleProperties.h"
47 #include "StyleRule.h"
48
49 namespace WebCore {
50
51 template<typename T> void iterateClients(HashSet<CSSFontFace::Client*>& clients, T callback)
52 {
53     Vector<Ref<CSSFontFace::Client>> clientsCopy;
54     clientsCopy.reserveInitialCapacity(clients.size());
55     for (auto* client : clients)
56         clientsCopy.uncheckedAppend(*client);
57
58     for (auto* client : clients)
59         callback(*client);
60 }
61
62 void CSSFontFace::appendSources(CSSFontFace& fontFace, CSSValueList& srcList, Document* document, bool isInitiatingElementInUserAgentShadowTree)
63 {
64     for (auto& src : srcList) {
65         // An item in the list either specifies a string (local font name) or a URL (remote font to download).
66         CSSFontFaceSrcValue& item = downcast<CSSFontFaceSrcValue>(src.get());
67         std::unique_ptr<CSSFontFaceSource> source;
68         SVGFontFaceElement* fontFaceElement = nullptr;
69         bool foundSVGFont = false;
70
71 #if ENABLE(SVG_FONTS)
72         foundSVGFont = item.isSVGFontFaceSrc() || item.svgFontFaceElement();
73         fontFaceElement = item.svgFontFaceElement();
74 #endif
75         if (!item.isLocal()) {
76             const Settings* settings = document ? &document->settings() : nullptr;
77             bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled());
78             if (allowDownloading && item.isSupportedFormat() && document) {
79                 if (CachedFont* cachedFont = item.cachedFont(document, foundSVGFont, isInitiatingElementInUserAgentShadowTree))
80                     source = std::make_unique<CSSFontFaceSource>(fontFace, item.resource(), cachedFont);
81             }
82         } else
83             source = std::make_unique<CSSFontFaceSource>(fontFace, item.resource(), nullptr, fontFaceElement);
84
85         if (source)
86             fontFace.adoptSource(WTFMove(source));
87     }
88     fontFace.sourcesPopulated();
89 }
90
91 CSSFontFace::CSSFontFace(CSSFontSelector* fontSelector, StyleRuleFontFace* cssConnection, FontFace* wrapper, bool isLocalFallback)
92     : m_timeoutTimer(*this, &CSSFontFace::timeoutFired)
93     , m_fontSelector(fontSelector)
94     , m_cssConnection(cssConnection)
95     , m_wrapper(wrapper ? wrapper->createWeakPtr() : WeakPtr<FontFace>())
96     , m_isLocalFallback(isLocalFallback)
97     , m_mayBePurged(!wrapper)
98 {
99 }
100
101 CSSFontFace::~CSSFontFace()
102 {
103 }
104
105 bool CSSFontFace::setFamilies(CSSValue& family)
106 {
107     if (!is<CSSValueList>(family))
108         return false;
109
110     CSSValueList& familyList = downcast<CSSValueList>(family);
111     if (!familyList.length())
112         return false;
113
114     RefPtr<CSSValueList> oldFamilies = m_families;
115     m_families = &familyList;
116
117     if (m_cssConnection)
118         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontFamily, &family);
119
120     iterateClients(m_clients, [&](Client& client) {
121         client.fontPropertyChanged(*this, oldFamilies.get());
122     });
123
124     return true;
125 }
126
127 static FontSelectionRange calculateWeightRange(CSSValue& value)
128 {
129     if (value.isValueList()) {
130         auto& valueList = downcast<CSSValueList>(value);
131         ASSERT(valueList.length() == 2);
132         if (valueList.length() != 2)
133             return { normalWeightValue(), normalWeightValue() };
134         ASSERT(valueList.item(0)->isPrimitiveValue());
135         ASSERT(valueList.item(1)->isPrimitiveValue());
136         auto& value0 = downcast<CSSPrimitiveValue>(*valueList.item(0));
137         auto& value1 = downcast<CSSPrimitiveValue>(*valueList.item(1));
138         auto result0 = StyleBuilderConverter::convertFontWeightFromValue(value0);
139         auto result1 = StyleBuilderConverter::convertFontWeightFromValue(value1);
140         return { result0, result1 };
141     }
142
143     ASSERT(is<CSSPrimitiveValue>(value));
144     auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
145     FontSelectionValue result = StyleBuilderConverter::convertFontWeightFromValue(primitiveValue);
146     return { result, result };
147 }
148
149 void CSSFontFace::setWeight(CSSValue& weight)
150 {
151     auto range = calculateWeightRange(weight);
152     if (m_fontSelectionCapabilities.weight == range)
153         return;
154
155     setWeight(range);
156
157     if (m_cssConnection)
158         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontWeight, &weight);
159
160     iterateClients(m_clients, [&](Client& client) {
161         client.fontPropertyChanged(*this);
162     });
163 }
164
165 static FontSelectionRange calculateStretchRange(CSSValue& value)
166 {
167     if (value.isValueList()) {
168         auto& valueList = downcast<CSSValueList>(value);
169         ASSERT(valueList.length() == 2);
170         if (valueList.length() != 2)
171             return { normalStretchValue(), normalStretchValue() };
172         ASSERT(valueList.item(0)->isPrimitiveValue());
173         ASSERT(valueList.item(1)->isPrimitiveValue());
174         auto& value0 = downcast<CSSPrimitiveValue>(*valueList.item(0));
175         auto& value1 = downcast<CSSPrimitiveValue>(*valueList.item(1));
176         auto result0 = StyleBuilderConverter::convertFontStretchFromValue(value0);
177         auto result1 = StyleBuilderConverter::convertFontStretchFromValue(value1);
178         return { result0, result1 };
179     }
180
181     ASSERT(is<CSSPrimitiveValue>(value));
182     const auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
183     FontSelectionValue result = StyleBuilderConverter::convertFontStretchFromValue(primitiveValue);
184     return { result, result };
185 }
186
187 void CSSFontFace::setStretch(CSSValue& style)
188 {
189     auto range = calculateStretchRange(style);
190     if (m_fontSelectionCapabilities.width == range)
191         return;
192
193     setStretch(range);
194
195     if (m_cssConnection)
196         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontStretch, &style);
197
198     iterateClients(m_clients, [&](Client& client) {
199         client.fontPropertyChanged(*this);
200     });
201 }
202
203 static FontSelectionRange calculateItalicRange(CSSValue& value)
204 {
205     if (value.isFontStyleValue()) {
206         auto result = StyleBuilderConverter::convertFontStyleFromValue(value);
207         return { result, result };
208     }
209
210     ASSERT(value.isFontStyleRangeValue());
211     auto& rangeValue = downcast<CSSFontStyleRangeValue>(value);
212     ASSERT(rangeValue.fontStyleValue->isValueID());
213     auto valueID = rangeValue.fontStyleValue->valueID();
214     if (!rangeValue.obliqueValues) {
215         if (valueID == CSSValueNormal)
216             return { normalItalicValue(), normalItalicValue() };
217         ASSERT(valueID == CSSValueItalic || valueID == CSSValueOblique);
218         return { italicValue(), italicValue() };
219     }
220     ASSERT(valueID == CSSValueOblique);
221     auto length = rangeValue.obliqueValues->length();
222     if (length == 1) {
223         auto& primitiveValue = downcast<CSSPrimitiveValue>(*rangeValue.obliqueValues->item(0));
224         FontSelectionValue result(primitiveValue.value<float>(CSSPrimitiveValue::CSS_DEG));
225         return { result, result };
226     }
227     ASSERT(length == 2);
228     auto& primitiveValue1 = downcast<CSSPrimitiveValue>(*rangeValue.obliqueValues->item(0));
229     auto& primitiveValue2 = downcast<CSSPrimitiveValue>(*rangeValue.obliqueValues->item(1));
230     FontSelectionValue result1(primitiveValue1.value<float>(CSSPrimitiveValue::CSS_DEG));
231     FontSelectionValue result2(primitiveValue2.value<float>(CSSPrimitiveValue::CSS_DEG));
232     return { result1, result2 };
233 }
234
235 void CSSFontFace::setStyle(CSSValue& style)
236 {
237     auto range = calculateItalicRange(style);
238     if (m_fontSelectionCapabilities.slope == range)
239         return;
240
241     setStyle(range);
242
243     if (m_cssConnection)
244         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontStyle, &style);
245
246     iterateClients(m_clients, [&](Client& client) {
247         client.fontPropertyChanged(*this);
248     });
249 }
250
251 bool CSSFontFace::setUnicodeRange(CSSValue& unicodeRange)
252 {
253     if (!is<CSSValueList>(unicodeRange))
254         return false;
255
256     Vector<UnicodeRange> ranges;
257     auto& list = downcast<CSSValueList>(unicodeRange);
258     for (auto& rangeValue : list) {
259         auto& range = downcast<CSSUnicodeRangeValue>(rangeValue.get());
260         ranges.append({ range.from(), range.to() });
261     }
262
263     if (ranges.size() == m_ranges.size()) {
264         bool same = true;
265         for (size_t i = 0; i < ranges.size(); ++i) {
266             if (ranges[i] != m_ranges[i]) {
267                 same = false;
268                 break;
269             }
270         }
271         if (same)
272             return true;
273     }
274
275     m_ranges = WTFMove(ranges);
276
277     if (m_cssConnection)
278         m_cssConnection->mutableProperties().setProperty(CSSPropertyUnicodeRange, &unicodeRange);
279
280     iterateClients(m_clients, [&](Client& client) {
281         client.fontPropertyChanged(*this);
282     });
283
284     return true;
285 }
286
287 bool CSSFontFace::setVariantLigatures(CSSValue& variantLigatures)
288 {
289     auto ligatures = extractFontVariantLigatures(variantLigatures);
290
291     if (m_variantSettings.commonLigatures == ligatures.commonLigatures
292         && m_variantSettings.discretionaryLigatures == ligatures.discretionaryLigatures
293         && m_variantSettings.historicalLigatures == ligatures.historicalLigatures
294         && m_variantSettings.contextualAlternates == ligatures.contextualAlternates)
295         return true;
296
297     m_variantSettings.commonLigatures = ligatures.commonLigatures;
298     m_variantSettings.discretionaryLigatures = ligatures.discretionaryLigatures;
299     m_variantSettings.historicalLigatures = ligatures.historicalLigatures;
300     m_variantSettings.contextualAlternates = ligatures.contextualAlternates;
301
302     if (m_cssConnection)
303         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantLigatures, &variantLigatures);
304
305     iterateClients(m_clients, [&](Client& client) {
306         client.fontPropertyChanged(*this);
307     });
308
309     return true;
310 }
311
312 bool CSSFontFace::setVariantPosition(CSSValue& variantPosition)
313 {
314     if (!is<CSSPrimitiveValue>(variantPosition))
315         return false;
316
317     FontVariantPosition position = downcast<CSSPrimitiveValue>(variantPosition);
318
319     if (m_variantSettings.position == position)
320         return true;
321
322     m_variantSettings.position = position;
323
324     if (m_cssConnection)
325         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantPosition, &variantPosition);
326
327     iterateClients(m_clients, [&](Client& client) {
328         client.fontPropertyChanged(*this);
329     });
330
331     return true;
332 }
333
334 bool CSSFontFace::setVariantCaps(CSSValue& variantCaps)
335 {
336     if (!is<CSSPrimitiveValue>(variantCaps))
337         return false;
338
339     FontVariantCaps caps = downcast<CSSPrimitiveValue>(variantCaps);
340
341     if (m_variantSettings.caps == caps)
342         return true;
343
344     m_variantSettings.caps = caps;
345
346     if (m_cssConnection)
347         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantCaps, &variantCaps);
348
349     iterateClients(m_clients, [&](Client& client) {
350         client.fontPropertyChanged(*this);
351     });
352
353     return true;
354 }
355
356 bool CSSFontFace::setVariantNumeric(CSSValue& variantNumeric)
357 {
358     auto numeric = extractFontVariantNumeric(variantNumeric);
359
360     if (m_variantSettings.numericFigure == numeric.figure
361         && m_variantSettings.numericSpacing == numeric.spacing
362         && m_variantSettings.numericFraction == numeric.fraction
363         && m_variantSettings.numericOrdinal == numeric.ordinal
364         && m_variantSettings.numericSlashedZero == numeric.slashedZero)
365         return true;
366
367     m_variantSettings.numericFigure = numeric.figure;
368     m_variantSettings.numericSpacing = numeric.spacing;
369     m_variantSettings.numericFraction = numeric.fraction;
370     m_variantSettings.numericOrdinal = numeric.ordinal;
371     m_variantSettings.numericSlashedZero = numeric.slashedZero;
372
373     if (m_cssConnection)
374         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantNumeric, &variantNumeric);
375
376     iterateClients(m_clients, [&](Client& client) {
377         client.fontPropertyChanged(*this);
378     });
379
380     return true;
381 }
382
383 bool CSSFontFace::setVariantAlternates(CSSValue& variantAlternates)
384 {
385     if (!is<CSSPrimitiveValue>(variantAlternates))
386         return false;
387
388     FontVariantAlternates alternates = downcast<CSSPrimitiveValue>(variantAlternates);
389
390     if (m_variantSettings.alternates == alternates)
391         return true;
392
393     m_variantSettings.alternates = alternates;
394
395     if (m_cssConnection)
396         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantAlternates, &variantAlternates);
397
398     iterateClients(m_clients, [&](Client& client) {
399         client.fontPropertyChanged(*this);
400     });
401
402     return true;
403 }
404
405 bool CSSFontFace::setVariantEastAsian(CSSValue& variantEastAsian)
406 {
407     auto eastAsian = extractFontVariantEastAsian(variantEastAsian);
408
409     if (m_variantSettings.eastAsianVariant == eastAsian.variant
410         && m_variantSettings.eastAsianWidth == eastAsian.width
411         && m_variantSettings.eastAsianRuby == eastAsian.ruby)
412         return true;
413
414     m_variantSettings.eastAsianVariant = eastAsian.variant;
415     m_variantSettings.eastAsianWidth = eastAsian.width;
416     m_variantSettings.eastAsianRuby = eastAsian.ruby;
417
418     if (m_cssConnection)
419         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantEastAsian, &variantEastAsian);
420
421     iterateClients(m_clients, [&](Client& client) {
422         client.fontPropertyChanged(*this);
423     });
424
425     return true;
426 }
427
428 void CSSFontFace::setFeatureSettings(CSSValue& featureSettings)
429 {
430     // Can only call this with a primitive value of normal, or a value list containing font feature values.
431     ASSERT(is<CSSPrimitiveValue>(featureSettings) || is<CSSValueList>(featureSettings));
432
433     FontFeatureSettings settings;
434
435     if (is<CSSValueList>(featureSettings)) {
436         auto& list = downcast<CSSValueList>(featureSettings);
437         for (auto& rangeValue : list) {
438             auto& feature = downcast<CSSFontFeatureValue>(rangeValue.get());
439             settings.insert({ feature.tag(), feature.value() });
440         }
441     }
442
443     if (m_featureSettings == settings)
444         return;
445
446     m_featureSettings = WTFMove(settings);
447
448     if (m_cssConnection)
449         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontFeatureSettings, &featureSettings);
450
451     iterateClients(m_clients, [&](Client& client) {
452         client.fontPropertyChanged(*this);
453     });
454 }
455
456 void CSSFontFace::setLoadingBehavior(CSSValue& loadingBehaviorValue)
457 {
458     auto loadingBehavior = static_cast<FontLoadingBehavior>(downcast<CSSPrimitiveValue>(loadingBehaviorValue).valueID());
459
460     if (m_loadingBehavior == loadingBehavior)
461         return;
462
463     m_loadingBehavior = loadingBehavior;
464
465     if (m_cssConnection)
466         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontDisplay, &loadingBehaviorValue);
467
468     iterateClients(m_clients, [&](Client& client) {
469         client.fontPropertyChanged(*this);
470     });
471 }
472
473 bool CSSFontFace::rangesMatchCodePoint(UChar32 character) const
474 {
475     if (m_ranges.isEmpty())
476         return true;
477
478     for (auto& range : m_ranges) {
479         if (range.from <= character && character <= range.to)
480             return true;
481     }
482     return false;
483 }
484
485 void CSSFontFace::fontLoadEventOccurred()
486 {
487     Ref<CSSFontFace> protectedThis(*this);
488
489     // If the font is already in the cache, CSSFontFaceSource may report it's loaded before it is added here as a source.
490     // Let's not pump the state machine until we've got all our sources. font() and load() are smart enough to act correctly
491     // when a source is failed or succeeded before we have asked it to load.
492     if (m_sourcesPopulated && !webFontsShouldAlwaysFallBack())
493         pump(ExternalResourceDownloadPolicy::Forbid);
494
495     ASSERT(m_fontSelector);
496     m_fontSelector->fontLoaded();
497
498     iterateClients(m_clients, [&](Client& client) {
499         client.fontLoaded(*this);
500     });
501 }
502
503 void CSSFontFace::timeoutFired()
504 {
505     setStatus(Status::TimedOut);
506
507     fontLoadEventOccurred();
508 }
509
510 bool CSSFontFace::allSourcesFailed() const
511 {
512     for (auto& source : m_sources) {
513         if (source->status() != CSSFontFaceSource::Status::Failure)
514             return false;
515     }
516     return true;
517 }
518
519 void CSSFontFace::addClient(Client& client)
520 {
521     m_clients.add(&client);
522 }
523
524 void CSSFontFace::removeClient(Client& client)
525 {
526     ASSERT(m_clients.contains(&client));
527     m_clients.remove(&client);
528 }
529
530 void CSSFontFace::initializeWrapper()
531 {
532     switch (m_status) {
533     case Status::Pending:
534         break;
535     case Status::Loading:
536         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
537         break;
538     case Status::TimedOut:
539         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
540         m_wrapper->fontStateChanged(*this, Status::Loading, Status::TimedOut);
541         break;
542     case Status::Success:
543         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
544         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Success);
545         break;
546     case Status::Failure:
547         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
548         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Failure);
549         break;
550     }
551     m_mayBePurged = false;
552 }
553
554 Ref<FontFace> CSSFontFace::wrapper()
555 {
556     if (m_wrapper)
557         return *m_wrapper.get();
558
559     auto wrapper = FontFace::create(*this);
560     m_wrapper = wrapper->createWeakPtr();
561     initializeWrapper();
562     return wrapper;
563 }
564
565 void CSSFontFace::setWrapper(FontFace& newWrapper)
566 {
567     m_wrapper = newWrapper.createWeakPtr();
568     initializeWrapper();
569 }
570
571 void CSSFontFace::adoptSource(std::unique_ptr<CSSFontFaceSource>&& source)
572 {
573     m_sources.append(WTFMove(source));
574
575     // We should never add sources in the middle of loading.
576     ASSERT(!m_sourcesPopulated);
577 }
578
579 void CSSFontFace::setStatus(Status newStatus)
580 {
581     switch (newStatus) {
582     case Status::Pending:
583         ASSERT_NOT_REACHED();
584         break;
585     case Status::Loading:
586         ASSERT(m_status == Status::Pending);
587         break;
588     case Status::TimedOut:
589         ASSERT(m_status == Status::Loading);
590         break;
591     case Status::Success:
592         ASSERT(m_status == Status::Loading || m_status == Status::TimedOut);
593         break;
594     case Status::Failure:
595         ASSERT(m_status == Status::Loading || m_status == Status::TimedOut);
596         break;
597     }
598
599     bool webFontsShouldAlwaysFallBack = this->webFontsShouldAlwaysFallBack();
600     if (!webFontsShouldAlwaysFallBack) {
601         if (newStatus == Status::Loading) {
602             Seconds timeUntilInterstitialFontIsDrawnVisibly = 3_s;
603             m_timeoutTimer.startOneShot(timeUntilInterstitialFontIsDrawnVisibly);
604         } else if (newStatus == Status::Success || newStatus == Status::Failure)
605             m_timeoutTimer.stop();
606     }
607
608     iterateClients(m_clients, [&](Client& client) {
609         client.fontStateChanged(*this, m_status, newStatus);
610     });
611
612     m_status = newStatus;
613
614     if (newStatus == Status::Loading && webFontsShouldAlwaysFallBack)
615         timeoutFired();
616 }
617
618 void CSSFontFace::fontLoaded(CSSFontFaceSource&)
619 {
620     ASSERT(!webFontsShouldAlwaysFallBack());
621
622     fontLoadEventOccurred();
623 }
624
625 bool CSSFontFace::webFontsShouldAlwaysFallBack() const
626 {
627     return m_fontSelector && m_fontSelector->document() && m_fontSelector->document()->settings().webFontsAlwaysFallBack();
628 }
629
630 size_t CSSFontFace::pump(ExternalResourceDownloadPolicy policy)
631 {
632     size_t i;
633     for (i = 0; i < m_sources.size(); ++i) {
634         auto& source = m_sources[i];
635
636         if (source->status() == CSSFontFaceSource::Status::Pending) {
637             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut);
638             if (policy == ExternalResourceDownloadPolicy::Allow || !source->requiresExternalResource()) {
639                 if (m_status == Status::Pending)
640                     setStatus(Status::Loading);
641                 source->load(m_fontSelector.get());
642             }
643         }
644
645         switch (source->status()) {
646         case CSSFontFaceSource::Status::Pending:
647             ASSERT(policy == ExternalResourceDownloadPolicy::Forbid);
648             return i;
649         case CSSFontFaceSource::Status::Loading:
650             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut);
651             if (m_status == Status::Pending)
652                 setStatus(Status::Loading);
653             return i;
654         case CSSFontFaceSource::Status::Success:
655             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut || m_status == Status::Success);
656             if (m_status == Status::Pending)
657                 setStatus(Status::Loading);
658             if (m_status == Status::Loading || m_status == Status::TimedOut)
659                 setStatus(Status::Success);
660             return i;
661         case CSSFontFaceSource::Status::Failure:
662             if (m_status == Status::Pending)
663                 setStatus(Status::Loading);
664             break;
665         }
666     }
667     if (m_sources.isEmpty() && m_status == Status::Pending)
668         setStatus(Status::Loading);
669     if (m_status == Status::Loading || m_status == Status::TimedOut)
670         setStatus(Status::Failure);
671     return m_sources.size();
672 }
673
674 void CSSFontFace::load()
675 {
676     pump(ExternalResourceDownloadPolicy::Allow);
677 }
678
679 RefPtr<Font> CSSFontFace::font(const FontDescription& fontDescription, bool syntheticBold, bool syntheticItalic, ExternalResourceDownloadPolicy policy)
680 {
681     if (allSourcesFailed())
682         return nullptr;
683
684     // Our status is derived from the first non-failed source. However, this source may
685     // return null from font(), which means we need to continue looping through the remainder
686     // of the sources to try to find a font to use. These subsequent tries should not affect
687     // our own state, though.
688     size_t startIndex = pump(policy);
689     for (size_t i = startIndex; i < m_sources.size(); ++i) {
690         auto& source = m_sources[i];
691         if (source->status() == CSSFontFaceSource::Status::Pending && (policy == ExternalResourceDownloadPolicy::Allow || !source->requiresExternalResource()))
692             source->load(m_fontSelector.get());
693
694         switch (source->status()) {
695         case CSSFontFaceSource::Status::Pending:
696         case CSSFontFaceSource::Status::Loading: {
697             Font::Visibility visibility = status() == Status::TimedOut ? Font::Visibility::Visible : Font::Visibility::Invisible;
698             return Font::create(FontCache::singleton().lastResortFallbackFont(fontDescription)->platformData(), Font::Origin::Remote, Font::Interstitial::Yes, visibility);
699         }
700         case CSSFontFaceSource::Status::Success:
701             if (RefPtr<Font> result = source->font(fontDescription, syntheticBold, syntheticItalic, m_featureSettings, m_variantSettings, m_fontSelectionCapabilities))
702                 return result;
703             break;
704         case CSSFontFaceSource::Status::Failure:
705             break;
706         }
707     }
708
709     return nullptr;
710 }
711
712 bool CSSFontFace::purgeable() const
713 {
714     return cssConnection() && m_mayBePurged;
715 }
716
717 void CSSFontFace::updateStyleIfNeeded()
718 {
719     if (m_fontSelector && m_fontSelector->document())
720         m_fontSelector->document()->updateStyleIfNeeded();
721 }
722
723 #if ENABLE(SVG_FONTS)
724 bool CSSFontFace::hasSVGFontFaceSource() const
725 {
726     size_t size = m_sources.size();
727     for (size_t i = 0; i < size; i++) {
728         if (m_sources[i]->isSVGFontFaceSource())
729             return true;
730     }
731     return false;
732 }
733 #endif
734
735 }