Extract UTI mapping and allow for additions
[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 "Logging.h"
49 #include "ResourceLoadObserver.h"
50 #include "RuntimeEnabledFeatures.h"
51 #include "Settings.h"
52 #include "StyleProperties.h"
53 #include "StyleResolver.h"
54 #include "StyleRule.h"
55 #include "WebKitFontFamilyNames.h"
56 #include <wtf/Ref.h>
57 #include <wtf/SetForScope.h>
58 #include <wtf/text/AtomicString.h>
59
60 namespace WebCore {
61
62 static unsigned fontSelectorId;
63
64 CSSFontSelector::CSSFontSelector(Document& document)
65     : m_document(makeWeakPtr(document))
66     , m_cssFontFaceSet(CSSFontFaceSet::create(this))
67     , m_beginLoadingTimer(*this, &CSSFontSelector::beginLoadTimerFired)
68     , m_uniqueId(++fontSelectorId)
69     , m_version(0)
70 {
71     ASSERT(m_document);
72     FontCache::singleton().addClient(*this);
73     m_cssFontFaceSet->addClient(*this);
74     LOG(Fonts, "CSSFontSelector %p ctor", this);
75 }
76
77 CSSFontSelector::~CSSFontSelector()
78 {
79     LOG(Fonts, "CSSFontSelector %p dtor", this);
80
81     clearDocument();
82     m_cssFontFaceSet->removeClient(*this);
83     FontCache::singleton().removeClient(*this);
84 }
85
86 FontFaceSet& CSSFontSelector::fontFaceSet()
87 {
88     if (!m_fontFaceSet) {
89         ASSERT(m_document);
90         m_fontFaceSet = FontFaceSet::create(*m_document, m_cssFontFaceSet.get());
91     }
92
93     return *m_fontFaceSet;
94 }
95
96 bool CSSFontSelector::isEmpty() const
97 {
98     return !m_cssFontFaceSet->faceCount();
99 }
100
101 void CSSFontSelector::emptyCaches()
102 {
103     m_cssFontFaceSet->emptyCaches();
104 }
105
106 void CSSFontSelector::buildStarted()
107 {
108     m_buildIsUnderway = true;
109     m_cssFontFaceSet->purge();
110     ++m_version;
111
112     ASSERT(m_cssConnectionsPossiblyToRemove.isEmpty());
113     ASSERT(m_cssConnectionsEncounteredDuringBuild.isEmpty());
114     ASSERT(m_stagingArea.isEmpty());
115     for (size_t i = 0; i < m_cssFontFaceSet->faceCount(); ++i) {
116         CSSFontFace& face = m_cssFontFaceSet.get()[i];
117         if (face.cssConnection())
118             m_cssConnectionsPossiblyToRemove.add(&face);
119     }
120 }
121
122 void CSSFontSelector::buildCompleted()
123 {
124     if (!m_buildIsUnderway)
125         return;
126
127     m_buildIsUnderway = false;
128
129     // Some font faces weren't re-added during the build process.
130     for (auto& face : m_cssConnectionsPossiblyToRemove) {
131         auto* connection = face->cssConnection();
132         ASSERT(connection);
133         if (!m_cssConnectionsEncounteredDuringBuild.contains(connection))
134             m_cssFontFaceSet->remove(*face);
135     }
136
137     for (auto& item : m_stagingArea)
138         addFontFaceRule(item.styleRuleFontFace, item.isInitiatingElementInUserAgentShadowTree);
139     m_cssConnectionsEncounteredDuringBuild.clear();
140     m_stagingArea.clear();
141     m_cssConnectionsPossiblyToRemove.clear();
142 }
143
144 void CSSFontSelector::addFontFaceRule(StyleRuleFontFace& fontFaceRule, bool isInitiatingElementInUserAgentShadowTree)
145 {
146     if (m_buildIsUnderway) {
147         m_cssConnectionsEncounteredDuringBuild.add(&fontFaceRule);
148         m_stagingArea.append({fontFaceRule, isInitiatingElementInUserAgentShadowTree});
149         return;
150     }
151
152     const StyleProperties& style = fontFaceRule.properties();
153     RefPtr<CSSValue> fontFamily = style.getPropertyCSSValue(CSSPropertyFontFamily);
154     RefPtr<CSSValue> fontStyle = style.getPropertyCSSValue(CSSPropertyFontStyle);
155     RefPtr<CSSValue> fontWeight = style.getPropertyCSSValue(CSSPropertyFontWeight);
156     RefPtr<CSSValue> fontStretch = style.getPropertyCSSValue(CSSPropertyFontStretch);
157     RefPtr<CSSValue> src = style.getPropertyCSSValue(CSSPropertySrc);
158     RefPtr<CSSValue> unicodeRange = style.getPropertyCSSValue(CSSPropertyUnicodeRange);
159     RefPtr<CSSValue> featureSettings = style.getPropertyCSSValue(CSSPropertyFontFeatureSettings);
160     RefPtr<CSSValue> variantLigatures = style.getPropertyCSSValue(CSSPropertyFontVariantLigatures);
161     RefPtr<CSSValue> variantPosition = style.getPropertyCSSValue(CSSPropertyFontVariantPosition);
162     RefPtr<CSSValue> variantCaps = style.getPropertyCSSValue(CSSPropertyFontVariantCaps);
163     RefPtr<CSSValue> variantNumeric = style.getPropertyCSSValue(CSSPropertyFontVariantNumeric);
164     RefPtr<CSSValue> variantAlternates = style.getPropertyCSSValue(CSSPropertyFontVariantAlternates);
165     RefPtr<CSSValue> variantEastAsian = style.getPropertyCSSValue(CSSPropertyFontVariantEastAsian);
166     RefPtr<CSSValue> loadingBehavior = style.getPropertyCSSValue(CSSPropertyFontDisplay);
167     if (!is<CSSValueList>(fontFamily) || !is<CSSValueList>(src) || (unicodeRange && !is<CSSValueList>(*unicodeRange)))
168         return;
169
170     CSSValueList& familyList = downcast<CSSValueList>(*fontFamily);
171     if (!familyList.length())
172         return;
173
174     CSSValueList* rangeList = downcast<CSSValueList>(unicodeRange.get());
175
176     CSSValueList& srcList = downcast<CSSValueList>(*src);
177     if (!srcList.length())
178         return;
179
180     SetForScope<bool> creatingFont(m_creatingFont, true);
181     Ref<CSSFontFace> fontFace = CSSFontFace::create(this, &fontFaceRule);
182
183     if (!fontFace->setFamilies(*fontFamily))
184         return;
185     if (fontStyle)
186         fontFace->setStyle(*fontStyle);
187     if (fontWeight)
188         fontFace->setWeight(*fontWeight);
189     if (fontStretch)
190         fontFace->setStretch(*fontStretch);
191     if (rangeList && !fontFace->setUnicodeRange(*rangeList))
192         return;
193     if (variantLigatures && !fontFace->setVariantLigatures(*variantLigatures))
194         return;
195     if (variantPosition && !fontFace->setVariantPosition(*variantPosition))
196         return;
197     if (variantCaps && !fontFace->setVariantCaps(*variantCaps))
198         return;
199     if (variantNumeric && !fontFace->setVariantNumeric(*variantNumeric))
200         return;
201     if (variantAlternates && !fontFace->setVariantAlternates(*variantAlternates))
202         return;
203     if (variantEastAsian && !fontFace->setVariantEastAsian(*variantEastAsian))
204         return;
205     if (featureSettings)
206         fontFace->setFeatureSettings(*featureSettings);
207     if (loadingBehavior)
208         fontFace->setLoadingBehavior(*loadingBehavior);
209
210     CSSFontFace::appendSources(fontFace, srcList, m_document.get(), isInitiatingElementInUserAgentShadowTree);
211     if (fontFace->computeFailureState())
212         return;
213
214     if (RefPtr<CSSFontFace> existingFace = m_cssFontFaceSet->lookUpByCSSConnection(fontFaceRule)) {
215         // This adoption is fairly subtle. Script can trigger a purge of m_cssFontFaceSet at any time,
216         // which will cause us to just rely on the memory cache to retain the bytes of the file the next
217         // time we build up the CSSFontFaceSet. However, when the CSS Font Loading API is involved,
218         // the FontFace and FontFaceSet objects need to retain state. We create the new CSSFontFace object
219         // while the old one is still in scope so that the memory cache will be forced to retain the bytes
220         // of the resource. This means that the CachedFont will temporarily have two clients (until the
221         // old CSSFontFace goes out of scope, which should happen at the end of this "if" block). Because
222         // the CSSFontFaceSource objects will inspect their CachedFonts, the new CSSFontFace is smart enough
223         // to enter the correct state() during the next pump(). This approach of making a new CSSFontFace is
224         // simpler than computing and applying a diff of the StyleProperties.
225         m_cssFontFaceSet->remove(*existingFace);
226         if (auto* existingWrapper = existingFace->existingWrapper())
227             existingWrapper->adopt(fontFace.get());
228     }
229
230     m_cssFontFaceSet->add(fontFace.get());
231     ++m_version;
232 }
233
234 void CSSFontSelector::registerForInvalidationCallbacks(FontSelectorClient& client)
235 {
236     m_clients.add(&client);
237 }
238
239 void CSSFontSelector::unregisterForInvalidationCallbacks(FontSelectorClient& client)
240 {
241     m_clients.remove(&client);
242 }
243
244 void CSSFontSelector::dispatchInvalidationCallbacks()
245 {
246     ++m_version;
247
248     for (auto& client : copyToVector(m_clients))
249         client->fontsNeedUpdate(*this);
250 }
251
252 void CSSFontSelector::opportunisticallyStartFontDataURLLoading(const FontCascadeDescription& description, const AtomicString& familyName)
253 {
254     const auto& segmentedFontFace = m_cssFontFaceSet->fontFace(description.fontSelectionRequest(), familyName);
255     if (!segmentedFontFace)
256         return;
257     for (auto& face : segmentedFontFace->constituentFaces())
258         face->opportunisticallyStartFontDataURLLoading(*this);
259 }
260
261 void CSSFontSelector::fontLoaded()
262 {
263     dispatchInvalidationCallbacks();
264 }
265
266 void CSSFontSelector::fontModified()
267 {
268     if (!m_creatingFont && !m_buildIsUnderway)
269         dispatchInvalidationCallbacks();
270 }
271
272 void CSSFontSelector::fontCacheInvalidated()
273 {
274     dispatchInvalidationCallbacks();
275 }
276
277 static AtomicString resolveGenericFamily(Document* document, const FontDescription& fontDescription, const AtomicString& familyName)
278 {
279     auto platformResult = FontDescription::platformResolveGenericFamily(fontDescription.script(), fontDescription.locale(), familyName);
280     if (!platformResult.isNull())
281         return platformResult;
282
283     if (!document)
284         return familyName;
285
286     const Settings& settings = document->settings();
287
288     UScriptCode script = fontDescription.script();
289     if (familyName == serifFamily)
290         return settings.serifFontFamily(script);
291     if (familyName == sansSerifFamily)
292         return settings.sansSerifFontFamily(script);
293     if (familyName == cursiveFamily)
294         return settings.cursiveFontFamily(script);
295     if (familyName == fantasyFamily)
296         return settings.fantasyFontFamily(script);
297     if (familyName == monospaceFamily)
298         return settings.fixedFontFamily(script);
299     if (familyName == pictographFamily)
300         return settings.pictographFontFamily(script);
301     if (familyName == standardFamily)
302         return settings.standardFontFamily(script);
303
304     return familyName;
305 }
306
307 FontRanges CSSFontSelector::fontRangesForFamily(const FontDescription& fontDescription, const AtomicString& familyName)
308 {
309     // If this ASSERT() fires, it usually means you forgot a document.updateStyleIfNeeded() somewhere.
310     ASSERT(!m_buildIsUnderway || m_computingRootStyleFontCount);
311
312     // FIXME: The spec (and Firefox) says user specified generic families (sans-serif etc.) should be resolved before the @font-face lookup too.
313     bool resolveGenericFamilyFirst = familyName == standardFamily;
314
315     AtomicString familyForLookup = resolveGenericFamilyFirst ? resolveGenericFamily(m_document.get(), fontDescription, familyName) : familyName;
316     auto* face = m_cssFontFaceSet->fontFace(fontDescription.fontSelectionRequest(), familyForLookup);
317     if (face) {
318         if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled()) {
319             if (m_document)
320                 ResourceLoadObserver::shared().logFontLoad(*m_document, familyForLookup.string(), true);
321         }
322         return face->fontRanges(fontDescription);
323     }
324     if (!resolveGenericFamilyFirst)
325         familyForLookup = resolveGenericFamily(m_document.get(), fontDescription, familyName);
326     auto font = FontCache::singleton().fontForFamily(fontDescription, familyForLookup);
327     if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled()) {
328         if (m_document)
329             ResourceLoadObserver::shared().logFontLoad(*m_document, familyForLookup.string(), !!font);
330     }
331     return FontRanges { WTFMove(font) };
332 }
333
334 void CSSFontSelector::clearDocument()
335 {
336     if (!m_document) {
337         ASSERT(!m_beginLoadingTimer.isActive());
338         ASSERT(m_fontsToBeginLoading.isEmpty());
339         return;
340     }
341
342     m_beginLoadingTimer.stop();
343
344     CachedResourceLoader& cachedResourceLoader = m_document->cachedResourceLoader();
345     for (auto& fontHandle : m_fontsToBeginLoading) {
346         // Balances incrementRequestCount() in beginLoadingFontSoon().
347         cachedResourceLoader.decrementRequestCount(*fontHandle);
348     }
349     m_fontsToBeginLoading.clear();
350
351     m_document = nullptr;
352
353     m_cssFontFaceSet->clear();
354     m_clients.clear();
355 }
356
357 void CSSFontSelector::beginLoadingFontSoon(CachedFont& font)
358 {
359     if (!m_document)
360         return;
361
362     m_fontsToBeginLoading.append(&font);
363     // Increment the request count now, in order to prevent didFinishLoad from being dispatched
364     // after this font has been requested but before it began loading. Balanced by
365     // decrementRequestCount() in beginLoadTimerFired() and in clearDocument().
366     m_document->cachedResourceLoader().incrementRequestCount(font);
367
368     m_beginLoadingTimer.startOneShot(0_s);
369 }
370
371 void CSSFontSelector::beginLoadTimerFired()
372 {
373     Vector<CachedResourceHandle<CachedFont>> fontsToBeginLoading;
374     fontsToBeginLoading.swap(m_fontsToBeginLoading);
375
376     // CSSFontSelector could get deleted via beginLoadIfNeeded() or loadDone() unless protected.
377     Ref<CSSFontSelector> protectedThis(*this);
378
379     CachedResourceLoader& cachedResourceLoader = m_document->cachedResourceLoader();
380     for (auto& fontHandle : fontsToBeginLoading) {
381         fontHandle->beginLoadIfNeeded(cachedResourceLoader);
382         // Balances incrementRequestCount() in beginLoadingFontSoon().
383         cachedResourceLoader.decrementRequestCount(*fontHandle);
384     }
385     // Ensure that if the request count reaches zero, the frame loader will know about it.
386     // New font loads may be triggered by layout after the document load is complete but before we have dispatched
387     // didFinishLoading for the frame. Make sure the delegate is always dispatched by checking explicitly.
388     if (m_document && m_document->frame())
389         m_document->frame()->loader().checkLoadComplete();
390     cachedResourceLoader.loadDone(LoadCompletionType::Finish);
391 }
392
393
394 size_t CSSFontSelector::fallbackFontCount()
395 {
396     if (!m_document)
397         return 0;
398
399     return m_document->settings().fontFallbackPrefersPictographs() ? 1 : 0;
400 }
401
402 RefPtr<Font> CSSFontSelector::fallbackFontAt(const FontDescription& fontDescription, size_t index)
403 {
404     ASSERT_UNUSED(index, !index);
405
406     if (!m_document)
407         return nullptr;
408
409     if (!m_document->settings().fontFallbackPrefersPictographs())
410         return nullptr;
411     auto& pictographFontFamily = m_document->settings().pictographFontFamily();
412     auto font = FontCache::singleton().fontForFamily(fontDescription, pictographFontFamily);
413     if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
414         ResourceLoadObserver::shared().logFontLoad(*m_document, pictographFontFamily.string(), !!font);
415     
416     return font;
417 }
418
419 }