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 18, 2024
1 parent 999c591 commit 6c793e2
Show file tree
Hide file tree
Showing 15 changed files with 267 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
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ padding-top: 0px
position: static
r: 0px
right: auto
rotate: none
row-gap: auto
rx: auto
ry: auto
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 @@ -140,6 +140,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
3 changes: 3 additions & 0 deletions Userland/Libraries/LibWeb/CSS/ComputedValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ class ComputedValues {
Vector<CSS::Transformation> const& transformations() const { return m_noninherited.transformations; }
CSS::TransformBox const& transform_box() const { return m_noninherited.transform_box; }
CSS::TransformOrigin const& transform_origin() const { return m_noninherited.transform_origin; }
Optional<CSS::Transformation> const& rotate() const { return m_noninherited.rotate; }

Gfx::FontCascadeList const& font_list() const { return *m_inherited.font_list; }
CSSPixels font_size() const { return m_inherited.font_size; }
Expand Down Expand Up @@ -653,6 +654,7 @@ class ComputedValues {
CSS::ObjectFit object_fit { InitialValues::object_fit() };
CSS::ObjectPosition object_position { InitialValues::object_position() };
CSS::UnicodeBidi unicode_bidi { InitialValues::unicode_bidi() };
Optional<CSS::Transformation> rotate;

Optional<MaskReference> mask;
CSS::MaskType mask_type { InitialValues::mask_type() };
Expand Down Expand Up @@ -760,6 +762,7 @@ class MutableComputedValues final : public ComputedValues {
void set_justify_items(CSS::JustifyItems value) { m_noninherited.justify_items = value; }
void set_justify_self(CSS::JustifySelf value) { m_noninherited.justify_self = value; }
void set_box_shadow(Vector<ShadowData>&& value) { m_noninherited.box_shadow = move(value); }
void set_rotate(CSS::Transformation value) { m_noninherited.rotate = value; }
void set_transformations(Vector<CSS::Transformation> value) { m_noninherited.transformations = move(value); }
void set_transform_box(CSS::TransformBox value) { m_noninherited.transform_box = value; }
void set_transform_origin(CSS::TransformOrigin value) { m_noninherited.transform_origin = value; }
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 @@ -75,6 +75,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 @@ -4800,6 +4801,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 @@ -7954,6 +8042,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 @@ -329,6 +329,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
39 changes: 39 additions & 0 deletions Userland/Libraries/LibWeb/CSS/StyleProperties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
#include <LibWeb/CSS/StyleValues/PositionStyleValue.h>
#include <LibWeb/CSS/StyleValues/RectStyleValue.h>
#include <LibWeb/CSS/StyleValues/RotationStyleValue.h>
#include <LibWeb/CSS/StyleValues/ScrollbarGutterStyleValue.h>
#include <LibWeb/CSS/StyleValues/ShadowStyleValue.h>
#include <LibWeb/CSS/StyleValues/StringStyleValue.h>
Expand Down Expand Up @@ -528,6 +529,44 @@ Vector<CSS::Transformation> StyleProperties::transformations() const
return transformations_for_style_value(property(CSS::PropertyID::Transform));
}

Optional<CSS::Transformation> StyleProperties::rotate() const
{
auto value = property(CSS::PropertyID::Rotate);
if (!value->is_rotation())
return {};

auto& rotation = value->as_rotation();

auto resolve_angle = [&](CSSStyleValue const& value) -> Optional<Angle> {
if (value.is_angle())
return value.as_angle().angle();
if (value.is_math() && value.as_math().resolves_to_angle())
return value.as_math().resolve_angle().value();
return {};
};

auto resolve_number = [&](CSSStyleValue const& value) -> Optional<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();
return {};
};

auto x = resolve_number(rotation.rotation_x()).value_or(0);
auto y = resolve_number(rotation.rotation_y()).value_or(0);
auto z = resolve_number(rotation.rotation_z()).value_or(0);
auto angle = resolve_angle(rotation.angle()).value_or(Angle::make_degrees(0));

Vector<TransformValue> values;
values.append({ Number(Number::Type::Number, x) });
values.append({ Number(Number::Type::Number, y) });
values.append({ Number(Number::Type::Number, z) });
values.append({ angle });

return CSS::Transformation(CSS::TransformFunction::Rotate3d, move(values));
}

static Optional<LengthPercentage> length_percentage_for_style_value(CSSStyleValue const& value)
{
if (value.is_length())
Expand Down
1 change: 1 addition & 0 deletions Userland/Libraries/LibWeb/CSS/StyleProperties.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ class StyleProperties : public RefCounted<StyleProperties> {
Vector<CSS::Transformation> transformations() const;
Optional<CSS::TransformBox> transform_box() const;
CSS::TransformOrigin transform_origin() const;
Optional<CSS::Transformation> rotate() const;

Optional<CSS::MaskType> mask_type() const;
Color stop_color() const;
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) -> Optional<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();

VERIFY_NOT_REACHED();
};

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

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

if (x_value == 0 && y_value > 0.0 && z_value == 0)
return MUST(String::formatted("y {}", m_properties.angle->to_string()));

// 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 > 0.0)
return 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("{} {} {} {}", m_properties.rotation_x->to_string(), m_properties.rotation_y->to_string(), m_properties.rotation_z->to_string(), 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 @@ -194,6 +194,7 @@ class RectStyleValue;
class Resolution;
class ResolutionOrCalculated;
class ResolutionStyleValue;
class RotationStyleValue;
class Screen;
class ScreenOrientation;
class ScrollbarGutterStyleValue;
Expand Down
Loading

0 comments on commit 6c793e2

Please sign in to comment.