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 "CSSValueKeywords.h" 35 #include "FontFamilyNames.h" 36 #include "bindings/v8/Dictionary.h" 37 #include "bindings/v8/ExceptionState.h" 38 #include "bindings/v8/ScriptPromiseResolver.h" 39 #include "bindings/v8/ScriptScope.h" 40 #include "bindings/v8/ScriptState.h" 41 #include "core/css/CSSFontFace.h" 42 #include "core/css/CSSFontFaceSrcValue.h" 43 #include "core/css/CSSParser.h" 44 #include "core/css/CSSPrimitiveValue.h" 45 #include "core/css/CSSUnicodeRangeValue.h" 46 #include "core/css/CSSValueList.h" 47 #include "core/css/StylePropertySet.h" 48 #include "core/css/StyleRule.h" 49 #include "core/dom/DOMError.h" 50 #include "core/dom/Document.h" 51 #include "core/dom/ExceptionCode.h" 52 #include "core/frame/Frame.h" 53 #include "core/frame/Settings.h" 54 #include "core/svg/SVGFontFaceElement.h" 55 #include "platform/fonts/FontDescription.h" 56 #include "platform/fonts/FontTraitsMask.h" 57 #include "platform/fonts/SimpleFontData.h" 58 59 namespace WebCore { 60 61 class FontFaceReadyPromiseResolver { 62 public: 63 static PassOwnPtr<FontFaceReadyPromiseResolver> create(ScriptPromise promise, ExecutionContext* context) 64 { 65 return adoptPtr(new FontFaceReadyPromiseResolver(promise, context)); 66 } 67 68 void resolve(PassRefPtr<FontFace> fontFace) 69 { 70 ScriptScope scope(m_scriptState); 71 switch (fontFace->loadStatus()) { 72 case FontFace::Loaded: 73 m_resolver->resolve(fontFace); 74 break; 75 case FontFace::Error: 76 m_resolver->reject(DOMError::create(NetworkError)); 77 break; 78 default: 79 ASSERT_NOT_REACHED(); 80 } 81 } 82 83 private: 84 FontFaceReadyPromiseResolver(ScriptPromise promise, ExecutionContext* context) 85 : m_scriptState(ScriptState::current()) 86 , m_resolver(ScriptPromiseResolver::create(promise, context)) 87 { } 88 ScriptState* m_scriptState; 89 RefPtr<ScriptPromiseResolver> m_resolver; 90 }; 91 92 static PassRefPtr<CSSValue> parseCSSValue(const String& s, CSSPropertyID propertyID) 93 { 94 if (s.isEmpty()) 95 return 0; 96 RefPtr<MutableStylePropertySet> parsedStyle = MutableStylePropertySet::create(); 97 CSSParser::parseValue(parsedStyle.get(), propertyID, s, true, HTMLStandardMode, 0); 98 return parsedStyle->getPropertyCSSValue(propertyID); 99 } 100 101 PassRefPtr<FontFace> FontFace::create(const AtomicString& family, const String& source, const Dictionary& descriptors, ExceptionState& exceptionState) 102 { 103 RefPtr<CSSValue> src = parseCSSValue(source, CSSPropertySrc); 104 if (!src || !src->isValueList()) { 105 exceptionState.throwUninformativeAndGenericDOMException(SyntaxError); 106 return 0; 107 } 108 109 RefPtr<FontFace> fontFace = adoptRef<FontFace>(new FontFace(src)); 110 fontFace->setFamily(family, exceptionState); 111 if (exceptionState.hadException()) 112 return 0; 113 114 String value; 115 if (descriptors.get("style", value)) { 116 fontFace->setStyle(value, exceptionState); 117 if (exceptionState.hadException()) 118 return 0; 119 } 120 if (descriptors.get("weight", value)) { 121 fontFace->setWeight(value, exceptionState); 122 if (exceptionState.hadException()) 123 return 0; 124 } 125 if (descriptors.get("stretch", value)) { 126 fontFace->setStretch(value, exceptionState); 127 if (exceptionState.hadException()) 128 return 0; 129 } 130 if (descriptors.get("unicodeRange", value)) { 131 fontFace->setUnicodeRange(value, exceptionState); 132 if (exceptionState.hadException()) 133 return 0; 134 } 135 if (descriptors.get("variant", value)) { 136 fontFace->setVariant(value, exceptionState); 137 if (exceptionState.hadException()) 138 return 0; 139 } 140 if (descriptors.get("featureSettings", value)) { 141 fontFace->setFeatureSettings(value, exceptionState); 142 if (exceptionState.hadException()) 143 return 0; 144 } 145 146 return fontFace; 147 } 148 149 PassRefPtr<FontFace> FontFace::create(const StyleRuleFontFace* fontFaceRule) 150 { 151 const StylePropertySet* properties = fontFaceRule->properties(); 152 153 // Obtain the font-family property and the src property. Both must be defined. 154 RefPtr<CSSValue> family = properties->getPropertyCSSValue(CSSPropertyFontFamily); 155 if (!family || !family->isValueList()) 156 return 0; 157 RefPtr<CSSValue> src = properties->getPropertyCSSValue(CSSPropertySrc); 158 if (!src || !src->isValueList()) 159 return 0; 160 161 RefPtr<FontFace> fontFace = adoptRef<FontFace>(new FontFace(src)); 162 163 if (fontFace->setFamilyValue(toCSSValueList(family.get())) 164 && fontFace->setPropertyFromStyle(properties, CSSPropertyFontStyle) 165 && fontFace->setPropertyFromStyle(properties, CSSPropertyFontWeight) 166 && fontFace->setPropertyFromStyle(properties, CSSPropertyFontStretch) 167 && fontFace->setPropertyFromStyle(properties, CSSPropertyUnicodeRange) 168 && fontFace->setPropertyFromStyle(properties, CSSPropertyFontVariant) 169 && fontFace->setPropertyFromStyle(properties, CSSPropertyWebkitFontFeatureSettings)) 170 return fontFace; 171 return 0; 172 } 173 174 FontFace::FontFace(PassRefPtr<CSSValue> src) 175 : m_src(src) 176 , m_status(Unloaded) 177 , m_cssFontFace(0) 178 { 179 } 180 181 FontFace::~FontFace() 182 { 183 } 184 185 String FontFace::style() const 186 { 187 return m_style ? m_style->cssText() : "normal"; 188 } 189 190 String FontFace::weight() const 191 { 192 return m_weight ? m_weight->cssText() : "normal"; 193 } 194 195 String FontFace::stretch() const 196 { 197 return m_stretch ? m_stretch->cssText() : "normal"; 198 } 199 200 String FontFace::unicodeRange() const 201 { 202 return m_unicodeRange ? m_unicodeRange->cssText() : "U+0-10FFFF"; 203 } 204 205 String FontFace::variant() const 206 { 207 return m_variant ? m_variant->cssText() : "normal"; 208 } 209 210 String FontFace::featureSettings() const 211 { 212 return m_featureSettings ? m_featureSettings->cssText() : "normal"; 213 } 214 215 void FontFace::setStyle(const String& s, ExceptionState& exceptionState) 216 { 217 setPropertyFromString(s, CSSPropertyFontStyle, exceptionState); 218 } 219 220 void FontFace::setWeight(const String& s, ExceptionState& exceptionState) 221 { 222 setPropertyFromString(s, CSSPropertyFontWeight, exceptionState); 223 } 224 225 void FontFace::setStretch(const String& s, ExceptionState& exceptionState) 226 { 227 setPropertyFromString(s, CSSPropertyFontStretch, exceptionState); 228 } 229 230 void FontFace::setUnicodeRange(const String& s, ExceptionState& exceptionState) 231 { 232 setPropertyFromString(s, CSSPropertyUnicodeRange, exceptionState); 233 } 234 235 void FontFace::setVariant(const String& s, ExceptionState& exceptionState) 236 { 237 setPropertyFromString(s, CSSPropertyFontVariant, exceptionState); 238 } 239 240 void FontFace::setFeatureSettings(const String& s, ExceptionState& exceptionState) 241 { 242 setPropertyFromString(s, CSSPropertyWebkitFontFeatureSettings, exceptionState); 243 } 244 245 void FontFace::setPropertyFromString(const String& s, CSSPropertyID propertyID, ExceptionState& exceptionState) 246 { 247 RefPtr<CSSValue> value = parseCSSValue(s, propertyID); 248 if (!value || !setPropertyValue(value, propertyID)) 249 exceptionState.throwUninformativeAndGenericDOMException(SyntaxError); 250 } 251 252 bool FontFace::setPropertyFromStyle(const StylePropertySet* properties, CSSPropertyID propertyID) 253 { 254 return setPropertyValue(properties->getPropertyCSSValue(propertyID), propertyID); 255 } 256 257 bool FontFace::setPropertyValue(PassRefPtr<CSSValue> value, CSSPropertyID propertyID) 258 { 259 switch (propertyID) { 260 case CSSPropertyFontStyle: 261 m_style = value; 262 break; 263 case CSSPropertyFontWeight: 264 m_weight = value; 265 break; 266 case CSSPropertyFontStretch: 267 m_stretch = value; 268 break; 269 case CSSPropertyUnicodeRange: 270 if (value && !value->isValueList()) 271 return false; 272 m_unicodeRange = value; 273 break; 274 case CSSPropertyFontVariant: 275 m_variant = value; 276 break; 277 case CSSPropertyWebkitFontFeatureSettings: 278 m_featureSettings = value; 279 break; 280 default: 281 ASSERT_NOT_REACHED(); 282 return false; 283 } 284 return true; 285 } 286 287 bool FontFace::setFamilyValue(CSSValueList* familyList) 288 { 289 // The font-family descriptor has to have exactly one family name. 290 if (familyList->length() != 1) 291 return false; 292 293 CSSPrimitiveValue* familyValue = toCSSPrimitiveValue(familyList->itemWithoutBoundsCheck(0)); 294 AtomicString family; 295 if (familyValue->isString()) { 296 family = AtomicString(familyValue->getStringValue()); 297 } else if (familyValue->isValueID()) { 298 // We need to use the raw text for all the generic family types, since @font-face is a way of actually 299 // defining what font to use for those types. 300 switch (familyValue->getValueID()) { 301 case CSSValueSerif: 302 family = FontFamilyNames::webkit_serif; 303 break; 304 case CSSValueSansSerif: 305 family = FontFamilyNames::webkit_sans_serif; 306 break; 307 case CSSValueCursive: 308 family = FontFamilyNames::webkit_cursive; 309 break; 310 case CSSValueFantasy: 311 family = FontFamilyNames::webkit_fantasy; 312 break; 313 case CSSValueMonospace: 314 family = FontFamilyNames::webkit_monospace; 315 break; 316 case CSSValueWebkitPictograph: 317 family = FontFamilyNames::webkit_pictograph; 318 break; 319 default: 320 return false; 321 } 322 } 323 m_family = family; 324 return true; 325 } 326 327 String FontFace::status() const 328 { 329 switch (m_status) { 330 case Unloaded: 331 return "unloaded"; 332 case Loading: 333 return "loading"; 334 case Loaded: 335 return "loaded"; 336 case Error: 337 return "error"; 338 default: 339 ASSERT_NOT_REACHED(); 340 } 341 return emptyString(); 342 } 343 344 void FontFace::setLoadStatus(LoadStatus status) 345 { 346 m_status = status; 347 if (m_status == Loaded || m_status == Error) 348 resolveReadyPromises(); 349 } 350 351 void FontFace::load() 352 { 353 // FIXME: This does not load FontFace created by JavaScript, since m_cssFontFace is null. 354 if (m_status != Unloaded || !m_cssFontFace) 355 return; 356 357 FontDescription fontDescription; 358 FontFamily fontFamily; 359 fontFamily.setFamily(m_family); 360 fontDescription.setFamily(fontFamily); 361 fontDescription.setTraitsMask(static_cast<FontTraitsMask>(traitsMask())); 362 363 RefPtr<SimpleFontData> fontData = m_cssFontFace->getFontData(fontDescription); 364 if (fontData && fontData->customFontData()) 365 fontData->customFontData()->beginLoadIfNeeded(); 366 } 367 368 ScriptPromise FontFace::ready(ExecutionContext* context) 369 { 370 ScriptPromise promise = ScriptPromise::createPending(context); 371 OwnPtr<FontFaceReadyPromiseResolver> resolver = FontFaceReadyPromiseResolver::create(promise, context); 372 if (m_status == Loaded || m_status == Error) 373 resolver->resolve(this); 374 else 375 m_readyResolvers.append(resolver.release()); 376 return promise; 377 } 378 379 void FontFace::resolveReadyPromises() 380 { 381 for (size_t i = 0; i < m_readyResolvers.size(); i++) 382 m_readyResolvers[i]->resolve(this); 383 m_readyResolvers.clear(); 384 } 385 386 unsigned FontFace::traitsMask() const 387 { 388 unsigned traitsMask = 0; 389 390 if (m_style) { 391 if (!m_style->isPrimitiveValue()) 392 return 0; 393 394 switch (toCSSPrimitiveValue(m_style.get())->getValueID()) { 395 case CSSValueNormal: 396 traitsMask |= FontStyleNormalMask; 397 break; 398 case CSSValueItalic: 399 case CSSValueOblique: 400 traitsMask |= FontStyleItalicMask; 401 break; 402 default: 403 break; 404 } 405 } else { 406 traitsMask |= FontStyleNormalMask; 407 } 408 409 if (m_weight) { 410 if (!m_weight->isPrimitiveValue()) 411 return 0; 412 413 switch (toCSSPrimitiveValue(m_weight.get())->getValueID()) { 414 case CSSValueBold: 415 case CSSValue700: 416 traitsMask |= FontWeight700Mask; 417 break; 418 case CSSValueNormal: 419 case CSSValue400: 420 traitsMask |= FontWeight400Mask; 421 break; 422 case CSSValue900: 423 traitsMask |= FontWeight900Mask; 424 break; 425 case CSSValue800: 426 traitsMask |= FontWeight800Mask; 427 break; 428 case CSSValue600: 429 traitsMask |= FontWeight600Mask; 430 break; 431 case CSSValue500: 432 traitsMask |= FontWeight500Mask; 433 break; 434 case CSSValue300: 435 traitsMask |= FontWeight300Mask; 436 break; 437 case CSSValue200: 438 traitsMask |= FontWeight200Mask; 439 break; 440 case CSSValue100: 441 traitsMask |= FontWeight100Mask; 442 break; 443 default: 444 break; 445 } 446 } else { 447 traitsMask |= FontWeight400Mask; 448 } 449 450 if (RefPtr<CSSValue> fontVariant = m_variant) { 451 // font-variant descriptor can be a value list. 452 if (fontVariant->isPrimitiveValue()) { 453 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); 454 list->append(fontVariant); 455 fontVariant = list; 456 } else if (!fontVariant->isValueList()) { 457 return 0; 458 } 459 460 CSSValueList* variantList = toCSSValueList(fontVariant.get()); 461 unsigned numVariants = variantList->length(); 462 if (!numVariants) 463 return 0; 464 465 for (unsigned i = 0; i < numVariants; ++i) { 466 switch (toCSSPrimitiveValue(variantList->itemWithoutBoundsCheck(i))->getValueID()) { 467 case CSSValueNormal: 468 traitsMask |= FontVariantNormalMask; 469 break; 470 case CSSValueSmallCaps: 471 traitsMask |= FontVariantSmallCapsMask; 472 break; 473 default: 474 break; 475 } 476 } 477 } else { 478 traitsMask |= FontVariantNormalMask; 479 } 480 return traitsMask; 481 } 482 483 PassRefPtr<CSSFontFace> FontFace::createCSSFontFace(Document* document) 484 { 485 if (m_cssFontFace) 486 return m_cssFontFace; 487 488 RefPtr<CSSFontFace> cssFontFace = CSSFontFace::create(this); 489 m_cssFontFace = cssFontFace.get(); 490 491 // Each item in the src property's list is a single CSSFontFaceSource. Put them all into a CSSFontFace. 492 CSSValueList* srcList = toCSSValueList(m_src.get()); 493 int srcLength = srcList->length(); 494 495 bool foundSVGFont = false; 496 497 for (int i = 0; i < srcLength; i++) { 498 // An item in the list either specifies a string (local font name) or a URL (remote font to download). 499 CSSFontFaceSrcValue* item = toCSSFontFaceSrcValue(srcList->itemWithoutBoundsCheck(i)); 500 OwnPtr<CSSFontFaceSource> source; 501 502 #if ENABLE(SVG_FONTS) 503 foundSVGFont = item->isSVGFontFaceSrc() || item->svgFontFaceElement(); 504 #endif 505 if (!item->isLocal()) { 506 Settings* settings = document ? document->frame() ? document->frame()->settings() : 0 : 0; 507 bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled()); 508 if (allowDownloading && item->isSupportedFormat() && document) { 509 FontResource* fetched = item->fetch(document); 510 if (fetched) { 511 source = adoptPtr(new CSSFontFaceSource(item->resource(), fetched)); 512 #if ENABLE(SVG_FONTS) 513 if (foundSVGFont) 514 source->setHasExternalSVGFont(true); 515 #endif 516 } 517 } 518 } else { 519 source = adoptPtr(new CSSFontFaceSource(item->resource())); 520 } 521 522 if (source) { 523 #if ENABLE(SVG_FONTS) 524 source->setSVGFontFaceElement(item->svgFontFaceElement()); 525 #endif 526 cssFontFace->addSource(source.release()); 527 } 528 } 529 530 if (CSSValueList* rangeList = toCSSValueList(m_unicodeRange.get())) { 531 unsigned numRanges = rangeList->length(); 532 for (unsigned i = 0; i < numRanges; i++) { 533 CSSUnicodeRangeValue* range = toCSSUnicodeRangeValue(rangeList->itemWithoutBoundsCheck(i)); 534 cssFontFace->ranges().add(range->from(), range->to()); 535 } 536 } 537 return cssFontFace; 538 } 539 540 } // namespace WebCore 541