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..6b9de22812c60a3479f4603c1bf4de65755c0671 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,219 @@ static PassRefPtrWillBeRawPtr<CSSValue> consumeCursor(CSSParserTokenRange& range |
| return list.release(); |
| } |
| +static bool consumeGradientColorStops(CSSParserTokenRange& range, CSSParserContext context, CSSGradientValue* gradient) |
|
Timothy Loh
2015/12/10 04:20:07
Can you update all the CSSParserContext arguments
rwlbuis
2015/12/10 14:00:08
Done.
|
| +{ |
| + bool supportsColorHints = gradient->gradientType() == CSSLinearGradient || gradient->gradientType() == CSSRadialGradient; |
| + |
| + // The first color stop cannot be a color hint. |
| + bool previousStopWasColorHint = true; |
| + do { |
| + 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); |
| + } while (consumeCommaIncludingWhitespace(range)); |
| + |
| + // 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> shape = nullptr; |
| + RefPtrWillBeRawPtr<CSSPrimitiveValue> sizeKeyword = 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 (shape) |
| + return nullptr; |
| + shape = consumeIdent(args); |
| + } else if (id == CSSValueClosestSide || id == CSSValueClosestCorner || id == CSSValueFarthestSide || id == CSSValueFarthestCorner) { |
| + if (sizeKeyword) |
| + return nullptr; |
| + sizeKeyword = consumeIdent(args); |
| + } else { |
| + break; |
| + } |
| + } else { |
| + RefPtrWillBeRawPtr<CSSPrimitiveValue> center = consumeLengthOrPercent(args, context.mode(), ValueRangeAll); |
| + if (!center) |
| + break; |
| + if (horizontalSize) |
| + 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 (sizeKeyword && horizontalSize) |
| + return nullptr; |
| + // Circles must have 0 or 1 lengths. |
| + if (shape && shape->getValueID() == CSSValueCircle && verticalSize) |
| + return nullptr; |
| + // Ellipses must have 0 or 2 length/percentages. |
| + if (shape && shape->getValueID() == CSSValueEllipse && horizontalSize && !verticalSize) |
| + return nullptr; |
| + // If there's only one size, it must be a length. |
| + // TODO(timloh): Calcs with both lengths and percentages should be rejected. |
| + if (!verticalSize && horizontalSize && horizontalSize->isPercentage()) |
| + return nullptr; |
| + |
| + result->setShape(shape); |
| + result->setSizingBehavior(sizeKeyword); |
| + 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); |
| + } |
| + |
| + if ((shape || sizeKeyword || horizontalSize || centerX || centerY) && !consumeCommaIncludingWhitespace(args)) |
| + return nullptr; |
| + if (!consumeGradientColorStops(args, context, result.get())) |
| + 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 (gradientType == CSSLinearGradient && !endX && !endY) |
|
Timothy Loh
2015/12/10 04:20:07
Clearer like this IMO, moving the two !endX && !en
rwlbuis
2015/12/10 14:00:08
Much better, done.
|
| + return nullptr; |
| + if (!endX) |
| + endX = consumeIdent<CSSValueLeft, CSSValueRight>(args); |
| + if (!endY) |
| + endY = consumeIdent<CSSValueBottom, CSSValueTop>(args); |
| + |
| + if (!endX && !endY) { |
| + endY = cssValuePool().createIdentifierValue(CSSValueTop); |
| + expectComma = false; |
| + } |
| + |
| + result->setFirstX(endX.release()); |
| + result->setFirstY(endY.release()); |
| + } |
|
Timothy Loh
2015/12/10 04:20:07
Since you made expectComma start at true, we need
rwlbuis
2015/12/10 14:00:08
Done.
|
| + |
| + if (expectComma && !consumeCommaIncludingWhitespace(args)) |
| + return nullptr; |
| + if (!consumeGradientColorStops(args, context, result.get())) |
| + 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) |
| + return nullptr; |
| + return CSSCrossfadeValue::create(fromImageValue, toImageValue, percentage); |
| +} |
| + |
| +static PassRefPtrWillBeRawPtr<CSSValue> consumeGeneratedImage(CSSParserTokenRange& range, CSSParserContext context) |
| +{ |
| + CSSValueID id = range.peek().functionId(); |
| + CSSParserTokenRange rangeCopy = range; |
| + CSSParserTokenRange args = consumeFunction(rangeCopy); |
| + RefPtrWillBeRawPtr<CSSValue> result = nullptr; |
| + if (id == CSSValueRadialGradient) |
| + result = consumeRadialGradient(args, context, NonRepeating); |
| + if (id == CSSValueWebkitLinearGradient) { |
| + // FIXME: This should send a deprecation message. |
| + if (context.useCounter()) |
| + context.useCounter()->count(UseCounter::DeprecatedWebKitLinearGradient); |
| + result = consumeLinearGradient(args, context, NonRepeating, CSSPrefixedLinearGradient); |
| + } |
| + if (id == CSSValueWebkitRepeatingLinearGradient) { |
| + // FIXME: This should send a deprecation message. |
| + if (context.useCounter()) |
| + context.useCounter()->count(UseCounter::DeprecatedWebKitRepeatingLinearGradient); |
| + result = consumeLinearGradient(args, context, Repeating, CSSPrefixedLinearGradient); |
| + } |
| + if (id == CSSValueLinearGradient) |
| + result = consumeLinearGradient(args, context, NonRepeating, CSSLinearGradient); |
| + if (id == CSSValueWebkitCrossFade) |
| + result = consumeCrossFade(args, context); |
| + if (!result || !args.atEnd()) |
| + return nullptr; |
| + range = rangeCopy; |
| + return result; |
| +} |
| + |
| +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); |