SelectorFilter.cpp [plain text]
#include "config.h"
#include "SelectorFilter.h"
#include "CSSSelector.h"
#include "ShadowRoot.h"
#include "StyledElement.h"
namespace WebCore {
enum { TagNameSalt = 13, IdAttributeSalt = 17, ClassAttributeSalt = 19 };
static inline void collectElementIdentifierHashes(const Element* element, Vector<unsigned, 4>& identifierHashes)
{
AtomicString tagLowercaseLocalName = element->localName().convertToASCIILowercase();
identifierHashes.append(tagLowercaseLocalName.impl()->existingHash() * TagNameSalt);
auto& id = element->idForStyleResolution();
if (!id.isNull())
identifierHashes.append(id.impl()->existingHash() * IdAttributeSalt);
const StyledElement* styledElement = element->isStyledElement() ? static_cast<const StyledElement*>(element) : 0;
if (styledElement && styledElement->hasClass()) {
const SpaceSplitString& classNames = styledElement->classNames();
size_t count = classNames.size();
for (size_t i = 0; i < count; ++i)
identifierHashes.append(classNames[i].impl()->existingHash() * ClassAttributeSalt);
}
}
bool SelectorFilter::parentStackIsConsistent(const ContainerNode* parentNode) const
{
if (!parentNode || is<Document>(parentNode) || is<ShadowRoot>(parentNode))
return m_parentStack.isEmpty();
return !m_parentStack.isEmpty() && m_parentStack.last().element == parentNode;
}
void SelectorFilter::pushParentStackFrame(Element* parent)
{
ASSERT(m_parentStack.isEmpty() || m_parentStack.last().element == parent->parentElement());
ASSERT(!m_parentStack.isEmpty() || !parent->parentElement());
m_parentStack.append(ParentStackFrame(parent));
ParentStackFrame& parentFrame = m_parentStack.last();
collectElementIdentifierHashes(parent, parentFrame.identifierHashes);
size_t count = parentFrame.identifierHashes.size();
for (size_t i = 0; i < count; ++i)
m_ancestorIdentifierFilter.add(parentFrame.identifierHashes[i]);
}
void SelectorFilter::popParentStackFrame()
{
ASSERT(!m_parentStack.isEmpty());
const ParentStackFrame& parentFrame = m_parentStack.last();
size_t count = parentFrame.identifierHashes.size();
for (size_t i = 0; i < count; ++i)
m_ancestorIdentifierFilter.remove(parentFrame.identifierHashes[i]);
m_parentStack.removeLast();
if (m_parentStack.isEmpty()) {
ASSERT(m_ancestorIdentifierFilter.likelyEmpty());
m_ancestorIdentifierFilter.clear();
}
}
void SelectorFilter::pushParent(Element* parent)
{
pushParentStackFrame(parent);
}
static inline void collectDescendantSelectorIdentifierHashes(const CSSSelector* selector, unsigned*& hash)
{
switch (selector->match()) {
case CSSSelector::Id:
if (!selector->value().isEmpty())
(*hash++) = selector->value().impl()->existingHash() * IdAttributeSalt;
break;
case CSSSelector::Class:
if (!selector->value().isEmpty())
(*hash++) = selector->value().impl()->existingHash() * ClassAttributeSalt;
break;
case CSSSelector::Tag: {
const AtomicString& tagLowercaseLocalName = selector->tagLowercaseLocalName();
if (tagLowercaseLocalName != starAtom)
(*hash++) = tagLowercaseLocalName.impl()->existingHash() * TagNameSalt;
break;
}
default:
break;
}
}
void SelectorFilter::collectIdentifierHashes(const CSSSelector* selector, unsigned* identifierHashes, unsigned maximumIdentifierCount)
{
unsigned* hash = identifierHashes;
unsigned* end = identifierHashes + maximumIdentifierCount;
CSSSelector::Relation relation = selector->relation();
bool skipOverSubselectors = true;
for (selector = selector->tagHistory(); selector; selector = selector->tagHistory()) {
switch (relation) {
case CSSSelector::SubSelector:
if (!skipOverSubselectors)
collectDescendantSelectorIdentifierHashes(selector, hash);
break;
case CSSSelector::DirectAdjacent:
case CSSSelector::IndirectAdjacent:
case CSSSelector::ShadowDescendant:
skipOverSubselectors = true;
break;
case CSSSelector::Descendant:
case CSSSelector::Child:
skipOverSubselectors = false;
collectDescendantSelectorIdentifierHashes(selector, hash);
break;
}
if (hash == end)
return;
relation = selector->relation();
}
*hash = 0;
}
}