Add modern API for overriding the page's specified viewport configuration
[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_fontSelector(fontSelector)
93     , m_cssConnection(cssConnection)
94     , m_wrapper(makeWeakPtr(wrapper))
95     , m_isLocalFallback(isLocalFallback)
96     , m_mayBePurged(!wrapper)
97     , m_timeoutTimer(*this, &CSSFontFace::timeoutFired)
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.valueOr(normalItalicValue()), result.valueOr(normalItalicValue()) };
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 = makeWeakPtr(wrapper.get());
567     initializeWrapper();
568     return wrapper;
569 }
570
571 void CSSFontFace::setWrapper(FontFace& newWrapper)
572 {
573     m_wrapper = makeWeakPtr(newWrapper);
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 AllowUserInstalledFonts CSSFontFace::allowUserInstalledFonts() const
586 {
587     if (m_fontSelector && m_fontSelector->document())
588         return m_fontSelector->document()->settings().shouldAllowUserInstalledFonts() ? AllowUserInstalledFonts::Yes : AllowUserInstalledFonts::No;
589     return AllowUserInstalledFonts::Yes;
590 }
591
592 static Settings::FontLoadTimingOverride fontLoadTimingOverride(CSSFontSelector* fontSelector)
593 {
594     auto overrideValue = Settings::FontLoadTimingOverride::None;
595     if (fontSelector && fontSelector->document())
596         overrideValue = fontSelector->document()->settings().fontLoadTimingOverride();
597     return overrideValue;
598 }
599
600 auto CSSFontFace::fontLoadTiming() const -> FontLoadTiming
601 {
602     switch (fontLoadTimingOverride(m_fontSelector.get())) {
603     case Settings::FontLoadTimingOverride::None:
604         switch (m_loadingBehavior) {
605         case FontLoadingBehavior::Auto:
606         case FontLoadingBehavior::Block:
607             return { 3_s, Seconds::infinity() };
608         case FontLoadingBehavior::Swap:
609             return { 0_s, Seconds::infinity() };
610         case FontLoadingBehavior::Fallback:
611             return { 0.1_s, 3_s };
612         case FontLoadingBehavior::Optional:
613             return { 0.1_s, 0_s };
614         }
615         RELEASE_ASSERT_NOT_REACHED();
616     case Settings::FontLoadTimingOverride::Block:
617         return { Seconds::infinity(), 0_s };
618     case Settings::FontLoadTimingOverride::Swap:
619         return { 0_s, Seconds::infinity() };
620     case Settings::FontLoadTimingOverride::Failure:
621         return { 0_s, 0_s };
622     }
623     RELEASE_ASSERT_NOT_REACHED();
624 }
625
626 void CSSFontFace::setStatus(Status newStatus)
627 {
628     switch (newStatus) {
629     case Status::Pending:
630         ASSERT_NOT_REACHED();
631         break;
632     case Status::Loading:
633         ASSERT(m_status == Status::Pending);
634         break;
635     case Status::TimedOut:
636         ASSERT(m_status == Status::Loading);
637         break;
638     case Status::Success:
639         ASSERT(m_status == Status::Loading || m_status == Status::TimedOut);
640         break;
641     case Status::Failure:
642         ASSERT(m_status == Status::Loading || m_status == Status::TimedOut);
643         break;
644     }
645
646     iterateClients(m_clients, [&](Client& client) {
647         client.fontStateChanged(*this, m_status, newStatus);
648     });
649
650     m_status = newStatus;
651
652     Seconds blockPeriodTimeout;
653     Seconds swapPeriodTimeout;
654     auto timeouts = fontLoadTiming();
655     blockPeriodTimeout = timeouts.blockPeriod;
656     swapPeriodTimeout = timeouts.swapPeriod;
657
658     // Transfer across 0-delay timers synchronously. Layouts/script may
659     // take arbitrarily long time, and we shouldn't be in a 0-duration
660     // state for an arbitrarily long time. Also it's necessary for
661     // testing so we don't have a race with the font load.
662     switch (newStatus) {
663     case Status::Pending:
664         ASSERT_NOT_REACHED();
665         break;
666     case Status::Loading:
667         if (blockPeriodTimeout == 0_s)
668             setStatus(Status::TimedOut);
669         else if (std::isfinite(blockPeriodTimeout.value()))
670             m_timeoutTimer.startOneShot(blockPeriodTimeout);
671         break;
672     case Status::TimedOut:
673         if (swapPeriodTimeout == 0_s)
674             setStatus(Status::Failure);
675         else if (std::isfinite(swapPeriodTimeout.value()))
676             m_timeoutTimer.startOneShot(swapPeriodTimeout);
677         break;
678     case Status::Success:
679     case Status::Failure:
680         m_timeoutTimer.stop();
681         break;
682     }
683 }
684
685 void CSSFontFace::fontLoaded(CSSFontFaceSource&)
686 {
687     fontLoadEventOccurred();
688 }
689
690 bool CSSFontFace::shouldIgnoreFontLoadCompletions() const
691 {
692     if (m_fontSelector && m_fontSelector->document())
693         return m_fontSelector->document()->settings().shouldIgnoreFontLoadCompletions();
694     return false;
695 }
696
697 void CSSFontFace::opportunisticallyStartFontDataURLLoading(CSSFontSelector& fontSelector)
698 {
699     // We don't want to go crazy here and blow the cache. Usually these data URLs are the first item in the src: list, so let's just check that one.
700     if (!m_sources.isEmpty())
701         m_sources[0]->opportunisticallyStartFontDataURLLoading(fontSelector);
702 }
703
704 size_t CSSFontFace::pump(ExternalResourceDownloadPolicy policy)
705 {
706     if (status() == Status::Failure)
707         return 0;
708
709     size_t i;
710     for (i = 0; i < m_sources.size(); ++i) {
711         auto& source = m_sources[i];
712
713         if (source->status() == CSSFontFaceSource::Status::Pending) {
714             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut);
715             // This is a little tricky. After calling CSSFontFace::font(Forbid), a font must never fail later in
716             // this turn of the runloop because the return value of CSSFontFace::font() shouldn't get nulled out
717             // from under an existing FontRanges object. Remote fonts are all downloaded asynchronously, so this
718             // isn't a problem for them because CSSFontFace::font() will always return the interstitial font.
719             // However, local fonts may synchronously fail when you call load() on them. Therefore, we have to call
720             // load() here in order to guarantee that, if the font synchronously fails, it happens now during the
721             // first call to CSSFontFace::font() and the FontRanges object sees a consistent view of the
722             // CSSFontFace. This means we eagerly create some internal font objects when they may not be needed,
723             // but it seems that this behavior is a requirement of the design of FontRanges. FIXME: Perhaps rethink
724             // this design.
725             if (policy == ExternalResourceDownloadPolicy::Allow || !source->requiresExternalResource()) {
726                 if (m_status == Status::Pending)
727                     setStatus(Status::Loading);
728                 source->load(m_fontSelector.get());
729             }
730         }
731
732         switch (source->status()) {
733         case CSSFontFaceSource::Status::Pending:
734             ASSERT(policy == ExternalResourceDownloadPolicy::Forbid);
735             return i;
736         case CSSFontFaceSource::Status::Loading:
737             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut || m_status == Status::Failure);
738             if (m_status == Status::Pending)
739                 setStatus(Status::Loading);
740             return i;
741         case CSSFontFaceSource::Status::Success:
742             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut || m_status == Status::Success || m_status == Status::Failure);
743             if (m_status == Status::Pending)
744                 setStatus(Status::Loading);
745             if (m_status == Status::Loading || m_status == Status::TimedOut)
746                 setStatus(Status::Success);
747             return i;
748         case CSSFontFaceSource::Status::Failure:
749             if (m_status == Status::Pending)
750                 setStatus(Status::Loading);
751             break;
752         }
753     }
754     if (m_sources.isEmpty() && m_status == Status::Pending)
755         setStatus(Status::Loading);
756     if (m_status == Status::Loading || m_status == Status::TimedOut)
757         setStatus(Status::Failure);
758     return m_sources.size();
759 }
760
761 void CSSFontFace::load()
762 {
763     pump(ExternalResourceDownloadPolicy::Allow);
764 }
765
766 static Font::Visibility visibility(CSSFontFace::Status status, CSSFontFace::FontLoadTiming timing)
767 {
768     switch (status) {
769     case CSSFontFace::Status::Pending:
770         return timing.blockPeriod == 0_s ? Font::Visibility::Visible : Font::Visibility::Invisible;
771     case CSSFontFace::Status::Loading:
772         return Font::Visibility::Invisible;
773     case CSSFontFace::Status::TimedOut:
774     case CSSFontFace::Status::Failure:
775     case CSSFontFace::Status::Success:
776     default:
777         return Font::Visibility::Visible;
778     }
779 }
780
781 RefPtr<Font> CSSFontFace::font(const FontDescription& fontDescription, bool syntheticBold, bool syntheticItalic, ExternalResourceDownloadPolicy policy)
782 {
783     if (computeFailureState())
784         return nullptr;
785
786     // Our status is derived from the first non-failed source. However, this source may
787     // return null from font(), which means we need to continue looping through the remainder
788     // of the sources to try to find a font to use. These subsequent tries should not affect
789     // our own state, though.
790     size_t startIndex = pump(policy);
791
792     if (computeFailureState())
793         return nullptr;
794
795     for (size_t i = startIndex; i < m_sources.size(); ++i) {
796         auto& source = m_sources[i];
797         if (source->status() == CSSFontFaceSource::Status::Pending && (policy == ExternalResourceDownloadPolicy::Allow || !source->requiresExternalResource()))
798             source->load(m_fontSelector.get());
799
800         switch (source->status()) {
801         case CSSFontFaceSource::Status::Pending:
802         case CSSFontFaceSource::Status::Loading: {
803             Font::Visibility visibility = WebCore::visibility(status(), fontLoadTiming());
804             return Font::create(FontCache::singleton().lastResortFallbackFont(fontDescription)->platformData(), Font::Origin::Local, Font::Interstitial::Yes, visibility);
805         }
806         case CSSFontFaceSource::Status::Success:
807             if (RefPtr<Font> result = source->font(fontDescription, syntheticBold, syntheticItalic, m_featureSettings, m_variantSettings, m_fontSelectionCapabilities))
808                 return result;
809             break;
810         case CSSFontFaceSource::Status::Failure:
811             break;
812         }
813     }
814
815     return nullptr;
816 }
817
818 bool CSSFontFace::purgeable() const
819 {
820     return cssConnection() && m_mayBePurged;
821 }
822
823 void CSSFontFace::updateStyleIfNeeded()
824 {
825     if (m_fontSelector && m_fontSelector->document())
826         m_fontSelector->document()->updateStyleIfNeeded();
827 }
828
829 #if ENABLE(SVG_FONTS)
830 bool CSSFontFace::hasSVGFontFaceSource() const
831 {
832     size_t size = m_sources.size();
833     for (size_t i = 0; i < size; i++) {
834         if (m_sources[i]->isSVGFontFaceSource())
835             return true;
836     }
837     return false;
838 }
839 #endif
840
841 }