mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-21 20:15:17 +00:00
LibWeb: Use fast CSS selector matching in default matches() code path
Before this change, checking if fast selector matching could be used was only enabled in style recalculation and hover invalidation. With this change it's enabled for all callers of SelectorEngine::matches() by default. This way APIs like `Element.matches()` and `querySelector()` could take advantage of this optimization.
This commit is contained in:
parent
17c0d4469c
commit
0f17ad9ebc
Notes:
github-actions[bot]
2025-02-03 09:29:08 +00:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/LadybirdBrowser/ladybird/commit/0f17ad9ebcb Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3431 Reviewed-by: https://github.com/gmta ✅
7 changed files with 59 additions and 66 deletions
|
@ -33,6 +33,50 @@ static bool component_value_contains_nesting_selector(Parser::ComponentValue con
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool can_selector_use_fast_matches(CSS::Selector const& selector)
|
||||
{
|
||||
for (auto const& compound_selector : selector.compound_selectors()) {
|
||||
if (compound_selector.combinator != CSS::Selector::Combinator::None
|
||||
&& compound_selector.combinator != CSS::Selector::Combinator::Descendant
|
||||
&& compound_selector.combinator != CSS::Selector::Combinator::ImmediateChild) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto const& simple_selector : compound_selector.simple_selectors) {
|
||||
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoClass) {
|
||||
auto const pseudo_class = simple_selector.pseudo_class().type;
|
||||
if (pseudo_class != CSS::PseudoClass::FirstChild
|
||||
&& pseudo_class != CSS::PseudoClass::LastChild
|
||||
&& pseudo_class != CSS::PseudoClass::OnlyChild
|
||||
&& pseudo_class != CSS::PseudoClass::Hover
|
||||
&& pseudo_class != CSS::PseudoClass::Active
|
||||
&& pseudo_class != CSS::PseudoClass::Focus
|
||||
&& pseudo_class != CSS::PseudoClass::FocusVisible
|
||||
&& pseudo_class != CSS::PseudoClass::FocusWithin
|
||||
&& pseudo_class != CSS::PseudoClass::Link
|
||||
&& pseudo_class != CSS::PseudoClass::AnyLink
|
||||
&& pseudo_class != CSS::PseudoClass::Visited
|
||||
&& pseudo_class != CSS::PseudoClass::LocalLink
|
||||
&& pseudo_class != CSS::PseudoClass::Empty
|
||||
&& pseudo_class != CSS::PseudoClass::Root
|
||||
&& pseudo_class != CSS::PseudoClass::Enabled
|
||||
&& pseudo_class != CSS::PseudoClass::Disabled
|
||||
&& pseudo_class != CSS::PseudoClass::Checked) {
|
||||
return false;
|
||||
}
|
||||
} else if (simple_selector.type != CSS::Selector::SimpleSelector::Type::TagName
|
||||
&& simple_selector.type != CSS::Selector::SimpleSelector::Type::Universal
|
||||
&& simple_selector.type != CSS::Selector::SimpleSelector::Type::Class
|
||||
&& simple_selector.type != CSS::Selector::SimpleSelector::Type::Id
|
||||
&& simple_selector.type != CSS::Selector::SimpleSelector::Type::Attribute) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Selector::Selector(Vector<CompoundSelector>&& compound_selectors)
|
||||
: m_compound_selectors(move(compound_selectors))
|
||||
{
|
||||
|
@ -88,6 +132,8 @@ Selector::Selector(Vector<CompoundSelector>&& compound_selectors)
|
|||
}
|
||||
|
||||
collect_ancestor_hashes();
|
||||
|
||||
m_can_use_fast_matches = can_selector_use_fast_matches(*this);
|
||||
}
|
||||
|
||||
void Selector::collect_ancestor_hashes()
|
||||
|
|
|
@ -268,12 +268,15 @@ public:
|
|||
|
||||
auto const& ancestor_hashes() const { return m_ancestor_hashes; }
|
||||
|
||||
bool can_use_fast_matches() const { return m_can_use_fast_matches; }
|
||||
|
||||
private:
|
||||
explicit Selector(Vector<CompoundSelector>&&);
|
||||
|
||||
Vector<CompoundSelector> m_compound_selectors;
|
||||
mutable Optional<u32> m_specificity;
|
||||
Optional<Selector::PseudoElement> m_pseudo_element;
|
||||
bool m_can_use_fast_matches { false };
|
||||
bool m_contains_the_nesting_selector { false };
|
||||
bool m_contains_hover_pseudo_class { false };
|
||||
|
||||
|
|
|
@ -157,7 +157,7 @@ static inline bool matches_link_pseudo_class(DOM::Element const& element)
|
|||
return element.has_attribute(HTML::AttributeNames::href);
|
||||
}
|
||||
|
||||
bool matches_hover_pseudo_class(DOM::Element const& element)
|
||||
static bool matches_hover_pseudo_class(DOM::Element const& element)
|
||||
{
|
||||
auto* hovered_node = element.document().hovered_node();
|
||||
if (!hovered_node)
|
||||
|
@ -899,8 +899,13 @@ bool matches(CSS::Selector const& selector, int component_list_index, DOM::Eleme
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
bool fast_matches(CSS::Selector const& selector, DOM::Element const& element_to_match, GC::Ptr<DOM::Element const> shadow_host, MatchContext& context);
|
||||
|
||||
bool matches(CSS::Selector const& selector, DOM::Element const& element, GC::Ptr<DOM::Element const> shadow_host, MatchContext& context, Optional<CSS::Selector::PseudoElement::Type> pseudo_element, GC::Ptr<DOM::ParentNode const> scope, SelectorKind selector_kind, GC::Ptr<DOM::Element const> anchor)
|
||||
{
|
||||
if (selector_kind == SelectorKind::Normal && selector.can_use_fast_matches()) {
|
||||
return fast_matches(selector, element, shadow_host, context);
|
||||
}
|
||||
VERIFY(!selector.compound_selectors().is_empty());
|
||||
if (pseudo_element.has_value() && selector.pseudo_element().has_value() && selector.pseudo_element().value().type() != pseudo_element)
|
||||
return false;
|
||||
|
@ -1011,48 +1016,4 @@ bool fast_matches(CSS::Selector const& selector, DOM::Element const& element_to_
|
|||
}
|
||||
}
|
||||
|
||||
bool can_use_fast_matches(CSS::Selector const& selector)
|
||||
{
|
||||
for (auto const& compound_selector : selector.compound_selectors()) {
|
||||
if (compound_selector.combinator != CSS::Selector::Combinator::None
|
||||
&& compound_selector.combinator != CSS::Selector::Combinator::Descendant
|
||||
&& compound_selector.combinator != CSS::Selector::Combinator::ImmediateChild) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto const& simple_selector : compound_selector.simple_selectors) {
|
||||
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoClass) {
|
||||
auto const pseudo_class = simple_selector.pseudo_class().type;
|
||||
if (pseudo_class != CSS::PseudoClass::FirstChild
|
||||
&& pseudo_class != CSS::PseudoClass::LastChild
|
||||
&& pseudo_class != CSS::PseudoClass::OnlyChild
|
||||
&& pseudo_class != CSS::PseudoClass::Hover
|
||||
&& pseudo_class != CSS::PseudoClass::Active
|
||||
&& pseudo_class != CSS::PseudoClass::Focus
|
||||
&& pseudo_class != CSS::PseudoClass::FocusVisible
|
||||
&& pseudo_class != CSS::PseudoClass::FocusWithin
|
||||
&& pseudo_class != CSS::PseudoClass::Link
|
||||
&& pseudo_class != CSS::PseudoClass::AnyLink
|
||||
&& pseudo_class != CSS::PseudoClass::Visited
|
||||
&& pseudo_class != CSS::PseudoClass::LocalLink
|
||||
&& pseudo_class != CSS::PseudoClass::Empty
|
||||
&& pseudo_class != CSS::PseudoClass::Root
|
||||
&& pseudo_class != CSS::PseudoClass::Enabled
|
||||
&& pseudo_class != CSS::PseudoClass::Disabled
|
||||
&& pseudo_class != CSS::PseudoClass::Checked) {
|
||||
return false;
|
||||
}
|
||||
} else if (simple_selector.type != CSS::Selector::SimpleSelector::Type::TagName
|
||||
&& simple_selector.type != CSS::Selector::SimpleSelector::Type::Universal
|
||||
&& simple_selector.type != CSS::Selector::SimpleSelector::Type::Class
|
||||
&& simple_selector.type != CSS::Selector::SimpleSelector::Type::Id
|
||||
&& simple_selector.type != CSS::Selector::SimpleSelector::Type::Attribute) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,9 +25,4 @@ struct MatchContext {
|
|||
|
||||
bool matches(CSS::Selector const&, DOM::Element const&, GC::Ptr<DOM::Element const> shadow_host, MatchContext& context, Optional<CSS::Selector::PseudoElement::Type> = {}, GC::Ptr<DOM::ParentNode const> scope = {}, SelectorKind selector_kind = SelectorKind::Normal, GC::Ptr<DOM::Element const> anchor = nullptr);
|
||||
|
||||
[[nodiscard]] bool fast_matches(CSS::Selector const&, DOM::Element const&, GC::Ptr<DOM::Element const> shadow_host, MatchContext& context);
|
||||
[[nodiscard]] bool can_use_fast_matches(CSS::Selector const&);
|
||||
|
||||
[[nodiscard]] bool matches_hover_pseudo_class(DOM::Element const&);
|
||||
|
||||
}
|
||||
|
|
|
@ -595,13 +595,8 @@ Vector<MatchingRule const*> StyleComputer::collect_matching_rules(DOM::Element c
|
|||
if (context.did_match_any_hover_rules)
|
||||
did_match_any_hover_rules = true;
|
||||
};
|
||||
if (rule_to_run.can_use_fast_matches) {
|
||||
if (!SelectorEngine::fast_matches(selector, element, shadow_host_to_use, context))
|
||||
continue;
|
||||
} else {
|
||||
if (!SelectorEngine::matches(selector, element, shadow_host_to_use, context, pseudo_element))
|
||||
continue;
|
||||
}
|
||||
if (!SelectorEngine::matches(selector, element, shadow_host_to_use, context, pseudo_element))
|
||||
continue;
|
||||
matching_rules.append(&rule_to_run);
|
||||
}
|
||||
|
||||
|
@ -2686,7 +2681,6 @@ void StyleComputer::make_rule_cache_for_cascade_origin(CascadeOrigin cascade_ori
|
|||
selector.specificity(),
|
||||
cascade_origin,
|
||||
false,
|
||||
SelectorEngine::can_use_fast_matches(selector),
|
||||
false,
|
||||
};
|
||||
|
||||
|
|
|
@ -86,7 +86,6 @@ struct MatchingRule {
|
|||
u32 specificity { 0 };
|
||||
CascadeOrigin cascade_origin;
|
||||
bool contains_pseudo_element { false };
|
||||
bool can_use_fast_matches { false };
|
||||
bool must_be_hovered { false };
|
||||
|
||||
// Helpers to deal with the fact that `rule` might be a CSSStyleRule or a CSSNestedDeclarations
|
||||
|
|
|
@ -1728,13 +1728,8 @@ void Document::invalidate_style_for_elements_affected_by_hover_change(Node& old_
|
|||
|
||||
SelectorEngine::MatchContext context;
|
||||
bool selector_matched = false;
|
||||
if (rule.can_use_fast_matches) {
|
||||
if (SelectorEngine::fast_matches(selector, element, {}, context))
|
||||
selector_matched = true;
|
||||
} else {
|
||||
if (SelectorEngine::matches(selector, element, {}, context, {}))
|
||||
selector_matched = true;
|
||||
}
|
||||
if (SelectorEngine::matches(selector, element, {}, context, {}))
|
||||
selector_matched = true;
|
||||
if (element.has_pseudo_elements()) {
|
||||
if (SelectorEngine::matches(selector, element, {}, context, CSS::Selector::PseudoElement::Type::Before))
|
||||
selector_matched = true;
|
||||
|
|
Loading…
Add table
Reference in a new issue