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