| Index: Source/core/css/parser/CSSPropertyParser.cpp
|
| diff --git a/Source/core/css/parser/CSSPropertyParser.cpp b/Source/core/css/parser/CSSPropertyParser.cpp
|
| index 2344e5bd7981234feaa353bd3d811bc36d9c2bb2..398fc32aa9145d8d53f1ecdfafc86f894af3a645 100644
|
| --- a/Source/core/css/parser/CSSPropertyParser.cpp
|
| +++ b/Source/core/css/parser/CSSPropertyParser.cpp
|
| @@ -7,10 +7,14 @@
|
|
|
| #include "core/StylePropertyShorthand.h"
|
| #include "core/css/CSSCalculationValue.h"
|
| +#include "core/css/CSSFontFaceSrcValue.h"
|
| +#include "core/css/CSSFontFeatureValue.h"
|
| +#include "core/css/CSSUnicodeRangeValue.h"
|
| #include "core/css/CSSValuePool.h"
|
| #include "core/css/parser/CSSParserFastPaths.h"
|
| #include "core/css/parser/CSSParserValues.h"
|
| #include "core/frame/UseCounter.h"
|
| +#include "wtf/text/StringBuilder.h"
|
|
|
| namespace blink {
|
|
|
| @@ -91,6 +95,42 @@ static PassRefPtrWillBeRawPtr<CSSPrimitiveValue> consumeString(CSSParserTokenRan
|
| return cssValuePool().createValue(range.consumeIncludingWhitespace().value(), CSSPrimitiveValue::UnitType::String);
|
| }
|
|
|
| +static String consumeUrl(CSSParserTokenRange& range)
|
| +{
|
| + const CSSParserToken& token = range.peek();
|
| + if (token.type() == UrlToken) {
|
| + range.consumeIncludingWhitespace();
|
| + return token.value();
|
| + }
|
| + if (token.functionId() == CSSValueUrl) {
|
| + CSSParserTokenRange urlRange = range;
|
| + CSSParserTokenRange urlArgs = urlRange.consumeBlock();
|
| + const CSSParserToken& next = urlArgs.consumeIncludingWhitespace();
|
| + if (next.type() == BadStringToken || !urlArgs.atEnd())
|
| + return String();
|
| + ASSERT(next.type() == StringToken);
|
| + range = urlRange;
|
| + range.consumeWhitespace();
|
| + return next.value();
|
| + }
|
| +
|
| + return String();
|
| +}
|
| +
|
| +static CSSParserTokenRange consumeFunction(CSSParserTokenRange& range)
|
| +{
|
| + ASSERT(range.peek().type() == FunctionToken);
|
| + CSSParserTokenRange contents = range.consumeBlock();
|
| + range.consumeWhitespace();
|
| + contents.consumeWhitespace();
|
| + return contents;
|
| +}
|
| +
|
| +static inline bool isCSSWideKeyword(const CSSValueID& id)
|
| +{
|
| + return id == CSSValueInitial || id == CSSValueInherit || id == CSSValueUnset || id == CSSValueDefault;
|
| +}
|
| +
|
| // Methods for consuming non-shorthand properties starts here.
|
| static PassRefPtrWillBeRawPtr<CSSValue> consumeWillChange(CSSParserTokenRange& range)
|
| {
|
| @@ -142,6 +182,51 @@ static PassRefPtrWillBeRawPtr<CSSValue> consumeWillChange(CSSParserTokenRange& r
|
| return values.release();
|
| }
|
|
|
| +static PassRefPtrWillBeRawPtr<CSSFontFeatureValue> consumeFontFeatureTag(CSSParserTokenRange& range)
|
| +{
|
| + // Feature tag name consists of 4-letter characters.
|
| + static const unsigned tagNameLength = 4;
|
| +
|
| + const CSSParserToken& token = range.consumeIncludingWhitespace();
|
| + // Feature tag name comes first
|
| + if (token.type() != StringToken)
|
| + return nullptr;
|
| + if (token.value().length() != tagNameLength)
|
| + return nullptr;
|
| + AtomicString tag = token.value();
|
| + for (unsigned i = 0; i < tagNameLength; ++i) {
|
| + // Limits the range of characters to 0x20-0x7E, following the tag name rules defiend in the OpenType specification.
|
| + UChar character = tag[i];
|
| + if (character < 0x20 || character > 0x7E)
|
| + return nullptr;
|
| + }
|
| +
|
| + int tagValue = 1;
|
| + // Feature tag values could follow: <integer> | on | off
|
| + if (range.peek().type() == NumberToken && range.peek().numericValueType() == IntegerValueType && range.peek().numericValue() >= 0) {
|
| + tagValue = clampTo<int>(range.consumeIncludingWhitespace().numericValue());
|
| + if (tagValue < 0)
|
| + return nullptr;
|
| + } else if (range.peek().id() == CSSValueOn || range.peek().id() == CSSValueOff) {
|
| + tagValue = range.consumeIncludingWhitespace().id() == CSSValueOn;
|
| + }
|
| + return CSSFontFeatureValue::create(tag, tagValue);
|
| +}
|
| +
|
| +static PassRefPtrWillBeRawPtr<CSSValue> consumeFontFeatureSettings(CSSParserTokenRange& range)
|
| +{
|
| + if (range.peek().id() == CSSValueNormal)
|
| + return consumeIdent(range);
|
| + RefPtrWillBeRawPtr<CSSValueList> settings = CSSValueList::createCommaSeparated();
|
| + do {
|
| + RefPtrWillBeRawPtr<CSSFontFeatureValue> fontFeatureValue = consumeFontFeatureTag(range);
|
| + if (!fontFeatureValue)
|
| + return nullptr;
|
| + settings->append(fontFeatureValue);
|
| + } while (consumeCommaIncludingWhitespace(range));
|
| + return settings.release();
|
| +}
|
| +
|
| static PassRefPtrWillBeRawPtr<CSSPrimitiveValue> consumePage(CSSParserTokenRange& range)
|
| {
|
| if (range.peek().id() == CSSValueAuto)
|
| @@ -149,7 +234,6 @@ static PassRefPtrWillBeRawPtr<CSSPrimitiveValue> consumePage(CSSParserTokenRange
|
| return consumeCustomIdent(range);
|
| }
|
|
|
| -// [ <string> <string> ]+ | none
|
| static PassRefPtrWillBeRawPtr<CSSValue> consumeQuotes(CSSParserTokenRange& range)
|
| {
|
| if (range.peek().id() == CSSValueNone)
|
| @@ -218,6 +302,48 @@ static PassRefPtrWillBeRawPtr<CSSValue> consumeFontVariantLigatures(CSSParserTok
|
| return ligatureValues.release();
|
| }
|
|
|
| +PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::consumeFontVariant()
|
| +{
|
| + if (m_ruleType != StyleRule::FontFace) {
|
| + if (m_range.peek().id() != CSSValueNormal && m_range.peek().id() != CSSValueSmallCaps)
|
| + return nullptr;
|
| + return consumeIdent(m_range);
|
| + }
|
| + RefPtrWillBeRawPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
|
| + do {
|
| + if (m_range.peek().id() == CSSValueAll) {
|
| + // FIXME: CSSPropertyParser::parseFontVariant() implements
|
| + // the old css3 draft:
|
| + // http://www.w3.org/TR/2002/WD-css3-webfonts-20020802/#font-variant
|
| + // 'all' is only allowed in @font-face and with no other values.
|
| + if (values->length())
|
| + return nullptr;
|
| + return consumeIdent(m_range);
|
| + }
|
| + if (m_range.peek().id() == CSSValueNormal || m_range.peek().id() == CSSValueSmallCaps)
|
| + values->append(consumeIdent(m_range));
|
| + } while (consumeCommaIncludingWhitespace(m_range));
|
| +
|
| + if (values->length())
|
| + return values.release();
|
| +
|
| + return nullptr;
|
| +}
|
| +
|
| +static PassRefPtrWillBeRawPtr<CSSValue> consumeFontWeight(CSSParserTokenRange& range)
|
| +{
|
| + const CSSParserToken& token = range.peek();
|
| + if (token.id() >= CSSValueNormal && token.id() <= CSSValueLighter)
|
| + return consumeIdent(range);
|
| + if (token.type() != NumberToken || token.numericValueType() != IntegerValueType)
|
| + return nullptr;
|
| + int weight = static_cast<int>(token.numericValue());
|
| + if ((weight % 100) || weight < 100 || weight > 900)
|
| + return nullptr;
|
| + range.consumeIncludingWhitespace();
|
| + return cssValuePool().createIdentifierValue(static_cast<CSSValueID>(CSSValue100 + weight / 100 - 1));
|
| +}
|
| +
|
| PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID propId)
|
| {
|
| m_range.consumeWhitespace();
|
| @@ -232,11 +358,190 @@ PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSProperty
|
| return consumeWebkitHighlight(m_range);
|
| case CSSPropertyFontVariantLigatures:
|
| return consumeFontVariantLigatures(m_range);
|
| + case CSSPropertyWebkitFontFeatureSettings:
|
| + return consumeFontFeatureSettings(m_range);
|
| + case CSSPropertyFontVariant:
|
| + return consumeFontVariant();
|
| + case CSSPropertyFontFamily:
|
| + return consumeFontFamily();
|
| + case CSSPropertyFontWeight:
|
| + return consumeFontWeight(m_range);
|
| default:
|
| return nullptr;
|
| }
|
| }
|
|
|
| +static PassRefPtrWillBeRawPtr<CSSValueList> consumeFontFaceUnicodeRange(CSSParserTokenRange& range)
|
| +{
|
| + RefPtrWillBeRawPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
|
| +
|
| + do {
|
| + const CSSParserToken& token = range.consumeIncludingWhitespace();
|
| + if (token.type() != UnicodeRangeToken)
|
| + return nullptr;
|
| +
|
| + UChar32 start = token.unicodeRangeStart();
|
| + UChar32 end = token.unicodeRangeEnd();
|
| + if (start > end)
|
| + return nullptr;
|
| + values->append(CSSUnicodeRangeValue::create(start, end));
|
| + } while (consumeCommaIncludingWhitespace(range));
|
| +
|
| + return values.release();
|
| +}
|
| +
|
| +PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::consumeFontFaceSrcURI()
|
| +{
|
| + String url = consumeUrl(m_range);
|
| + if (url.isNull())
|
| + return nullptr;
|
| + RefPtrWillBeRawPtr<CSSFontFaceSrcValue> uriValue(CSSFontFaceSrcValue::create(completeURL(url), m_context.shouldCheckContentSecurityPolicy()));
|
| + uriValue->setReferrer(m_context.referrer());
|
| +
|
| + if (m_range.peek().functionId() != CSSValueFormat)
|
| + return uriValue.release();
|
| +
|
| + // FIXME: https://drafts.csswg.org/css-fonts says that format() contains a comma-separated list of strings,
|
| + // but CSSFontFaceSrcValue stores only one format. Allowing one format for now.
|
| + // FIXME: IdentToken should not be supported here.
|
| + CSSParserTokenRange args = consumeFunction(m_range);
|
| + const CSSParserToken& arg = args.consumeIncludingWhitespace();
|
| + if ((arg.type() != StringToken && arg.type() != IdentToken) || !args.atEnd())
|
| + return nullptr;
|
| + uriValue->setFormat(arg.value());
|
| + return uriValue.release();
|
| +}
|
| +
|
| +static String concatenateFamilyName(CSSParserTokenRange& range)
|
| +{
|
| + StringBuilder builder;
|
| + bool addedSpace = false;
|
| + const CSSParserToken& firstToken = range.peek();
|
| + while (range.peek().type() == IdentToken) {
|
| + if (!builder.isEmpty()) {
|
| + builder.append(' ');
|
| + addedSpace = true;
|
| + }
|
| + builder.append(range.consumeIncludingWhitespace().value());
|
| + }
|
| + if (!addedSpace && isCSSWideKeyword(firstToken.id())) {
|
| + return String();
|
| +}
|
| + return builder.toString();
|
| +}
|
| +
|
| +PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::consumeFontFaceSrcLocal()
|
| +{
|
| + CSSParserTokenRange args = consumeFunction(m_range);
|
| + ContentSecurityPolicyDisposition shouldCheckContentSecurityPolicy = m_context.shouldCheckContentSecurityPolicy();
|
| + if (args.peek().type() == StringToken) {
|
| + const CSSParserToken& arg = args.consumeIncludingWhitespace();
|
| + if (!args.atEnd())
|
| + return nullptr;
|
| + return CSSFontFaceSrcValue::createLocal(arg.value(), shouldCheckContentSecurityPolicy);
|
| + }
|
| + if (args.peek().type() == IdentToken) {
|
| + String familyName = concatenateFamilyName(args);
|
| + if (!args.atEnd())
|
| + return nullptr;
|
| + return CSSFontFaceSrcValue::createLocal(familyName, shouldCheckContentSecurityPolicy);
|
| + }
|
| + return nullptr;
|
| +}
|
| +
|
| +PassRefPtrWillBeRawPtr<CSSValueList> CSSPropertyParser::consumeFontFaceSrc()
|
| +{
|
| + RefPtrWillBeRawPtr<CSSValueList> values(CSSValueList::createCommaSeparated());
|
| +
|
| + do {
|
| + const CSSParserToken& token = m_range.peek();
|
| + RefPtrWillBeRawPtr<CSSValue> parsedValue = nullptr;
|
| + if (token.functionId() == CSSValueLocal)
|
| + parsedValue = consumeFontFaceSrcLocal();
|
| + else
|
| + parsedValue = consumeFontFaceSrcURI();
|
| + if (!parsedValue)
|
| + return nullptr;
|
| + values->append(parsedValue);
|
| + } while (consumeCommaIncludingWhitespace(m_range));
|
| + return values.release();
|
| +}
|
| +
|
| +static PassRefPtrWillBeRawPtr<CSSValue> consumeFamilyName(CSSParserTokenRange& range)
|
| +{
|
| + if (range.peek().type() == StringToken)
|
| + return cssValuePool().createFontFamilyValue(range.consumeIncludingWhitespace().value());
|
| + if (range.peek().type() != IdentToken)
|
| + return nullptr;
|
| + String familyName = concatenateFamilyName(range);
|
| + if (familyName.isNull())
|
| + return nullptr;
|
| + return cssValuePool().createFontFamilyValue(familyName);
|
| +}
|
| +
|
| +static PassRefPtrWillBeRawPtr<CSSValue> consumeGenericFamily(CSSParserTokenRange& range)
|
| +{
|
| + if (range.peek().id() >= CSSValueSerif && range.peek().id() <= CSSValueWebkitBody)
|
| + return consumeIdent(range);
|
| + return nullptr;
|
| +}
|
| +
|
| +PassRefPtrWillBeRawPtr<CSSValueList> CSSPropertyParser::consumeFontFamily()
|
| +{
|
| + RefPtrWillBeRawPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
|
| + do {
|
| + RefPtrWillBeRawPtr<CSSValue> parsedValue = nullptr;
|
| + if ((parsedValue = consumeGenericFamily(m_range))) {
|
| + list->append(parsedValue);
|
| + } else if ((parsedValue = consumeFamilyName(m_range))) {
|
| + list->append(parsedValue);
|
| + } else {
|
| + return nullptr;
|
| + }
|
| + } while (consumeCommaIncludingWhitespace(m_range));
|
| + if (m_ruleType == StyleRule::FontFace && list->length() > 1)
|
| + return nullptr;
|
| + return list.release();
|
| +}
|
| +
|
| +bool CSSPropertyParser::parseFontFaceDescriptor(CSSPropertyID propId)
|
| +{
|
| + RefPtrWillBeRawPtr<CSSValue> parsedValue = nullptr;
|
| +
|
| + m_range.consumeWhitespace();
|
| + switch (propId) {
|
| + case CSSPropertySrc: // This is a list of urls or local references.
|
| + parsedValue = consumeFontFaceSrc();
|
| + break;
|
| + case CSSPropertyUnicodeRange:
|
| + parsedValue = consumeFontFaceUnicodeRange(m_range);
|
| + break;
|
| + case CSSPropertyFontStretch:
|
| + case CSSPropertyFontStyle: {
|
| + CSSValueID id = m_range.consumeIncludingWhitespace().id();
|
| + if (!CSSParserFastPaths::isValidKeywordPropertyAndValue(propId, id))
|
| + return false;
|
| + parsedValue = cssValuePool().createIdentifierValue(id);
|
| + break;
|
| + }
|
| + // TODO(rwlbuis): check there is only one family-name in font-face descriptor case
|
| + case CSSPropertyFontFamily:
|
| + case CSSPropertyFontVariant:
|
| + case CSSPropertyFontWeight:
|
| + case CSSPropertyWebkitFontFeatureSettings:
|
| + parsedValue = parseSingleValue(propId);
|
| + break;
|
| + default:
|
| + break;
|
| + }
|
| +
|
| + if (!parsedValue || !m_range.atEnd())
|
| + return false;
|
| +
|
| + addProperty(propId, parsedValue.release(), false);
|
| + return true;
|
| +}
|
| +
|
| bool CSSPropertyParser::parseShorthand(CSSPropertyID propId, bool important)
|
| {
|
| m_range.consumeWhitespace();
|
|
|