Changeset 286180 in webkit


Ignore:
Timestamp:
Nov 26, 2021 7:13:03 AM (8 months ago)
Author:
Antti Koivisto
Message:

[:has() pseudo-class] Don't traverse descendants during selector matching unless needed
https://bugs.webkit.org/show_bug.cgi?id=233520

Reviewed by Alan Bujtas.

It is sufficient to traverse direct children to match something like :has(> foo).

  • css/SelectorChecker.cpp:

(WebCore::SelectorChecker::checkOne const):
(WebCore::SelectorChecker::matchHasPseudoClass const):

Factor into a function.
Compute the match element for the :has() argument and use it to choose what to traverse.

  • css/SelectorChecker.h:
  • style/RuleFeature.cpp:

(WebCore::Style::computeNextMatchElement):
(WebCore::Style::computeHasPseudoClassMatchElement):
(WebCore::Style::computeSubSelectorMatchElement):

Make these free-standing functions and expose computeHasPseudoClassMatchElement.

(WebCore::Style::RuleFeatureSet::computeNextMatchElement): Deleted.
(WebCore::Style::RuleFeatureSet::computeSubSelectorMatchElement): Deleted.

  • style/RuleFeature.h:
Location:
trunk/Source/WebCore
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r286179 r286180  
     12021-11-26  Antti Koivisto  <antti@apple.com>
     2
     3        [:has() pseudo-class] Don't traverse descendants during selector matching unless needed
     4        https://bugs.webkit.org/show_bug.cgi?id=233520
     5
     6        Reviewed by Alan Bujtas.
     7
     8        It is sufficient to traverse direct children to match something like :has(> foo).
     9
     10        * css/SelectorChecker.cpp:
     11        (WebCore::SelectorChecker::checkOne const):
     12        (WebCore::SelectorChecker::matchHasPseudoClass const):
     13
     14        Factor into a function.
     15        Compute the match element for the :has() argument and use it to choose what to traverse.
     16
     17        * css/SelectorChecker.h:
     18        * style/RuleFeature.cpp:
     19        (WebCore::Style::computeNextMatchElement):
     20        (WebCore::Style::computeHasPseudoClassMatchElement):
     21        (WebCore::Style::computeSubSelectorMatchElement):
     22
     23        Make these free-standing functions and expose computeHasPseudoClassMatchElement.
     24
     25        (WebCore::Style::RuleFeatureSet::computeNextMatchElement): Deleted.
     26        (WebCore::Style::RuleFeatureSet::computeSubSelectorMatchElement): Deleted.
     27        * style/RuleFeature.h:
     28
    1292021-11-26  Antti Koivisto  <antti@apple.com>
    230
  • trunk/Source/WebCore/css/SelectorChecker.cpp

    r285610 r286180  
    4444#include "Page.h"
    4545#include "RenderElement.h"
     46#include "RuleFeature.h"
    4647#include "SelectorCheckerTestFunctions.h"
    4748#include "ShadowRoot.h"
     
    862863            }
    863864        case CSSSelector::PseudoClassHas: {
    864             // FIXME: This is the worst possible implementation in terms of performance.
    865             auto checkRelative = [&](auto& elementToCheck) {
    866                 for (auto* subselector = selector.selectorList()->first(); subselector; subselector = CSSSelectorList::next(subselector)) {
    867                     SelectorChecker selectorChecker(element.document());
    868                     CheckingContext selectorCheckingContext(SelectorChecker::Mode::ResolvingStyle);
    869                     selectorCheckingContext.scope = &element;
    870                     if (selectorChecker.match(*subselector, elementToCheck, selectorCheckingContext))
    871                         return true;
    872                 }
    873                 return false;
    874             };
    875             for (auto& descendant : descendantsOfType<Element>(element)) {
    876                 if (checkRelative(descendant))
     865            for (auto* hasSelector = selector.selectorList()->first(); hasSelector; hasSelector = CSSSelectorList::next(hasSelector)) {
     866                if (matchHasPseudoClass(checkingContext, element, *hasSelector))
    877867                    return true;
    878             }
    879             for (auto* sibling = element.nextElementSibling(); sibling; sibling = sibling->nextElementSibling()) {
    880                 if (checkRelative(*sibling))
    881                     return true;
    882                 for (auto& descendant : descendantsOfType<Element>(*sibling)) {
    883                     if (checkRelative(descendant))
    884                         return true;
    885                 }
    886868            }
    887869            return false;
     
    12591241}
    12601242
     1243bool SelectorChecker::matchHasPseudoClass(CheckingContext&, const Element& element, const CSSSelector& hasSelector) const
     1244{
     1245    // FIXME: This is almost the worst possible implementation in terms of performance.
     1246
     1247    SelectorChecker hasChecker(element.document());
     1248
     1249    auto checkRelative = [&](auto& elementToCheck) {
     1250        CheckingContext hasCheckingContext(SelectorChecker::Mode::ResolvingStyle);
     1251        hasCheckingContext.scope = &element;
     1252        return hasChecker.match(hasSelector, elementToCheck, hasCheckingContext);
     1253    };
     1254
     1255    auto matchElement = Style::computeHasPseudoClassMatchElement(hasSelector);
     1256
     1257    switch (matchElement) {
     1258    case Style::MatchElement::HasChild:
     1259        for (auto& child : childrenOfType<Element>(element)) {
     1260            if (checkRelative(child))
     1261                return true;
     1262        }
     1263        break;
     1264    case Style::MatchElement::HasDescendant:
     1265        for (auto& descendant : descendantsOfType<Element>(element)) {
     1266            if (checkRelative(descendant))
     1267                return true;
     1268        }
     1269        break;
     1270    case Style::MatchElement::HasSibling:
     1271        for (auto* sibling = element.nextElementSibling(); sibling; sibling = sibling->nextElementSibling()) {
     1272            if (checkRelative(*sibling))
     1273                return true;
     1274            for (auto& descendant : descendantsOfType<Element>(*sibling)) {
     1275                if (checkRelative(descendant))
     1276                    return true;
     1277            }
     1278        }
     1279        break;
     1280    default:
     1281        ASSERT_NOT_REACHED();
     1282        break;
     1283    }
     1284
     1285    return false;
     1286}
     1287
    12611288bool SelectorChecker::checkScrollbarPseudoClass(const CheckingContext& checkingContext, const Element& element, const CSSSelector& selector) const
    12621289{
  • trunk/Source/WebCore/css/SelectorChecker.h

    r285316 r286180  
    118118    bool checkOne(CheckingContext&, const LocalContext&, MatchType&) const;
    119119    bool matchSelectorList(CheckingContext&, const LocalContext&, const Element&, const CSSSelectorList&) const;
     120    bool matchHasPseudoClass(CheckingContext&, const Element&, const CSSSelector&) const;
    120121
    121122    bool checkScrollbarPseudoClass(const CheckingContext&, const Element&, const CSSSelector&) const;
  • trunk/Source/WebCore/style/RuleFeature.cpp

    r286169 r286180  
    8181}
    8282
    83 MatchElement RuleFeatureSet::computeNextMatchElement(MatchElement matchElement, CSSSelector::RelationType relation)
     83static MatchElement computeNextMatchElement(MatchElement matchElement, CSSSelector::RelationType relation)
    8484{
    8585    if (isHasPseudoClassMatchElement(matchElement))
     
    130130};
    131131
    132 MatchElement RuleFeatureSet::computeSubSelectorMatchElement(MatchElement matchElement, const CSSSelector& selector, const CSSSelector& childSelector)
     132MatchElement computeHasPseudoClassMatchElement(const CSSSelector& hasSelector)
     133{
     134    auto hasMatchElement = MatchElement::Subject;
     135    for (auto* simpleSelector = &hasSelector; simpleSelector->tagHistory(); simpleSelector = simpleSelector->tagHistory())
     136        hasMatchElement = computeNextMatchElement(hasMatchElement, simpleSelector->relation());
     137
     138    if (hasMatchElement == MatchElement::Parent)
     139        return MatchElement::HasChild;
     140
     141    switch (hasMatchElement) {
     142    case MatchElement::Parent:
     143    case MatchElement::Subject:
     144        return MatchElement::HasChild;
     145    case MatchElement::Ancestor:
     146        return MatchElement::HasDescendant;
     147    case MatchElement::IndirectSibling:
     148    case MatchElement::DirectSibling:
     149    case MatchElement::ParentSibling:
     150    case MatchElement::AncestorSibling:
     151    case MatchElement::AnySibling:
     152        return MatchElement::HasSibling;
     153    case MatchElement::HasChild:
     154    case MatchElement::HasDescendant:
     155    case MatchElement::HasSibling:
     156    case MatchElement::Host:
     157        ASSERT_NOT_REACHED();
     158        break;
     159    }
     160    return MatchElement::HasChild;
     161}
     162
     163static MatchElement computeSubSelectorMatchElement(MatchElement matchElement, const CSSSelector& selector, const CSSSelector& childSelector)
    133164{
    134165    if (selector.match() == CSSSelector::PseudoClass) {
     
    142173            return MatchElement::Host;
    143174
    144         if (type == CSSSelector::PseudoClassHas) {
    145             auto hasMatchElement = MatchElement::Subject;
    146             for (auto* simpleSelector = &childSelector; simpleSelector->tagHistory(); simpleSelector = simpleSelector->tagHistory())
    147                 hasMatchElement = computeNextMatchElement(hasMatchElement, simpleSelector->relation());
    148 
    149             if (hasMatchElement == MatchElement::Parent)
    150                 return MatchElement::HasChild;
    151             if (isSiblingOrSubject(hasMatchElement))
    152                 return MatchElement::HasSibling;
    153             return MatchElement::HasDescendant;
    154         }
     175        if (type == CSSSelector::PseudoClassHas)
     176            return computeHasPseudoClassMatchElement(childSelector);
    155177    }
    156178    if (selector.match() == CSSSelector::PseudoElement) {
  • trunk/Source/WebCore/style/RuleFeature.h

    r286169 r286180  
    9393
    9494private:
    95     static MatchElement computeNextMatchElement(MatchElement, CSSSelector::RelationType);
    96     static MatchElement computeSubSelectorMatchElement(MatchElement, const CSSSelector&, const CSSSelector& childSelector);
    97 
    9895    struct SelectorFeatures {
    9996        bool hasSiblingSelector { false };
     
    108105
    109106bool isHasPseudoClassMatchElement(MatchElement);
     107MatchElement computeHasPseudoClassMatchElement(const CSSSelector&);
    110108
    111109} // namespace Style
Note: See TracChangeset for help on using the changeset viewer.