1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 05:17:34 +00:00

LibWeb: Cache lowercased names in SimpleSelector

When matching selectors in HTML documents, we know that all the elements
have lowercase local names already (the parser makes sure of this.)

Style sheets still need to remember the original name strings, in case
we want to match against non-HTML content like XML/SVG. To make the
common HTML case faster, we now cache a lowercase version of the name
with each type/class/id SimpleSelector.

This makes tag type checks O(1) instead of O(n).
This commit is contained in:
Andreas Kling 2022-09-15 13:51:30 +02:00
parent d9c64ee876
commit 1dd4e2dc87
4 changed files with 24 additions and 10 deletions

View file

@ -695,7 +695,7 @@ Parser::ParseErrorOr<Optional<Selector::SimpleSelector>> Parser::parse_simple_se
}
return Selector::SimpleSelector {
.type = Selector::SimpleSelector::Type::Class,
.value = FlyString { class_name_value.token().ident() }
.value = Selector::SimpleSelector::Name { class_name_value.token().ident() }
};
}
case '>':
@ -719,13 +719,13 @@ Parser::ParseErrorOr<Optional<Selector::SimpleSelector>> Parser::parse_simple_se
}
return Selector::SimpleSelector {
.type = Selector::SimpleSelector::Type::Id,
.value = FlyString { first_value.token().hash_value() }
.value = Selector::SimpleSelector::Name { first_value.token().hash_value() }
};
}
if (first_value.is(Token::Type::Ident)) {
return Selector::SimpleSelector {
.type = Selector::SimpleSelector::Type::TagName,
.value = FlyString { first_value.token().ident() }
.value = Selector::SimpleSelector::Name { first_value.token().ident() }
};
}
if (first_value.is_block() && first_value.block().is_square())

View file

@ -142,8 +142,19 @@ public:
CaseType case_type;
};
struct Name {
Name(FlyString n)
: name(move(n))
, lowercase_name(name.to_lowercase())
{
}
FlyString name;
FlyString lowercase_name;
};
Type type;
Variant<Empty, Attribute, PseudoClass, PseudoElement, FlyString> value {};
Variant<Empty, Attribute, PseudoClass, PseudoElement, Name> value {};
Attribute const& attribute() const { return value.get<Attribute>(); }
Attribute& attribute() { return value.get<Attribute>(); }
@ -151,8 +162,11 @@ public:
PseudoClass& pseudo_class() { return value.get<PseudoClass>(); }
PseudoElement const& pseudo_element() const { return value.get<PseudoElement>(); }
PseudoElement& pseudo_element() { return value.get<PseudoElement>(); }
FlyString const& name() const { return value.get<FlyString>(); }
FlyString& name() { return value.get<FlyString>(); }
FlyString const& name() const { return value.get<Name>().name; }
FlyString& name() { return value.get<Name>().name; }
FlyString const& lowercase_name() const { return value.get<Name>().lowercase_name; }
FlyString& lowercase_name() { return value.get<Name>().lowercase_name; }
String serialize() const;
};

View file

@ -353,9 +353,9 @@ static inline bool matches(CSS::Selector::SimpleSelector const& component, DOM::
return element.has_class(component.name());
case CSS::Selector::SimpleSelector::Type::TagName:
// See https://html.spec.whatwg.org/multipage/semantics-other.html#case-sensitivity-of-selectors
if (is<HTML::HTMLElement>(element) && element.document().document_type() != DOM::Document::Type::XML)
if (element.document().document_type() == DOM::Document::Type::HTML)
return component.lowercase_name() == element.local_name();
return component.name().equals_ignoring_case(element.local_name());
return component.name() == element.local_name();
case CSS::Selector::SimpleSelector::Type::Attribute:
return matches_attribute(component.attribute(), element);
case CSS::Selector::SimpleSelector::Type::PseudoClass:

View file

@ -372,7 +372,7 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector)
builder.appendff("{}:", type_description);
// FIXME: This is goofy
if (simple_selector.value.has<FlyString>())
if (simple_selector.value.has<CSS::Selector::SimpleSelector::Name>())
builder.append(simple_selector.name());
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoClass) {