• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
create(FontFaceArray faces,ScriptState * scriptState)53     static PassRefPtrWillBeRawPtr<LoadFontPromiseResolver> create(FontFaceArray faces, ScriptState* scriptState)
54     {
55         return adoptRefWillBeNoop(new LoadFontPromiseResolver(faces, scriptState));
56     }
57 
58     void loadFonts(ExecutionContext*);
promise()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:
LoadFontPromiseResolver(FontFaceArray faces,ScriptState * scriptState)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 
loadFonts(ExecutionContext * context)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 
notifyLoaded(FontFace * fontFace)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 
notifyError(FontFace * fontFace)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 
trace(Visitor * visitor)110 void LoadFontPromiseResolver::trace(Visitor* visitor)
111 {
112     visitor->trace(m_fontFaces);
113     LoadFontCallback::trace(visitor);
114 }
115 
116 class FontsReadyPromiseResolver {
117 public:
create(ScriptState * scriptState)118     static PassOwnPtr<FontsReadyPromiseResolver> create(ScriptState* scriptState)
119     {
120         return adoptPtr(new FontsReadyPromiseResolver(scriptState));
121     }
122 
resolve(PassRefPtrWillBeRawPtr<FontFaceSet> fontFaceSet)123     void resolve(PassRefPtrWillBeRawPtr<FontFaceSet> fontFaceSet)
124     {
125         m_resolver->resolve(fontFaceSet);
126     }
127 
promise()128     ScriptPromise promise() { return m_resolver->promise(); }
129 
130 private:
FontsReadyPromiseResolver(ScriptState * scriptState)131     explicit FontsReadyPromiseResolver(ScriptState* scriptState)
132         : m_resolver(ScriptPromiseResolver::create(scriptState))
133     {
134     }
135 
136     RefPtr<ScriptPromiseResolver> m_resolver;
137 };
138 
FontFaceSet(Document & document)139 FontFaceSet::FontFaceSet(Document& document)
140     : ActiveDOMObject(&document)
141     , m_shouldFireLoadingEvent(false)
142     , m_asyncRunner(this, &FontFaceSet::handlePendingEventsAndPromises)
143 {
144     suspendIfNeeded();
145 }
146 
~FontFaceSet()147 FontFaceSet::~FontFaceSet()
148 {
149 }
150 
document() const151 Document* FontFaceSet::document() const
152 {
153     return toDocument(executionContext());
154 }
155 
inActiveDocumentContext() const156 bool FontFaceSet::inActiveDocumentContext() const
157 {
158     ExecutionContext* context = executionContext();
159     return context && toDocument(context)->isActive();
160 }
161 
addFontFacesToFontFaceCache(FontFaceCache * fontFaceCache,CSSFontSelector * fontSelector)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 
interfaceName() const168 const AtomicString& FontFaceSet::interfaceName() const
169 {
170     return EventTargetNames::FontFaceSet;
171 }
172 
executionContext() const173 ExecutionContext* FontFaceSet::executionContext() const
174 {
175     return ActiveDOMObject::executionContext();
176 }
177 
status() const178 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 
handlePendingEventsAndPromisesSoon()185 void FontFaceSet::handlePendingEventsAndPromisesSoon()
186 {
187     // m_asyncRunner will be automatically stopped on destruction.
188     m_asyncRunner.runAsync();
189 }
190 
didLayout()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 
handlePendingEventsAndPromises()200 void FontFaceSet::handlePendingEventsAndPromises()
201 {
202     fireLoadingEvent();
203     fireDoneEventIfPossible();
204 }
205 
fireLoadingEvent()206 void FontFaceSet::fireLoadingEvent()
207 {
208     if (m_shouldFireLoadingEvent) {
209         m_shouldFireLoadingEvent = false;
210         dispatchEvent(FontFaceSetLoadEvent::createForFontFaces(EventTypeNames::loading));
211     }
212 }
213 
suspend()214 void FontFaceSet::suspend()
215 {
216     m_asyncRunner.suspend();
217 }
218 
resume()219 void FontFaceSet::resume()
220 {
221     m_asyncRunner.resume();
222 }
223 
stop()224 void FontFaceSet::stop()
225 {
226     m_asyncRunner.stop();
227 }
228 
beginFontLoading(FontFace * fontFace)229 void FontFaceSet::beginFontLoading(FontFace* fontFace)
230 {
231     m_histogram.incrementCount();
232     addToLoadingFonts(fontFace);
233 }
234 
fontLoaded(FontFace * fontFace)235 void FontFaceSet::fontLoaded(FontFace* fontFace)
236 {
237     m_histogram.updateStatus(fontFace);
238     m_loadedFonts.append(fontFace);
239     removeFromLoadingFonts(fontFace);
240 }
241 
loadError(FontFace * fontFace)242 void FontFaceSet::loadError(FontFace* fontFace)
243 {
244     m_histogram.updateStatus(fontFace);
245     m_failedFonts.append(fontFace);
246     removeFromLoadingFonts(fontFace);
247 }
248 
addToLoadingFonts(PassRefPtrWillBeRawPtr<FontFace> fontFace)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 
removeFromLoadingFonts(PassRefPtrWillBeRawPtr<FontFace> fontFace)258 void FontFaceSet::removeFromLoadingFonts(PassRefPtrWillBeRawPtr<FontFace> fontFace)
259 {
260     m_loadingFonts.remove(fontFace);
261     if (m_loadingFonts.isEmpty())
262         handlePendingEventsAndPromisesSoon();
263 }
264 
ready(ScriptState * scriptState)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 
add(FontFace * fontFace,ExceptionState & exceptionState)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 
clear()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 
remove(FontFace * fontFace,ExceptionState & exceptionState)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 
has(FontFace * fontFace,ExceptionState & exceptionState) const336 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 
cssConnectedFontFaceList() const347 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 
isCSSConnectedFontFace(FontFace * fontFace) const354 bool FontFaceSet::isCSSConnectedFontFace(FontFace* fontFace) const
355 {
356     return cssConnectedFontFaceList().contains(fontFace);
357 }
358 
forEach(FontFaceSetForEachCallback * callback,const ScriptValue & thisArg) const359 void FontFaceSet::forEach(FontFaceSetForEachCallback* callback, const ScriptValue& thisArg) const
360 {
361     forEachInternal(callback, &thisArg);
362 }
363 
forEach(FontFaceSetForEachCallback * callback) const364 void FontFaceSet::forEach(FontFaceSetForEachCallback* callback) const
365 {
366     forEachInternal(callback, 0);
367 }
368 
forEachInternal(FontFaceSetForEachCallback * callback,const ScriptValue * thisArg) const369 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 
size() const390 unsigned long FontFaceSet::size() const
391 {
392     if (!inActiveDocumentContext())
393         return m_nonCSSConnectedFaces.size();
394     return cssConnectedFontFaceList().size() + m_nonCSSConnectedFaces.size();
395 }
396 
fireDoneEventIfPossible()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 
load(ScriptState * scriptState,const String & fontString,const String & text)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 
check(const String & fontString,const String & text,ExceptionState & exceptionState)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 
resolveFontStyle(const String & fontString,Font & font)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 
updateStatus(FontFace * fontFace)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 
record()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 
supplementName()561 static const char* supplementName()
562 {
563     return "FontFaceSet";
564 }
565 
from(Document & document)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 
didLayout(Document & document)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)
trace(Visitor * visitor)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