diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/CSS/StyleValues/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibWeb/CSS/StyleValues/BUILD.gn index 7c27a995daf1..3196053d44aa 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWeb/CSS/StyleValues/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/CSS/StyleValues/BUILD.gn @@ -37,6 +37,7 @@ source_set("StyleValues") { "PositionStyleValue.cpp", "RadialGradientStyleValue.cpp", "RectStyleValue.cpp", + "RotationStyleValue.cpp", "ShadowStyleValue.cpp", "ShorthandStyleValue.cpp", "StyleValueList.cpp", diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 39febaba6a26..7ff78167983f 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -139,6 +139,7 @@ set(SOURCES CSS/StyleValues/PositionStyleValue.cpp CSS/StyleValues/RadialGradientStyleValue.cpp CSS/StyleValues/RectStyleValue.cpp + CSS/StyleValues/RotationStyleValue.cpp CSS/StyleValues/ShadowStyleValue.cpp CSS/StyleValues/ShorthandStyleValue.cpp CSS/StyleValues/StyleValueList.cpp diff --git a/Userland/Libraries/LibWeb/CSS/CSSStyleValue.cpp b/Userland/Libraries/LibWeb/CSS/CSSStyleValue.cpp index 792170111dd6..78b5b0a220dc 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSStyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/CSSStyleValue.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -287,6 +288,12 @@ ResolutionStyleValue const& CSSStyleValue::as_resolution() const return static_cast(*this); } +RotationStyleValue const& CSSStyleValue::as_rotation() const +{ + VERIFY(is_rotation()); + return static_cast(*this); +} + ScrollbarGutterStyleValue const& CSSStyleValue::as_scrollbar_gutter() const { VERIFY(is_scrollbar_gutter()); diff --git a/Userland/Libraries/LibWeb/CSS/CSSStyleValue.h b/Userland/Libraries/LibWeb/CSS/CSSStyleValue.h index 269cb8d2ebf6..312b5d6a8bca 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSStyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/CSSStyleValue.h @@ -124,6 +124,7 @@ class CSSStyleValue : public RefCounted { Ratio, Rect, Resolution, + Rotation, ScrollbarGutter, Shadow, Shorthand, @@ -289,6 +290,10 @@ class CSSStyleValue : public RefCounted { ResolutionStyleValue const& as_resolution() const; ResolutionStyleValue& as_resolution() { return const_cast(const_cast(*this).as_resolution()); } + bool is_rotation() const { return type() == Type::Rotation; } + RotationStyleValue const& as_rotation() const; + RotationStyleValue& as_rotation() { return const_cast(const_cast(*this).as_rotation()); } + bool is_scrollbar_gutter() const { return type() == Type::ScrollbarGutter; } ScrollbarGutterStyleValue const& as_scrollbar_gutter() const; ScrollbarGutterStyleValue& as_scrollbar_gutter() { return const_cast(const_cast(*this).as_scrollbar_gutter()); } diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index f36cb01f34ab..805851f73a08 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -74,6 +74,7 @@ #include #include #include +#include #include #include #include @@ -4725,6 +4726,83 @@ RefPtr Parser::parse_single_shadow_value(TokenStream Parser::parse_rotate_value(TokenStream& tokens) +{ + // Value: none | | [ x | y | z | {3} ] && + + // "none" + if (tokens.remaining_token_count() == 1) { + auto transaction = tokens.begin_transaction(); + if (auto keyword = parse_keyword_value(tokens)) { + if (keyword->to_keyword() == Keyword::None) { + transaction.commit(); + return keyword; + } + } + } + + // + if (auto angle = parse_angle_value(tokens)) { + // FIXME: Handle calc() + if (angle->is_angle()) + return RotationStyleValue::create(angle->as_angle().angle(), 0, 0, 1); + } + + // [ x | y | z ] && + if (tokens.remaining_token_count() == 2) { + auto transaction = tokens.begin_transaction(); + auto axis = tokens.consume_a_token(); + if (axis.is_ident("x"sv) || axis.is_ident("y"sv) || axis.is_ident("z"sv)) { + auto angle = parse_angle_value(tokens); + if (angle && angle->is_angle()) { + auto x = 0; + if (axis.is_ident("x"sv)) + x = 1; + + auto y = 0; + if (axis.is_ident("y"sv)) + y = 1; + + auto z = 0; + if (axis.is_ident("z"sv)) + z = 1; + + transaction.commit(); + return RotationStyleValue::create(angle->as_angle().angle(), x, y, z); + } + } + } + + // {3} && + if (tokens.remaining_token_count() == 4) { + auto transaction = tokens.begin_transaction(); + auto x = parse_number_value(tokens); + if (!x || !x->is_number()) + return nullptr; + + auto y = parse_number_value(tokens); + if (!y || !y->is_number()) + return nullptr; + + auto z = parse_number_value(tokens); + if (!z || !z->is_number()) + return nullptr; + + auto angle = parse_angle_value(tokens); + if (!angle) + return nullptr; + + // FIXME: Handle calc() + if (!angle->is_angle()) + return nullptr; + + transaction.commit(); + return RotationStyleValue::create(angle->as_angle().angle(), x->as_number().number(), y->as_number().number(), z->as_number().number()); + } + + return nullptr; +} + RefPtr Parser::parse_content_value(TokenStream& tokens) { // FIXME: `content` accepts several kinds of function() type, which we don't handle in property_accepts_value() yet. @@ -7879,6 +7957,10 @@ Parser::ParseErrorOr> Parser::parse_css_value(Prope if (auto parsed_value = parse_quotes_value(tokens); parsed_value && !tokens.has_next_token()) return parsed_value.release_nonnull(); return ParseError::SyntaxError; + case PropertyID::Rotate: + if (auto parsed_value = parse_rotate_value(tokens); parsed_value && !tokens.has_next_token()) + return parsed_value.release_nonnull(); + return ParseError::SyntaxError; case PropertyID::ScrollbarGutter: if (auto parsed_value = parse_scrollbar_gutter_value(tokens); parsed_value && !tokens.has_next_token()) return parsed_value.release_nonnull(); diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index 53dc680b51b6..c49c281a85a2 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -328,6 +328,7 @@ class Parser { RefPtr parse_single_shadow_value(TokenStream&, AllowInsetKeyword); RefPtr parse_text_decoration_value(TokenStream&); RefPtr parse_text_decoration_line_value(TokenStream&); + RefPtr parse_rotate_value(TokenStream&); RefPtr parse_easing_value(TokenStream&); RefPtr parse_transform_value(TokenStream&); RefPtr parse_transform_origin_value(TokenStream&); diff --git a/Userland/Libraries/LibWeb/CSS/Properties.json b/Userland/Libraries/LibWeb/CSS/Properties.json index 55d603ca0482..9c970b6064e6 100644 --- a/Userland/Libraries/LibWeb/CSS/Properties.json +++ b/Userland/Libraries/LibWeb/CSS/Properties.json @@ -2287,6 +2287,13 @@ "unitless-length" ] }, + "rotate": { + "animation-type": "custom", + "inherited": false, + "initial": "none", + "affects-layout": false, + "affects-stacking-context": true + }, "row-gap": { "animation-type": "by-computed-value", "inherited": false, diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/RotationStyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValues/RotationStyleValue.cpp new file mode 100644 index 000000000000..53239f9214d8 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/RotationStyleValue.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024, Steffen T. Larssen + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "RotationStyleValue.h" +#include + +namespace Web::CSS { + +RotationStyleValue::RotationStyleValue(Angle angle, float x, float y, float z) + : CSSUnitValue(Type::Rotation) + , m_angle(move(angle)) + , m_rotation_vector(x, y, z) +{ +} + +RotationStyleValue::~RotationStyleValue() = default; + +// https://www.w3.org/TR/2021/WD-css-transforms-2-20211109/#individual-transform-serialization +String RotationStyleValue::to_string() const +{ + // If a rotation about the z axis (that is, in 2D) is specified, the property must serialize as just an . + if (m_rotation_vector.x() == 0 && m_rotation_vector.y() == 0 && m_rotation_vector.z() == 1) + return m_angle.to_string(); + + // If any other rotation is specified, the property must serialize with an axis specified. + // If the axis is parallel with the x or y axes, it must serialize as the appropriate keyword. + if (m_rotation_vector.x() == 1 && m_rotation_vector.y() == 0 && m_rotation_vector.z() == 0) + return MUST(String::formatted("x {}", m_angle.to_string())); + + if (m_rotation_vector.x() == 0 && m_rotation_vector.y() == 1 && m_rotation_vector.z() == 0) + return MUST(String::formatted("y {}", m_angle.to_string())); + + if (m_rotation_vector.x() != 0 || m_rotation_vector.y() != 0 || m_rotation_vector.z() != 0) + return MUST(String::formatted("{} {} {} {}", m_rotation_vector.x(), m_rotation_vector.y(), m_rotation_vector.z(), m_angle.to_string())); + + // It must serialize as the keyword none if and only if none was originally specified. + // NOTE: This is handled by returning a keyword from the parser. + VERIFY_NOT_REACHED(); +} + +bool RotationStyleValue::equals(CSSStyleValue const& other) const +{ + if (type() != other.type()) + return false; + auto const& other_angle = other.as_rotation(); + return m_angle == other_angle.m_angle; +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/RotationStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/RotationStyleValue.h new file mode 100644 index 000000000000..035123323dfe --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/RotationStyleValue.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024, Steffen T. Larssen + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Web::CSS { + +class RotationStyleValue : public CSSUnitValue { +public: + static ValueComparingNonnullRefPtr create(Angle angle, float x, float y, float z) + { + return adopt_ref(*new (nothrow) RotationStyleValue(move(angle), x, y, z)); + } + virtual ~RotationStyleValue() override; + + Angle const& angle() const { return m_angle; } + FloatVector3 const& rotation_vector() const { return m_rotation_vector; } + + // FIXME: Return the correct things here... + virtual double value() const override { return m_angle.raw_value(); } + virtual StringView unit() const override { return m_angle.unit_name(); } + + virtual String to_string() const override; + + bool equals(CSSStyleValue const& other) const override; + +private: + explicit RotationStyleValue(Angle angle, float x, float y, float z); + + Angle m_angle; + FloatVector3 m_rotation_vector; +}; + +} diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 57d0235611e3..7d3186975147 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -192,6 +192,7 @@ class RectStyleValue; class Resolution; class ResolutionOrCalculated; class ResolutionStyleValue; +class RotationStyleValue; class Screen; class ScreenOrientation; class ScrollbarGutterStyleValue;