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