This source file includes following definitions.
- create
- resolve
- promise
- m_resolver
- parseCSSValue
- initFontFace
- create
- create
- create
- create
- style
- weight
- stretch
- unicodeRange
- variant
- featureSettings
- setStyle
- setWeight
- setStretch
- setUnicodeRange
- setVariant
- setFeatureSettings
- setPropertyFromString
- setPropertyFromStyle
- setPropertyValue
- setFamilyValue
- status
- setLoadStatus
- load
- loadWithCallback
- loadInternal
- resolveReadyPromises
- traits
- createCSSFontFace
- initCSSFontFace
- initCSSFontFace
- trace
- hadBlankText
#include "config.h"
#include "core/css/FontFace.h"
#include "CSSValueKeywords.h"
#include "FontFamilyNames.h"
#include "bindings/v8/Dictionary.h"
#include "bindings/v8/ExceptionState.h"
#include "bindings/v8/ScriptPromiseResolver.h"
#include "bindings/v8/ScriptScope.h"
#include "bindings/v8/ScriptState.h"
#include "core/css/BinaryDataFontFaceSource.h"
#include "core/css/CSSFontFace.h"
#include "core/css/CSSFontFaceSrcValue.h"
#include "core/css/CSSFontSelector.h"
#include "core/css/CSSPrimitiveValue.h"
#include "core/css/CSSUnicodeRangeValue.h"
#include "core/css/CSSValueList.h"
#include "core/css/LocalFontFaceSource.h"
#include "core/css/RemoteFontFaceSource.h"
#include "core/css/StylePropertySet.h"
#include "core/css/StyleRule.h"
#include "core/css/parser/BisonCSSParser.h"
#include "core/dom/DOMError.h"
#include "core/dom/Document.h"
#include "core/dom/ExceptionCode.h"
#include "core/dom/StyleEngine.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/Settings.h"
#include "core/svg/SVGFontFaceElement.h"
#include "core/svg/SVGFontFaceSource.h"
#include "core/svg/SVGRemoteFontFaceSource.h"
#include "platform/SharedBuffer.h"
#include "platform/fonts/FontDescription.h"
namespace WebCore {
class FontFaceReadyPromiseResolver {
public:
static PassOwnPtr<FontFaceReadyPromiseResolver> create(ExecutionContext* context)
{
return adoptPtr(new FontFaceReadyPromiseResolver(context));
}
void resolve(PassRefPtr<FontFace> fontFace)
{
ScriptScope scope(m_scriptState);
switch (fontFace->loadStatus()) {
case FontFace::Loaded:
m_resolver->resolve(fontFace);
break;
case FontFace::Error:
m_resolver->reject(fontFace->error());
break;
default:
ASSERT_NOT_REACHED();
}
}
ScriptPromise promise() { return m_resolver->promise(); }
private:
FontFaceReadyPromiseResolver(ExecutionContext* context)
: m_scriptState(ScriptState::current())
, m_resolver(ScriptPromiseResolver::create(context))
{ }
ScriptState* m_scriptState;
RefPtr<ScriptPromiseResolver> m_resolver;
};
static PassRefPtrWillBeRawPtr<CSSValue> parseCSSValue(const Document* document, const String& s, CSSPropertyID propertyID)
{
if (s.isEmpty())
return nullptr;
RefPtrWillBeRawPtr<MutableStylePropertySet> parsedStyle = MutableStylePropertySet::create();
BisonCSSParser::parseValue(parsedStyle.get(), propertyID, s, true, *document);
return parsedStyle->getPropertyCSSValue(propertyID);
}
static bool initFontFace(FontFace* fontFace, ExecutionContext* context, const AtomicString& family, const Dictionary& descriptors, ExceptionState& exceptionState)
{
fontFace->setFamily(context, family, exceptionState);
if (exceptionState.hadException())
return false;
String value;
if (descriptors.get("style", value)) {
fontFace->setStyle(context, value, exceptionState);
if (exceptionState.hadException())
return false;
}
if (descriptors.get("weight", value)) {
fontFace->setWeight(context, value, exceptionState);
if (exceptionState.hadException())
return false;
}
if (descriptors.get("stretch", value)) {
fontFace->setStretch(context, value, exceptionState);
if (exceptionState.hadException())
return false;
}
if (descriptors.get("unicodeRange", value)) {
fontFace->setUnicodeRange(context, value, exceptionState);
if (exceptionState.hadException())
return false;
}
if (descriptors.get("variant", value)) {
fontFace->setVariant(context, value, exceptionState);
if (exceptionState.hadException())
return false;
}
if (descriptors.get("featureSettings", value)) {
fontFace->setFeatureSettings(context, value, exceptionState);
if (exceptionState.hadException())
return false;
}
return true;
}
PassRefPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, const String& source, const Dictionary& descriptors, ExceptionState& exceptionState)
{
RefPtrWillBeRawPtr<CSSValue> src = parseCSSValue(toDocument(context), source, CSSPropertySrc);
if (!src || !src->isValueList()) {
exceptionState.throwDOMException(SyntaxError, "The source provided ('" + source + "') could not be parsed as a value list.");
return nullptr;
}
RefPtr<FontFace> fontFace = adoptRefWillBeRefCountedGarbageCollected<FontFace>(new FontFace());
if (initFontFace(fontFace.get(), context, family, descriptors, exceptionState))
fontFace->initCSSFontFace(toDocument(context), src);
return fontFace.release();
}
PassRefPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, PassRefPtr<ArrayBuffer> source, const Dictionary& descriptors, ExceptionState& exceptionState)
{
RefPtr<FontFace> fontFace = adoptRefWillBeRefCountedGarbageCollected<FontFace>(new FontFace());
if (initFontFace(fontFace.get(), context, family, descriptors, exceptionState))
fontFace->initCSSFontFace(static_cast<const unsigned char*>(source->data()), source->byteLength());
return fontFace.release();
}
PassRefPtr<FontFace> FontFace::create(ExecutionContext* context, const AtomicString& family, PassRefPtr<ArrayBufferView> source, const Dictionary& descriptors, ExceptionState& exceptionState)
{
RefPtr<FontFace> fontFace = adoptRefWillBeRefCountedGarbageCollected<FontFace>(new FontFace());
if (initFontFace(fontFace.get(), context, family, descriptors, exceptionState))
fontFace->initCSSFontFace(static_cast<const unsigned char*>(source->baseAddress()), source->byteLength());
return fontFace.release();
}
PassRefPtr<FontFace> FontFace::create(Document* document, const StyleRuleFontFace* fontFaceRule)
{
const StylePropertySet& properties = fontFaceRule->properties();
RefPtrWillBeRawPtr<CSSValue> family = properties.getPropertyCSSValue(CSSPropertyFontFamily);
if (!family || !family->isValueList())
return nullptr;
RefPtrWillBeRawPtr<CSSValue> src = properties.getPropertyCSSValue(CSSPropertySrc);
if (!src || !src->isValueList())
return nullptr;
RefPtr<FontFace> fontFace = adoptRefWillBeRefCountedGarbageCollected<FontFace>(new FontFace());
if (fontFace->setFamilyValue(toCSSValueList(family.get()))
&& fontFace->setPropertyFromStyle(properties, CSSPropertyFontStyle)
&& fontFace->setPropertyFromStyle(properties, CSSPropertyFontWeight)
&& fontFace->setPropertyFromStyle(properties, CSSPropertyFontStretch)
&& fontFace->setPropertyFromStyle(properties, CSSPropertyUnicodeRange)
&& fontFace->setPropertyFromStyle(properties, CSSPropertyFontVariant)
&& fontFace->setPropertyFromStyle(properties, CSSPropertyWebkitFontFeatureSettings)
&& !fontFace->family().isEmpty()
&& fontFace->traits().mask()) {
fontFace->initCSSFontFace(document, src);
return fontFace.release();
}
return nullptr;
}
FontFace::FontFace()
: m_status(Unloaded)
{
}
FontFace::~FontFace()
{
}
String FontFace::style() const
{
return m_style ? m_style->cssText() : "normal";
}
String FontFace::weight() const
{
return m_weight ? m_weight->cssText() : "normal";
}
String FontFace::stretch() const
{
return m_stretch ? m_stretch->cssText() : "normal";
}
String FontFace::unicodeRange() const
{
return m_unicodeRange ? m_unicodeRange->cssText() : "U+0-10FFFF";
}
String FontFace::variant() const
{
return m_variant ? m_variant->cssText() : "normal";
}
String FontFace::featureSettings() const
{
return m_featureSettings ? m_featureSettings->cssText() : "normal";
}
void FontFace::setStyle(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
{
setPropertyFromString(toDocument(context), s, CSSPropertyFontStyle, exceptionState);
}
void FontFace::setWeight(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
{
setPropertyFromString(toDocument(context), s, CSSPropertyFontWeight, exceptionState);
}
void FontFace::setStretch(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
{
setPropertyFromString(toDocument(context), s, CSSPropertyFontStretch, exceptionState);
}
void FontFace::setUnicodeRange(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
{
setPropertyFromString(toDocument(context), s, CSSPropertyUnicodeRange, exceptionState);
}
void FontFace::setVariant(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
{
setPropertyFromString(toDocument(context), s, CSSPropertyFontVariant, exceptionState);
}
void FontFace::setFeatureSettings(ExecutionContext* context, const String& s, ExceptionState& exceptionState)
{
setPropertyFromString(toDocument(context), s, CSSPropertyWebkitFontFeatureSettings, exceptionState);
}
void FontFace::setPropertyFromString(const Document* document, const String& s, CSSPropertyID propertyID, ExceptionState& exceptionState)
{
RefPtrWillBeRawPtr<CSSValue> value = parseCSSValue(document, s, propertyID);
if (!value || !setPropertyValue(value, propertyID))
exceptionState.throwDOMException(SyntaxError, "Failed to set '" + s + "' as a property value.");
}
bool FontFace::setPropertyFromStyle(const StylePropertySet& properties, CSSPropertyID propertyID)
{
return setPropertyValue(properties.getPropertyCSSValue(propertyID), propertyID);
}
bool FontFace::setPropertyValue(PassRefPtrWillBeRawPtr<CSSValue> value, CSSPropertyID propertyID)
{
switch (propertyID) {
case CSSPropertyFontStyle:
m_style = value;
break;
case CSSPropertyFontWeight:
m_weight = value;
break;
case CSSPropertyFontStretch:
m_stretch = value;
break;
case CSSPropertyUnicodeRange:
if (value && !value->isValueList())
return false;
m_unicodeRange = value;
break;
case CSSPropertyFontVariant:
m_variant = value;
break;
case CSSPropertyWebkitFontFeatureSettings:
m_featureSettings = value;
break;
default:
ASSERT_NOT_REACHED();
return false;
}
return true;
}
bool FontFace::setFamilyValue(CSSValueList* familyList)
{
if (familyList->length() != 1)
return false;
CSSPrimitiveValue* familyValue = toCSSPrimitiveValue(familyList->itemWithoutBoundsCheck(0));
AtomicString family;
if (familyValue->isString()) {
family = AtomicString(familyValue->getStringValue());
} else if (familyValue->isValueID()) {
switch (familyValue->getValueID()) {
case CSSValueSerif:
family = FontFamilyNames::webkit_serif;
break;
case CSSValueSansSerif:
family = FontFamilyNames::webkit_sans_serif;
break;
case CSSValueCursive:
family = FontFamilyNames::webkit_cursive;
break;
case CSSValueFantasy:
family = FontFamilyNames::webkit_fantasy;
break;
case CSSValueMonospace:
family = FontFamilyNames::webkit_monospace;
break;
case CSSValueWebkitPictograph:
family = FontFamilyNames::webkit_pictograph;
break;
default:
return false;
}
}
m_family = family;
return true;
}
String FontFace::status() const
{
switch (m_status) {
case Unloaded:
return "unloaded";
case Loading:
return "loading";
case Loaded:
return "loaded";
case Error:
return "error";
default:
ASSERT_NOT_REACHED();
}
return emptyString();
}
void FontFace::setLoadStatus(LoadStatus status)
{
m_status = status;
if (m_status == Error)
m_error = DOMError::create(NetworkError);
if (m_status == Loaded || m_status == Error) {
resolveReadyPromises();
Vector<RefPtr<LoadFontCallback> > callbacks;
m_callbacks.swap(callbacks);
for (size_t i = 0; i < callbacks.size(); ++i) {
if (m_status == Loaded)
callbacks[i]->notifyLoaded(this);
else
callbacks[i]->notifyError(this);
}
}
}
ScriptPromise FontFace::load(ExecutionContext* context)
{
OwnPtr<FontFaceReadyPromiseResolver> resolver = FontFaceReadyPromiseResolver::create(context);
ScriptPromise promise = resolver->promise();
if (m_status == Loaded || m_status == Error)
resolver->resolve(this);
else
m_readyResolvers.append(resolver.release());
loadInternal(context);
return promise;
}
void FontFace::loadWithCallback(PassRefPtr<LoadFontCallback> callback, ExecutionContext* context)
{
loadInternal(context);
if (m_status == Loaded)
callback->notifyLoaded(this);
else if (m_status == Error)
callback->notifyError(this);
else
m_callbacks.append(callback);
}
void FontFace::loadInternal(ExecutionContext* context)
{
if (m_status != Unloaded)
return;
FontDescription fontDescription;
FontFamily fontFamily;
fontFamily.setFamily(m_family);
fontDescription.setFamily(fontFamily);
fontDescription.setTraits(traits());
CSSFontSelector* fontSelector = toDocument(context)->styleEngine()->fontSelector();
m_cssFontFace->load(fontDescription, fontSelector);
fontSelector->loadPendingFonts();
}
void FontFace::resolveReadyPromises()
{
for (size_t i = 0; i < m_readyResolvers.size(); i++)
m_readyResolvers[i]->resolve(this);
m_readyResolvers.clear();
}
FontTraits FontFace::traits() const
{
FontStyle style = FontStyleNormal;
if (m_style) {
if (!m_style->isPrimitiveValue())
return 0;
switch (toCSSPrimitiveValue(m_style.get())->getValueID()) {
case CSSValueNormal:
style = FontStyleNormal;
break;
case CSSValueItalic:
case CSSValueOblique:
style = FontStyleItalic;
break;
default:
break;
}
}
FontWeight weight = FontWeight400;
if (m_weight) {
if (!m_weight->isPrimitiveValue())
return 0;
switch (toCSSPrimitiveValue(m_weight.get())->getValueID()) {
case CSSValueBold:
case CSSValue700:
weight = FontWeight700;
break;
case CSSValueNormal:
case CSSValue400:
weight = FontWeight400;
break;
case CSSValue900:
weight = FontWeight900;
break;
case CSSValue800:
weight = FontWeight800;
break;
case CSSValue600:
weight = FontWeight600;
break;
case CSSValue500:
weight = FontWeight500;
break;
case CSSValue300:
weight = FontWeight300;
break;
case CSSValue200:
weight = FontWeight200;
break;
case CSSValueLighter:
case CSSValue100:
weight = FontWeight100;
break;
default:
ASSERT_NOT_REACHED();
break;
}
}
FontVariant variant = FontVariantNormal;
if (RefPtrWillBeRawPtr<CSSValue> fontVariant = m_variant) {
if (fontVariant->isPrimitiveValue()) {
RefPtrWillBeRawPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
list->append(fontVariant);
fontVariant = list;
} else if (!fontVariant->isValueList()) {
return 0;
}
CSSValueList* variantList = toCSSValueList(fontVariant.get());
unsigned numVariants = variantList->length();
if (!numVariants)
return 0;
for (unsigned i = 0; i < numVariants; ++i) {
switch (toCSSPrimitiveValue(variantList->itemWithoutBoundsCheck(i))->getValueID()) {
case CSSValueNormal:
variant = FontVariantNormal;
break;
case CSSValueSmallCaps:
variant = FontVariantSmallCaps;
break;
default:
break;
}
}
}
return FontTraits(style, variant, weight, FontStretchNormal);
}
static PassOwnPtr<CSSFontFace> createCSSFontFace(FontFace* fontFace, CSSValue* unicodeRange)
{
Vector<CSSFontFace::UnicodeRange> ranges;
if (CSSValueList* rangeList = toCSSValueList(unicodeRange)) {
unsigned numRanges = rangeList->length();
for (unsigned i = 0; i < numRanges; i++) {
CSSUnicodeRangeValue* range = toCSSUnicodeRangeValue(rangeList->itemWithoutBoundsCheck(i));
ranges.append(CSSFontFace::UnicodeRange(range->from(), range->to()));
}
}
return adoptPtr(new CSSFontFace(fontFace, ranges));
}
void FontFace::initCSSFontFace(Document* document, PassRefPtrWillBeRawPtr<CSSValue> src)
{
m_cssFontFace = createCSSFontFace(this, m_unicodeRange.get());
CSSValueList* srcList = toCSSValueList(src.get());
int srcLength = srcList->length();
bool foundSVGFont = false;
for (int i = 0; i < srcLength; i++) {
CSSFontFaceSrcValue* item = toCSSFontFaceSrcValue(srcList->itemWithoutBoundsCheck(i));
OwnPtr<CSSFontFaceSource> source;
#if ENABLE(SVG_FONTS)
foundSVGFont = item->isSVGFontFaceSrc() || item->svgFontFaceElement();
#endif
if (!item->isLocal()) {
Settings* settings = document ? document->frame() ? document->frame()->settings() : 0 : 0;
bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled());
if (allowDownloading && item->isSupportedFormat() && document) {
FontResource* fetched = item->fetch(document);
if (fetched) {
#if ENABLE(SVG_FONTS)
if (foundSVGFont) {
source = adoptPtr(new SVGRemoteFontFaceSource(item->resource(), fetched));
} else
#endif
{
source = adoptPtr(new RemoteFontFaceSource(fetched));
}
}
}
} else {
#if ENABLE(SVG_FONTS)
if (item->svgFontFaceElement()) {
source = adoptPtr(new SVGFontFaceSource(item->svgFontFaceElement()));
} else
#endif
{
source = adoptPtr(new LocalFontFaceSource(item->resource()));
}
}
if (source)
m_cssFontFace->addSource(source.release());
}
}
void FontFace::initCSSFontFace(const unsigned char* data, unsigned size)
{
m_cssFontFace = createCSSFontFace(this, m_unicodeRange.get());
RefPtr<SharedBuffer> buffer = SharedBuffer::create(data, size);
OwnPtr<BinaryDataFontFaceSource> source = adoptPtr(new BinaryDataFontFaceSource(buffer.get()));
if (source->isValid()) {
m_status = Loaded;
} else {
m_status = Error;
m_error = DOMError::create(SyntaxError, "Invalid font data in ArrayBuffer.");
}
m_cssFontFace->addSource(source.release());
}
void FontFace::trace(Visitor* visitor)
{
visitor->trace(m_src);
visitor->trace(m_style);
visitor->trace(m_weight);
visitor->trace(m_stretch);
visitor->trace(m_unicodeRange);
visitor->trace(m_variant);
visitor->trace(m_featureSettings);
visitor->trace(m_error);
}
bool FontFace::hadBlankText() const
{
return m_cssFontFace->hadBlankText();
}
}