Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / css / FontFaceSet.cpp
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/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 blink {
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(ScriptPromiseResolver::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<ScriptPromiseResolver> 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(ScriptPromiseResolver::create(scriptState))
134     {
135     }
136
137     RefPtr<ScriptPromiseResolver> m_resolver;
138 };
139
140 FontFaceSet::FontFaceSet(Document& document)
141     : ActiveDOMObject(&document)
142     , m_shouldFireLoadingEvent(false)
143     , m_asyncRunner(this, &FontFaceSet::handlePendingEventsAndPromises)
144 {
145     ScriptWrappable::init(this);
146     suspendIfNeeded();
147 }
148
149 FontFaceSet::~FontFaceSet()
150 {
151 }
152
153 Document* FontFaceSet::document() const
154 {
155     return toDocument(executionContext());
156 }
157
158 bool FontFaceSet::inActiveDocumentContext() const
159 {
160     ExecutionContext* context = executionContext();
161     return context && toDocument(context)->isActive();
162 }
163
164 void FontFaceSet::addFontFacesToFontFaceCache(FontFaceCache* fontFaceCache, CSSFontSelector* fontSelector)
165 {
166     for (WillBeHeapListHashSet<RefPtrWillBeMember<FontFace> >::iterator it = m_nonCSSConnectedFaces.begin(); it != m_nonCSSConnectedFaces.end(); ++it)
167         fontFaceCache->addFontFace(fontSelector, *it, false);
168 }
169
170 const AtomicString& FontFaceSet::interfaceName() const
171 {
172     return EventTargetNames::FontFaceSet;
173 }
174
175 ExecutionContext* FontFaceSet::executionContext() const
176 {
177     return ActiveDOMObject::executionContext();
178 }
179
180 AtomicString FontFaceSet::status() const
181 {
182     DEFINE_STATIC_LOCAL(AtomicString, loading, ("loading", AtomicString::ConstructFromLiteral));
183     DEFINE_STATIC_LOCAL(AtomicString, loaded, ("loaded", AtomicString::ConstructFromLiteral));
184     return (!m_loadingFonts.isEmpty() || hasLoadedFonts()) ? loading : loaded;
185 }
186
187 void FontFaceSet::handlePendingEventsAndPromisesSoon()
188 {
189     // m_asyncRunner will be 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(FontFaceSetLoadEvent::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 (WillBeHeapListHashSet<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     WillBeHeapListHashSet<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 WillBeHeapListHashSet<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 WillBeHeapListHashSet<RefPtrWillBeMember<FontFace> >& cssConnectedFaces = cssConnectedFontFaceList();
380     WillBeHeapVector<RefPtrWillBeMember<FontFace> > fontFaces;
381     fontFaces.reserveInitialCapacity(cssConnectedFaces.size() + m_nonCSSConnectedFaces.size());
382     for (WillBeHeapListHashSet<RefPtrWillBeMember<FontFace> >::const_iterator it = cssConnectedFaces.begin(); it != cssConnectedFaces.end(); ++it)
383         fontFaces.append(*it);
384     for (WillBeHeapListHashSet<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<FontFaceSetLoadEvent> doneEvent = nullptr;
419         RefPtrWillBeRawPtr<FontFaceSetLoadEvent> errorEvent = nullptr;
420         doneEvent = FontFaceSetLoadEvent::createForFontFaces(EventTypeNames::loadingdone, m_loadedFonts);
421         m_loadedFonts.clear();
422         if (!m_failedFonts.isEmpty()) {
423             errorEvent = FontFaceSetLoadEvent::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(DOMException::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(CSSPropertyFontStretch, *parsedStyle),
537         CSSPropertyValue(CSSPropertyFontStyle, *parsedStyle),
538         CSSPropertyValue(CSSPropertyFontVariant, *parsedStyle),
539         CSSPropertyValue(CSSPropertyFontWeight, *parsedStyle),
540         CSSPropertyValue(CSSPropertyFontSize, *parsedStyle),
541         CSSPropertyValue(CSSPropertyLineHeight, *parsedStyle),
542     };
543     StyleResolver& styleResolver = document()->ensureStyleResolver();
544     styleResolver.applyPropertiesToStyle(properties, WTF_ARRAY_LENGTH(properties), style.get());
545
546     font = style->font();
547     font.update(document()->styleEngine()->fontSelector());
548     return true;
549 }
550
551 void FontFaceSet::FontLoadHistogram::updateStatus(FontFace* fontFace)
552 {
553     if (m_status == Reported)
554         return;
555     if (fontFace->hadBlankText())
556         m_status = HadBlankText;
557     else if (m_status == NoWebFonts)
558         m_status = DidNotHaveBlankText;
559 }
560
561 void FontFaceSet::FontLoadHistogram::record()
562 {
563     if (!m_recorded) {
564         m_recorded = true;
565         blink::Platform::current()->histogramCustomCounts("WebFont.WebFontsInPage", m_count, 1, 100, 50);
566     }
567     if (m_status == HadBlankText || m_status == DidNotHaveBlankText) {
568         blink::Platform::current()->histogramEnumeration("WebFont.HadBlankText", m_status == HadBlankText ? 1 : 0, 2);
569         m_status = Reported;
570     }
571 }
572
573 static const char* supplementName()
574 {
575     return "FontFaceSet";
576 }
577
578 PassRefPtrWillBeRawPtr<FontFaceSet> FontFaceSet::from(Document& document)
579 {
580     RefPtrWillBeRawPtr<FontFaceSet> fonts = static_cast<FontFaceSet*>(SupplementType::from(document, supplementName()));
581     if (!fonts) {
582         fonts = FontFaceSet::create(document);
583         SupplementType::provideTo(document, supplementName(), fonts);
584     }
585
586     return fonts.release();
587 }
588
589 void FontFaceSet::didLayout(Document& document)
590 {
591     if (FontFaceSet* fonts = static_cast<FontFaceSet*>(SupplementType::from(document, supplementName())))
592         fonts->didLayout();
593 }
594
595 #if ENABLE(OILPAN)
596 void FontFaceSet::trace(Visitor* visitor)
597 {
598     visitor->trace(m_loadingFonts);
599     visitor->trace(m_loadedFonts);
600     visitor->trace(m_failedFonts);
601     visitor->trace(m_nonCSSConnectedFaces);
602     DocumentSupplement::trace(visitor);
603     EventTargetWithInlineData::trace(visitor);
604 }
605 #endif
606
607 } // namespace blink