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