Chromium Code Reviews| 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..c73aa6301839bab8e2c72365b596b3ca1546b0a3 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,46 @@ 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(); |
| + return contents; |
| +} |
| + |
| +static inline bool isComma(const CSSParserToken& value) |
|
Timothy Loh
2015/09/18 03:49:18
not used now, not sure if we'll actually need it :
|
| +{ |
| + return value.type() == CommaToken; |
| +} |
| + |
| +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 +186,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 +238,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 +306,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 +362,194 @@ 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; |
| + CSSParserToken token = range.peek(); |
|
Timothy Loh
2015/09/18 03:49:18
Copying CSSParserTokens is a bit dodgy, I've been
|
| + bool addedSpace = false; |
| + while (range.peek().type() == IdentToken) { |
| + token = range.consumeIncludingWhitespace(); |
| + if (!builder.isEmpty()) { |
| + builder.append(' '); |
| + addedSpace = true; |
| + } |
| + builder.append(token.value()); |
| + } |
| + if (!addedSpace && isCSSWideKeyword(token.id())) { |
| + return String(); |
| +} |
| + return builder.toString(); |
| +} |
| + |
| +PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::consumeFontFaceSrcLocal() |
| +{ |
| + CSSParserTokenRange args = consumeFunction(m_range); |
| + if (args.atEnd()) |
|
Timothy Loh
2015/09/18 03:49:18
No need to check this I guess. I think the only pl
|
| + return nullptr; |
| + |
| + 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 (!list->length() || (m_ruleType == StyleRule::FontFace && list->length() > 1)) |
|
Timothy Loh
2015/09/18 03:49:18
list is never empty here. Also how about just retu
|
| + list = 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(); |