mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2026-06-28 04:00:33 +00:00
Previously, and according to the spec, `a::part(foo)::before` would be a single CompoundSelector, even though it matches against 3 different targets. This meant some awkward swapping of targets in the middle of matching, and in particular it made `::part()` and `::slotted()` quite hacky, requiring them to track extra data on the MatchContext to then use later. This was scattered around and difficult to follow. Partly inspired by Gecko, this commit instead introduces an invisible PseudoElement combinator. After parsing a selector, we find any CompoundSelectors that contain a pseudo-element and split them up, so that each CompoundSelector only has a single target in the end. Where the pseudo-element was at the start of a CompoundSelector, we insert an invisible universal selector before it to represent its originating element. So now, a CompoundSelector deals with one target, and switching targets is done at the combinator. The one inconsistency is that we match the target of ::slotted() and ::part() in pseudo_element_transition_target(), instead of before then when processing the SimpleSelector. This is to avoid repeating the same computations twice. No outward-facing behaviour changes, though the invalidation metrics have changed.
63 lines
2 KiB
C++
63 lines
2 KiB
C++
/*
|
|
* Copyright (c) 2018-2024, Andreas Kling <andreas@ladybird.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/HashMap.h>
|
|
#include <LibWeb/CSS/Selector.h>
|
|
#include <LibWeb/DOM/Element.h>
|
|
|
|
namespace Web::SelectorEngine {
|
|
|
|
enum class SelectorKind {
|
|
Normal,
|
|
Relative,
|
|
};
|
|
|
|
enum class HasMatchResult : u8 {
|
|
Matched,
|
|
NotMatched,
|
|
};
|
|
|
|
struct HasResultCacheKey {
|
|
CSS::Selector const* selector;
|
|
GC::Ptr<DOM::Element const> element;
|
|
|
|
void visit_edges(GC::Cell::Visitor& visitor)
|
|
{
|
|
visitor.visit(element);
|
|
}
|
|
|
|
bool operator==(HasResultCacheKey const&) const = default;
|
|
};
|
|
|
|
struct HasResultCacheKeyTraits : Traits<HasResultCacheKey> {
|
|
static unsigned hash(HasResultCacheKey const& key)
|
|
{
|
|
return pair_int_hash(ptr_hash(key.selector), ptr_hash(key.element.ptr()));
|
|
}
|
|
};
|
|
|
|
using HasResultCache = HashMap<HasResultCacheKey, HasMatchResult, HasResultCacheKeyTraits>;
|
|
|
|
struct MatchContext {
|
|
GC::Ptr<CSS::CSSStyleSheet const> style_sheet_for_rule {};
|
|
GC::Ptr<DOM::Element const> subject {};
|
|
GC::Ptr<DOM::ShadowRoot const> rule_shadow_root {}; // Shadow root the matched rule belongs to
|
|
bool collect_per_element_selector_involvement_metadata { false };
|
|
// True while we are evaluating the argument of a :has() pseudo-class.
|
|
// Elements visited by selector walks (descendants, siblings, etc.) while
|
|
// this is set get marked as in_has_scope so the invalidation walker can
|
|
// later terminate once it leaves the scope. Transparent to callers; set
|
|
// by matches_has_pseudo_class with a ScopeGuard.
|
|
bool inside_has_argument_match { false };
|
|
CSS::PseudoClassBitmap attempted_pseudo_class_matches {};
|
|
HasResultCache* has_result_cache { nullptr };
|
|
};
|
|
|
|
bool matches(CSS::Selector const&, DOM::AbstractElement const&, GC::Ptr<DOM::Element const> shadow_host, MatchContext& context, GC::Ptr<DOM::ParentNode const> scope = {}, SelectorKind selector_kind = SelectorKind::Normal, GC::Ptr<DOM::Element const> anchor = nullptr);
|
|
|
|
}
|