Skip to content

Commit

Permalink
[css-color-5] Implement light-dark() function for color values
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=266889
rdar://120171629

Reviewed by Tim Nguyen.

`light-dark()` allows authors to easily specify colors that adjust depending on
an element's used color scheme.

Spec: https://drafts.csswg.org/css-color-5/#light-dark

* LayoutTests/TestExpectations:
* LayoutTests/imported/w3c/web-platform-tests/css/css-color/light-dark-basic-expected.txt:
* Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml:
* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/css/CSSValueKeywords.in:
* Source/WebCore/css/color/CSSUnresolvedColor.cpp:
(WebCore::CSSUnresolvedColor::containsCurrentColor const):
(WebCore::CSSUnresolvedColor::createStyleColor const):
* Source/WebCore/css/color/CSSUnresolvedColor.h:
* Source/WebCore/css/color/CSSUnresolvedLightDark.cpp: Added.
(WebCore::serializationForCSS):
(WebCore::operator==):
(WebCore::createStyleColor):
* Source/WebCore/css/color/CSSUnresolvedLightDark.h: Added.
* Source/WebCore/css/parser/CSSParserContext.cpp:
(WebCore::CSSParserContext::CSSParserContext):
(WebCore::add):
* Source/WebCore/css/parser/CSSParserContext.h:
* Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp:
(WebCore::CSSPropertyParserHelpers::parseLightDarkFunctionParameters):
(WebCore::CSSPropertyParserHelpers::parseColorFunctionRaw):

Similar to system colors, there is not an existing mechanism to resolve dynamic
colors in workers.

(WebCore::CSSPropertyParserHelpers::parseColorFunction):
* Source/WebInspectorUI/UserInterface/Models/CSSKeywordCompletions.js:

Canonical link: https://commits.webkit.org/272560@main
  • Loading branch information
pxlcoder committed Jan 2, 2024
1 parent 7410260 commit 9240183
Show file tree
Hide file tree
Showing 14 changed files with 189 additions and 10 deletions.
4 changes: 0 additions & 4 deletions LayoutTests/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -4758,10 +4758,6 @@ webkit.org/b/245970 imported/w3c/web-platform-tests/css/css-color/relative-curre
webkit.org/b/245970 imported/w3c/web-platform-tests/css/css-color/relative-currentcolor-xyzd50-01.html [ ImageOnlyFailure ]
webkit.org/b/245970 imported/w3c/web-platform-tests/css/css-color/relative-currentcolor-xyzd65-01.html [ ImageOnlyFailure ]

# light-dark()
webkit.org/b/266889 imported/w3c/web-platform-tests/css/css-color/light-dark-currentcolor.html [ ImageOnlyFailure ]
webkit.org/b/266889 imported/w3c/web-platform-tests/css/css-color/light-dark-inheritance.html [ ImageOnlyFailure ]

webkit.org/b/214456 imported/w3c/web-platform-tests/css/css-images/conic-gradient-angle.html [ ImageOnlyFailure ]
webkit.org/b/214456 imported/w3c/web-platform-tests/css/css-images/conic-gradient-angle-negative.html [ ImageOnlyFailure ]
webkit.org/b/214456 imported/w3c/web-platform-tests/css/css-images/conic-gradient-center.html [ ImageOnlyFailure ]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

FAIL light-dark(white, black) assert_not_equals: Should be valid got disallowed value ""
FAIL light-dark(light-dark(white, red), red) assert_not_equals: Should be valid got disallowed value ""
PASS light-dark(white, black)
PASS light-dark(light-dark(white, red), red)

14 changes: 14 additions & 0 deletions Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1118,6 +1118,20 @@ CSSInputSecurityEnabled:
WebCore:
default: false

CSSLightDarkEnabled:
type: bool
status: testable
category: css
humanReadableName: "CSS light-dark()"
humanReadableDescription: "Enable support for CSS light-dark() defined in CSS Color 5"
defaultValue:
WebKitLegacy:
default: false
WebKit:
default: false
WebCore:
default: false

CSSMarginTrimEnabled:
type: bool
status: stable
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/Sources.txt
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,7 @@ css/calc/CSSCalcValue.cpp
css/color/CSSResolvedColorMix.cpp
css/color/CSSUnresolvedColor.cpp
css/color/CSSUnresolvedColorMix.cpp
css/color/CSSUnresolvedLightDark.cpp
css/parser/CSSAtRuleID.cpp
css/parser/CSSCustomPropertySyntax.cpp
css/parser/CSSParser.cpp
Expand Down
4 changes: 4 additions & 0 deletions Source/WebCore/WebCore.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19720,6 +19720,8 @@
E517670220B88C1400D41167 /* DataListSuggestionInformation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DataListSuggestionInformation.h; sourceTree = "<group>"; };
E51A81DE17298D7700BFCA61 /* JSPerformance.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSPerformance.cpp; sourceTree = "<group>"; };
E51D6A1D24E1E25500891CFA /* DateTimeFieldsState.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DateTimeFieldsState.h; sourceTree = "<group>"; };
E521F8292B39F09C004BD905 /* CSSUnresolvedLightDark.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSSUnresolvedLightDark.h; sourceTree = "<group>"; };
E521F82B2B39F192004BD905 /* CSSUnresolvedLightDark.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CSSUnresolvedLightDark.cpp; sourceTree = "<group>"; };
E526AF3E1727F8F200E41781 /* Performance.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Performance.cpp; sourceTree = "<group>"; };
E52CF54C20A268AC00DADA27 /* DataListSuggestionsClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DataListSuggestionsClient.h; sourceTree = "<group>"; };
E52CF54E20A35A2800DADA27 /* DataListSuggestionPicker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DataListSuggestionPicker.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -32884,6 +32886,8 @@
BCB35370295D121700298CA3 /* CSSUnresolvedColor.h */,
BC04A7C8296E57810003F9DB /* CSSUnresolvedColorMix.cpp */,
BC04A7C9296E57810003F9DB /* CSSUnresolvedColorMix.h */,
E521F82B2B39F192004BD905 /* CSSUnresolvedLightDark.cpp */,
E521F8292B39F09C004BD905 /* CSSUnresolvedLightDark.h */,
);
path = color;
sourceTree = "<group>";
Expand Down
3 changes: 3 additions & 0 deletions Source/WebCore/css/CSSValueKeywords.in
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,9 @@ AAA-large
// color-mix()
color-mix

// light-dark()
light-dark

// color-space-interpolation
in
// srgb
Expand Down
5 changes: 4 additions & 1 deletion Source/WebCore/css/color/CSSUnresolvedColor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ bool CSSUnresolvedColor::containsCurrentColor() const
return WTF::switchOn(m_value,
[&] (const CSSUnresolvedColorMix& unresolved) {
return StyleColor::containsCurrentColor(unresolved.mixComponents1.color) || StyleColor::containsCurrentColor(unresolved.mixComponents2.color);
},
[&] (const CSSUnresolvedLightDark& unresolved) {
return StyleColor::containsCurrentColor(unresolved.lightColor) || StyleColor::containsCurrentColor(unresolved.darkColor);
}
);
}
Expand All @@ -60,7 +63,7 @@ bool CSSUnresolvedColor::equals(const CSSUnresolvedColor& other) const
StyleColor CSSUnresolvedColor::createStyleColor(const Document& document, RenderStyle& style, Style::ForVisitedLink forVisitedLink) const
{
return WTF::switchOn(m_value,
[&] (const CSSUnresolvedColorMix& unresolved) {
[&] (const auto& unresolved) {
return WebCore::createStyleColor(unresolved, document, style, forVisitedLink);
}
);
Expand Down
4 changes: 3 additions & 1 deletion Source/WebCore/css/color/CSSUnresolvedColor.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#pragma once

#include "CSSUnresolvedColorMix.h"
#include "CSSUnresolvedLightDark.h"
#include <variant>
#include <wtf/Forward.h>

Expand Down Expand Up @@ -60,7 +61,8 @@ class CSSUnresolvedColor {
private:
// FIXME: Add support for unresolved relative colors.
std::variant<
CSSUnresolvedColorMix
CSSUnresolvedColorMix,
CSSUnresolvedLightDark
> m_value;
};

Expand Down
61 changes: 61 additions & 0 deletions Source/WebCore/css/color/CSSUnresolvedLightDark.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (C) 2023 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "config.h"
#include "CSSUnresolvedLightDark.h"

#include "ColorFromPrimitiveValue.h"
#include "ColorSerialization.h"
#include "Document.h"
#include "StyleBuilderState.h"

namespace WebCore {

void serializationForCSS(StringBuilder& builder, const CSSUnresolvedLightDark& lightDark)
{
builder.append("light-dark("_s, lightDark.lightColor->customCSSText(), ", "_s, lightDark.darkColor->customCSSText(), ')');
}

String serializationForCSS(const CSSUnresolvedLightDark& unresolved)
{
StringBuilder builder;
serializationForCSS(builder, unresolved);
return builder.toString();
}

bool operator==(const CSSUnresolvedLightDark& a, const CSSUnresolvedLightDark& b)
{
return compareCSSValue(a.lightColor, b.lightColor)
&& compareCSSValue(a.darkColor, b.darkColor);
}

StyleColor createStyleColor(const CSSUnresolvedLightDark& unresolved, const Document& document, RenderStyle& style, Style::ForVisitedLink forVisitedLink)
{
if (document.useDarkAppearance(&style))
return colorFromPrimitiveValue(document, style, unresolved.darkColor.get(), forVisitedLink);
return colorFromPrimitiveValue(document, style, unresolved.lightColor.get(), forVisitedLink);
}

} // namespace WebCore
54 changes: 54 additions & 0 deletions Source/WebCore/css/color/CSSUnresolvedLightDark.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (C) 2023 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/

#pragma once

#include "CSSPrimitiveValue.h"
#include "StyleColor.h"
#include <wtf/Forward.h>
#include <wtf/RefCounted.h>

namespace WebCore {

namespace Style {
enum class ForVisitedLink : bool;
}

class Document;
class RenderStyle;

struct CSSUnresolvedLightDark {
friend bool operator==(const CSSUnresolvedLightDark&, const CSSUnresolvedLightDark&);

Ref<CSSPrimitiveValue> lightColor;
Ref<CSSPrimitiveValue> darkColor;
};

void serializationForCSS(StringBuilder&, const CSSUnresolvedLightDark&);
String serializationForCSS(const CSSUnresolvedLightDark&);

StyleColor createStyleColor(const CSSUnresolvedLightDark&, const Document&, RenderStyle&, Style::ForVisitedLink);

} // namespace WebCore
5 changes: 4 additions & 1 deletion Source/WebCore/css/parser/CSSParserContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ CSSParserContext::CSSParserContext(CSSParserMode mode, const URL& baseURL)
if (mode == UASheetMode) {
colorMixEnabled = true;
focusVisibleEnabled = true;
lightDarkEnabled = true;
popoverAttributeEnabled = true;
propertySettings.cssContainmentEnabled = true;
propertySettings.cssInputSecurityEnabled = true;
Expand Down Expand Up @@ -112,6 +113,7 @@ CSSParserContext::CSSParserContext(const Document& document, const URL& sheetBas
#if ENABLE(SERVICE_CONTROLS)
, imageControlsEnabled { document.settings().imageControlsEnabled() }
#endif
, lightDarkEnabled { document.settings().cssLightDarkEnabled() }
, propertySettings { CSSPropertySettings { document.settings() } }
{
}
Expand Down Expand Up @@ -153,7 +155,8 @@ void add(Hasher& hasher, const CSSParserContext& context)
#if ENABLE(SERVICE_CONTROLS)
| context.imageControlsEnabled << 30
#endif
| (uint64_t)context.mode << 31; // This is multiple bits, so keep it last.
| context.lightDarkEnabled << 31
| (uint64_t)context.mode << 32; // This is multiple bits, so keep it last.
add(hasher, context.baseURL, context.charset, context.propertySettings, bits);
}

Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/css/parser/CSSParserContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ struct CSSParserContext {
#if ENABLE(SERVICE_CONTROLS)
bool imageControlsEnabled : 1 { false };
#endif
bool lightDarkEnabled : 1 { false };

// Settings, those affecting properties.
CSSPropertySettings propertySettings;
Expand Down
37 changes: 37 additions & 0 deletions Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3117,6 +3117,37 @@ static std::optional<ColorOrUnresolvedColor> parseColorMixFunctionParameters(CSS
} } };
}

static std::optional<ColorOrUnresolvedColor> parseLightDarkFunctionParameters(CSSParserTokenRange& range, const CSSParserContext& context)
{
// light-dark() = light-dark( <color>, <color> )

ASSERT(range.peek().functionId() == CSSValueLightDark);

if (!context.lightDarkEnabled)
return std::nullopt;

auto args = consumeFunction(range);

auto lightColor = consumeColor(args, context);
if (!lightColor)
return std::nullopt;

if (!consumeCommaIncludingWhitespace(args))
return std::nullopt;

auto darkColor = consumeColor(args, context);
if (!darkColor)
return std::nullopt;

if (!args.atEnd())
return std::nullopt;

return { CSSUnresolvedColor { CSSUnresolvedLightDark {
lightColor.releaseNonNull(),
darkColor.releaseNonNull()
} } };
}

static std::optional<SRGBA<uint8_t>> parseHexColor(CSSParserTokenRange& range, bool acceptQuirkyColors)
{
String string;
Expand Down Expand Up @@ -3200,6 +3231,9 @@ static Color parseColorFunctionRaw(CSSParserTokenRange& range, const CSSParserCo
case CSSValueColorMix:
color = parseColorMixFunctionParametersRaw(colorRange, context);
break;
case CSSValueLightDark:
// FIXME: Need a worker-safe way to compute light-dark colors.
return { };
default:
return { };
}
Expand Down Expand Up @@ -3256,6 +3290,9 @@ static std::optional<ColorOrUnresolvedColor> parseColorFunction(CSSParserTokenRa
case CSSValueColorMix:
color = parseColorMixFunctionParameters(colorRange, context);
break;
case CSSValueLightDark:
color = parseLightDarkFunctionParameters(colorRange, context);
break;
default:
return { };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ WI.CSSKeywordCompletions._colors = [
"royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna", "skyblue", "slateblue",
"slategray", "slategrey", "snow", "springgreen", "steelblue", "tan", "thistle", "tomato", "turquoise", "violet",
"wheat", "whitesmoke", "yellowgreen", "rgb()", "rgba()", "hsl()", "hsla()", "color()", "hwb()", "lch()", "lab()",
"color-mix()", "color-contrast()",
"color-mix()", "color-contrast()", "light-dark()",
];

WI.CSSKeywordCompletions._colorAwareProperties = new Set([
Expand Down

0 comments on commit 9240183

Please sign in to comment.