From 1dd4e2dc87843646d5bea67fd351927699b5eb3e Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 15 Sep 2022 13:51:30 +0200 Subject: [PATCH] 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). --- .../Libraries/LibWeb/CSS/Parser/Parser.cpp | 6 +++--- Userland/Libraries/LibWeb/CSS/Selector.h | 20 ++++++++++++++++--- .../Libraries/LibWeb/CSS/SelectorEngine.cpp | 6 +++--- Userland/Libraries/LibWeb/Dump.cpp | 2 +- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index f29328c7237..754182d5bbb 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -695,7 +695,7 @@ Parser::ParseErrorOr> 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> 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()) diff --git a/Userland/Libraries/LibWeb/CSS/Selector.h b/Userland/Libraries/LibWeb/CSS/Selector.h index 4d9a1c0ee28..14ebcdadcac 100644 --- a/Userland/Libraries/LibWeb/CSS/Selector.h +++ b/Userland/Libraries/LibWeb/CSS/Selector.h @@ -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 value {}; + Variant value {}; Attribute const& attribute() const { return value.get(); } Attribute& attribute() { return value.get(); } @@ -151,8 +162,11 @@ public: PseudoClass& pseudo_class() { return value.get(); } PseudoElement const& pseudo_element() const { return value.get(); } PseudoElement& pseudo_element() { return value.get(); } - FlyString const& name() const { return value.get(); } - FlyString& name() { return value.get(); } + + FlyString const& name() const { return value.get().name; } + FlyString& name() { return value.get().name; } + FlyString const& lowercase_name() const { return value.get().lowercase_name; } + FlyString& lowercase_name() { return value.get().lowercase_name; } String serialize() const; }; diff --git a/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp b/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp index dfdf426bf8b..265b8df8aa9 100644 --- a/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp +++ b/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp @@ -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(element) && element.document().document_type() != DOM::Document::Type::XML) - return component.name().equals_ignoring_case(element.local_name()); - return component.name() == element.local_name(); + 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()); case CSS::Selector::SimpleSelector::Type::Attribute: return matches_attribute(component.attribute(), element); case CSS::Selector::SimpleSelector::Type::PseudoClass: diff --git a/Userland/Libraries/LibWeb/Dump.cpp b/Userland/Libraries/LibWeb/Dump.cpp index 7b2d642c2b6..92e13a5a75f 100644 --- a/Userland/Libraries/LibWeb/Dump.cpp +++ b/Userland/Libraries/LibWeb/Dump.cpp @@ -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()) + if (simple_selector.value.has()) builder.append(simple_selector.name()); if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoClass) {