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 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "core/css/FontFace.h" 33 34 #include "bindings/v8/Dictionary.h" 35 #include "bindings/v8/ExceptionState.h" 36 #include "bindings/v8/ScriptPromiseResolverWithContext.h" 37 #include "bindings/v8/ScriptState.h" 38 #include "core/CSSValueKeywords.h" 39 #include "core/css/BinaryDataFontFaceSource.h" 40 #include "core/css/CSSFontFace.h" 41 #include "core/css/CSSFontFaceSrcValue.h" 42 #include "core/css/CSSFontSelector.h" 43 #include "core/css/CSSPrimitiveValue.h" 44 #include "core/css/CSSUnicodeRangeValue.h" 45 #include "core/css/CSSValueList.h" 46 #include "core/css/LocalFontFaceSource.h" 47 #include "core/css/RemoteFontFaceSource.h" 48 #include "core/css/StylePropertySet.h" 49 #include "core/css/StyleRule.h" 50 #include "core/css/parser/BisonCSSParser.h" 51 #include "core/dom/DOMError.h" 52 #include "core/dom/Document.h" 53 #include "core/dom/ExceptionCode.h" 54 #include "core/dom/StyleEngine.h" 55 #include "core/frame/LocalFrame.h" 56 #include "core/frame/Settings.h" 57 #include "core/svg/SVGFontFaceElement.h" 58 #include "core/svg/SVGFontFaceSource.h" 59 #include "core/svg/SVGRemoteFontFaceSource.h" 60 #include "platform/FontFamilyNames.h" 61 #include "platform/SharedBuffer.h" 62 63 namespace WebCore { 64 65 class FontFaceReadyPromiseResolver { 66 public: 67 static PassOwnPtr<FontFaceReadyPromiseResolver> create(ScriptState* scriptState) 68 { 69 return adoptPtr(new FontFaceReadyPromiseResolver(scriptState)); 70 } 71 72 void resolve(PassRefPtrWillBeRawPtr<FontFace> fontFace) 73 { 74 switch (fontFace->loadStatus()) { 75 case FontFace::Loaded: 76 m_resolver->resolve(fontFace); 77 break; 78 case FontFace::Error: 79 m_resolver->reject(fontFace->error()); 80 break; 81 default: 82 ASSERT_NOT_REACHED(); 83 } 84 } 85 86 ScriptPromise promise() { return m_resolver->promise(); } 87 88 private: 89 FontFaceReadyPromiseResolver(ScriptState* scriptState) 90 : m_resolver(ScriptPromiseResolverWithContext::create(scriptState)) 91 { 92 } 93 94 RefPtr<ScriptPromiseResolverWithContext> m_resolver; 95 }; 96 97 static PassRefPtrWillBeRawPtr<CSSValue> parseCSSValue(const Document* document, const String& s, CSSPropertyID propertyID) 98 { 99 if (s.isEmpty()) 100 return nullptr; 101 RefPtrWillBeRawPtr<MutableStylePropertySet> parsedStyle = MutableStylePropertySet::create(); 102 BisonCSSParser::parseValue(parsedStyle.get(), propertyID, s, true, *document); 103 return parsedStyle->getPropertyCSSValue(propertyID); 104 } 105 106 static bool initFontFace(FontFace* fontFace, ExecutionContext* context, const AtomicString& family, const Dictionary& descriptors, ExceptionState& exceptionState) 107 { 108 fontFace->setFamily(context, family, exceptionState); 109 if (exceptionState.hadException()) 110 return false; 111 112 String value; 113 if (descriptors.get("style", value)) { 114 fontFace->setStyle(context, value, exceptionState); 115 if (exceptionState.hadException()) 116 return false; 117 } 118 if (descriptors.get("weight", value)) { 119 fontFace->setWeight(context, value, exceptionState); 120 if (exceptionState.hadException()) 121 return false; 122 } 123 if (descriptors.get("stretch", value)) { 124 fontFace->setStretch(context, value, exceptionState); 125 if (exceptionState.hadException()) 126 return false; 127 } 128 if (descriptors.get("unicodeRange", value)) { 129 fontFace->setUnicodeRange(context, value, exceptionState); 130 if (exceptionState.hadException()) 131 return false; 132 } 133 if (descriptors.get("variant", value)) { 134 fontFace->setVariant(context, value, exceptionState); 135 if (exceptionState.hadException()) 136 return false; 137 } 138 if (descriptors.get("featureSettings", value)) { 139 fontFace->setFeatureSettings(context, value, exceptionState); 140 if (exceptionState.hadException()) 141 return false; 142 } 143 return true; 144 } 145 146 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, const String& source, const Dictionary& descriptors, ExceptionState& exceptionState) 147 { 148 RefPtrWillBeRawPtr<CSSValue> src = parseCSSValue(toDocument(context), source, CSSPropertySrc); 149 if (!src || !src->isValueList()) { 150 exceptionState.throwDOMException(SyntaxError, "The source provided ('" + source + "') could not be parsed as a value list."); 151 return nullptr; 152 } 153 154 RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace()); 155 if (initFontFace(fontFace.get(), context, family, descriptors, exceptionState)) 156 fontFace->initCSSFontFace(toDocument(context), src); 157 return fontFace.release(); 158 } 159 160 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, PassRefPtr<ArrayBuffer> source, const Dictionary& descriptors, ExceptionState& exceptionState) 161 { 162 RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace()); 163 if (initFontFace(fontFace.get(), context, family, descriptors, exceptionState)) 164 fontFace->initCSSFontFace(static_cast<const unsigned char*>(source->data()), source->byteLength()); 165 return fontFace.release(); 166 } 167 168 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, PassRefPtr<ArrayBufferView> source, const Dictionary& descriptors, ExceptionState& exceptionState) 169 { 170 RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace()); 171 if (initFontFace(fontFace.get(), context, family, descriptors, exceptionState)) 172 fontFace->initCSSFontFace(static_cast<const unsigned char*>(source->baseAddress()), source->byteLength()); 173 return fontFace.release(); 174 } 175 176 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(Document* document, const StyleRuleFontFace* fontFaceRule) 177 { 178 const StylePropertySet& properties = fontFaceRule->properties(); 179 180 // Obtain the font-family property and the src property. Both must be defined. 181 RefPtrWillBeRawPtr<CSSValue> family = properties.getPropertyCSSValue(CSSPropertyFontFamily); 182 if (!family || !family->isValueList()) 183 return nullptr; 184 RefPtrWillBeRawPtr<CSSValue> src = properties.getPropertyCSSValue(CSSPropertySrc); 185 if (!src || !src->isValueList()) 186 return nullptr; 187 188 RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace()); 189 190 if (fontFace->setFamilyValue(toCSSValueList(family.get())) 191 && fontFace->setPropertyFromStyle(properties, CSSPropertyFontStyle) 192 && fontFace->setPropertyFromStyle(properties, CSSPropertyFontWeight) 193 && fontFace->setPropertyFromStyle(properties, CSSPropertyFontStretch) 194 && fontFace->setPropertyFromStyle(properties, CSSPropertyUnicodeRange) 195 && fontFace->setPropertyFromStyle(properties, CSSPropertyFontVariant) 196 && fontFace->setPropertyFromStyle(properties, CSSPropertyWebkitFontFeatureSettings) 197 && !fontFace->family().isEmpty() 198 && fontFace->traits().mask()) { 199 fontFace->initCSSFontFace(document, src); 200 return fontFace.release(); 201 } 202 return nullptr; 203 } 204 205 FontFace::FontFace() 206 : m_status(Unloaded) 207 { 208 } 209 210 FontFace::~FontFace() 211 { 212 } 213 214 String FontFace::style() const 215 { 216 return m_style ? m_style->cssText() : "normal"; 217 } 218 219 String FontFace::weight() const 220 { 221 return m_weight ? m_weight->cssText() : "normal"; 222 } 223 224 String FontFace::stretch() const 225 { 226 return m_stretch ? m_stretch->cssText() : "normal"; 227 } 228 229 String FontFace::unicodeRange() const 230 { 231 return m_unicodeRange ? m_unicodeRange->cssText() : "U+0-10FFFF"; 232 } 233 234 String FontFace::variant() const 235 { 236 return m_variant ? m_variant->cssText() : "normal"; 237 } 238 239 String FontFace::featureSettings() const 240 { 241 return m_featureSettings ? m_featureSettings->cssText() : "normal"; 242 } 243 244 void FontFace::setStyle(ExecutionContext* context, const String& s, ExceptionState& exceptionState) 245 { 246 setPropertyFromString(toDocument(context), s, CSSPropertyFontStyle, exceptionState); 247 } 248 249 void FontFace::setWeight(ExecutionContext* context, const String& s, ExceptionState& exceptionState) 250 { 251 setPropertyFromString(toDocument(context), s, CSSPropertyFontWeight, exceptionState); 252 } 253 254 void FontFace::setStretch(ExecutionContext* context, const String& s, ExceptionState& exceptionState) 255 { 256 setPropertyFromString(toDocument(context), s, CSSPropertyFontStretch, exceptionState); 257 } 258 259 void FontFace::setUnicodeRange(ExecutionContext* context, const String& s, ExceptionState& exceptionState) 260 { 261 setPropertyFromString(toDocument(context), s, CSSPropertyUnicodeRange, exceptionState); 262 } 263 264 void FontFace::setVariant(ExecutionContext* context, const String& s, ExceptionState& exceptionState) 265 { 266 setPropertyFromString(toDocument(context), s, CSSPropertyFontVariant, exceptionState); 267 } 268 269 void FontFace::setFeatureSettings(ExecutionContext* context, const String& s, ExceptionState& exceptionState) 270 { 271 setPropertyFromString(toDocument(context), s, CSSPropertyWebkitFontFeatureSettings, exceptionState); 272 } 273 274 void FontFace::setPropertyFromString(const Document* document, const String& s, CSSPropertyID propertyID, ExceptionState& exceptionState) 275 { 276 RefPtrWillBeRawPtr<CSSValue> value = parseCSSValue(document, s, propertyID); 277 if (!value || !setPropertyValue(value, propertyID)) 278 exceptionState.throwDOMException(SyntaxError, "Failed to set '" + s + "' as a property value."); 279 } 280 281 bool FontFace::setPropertyFromStyle(const StylePropertySet& properties, CSSPropertyID propertyID) 282 { 283 return setPropertyValue(properties.getPropertyCSSValue(propertyID), propertyID); 284 } 285 286 bool FontFace::setPropertyValue(PassRefPtrWillBeRawPtr<CSSValue> value, CSSPropertyID propertyID) 287 { 288 switch (propertyID) { 289 case CSSPropertyFontStyle: 290 m_style = value; 291 break; 292 case CSSPropertyFontWeight: 293 m_weight = value; 294 break; 295 case CSSPropertyFontStretch: 296 m_stretch = value; 297 break; 298 case CSSPropertyUnicodeRange: 299 if (value && !value->isValueList()) 300 return false; 301 m_unicodeRange = value; 302 break; 303 case CSSPropertyFontVariant: 304 m_variant = value; 305 break; 306 case CSSPropertyWebkitFontFeatureSettings: 307 m_featureSettings = value; 308 break; 309 default: 310 ASSERT_NOT_REACHED(); 311 return false; 312 } 313 return true; 314 } 315 316 bool FontFace::setFamilyValue(CSSValueList* familyList) 317 { 318 // The font-family descriptor has to have exactly one family name. 319 if (familyList->length() != 1) 320 return false; 321 322 CSSPrimitiveValue* familyValue = toCSSPrimitiveValue(familyList->itemWithoutBoundsCheck(0)); 323 AtomicString family; 324 if (familyValue->isString()) { 325 family = AtomicString(familyValue->getStringValue()); 326 } else if (familyValue->isValueID()) { 327 // We need to use the raw text for all the generic family types, since @font-face is a way of actually 328 // defining what font to use for those types. 329 switch (familyValue->getValueID()) { 330 case CSSValueSerif: 331 family = FontFamilyNames::webkit_serif; 332 break; 333 case CSSValueSansSerif: 334 family = FontFamilyNames::webkit_sans_serif; 335 break; 336 case CSSValueCursive: 337 family = FontFamilyNames::webkit_cursive; 338 break; 339 case CSSValueFantasy: 340 family = FontFamilyNames::webkit_fantasy; 341 break; 342 case CSSValueMonospace: 343 family = FontFamilyNames::webkit_monospace; 344 break; 345 case CSSValueWebkitPictograph: 346 family = FontFamilyNames::webkit_pictograph; 347 break; 348 default: 349 return false; 350 } 351 } 352 m_family = family; 353 return true; 354 } 355 356 String FontFace::status() const 357 { 358 switch (m_status) { 359 case Unloaded: 360 return "unloaded"; 361 case Loading: 362 return "loading"; 363 case Loaded: 364 return "loaded"; 365 case Error: 366 return "error"; 367 default: 368 ASSERT_NOT_REACHED(); 369 } 370 return emptyString(); 371 } 372 373 void FontFace::setLoadStatus(LoadStatus status) 374 { 375 m_status = status; 376 if (m_status == Error) 377 m_error = DOMError::create(NetworkError); 378 if (m_status == Loaded || m_status == Error) { 379 resolveReadyPromises(); 380 381 WillBeHeapVector<RefPtrWillBeMember<LoadFontCallback> > callbacks; 382 m_callbacks.swap(callbacks); 383 for (size_t i = 0; i < callbacks.size(); ++i) { 384 if (m_status == Loaded) 385 callbacks[i]->notifyLoaded(this); 386 else 387 callbacks[i]->notifyError(this); 388 } 389 } 390 } 391 392 ScriptPromise FontFace::fontStatusPromise(ScriptState* scriptState) 393 { 394 // Since we cannot hold a ScriptPromise as a member of FontFace (that will 395 // cause a circular reference), this creates new Promise every time. 396 OwnPtr<FontFaceReadyPromiseResolver> resolver = FontFaceReadyPromiseResolver::create(scriptState); 397 ScriptPromise promise = resolver->promise(); 398 if (m_status == Loaded || m_status == Error) 399 resolver->resolve(this); 400 else 401 m_readyResolvers.append(resolver.release()); 402 403 return promise; 404 } 405 406 ScriptPromise FontFace::load(ScriptState* scriptState) 407 { 408 loadInternal(scriptState->executionContext()); 409 return fontStatusPromise(scriptState); 410 } 411 412 void FontFace::loadWithCallback(PassRefPtrWillBeRawPtr<LoadFontCallback> callback, ExecutionContext* context) 413 { 414 loadInternal(context); 415 if (m_status == Loaded) 416 callback->notifyLoaded(this); 417 else if (m_status == Error) 418 callback->notifyError(this); 419 else 420 m_callbacks.append(callback); 421 } 422 423 void FontFace::loadInternal(ExecutionContext* context) 424 { 425 if (m_status != Unloaded) 426 return; 427 428 m_cssFontFace->load(); 429 toDocument(context)->styleEngine()->fontSelector()->fontLoader()->loadPendingFonts(); 430 } 431 432 void FontFace::resolveReadyPromises() 433 { 434 for (size_t i = 0; i < m_readyResolvers.size(); i++) 435 m_readyResolvers[i]->resolve(this); 436 m_readyResolvers.clear(); 437 } 438 439 FontTraits FontFace::traits() const 440 { 441 FontStyle style = FontStyleNormal; 442 if (m_style) { 443 if (!m_style->isPrimitiveValue()) 444 return 0; 445 446 switch (toCSSPrimitiveValue(m_style.get())->getValueID()) { 447 case CSSValueNormal: 448 style = FontStyleNormal; 449 break; 450 case CSSValueItalic: 451 case CSSValueOblique: 452 style = FontStyleItalic; 453 break; 454 default: 455 break; 456 } 457 } 458 459 FontWeight weight = FontWeight400; 460 if (m_weight) { 461 if (!m_weight->isPrimitiveValue()) 462 return 0; 463 464 switch (toCSSPrimitiveValue(m_weight.get())->getValueID()) { 465 case CSSValueBold: 466 case CSSValue700: 467 weight = FontWeight700; 468 break; 469 case CSSValueNormal: 470 case CSSValue400: 471 weight = FontWeight400; 472 break; 473 case CSSValue900: 474 weight = FontWeight900; 475 break; 476 case CSSValue800: 477 weight = FontWeight800; 478 break; 479 case CSSValue600: 480 weight = FontWeight600; 481 break; 482 case CSSValue500: 483 weight = FontWeight500; 484 break; 485 case CSSValue300: 486 weight = FontWeight300; 487 break; 488 case CSSValue200: 489 weight = FontWeight200; 490 break; 491 case CSSValueLighter: 492 case CSSValue100: 493 weight = FontWeight100; 494 break; 495 default: 496 ASSERT_NOT_REACHED(); 497 break; 498 } 499 } 500 501 FontVariant variant = FontVariantNormal; 502 if (RefPtrWillBeRawPtr<CSSValue> fontVariant = m_variant) { 503 // font-variant descriptor can be a value list. 504 if (fontVariant->isPrimitiveValue()) { 505 RefPtrWillBeRawPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); 506 list->append(fontVariant); 507 fontVariant = list; 508 } else if (!fontVariant->isValueList()) { 509 return 0; 510 } 511 512 CSSValueList* variantList = toCSSValueList(fontVariant.get()); 513 unsigned numVariants = variantList->length(); 514 if (!numVariants) 515 return 0; 516 517 for (unsigned i = 0; i < numVariants; ++i) { 518 switch (toCSSPrimitiveValue(variantList->itemWithoutBoundsCheck(i))->getValueID()) { 519 case CSSValueNormal: 520 variant = FontVariantNormal; 521 break; 522 case CSSValueSmallCaps: 523 variant = FontVariantSmallCaps; 524 break; 525 default: 526 break; 527 } 528 } 529 } 530 531 return FontTraits(style, variant, weight, FontStretchNormal); 532 } 533 534 static PassOwnPtrWillBeRawPtr<CSSFontFace> createCSSFontFace(FontFace* fontFace, CSSValue* unicodeRange) 535 { 536 Vector<CSSFontFace::UnicodeRange> ranges; 537 if (CSSValueList* rangeList = toCSSValueList(unicodeRange)) { 538 unsigned numRanges = rangeList->length(); 539 for (unsigned i = 0; i < numRanges; i++) { 540 CSSUnicodeRangeValue* range = toCSSUnicodeRangeValue(rangeList->itemWithoutBoundsCheck(i)); 541 ranges.append(CSSFontFace::UnicodeRange(range->from(), range->to())); 542 } 543 } 544 545 return adoptPtrWillBeNoop(new CSSFontFace(fontFace, ranges)); 546 } 547 548 void FontFace::initCSSFontFace(Document* document, PassRefPtrWillBeRawPtr<CSSValue> src) 549 { 550 m_cssFontFace = createCSSFontFace(this, m_unicodeRange.get()); 551 552 // Each item in the src property's list is a single CSSFontFaceSource. Put them all into a CSSFontFace. 553 CSSValueList* srcList = toCSSValueList(src.get()); 554 int srcLength = srcList->length(); 555 556 bool foundSVGFont = false; 557 558 for (int i = 0; i < srcLength; i++) { 559 // An item in the list either specifies a string (local font name) or a URL (remote font to download). 560 CSSFontFaceSrcValue* item = toCSSFontFaceSrcValue(srcList->itemWithoutBoundsCheck(i)); 561 OwnPtrWillBeRawPtr<CSSFontFaceSource> source = nullptr; 562 563 #if ENABLE(SVG_FONTS) 564 foundSVGFont = item->isSVGFontFaceSrc() || item->svgFontFaceElement(); 565 #endif 566 if (!item->isLocal()) { 567 Settings* settings = document ? document->frame() ? document->frame()->settings() : 0 : 0; 568 bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled()); 569 if (allowDownloading && item->isSupportedFormat() && document) { 570 FontResource* fetched = item->fetch(document); 571 if (fetched) { 572 FontLoader* fontLoader = document->styleEngine()->fontSelector()->fontLoader(); 573 574 #if ENABLE(SVG_FONTS) 575 if (foundSVGFont) { 576 source = adoptPtrWillBeNoop(new SVGRemoteFontFaceSource(item->resource(), fetched, fontLoader)); 577 } else 578 #endif 579 { 580 source = adoptPtrWillBeNoop(new RemoteFontFaceSource(fetched, fontLoader)); 581 } 582 } 583 } 584 } else { 585 #if ENABLE(SVG_FONTS) 586 if (item->svgFontFaceElement()) { 587 RefPtrWillBeRawPtr<SVGFontFaceElement> fontfaceElement = item->svgFontFaceElement(); 588 // SVGFontFaceSource assumes that it is the case where <font-face> element resides in the same document. 589 // We put a RELEASE_ASSERT here as it will cause UAF if the assumption is false. 590 RELEASE_ASSERT(fontfaceElement->inDocument()); 591 RELEASE_ASSERT(fontfaceElement->document() == document); 592 source = adoptPtrWillBeNoop(new SVGFontFaceSource(fontfaceElement.get())); 593 } else 594 #endif 595 { 596 source = adoptPtrWillBeNoop(new LocalFontFaceSource(item->resource())); 597 } 598 } 599 600 if (source) 601 m_cssFontFace->addSource(source.release()); 602 } 603 } 604 605 void FontFace::initCSSFontFace(const unsigned char* data, unsigned size) 606 { 607 m_cssFontFace = createCSSFontFace(this, m_unicodeRange.get()); 608 609 RefPtr<SharedBuffer> buffer = SharedBuffer::create(data, size); 610 OwnPtrWillBeRawPtr<BinaryDataFontFaceSource> source = adoptPtrWillBeNoop(new BinaryDataFontFaceSource(buffer.get())); 611 if (source->isValid()) { 612 m_status = Loaded; 613 } else { 614 m_status = Error; 615 m_error = DOMError::create(SyntaxError, "Invalid font data in ArrayBuffer."); 616 } 617 m_cssFontFace->addSource(source.release()); 618 } 619 620 void FontFace::trace(Visitor* visitor) 621 { 622 visitor->trace(m_src); 623 visitor->trace(m_style); 624 visitor->trace(m_weight); 625 visitor->trace(m_stretch); 626 visitor->trace(m_unicodeRange); 627 visitor->trace(m_variant); 628 visitor->trace(m_featureSettings); 629 visitor->trace(m_error); 630 visitor->trace(m_cssFontFace); 631 visitor->trace(m_callbacks); 632 } 633 634 bool FontFace::hadBlankText() const 635 { 636 return m_cssFontFace->hadBlankText(); 637 } 638 639 } // namespace WebCore 640