Web Inspector: don't use `:matches(:before, :after)` after r255059
[WebKit-https.git] / Source / WebCore / css / parser / CSSPropertyParserHelpers.cpp
index bddf016..b2f5ce3 100644 (file)
 #include "CSSPropertyParserHelpers.h"
 
 #include "CSSCalculationValue.h"
+#include "CSSCanvasValue.h"
 #include "CSSCrossfadeValue.h"
+#include "CSSFilterImageValue.h"
 #include "CSSGradientValue.h"
 #include "CSSImageSetValue.h"
 #include "CSSImageValue.h"
+#include "CSSNamedImageValue.h"
+#include "CSSPaintImageValue.h"
 #include "CSSParserIdioms.h"
 #include "CSSValuePool.h"
 #include "Pair.h"
+#include "RuntimeEnabledFeatures.h"
 #include "StyleColor.h"
+#include <wtf/text/StringConcatenateNumbers.h>
 
 namespace WebCore {
 
@@ -73,43 +79,54 @@ CSSParserTokenRange consumeFunction(CSSParserTokenRange& range)
 
 // FIXME: consider pulling in the parsing logic from CSSCalculationValue.cpp.
 class CalcParser {
-
 public:
-    explicit CalcParser(CSSParserTokenRange& range, ValueRange valueRange = ValueRangeAll)
+    explicit CalcParser(CSSParserTokenRange& range, CalculationCategory destinationCategory, ValueRange valueRange = ValueRangeAll)
         : m_sourceRange(range)
         , m_range(range)
     {
         const CSSParserToken& token = range.peek();
-        if (token.functionId() == CSSValueCalc || token.functionId() == CSSValueWebkitCalc)
-            m_calcValue = CSSCalcValue::create(consumeFunction(m_range), valueRange);
+        auto functionId = token.functionId();
+        if (CSSCalcValue::isCalcFunction(functionId))
+            m_calcValue = CSSCalcValue::create(functionId, consumeFunction(m_range), destinationCategory, valueRange);
     }
 
     const CSSCalcValue* value() const { return m_calcValue.get(); }
+
     RefPtr<CSSPrimitiveValue> consumeValue()
     {
         if (!m_calcValue)
             return nullptr;
         m_sourceRange = m_range;
-        return CSSPrimitiveValue::create(m_calcValue.release());
+        return CSSValuePool::singleton().createValue(WTFMove(m_calcValue));
     }
+
+    RefPtr<CSSPrimitiveValue> consumeInteger(double minimumValue)
+    {
+        if (!m_calcValue)
+            return nullptr;
+        m_sourceRange = m_range;
+        double value = std::max(m_calcValue->doubleValue(), minimumValue);
+        value = std::round(value);
+        return CSSValuePool::singleton().createValue(value, CSSUnitType::CSS_NUMBER);
+    }
+
     RefPtr<CSSPrimitiveValue> consumeNumber()
     {
         if (!m_calcValue)
             return nullptr;
         m_sourceRange = m_range;
-        CSSPrimitiveValue::UnitTypes unitType = m_calcValue->isInt() ? CSSPrimitiveValue::UnitTypes::CSS_PARSER_INTEGER : CSSPrimitiveValue::UnitTypes::CSS_NUMBER;
-        return CSSPrimitiveValue::create(m_calcValue->doubleValue(), unitType);
+        return CSSValuePool::singleton().createValue(m_calcValue->doubleValue(), CSSUnitType::CSS_NUMBER);
     }
 
     bool consumeNumberRaw(double& result)
     {
-        if (!m_calcValue || m_calcValue->category() != CalcNumber)
+        if (!m_calcValue || m_calcValue->category() != CalculationCategory::Number)
             return false;
         m_sourceRange = m_range;
         result = m_calcValue->doubleValue();
         return true;
     }
-
+    
 private:
     CSSParserTokenRange& m_sourceRange;
     CSSParserTokenRange m_range;
@@ -122,17 +139,19 @@ RefPtr<CSSPrimitiveValue> consumeInteger(CSSParserTokenRange& range, double mini
     if (token.type() == NumberToken) {
         if (token.numericValueType() == NumberValueType || token.numericValue() < minimumValue)
             return nullptr;
-        return CSSPrimitiveValue::create(range.consumeIncludingWhitespace().numericValue(), CSSPrimitiveValue::UnitTypes::CSS_PARSER_INTEGER);
+        return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSUnitType::CSS_NUMBER);
     }
-    CalcParser calcParser(range);
+
+    if (token.type() != FunctionToken)
+        return nullptr;
+
+    CalcParser calcParser(range, CalculationCategory::Number);
     if (const CSSCalcValue* calculation = calcParser.value()) {
-        if (calculation->category() != CalcNumber || !calculation->isInt())
+        if (calculation->category() != CalculationCategory::Number)
             return nullptr;
-        double value = calculation->doubleValue();
-        if (value < minimumValue)
-            return nullptr;
-        return calcParser.consumeNumber();
+        return calcParser.consumeInteger(minimumValue);
     }
+
     return nullptr;
 }
 
@@ -143,11 +162,16 @@ RefPtr<CSSPrimitiveValue> consumePositiveInteger(CSSParserTokenRange& range)
 
 bool consumeNumberRaw(CSSParserTokenRange& range, double& result)
 {
-    if (range.peek().type() == NumberToken) {
+    const CSSParserToken& token = range.peek();
+    if (token.type() == NumberToken) {
         result = range.consumeIncludingWhitespace().numericValue();
         return true;
     }
-    CalcParser calcParser(range, ValueRangeAll);
+
+    if (token.type() != FunctionToken)
+        return false;
+
+    CalcParser calcParser(range, CalculationCategory::Number, ValueRangeAll);
     return calcParser.consumeNumberRaw(result);
 }
 
@@ -158,16 +182,55 @@ RefPtr<CSSPrimitiveValue> consumeNumber(CSSParserTokenRange& range, ValueRange v
     if (token.type() == NumberToken) {
         if (valueRange == ValueRangeNonNegative && token.numericValue() < 0)
             return nullptr;
-        return CSSPrimitiveValue::create(range.consumeIncludingWhitespace().numericValue(), token.unitType());
+        return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
     }
-    CalcParser calcParser(range, ValueRangeAll);
-    if (const CSSCalcValue* calculation = calcParser.value()) {
-        // FIXME: Calcs should not be subject to parse time range checks.
-        // spec: https://drafts.csswg.org/css-values-3/#calc-range
-        if (calculation->category() != CalcNumber || (valueRange == ValueRangeNonNegative && calculation->isNegative()))
+
+    if (token.type() != FunctionToken)
+        return nullptr;
+
+    CalcParser calcParser(range, CalculationCategory::Number, valueRange);
+    if (const CSSCalcValue* calcValue = calcParser.value()) {
+        if (calcValue->category() != CalculationCategory::Number)
             return nullptr;
-        return calcParser.consumeNumber();
+        return calcParser.consumeValue();
     }
+
+    return nullptr;
+}
+
+#if !ENABLE(VARIATION_FONTS)
+static inline bool divisibleBy100(double value)
+{
+    return static_cast<int>(value / 100) * 100 == value;
+}
+#endif
+
+RefPtr<CSSPrimitiveValue> consumeFontWeightNumber(CSSParserTokenRange& range)
+{
+    // Values less than or equal to 0 or greater than or equal to 1000 are parse errors.
+    auto& token = range.peek();
+    if (token.type() == NumberToken && token.numericValue() >= 1 && token.numericValue() <= 1000
+#if !ENABLE(VARIATION_FONTS)
+        && token.numericValueType() == IntegerValueType && divisibleBy100(token.numericValue())
+#endif
+    )
+        return consumeNumber(range, ValueRangeAll);
+
+    if (token.type() != FunctionToken)
+        return nullptr;
+
+    // "[For calc()], the used value resulting from an expression must be clamped to the range allowed in the target context."
+    CalcParser calcParser(range, CalculationCategory::Number, ValueRangeAll);
+    double result;
+    if (calcParser.consumeNumberRaw(result)
+#if !ENABLE(VARIATION_FONTS)
+        && result > 0 && result < 1000 && divisibleBy100(result)
+#endif
+    ) {
+        result = std::min(std::max(result, std::nextafter(0., 1.)), std::nextafter(1000., 0.));
+        return CSSValuePool::singleton().createValue(result, CSSUnitType::CSS_NUMBER);
+    }
+
     return nullptr;
 }
 
@@ -184,44 +247,50 @@ RefPtr<CSSPrimitiveValue> consumeLength(CSSParserTokenRange& range, CSSParserMod
     const CSSParserToken& token = range.peek();
     if (token.type() == DimensionToken) {
         switch (token.unitType()) {
-        case CSSPrimitiveValue::UnitTypes::CSS_QUIRKY_EMS:
+        case CSSUnitType::CSS_QUIRKY_EMS:
             if (cssParserMode != UASheetMode)
                 return nullptr;
             FALLTHROUGH;
-        case CSSPrimitiveValue::UnitTypes::CSS_EMS:
-        case CSSPrimitiveValue::UnitTypes::CSS_REMS:
-        case CSSPrimitiveValue::UnitTypes::CSS_CHS:
-        case CSSPrimitiveValue::UnitTypes::CSS_EXS:
-        case CSSPrimitiveValue::UnitTypes::CSS_PX:
-        case CSSPrimitiveValue::UnitTypes::CSS_CM:
-        case CSSPrimitiveValue::UnitTypes::CSS_MM:
-        case CSSPrimitiveValue::UnitTypes::CSS_IN:
-        case CSSPrimitiveValue::UnitTypes::CSS_PT:
-        case CSSPrimitiveValue::UnitTypes::CSS_PC:
-        case CSSPrimitiveValue::UnitTypes::CSS_VW:
-        case CSSPrimitiveValue::UnitTypes::CSS_VH:
-        case CSSPrimitiveValue::UnitTypes::CSS_VMIN:
-        case CSSPrimitiveValue::UnitTypes::CSS_VMAX:
+        case CSSUnitType::CSS_EMS:
+        case CSSUnitType::CSS_REMS:
+        case CSSUnitType::CSS_CHS:
+        case CSSUnitType::CSS_EXS:
+        case CSSUnitType::CSS_PX:
+        case CSSUnitType::CSS_CM:
+        case CSSUnitType::CSS_MM:
+        case CSSUnitType::CSS_IN:
+        case CSSUnitType::CSS_PT:
+        case CSSUnitType::CSS_PC:
+        case CSSUnitType::CSS_VW:
+        case CSSUnitType::CSS_VH:
+        case CSSUnitType::CSS_VMIN:
+        case CSSUnitType::CSS_VMAX:
+        case CSSUnitType::CSS_Q:
             break;
         default:
             return nullptr;
         }
-        if (valueRange == ValueRangeNonNegative && token.numericValue() < 0)
+        if ((valueRange == ValueRangeNonNegative && token.numericValue() < 0) || std::isinf(token.numericValue()))
             return nullptr;
-        return CSSPrimitiveValue::create(range.consumeIncludingWhitespace().numericValue(), token.unitType());
+        return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
     }
     if (token.type() == NumberToken) {
         if (!shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless)
             || (valueRange == ValueRangeNonNegative && token.numericValue() < 0))
             return nullptr;
-        CSSPrimitiveValue::UnitTypes unitType = CSSPrimitiveValue::UnitTypes::CSS_PX;
-        return CSSPrimitiveValue::create(range.consumeIncludingWhitespace().numericValue(), unitType);
+        if (std::isinf(token.numericValue()))
+            return nullptr;
+        CSSUnitType unitType = CSSUnitType::CSS_PX;
+        return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), unitType);
     }
-    if (cssParserMode == SVGAttributeMode)
+
+    if (token.type() != FunctionToken)
         return nullptr;
-    CalcParser calcParser(range, valueRange);
-    if (calcParser.value() && calcParser.value()->category() == CalcLength)
+
+    CalcParser calcParser(range, CalculationCategory::Length, valueRange);
+    if (calcParser.value() && calcParser.value()->category() == CalculationCategory::Length)
         return calcParser.consumeValue();
+
     return nullptr;
 }
 
@@ -229,13 +298,17 @@ RefPtr<CSSPrimitiveValue> consumePercent(CSSParserTokenRange& range, ValueRange
 {
     const CSSParserToken& token = range.peek();
     if (token.type() == PercentageToken) {
-        if (valueRange == ValueRangeNonNegative && token.numericValue() < 0)
+        if ((valueRange == ValueRangeNonNegative && token.numericValue() < 0) || std::isinf(token.numericValue()))
             return nullptr;
-        return CSSPrimitiveValue::create(range.consumeIncludingWhitespace().numericValue(), CSSPrimitiveValue::UnitTypes::CSS_PERCENTAGE);
+        return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSUnitType::CSS_PERCENTAGE);
     }
-    CalcParser calcParser(range, valueRange);
+
+    if (token.type() != FunctionToken)
+        return nullptr;
+
+    CalcParser calcParser(range, CalculationCategory::Percent, valueRange);
     if (const CSSCalcValue* calculation = calcParser.value()) {
-        if (calculation->category() == CalcPercent)
+        if (calculation->category() == CalculationCategory::Percent)
             return calcParser.consumeValue();
     }
     return nullptr;
@@ -243,13 +316,13 @@ RefPtr<CSSPrimitiveValue> consumePercent(CSSParserTokenRange& range, ValueRange
 
 static bool canConsumeCalcValue(CalculationCategory category, CSSParserMode cssParserMode)
 {
-    if (category == CalcLength || category == CalcPercent || category == CalcPercentLength)
+    if (category == CalculationCategory::Length || category == CalculationCategory::Percent || category == CalculationCategory::PercentLength)
         return true;
 
     if (cssParserMode != SVGAttributeMode)
         return false;
 
-    if (category == CalcNumber || category == CalcPercentNumber)
+    if (category == CalculationCategory::Number || category == CalculationCategory::PercentNumber)
         return true;
 
     return false;
@@ -262,7 +335,11 @@ RefPtr<CSSPrimitiveValue> consumeLengthOrPercent(CSSParserTokenRange& range, CSS
         return consumeLength(range, cssParserMode, valueRange, unitless);
     if (token.type() == PercentageToken)
         return consumePercent(range, valueRange);
-    CalcParser calcParser(range, valueRange);
+
+    if (token.type() != FunctionToken)
+        return nullptr;
+
+    CalcParser calcParser(range, CalculationCategory::Length, valueRange);
     if (const CSSCalcValue* calculation = calcParser.value()) {
         if (canConsumeCalcValue(calculation->category(), cssParserMode))
             return calcParser.consumeValue();
@@ -275,55 +352,113 @@ RefPtr<CSSPrimitiveValue> consumeAngle(CSSParserTokenRange& range, CSSParserMode
     const CSSParserToken& token = range.peek();
     if (token.type() == DimensionToken) {
         switch (token.unitType()) {
-        case CSSPrimitiveValue::UnitTypes::CSS_DEG:
-        case CSSPrimitiveValue::UnitTypes::CSS_RAD:
-        case CSSPrimitiveValue::UnitTypes::CSS_GRAD:
-        case CSSPrimitiveValue::UnitTypes::CSS_TURN:
-            return CSSPrimitiveValue::create(range.consumeIncludingWhitespace().numericValue(), token.unitType());
+        case CSSUnitType::CSS_DEG:
+        case CSSUnitType::CSS_RAD:
+        case CSSUnitType::CSS_GRAD:
+        case CSSUnitType::CSS_TURN:
+            return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
         default:
             return nullptr;
         }
     }
-    if (token.type() == NumberToken && shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless)) {
-        range.consumeIncludingWhitespace();
-        return CSSPrimitiveValue::create(0, CSSPrimitiveValue::UnitTypes::CSS_DEG);
-    }
 
-    CalcParser calcParser(range, ValueRangeAll);
+    if (token.type() == NumberToken && shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless))
+        return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSUnitType::CSS_DEG);
+
+    if (token.type() != FunctionToken)
+        return nullptr;
+
+    CalcParser calcParser(range, CalculationCategory::Angle, ValueRangeAll);
     if (const CSSCalcValue* calculation = calcParser.value()) {
-        if (calculation->category() == CalcAngle)
+        if (calculation->category() == CalculationCategory::Angle)
             return calcParser.consumeValue();
     }
     return nullptr;
 }
 
+static RefPtr<CSSPrimitiveValue> consumeAngleOrPercent(CSSParserTokenRange& range, CSSParserMode cssParserMode, ValueRange valueRange, UnitlessQuirk unitless)
+{
+    const CSSParserToken& token = range.peek();
+    if (token.type() == DimensionToken) {
+        switch (token.unitType()) {
+        case CSSUnitType::CSS_DEG:
+        case CSSUnitType::CSS_RAD:
+        case CSSUnitType::CSS_GRAD:
+        case CSSUnitType::CSS_TURN:
+            return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
+        default:
+            return nullptr;
+        }
+    }
+    if (token.type() == NumberToken && shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless))
+        return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSUnitType::CSS_DEG);
+
+    if (token.type() == PercentageToken)
+        return consumePercent(range, valueRange);
+
+     if (token.type() != FunctionToken)
+         return nullptr;
+
+    CalcParser angleCalcParser(range, CalculationCategory::Angle, valueRange);
+    if (const CSSCalcValue* calculation = angleCalcParser.value()) {
+        if (calculation->category() == CalculationCategory::Angle)
+            return angleCalcParser.consumeValue();
+    }
+
+    CalcParser percentCalcParser(range, CalculationCategory::Percent, valueRange);
+    if (const CSSCalcValue* calculation = percentCalcParser.value()) {
+        if (calculation->category() == CalculationCategory::Percent)
+            return percentCalcParser.consumeValue();
+    }
+    return nullptr;
+}
+
 RefPtr<CSSPrimitiveValue> consumeTime(CSSParserTokenRange& range, CSSParserMode cssParserMode, ValueRange valueRange, UnitlessQuirk unitless)
 {
     const CSSParserToken& token = range.peek();
-    CSSPrimitiveValue::UnitTypes unit = token.unitType();
-    bool acceptUnitless = token.type() == NumberToken && shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless);
+    CSSUnitType unit = token.unitType();
+    bool acceptUnitless = token.type() == NumberToken && unitless == UnitlessQuirk::Allow && shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless);
     if (acceptUnitless)
-        unit = CSSPrimitiveValue::UnitTypes::CSS_MS;
+        unit = CSSUnitType::CSS_MS;
     if (token.type() == DimensionToken || acceptUnitless) {
         if (valueRange == ValueRangeNonNegative && token.numericValue() < 0)
             return nullptr;
-        if (unit == CSSPrimitiveValue::UnitTypes::CSS_MS || unit == CSSPrimitiveValue::UnitTypes::CSS_S)
-            return CSSPrimitiveValue::create(range.consumeIncludingWhitespace().numericValue(), token.unitType());
+        if (unit == CSSUnitType::CSS_MS || unit == CSSUnitType::CSS_S)
+            return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), unit);
         return nullptr;
     }
-    CalcParser calcParser(range, valueRange);
+
+    if (token.type() != FunctionToken)
+        return nullptr;
+
+    CalcParser calcParser(range, CalculationCategory::Time, valueRange);
     if (const CSSCalcValue* calculation = calcParser.value()) {
-        if (calculation->category() == CalcTime)
+        if (calculation->category() == CalculationCategory::Time)
             return calcParser.consumeValue();
     }
     return nullptr;
 }
 
+RefPtr<CSSPrimitiveValue> consumeResolution(CSSParserTokenRange& range, AllowXResolutionUnit allowX)
+{
+    const CSSParserToken& token = range.peek();
+    // Unlike the other types, calc() does not work with <resolution>.
+    if (token.type() != DimensionToken)
+        return nullptr;
+    CSSUnitType unit = token.unitType();
+    if (unit == CSSUnitType::CSS_DPPX || unit == CSSUnitType::CSS_DPI || unit == CSSUnitType::CSS_DPCM)
+        return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), unit);
+    if (allowX == AllowXResolutionUnit::Allow && token.value() == "x")
+        return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSUnitType::CSS_DPPX);
+
+    return nullptr;
+}
+
 RefPtr<CSSPrimitiveValue> consumeIdent(CSSParserTokenRange& range)
 {
     if (range.peek().type() != IdentToken)
         return nullptr;
-    return CSSPrimitiveValue::createIdentifier(range.consumeIncludingWhitespace().id());
+    return CSSValuePool::singleton().createIdentifierValue(range.consumeIncludingWhitespace().id());
 }
 
 RefPtr<CSSPrimitiveValue> consumeIdentRange(CSSParserTokenRange& range, CSSValueID lower, CSSValueID upper)
@@ -333,18 +468,21 @@ RefPtr<CSSPrimitiveValue> consumeIdentRange(CSSParserTokenRange& range, CSSValue
     return consumeIdent(range);
 }
 
-RefPtr<CSSCustomIdentValue> consumeCustomIdent(CSSParserTokenRange& range)
+// FIXME-NEWPARSER: Eventually we'd like this to use CSSCustomIdentValue, but we need
+// to do other plumbing work first (like changing Pair to CSSValuePair and make it not
+// use only primitive values).
+RefPtr<CSSPrimitiveValue> consumeCustomIdent(CSSParserTokenRange& range)
 {
     if (range.peek().type() != IdentToken || isCSSWideKeyword(range.peek().id()))
         return nullptr;
-    return CSSCustomIdentValue::create(range.consumeIncludingWhitespace().value().toString());
+    return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().value().toString(), CSSUnitType::CSS_STRING);
 }
 
 RefPtr<CSSPrimitiveValue> consumeString(CSSParserTokenRange& range)
 {
     if (range.peek().type() != StringToken)
         return nullptr;
-    return CSSPrimitiveValue::create(range.consumeIncludingWhitespace().value().toString(), CSSPrimitiveValue::UnitTypes::CSS_STRING);
+    return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().value().toString(), CSSUnitType::CSS_STRING);
 }
 
 StringView consumeUrlAsStringView(CSSParserTokenRange& range)
@@ -374,100 +512,202 @@ RefPtr<CSSPrimitiveValue> consumeUrl(CSSParserTokenRange& range)
     StringView url = consumeUrlAsStringView(range);
     if (url.isNull())
         return nullptr;
-    return CSSPrimitiveValue::create(url.toString(), CSSPrimitiveValue::UnitTypes::CSS_URI);
+    return CSSValuePool::singleton().createValue(url.toString(), CSSUnitType::CSS_URI);
 }
 
 static int clampRGBComponent(const CSSPrimitiveValue& value)
 {
-    double result = value.getDoubleValue();
-    // FIXME: Multiply by 2.55 and round instead of floor.
+    double result = value.doubleValue();
     if (value.isPercentage())
-        result *= 2.56;
-    return clampTo<int>(result, 0, 255);
+        result = result / 100.0 * 255.0;
+
+    return clampTo<int>(round(result), 0, 255);
 }
 
-static bool parseRGBParameters(CSSParserTokenRange& range, RGBA32& result, bool parseAlpha)
+static Color parseRGBParameters(CSSParserTokenRange& range)
 {
     ASSERT(range.peek().functionId() == CSSValueRgb || range.peek().functionId() == CSSValueRgba);
+    Color result;
     CSSParserTokenRange args = consumeFunction(range);
-    RefPtr<CSSPrimitiveValue> colorParameter = consumeInteger(args);
+    RefPtr<CSSPrimitiveValue> colorParameter = consumeNumber(args, ValueRangeAll);
     if (!colorParameter)
         colorParameter = consumePercent(args, ValueRangeAll);
     if (!colorParameter)
-        return false;
+        return Color();
+
     const bool isPercent = colorParameter->isPercentage();
+
+    enum class ColorSyntax {
+        Commas,
+        WhitespaceSlash,
+    };
+
+    ColorSyntax syntax = ColorSyntax::Commas;
+    auto consumeSeparator = [&] {
+        if (syntax == ColorSyntax::Commas)
+            return consumeCommaIncludingWhitespace(args);
+        
+        return true;
+    };
+
     int colorArray[3];
     colorArray[0] = clampRGBComponent(*colorParameter);
     for (int i = 1; i < 3; i++) {
-        if (!consumeCommaIncludingWhitespace(args))
-            return false;
-        colorParameter = isPercent ? consumePercent(args, ValueRangeAll) : consumeInteger(args);
+        if (i == 1)
+            syntax = consumeCommaIncludingWhitespace(args) ? ColorSyntax::Commas : ColorSyntax::WhitespaceSlash;
+        else if (!consumeSeparator())
+            return Color();
+
+        colorParameter = isPercent ? consumePercent(args, ValueRangeAll) : consumeNumber(args, ValueRangeAll);
         if (!colorParameter)
-            return false;
+            return Color();
         colorArray[i] = clampRGBComponent(*colorParameter);
     }
-    if (parseAlpha) {
-        if (!consumeCommaIncludingWhitespace(args))
-            return false;
+
+    // Historically, alpha was only parsed for rgba(), but css-color-4 specifies that rgba() is a simple alias for rgb().
+    auto consumeAlphaSeparator = [&] {
+        if (syntax == ColorSyntax::Commas)
+            return consumeCommaIncludingWhitespace(args);
+        
+        return consumeSlashIncludingWhitespace(args);
+    };
+
+    int alphaComponent = 255;
+    if (consumeAlphaSeparator()) {
         double alpha;
-        if (!consumeNumberRaw(args, alpha))
-            return false;
-        // Convert the floating pointer number of alpha to an integer in the range [0, 256),
-        // with an equal distribution across all 256 values.
-        int alphaComponent = static_cast<int>(clampTo<double>(alpha, 0.0, 1.0) * nextafter(256.0, 0.0));
-        result = makeRGBA(colorArray[0], colorArray[1], colorArray[2], alphaComponent);
-    } else {
-        result = makeRGB(colorArray[0], colorArray[1], colorArray[2]);
-    }
-    return args.atEnd();
+        if (!consumeNumberRaw(args, alpha)) {
+            auto alphaPercent = consumePercent(args, ValueRangeAll);
+            if (!alphaPercent)
+                return Color();
+            alpha = alphaPercent->doubleValue() / 100.0;
+        }
+
+        // W3 standard stipulates a 2.55 alpha value multiplication factor.
+        alphaComponent = static_cast<int>(lroundf(clampTo<double>(alpha, 0.0, 1.0) * 255.0f));
+    };
+
+    result = Color(makeRGBA(colorArray[0], colorArray[1], colorArray[2], alphaComponent));
+
+    if (!args.atEnd())
+        return Color();
+
+    return result;
 }
 
-static bool parseHSLParameters(CSSParserTokenRange& range, RGBA32& result, bool parseAlpha)
+static Color parseHSLParameters(CSSParserTokenRange& range, CSSParserMode cssParserMode)
 {
     ASSERT(range.peek().functionId() == CSSValueHsl || range.peek().functionId() == CSSValueHsla);
     CSSParserTokenRange args = consumeFunction(range);
-    RefPtr<CSSPrimitiveValue> hslValue = consumeNumber(args, ValueRangeAll);
-    if (!hslValue)
-        return false;
+    auto hslValue = consumeAngle(args, cssParserMode, UnitlessQuirk::Forbid);
+    double angleInDegrees;
+    if (!hslValue) {
+        hslValue = consumeNumber(args, ValueRangeAll);
+        if (!hslValue)
+            return Color();
+        angleInDegrees = hslValue->doubleValue();
+    } else
+        angleInDegrees = hslValue->computeDegrees();
+
     double colorArray[3];
-    colorArray[0] = (((hslValue->getIntValue() % 360) + 360) % 360) / 360.0;
+    colorArray[0] = fmod(fmod(angleInDegrees, 360.0) + 360.0, 360.0) / 360.0;
+    bool requiresCommas = false;
     for (int i = 1; i < 3; i++) {
-        if (!consumeCommaIncludingWhitespace(args))
-            return false;
+        if (consumeCommaIncludingWhitespace(args)) {
+            if (i != 1 && !requiresCommas)
+                return Color();
+            requiresCommas = true;
+        } else if (requiresCommas || args.atEnd() || (&args.peek() - 1)->type() != WhitespaceToken)
+            return Color();
         hslValue = consumePercent(args, ValueRangeAll);
         if (!hslValue)
-            return false;
-        double doubleValue = hslValue->getDoubleValue();
+            return Color();
+        double doubleValue = hslValue->doubleValue();
         colorArray[i] = clampTo<double>(doubleValue, 0.0, 100.0) / 100.0; // Needs to be value between 0 and 1.0.
     }
+
     double alpha = 1.0;
-    if (parseAlpha) {
-        if (!consumeCommaIncludingWhitespace(args))
-            return false;
-        if (!consumeNumberRaw(args, alpha))
-            return false;
+    bool commaConsumed = consumeCommaIncludingWhitespace(args);
+    bool slashConsumed = consumeSlashIncludingWhitespace(args);
+    if ((commaConsumed && !requiresCommas) || (slashConsumed && requiresCommas))
+        return Color();
+    if (commaConsumed || slashConsumed) {
+        if (!consumeNumberRaw(args, alpha)) {
+            auto alphaPercent = consumePercent(args, ValueRangeAll);
+            if (!alphaPercent)
+                return Color();
+            alpha = alphaPercent->doubleValue() / 100.0f;
+        }
         alpha = clampTo<double>(alpha, 0.0, 1.0);
     }
-    result = makeRGBAFromHSLA(colorArray[0], colorArray[1], colorArray[2], alpha);
-    return args.atEnd();
+
+    if (!args.atEnd())
+        return Color();
+
+    return Color(makeRGBAFromHSLA(static_cast<float>(colorArray[0]), static_cast<float>(colorArray[1]), static_cast<float>(colorArray[2]), static_cast<float>(alpha)));
 }
 
-static bool parseHexColor(CSSParserTokenRange& range, RGBA32& result, bool acceptQuirkyColors)
+static Color parseColorFunctionParameters(CSSParserTokenRange& range)
 {
+    ASSERT(range.peek().functionId() == CSSValueColor);
+    CSSParserTokenRange args = consumeFunction(range);
+
+    ColorSpace colorSpace;
+    switch (args.peek().id()) {
+    case CSSValueSRGB:
+        colorSpace = ColorSpace::SRGB;
+        break;
+    case CSSValueDisplayP3:
+        colorSpace = ColorSpace::DisplayP3;
+        break;
+    default:
+        return Color();
+    }
+    consumeIdent(args);
+
+    double colorChannels[4] = { 0, 0, 0, 1 };
+    for (int i = 0; i < 3; ++i) {
+        double value;
+        if (consumeNumberRaw(args, value))
+            colorChannels[i] = std::max(0.0, std::min(1.0, value));
+        else
+            break;
+    }
+
+    if (consumeSlashIncludingWhitespace(args)) {
+        auto alphaParameter = consumePercent(args, ValueRangeAll);
+        if (!alphaParameter)
+            alphaParameter = consumeNumber(args, ValueRangeAll);
+        if (!alphaParameter)
+            return Color();
+
+        colorChannels[3] = std::max(0.0, std::min(1.0, alphaParameter->isPercentage() ? (alphaParameter->doubleValue() / 100) : alphaParameter->doubleValue()));
+    }
+
+    // FIXME: Support the comma-separated list of fallback color values.
+
+    if (!args.atEnd())
+        return Color();
+    
+    return Color(colorChannels[0], colorChannels[1], colorChannels[2], colorChannels[3], colorSpace);
+}
+
+static Color parseHexColor(CSSParserTokenRange& range, bool acceptQuirkyColors)
+{
+    RGBA32 result;
     const CSSParserToken& token = range.peek();
     if (token.type() == HashToken) {
         if (!Color::parseHexColor(token.value(), result))
-            return false;
+            return Color();
     } else if (acceptQuirkyColors) {
         String color;
         if (token.type() == NumberToken || token.type() == DimensionToken) {
             if (token.numericValueType() != IntegerValueType
                 || token.numericValue() < 0. || token.numericValue() >= 1000000.)
-                return false;
+                return Color();
             if (token.type() == NumberToken) // e.g. 112233
-                color = String::format("%d", static_cast<int>(token.numericValue()));
+                color = String::number(static_cast<int>(token.numericValue()));
             else // e.g. 0001FF
-                color = String::number(static_cast<int>(token.numericValue())) + token.value().toString();
+                color = makeString(static_cast<int>(token.numericValue()), token.value().toString());
             while (color.length() < 6)
                 color = "0" + color;
         } else if (token.type() == IdentToken) { // e.g. FF0000
@@ -475,27 +715,39 @@ static bool parseHexColor(CSSParserTokenRange& range, RGBA32& result, bool accep
         }
         unsigned length = color.length();
         if (length != 3 && length != 6)
-            return false;
+            return Color();
         if (!Color::parseHexColor(color, result))
-            return false;
+            return Color();
     } else {
-        return false;
+        return Color();
     }
     range.consumeIncludingWhitespace();
-    return true;
+    return Color(result);
 }
 
-static bool parseColorFunction(CSSParserTokenRange& range, RGBA32& result)
+static Color parseColorFunction(CSSParserTokenRange& range, CSSParserMode cssParserMode)
 {
-    CSSValueID functionId = range.peek().functionId();
-    if (functionId < CSSValueRgb || functionId > CSSValueHsla)
-        return false;
     CSSParserTokenRange colorRange = range;
-    if ((functionId <= CSSValueRgba && !parseRGBParameters(colorRange, result, functionId == CSSValueRgba))
-        || (functionId >= CSSValueHsl && !parseHSLParameters(colorRange, result, functionId == CSSValueHsla)))
-        return false;
-    range = colorRange;
-    return true;
+    CSSValueID functionId = range.peek().functionId();
+    Color color;
+    switch (functionId) {
+    case CSSValueRgb:
+    case CSSValueRgba:
+        color = parseRGBParameters(colorRange);
+        break;
+    case CSSValueHsl:
+    case CSSValueHsla:
+        color = parseHSLParameters(colorRange, cssParserMode);
+        break;
+    case CSSValueColor:
+        color = parseColorFunctionParameters(colorRange);
+        break;
+    default:
+        return Color();
+    }
+    if (color.isValid())
+        range = colorRange;
+    return color;
 }
 
 RefPtr<CSSPrimitiveValue> consumeColor(CSSParserTokenRange& range, CSSParserMode cssParserMode, bool acceptQuirkyColors)
@@ -506,10 +758,12 @@ RefPtr<CSSPrimitiveValue> consumeColor(CSSParserTokenRange& range, CSSParserMode
             return nullptr;
         return consumeIdent(range);
     }
-    RGBA32 color = Color::transparent;
-    if (!parseHexColor(range, color, acceptQuirkyColors) && !parseColorFunction(range, color))
+    Color color = parseHexColor(range, acceptQuirkyColors);
+    if (!color.isValid())
+        color = parseColorFunction(range, cssParserMode);
+    if (!color.isValid())
         return nullptr;
-    return CSSPrimitiveValue::create(Color(color));
+    return CSSValuePool::singleton().createValue(color);
 }
 
 static RefPtr<CSSPrimitiveValue> consumePositionComponent(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
@@ -521,12 +775,12 @@ static RefPtr<CSSPrimitiveValue> consumePositionComponent(CSSParserTokenRange& r
 
 static bool isHorizontalPositionKeywordOnly(const CSSPrimitiveValue& value)
 {
-    return value.isValueID() && (value.getValueID() == CSSValueLeft || value.getValueID() == CSSValueRight);
+    return value.isValueID() && (value.valueID() == CSSValueLeft || value.valueID() == CSSValueRight);
 }
 
 static bool isVerticalPositionKeywordOnly(const CSSPrimitiveValue& value)
 {
-    return value.isValueID() && (value.getValueID() == CSSValueTop || value.getValueID() == CSSValueBottom);
+    return value.isValueID() && (value.valueID() == CSSValueTop || value.valueID() == CSSValueBottom);
 }
 
 static void positionFromOneValue(CSSPrimitiveValue& value, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
@@ -553,22 +807,33 @@ static bool positionFromTwoValues(CSSPrimitiveValue& value1, CSSPrimitiveValue&
     return true;
 }
 
-    
+namespace CSSPropertyParserHelpersInternal {
 template<typename... Args>
 static Ref<CSSPrimitiveValue> createPrimitiveValuePair(Args&&... args)
 {
     return CSSValuePool::singleton().createValue(Pair::create(std::forward<Args>(args)...));
 }
+}
 
-static bool positionFromThreeOrFourValues(CSSPrimitiveValue** values, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
+// https://drafts.csswg.org/css-backgrounds-3/#propdef-background-position
+// background-position has special parsing rules, allowing a 3-value syntax:
+// <bg-position> =  [ left | center | right | top | bottom | <length-percentage> ]
+// |
+//   [ left | center | right | <length-percentage> ]
+//   [ top | center | bottom | <length-percentage> ]
+// |
+//   [ center | [ left | right ] <length-percentage>? ] &&
+//   [ center | [ top | bottom ] <length-percentage>? ]
+//
+static bool backgroundPositionFromThreeValues(const std::array<CSSPrimitiveValue*, 5>& values, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
 {
     CSSPrimitiveValue* center = nullptr;
     for (int i = 0; values[i]; i++) {
         CSSPrimitiveValue* currentValue = values[i];
         if (!currentValue->isValueID())
             return false;
-        CSSValueID id = currentValue->getValueID();
 
+        CSSValueID id = currentValue->valueID();
         if (id == CSSValueCenter) {
             if (center)
                 return false;
@@ -578,7 +843,7 @@ static bool positionFromThreeOrFourValues(CSSPrimitiveValue** values, RefPtr<CSS
 
         RefPtr<CSSPrimitiveValue> result;
         if (values[i + 1] && !values[i + 1]->isValueID())
-            result = createPrimitiveValuePair(currentValue, values[++i]);
+            result = CSSPropertyParserHelpersInternal::createPrimitiveValuePair(currentValue, values[++i]);
         else
             result = currentValue;
 
@@ -608,9 +873,52 @@ static bool positionFromThreeOrFourValues(CSSPrimitiveValue** values, RefPtr<CSS
     return true;
 }
 
+// https://drafts.csswg.org/css-values-4/#typedef-position
+// <position> = [
+//   [ left | center | right ] || [ top | center | bottom ]
+// |
+//   [ left | center | right | <length-percentage> ]
+//   [ top | center | bottom | <length-percentage> ]?
+// |
+//   [ [ left | right ] <length-percentage> ] &&
+//   [ [ top | bottom ] <length-percentage> ]
+//
+static bool positionFromFourValues(const std::array<CSSPrimitiveValue*, 5>& values, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
+{
+    for (int i = 0; values[i]; i++) {
+        CSSPrimitiveValue* currentValue = values[i];
+        if (!currentValue->isValueID())
+            return false;
+
+        CSSValueID id = currentValue->valueID();
+        if (id == CSSValueCenter)
+            return false;
+
+        RefPtr<CSSPrimitiveValue> result;
+        if (values[i + 1] && !values[i + 1]->isValueID())
+            result = CSSPropertyParserHelpersInternal::createPrimitiveValuePair(currentValue, values[++i]);
+        else
+            result = currentValue;
+
+        if (id == CSSValueLeft || id == CSSValueRight) {
+            if (resultX)
+                return false;
+            resultX = result;
+        } else {
+            ASSERT(id == CSSValueTop || id == CSSValueBottom);
+            if (resultY)
+                return false;
+            resultY = result;
+        }
+    }
+
+    ASSERT(resultX && resultY);
+    return true;
+}
+
 // FIXME: This may consume from the range upon failure. The background
 // shorthand works around it, but we should just fix it here.
-bool consumePosition(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
+bool consumePosition(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless, PositionSyntax positionSyntax, RefPtr<CSSPrimitiveValue>& resultX, RefPtr<CSSPrimitiveValue>& resultY)
 {
     RefPtr<CSSPrimitiveValue> value1 = consumePositionComponent(range, cssParserMode, unitless);
     if (!value1)
@@ -627,21 +935,29 @@ bool consumePosition(CSSParserTokenRange& range, CSSParserMode cssParserMode, Un
         return positionFromTwoValues(*value1, *value2, resultX, resultY);
 
     RefPtr<CSSPrimitiveValue> value4 = consumePositionComponent(range, cssParserMode, unitless);
-    CSSPrimitiveValue* values[5];
+    
+    std::array<CSSPrimitiveValue*, 5> values;
     values[0] = value1.get();
     values[1] = value2.get();
     values[2] = value3.get();
     values[3] = value4.get();
     values[4] = nullptr;
-    return positionFromThreeOrFourValues(values, resultX, resultY);
+    
+    if (value4)
+        return positionFromFourValues(values, resultX, resultY);
+    
+    if (positionSyntax != PositionSyntax::BackgroundPosition)
+        return false;
+    
+    return backgroundPositionFromThreeValues(values, resultX, resultY);
 }
 
-RefPtr<CSSPrimitiveValue> consumePosition(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless)
+RefPtr<CSSPrimitiveValue> consumePosition(CSSParserTokenRange& range, CSSParserMode cssParserMode, UnitlessQuirk unitless, PositionSyntax positionSyntax)
 {
     RefPtr<CSSPrimitiveValue> resultX;
     RefPtr<CSSPrimitiveValue> resultY;
-    if (consumePosition(range, cssParserMode, unitless, resultX, resultY))
-        return createPrimitiveValuePair(resultX.releaseNonNull(), resultY.releaseNonNull());
+    if (consumePosition(range, cssParserMode, unitless, positionSyntax, resultX, resultY))
+        return CSSPropertyParserHelpersInternal::createPrimitiveValuePair(resultX.releaseNonNull(), resultY.releaseNonNull());
     return nullptr;
 }
 
@@ -663,11 +979,11 @@ static RefPtr<CSSPrimitiveValue> consumeDeprecatedGradientPoint(CSSParserTokenRa
 {
     if (args.peek().type() == IdentToken) {
         if ((horizontal && consumeIdent<CSSValueLeft>(args)) || (!horizontal && consumeIdent<CSSValueTop>(args)))
-            return CSSPrimitiveValue::create(0., CSSPrimitiveValue::UnitTypes::CSS_PERCENTAGE);
+            return CSSValuePool::singleton().createValue(0., CSSUnitType::CSS_PERCENTAGE);
         if ((horizontal && consumeIdent<CSSValueRight>(args)) || (!horizontal && consumeIdent<CSSValueBottom>(args)))
-            return CSSPrimitiveValue::create(100., CSSPrimitiveValue::UnitTypes::CSS_PERCENTAGE);
+            return CSSValuePool::singleton().createValue(100., CSSUnitType::CSS_PERCENTAGE);
         if (consumeIdent<CSSValueCenter>(args))
-            return CSSPrimitiveValue::create(50., CSSPrimitiveValue::UnitTypes::CSS_PERCENTAGE);
+            return CSSValuePool::singleton().createValue(50., CSSUnitType::CSS_PERCENTAGE);
         return nullptr;
     }
     RefPtr<CSSPrimitiveValue> result = consumePercent(args, ValueRangeAll);
@@ -696,19 +1012,16 @@ static bool consumeDeprecatedGradientColorStop(CSSParserTokenRange& range, CSSGr
         position = (id == CSSValueFrom) ? 0 : 1;
     } else {
         ASSERT(id == CSSValueColorStop);
-        const CSSParserToken& arg = args.consumeIncludingWhitespace();
-        if (arg.type() == PercentageToken)
-            position = arg.numericValue() / 100.0;
-        else if (arg.type() == NumberToken)
-            position = arg.numericValue();
-        else
+        if (auto percentValue = consumePercent(args, ValueRangeAll))
+            position = percentValue->doubleValue() / 100.0;
+        else if (!consumeNumberRaw(args, position))
             return false;
 
         if (!consumeCommaIncludingWhitespace(args))
             return false;
     }
 
-    stop.m_position = CSSPrimitiveValue::create(position, CSSPrimitiveValue::UnitTypes::CSS_NUMBER);
+    stop.m_position = CSSValuePool::singleton().createValue(position, CSSUnitType::CSS_NUMBER);
     stop.m_color = consumeDeprecatedGradientStopColor(args, cssParserMode);
     return stop.m_color && args.atEnd();
 }
@@ -725,43 +1038,43 @@ static RefPtr<CSSValue> consumeDeprecatedGradient(CSSParserTokenRange& args, CSS
     if (!result || !consumeCommaIncludingWhitespace(args))
         return nullptr;
 
-    RefPtr<CSSPrimitiveValue> point = consumeDeprecatedGradientPoint(args, true);
+    auto point = consumeDeprecatedGradientPoint(args, true);
     if (!point)
         return nullptr;
-    result->setFirstX(WTFMove(point));
+    result->setFirstX(point.copyRef());
     point = consumeDeprecatedGradientPoint(args, false);
     if (!point)
         return nullptr;
-    result->setFirstY(WTFMove(point));
+    result->setFirstY(point.copyRef());
 
     if (!consumeCommaIncludingWhitespace(args))
         return nullptr;
 
     // For radial gradients only, we now expect a numeric radius.
     if (isDeprecatedRadialGradient) {
-        RefPtr<CSSPrimitiveValue> radius = consumeNumber(args, ValueRangeAll);
+        auto radius = consumeNumber(args, ValueRangeNonNegative);
         if (!radius || !consumeCommaIncludingWhitespace(args))
             return nullptr;
-        downcast<CSSRadialGradientValue>(result.get())->setFirstRadius(WTFMove(radius));
+        downcast<CSSRadialGradientValue>(result.get())->setFirstRadius(radius.copyRef());
     }
 
     point = consumeDeprecatedGradientPoint(args, true);
     if (!point)
         return nullptr;
-    result->setSecondX(WTFMove(point));
+    result->setSecondX(point.copyRef());
     point = consumeDeprecatedGradientPoint(args, false);
     if (!point)
         return nullptr;
-    result->setSecondY(WTFMove(point));
+    result->setSecondY(point.copyRef());
 
     // For radial gradients only, we now expect the second radius.
     if (isDeprecatedRadialGradient) {
         if (!consumeCommaIncludingWhitespace(args))
             return nullptr;
-        RefPtr<CSSPrimitiveValue> radius = consumeNumber(args, ValueRangeAll);
+        auto radius = consumeNumber(args, ValueRangeNonNegative);
         if (!radius)
             return nullptr;
-        downcast<CSSRadialGradientValue>(result.get())->setSecondRadius(WTFMove(radius));
+        downcast<CSSRadialGradientValue>(result.get())->setSecondRadius(radius.copyRef());
     }
 
     CSSGradientColorStop stop;
@@ -771,12 +1084,15 @@ static RefPtr<CSSValue> consumeDeprecatedGradient(CSSParserTokenRange& args, CSS
         result->addStop(stop);
     }
 
+    result->doneAddingStops();
     return result;
 }
 
-static bool consumeGradientColorStops(CSSParserTokenRange& range, CSSParserMode cssParserMode, CSSGradientValue* gradient)
+static bool consumeGradientColorStops(CSSParserTokenRange& range, CSSParserMode cssParserMode, CSSGradientValue& gradient)
 {
-    bool supportsColorHints = gradient->gradientType() == CSSLinearGradient || gradient->gradientType() == CSSRadialGradient;
+    bool supportsColorHints = gradient.gradientType() == CSSLinearGradient || gradient.gradientType() == CSSRadialGradient || gradient.gradientType() == CSSConicGradient;
+    
+    bool isConicGradient = gradient.gradientType() == CSSConicGradient;
 
     // The first color stop cannot be a color hint.
     bool previousStopWasColorHint = true;
@@ -786,19 +1102,44 @@ static bool consumeGradientColorStops(CSSParserTokenRange& 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);
+        
+        // FIXME-NEWPARSER: This boolean could be removed. Null checking color would be sufficient.
+        stop.isMidpoint = !stop.m_color;
+
+        if (isConicGradient)
+            stop.m_position = consumeAngleOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Forbid);
+        else
+            stop.m_position = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll);
+        
         if (!stop.m_color && !stop.m_position)
             return false;
-        gradient->addStop(stop);
+
+        gradient.addStop(stop);
+
+        if (!stop.m_color || !stop.m_position)
+            continue;
+
+        CSSGradientColorStop secondStop;
+        if (isConicGradient)
+            secondStop.m_position = consumeAngleOrPercent(range, cssParserMode, ValueRangeAll, UnitlessQuirk::Forbid);
+        else
+            secondStop.m_position = consumeLengthOrPercent(range, cssParserMode, ValueRangeAll);
+        
+        if (secondStop.m_position)
+            gradient.addStop(secondStop);
+        
     } while (consumeCommaIncludingWhitespace(range));
 
+    gradient.doneAddingStops();
+
     // 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;
+    return gradient.stopCount() >= 2;
 }
 
 static RefPtr<CSSValue> consumeDeprecatedRadialGradient(CSSParserTokenRange& args, CSSParserMode cssParserMode, CSSGradientRepeat repeating)
@@ -810,34 +1151,34 @@ static RefPtr<CSSValue> consumeDeprecatedRadialGradient(CSSParserTokenRange& arg
     if ((centerX || centerY) && !consumeCommaIncludingWhitespace(args))
         return nullptr;
 
-    result->setFirstX(WTFMove(centerX));
-    result->setFirstY(WTFMove(centerY));
-    result->setSecondX(WTFMove(centerX));
-    result->setSecondY(WTFMove(centerY));
+    result->setFirstX(centerX.copyRef());
+    result->setFirstY(centerY.copyRef());
+    result->setSecondX(centerX.copyRef());
+    result->setSecondY(centerY.copyRef());
 
-    RefPtr<CSSPrimitiveValue> shape = consumeIdent<CSSValueCircle, CSSValueEllipse>(args);
-    RefPtr<CSSPrimitiveValue> sizeKeyword = consumeIdent<CSSValueClosestSide, CSSValueClosestCorner, CSSValueFarthestSide, CSSValueFarthestCorner, CSSValueContain, CSSValueCover>(args);
+    auto shape = consumeIdent<CSSValueCircle, CSSValueEllipse>(args);
+    auto sizeKeyword = consumeIdent<CSSValueClosestSide, CSSValueClosestCorner, CSSValueFarthestSide, CSSValueFarthestCorner, CSSValueContain, CSSValueCover>(args);
     if (!shape)
         shape = consumeIdent<CSSValueCircle, CSSValueEllipse>(args);
-    result->setShape(WTFMove(shape));
-    result->setSizingBehavior(WTFMove(sizeKeyword));
+    result->setShape(shape.copyRef());
+    result->setSizingBehavior(sizeKeyword.copyRef());
 
     // Or, two lengths or percentages
     if (!shape && !sizeKeyword) {
-        RefPtr<CSSPrimitiveValue> horizontalSize = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
+        auto horizontalSize = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
         RefPtr<CSSPrimitiveValue> verticalSize;
         if (horizontalSize) {
-            verticalSize = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
+            verticalSize = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
             if (!verticalSize)
                 return nullptr;
             consumeCommaIncludingWhitespace(args);
-            result->setEndHorizontalSize(WTFMove(horizontalSize));
-            result->setEndVerticalSize(WTFMove(verticalSize));
+            result->setEndHorizontalSize(horizontalSize.copyRef());
+            result->setEndVerticalSize(verticalSize.copyRef());
         }
     } else {
         consumeCommaIncludingWhitespace(args);
     }
-    if (!consumeGradientColorStops(args, cssParserMode, result.get()))
+    if (!consumeGradientColorStops(args, cssParserMode, *result))
         return nullptr;
 
     return result;
@@ -871,13 +1212,13 @@ static RefPtr<CSSValue> consumeRadialGradient(CSSParserTokenRange& args, CSSPars
                 break;
             }
         } else {
-            RefPtr<CSSPrimitiveValue> center = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
+            auto center = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
             if (!center)
                 break;
             if (horizontalSize)
                 return nullptr;
             horizontalSize = center;
-            center = consumeLengthOrPercent(args, cssParserMode, ValueRangeAll);
+            center = consumeLengthOrPercent(args, cssParserMode, ValueRangeNonNegative);
             if (center) {
                 verticalSize = center;
                 ++i;
@@ -889,10 +1230,10 @@ static RefPtr<CSSValue> consumeRadialGradient(CSSParserTokenRange& args, CSSPars
     if (sizeKeyword && horizontalSize)
         return nullptr;
     // Circles must have 0 or 1 lengths.
-    if (shape && shape->getValueID() == CSSValueCircle && verticalSize)
+    if (shape && shape->valueID() == CSSValueCircle && verticalSize)
         return nullptr;
     // Ellipses must have 0 or 2 length/percentages.
-    if (shape && shape->getValueID() == CSSValueEllipse && horizontalSize && !verticalSize)
+    if (shape && shape->valueID() == CSSValueEllipse && horizontalSize && !verticalSize)
         return nullptr;
     // If there's only one size, it must be a length.
     if (!verticalSize && horizontalSize && horizontalSize->isPercentage())
@@ -901,30 +1242,30 @@ static RefPtr<CSSValue> consumeRadialGradient(CSSParserTokenRange& args, CSSPars
         || (verticalSize && verticalSize->isCalculatedPercentageWithLength()))
         return nullptr;
 
-    result->setShape(WTFMove(shape));
-    result->setSizingBehavior(WTFMove(sizeKeyword));
-    result->setEndHorizontalSize(WTFMove(horizontalSize));
-    result->setEndVerticalSize(WTFMove(verticalSize));
+    result->setShape(shape.copyRef());
+    result->setSizingBehavior(sizeKeyword.copyRef());
+    result->setEndHorizontalSize(horizontalSize.copyRef());
+    result->setEndVerticalSize(verticalSize.copyRef());
 
     RefPtr<CSSPrimitiveValue> centerX;
     RefPtr<CSSPrimitiveValue> centerY;
     if (args.peek().id() == CSSValueAt) {
         args.consumeIncludingWhitespace();
-        consumePosition(args, cssParserMode, UnitlessQuirk::Forbid, centerX, centerY);
+        consumePosition(args, cssParserMode, UnitlessQuirk::Forbid, PositionSyntax::Position, centerX, centerY);
         if (!(centerX && centerY))
             return nullptr;
         
-        result->setFirstX(WTFMove(centerX));
-        result->setFirstY(WTFMove(centerY));
+        result->setFirstX(centerX.copyRef());
+        result->setFirstY(centerY.copyRef());
         
         // Right now, CSS radial gradients have the same start and end centers.
-        result->setSecondX(WTFMove(centerX));
-        result->setSecondY(WTFMove(centerY));
+        result->setSecondX(centerX.copyRef());
+        result->setSecondY(centerY.copyRef());
     }
 
     if ((shape || sizeKeyword || horizontalSize || centerX || centerY) && !consumeCommaIncludingWhitespace(args))
         return nullptr;
-    if (!consumeGradientColorStops(args, cssParserMode, result.get()))
+    if (!consumeGradientColorStops(args, cssParserMode, *result))
         return nullptr;
     return result;
 }
@@ -949,17 +1290,63 @@ static RefPtr<CSSValue> consumeLinearGradient(CSSParserTokenRange& args, CSSPars
             endX = consumeIdent<CSSValueLeft, CSSValueRight>(args);
         }
 
-        result->setFirstX(WTFMove(endX));
-        result->setFirstY(WTFMove(endY));
+        result->setFirstX(endX.copyRef());
+        result->setFirstY(endY.copyRef());
     } else {
         expectComma = false;
     }
 
     if (expectComma && !consumeCommaIncludingWhitespace(args))
         return nullptr;
-    if (!consumeGradientColorStops(args, cssParserMode, result.get()))
+    if (!consumeGradientColorStops(args, cssParserMode, *result))
+        return nullptr;
+    return result;
+}
+
+static RefPtr<CSSValue> consumeConicGradient(CSSParserTokenRange& args, CSSParserContext context, CSSGradientRepeat repeating)
+{
+#if ENABLE(CSS_CONIC_GRADIENTS)
+    RefPtr<CSSConicGradientValue> result = CSSConicGradientValue::create(repeating);
+
+    bool expectComma = false;
+    if (args.peek().type() == IdentToken) {
+        if (consumeIdent<CSSValueFrom>(args)) {
+            auto angle = consumeAngle(args, context.mode, UnitlessQuirk::Forbid);
+            if (!angle)
+                return nullptr;
+            result->setAngle(angle.releaseNonNull());
+            expectComma = true;
+        }
+        
+        if (consumeIdent<CSSValueAt>(args)) {
+            RefPtr<CSSPrimitiveValue> centerX;
+            RefPtr<CSSPrimitiveValue> centerY;
+            consumePosition(args, context.mode, UnitlessQuirk::Forbid, PositionSyntax::Position, centerX, centerY);
+            if (!(centerX && centerY))
+                return nullptr;
+            
+            result->setFirstX(centerX.copyRef());
+            result->setFirstY(centerY.copyRef());
+            
+            // Right now, conic gradients have the same start and end centers.
+            result->setSecondX(centerX.copyRef());
+            result->setSecondY(centerY.copyRef());
+    
+            expectComma = true;
+        }
+    }
+
+    if (expectComma && !consumeCommaIncludingWhitespace(args))
+        return nullptr;
+    if (!consumeGradientColorStops(args, context.mode, *result))
         return nullptr;
     return result;
+#else
+    UNUSED_PARAM(args);
+    UNUSED_PARAM(context);
+    UNUSED_PARAM(repeating);
+    return nullptr;
+#endif
 }
 
 RefPtr<CSSValue> consumeImageOrNone(CSSParserTokenRange& range, CSSParserContext context)
@@ -969,7 +1356,7 @@ RefPtr<CSSValue> consumeImageOrNone(CSSParserTokenRange& range, CSSParserContext
     return consumeImage(range, context);
 }
 
-static RefPtr<CSSValue> consumeCrossFade(CSSParserTokenRange& args, CSSParserContext context)
+static RefPtr<CSSValue> consumeCrossFade(CSSParserTokenRange& args, CSSParserContext context, bool prefixed)
 {
     RefPtr<CSSValue> fromImageValue = consumeImageOrNone(args, context);
     if (!fromImageValue || !consumeCommaIncludingWhitespace(args))
@@ -979,17 +1366,76 @@ static RefPtr<CSSValue> consumeCrossFade(CSSParserTokenRange& args, CSSParserCon
         return nullptr;
 
     RefPtr<CSSPrimitiveValue> percentage;
-    const CSSParserToken& percentageArg = args.consumeIncludingWhitespace();
-    if (percentageArg.type() == PercentageToken)
-        percentage = CSSPrimitiveValue::create(clampTo<double>(percentageArg.numericValue() / 100, 0, 1), CSSPrimitiveValue::UnitTypes::CSS_NUMBER);
-    else if (percentageArg.type() == NumberToken)
-        percentage = CSSPrimitiveValue::create(clampTo<double>(percentageArg.numericValue(), 0, 1), CSSPrimitiveValue::UnitTypes::CSS_NUMBER);
+    if (auto percentValue = consumePercent(args, ValueRangeAll))
+        percentage = CSSValuePool::singleton().createValue(clampTo<double>(percentValue->doubleValue() / 100.0, 0, 1), CSSUnitType::CSS_NUMBER);
+    else if (auto numberValue = consumeNumber(args, ValueRangeAll))
+        percentage = CSSValuePool::singleton().createValue(clampTo<double>(numberValue->doubleValue(), 0, 1), CSSUnitType::CSS_NUMBER);
 
     if (!percentage)
         return nullptr;
-    return CSSCrossfadeValue::create(fromImageValue.releaseNonNull(), toImageValue.releaseNonNull(), percentage.releaseNonNull());
+    return CSSCrossfadeValue::create(fromImageValue.releaseNonNull(), toImageValue.releaseNonNull(), percentage.releaseNonNull(), prefixed);
+}
+
+static RefPtr<CSSValue> consumeWebkitCanvas(CSSParserTokenRange& args)
+{
+    if (args.peek().type() != IdentToken)
+        return nullptr;
+    auto canvasName = args.consumeIncludingWhitespace().value().toString();
+    if (!args.atEnd())
+        return nullptr;
+    return CSSCanvasValue::create(canvasName);
 }
 
+static RefPtr<CSSValue> consumeWebkitNamedImage(CSSParserTokenRange& args)
+{
+    if (args.peek().type() != IdentToken)
+        return nullptr;
+    auto imageName = args.consumeIncludingWhitespace().value().toString();
+    if (!args.atEnd())
+        return nullptr;
+    return CSSNamedImageValue::create(imageName);
+}
+
+static RefPtr<CSSValue> consumeFilterImage(CSSParserTokenRange& args, const CSSParserContext& context)
+{
+    auto imageValue = consumeImageOrNone(args, context);
+    if (!imageValue || !consumeCommaIncludingWhitespace(args))
+        return nullptr;
+
+    auto filterValue = consumeFilter(args, context, AllowedFilterFunctions::PixelFilters);
+
+    if (!filterValue)
+        return nullptr;
+
+    if (!args.atEnd())
+        return nullptr;
+
+    return CSSFilterImageValue::create(imageValue.releaseNonNull(), filterValue.releaseNonNull());
+}
+
+#if ENABLE(CSS_PAINTING_API)
+static RefPtr<CSSValue> consumeCustomPaint(CSSParserTokenRange& args)
+{
+    if (!RuntimeEnabledFeatures::sharedFeatures().cssPaintingAPIEnabled())
+        return nullptr;
+    if (args.peek().type() != IdentToken)
+        return nullptr;
+    auto name = args.consumeIncludingWhitespace().value().toString();
+
+    if (!args.atEnd() && args.peek() != CommaToken)
+        return nullptr;
+    if (!args.atEnd())
+        args.consume();
+
+    auto argumentList = CSSVariableData::create(args);
+
+    while (!args.atEnd())
+        args.consume();
+
+    return CSSPaintImageValue::create(name, WTFMove(argumentList));
+}
+#endif
+
 static RefPtr<CSSValue> consumeGeneratedImage(CSSParserTokenRange& range, CSSParserContext context)
 {
     CSSValueID id = range.peek().functionId();
@@ -1014,38 +1460,45 @@ static RefPtr<CSSValue> consumeGeneratedImage(CSSParserTokenRange& range, CSSPar
         result = consumeDeprecatedRadialGradient(args, context.mode, NonRepeating);
     else if (id == CSSValueWebkitRepeatingRadialGradient)
         result = consumeDeprecatedRadialGradient(args, context.mode, Repeating);
-    else if (id == CSSValueWebkitCrossFade)
-        result = consumeCrossFade(args, context);
-
+    else if (id == CSSValueConicGradient)
+        result = consumeConicGradient(args, context, NonRepeating);
+    else if (id == CSSValueRepeatingConicGradient)
+        result = consumeConicGradient(args, context, Repeating);
+    else if (id == CSSValueWebkitCrossFade || id == CSSValueCrossFade)
+        result = consumeCrossFade(args, context, id == CSSValueWebkitCrossFade);
+    else if (id == CSSValueWebkitCanvas)
+        result = consumeWebkitCanvas(args);
+    else if (id == CSSValueWebkitNamedImage)
+        result = consumeWebkitNamedImage(args);
+    else if (id == CSSValueWebkitFilter || id == CSSValueFilter)
+        result = consumeFilterImage(args, context);
+#if ENABLE(CSS_PAINTING_API)
+    else if (id == CSSValuePaint)
+        result = consumeCustomPaint(args);
+#endif
     if (!result || !args.atEnd())
         return nullptr;
     range = rangeCopy;
     return result;
 }
 
-static RefPtr<CSSValue> consumeImageSet(CSSParserTokenRange& range, const CSSParserContext& context)
+static RefPtr<CSSValue> consumeImageSet(CSSParserTokenRange& range, const CSSParserContext& context, OptionSet<AllowedImageType> allowedImageType)
 {
     CSSParserTokenRange rangeCopy = range;
     CSSParserTokenRange args = consumeFunction(rangeCopy);
     RefPtr<CSSImageSetValue> imageSet = CSSImageSetValue::create();
     do {
-        AtomicString urlValue = consumeUrlAsStringView(args).toAtomicString();
-        if (urlValue.isNull())
+        auto image = consumeImage(args, context, (allowedImageType | AllowedImageType::RawStringAsURL) - AllowedImageType::ImageSet);
+        if (!image)
             return nullptr;
 
-        RefPtr<CSSValue> image = CSSImageValue::create(completeURL(context, urlValue));
-        imageSet->append(*image);
+        imageSet->append(image.releaseNonNull());
 
-        const CSSParserToken& token = args.consumeIncludingWhitespace();
-        if (token.type() != DimensionToken)
-            return nullptr;
-        if (token.value() != "x")
+        auto resolution = consumeResolution(args, AllowXResolutionUnit::Allow);
+        if (!resolution || resolution->floatValue() <= 0)
             return nullptr;
-        ASSERT(token.unitType() == CSSPrimitiveValue::UnitTypes::CSS_UNKNOWN);
-        double imageScaleFactor = token.numericValue();
-        if (imageScaleFactor <= 0)
-            return nullptr;
-        imageSet->append(CSSPrimitiveValue::create(imageScaleFactor, CSSPrimitiveValue::UnitTypes::CSS_NUMBER));
+
+        imageSet->append(resolution.releaseNonNull());
     } while (consumeCommaIncludingWhitespace(args));
     if (!args.atEnd())
         return nullptr;
@@ -1055,25 +1508,218 @@ static RefPtr<CSSValue> consumeImageSet(CSSParserTokenRange& range, const CSSPar
 
 static bool isGeneratedImage(CSSValueID id)
 {
-    return id == CSSValueLinearGradient || id == CSSValueRadialGradient
-        || id == CSSValueRepeatingLinearGradient || id == CSSValueRepeatingRadialGradient
-        || id == CSSValueWebkitLinearGradient || id == CSSValueWebkitRadialGradient
-        || id == CSSValueWebkitRepeatingLinearGradient || id == CSSValueWebkitRepeatingRadialGradient
-        || id == CSSValueWebkitGradient || id == CSSValueWebkitCrossFade || id == CSSValuePaint;
+    return id == CSSValueLinearGradient
+        || id == CSSValueRadialGradient
+        || id == CSSValueConicGradient
+        || id == CSSValueRepeatingLinearGradient
+        || id == CSSValueRepeatingRadialGradient
+        || id == CSSValueRepeatingConicGradient
+        || id == CSSValueWebkitLinearGradient
+        || id == CSSValueWebkitRadialGradient
+        || id == CSSValueWebkitRepeatingLinearGradient
+        || id == CSSValueWebkitRepeatingRadialGradient
+        || id == CSSValueWebkitGradient
+        || id == CSSValueWebkitCrossFade
+        || id == CSSValueWebkitCanvas
+        || id == CSSValueCrossFade
+        || id == CSSValueWebkitNamedImage
+        || id == CSSValueWebkitFilter
+#if ENABLE(CSS_PAINTING_API)
+        || id == CSSValuePaint
+#endif
+        || id == CSSValueFilter;
+}
+    
+static bool isPixelFilterFunction(CSSValueID filterFunction)
+{
+    switch (filterFunction) {
+    case CSSValueBlur:
+    case CSSValueBrightness:
+    case CSSValueContrast:
+    case CSSValueDropShadow:
+    case CSSValueGrayscale:
+    case CSSValueHueRotate:
+    case CSSValueInvert:
+    case CSSValueOpacity:
+    case CSSValueSaturate:
+    case CSSValueSepia:
+        return true;
+    default:
+        return false;
+    }
+}
+
+static bool isColorFilterFunction(CSSValueID filterFunction)
+{
+    switch (filterFunction) {
+    case CSSValueBrightness:
+    case CSSValueContrast:
+    case CSSValueGrayscale:
+    case CSSValueHueRotate:
+    case CSSValueInvert:
+    case CSSValueOpacity:
+    case CSSValueSaturate:
+    case CSSValueSepia:
+    case CSSValueAppleInvertLightness:
+        return true;
+    default:
+        return false;
+    }
 }
 
-RefPtr<CSSValue> consumeImage(CSSParserTokenRange& range, CSSParserContext context, ConsumeGeneratedImage generatedImage)
+static bool allowsValuesGreaterThanOne(CSSValueID filterFunction)
 {
-    AtomicString uri = consumeUrlAsStringView(range).toAtomicString();
-    if (!uri.isNull())
-        return CSSImageValue::create(completeURL(context, uri));
+    switch (filterFunction) {
+    case CSSValueBrightness:
+    case CSSValueContrast:
+    case CSSValueSaturate:
+        return true;
+    default:
+        return false;
+    }
+}
+
+static RefPtr<CSSFunctionValue> consumeFilterFunction(CSSParserTokenRange& range, const CSSParserContext& context, AllowedFilterFunctions allowedFunctions)
+{
+    CSSValueID filterType = range.peek().functionId();
+    switch (allowedFunctions) {
+    case AllowedFilterFunctions::PixelFilters:
+        if (!isPixelFilterFunction(filterType))
+            return nullptr;
+        break;
+    case AllowedFilterFunctions::ColorFilters:
+        if (!isColorFilterFunction(filterType))
+            return nullptr;
+        break;
+    }
+
+    CSSParserTokenRange args = consumeFunction(range);
+    RefPtr<CSSFunctionValue> filterValue = CSSFunctionValue::create(filterType);
+
+    if (filterType == CSSValueAppleInvertLightness) {
+        if (!args.atEnd())
+            return nullptr;
+        return filterValue;
+    }
+
+    RefPtr<CSSValue> parsedValue;
+
+    if (filterType == CSSValueDropShadow)
+        parsedValue = consumeSingleShadow(args, context.mode, false, false);
+    else {
+        if (args.atEnd())
+            return filterValue;
+
+        if (filterType == CSSValueHueRotate)
+            parsedValue = consumeAngle(args, context.mode, UnitlessQuirk::Forbid);
+        else if (filterType == CSSValueBlur)
+            parsedValue = consumeLength(args, HTMLStandardMode, ValueRangeNonNegative);
+        else {
+            parsedValue = consumePercent(args, ValueRangeNonNegative);
+            if (!parsedValue)
+                parsedValue = consumeNumber(args, ValueRangeNonNegative);
+            if (parsedValue && !allowsValuesGreaterThanOne(filterType)) {
+                bool isPercentage = downcast<CSSPrimitiveValue>(*parsedValue).isPercentage();
+                double maxAllowed = isPercentage ? 100.0 : 1.0;
+                if (downcast<CSSPrimitiveValue>(*parsedValue).doubleValue() > maxAllowed)
+                    parsedValue = CSSPrimitiveValue::create(maxAllowed, isPercentage ? CSSUnitType::CSS_PERCENTAGE : CSSUnitType::CSS_NUMBER);
+            }
+        }
+    }
+    if (!parsedValue || !args.atEnd())
+        return nullptr;
+    filterValue->append(parsedValue.releaseNonNull());
+    return filterValue;
+}
+
+RefPtr<CSSValue> consumeFilter(CSSParserTokenRange& range, const CSSParserContext& context, AllowedFilterFunctions allowedFunctions)
+{
+    if (range.peek().id() == CSSValueNone)
+        return consumeIdent(range);
+
+    bool referenceFiltersAllowed = allowedFunctions == AllowedFilterFunctions::PixelFilters;
+    auto list = CSSValueList::createSpaceSeparated();
+    do {
+        RefPtr<CSSValue> filterValue = referenceFiltersAllowed ? consumeUrl(range) : nullptr;
+        if (!filterValue) {
+            filterValue = consumeFilterFunction(range, context, allowedFunctions);
+            if (!filterValue)
+                return nullptr;
+        }
+        list->append(filterValue.releaseNonNull());
+    } while (!range.atEnd());
+
+    return list.ptr();
+}
+
+RefPtr<CSSShadowValue> consumeSingleShadow(CSSParserTokenRange& range, CSSParserMode cssParserMode, bool allowInset, bool allowSpread)
+{
+    RefPtr<CSSPrimitiveValue> style;
+    RefPtr<CSSPrimitiveValue> color;
+
+    if (range.atEnd())
+        return nullptr;
+    if (range.peek().id() == CSSValueInset) {
+        if (!allowInset)
+            return nullptr;
+        style = consumeIdent(range);
+    }
+    color = consumeColor(range, cssParserMode);
+
+    auto horizontalOffset = consumeLength(range, cssParserMode, ValueRangeAll);
+    if (!horizontalOffset)
+        return nullptr;
+
+    auto verticalOffset = consumeLength(range, cssParserMode, ValueRangeAll);
+    if (!verticalOffset)
+        return nullptr;
+
+    RefPtr<CSSPrimitiveValue> blurRadius;
+    RefPtr<CSSPrimitiveValue> spreadDistance;
+
+    const CSSParserToken& token = range.peek();
+    // The explicit check for calc() is unfortunate. This is ensuring that we only fail parsing if there is a length, but it fails the range check.
+    if (token.type() == DimensionToken || token.type() == NumberToken || (token.type() == FunctionToken && CSSCalcValue::isCalcFunction(token.functionId()))) {
+        blurRadius = consumeLength(range, cssParserMode, ValueRangeNonNegative);
+        if (!blurRadius)
+            return nullptr;
+    }
+
+    if (blurRadius && allowSpread)
+        spreadDistance = consumeLength(range, cssParserMode, ValueRangeAll);
+
+    if (!range.atEnd()) {
+        if (!color)
+            color = consumeColor(range, cssParserMode);
+        if (range.peek().id() == CSSValueInset) {
+            if (!allowInset || style)
+                return nullptr;
+            style = consumeIdent(range);
+        }
+    }
+
+    return CSSShadowValue::create(WTFMove(horizontalOffset), WTFMove(verticalOffset), WTFMove(blurRadius), WTFMove(spreadDistance), WTFMove(style), WTFMove(color));
+}
+
+RefPtr<CSSValue> consumeImage(CSSParserTokenRange& range, CSSParserContext context, OptionSet<AllowedImageType> AllowedImageType)
+{
+    if ((range.peek().type() == StringToken) && (AllowedImageType & AllowedImageType::RawStringAsURL))
+        return CSSImageValue::create(completeURL(context, range.consumeIncludingWhitespace().value().toAtomString()), context.isContentOpaque ? LoadedFromOpaqueSource::Yes : LoadedFromOpaqueSource::No);
+
     if (range.peek().type() == FunctionToken) {
         CSSValueID id = range.peek().functionId();
-        if (id == CSSValueWebkitImageSet)
-            return consumeImageSet(range, context);
-        if (generatedImage == ConsumeGeneratedImage::Allow && isGeneratedImage(id))
+        if ((AllowedImageType & AllowedImageType::ImageSet) && (id == CSSValueWebkitImageSet || id == CSSValueImageSet))
+            return consumeImageSet(range, context, AllowedImageType);
+        if ((AllowedImageType & AllowedImageType::GeneratedImage) && isGeneratedImage(id))
             return consumeGeneratedImage(range, context);
     }
+
+    if (AllowedImageType & AllowedImageType::URLFunction) {
+        auto uri = consumeUrlAsStringView(range);
+        if (!uri.isNull())
+            return CSSImageValue::create(completeURL(context, uri.toAtomString()), context.isContentOpaque ? LoadedFromOpaqueSource::Yes : LoadedFromOpaqueSource::No);
+    }
+
     return nullptr;
 }