Changeset 273127 in webkit


Ignore:
Timestamp:
Feb 18, 2021 9:51:08 PM (17 months ago)
Author:
weinig@apple.com
Message:

Add experimental support for CSS Color 5 Relative Color Syntax
https://bugs.webkit.org/show_bug.cgi?id=221880

Reviewed by Darin Adler.

Source/WebCore:

Adds initial support for CSS Color 5 Relative Color Syntax - https://drafts.csswg.org/css-color-5/#relative-colors

This feature is off by default and can be enabled via the CSSRelativeColorSyntaxEnabled
experimental preference flag.

As this is the initial commit for this functionality, it is not yet complete.
This adds basic parsing and resolution for relative rgb(), hsl(), hwb(), lab()
and lch(), but does not yet implement support for calc() functions that can operate
on the channel keywords nor does it support using system keyword colors as the
origin of the relative colors.

Some additional caveats about this initial implementation that need further
clarification from the spec editors:

  1. Currently, we only allow using the channel corresponding to to the position of that channel

in the replacement syntax. This means you can't do:

rgb(from red g r b)

to permute the channels.

  1. For rgb(), which allows r, g, and b to be all numbers or all percentages,

we currently don't assume either number or percentage, and wait until some
constant value is provided to set the type. That means that:

rgb(from red r g 10)

will treat r, ang g as numbers, but:

rgb(from red r g 10%)

will treat r and g as percentages. This is not currently observable other than
that we reject something that mixes the two, such as

rgb(from red r 10 10%)

but will be fully observable once processing the channels via calc() is supported.

Test: fast/css/parsing-relative-color-syntax.html

  • css/CSSValueKeywords.in:
  • css/parser/CSSParserContext.cpp:

(WebCore::operator==):

  • css/parser/CSSParserContext.h:

(WebCore::CSSParserContextHash::hash):

  • css/parser/CSSPropertyParserHelpers.cpp:

(WebCore::CSSPropertyParserHelpers::consumeOriginColor):
(WebCore::CSSPropertyParserHelpers::consumeOptionalAlphaOrIdent):
(WebCore::CSSPropertyParserHelpers::consumeHueOrIdent):
(WebCore::CSSPropertyParserHelpers::consumeNumberOrIdent):
(WebCore::CSSPropertyParserHelpers::consumePercentOrIdent):
(WebCore::CSSPropertyParserHelpers::extractChannelValue):
(WebCore::CSSPropertyParserHelpers::resolveRelativeColorChannel):
(WebCore::CSSPropertyParserHelpers::consumeRelativeRGBComponent):
(WebCore::CSSPropertyParserHelpers::parseRelativeRGBParameters):
(WebCore::CSSPropertyParserHelpers::parseRGBParameters):
(WebCore::CSSPropertyParserHelpers::parseRelativeHSLParameters):
(WebCore::CSSPropertyParserHelpers::parseHSLParameters):
(WebCore::CSSPropertyParserHelpers::normalizeWhitenessBlackness):
(WebCore::CSSPropertyParserHelpers::parseRelativeHWBParameters):
(WebCore::CSSPropertyParserHelpers::parseHWBParameters):
(WebCore::CSSPropertyParserHelpers::parseRelativeLabParameters):
(WebCore::CSSPropertyParserHelpers::parseLabParameters):
(WebCore::CSSPropertyParserHelpers::parseRelativeLCHParameters):
(WebCore::CSSPropertyParserHelpers::parseLCHParameters):
(WebCore::CSSPropertyParserHelpers::parseColorFunction):

Source/WTF:

  • Scripts/Preferences/WebPreferencesExperimental.yaml:

Add new experimental preference for CSS Color 5 Relative Color Syntax
which is off by default.

LayoutTests:

  • fast/css/parsing-relative-color-syntax-expected.txt: Added.
  • fast/css/parsing-relative-color-syntax.html: Added.

Add basic parsing and computed style computation tests for relative color syntax.

Location:
trunk
Files:
2 added
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r273124 r273127  
     12021-02-18  Sam Weinig  <weinig@apple.com>
     2
     3        Add experimental support for CSS Color 5 Relative Color Syntax
     4        https://bugs.webkit.org/show_bug.cgi?id=221880
     5
     6        Reviewed by Darin Adler.
     7
     8        * fast/css/parsing-relative-color-syntax-expected.txt: Added.
     9        * fast/css/parsing-relative-color-syntax.html: Added.
     10        Add basic parsing and computed style computation tests for relative color syntax.
     11
    1122021-02-18  Lauro Moura  <lmoura@igalia.com>
    213
  • trunk/Source/WTF/ChangeLog

    r273085 r273127  
     12021-02-18  Sam Weinig  <weinig@apple.com>
     2
     3        Add experimental support for CSS Color 5 Relative Color Syntax
     4        https://bugs.webkit.org/show_bug.cgi?id=221880
     5
     6        Reviewed by Darin Adler.
     7
     8        * Scripts/Preferences/WebPreferencesExperimental.yaml:
     9        Add new experimental preference for CSS Color 5 Relative Color Syntax
     10        which is off by default.
     11
    1122021-02-18  Youenn Fablet  <youenn@apple.com>
    213
  • trunk/Source/WTF/Scripts/Preferences/WebPreferencesExperimental.yaml

    r272983 r273127  
    1 # Copyright (c) 2020 Apple Inc. All rights reserved.
     1# Copyright (c) 2020-2021 Apple Inc. All rights reserved.
    22#
    33# Redistribution and use in source and binary forms, with or without
     
    164164      default: false
    165165
     166CSSRelativeColorSyntaxEnabled:
     167  type: bool
     168  humanReadableName: "CSS Relative Color Syntax"
     169  humanReadableDescription: "Enable support for CSS Relative Color Syntax defined in CSS Color 5"
     170  defaultValue:
     171    WebKitLegacy:
     172      default: false
     173    WebKit:
     174      default: false
     175    WebCore:
     176      default: false
     177
    166178CSSTypedOMEnabled:
    167179  type: bool
  • trunk/Source/WebCore/ChangeLog

    r273121 r273127  
     12021-02-18  Sam Weinig  <weinig@apple.com>
     2
     3        Add experimental support for CSS Color 5 Relative Color Syntax
     4        https://bugs.webkit.org/show_bug.cgi?id=221880
     5
     6        Reviewed by Darin Adler.
     7
     8        Adds initial support for CSS Color 5 Relative Color Syntax - https://drafts.csswg.org/css-color-5/#relative-colors
     9
     10        This feature is off by default and can be enabled via the CSSRelativeColorSyntaxEnabled
     11        experimental preference flag.
     12
     13        As this is the initial commit for this functionality, it is not yet complete.
     14        This adds basic parsing and resolution for relative rgb(), hsl(), hwb(), lab()
     15        and lch(), but does not yet implement support for calc() functions that can operate
     16        on the channel keywords nor does it support using system keyword colors as the
     17        origin of the relative colors.
     18
     19        Some additional caveats about this initial implementation that need further
     20        clarification from the spec editors:
     21
     22        1. Currently, we only allow using the channel corresponding to to the position of that channel
     23        in the replacement syntax. This means you can't do:
     24
     25            rgb(from red g r b)
     26
     27        to permute the channels.
     28
     29        2. For rgb(), which allows r, g, and b to be all numbers or all percentages,
     30        we currently don't assume either number or percentage, and wait until some
     31        constant value is provided to set the type. That means that:
     32
     33            rgb(from red r g 10)
     34
     35        will treat r, ang g as numbers, but:
     36
     37            rgb(from red r g 10%)
     38
     39        will treat r and g as percentages. This is not currently observable other than
     40        that we reject something that mixes the two, such as
     41
     42            rgb(from red r 10 10%)
     43
     44        but will be fully observable once processing the channels via calc() is supported.
     45
     46        Test: fast/css/parsing-relative-color-syntax.html
     47
     48        * css/CSSValueKeywords.in:
     49        * css/parser/CSSParserContext.cpp:
     50        (WebCore::operator==):
     51        * css/parser/CSSParserContext.h:
     52        (WebCore::CSSParserContextHash::hash):
     53        * css/parser/CSSPropertyParserHelpers.cpp:
     54        (WebCore::CSSPropertyParserHelpers::consumeOriginColor):
     55        (WebCore::CSSPropertyParserHelpers::consumeOptionalAlphaOrIdent):
     56        (WebCore::CSSPropertyParserHelpers::consumeHueOrIdent):
     57        (WebCore::CSSPropertyParserHelpers::consumeNumberOrIdent):
     58        (WebCore::CSSPropertyParserHelpers::consumePercentOrIdent):
     59        (WebCore::CSSPropertyParserHelpers::extractChannelValue):
     60        (WebCore::CSSPropertyParserHelpers::resolveRelativeColorChannel):
     61        (WebCore::CSSPropertyParserHelpers::consumeRelativeRGBComponent):
     62        (WebCore::CSSPropertyParserHelpers::parseRelativeRGBParameters):
     63        (WebCore::CSSPropertyParserHelpers::parseRGBParameters):
     64        (WebCore::CSSPropertyParserHelpers::parseRelativeHSLParameters):
     65        (WebCore::CSSPropertyParserHelpers::parseHSLParameters):
     66        (WebCore::CSSPropertyParserHelpers::normalizeWhitenessBlackness):
     67        (WebCore::CSSPropertyParserHelpers::parseRelativeHWBParameters):
     68        (WebCore::CSSPropertyParserHelpers::parseHWBParameters):
     69        (WebCore::CSSPropertyParserHelpers::parseRelativeLabParameters):
     70        (WebCore::CSSPropertyParserHelpers::parseLabParameters):
     71        (WebCore::CSSPropertyParserHelpers::parseRelativeLCHParameters):
     72        (WebCore::CSSPropertyParserHelpers::parseLCHParameters):
     73        (WebCore::CSSPropertyParserHelpers::parseColorFunction):
     74
    1752021-02-18  Myles C. Maxfield  <mmaxfield@apple.com>
    276
  • trunk/Source/WebCore/css/CSSValueKeywords.in

    r272987 r273127  
    12701270//color
    12711271
     1272// relative color identifiers
     1273// from
     1274r
     1275g
     1276b
     1277h
     1278s
     1279l
     1280// h
     1281w
     1282// b
     1283// l
     1284a
     1285// b
     1286// l
     1287c
     1288// h
     1289// alpha
     1290
    12721291// transform
    12731292matrix
  • trunk/Source/WebCore/css/parser/CSSParserContext.cpp

    r272987 r273127  
    7878#endif
    7979    , overscrollBehaviorEnabled { document.settings().overscrollBehaviorEnabled() }
     80    , relativeColorSyntaxEnabled { document.settings().cssRelativeColorSyntaxEnabled() }
    8081    , scrollBehaviorEnabled { document.settings().CSSOMViewSmoothScrollingEnabled() }
    8182    , springTimingFunctionEnabled { document.settings().springTimingFunctionEnabled() }
     
    114115#endif
    115116        && a.overscrollBehaviorEnabled == b.overscrollBehaviorEnabled
     117        && a.relativeColorSyntaxEnabled == b.relativeColorSyntaxEnabled
    116118        && a.scrollBehaviorEnabled == b.scrollBehaviorEnabled
    117119        && a.springTimingFunctionEnabled == b.springTimingFunctionEnabled
  • trunk/Source/WebCore/css/parser/CSSParserContext.h

    r272987 r273127  
    6868#endif
    6969    bool overscrollBehaviorEnabled { false };
     70    bool relativeColorSyntaxEnabled { false };
    7071    bool scrollBehaviorEnabled { false };
    7172    bool springTimingFunctionEnabled { false };
     
    103104            hash ^= StringHash::hash(key.charset);
    104105        unsigned bits = key.isHTMLDocument                  << 0
     106            & key.hasDocumentSecurityOrigin                 << 1
     107            & key.isContentOpaque                           << 2
     108            & key.useSystemAppearance                       << 3
     109            & key.aspectRatioEnabled                        << 4
     110            & key.colorFilterEnabled                        << 5
     111            & key.constantPropertiesEnabled                 << 6
     112            & key.deferredCSSParserEnabled                  << 7
     113            & key.enforcesCSSMIMETypeInNoQuirksMode         << 8
     114            & key.individualTransformPropertiesEnabled      << 9
     115#if ENABLE(OVERFLOW_SCROLLING_TOUCH)
     116            & key.legacyOverflowScrollingTouchEnabled       << 10
     117#endif
     118            & key.overscrollBehaviorEnabled                 << 11
     119            & key.relativeColorSyntaxEnabled                << 12
     120            & key.scrollBehaviorEnabled                     << 13
     121            & key.springTimingFunctionEnabled               << 14
    105122#if ENABLE(TEXT_AUTOSIZING)
    106             & key.textAutosizingEnabled                     << 1
     123            & key.textAutosizingEnabled                     << 15
    107124#endif
    108 #if ENABLE(OVERFLOW_SCROLLING_TOUCH)
    109             & key.legacyOverflowScrollingTouchEnabled       << 2
     125#if ENABLE(CSS_TRANSFORM_STYLE_OPTIMIZED_3D)
     126            & key.transformStyleOptimized3DEnabled          << 16
    110127#endif
    111             & key.enforcesCSSMIMETypeInNoQuirksMode         << 3
    112             & key.useLegacyBackgroundSizeShorthandBehavior  << 4
    113             & key.springTimingFunctionEnabled               << 5
    114             & key.constantPropertiesEnabled                 << 6
    115             & key.colorFilterEnabled                        << 7
    116             & key.deferredCSSParserEnabled                  << 8
    117             & key.hasDocumentSecurityOrigin                 << 9
    118             & key.useSystemAppearance                       << 10
     128            & key.useLegacyBackgroundSizeShorthandBehavior  << 17
     129            & key.focusVisibleEnabled                       << 18
    119130#if ENABLE(ATTACHMENT_ELEMENT)
    120             & key.attachmentEnabled                         << 11
     131            & key.attachmentEnabled                         << 19
    121132#endif
    122             & key.scrollBehaviorEnabled                     << 12
    123             & key.individualTransformPropertiesEnabled      << 13
    124             & key.overscrollBehaviorEnabled                 << 14
    125 #if ENABLE(CSS_TRANSFORM_STYLE_OPTIMIZED_3D)
    126             & key.transformStyleOptimized3DEnabled          << 15
    127 #endif
    128             & key.mode                                      << 16; // Keep this last.
     133            & key.mode                                      << 20; // Keep this last.
    129134        hash ^= WTF::intHash(bits);
    130135        return hash;
  • trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp

    r273078 r273127  
    691691}
    692692
     693static Color consumeOriginColor(CSSParserTokenRange& args, const CSSParserContext& context)
     694{
     695    auto value = consumeColor(args, context);
     696    if (!value)
     697        return { };
     698
     699    if (value->isRGBColor())
     700        return value->color();
     701
     702    ASSERT(value->isValueID());
     703    auto keyword = value->valueID();
     704
     705    // FIXME: We don't have enough context in the parser to resolving a system keyword
     706    // correctly. We should package up the relative color parameters and resolve the
     707    // whole thing at the appropriate time when the origin color is a system keyword.
     708    if (StyleColor::isSystemColor(keyword))
     709        return { };
     710
     711    return StyleColor::colorFromKeyword(keyword, { });
     712}
     713
    693714static Optional<double> consumeOptionalAlpha(CSSParserTokenRange& range)
    694715{
     
    702723}
    703724
     725template<CSSValueID... allowedIdents> static Optional<Variant<double, CSSValueID>> consumeOptionalAlphaOrIdent(CSSParserTokenRange& range)
     726{
     727    if (auto alpha = consumeOptionalAlpha(range))
     728        return { *alpha };
     729
     730    if (auto ident = consumeIdentRaw<allowedIdents...>(range))
     731        return { *ident };
     732
     733    return WTF::nullopt;
     734}
     735
    704736static Optional<double> consumeHue(CSSParserTokenRange& range, const CSSParserContext& context)
    705737{
     
    710742}
    711743
     744template<CSSValueID... allowedIdents> static Optional<Variant<double, CSSValueID>> consumeHueOrIdent(CSSParserTokenRange& range, const CSSParserContext& context)
     745{
     746    if (auto hue = consumeHue(range, context))
     747        return { *hue };
     748
     749    if (auto ident = consumeIdentRaw<allowedIdents...>(range))
     750        return { *ident };
     751
     752    return WTF::nullopt;
     753}
     754
    712755static double normalizeHue(double hue)
    713756{
    714757    return std::fmod(std::fmod(hue, 360.0) + 360.0, 360.0);
     758}
     759
     760template<CSSValueID... allowedIdents> static Optional<Variant<double, CSSValueID>> consumeNumberOrIdent(CSSParserTokenRange& range)
     761{
     762    if (auto number = consumeNumberRaw(range))
     763        return { *number };
     764
     765    if (auto ident = consumeIdentRaw<allowedIdents...>(range))
     766        return { *ident };
     767
     768    return WTF::nullopt;
     769}
     770
     771template<CSSValueID... allowedIdents> static Optional<Variant<double, CSSValueID>> consumePercentOrIdent(CSSParserTokenRange& range)
     772{
     773    if (auto percent = consumePercentRaw(range))
     774        return { *percent };
     775
     776    if (auto ident = consumeIdentRaw<allowedIdents...>(range))
     777        return { *ident };
     778
     779    return WTF::nullopt;
     780}
     781
     782template<CSSValueID C1, CSSValueID C2, CSSValueID C3, CSSValueID AlphaChannel, typename ColorType> static auto extractChannelValue(CSSValueID channel, const ColorType& originColor) -> typename ColorType::ComponentType
     783{
     784    auto components = asColorComponents(originColor);
     785    switch (channel) {
     786    case C1:
     787        return components[0];
     788    case C2:
     789        return components[1];
     790    case C3:
     791        return components[2];
     792    case AlphaChannel:
     793        return components[3];
     794    default:
     795        ASSERT_NOT_REACHED();
     796    }
     797
     798    return 0;
     799}
     800
     801template<CSSValueID C1, CSSValueID C2, CSSValueID C3, CSSValueID AlphaChannel, typename ColorType, typename ValueTransformer> static decltype(auto) resolveRelativeColorChannel(const Variant<double, CSSValueID>& parsedChannel, const ColorType& originColor, ValueTransformer&& valueTransformer)
     802{
     803    return switchOn(parsedChannel,
     804        [&] (double value) {
     805            return valueTransformer(value);
     806        },
     807        [&] (CSSValueID channel) {
     808            return extractChannelValue<C1, C2, C3, AlphaChannel>(channel, originColor);
     809        }
     810    );
     811}
     812
     813template<CSSValueID C1, CSSValueID C2, CSSValueID C3, CSSValueID AlphaChannel, typename ColorType> static decltype(auto) resolveRelativeColorChannel(const Variant<double, CSSValueID>& parsedChannel, const ColorType& originColor)
     814{
     815    return resolveRelativeColorChannel<C1, C2, C3, AlphaChannel>(parsedChannel, originColor, [](auto value) { return value; });
    715816}
    716817
     
    749850}
    750851
    751 static Color parseRGBParameters(CSSParserTokenRange& range, const CSSParserContext&)
     852struct RelativeRGBComponent {
     853    Variant<double, CSSValueID> value;
     854    Optional<RGBComponentType> type;
     855};
     856
     857template<CSSValueID C> static Optional<RelativeRGBComponent> consumeRelativeRGBComponent(CSSParserTokenRange& args, Optional<RGBComponentType> componentType)
     858{
     859    if (!componentType) {
     860        if (auto number = consumeNumberRaw(args))
     861            return RelativeRGBComponent { *number, RGBComponentType::Number };
     862        if (auto percent = consumePercentRaw(args))
     863            return RelativeRGBComponent { *percent, RGBComponentType::Percentage };
     864        if (auto channel = consumeIdentRaw<C>(args))
     865            return RelativeRGBComponent { *channel, WTF::nullopt };
     866        return WTF::nullopt;
     867    }
     868
     869    switch (*componentType) {
     870    case RGBComponentType::Number: {
     871        if (auto number = consumeNumberRaw(args))
     872            return RelativeRGBComponent { *number, RGBComponentType::Number };
     873        if (auto channel = consumeIdentRaw<C>(args))
     874            return RelativeRGBComponent { *channel, RGBComponentType::Number };
     875        return WTF::nullopt;
     876    }
     877    case RGBComponentType::Percentage: {
     878        if (auto percent = consumePercentRaw(args))
     879            return RelativeRGBComponent { *percent, RGBComponentType::Percentage };
     880        if (auto channel = consumeIdentRaw<C>(args))
     881            return RelativeRGBComponent { *channel, RGBComponentType::Percentage };
     882        return WTF::nullopt;
     883    }
     884    }
     885}
     886
     887static Color parseRelativeRGBParameters(CSSParserTokenRange& args, const CSSParserContext& context)
     888{
     889    ASSERT(args.peek().id() == CSSValueFrom);
     890    consumeIdentRaw(args);
     891
     892    auto originColor = consumeOriginColor(args, context);
     893    if (!originColor.isValid())
     894        return { };
     895
     896    Optional<RGBComponentType> componentType;
     897    auto redResult = consumeRelativeRGBComponent<CSSValueR>(args, componentType);
     898    if (!redResult)
     899        return { };
     900    auto red = redResult->value;
     901    componentType = redResult->type;
     902
     903    auto greenResult = consumeRelativeRGBComponent<CSSValueG>(args, componentType);
     904    if (!greenResult)
     905        return { };
     906    auto green = greenResult->value;
     907    componentType = greenResult->type;
     908
     909    auto blueResult = consumeRelativeRGBComponent<CSSValueB>(args, componentType);
     910    if (!blueResult)
     911        return { };
     912    auto blue = blueResult->value;
     913    componentType = blueResult->type;
     914
     915    auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
     916    if (!alpha)
     917        return { };
     918
     919    if (!args.atEnd())
     920        return { };
     921
     922    // After parsing, convert identifiers to values from the origin color.
     923
     924    // FIXME: Do we want to being doing this in uint8_t values? Or should we use
     925    // higher precision and clamp at the end? It won't make a difference until we
     926    // support calculations on the origin's components.
     927    auto originColorAsSRGB = originColor.toSRGBALossy<uint8_t>();
     928
     929    auto resolvedComponentType = componentType.valueOr(RGBComponentType::Percentage);
     930    auto channelResolver = [resolvedComponentType](auto value) {
     931        return clampRGBComponent(value, resolvedComponentType);
     932    };
     933
     934    auto resolvedRed = resolveRelativeColorChannel<CSSValueR, CSSValueG, CSSValueB, CSSValueAlpha>(red, originColorAsSRGB, channelResolver);
     935    auto resolvedGreen = resolveRelativeColorChannel<CSSValueR, CSSValueG, CSSValueB, CSSValueAlpha>(green, originColorAsSRGB, channelResolver);
     936    auto resolvedBlue = resolveRelativeColorChannel<CSSValueR, CSSValueG, CSSValueB, CSSValueAlpha>(blue, originColorAsSRGB, channelResolver);
     937    auto resolvedAlpha = resolveRelativeColorChannel<CSSValueR, CSSValueG, CSSValueB, CSSValueAlpha>(*alpha, originColorAsSRGB, [](auto value) {
     938        return convertFloatAlphaTo<uint8_t>(value);
     939    });
     940
     941    return SRGBA<uint8_t> { resolvedRed, resolvedGreen, resolvedBlue, resolvedAlpha };
     942}
     943
     944enum class RGBFunctionMode { RGB, RGBA };
     945
     946template<RGBFunctionMode Mode> static Color parseRGBParameters(CSSParserTokenRange& range, const CSSParserContext& context)
    752947{
    753948    ASSERT(range.peek().functionId() == CSSValueRgb || range.peek().functionId() == CSSValueRgba);
    754949    auto args = consumeFunction(range);
     950
     951    if constexpr (Mode == RGBFunctionMode::RGB) {
     952        if (context.relativeColorSyntaxEnabled && args.peek().id() == CSSValueFrom)
     953            return parseRelativeRGBParameters(args, context);
     954    }
    755955
    756956    struct InitialComponent {
     
    8121012}
    8131013
    814 static Color parseHSLParameters(CSSParserTokenRange& range, const CSSParserContext& context)
     1014static Color parseRelativeHSLParameters(CSSParserTokenRange& args, const CSSParserContext& context)
     1015{
     1016    ASSERT(args.peek().id() == CSSValueFrom);
     1017    consumeIdentRaw(args);
     1018
     1019    auto originColor = consumeOriginColor(args, context);
     1020    if (!originColor.isValid())
     1021        return { };
     1022
     1023    auto hue = consumeHueOrIdent<CSSValueH>(args, context);
     1024    if (!hue)
     1025        return { };
     1026
     1027    auto saturation = consumePercentOrIdent<CSSValueS>(args);
     1028    if (!saturation)
     1029        return { };
     1030
     1031    auto lightness = consumePercentOrIdent<CSSValueL>(args);
     1032    if (!lightness)
     1033        return { };
     1034
     1035    auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
     1036    if (!alpha)
     1037        return { };
     1038
     1039    if (!args.atEnd())
     1040        return { };
     1041
     1042    // After parsing, convert identifiers to values from the origin color.
     1043
     1044    auto originColorAsHSL = originColor.toColorTypeLossy<HSLA<float>>();
     1045
     1046    auto resolvedHue = resolveRelativeColorChannel<CSSValueH, CSSValueS, CSSValueL, CSSValueAlpha>(*hue, originColorAsHSL, [](auto hue) {
     1047        return normalizeHue(hue);
     1048    });
     1049    auto resolvedSaturation = resolveRelativeColorChannel<CSSValueH, CSSValueS, CSSValueL, CSSValueAlpha>(*saturation, originColorAsHSL, [](auto saturation) {
     1050        return clampTo(saturation, 0.0, 100.0);
     1051    });
     1052    auto resolvedLightness = resolveRelativeColorChannel<CSSValueH, CSSValueS, CSSValueL, CSSValueAlpha>(*lightness, originColorAsHSL, [](auto lightness) {
     1053        return clampTo(lightness, 0.0, 100.0);
     1054    });
     1055    auto resolvedAlpha = resolveRelativeColorChannel<CSSValueH, CSSValueS, CSSValueL, CSSValueAlpha>(*alpha, originColorAsHSL);
     1056
     1057    return convertColor<SRGBA<uint8_t>>(HSLA<float> { static_cast<float>(resolvedHue), static_cast<float>(resolvedSaturation), static_cast<float>(resolvedLightness), static_cast<float>(resolvedAlpha) });
     1058}
     1059
     1060enum class HSLFunctionMode { HSL, HSLA };
     1061
     1062template<HSLFunctionMode Mode> static Color parseHSLParameters(CSSParserTokenRange& range, const CSSParserContext& context)
    8151063{
    8161064    ASSERT(range.peek().functionId() == CSSValueHsl || range.peek().functionId() == CSSValueHsla);
    8171065    auto args = consumeFunction(range);
    8181066
     1067    if constexpr (Mode == HSLFunctionMode::HSL) {
     1068        if (context.relativeColorSyntaxEnabled && args.peek().id() == CSSValueFrom)
     1069            return parseRelativeHSLParameters(args, context);
     1070    }
     1071
    8191072    auto hue = consumeHue(args, context);
    8201073    if (!hue)
     
    8421095
    8431096    auto normalizedHue = normalizeHue(*hue);
    844     auto normalizedSaturation = clampTo<double>(*saturation, 0.0, 100.0);
    845     auto normalizedLightness = clampTo<double>(*lightness, 0.0, 100.0);
    846     auto normalizedAlpha = clampTo<double>(*alpha, 0.0, 1.0);
     1097    auto normalizedSaturation = clampTo(*saturation, 0.0, 100.0);
     1098    auto normalizedLightness = clampTo(*lightness, 0.0, 100.0);
     1099    auto normalizedAlpha = clampTo(*alpha, 0.0, 1.0);
    8471100
    8481101    return convertColor<SRGBA<uint8_t>>(HSLA<float> { static_cast<float>(normalizedHue), static_cast<float>(normalizedSaturation), static_cast<float>(normalizedLightness), static_cast<float>(normalizedAlpha) });
     
    8551108static WhitenessBlackness normalizeWhitenessBlackness(double whiteness, double blackness)
    8561109{
    857     WhitenessBlackness result;
    858 
    8591110    //   Values outside of these ranges are not invalid, but are clamped to the
    8601111    //   ranges defined here at computed-value time.
    861     result.whiteness = clampTo<double>(whiteness, 0, 100);
    862     result.blackness = clampTo<double>(blackness, 0, 100);
     1112    WhitenessBlackness result {
     1113        clampTo(whiteness, 0.0, 100.0),
     1114        clampTo(blackness, 0.0, 100.0)
     1115    };
    8631116
    8641117    //   If the sum of these two arguments is greater than 100%, then at
     
    8731126}
    8741127
     1128static Color parseRelativeHWBParameters(CSSParserTokenRange& args, const CSSParserContext& context)
     1129{
     1130    ASSERT(args.peek().id() == CSSValueFrom);
     1131    consumeIdentRaw(args);
     1132
     1133    auto originColor = consumeOriginColor(args, context);
     1134    if (!originColor.isValid())
     1135        return { };
     1136
     1137    auto hue = consumeHueOrIdent<CSSValueH>(args, context);
     1138    if (!hue)
     1139        return { };
     1140
     1141    auto whiteness = consumePercentOrIdent<CSSValueW>(args);
     1142    if (!whiteness)
     1143        return { };
     1144
     1145    auto blackness = consumePercentOrIdent<CSSValueB>(args);
     1146    if (!blackness)
     1147        return { };
     1148
     1149    auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
     1150    if (!alpha)
     1151        return { };
     1152
     1153    if (!args.atEnd())
     1154        return { };
     1155
     1156    // After parsing, convert identifiers to values from the origin color.
     1157
     1158    auto originColorAsHWB = originColor.toColorTypeLossy<HWBA<float>>();
     1159
     1160    auto resolvedHue = resolveRelativeColorChannel<CSSValueH, CSSValueW, CSSValueB, CSSValueAlpha>(*hue, originColorAsHWB, [](auto hue) {
     1161        return normalizeHue(hue);
     1162    });
     1163    auto resolvedWhiteness = resolveRelativeColorChannel<CSSValueH, CSSValueW, CSSValueB, CSSValueAlpha>(*whiteness, originColorAsHWB);
     1164    auto resolvedBlackness = resolveRelativeColorChannel<CSSValueH, CSSValueW, CSSValueB, CSSValueAlpha>(*blackness, originColorAsHWB);
     1165    auto resolvedAlpha = resolveRelativeColorChannel<CSSValueH, CSSValueW, CSSValueB, CSSValueAlpha>(*alpha, originColorAsHWB);
     1166
     1167    auto [normalizedWhitness, normalizedBlackness] = normalizeWhitenessBlackness(resolvedWhiteness, resolvedBlackness);
     1168
     1169    return convertColor<SRGBA<uint8_t>>(HWBA<float> { static_cast<float>(resolvedHue), static_cast<float>(normalizedWhitness), static_cast<float>(normalizedBlackness), static_cast<float>(resolvedAlpha) });
     1170}
     1171
    8751172static Color parseHWBParameters(CSSParserTokenRange& range, const CSSParserContext& context)
    8761173{
     
    8781175    auto args = consumeFunction(range);
    8791176
     1177    if (context.relativeColorSyntaxEnabled && args.peek().id() == CSSValueFrom)
     1178        return parseRelativeHWBParameters(args, context);
     1179
    8801180    auto hue = consumeHue(args, context);
    8811181    if (!hue)
     
    9031203}
    9041204
    905 static Color parseLabParameters(CSSParserTokenRange& range, const CSSParserContext&)
     1205static Color parseRelativeLabParameters(CSSParserTokenRange& args, const CSSParserContext& context)
     1206{
     1207    ASSERT(args.peek().id() == CSSValueFrom);
     1208    consumeIdentRaw(args);
     1209
     1210    auto originColor = consumeOriginColor(args, context);
     1211    if (!originColor.isValid())
     1212        return { };
     1213
     1214    auto lightness = consumePercentOrIdent<CSSValueL>(args);
     1215    if (!lightness)
     1216        return { };
     1217
     1218    auto aValue = consumeNumberOrIdent<CSSValueA>(args);
     1219    if (!aValue)
     1220        return { };
     1221
     1222    auto bValue = consumeNumberOrIdent<CSSValueB>(args);
     1223    if (!bValue)
     1224        return { };
     1225
     1226    auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
     1227    if (!alpha)
     1228        return { };
     1229
     1230    if (!args.atEnd())
     1231        return { };
     1232
     1233    // After parsing, convert identifiers to values from the origin color.
     1234
     1235    auto originColorAsLab = originColor.toColorTypeLossy<Lab<float>>();
     1236
     1237    auto resolvedLightness = resolveRelativeColorChannel<CSSValueL, CSSValueA, CSSValueB, CSSValueAlpha>(*lightness, originColorAsLab, [](auto lightness) {
     1238        return std::max(0.0, lightness);
     1239    });
     1240    auto resolvedAValue = resolveRelativeColorChannel<CSSValueL, CSSValueA, CSSValueB, CSSValueAlpha>(*aValue, originColorAsLab);
     1241    auto resolvedBValue = resolveRelativeColorChannel<CSSValueL, CSSValueA, CSSValueB, CSSValueAlpha>(*bValue, originColorAsLab);
     1242    auto resolvedAlpha = resolveRelativeColorChannel<CSSValueL, CSSValueA, CSSValueB, CSSValueAlpha>(*alpha, originColorAsLab);
     1243
     1244    return Lab<float> { static_cast<float>(resolvedLightness), static_cast<float>(resolvedAValue), static_cast<float>(resolvedBValue), static_cast<float>(resolvedAlpha) };
     1245}
     1246
     1247static Color parseLabParameters(CSSParserTokenRange& range, const CSSParserContext& context)
    9061248{
    9071249    ASSERT(range.peek().functionId() == CSSValueLab);
    9081250    auto args = consumeFunction(range);
    9091251
     1252    if (context.relativeColorSyntaxEnabled && args.peek().id() == CSSValueFrom)
     1253        return parseRelativeLabParameters(args, context);
     1254
    9101255    auto lightness = consumePercentRaw(args);
    9111256    if (!lightness)
     
    9321277}
    9331278
     1279static Color parseRelativeLCHParameters(CSSParserTokenRange& args, const CSSParserContext& context)
     1280{
     1281    ASSERT(args.peek().id() == CSSValueFrom);
     1282    consumeIdentRaw(args);
     1283
     1284    auto originColor = consumeOriginColor(args, context);
     1285    if (!originColor.isValid())
     1286        return { };
     1287
     1288    auto lightness = consumePercentOrIdent<CSSValueL>(args);
     1289    if (!lightness)
     1290        return { };
     1291
     1292    auto chroma = consumeNumberOrIdent<CSSValueC>(args);
     1293    if (!chroma)
     1294        return { };
     1295
     1296    auto hue = consumeHueOrIdent<CSSValueH>(args, context);
     1297    if (!hue)
     1298        return { };
     1299
     1300    auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
     1301    if (!alpha)
     1302        return { };
     1303
     1304    if (!args.atEnd())
     1305        return { };
     1306
     1307    auto originColorAsLCH = originColor.toColorTypeLossy<LCHA<float>>();
     1308
     1309    auto resolvedLightness = resolveRelativeColorChannel<CSSValueL, CSSValueC, CSSValueH, CSSValueAlpha>(*lightness, originColorAsLCH, [](auto lightness) {
     1310        return std::max(0.0, lightness);
     1311    });
     1312    auto resolvedChroma = resolveRelativeColorChannel<CSSValueL, CSSValueC, CSSValueH, CSSValueAlpha>(*chroma, originColorAsLCH, [](auto chroma) {
     1313        return std::max(0.0, chroma);
     1314    });
     1315    auto resolvedHue = resolveRelativeColorChannel<CSSValueL, CSSValueC, CSSValueH, CSSValueAlpha>(*hue, originColorAsLCH, [](auto hue) {
     1316        return normalizeHue(hue);
     1317    });
     1318    auto resolvedAlpha = resolveRelativeColorChannel<CSSValueL, CSSValueC, CSSValueH, CSSValueAlpha>(*alpha, originColorAsLCH);
     1319
     1320    return LCHA<float> { static_cast<float>(resolvedLightness), static_cast<float>(resolvedChroma), static_cast<float>(resolvedHue), static_cast<float>(resolvedAlpha) };
     1321}
     1322
    9341323static Color parseLCHParameters(CSSParserTokenRange& range, const CSSParserContext& context)
    9351324{
    9361325    ASSERT(range.peek().functionId() == CSSValueLch);
    9371326    auto args = consumeFunction(range);
     1327
     1328    if (context.relativeColorSyntaxEnabled && args.peek().id() == CSSValueFrom)
     1329        return parseRelativeLCHParameters(args, context);
    9381330
    9391331    auto lightness = consumePercentRaw(args);
     
    11321524    switch (functionId) {
    11331525    case CSSValueRgb:
     1526        color = parseRGBParameters<RGBFunctionMode::RGB>(colorRange, context);
     1527        break;
    11341528    case CSSValueRgba:
    1135         color = parseRGBParameters(colorRange, context);
     1529        color = parseRGBParameters<RGBFunctionMode::RGBA>(colorRange, context);
    11361530        break;
    11371531    case CSSValueHsl:
     1532        color = parseHSLParameters<HSLFunctionMode::HSL>(colorRange, context);
     1533        break;
    11381534    case CSSValueHsla:
    1139         color = parseHSLParameters(colorRange, context);
     1535        color = parseHSLParameters<HSLFunctionMode::HSLA>(colorRange, context);
    11401536        break;
    11411537    case CSSValueHwb:
Note: See TracChangeset for help on using the changeset viewer.