diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp index dd3889c825d..326de53565c 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -835,15 +835,15 @@ void StyleComputer::for_each_property_expanding_shorthands(PropertyID property_i set_longhand_property(property_id, value); } -void StyleComputer::set_property_expanding_shorthands(StyleProperties& style, CSS::PropertyID property_id, StyleValue const& value, CSS::CSSStyleDeclaration const* declaration, StyleProperties::PropertyValues const& properties_for_revert, CSS::CSSStyleDeclaration const* animation_name_source_for_revert, StyleProperties::Important important) +void StyleComputer::set_property_expanding_shorthands(StyleProperties& style, CSS::PropertyID property_id, StyleValue const& value, CSS::CSSStyleDeclaration const* declaration, StyleProperties const& style_for_revert, Important important) { for_each_property_expanding_shorthands(property_id, value, AllowUnresolved::No, [&](PropertyID shorthand_id, StyleValue const& shorthand_value) { if (shorthand_value.is_revert()) { - auto& property_in_previous_cascade_origin = properties_for_revert[to_underlying(shorthand_id)]; - if (property_in_previous_cascade_origin.style) { - style.set_property(shorthand_id, *property_in_previous_cascade_origin.style, StyleProperties::Inherited::No, important); + auto const& property_in_previous_cascade_origin = style_for_revert.m_property_values[to_underlying(shorthand_id)]; + if (property_in_previous_cascade_origin) { + style.set_property(shorthand_id, *property_in_previous_cascade_origin, StyleProperties::Inherited::No, important); if (shorthand_id == CSS::PropertyID::AnimationName) - style.set_animation_name_source(animation_name_source_for_revert); + style.set_animation_name_source(style_for_revert.animation_name_source()); } } else { style.set_property(shorthand_id, shorthand_value, StyleProperties::Inherited::No, important); @@ -853,23 +853,36 @@ void StyleComputer::set_property_expanding_shorthands(StyleProperties& style, CS }); } -void StyleComputer::set_all_properties(DOM::Element& element, Optional pseudo_element, StyleProperties& style, StyleValue const& value, DOM::Document& document, CSS::CSSStyleDeclaration const* declaration, StyleProperties::PropertyValues const& properties_for_revert, CSS::CSSStyleDeclaration const* animation_name_source_for_revert, StyleProperties::Important important) const +void StyleComputer::set_all_properties(DOM::Element& element, Optional pseudo_element, StyleProperties& style, StyleValue const& value, DOM::Document& document, CSS::CSSStyleDeclaration const* declaration, StyleProperties const& style_for_revert, Important important) const { for (auto i = to_underlying(CSS::first_longhand_property_id); i <= to_underlying(CSS::last_longhand_property_id); ++i) { auto property_id = (CSS::PropertyID)i; if (value.is_revert()) { - style.m_property_values[to_underlying(property_id)] = properties_for_revert[to_underlying(property_id)]; - style.m_property_values[to_underlying(property_id)].important = important; + style.set_property( + property_id, + style_for_revert.property(property_id), + style_for_revert.is_property_inherited(property_id) + ? StyleProperties::Inherited::Yes + : StyleProperties::Inherited::No, + important); continue; } if (value.is_unset()) { - if (is_inherited_property(property_id)) - style.m_property_values[to_underlying(property_id)] = { get_inherit_value(document.realm(), property_id, &element, pseudo_element) }; - else - style.m_property_values[to_underlying(property_id)] = { property_initial_value(document.realm(), property_id) }; - style.m_property_values[to_underlying(property_id)].important = important; + if (is_inherited_property(property_id)) { + style.set_property( + property_id, + get_inherit_value(document.realm(), property_id, &element, pseudo_element), + StyleProperties::Inherited::Yes, + important); + } else { + style.set_property( + property_id, + property_initial_value(document.realm(), property_id), + StyleProperties::Inherited::No, + important); + } continue; } @@ -877,18 +890,17 @@ void StyleComputer::set_all_properties(DOM::Element& element, Optionalis_unresolved()) property_value = Parser::Parser::resolve_unresolved_style_value(Parser::ParsingContext { document }, element, pseudo_element, property_id, property_value->as_unresolved()); if (!property_value->is_unresolved()) - set_property_expanding_shorthands(style, property_id, property_value, declaration, properties_for_revert, animation_name_source_for_revert); + set_property_expanding_shorthands(style, property_id, property_value, declaration, style_for_revert); - style.m_property_values[to_underlying(property_id)].important = important; + style.set_property_important(property_id, important); - set_property_expanding_shorthands(style, property_id, value, declaration, properties_for_revert, animation_name_source_for_revert, important); + set_property_expanding_shorthands(style, property_id, value, declaration, style_for_revert, important); } } void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& element, Optional pseudo_element, Vector const& matching_rules, CascadeOrigin cascade_origin, Important important) const { - auto animation_name_source_for_revert = style.animation_name_source(); - auto properties_for_revert = style.properties(); + auto style_for_revert = style.clone(); for (auto const& match : matching_rules) { for (auto const& property : match.rule->declaration().properties()) { @@ -896,7 +908,7 @@ void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& e continue; if (property.property_id == CSS::PropertyID::All) { - set_all_properties(element, pseudo_element, style, property.value, m_document, &match.rule->declaration(), properties_for_revert, animation_name_source_for_revert, important == Important::Yes ? StyleProperties::Important::Yes : StyleProperties::Important::No); + set_all_properties(element, pseudo_element, style, property.value, m_document, &match.rule->declaration(), style_for_revert, important); continue; } @@ -904,7 +916,7 @@ void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& e if (property.value->is_unresolved()) property_value = Parser::Parser::resolve_unresolved_style_value(Parser::ParsingContext { document() }, element, pseudo_element, property.property_id, property.value->as_unresolved()); if (!property_value->is_unresolved()) - set_property_expanding_shorthands(style, property.property_id, property_value, &match.rule->declaration(), properties_for_revert, animation_name_source_for_revert, important == Important::Yes ? StyleProperties::Important::Yes : StyleProperties::Important::No); + set_property_expanding_shorthands(style, property.property_id, property_value, &match.rule->declaration(), style_for_revert, important); } } @@ -915,7 +927,7 @@ void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& e continue; if (property.property_id == CSS::PropertyID::All) { - set_all_properties(element, pseudo_element, style, property.value, m_document, inline_style, properties_for_revert, animation_name_source_for_revert, important == Important::Yes ? StyleProperties::Important::Yes : StyleProperties::Important::No); + set_all_properties(element, pseudo_element, style, property.value, m_document, inline_style, style_for_revert, important); continue; } @@ -923,7 +935,7 @@ void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& e if (property.value->is_unresolved()) property_value = Parser::Parser::resolve_unresolved_style_value(Parser::ParsingContext { document() }, element, pseudo_element, property.property_id, property.value->as_unresolved()); if (!property_value->is_unresolved()) - set_property_expanding_shorthands(style, property.property_id, property_value, inline_style, properties_for_revert, animation_name_source_for_revert, important == Important::Yes ? StyleProperties::Important::Yes : StyleProperties::Important::No); + set_property_expanding_shorthands(style, property.property_id, property_value, inline_style, style_for_revert, important); } } } @@ -1757,8 +1769,8 @@ void StyleComputer::compute_cascaded_values(StyleProperties& style, DOM::Element for (auto i = to_underlying(CSS::first_property_id); i <= to_underlying(CSS::last_property_id); ++i) { auto property_id = (CSS::PropertyID)i; auto& property = style.m_property_values[i]; - if (property.style && property.style->is_unresolved()) - property.style = Parser::Parser::resolve_unresolved_style_value(Parser::ParsingContext { document() }, element, pseudo_element, property_id, property.style->as_unresolved()); + if (property && property->is_unresolved()) + property = Parser::Parser::resolve_unresolved_style_value(Parser::ParsingContext { document() }, element, pseudo_element, property_id, property->as_unresolved()); } } } @@ -1866,35 +1878,40 @@ void StyleComputer::compute_defaulted_property_value(StyleProperties& style, DOM // FIXME: If we don't know the correct initial value for a property, we fall back to InitialStyleValue. auto& value_slot = style.m_property_values[to_underlying(property_id)]; - if (!value_slot.style) { - if (is_inherited_property(property_id)) - style.m_property_values[to_underlying(property_id)] = { get_inherit_value(document().realm(), property_id, element, pseudo_element), StyleProperties::Important::No, StyleProperties::Inherited::Yes }; - else - style.m_property_values[to_underlying(property_id)] = { property_initial_value(document().realm(), property_id) }; + if (!value_slot) { + if (is_inherited_property(property_id)) { + style.set_property( + property_id, + get_inherit_value(document().realm(), property_id, element, pseudo_element), + StyleProperties::Inherited::Yes, + Important::No); + } else { + style.set_property(property_id, property_initial_value(document().realm(), property_id)); + } return; } - if (value_slot.style->is_initial()) { - value_slot.style = property_initial_value(document().realm(), property_id); + if (value_slot->is_initial()) { + value_slot = property_initial_value(document().realm(), property_id); return; } - if (value_slot.style->is_inherit()) { - value_slot.style = get_inherit_value(document().realm(), property_id, element, pseudo_element); - value_slot.inherited = StyleProperties::Inherited::Yes; + if (value_slot->is_inherit()) { + value_slot = get_inherit_value(document().realm(), property_id, element, pseudo_element); + style.set_property_inherited(property_id, StyleProperties::Inherited::Yes); return; } // https://www.w3.org/TR/css-cascade-4/#inherit-initial // If the cascaded value of a property is the unset keyword, - if (value_slot.style->is_unset()) { + if (value_slot->is_unset()) { if (is_inherited_property(property_id)) { // then if it is an inherited property, this is treated as inherit, - value_slot.style = get_inherit_value(document().realm(), property_id, element, pseudo_element); - value_slot.inherited = StyleProperties::Inherited::Yes; + value_slot = get_inherit_value(document().realm(), property_id, element, pseudo_element); + style.set_property_inherited(property_id, StyleProperties::Inherited::Yes); } else { // and if it is not, this is treated as initial. - value_slot.style = property_initial_value(document().realm(), property_id); + value_slot = property_initial_value(document().realm(), property_id); } } } @@ -2327,7 +2344,7 @@ void StyleComputer::absolutize_values(StyleProperties& style) const // We have to resolve them right away, so that the *computed* line-height is ready for inheritance. // We can't simply absolutize *all* percentage values against the font size, // because most percentages are relative to containing block metrics. - auto& line_height_value_slot = style.m_property_values[to_underlying(CSS::PropertyID::LineHeight)].style; + auto& line_height_value_slot = style.m_property_values[to_underlying(CSS::PropertyID::LineHeight)]; if (line_height_value_slot && line_height_value_slot->is_percentage()) { line_height_value_slot = LengthStyleValue::create( Length::make_px(CSSPixels::nearest_value_for(font_size * static_cast(line_height_value_slot->as_percentage().percentage().as_fraction())))); @@ -2342,9 +2359,9 @@ void StyleComputer::absolutize_values(StyleProperties& style) const for (size_t i = 0; i < style.m_property_values.size(); ++i) { auto& value_slot = style.m_property_values[i]; - if (!value_slot.style) + if (!value_slot) continue; - value_slot.style = value_slot.style->absolutized(viewport_rect(), font_metrics, m_root_element_font_metrics); + value_slot = value_slot->absolutized(viewport_rect(), font_metrics, m_root_element_font_metrics); } style.set_line_height({}, line_height); diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.h b/Userland/Libraries/LibWeb/CSS/StyleComputer.h index 0d60aa0ab8a..6965e7201f8 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.h +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.h @@ -113,7 +113,7 @@ public: No, }; static void for_each_property_expanding_shorthands(PropertyID, StyleValue const&, AllowUnresolved, Function const& set_longhand_property); - static void set_property_expanding_shorthands(StyleProperties&, PropertyID, StyleValue const&, CSS::CSSStyleDeclaration const*, StyleProperties::PropertyValues const& properties_for_revert, CSS::CSSStyleDeclaration const* animation_name_source_for_revert, StyleProperties::Important = StyleProperties::Important::No); + static void set_property_expanding_shorthands(StyleProperties&, PropertyID, StyleValue const&, CSS::CSSStyleDeclaration const*, StyleProperties const& style_for_revert, Important = Important::No); static NonnullRefPtr get_inherit_value(JS::Realm& initial_value_context_realm, CSS::PropertyID, DOM::Element const*, Optional = {}); explicit StyleComputer(DOM::Document&); @@ -177,7 +177,7 @@ private: void compute_defaulted_property_value(StyleProperties&, DOM::Element const*, CSS::PropertyID, Optional) const; - void set_all_properties(DOM::Element&, Optional, StyleProperties&, StyleValue const&, DOM::Document&, CSS::CSSStyleDeclaration const*, StyleProperties::PropertyValues const& properties_for_revert, CSS::CSSStyleDeclaration const* animation_name_source_for_revert, StyleProperties::Important = StyleProperties::Important::No) const; + void set_all_properties(DOM::Element&, Optional, StyleProperties&, StyleValue const&, DOM::Document&, CSS::CSSStyleDeclaration const*, StyleProperties const& style_for_revert, Important = Important::No) const; template void for_each_stylesheet(CascadeOrigin, Callback) const; diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp index 3f32e2e1f82..58d433302d7 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2023, Andreas Kling + * Copyright (c) 2018-2024, Andreas Kling * Copyright (c) 2021-2024, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause @@ -37,19 +37,55 @@ namespace Web::CSS { +NonnullRefPtr StyleProperties::clone() const +{ + auto clone = adopt_ref(*new StyleProperties); + clone->m_property_values = m_property_values; + clone->m_animated_property_values = m_animated_property_values; + clone->m_property_important = m_property_important; + clone->m_property_inherited = m_property_inherited; + clone->m_font_list = m_font_list; + clone->m_line_height = m_line_height; + clone->m_animation_name_source = m_animation_name_source; + clone->m_math_depth = m_math_depth; + return clone; +} + bool StyleProperties::is_property_important(CSS::PropertyID property_id) const { - return m_property_values[to_underlying(property_id)].style && m_property_values[to_underlying(property_id)].important == Important::Yes; + size_t n = to_underlying(property_id); + return m_property_important[n / 8] & (1 << (n % 8)); +} + +void StyleProperties::set_property_important(CSS::PropertyID property_id, Important important) +{ + size_t n = to_underlying(property_id); + if (important == Important::Yes) + m_property_important[n / 8] |= (1 << (n % 8)); + else + m_property_important[n / 8] &= ~(1 << (n % 8)); } bool StyleProperties::is_property_inherited(CSS::PropertyID property_id) const { - return m_property_values[to_underlying(property_id)].style && m_property_values[to_underlying(property_id)].inherited == Inherited::Yes; + size_t n = to_underlying(property_id); + return m_property_inherited[n / 8] & (1 << (n % 8)); +} + +void StyleProperties::set_property_inherited(CSS::PropertyID property_id, Inherited inherited) +{ + size_t n = to_underlying(property_id); + if (inherited == Inherited::Yes) + m_property_inherited[n / 8] |= (1 << (n % 8)); + else + m_property_inherited[n / 8] &= ~(1 << (n % 8)); } void StyleProperties::set_property(CSS::PropertyID id, NonnullRefPtr value, Inherited inherited, Important important) { - m_property_values[to_underlying(id)] = StyleValueAndMetadata { move(value), important, inherited }; + m_property_values[to_underlying(id)] = move(value); + set_property_important(id, important); + set_property_inherited(id, inherited); } void StyleProperties::set_animated_property(CSS::PropertyID id, NonnullRefPtr value) @@ -68,14 +104,14 @@ NonnullRefPtr StyleProperties::property(CSS::PropertyID proper return *animated_value; // By the time we call this method, all properties have values assigned. - return *m_property_values[to_underlying(property_id)].style; + return *m_property_values[to_underlying(property_id)]; } RefPtr StyleProperties::maybe_null_property(CSS::PropertyID property_id) const { if (auto animated_value = m_animated_property_values.get(property_id).value_or(nullptr)) return *animated_value; - return m_property_values[to_underlying(property_id)].style; + return m_property_values[to_underlying(property_id)]; } CSS::Size StyleProperties::size_value(CSS::PropertyID id) const @@ -571,15 +607,15 @@ bool StyleProperties::operator==(StyleProperties const& other) const for (size_t i = 0; i < m_property_values.size(); ++i) { auto const& my_style = m_property_values[i]; auto const& other_style = other.m_property_values[i]; - if (!my_style.style) { - if (other_style.style) + if (!my_style) { + if (other_style) return false; continue; } - if (!other_style.style) + if (!other_style) return false; - auto const& my_value = *my_style.style; - auto const& other_value = *other_style.style; + auto const& my_value = *my_style; + auto const& other_value = *other_style; if (my_value.type() != other_value.type()) return false; if (my_value != other_value) diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.h b/Userland/Libraries/LibWeb/CSS/StyleProperties.h index d5fb1caf83b..a30bf63ec64 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.h +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.h @@ -15,6 +15,7 @@ #include #include #include +#include namespace Web::CSS { @@ -23,41 +24,31 @@ public: StyleProperties() = default; static NonnullRefPtr create() { return adopt_ref(*new StyleProperties); } + NonnullRefPtr clone() const; template inline void for_each_property(Callback callback) const { for (size_t i = 0; i < m_property_values.size(); ++i) { - if (m_property_values[i].style) - callback((CSS::PropertyID)i, *m_property_values[i].style); + if (m_property_values[i]) + callback((CSS::PropertyID)i, *m_property_values[i]); } } - enum class Important { - No, - Yes - }; - enum class Inherited { No, Yes }; - struct StyleValueAndMetadata { - RefPtr style; - Important important { Important::No }; - Inherited inherited { Inherited::No }; - }; - using PropertyValues = Array; - - auto& properties() { return m_property_values; } - auto const& properties() const { return m_property_values; } + static constexpr size_t number_of_properties = to_underlying(CSS::last_property_id) + 1; HashMap> const& animated_property_values() const { return m_animated_property_values; } void reset_animated_properties(); bool is_property_important(CSS::PropertyID property_id) const; bool is_property_inherited(CSS::PropertyID property_id) const; + void set_property_important(CSS::PropertyID, Important); + void set_property_inherited(CSS::PropertyID, Inherited); void set_property(CSS::PropertyID, NonnullRefPtr value, Inherited = Inherited::No, Important = Important::No); void set_animated_property(CSS::PropertyID, NonnullRefPtr value); @@ -192,7 +183,10 @@ private: // FIXME: This needs protection from GC! JS::GCPtr m_animation_name_source; - PropertyValues m_property_values; + Array, number_of_properties> m_property_values; + Array m_property_important {}; + Array m_property_inherited {}; + HashMap> m_animated_property_values; Optional overflow(CSS::PropertyID) const; diff --git a/Userland/Services/WebContent/ConnectionFromClient.cpp b/Userland/Services/WebContent/ConnectionFromClient.cpp index 03e37c6fe45..36f0fb911d0 100644 --- a/Userland/Services/WebContent/ConnectionFromClient.cpp +++ b/Userland/Services/WebContent/ConnectionFromClient.cpp @@ -332,9 +332,10 @@ void ConnectionFromClient::debug_request(u64 page_id, ByteString const& request, if (element->is_element()) { auto styles = doc->style_computer().compute_style(*static_cast(element)); dbgln("+ Element {}", element->debug_description()); - auto& properties = styles->properties(); - for (size_t i = 0; i < properties.size(); ++i) - dbgln("| {} = {}", Web::CSS::string_from_property_id(static_cast(i)), properties[i].style ? properties[i].style->to_string() : ""_string); + for (size_t i = 0; i < Web::CSS::StyleProperties::number_of_properties; ++i) { + auto property = styles->maybe_null_property(static_cast(i)); + dbgln("| {} = {}", Web::CSS::string_from_property_id(static_cast(i)), property ? property->to_string() : ""_string); + } dbgln("---"); } }