From 0b2a5ac14375e16cf78e59062fc3db1b5b58c96f Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 18 Jun 2026 21:49:22 +0200 Subject: [PATCH] LibWeb: Salt ancestor filter hashes by selector component Separate ancestor filter hashes for tag names, ids, classes, and attributes. This avoids false positives where identical strings in different selector component kinds satisfy each other, while keeping the filter conservative. --- Libraries/LibWeb/CSS/AncestorFilter.h | 35 ++++++++++++++++++++++++++ Libraries/LibWeb/CSS/Selector.cpp | 9 ++++--- Libraries/LibWeb/CSS/StyleComputer.cpp | 9 ++++--- 3 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 Libraries/LibWeb/CSS/AncestorFilter.h diff --git a/Libraries/LibWeb/CSS/AncestorFilter.h b/Libraries/LibWeb/CSS/AncestorFilter.h new file mode 100644 index 0000000000..cf3078de58 --- /dev/null +++ b/Libraries/LibWeb/CSS/AncestorFilter.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2026-present, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Web::CSS { + +// Separate otherwise identical string hashes by selector component kind, +// e.g. `.article` versus `
`. +static constexpr u32 ancestor_filter_hash_for_tag_name(u32 hash) +{ + return hash * 13; +} + +static constexpr u32 ancestor_filter_hash_for_id(u32 hash) +{ + return hash * 17; +} + +static constexpr u32 ancestor_filter_hash_for_class(u32 hash) +{ + return hash * 19; +} + +static constexpr u32 ancestor_filter_hash_for_attribute(u32 hash) +{ + return hash * 23; +} + +} diff --git a/Libraries/LibWeb/CSS/Selector.cpp b/Libraries/LibWeb/CSS/Selector.cpp index 5ba1e4012b..2329fe8551 100644 --- a/Libraries/LibWeb/CSS/Selector.cpp +++ b/Libraries/LibWeb/CSS/Selector.cpp @@ -8,6 +8,7 @@ #include "Selector.h" #include #include +#include #include #include #include @@ -201,14 +202,16 @@ void Selector::collect_ancestor_hashes() Vector hashes; switch (simple_selector.type) { case SimpleSelector::Type::Id: + hashes.append(ancestor_filter_hash_for_id(simple_selector.name().hash())); + break; case SimpleSelector::Type::Class: - hashes.append(simple_selector.name().hash()); + hashes.append(ancestor_filter_hash_for_class(simple_selector.name().hash())); break; case SimpleSelector::Type::TagName: - hashes.append(simple_selector.qualified_name().name.lowercase_name.hash()); + hashes.append(ancestor_filter_hash_for_tag_name(simple_selector.qualified_name().name.lowercase_name.hash())); break; case SimpleSelector::Type::Attribute: - hashes.append(simple_selector.attribute().qualified_name.name.lowercase_name.hash()); + hashes.append(ancestor_filter_hash_for_attribute(simple_selector.attribute().qualified_name.name.lowercase_name.hash())); break; case SimpleSelector::Type::PseudoClass: { auto const& pseudo_class = simple_selector.pseudo_class(); diff --git a/Libraries/LibWeb/CSS/StyleComputer.cpp b/Libraries/LibWeb/CSS/StyleComputer.cpp index 36e7d18af3..200087d893 100644 --- a/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -3787,13 +3788,13 @@ NonnullRefPtr StyleComputer::compute_math_depth(NonnullRefPtr< static void for_each_element_hash(DOM::Element const& element, auto callback) { - callback(element.local_name().ascii_case_insensitive_hash()); + callback(ancestor_filter_hash_for_tag_name(element.local_name().ascii_case_insensitive_hash())); if (element.id().has_value()) - callback(element.id().value().hash()); + callback(ancestor_filter_hash_for_id(element.id().value().hash())); for (auto const& class_ : element.class_names()) - callback(class_.hash()); + callback(ancestor_filter_hash_for_class(class_.hash())); element.for_each_attribute([&](auto& attribute) { - callback(attribute.name().ascii_case_insensitive_hash()); + callback(ancestor_filter_hash_for_attribute(attribute.name().ascii_case_insensitive_hash())); }); }