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 438f6a6
Show file tree
Hide file tree
Showing 10 changed files with 220 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
92 changes: 92 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,93 @@ 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 (auto none = parse_all_as_single_keyword_value(tokens, Keyword::None))
return none;

// <angle>
if (auto angle = parse_angle_value(tokens))
return RotationStyleValue::create(angle.release_nonnull(), NumberStyleValue::create(0), NumberStyleValue::create(0), NumberStyleValue::create(1));

auto parse_one_of_xyz = [&]() -> Optional<ComponentValue> {
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)) {
transaction.commit();
return axis;
}

return {};
};

// [ x | y | z ] && <angle>
if (tokens.remaining_token_count() == 2) {
// Try parsing `x <angle>`
if (auto axis = parse_one_of_xyz(); axis.has_value()) {
if (auto angle = parse_angle_value(tokens); angle) {
if (axis->is_ident("x"sv))
return RotationStyleValue::create(angle.release_nonnull(), NumberStyleValue::create(1), NumberStyleValue::create(0), NumberStyleValue::create(0));
if (axis->is_ident("y"sv))
return RotationStyleValue::create(angle.release_nonnull(), NumberStyleValue::create(0), NumberStyleValue::create(1), NumberStyleValue::create(0));
if (axis->is_ident("z"sv))
return RotationStyleValue::create(angle.release_nonnull(), NumberStyleValue::create(0), NumberStyleValue::create(0), NumberStyleValue::create(1));
}
}

// Try parsing `<angle> x`
if (auto angle = parse_angle_value(tokens); angle) {
if (auto axis = parse_one_of_xyz(); axis.has_value()) {
if (axis->is_ident("x"sv))
return RotationStyleValue::create(angle.release_nonnull(), NumberStyleValue::create(1), NumberStyleValue::create(0), NumberStyleValue::create(0));
if (axis->is_ident("y"sv))
return RotationStyleValue::create(angle.release_nonnull(), NumberStyleValue::create(0), NumberStyleValue::create(1), NumberStyleValue::create(0));
if (axis->is_ident("z"sv))
return RotationStyleValue::create(angle.release_nonnull(), NumberStyleValue::create(0), NumberStyleValue::create(0), NumberStyleValue::create(1));
}
}
}

auto parse_three_numbers = [&]() -> Optional<StyleValueVector> {
auto transaction = tokens.begin_transaction();
StyleValueVector numbers;
for (size_t i = 0; i < 3; ++i) {
if (auto number = parse_number_value(tokens); number) {
numbers.append(number.release_nonnull());
} else {
return {};
}
}
transaction.commit();
return numbers;
};

// <number>{3} && <angle>
if (tokens.remaining_token_count() == 4) {
// Try parsing <number>{3} <angle>
if (auto maybe_numbers = parse_three_numbers(); maybe_numbers.has_value()) {
if (auto angle = parse_angle_value(tokens); angle) {
auto numbers = maybe_numbers.release_value();
return RotationStyleValue::create(angle.release_nonnull(), numbers[0], numbers[1], numbers[2]);
}
}

// Try parsing <angle> <number>{3}
if (auto angle = parse_angle_value(tokens); angle) {
if (auto maybe_numbers = parse_three_numbers(); maybe_numbers.has_value()) {
auto numbers = maybe_numbers.release_value();
return RotationStyleValue::create(angle.release_nonnull(), numbers[0], numbers[1], numbers[2]);
}
}
}

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

#include <AK/String.h>
#include <LibWeb/CSS/StyleValues/CSSMathValue.h>
#include <LibWeb/CSS/StyleValues/NumberStyleValue.h>

#include "RotationStyleValue.h"

namespace Web::CSS {

// https://www.w3.org/TR/2021/WD-css-transforms-2-20211109/#individual-transform-serialization
String RotationStyleValue::to_string() const
{
auto resolve_to_number = [](ValueComparingNonnullRefPtr<CSSStyleValue const> const& value) -> double {
if (value->is_number())
return value->as_number().number();
if (value->is_math() && value->as_math().resolves_to_number())
return value->as_math().resolve_number().value();

VERIFY_NOT_REACHED();
};

auto x_value = resolve_to_number(m_properties.rotation_x);
auto y_value = resolve_to_number(m_properties.rotation_y);
auto z_value = resolve_to_number(m_properties.rotation_z);

// If a rotation about the z axis (that is, in 2D) is specified, the property must serialize as just an <angle>.
if (x_value == 0 && y_value == 0 && z_value == 1)
return m_properties.angle->to_string();

// If the axis is parallel with the x or y axes, it must serialize as the appropriate keyword.
if (x_value == 1 && y_value == 0 && z_value == 0)
return MUST(String::formatted("x {}", m_properties.angle->to_string()));

if (x_value == 0 && y_value == 1 && z_value == 0)
return MUST(String::formatted("y {}", m_properties.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.

// If any other rotation is specified, the property must serialize with an axis specified.
return MUST(String::formatted("{} {} {} {}", x_value, y_value, z_value, m_properties.angle->to_string()));
}

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

#pragma once

#include <LibWeb/CSS/CSSStyleValue.h>

namespace Web::CSS {

class RotationStyleValue : public StyleValueWithDefaultOperators<RotationStyleValue> {
public:
static ValueComparingNonnullRefPtr<RotationStyleValue> create(ValueComparingNonnullRefPtr<CSSStyleValue const> angle, ValueComparingNonnullRefPtr<CSSStyleValue const> rotation_x, ValueComparingNonnullRefPtr<CSSStyleValue const> rotation_y, ValueComparingNonnullRefPtr<CSSStyleValue const> rotation_z)
{
return adopt_ref(*new (nothrow) RotationStyleValue(move(angle), move(rotation_x), move(rotation_y), move(rotation_z)));
}

virtual ~RotationStyleValue() override = default;

ValueComparingNonnullRefPtr<CSSStyleValue const> const& angle() const { return m_properties.angle; }
ValueComparingNonnullRefPtr<CSSStyleValue const> const& rotation_x() const { return m_properties.rotation_x; }
ValueComparingNonnullRefPtr<CSSStyleValue const> const& rotation_y() const { return m_properties.rotation_y; }
ValueComparingNonnullRefPtr<CSSStyleValue const> const& rotation_z() const { return m_properties.rotation_z; }

virtual String to_string() const override;

bool properties_equal(RotationStyleValue const& other) const { return m_properties == other.m_properties; }

private:
explicit RotationStyleValue(
ValueComparingNonnullRefPtr<CSSStyleValue const> angle,
ValueComparingNonnullRefPtr<CSSStyleValue const> rotation_x,
ValueComparingNonnullRefPtr<CSSStyleValue const> rotation_y,
ValueComparingNonnullRefPtr<CSSStyleValue const> rotation_z)
: StyleValueWithDefaultOperators(Type::Rotation)
, m_properties {
.angle = move(angle),
.rotation_x = move(rotation_x),
.rotation_y = move(rotation_y),
.rotation_z = move(rotation_z)
}
{
}

struct Properties {
ValueComparingNonnullRefPtr<CSSStyleValue const> angle;
ValueComparingNonnullRefPtr<CSSStyleValue const> rotation_x;
ValueComparingNonnullRefPtr<CSSStyleValue const> rotation_y;
ValueComparingNonnullRefPtr<CSSStyleValue const> rotation_z;
bool operator==(Properties const&) const = default;
} m_properties;
};

}
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 438f6a6

Please sign in to comment.