/* * Copyright (c) 2024, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include namespace Web::CSS { GC_DEFINE_ALLOCATOR(CascadedProperties); CascadedProperties::CascadedProperties() = default; CascadedProperties::~CascadedProperties() = default; void CascadedProperties::visit_edges(Visitor& visitor) { Base::visit_edges(visitor); for (auto const& [property_id, entries] : m_properties) { for (auto const& entry : entries) { visitor.visit(entry.source); visitor.visit(entry.source_shadow_root); } } } void CascadedProperties::revert_property(PropertyID property_id, Important important, CascadeOrigin cascade_origin) { auto it = m_properties.find(property_id); if (it == m_properties.end()) return; auto& entries = it->value; entries.remove_all_matching([&](auto& entry) { // https://drafts.csswg.org/css-cascade-5/#author-presentational-hint-origin // For the purpose of cascading this author presentational hint origin is treated as an independent origin, but // for the purpose of the revert keyword it is considered part of the author origin. auto origin_matches = entry.origin == cascade_origin || (cascade_origin == CascadeOrigin::Author && entry.origin == CascadeOrigin::AuthorPresentationalHint); return entry.property.property_id == property_id && entry.property.important == important && origin_matches; }); if (entries.is_empty()) { m_contained_properties_cache.set(to_underlying(property_id), false); m_properties.remove(it); } } void CascadedProperties::revert_layer_property(PropertyID property_id, Important important, Optional layer_name) { auto it = m_properties.find(property_id); if (it == m_properties.end()) return; auto& entries = it->value; entries.remove_all_matching([&](auto& entry) { return entry.property.property_id == property_id && entry.property.important == important && layer_name == entry.layer_name; }); if (entries.is_empty()) { m_contained_properties_cache.set(to_underlying(property_id), false); m_properties.remove(it); } } void CascadedProperties::set_property(PropertyID property_id, NonnullRefPtr value, Important important, CascadeOrigin origin, Optional layer_name, GC::Ptr source, GC::Ptr source_shadow_root) { m_contained_properties_cache.set(to_underlying(property_id), true); auto& entries = m_properties.ensure(property_id); for (auto& entry : entries.in_reverse()) { if (entry.origin == origin && entry.layer_name == layer_name) { if (entry.property.important == Important::Yes && important == Important::No) return; entry.property = StyleProperty { .important = important, .property_id = property_id, .value = value, }; entry.cascade_index = m_next_cascade_index++; entry.source = source; entry.source_shadow_root = source_shadow_root; return; } } entries.append(Entry { .property = StyleProperty { .important = important, .property_id = property_id, .value = value, }, .cascade_index = m_next_cascade_index++, .origin = origin, .layer_name = move(layer_name), .source = source, .source_shadow_root = source_shadow_root, }); } RefPtr CascadedProperties::property(PropertyID property_id) const { if (!m_contained_properties_cache.get(to_underlying(property_id))) return nullptr; return m_properties.get(property_id)->last().property.value; } PropertyID CascadedProperties::property_with_higher_priority(PropertyID first_property_id, PropertyID second_property_id) const { if (!m_contained_properties_cache.get(to_underlying(first_property_id))) return second_property_id; if (!m_contained_properties_cache.get(to_underlying(second_property_id))) return first_property_id; if (m_properties.get(first_property_id)->last().cascade_index >= m_properties.get(second_property_id)->last().cascade_index) return first_property_id; return second_property_id; } GC::Ptr CascadedProperties::property_source(PropertyID property_id) const { if (!m_contained_properties_cache.get(to_underlying(property_id))) return nullptr; return m_properties.get(property_id)->last().source; } GC::Ptr CascadedProperties::property_source_shadow_root(PropertyID property_id) const { if (!m_contained_properties_cache.get(to_underlying(property_id))) return nullptr; return m_properties.get(property_id)->last().source_shadow_root; } Optional CascadedProperties::style_property(PropertyID property_id) const { if (!m_contained_properties_cache.get(to_underlying(property_id))) return {}; return m_properties.get(property_id)->last().property; } }