static unsigned simpleSelectorFunctionalPseudoClassStaticSpecificity(const CSSSelector& simpleSelector, bool& ok) { if (simpleSelector.match() == CSSSelector::PseudoClass) { CSSSelector::PseudoClassType pseudoClassType = simpleSelector.pseudoClassType(); if (pseudoClassType == CSSSelector::PseudoClassMatches || pseudoClassType == CSSSelector::PseudoClassNthChild || pseudoClassType == CSSSelector::PseudoClassNthLastChild) { const CSSSelectorList* selectorList = simpleSelector.selectorList(); if (!selectorList) { ASSERT_WITH_MESSAGE(pseudoClassType != CSSSelector::PseudoClassMatches, ":matches() should never be created without a valid selector list."); return 0; } const CSSSelector& firstSubselector = *selectorList->first(); unsigned initialSpecificity = staticSpecificityInternal(firstSubselector, ok); if (!ok) return 0; const CSSSelector* subselector = &firstSubselector; while ((subselector = CSSSelectorList::next(subselector))) { unsigned subSelectorSpecificity = staticSpecificityInternal(*subselector, ok); if (initialSpecificity != subSelectorSpecificity) ok = false; if (!ok) return 0; } return initialSpecificity; } } return 0; }
static inline MatchBasedOnRuleHash computeMatchBasedOnRuleHash(const CSSSelector& selector) { if (selector.tagHistory()) return MatchBasedOnRuleHash::None; if (selector.match() == CSSSelector::Tag) { const QualifiedName& tagQualifiedName = selector.tagQName(); const AtomicString& selectorNamespace = tagQualifiedName.namespaceURI(); if (selectorNamespace == starAtom || selectorNamespace == xhtmlNamespaceURI) { if (tagQualifiedName == anyQName()) return MatchBasedOnRuleHash::Universal; return MatchBasedOnRuleHash::ClassC; } return MatchBasedOnRuleHash::None; } if (SelectorChecker::isCommonPseudoClassSelector(&selector)) return MatchBasedOnRuleHash::ClassB; #if ENABLE(SHADOW_DOM) if (selector.match() == CSSSelector::PseudoClass && selector.pseudoClassType() == CSSSelector::PseudoClassHost) return MatchBasedOnRuleHash::ClassB; #endif if (selector.match() == CSSSelector::Id) return MatchBasedOnRuleHash::ClassA; if (selector.match() == CSSSelector::Class) return MatchBasedOnRuleHash::ClassB; return MatchBasedOnRuleHash::None; }
static unsigned simpleSelectorSpecificityInternal(const CSSSelector& simpleSelector, bool isComputingMaximumSpecificity) { ASSERT_WITH_MESSAGE(!simpleSelector.isForPage(), "At the time of this writing, page selectors are not treated as real selectors that are matched. The value computed here only account for real selectors."); switch (simpleSelector.match()) { case CSSSelector::Id: return static_cast<unsigned>(SelectorSpecificityIncrement::ClassA); case CSSSelector::PagePseudoClass: break; case CSSSelector::PseudoClass: if (simpleSelector.pseudoClassType() == CSSSelector::PseudoClassMatches) { ASSERT_WITH_MESSAGE(simpleSelector.selectorList() && simpleSelector.selectorList()->first(), "The parser should never generate a valid selector for an empty :matches()."); if (!isComputingMaximumSpecificity) return 0; return maxSpecificity(*simpleSelector.selectorList()); } if (simpleSelector.pseudoClassType() == CSSSelector::PseudoClassNot) { ASSERT_WITH_MESSAGE(simpleSelector.selectorList() && simpleSelector.selectorList()->first(), "The parser should never generate a valid selector for an empty :not()."); return maxSpecificity(*simpleSelector.selectorList()); } FALLTHROUGH; case CSSSelector::Exact: case CSSSelector::Class: case CSSSelector::Set: case CSSSelector::List: case CSSSelector::Hyphen: case CSSSelector::Contain: case CSSSelector::Begin: case CSSSelector::End: return static_cast<unsigned>(SelectorSpecificityIncrement::ClassB); case CSSSelector::Tag: return (simpleSelector.tagQName().localName() != starAtom) ? static_cast<unsigned>(SelectorSpecificityIncrement::ClassC) : 0; case CSSSelector::PseudoElement: return static_cast<unsigned>(SelectorSpecificityIncrement::ClassC); case CSSSelector::Unknown: return 0; } ASSERT_NOT_REACHED(); return 0; }