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