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 db7cdba5f2faa52e27f976644b9a87fe5e5d2bd1..6432b846e62d74e23d9fd369d8c4ff6327c26caa 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,217 @@ static PassRefPtrWillBeRawPtr<CSSValue> consumeCursor(CSSParserTokenRange& range |
return list.release(); |
} |
+static bool consumeGradientColorStops(CSSParserTokenRange& range, CSSParserMode cssParserMode, CSSGradientValue* gradient) |
+{ |
+ 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, cssParserMode); |
+ // 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, cssParserMode, 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, CSSParserMode cssParserMode, 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, cssParserMode, ValueRangeAll); |
+ if (!center) |
+ break; |
+ if (horizontalSize) |
+ return nullptr; |
+ horizontalSize = center; |
+ if ((center = consumeLengthOrPercent(args, cssParserMode, 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, cssParserMode, 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, cssParserMode, result.get())) |
+ return nullptr; |
+ return result.release(); |
+} |
+ |
+static PassRefPtrWillBeRawPtr<CSSValue> consumeLinearGradient(CSSParserTokenRange& args, CSSParserMode cssParserMode, CSSGradientRepeat repeating, CSSGradientType gradientType) |
+{ |
+ RefPtrWillBeRawPtr<CSSLinearGradientValue> result = CSSLinearGradientValue::create(repeating, gradientType); |
+ |
+ bool expectComma = true; |
+ RefPtrWillBeRawPtr<CSSPrimitiveValue> angle = consumeAngle(args, cssParserMode); |
+ 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) { |
+ if (gradientType == CSSLinearGradient) |
+ return nullptr; |
+ endY = cssValuePool().createIdentifierValue(CSSValueTop); |
+ expectComma = false; |
+ } else if (!endX) { |
+ endX = consumeIdent<CSSValueLeft, CSSValueRight>(args); |
+ } |
+ |
+ result->setFirstX(endX.release()); |
+ result->setFirstY(endY.release()); |
+ } else { |
+ expectComma = false; |
+ } |
+ |
+ if (expectComma && !consumeCommaIncludingWhitespace(args)) |
+ return nullptr; |
+ if (!consumeGradientColorStops(args, cssParserMode, 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.mode(), NonRepeating); |
+ } else if (id == CSSValueWebkitLinearGradient) { |
+ // FIXME: This should send a deprecation message. |
+ if (context.useCounter()) |
+ context.useCounter()->count(UseCounter::DeprecatedWebKitLinearGradient); |
+ result = consumeLinearGradient(args, context.mode(), NonRepeating, CSSPrefixedLinearGradient); |
+ } else if (id == CSSValueWebkitRepeatingLinearGradient) { |
+ // FIXME: This should send a deprecation message. |
+ if (context.useCounter()) |
+ context.useCounter()->count(UseCounter::DeprecatedWebKitRepeatingLinearGradient); |
+ result = consumeLinearGradient(args, context.mode(), Repeating, CSSPrefixedLinearGradient); |
+ } else if (id == CSSValueLinearGradient) { |
+ result = consumeLinearGradient(args, context.mode(), NonRepeating, CSSLinearGradient); |
+ } else 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); |