diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 93548004139..44add3c408d 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -268,6 +268,7 @@ set(SOURCES CSS/StyleValues/RectStyleValue.cpp CSS/StyleValues/RepeatStyleStyleValue.cpp CSS/StyleValues/ScrollbarColorStyleValue.cpp + CSS/StyleValues/ScrollFunctionStyleValue.cpp CSS/StyleValues/ShadowStyleValue.cpp CSS/StyleValues/ShorthandStyleValue.cpp CSS/StyleValues/StyleValue.cpp diff --git a/Libraries/LibWeb/CSS/Enums.json b/Libraries/LibWeb/CSS/Enums.json index 0bdd578cdee..00d4b23f8e4 100644 --- a/Libraries/LibWeb/CSS/Enums.json +++ b/Libraries/LibWeb/CSS/Enums.json @@ -721,6 +721,11 @@ "thin", "none" ], + "scroller": [ + "root", + "nearest", + "self" + ], "shape-box": [ "content-box", "padding-box", diff --git a/Libraries/LibWeb/CSS/Keywords.json b/Libraries/LibWeb/CSS/Keywords.json index 2a301d937a3..2344561ba9f 100644 --- a/Libraries/LibWeb/CSS/Keywords.json +++ b/Libraries/LibWeb/CSS/Keywords.json @@ -450,6 +450,7 @@ "revert-layer", "ridge", "right", + "root", "rotate-left", "rotate-right", "round", @@ -480,6 +481,7 @@ "searchfield", "selecteditem", "selecteditemtext", + "self", "self-block", "self-block-end", "self-block-start", diff --git a/Libraries/LibWeb/CSS/Parser/Parser.h b/Libraries/LibWeb/CSS/Parser/Parser.h index 81d01a58e9a..1a3061a825a 100644 --- a/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Libraries/LibWeb/CSS/Parser/Parser.h @@ -444,6 +444,7 @@ private: RefPtr parse_time_percentage_value(TokenStream&); RefPtr parse_view_timeline_inset_value(TokenStream&); + RefPtr parse_scroll_function_value(TokenStream&); using ParseFunction = AK::Function(TokenStream&)>; RefPtr parse_comma_separated_value_list(TokenStream&, ParseFunction); diff --git a/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp b/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp index 66133720f56..2b47f57fbb6 100644 --- a/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp @@ -248,6 +248,8 @@ Optional Parser::parse_css_value_for_properties(Readon return parsed.release_value(); if (auto parsed = parse_for_type(ValueType::Rect); parsed.has_value()) return parsed.release_value(); + if (auto parsed = parse_for_type(ValueType::ScrollFunction); parsed.has_value()) + return parsed.release_value(); if (auto parsed = parse_for_type(ValueType::String); parsed.has_value()) return parsed.release_value(); if (auto parsed = parse_for_type(ValueType::TransformFunction); parsed.has_value()) diff --git a/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp b/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp index fe4d6d00151..81639cc8493 100644 --- a/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/ValueParsing.cpp @@ -64,6 +64,7 @@ #include #include #include +#include #include #include #include @@ -1429,6 +1430,61 @@ RefPtr Parser::parse_keyword_value(TokenStream return nullptr; } +// https://drafts.csswg.org/scroll-animations-1/#funcdef-scroll +RefPtr Parser::parse_scroll_function_value(TokenStream& tokens) +{ + // = scroll( [ || ]? ) + auto transaction = tokens.begin_transaction(); + auto const& function_token = tokens.consume_a_token(); + if (!function_token.is_function("scroll"sv)) + return nullptr; + + Optional scroller; + Optional axis; + + auto argument_tokens = TokenStream { function_token.function().value }; + + while (argument_tokens.has_next_token()) { + tokens.discard_whitespace(); + + if (!argument_tokens.has_next_token()) + break; + + auto keyword_value = parse_keyword_value(argument_tokens); + + if (!keyword_value) + return nullptr; + + if (auto maybe_scroller = keyword_to_scroller(keyword_value->to_keyword()); maybe_scroller.has_value()) { + if (scroller.has_value()) + return nullptr; + + scroller = maybe_scroller; + continue; + } + + if (auto maybe_axis = keyword_to_axis(keyword_value->to_keyword()); maybe_axis.has_value()) { + if (axis.has_value()) + return nullptr; + + axis = maybe_axis; + continue; + } + + return nullptr; + } + + // By default, scroll() references the block axis of the nearest ancestor scroll container. + if (!scroller.has_value()) + scroller = Scroller::Nearest; + + if (!axis.has_value()) + axis = Axis::Block; + + transaction.commit(); + return ScrollFunctionStyleValue::create(scroller.value(), axis.value()); +} + // https://www.w3.org/TR/CSS2/visufx.html#value-def-shape RefPtr Parser::parse_rect_value(TokenStream& tokens) { @@ -5074,6 +5130,8 @@ RefPtr Parser::parse_value(ValueType value_type, TokenStream + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "ScrollFunctionStyleValue.h" + +namespace Web::CSS { + +String ScrollFunctionStyleValue::to_string(SerializationMode) const +{ + StringBuilder builder; + builder.append("scroll("sv); + + if (m_scroller != Scroller::Nearest) + builder.append(CSS::to_string(m_scroller)); + + if (m_axis != Axis::Block) { + if (m_scroller != Scroller::Nearest) + builder.append(' '); + builder.append(CSS::to_string(m_axis)); + } + + builder.append(')'); + return builder.to_string_without_validation(); +} + +} diff --git a/Libraries/LibWeb/CSS/StyleValues/ScrollFunctionStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/ScrollFunctionStyleValue.h new file mode 100644 index 00000000000..0e639b678a6 --- /dev/null +++ b/Libraries/LibWeb/CSS/StyleValues/ScrollFunctionStyleValue.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025, Callum Law + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::CSS { + +class ScrollFunctionStyleValue final : public StyleValueWithDefaultOperators { +public: + static ValueComparingNonnullRefPtr create(Scroller scroller, Axis axis) + { + return adopt_ref(*new ScrollFunctionStyleValue(scroller, axis)); + } + virtual ~ScrollFunctionStyleValue() override = default; + + virtual String to_string(SerializationMode) const override; + bool properties_equal(ScrollFunctionStyleValue const& other) const { return m_scroller == other.m_scroller && m_axis == other.m_axis; } + + Scroller scroller() const { return m_scroller; } + Axis axis() const { return m_axis; } + +private: + explicit ScrollFunctionStyleValue(Scroller scroller, Axis axis) + : StyleValueWithDefaultOperators(Type::ScrollFunction) + , m_scroller(scroller) + , m_axis(axis) + { + } + + Scroller m_scroller; + Axis m_axis; +}; + +} diff --git a/Libraries/LibWeb/CSS/StyleValues/StyleValue.cpp b/Libraries/LibWeb/CSS/StyleValues/StyleValue.cpp index edd902ef21a..c65f55b4d69 100644 --- a/Libraries/LibWeb/CSS/StyleValues/StyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValues/StyleValue.cpp @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include diff --git a/Libraries/LibWeb/CSS/StyleValues/StyleValue.h b/Libraries/LibWeb/CSS/StyleValues/StyleValue.h index 4adeb032662..72bfbf57453 100644 --- a/Libraries/LibWeb/CSS/StyleValues/StyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/StyleValue.h @@ -79,6 +79,7 @@ namespace Web::CSS { __ENUMERATE_CSS_STYLE_VALUE_TYPE(Resolution, resolution, ResolutionStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(ScrollbarColor, scrollbar_color, ScrollbarColorStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(ScrollbarGutter, scrollbar_gutter, ScrollbarGutterStyleValue) \ + __ENUMERATE_CSS_STYLE_VALUE_TYPE(ScrollFunction, scroll_function, ScrollFunctionStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Shadow, shadow, ShadowStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Shorthand, shorthand, ShorthandStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(String, string, StringStyleValue) \ diff --git a/Libraries/LibWeb/CSS/ValueType.cpp b/Libraries/LibWeb/CSS/ValueType.cpp index adf1202f4e2..f93c49f3ea3 100644 --- a/Libraries/LibWeb/CSS/ValueType.cpp +++ b/Libraries/LibWeb/CSS/ValueType.cpp @@ -67,6 +67,8 @@ Optional value_type_from_string(StringView string) return ValueType::Rect; if (string.equals_ignoring_ascii_case("resolution"sv)) return ValueType::Resolution; + if (string.equals_ignoring_ascii_case("scroll-function"sv)) + return ValueType::ScrollFunction; if (string.equals_ignoring_ascii_case("string"sv)) return ValueType::String; if (string.equals_ignoring_ascii_case("time"sv)) @@ -147,6 +149,8 @@ StringView value_type_to_string(ValueType value_type) return "Rect"sv; case Web::CSS::ValueType::Resolution: return "Resolution"sv; + case Web::CSS::ValueType::ScrollFunction: + return "ScrollFunction"sv; case Web::CSS::ValueType::String: return "String"sv; case Web::CSS::ValueType::Time: diff --git a/Libraries/LibWeb/CSS/ValueType.h b/Libraries/LibWeb/CSS/ValueType.h index 1333ca2efcd..f17f3eb7fe2 100644 --- a/Libraries/LibWeb/CSS/ValueType.h +++ b/Libraries/LibWeb/CSS/ValueType.h @@ -43,6 +43,7 @@ enum class ValueType : u8 { Ratio, Rect, Resolution, + ScrollFunction, String, Time, TimePercentage, diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index 0567972792e..e968c8786d4 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -366,6 +366,7 @@ class ShadowStyleValue; class ShorthandStyleValue; class Size; class ScrollbarColorStyleValue; +class ScrollFunctionStyleValue; class StringStyleValue; class StyleComputer; class StylePropertyMap; diff --git a/Tests/LibWeb/Text/expected/wpt-import/scroll-animations/css/animation-timeline-computed.txt b/Tests/LibWeb/Text/expected/wpt-import/scroll-animations/css/animation-timeline-computed.txt index 970e291a09f..1f7de730af2 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/scroll-animations/css/animation-timeline-computed.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/scroll-animations/css/animation-timeline-computed.txt @@ -2,8 +2,8 @@ Harness status: OK Found 44 tests -13 Pass -31 Fail +27 Pass +17 Fail Pass Property animation-timeline value 'initial' Pass Property animation-timeline value 'inherit' Pass Property animation-timeline value 'unset' @@ -19,20 +19,20 @@ Pass Property animation-timeline value '--test1, --test2' Pass Property animation-timeline value '--test1, --test2, none, --test3, auto' Pass The animation-timeline property shows up in CSSStyleDeclaration enumeration Pass The animation-timeline property shows up in CSSStyleDeclaration.cssText -Fail Property animation-timeline value 'scroll()' -Fail Property animation-timeline value 'scroll(block)' -Fail Property animation-timeline value 'scroll(inline)' -Fail Property animation-timeline value 'scroll(x)' -Fail Property animation-timeline value 'scroll(y)' -Fail Property animation-timeline value 'scroll(root)' -Fail Property animation-timeline value 'scroll(nearest)' -Fail Property animation-timeline value 'scroll(self)' -Fail Property animation-timeline value 'scroll(self), scroll(nearest)' -Fail Property animation-timeline value 'scroll(inline nearest)' -Fail Property animation-timeline value 'scroll(nearest inline)' -Fail Property animation-timeline value 'scroll(block self)' -Fail Property animation-timeline value 'scroll(self block)' -Fail Property animation-timeline value 'scroll(y root)' +Pass Property animation-timeline value 'scroll()' +Pass Property animation-timeline value 'scroll(block)' +Pass Property animation-timeline value 'scroll(inline)' +Pass Property animation-timeline value 'scroll(x)' +Pass Property animation-timeline value 'scroll(y)' +Pass Property animation-timeline value 'scroll(root)' +Pass Property animation-timeline value 'scroll(nearest)' +Pass Property animation-timeline value 'scroll(self)' +Pass Property animation-timeline value 'scroll(self), scroll(nearest)' +Pass Property animation-timeline value 'scroll(inline nearest)' +Pass Property animation-timeline value 'scroll(nearest inline)' +Pass Property animation-timeline value 'scroll(block self)' +Pass Property animation-timeline value 'scroll(self block)' +Pass Property animation-timeline value 'scroll(y root)' Fail Property animation-timeline value 'view()' Fail Property animation-timeline value 'view(block)' Fail Property animation-timeline value 'view(inline)' diff --git a/Tests/LibWeb/Text/expected/wpt-import/scroll-animations/css/animation-timeline-parsing.txt b/Tests/LibWeb/Text/expected/wpt-import/scroll-animations/css/animation-timeline-parsing.txt index 52f7b2afc15..2badf3917aa 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/scroll-animations/css/animation-timeline-parsing.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/scroll-animations/css/animation-timeline-parsing.txt @@ -2,8 +2,8 @@ Harness status: OK Found 69 tests -31 Pass -38 Fail +45 Pass +24 Fail Pass e.style['animation-timeline'] = "initial" should set the property value Pass e.style['animation-timeline'] = "inherit" should set the property value Pass e.style['animation-timeline'] = "unset" should set the property value @@ -26,20 +26,20 @@ Pass e.style['animation-timeline'] = "\"foo\" \"bar\"" should not set the proper Pass e.style['animation-timeline'] = "rgb(1, 2, 3)" should not set the property value Pass e.style['animation-timeline'] = "#fefefe" should not set the property value Pass e.style['animation-timeline'] = "\"test\"" should not set the property value -Fail e.style['animation-timeline'] = "scroll()" should set the property value -Fail e.style['animation-timeline'] = " scroll() " should set the property value -Fail e.style['animation-timeline'] = "scroll(block)" should set the property value -Fail e.style['animation-timeline'] = "scroll(inline)" should set the property value -Fail e.style['animation-timeline'] = "scroll(x)" should set the property value -Fail e.style['animation-timeline'] = "scroll(y)" should set the property value -Fail e.style['animation-timeline'] = "scroll(root)" should set the property value -Fail e.style['animation-timeline'] = "scroll(nearest)" should set the property value -Fail e.style['animation-timeline'] = "scroll(self)" should set the property value -Fail e.style['animation-timeline'] = "scroll(inline nearest)" should set the property value -Fail e.style['animation-timeline'] = "scroll(nearest inline)" should set the property value -Fail e.style['animation-timeline'] = "scroll(block self)" should set the property value -Fail e.style['animation-timeline'] = "scroll(self block)" should set the property value -Fail e.style['animation-timeline'] = "scroll(y root)" should set the property value +Pass e.style['animation-timeline'] = "scroll()" should set the property value +Pass e.style['animation-timeline'] = " scroll() " should set the property value +Pass e.style['animation-timeline'] = "scroll(block)" should set the property value +Pass e.style['animation-timeline'] = "scroll(inline)" should set the property value +Pass e.style['animation-timeline'] = "scroll(x)" should set the property value +Pass e.style['animation-timeline'] = "scroll(y)" should set the property value +Pass e.style['animation-timeline'] = "scroll(root)" should set the property value +Pass e.style['animation-timeline'] = "scroll(nearest)" should set the property value +Pass e.style['animation-timeline'] = "scroll(self)" should set the property value +Pass e.style['animation-timeline'] = "scroll(inline nearest)" should set the property value +Pass e.style['animation-timeline'] = "scroll(nearest inline)" should set the property value +Pass e.style['animation-timeline'] = "scroll(block self)" should set the property value +Pass e.style['animation-timeline'] = "scroll(self block)" should set the property value +Pass e.style['animation-timeline'] = "scroll(y root)" should set the property value Pass e.style['animation-timeline'] = "scroll(abc root)" should not set the property value Pass e.style['animation-timeline'] = "scroll(abc)" should not set the property value Pass e.style['animation-timeline'] = "scroll(y abc)" should not set the property value