Home | History | Annotate | Download | only in css
      1 /*
      2  * Copyright (C) 2013 Google 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 are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "core/css/FontFace.h"
     33 
     34 #include "CSSValueKeywords.h"
     35 #include "FontFamilyNames.h"
     36 #include "bindings/v8/Dictionary.h"
     37 #include "bindings/v8/ExceptionState.h"
     38 #include "bindings/v8/ScriptPromiseResolver.h"
     39 #include "bindings/v8/ScriptScope.h"
     40 #include "bindings/v8/ScriptState.h"
     41 #include "core/css/CSSFontFace.h"
     42 #include "core/css/CSSFontFaceSrcValue.h"
     43 #include "core/css/CSSParser.h"
     44 #include "core/css/CSSPrimitiveValue.h"
     45 #include "core/css/CSSUnicodeRangeValue.h"
     46 #include "core/css/CSSValueList.h"
     47 #include "core/css/StylePropertySet.h"
     48 #include "core/css/StyleRule.h"
     49 #include "core/dom/DOMError.h"
     50 #include "core/dom/Document.h"
     51 #include "core/dom/ExceptionCode.h"
     52 #include "core/frame/Frame.h"
     53 #include "core/frame/Settings.h"
     54 #include "core/svg/SVGFontFaceElement.h"
     55 #include "platform/fonts/FontDescription.h"
     56 #include "platform/fonts/FontTraitsMask.h"
     57 #include "platform/fonts/SimpleFontData.h"
     58 
     59 namespace WebCore {
     60 
     61 class FontFaceReadyPromiseResolver {
     62 public:
     63     static PassOwnPtr<FontFaceReadyPromiseResolver> create(ScriptPromise promise, ExecutionContext* context)
     64     {
     65         return adoptPtr(new FontFaceReadyPromiseResolver(promise, context));
     66     }
     67 
     68     void resolve(PassRefPtr<FontFace> fontFace)
     69     {
     70         ScriptScope scope(m_scriptState);
     71         switch (fontFace->loadStatus()) {
     72         case FontFace::Loaded:
     73             m_resolver->resolve(fontFace);
     74             break;
     75         case FontFace::Error:
     76             m_resolver->reject(DOMError::create(NetworkError));
     77             break;
     78         default:
     79             ASSERT_NOT_REACHED();
     80         }
     81     }
     82 
     83 private:
     84     FontFaceReadyPromiseResolver(ScriptPromise promise, ExecutionContext* context)
     85         : m_scriptState(ScriptState::current())
     86         , m_resolver(ScriptPromiseResolver::create(promise, context))
     87     { }
     88     ScriptState* m_scriptState;
     89     RefPtr<ScriptPromiseResolver> m_resolver;
     90 };
     91 
     92 static PassRefPtr<CSSValue> parseCSSValue(const String& s, CSSPropertyID propertyID)
     93 {
     94     if (s.isEmpty())
     95         return 0;
     96     RefPtr<MutableStylePropertySet> parsedStyle = MutableStylePropertySet::create();
     97     CSSParser::parseValue(parsedStyle.get(), propertyID, s, true, HTMLStandardMode, 0);
     98     return parsedStyle->getPropertyCSSValue(propertyID);
     99 }
    100 
    101 PassRefPtr<FontFace> FontFace::create(const AtomicString& family, const String& source, const Dictionary& descriptors, ExceptionState& exceptionState)
    102 {
    103     RefPtr<CSSValue> src = parseCSSValue(source, CSSPropertySrc);
    104     if (!src || !src->isValueList()) {
    105         exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
    106         return 0;
    107     }
    108 
    109     RefPtr<FontFace> fontFace = adoptRef<FontFace>(new FontFace(src));
    110     fontFace->setFamily(family, exceptionState);
    111     if (exceptionState.hadException())
    112         return 0;
    113 
    114     String value;
    115     if (descriptors.get("style", value)) {
    116         fontFace->setStyle(value, exceptionState);
    117         if (exceptionState.hadException())
    118             return 0;
    119     }
    120     if (descriptors.get("weight", value)) {
    121         fontFace->setWeight(value, exceptionState);
    122         if (exceptionState.hadException())
    123             return 0;
    124     }
    125     if (descriptors.get("stretch", value)) {
    126         fontFace->setStretch(value, exceptionState);
    127         if (exceptionState.hadException())
    128             return 0;
    129     }
    130     if (descriptors.get("unicodeRange", value)) {
    131         fontFace->setUnicodeRange(value, exceptionState);
    132         if (exceptionState.hadException())
    133             return 0;
    134     }
    135     if (descriptors.get("variant", value)) {
    136         fontFace->setVariant(value, exceptionState);
    137         if (exceptionState.hadException())
    138             return 0;
    139     }
    140     if (descriptors.get("featureSettings", value)) {
    141         fontFace->setFeatureSettings(value, exceptionState);
    142         if (exceptionState.hadException())
    143             return 0;
    144     }
    145 
    146     return fontFace;
    147 }
    148 
    149 PassRefPtr<FontFace> FontFace::create(const StyleRuleFontFace* fontFaceRule)
    150 {
    151     const StylePropertySet* properties = fontFaceRule->properties();
    152 
    153     // Obtain the font-family property and the src property. Both must be defined.
    154     RefPtr<CSSValue> family = properties->getPropertyCSSValue(CSSPropertyFontFamily);
    155     if (!family || !family->isValueList())
    156         return 0;
    157     RefPtr<CSSValue> src = properties->getPropertyCSSValue(CSSPropertySrc);
    158     if (!src || !src->isValueList())
    159         return 0;
    160 
    161     RefPtr<FontFace> fontFace = adoptRef<FontFace>(new FontFace(src));
    162 
    163     if (fontFace->setFamilyValue(toCSSValueList(family.get()))
    164         && fontFace->setPropertyFromStyle(properties, CSSPropertyFontStyle)
    165         && fontFace->setPropertyFromStyle(properties, CSSPropertyFontWeight)
    166         && fontFace->setPropertyFromStyle(properties, CSSPropertyFontStretch)
    167         && fontFace->setPropertyFromStyle(properties, CSSPropertyUnicodeRange)
    168         && fontFace->setPropertyFromStyle(properties, CSSPropertyFontVariant)
    169         && fontFace->setPropertyFromStyle(properties, CSSPropertyWebkitFontFeatureSettings))
    170         return fontFace;
    171     return 0;
    172 }
    173 
    174 FontFace::FontFace(PassRefPtr<CSSValue> src)
    175     : m_src(src)
    176     , m_status(Unloaded)
    177     , m_cssFontFace(0)
    178 {
    179 }
    180 
    181 FontFace::~FontFace()
    182 {
    183 }
    184 
    185 String FontFace::style() const
    186 {
    187     return m_style ? m_style->cssText() : "normal";
    188 }
    189 
    190 String FontFace::weight() const
    191 {
    192     return m_weight ? m_weight->cssText() : "normal";
    193 }
    194 
    195 String FontFace::stretch() const
    196 {
    197     return m_stretch ? m_stretch->cssText() : "normal";
    198 }
    199 
    200 String FontFace::unicodeRange() const
    201 {
    202     return m_unicodeRange ? m_unicodeRange->cssText() : "U+0-10FFFF";
    203 }
    204 
    205 String FontFace::variant() const
    206 {
    207     return m_variant ? m_variant->cssText() : "normal";
    208 }
    209 
    210 String FontFace::featureSettings() const
    211 {
    212     return m_featureSettings ? m_featureSettings->cssText() : "normal";
    213 }
    214 
    215 void FontFace::setStyle(const String& s, ExceptionState& exceptionState)
    216 {
    217     setPropertyFromString(s, CSSPropertyFontStyle, exceptionState);
    218 }
    219 
    220 void FontFace::setWeight(const String& s, ExceptionState& exceptionState)
    221 {
    222     setPropertyFromString(s, CSSPropertyFontWeight, exceptionState);
    223 }
    224 
    225 void FontFace::setStretch(const String& s, ExceptionState& exceptionState)
    226 {
    227     setPropertyFromString(s, CSSPropertyFontStretch, exceptionState);
    228 }
    229 
    230 void FontFace::setUnicodeRange(const String& s, ExceptionState& exceptionState)
    231 {
    232     setPropertyFromString(s, CSSPropertyUnicodeRange, exceptionState);
    233 }
    234 
    235 void FontFace::setVariant(const String& s, ExceptionState& exceptionState)
    236 {
    237     setPropertyFromString(s, CSSPropertyFontVariant, exceptionState);
    238 }
    239 
    240 void FontFace::setFeatureSettings(const String& s, ExceptionState& exceptionState)
    241 {
    242     setPropertyFromString(s, CSSPropertyWebkitFontFeatureSettings, exceptionState);
    243 }
    244 
    245 void FontFace::setPropertyFromString(const String& s, CSSPropertyID propertyID, ExceptionState& exceptionState)
    246 {
    247     RefPtr<CSSValue> value = parseCSSValue(s, propertyID);
    248     if (!value || !setPropertyValue(value, propertyID))
    249         exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
    250 }
    251 
    252 bool FontFace::setPropertyFromStyle(const StylePropertySet* properties, CSSPropertyID propertyID)
    253 {
    254     return setPropertyValue(properties->getPropertyCSSValue(propertyID), propertyID);
    255 }
    256 
    257 bool FontFace::setPropertyValue(PassRefPtr<CSSValue> value, CSSPropertyID propertyID)
    258 {
    259     switch (propertyID) {
    260     case CSSPropertyFontStyle:
    261         m_style = value;
    262         break;
    263     case CSSPropertyFontWeight:
    264         m_weight = value;
    265         break;
    266     case CSSPropertyFontStretch:
    267         m_stretch = value;
    268         break;
    269     case CSSPropertyUnicodeRange:
    270         if (value && !value->isValueList())
    271             return false;
    272         m_unicodeRange = value;
    273         break;
    274     case CSSPropertyFontVariant:
    275         m_variant = value;
    276         break;
    277     case CSSPropertyWebkitFontFeatureSettings:
    278         m_featureSettings = value;
    279         break;
    280     default:
    281         ASSERT_NOT_REACHED();
    282         return false;
    283     }
    284     return true;
    285 }
    286 
    287 bool FontFace::setFamilyValue(CSSValueList* familyList)
    288 {
    289     // The font-family descriptor has to have exactly one family name.
    290     if (familyList->length() != 1)
    291         return false;
    292 
    293     CSSPrimitiveValue* familyValue = toCSSPrimitiveValue(familyList->itemWithoutBoundsCheck(0));
    294     AtomicString family;
    295     if (familyValue->isString()) {
    296         family = AtomicString(familyValue->getStringValue());
    297     } else if (familyValue->isValueID()) {
    298         // We need to use the raw text for all the generic family types, since @font-face is a way of actually
    299         // defining what font to use for those types.
    300         switch (familyValue->getValueID()) {
    301         case CSSValueSerif:
    302             family =  FontFamilyNames::webkit_serif;
    303             break;
    304         case CSSValueSansSerif:
    305             family =  FontFamilyNames::webkit_sans_serif;
    306             break;
    307         case CSSValueCursive:
    308             family =  FontFamilyNames::webkit_cursive;
    309             break;
    310         case CSSValueFantasy:
    311             family =  FontFamilyNames::webkit_fantasy;
    312             break;
    313         case CSSValueMonospace:
    314             family =  FontFamilyNames::webkit_monospace;
    315             break;
    316         case CSSValueWebkitPictograph:
    317             family =  FontFamilyNames::webkit_pictograph;
    318             break;
    319         default:
    320             return false;
    321         }
    322     }
    323     m_family = family;
    324     return true;
    325 }
    326 
    327 String FontFace::status() const
    328 {
    329     switch (m_status) {
    330     case Unloaded:
    331         return "unloaded";
    332     case Loading:
    333         return "loading";
    334     case Loaded:
    335         return "loaded";
    336     case Error:
    337         return "error";
    338     default:
    339         ASSERT_NOT_REACHED();
    340     }
    341     return emptyString();
    342 }
    343 
    344 void FontFace::setLoadStatus(LoadStatus status)
    345 {
    346     m_status = status;
    347     if (m_status == Loaded || m_status == Error)
    348         resolveReadyPromises();
    349 }
    350 
    351 void FontFace::load()
    352 {
    353     // FIXME: This does not load FontFace created by JavaScript, since m_cssFontFace is null.
    354     if (m_status != Unloaded || !m_cssFontFace)
    355         return;
    356 
    357     FontDescription fontDescription;
    358     FontFamily fontFamily;
    359     fontFamily.setFamily(m_family);
    360     fontDescription.setFamily(fontFamily);
    361     fontDescription.setTraitsMask(static_cast<FontTraitsMask>(traitsMask()));
    362 
    363     RefPtr<SimpleFontData> fontData = m_cssFontFace->getFontData(fontDescription);
    364     if (fontData && fontData->customFontData())
    365         fontData->customFontData()->beginLoadIfNeeded();
    366 }
    367 
    368 ScriptPromise FontFace::ready(ExecutionContext* context)
    369 {
    370     ScriptPromise promise = ScriptPromise::createPending(context);
    371     OwnPtr<FontFaceReadyPromiseResolver> resolver = FontFaceReadyPromiseResolver::create(promise, context);
    372     if (m_status == Loaded || m_status == Error)
    373         resolver->resolve(this);
    374     else
    375         m_readyResolvers.append(resolver.release());
    376     return promise;
    377 }
    378 
    379 void FontFace::resolveReadyPromises()
    380 {
    381     for (size_t i = 0; i < m_readyResolvers.size(); i++)
    382         m_readyResolvers[i]->resolve(this);
    383     m_readyResolvers.clear();
    384 }
    385 
    386 unsigned FontFace::traitsMask() const
    387 {
    388     unsigned traitsMask = 0;
    389 
    390     if (m_style) {
    391         if (!m_style->isPrimitiveValue())
    392             return 0;
    393 
    394         switch (toCSSPrimitiveValue(m_style.get())->getValueID()) {
    395         case CSSValueNormal:
    396             traitsMask |= FontStyleNormalMask;
    397             break;
    398         case CSSValueItalic:
    399         case CSSValueOblique:
    400             traitsMask |= FontStyleItalicMask;
    401             break;
    402         default:
    403             break;
    404         }
    405     } else {
    406         traitsMask |= FontStyleNormalMask;
    407     }
    408 
    409     if (m_weight) {
    410         if (!m_weight->isPrimitiveValue())
    411             return 0;
    412 
    413         switch (toCSSPrimitiveValue(m_weight.get())->getValueID()) {
    414         case CSSValueBold:
    415         case CSSValue700:
    416             traitsMask |= FontWeight700Mask;
    417             break;
    418         case CSSValueNormal:
    419         case CSSValue400:
    420             traitsMask |= FontWeight400Mask;
    421             break;
    422         case CSSValue900:
    423             traitsMask |= FontWeight900Mask;
    424             break;
    425         case CSSValue800:
    426             traitsMask |= FontWeight800Mask;
    427             break;
    428         case CSSValue600:
    429             traitsMask |= FontWeight600Mask;
    430             break;
    431         case CSSValue500:
    432             traitsMask |= FontWeight500Mask;
    433             break;
    434         case CSSValue300:
    435             traitsMask |= FontWeight300Mask;
    436             break;
    437         case CSSValue200:
    438             traitsMask |= FontWeight200Mask;
    439             break;
    440         case CSSValue100:
    441             traitsMask |= FontWeight100Mask;
    442             break;
    443         default:
    444             break;
    445         }
    446     } else {
    447         traitsMask |= FontWeight400Mask;
    448     }
    449 
    450     if (RefPtr<CSSValue> fontVariant = m_variant) {
    451         // font-variant descriptor can be a value list.
    452         if (fontVariant->isPrimitiveValue()) {
    453             RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
    454             list->append(fontVariant);
    455             fontVariant = list;
    456         } else if (!fontVariant->isValueList()) {
    457             return 0;
    458         }
    459 
    460         CSSValueList* variantList = toCSSValueList(fontVariant.get());
    461         unsigned numVariants = variantList->length();
    462         if (!numVariants)
    463             return 0;
    464 
    465         for (unsigned i = 0; i < numVariants; ++i) {
    466             switch (toCSSPrimitiveValue(variantList->itemWithoutBoundsCheck(i))->getValueID()) {
    467             case CSSValueNormal:
    468                 traitsMask |= FontVariantNormalMask;
    469                 break;
    470             case CSSValueSmallCaps:
    471                 traitsMask |= FontVariantSmallCapsMask;
    472                 break;
    473             default:
    474                 break;
    475             }
    476         }
    477     } else {
    478         traitsMask |= FontVariantNormalMask;
    479     }
    480     return traitsMask;
    481 }
    482 
    483 PassRefPtr<CSSFontFace> FontFace::createCSSFontFace(Document* document)
    484 {
    485     if (m_cssFontFace)
    486         return m_cssFontFace;
    487 
    488     RefPtr<CSSFontFace> cssFontFace = CSSFontFace::create(this);
    489     m_cssFontFace = cssFontFace.get();
    490 
    491     // Each item in the src property's list is a single CSSFontFaceSource. Put them all into a CSSFontFace.
    492     CSSValueList* srcList = toCSSValueList(m_src.get());
    493     int srcLength = srcList->length();
    494 
    495     bool foundSVGFont = false;
    496 
    497     for (int i = 0; i < srcLength; i++) {
    498         // An item in the list either specifies a string (local font name) or a URL (remote font to download).
    499         CSSFontFaceSrcValue* item = toCSSFontFaceSrcValue(srcList->itemWithoutBoundsCheck(i));
    500         OwnPtr<CSSFontFaceSource> source;
    501 
    502 #if ENABLE(SVG_FONTS)
    503         foundSVGFont = item->isSVGFontFaceSrc() || item->svgFontFaceElement();
    504 #endif
    505         if (!item->isLocal()) {
    506             Settings* settings = document ? document->frame() ? document->frame()->settings() : 0 : 0;
    507             bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled());
    508             if (allowDownloading && item->isSupportedFormat() && document) {
    509                 FontResource* fetched = item->fetch(document);
    510                 if (fetched) {
    511                     source = adoptPtr(new CSSFontFaceSource(item->resource(), fetched));
    512 #if ENABLE(SVG_FONTS)
    513                     if (foundSVGFont)
    514                         source->setHasExternalSVGFont(true);
    515 #endif
    516                 }
    517             }
    518         } else {
    519             source = adoptPtr(new CSSFontFaceSource(item->resource()));
    520         }
    521 
    522         if (source) {
    523 #if ENABLE(SVG_FONTS)
    524             source->setSVGFontFaceElement(item->svgFontFaceElement());
    525 #endif
    526             cssFontFace->addSource(source.release());
    527         }
    528     }
    529 
    530     if (CSSValueList* rangeList = toCSSValueList(m_unicodeRange.get())) {
    531         unsigned numRanges = rangeList->length();
    532         for (unsigned i = 0; i < numRanges; i++) {
    533             CSSUnicodeRangeValue* range = toCSSUnicodeRangeValue(rangeList->itemWithoutBoundsCheck(i));
    534             cssFontFace->ranges().add(range->from(), range->to());
    535         }
    536     }
    537     return cssFontFace;
    538 }
    539 
    540 } // namespace WebCore
    541