diff --git a/Base/res/html/misc/custom-properties.html b/Base/res/html/misc/custom-properties.html
index 87c341e85c..8ff9584355 100644
--- a/Base/res/html/misc/custom-properties.html
+++ b/Base/res/html/misc/custom-properties.html
@@ -13,7 +13,7 @@
}
.test {
- background: var(--my-color);
+ background-color: var(--my-color);
}
.test-parent {
@@ -37,7 +37,7 @@
}
.test {
- background: var(--my-color);
+ background-color: var(--my-color);
}
.test-parent {
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
index 0553a43f69..b222f02453 100644
--- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
+++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
@@ -1746,6 +1746,108 @@ RefPtr Parser::parse_image_value(ParsingContext const& context, Styl
return {};
}
+RefPtr Parser::parse_background_value(ParsingContext const& context, Vector const& component_values)
+{
+ auto is_background_repeat = [](StyleValue const& value) -> bool {
+ switch (value.to_identifier()) {
+ case CSS::ValueID::NoRepeat:
+ case CSS::ValueID::Repeat:
+ case CSS::ValueID::RepeatX:
+ case CSS::ValueID::RepeatY:
+ case CSS::ValueID::Round:
+ case CSS::ValueID::Space:
+ return true;
+ default:
+ return false;
+ }
+ };
+
+ auto is_background_image = [](StyleValue const& value) -> bool {
+ if (value.is_image())
+ return true;
+ if (value.to_identifier() == ValueID::None)
+ return true;
+ return false;
+ };
+
+ RefPtr background_color;
+ RefPtr background_image;
+ RefPtr repeat_x;
+ RefPtr repeat_y;
+ // FIXME: Implement background-position.
+ // FIXME: Implement background-size.
+ // FIXME: Implement background-attachment.
+ // FIXME: Implement background-clip.
+ // FIXME: Implement background-origin.
+
+ for (size_t i = 0; i < component_values.size(); ++i) {
+ auto& part = component_values[i];
+
+ // FIXME: Handle multiple backgrounds, by returning a List of BackgroundStyleValues.
+ if (part.is(Token::Type::Comma)) {
+ dbgln("CSS Parser does not yet support multiple comma-separated values for background.");
+ break;
+ }
+
+ auto value = parse_css_value(context, PropertyID::Background, part);
+ if (!value) {
+ return nullptr;
+ }
+
+ if (value->is_color()) {
+ if (background_color)
+ return nullptr;
+ background_color = value.release_nonnull();
+ continue;
+ }
+ if (is_background_image(*value)) {
+ if (background_image)
+ return nullptr;
+ background_image = value.release_nonnull();
+ continue;
+ }
+ if (is_background_repeat(*value)) {
+ if (repeat_x)
+ return nullptr;
+
+ auto value_id = value->to_identifier();
+ if (value_id == ValueID::RepeatX || value_id == ValueID::RepeatY) {
+ repeat_x = IdentifierStyleValue::create(value_id == ValueID::RepeatX ? ValueID::Repeat : ValueID::NoRepeat);
+ repeat_y = IdentifierStyleValue::create(value_id == ValueID::RepeatX ? ValueID::NoRepeat : ValueID::Repeat);
+ continue;
+ }
+
+ // Check following value, if it's also a repeat, set both.
+ if (i + 1 < component_values.size()) {
+ auto next_value = parse_css_value(context, PropertyID::Background, component_values[i + 1]);
+ if (next_value && is_background_repeat(*next_value)) {
+ ++i;
+ repeat_x = value.release_nonnull();
+ repeat_y = next_value.release_nonnull();
+ continue;
+ }
+ }
+ auto repeat = value.release_nonnull();
+ repeat_x = repeat;
+ repeat_y = repeat;
+ continue;
+ }
+
+ return nullptr;
+ }
+
+ if (!background_color)
+ background_color = ColorStyleValue::create(Color::Transparent);
+ if (!background_image)
+ background_image = IdentifierStyleValue::create(ValueID::None);
+ if (!repeat_x)
+ repeat_x = IdentifierStyleValue::create(ValueID::Repeat);
+ if (!repeat_y)
+ repeat_y = IdentifierStyleValue::create(ValueID::Repeat);
+
+ return BackgroundStyleValue::create(background_color.release_nonnull(), background_image.release_nonnull(), repeat_x.release_nonnull(), repeat_y.release_nonnull());
+}
+
RefPtr Parser::parse_box_shadow_value(ParsingContext const& context, Vector const& component_values)
{
// FIXME: Also support inset, spread-radius and multiple comma-seperated box-shadows
@@ -1991,6 +2093,10 @@ RefPtr Parser::parse_css_value(PropertyID property_id, TokenStream parse_color_value(ParsingContext const&, StyleComponentValueRule const&);
static RefPtr parse_string_value(ParsingContext const&, StyleComponentValueRule const&);
static RefPtr parse_image_value(ParsingContext const&, StyleComponentValueRule const&);
+ static RefPtr parse_background_value(ParsingContext const&, Vector const&);
static RefPtr parse_box_shadow_value(ParsingContext const&, Vector const&);
static RefPtr parse_font_value(ParsingContext const&, Vector const&);
diff --git a/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp b/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp
index f86dd6d7a2..89ae825ddb 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp
+++ b/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp
@@ -208,35 +208,6 @@ static inline void set_property_border_style(StyleProperties& style, StyleValue
style.set_property(CSS::PropertyID::BorderLeftStyle, value);
}
-static inline bool is_background_repeat(StyleValue const& value)
-{
- if (value.is_builtin_or_dynamic())
- return true;
-
- switch (value.to_identifier()) {
- case CSS::ValueID::NoRepeat:
- case CSS::ValueID::Repeat:
- case CSS::ValueID::RepeatX:
- case CSS::ValueID::RepeatY:
- case CSS::ValueID::Round:
- case CSS::ValueID::Space:
- return true;
- default:
- return false;
- }
-}
-
-static inline bool is_background_image(StyleValue const& value)
-{
- if (value.is_builtin_or_dynamic())
- return true;
- if (value.is_image())
- return true;
- if (value.to_identifier() == ValueID::None)
- return true;
- return false;
-}
-
static inline bool is_color(StyleValue const& value)
{
if (value.is_builtin_or_dynamic())
@@ -748,98 +719,33 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
}
if (property_id == CSS::PropertyID::Background) {
- if (value.to_identifier() == ValueID::None) {
- style.set_property(CSS::PropertyID::BackgroundColor, ColorStyleValue::create(Color::Transparent));
+ auto set_single_background = [&](CSS::BackgroundStyleValue const& background) {
+ set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundColor, background.color(), document);
+ set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundImage, background.image(), document);
+ set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatX, background.repeat_x(), document, true);
+ set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatY, background.repeat_y(), document, true);
+ };
+
+ if (value.is_background()) {
+ auto& background = static_cast(value);
+ set_single_background(background);
return;
}
-
- if (is_background_image(value)) {
- set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundImage, value, document);
- return;
- }
-
- if (is_color(value)) {
- set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundColor, value, document);
- return;
- }
-
- if (value.is_component_value_list()) {
- auto parts = static_cast(value).values();
-
- RefPtr background_color_value;
- RefPtr background_image_value;
- RefPtr repeat_x_value;
- RefPtr repeat_y_value;
- // FIXME: Implement background-position.
- // FIXME: Implement background-size.
- // FIXME: Implement background-attachment.
- // FIXME: Implement background-clip.
- // FIXME: Implement background-origin.
-
- for (size_t i = 0; i < parts.size(); ++i) {
- auto& part = parts[i];
-
- // FIXME: Handle multiple backgrounds.
- if (part.is(Token::Type::Comma))
- break;
-
- auto value = Parser::parse_css_value(context, property_id, part);
- if (!value) {
- dbgln("Unable to parse token in `background` as a CSS value: '{}'", part.to_debug_string());
- return;
- }
-
- if (value->is_color()) {
- if (background_color_value)
- return;
- background_color_value = move(value);
- continue;
- }
- if (is_background_image(*value)) {
- if (background_image_value)
- return;
- background_image_value = move(value);
- continue;
- }
- if (is_background_repeat(*value)) {
- if (repeat_x_value)
- return;
-
- auto value_id = value->to_identifier();
- if (value_id == ValueID::RepeatX || value_id == ValueID::RepeatY) {
- repeat_x_value = IdentifierStyleValue::create(value_id == ValueID::RepeatX ? ValueID::Repeat : ValueID::NoRepeat);
- repeat_y_value = IdentifierStyleValue::create(value_id == ValueID::RepeatX ? ValueID::NoRepeat : ValueID::Repeat);
- continue;
- }
-
- // Check following value, if it's also a repeat, set both.
- if (i + 1 < parts.size()) {
- auto next_value = Parser::parse_css_value(context, property_id, parts[i + 1]);
- if (next_value && is_background_repeat(*next_value)) {
- ++i;
- repeat_x_value = move(value);
- repeat_y_value = move(next_value);
- continue;
- }
- }
- repeat_x_value = value;
- repeat_y_value = value;
- continue;
- }
-
- dbgln("Unhandled token in `background` declaration: '{}'", part.to_debug_string());
- return;
+ if (value.is_value_list()) {
+ auto& background_list = static_cast(value).values();
+ // FIXME: Handle multiple backgrounds.
+ if (!background_list.is_empty()) {
+ auto& background = background_list.first();
+ if (background.is_background())
+ set_single_background(static_cast(background));
}
-
- if (background_color_value)
- set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundColor, background_color_value.release_nonnull(), document);
- if (background_image_value)
- set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundImage, background_image_value.release_nonnull(), document);
- if (repeat_x_value)
- set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatX, repeat_x_value.release_nonnull(), document, true);
- if (repeat_y_value)
- set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatY, repeat_y_value.release_nonnull(), document, true);
-
+ return;
+ }
+ if (value.is_builtin()) {
+ set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundColor, value, document);
+ set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundImage, value, document);
+ set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatX, value, document, true);
+ set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatY, value, document, true);
return;
}
@@ -847,11 +753,6 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
}
if (property_id == CSS::PropertyID::BackgroundImage) {
- if (is_background_image(value)) {
- style.set_property(CSS::PropertyID::BackgroundImage, value);
- return;
- }
-
// FIXME: Remove string parsing once DeprecatedCSSParser is gone.
if (value.is_string()) {
return;
@@ -882,7 +783,7 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
return;
}
- dbgln("Unsure what to do with CSS background-image value '{}'", value.to_string());
+ style.set_property(CSS::PropertyID::BackgroundImage, value);
return;
}
@@ -900,10 +801,6 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
}
};
- if (is_background_repeat(value)) {
- assign_background_repeat_from_single_value(value);
- }
-
if (value.is_component_value_list()) {
auto parts = static_cast(value).values();
NonnullRefPtrVector repeat_values;
@@ -922,8 +819,10 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
set_property_expanding_shorthands(style, PropertyID::BackgroundRepeatX, repeat_values[0], document, true);
set_property_expanding_shorthands(style, PropertyID::BackgroundRepeatY, repeat_values[1], document, true);
}
+ return;
}
+ assign_background_repeat_from_single_value(value);
return;
}
diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h
index c3a0a97a3a..fa0d859b86 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleValue.h
+++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h
@@ -229,6 +229,7 @@ public:
ValueList,
ComponentValueList,
Calculated,
+ Background,
BoxShadow,
Font,
};
@@ -247,6 +248,7 @@ public:
bool is_value_list() const { return type() == Type::ValueList; }
bool is_component_value_list() const { return type() == Type::ComponentValueList; }
bool is_calculated() const { return type() == Type::Calculated; }
+ bool is_background() const { return type() == Type::Background; }
bool is_box_shadow() const { return type() == Type::BoxShadow; }
bool is_font() const { return type() == Type::Font; }
@@ -625,6 +627,52 @@ private:
RefPtr m_bitmap;
};
+class BackgroundStyleValue final : public StyleValue {
+public:
+ static NonnullRefPtr create(
+ NonnullRefPtr color,
+ NonnullRefPtr image,
+ NonnullRefPtr repeat_x,
+ NonnullRefPtr repeat_y)
+ {
+ return adopt_ref(*new BackgroundStyleValue(color, image, repeat_x, repeat_y));
+ }
+ virtual ~BackgroundStyleValue() override { }
+
+ NonnullRefPtr color() const { return m_color; }
+ NonnullRefPtr image() const { return m_image; }
+ NonnullRefPtr repeat_x() const { return m_repeat_x; }
+ NonnullRefPtr repeat_y() const { return m_repeat_y; }
+
+ virtual String to_string() const override
+ {
+ return String::formatted("Background color: {}, image: {}, repeat: {}/{}", m_color->to_string(), m_image->to_string(), m_repeat_x->to_string(), m_repeat_y->to_string());
+ }
+
+private:
+ BackgroundStyleValue(
+ NonnullRefPtr color,
+ NonnullRefPtr image,
+ NonnullRefPtr repeat_x,
+ NonnullRefPtr repeat_y)
+ : StyleValue(Type::Background)
+ , m_color(color)
+ , m_image(image)
+ , m_repeat_x(repeat_x)
+ , m_repeat_y(repeat_y)
+ {
+ }
+ NonnullRefPtr m_color;
+ NonnullRefPtr m_image;
+ // FIXME: background-position
+ // FIXME: background-size
+ NonnullRefPtr m_repeat_x;
+ NonnullRefPtr m_repeat_y;
+ // FIXME: background-attachment
+ // FIXME: background-clip
+ // FIXME: background-origin
+};
+
class FontStyleValue final : public StyleValue {
public:
static NonnullRefPtr create(NonnullRefPtr font_style, NonnullRefPtr font_weight, NonnullRefPtr font_size, NonnullRefPtr line_height, NonnullRefPtrVector&& font_families) { return adopt_ref(*new FontStyleValue(font_style, font_weight, font_size, line_height, move(font_families))); }