Parse font-display
[WebKit-https.git] / Source / WebCore / css / CSSFontSelector.cpp
1 /*
2  * Copyright (C) 2007, 2008, 2011, 2013 Apple Inc. All rights reserved.
3  *           (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "CSSFontSelector.h"
29
30 #include "CachedFont.h"
31 #include "CSSFontFace.h"
32 #include "CSSFontFaceSource.h"
33 #include "CSSFontFamily.h"
34 #include "CSSPrimitiveValue.h"
35 #include "CSSPropertyNames.h"
36 #include "CSSSegmentedFontFace.h"
37 #include "CSSValueKeywords.h"
38 #include "CSSValueList.h"
39 #include "CachedResourceLoader.h"
40 #include "Document.h"
41 #include "Font.h"
42 #include "FontCache.h"
43 #include "FontFace.h"
44 #include "FontFaceSet.h"
45 #include "FontSelectorClient.h"
46 #include "Frame.h"
47 #include "FrameLoader.h"
48 #include "Settings.h"
49 #include "StyleProperties.h"
50 #include "StyleResolver.h"
51 #include "StyleRule.h"
52 #include "WebKitFontFamilyNames.h"
53 #include <wtf/Ref.h>
54 #include <wtf/SetForScope.h>
55 #include <wtf/text/AtomicString.h>
56
57 namespace WebCore {
58
59 static unsigned fontSelectorId;
60
61 CSSFontSelector::CSSFontSelector(Document& document)
62     : m_document(&document)
63     , m_cssFontFaceSet(CSSFontFaceSet::create())
64     , m_beginLoadingTimer(*this, &CSSFontSelector::beginLoadTimerFired)
65     , m_uniqueId(++fontSelectorId)
66     , m_version(0)
67 {
68     ASSERT(m_document);
69     FontCache::singleton().addClient(*this);
70     m_cssFontFaceSet->addClient(*this);
71 }
72
73 CSSFontSelector::~CSSFontSelector()
74 {
75     clearDocument();
76     m_cssFontFaceSet->removeClient(*this);
77     FontCache::singleton().removeClient(*this);
78 }
79
80 FontFaceSet& CSSFontSelector::fontFaceSet()
81 {
82     if (!m_fontFaceSet) {
83         ASSERT(m_document);
84         m_fontFaceSet = FontFaceSet::create(*m_document, m_cssFontFaceSet.get());
85     }
86
87     return *m_fontFaceSet;
88 }
89
90 bool CSSFontSelector::isEmpty() const
91 {
92     return !m_cssFontFaceSet->faceCount();
93 }
94
95 void CSSFontSelector::emptyCaches()
96 {
97     m_cssFontFaceSet->emptyCaches();
98 }
99
100 void CSSFontSelector::buildStarted()
101 {
102     m_buildIsUnderway = true;
103     m_cssFontFaceSet->purge();
104     ++m_version;
105
106     ASSERT(m_cssConnectionsPossiblyToRemove.isEmpty());
107     ASSERT(m_cssConnectionsEncounteredDuringBuild.isEmpty());
108     ASSERT(m_stagingArea.isEmpty());
109     for (size_t i = 0; i < m_cssFontFaceSet->faceCount(); ++i) {
110         CSSFontFace& face = m_cssFontFaceSet.get()[i];
111         if (face.cssConnection())
112             m_cssConnectionsPossiblyToRemove.add(&face);
113     }
114 }
115
116 void CSSFontSelector::buildCompleted()
117 {
118     if (!m_buildIsUnderway)
119         return;
120
121     m_buildIsUnderway = false;
122
123     // Some font faces weren't re-added during the build process.
124     for (auto& face : m_cssConnectionsPossiblyToRemove) {
125         auto* connection = face->cssConnection();
126         ASSERT(connection);
127         if (!m_cssConnectionsEncounteredDuringBuild.contains(connection))
128             m_cssFontFaceSet->remove(*face);
129     }
130
131     for (auto& item : m_stagingArea)
132         addFontFaceRule(item.styleRuleFontFace, item.isInitiatingElementInUserAgentShadowTree);
133     m_cssConnectionsEncounteredDuringBuild.clear();
134     m_stagingArea.clear();
135     m_cssConnectionsPossiblyToRemove.clear();
136 }
137
138 void CSSFontSelector::addFontFaceRule(StyleRuleFontFace& fontFaceRule, bool isInitiatingElementInUserAgentShadowTree)
139 {
140     if (m_buildIsUnderway) {
141         m_cssConnectionsEncounteredDuringBuild.add(&fontFaceRule);
142         m_stagingArea.append({fontFaceRule, isInitiatingElementInUserAgentShadowTree});
143         return;
144     }
145
146     const StyleProperties& style = fontFaceRule.properties();
147     RefPtr<CSSValue> fontFamily = style.getPropertyCSSValue(CSSPropertyFontFamily);
148     RefPtr<CSSValue> fontStyle = style.getPropertyCSSValue(CSSPropertyFontStyle);
149     RefPtr<CSSValue> fontWeight = style.getPropertyCSSValue(CSSPropertyFontWeight);
150     RefPtr<CSSValue> fontStretch = style.getPropertyCSSValue(CSSPropertyFontStretch);
151     RefPtr<CSSValue> src = style.getPropertyCSSValue(CSSPropertySrc);
152     RefPtr<CSSValue> unicodeRange = style.getPropertyCSSValue(CSSPropertyUnicodeRange);
153     RefPtr<CSSValue> featureSettings = style.getPropertyCSSValue(CSSPropertyFontFeatureSettings);
154     RefPtr<CSSValue> variantLigatures = style.getPropertyCSSValue(CSSPropertyFontVariantLigatures);
155     RefPtr<CSSValue> variantPosition = style.getPropertyCSSValue(CSSPropertyFontVariantPosition);
156     RefPtr<CSSValue> variantCaps = style.getPropertyCSSValue(CSSPropertyFontVariantCaps);
157     RefPtr<CSSValue> variantNumeric = style.getPropertyCSSValue(CSSPropertyFontVariantNumeric);
158     RefPtr<CSSValue> variantAlternates = style.getPropertyCSSValue(CSSPropertyFontVariantAlternates);
159     RefPtr<CSSValue> variantEastAsian = style.getPropertyCSSValue(CSSPropertyFontVariantEastAsian);
160     RefPtr<CSSValue> loadingBehavior = style.getPropertyCSSValue(CSSPropertyFontDisplay);
161     if (!is<CSSValueList>(fontFamily.get()) || !is<CSSValueList>(src.get()) || (unicodeRange && !is<CSSValueList>(*unicodeRange)))
162         return;
163
164     CSSValueList& familyList = downcast<CSSValueList>(*fontFamily);
165     if (!familyList.length())
166         return;
167
168     CSSValueList* rangeList = downcast<CSSValueList>(unicodeRange.get());
169
170     CSSValueList& srcList = downcast<CSSValueList>(*src);
171     if (!srcList.length())
172         return;
173
174     SetForScope<bool> creatingFont(m_creatingFont, true);
175     Ref<CSSFontFace> fontFace = CSSFontFace::create(this, &fontFaceRule);
176
177     if (!fontFace->setFamilies(*fontFamily))
178         return;
179     if (fontStyle)
180         fontFace->setStyle(*fontStyle);
181     if (fontWeight)
182         fontFace->setWeight(*fontWeight);
183     if (fontStretch)
184         fontFace->setStretch(*fontStretch);
185     if (rangeList && !fontFace->setUnicodeRange(*rangeList))
186         return;
187     if (variantLigatures && !fontFace->setVariantLigatures(*variantLigatures))
188         return;
189     if (variantPosition && !fontFace->setVariantPosition(*variantPosition))
190         return;
191     if (variantCaps && !fontFace->setVariantCaps(*variantCaps))
192         return;
193     if (variantNumeric && !fontFace->setVariantNumeric(*variantNumeric))
194         return;
195     if (variantAlternates && !fontFace->setVariantAlternates(*variantAlternates))
196         return;
197     if (variantEastAsian && !fontFace->setVariantEastAsian(*variantEastAsian))
198         return;
199     if (featureSettings)
200         fontFace->setFeatureSettings(*featureSettings);
201     if (loadingBehavior)
202         fontFace->setLoadingBehavior(*loadingBehavior);
203
204     CSSFontFace::appendSources(fontFace, srcList, m_document, isInitiatingElementInUserAgentShadowTree);
205     if (fontFace->allSourcesFailed())
206         return;
207
208     if (RefPtr<CSSFontFace> existingFace = m_cssFontFaceSet->lookUpByCSSConnection(fontFaceRule)) {
209         // This adoption is fairly subtle. Script can trigger a purge of m_cssFontFaceSet at any time,
210         // which will cause us to just rely on the memory cache to retain the bytes of the file the next
211         // time we build up the CSSFontFaceSet. However, when the CSS Font Loading API is involved,
212         // the FontFace and FontFaceSet objects need to retain state. We create the new CSSFontFace object
213         // while the old one is still in scope so that the memory cache will be forced to retain the bytes
214         // of the resource. This means that the CachedFont will temporarily have two clients (until the
215         // old CSSFontFace goes out of scope, which should happen at the end of this "if" block). Because
216         // the CSSFontFaceSource objects will inspect their CachedFonts, the new CSSFontFace is smart enough
217         // to enter the correct state() during the next pump(). This approach of making a new CSSFontFace is
218         // simpler than computing and applying a diff of the StyleProperties.
219         m_cssFontFaceSet->remove(*existingFace);
220         if (auto* existingWrapper = existingFace->existingWrapper())
221             existingWrapper->adopt(fontFace.get());
222     }
223
224     m_cssFontFaceSet->add(fontFace.get());
225     ++m_version;
226 }
227
228 void CSSFontSelector::registerForInvalidationCallbacks(FontSelectorClient& client)
229 {
230     m_clients.add(&client);
231 }
232
233 void CSSFontSelector::unregisterForInvalidationCallbacks(FontSelectorClient& client)
234 {
235     m_clients.remove(&client);
236 }
237
238 void CSSFontSelector::dispatchInvalidationCallbacks()
239 {
240     ++m_version;
241
242     Vector<FontSelectorClient*> clients;
243     copyToVector(m_clients, clients);
244     for (size_t i = 0; i < clients.size(); ++i)
245         clients[i]->fontsNeedUpdate(*this);
246 }
247
248 void CSSFontSelector::fontLoaded()
249 {
250     dispatchInvalidationCallbacks();
251 }
252
253 void CSSFontSelector::fontModified()
254 {
255     if (!m_creatingFont && !m_buildIsUnderway)
256         dispatchInvalidationCallbacks();
257 }
258
259 void CSSFontSelector::fontCacheInvalidated()
260 {
261     dispatchInvalidationCallbacks();
262 }
263
264 static const AtomicString& resolveGenericFamily(Document* document, const FontDescription& fontDescription, const AtomicString& familyName)
265 {
266     if (!document)
267         return familyName;
268
269     const Settings& settings = document->settings();
270
271     UScriptCode script = fontDescription.script();
272     if (familyName == serifFamily)
273         return settings.serifFontFamily(script);
274     if (familyName == sansSerifFamily)
275         return settings.sansSerifFontFamily(script);
276     if (familyName == cursiveFamily)
277         return settings.cursiveFontFamily(script);
278     if (familyName == fantasyFamily)
279         return settings.fantasyFontFamily(script);
280     if (familyName == monospaceFamily)
281         return settings.fixedFontFamily(script);
282     if (familyName == pictographFamily)
283         return settings.pictographFontFamily(script);
284     if (familyName == standardFamily)
285         return settings.standardFontFamily(script);
286
287     return familyName;
288 }
289
290 FontRanges CSSFontSelector::fontRangesForFamily(const FontDescription& fontDescription, const AtomicString& familyName)
291 {
292     // If this ASSERT() fires, it usually means you forgot a document.updateStyleIfNeeded() somewhere.
293     ASSERT(!m_buildIsUnderway || m_computingRootStyleFontCount);
294
295     // FIXME: The spec (and Firefox) says user specified generic families (sans-serif etc.) should be resolved before the @font-face lookup too.
296     bool resolveGenericFamilyFirst = familyName == standardFamily;
297
298     AtomicString familyForLookup = resolveGenericFamilyFirst ? resolveGenericFamily(m_document, fontDescription, familyName) : familyName;
299     auto* face = m_cssFontFaceSet->fontFace(fontDescription.fontSelectionRequest(), familyForLookup);
300     if (!face) {
301         if (!resolveGenericFamilyFirst)
302             familyForLookup = resolveGenericFamily(m_document, fontDescription, familyName);
303         return FontRanges(FontCache::singleton().fontForFamily(fontDescription, familyForLookup));
304     }
305
306     return face->fontRanges(fontDescription);
307 }
308
309 void CSSFontSelector::clearDocument()
310 {
311     if (!m_document) {
312         ASSERT(!m_beginLoadingTimer.isActive());
313         ASSERT(m_fontsToBeginLoading.isEmpty());
314         return;
315     }
316
317     m_beginLoadingTimer.stop();
318
319     CachedResourceLoader& cachedResourceLoader = m_document->cachedResourceLoader();
320     for (auto& fontHandle : m_fontsToBeginLoading) {
321         // Balances incrementRequestCount() in beginLoadingFontSoon().
322         cachedResourceLoader.decrementRequestCount(*fontHandle);
323     }
324     m_fontsToBeginLoading.clear();
325
326     m_document = nullptr;
327
328     m_cssFontFaceSet->clear();
329     m_clients.clear();
330 }
331
332 void CSSFontSelector::beginLoadingFontSoon(CachedFont& font)
333 {
334     if (!m_document)
335         return;
336
337     if (!m_document->settings().webFontsAlwaysFallBack()) {
338         m_fontsToBeginLoading.append(&font);
339         // Increment the request count now, in order to prevent didFinishLoad from being dispatched
340         // after this font has been requested but before it began loading. Balanced by
341         // decrementRequestCount() in beginLoadTimerFired() and in clearDocument().
342         m_document->cachedResourceLoader().incrementRequestCount(font);
343     }
344     m_beginLoadingTimer.startOneShot(0_s);
345 }
346
347 void CSSFontSelector::beginLoadTimerFired()
348 {
349     Vector<CachedResourceHandle<CachedFont>> fontsToBeginLoading;
350     fontsToBeginLoading.swap(m_fontsToBeginLoading);
351
352     // CSSFontSelector could get deleted via beginLoadIfNeeded() or loadDone() unless protected.
353     Ref<CSSFontSelector> protectedThis(*this);
354
355     CachedResourceLoader& cachedResourceLoader = m_document->cachedResourceLoader();
356     for (auto& fontHandle : fontsToBeginLoading) {
357         fontHandle->beginLoadIfNeeded(cachedResourceLoader);
358         // Balances incrementRequestCount() in beginLoadingFontSoon().
359         cachedResourceLoader.decrementRequestCount(*fontHandle);
360     }
361     // Ensure that if the request count reaches zero, the frame loader will know about it.
362     cachedResourceLoader.loadDone();
363     // New font loads may be triggered by layout after the document load is complete but before we have dispatched
364     // didFinishLoading for the frame. Make sure the delegate is always dispatched by checking explicitly.
365     if (m_document && m_document->frame())
366         m_document->frame()->loader().checkLoadComplete();
367 }
368
369
370 size_t CSSFontSelector::fallbackFontCount()
371 {
372     if (!m_document)
373         return 0;
374
375     return m_document->settings().fontFallbackPrefersPictographs() ? 1 : 0;
376 }
377
378 RefPtr<Font> CSSFontSelector::fallbackFontAt(const FontDescription& fontDescription, size_t index)
379 {
380     ASSERT_UNUSED(index, !index);
381
382     if (!m_document)
383         return nullptr;
384
385     if (!m_document->settings().fontFallbackPrefersPictographs())
386         return nullptr;
387
388     return FontCache::singleton().fontForFamily(fontDescription, m_document->settings().pictographFontFamily());
389 }
390
391 }