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/core/v8/ExceptionState.h" 35 #include "bindings/core/v8/ScriptState.h" 36 #include "core/CSSValueKeywords.h" 37 #include "core/css/BinaryDataFontFaceSource.h" 38 #include "core/css/CSSFontFace.h" 39 #include "core/css/CSSFontFaceSrcValue.h" 40 #include "core/css/CSSFontSelector.h" 41 #include "core/css/CSSPrimitiveValue.h" 42 #include "core/css/CSSUnicodeRangeValue.h" 43 #include "core/css/CSSValueList.h" 44 #include "core/css/FontFaceDescriptors.h" 45 #include "core/css/LocalFontFaceSource.h" 46 #include "core/css/RemoteFontFaceSource.h" 47 #include "core/css/StylePropertySet.h" 48 #include "core/css/StyleRule.h" 49 #include "core/css/parser/CSSParser.h" 50 #include "core/dom/DOMException.h" 51 #include "core/dom/Document.h" 52 #include "core/dom/ExceptionCode.h" 53 #include "core/dom/StyleEngine.h" 54 #include "core/frame/LocalFrame.h" 55 #include "core/frame/Settings.h" 56 #include "core/frame/UseCounter.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 blink { 64 65 static PassRefPtrWillBeRawPtr<CSSValue> parseCSSValue(const Document* document, const String& s, CSSPropertyID propertyID) 66 { 67 CSSParserContext context(*document, UseCounter::getFrom(document)); 68 return CSSParser::parseSingleValue(propertyID, s, context); 69 } 70 71 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, const String& source, const FontFaceDescriptors& descriptors) 72 { 73 RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace(context, family, descriptors)); 74 75 RefPtrWillBeRawPtr<CSSValue> src = parseCSSValue(toDocument(context), source, CSSPropertySrc); 76 if (!src || !src->isValueList()) 77 fontFace->setError(DOMException::create(SyntaxError, "The source provided ('" + source + "') could not be parsed as a value list.")); 78 79 fontFace->initCSSFontFace(toDocument(context), src); 80 return fontFace.release(); 81 } 82 83 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, PassRefPtr<ArrayBuffer> source, const FontFaceDescriptors& descriptors) 84 { 85 RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace(context, family, descriptors)); 86 fontFace->initCSSFontFace(static_cast<const unsigned char*>(source->data()), source->byteLength()); 87 return fontFace.release(); 88 } 89 90 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, PassRefPtr<ArrayBufferView> source, const FontFaceDescriptors& descriptors) 91 { 92 RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace(context, family, descriptors)); 93 fontFace->initCSSFontFace(static_cast<const unsigned char*>(source->baseAddress()), source->byteLength()); 94 return fontFace.release(); 95 } 96 97 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(Document* document, const StyleRuleFontFace* fontFaceRule) 98 { 99 const StylePropertySet& properties = fontFaceRule->properties(); 100 101 // Obtain the font-family property and the src property. Both must be defined. 102 RefPtrWillBeRawPtr<CSSValue> family = properties.getPropertyCSSValue(CSSPropertyFontFamily); 103 if (!family || !family->isValueList()) 104 return nullptr; 105 RefPtrWillBeRawPtr<CSSValue> src = properties.getPropertyCSSValue(CSSPropertySrc); 106 if (!src || !src->isValueList()) 107 return nullptr; 108 109 RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace(document)); 110 111 if (fontFace->setFamilyValue(toCSSValueList(family.get())) 112 && fontFace->setPropertyFromStyle(properties, CSSPropertyFontStyle) 113 && fontFace->setPropertyFromStyle(properties, CSSPropertyFontWeight) 114 && fontFace->setPropertyFromStyle(properties, CSSPropertyFontStretch) 115 && fontFace->setPropertyFromStyle(properties, CSSPropertyUnicodeRange) 116 && fontFace->setPropertyFromStyle(properties, CSSPropertyFontVariant) 117 && fontFace->setPropertyFromStyle(properties, CSSPropertyWebkitFontFeatureSettings) 118 && !fontFace->family().isEmpty() 119 && fontFace->traits().bitfield()) { 120 fontFace->initCSSFontFace(document, src); 121 return fontFace.release(); 122 } 123 return nullptr; 124 } 125 126 FontFace::FontFace(ExecutionContext* context) 127 : ActiveDOMObject(context) 128 , m_status(Unloaded) 129 { 130 suspendIfNeeded(); 131 } 132 133 FontFace::FontFace(ExecutionContext* context, const AtomicString& family, const FontFaceDescriptors& descriptors) 134 : ActiveDOMObject(context) 135 , m_family(family) 136 , m_status(Unloaded) 137 { 138 Document* document = toDocument(context); 139 setPropertyFromString(document, descriptors.style(), CSSPropertyFontStyle); 140 setPropertyFromString(document, descriptors.weight(), CSSPropertyFontWeight); 141 // FIXME: we don't implement 'font-strech' property yet so we can't set the property. 142 setPropertyFromString(document, descriptors.unicodeRange(), CSSPropertyUnicodeRange); 143 setPropertyFromString(document, descriptors.variant(), CSSPropertyFontVariant); 144 setPropertyFromString(document, descriptors.featureSettings(), CSSPropertyWebkitFontFeatureSettings); 145 146 suspendIfNeeded(); 147 } 148 149 FontFace::~FontFace() 150 { 151 } 152 153 String FontFace::style() const 154 { 155 return m_style ? m_style->cssText() : "normal"; 156 } 157 158 String FontFace::weight() const 159 { 160 return m_weight ? m_weight->cssText() : "normal"; 161 } 162 163 String FontFace::stretch() const 164 { 165 return m_stretch ? m_stretch->cssText() : "normal"; 166 } 167 168 String FontFace::unicodeRange() const 169 { 170 return m_unicodeRange ? m_unicodeRange->cssText() : "U+0-10FFFF"; 171 } 172 173 String FontFace::variant() const 174 { 175 return m_variant ? m_variant->cssText() : "normal"; 176 } 177 178 String FontFace::featureSettings() const 179 { 180 return m_featureSettings ? m_featureSettings->cssText() : "normal"; 181 } 182 183 void FontFace::setStyle(ExecutionContext* context, const String& s, ExceptionState& exceptionState) 184 { 185 setPropertyFromString(toDocument(context), s, CSSPropertyFontStyle, &exceptionState); 186 } 187 188 void FontFace::setWeight(ExecutionContext* context, const String& s, ExceptionState& exceptionState) 189 { 190 setPropertyFromString(toDocument(context), s, CSSPropertyFontWeight, &exceptionState); 191 } 192 193 void FontFace::setStretch(ExecutionContext* context, const String& s, ExceptionState& exceptionState) 194 { 195 setPropertyFromString(toDocument(context), s, CSSPropertyFontStretch, &exceptionState); 196 } 197 198 void FontFace::setUnicodeRange(ExecutionContext* context, const String& s, ExceptionState& exceptionState) 199 { 200 setPropertyFromString(toDocument(context), s, CSSPropertyUnicodeRange, &exceptionState); 201 } 202 203 void FontFace::setVariant(ExecutionContext* context, const String& s, ExceptionState& exceptionState) 204 { 205 setPropertyFromString(toDocument(context), s, CSSPropertyFontVariant, &exceptionState); 206 } 207 208 void FontFace::setFeatureSettings(ExecutionContext* context, const String& s, ExceptionState& exceptionState) 209 { 210 setPropertyFromString(toDocument(context), s, CSSPropertyWebkitFontFeatureSettings, &exceptionState); 211 } 212 213 void FontFace::setPropertyFromString(const Document* document, const String& s, CSSPropertyID propertyID, ExceptionState* exceptionState) 214 { 215 RefPtrWillBeRawPtr<CSSValue> value = parseCSSValue(document, s, propertyID); 216 if (value && setPropertyValue(value, propertyID)) 217 return; 218 219 String message = "Failed to set '" + s + "' as a property value."; 220 if (exceptionState) 221 exceptionState->throwDOMException(SyntaxError, message); 222 else 223 setError(DOMException::create(SyntaxError, message)); 224 } 225 226 bool FontFace::setPropertyFromStyle(const StylePropertySet& properties, CSSPropertyID propertyID) 227 { 228 return setPropertyValue(properties.getPropertyCSSValue(propertyID), propertyID); 229 } 230 231 bool FontFace::setPropertyValue(PassRefPtrWillBeRawPtr<CSSValue> value, CSSPropertyID propertyID) 232 { 233 switch (propertyID) { 234 case CSSPropertyFontStyle: 235 m_style = value; 236 break; 237 case CSSPropertyFontWeight: 238 m_weight = value; 239 break; 240 case CSSPropertyFontStretch: 241 m_stretch = value; 242 break; 243 case CSSPropertyUnicodeRange: 244 if (value && !value->isValueList()) 245 return false; 246 m_unicodeRange = value; 247 break; 248 case CSSPropertyFontVariant: 249 m_variant = value; 250 break; 251 case CSSPropertyWebkitFontFeatureSettings: 252 m_featureSettings = value; 253 break; 254 default: 255 ASSERT_NOT_REACHED(); 256 return false; 257 } 258 return true; 259 } 260 261 bool FontFace::setFamilyValue(CSSValueList* familyList) 262 { 263 // The font-family descriptor has to have exactly one family name. 264 if (familyList->length() != 1) 265 return false; 266 267 CSSPrimitiveValue* familyValue = toCSSPrimitiveValue(familyList->item(0)); 268 AtomicString family; 269 if (familyValue->isString()) { 270 family = AtomicString(familyValue->getStringValue()); 271 } else if (familyValue->isValueID()) { 272 // We need to use the raw text for all the generic family types, since @font-face is a way of actually 273 // defining what font to use for those types. 274 switch (familyValue->getValueID()) { 275 case CSSValueSerif: 276 family = FontFamilyNames::webkit_serif; 277 break; 278 case CSSValueSansSerif: 279 family = FontFamilyNames::webkit_sans_serif; 280 break; 281 case CSSValueCursive: 282 family = FontFamilyNames::webkit_cursive; 283 break; 284 case CSSValueFantasy: 285 family = FontFamilyNames::webkit_fantasy; 286 break; 287 case CSSValueMonospace: 288 family = FontFamilyNames::webkit_monospace; 289 break; 290 case CSSValueWebkitPictograph: 291 family = FontFamilyNames::webkit_pictograph; 292 break; 293 default: 294 return false; 295 } 296 } 297 m_family = family; 298 return true; 299 } 300 301 String FontFace::status() const 302 { 303 switch (m_status) { 304 case Unloaded: 305 return "unloaded"; 306 case Loading: 307 return "loading"; 308 case Loaded: 309 return "loaded"; 310 case Error: 311 return "error"; 312 default: 313 ASSERT_NOT_REACHED(); 314 } 315 return emptyString(); 316 } 317 318 void FontFace::setLoadStatus(LoadStatus status) 319 { 320 m_status = status; 321 ASSERT(m_status != Error || m_error); 322 323 if (m_status == Loaded || m_status == Error) { 324 if (m_loadedProperty) { 325 if (m_status == Loaded) 326 m_loadedProperty->resolve(this); 327 else 328 m_loadedProperty->reject(m_error.get()); 329 } 330 331 WillBeHeapVector<RefPtrWillBeMember<LoadFontCallback> > callbacks; 332 m_callbacks.swap(callbacks); 333 for (size_t i = 0; i < callbacks.size(); ++i) { 334 if (m_status == Loaded) 335 callbacks[i]->notifyLoaded(this); 336 else 337 callbacks[i]->notifyError(this); 338 } 339 } 340 } 341 342 void FontFace::setError(PassRefPtrWillBeRawPtr<DOMException> error) 343 { 344 if (!m_error) 345 m_error = error ? error : DOMException::create(NetworkError); 346 setLoadStatus(Error); 347 } 348 349 ScriptPromise FontFace::fontStatusPromise(ScriptState* scriptState) 350 { 351 if (!m_loadedProperty) { 352 m_loadedProperty = new LoadedProperty(scriptState->executionContext(), this, LoadedProperty::Loaded); 353 if (m_status == Loaded) 354 m_loadedProperty->resolve(this); 355 else if (m_status == Error) 356 m_loadedProperty->reject(m_error.get()); 357 } 358 return m_loadedProperty->promise(scriptState->world()); 359 } 360 361 ScriptPromise FontFace::load(ScriptState* scriptState) 362 { 363 loadInternal(scriptState->executionContext()); 364 return fontStatusPromise(scriptState); 365 } 366 367 void FontFace::loadWithCallback(PassRefPtrWillBeRawPtr<LoadFontCallback> callback, ExecutionContext* context) 368 { 369 loadInternal(context); 370 if (m_status == Loaded) 371 callback->notifyLoaded(this); 372 else if (m_status == Error) 373 callback->notifyError(this); 374 else 375 m_callbacks.append(callback); 376 } 377 378 void FontFace::loadInternal(ExecutionContext* context) 379 { 380 if (m_status != Unloaded) 381 return; 382 383 m_cssFontFace->load(); 384 toDocument(context)->styleEngine()->fontSelector()->fontLoader()->loadPendingFonts(); 385 } 386 387 FontTraits FontFace::traits() const 388 { 389 FontStyle style = FontStyleNormal; 390 if (m_style) { 391 if (!m_style->isPrimitiveValue()) 392 return 0; 393 394 switch (toCSSPrimitiveValue(m_style.get())->getValueID()) { 395 case CSSValueNormal: 396 style = FontStyleNormal; 397 break; 398 case CSSValueItalic: 399 case CSSValueOblique: 400 style = FontStyleItalic; 401 break; 402 default: 403 break; 404 } 405 } 406 407 FontWeight weight = FontWeight400; 408 if (m_weight) { 409 if (!m_weight->isPrimitiveValue()) 410 return 0; 411 412 switch (toCSSPrimitiveValue(m_weight.get())->getValueID()) { 413 case CSSValueBold: 414 case CSSValue700: 415 weight = FontWeight700; 416 break; 417 case CSSValueNormal: 418 case CSSValue400: 419 weight = FontWeight400; 420 break; 421 case CSSValue900: 422 weight = FontWeight900; 423 break; 424 case CSSValue800: 425 weight = FontWeight800; 426 break; 427 case CSSValue600: 428 weight = FontWeight600; 429 break; 430 case CSSValue500: 431 weight = FontWeight500; 432 break; 433 case CSSValue300: 434 weight = FontWeight300; 435 break; 436 case CSSValue200: 437 weight = FontWeight200; 438 break; 439 case CSSValue100: 440 weight = FontWeight100; 441 break; 442 // Although 'lighter' and 'bolder' are valid keywords for font-weights, they are invalid 443 // inside font-face rules so they are ignored. Reference: http://www.w3.org/TR/css3-fonts/#descdef-font-weight. 444 case CSSValueLighter: 445 case CSSValueBolder: 446 break; 447 default: 448 ASSERT_NOT_REACHED(); 449 break; 450 } 451 } 452 453 FontVariant variant = FontVariantNormal; 454 if (RefPtrWillBeRawPtr<CSSValue> fontVariant = m_variant) { 455 // font-variant descriptor can be a value list. 456 if (fontVariant->isPrimitiveValue()) { 457 RefPtrWillBeRawPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); 458 list->append(fontVariant); 459 fontVariant = list; 460 } else if (!fontVariant->isValueList()) { 461 return 0; 462 } 463 464 CSSValueList* variantList = toCSSValueList(fontVariant.get()); 465 unsigned numVariants = variantList->length(); 466 if (!numVariants) 467 return 0; 468 469 for (unsigned i = 0; i < numVariants; ++i) { 470 switch (toCSSPrimitiveValue(variantList->item(i))->getValueID()) { 471 case CSSValueNormal: 472 variant = FontVariantNormal; 473 break; 474 case CSSValueSmallCaps: 475 variant = FontVariantSmallCaps; 476 break; 477 default: 478 break; 479 } 480 } 481 } 482 483 return FontTraits(style, variant, weight, FontStretchNormal); 484 } 485 486 static PassOwnPtrWillBeRawPtr<CSSFontFace> createCSSFontFace(FontFace* fontFace, CSSValue* unicodeRange) 487 { 488 Vector<CSSFontFace::UnicodeRange> ranges; 489 if (CSSValueList* rangeList = toCSSValueList(unicodeRange)) { 490 unsigned numRanges = rangeList->length(); 491 for (unsigned i = 0; i < numRanges; i++) { 492 CSSUnicodeRangeValue* range = toCSSUnicodeRangeValue(rangeList->item(i)); 493 ranges.append(CSSFontFace::UnicodeRange(range->from(), range->to())); 494 } 495 } 496 497 return adoptPtrWillBeNoop(new CSSFontFace(fontFace, ranges)); 498 } 499 500 void FontFace::initCSSFontFace(Document* document, PassRefPtrWillBeRawPtr<CSSValue> src) 501 { 502 m_cssFontFace = createCSSFontFace(this, m_unicodeRange.get()); 503 if (m_error) 504 return; 505 506 // Each item in the src property's list is a single CSSFontFaceSource. Put them all into a CSSFontFace. 507 ASSERT(src); 508 ASSERT(src->isValueList()); 509 CSSValueList* srcList = toCSSValueList(src.get()); 510 int srcLength = srcList->length(); 511 512 bool foundSVGFont = false; 513 514 for (int i = 0; i < srcLength; i++) { 515 // An item in the list either specifies a string (local font name) or a URL (remote font to download). 516 CSSFontFaceSrcValue* item = toCSSFontFaceSrcValue(srcList->item(i)); 517 OwnPtrWillBeRawPtr<CSSFontFaceSource> source = nullptr; 518 519 #if ENABLE(SVG_FONTS) 520 foundSVGFont = item->isSVGFontFaceSrc() || item->svgFontFaceElement(); 521 #endif 522 if (!item->isLocal()) { 523 Settings* settings = document ? document->frame() ? document->frame()->settings() : 0 : 0; 524 bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled()); 525 if (allowDownloading && item->isSupportedFormat() && document) { 526 FontResource* fetched = item->fetch(document); 527 if (fetched) { 528 FontLoader* fontLoader = document->styleEngine()->fontSelector()->fontLoader(); 529 530 #if ENABLE(SVG_FONTS) 531 if (foundSVGFont) { 532 source = adoptPtrWillBeNoop(new SVGRemoteFontFaceSource(item->resource(), fetched, fontLoader)); 533 } else 534 #endif 535 { 536 source = adoptPtrWillBeNoop(new RemoteFontFaceSource(fetched, fontLoader)); 537 } 538 } 539 } 540 } else { 541 #if ENABLE(SVG_FONTS) 542 if (item->svgFontFaceElement()) { 543 RefPtrWillBeRawPtr<SVGFontFaceElement> fontfaceElement = item->svgFontFaceElement(); 544 // SVGFontFaceSource assumes that it is the case where <font-face> element resides in the same document. 545 // We put a RELEASE_ASSERT here as it will cause UAF if the assumption is false. 546 RELEASE_ASSERT(fontfaceElement->inDocument()); 547 RELEASE_ASSERT(fontfaceElement->document() == document); 548 source = adoptPtrWillBeNoop(new SVGFontFaceSource(fontfaceElement.get())); 549 } else 550 #endif 551 { 552 source = adoptPtrWillBeNoop(new LocalFontFaceSource(item->resource())); 553 } 554 } 555 556 if (source) 557 m_cssFontFace->addSource(source.release()); 558 } 559 } 560 561 void FontFace::initCSSFontFace(const unsigned char* data, unsigned size) 562 { 563 m_cssFontFace = createCSSFontFace(this, m_unicodeRange.get()); 564 if (m_error) 565 return; 566 567 RefPtr<SharedBuffer> buffer = SharedBuffer::create(data, size); 568 OwnPtrWillBeRawPtr<BinaryDataFontFaceSource> source = adoptPtrWillBeNoop(new BinaryDataFontFaceSource(buffer.get())); 569 if (source->isValid()) 570 setLoadStatus(Loaded); 571 else 572 setError(DOMException::create(SyntaxError, "Invalid font data in ArrayBuffer.")); 573 m_cssFontFace->addSource(source.release()); 574 } 575 576 void FontFace::trace(Visitor* visitor) 577 { 578 visitor->trace(m_src); 579 visitor->trace(m_style); 580 visitor->trace(m_weight); 581 visitor->trace(m_stretch); 582 visitor->trace(m_unicodeRange); 583 visitor->trace(m_variant); 584 visitor->trace(m_featureSettings); 585 visitor->trace(m_error); 586 visitor->trace(m_loadedProperty); 587 visitor->trace(m_cssFontFace); 588 visitor->trace(m_callbacks); 589 } 590 591 bool FontFace::hadBlankText() const 592 { 593 return m_cssFontFace->hadBlankText(); 594 } 595 596 bool FontFace::hasPendingActivity() const 597 { 598 return m_status == Loading && executionContext() && !executionContext()->activeDOMObjectsAreStopped(); 599 } 600 601 } // namespace blink 602