Home | History | Annotate | Download | only in css
      1 /*
      2  * Copyright (C) 2007, 2008, 2011 Apple Inc. All rights reserved.
      3  * Copyright (C) 2013 Google Inc. All rights reserved.
      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 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.
     25  */
     26 
     27 
     28 #include "config.h"
     29 #include "core/css/FontFaceCache.h"
     30 
     31 #include "core/css/CSSFontSelector.h"
     32 #include "core/css/CSSSegmentedFontFace.h"
     33 #include "core/css/CSSValueList.h"
     34 #include "core/css/FontFace.h"
     35 #include "core/css/StyleRule.h"
     36 #include "core/fetch/FontResource.h"
     37 #include "core/fetch/ResourceFetcher.h"
     38 #include "platform/FontFamilyNames.h"
     39 #include "platform/fonts/FontDescription.h"
     40 #include "wtf/text/AtomicString.h"
     41 
     42 namespace WebCore {
     43 
     44 FontFaceCache::FontFaceCache()
     45     : m_version(0)
     46 {
     47 }
     48 
     49 void FontFaceCache::add(CSSFontSelector* cssFontSelector, const StyleRuleFontFace* fontFaceRule, PassRefPtrWillBeRawPtr<FontFace> prpFontFace)
     50 {
     51     RefPtrWillBeRawPtr<FontFace> fontFace = prpFontFace;
     52     if (!m_styleRuleToFontFace.add(fontFaceRule, fontFace).isNewEntry)
     53         return;
     54     addFontFace(cssFontSelector, fontFace, true);
     55 }
     56 
     57 void FontFaceCache::addFontFace(CSSFontSelector* cssFontSelector, PassRefPtrWillBeRawPtr<FontFace> prpFontFace, bool cssConnected)
     58 {
     59     RefPtrWillBeRawPtr<FontFace> fontFace = prpFontFace;
     60 
     61     FamilyToTraitsMap::AddResult traitsResult = m_fontFaces.add(fontFace->family(), nullptr);
     62     if (!traitsResult.storedValue->value)
     63         traitsResult.storedValue->value = adoptPtrWillBeNoop(new TraitsMap);
     64 
     65     TraitsMap::AddResult segmentedFontFaceResult = traitsResult.storedValue->value->add(fontFace->traits().mask(), nullptr);
     66     if (!segmentedFontFaceResult.storedValue->value)
     67         segmentedFontFaceResult.storedValue->value = CSSSegmentedFontFace::create(cssFontSelector, fontFace->traits());
     68 
     69     segmentedFontFaceResult.storedValue->value->addFontFace(fontFace, cssConnected);
     70     if (cssConnected)
     71         m_cssConnectedFontFaces.add(fontFace);
     72 
     73     ++m_version;
     74 }
     75 
     76 void FontFaceCache::remove(const StyleRuleFontFace* fontFaceRule)
     77 {
     78     StyleRuleToFontFace::iterator it = m_styleRuleToFontFace.find(fontFaceRule);
     79     if (it != m_styleRuleToFontFace.end()) {
     80         removeFontFace(it->value.get(), true);
     81         m_styleRuleToFontFace.remove(it);
     82     }
     83 }
     84 
     85 void FontFaceCache::removeFontFace(FontFace* fontFace, bool cssConnected)
     86 {
     87     FamilyToTraitsMap::iterator fontFacesIter = m_fontFaces.find(fontFace->family());
     88     if (fontFacesIter == m_fontFaces.end())
     89         return;
     90     TraitsMap* familyFontFaces = fontFacesIter->value.get();
     91 
     92     TraitsMap::iterator familyFontFacesIter = familyFontFaces->find(fontFace->traits().mask());
     93     if (familyFontFacesIter == familyFontFaces->end())
     94         return;
     95     RefPtrWillBeRawPtr<CSSSegmentedFontFace> segmentedFontFace = familyFontFacesIter->value;
     96 
     97     segmentedFontFace->removeFontFace(fontFace);
     98     if (segmentedFontFace->isEmpty()) {
     99         familyFontFaces->remove(familyFontFacesIter);
    100         if (familyFontFaces->isEmpty())
    101             m_fontFaces.remove(fontFacesIter);
    102     }
    103     m_fonts.clear();
    104     if (cssConnected)
    105         m_cssConnectedFontFaces.remove(fontFace);
    106 
    107     ++m_version;
    108 }
    109 
    110 void FontFaceCache::clear()
    111 {
    112     for (StyleRuleToFontFace::iterator it = m_styleRuleToFontFace.begin(); it != m_styleRuleToFontFace.end(); ++it)
    113         removeFontFace(it->value.get(), true);
    114     m_styleRuleToFontFace.clear();
    115 }
    116 
    117 static inline bool compareFontFaces(CSSSegmentedFontFace* first, CSSSegmentedFontFace* second, FontTraits desiredTraits)
    118 {
    119     const FontTraits& firstTraits = first->traits();
    120     const FontTraits& secondTraits = second->traits();
    121 
    122     bool firstHasDesiredVariant = firstTraits.variant() == desiredTraits.variant();
    123     bool secondHasDesiredVariant = secondTraits.variant() == desiredTraits.variant();
    124 
    125     if (firstHasDesiredVariant != secondHasDesiredVariant)
    126         return firstHasDesiredVariant;
    127 
    128     // We need to check font-variant css property for CSS2.1 compatibility.
    129     if (desiredTraits.variant() == FontVariantSmallCaps) {
    130         // Prefer a font that has indicated that it can only support small-caps to a font that claims to support
    131         // all variants. The specialized font is more likely to be true small-caps and not require synthesis.
    132         bool firstRequiresSmallCaps = firstTraits.variant() == FontVariantSmallCaps;
    133         bool secondRequiresSmallCaps = secondTraits.variant() == FontVariantSmallCaps;
    134         if (firstRequiresSmallCaps != secondRequiresSmallCaps)
    135             return firstRequiresSmallCaps;
    136     }
    137 
    138     bool firstHasDesiredStyle = firstTraits.style() == desiredTraits.style();
    139     bool secondHasDesiredStyle = secondTraits.style() == desiredTraits.style();
    140 
    141     if (firstHasDesiredStyle != secondHasDesiredStyle)
    142         return firstHasDesiredStyle;
    143 
    144     if (desiredTraits.style() == FontStyleItalic) {
    145         // Prefer a font that has indicated that it can only support italics to a font that claims to support
    146         // all styles. The specialized font is more likely to be the one the author wants used.
    147         bool firstRequiresItalics = firstTraits.style() == FontStyleItalic;
    148         bool secondRequiresItalics = secondTraits.style() == FontStyleItalic;
    149         if (firstRequiresItalics != secondRequiresItalics)
    150             return firstRequiresItalics;
    151     }
    152     if (secondTraits.weight() == desiredTraits.weight())
    153         return false;
    154 
    155     if (firstTraits.weight() == desiredTraits.weight())
    156         return true;
    157 
    158     // http://www.w3.org/TR/2011/WD-css3-fonts-20111004/#font-matching-algorithm says :
    159     //   - 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.
    160     //   - 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.
    161     //   - If the desired weight is 400, 500 is checked first and then the rule for desired weights less than 400 is used.
    162     //   - If the desired weight is 500, 400 is checked first and then the rule for desired weights less than 400 is used.
    163     static const unsigned fallbackRuleSets = 9;
    164     static const unsigned rulesPerSet = 8;
    165     static const FontWeight weightFallbackRuleSets[fallbackRuleSets][rulesPerSet] = {
    166         { FontWeight200, FontWeight300, FontWeight400, FontWeight500, FontWeight600, FontWeight700, FontWeight800, FontWeight900 },
    167         { FontWeight100, FontWeight300, FontWeight400, FontWeight500, FontWeight600, FontWeight700, FontWeight800, FontWeight900 },
    168         { FontWeight200, FontWeight100, FontWeight400, FontWeight500, FontWeight600, FontWeight700, FontWeight800, FontWeight900 },
    169         { FontWeight500, FontWeight300, FontWeight200, FontWeight100, FontWeight600, FontWeight700, FontWeight800, FontWeight900 },
    170         { FontWeight400, FontWeight300, FontWeight200, FontWeight100, FontWeight600, FontWeight700, FontWeight800, FontWeight900 },
    171         { FontWeight700, FontWeight800, FontWeight900, FontWeight500, FontWeight400, FontWeight300, FontWeight200, FontWeight100 },
    172         { FontWeight800, FontWeight900, FontWeight600, FontWeight500, FontWeight400, FontWeight300, FontWeight200, FontWeight100 },
    173         { FontWeight900, FontWeight700, FontWeight600, FontWeight500, FontWeight400, FontWeight300, FontWeight200, FontWeight100 },
    174         { FontWeight800, FontWeight700, FontWeight600, FontWeight500, FontWeight400, FontWeight300, FontWeight200, FontWeight100 }
    175     };
    176 
    177     unsigned ruleSetIndex = static_cast<unsigned>(desiredTraits.weight());
    178     ASSERT(ruleSetIndex < fallbackRuleSets);
    179     const FontWeight* weightFallbackRule = weightFallbackRuleSets[ruleSetIndex];
    180     for (unsigned i = 0; i < rulesPerSet; ++i) {
    181         if (secondTraits.weight() == weightFallbackRule[i])
    182             return false;
    183         if (firstTraits.weight() == weightFallbackRule[i])
    184             return true;
    185     }
    186 
    187     return false;
    188 }
    189 
    190 CSSSegmentedFontFace* FontFaceCache::get(const FontDescription& fontDescription, const AtomicString& family)
    191 {
    192     TraitsMap* familyFontFaces = m_fontFaces.get(family);
    193     if (!familyFontFaces || familyFontFaces->isEmpty())
    194         return 0;
    195 
    196     FamilyToTraitsMap::AddResult traitsResult = m_fonts.add(family, nullptr);
    197     if (!traitsResult.storedValue->value)
    198         traitsResult.storedValue->value = adoptPtrWillBeNoop(new TraitsMap);
    199 
    200     FontTraits traits = fontDescription.traits();
    201     TraitsMap::AddResult faceResult = traitsResult.storedValue->value->add(traits.mask(), nullptr);
    202     if (!faceResult.storedValue->value) {
    203         for (TraitsMap::const_iterator i = familyFontFaces->begin(); i != familyFontFaces->end(); ++i) {
    204             CSSSegmentedFontFace* candidate = i->value.get();
    205             FontTraits candidateTraits = candidate->traits();
    206             if (traits.style() == FontStyleNormal && candidateTraits.style() != FontStyleNormal)
    207                 continue;
    208             if (traits.variant() == FontVariantNormal && candidateTraits.variant() != FontVariantNormal)
    209                 continue;
    210             if (!faceResult.storedValue->value || compareFontFaces(candidate, faceResult.storedValue->value.get(), traits))
    211                 faceResult.storedValue->value = candidate;
    212         }
    213     }
    214     return faceResult.storedValue->value.get();
    215 }
    216 
    217 void FontFaceCache::trace(Visitor* visitor)
    218 {
    219     visitor->trace(m_fontFaces);
    220     visitor->trace(m_fonts);
    221     visitor->trace(m_styleRuleToFontFace);
    222     visitor->trace(m_cssConnectedFontFaces);
    223 }
    224 
    225 }
    226