#ifndef SelectorChecker_h
#define SelectorChecker_h
#include "CSSSelector.h"
#include "Element.h"
#include "SpaceSplitString.h"
#include <wtf/HashSet.h>
#include <wtf/Vector.h>
namespace WebCore {
class CSSSelector;
class Element;
class RenderScrollbar;
class RenderStyle;
class SelectorChecker {
WTF_MAKE_NONCOPYABLE(SelectorChecker);
enum class Match { SelectorMatches, SelectorFailsLocally, SelectorFailsAllSiblings, SelectorFailsCompletely };
enum class MatchType { VirtualPseudoElementOnly, Element };
struct MatchResult {
Match match;
MatchType matchType;
static MatchResult matches(MatchType matchType)
{
return { Match::SelectorMatches, matchType };
}
static MatchResult updateWithMatchType(MatchResult result, MatchType matchType)
{
if (matchType == MatchType::VirtualPseudoElementOnly)
result.matchType = MatchType::VirtualPseudoElementOnly;
return result;
}
static MatchResult fails(Match match)
{
return { match, MatchType::Element };
}
};
public:
enum class Mode : unsigned char {
ResolvingStyle = 0, CollectingRules, CollectingRulesIgnoringVirtualPseudoElements, QueryingRules
};
SelectorChecker(Document&);
struct CheckingContext {
CheckingContext(SelectorChecker::Mode resolvingMode)
: resolvingMode(resolvingMode)
, elementStyle(nullptr)
, pseudoId(NOPSEUDO)
, scrollbar(nullptr)
, scrollbarPart(NoPart)
, scope(nullptr)
{ }
SelectorChecker::Mode resolvingMode;
RenderStyle* elementStyle;
PseudoId pseudoId;
RenderScrollbar* scrollbar;
ScrollbarPart scrollbarPart;
const ContainerNode* scope;
};
struct CheckingContextWithStatus;
bool match(const CSSSelector*, Element*, const CheckingContext&, unsigned& specificity) const;
static bool isCommonPseudoClassSelector(const CSSSelector*);
static bool matchesFocusPseudoClass(const Element*);
static bool checkExactAttribute(const Element*, const CSSSelector*, const QualifiedName& selectorAttributeName, const AtomicStringImpl* value);
enum LinkMatchMask { MatchLink = 1, MatchVisited = 2, MatchAll = MatchLink | MatchVisited };
static unsigned determineLinkMatchType(const CSSSelector*);
private:
MatchResult matchRecursively(const CheckingContextWithStatus&, PseudoIdSet&, unsigned& specificity) const;
bool checkOne(const CheckingContextWithStatus&, PseudoIdSet&, MatchType&, unsigned& specificity) const;
bool matchSelectorList(const CheckingContextWithStatus&, Element&, const CSSSelectorList&, unsigned& specificity) const;
bool checkScrollbarPseudoClass(const CheckingContextWithStatus&, const CSSSelector*) const;
bool m_strictParsing;
bool m_documentIsHTML;
};
inline bool SelectorChecker::isCommonPseudoClassSelector(const CSSSelector* selector)
{
if (selector->match() != CSSSelector::PseudoClass)
return false;
CSSSelector::PseudoClassType pseudoType = selector->pseudoClassType();
return pseudoType == CSSSelector::PseudoClassLink
|| pseudoType == CSSSelector::PseudoClassAnyLink
|| pseudoType == CSSSelector::PseudoClassAnyLinkDeprecated
|| pseudoType == CSSSelector::PseudoClassVisited
|| pseudoType == CSSSelector::PseudoClassFocus;
}
inline bool SelectorChecker::checkExactAttribute(const Element* element, const CSSSelector* selector, const QualifiedName& selectorAttributeName, const AtomicStringImpl* value)
{
if (!element->hasAttributesWithoutUpdate())
return false;
const AtomicString& localName = element->isHTMLElement() ? selector->attributeCanonicalLocalName() : selectorAttributeName.localName();
for (const Attribute& attribute : element->attributesIterator()) {
if (attribute.matches(selectorAttributeName.prefix(), localName, selectorAttributeName.namespaceURI()) && (!value || attribute.value().impl() == value))
return true;
}
return false;
}
}
#endif