bool SelectorChecker::checkOne(const CSSSelector& selector) { switch (selector.match()) { case CSSSelector::Tag: { const AtomicString& localName = selector.tagQName().localName(); return localName == starAtom || localName == m_element.localName(); } case CSSSelector::Class: return m_element.hasClass() && m_element.classNames().contains(selector.value()); case CSSSelector::Id: return m_element.hasID() && m_element.idForStyleResolution() == selector.value(); case CSSSelector::Exact: case CSSSelector::Set: if (anyAttributeMatches(m_element, selector.match(), selector)) { m_matchedAttributeSelector = true; return true; } return false; case CSSSelector::PseudoClass: return checkPseudoClass(selector); // FIXME(sky): Remove pseudo elements completely. case CSSSelector::PseudoElement: case CSSSelector::Unknown: return false; } ASSERT_NOT_REACHED(); return false; }
static bool determineSelectorScopes(const CSSSelectorList& selectorList, HashSet<AtomicStringImpl*>& idScopes, HashSet<AtomicStringImpl*>& classScopes) { for (CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) { CSSSelector* scopeSelector = 0; // This picks the widest scope, not the narrowest, to minimize the number of found scopes. for (CSSSelector* current = selector; current; current = current->tagHistory()) { // Prefer ids over classes. if (current->m_match == CSSSelector::Id) scopeSelector = current; else if (current->m_match == CSSSelector::Class && (!scopeSelector || scopeSelector->m_match != CSSSelector::Id)) scopeSelector = current; CSSSelector::Relation relation = current->relation(); if (relation != CSSSelector::Descendant && relation != CSSSelector::Child && relation != CSSSelector::SubSelector) break; } if (!scopeSelector) return false; ASSERT(scopeSelector->m_match == CSSSelector::Class || scopeSelector->m_match == CSSSelector::Id); if (scopeSelector->m_match == CSSSelector::Id) idScopes.add(scopeSelector->value().impl()); else classScopes.add(scopeSelector->value().impl()); } return true; }
void extractClassIdOrTag(const CSSSelector& selector, Vector<AtomicString>& classes, AtomicString& id, AtomicString& tagName) { if (selector.m_match == CSSSelector::Tag) tagName = selector.tagQName().localName(); else if (selector.m_match == CSSSelector::Id) id = selector.value(); else if (selector.m_match == CSSSelector::Class) classes.append(selector.value()); }
void RuleFeatureSet::extractInvalidationSetFeature(const CSSSelector& selector, InvalidationSetFeatures& features) { if (selector.m_match == CSSSelector::Tag) features.tagName = selector.tagQName().localName(); else if (selector.m_match == CSSSelector::Id) features.id = selector.value(); else if (selector.m_match == CSSSelector::Class) features.classes.append(selector.value()); else if (selector.isAttributeSelector()) features.attributes.append(selector.attribute().localName()); else if (selector.isCustomPseudoElement()) features.customPseudoElement = true; }
void RuleSet::addRule(StyleRule* rule, unsigned selectorIndex, AddRuleFlags addRuleFlags) { RuleData ruleData(rule, selectorIndex, m_ruleCount++, addRuleFlags); collectFeaturesFromRuleData(m_features, ruleData); CSSSelector* selector = ruleData.selector(); if (selector->m_match == CSSSelector::Id) { addToRuleSet(selector->value().impl(), m_idRules, ruleData); return; } if (selector->m_match == CSSSelector::Class) { addToRuleSet(selector->value().impl(), m_classRules, ruleData); return; } if (selector->isCustomPseudoElement()) { addToRuleSet(selector->value().impl(), m_shadowPseudoElementRules, ruleData); return; } #if ENABLE(VIDEO_TRACK) if (selector->pseudoType() == CSSSelector::PseudoCue) { m_cuePseudoRules.append(ruleData); return; } #endif if (SelectorChecker::isCommonPseudoClassSelector(selector)) { switch (selector->pseudoType()) { case CSSSelector::PseudoLink: case CSSSelector::PseudoVisited: case CSSSelector::PseudoAnyLink: m_linkPseudoClassRules.append(ruleData); return; case CSSSelector::PseudoFocus: m_focusPseudoClassRules.append(ruleData); return; default: ASSERT_NOT_REACHED(); } return; } const AtomicString& localName = selector->tag().localName(); if (localName != starAtom) { addToRuleSet(localName.impl(), m_tagRules, ruleData); return; } m_universalRules.append(ruleData); }
static bool anyAttributeMatches(Element& element, CSSSelector::Match match, const CSSSelector& selector) { const QualifiedName& selectorAttr = selector.attribute(); ASSERT(selectorAttr.localName() != starAtom); // Should not be possible from the CSS grammar. // Synchronize the attribute in case it is lazy-computed. element.synchronizeAttribute(selectorAttr.localName()); const AtomicString& selectorValue = selector.value(); bool caseInsensitive = selector.attributeMatchType() == CSSSelector::CaseInsensitive; AttributeCollection attributes = element.attributesWithoutUpdate(); AttributeCollection::iterator end = attributes.end(); for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) { const Attribute& attributeItem = *it; if (!attributeItem.matches(selectorAttr)) continue; if (attributeValueMatches(attributeItem, match, selectorValue, !caseInsensitive)) return true; } return false; }
void RuleSet::addRule(StyleRule* rule, unsigned selectorIndex, AddRuleFlags addRuleFlags) { RuleData ruleData(rule, selectorIndex, m_ruleCount++, addRuleFlags); static const unsigned athostRuleSpecificity = 0x100000; if (addRuleFlags & RuleIsHostRule) ruleData.increaseSpecificity(athostRuleSpecificity); collectFeaturesFromRuleData(m_features, ruleData); CSSSelector* selector = ruleData.selector(); if (selector->m_match == CSSSelector::Id) { addToRuleSet(selector->value().impl(), m_idRules, ruleData); return; } if (selector->m_match == CSSSelector::Class) { addToRuleSet(selector->value().impl(), m_classRules, ruleData); return; } if (selector->isUnknownPseudoElement()) { addToRuleSet(selector->value().impl(), m_shadowPseudoElementRules, ruleData); return; } if (SelectorChecker::isCommonPseudoClassSelector(selector)) { switch (selector->pseudoType()) { case CSSSelector::PseudoLink: case CSSSelector::PseudoVisited: case CSSSelector::PseudoAnyLink: m_linkPseudoClassRules.append(ruleData); return; case CSSSelector::PseudoFocus: m_focusPseudoClassRules.append(ruleData); return; default: ASSERT_NOT_REACHED(); } return; } const AtomicString& localName = selector->tag().localName(); if (localName != starAtom) { addToRuleSet(localName.impl(), m_tagRules, ruleData); return; } m_universalRules.append(ruleData); }
bool RuleFeatureSet::extractInvalidationSetFeature(const CSSSelector& selector, InvalidationSetFeatures& features) { if (selector.match() == CSSSelector::Tag && selector.tagQName().localName() != starAtom) features.tagName = selector.tagQName().localName(); else if (selector.match() == CSSSelector::Id) features.id = selector.value(); else if (selector.match() == CSSSelector::Class) features.classes.append(selector.value()); else if (selector.isAttributeSelector()) features.attributes.append(selector.attribute().localName()); else if (selector.pseudoType() == CSSSelector::PseudoWebKitCustomElement) features.customPseudoElement = true; else if (selector.pseudoType() == CSSSelector::PseudoBefore || selector.pseudoType() == CSSSelector::PseudoAfter) features.hasBeforeOrAfter = true; else return false; return true; }
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: if (selector.tagQName().localName() != starAtom) (*hash++) = selector.tagQName().localName().impl()->existingHash() * TagNameSalt; break; default: break; } }
static bool anyAttributeMatches(const Element& element, CSSSelector::Match match, const CSSSelector& selector) { const QualifiedName& selectorAttr = selector.attribute(); ASSERT(selectorAttr.localName() != starAtom); // Should not be possible from the CSS grammar. if (match == CSSSelector::Set) return element.hasAttribute(selectorAttr); ASSERT(match == CSSSelector::Exact); const AtomicString& selectorValue = selector.value(); const AtomicString& value = element.getAttribute(selectorAttr); if (value.isNull()) return false; if (selector.attributeMatchType() == CSSSelector::CaseInsensitive) return equalIgnoringCase(selectorValue, value); return selectorValue == value; }
void SelectorDataList::execute(const SelectorChecker& selectorChecker, Node* rootNode, Vector<RefPtr<Node> >& matchedElements) const { if (canUseIdLookup(rootNode)) { ASSERT(m_selectors.size() == 1); CSSSelector* selector = m_selectors[0].selector; Element* element = rootNode->treeScope()->getElementById(selector->value()); if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(rootNode))) return; if (selectorChecker.matches(m_selectors[0].selector, element, m_selectors[0].isFastCheckable)) matchedElements.append(element); return; } unsigned selectorCount = m_selectors.size(); Node* n = rootNode->firstChild(); while (n) { if (n->isElementNode()) { Element* element = static_cast<Element*>(n); for (unsigned i = 0; i < selectorCount; ++i) { if (selectorChecker.matches(m_selectors[i].selector, element, m_selectors[i].isFastCheckable)) { matchedElements.append(element); if (firstMatchOnly) return; break; } } if (element->firstChild()) { n = element->firstChild(); continue; } } while (!n->nextSibling()) { n = n->parentNode(); if (n == rootNode) return; } n = n->nextSibling(); } }
static RuleFeatureSet::AttributeRules::SelectorKey makeAttributeSelectorKey(const CSSSelector& selector) { bool caseInsensitive = selector.attributeValueMatchingIsCaseInsensitive(); unsigned matchAndCase = static_cast<unsigned>(selector.match()) << 1 | (caseInsensitive ? 1 : 0); return std::make_pair(selector.attributeCanonicalLocalName().impl(), std::make_pair(selector.value().impl(), matchAndCase)); }