2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "core/css/FontFace.h"
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"
65 static PassRefPtrWillBeRawPtr<CSSValue> parseCSSValue(const Document* document, const String& s, CSSPropertyID propertyID)
67 CSSParserContext context(*document, UseCounter::getFrom(document));
68 return CSSParser::parseSingleValue(propertyID, s, context);
71 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, const String& source, const FontFaceDescriptors& descriptors)
73 RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace(context, family, descriptors));
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."));
79 fontFace->initCSSFontFace(toDocument(context), src);
80 return fontFace.release();
83 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, PassRefPtr<ArrayBuffer> source, const FontFaceDescriptors& descriptors)
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();
90 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, PassRefPtr<ArrayBufferView> source, const FontFaceDescriptors& descriptors)
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();
97 PassRefPtrWillBeRawPtr<FontFace> FontFace::create(Document* document, const StyleRuleFontFace* fontFaceRule)
99 const StylePropertySet& properties = fontFaceRule->properties();
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())
105 RefPtrWillBeRawPtr<CSSValue> src = properties.getPropertyCSSValue(CSSPropertySrc);
106 if (!src || !src->isValueList())
109 RefPtrWillBeRawPtr<FontFace> fontFace = adoptRefWillBeNoop(new FontFace(document));
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();
126 FontFace::FontFace(ExecutionContext* context)
127 : ActiveDOMObject(context)
133 FontFace::FontFace(ExecutionContext* context, const AtomicString& family, const FontFaceDescriptors& descriptors)
134 : ActiveDOMObject(context)
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);
149 FontFace::~FontFace()
153 String FontFace::style() const
155 return m_style ? m_style->cssText() : "normal";
158 String FontFace::weight() const
160 return m_weight ? m_weight->cssText() : "normal";
163 String FontFace::stretch() const
165 return m_stretch ? m_stretch->cssText() : "normal";
168 String FontFace::unicodeRange() const
170 return m_unicodeRange ? m_unicodeRange->cssText() : "U+0-10FFFF";
173 String FontFace::variant() const
175 return m_variant ? m_variant->cssText() : "normal";
178 String FontFace::featureSettings() const
180 return m_featureSettings ? m_featureSettings->cssText() : "normal";
183 void FontFace::setStyle(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
185 setPropertyFromString(toDocument(context), s, CSSPropertyFontStyle, &exceptionState);
188 void FontFace::setWeight(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
190 setPropertyFromString(toDocument(context), s, CSSPropertyFontWeight, &exceptionState);
193 void FontFace::setStretch(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
195 setPropertyFromString(toDocument(context), s, CSSPropertyFontStretch, &exceptionState);
198 void FontFace::setUnicodeRange(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
200 setPropertyFromString(toDocument(context), s, CSSPropertyUnicodeRange, &exceptionState);
203 void FontFace::setVariant(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
205 setPropertyFromString(toDocument(context), s, CSSPropertyFontVariant, &exceptionState);
208 void FontFace::setFeatureSettings(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
210 setPropertyFromString(toDocument(context), s, CSSPropertyWebkitFontFeatureSettings, &exceptionState);
213 void FontFace::setPropertyFromString(const Document* document, const String& s, CSSPropertyID propertyID, ExceptionState* exceptionState)
215 RefPtrWillBeRawPtr<CSSValue> value = parseCSSValue(document, s, propertyID);
216 if (value && setPropertyValue(value, propertyID))
219 String message = "Failed to set '" + s + "' as a property value.";
221 exceptionState->throwDOMException(SyntaxError, message);
223 setError(DOMException::create(SyntaxError, message));
226 bool FontFace::setPropertyFromStyle(const StylePropertySet& properties, CSSPropertyID propertyID)
228 return setPropertyValue(properties.getPropertyCSSValue(propertyID), propertyID);
231 bool FontFace::setPropertyValue(PassRefPtrWillBeRawPtr<CSSValue> value, CSSPropertyID propertyID)
233 switch (propertyID) {
234 case CSSPropertyFontStyle:
237 case CSSPropertyFontWeight:
240 case CSSPropertyFontStretch:
243 case CSSPropertyUnicodeRange:
244 if (value && !value->isValueList())
246 m_unicodeRange = value;
248 case CSSPropertyFontVariant:
251 case CSSPropertyWebkitFontFeatureSettings:
252 m_featureSettings = value;
255 ASSERT_NOT_REACHED();
261 bool FontFace::setFamilyValue(CSSValueList* familyList)
263 // The font-family descriptor has to have exactly one family name.
264 if (familyList->length() != 1)
267 CSSPrimitiveValue* familyValue = toCSSPrimitiveValue(familyList->item(0));
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()) {
276 family = FontFamilyNames::webkit_serif;
278 case CSSValueSansSerif:
279 family = FontFamilyNames::webkit_sans_serif;
281 case CSSValueCursive:
282 family = FontFamilyNames::webkit_cursive;
284 case CSSValueFantasy:
285 family = FontFamilyNames::webkit_fantasy;
287 case CSSValueMonospace:
288 family = FontFamilyNames::webkit_monospace;
290 case CSSValueWebkitPictograph:
291 family = FontFamilyNames::webkit_pictograph;
301 String FontFace::status() const
313 ASSERT_NOT_REACHED();
315 return emptyString();
318 void FontFace::setLoadStatus(LoadStatus status)
321 ASSERT(m_status != Error || m_error);
323 if (m_status == Loaded || m_status == Error) {
324 if (m_loadedProperty) {
325 if (m_status == Loaded)
326 m_loadedProperty->resolve(this);
328 m_loadedProperty->reject(m_error.get());
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);
337 callbacks[i]->notifyError(this);
342 void FontFace::setError(PassRefPtrWillBeRawPtr<DOMException> error)
345 m_error = error ? error : DOMException::create(NetworkError);
346 setLoadStatus(Error);
349 ScriptPromise FontFace::fontStatusPromise(ScriptState* scriptState)
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());
358 return m_loadedProperty->promise(scriptState->world());
361 ScriptPromise FontFace::load(ScriptState* scriptState)
363 loadInternal(scriptState->executionContext());
364 return fontStatusPromise(scriptState);
367 void FontFace::loadWithCallback(PassRefPtrWillBeRawPtr<LoadFontCallback> callback, ExecutionContext* context)
369 loadInternal(context);
370 if (m_status == Loaded)
371 callback->notifyLoaded(this);
372 else if (m_status == Error)
373 callback->notifyError(this);
375 m_callbacks.append(callback);
378 void FontFace::loadInternal(ExecutionContext* context)
380 if (m_status != Unloaded)
383 m_cssFontFace->load();
384 toDocument(context)->styleEngine()->fontSelector()->fontLoader()->loadPendingFonts();
387 FontTraits FontFace::traits() const
389 FontStyle style = FontStyleNormal;
391 if (!m_style->isPrimitiveValue())
394 switch (toCSSPrimitiveValue(m_style.get())->getValueID()) {
396 style = FontStyleNormal;
399 case CSSValueOblique:
400 style = FontStyleItalic;
407 FontWeight weight = FontWeight400;
409 if (!m_weight->isPrimitiveValue())
412 switch (toCSSPrimitiveValue(m_weight.get())->getValueID()) {
415 weight = FontWeight700;
419 weight = FontWeight400;
422 weight = FontWeight900;
425 weight = FontWeight800;
428 weight = FontWeight600;
431 weight = FontWeight500;
434 weight = FontWeight300;
437 weight = FontWeight200;
440 weight = FontWeight100;
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:
448 ASSERT_NOT_REACHED();
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);
460 } else if (!fontVariant->isValueList()) {
464 CSSValueList* variantList = toCSSValueList(fontVariant.get());
465 unsigned numVariants = variantList->length();
469 for (unsigned i = 0; i < numVariants; ++i) {
470 switch (toCSSPrimitiveValue(variantList->item(i))->getValueID()) {
472 variant = FontVariantNormal;
474 case CSSValueSmallCaps:
475 variant = FontVariantSmallCaps;
483 return FontTraits(style, variant, weight, FontStretchNormal);
486 static PassOwnPtrWillBeRawPtr<CSSFontFace> createCSSFontFace(FontFace* fontFace, CSSValue* unicodeRange)
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()));
497 return adoptPtrWillBeNoop(new CSSFontFace(fontFace, ranges));
500 void FontFace::initCSSFontFace(Document* document, PassRefPtrWillBeRawPtr<CSSValue> src)
502 m_cssFontFace = createCSSFontFace(this, m_unicodeRange.get());
506 // Each item in the src property's list is a single CSSFontFaceSource. Put them all into a CSSFontFace.
508 ASSERT(src->isValueList());
509 CSSValueList* srcList = toCSSValueList(src.get());
510 int srcLength = srcList->length();
512 bool foundSVGFont = false;
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;
519 #if ENABLE(SVG_FONTS)
520 foundSVGFont = item->isSVGFontFaceSrc() || item->svgFontFaceElement();
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);
528 FontLoader* fontLoader = document->styleEngine()->fontSelector()->fontLoader();
530 #if ENABLE(SVG_FONTS)
532 source = adoptPtrWillBeNoop(new SVGRemoteFontFaceSource(item->resource(), fetched, fontLoader));
536 source = adoptPtrWillBeNoop(new RemoteFontFaceSource(fetched, fontLoader));
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()));
552 source = adoptPtrWillBeNoop(new LocalFontFaceSource(item->resource()));
557 m_cssFontFace->addSource(source.release());
561 void FontFace::initCSSFontFace(const unsigned char* data, unsigned size)
563 m_cssFontFace = createCSSFontFace(this, m_unicodeRange.get());
567 RefPtr<SharedBuffer> buffer = SharedBuffer::create(data, size);
568 OwnPtrWillBeRawPtr<BinaryDataFontFaceSource> source = adoptPtrWillBeNoop(new BinaryDataFontFaceSource(buffer.get()));
569 if (source->isValid())
570 setLoadStatus(Loaded);
572 setError(DOMException::create(SyntaxError, "Invalid font data in ArrayBuffer."));
573 m_cssFontFace->addSource(source.release());
576 void FontFace::trace(Visitor* visitor)
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);
591 bool FontFace::hadBlankText() const
593 return m_cssFontFace->hadBlankText();
596 bool FontFace::hasPendingActivity() const
598 return m_status == Loading && executionContext() && !executionContext()->activeDOMObjectsAreStopped();