Chromium Code Reviews| Index: third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp |
| diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp |
| index 6e2b2211d22e2763058d7ae3233c3358c86d817f..a2ef560f4c6edbdc74aeb6f2cf6295a280a63a7f 100644 |
| --- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp |
| +++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp |
| @@ -7,6 +7,7 @@ |
| #include "core/StylePropertyShorthand.h" |
| #include "core/css/CSSCalculationValue.h" |
| +#include "core/css/CSSCrossfadeValue.h" |
| #include "core/css/CSSCursorImageValue.h" |
| #include "core/css/CSSCustomIdentValue.h" |
| #include "core/css/CSSFontFaceSrcValue.h" |
| @@ -2405,6 +2406,227 @@ static PassRefPtrWillBeRawPtr<CSSValue> consumeCursor(CSSParserTokenRange& range |
| return list.release(); |
| } |
| +static bool consumeGradientColorStops(CSSParserTokenRange& range, CSSParserContext context, CSSGradientValue* gradient, bool expectComma) |
| +{ |
| + bool supportsColorHints = gradient->gradientType() == CSSLinearGradient || gradient->gradientType() == CSSRadialGradient; |
| + |
| + // The first color stop cannot be a color hint. |
| + bool previousStopWasColorHint = true; |
| + while (!range.atEnd()) { |
|
Timothy Loh
2015/12/09 06:59:57
Can we just use a regular do-while loop here and m
rwlbuis
2015/12/09 21:31:13
Done.
|
| + if (expectComma && !consumeCommaIncludingWhitespace(range)) |
| + return false; |
| + |
| + CSSGradientColorStop stop; |
| + stop.m_color = consumeColor(range, context); |
| + |
| + // Two hints in a row are not allowed. |
| + if (!stop.m_color && (!supportsColorHints || previousStopWasColorHint)) |
| + return false; |
| + previousStopWasColorHint = !stop.m_color; |
| + stop.m_position = consumeLengthOrPercent(range, context.mode(), ValueRangeAll); |
| + if (!stop.m_color && !stop.m_position) |
| + return false; |
| + |
| + gradient->addStop(stop); |
| + expectComma = true; |
| + } |
| + |
| + // The last color stop cannot be a color hint. |
| + if (previousStopWasColorHint) |
| + return false; |
| + |
| + // Must have 2 or more stops to be valid. |
| + return gradient->stopCount() >= 2; |
| +} |
| + |
| +static PassRefPtrWillBeRawPtr<CSSValue> consumeRadialGradient(CSSParserTokenRange args, CSSParserContext context, CSSGradientRepeat repeating) |
| +{ |
| + RefPtrWillBeRawPtr<CSSRadialGradientValue> result = CSSRadialGradientValue::create(repeating, CSSRadialGradient); |
| + |
| + RefPtrWillBeRawPtr<CSSPrimitiveValue> shapeValue = nullptr; |
|
Timothy Loh
2015/12/09 06:59:57
imo shapeValue -> shape, sizeValue -> sizeKeyword
rwlbuis
2015/12/09 21:31:13
Done.
|
| + RefPtrWillBeRawPtr<CSSPrimitiveValue> sizeValue = nullptr; |
| + RefPtrWillBeRawPtr<CSSPrimitiveValue> horizontalSize = nullptr; |
| + RefPtrWillBeRawPtr<CSSPrimitiveValue> verticalSize = nullptr; |
| + |
| + // First part of grammar, the size/shape clause: |
| + // [ circle || <length> ] | |
| + // [ ellipse || [ <length> | <percentage> ]{2} ] | |
| + // [ [ circle | ellipse] || <size-keyword> ] |
| + for (int i = 0; i < 3; ++i) { |
| + if (args.peek().type() == IdentToken) { |
| + CSSValueID id = args.peek().id(); |
| + if (id == CSSValueCircle || id == CSSValueEllipse) { |
| + if (shapeValue) |
| + return nullptr; |
| + shapeValue = consumeIdent(args); |
| + } else if (id == CSSValueClosestSide || id == CSSValueClosestCorner || id == CSSValueFarthestSide || id == CSSValueFarthestCorner) { |
| + if (sizeValue || horizontalSize) |
|
Timothy Loh
2015/12/09 06:59:57
Probably nicer to leave out the horizontalSize che
rwlbuis
2015/12/09 21:31:14
Done.
|
| + return nullptr; |
| + sizeValue = consumeIdent(args); |
| + } else { |
| + break; |
| + } |
| + } else { |
| + RefPtrWillBeRawPtr<CSSPrimitiveValue> center = consumeLengthOrPercent(args, context.mode(), ValueRangeAll); |
| + if (!center) |
| + break; |
| + if (sizeValue || horizontalSize) |
|
Timothy Loh
2015/12/09 06:59:57
and leave out the sizeValue check here
rwlbuis
2015/12/09 21:31:14
Done.
|
| + return nullptr; |
| + horizontalSize = center; |
| + |
| + if ((center = consumeLengthOrPercent(args, context.mode(), ValueRangeAll))) { |
| + verticalSize = center.release(); |
| + ++i; |
| + } |
| + } |
| + } |
| + |
| + // You can specify size as a keyword or a length/percentage, not both. |
| + if (sizeValue && horizontalSize) |
| + return nullptr; |
| + // Circles must have 0 or 1 lengths. |
| + if (shapeValue && shapeValue->getValueID() == CSSValueCircle && verticalSize) |
| + return nullptr; |
| + // Ellipses must have 0 or 2 length/percentages. |
| + if (shapeValue && shapeValue->getValueID() == CSSValueEllipse && horizontalSize && !verticalSize) |
| + return nullptr; |
| + // If there's only one size, it must be a length. |
| + if (!verticalSize && horizontalSize && horizontalSize->isPercentage()) |
|
Timothy Loh
2015/12/09 06:59:57
// TODO(timloh): Calcs with both lengths and perce
rwlbuis
2015/12/09 21:31:13
Done.
|
| + return nullptr; |
| + |
| + result->setShape(shapeValue); |
| + result->setSizingBehavior(sizeValue); |
| + result->setEndHorizontalSize(horizontalSize); |
| + result->setEndVerticalSize(verticalSize); |
| + |
| + RefPtrWillBeRawPtr<CSSValue> centerX = nullptr; |
| + RefPtrWillBeRawPtr<CSSValue> centerY = nullptr; |
| + if (args.peek().id() == CSSValueAt) { |
| + args.consumeIncludingWhitespace(); |
| + |
| + consumePosition(args, context.mode(), UnitlessQuirk::Forbid, centerX, centerY); |
| + if (!(centerX && centerY)) |
| + return nullptr; |
| + |
| + result->setFirstX(centerX); |
| + result->setFirstY(centerY); |
| + // Right now, CSS radial gradients have the same start and end centers. |
| + result->setSecondX(centerX); |
| + result->setSecondY(centerY); |
| + } |
| + |
| + bool expectComma = shapeValue || sizeValue || horizontalSize || centerX || centerY; |
| + if (!consumeGradientColorStops(args, context, result.get(), expectComma) || !args.atEnd()) |
| + return nullptr; |
| + |
| + return result.release(); |
| +} |
| + |
| +static PassRefPtrWillBeRawPtr<CSSValue> consumeLinearGradient(CSSParserTokenRange args, CSSParserContext context, CSSGradientRepeat repeating, CSSGradientType gradientType) |
| +{ |
| + RefPtrWillBeRawPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::create(repeating, gradientType); |
| + |
| + bool expectComma = true; |
| + RefPtrWillBeRawPtr<CSSPrimitiveValue> angle = consumeAngle(args, context.mode()); |
| + if (angle) { |
| + result->setAngle(angle.release()); |
| + } else if (gradientType == CSSPrefixedLinearGradient || consumeIdent<CSSValueTo>(args)) { |
| + RefPtrWillBeRawPtr<CSSPrimitiveValue> endX = consumeIdent<CSSValueLeft, CSSValueRight>(args); |
| + RefPtrWillBeRawPtr<CSSPrimitiveValue> endY = consumeIdent<CSSValueBottom, CSSValueTop>(args); |
| + if (!endX && !endY) |
| + return nullptr; |
| + RefPtrWillBeRawPtr<CSSPrimitiveValue> location = consumeIdent<CSSValueLeft, CSSValueRight>(args); |
|
Timothy Loh
2015/12/09 06:59:57
Should be enough to just write
if (!endX)
end
rwlbuis
2015/12/09 21:31:13
I think endX and location have to be separate. Oth
rwlbuis
2015/12/09 23:08:46
Oh, I see why you mean here too now, should be fix
|
| + if (location) { |
| + if (endX) |
| + return nullptr; |
| + endX = location; |
| + } else if ((location = consumeIdent<CSSValueBottom, CSSValueTop>(args))) { |
| + if (endY) |
| + return nullptr; |
| + endY = location; |
| + } |
| + |
| + if (!endX && !endY) { |
| + endY = cssValuePool().createIdentifierValue(CSSValueTop); |
|
Timothy Loh
2015/12/09 06:59:57
Unreachable?
rwlbuis
2015/12/09 21:31:13
Sorry, I merged a patch wrong, this is still neede
|
| + expectComma = false; |
| + } |
| + |
| + result->setFirstX(endX.release()); |
| + result->setFirstY(endY.release()); |
| + } |
| + |
| + if (!consumeGradientColorStops(args, context, result.get(), expectComma) || !args.atEnd()) |
| + return nullptr; |
| + |
| + return result.release(); |
| +} |
| + |
| +static PassRefPtrWillBeRawPtr<CSSValue> consumeImage(CSSParserTokenRange&, CSSParserContext); |
| + |
| +static PassRefPtrWillBeRawPtr<CSSValue> consumeCrossFade(CSSParserTokenRange args, CSSParserContext context) |
| +{ |
| + RefPtrWillBeRawPtr<CSSValue> fromImageValue = consumeImage(args, context); |
| + if (!fromImageValue || !consumeCommaIncludingWhitespace(args)) |
| + return nullptr; |
| + RefPtrWillBeRawPtr<CSSValue> toImageValue = consumeImage(args, context); |
| + if (!toImageValue || !consumeCommaIncludingWhitespace(args)) |
| + return nullptr; |
| + |
| + RefPtrWillBeRawPtr<CSSPrimitiveValue> percentage = nullptr; |
| + const CSSParserToken& percentageArg = args.consumeIncludingWhitespace(); |
| + if (percentageArg.type() == PercentageToken) |
| + percentage = cssValuePool().createValue(clampTo<double>(percentageArg.numericValue() / 100, 0, 1), CSSPrimitiveValue::UnitType::Number); |
| + else if (percentageArg.type() == NumberToken) |
| + percentage = cssValuePool().createValue(clampTo<double>(percentageArg.numericValue(), 0, 1), CSSPrimitiveValue::UnitType::Number); |
| + |
| + if (!percentage || !args.atEnd()) |
| + return nullptr; |
| + |
| + return CSSCrossfadeValue::create(fromImageValue, toImageValue, percentage); |
| +} |
| + |
| +static PassRefPtrWillBeRawPtr<CSSValue> consumeGeneratedImage(CSSParserTokenRange& range, CSSParserContext context) |
|
Timothy Loh
2015/12/09 06:59:57
As usual, we need to make sure this doesn't accide
rwlbuis
2015/12/09 21:31:14
Done.
Timothy Loh
2015/12/10 04:20:07
Any reason to not use else if here? (which imo mak
|
| +{ |
| + CSSValueID id = range.peek().functionId(); |
| + if (id == CSSValueRadialGradient) |
| + return consumeRadialGradient(consumeFunction(range), context, NonRepeating); |
| + if (id == CSSValueWebkitLinearGradient) { |
| + // FIXME: This should send a deprecation message. |
| + if (context.useCounter()) |
| + context.useCounter()->count(UseCounter::DeprecatedWebKitLinearGradient); |
| + return consumeLinearGradient(consumeFunction(range), context, NonRepeating, CSSPrefixedLinearGradient); |
| + } |
| + if (id == CSSValueWebkitRepeatingLinearGradient) { |
| + // FIXME: This should send a deprecation message. |
| + if (context.useCounter()) |
| + context.useCounter()->count(UseCounter::DeprecatedWebKitRepeatingLinearGradient); |
| + return consumeLinearGradient(consumeFunction(range), context, Repeating, CSSPrefixedLinearGradient); |
| + } |
| + if (id == CSSValueLinearGradient) |
| + return consumeLinearGradient(consumeFunction(range), context, NonRepeating, CSSLinearGradient); |
| + if (id == CSSValueWebkitCrossFade) |
| + return consumeCrossFade(consumeFunction(range), context); |
| + return nullptr; |
| +} |
| + |
| +static PassRefPtrWillBeRawPtr<CSSValue> consumeImage(CSSParserTokenRange& range, CSSParserContext context) |
| +{ |
| + if (range.peek().id() == CSSValueNone) |
| + return consumeIdent(range); |
| + |
| + AtomicString uri(consumeUrl(range)); |
| + if (!uri.isNull()) |
| + return CSSPropertyParser::createCSSImageValueWithReferrer(uri, context); |
| + if (range.peek().type() == FunctionToken) { |
| + CSSValueID id = range.peek().functionId(); |
| + if (id == CSSValueWebkitImageSet) |
| + return consumeImageSet(range, context); |
| + if (CSSPropertyParser::isGeneratedImage(id)) |
| + return consumeGeneratedImage(range, context); |
| + } |
| + return nullptr; |
| +} |
| + |
| PassRefPtrWillBeRawPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID unresolvedProperty) |
| { |
| CSSPropertyID property = resolveCSSPropertyID(unresolvedProperty); |