Unreviewed, rolling out r201887.
[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 "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             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 {
98 }
99
100 CSSFontFace::~CSSFontFace()
101 {
102 }
103
104 bool CSSFontFace::setFamilies(CSSValue& family)
105 {
106     if (!is<CSSValueList>(family))
107         return false;
108
109     CSSValueList& familyList = downcast<CSSValueList>(family);
110     if (!familyList.length())
111         return false;
112
113     RefPtr<CSSValueList> oldFamilies = m_families;
114     m_families = &familyList;
115
116     iterateClients(m_clients, [&](Client& client) {
117         client.fontPropertyChanged(*this, oldFamilies.get());
118     });
119
120     return true;
121 }
122
123 Optional<FontTraitsMask> CSSFontFace::calculateStyleMask(CSSValue& style)
124 {
125     if (!is<CSSPrimitiveValue>(style))
126         return Nullopt;
127
128     switch (downcast<CSSPrimitiveValue>(style).getValueID()) {
129     case CSSValueNormal:
130         return FontStyleNormalMask;
131     case CSSValueItalic:
132     case CSSValueOblique:
133         return FontStyleItalicMask;
134     default:
135         return FontStyleNormalMask;
136     }
137
138     return FontStyleNormalMask;
139 }
140
141 bool CSSFontFace::setStyle(CSSValue& style)
142 {
143     if (auto mask = calculateStyleMask(style)) {
144         m_traitsMask = static_cast<FontTraitsMask>((static_cast<unsigned>(m_traitsMask) & (~FontStyleMask)) | mask.value());
145
146         iterateClients(m_clients, [&](Client& client) {
147             client.fontPropertyChanged(*this);
148         });
149
150         return true;
151     }
152     return false;
153 }
154
155 Optional<FontTraitsMask> CSSFontFace::calculateWeightMask(CSSValue& weight)
156 {
157     if (!is<CSSPrimitiveValue>(weight))
158         return Nullopt;
159
160     switch (downcast<CSSPrimitiveValue>(weight).getValueID()) {
161     case CSSValueBold:
162     case CSSValueBolder:
163     case CSSValue700:
164         return FontWeight700Mask;
165     case CSSValueNormal:
166     case CSSValue400:
167         return FontWeight400Mask;
168     case CSSValue900:
169         return FontWeight900Mask;
170     case CSSValue800:
171         return FontWeight800Mask;
172     case CSSValue600:
173         return FontWeight600Mask;
174     case CSSValue500:
175         return FontWeight500Mask;
176     case CSSValue300:
177         return FontWeight300Mask;
178     case CSSValueLighter:
179     case CSSValue200:
180         return FontWeight200Mask;
181     case CSSValue100:
182         return FontWeight100Mask;
183     default:
184         return FontWeight400Mask;
185     }
186
187     return FontWeight400Mask;
188 }
189
190 bool CSSFontFace::setWeight(CSSValue& weight)
191 {
192     if (auto mask = calculateWeightMask(weight)) {
193         m_traitsMask = static_cast<FontTraitsMask>((static_cast<unsigned>(m_traitsMask) & (~FontWeightMask)) | mask.value());
194
195         iterateClients(m_clients, [&](Client& client) {
196             client.fontPropertyChanged(*this);
197         });
198
199         return true;
200     }
201
202     return false;
203 }
204
205 bool CSSFontFace::setUnicodeRange(CSSValue& unicodeRange)
206 {
207     if (!is<CSSValueList>(unicodeRange))
208         return false;
209
210     m_ranges.clear();
211     auto& list = downcast<CSSValueList>(unicodeRange);
212     for (auto& rangeValue : list) {
213         auto& range = downcast<CSSUnicodeRangeValue>(rangeValue.get());
214         m_ranges.append({ range.from(), range.to() });
215     }
216
217     iterateClients(m_clients, [&](Client& client) {
218         client.fontPropertyChanged(*this);
219     });
220
221     return true;
222 }
223
224 bool CSSFontFace::setVariantLigatures(CSSValue& variantLigatures)
225 {
226     auto ligatures = extractFontVariantLigatures(variantLigatures);
227
228     m_variantSettings.commonLigatures = ligatures.commonLigatures;
229     m_variantSettings.discretionaryLigatures = ligatures.discretionaryLigatures;
230     m_variantSettings.historicalLigatures = ligatures.historicalLigatures;
231     m_variantSettings.contextualAlternates = ligatures.contextualAlternates;
232
233     iterateClients(m_clients, [&](Client& client) {
234         client.fontPropertyChanged(*this);
235     });
236
237     return true;
238 }
239
240 bool CSSFontFace::setVariantPosition(CSSValue& variantPosition)
241 {
242     if (!is<CSSPrimitiveValue>(variantPosition))
243         return false;
244
245     m_variantSettings.position = downcast<CSSPrimitiveValue>(variantPosition);
246
247     iterateClients(m_clients, [&](Client& client) {
248         client.fontPropertyChanged(*this);
249     });
250
251     return true;
252 }
253
254 bool CSSFontFace::setVariantCaps(CSSValue& variantCaps)
255 {
256     if (!is<CSSPrimitiveValue>(variantCaps))
257         return false;
258
259     m_variantSettings.caps = downcast<CSSPrimitiveValue>(variantCaps);
260
261     iterateClients(m_clients, [&](Client& client) {
262         client.fontPropertyChanged(*this);
263     });
264
265     return true;
266 }
267
268 bool CSSFontFace::setVariantNumeric(CSSValue& variantNumeric)
269 {
270     auto numeric = extractFontVariantNumeric(variantNumeric);
271
272     m_variantSettings.numericFigure = numeric.figure;
273     m_variantSettings.numericSpacing = numeric.spacing;
274     m_variantSettings.numericFraction = numeric.fraction;
275     m_variantSettings.numericOrdinal = numeric.ordinal;
276     m_variantSettings.numericSlashedZero = numeric.slashedZero;
277
278     iterateClients(m_clients, [&](Client& client) {
279         client.fontPropertyChanged(*this);
280     });
281
282     return true;
283 }
284
285 bool CSSFontFace::setVariantAlternates(CSSValue& variantAlternates)
286 {
287     if (!is<CSSPrimitiveValue>(variantAlternates))
288         return false;
289
290     m_variantSettings.alternates = downcast<CSSPrimitiveValue>(variantAlternates);
291
292     iterateClients(m_clients, [&](Client& client) {
293         client.fontPropertyChanged(*this);
294     });
295
296     return true;
297 }
298
299 bool CSSFontFace::setVariantEastAsian(CSSValue& variantEastAsian)
300 {
301     auto eastAsian = extractFontVariantEastAsian(variantEastAsian);
302
303     m_variantSettings.eastAsianVariant = eastAsian.variant;
304     m_variantSettings.eastAsianWidth = eastAsian.width;
305     m_variantSettings.eastAsianRuby = eastAsian.ruby;
306
307     iterateClients(m_clients, [&](Client& client) {
308         client.fontPropertyChanged(*this);
309     });
310
311     return true;
312 }
313
314 void CSSFontFace::setFeatureSettings(CSSValue& featureSettings)
315 {
316     // Can only call this with a primitive value of normal, or a value list containing font feature values.
317     ASSERT(is<CSSPrimitiveValue>(featureSettings) || is<CSSValueList>(featureSettings));
318
319     FontFeatureSettings settings;
320
321     if (is<CSSValueList>(featureSettings)) {
322         auto& list = downcast<CSSValueList>(featureSettings);
323         for (auto& rangeValue : list) {
324             auto& feature = downcast<CSSFontFeatureValue>(rangeValue.get());
325             settings.insert({ feature.tag(), feature.value() });
326         }
327     }
328
329     if (m_featureSettings == settings)
330         return;
331
332     m_featureSettings = WTFMove(settings);
333
334     iterateClients(m_clients, [&](Client& client) {
335         client.fontPropertyChanged(*this);
336     });
337 }
338
339 void CSSFontFace::fontLoadEventOccurred()
340 {
341     Ref<CSSFontFace> protectedThis(*this);
342
343     // If the font is already in the cache, CSSFontFaceSource may report it's loaded before it is added here as a source.
344     // Let's not pump the state machine until we've got all our sources. font() and load() are smart enough to act correctly
345     // when a source is failed or succeeded before we have asked it to load.
346     if (m_sourcesPopulated)
347         pump();
348
349     ASSERT(m_fontSelector);
350     m_fontSelector->fontLoaded();
351
352     iterateClients(m_clients, [&](Client& client) {
353         client.fontLoaded(*this);
354     });
355 }
356
357 void CSSFontFace::timeoutFired()
358 {
359     setStatus(Status::TimedOut);
360
361     fontLoadEventOccurred();
362 }
363
364 bool CSSFontFace::allSourcesFailed() const
365 {
366     for (auto& source : m_sources) {
367         if (source->status() != CSSFontFaceSource::Status::Failure)
368             return false;
369     }
370     return true;
371 }
372
373 void CSSFontFace::addClient(Client& client)
374 {
375     m_clients.add(&client);
376 }
377
378 void CSSFontFace::removeClient(Client& client)
379 {
380     ASSERT(m_clients.contains(&client));
381     m_clients.remove(&client);
382 }
383
384 Ref<FontFace> CSSFontFace::wrapper()
385 {
386     if (m_wrapper)
387         return Ref<FontFace>(*m_wrapper.get());
388
389     Ref<FontFace> wrapper = FontFace::create(*this);
390     switch (m_status) {
391     case Status::Pending:
392         break;
393     case Status::Loading:
394         wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
395         break;
396     case Status::TimedOut:
397         wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
398         wrapper->fontStateChanged(*this, Status::Loading, Status::TimedOut);
399         break;
400     case Status::Success:
401         wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
402         wrapper->fontStateChanged(*this, Status::Pending, Status::Success);
403         break;
404     case Status::Failure:
405         wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
406         wrapper->fontStateChanged(*this, Status::Pending, Status::Failure);
407         break;
408     }
409     m_wrapper = wrapper->createWeakPtr();
410     return wrapper;
411 }
412
413 void CSSFontFace::adoptSource(std::unique_ptr<CSSFontFaceSource>&& source)
414 {
415     m_sources.append(WTFMove(source));
416
417     // We should never add sources in the middle of loading.
418     ASSERT(!m_sourcesPopulated);
419 }
420
421 void CSSFontFace::setStatus(Status newStatus)
422 {
423     switch (newStatus) {
424     case Status::Pending:
425         ASSERT_NOT_REACHED();
426         break;
427     case Status::Loading:
428         ASSERT(m_status == Status::Pending);
429         break;
430     case Status::TimedOut:
431         ASSERT(m_status == Status::Loading);
432         break;
433     case Status::Success:
434         ASSERT(m_status == Status::Loading || m_status == Status::TimedOut);
435         break;
436     case Status::Failure:
437         ASSERT(m_status == Status::Loading || m_status == Status::TimedOut);
438         break;
439     }
440
441     if (newStatus == Status::Loading)
442         m_timeoutTimer.startOneShot(webFontsShouldAlwaysFallBack() ? 0 : 3);
443     else if (newStatus == Status::Success || newStatus == Status::Failure)
444         m_timeoutTimer.stop();
445
446     iterateClients(m_clients, [&](Client& client) {
447         client.fontStateChanged(*this, m_status, newStatus);
448     });
449
450     m_status = newStatus;
451 }
452
453 void CSSFontFace::fontLoaded(CSSFontFaceSource&)
454 {
455     ASSERT(!webFontsShouldAlwaysFallBack());
456
457     fontLoadEventOccurred();
458 }
459
460 bool CSSFontFace::webFontsShouldAlwaysFallBack() const
461 {
462     return m_fontSelector && m_fontSelector->document() && m_fontSelector->document()->settings() && m_fontSelector->document()->settings()->webFontsAlwaysFallBack();
463 }
464
465 size_t CSSFontFace::pump()
466 {
467     size_t i;
468     for (i = 0; i < m_sources.size(); ++i) {
469         auto& source = m_sources[i];
470
471         if (source->status() == CSSFontFaceSource::Status::Pending) {
472             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut);
473             ASSERT(m_fontSelector);
474             if (m_status == Status::Pending)
475                 setStatus(Status::Loading);
476             source->load(*m_fontSelector);
477         }
478
479         switch (source->status()) {
480         case CSSFontFaceSource::Status::Pending:
481             ASSERT_NOT_REACHED();
482             break;
483         case CSSFontFaceSource::Status::Loading:
484             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut);
485             if (m_status == Status::Pending)
486                 setStatus(Status::Loading);
487             return i;
488         case CSSFontFaceSource::Status::Success:
489             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut || m_status == Status::Success);
490             if (m_status == Status::Pending)
491                 setStatus(Status::Loading);
492             if (m_status == Status::Loading || m_status == Status::TimedOut)
493                 setStatus(Status::Success);
494             return i;
495         case CSSFontFaceSource::Status::Failure:
496             if (m_status == Status::Pending)
497                 setStatus(Status::Loading);
498             break;
499         }
500     }
501     if (m_sources.isEmpty() && m_status == Status::Pending)
502         setStatus(Status::Loading);
503     if (m_status == Status::Loading || m_status == Status::TimedOut)
504         setStatus(Status::Failure);
505     return m_sources.size();
506 }
507
508 void CSSFontFace::load()
509 {
510     pump();
511 }
512
513 RefPtr<Font> CSSFontFace::font(const FontDescription& fontDescription, bool syntheticBold, bool syntheticItalic)
514 {
515     if (allSourcesFailed())
516         return nullptr;
517
518     // Our status is derived from the first non-failed source. However, this source may
519     // return null from font(), which means we need to continue looping through the remainder
520     // of the sources to try to find a font to use. These subsequent tries should not affect
521     // our own state, though.
522     size_t startIndex = pump();
523     bool fontIsLoading = false;
524     for (size_t i = startIndex; i < m_sources.size(); ++i) {
525         auto& source = m_sources[i];
526         if (source->status() == CSSFontFaceSource::Status::Pending) {
527             ASSERT(m_fontSelector);
528             if (fontIsLoading)
529                 continue;
530             source->load(*m_fontSelector);
531         }
532
533         switch (source->status()) {
534         case CSSFontFaceSource::Status::Pending:
535             ASSERT_NOT_REACHED();
536             break;
537         case CSSFontFaceSource::Status::Loading:
538             ASSERT(!fontIsLoading);
539             fontIsLoading = true;
540             if (status() == Status::TimedOut)
541                 continue;
542             return Font::create(FontCache::singleton().lastResortFallbackFont(fontDescription)->platformData(), true, true);
543         case CSSFontFaceSource::Status::Success:
544             if (RefPtr<Font> result = source->font(fontDescription, syntheticBold, syntheticItalic, m_featureSettings, m_variantSettings))
545                 return result;
546             break;
547         case CSSFontFaceSource::Status::Failure:
548             break;
549         }
550     }
551
552     return nullptr;
553 }
554
555 #if ENABLE(SVG_FONTS)
556 bool CSSFontFace::hasSVGFontFaceSource() const
557 {
558     size_t size = m_sources.size();
559     for (size_t i = 0; i < size; i++) {
560         if (m_sources[i]->isSVGFontFaceSource())
561             return true;
562     }
563     return false;
564 }
565 #endif
566
567 }