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 met:
      6  *
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
     14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     16  * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
     17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     20  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
     23  * DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "core/css/FontFaceSet.h"
     28 
     29 #include "bindings/core/v8/Dictionary.h"
     30 #include "bindings/core/v8/ScriptPromiseResolver.h"
     31 #include "bindings/core/v8/ScriptState.h"
     32 #include "core/css/CSSFontSelector.h"
     33 #include "core/css/CSSSegmentedFontFace.h"
     34 #include "core/css/FontFaceCache.h"
     35 #include "core/css/FontFaceSetLoadEvent.h"
     36 #include "core/css/StylePropertySet.h"
     37 #include "core/css/parser/CSSParser.h"
     38 #include "core/css/resolver/StyleResolver.h"
     39 #include "core/dom/Document.h"
     40 #include "core/dom/StyleEngine.h"
     41 #include "core/frame/FrameView.h"
     42 #include "core/frame/LocalFrame.h"
     43 #include "core/rendering/style/StyleInheritedData.h"
     44 #include "public/platform/Platform.h"
     45 
     46 namespace blink {
     47 
     48 static const int defaultFontSize = 10;
     49 static const char defaultFontFamily[] = "sans-serif";
     50 
     51 class LoadFontPromiseResolver FINAL : public FontFace::LoadFontCallback {
     52 public:
     53     static PassRefPtrWillBeRawPtr<LoadFontPromiseResolver> create(FontFaceArray faces, ScriptState* scriptState)
     54     {
     55         return adoptRefWillBeNoop(new LoadFontPromiseResolver(faces, scriptState));
     56     }
     57 
     58     void loadFonts(ExecutionContext*);
     59     ScriptPromise promise() { return m_resolver->promise(); }
     60 
     61     virtual void notifyLoaded(FontFace*) OVERRIDE;
     62     virtual void notifyError(FontFace*) OVERRIDE;
     63 
     64     virtual void trace(Visitor*) OVERRIDE;
     65 
     66 private:
     67     LoadFontPromiseResolver(FontFaceArray faces, ScriptState* scriptState)
     68         : m_numLoading(faces.size())
     69         , m_errorOccured(false)
     70         , m_resolver(ScriptPromiseResolver::create(scriptState))
     71     {
     72         m_fontFaces.swap(faces);
     73     }
     74 
     75     WillBeHeapVector<RefPtrWillBeMember<FontFace> > m_fontFaces;
     76     int m_numLoading;
     77     bool m_errorOccured;
     78     RefPtr<ScriptPromiseResolver> m_resolver;
     79 };
     80 
     81 void LoadFontPromiseResolver::loadFonts(ExecutionContext* context)
     82 {
     83     if (!m_numLoading) {
     84         m_resolver->resolve(m_fontFaces);
     85         return;
     86     }
     87 
     88     for (size_t i = 0; i < m_fontFaces.size(); i++)
     89         m_fontFaces[i]->loadWithCallback(this, context);
     90 }
     91 
     92 void LoadFontPromiseResolver::notifyLoaded(FontFace* fontFace)
     93 {
     94     m_numLoading--;
     95     if (m_numLoading || m_errorOccured)
     96         return;
     97 
     98     m_resolver->resolve(m_fontFaces);
     99 }
    100 
    101 void LoadFontPromiseResolver::notifyError(FontFace* fontFace)
    102 {
    103     m_numLoading--;
    104     if (!m_errorOccured) {
    105         m_errorOccured = true;
    106         m_resolver->reject(fontFace->error());
    107     }
    108 }
    109 
    110 void LoadFontPromiseResolver::trace(Visitor* visitor)
    111 {
    112     visitor->trace(m_fontFaces);
    113     LoadFontCallback::trace(visitor);
    114 }
    115 
    116 class FontsReadyPromiseResolver {
    117 public:
    118     static PassOwnPtr<FontsReadyPromiseResolver> create(ScriptState* scriptState)
    119     {
    120         return adoptPtr(new FontsReadyPromiseResolver(scriptState));
    121     }
    122 
    123     void resolve(PassRefPtrWillBeRawPtr<FontFaceSet> fontFaceSet)
    124     {
    125         m_resolver->resolve(fontFaceSet);
    126     }
    127 
    128     ScriptPromise promise() { return m_resolver->promise(); }
    129 
    130 private:
    131     explicit FontsReadyPromiseResolver(ScriptState* scriptState)
    132         : m_resolver(ScriptPromiseResolver::create(scriptState))
    133     {
    134     }
    135 
    136     RefPtr<ScriptPromiseResolver> m_resolver;
    137 };
    138 
    139 FontFaceSet::FontFaceSet(Document& document)
    140     : ActiveDOMObject(&document)
    141     , m_shouldFireLoadingEvent(false)
    142     , m_asyncRunner(this, &FontFaceSet::handlePendingEventsAndPromises)
    143 {
    144     suspendIfNeeded();
    145 }
    146 
    147 FontFaceSet::~FontFaceSet()
    148 {
    149 }
    150 
    151 Document* FontFaceSet::document() const
    152 {
    153     return toDocument(executionContext());
    154 }
    155 
    156 bool FontFaceSet::inActiveDocumentContext() const
    157 {
    158     ExecutionContext* context = executionContext();
    159     return context && toDocument(context)->isActive();
    160 }
    161 
    162 void FontFaceSet::addFontFacesToFontFaceCache(FontFaceCache* fontFaceCache, CSSFontSelector* fontSelector)
    163 {
    164     for (WillBeHeapListHashSet<RefPtrWillBeMember<FontFace> >::iterator it = m_nonCSSConnectedFaces.begin(); it != m_nonCSSConnectedFaces.end(); ++it)
    165         fontFaceCache->addFontFace(fontSelector, *it, false);
    166 }
    167 
    168 const AtomicString& FontFaceSet::interfaceName() const
    169 {
    170     return EventTargetNames::FontFaceSet;
    171 }
    172 
    173 ExecutionContext* FontFaceSet::executionContext() const
    174 {
    175     return ActiveDOMObject::executionContext();
    176 }
    177 
    178 AtomicString FontFaceSet::status() const
    179 {
    180     DEFINE_STATIC_LOCAL(AtomicString, loading, ("loading", AtomicString::ConstructFromLiteral));
    181     DEFINE_STATIC_LOCAL(AtomicString, loaded, ("loaded", AtomicString::ConstructFromLiteral));
    182     return (!m_loadingFonts.isEmpty() || hasLoadedFonts()) ? loading : loaded;
    183 }
    184 
    185 void FontFaceSet::handlePendingEventsAndPromisesSoon()
    186 {
    187     // m_asyncRunner will be automatically stopped on destruction.
    188     m_asyncRunner.runAsync();
    189 }
    190 
    191 void FontFaceSet::didLayout()
    192 {
    193     if (document()->frame()->isMainFrame() && m_loadingFonts.isEmpty())
    194         m_histogram.record();
    195     if (!m_loadingFonts.isEmpty() || (!hasLoadedFonts() && m_readyResolvers.isEmpty()))
    196         return;
    197     handlePendingEventsAndPromisesSoon();
    198 }
    199 
    200 void FontFaceSet::handlePendingEventsAndPromises()
    201 {
    202     fireLoadingEvent();
    203     fireDoneEventIfPossible();
    204 }
    205 
    206 void FontFaceSet::fireLoadingEvent()
    207 {
    208     if (m_shouldFireLoadingEvent) {
    209         m_shouldFireLoadingEvent = false;
    210         dispatchEvent(FontFaceSetLoadEvent::createForFontFaces(EventTypeNames::loading));
    211     }
    212 }
    213 
    214 void FontFaceSet::suspend()
    215 {
    216     m_asyncRunner.suspend();
    217 }
    218 
    219 void FontFaceSet::resume()
    220 {
    221     m_asyncRunner.resume();
    222 }
    223 
    224 void FontFaceSet::stop()
    225 {
    226     m_asyncRunner.stop();
    227 }
    228 
    229 void FontFaceSet::beginFontLoading(FontFace* fontFace)
    230 {
    231     m_histogram.incrementCount();
    232     addToLoadingFonts(fontFace);
    233 }
    234 
    235 void FontFaceSet::fontLoaded(FontFace* fontFace)
    236 {
    237     m_histogram.updateStatus(fontFace);
    238     m_loadedFonts.append(fontFace);
    239     removeFromLoadingFonts(fontFace);
    240 }
    241 
    242 void FontFaceSet::loadError(FontFace* fontFace)
    243 {
    244     m_histogram.updateStatus(fontFace);
    245     m_failedFonts.append(fontFace);
    246     removeFromLoadingFonts(fontFace);
    247 }
    248 
    249 void FontFaceSet::addToLoadingFonts(PassRefPtrWillBeRawPtr<FontFace> fontFace)
    250 {
    251     if (m_loadingFonts.isEmpty() && !hasLoadedFonts()) {
    252         m_shouldFireLoadingEvent = true;
    253         handlePendingEventsAndPromisesSoon();
    254     }
    255     m_loadingFonts.add(fontFace);
    256 }
    257 
    258 void FontFaceSet::removeFromLoadingFonts(PassRefPtrWillBeRawPtr<FontFace> fontFace)
    259 {
    260     m_loadingFonts.remove(fontFace);
    261     if (m_loadingFonts.isEmpty())
    262         handlePendingEventsAndPromisesSoon();
    263 }
    264 
    265 ScriptPromise FontFaceSet::ready(ScriptState* scriptState)
    266 {
    267     if (!inActiveDocumentContext())
    268         return ScriptPromise();
    269     OwnPtr<FontsReadyPromiseResolver> resolver = FontsReadyPromiseResolver::create(scriptState);
    270     ScriptPromise promise = resolver->promise();
    271     m_readyResolvers.append(resolver.release());
    272     handlePendingEventsAndPromisesSoon();
    273     return promise;
    274 }
    275 
    276 void FontFaceSet::add(FontFace* fontFace, ExceptionState& exceptionState)
    277 {
    278     if (!inActiveDocumentContext())
    279         return;
    280     if (!fontFace) {
    281         exceptionState.throwTypeError("The argument is not a FontFace.");
    282         return;
    283     }
    284     if (m_nonCSSConnectedFaces.contains(fontFace))
    285         return;
    286     if (isCSSConnectedFontFace(fontFace)) {
    287         exceptionState.throwDOMException(InvalidModificationError, "Cannot add a CSS-connected FontFace.");
    288         return;
    289     }
    290     CSSFontSelector* fontSelector = document()->styleEngine()->fontSelector();
    291     m_nonCSSConnectedFaces.add(fontFace);
    292     fontSelector->fontFaceCache()->addFontFace(fontSelector, fontFace, false);
    293     if (fontFace->loadStatus() == FontFace::Loading)
    294         addToLoadingFonts(fontFace);
    295     fontSelector->fontFaceInvalidated();
    296 }
    297 
    298 void FontFaceSet::clear()
    299 {
    300     if (!inActiveDocumentContext() || m_nonCSSConnectedFaces.isEmpty())
    301         return;
    302     CSSFontSelector* fontSelector = document()->styleEngine()->fontSelector();
    303     FontFaceCache* fontFaceCache = fontSelector->fontFaceCache();
    304     for (WillBeHeapListHashSet<RefPtrWillBeMember<FontFace> >::iterator it = m_nonCSSConnectedFaces.begin(); it != m_nonCSSConnectedFaces.end(); ++it) {
    305         fontFaceCache->removeFontFace(it->get(), false);
    306         if ((*it)->loadStatus() == FontFace::Loading)
    307             removeFromLoadingFonts(*it);
    308     }
    309     m_nonCSSConnectedFaces.clear();
    310     fontSelector->fontFaceInvalidated();
    311 }
    312 
    313 bool FontFaceSet::remove(FontFace* fontFace, ExceptionState& exceptionState)
    314 {
    315     if (!inActiveDocumentContext())
    316         return false;
    317     if (!fontFace) {
    318         exceptionState.throwTypeError("The argument is not a FontFace.");
    319         return false;
    320     }
    321     WillBeHeapListHashSet<RefPtrWillBeMember<FontFace> >::iterator it = m_nonCSSConnectedFaces.find(fontFace);
    322     if (it != m_nonCSSConnectedFaces.end()) {
    323         m_nonCSSConnectedFaces.remove(it);
    324         CSSFontSelector* fontSelector = document()->styleEngine()->fontSelector();
    325         fontSelector->fontFaceCache()->removeFontFace(fontFace, false);
    326         if (fontFace->loadStatus() == FontFace::Loading)
    327             removeFromLoadingFonts(fontFace);
    328         fontSelector->fontFaceInvalidated();
    329         return true;
    330     }
    331     if (isCSSConnectedFontFace(fontFace))
    332         exceptionState.throwDOMException(InvalidModificationError, "Cannot delete a CSS-connected FontFace.");
    333     return false;
    334 }
    335 
    336 bool FontFaceSet::has(FontFace* fontFace, ExceptionState& exceptionState) const
    337 {
    338     if (!inActiveDocumentContext())
    339         return false;
    340     if (!fontFace) {
    341         exceptionState.throwTypeError("The argument is not a FontFace.");
    342         return false;
    343     }
    344     return m_nonCSSConnectedFaces.contains(fontFace) || isCSSConnectedFontFace(fontFace);
    345 }
    346 
    347 const WillBeHeapListHashSet<RefPtrWillBeMember<FontFace> >& FontFaceSet::cssConnectedFontFaceList() const
    348 {
    349     Document* d = document();
    350     d->ensureStyleResolver(); // Flush pending style changes.
    351     return d->styleEngine()->fontSelector()->fontFaceCache()->cssConnectedFontFaces();
    352 }
    353 
    354 bool FontFaceSet::isCSSConnectedFontFace(FontFace* fontFace) const
    355 {
    356     return cssConnectedFontFaceList().contains(fontFace);
    357 }
    358 
    359 void FontFaceSet::forEach(FontFaceSetForEachCallback* callback, const ScriptValue& thisArg) const
    360 {
    361     forEachInternal(callback, &thisArg);
    362 }
    363 
    364 void FontFaceSet::forEach(FontFaceSetForEachCallback* callback) const
    365 {
    366     forEachInternal(callback, 0);
    367 }
    368 
    369 void FontFaceSet::forEachInternal(FontFaceSetForEachCallback* callback, const ScriptValue* thisArg) const
    370 {
    371     if (!inActiveDocumentContext())
    372         return;
    373     const WillBeHeapListHashSet<RefPtrWillBeMember<FontFace> >& cssConnectedFaces = cssConnectedFontFaceList();
    374     WillBeHeapVector<RefPtrWillBeMember<FontFace> > fontFaces;
    375     fontFaces.reserveInitialCapacity(cssConnectedFaces.size() + m_nonCSSConnectedFaces.size());
    376     for (WillBeHeapListHashSet<RefPtrWillBeMember<FontFace> >::const_iterator it = cssConnectedFaces.begin(); it != cssConnectedFaces.end(); ++it)
    377         fontFaces.append(*it);
    378     for (WillBeHeapListHashSet<RefPtrWillBeMember<FontFace> >::const_iterator it = m_nonCSSConnectedFaces.begin(); it != m_nonCSSConnectedFaces.end(); ++it)
    379         fontFaces.append(*it);
    380 
    381     for (size_t i = 0; i < fontFaces.size(); ++i) {
    382         FontFace* face = fontFaces[i].get();
    383         if (thisArg)
    384             callback->handleItem(*thisArg, face, face, const_cast<FontFaceSet*>(this));
    385         else
    386             callback->handleItem(face, face, const_cast<FontFaceSet*>(this));
    387     }
    388 }
    389 
    390 unsigned long FontFaceSet::size() const
    391 {
    392     if (!inActiveDocumentContext())
    393         return m_nonCSSConnectedFaces.size();
    394     return cssConnectedFontFaceList().size() + m_nonCSSConnectedFaces.size();
    395 }
    396 
    397 void FontFaceSet::fireDoneEventIfPossible()
    398 {
    399     if (m_shouldFireLoadingEvent)
    400         return;
    401     if (!m_loadingFonts.isEmpty() || (!hasLoadedFonts() && m_readyResolvers.isEmpty()))
    402         return;
    403 
    404     // If the layout was invalidated in between when we thought layout
    405     // was updated and when we're ready to fire the event, just wait
    406     // until after the next layout before firing events.
    407     Document* d = document();
    408     if (!d->view() || d->view()->needsLayout())
    409         return;
    410 
    411     if (hasLoadedFonts()) {
    412         RefPtrWillBeRawPtr<FontFaceSetLoadEvent> doneEvent = nullptr;
    413         RefPtrWillBeRawPtr<FontFaceSetLoadEvent> errorEvent = nullptr;
    414         doneEvent = FontFaceSetLoadEvent::createForFontFaces(EventTypeNames::loadingdone, m_loadedFonts);
    415         m_loadedFonts.clear();
    416         if (!m_failedFonts.isEmpty()) {
    417             errorEvent = FontFaceSetLoadEvent::createForFontFaces(EventTypeNames::loadingerror, m_failedFonts);
    418             m_failedFonts.clear();
    419         }
    420         dispatchEvent(doneEvent);
    421         if (errorEvent)
    422             dispatchEvent(errorEvent);
    423     }
    424 
    425     if (!m_readyResolvers.isEmpty()) {
    426         Vector<OwnPtr<FontsReadyPromiseResolver> > resolvers;
    427         m_readyResolvers.swap(resolvers);
    428         for (size_t index = 0; index < resolvers.size(); ++index)
    429             resolvers[index]->resolve(this);
    430     }
    431 }
    432 
    433 ScriptPromise FontFaceSet::load(ScriptState* scriptState, const String& fontString, const String& text)
    434 {
    435     if (!inActiveDocumentContext())
    436         return ScriptPromise();
    437 
    438     Font font;
    439     if (!resolveFontStyle(fontString, font)) {
    440         RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState);
    441         ScriptPromise promise = resolver->promise();
    442         resolver->reject(DOMException::create(SyntaxError, "Could not resolve '" + fontString + "' as a font."));
    443         return promise;
    444     }
    445 
    446     FontFaceCache* fontFaceCache = document()->styleEngine()->fontSelector()->fontFaceCache();
    447     FontFaceArray faces;
    448     for (const FontFamily* f = &font.fontDescription().family(); f; f = f->next()) {
    449         CSSSegmentedFontFace* segmentedFontFace = fontFaceCache->get(font.fontDescription(), f->family());
    450         if (segmentedFontFace)
    451             segmentedFontFace->match(text, faces);
    452     }
    453 
    454     RefPtrWillBeRawPtr<LoadFontPromiseResolver> resolver = LoadFontPromiseResolver::create(faces, scriptState);
    455     ScriptPromise promise = resolver->promise();
    456     resolver->loadFonts(executionContext()); // After this, resolver->promise() may return null.
    457     return promise;
    458 }
    459 
    460 bool FontFaceSet::check(const String& fontString, const String& text, ExceptionState& exceptionState)
    461 {
    462     if (!inActiveDocumentContext())
    463         return false;
    464 
    465     Font font;
    466     if (!resolveFontStyle(fontString, font)) {
    467         exceptionState.throwDOMException(SyntaxError, "Could not resolve '" + fontString + "' as a font.");
    468         return false;
    469     }
    470 
    471     CSSFontSelector* fontSelector = document()->styleEngine()->fontSelector();
    472     FontFaceCache* fontFaceCache = fontSelector->fontFaceCache();
    473 
    474     bool hasLoadedFaces = false;
    475     for (const FontFamily* f = &font.fontDescription().family(); f; f = f->next()) {
    476         CSSSegmentedFontFace* face = fontFaceCache->get(font.fontDescription(), f->family());
    477         if (face) {
    478             if (!face->checkFont(text))
    479                 return false;
    480             hasLoadedFaces = true;
    481         }
    482     }
    483     if (hasLoadedFaces)
    484         return true;
    485     for (const FontFamily* f = &font.fontDescription().family(); f; f = f->next()) {
    486         if (fontSelector->isPlatformFontAvailable(font.fontDescription(), f->family()))
    487             return true;
    488     }
    489     return false;
    490 }
    491 
    492 bool FontFaceSet::resolveFontStyle(const String& fontString, Font& font)
    493 {
    494     if (fontString.isEmpty())
    495         return false;
    496 
    497     // Interpret fontString in the same way as the 'font' attribute of CanvasRenderingContext2D.
    498     RefPtrWillBeRawPtr<MutableStylePropertySet> parsedStyle = MutableStylePropertySet::create();
    499     CSSParser::parseValue(parsedStyle.get(), CSSPropertyFont, fontString, true, HTMLStandardMode, 0);
    500     if (parsedStyle->isEmpty())
    501         return false;
    502 
    503     String fontValue = parsedStyle->getPropertyValue(CSSPropertyFont);
    504     if (fontValue == "inherit" || fontValue == "initial")
    505         return false;
    506 
    507     RefPtr<RenderStyle> style = RenderStyle::create();
    508 
    509     FontFamily fontFamily;
    510     fontFamily.setFamily(defaultFontFamily);
    511 
    512     FontDescription defaultFontDescription;
    513     defaultFontDescription.setFamily(fontFamily);
    514     defaultFontDescription.setSpecifiedSize(defaultFontSize);
    515     defaultFontDescription.setComputedSize(defaultFontSize);
    516 
    517     style->setFontDescription(defaultFontDescription);
    518 
    519     style->font().update(style->font().fontSelector());
    520 
    521     // Now map the font property longhands into the style.
    522     CSSPropertyValue properties[] = {
    523         CSSPropertyValue(CSSPropertyFontFamily, *parsedStyle),
    524         CSSPropertyValue(CSSPropertyFontStretch, *parsedStyle),
    525         CSSPropertyValue(CSSPropertyFontStyle, *parsedStyle),
    526         CSSPropertyValue(CSSPropertyFontVariant, *parsedStyle),
    527         CSSPropertyValue(CSSPropertyFontWeight, *parsedStyle),
    528         CSSPropertyValue(CSSPropertyFontSize, *parsedStyle),
    529         CSSPropertyValue(CSSPropertyLineHeight, *parsedStyle),
    530     };
    531     StyleResolver& styleResolver = document()->ensureStyleResolver();
    532     styleResolver.applyPropertiesToStyle(properties, WTF_ARRAY_LENGTH(properties), style.get());
    533 
    534     font = style->font();
    535     font.update(document()->styleEngine()->fontSelector());
    536     return true;
    537 }
    538 
    539 void FontFaceSet::FontLoadHistogram::updateStatus(FontFace* fontFace)
    540 {
    541     if (m_status == Reported)
    542         return;
    543     if (fontFace->hadBlankText())
    544         m_status = HadBlankText;
    545     else if (m_status == NoWebFonts)
    546         m_status = DidNotHaveBlankText;
    547 }
    548 
    549 void FontFaceSet::FontLoadHistogram::record()
    550 {
    551     if (!m_recorded) {
    552         m_recorded = true;
    553         blink::Platform::current()->histogramCustomCounts("WebFont.WebFontsInPage", m_count, 1, 100, 50);
    554     }
    555     if (m_status == HadBlankText || m_status == DidNotHaveBlankText) {
    556         blink::Platform::current()->histogramEnumeration("WebFont.HadBlankText", m_status == HadBlankText ? 1 : 0, 2);
    557         m_status = Reported;
    558     }
    559 }
    560 
    561 static const char* supplementName()
    562 {
    563     return "FontFaceSet";
    564 }
    565 
    566 PassRefPtrWillBeRawPtr<FontFaceSet> FontFaceSet::from(Document& document)
    567 {
    568     RefPtrWillBeRawPtr<FontFaceSet> fonts = static_cast<FontFaceSet*>(SupplementType::from(document, supplementName()));
    569     if (!fonts) {
    570         fonts = FontFaceSet::create(document);
    571         SupplementType::provideTo(document, supplementName(), fonts);
    572     }
    573 
    574     return fonts.release();
    575 }
    576 
    577 void FontFaceSet::didLayout(Document& document)
    578 {
    579     if (FontFaceSet* fonts = static_cast<FontFaceSet*>(SupplementType::from(document, supplementName())))
    580         fonts->didLayout();
    581 }
    582 
    583 #if ENABLE(OILPAN)
    584 void FontFaceSet::trace(Visitor* visitor)
    585 {
    586     visitor->trace(m_loadingFonts);
    587     visitor->trace(m_loadedFonts);
    588     visitor->trace(m_failedFonts);
    589     visitor->trace(m_nonCSSConnectedFaces);
    590     DocumentSupplement::trace(visitor);
    591     EventTargetWithInlineData::trace(visitor);
    592 }
    593 #endif
    594 
    595 } // namespace blink
    596