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 "bindings/core/v8/ExceptionState.h"
     35 #include "bindings/core/v8/ScriptState.h"
     36 #include "core/CSSValueKeywords.h"
     37 #include "core/css/BinaryDataFontFaceSource.h"
     38 #include "core/css/CSSFontFace.h"
     39 #include "core/css/CSSFontFaceSrcValue.h"
     40 #include "core/css/CSSFontSelector.h"
     41 #include "core/css/CSSPrimitiveValue.h"
     42 #include "core/css/CSSUnicodeRangeValue.h"
     43 #include "core/css/CSSValueList.h"
     44 #include "core/css/FontFaceDescriptors.h"
     45 #include "core/css/LocalFontFaceSource.h"
     46 #include "core/css/RemoteFontFaceSource.h"
     47 #include "core/css/StylePropertySet.h"
     48 #include "core/css/StyleRule.h"
     49 #include "core/css/parser/CSSParser.h"
     50 #include "core/dom/DOMException.h"
     51 #include "core/dom/Document.h"
     52 #include "core/dom/ExceptionCode.h"
     53 #include "core/dom/StyleEngine.h"
     54 #include "core/frame/LocalFrame.h"
     55 #include "core/frame/Settings.h"
     56 #include "core/frame/UseCounter.h"
     57 #include "core/svg/SVGFontFaceElement.h"
     58 #include "core/svg/SVGFontFaceSource.h"
     59 #include "core/svg/SVGRemoteFontFaceSource.h"
     60 #include "platform/FontFamilyNames.h"
     61 #include "platform/SharedBuffer.h"
     62 
     63 namespace blink {
     64 
     65 static PassRefPtrWillBeRawPtr<CSSValue> parseCSSValue(const Document* document, const String& s, CSSPropertyID propertyID)
     66 {
     67     CSSParserContext context(*document, UseCounter::getFrom(document));
     68     return CSSParser::parseSingleValue(propertyID, s, context);
     69 }
     70 
     71 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, const String& source, const FontFaceDescriptors& descriptors)
     72 {
     73     RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace(context, family, descriptors));
     74 
     75     RefPtrWillBeRawPtr<CSSValue> src = parseCSSValue(toDocument(context), source, CSSPropertySrc);
     76     if (!src || !src->isValueList())
     77         fontFace->setError(DOMException::create(SyntaxError, "The source provided ('" + source + "') could not be parsed as a value list."));
     78 
     79     fontFace->initCSSFontFace(toDocument(context), src);
     80     return fontFace.release();
     81 }
     82 
     83 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, PassRefPtr<ArrayBuffer> source, const FontFaceDescriptors& descriptors)
     84 {
     85     RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace(context, family, descriptors));
     86     fontFace->initCSSFontFace(static_cast<const unsigned char*>(source->data()), source->byteLength());
     87     return fontFace.release();
     88 }
     89 
     90 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, PassRefPtr<ArrayBufferView> source, const FontFaceDescriptors& descriptors)
     91 {
     92     RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace(context, family, descriptors));
     93     fontFace->initCSSFontFace(static_cast<const unsigned char*>(source->baseAddress()), source->byteLength());
     94     return fontFace.release();
     95 }
     96 
     97 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(Document* document, const StyleRuleFontFace* fontFaceRule)
     98 {
     99     const StylePropertySet& properties = fontFaceRule->properties();
    100 
    101     // Obtain the font-family property and the src property. Both must be defined.
    102     RefPtrWillBeRawPtr<CSSValue> family = properties.getPropertyCSSValue(CSSPropertyFontFamily);
    103     if (!family || !family->isValueList())
    104         return nullptr;
    105     RefPtrWillBeRawPtr<CSSValue> src = properties.getPropertyCSSValue(CSSPropertySrc);
    106     if (!src || !src->isValueList())
    107         return nullptr;
    108 
    109     RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace(document));
    110 
    111     if (fontFace->setFamilyValue(toCSSValueList(family.get()))
    112         && fontFace->setPropertyFromStyle(properties, CSSPropertyFontStyle)
    113         && fontFace->setPropertyFromStyle(properties, CSSPropertyFontWeight)
    114         && fontFace->setPropertyFromStyle(properties, CSSPropertyFontStretch)
    115         && fontFace->setPropertyFromStyle(properties, CSSPropertyUnicodeRange)
    116         && fontFace->setPropertyFromStyle(properties, CSSPropertyFontVariant)
    117         && fontFace->setPropertyFromStyle(properties, CSSPropertyWebkitFontFeatureSettings)
    118         && !fontFace->family().isEmpty()
    119         && fontFace->traits().bitfield()) {
    120         fontFace->initCSSFontFace(document, src);
    121         return fontFace.release();
    122     }
    123     return nullptr;
    124 }
    125 
    126 FontFace::FontFace(ExecutionContext* context)
    127     : ActiveDOMObject(context)
    128     , m_status(Unloaded)
    129 {
    130     suspendIfNeeded();
    131 }
    132 
    133 FontFace::FontFace(ExecutionContext* context, const AtomicString& family, const FontFaceDescriptors& descriptors)
    134     : ActiveDOMObject(context)
    135     , m_family(family)
    136     , m_status(Unloaded)
    137 {
    138     Document* document = toDocument(context);
    139     setPropertyFromString(document, descriptors.style(), CSSPropertyFontStyle);
    140     setPropertyFromString(document, descriptors.weight(), CSSPropertyFontWeight);
    141     // FIXME: we don't implement 'font-strech' property yet so we can't set the property.
    142     setPropertyFromString(document, descriptors.unicodeRange(), CSSPropertyUnicodeRange);
    143     setPropertyFromString(document, descriptors.variant(), CSSPropertyFontVariant);
    144     setPropertyFromString(document, descriptors.featureSettings(), CSSPropertyWebkitFontFeatureSettings);
    145 
    146     suspendIfNeeded();
    147 }
    148 
    149 FontFace::~FontFace()
    150 {
    151 }
    152 
    153 String FontFace::style() const
    154 {
    155     return m_style ? m_style->cssText() : "normal";
    156 }
    157 
    158 String FontFace::weight() const
    159 {
    160     return m_weight ? m_weight->cssText() : "normal";
    161 }
    162 
    163 String FontFace::stretch() const
    164 {
    165     return m_stretch ? m_stretch->cssText() : "normal";
    166 }
    167 
    168 String FontFace::unicodeRange() const
    169 {
    170     return m_unicodeRange ? m_unicodeRange->cssText() : "U+0-10FFFF";
    171 }
    172 
    173 String FontFace::variant() const
    174 {
    175     return m_variant ? m_variant->cssText() : "normal";
    176 }
    177 
    178 String FontFace::featureSettings() const
    179 {
    180     return m_featureSettings ? m_featureSettings->cssText() : "normal";
    181 }
    182 
    183 void FontFace::setStyle(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
    184 {
    185     setPropertyFromString(toDocument(context), s, CSSPropertyFontStyle, &exceptionState);
    186 }
    187 
    188 void FontFace::setWeight(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
    189 {
    190     setPropertyFromString(toDocument(context), s, CSSPropertyFontWeight, &exceptionState);
    191 }
    192 
    193 void FontFace::setStretch(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
    194 {
    195     setPropertyFromString(toDocument(context), s, CSSPropertyFontStretch, &exceptionState);
    196 }
    197 
    198 void FontFace::setUnicodeRange(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
    199 {
    200     setPropertyFromString(toDocument(context), s, CSSPropertyUnicodeRange, &exceptionState);
    201 }
    202 
    203 void FontFace::setVariant(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
    204 {
    205     setPropertyFromString(toDocument(context), s, CSSPropertyFontVariant, &exceptionState);
    206 }
    207 
    208 void FontFace::setFeatureSettings(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
    209 {
    210     setPropertyFromString(toDocument(context), s, CSSPropertyWebkitFontFeatureSettings, &exceptionState);
    211 }
    212 
    213 void FontFace::setPropertyFromString(const Document* document, const String& s, CSSPropertyID propertyID, ExceptionState* exceptionState)
    214 {
    215     RefPtrWillBeRawPtr<CSSValue> value = parseCSSValue(document, s, propertyID);
    216     if (value && setPropertyValue(value, propertyID))
    217         return;
    218 
    219     String message = "Failed to set '" + s + "' as a property value.";
    220     if (exceptionState)
    221         exceptionState->throwDOMException(SyntaxError, message);
    222     else
    223         setError(DOMException::create(SyntaxError, message));
    224 }
    225 
    226 bool FontFace::setPropertyFromStyle(const StylePropertySet& properties, CSSPropertyID propertyID)
    227 {
    228     return setPropertyValue(properties.getPropertyCSSValue(propertyID), propertyID);
    229 }
    230 
    231 bool FontFace::setPropertyValue(PassRefPtrWillBeRawPtr<CSSValue> value, CSSPropertyID propertyID)
    232 {
    233     switch (propertyID) {
    234     case CSSPropertyFontStyle:
    235         m_style = value;
    236         break;
    237     case CSSPropertyFontWeight:
    238         m_weight = value;
    239         break;
    240     case CSSPropertyFontStretch:
    241         m_stretch = value;
    242         break;
    243     case CSSPropertyUnicodeRange:
    244         if (value && !value->isValueList())
    245             return false;
    246         m_unicodeRange = value;
    247         break;
    248     case CSSPropertyFontVariant:
    249         m_variant = value;
    250         break;
    251     case CSSPropertyWebkitFontFeatureSettings:
    252         m_featureSettings = value;
    253         break;
    254     default:
    255         ASSERT_NOT_REACHED();
    256         return false;
    257     }
    258     return true;
    259 }
    260 
    261 bool FontFace::setFamilyValue(CSSValueList* familyList)
    262 {
    263     // The font-family descriptor has to have exactly one family name.
    264     if (familyList->length() != 1)
    265         return false;
    266 
    267     CSSPrimitiveValue* familyValue = toCSSPrimitiveValue(familyList->item(0));
    268     AtomicString family;
    269     if (familyValue->isString()) {
    270         family = AtomicString(familyValue->getStringValue());
    271     } else if (familyValue->isValueID()) {
    272         // We need to use the raw text for all the generic family types, since @font-face is a way of actually
    273         // defining what font to use for those types.
    274         switch (familyValue->getValueID()) {
    275         case CSSValueSerif:
    276             family =  FontFamilyNames::webkit_serif;
    277             break;
    278         case CSSValueSansSerif:
    279             family =  FontFamilyNames::webkit_sans_serif;
    280             break;
    281         case CSSValueCursive:
    282             family =  FontFamilyNames::webkit_cursive;
    283             break;
    284         case CSSValueFantasy:
    285             family =  FontFamilyNames::webkit_fantasy;
    286             break;
    287         case CSSValueMonospace:
    288             family =  FontFamilyNames::webkit_monospace;
    289             break;
    290         case CSSValueWebkitPictograph:
    291             family =  FontFamilyNames::webkit_pictograph;
    292             break;
    293         default:
    294             return false;
    295         }
    296     }
    297     m_family = family;
    298     return true;
    299 }
    300 
    301 String FontFace::status() const
    302 {
    303     switch (m_status) {
    304     case Unloaded:
    305         return "unloaded";
    306     case Loading:
    307         return "loading";
    308     case Loaded:
    309         return "loaded";
    310     case Error:
    311         return "error";
    312     default:
    313         ASSERT_NOT_REACHED();
    314     }
    315     return emptyString();
    316 }
    317 
    318 void FontFace::setLoadStatus(LoadStatus status)
    319 {
    320     m_status = status;
    321     ASSERT(m_status != Error || m_error);
    322 
    323     if (m_status == Loaded || m_status == Error) {
    324         if (m_loadedProperty) {
    325             if (m_status == Loaded)
    326                 m_loadedProperty->resolve(this);
    327             else
    328                 m_loadedProperty->reject(m_error.get());
    329         }
    330 
    331         WillBeHeapVector<RefPtrWillBeMember<LoadFontCallback> > callbacks;
    332         m_callbacks.swap(callbacks);
    333         for (size_t i = 0; i < callbacks.size(); ++i) {
    334             if (m_status == Loaded)
    335                 callbacks[i]->notifyLoaded(this);
    336             else
    337                 callbacks[i]->notifyError(this);
    338         }
    339     }
    340 }
    341 
    342 void FontFace::setError(PassRefPtrWillBeRawPtr<DOMException> error)
    343 {
    344     if (!m_error)
    345         m_error = error ? error : DOMException::create(NetworkError);
    346     setLoadStatus(Error);
    347 }
    348 
    349 ScriptPromise FontFace::fontStatusPromise(ScriptState* scriptState)
    350 {
    351     if (!m_loadedProperty) {
    352         m_loadedProperty = new LoadedProperty(scriptState->executionContext(), this, LoadedProperty::Loaded);
    353         if (m_status == Loaded)
    354             m_loadedProperty->resolve(this);
    355         else if (m_status == Error)
    356             m_loadedProperty->reject(m_error.get());
    357     }
    358     return m_loadedProperty->promise(scriptState->world());
    359 }
    360 
    361 ScriptPromise FontFace::load(ScriptState* scriptState)
    362 {
    363     loadInternal(scriptState->executionContext());
    364     return fontStatusPromise(scriptState);
    365 }
    366 
    367 void FontFace::loadWithCallback(PassRefPtrWillBeRawPtr<LoadFontCallback> callback, ExecutionContext* context)
    368 {
    369     loadInternal(context);
    370     if (m_status == Loaded)
    371         callback->notifyLoaded(this);
    372     else if (m_status == Error)
    373         callback->notifyError(this);
    374     else
    375         m_callbacks.append(callback);
    376 }
    377 
    378 void FontFace::loadInternal(ExecutionContext* context)
    379 {
    380     if (m_status != Unloaded)
    381         return;
    382 
    383     m_cssFontFace->load();
    384     toDocument(context)->styleEngine()->fontSelector()->fontLoader()->loadPendingFonts();
    385 }
    386 
    387 FontTraits FontFace::traits() const
    388 {
    389     FontStyle style = FontStyleNormal;
    390     if (m_style) {
    391         if (!m_style->isPrimitiveValue())
    392             return 0;
    393 
    394         switch (toCSSPrimitiveValue(m_style.get())->getValueID()) {
    395         case CSSValueNormal:
    396             style = FontStyleNormal;
    397             break;
    398         case CSSValueItalic:
    399         case CSSValueOblique:
    400             style = FontStyleItalic;
    401             break;
    402         default:
    403             break;
    404         }
    405     }
    406 
    407     FontWeight weight = FontWeight400;
    408     if (m_weight) {
    409         if (!m_weight->isPrimitiveValue())
    410             return 0;
    411 
    412         switch (toCSSPrimitiveValue(m_weight.get())->getValueID()) {
    413         case CSSValueBold:
    414         case CSSValue700:
    415             weight = FontWeight700;
    416             break;
    417         case CSSValueNormal:
    418         case CSSValue400:
    419             weight = FontWeight400;
    420             break;
    421         case CSSValue900:
    422             weight = FontWeight900;
    423             break;
    424         case CSSValue800:
    425             weight = FontWeight800;
    426             break;
    427         case CSSValue600:
    428             weight = FontWeight600;
    429             break;
    430         case CSSValue500:
    431             weight = FontWeight500;
    432             break;
    433         case CSSValue300:
    434             weight = FontWeight300;
    435             break;
    436         case CSSValue200:
    437             weight = FontWeight200;
    438             break;
    439         case CSSValue100:
    440             weight = FontWeight100;
    441             break;
    442         // Although 'lighter' and 'bolder' are valid keywords for font-weights, they are invalid
    443         // inside font-face rules so they are ignored. Reference: http://www.w3.org/TR/css3-fonts/#descdef-font-weight.
    444         case CSSValueLighter:
    445         case CSSValueBolder:
    446             break;
    447         default:
    448             ASSERT_NOT_REACHED();
    449             break;
    450         }
    451     }
    452 
    453     FontVariant variant = FontVariantNormal;
    454     if (RefPtrWillBeRawPtr<CSSValue> fontVariant = m_variant) {
    455         // font-variant descriptor can be a value list.
    456         if (fontVariant->isPrimitiveValue()) {
    457             RefPtrWillBeRawPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
    458             list->append(fontVariant);
    459             fontVariant = list;
    460         } else if (!fontVariant->isValueList()) {
    461             return 0;
    462         }
    463 
    464         CSSValueList* variantList = toCSSValueList(fontVariant.get());
    465         unsigned numVariants = variantList->length();
    466         if (!numVariants)
    467             return 0;
    468 
    469         for (unsigned i = 0; i < numVariants; ++i) {
    470             switch (toCSSPrimitiveValue(variantList->item(i))->getValueID()) {
    471             case CSSValueNormal:
    472                 variant = FontVariantNormal;
    473                 break;
    474             case CSSValueSmallCaps:
    475                 variant = FontVariantSmallCaps;
    476                 break;
    477             default:
    478                 break;
    479             }
    480         }
    481     }
    482 
    483     return FontTraits(style, variant, weight, FontStretchNormal);
    484 }
    485 
    486 static PassOwnPtrWillBeRawPtr<CSSFontFace> createCSSFontFace(FontFace* fontFace, CSSValue* unicodeRange)
    487 {
    488     Vector<CSSFontFace::UnicodeRange> ranges;
    489     if (CSSValueList* rangeList = toCSSValueList(unicodeRange)) {
    490         unsigned numRanges = rangeList->length();
    491         for (unsigned i = 0; i < numRanges; i++) {
    492             CSSUnicodeRangeValue* range = toCSSUnicodeRangeValue(rangeList->item(i));
    493             ranges.append(CSSFontFace::UnicodeRange(range->from(), range->to()));
    494         }
    495     }
    496 
    497     return adoptPtrWillBeNoop(new CSSFontFace(fontFace, ranges));
    498 }
    499 
    500 void FontFace::initCSSFontFace(Document* document, PassRefPtrWillBeRawPtr<CSSValue> src)
    501 {
    502     m_cssFontFace = createCSSFontFace(this, m_unicodeRange.get());
    503     if (m_error)
    504         return;
    505 
    506     // Each item in the src property's list is a single CSSFontFaceSource. Put them all into a CSSFontFace.
    507     ASSERT(src);
    508     ASSERT(src->isValueList());
    509     CSSValueList* srcList = toCSSValueList(src.get());
    510     int srcLength = srcList->length();
    511 
    512     bool foundSVGFont = false;
    513 
    514     for (int i = 0; i < srcLength; i++) {
    515         // An item in the list either specifies a string (local font name) or a URL (remote font to download).
    516         CSSFontFaceSrcValue* item = toCSSFontFaceSrcValue(srcList->item(i));
    517         OwnPtrWillBeRawPtr<CSSFontFaceSource> source = nullptr;
    518 
    519 #if ENABLE(SVG_FONTS)
    520         foundSVGFont = item->isSVGFontFaceSrc() || item->svgFontFaceElement();
    521 #endif
    522         if (!item->isLocal()) {
    523             Settings* settings = document ? document->frame() ? document->frame()->settings() : 0 : 0;
    524             bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled());
    525             if (allowDownloading && item->isSupportedFormat() && document) {
    526                 FontResource* fetched = item->fetch(document);
    527                 if (fetched) {
    528                     FontLoader* fontLoader = document->styleEngine()->fontSelector()->fontLoader();
    529 
    530 #if ENABLE(SVG_FONTS)
    531                     if (foundSVGFont) {
    532                         source = adoptPtrWillBeNoop(new SVGRemoteFontFaceSource(item->resource(), fetched, fontLoader));
    533                     } else
    534 #endif
    535                     {
    536                         source = adoptPtrWillBeNoop(new RemoteFontFaceSource(fetched, fontLoader));
    537                     }
    538                 }
    539             }
    540         } else {
    541 #if ENABLE(SVG_FONTS)
    542             if (item->svgFontFaceElement()) {
    543                 RefPtrWillBeRawPtr<SVGFontFaceElement> fontfaceElement = item->svgFontFaceElement();
    544                 // SVGFontFaceSource assumes that it is the case where <font-face> element resides in the same document.
    545                 // We put a RELEASE_ASSERT here as it will cause UAF if the assumption is false.
    546                 RELEASE_ASSERT(fontfaceElement->inDocument());
    547                 RELEASE_ASSERT(fontfaceElement->document() == document);
    548                 source = adoptPtrWillBeNoop(new SVGFontFaceSource(fontfaceElement.get()));
    549             } else
    550 #endif
    551             {
    552                 source = adoptPtrWillBeNoop(new LocalFontFaceSource(item->resource()));
    553             }
    554         }
    555 
    556         if (source)
    557             m_cssFontFace->addSource(source.release());
    558     }
    559 }
    560 
    561 void FontFace::initCSSFontFace(const unsigned char* data, unsigned size)
    562 {
    563     m_cssFontFace = createCSSFontFace(this, m_unicodeRange.get());
    564     if (m_error)
    565         return;
    566 
    567     RefPtr<SharedBuffer> buffer = SharedBuffer::create(data, size);
    568     OwnPtrWillBeRawPtr<BinaryDataFontFaceSource> source = adoptPtrWillBeNoop(new BinaryDataFontFaceSource(buffer.get()));
    569     if (source->isValid())
    570         setLoadStatus(Loaded);
    571     else
    572         setError(DOMException::create(SyntaxError, "Invalid font data in ArrayBuffer."));
    573     m_cssFontFace->addSource(source.release());
    574 }
    575 
    576 void FontFace::trace(Visitor* visitor)
    577 {
    578     visitor->trace(m_src);
    579     visitor->trace(m_style);
    580     visitor->trace(m_weight);
    581     visitor->trace(m_stretch);
    582     visitor->trace(m_unicodeRange);
    583     visitor->trace(m_variant);
    584     visitor->trace(m_featureSettings);
    585     visitor->trace(m_error);
    586     visitor->trace(m_loadedProperty);
    587     visitor->trace(m_cssFontFace);
    588     visitor->trace(m_callbacks);
    589 }
    590 
    591 bool FontFace::hadBlankText() const
    592 {
    593     return m_cssFontFace->hadBlankText();
    594 }
    595 
    596 bool FontFace::hasPendingActivity() const
    597 {
    598     return m_status == Loading && executionContext() && !executionContext()->activeDOMObjectsAreStopped();
    599 }
    600 
    601 } // namespace blink
    602