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