[Font Loading] Split CSSFontSelector into a FontFaceSet implementation and the rest...
[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 void CSSFontFace::appendSources(CSSFontFace& fontFace, CSSValueList& srcList, Document* document, bool isInitiatingElementInUserAgentShadowTree)
52 {
53     for (auto& src : srcList) {
54         // An item in the list either specifies a string (local font name) or a URL (remote font to download).
55         CSSFontFaceSrcValue& item = downcast<CSSFontFaceSrcValue>(src.get());
56         std::unique_ptr<CSSFontFaceSource> source;
57         SVGFontFaceElement* fontFaceElement = nullptr;
58         bool foundSVGFont = false;
59
60 #if ENABLE(SVG_FONTS)
61         foundSVGFont = item.isSVGFontFaceSrc() || item.svgFontFaceElement();
62         fontFaceElement = item.svgFontFaceElement();
63 #endif
64         if (!item.isLocal()) {
65             Settings* settings = document ? document->settings() : nullptr;
66             bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled());
67             if (allowDownloading && item.isSupportedFormat() && document) {
68                 if (CachedFont* cachedFont = item.cachedFont(document, foundSVGFont, isInitiatingElementInUserAgentShadowTree))
69                     source = std::make_unique<CSSFontFaceSource>(fontFace, item.resource(), cachedFont);
70             }
71         } else
72             source = std::make_unique<CSSFontFaceSource>(fontFace, item.resource(), nullptr, fontFaceElement);
73
74         if (source)
75             fontFace.adoptSource(WTFMove(source));
76     }
77     fontFace.sourcesPopulated();
78 }
79
80 CSSFontFace::CSSFontFace(CSSFontSelector* fontSelector, StyleRuleFontFace* cssConnection, FontFace* wrapper, bool isLocalFallback)
81     : m_fontSelector(fontSelector)
82     , m_cssConnection(cssConnection)
83     , m_wrapper(wrapper ? wrapper->createWeakPtr() : WeakPtr<FontFace>())
84     , m_isLocalFallback(isLocalFallback)
85 {
86 }
87
88 CSSFontFace::~CSSFontFace()
89 {
90 }
91
92 void CSSFontFace::notifyClientsOfFontPropertyChange()
93 {
94     auto clientsCopy = m_clients;
95     for (auto* client : clientsCopy) {
96         if (m_clients.contains(client))
97             client->fontPropertyChanged(*this);
98     }
99 }
100
101 bool CSSFontFace::setFamilies(CSSValue& family)
102 {
103     if (!is<CSSValueList>(family))
104         return false;
105
106     CSSValueList& familyList = downcast<CSSValueList>(family);
107     if (!familyList.length())
108         return false;
109
110     RefPtr<CSSValueList> oldFamilies = m_families;
111     m_families = &familyList;
112
113     auto clientsCopy = m_clients;
114     for (auto* client : clientsCopy) {
115         if (m_clients.contains(client))
116             client->fontPropertyChanged(*this, oldFamilies.get());
117     }
118
119     return true;
120 }
121
122 Optional<FontTraitsMask> CSSFontFace::calculateStyleMask(CSSValue& style)
123 {
124     if (!is<CSSPrimitiveValue>(style))
125         return Nullopt;
126
127     switch (downcast<CSSPrimitiveValue>(style).getValueID()) {
128     case CSSValueNormal:
129         return FontStyleNormalMask;
130     case CSSValueItalic:
131     case CSSValueOblique:
132         return FontStyleItalicMask;
133     default:
134         return FontStyleNormalMask;
135     }
136
137     return FontStyleNormalMask;
138 }
139
140 bool CSSFontFace::setStyle(CSSValue& style)
141 {
142     if (auto mask = calculateStyleMask(style)) {
143         m_traitsMask = static_cast<FontTraitsMask>((static_cast<unsigned>(m_traitsMask) & (~FontStyleMask)) | mask.value());
144
145         notifyClientsOfFontPropertyChange();
146
147         return true;
148     }
149     return false;
150 }
151
152 Optional<FontTraitsMask> CSSFontFace::calculateWeightMask(CSSValue& weight)
153 {
154     if (!is<CSSPrimitiveValue>(weight))
155         return Nullopt;
156
157     switch (downcast<CSSPrimitiveValue>(weight).getValueID()) {
158     case CSSValueBold:
159     case CSSValueBolder:
160     case CSSValue700:
161         return FontWeight700Mask;
162     case CSSValueNormal:
163     case CSSValue400:
164         return FontWeight400Mask;
165     case CSSValue900:
166         return FontWeight900Mask;
167     case CSSValue800:
168         return FontWeight800Mask;
169     case CSSValue600:
170         return FontWeight600Mask;
171     case CSSValue500:
172         return FontWeight500Mask;
173     case CSSValue300:
174         return FontWeight300Mask;
175     case CSSValueLighter:
176     case CSSValue200:
177         return FontWeight200Mask;
178     case CSSValue100:
179         return FontWeight100Mask;
180     default:
181         return FontWeight400Mask;
182     }
183
184     return FontWeight400Mask;
185 }
186
187 bool CSSFontFace::setWeight(CSSValue& weight)
188 {
189     if (auto mask = calculateWeightMask(weight)) {
190         m_traitsMask = static_cast<FontTraitsMask>((static_cast<unsigned>(m_traitsMask) & (~FontWeightMask)) | mask.value());
191
192         notifyClientsOfFontPropertyChange();
193
194         return true;
195     }
196
197     return false;
198 }
199
200 bool CSSFontFace::setUnicodeRange(CSSValue& unicodeRange)
201 {
202     if (!is<CSSValueList>(unicodeRange))
203         return false;
204
205     m_ranges.clear();
206     auto& list = downcast<CSSValueList>(unicodeRange);
207     for (auto& rangeValue : list) {
208         CSSUnicodeRangeValue& range = downcast<CSSUnicodeRangeValue>(rangeValue.get());
209         m_ranges.append(UnicodeRange(range.from(), range.to()));
210     }
211
212     notifyClientsOfFontPropertyChange();
213
214     return true;
215 }
216
217 bool CSSFontFace::setVariantLigatures(CSSValue& variantLigatures)
218 {
219     auto ligatures = extractFontVariantLigatures(variantLigatures);
220     m_variantSettings.commonLigatures = ligatures.commonLigatures;
221     m_variantSettings.discretionaryLigatures = ligatures.discretionaryLigatures;
222     m_variantSettings.historicalLigatures = ligatures.historicalLigatures;
223     m_variantSettings.contextualAlternates = ligatures.contextualAlternates;
224
225     notifyClientsOfFontPropertyChange();
226
227     return true;
228 }
229
230 bool CSSFontFace::setVariantPosition(CSSValue& variantPosition)
231 {
232     if (!is<CSSPrimitiveValue>(variantPosition))
233         return false;
234     m_variantSettings.position = downcast<CSSPrimitiveValue>(variantPosition);
235
236     notifyClientsOfFontPropertyChange();
237
238     return true;
239 }
240
241 bool CSSFontFace::setVariantCaps(CSSValue& variantCaps)
242 {
243     if (!is<CSSPrimitiveValue>(variantCaps))
244         return false;
245     m_variantSettings.caps = downcast<CSSPrimitiveValue>(variantCaps);
246
247     notifyClientsOfFontPropertyChange();
248
249     return true;
250 }
251
252 bool CSSFontFace::setVariantNumeric(CSSValue& variantNumeric)
253 {
254     auto numeric = extractFontVariantNumeric(variantNumeric);
255     m_variantSettings.numericFigure = numeric.figure;
256     m_variantSettings.numericSpacing = numeric.spacing;
257     m_variantSettings.numericFraction = numeric.fraction;
258     m_variantSettings.numericOrdinal = numeric.ordinal;
259     m_variantSettings.numericSlashedZero = numeric.slashedZero;
260
261     notifyClientsOfFontPropertyChange();
262
263     return true;
264 }
265
266 bool CSSFontFace::setVariantAlternates(CSSValue& variantAlternates)
267 {
268     if (!is<CSSPrimitiveValue>(variantAlternates))
269         return false;
270     m_variantSettings.alternates = downcast<CSSPrimitiveValue>(variantAlternates);
271
272     notifyClientsOfFontPropertyChange();
273
274     return true;
275 }
276
277 bool CSSFontFace::setVariantEastAsian(CSSValue& variantEastAsian)
278 {
279     auto eastAsian = extractFontVariantEastAsian(variantEastAsian);
280     m_variantSettings.eastAsianVariant = eastAsian.variant;
281     m_variantSettings.eastAsianWidth = eastAsian.width;
282     m_variantSettings.eastAsianRuby = eastAsian.ruby;
283
284     notifyClientsOfFontPropertyChange();
285
286     return true;
287 }
288
289 bool CSSFontFace::setFeatureSettings(CSSValue& featureSettings)
290 {
291     if (!is<CSSValueList>(featureSettings))
292         return false;
293
294     m_featureSettings = FontFeatureSettings();
295     auto& list = downcast<CSSValueList>(featureSettings);
296     for (auto& rangeValue : list) {
297         CSSFontFeatureValue& feature = downcast<CSSFontFeatureValue>(rangeValue.get());
298         m_featureSettings.insert(FontFeature(feature.tag(), feature.value()));
299     }
300
301     notifyClientsOfFontPropertyChange();
302
303     return true;
304 }
305
306 bool CSSFontFace::allSourcesFailed() const
307 {
308     for (auto& source : m_sources) {
309         if (source->status() != CSSFontFaceSource::Status::Failure)
310             return false;
311     }
312     return true;
313 }
314
315 void CSSFontFace::addClient(Client& client)
316 {
317     m_clients.add(&client);
318 }
319
320 void CSSFontFace::removeClient(Client& client)
321 {
322     ASSERT(m_clients.contains(&client));
323     m_clients.remove(&client);
324 }
325
326 Ref<FontFace> CSSFontFace::wrapper(JSC::ExecState& execState)
327 {
328     if (m_wrapper)
329         return Ref<FontFace>(*m_wrapper.get());
330
331     Ref<FontFace> wrapper = FontFace::create(execState, *this);
332     switch (m_status) {
333     case Status::Pending:
334         break;
335     case Status::Loading:
336         wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
337         break;
338     case Status::TimedOut:
339         wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
340         wrapper->fontStateChanged(*this, Status::Loading, Status::TimedOut);
341         break;
342     case Status::Success:
343         wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
344         wrapper->fontStateChanged(*this, Status::Pending, Status::Success);
345         break;
346     case Status::Failure:
347         wrapper->fontStateChanged(*this, Status::Pending, Status::Loading);
348         wrapper->fontStateChanged(*this, Status::Pending, Status::Failure);
349         break;
350     }
351     m_wrapper = wrapper->createWeakPtr();
352     return wrapper;
353 }
354
355 void CSSFontFace::adoptSource(std::unique_ptr<CSSFontFaceSource>&& source)
356 {
357     m_sources.append(WTFMove(source));
358
359     // We should never add sources in the middle of loading.
360     ASSERT(!m_sourcesPopulated);
361 }
362
363 void CSSFontFace::setStatus(Status newStatus)
364 {
365     switch (newStatus) {
366     case Status::Pending:
367         ASSERT_NOT_REACHED();
368         break;
369     case Status::Loading:
370         ASSERT(m_status == Status::Pending);
371         break;
372     case Status::TimedOut:
373         ASSERT(m_status == Status::Loading);
374         break;
375     case Status::Success:
376         ASSERT(m_status == Status::Loading || m_status == Status::TimedOut);
377         break;
378     case Status::Failure:
379         ASSERT(m_status == Status::Loading || m_status == Status::TimedOut);
380         break;
381     }
382
383     for (auto* client : m_clients)
384         client->fontStateChanged(*this, m_status, newStatus);
385
386     m_status = newStatus;
387 }
388
389 void CSSFontFace::fontLoaded(CSSFontFaceSource&)
390 {
391     // If the font is already in the cache, CSSFontFaceSource may report it's loaded before it is added here as a source.
392     // Let's not pump the state machine until we've got all our sources. font() and load() are smart enough to act correctly
393     // when a source is failed or succeeded before we have asked it to load.
394     if (m_sourcesPopulated)
395         pump();
396
397     ASSERT(m_fontSelector);
398     m_fontSelector->fontLoaded();
399
400     for (auto* client : m_clients)
401         client->fontLoaded(*this);
402 }
403
404 size_t CSSFontFace::pump()
405 {
406     size_t i;
407     for (i = 0; i < m_sources.size(); ++i) {
408         auto& source = m_sources[i];
409
410         if (source->status() == CSSFontFaceSource::Status::Pending) {
411             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut);
412             ASSERT(m_fontSelector);
413             if (m_status == Status::Pending)
414                 setStatus(Status::Loading);
415             source->load(*m_fontSelector);
416         }
417
418         switch (source->status()) {
419         case CSSFontFaceSource::Status::Pending:
420             ASSERT_NOT_REACHED();
421             break;
422         case CSSFontFaceSource::Status::Loading:
423             ASSERT(m_status == Status::Pending || m_status == Status::Loading);
424             if (m_status == Status::Pending)
425                 setStatus(Status::Loading);
426             return i;
427         case CSSFontFaceSource::Status::Success:
428             ASSERT(m_status == Status::Pending || m_status == Status::Loading || m_status == Status::TimedOut || m_status == Status::Success);
429             if (m_status == Status::Pending)
430                 setStatus(Status::Loading);
431             if (m_status == Status::Loading || m_status == Status::TimedOut)
432                 setStatus(Status::Success);
433             return i;
434         case CSSFontFaceSource::Status::Failure:
435             if (m_status == Status::Pending)
436                 setStatus(Status::Loading);
437             break;
438         }
439     }
440     if (m_status == Status::Loading || m_status == Status::TimedOut)
441         setStatus(Status::Failure);
442     return m_sources.size();
443 }
444
445 void CSSFontFace::load()
446 {
447     pump();
448 }
449
450 RefPtr<Font> CSSFontFace::font(const FontDescription& fontDescription, bool syntheticBold, bool syntheticItalic)
451 {
452     if (allSourcesFailed())
453         return nullptr;
454
455     // Our status is derived from the first non-failed source. However, this source may
456     // return null from font(), which means we need to continue looping through the remainder
457     // of the sources to try to find a font to use. These subsequent tries should not affect
458     // our own state, though.
459     size_t startIndex = pump();
460     for (size_t i = startIndex; i < m_sources.size(); ++i) {
461         auto& source = m_sources[i];
462         if (source->status() == CSSFontFaceSource::Status::Pending) {
463             ASSERT(m_fontSelector);
464             source->load(*m_fontSelector);
465         }
466
467         switch (source->status()) {
468         case CSSFontFaceSource::Status::Pending:
469             ASSERT_NOT_REACHED();
470             break;
471         case CSSFontFaceSource::Status::Loading:
472             return Font::create(FontCache::singleton().lastResortFallbackFont(fontDescription)->platformData(), true, true);
473         case CSSFontFaceSource::Status::Success:
474             if (RefPtr<Font> result = source->font(fontDescription, syntheticBold, syntheticItalic, m_featureSettings, m_variantSettings))
475                 return result;
476             break;
477         case CSSFontFaceSource::Status::Failure:
478             break;
479         }
480     }
481
482     return nullptr;
483 }
484
485 #if ENABLE(SVG_FONTS)
486 bool CSSFontFace::hasSVGFontFaceSource() const
487 {
488     size_t size = m_sources.size();
489     for (size_t i = 0; i < size; i++) {
490         if (m_sources[i]->isSVGFontFaceSource())
491             return true;
492     }
493     return false;
494 }
495 #endif
496
497 }