From 628932e57eb445346f28a19dc53fa22c80bcc039 Mon Sep 17 00:00:00 2001 From: "antti@apple.com" Date: Wed, 28 Feb 2018 15:50:00 +0000 Subject: [PATCH] Filter attribute selectors with selector filter https://bugs.webkit.org/show_bug.cgi?id=183200 Reviewed by Zalan Bujtas. Currently selector filtering is done based on tags, classes and ids. We should include attributes too. This patch adds filtering based on attribute name (but not content). * css/SelectorFilter.cpp: (WebCore::isExcludedAttribute): Ignore id, class and style attributes. First two are already handled and the last is common but is rarely used in selectors. (WebCore::collectElementIdentifierHashes): Collect attributes. Remove the unnecessary StyledElement casting. (WebCore::collectSimpleSelectorHash): Collect attribute selectors. (WebCore::chooseSelectorHashesForFilter): Pick attributes with high priority for the filter as it is likely a good signal. git-svn-id: https://svn.webkit.org/repository/webkit/trunk@229090 268f45cc-cd09-0410-ab3c-d52691b4dbfc --- Source/WebCore/ChangeLog | 30 +++++++++++++++ Source/WebCore/css/SelectorFilter.cpp | 53 +++++++++++++++++++++------ 2 files changed, 71 insertions(+), 12 deletions(-) diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog index 8dee284b74ec..736ac24b8d37 100644 --- a/Source/WebCore/ChangeLog +++ b/Source/WebCore/ChangeLog @@ -1,3 +1,33 @@ +2018-02-28 Antti Koivisto + + Filter attribute selectors with selector filter + https://bugs.webkit.org/show_bug.cgi?id=183200 + + Reviewed by Zalan Bujtas. + + Currently selector filtering is done based on tags, classes and ids. We should include attributes too. + + This patch adds filtering based on attribute name (but not content). + + * css/SelectorFilter.cpp: + (WebCore::isExcludedAttribute): + + Ignore id, class and style attributes. First two are already handled and the last is common but is rarely + used in selectors. + + (WebCore::collectElementIdentifierHashes): + + Collect attributes. + Remove the unnecessary StyledElement casting. + + (WebCore::collectSimpleSelectorHash): + + Collect attribute selectors. + + (WebCore::chooseSelectorHashesForFilter): + + Pick attributes with high priority for the filter as it is likely a good signal. + 2018-02-27 Sergio Villar Senin [WebVR] Convert VRPlatformDisplayInfo into a class diff --git a/Source/WebCore/css/SelectorFilter.cpp b/Source/WebCore/css/SelectorFilter.cpp index 14303db53aa1..8862c69042bb 100644 --- a/Source/WebCore/css/SelectorFilter.cpp +++ b/Source/WebCore/css/SelectorFilter.cpp @@ -36,22 +36,36 @@ namespace WebCore { // Salt to separate otherwise identical string hashes so a class-selector like .article won't match
elements. -enum { TagNameSalt = 13, IdAttributeSalt = 17, ClassAttributeSalt = 19 }; +enum { TagNameSalt = 13, IdSalt = 17, ClassSalt = 19, AttributeSalt = 23 }; -static inline void collectElementIdentifierHashes(const Element* element, Vector& identifierHashes) +static bool isExcludedAttribute(const AtomicString& name) { - AtomicString tagLowercaseLocalName = element->localName().convertToASCIILowercase(); + return name == HTMLNames::classAttr->localName() || name == HTMLNames::idAttr->localName() || name == HTMLNames::styleAttr->localName(); +} + +static inline void collectElementIdentifierHashes(const Element& element, Vector& identifierHashes) +{ + AtomicString tagLowercaseLocalName = element.localName().convertToASCIILowercase(); identifierHashes.append(tagLowercaseLocalName.impl()->existingHash() * TagNameSalt); - auto& id = element->idForStyleResolution(); + auto& id = element.idForStyleResolution(); if (!id.isNull()) - identifierHashes.append(id.impl()->existingHash() * IdAttributeSalt); - const StyledElement* styledElement = element->isStyledElement() ? static_cast(element) : 0; - if (styledElement && styledElement->hasClass()) { - const SpaceSplitString& classNames = styledElement->classNames(); + identifierHashes.append(id.impl()->existingHash() * IdSalt); + + if (element.hasClass()) { + const SpaceSplitString& classNames = element.classNames(); size_t count = classNames.size(); for (size_t i = 0; i < count; ++i) - identifierHashes.append(classNames[i].impl()->existingHash() * ClassAttributeSalt); + identifierHashes.append(classNames[i].impl()->existingHash() * ClassSalt); + } + + if (element.hasAttributesWithoutUpdate()) { + for (auto& attribute : element.attributesIterator()) { + auto attributeName = element.isHTMLElement() ? attribute.localName() : attribute.localName().convertToASCIILowercase(); + if (isExcludedAttribute(attributeName)) + continue; + identifierHashes.append(attributeName.impl()->existingHash() * AttributeSalt); + } } } @@ -80,7 +94,7 @@ void SelectorFilter::pushParent(Element* parent) ParentStackFrame& parentFrame = m_parentStack.last(); // Mix tags, class names and ids into some sort of weird bouillabaisse. // The filter is used for fast rejection of child and descendant selectors. - collectElementIdentifierHashes(parent, parentFrame.identifierHashes); + collectElementIdentifierHashes(*parent, parentFrame.identifierHashes); size_t count = parentFrame.identifierHashes.size(); for (size_t i = 0; i < count; ++i) m_ancestorIdentifierFilter.add(parentFrame.identifierHashes[i]); @@ -123,6 +137,7 @@ struct CollectedSelectorHashes { HashVector ids; HashVector classes; HashVector tags; + HashVector attributes; }; static inline void collectSimpleSelectorHash(CollectedSelectorHashes& collectedHashes, const CSSSelector& selector) @@ -130,11 +145,11 @@ static inline void collectSimpleSelectorHash(CollectedSelectorHashes& collectedH switch (selector.match()) { case CSSSelector::Id: if (!selector.value().isEmpty()) - collectedHashes.ids.append(selector.value().impl()->existingHash() * IdAttributeSalt); + collectedHashes.ids.append(selector.value().impl()->existingHash() * IdSalt); break; case CSSSelector::Class: if (!selector.value().isEmpty()) - collectedHashes.classes.append(selector.value().impl()->existingHash() * ClassAttributeSalt); + collectedHashes.classes.append(selector.value().impl()->existingHash() * ClassSalt); break; case CSSSelector::Tag: { auto& tagLowercaseLocalName = selector.tagLowercaseLocalName(); @@ -142,6 +157,18 @@ static inline void collectSimpleSelectorHash(CollectedSelectorHashes& collectedH collectedHashes.tags.append(tagLowercaseLocalName.impl()->existingHash() * TagNameSalt); break; } + case CSSSelector::Exact: + case CSSSelector::Set: + case CSSSelector::List: + case CSSSelector::Hyphen: + case CSSSelector::Contain: + case CSSSelector::Begin: + case CSSSelector::End: { + auto attributeName = selector.attributeCanonicalLocalName().convertToASCIILowercase(); + if (!isExcludedAttribute(attributeName)) + collectedHashes.attributes.append(attributeName.impl()->existingHash() * AttributeSalt); + break; + } default: break; } @@ -204,6 +231,8 @@ static SelectorFilter::Hashes chooseSelectorHashesForFilter(const CollectedSelec // There is a limited number of slots. Prefer more specific selector types. if (copyHashes(collectedSelectorHashes.ids)) return resultHashes; + if (copyHashes(collectedSelectorHashes.attributes)) + return resultHashes; if (copyHashes(collectedSelectorHashes.classes)) return resultHashes; if (copyHashes(collectedSelectorHashes.tags)) -- 2.36.0