Skip to content

Add modern HSL syntax fast path to CSSParserFastPaths#62026

Merged
webkit-commit-queue merged 1 commit intoWebKit:mainfrom
cdumez:311468_modern_hsl
Apr 5, 2026
Merged

Add modern HSL syntax fast path to CSSParserFastPaths#62026
webkit-commit-queue merged 1 commit intoWebKit:mainfrom
cdumez:311468_modern_hsl

Conversation

@cdumez
Copy link
Copy Markdown
Contributor

@cdumez cdumez commented Apr 4, 2026

524b854

Add modern HSL syntax fast path to CSSParserFastPaths
https://bugs.webkit.org/show_bug.cgi?id=311468

Reviewed by Darin Adler.

The existing HSL fast path only handled the legacy comma-separated syntax
(e.g., hsl(120, 100%, 50%)). Modern CSS uses space-separated syntax
(e.g., hsl(120 100% 50%) or hsl(120 100% 50% / 0.5)), which was falling
through to the full tokenizer and parser.

Rename parseLegacyHSL() to parseHSL() and teach it to handle the modern
space-separated syntax with optional slash-separated alpha, including
support for angle units (deg, rad) and percentage alpha values. The
legacy path is tried first since its comma check is effectively free
when it fails.

Also add API tests for both legacy and modern HSL parsing through
parseSimpleColor(), covering primary colors, black/white, alpha variants,
angle units, hue wrapping, and modern-vs-legacy equivalence, along with
negative tests for malformed modern HSL inputs.

Test: Tools/TestWebKitAPI/Tests/WebCore/CSSParserFastPaths.cpp

* Source/WebCore/css/parser/CSSParserFastPaths.cpp:
(WebCore::parseHSL):
(WebCore::parseNumericColor):
(WebCore::parseColor):
(WebCore::CSSParserFastPaths::parseNamedColor):
(WebCore::isUniversalKeyword):
(WebCore::parseLegacyHSL): Deleted.
* Tools/TestWebKitAPI/Tests/WebCore/CSSParserFastPaths.cpp:
(TestWebKitAPI::TEST(CSSParserFastPaths, ParseLegacyHsl)):
(TestWebKitAPI::TEST(CSSParserFastPaths, ParseModernHsl)):
(TestWebKitAPI::TEST(CSSParserFastPaths, ParseModernHslInvalid)):

Canonical link: https://commits.webkit.org/310603@main

9ad8e88

Misc iOS, visionOS, tvOS & watchOS macOS Linux Windows Apple Internal
✅ 🧪 style ✅ 🛠 ios ✅ 🛠 mac ✅ 🛠 wpe ✅ 🛠 win ✅ 🛠 ios-apple
✅ 🧪 bindings ✅ 🛠 ios-sim ✅ 🛠 mac-AS-debug ✅ 🧪 wpe-wk2 ✅ 🧪 win-tests ✅ 🛠 mac-apple
✅ 🧪 webkitperl ✅ 🧪 ios-wk2 ✅ 🧪 api-mac ✅ 🧪 api-wpe ⏳ 🛠 vision-apple
✅ 🧪 ios-wk2-wpt ✅ 🧪 api-mac-debug ✅ 🛠 gtk3-libwebrtc
✅ 🧪 api-ios ✅ 🧪 mac-wk1 ✅ 🛠 gtk
✅ 🛠 ios-safer-cpp ✅ 🧪 mac-wk2 ❌ 🧪 gtk-wk2
✅ 🛠 vision ✅ 🧪 mac-AS-debug-wk2 ✅ 🧪 api-gtk
✅ 🛠 🧪 merge ✅ 🛠 vision-sim ✅ 🧪 mac-wk2-stress ✅ 🛠 playstation
✅ 🧪 vision-wk2 ✅ 🧪 mac-intel-wk2
✅ 🛠 tv ✅ 🛠 mac-safer-cpp
✅ 🛠 tv-sim
✅ 🛠 watch
✅ 🛠 watch-sim

@cdumez cdumez self-assigned this Apr 4, 2026
@cdumez cdumez added the CSS Cascading Style Sheets implementation label Apr 4, 2026
@weinig
Copy link
Copy Markdown
Contributor

weinig commented Apr 4, 2026

I can’t review in detail right away, and I have no problem with adding this, but I have been curious for a while if we have any data on how common the fast parser gets hit in the wild and/or perf tests.

My recollection is that it only gets applied for JS element.style[prop] = “foo”. Is that still the case?

@cdumez
Copy link
Copy Markdown
Contributor Author

cdumez commented Apr 4, 2026

I can’t review in detail right away, and I have no problem with adding this, but I have been curious for a while if we have any data on how common the fast parser gets hit in the wild and/or perf tests.

My recollection is that it only gets applied for JS element.style[prop] = “foo”. Is that still the case?

I can't say I'm very familiar with our CSS parser so I asked Claude and it appears you are mostly correct!

CSSParserFastPaths are essentially only used when parsing a CSS property value from a raw string — which is primarily the CSSOM / JavaScript path. The
main call sites are:

  1. CSSParser::parseValue — called from MutableStyleProperties::setProperty, which is the CSSOM path (element.style.prop = "foo",
    element.style.setProperty(...), editing commands, @font-face JS API setters, etc.)
  2. CSSPropertyParser::parseStylePropertyLonghand(String&) — only called from CSSValuePool for caching font family values.
  3. Color/HTMLElement — for legacy HTML color attribute parsing.
  4. CSS.supports() validation.

During normal stylesheet parsing (parsing <style> blocks or .css files), the input is already tokenized into a CSSParserTokenRange, and the parser calls
CSSPropertyParsing::parseStylePropertyLonghand(range, ...) directly — bypassing CSSParserFastPaths entirely.

I did not realize that when I wrote the PR but I did think it was odd to support the legacy syntax and not the modern one in the fast path. I figured more and more developers are likely switching to the new syntax 🤷🏻 I'll let CSS experts decide if it's worth it.

@cdumez cdumez marked this pull request as ready for review April 4, 2026 11:24
@cdumez cdumez requested review from anttijk, darinadler and weinig April 4, 2026 11:25
Copy link
Copy Markdown
Member

@darinadler darinadler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m kind of surprised that the syntax for modern HSL is the same inside hsl() and hsla().


skip(characters, hueEnd);

auto parsePercentage = [&](std::span<const CharacterType>& characters) -> std::optional<double> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we’re going to capture something here, why not capture characters too?

Comment on lines +672 to +673
auto resultColor = convertToColor<HSLFunctionModern, CSSColorFunctionForm::Absolute>(typedColor, 0);
return resultColor.tryGetAsSRGBABytes();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
auto resultColor = convertToColor<HSLFunctionModern, CSSColorFunctionForm::Absolute>(typedColor, 0);
return resultColor.tryGetAsSRGBABytes();
return convertToColor<HSLFunctionModern, CSSColorFunctionForm::Absolute>(typedColor, 0).tryGetAsSRGBABytes();

Comment on lines +731 to +736
if (mightBeHSLA(characters)) {
auto innerCharacters = characters.subspan(5);
if (auto color = parseLegacyHSL(innerCharacters))
return color;
return parseModernHSL(innerCharacters);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not add a parseHSL that does both legacy and modern?

Suggested change
if (mightBeHSLA(characters)) {
auto innerCharacters = characters.subspan(5);
if (auto color = parseLegacyHSL(innerCharacters))
return color;
return parseModernHSL(innerCharacters);
}
if (mightBeHSLA(characters))
return parseHSL(characters.subspan(5));

Comment on lines +738 to +743
if (mightBeHSL(characters)) {
auto innerCharacters = characters.subspan(4);
if (auto color = parseLegacyHSL(innerCharacters))
return color;
return parseModernHSL(innerCharacters);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not add a parseHSL that does both legacy and modern?

Suggested change
if (mightBeHSL(characters)) {
auto innerCharacters = characters.subspan(4);
if (auto color = parseLegacyHSL(innerCharacters))
return color;
return parseModernHSL(innerCharacters);
}
if (mightBeHSL(characters))
return parseHSL(characters.subspan(4));

@cdumez cdumez force-pushed the 311468_modern_hsl branch from 6f4b85c to 9ad8e88 Compare April 5, 2026 08:33
@cdumez cdumez requested a review from darinadler April 5, 2026 08:35
@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Apr 5, 2026
@cdumez cdumez added merge-queue Applied to send a pull request to merge-queue and removed merging-blocked Applied to prevent a change from being merged labels Apr 5, 2026
https://bugs.webkit.org/show_bug.cgi?id=311468

Reviewed by Darin Adler.

The existing HSL fast path only handled the legacy comma-separated syntax
(e.g., hsl(120, 100%, 50%)). Modern CSS uses space-separated syntax
(e.g., hsl(120 100% 50%) or hsl(120 100% 50% / 0.5)), which was falling
through to the full tokenizer and parser.

Rename parseLegacyHSL() to parseHSL() and teach it to handle the modern
space-separated syntax with optional slash-separated alpha, including
support for angle units (deg, rad) and percentage alpha values. The
legacy path is tried first since its comma check is effectively free
when it fails.

Also add API tests for both legacy and modern HSL parsing through
parseSimpleColor(), covering primary colors, black/white, alpha variants,
angle units, hue wrapping, and modern-vs-legacy equivalence, along with
negative tests for malformed modern HSL inputs.

Test: Tools/TestWebKitAPI/Tests/WebCore/CSSParserFastPaths.cpp

* Source/WebCore/css/parser/CSSParserFastPaths.cpp:
(WebCore::parseHSL):
(WebCore::parseNumericColor):
(WebCore::parseColor):
(WebCore::CSSParserFastPaths::parseNamedColor):
(WebCore::isUniversalKeyword):
(WebCore::parseLegacyHSL): Deleted.
* Tools/TestWebKitAPI/Tests/WebCore/CSSParserFastPaths.cpp:
(TestWebKitAPI::TEST(CSSParserFastPaths, ParseLegacyHsl)):
(TestWebKitAPI::TEST(CSSParserFastPaths, ParseModernHsl)):
(TestWebKitAPI::TEST(CSSParserFastPaths, ParseModernHslInvalid)):

Canonical link: https://commits.webkit.org/310603@main
@webkit-commit-queue
Copy link
Copy Markdown
Collaborator

Committed 310603@main (524b854): https://commits.webkit.org/310603@main

Reviewed commits have been landed. Closing PR #62026 and removing active labels.

@webkit-commit-queue webkit-commit-queue merged commit 524b854 into WebKit:main Apr 5, 2026
@webkit-commit-queue webkit-commit-queue removed the merge-queue Applied to send a pull request to merge-queue label Apr 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CSS Cascading Style Sheets implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants