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))); }