2 * Copyright (C) 2007, 2008, 2011 Apple Inc. All rights reserved.
3 * (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, 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 COMPUTER, 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.
28 #include "CSSFontSelector.h"
30 #include "CachedFont.h"
31 #include "CSSFontFace.h"
32 #include "CSSFontFaceSource.h"
33 #include "CSSFontFaceSrcValue.h"
34 #include "CSSPrimitiveValue.h"
35 #include "CSSPropertyNames.h"
36 #include "CSSSegmentedFontFace.h"
37 #include "CSSUnicodeRangeValue.h"
38 #include "CSSValueKeywords.h"
39 #include "CSSValueList.h"
40 #include "CachedResourceLoader.h"
42 #include "FontCache.h"
44 #include "RenderObject.h"
46 #include "SimpleFontData.h"
47 #include "StylePropertySet.h"
48 #include "StyleResolver.h"
49 #include "StyleRule.h"
50 #include "WebKitFontFamilyNames.h"
51 #include <wtf/text/AtomicString.h>
54 #include "SVGFontFaceElement.h"
62 CSSFontSelector::CSSFontSelector(Document* document)
63 : m_document(document)
64 , m_beginLoadingTimer(this, &CSSFontSelector::beginLoadTimerFired)
67 // FIXME: An old comment used to say there was no need to hold a reference to m_document
68 // because "we are guaranteed to be destroyed before the document". But there does not
69 // seem to be any such guarantee.
72 fontCache()->addClient(this);
75 CSSFontSelector::~CSSFontSelector()
78 fontCache()->removeClient(this);
81 bool CSSFontSelector::isEmpty() const
83 return m_fonts.isEmpty();
86 void CSSFontSelector::addFontFaceRule(const StyleRuleFontFace* fontFaceRule)
88 // Obtain the font-family property and the src property. Both must be defined.
89 const StylePropertySet* style = fontFaceRule->properties();
90 RefPtr<CSSValue> fontFamily = style->getPropertyCSSValue(CSSPropertyFontFamily);
91 RefPtr<CSSValue> src = style->getPropertyCSSValue(CSSPropertySrc);
92 RefPtr<CSSValue> unicodeRange = style->getPropertyCSSValue(CSSPropertyUnicodeRange);
93 if (!fontFamily || !src || !fontFamily->isValueList() || !src->isValueList() || (unicodeRange && !unicodeRange->isValueList()))
96 CSSValueList* familyList = static_cast<CSSValueList*>(fontFamily.get());
97 if (!familyList->length())
100 CSSValueList* srcList = static_cast<CSSValueList*>(src.get());
101 if (!srcList->length())
104 CSSValueList* rangeList = static_cast<CSSValueList*>(unicodeRange.get());
106 unsigned traitsMask = 0;
108 if (RefPtr<CSSValue> fontStyle = style->getPropertyCSSValue(CSSPropertyFontStyle)) {
109 if (!fontStyle->isPrimitiveValue())
112 switch (static_cast<CSSPrimitiveValue*>(fontStyle.get())->getIdent()) {
114 traitsMask |= FontStyleNormalMask;
117 case CSSValueOblique:
118 traitsMask |= FontStyleItalicMask;
124 traitsMask |= FontStyleNormalMask;
126 if (RefPtr<CSSValue> fontWeight = style->getPropertyCSSValue(CSSPropertyFontWeight)) {
127 if (!fontWeight->isPrimitiveValue())
130 switch (static_cast<CSSPrimitiveValue*>(fontWeight.get())->getIdent()) {
133 traitsMask |= FontWeight700Mask;
137 traitsMask |= FontWeight400Mask;
140 traitsMask |= FontWeight900Mask;
143 traitsMask |= FontWeight800Mask;
146 traitsMask |= FontWeight600Mask;
149 traitsMask |= FontWeight500Mask;
152 traitsMask |= FontWeight300Mask;
155 traitsMask |= FontWeight200Mask;
158 traitsMask |= FontWeight100Mask;
164 traitsMask |= FontWeight400Mask;
166 if (RefPtr<CSSValue> fontVariant = style->getPropertyCSSValue(CSSPropertyFontVariant)) {
167 // font-variant descriptor can be a value list.
168 if (fontVariant->isPrimitiveValue()) {
169 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
170 list->append(fontVariant);
172 } else if (!fontVariant->isValueList())
175 CSSValueList* variantList = static_cast<CSSValueList*>(fontVariant.get());
176 unsigned numVariants = variantList->length();
180 for (unsigned i = 0; i < numVariants; ++i) {
181 switch (static_cast<CSSPrimitiveValue*>(variantList->itemWithoutBoundsCheck(i))->getIdent()) {
183 traitsMask |= FontVariantNormalMask;
185 case CSSValueSmallCaps:
186 traitsMask |= FontVariantSmallCapsMask;
193 traitsMask |= FontVariantMask;
195 // Each item in the src property's list is a single CSSFontFaceSource. Put them all into a CSSFontFace.
196 RefPtr<CSSFontFace> fontFace;
198 int srcLength = srcList->length();
200 bool foundSVGFont = false;
202 for (int i = 0; i < srcLength; i++) {
203 // An item in the list either specifies a string (local font name) or a URL (remote font to download).
204 CSSFontFaceSrcValue* item = static_cast<CSSFontFaceSrcValue*>(srcList->itemWithoutBoundsCheck(i));
205 OwnPtr<CSSFontFaceSource> source;
207 #if ENABLE(SVG_FONTS)
208 foundSVGFont = item->isSVGFontFaceSrc() || item->svgFontFaceElement();
210 if (!item->isLocal()) {
211 Settings* settings = m_document ? m_document->frame() ? m_document->frame()->settings() : 0 : 0;
212 bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled());
213 if (allowDownloading && item->isSupportedFormat() && m_document) {
214 CachedFont* cachedFont = item->cachedFont(m_document);
216 source = adoptPtr(new CSSFontFaceSource(item->resource(), cachedFont));
217 #if ENABLE(SVG_FONTS)
219 source->setHasExternalSVGFont(true);
224 source = adoptPtr(new CSSFontFaceSource(item->resource()));
228 fontFace = CSSFontFace::create(static_cast<FontTraitsMask>(traitsMask));
231 #if ENABLE(SVG_FONTS)
232 source->setSVGFontFaceElement(item->svgFontFaceElement());
234 fontFace->addSource(source.release());
240 if (fontFace && !fontFace->isValid())
244 unsigned numRanges = rangeList->length();
245 for (unsigned i = 0; i < numRanges; i++) {
246 CSSUnicodeRangeValue* range = static_cast<CSSUnicodeRangeValue*>(rangeList->itemWithoutBoundsCheck(i));
247 fontFace->addRange(range->from(), range->to());
251 // Hash under every single family name.
252 int familyLength = familyList->length();
253 for (int i = 0; i < familyLength; i++) {
254 CSSPrimitiveValue* item = static_cast<CSSPrimitiveValue*>(familyList->itemWithoutBoundsCheck(i));
256 if (item->isString())
257 familyName = item->getStringValue();
258 else if (item->isIdent()) {
259 // We need to use the raw text for all the generic family types, since @font-face is a way of actually
260 // defining what font to use for those types.
261 switch (item->getIdent()) {
263 familyName = serifFamily;
265 case CSSValueSansSerif:
266 familyName = sansSerifFamily;
268 case CSSValueCursive:
269 familyName = cursiveFamily;
271 case CSSValueFantasy:
272 familyName = fantasyFamily;
274 case CSSValueMonospace:
275 familyName = monospaceFamily;
277 case CSSValueWebkitPictograph:
278 familyName = pictographFamily;
285 if (familyName.isEmpty())
288 OwnPtr<Vector<RefPtr<CSSFontFace> > >& familyFontFaces = m_fontFaces.add(familyName, nullptr).iterator->second;
289 if (!familyFontFaces) {
290 familyFontFaces = adoptPtr(new Vector<RefPtr<CSSFontFace> >);
292 ASSERT(!m_locallyInstalledFontFaces.contains(familyName));
294 Vector<unsigned> locallyInstalledFontsTraitsMasks;
295 fontCache()->getTraitsInFamily(familyName, locallyInstalledFontsTraitsMasks);
296 if (unsigned numLocallyInstalledFaces = locallyInstalledFontsTraitsMasks.size()) {
297 OwnPtr<Vector<RefPtr<CSSFontFace> > > familyLocallyInstalledFaces = adoptPtr(new Vector<RefPtr<CSSFontFace> >);
299 for (unsigned i = 0; i < numLocallyInstalledFaces; ++i) {
300 RefPtr<CSSFontFace> locallyInstalledFontFace = CSSFontFace::create(static_cast<FontTraitsMask>(locallyInstalledFontsTraitsMasks[i]), true);
301 locallyInstalledFontFace->addSource(adoptPtr(new CSSFontFaceSource(familyName)));
302 ASSERT(locallyInstalledFontFace->isValid());
303 familyLocallyInstalledFaces->append(locallyInstalledFontFace);
306 m_locallyInstalledFontFaces.set(familyName, familyLocallyInstalledFaces.release());
310 familyFontFaces->append(fontFace);
316 void CSSFontSelector::registerForInvalidationCallbacks(FontSelectorClient* client)
318 m_clients.add(client);
321 void CSSFontSelector::unregisterForInvalidationCallbacks(FontSelectorClient* client)
323 m_clients.remove(client);
326 void CSSFontSelector::dispatchInvalidationCallbacks()
328 Vector<FontSelectorClient*> clients;
329 copyToVector(m_clients, clients);
330 for (size_t i = 0; i < clients.size(); ++i)
331 clients[i]->fontsNeedUpdate(this);
333 // FIXME: Make Document a FontSelectorClient so that it can simply register for invalidation callbacks.
336 if (StyleResolver* styleResolver = m_document->styleResolverIfExists())
337 styleResolver->invalidateMatchedPropertiesCache();
338 if (m_document->inPageCache() || !m_document->renderer())
340 m_document->scheduleForcedStyleRecalc();
343 void CSSFontSelector::fontLoaded()
345 dispatchInvalidationCallbacks();
348 void CSSFontSelector::fontCacheInvalidated()
350 dispatchInvalidationCallbacks();
353 static FontData* fontDataForGenericFamily(Document* document, const FontDescription& fontDescription, const AtomicString& familyName)
355 if (!document || !document->frame())
358 const Settings* settings = document->frame()->settings();
362 AtomicString genericFamily;
363 UScriptCode script = fontDescription.script();
365 if (familyName == serifFamily)
366 genericFamily = settings->serifFontFamily(script);
367 else if (familyName == sansSerifFamily)
368 genericFamily = settings->sansSerifFontFamily(script);
369 else if (familyName == cursiveFamily)
370 genericFamily = settings->cursiveFontFamily(script);
371 else if (familyName == fantasyFamily)
372 genericFamily = settings->fantasyFontFamily(script);
373 else if (familyName == monospaceFamily)
374 genericFamily = settings->fixedFontFamily(script);
375 else if (familyName == pictographFamily)
376 genericFamily = settings->pictographFontFamily(script);
377 else if (familyName == standardFamily)
378 genericFamily = settings->standardFontFamily(script);
380 if (!genericFamily.isEmpty())
381 return fontCache()->getCachedFontData(fontDescription, genericFamily);
386 static FontTraitsMask desiredTraitsMaskForComparison;
388 static inline bool compareFontFaces(CSSFontFace* first, CSSFontFace* second)
390 FontTraitsMask firstTraitsMask = first->traitsMask();
391 FontTraitsMask secondTraitsMask = second->traitsMask();
393 bool firstHasDesiredVariant = firstTraitsMask & desiredTraitsMaskForComparison & FontVariantMask;
394 bool secondHasDesiredVariant = secondTraitsMask & desiredTraitsMaskForComparison & FontVariantMask;
396 if (firstHasDesiredVariant != secondHasDesiredVariant)
397 return firstHasDesiredVariant;
399 // We need to check font-variant css property for CSS2.1 compatibility.
400 if ((desiredTraitsMaskForComparison & FontVariantSmallCapsMask) && !first->isLocalFallback() && !second->isLocalFallback()) {
401 // Prefer a font that has indicated that it can only support small-caps to a font that claims to support
402 // all variants. The specialized font is more likely to be true small-caps and not require synthesis.
403 bool firstRequiresSmallCaps = (firstTraitsMask & FontVariantSmallCapsMask) && !(firstTraitsMask & FontVariantNormalMask);
404 bool secondRequiresSmallCaps = (secondTraitsMask & FontVariantSmallCapsMask) && !(secondTraitsMask & FontVariantNormalMask);
405 if (firstRequiresSmallCaps != secondRequiresSmallCaps)
406 return firstRequiresSmallCaps;
409 bool firstHasDesiredStyle = firstTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
410 bool secondHasDesiredStyle = secondTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
412 if (firstHasDesiredStyle != secondHasDesiredStyle)
413 return firstHasDesiredStyle;
415 if ((desiredTraitsMaskForComparison & FontStyleItalicMask) && !first->isLocalFallback() && !second->isLocalFallback()) {
416 // Prefer a font that has indicated that it can only support italics to a font that claims to support
417 // all styles. The specialized font is more likely to be the one the author wants used.
418 bool firstRequiresItalics = (firstTraitsMask & FontStyleItalicMask) && !(firstTraitsMask & FontStyleNormalMask);
419 bool secondRequiresItalics = (secondTraitsMask & FontStyleItalicMask) && !(secondTraitsMask & FontStyleNormalMask);
420 if (firstRequiresItalics != secondRequiresItalics)
421 return firstRequiresItalics;
424 if (secondTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
426 if (firstTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
429 // http://www.w3.org/TR/2011/WD-css3-fonts-20111004/#font-matching-algorithm says :
430 // - If the desired weight is less than 400, weights below the desired weight are checked in descending order followed by weights above the desired weight in ascending order until a match is found.
431 // - If the desired weight is greater than 500, weights above the desired weight are checked in ascending order followed by weights below the desired weight in descending order until a match is found.
432 // - If the desired weight is 400, 500 is checked first and then the rule for desired weights less than 400 is used.
433 // - If the desired weight is 500, 400 is checked first and then the rule for desired weights less than 400 is used.
435 static const unsigned fallbackRuleSets = 9;
436 static const unsigned rulesPerSet = 8;
437 static const FontTraitsMask weightFallbackRuleSets[fallbackRuleSets][rulesPerSet] = {
438 { FontWeight200Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
439 { FontWeight100Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
440 { FontWeight200Mask, FontWeight100Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
441 { FontWeight500Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
442 { FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
443 { FontWeight700Mask, FontWeight800Mask, FontWeight900Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
444 { FontWeight800Mask, FontWeight900Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
445 { FontWeight900Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
446 { FontWeight800Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask }
449 unsigned ruleSetIndex = 0;
450 unsigned w = FontWeight100Bit;
451 while (!(desiredTraitsMaskForComparison & (1 << w))) {
456 ASSERT(ruleSetIndex < fallbackRuleSets);
457 const FontTraitsMask* weightFallbackRule = weightFallbackRuleSets[ruleSetIndex];
458 for (unsigned i = 0; i < rulesPerSet; ++i) {
459 if (secondTraitsMask & weightFallbackRule[i])
461 if (firstTraitsMask & weightFallbackRule[i])
468 FontData* CSSFontSelector::getFontData(const FontDescription& fontDescription, const AtomicString& familyName)
470 if (m_fontFaces.isEmpty()) {
471 if (familyName.startsWith("-webkit-"))
472 return fontDataForGenericFamily(m_document, fontDescription, familyName);
473 if (fontDescription.genericFamily() == FontDescription::StandardFamily && !fontDescription.isSpecifiedFont())
474 return fontDataForGenericFamily(m_document, fontDescription, "-webkit-standard");
478 String family = familyName.string();
480 Vector<RefPtr<CSSFontFace> >* familyFontFaces = m_fontFaces.get(family);
481 // If no face was found, then return 0 and let the OS come up with its best match for the name.
482 if (!familyFontFaces || familyFontFaces->isEmpty()) {
483 // If we were handed a generic family, but there was no match, go ahead and return the correct font based off our
485 if (fontDescription.genericFamily() == FontDescription::StandardFamily && !fontDescription.isSpecifiedFont())
486 return fontDataForGenericFamily(m_document, fontDescription, "-webkit-standard");
487 return fontDataForGenericFamily(m_document, fontDescription, familyName);
490 OwnPtr<HashMap<unsigned, RefPtr<CSSSegmentedFontFace> > >& segmentedFontFaceCache = m_fonts.add(family, nullptr).iterator->second;
491 if (!segmentedFontFaceCache)
492 segmentedFontFaceCache = adoptPtr(new HashMap<unsigned, RefPtr<CSSSegmentedFontFace> >);
494 FontTraitsMask traitsMask = fontDescription.traitsMask();
496 RefPtr<CSSSegmentedFontFace>& face = segmentedFontFaceCache->add(traitsMask, 0).iterator->second;
498 face = CSSSegmentedFontFace::create(this);
500 // Collect all matching faces and sort them in order of preference.
501 Vector<CSSFontFace*, 32> candidateFontFaces;
502 for (int i = familyFontFaces->size() - 1; i >= 0; --i) {
503 CSSFontFace* candidate = familyFontFaces->at(i).get();
504 unsigned candidateTraitsMask = candidate->traitsMask();
505 if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask))
507 if ((traitsMask & FontVariantNormalMask) && !(candidateTraitsMask & FontVariantNormalMask))
509 #if ENABLE(SVG_FONTS)
510 // For SVG Fonts that specify that they only support the "normal" variant, we will assume they are incapable
511 // of small-caps synthesis and just ignore the font face as a candidate.
512 if (candidate->hasSVGFontFaceSource() && (traitsMask & FontVariantSmallCapsMask) && !(candidateTraitsMask & FontVariantSmallCapsMask))
515 candidateFontFaces.append(candidate);
518 if (Vector<RefPtr<CSSFontFace> >* familyLocallyInstalledFontFaces = m_locallyInstalledFontFaces.get(family)) {
519 unsigned numLocallyInstalledFontFaces = familyLocallyInstalledFontFaces->size();
520 for (unsigned i = 0; i < numLocallyInstalledFontFaces; ++i) {
521 CSSFontFace* candidate = familyLocallyInstalledFontFaces->at(i).get();
522 unsigned candidateTraitsMask = candidate->traitsMask();
523 if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask))
525 if ((traitsMask & FontVariantNormalMask) && !(candidateTraitsMask & FontVariantNormalMask))
527 candidateFontFaces.append(candidate);
531 desiredTraitsMaskForComparison = traitsMask;
532 stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), compareFontFaces);
533 unsigned numCandidates = candidateFontFaces.size();
534 for (unsigned i = 0; i < numCandidates; ++i)
535 face->appendFontFace(candidateFontFaces[i]);
538 // We have a face. Ask it for a font data. If it cannot produce one, it will fail, and the OS will take over.
539 return face->getFontData(fontDescription);
542 void CSSFontSelector::clearDocument()
545 ASSERT(!m_beginLoadingTimer.isActive());
546 ASSERT(m_fontsToBeginLoading.isEmpty());
550 m_beginLoadingTimer.stop();
552 CachedResourceLoader* cachedResourceLoader = m_document->cachedResourceLoader();
553 for (size_t i = 0; i < m_fontsToBeginLoading.size(); ++i) {
554 // Balances incrementRequestCount() in beginLoadingFontSoon().
555 cachedResourceLoader->decrementRequestCount(m_fontsToBeginLoading[i].get());
558 m_fontsToBeginLoading.clear();
563 void CSSFontSelector::beginLoadingFontSoon(CachedFont* font)
568 m_fontsToBeginLoading.append(font);
569 // Increment the request count now, in order to prevent didFinishLoad from being dispatched
570 // after this font has been requested but before it began loading. Balanced by
571 // decrementRequestCount() in beginLoadTimerFired() and in clearDocument().
572 m_document->cachedResourceLoader()->incrementRequestCount(font);
573 m_beginLoadingTimer.startOneShot(0);
576 void CSSFontSelector::beginLoadTimerFired(Timer<WebCore::CSSFontSelector>*)
578 Vector<CachedResourceHandle<CachedFont> > fontsToBeginLoading;
579 fontsToBeginLoading.swap(m_fontsToBeginLoading);
581 CachedResourceLoader* cachedResourceLoader = m_document->cachedResourceLoader();
582 for (size_t i = 0; i < fontsToBeginLoading.size(); ++i) {
583 fontsToBeginLoading[i]->beginLoadIfNeeded(cachedResourceLoader);
584 // Balances incrementRequestCount() in beginLoadingFontSoon().
585 cachedResourceLoader->decrementRequestCount(fontsToBeginLoading[i].get());
587 // Ensure that if the request count reaches zero, the frame loader will know about it.
588 cachedResourceLoader->loadDone();