0348cf9b2ba33b92a949356a871f3f69fbb2ead5
[WebKit.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 "CSSFontFamily.h"
32 #include "CSSFontFeatureValue.h"
33 #include "CSSFontSelector.h"
34 #include "CSSPrimitiveValueMappings.h"
35 #include "CSSSegmentedFontFace.h"
36 #include "CSSUnicodeRangeValue.h"
37 #include "CSSValue.h"
38 #include "CSSValueList.h"
39 #include "Document.h"
40 #include "Font.h"
41 #include "FontDescription.h"
42 #include "FontFace.h"
43 #include "FontVariantBuilder.h"
44 #include "RuntimeEnabledFeatures.h"
45 #include "Settings.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 std::optional<FontTraitsMask> CSSFontFace::calculateStyleMask(CSSValue& style)
128 {
129     if (!is<CSSPrimitiveValue>(style))
130         return std::nullopt;
131
132     switch (downcast<CSSPrimitiveValue>(style).valueID()) {
133     case CSSValueNormal:
134         return FontStyleNormalMask;
135     case CSSValueItalic:
136     case CSSValueOblique:
137         return FontStyleItalicMask;
138     default:
139         return FontStyleNormalMask;
140     }
141
142     return FontStyleNormalMask;
143 }
144
145 bool CSSFontFace::setStyle(CSSValue& style)
146 {
147     if (auto mask = calculateStyleMask(style)) {
148         m_traitsMask = static_cast<FontTraitsMask>((static_cast<unsigned>(m_traitsMask) & (~FontStyleMask)) | mask.value());
149
150         if (m_cssConnection)
151             m_cssConnection->mutableProperties().setProperty(CSSPropertyFontStyle, &style);
152
153         iterateClients(m_clients, [&](Client& client) {
154             client.fontPropertyChanged(*this);
155         });
156
157         return true;
158     }
159     return false;
160 }
161
162 std::optional<FontTraitsMask> CSSFontFace::calculateWeightMask(CSSValue& weight)
163 {
164     if (!is<CSSPrimitiveValue>(weight))
165         return std::nullopt;
166
167     switch (downcast<CSSPrimitiveValue>(weight).valueID()) {
168     case CSSValueBold:
169     case CSSValueBolder:
170     case CSSValue700:
171         return FontWeight700Mask;
172     case CSSValueNormal:
173     case CSSValue400:
174         return FontWeight400Mask;
175     case CSSValue900:
176         return FontWeight900Mask;
177     case CSSValue800:
178         return FontWeight800Mask;
179     case CSSValue600:
180         return FontWeight600Mask;
181     case CSSValue500:
182         return FontWeight500Mask;
183     case CSSValue300:
184         return FontWeight300Mask;
185     case CSSValueLighter:
186     case CSSValue200:
187         return FontWeight200Mask;
188     case CSSValue100:
189         return FontWeight100Mask;
190     default:
191         return FontWeight400Mask;
192     }
193
194     return FontWeight400Mask;
195 }
196
197 std::optional<FontSelectionValue> CSSFontFace::calculateStretch(CSSValue& stretch)
198 {
199     if (!is<CSSPrimitiveValue>(stretch))
200         return std::nullopt;
201
202     const auto& primitiveValue = downcast<CSSPrimitiveValue>(stretch);
203     if (primitiveValue.isPercentage() || primitiveValue.isNumber()) {
204         auto value = primitiveValue.floatValue();
205         if (value < static_cast<float>(FontSelectionValue::minimumValue()))
206             return FontSelectionValue::minimumValue();
207         if (value > static_cast<float>(FontSelectionValue::maximumValue()))
208             return FontSelectionValue::maximumValue();
209         return FontSelectionValue(value);
210     }
211
212     switch (primitiveValue.valueID()) {
213     case CSSValueUltraCondensed:
214         return FontSelectionValue(50);
215     case CSSValueExtraCondensed:
216         return FontSelectionValue(62.5f);
217     case CSSValueCondensed:
218         return FontSelectionValue(75);
219     case CSSValueSemiCondensed:
220         return FontSelectionValue(87.5f);
221     case CSSValueNormal:
222         return FontSelectionValue(100);
223     case CSSValueSemiExpanded:
224         return FontSelectionValue(112.5f);
225     case CSSValueExpanded:
226         return FontSelectionValue(125);
227     case CSSValueExtraExpanded:
228         return FontSelectionValue(150);
229     case CSSValueUltraExpanded:
230         return FontSelectionValue(200);
231     default:
232         ASSERT_NOT_REACHED();
233         return std::nullopt;
234     }
235 }
236
237 bool CSSFontFace::setWeight(CSSValue& weight)
238 {
239     if (auto mask = calculateWeightMask(weight)) {
240         m_traitsMask = static_cast<FontTraitsMask>((static_cast<unsigned>(m_traitsMask) & (~FontWeightMask)) | mask.value());
241
242         if (m_cssConnection)
243             m_cssConnection->mutableProperties().setProperty(CSSPropertyFontWeight, &weight);
244
245         iterateClients(m_clients, [&](Client& client) {
246             client.fontPropertyChanged(*this);
247         });
248
249         return true;
250     }
251
252     return false;
253 }
254
255 bool CSSFontFace::setStretch(CSSValue& stretch)
256 {
257     if (auto parsedStretch = calculateStretch(stretch)) {
258         m_stretch = FontSelectionRange(parsedStretch.value(), parsedStretch.value());
259
260         if (m_cssConnection)
261             m_cssConnection->mutableProperties().setProperty(CSSPropertyFontStretch, &stretch);
262
263         iterateClients(m_clients, [&](Client& client) {
264             client.fontPropertyChanged(*this);
265         });
266
267         return true;
268     }
269
270     return false;
271 }
272
273 bool CSSFontFace::setUnicodeRange(CSSValue& unicodeRange)
274 {
275     if (!is<CSSValueList>(unicodeRange))
276         return false;
277
278     m_ranges.clear();
279     auto& list = downcast<CSSValueList>(unicodeRange);
280     for (auto& rangeValue : list) {
281         auto& range = downcast<CSSUnicodeRangeValue>(rangeValue.get());
282         m_ranges.append({ range.from(), range.to() });
283     }
284
285     if (m_cssConnection)
286         m_cssConnection->mutableProperties().setProperty(CSSPropertyUnicodeRange, &unicodeRange);
287
288     iterateClients(m_clients, [&](Client& client) {
289         client.fontPropertyChanged(*this);
290     });
291
292     return true;
293 }
294
295 bool CSSFontFace::setVariantLigatures(CSSValue& variantLigatures)
296 {
297     auto ligatures = extractFontVariantLigatures(variantLigatures);
298
299     m_variantSettings.commonLigatures = ligatures.commonLigatures;
300     m_variantSettings.discretionaryLigatures = ligatures.discretionaryLigatures;
301     m_variantSettings.historicalLigatures = ligatures.historicalLigatures;
302     m_variantSettings.contextualAlternates = ligatures.contextualAlternates;
303
304     if (m_cssConnection)
305         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantLigatures, &variantLigatures);
306
307     iterateClients(m_clients, [&](Client& client) {
308         client.fontPropertyChanged(*this);
309     });
310
311     return true;
312 }
313
314 bool CSSFontFace::setVariantPosition(CSSValue& variantPosition)
315 {
316     if (!is<CSSPrimitiveValue>(variantPosition))
317         return false;
318
319     m_variantSettings.position = downcast<CSSPrimitiveValue>(variantPosition);
320
321     if (m_cssConnection)
322         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantPosition, &variantPosition);
323
324     iterateClients(m_clients, [&](Client& client) {
325         client.fontPropertyChanged(*this);
326     });
327
328     return true;
329 }
330
331 bool CSSFontFace::setVariantCaps(CSSValue& variantCaps)
332 {
333     if (!is<CSSPrimitiveValue>(variantCaps))
334         return false;
335
336     m_variantSettings.caps = downcast<CSSPrimitiveValue>(variantCaps);
337
338     if (m_cssConnection)
339         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantCaps, &variantCaps);
340
341     iterateClients(m_clients, [&](Client& client) {
342         client.fontPropertyChanged(*this);
343     });
344
345     return true;
346 }
347
348 bool CSSFontFace::setVariantNumeric(CSSValue& variantNumeric)
349 {
350     auto numeric = extractFontVariantNumeric(variantNumeric);
351
352     m_variantSettings.numericFigure = numeric.figure;
353     m_variantSettings.numericSpacing = numeric.spacing;
354     m_variantSettings.numericFraction = numeric.fraction;
355     m_variantSettings.numericOrdinal = numeric.ordinal;
356     m_variantSettings.numericSlashedZero = numeric.slashedZero;
357
358     if (m_cssConnection)
359         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantNumeric, &variantNumeric);
360
361     iterateClients(m_clients, [&](Client& client) {
362         client.fontPropertyChanged(*this);
363     });
364
365     return true;
366 }
367
368 bool CSSFontFace::setVariantAlternates(CSSValue& variantAlternates)
369 {
370     if (!is<CSSPrimitiveValue>(variantAlternates))
371         return false;
372
373     m_variantSettings.alternates = downcast<CSSPrimitiveValue>(variantAlternates);
374
375     if (m_cssConnection)
376         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantAlternates, &variantAlternates);
377
378     iterateClients(m_clients, [&](Client& client) {
379         client.fontPropertyChanged(*this);
380     });
381
382     return true;
383 }
384
385 bool CSSFontFace::setVariantEastAsian(CSSValue& variantEastAsian)
386 {
387     auto eastAsian = extractFontVariantEastAsian(variantEastAsian);
388
389     m_variantSettings.eastAsianVariant = eastAsian.variant;
390     m_variantSettings.eastAsianWidth = eastAsian.width;
391     m_variantSettings.eastAsianRuby = eastAsian.ruby;
392
393     if (m_cssConnection)
394         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontVariantEastAsian, &variantEastAsian);
395
396     iterateClients(m_clients, [&](Client& client) {
397         client.fontPropertyChanged(*this);
398     });
399
400     return true;
401 }
402
403 void CSSFontFace::setFeatureSettings(CSSValue& featureSettings)
404 {
405     // Can only call this with a primitive value of normal, or a value list containing font feature values.
406     ASSERT(is<CSSPrimitiveValue>(featureSettings) || is<CSSValueList>(featureSettings));
407
408     FontFeatureSettings settings;
409
410     if (is<CSSValueList>(featureSettings)) {
411         auto& list = downcast<CSSValueList>(featureSettings);
412         for (auto& rangeValue : list) {
413             auto& feature = downcast<CSSFontFeatureValue>(rangeValue.get());
414             settings.insert({ feature.tag(), feature.value() });
415         }
416     }
417
418     if (m_featureSettings == settings)
419         return;
420
421     m_featureSettings = WTFMove(settings);
422
423     if (m_cssConnection)
424         m_cssConnection->mutableProperties().setProperty(CSSPropertyFontFeatureSettings, &featureSettings);
425
426     iterateClients(m_clients, [&](Client& client) {
427         client.fontPropertyChanged(*this);
428     });
429 }
430
431 bool CSSFontFace::rangesMatchCodePoint(UChar32 character) const
432 {
433     if (m_ranges.isEmpty())
434         return true;
435
436     for (auto& range : m_ranges) {
437         if (range.from <= character && character <= range.to)
438             return true;
439     }
440     return false;
441 }
442
443 void CSSFontFace::fontLoadEventOccurred()
444 {
445     Ref<CSSFontFace> protectedThis(*this);
446
447     // If the font is already in the cache, CSSFontFaceSource may report it's loaded before it is added here as a source.
448     // Let's not pump the state machine until we've got all our sources. font() and load() are smart enough to act correctly
449     // when a source is failed or succeeded before we have asked it to load.
450     if (m_sourcesPopulated)
451         pump();
452
453     ASSERT(m_fontSelector);
454     m_fontSelector->fontLoaded();
455
456     iterateClients(m_clients, [&](Client& client) {
457         client.fontLoaded(*this);
458     });
459 }
460
461 void CSSFontFace::timeoutFired()
462 {
463     setStatus(Status::TimedOut);
464
465     fontLoadEventOccurred();
466 }
467
468 bool CSSFontFace::allSourcesFailed() const
469 {
470     for (auto& source : m_sources) {
471         if (source->status() != CSSFontFaceSource::Status::Failure)
472             return false;
473     }
474     return true;
475 }
476
477 void CSSFontFace::addClient(Client& client)
478 {
479     m_clients.add(&client);
480 }
481
482 void CSSFontFace::removeClient(Client& client)
483 {
484     ASSERT(m_clients.contains(&client));
485     m_clients.remove(&client);
486 }
487
488 void CSSFontFace::initializeWrapper()
489 {
490     switch (m_status) {
491     case Status::Pending:
492         break;
493     case Status::Loading:
494         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
495         break;
496     case Status::TimedOut:
497         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
498         m_wrapper->fontStateChanged(*this, Status::Loading, Status::TimedOut);
499         break;
500     case Status::Success:
501         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
502         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Success);
503         break;
504     case Status::Failure:
505         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
506         m_wrapper->fontStateChanged(*this, Status::Pending, Status::Failure);
507         break;
508     }
509     m_mayBePurged = false;
510 }
511
512 Ref<FontFace> CSSFontFace::wrapper()
513 {
514     if (m_wrapper)
515         return *m_wrapper.get();
516
517     auto wrapper = FontFace::create(*this);
518     m_wrapper = wrapper->createWeakPtr();
519     initializeWrapper();
520     return wrapper;
521 }
522
523 void CSSFontFace::setWrapper(FontFace& newWrapper)
524 {
525     m_wrapper = newWrapper.createWeakPtr();
526     initializeWrapper();
527 }
528
529 void CSSFontFace::adoptSource(std::unique_ptr<CSSFontFaceSource>&& source)
530 {
531     m_sources.append(WTFMove(source));
532
533     // We should never add sources in the middle of loading.
534     ASSERT(!m_sourcesPopulated);
535 }
536
537 void CSSFontFace::setStatus(Status newStatus)
538 {
539     switch (newStatus) {
540     case Status::Pending:
541         ASSERT_NOT_REACHED();
542         break;
543     case Status::Loading:
544         ASSERT(m_status == Status::Pending);
545         break;
546     case Status::TimedOut:
547         ASSERT(m_status == Status::Loading);
548         break;
549     case Status::Success:
550         ASSERT(m_status == Status::Loading || m_status == Status::TimedOut);
551         break;
552     case Status::Failure:
553         ASSERT(m_status == Status::Loading || m_status == Status::TimedOut);
554         break;
555     }
556
557     if (newStatus == Status::Loading)
558         m_timeoutTimer.startOneShot(webFontsShouldAlwaysFallBack() ? 0 : 3);
559     else if (newStatus == Status::Success || newStatus == Status::Failure)
560         m_timeoutTimer.stop();
561
562     iterateClients(m_clients, [&](Client& client) {
563         client.fontStateChanged(*this, m_status, newStatus);
564     });
565
566     m_status = newStatus;
567 }
568
569 void CSSFontFace::fontLoaded(CSSFontFaceSource&)
570 {
571     ASSERT(!webFontsShouldAlwaysFallBack());
572
573     fontLoadEventOccurred();
574 }
575
576 bool CSSFontFace::webFontsShouldAlwaysFallBack() const
577 {
578     return m_fontSelector && m_fontSelector->document() && m_fontSelector->document()->settings().webFontsAlwaysFallBack();
579 }
580
581 size_t CSSFontFace::pump()
582 {
583     size_t i;
584     for (i = 0; i < m_sources.size(); ++i) {
585         auto& source = m_sources[i];
586
587         if (source->status() == CSSFontFaceSource::Status::Pending) {
588             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut);
589             ASSERT(m_fontSelector);
590             if (m_status == Status::Pending)
591                 setStatus(Status::Loading);
592             source->load(*m_fontSelector);
593         }
594
595         switch (source->status()) {
596         case CSSFontFaceSource::Status::Pending:
597             ASSERT_NOT_REACHED();
598             break;
599         case CSSFontFaceSource::Status::Loading:
600             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut);
601             if (m_status == Status::Pending)
602                 setStatus(Status::Loading);
603             return i;
604         case CSSFontFaceSource::Status::Success:
605             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut || m_status == Status::Success);
606             if (m_status == Status::Pending)
607                 setStatus(Status::Loading);
608             if (m_status == Status::Loading || m_status == Status::TimedOut)
609                 setStatus(Status::Success);
610             return i;
611         case CSSFontFaceSource::Status::Failure:
612             if (m_status == Status::Pending)
613                 setStatus(Status::Loading);
614             break;
615         }
616     }
617     if (m_sources.isEmpty() && m_status == Status::Pending)
618         setStatus(Status::Loading);
619     if (m_status == Status::Loading || m_status == Status::TimedOut)
620         setStatus(Status::Failure);
621     return m_sources.size();
622 }
623
624 void CSSFontFace::load()
625 {
626     pump();
627 }
628
629 RefPtr<Font> CSSFontFace::font(const FontDescription& fontDescription, bool syntheticBold, bool syntheticItalic)
630 {
631     if (allSourcesFailed())
632         return nullptr;
633
634     // Our status is derived from the first non-failed source. However, this source may
635     // return null from font(), which means we need to continue looping through the remainder
636     // of the sources to try to find a font to use. These subsequent tries should not affect
637     // our own state, though.
638     size_t startIndex = pump();
639     bool fontIsLoading = false;
640     for (size_t i = startIndex; i < m_sources.size(); ++i) {
641         auto& source = m_sources[i];
642         if (source->status() == CSSFontFaceSource::Status::Pending) {
643             ASSERT(m_fontSelector);
644             if (fontIsLoading)
645                 continue;
646             source->load(*m_fontSelector);
647         }
648
649         switch (source->status()) {
650         case CSSFontFaceSource::Status::Pending:
651             ASSERT_NOT_REACHED();
652             break;
653         case CSSFontFaceSource::Status::Loading:
654             ASSERT(!fontIsLoading);
655             fontIsLoading = true;
656             if (status() == Status::TimedOut)
657                 continue;
658             return Font::create(FontCache::singleton().lastResortFallbackFontForEveryCharacter(fontDescription)->platformData(), true, true);
659         case CSSFontFaceSource::Status::Success:
660             if (RefPtr<Font> result = source->font(fontDescription, syntheticBold, syntheticItalic, m_featureSettings, m_variantSettings))
661                 return result;
662             break;
663         case CSSFontFaceSource::Status::Failure:
664             break;
665         }
666     }
667
668     return nullptr;
669 }
670
671 bool CSSFontFace::purgeable() const
672 {
673     return cssConnection() && m_mayBePurged;
674 }
675
676 void CSSFontFace::updateStyleIfNeeded()
677 {
678     if (m_fontSelector && m_fontSelector->document())
679         m_fontSelector->document()->updateStyleIfNeeded();
680 }
681
682 #if ENABLE(SVG_FONTS)
683 bool CSSFontFace::hasSVGFontFaceSource() const
684 {
685     size_t size = m_sources.size();
686     for (size_t i = 0; i < size; i++) {
687         if (m_sources[i]->isSVGFontFaceSource())
688             return true;
689     }
690     return false;
691 }
692 #endif
693
694 }