diff --git a/Tests/LibWeb/Layout/expected/place-content-shorthand-property.txt b/Tests/LibWeb/Layout/expected/place-content-shorthand-property.txt
new file mode 100644
index 0000000000..6be17aa7bd
--- /dev/null
+++ b/Tests/LibWeb/Layout/expected/place-content-shorthand-property.txt
@@ -0,0 +1,9 @@
+Viewport <#document> at (0,0) content-size 800x600 children: not-inline
+ BlockContainer at (1,1) content-size 798x37.46875 [BFC] children: not-inline
+ BlockContainer
at (10,10) content-size 780x19.46875 children: not-inline
+ Box at (11,11) content-size 800x17.46875 flex-container(row) [FFC] children: not-inline
+ BlockContainer <(anonymous)> at (392.210937,11) content-size 37.578125x17.46875 flex-item [BFC] children: inline
+ line 0 width: 37.578125, height: 17.46875, bottom: 17.46875, baseline: 13.53125
+ frag 0 from TextNode start: 0, length: 4, rect: [392.210937,11 37.578125x17.46875]
+ "Text"
+ TextNode <#text>
diff --git a/Tests/LibWeb/Layout/input/place-content-shorthand-property.html b/Tests/LibWeb/Layout/input/place-content-shorthand-property.html
new file mode 100644
index 0000000000..e50d4bdb82
--- /dev/null
+++ b/Tests/LibWeb/Layout/input/place-content-shorthand-property.html
@@ -0,0 +1,9 @@
+Text
\ No newline at end of file
diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt
index 41ff064808..e5a66521b2 100644
--- a/Userland/Libraries/LibWeb/CMakeLists.txt
+++ b/Userland/Libraries/LibWeb/CMakeLists.txt
@@ -101,6 +101,7 @@ set(SOURCES
CSS/StyleValues/ListStyleStyleValue.cpp
CSS/StyleValues/NumericStyleValue.cpp
CSS/StyleValues/OverflowStyleValue.cpp
+ CSS/StyleValues/PlaceContentStyleValue.cpp
CSS/StyleValues/PositionStyleValue.cpp
CSS/StyleValues/RadialGradientStyleValue.cpp
CSS/StyleValues/RectStyleValue.cpp
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
index 902babeda0..9a43ea5d34 100644
--- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
+++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
@@ -68,6 +68,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -6105,6 +6106,28 @@ ErrorOr> Parser::parse_overflow_value(Vector
return nullptr;
}
+ErrorOr> Parser::parse_place_content_value(Vector const& component_values)
+{
+ if (component_values.size() > 2)
+ return nullptr;
+
+ auto tokens = TokenStream { component_values };
+ auto maybe_align_content_value = TRY(parse_css_value_for_property(PropertyID::AlignContent, tokens));
+ if (!maybe_align_content_value)
+ return nullptr;
+
+ if (component_values.size() == 1) {
+ if (!property_accepts_identifier(PropertyID::JustifyContent, maybe_align_content_value->to_identifier()))
+ return nullptr;
+ return PlaceContentStyleValue::create(*maybe_align_content_value, *maybe_align_content_value);
+ }
+
+ auto maybe_justify_content_value = TRY(parse_css_value_for_property(PropertyID::JustifyContent, tokens));
+ if (!maybe_justify_content_value)
+ return nullptr;
+ return PlaceContentStyleValue::create(maybe_align_content_value.release_nonnull(), maybe_justify_content_value.release_nonnull());
+}
+
ErrorOr> Parser::parse_text_decoration_value(Vector const& component_values)
{
RefPtr decoration_line;
@@ -7227,6 +7250,10 @@ Parser::ParseErrorOr> Parser::parse_css_value(Property
if (auto parsed_value = FIXME_TRY(parse_overflow_value(component_values)))
return parsed_value.release_nonnull();
return ParseError::SyntaxError;
+ case PropertyID::PlaceContent:
+ if (auto parsed_value = FIXME_TRY(parse_place_content_value(component_values)))
+ return parsed_value.release_nonnull();
+ return ParseError::SyntaxError;
case PropertyID::TextDecoration:
if (auto parsed_value = FIXME_TRY(parse_text_decoration_value(component_values)))
return parsed_value.release_nonnull();
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h
index 6943ab0e0a..51cf8cd7a0 100644
--- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h
+++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h
@@ -317,6 +317,7 @@ private:
ErrorOr> parse_font_family_value(TokenStream&);
ErrorOr> parse_list_style_value(Vector const&);
ErrorOr> parse_overflow_value(Vector const&);
+ ErrorOr> parse_place_content_value(Vector const&);
enum class AllowInsetKeyword {
No,
Yes,
diff --git a/Userland/Libraries/LibWeb/CSS/Properties.json b/Userland/Libraries/LibWeb/CSS/Properties.json
index effb79a88e..03a5558263 100644
--- a/Userland/Libraries/LibWeb/CSS/Properties.json
+++ b/Userland/Libraries/LibWeb/CSS/Properties.json
@@ -1543,6 +1543,14 @@
"unitless-length"
]
},
+ "place-content": {
+ "inherited": false,
+ "initial": "normal",
+ "longhands": [
+ "align-content",
+ "justify-content"
+ ]
+ },
"pointer-events": {
"affects-layout": false,
"inherited": true,
diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp
index 0fc12fb9dd..6e7372af67 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp
+++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp
@@ -50,6 +50,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -343,6 +344,19 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
return;
}
+ if (property_id == CSS::PropertyID::PlaceContent) {
+ if (value.is_place_content()) {
+ auto const& place_content = value.as_place_content();
+ style.set_property(CSS::PropertyID::AlignContent, place_content.align_content());
+ style.set_property(CSS::PropertyID::JustifyContent, place_content.justify_content());
+ return;
+ }
+
+ style.set_property(CSS::PropertyID::AlignContent, value);
+ style.set_property(CSS::PropertyID::JustifyContent, value);
+ return;
+ }
+
if (property_id == CSS::PropertyID::Border) {
set_property_expanding_shorthands(style, CSS::PropertyID::BorderTop, value, document, declaration);
set_property_expanding_shorthands(style, CSS::PropertyID::BorderRight, value, document, declaration);
diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp
index 6a1bd6b180..8aaa8a357b 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp
+++ b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp
@@ -46,6 +46,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -295,6 +296,12 @@ PercentageStyleValue const& StyleValue::as_percentage() const
return static_cast(*this);
}
+PlaceContentStyleValue const& StyleValue::as_place_content() const
+{
+ VERIFY(is_place_content());
+ return static_cast(*this);
+}
+
PositionStyleValue const& StyleValue::as_position() const
{
VERIFY(is_position());
diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h
index 9fe07caa69..20465076ba 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleValue.h
+++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h
@@ -123,6 +123,7 @@ public:
Numeric,
Overflow,
Percentage,
+ PlaceContent,
Position,
RadialGradient,
Rect,
@@ -177,6 +178,7 @@ public:
bool is_numeric() const { return type() == Type::Numeric; }
bool is_overflow() const { return type() == Type::Overflow; }
bool is_percentage() const { return type() == Type::Percentage; }
+ bool is_place_content() const { return type() == Type::PlaceContent; }
bool is_position() const { return type() == Type::Position; }
bool is_radial_gradient() const { return type() == Type::RadialGradient; }
bool is_rect() const { return type() == Type::Rect; }
@@ -230,6 +232,7 @@ public:
NumericStyleValue const& as_numeric() const;
OverflowStyleValue const& as_overflow() const;
PercentageStyleValue const& as_percentage() const;
+ PlaceContentStyleValue const& as_place_content() const;
PositionStyleValue const& as_position() const;
RadialGradientStyleValue const& as_radial_gradient() const;
RectStyleValue const& as_rect() const;
@@ -280,6 +283,7 @@ public:
NumericStyleValue& as_numeric() { return const_cast(const_cast(*this).as_numeric()); }
OverflowStyleValue& as_overflow() { return const_cast(const_cast(*this).as_overflow()); }
PercentageStyleValue& as_percentage() { return const_cast(const_cast(*this).as_percentage()); }
+ PlaceContentStyleValue& as_place_content() { return const_cast(const_cast(*this).as_place_content()); }
PositionStyleValue& as_position() { return const_cast(const_cast(*this).as_position()); }
RadialGradientStyleValue& as_radial_gradient() { return const_cast(const_cast(*this).as_radial_gradient()); }
RectStyleValue& as_rect() { return const_cast(const_cast(*this).as_rect()); }
diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/PlaceContentStyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValues/PlaceContentStyleValue.cpp
new file mode 100644
index 0000000000..32d19ce176
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleValues/PlaceContentStyleValue.cpp
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2023, Hunter Salyer
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "PlaceContentStyleValue.h"
+
+namespace Web::CSS {
+
+ErrorOr PlaceContentStyleValue::to_string() const
+{
+ return String::formatted("{} {}", TRY(m_properties.align_content->to_string()), TRY(m_properties.justify_content->to_string()));
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/PlaceContentStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/PlaceContentStyleValue.h
new file mode 100644
index 0000000000..67fc27e636
--- /dev/null
+++ b/Userland/Libraries/LibWeb/CSS/StyleValues/PlaceContentStyleValue.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2023, Hunter Salyer
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include
+
+namespace Web::CSS {
+
+class PlaceContentStyleValue final : public StyleValueWithDefaultOperators {
+public:
+ static ErrorOr> create(ValueComparingNonnullRefPtr align_content, ValueComparingNonnullRefPtr justify_content)
+ {
+ return adopt_nonnull_ref_or_enomem(new (nothrow) PlaceContentStyleValue(move(align_content), move(justify_content)));
+ }
+ virtual ~PlaceContentStyleValue() override = default;
+
+ ValueComparingNonnullRefPtr align_content() const { return m_properties.align_content; }
+ ValueComparingNonnullRefPtr justify_content() const { return m_properties.justify_content; }
+
+ virtual ErrorOr to_string() const override;
+
+ bool properties_equal(PlaceContentStyleValue const& other) const { return m_properties == other.m_properties; };
+
+private:
+ PlaceContentStyleValue(ValueComparingNonnullRefPtr align_content, ValueComparingNonnullRefPtr justify_content)
+ : StyleValueWithDefaultOperators(Type::PlaceContent)
+ , m_properties { .align_content = move(align_content), .justify_content = move(justify_content) }
+ {
+ }
+
+ struct Properties {
+ ValueComparingNonnullRefPtr align_content;
+ ValueComparingNonnullRefPtr justify_content;
+ bool operator==(Properties const&) const = default;
+ } m_properties;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h
index d47b0ab8b7..1e0650c113 100644
--- a/Userland/Libraries/LibWeb/Forward.h
+++ b/Userland/Libraries/LibWeb/Forward.h
@@ -135,6 +135,7 @@ class OverflowStyleValue;
class Percentage;
class PercentageOrCalculated;
class PercentageStyleValue;
+class PlaceContentStyleValue;
class PositionStyleValue;
class PropertyOwningCSSStyleDeclaration;
class RadialGradientStyleValue;