Use "= default" to denote default constructor or destructor
[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() = default;
102
103 bool CSSFontFace::setFamilies(CSSValue& family)
104 {
105     if (!is<CSSValueList>(family))
106         return false;
107
108     CSSValueList& familyList = downcast<CSSValueList>(family);
109     if (!familyList.length())
110         return false;
111
112     RefPtr<CSSValueList> oldFamilies = m_families;
113     m_families = &familyList;
114
115     if (m_cssConnection)
116         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontFamily, &family);
117
118     iterateClients(m_clients, [&](Client& client) {
119         client.fontPropertyChanged(*this, oldFamilies.get());
120     });
121
122     return true;
123 }
124
125 static FontSelectionRange calculateWeightRange(CSSValue& value)
126 {
127     if (value.isValueList()) {
128         auto& valueList = downcast<CSSValueList>(value);
129         ASSERT(valueList.length() == 2);
130         if (valueList.length() != 2)
131             return { normalWeightValue(), normalWeightValue() };
132         ASSERT(valueList.item(0)->isPrimitiveValue());
133         ASSERT(valueList.item(1)->isPrimitiveValue());
134         auto& value0 = downcast<CSSPrimitiveValue>(*valueList.item(0));
135         auto& value1 = downcast<CSSPrimitiveValue>(*valueList.item(1));
136         auto result0 = StyleBuilderConverter::convertFontWeightFromValue(value0);
137         auto result1 = StyleBuilderConverter::convertFontWeightFromValue(value1);
138         return { result0, result1 };
139     }
140
141     ASSERT(is<CSSPrimitiveValue>(value));
142     auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
143     FontSelectionValue result = StyleBuilderConverter::convertFontWeightFromValue(primitiveValue);
144     return { result, result };
145 }
146
147 void CSSFontFace::setWeight(CSSValue& weight)
148 {
149     auto range = calculateWeightRange(weight);
150     if (m_fontSelectionCapabilities.weight == range)
151         return;
152
153     setWeight(range);
154
155     if (m_cssConnection)
156         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontWeight, &weight);
157
158     iterateClients(m_clients, [&](Client& client) {
159         client.fontPropertyChanged(*this);
160     });
161 }
162
163 static FontSelectionRange calculateStretchRange(CSSValue& value)
164 {
165     if (value.isValueList()) {
166         auto& valueList = downcast<CSSValueList>(value);
167         ASSERT(valueList.length() == 2);
168         if (valueList.length() != 2)
169             return { normalStretchValue(), normalStretchValue() };
170         ASSERT(valueList.item(0)->isPrimitiveValue());
171         ASSERT(valueList.item(1)->isPrimitiveValue());
172         auto& value0 = downcast<CSSPrimitiveValue>(*valueList.item(0));
173         auto& value1 = downcast<CSSPrimitiveValue>(*valueList.item(1));
174         auto result0 = StyleBuilderConverter::convertFontStretchFromValue(value0);
175         auto result1 = StyleBuilderConverter::convertFontStretchFromValue(value1);
176         return { result0, result1 };
177     }
178
179     ASSERT(is<CSSPrimitiveValue>(value));
180     const auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
181     FontSelectionValue result = StyleBuilderConverter::convertFontStretchFromValue(primitiveValue);
182     return { result, result };
183 }
184
185 void CSSFontFace::setStretch(CSSValue& style)
186 {
187     auto range = calculateStretchRange(style);
188     if (m_fontSelectionCapabilities.width == range)
189         return;
190
191     setStretch(range);
192
193     if (m_cssConnection)
194         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontStretch, &style);
195
196     iterateClients(m_clients, [&](Client& client) {
197         client.fontPropertyChanged(*this);
198     });
199 }
200
201 static FontSelectionRange calculateItalicRange(CSSValue& value)
202 {
203     if (value.isFontStyleValue()) {
204         auto result = StyleBuilderConverter::convertFontStyleFromValue(value);
205         return { result, result };
206     }
207
208     ASSERT(value.isFontStyleRangeValue());
209     auto& rangeValue = downcast<CSSFontStyleRangeValue>(value);
210     ASSERT(rangeValue.fontStyleValue->isValueID());
211     auto valueID = rangeValue.fontStyleValue->valueID();
212     if (!rangeValue.obliqueValues) {
213         if (valueID == CSSValueNormal)
214             return { normalItalicValue(), normalItalicValue() };
215         ASSERT(valueID == CSSValueItalic || valueID == CSSValueOblique);
216         return { italicValue(), italicValue() };
217     }
218     ASSERT(valueID == CSSValueOblique);
219     auto length = rangeValue.obliqueValues->length();
220     if (length == 1) {
221         auto& primitiveValue = downcast<CSSPrimitiveValue>(*rangeValue.obliqueValues->item(0));
222         FontSelectionValue result(primitiveValue.value<float>(CSSPrimitiveValue::CSS_DEG));
223         return { result, result };
224     }
225     ASSERT(length == 2);
226     auto& primitiveValue1 = downcast<CSSPrimitiveValue>(*rangeValue.obliqueValues->item(0));
227     auto& primitiveValue2 = downcast<CSSPrimitiveValue>(*rangeValue.obliqueValues->item(1));
228     FontSelectionValue result1(primitiveValue1.value<float>(CSSPrimitiveValue::CSS_DEG));
229     FontSelectionValue result2(primitiveValue2.value<float>(CSSPrimitiveValue::CSS_DEG));
230     return { result1, result2 };
231 }
232
233 void CSSFontFace::setStyle(CSSValue& style)
234 {
235     auto range = calculateItalicRange(style);
236     if (m_fontSelectionCapabilities.slope == range)
237         return;
238
239     setStyle(range);
240
241     if (m_cssConnection)
242         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontStyle, &style);
243
244     iterateClients(m_clients, [&](Client& client) {
245         client.fontPropertyChanged(*this);
246     });
247 }
248
249 bool CSSFontFace::setUnicodeRange(CSSValue& unicodeRange)
250 {
251     if (!is<CSSValueList>(unicodeRange))
252         return false;
253
254     Vector<UnicodeRange> ranges;
255     auto& list = downcast<CSSValueList>(unicodeRange);
256     for (auto& rangeValue : list) {
257         auto& range = downcast<CSSUnicodeRangeValue>(rangeValue.get());
258         ranges.append({ range.from(), range.to() });
259     }
260
261     if (ranges == m_ranges)
262         return true;
263
264     m_ranges = WTFMove(ranges);
265
266     if (m_cssConnection)
267         m_cssConnection->mutableProperties().setProperty(CSSPropertyUnicodeRange, &unicodeRange);
268
269     iterateClients(m_clients, [&](Client& client) {
270         client.fontPropertyChanged(*this);
271     });
272
273     return true;
274 }
275
276 bool CSSFontFace::setVariantLigatures(CSSValue& variantLigatures)
277 {
278     auto ligatures = extractFontVariantLigatures(variantLigatures);
279
280     if (m_variantSettings.commonLigatures == ligatures.commonLigatures
281         && m_variantSettings.discretionaryLigatures == ligatures.discretionaryLigatures
282         && m_variantSettings.historicalLigatures == ligatures.historicalLigatures
283         && m_variantSettings.contextualAlternates == ligatures.contextualAlternates)
284         return true;
285
286     m_variantSettings.commonLigatures = ligatures.commonLigatures;
287     m_variantSettings.discretionaryLigatures = ligatures.discretionaryLigatures;
288     m_variantSettings.historicalLigatures = ligatures.historicalLigatures;
289     m_variantSettings.contextualAlternates = ligatures.contextualAlternates;
290
291     if (m_cssConnection)
292         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantLigatures, &variantLigatures);
293
294     iterateClients(m_clients, [&](Client& client) {
295         client.fontPropertyChanged(*this);
296     });
297
298     return true;
299 }
300
301 bool CSSFontFace::setVariantPosition(CSSValue& variantPosition)
302 {
303     if (!is<CSSPrimitiveValue>(variantPosition))
304         return false;
305
306     FontVariantPosition position = downcast<CSSPrimitiveValue>(variantPosition);
307
308     if (m_variantSettings.position == position)
309         return true;
310
311     m_variantSettings.position = position;
312
313     if (m_cssConnection)
314         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantPosition, &variantPosition);
315
316     iterateClients(m_clients, [&](Client& client) {
317         client.fontPropertyChanged(*this);
318     });
319
320     return true;
321 }
322
323 bool CSSFontFace::setVariantCaps(CSSValue& variantCaps)
324 {
325     if (!is<CSSPrimitiveValue>(variantCaps))
326         return false;
327
328     FontVariantCaps caps = downcast<CSSPrimitiveValue>(variantCaps);
329
330     if (m_variantSettings.caps == caps)
331         return true;
332
333     m_variantSettings.caps = caps;
334
335     if (m_cssConnection)
336         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantCaps, &variantCaps);
337
338     iterateClients(m_clients, [&](Client& client) {
339         client.fontPropertyChanged(*this);
340     });
341
342     return true;
343 }
344
345 bool CSSFontFace::setVariantNumeric(CSSValue& variantNumeric)
346 {
347     auto numeric = extractFontVariantNumeric(variantNumeric);
348
349     if (m_variantSettings.numericFigure == numeric.figure
350         && m_variantSettings.numericSpacing == numeric.spacing
351         && m_variantSettings.numericFraction == numeric.fraction
352         && m_variantSettings.numericOrdinal == numeric.ordinal
353         && m_variantSettings.numericSlashedZero == numeric.slashedZero)
354         return true;
355
356     m_variantSettings.numericFigure = numeric.figure;
357     m_variantSettings.numericSpacing = numeric.spacing;
358     m_variantSettings.numericFraction = numeric.fraction;
359     m_variantSettings.numericOrdinal = numeric.ordinal;
360     m_variantSettings.numericSlashedZero = numeric.slashedZero;
361
362     if (m_cssConnection)
363         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantNumeric, &variantNumeric);
364
365     iterateClients(m_clients, [&](Client& client) {
366         client.fontPropertyChanged(*this);
367     });
368
369     return true;
370 }
371
372 bool CSSFontFace::setVariantAlternates(CSSValue& variantAlternates)
373 {
374     if (!is<CSSPrimitiveValue>(variantAlternates))
375         return false;
376
377     FontVariantAlternates alternates = downcast<CSSPrimitiveValue>(variantAlternates);
378
379     if (m_variantSettings.alternates == alternates)
380         return true;
381
382     m_variantSettings.alternates = alternates;
383
384     if (m_cssConnection)
385         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantAlternates, &variantAlternates);
386
387     iterateClients(m_clients, [&](Client& client) {
388         client.fontPropertyChanged(*this);
389     });
390
391     return true;
392 }
393
394 bool CSSFontFace::setVariantEastAsian(CSSValue& variantEastAsian)
395 {
396     auto eastAsian = extractFontVariantEastAsian(variantEastAsian);
397
398     if (m_variantSettings.eastAsianVariant == eastAsian.variant
399         && m_variantSettings.eastAsianWidth == eastAsian.width
400         && m_variantSettings.eastAsianRuby == eastAsian.ruby)
401         return true;
402
403     m_variantSettings.eastAsianVariant = eastAsian.variant;
404     m_variantSettings.eastAsianWidth = eastAsian.width;
405     m_variantSettings.eastAsianRuby = eastAsian.ruby;
406
407     if (m_cssConnection)
408         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantEastAsian, &variantEastAsian);
409
410     iterateClients(m_clients, [&](Client& client) {
411         client.fontPropertyChanged(*this);
412     });
413
414     return true;
415 }
416
417 void CSSFontFace::setFeatureSettings(CSSValue& featureSettings)
418 {
419     // Can only call this with a primitive value of normal, or a value list containing font feature values.
420     ASSERT(is<CSSPrimitiveValue>(featureSettings) || is<CSSValueList>(featureSettings));
421
422     FontFeatureSettings settings;
423
424     if (is<CSSValueList>(featureSettings)) {
425         auto& list = downcast<CSSValueList>(featureSettings);
426         for (auto& rangeValue : list) {
427             auto& feature = downcast<CSSFontFeatureValue>(rangeValue.get());
428             settings.insert({ feature.tag(), feature.value() });
429         }
430     }
431
432     if (m_featureSettings == settings)
433         return;
434
435     m_featureSettings = WTFMove(settings);
436
437     if (m_cssConnection)
438         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontFeatureSettings, &featureSettings);
439
440     iterateClients(m_clients, [&](Client& client) {
441         client.fontPropertyChanged(*this);
442     });
443 }
444
445 void CSSFontFace::setLoadingBehavior(CSSValue& loadingBehaviorValue)
446 {
447     auto loadingBehavior = static_cast<FontLoadingBehavior>(downcast<CSSPrimitiveValue>(loadingBehaviorValue));
448
449     if (m_loadingBehavior == loadingBehavior)
450         return;
451
452     m_loadingBehavior = loadingBehavior;
453
454     if (m_cssConnection)
455         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontDisplay, &loadingBehaviorValue);
456
457     iterateClients(m_clients, [&](Client& client) {
458         client.fontPropertyChanged(*this);
459     });
460 }
461
462 bool CSSFontFace::rangesMatchCodePoint(UChar32 character) const
463 {
464     if (m_ranges.isEmpty())
465         return true;
466
467     for (auto& range : m_ranges) {
468         if (range.from <= character && character <= range.to)
469             return true;
470     }
471     return false;
472 }
473
474 void CSSFontFace::fontLoadEventOccurred()
475 {
476     Ref<CSSFontFace> protectedThis(*this);
477
478     // If the font is already in the cache, CSSFontFaceSource may report it's loaded before it is added here as a source.
479     // Let's not pump the state machine until we've got all our sources. font() and load() are smart enough to act correctly
480     // when a source is failed or succeeded before we have asked it to load.
481     if (m_sourcesPopulated)
482         pump(ExternalResourceDownloadPolicy::Forbid);
483
484     ASSERT(m_fontSelector);
485     m_fontSelector->fontLoaded();
486
487     iterateClients(m_clients, [&](Client& client) {
488         client.fontLoaded(*this);
489     });
490 }
491
492 void CSSFontFace::timeoutFired()
493 {
494     switch (status()) {
495     case Status::Loading:
496         setStatus(Status::TimedOut);
497         break;
498     case Status::TimedOut:
499         // Cancelling the network request here could lead to a situation where a site's font never gets
500         // shown as the user navigates around to different pages on the site. This would occur if the
501         // download always takes longer than the timeout (even though the user may spend substantial time
502         // on each page). Therefore, we shouldn't cancel the network request here, but should use the
503         // loading infrastructure's timeout policies instead.
504         setStatus(Status::Failure);
505         break;
506     default:
507         ASSERT_NOT_REACHED();
508         break;
509     }
510
511     fontLoadEventOccurred();
512 }
513
514 bool CSSFontFace::computeFailureState() const
515 {
516     if (status() == Status::Failure)
517         return true;
518     for (auto& source : m_sources) {
519         if (source->status() != CSSFontFaceSource::Status::Failure)
520             return false;
521     }
522     return true;
523 }
524
525 void CSSFontFace::addClient(Client& client)
526 {
527     m_clients.add(&client);
528 }
529
530 void CSSFontFace::removeClient(Client& client)
531 {
532     ASSERT(m_clients.contains(&client));
533     m_clients.remove(&client);
534 }
535
536 void CSSFontFace::initializeWrapper()
537 {
538     switch (m_status) {
539     case Status::Pending:
540         break;
541     case Status::Loading:
542         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
543         break;
544     case Status::TimedOut:
545         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
546         m_wrapper->fontStateChanged(*this, Status::Loading, Status::TimedOut);
547         break;
548     case Status::Success:
549         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
550         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Success);
551         break;
552     case Status::Failure:
553         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
554         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Failure);
555         break;
556     }
557     m_mayBePurged = false;
558 }
559
560 Ref<FontFace> CSSFontFace::wrapper()
561 {
562     if (m_wrapper)
563         return *m_wrapper.get();
564
565     auto wrapper = FontFace::create(*this);
566     m_wrapper = wrapper->createWeakPtr();
567     initializeWrapper();
568     return wrapper;
569 }
570
571 void CSSFontFace::setWrapper(FontFace& newWrapper)
572 {
573     m_wrapper = newWrapper.createWeakPtr();
574     initializeWrapper();
575 }
576
577 void CSSFontFace::adoptSource(std::unique_ptr<CSSFontFaceSource>&& source)
578 {
579     m_sources.append(WTFMove(source));
580
581     // We should never add sources in the middle of loading.
582     ASSERT(!m_sourcesPopulated);
583 }
584
585 static Settings::FontLoadTimingOverride fontLoadTimingOverride(CSSFontSelector* fontSelector)
586 {
587     auto overrideValue = Settings::FontLoadTimingOverride::None;
588     if (fontSelector && fontSelector->document())
589         overrideValue = fontSelector->document()->settings().fontLoadTimingOverride();
590     return overrideValue;
591 }
592
593 auto CSSFontFace::fontLoadTiming() const -> FontLoadTiming
594 {
595     switch (fontLoadTimingOverride(m_fontSelector.get())) {
596     case Settings::FontLoadTimingOverride::None:
597         switch (m_loadingBehavior) {
598         case FontLoadingBehavior::Auto:
599         case FontLoadingBehavior::Block:
600             return { 3_s, Seconds::infinity() };
601         case FontLoadingBehavior::Swap:
602             return { 0_s, Seconds::infinity() };
603         case FontLoadingBehavior::Fallback:
604             return { 0.1_s, 3_s };
605         case FontLoadingBehavior::Optional:
606             return { 0.1_s, 0_s };
607         }
608     case Settings::FontLoadTimingOverride::Block:
609         return { Seconds::infinity(), 0_s };
610     case Settings::FontLoadTimingOverride::Swap:
611         return { 0_s, Seconds::infinity() };
612     case Settings::FontLoadTimingOverride::Failure:
613         return { 0_s, 0_s };
614     }
615     ASSERT_NOT_REACHED();
616     return { 3_s, Seconds::infinity() };
617 }
618
619 void CSSFontFace::setStatus(Status newStatus)
620 {
621     switch (newStatus) {
622     case Status::Pending:
623         ASSERT_NOT_REACHED();
624         break;
625     case Status::Loading:
626         ASSERT(m_status == Status::Pending);
627         break;
628     case Status::TimedOut:
629         ASSERT(m_status == Status::Loading);
630         break;
631     case Status::Success:
632         ASSERT(m_status == Status::Loading || m_status == Status::TimedOut);
633         break;
634     case Status::Failure:
635         ASSERT(m_status == Status::Loading || m_status == Status::TimedOut);
636         break;
637     }
638
639     iterateClients(m_clients, [&](Client& client) {
640         client.fontStateChanged(*this, m_status, newStatus);
641     });
642
643     m_status = newStatus;
644
645     Seconds blockPeriodTimeout;
646     Seconds swapPeriodTimeout;
647     auto timeouts = fontLoadTiming();
648     blockPeriodTimeout = timeouts.blockPeriod;
649     swapPeriodTimeout = timeouts.swapPeriod;
650
651     // Transfer across 0-delay timers synchronously. Layouts/script may
652     // take arbitrarily long time, and we shouldn't be in a 0-duration
653     // state for an arbitrarily long time. Also it's necessary for
654     // testing so we don't have a race with the font load.
655     switch (newStatus) {
656     case Status::Pending:
657         ASSERT_NOT_REACHED();
658         break;
659     case Status::Loading:
660         if (blockPeriodTimeout == 0_s)
661             setStatus(Status::TimedOut);
662         else if (std::isfinite(blockPeriodTimeout.value()))
663             m_timeoutTimer.startOneShot(blockPeriodTimeout);
664         break;
665     case Status::TimedOut:
666         if (swapPeriodTimeout == 0_s)
667             setStatus(Status::Failure);
668         else if (std::isfinite(swapPeriodTimeout.value()))
669             m_timeoutTimer.startOneShot(swapPeriodTimeout);
670         break;
671     case Status::Success:
672     case Status::Failure:
673         m_timeoutTimer.stop();
674         break;
675     }
676 }
677
678 void CSSFontFace::fontLoaded(CSSFontFaceSource&)
679 {
680     fontLoadEventOccurred();
681 }
682
683 bool CSSFontFace::shouldIgnoreFontLoadCompletions() const
684 {
685     if (m_fontSelector && m_fontSelector->document())
686         return m_fontSelector->document()->settings().shouldIgnoreFontLoadCompletions();
687     return false;
688 }
689
690 size_t CSSFontFace::pump(ExternalResourceDownloadPolicy policy)
691 {
692     if (status() == Status::Failure)
693         return 0;
694
695     size_t i;
696     for (i = 0; i < m_sources.size(); ++i) {
697         auto& source = m_sources[i];
698
699         if (source->status() == CSSFontFaceSource::Status::Pending) {
700             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut);
701             // This is a little tricky. After calling CSSFontFace::font(Forbid), a font must never fail later in
702             // this turn of the runloop because the return value of CSSFontFace::font() shouldn't get nulled out
703             // from under an existing FontRanges object. Remote fonts are all downloaded asynchronously, so this
704             // isn't a problem for them because CSSFontFace::font() will always return the interstitial font.
705             // However, local fonts may synchronously fail when you call load() on them. Therefore, we have to call
706             // load() here in order to guarantee that, if the font synchronously fails, it happens now during the
707             // first call to CSSFontFace::font() and the FontRanges object sees a consistent view of the
708             // CSSFontFace. This means we eagerly create some internal font objects when they may not be needed,
709             // but it seems that this behavior is a requirement of the design of FontRanges. FIXME: Perhaps rethink
710             // this design.
711             if (policy == ExternalResourceDownloadPolicy::Allow || !source->requiresExternalResource()) {
712                 if (m_status == Status::Pending)
713                     setStatus(Status::Loading);
714                 source->load(m_fontSelector.get());
715             }
716         }
717
718         switch (source->status()) {
719         case CSSFontFaceSource::Status::Pending:
720             ASSERT(policy == ExternalResourceDownloadPolicy::Forbid);
721             return i;
722         case CSSFontFaceSource::Status::Loading:
723             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut || m_status == Status::Failure);
724             if (m_status == Status::Pending)
725                 setStatus(Status::Loading);
726             return i;
727         case CSSFontFaceSource::Status::Success:
728             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut || m_status == Status::Success || m_status == Status::Failure);
729             if (m_status == Status::Pending)
730                 setStatus(Status::Loading);
731             if (m_status == Status::Loading || m_status == Status::TimedOut)
732                 setStatus(Status::Success);
733             return i;
734         case CSSFontFaceSource::Status::Failure:
735             if (m_status == Status::Pending)
736                 setStatus(Status::Loading);
737             break;
738         }
739     }
740     if (m_sources.isEmpty() && m_status == Status::Pending)
741         setStatus(Status::Loading);
742     if (m_status == Status::Loading || m_status == Status::TimedOut)
743         setStatus(Status::Failure);
744     return m_sources.size();
745 }
746
747 void CSSFontFace::load()
748 {
749     pump(ExternalResourceDownloadPolicy::Allow);
750 }
751
752 static Font::Visibility visibility(CSSFontFace::Status status, CSSFontFace::FontLoadTiming timing)
753 {
754     switch (status) {
755     case CSSFontFace::Status::Pending:
756         return timing.blockPeriod == 0_s ? Font::Visibility::Visible : Font::Visibility::Invisible;
757     case CSSFontFace::Status::Loading:
758         return Font::Visibility::Invisible;
759     case CSSFontFace::Status::TimedOut:
760     case CSSFontFace::Status::Failure:
761     case CSSFontFace::Status::Success:
762     default:
763         return Font::Visibility::Visible;
764     }
765 }
766
767 RefPtr<Font> CSSFontFace::font(const FontDescription& fontDescription, bool syntheticBold, bool syntheticItalic, ExternalResourceDownloadPolicy policy)
768 {
769     if (computeFailureState())
770         return nullptr;
771
772     // Our status is derived from the first non-failed source. However, this source may
773     // return null from font(), which means we need to continue looping through the remainder
774     // of the sources to try to find a font to use. These subsequent tries should not affect
775     // our own state, though.
776     size_t startIndex = pump(policy);
777
778     if (computeFailureState())
779         return nullptr;
780
781     for (size_t i = startIndex; i < m_sources.size(); ++i) {
782         auto& source = m_sources[i];
783         if (source->status() == CSSFontFaceSource::Status::Pending && (policy == ExternalResourceDownloadPolicy::Allow || !source->requiresExternalResource()))
784             source->load(m_fontSelector.get());
785
786         switch (source->status()) {
787         case CSSFontFaceSource::Status::Pending:
788         case CSSFontFaceSource::Status::Loading: {
789             Font::Visibility visibility = WebCore::visibility(status(), fontLoadTiming());
790             return Font::create(FontCache::singleton().lastResortFallbackFont(fontDescription)->platformData(), Font::Origin::Local, Font::Interstitial::Yes, visibility);
791         }
792         case CSSFontFaceSource::Status::Success:
793             if (RefPtr<Font> result = source->font(fontDescription, syntheticBold, syntheticItalic, m_featureSettings, m_variantSettings, m_fontSelectionCapabilities))
794                 return result;
795             break;
796         case CSSFontFaceSource::Status::Failure:
797             break;
798         }
799     }
800
801     return nullptr;
802 }
803
804 bool CSSFontFace::purgeable() const
805 {
806     return cssConnection() && m_mayBePurged;
807 }
808
809 void CSSFontFace::updateStyleIfNeeded()
810 {
811     if (m_fontSelector && m_fontSelector->document())
812         m_fontSelector->document()->updateStyleIfNeeded();
813 }
814
815 #if ENABLE(SVG_FONTS)
816 bool CSSFontFace::hasSVGFontFaceSource() const
817 {
818     size_t size = m_sources.size();
819     for (size_t i = 0; i < size; i++) {
820         if (m_sources[i]->isSVGFontFaceSource())
821             return true;
822     }
823     return false;
824 }
825 #endif
826
827 }