Skip to content

Commit

Permalink
LibWeb: Parse the rotate css property
Browse files Browse the repository at this point in the history
  • Loading branch information
stelar7 committed Oct 16, 2024
1 parent 81596b4 commit 170600b
Show file tree
Hide file tree
Showing 10 changed files with 198 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ source_set("StyleValues") {
"PositionStyleValue.cpp",
"RadialGradientStyleValue.cpp",
"RectStyleValue.cpp",
"RotationStyleValue.cpp",
"ShadowStyleValue.cpp",
"ShorthandStyleValue.cpp",
"StyleValueList.cpp",
Expand Down
1 change: 1 addition & 0 deletions Userland/Libraries/LibWeb/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions Userland/Libraries/LibWeb/CSS/CSSStyleValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include <LibWeb/CSS/StyleValues/RatioStyleValue.h>
#include <LibWeb/CSS/StyleValues/RectStyleValue.h>
#include <LibWeb/CSS/StyleValues/ResolutionStyleValue.h>
#include <LibWeb/CSS/StyleValues/RotationStyleValue.h>
#include <LibWeb/CSS/StyleValues/ScrollbarGutterStyleValue.h>
#include <LibWeb/CSS/StyleValues/ShadowStyleValue.h>
#include <LibWeb/CSS/StyleValues/ShorthandStyleValue.h>
Expand Down Expand Up @@ -287,6 +288,12 @@ ResolutionStyleValue const& CSSStyleValue::as_resolution() const
return static_cast<ResolutionStyleValue const&>(*this);
}

RotationStyleValue const& CSSStyleValue::as_rotation() const
{
VERIFY(is_rotation());
return static_cast<RotationStyleValue const&>(*this);
}

ScrollbarGutterStyleValue const& CSSStyleValue::as_scrollbar_gutter() const
{
VERIFY(is_scrollbar_gutter());
Expand Down
5 changes: 5 additions & 0 deletions Userland/Libraries/LibWeb/CSS/CSSStyleValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class CSSStyleValue : public RefCounted<CSSStyleValue> {
Ratio,
Rect,
Resolution,
Rotation,
ScrollbarGutter,
Shadow,
Shorthand,
Expand Down Expand Up @@ -289,6 +290,10 @@ class CSSStyleValue : public RefCounted<CSSStyleValue> {
ResolutionStyleValue const& as_resolution() const;
ResolutionStyleValue& as_resolution() { return const_cast<ResolutionStyleValue&>(const_cast<CSSStyleValue const&>(*this).as_resolution()); }

bool is_rotation() const { return type() == Type::Rotation; }
RotationStyleValue const& as_rotation() const;
RotationStyleValue& as_rotation() { return const_cast<RotationStyleValue&>(const_cast<CSSStyleValue const&>(*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<ScrollbarGutterStyleValue&>(const_cast<CSSStyleValue const&>(*this).as_scrollbar_gutter()); }
Expand Down
82 changes: 82 additions & 0 deletions Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
#include <LibWeb/CSS/StyleValues/RatioStyleValue.h>
#include <LibWeb/CSS/StyleValues/RectStyleValue.h>
#include <LibWeb/CSS/StyleValues/ResolutionStyleValue.h>
#include <LibWeb/CSS/StyleValues/RotationStyleValue.h>
#include <LibWeb/CSS/StyleValues/ScrollbarGutterStyleValue.h>
#include <LibWeb/CSS/StyleValues/ShadowStyleValue.h>
#include <LibWeb/CSS/StyleValues/ShorthandStyleValue.h>
Expand Down Expand Up @@ -4725,6 +4726,83 @@ RefPtr<CSSStyleValue> Parser::parse_single_shadow_value(TokenStream<ComponentVal
return ShadowStyleValue::create(color.release_nonnull(), offset_x.release_nonnull(), offset_y.release_nonnull(), blur_radius.release_nonnull(), spread_distance.release_nonnull(), placement.release_value());
}

RefPtr<CSSStyleValue> Parser::parse_rotate_value(TokenStream<ComponentValue>& tokens)
{
// Value: none | <angle> | [ x | y | z | <number>{3} ] && <angle>

// "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;
}
}
}

// <angle>
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 ] && <angle>
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);
}
}
}

// <number>{3} && <angle>
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<CSSStyleValue> Parser::parse_content_value(TokenStream<ComponentValue>& tokens)
{
// FIXME: `content` accepts several kinds of function() type, which we don't handle in property_accepts_value() yet.
Expand Down Expand Up @@ -7879,6 +7957,10 @@ Parser::ParseErrorOr<NonnullRefPtr<CSSStyleValue>> 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();
Expand Down
1 change: 1 addition & 0 deletions Userland/Libraries/LibWeb/CSS/Parser/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ class Parser {
RefPtr<CSSStyleValue> parse_single_shadow_value(TokenStream<ComponentValue>&, AllowInsetKeyword);
RefPtr<CSSStyleValue> parse_text_decoration_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_text_decoration_line_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_rotate_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_easing_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_transform_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue> parse_transform_origin_value(TokenStream<ComponentValue>&);
Expand Down
7 changes: 7 additions & 0 deletions Userland/Libraries/LibWeb/CSS/Properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
52 changes: 52 additions & 0 deletions Userland/Libraries/LibWeb/CSS/StyleValues/RotationStyleValue.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2024, Steffen T. Larssen <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include "RotationStyleValue.h"
#include <AK/String.h>

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 <angle>.
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;
}

}
41 changes: 41 additions & 0 deletions Userland/Libraries/LibWeb/CSS/StyleValues/RotationStyleValue.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2024, Steffen T. Larssen <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once

#include <LibGfx/Vector3.h>
#include <LibWeb/CSS/Angle.h>
#include <LibWeb/CSS/StyleValues/CSSUnitValue.h>

namespace Web::CSS {

class RotationStyleValue : public CSSUnitValue {
public:
static ValueComparingNonnullRefPtr<RotationStyleValue> 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;
};

}
1 change: 1 addition & 0 deletions Userland/Libraries/LibWeb/Forward.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ class RectStyleValue;
class Resolution;
class ResolutionOrCalculated;
class ResolutionStyleValue;
class RotationStyleValue;
class Screen;
class ScreenOrientation;
class ScrollbarGutterStyleValue;
Expand Down

0 comments on commit 170600b

Please sign in to comment.