Title: [287091] trunk/Source/WebCore
Revision
287091
Author
an...@apple.com
Date
2021-12-15 11:45:12 -0800 (Wed, 15 Dec 2021)

Log Message

[:has() pseudo-class] Use Bloom filter to quickly reject :has() selectors
https://bugs.webkit.org/show_bug.cgi?id=234341

Reviewed by Dean Jackson.

We can dramatically speed up cases where there are many :has rules, most of which don't match their argument
by building a Bloom filter describing the features of the subtree.

* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* css/SelectorChecker.cpp:
(WebCore::SelectorChecker::matchHasPseudoClass const):

Build and cache HasSelectorFilter per element/filter type. It will be constructed only if multiple :has()
selectors are tested for the same element (otherwise the regular match cache is more efficient).
Use it to quickly reject selectors.
Also add a basic inital optimization to bail out if there are no child/sibling elements that could match.

* css/SelectorFilter.cpp:
(WebCore::SelectorFilter::collectElementIdentifierHashes):
(WebCore::SelectorFilter::collectSimpleSelectorHash):
(WebCore::SelectorFilter::collectSelectorHashes):
(WebCore::SelectorFilter::chooseSelectorHashesForFilter):
(WebCore::collectElementIdentifierHashes): Deleted.
(WebCore::collectSimpleSelectorHash): Deleted.
(WebCore::collectSelectorHashes): Deleted.
(WebCore::chooseSelectorHashesForFilter): Deleted.
* css/SelectorFilter.h:
* style/HasSelectorFilter.cpp: Added.
(WebCore::Style::HasSelectorFilter::HasSelectorFilter):
(WebCore::Style::HasSelectorFilter::typeForMatchElement):
(WebCore::Style::HasSelectorFilter::makeKey):

The key consists of the most specific string in the rightmost compound of the selector along with
:hover pseudo class, if any.

(WebCore::Style::HasSelectorFilter::add):

Add an Element to the filter.
The features collected are the same as for the regular selector filter, plus permutations with
:hover pseudo-class if it would match.

* style/HasSelectorFilter.h: Copied from Source/WebCore/style/SelectorMatchingState.h.
(WebCore::Style::HasSelectorFilter::type const):
(WebCore::Style::HasSelectorFilter::reject const):

Add HasSelectorFilter which uses non-counting BloomFilter internally. The size of the filter is 512 bytes.

* style/SelectorMatchingState.h:
(WebCore::Style::makeHasPseudoClassSelectorFilterKey):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (287090 => 287091)


--- trunk/Source/WebCore/ChangeLog	2021-12-15 19:42:28 UTC (rev 287090)
+++ trunk/Source/WebCore/ChangeLog	2021-12-15 19:45:12 UTC (rev 287091)
@@ -1,3 +1,56 @@
+2021-12-15  Antti Koivisto  <an...@apple.com>
+
+        [:has() pseudo-class] Use Bloom filter to quickly reject :has() selectors
+        https://bugs.webkit.org/show_bug.cgi?id=234341
+
+        Reviewed by Dean Jackson.
+
+        We can dramatically speed up cases where there are many :has rules, most of which don't match their argument
+        by building a Bloom filter describing the features of the subtree.
+
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * css/SelectorChecker.cpp:
+        (WebCore::SelectorChecker::matchHasPseudoClass const):
+
+        Build and cache HasSelectorFilter per element/filter type. It will be constructed only if multiple :has()
+        selectors are tested for the same element (otherwise the regular match cache is more efficient).
+        Use it to quickly reject selectors.
+        Also add a basic inital optimization to bail out if there are no child/sibling elements that could match.
+
+        * css/SelectorFilter.cpp:
+        (WebCore::SelectorFilter::collectElementIdentifierHashes):
+        (WebCore::SelectorFilter::collectSimpleSelectorHash):
+        (WebCore::SelectorFilter::collectSelectorHashes):
+        (WebCore::SelectorFilter::chooseSelectorHashesForFilter):
+        (WebCore::collectElementIdentifierHashes): Deleted.
+        (WebCore::collectSimpleSelectorHash): Deleted.
+        (WebCore::collectSelectorHashes): Deleted.
+        (WebCore::chooseSelectorHashesForFilter): Deleted.
+        * css/SelectorFilter.h:
+        * style/HasSelectorFilter.cpp: Added.
+        (WebCore::Style::HasSelectorFilter::HasSelectorFilter):
+        (WebCore::Style::HasSelectorFilter::typeForMatchElement):
+        (WebCore::Style::HasSelectorFilter::makeKey):
+
+        The key consists of the most specific string in the rightmost compound of the selector along with
+        :hover pseudo class, if any.
+
+        (WebCore::Style::HasSelectorFilter::add):
+
+        Add an Element to the filter.
+        The features collected are the same as for the regular selector filter, plus permutations with
+        :hover pseudo-class if it would match.
+
+        * style/HasSelectorFilter.h: Copied from Source/WebCore/style/SelectorMatchingState.h.
+        (WebCore::Style::HasSelectorFilter::type const):
+        (WebCore::Style::HasSelectorFilter::reject const):
+
+        Add HasSelectorFilter which uses non-counting BloomFilter internally. The size of the filter is 512 bytes.
+
+        * style/SelectorMatchingState.h:
+        (WebCore::Style::makeHasPseudoClassSelectorFilterKey):
+
 2021-12-15  Alexey Shvayka  <ashva...@apple.com>
 
         [WebIDL] onselectionchange IDL attribute should not Document-reflect event listeners

Modified: trunk/Source/WebCore/Sources.txt (287090 => 287091)


--- trunk/Source/WebCore/Sources.txt	2021-12-15 19:42:28 UTC (rev 287090)
+++ trunk/Source/WebCore/Sources.txt	2021-12-15 19:45:12 UTC (rev 287091)
@@ -2543,6 +2543,7 @@
 style/ChildChangeInvalidation.cpp
 style/ClassChangeInvalidation.cpp
 style/ElementRuleCollector.cpp
+style/HasSelectorFilter.cpp
 style/IdChangeInvalidation.cpp
 style/InlineTextBoxStyle.cpp
 style/InspectorCSSOMWrappers.cpp

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (287090 => 287091)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-12-15 19:42:28 UTC (rev 287090)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-12-15 19:45:12 UTC (rev 287091)
@@ -5384,6 +5384,7 @@
 		E4E8B4EC216B79E500B8834D /* SystemFontDatabaseCoreText.h in Headers */ = {isa = PBXBuildFile; fileRef = E4E8B4EA216B79E500B8834D /* SystemFontDatabaseCoreText.h */; };
 		E4E8B4F5216B956500B8834D /* FontCascadeDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = E4E8B4F2216B8B6000B8834D /* FontCascadeDescription.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		E4E94D6122FF158A00DD191F /* LegacyLineLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = E4A1AC7822FAFD500017B75B /* LegacyLineLayout.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		E4ED3ECC2768A51D00F17AC8 /* HasSelectorFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = E4ED3ECA2768A51C00F17AC8 /* HasSelectorFilter.h */; };
 		E4F0BE3125712F6E009E7431 /* CaretRectComputation.h in Headers */ = {isa = PBXBuildFile; fileRef = E4F0BE2E25710A75009E7431 /* CaretRectComputation.h */; };
 		E4F38D1B2626F13B007B1064 /* DefaultResourceLoadPriority.h in Headers */ = {isa = PBXBuildFile; fileRef = E4F38D192626F13B007B1064 /* DefaultResourceLoadPriority.h */; };
 		E4F819C626FB4EBF0094E162 /* InlineBoxPainter.h in Headers */ = {isa = PBXBuildFile; fileRef = E4F819C526FB4EBF0094E162 /* InlineBoxPainter.h */; };
@@ -17466,6 +17467,8 @@
 		E4E8B4ED216B79F400B8834D /* SystemFontDatabaseCoreText.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SystemFontDatabaseCoreText.cpp; sourceTree = "<group>"; };
 		E4E8B4F0216B8B5F00B8834D /* FontCascadeDescription.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FontCascadeDescription.cpp; sourceTree = "<group>"; };
 		E4E8B4F2216B8B6000B8834D /* FontCascadeDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FontCascadeDescription.h; sourceTree = "<group>"; };
+		E4ED3ECA2768A51C00F17AC8 /* HasSelectorFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HasSelectorFilter.h; sourceTree = "<group>"; };
+		E4ED3ECD2768A68800F17AC8 /* HasSelectorFilter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HasSelectorFilter.cpp; sourceTree = "<group>"; };
 		E4F0BE2E25710A75009E7431 /* CaretRectComputation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CaretRectComputation.h; sourceTree = "<group>"; };
 		E4F0BE3025710A76009E7431 /* CaretRectComputation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CaretRectComputation.cpp; sourceTree = "<group>"; };
 		E4F38D192626F13B007B1064 /* DefaultResourceLoadPriority.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DefaultResourceLoadPriority.h; sourceTree = "<group>"; };
@@ -31080,6 +31083,8 @@
 				E4A814D31C6DEC4000BF85AC /* ClassChangeInvalidation.h */,
 				FBDB619A16D6032A00BB3394 /* ElementRuleCollector.cpp */,
 				FBDB619E16D6036500BB3394 /* ElementRuleCollector.h */,
+				E4ED3ECD2768A68800F17AC8 /* HasSelectorFilter.cpp */,
+				E4ED3ECA2768A51C00F17AC8 /* HasSelectorFilter.h */,
 				E4A814DD1C7338D100BF85AC /* IdChangeInvalidation.cpp */,
 				E4A814DF1C7338EB00BF85AC /* IdChangeInvalidation.h */,
 				1C0106FE192594DF008A4201 /* InlineTextBoxStyle.cpp */,
@@ -34473,6 +34478,7 @@
 				26EA89A71B4F2B75008C5FD2 /* HashableActionList.h in Headers */,
 				8482B7461198C35400BFB005 /* HashChangeEvent.h in Headers */,
 				A8748BE012CBF2DC001FBA41 /* HashTools.h in Headers */,
+				E4ED3ECC2768A51D00F17AC8 /* HasSelectorFilter.h in Headers */,
 				CD3EEF3D25799FB5006563BB /* HdrMetadataType.h in Headers */,
 				CDA595932146DEC300A84185 /* HEVCUtilities.h in Headers */,
 				CDA595982146DF7800A84185 /* HEVCUtilitiesCocoa.h in Headers */,

Modified: trunk/Source/WebCore/css/SelectorChecker.cpp (287090 => 287091)


--- trunk/Source/WebCore/css/SelectorChecker.cpp	2021-12-15 19:42:28 UTC (rev 287090)
+++ trunk/Source/WebCore/css/SelectorChecker.cpp	2021-12-15 19:45:12 UTC (rev 287091)
@@ -1249,12 +1249,31 @@
 
 bool SelectorChecker::matchHasPseudoClass(CheckingContext& checkingContext, const Element& element, const CSSSelector& hasSelector) const
 {
+    auto matchElement = Style::computeHasPseudoClassMatchElement(hasSelector);
+
+    auto canMatch = [&] {
+        switch (matchElement) {
+        case Style::MatchElement::HasChild:
+        case Style::MatchElement::HasDescendant:
+            return !!element.firstElementChild();
+        case Style::MatchElement::HasSibling:
+        case Style::MatchElement::HasSiblingDescendant:
+            return !!element.nextElementSibling();
+        default:
+            return true;
+        };
+    };
+
+    // See if there are any elements that this :has() selector could match.
+    if (!canMatch())
+        return false;
+
     auto* cache = checkingContext.selectorMatchingState ? &checkingContext.selectorMatchingState->hasPseudoClassMatchCache : nullptr;
 
-    Style::HasPseudoClassMatch* cachedMatch = nullptr;
-    if (cache) {
-        cachedMatch = &cache->add(Style::makeHasPseudoClassCacheKey(hasSelector, element), Style::HasPseudoClassMatch::None).iterator->value;
-        switch (*cachedMatch) {
+    auto checkForCachedMatch = [&]() -> std::optional<bool> {
+        if (!cache)
+            return { };
+        switch (cache->get(Style::makeHasPseudoClassCacheKey(element, hasSelector))) {
         case Style::HasPseudoClassMatch::Matches:
             return true;
         case Style::HasPseudoClassMatch::Fails:
@@ -1263,6 +1282,34 @@
         case Style::HasPseudoClassMatch::None:
             break;
         }
+        return { };
+    };
+
+    // See if we know the result already.
+    if (auto match = checkForCachedMatch())
+        return *match;
+
+    auto filterForElement = [&]() -> Style::HasSelectorFilter* {
+        if (!checkingContext.selectorMatchingState)
+            return nullptr;
+        auto type = Style::HasSelectorFilter::typeForMatchElement(matchElement);
+        if (!type)
+            return nullptr;
+        auto& filtersMap = checkingContext.selectorMatchingState->hasPseudoClassSelectorFilters;
+        auto addResult = filtersMap.add(Style::makeHasPseudoClassFilterKey(element, *type), std::unique_ptr<Style::HasSelectorFilter>());
+        // Only build a filter if the same element gets checked second time with a different selector (misses the match cache).
+        if (addResult.isNewEntry)
+            return nullptr;
+
+        if (!addResult.iterator->value)
+            addResult.iterator->value = makeUnique<Style::HasSelectorFilter>(element, *type);
+        return addResult.iterator->value.get();
+    };
+
+    // Check if the bloom filter rejects this selector
+    if (auto* filter = filterForElement()) {
+        if (filter->reject(hasSelector))
+            return false;
     }
 
     SelectorChecker hasChecker(element.document());
@@ -1283,8 +1330,8 @@
     auto checkDescendants = [&](const Element& descendantRoot) {
         for (auto it = descendantsOfType<Element>(descendantRoot).begin(); it;) {
             auto& descendant = *it;
-            if (cache) {
-                auto key = Style::makeHasPseudoClassCacheKey(hasSelector, descendant);
+            if (cache && descendant.firstElementChild()) {
+                auto key = Style::makeHasPseudoClassCacheKey(descendant, hasSelector);
                 if (cache->get(key) == Style::HasPseudoClassMatch::FailsSubtree) {
                     it.traverseNextSkippingChildren();
                     continue;
@@ -1300,8 +1347,6 @@
     };
 
     auto match = [&] {
-        auto matchElement = Style::computeHasPseudoClassMatchElement(hasSelector);
-
         switch (matchElement) {
         // :has(> .child)
         case Style::MatchElement::HasChild:
@@ -1312,12 +1357,10 @@
             break;
         // :has(.descendant)
         case Style::MatchElement::HasDescendant: {
-            if (!element.firstElementChild())
-                return false;
             if (cache) {
                 // See if we already know this descendant selector doesn't match in this subtree.
                 for (auto* ancestor = element.parentElement(); ancestor; ancestor = ancestor->parentElement()) {
-                    auto key = Style::makeHasPseudoClassCacheKey(hasSelector, *ancestor);
+                    auto key = Style::makeHasPseudoClassCacheKey(*ancestor, hasSelector);
                     if (cache->get(key) == Style::HasPseudoClassMatch::FailsSubtree)
                         return false;
                 }
@@ -1324,6 +1367,7 @@
             }
             if (checkDescendants(element))
                 return true;
+
             break;
         }
         // FIXME: Add a separate case for adjacent combinator.
@@ -1354,16 +1398,17 @@
 
     auto result = match();
 
-    if (cachedMatch) {
-        *cachedMatch = [&] {
-            if (result)
-                return Style::HasPseudoClassMatch::Matches;
-            if (matchedInsideScope)
-                return Style::HasPseudoClassMatch::Fails;
-            return Style::HasPseudoClassMatch::FailsSubtree;
-        }();
-    }
+    auto matchTypeForCache = [&] {
+        if (result)
+            return Style::HasPseudoClassMatch::Matches;
+        if (matchedInsideScope)
+            return Style::HasPseudoClassMatch::Fails;
+        return Style::HasPseudoClassMatch::FailsSubtree;
+    };
 
+    if (cache)
+        cache->add(Style::makeHasPseudoClassCacheKey(element, hasSelector), matchTypeForCache());
+
     return result;
 }
 

Modified: trunk/Source/WebCore/css/SelectorFilter.cpp (287090 => 287091)


--- trunk/Source/WebCore/css/SelectorFilter.cpp	2021-12-15 19:42:28 UTC (rev 287090)
+++ trunk/Source/WebCore/css/SelectorFilter.cpp	2021-12-15 19:45:12 UTC (rev 287091)
@@ -45,7 +45,7 @@
     return name == HTMLNames::classAttr->localName() || name == HTMLNames::idAttr->localName() || name == HTMLNames::styleAttr->localName();
 }
 
-static inline void collectElementIdentifierHashes(const Element& element, Vector<unsigned, 4>& identifierHashes)
+void SelectorFilter::collectElementIdentifierHashes(const Element& element, Vector<unsigned, 4>& identifierHashes)
 {
     AtomString tagLowercaseLocalName = element.localName().convertToASCIILowercase();
     identifierHashes.append(tagLowercaseLocalName.impl()->existingHash() * TagNameSalt);
@@ -134,15 +134,7 @@
     }
 }
 
-struct CollectedSelectorHashes {
-    using HashVector = Vector<unsigned, 8>;
-    HashVector ids;
-    HashVector classes;
-    HashVector tags;
-    HashVector attributes;
-};
-
-static inline void collectSimpleSelectorHash(CollectedSelectorHashes& collectedHashes, const CSSSelector& selector)
+void SelectorFilter::collectSimpleSelectorHash(CollectedSelectorHashes& collectedHashes, const CSSSelector& selector)
 {
     switch (selector.match()) {
     case CSSSelector::Id:
@@ -176,7 +168,7 @@
     }
 }
 
-static CollectedSelectorHashes collectSelectorHashes(const CSSSelector& rightmostSelector)
+auto SelectorFilter::collectSelectorHashes(const CSSSelector& rightmostSelector) -> CollectedSelectorHashes
 {
     CollectedSelectorHashes collectedHashes;
 
@@ -210,9 +202,9 @@
     return collectedHashes;
 }
 
-static SelectorFilter::Hashes chooseSelectorHashesForFilter(const CollectedSelectorHashes& collectedSelectorHashes)
+auto SelectorFilter::chooseSelectorHashesForFilter(const CollectedSelectorHashes& collectedSelectorHashes) -> Hashes
 {
-    SelectorFilter::Hashes resultHashes;
+    Hashes resultHashes;
     unsigned index = 0;
 
     auto addIfNew = [&] (unsigned hash) {

Modified: trunk/Source/WebCore/css/SelectorFilter.h (287090 => 287091)


--- trunk/Source/WebCore/css/SelectorFilter.h	2021-12-15 19:42:28 UTC (rev 287090)
+++ trunk/Source/WebCore/css/SelectorFilter.h	2021-12-15 19:45:12 UTC (rev 287091)
@@ -50,8 +50,21 @@
     bool fastRejectSelector(const Hashes&) const;
     static Hashes collectHashes(const CSSSelector&);
 
+    static void collectElementIdentifierHashes(const Element&, Vector<unsigned, 4>&);
+
+    struct CollectedSelectorHashes {
+        using HashVector = Vector<unsigned, 8>;
+        HashVector ids;
+        HashVector classes;
+        HashVector tags;
+        HashVector attributes;
+    };
+    static void collectSimpleSelectorHash(CollectedSelectorHashes&, const CSSSelector&);
+
 private:
     void initializeParentStack(Element& parent);
+    static CollectedSelectorHashes collectSelectorHashes(const CSSSelector& rightmostSelector);
+    static Hashes chooseSelectorHashesForFilter(const CollectedSelectorHashes&);
 
     struct ParentStackFrame {
         ParentStackFrame() : element(0) { }

Added: trunk/Source/WebCore/style/HasSelectorFilter.cpp (0 => 287091)


--- trunk/Source/WebCore/style/HasSelectorFilter.cpp	                        (rev 0)
+++ trunk/Source/WebCore/style/HasSelectorFilter.cpp	2021-12-15 19:45:12 UTC (rev 287091)
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "HasSelectorFilter.h"
+
+#include "ElementIterator.h"
+#include "RuleFeature.h"
+#include "SelectorFilter.h"
+
+namespace WebCore::Style {
+
+// FIXME: Support additional pseudo-classes.
+static constexpr unsigned HoverSalt = 101;
+
+HasSelectorFilter::HasSelectorFilter(const Element& element, Type type)
+    : m_type(type)
+{
+    switch (type) {
+    case Type::Descendants:
+        for (auto& descendant : descendantsOfType<Element>(element))
+            add(descendant);
+        break;
+    case Type::Children:
+        for (auto& child : childrenOfType<Element>(element))
+            add(child);
+        break;
+    }
+}
+
+auto HasSelectorFilter::typeForMatchElement(MatchElement matchElement) -> std::optional<Type>
+{
+    switch (matchElement) {
+    case MatchElement::HasChild:
+        return Type::Children;
+    case MatchElement::HasDescendant:
+        return Type::Descendants;
+    default:
+        return { };
+    }
+}
+
+auto HasSelectorFilter::makeKey(const CSSSelector& hasSelector) -> Key
+{
+    SelectorFilter::CollectedSelectorHashes hashes;
+    bool hasHoverInCompound = false;
+    for (auto* simpleSelector = &hasSelector; simpleSelector; simpleSelector = simpleSelector->tagHistory()) {
+        if (simpleSelector->match() == CSSSelector::PseudoClass && simpleSelector->pseudoClassType() == CSSSelector::PseudoClassHover)
+            hasHoverInCompound = true;
+        SelectorFilter::collectSimpleSelectorHash(hashes, *simpleSelector);
+        if (!hashes.ids.isEmpty())
+            break;
+        if (simpleSelector->relation() != CSSSelector::Subselector)
+            break;
+    }
+
+    auto pickKey = [&](auto& hashVector) -> Key {
+        if (hashVector.isEmpty())
+            return 0;
+        if (hasHoverInCompound)
+            return hashVector[0] * HoverSalt;
+        return hashVector[0];
+    };
+
+    if (auto key = pickKey(hashes.ids))
+        return key;
+    if (auto key = pickKey(hashes.classes))
+        return key;
+    if (auto key = pickKey(hashes.attributes))
+        return key;
+    return pickKey(hashes.tags);
+}
+
+void HasSelectorFilter::add(const Element& element)
+{
+    Vector<unsigned, 4> elementHashes;
+    SelectorFilter::collectElementIdentifierHashes(element, elementHashes);
+
+    for (auto hash : elementHashes)
+        m_filter.add(hash);
+
+    if (element.hovered()) {
+        for (auto hash : elementHashes)
+            m_filter.add(hash * HoverSalt);
+    }
+}
+
+}

Copied: trunk/Source/WebCore/style/HasSelectorFilter.h (from rev 287090, trunk/Source/WebCore/style/SelectorMatchingState.h) (0 => 287091)


--- trunk/Source/WebCore/style/HasSelectorFilter.h	                        (rev 0)
+++ trunk/Source/WebCore/style/HasSelectorFilter.h	2021-12-15 19:45:12 UTC (rev 287091)
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "CSSSelector.h"
+#include <wtf/BloomFilter.h>
+
+namespace WebCore {
+namespace Style {
+
+enum class MatchElement : uint8_t;
+
+class HasSelectorFilter {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    enum class Type : uint8_t { Children, Descendants };
+    HasSelectorFilter(const Element&, Type);
+
+    Type type() const { return m_type; }
+    static std::optional<Type> typeForMatchElement(MatchElement);
+
+    using Key = unsigned;
+    static Key makeKey(const CSSSelector& hasSelector);
+
+    bool reject(const CSSSelector& hasSelector) const { return reject(makeKey(hasSelector)); }
+    bool reject(Key key) const { return !m_filter.mayContain(key); }
+
+
+private:
+    void add(const Element&);
+
+    const Type m_type;
+    BloomFilter<12> m_filter;
+};
+
+}
+}

Modified: trunk/Source/WebCore/style/SelectorMatchingState.h (287090 => 287091)


--- trunk/Source/WebCore/style/SelectorMatchingState.h	2021-12-15 19:42:28 UTC (rev 287090)
+++ trunk/Source/WebCore/style/SelectorMatchingState.h	2021-12-15 19:45:12 UTC (rev 287091)
@@ -24,23 +24,32 @@
 
 #pragma once
 
+#include "HasSelectorFilter.h"
 #include "SelectorFilter.h"
 #include <wtf/HashMap.h>
 
 namespace WebCore::Style {
 
-using HasPseudoClassCacheKey = std::pair<const CSSSelector*, const Element*>;
+using HasPseudoClassCacheKey = std::pair<const Element*, const CSSSelector*>;
+using HasPseudoClassFilterKey = std::pair<const Element*, uint8_t>;
 
 enum class HasPseudoClassMatch : uint8_t { None, Matches, Fails, FailsSubtree };
 
 struct SelectorMatchingState {
     SelectorFilter selectorFilter;
+
     HashMap<HasPseudoClassCacheKey, HasPseudoClassMatch> hasPseudoClassMatchCache;
+    HashMap<HasPseudoClassFilterKey, std::unique_ptr<HasSelectorFilter>> hasPseudoClassSelectorFilters;
 };
 
-inline HasPseudoClassCacheKey makeHasPseudoClassCacheKey(const CSSSelector& selector, const Element& element)
+inline HasPseudoClassCacheKey makeHasPseudoClassCacheKey(const Element& element, const CSSSelector& selector)
 {
-    return { &selector, &element };
+    return { &element, &selector };
 }
 
+inline HasPseudoClassFilterKey makeHasPseudoClassFilterKey(const Element& element, HasSelectorFilter::Type type)
+{
+    return { &element, static_cast<uint8_t>(type) };
 }
+
+}
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to