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; }
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; }
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; }
PassRefPtr<StaticNodeList> createSelectorNodeList(PassRefPtr<Node> rootNode, CSSSelector* querySelector) { Vector<RefPtr<Node> > nodes; Document* document = rootNode->document(); AtomicString selectorValue = querySelector->m_value; if (!querySelector->next() && querySelector->m_match == CSSSelector::Id && !document->containsMultipleElementsWithId(selectorValue)) { Element* element = document->getElementById(selectorValue); if (element && (rootNode->isDocumentNode() || element->isDescendantOf(rootNode.get()))) nodes.append(element); } else { CSSStyleSelector::SelectorChecker selectorChecker(document, !document->inCompatMode()); for (Node* n = rootNode->firstChild(); n; n = n->traverseNextNode(rootNode.get())) { if (n->isElementNode()) { Element* element = static_cast<Element*>(n); for (CSSSelector* selector = querySelector; selector; selector = selector->next()) { if (selectorChecker.checkSelector(selector, element)) { nodes.append(n); break; } } } } } return StaticNodeList::adopt(nodes); }
static inline bool includesDisallowedPseudoClass(const CSSSelector& selector) { if (selector.pseudoType() == CSSSelector::PseudoNot) { const CSSSelector* subSelector = selector.selectorList()->first(); return subSelector->match() == CSSSelector::PseudoClass; } return selector.match() == CSSSelector::PseudoClass; }
bool operator()(const CSSSelector& selector) { if (selector.match() == CSSSelector::Tag && selector.tagQName().prefix() != nullAtom && selector.tagQName().prefix() != starAtom) return true; if (selector.isAttributeSelector() && selector.attribute().prefix() != nullAtom && selector.attribute().prefix() != starAtom) return true; return false; }
unsigned CSSSelectorList::length() const { if (!m_selectorArray) return 0; CSSSelector* current = m_selectorArray; while (!current->isLastInSelectorList()) ++current; return (current - m_selectorArray) + 1; }
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 CSSSelectorList::deleteSelectors() { if (!m_selectorArray) return; for (CSSSelector* s = m_selectorArray; !s->isLastInSelectorList(); ++s) s->~CSSSelector(); fastFree(m_selectorArray); }
String CSSStyleRule::selectorText() const { String str; for (CSSSelector* s = selectorList().first(); s; s = CSSSelectorList::next(s)) { if (s != selectorList().first()) str += ", "; str += s->selectorText(); } return str; }
String CSSStyleRule::generateSelectorText() const { StringBuilder builder; for (CSSSelector* s = m_styleRule->selectorList().first(); s; s = CSSSelectorList::next(s)) { if (s != m_styleRule->selectorList().first()) builder.append(", "); builder.append(s->selectorText()); } return builder.toString(); }
void CSSSelectorList::deleteSelectors() { ASSERT(m_selectorArray); bool finished = false; for (CSSSelector* s = m_selectorArray; !finished; ++s) { finished = s->isLastInSelectorList(); s->~CSSSelector(); } WTF::Partitions::fastFree(m_selectorArray); }
static inline bool selectorListContainsUncommonAttributeSelector(const CSSSelector* selector) { CSSSelectorList* selectorList = selector->selectorList(); if (!selectorList) return false; for (CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = CSSSelectorList::next(subSelector)) { if (subSelector->isAttributeSelector()) return true; } return false; }
String CSSPageRule::pageSelectorText() const { String text = "@page"; CSSSelector* selector = selectorList().first(); if (selector) { String pageSpecification = selector->selectorText(); if (!pageSpecification.isEmpty() && pageSpecification != starAtom) text += " " + pageSpecification; } return text; }
void CSSSelectorList::deleteSelectors() { if (!m_selectorArray) return; for (CSSSelector* s = m_selectorArray; ; ++s) { s->~CSSSelector(); if (s->isLastInSelectorList()) break; } fastFree(m_selectorArray); }
String CSSSelectorList::selectorsText() const { StringBuilder result; for (CSSSelector* s = first(); s; s = next(s)) { if (s != first()) result.append(", "); result.append(s->selectorText()); } return result.toString(); }
static inline bool isSelectorMatchingHTMLBasedOnRuleHash(const CSSSelector& selector) { if (selector.tagHistory()) return false; if (selector.match() == CSSSelector::Tag) { const AtomicString& selectorNamespace = selector.tagQName().namespaceURI(); return selectorNamespace == starAtom || selectorNamespace == xhtmlNamespaceURI; } if (SelectorChecker::isCommonPseudoClassSelector(&selector)) return true; return selector.match() == CSSSelector::Id || selector.match() == CSSSelector::Class; }
String CSSStyleRule::selectorText() const { if (m_selector) { String str; for (CSSSelector* s = m_selector; s; s = s->next()) { if (s != m_selector) str += ", "; str += s->selectorText(); } return str; } return String(); }
void CSSSelectorList::deleteSelectors() { if (!m_selectorArray) return; bool finished = false; for (CSSSelector* s = m_selectorArray; !finished; ++s) { finished = s->isLastInSelectorList(); s->~CSSSelector(); } fastFree(m_selectorArray); }
void SelectorFilter::collectIdentifierHashes(const CSSSelector& selector, unsigned* identifierHashes, unsigned maximumIdentifierCount) { unsigned* hash = identifierHashes; unsigned* end = identifierHashes + maximumIdentifierCount; CSSSelector::RelationType relation = selector.relation(); if (selector.relationIsAffectedByPseudoContent()) { // Disable fastRejectSelector. *identifierHashes = 0; return; } // Skip the topmost selector. It is handled quickly by the rule hashes. bool skipOverSubselectors = true; for (const CSSSelector* current = selector.tagHistory(); current; current = current->tagHistory()) { // Only collect identifiers that match ancestors. switch (relation) { case CSSSelector::SubSelector: if (!skipOverSubselectors) collectDescendantSelectorIdentifierHashes(*current, hash); break; case CSSSelector::DirectAdjacent: case CSSSelector::IndirectAdjacent: skipOverSubselectors = true; break; case CSSSelector::ShadowSlot: // Disable fastRejectSelector. *identifierHashes = 0; return; case CSSSelector::Descendant: case CSSSelector::Child: // Fall through. case CSSSelector::ShadowPseudo: case CSSSelector::ShadowDeep: skipOverSubselectors = false; collectDescendantSelectorIdentifierHashes(*current, hash); break; } if (hash == end) return; relation = current->relation(); if (current->relationIsAffectedByPseudoContent()) { // Disable fastRejectSelector. *identifierHashes = 0; return; } } *hash = 0; }
bool SelectorChecker::checkPseudoClass(const CSSSelector& selector) { switch (selector.pseudoType()) { case CSSSelector::PseudoFocus: m_matchedFocusSelector = true; return matchesFocusPseudoClass(m_element); case CSSSelector::PseudoHover: m_matchedHoverSelector = true; return m_element.hovered(); case CSSSelector::PseudoActive: m_matchedActiveSelector = true; return m_element.active(); case CSSSelector::PseudoLang: { AtomicString value = m_element.computeInheritedLanguage(); const AtomicString& argument = selector.argument(); if (value.isEmpty() || !value.startsWith(argument, false)) break; if (value.length() != argument.length() && value[argument.length()] != '-') break; return true; } case CSSSelector::PseudoHost: { // We can only get here if the selector was defined in the right // scope so we don't need to check it. // For empty parameter case, i.e. just :host or :host(). if (!selector.selectorList()) return true; for (const CSSSelector* current = selector.selectorList()->first(); current; current = CSSSelectorList::next(*current)) { if (match(*current)) return true; } return false; } case CSSSelector::PseudoUnknown: case CSSSelector::PseudoNotParsed: case CSSSelector::PseudoUserAgentCustomElement: return false; } ASSERT_NOT_REACHED(); return false; }
void SelectRuleFeatureSet::collectFeaturesFromSelector(const CSSSelector& selector) { m_cssRuleFeatureSet.collectFeaturesFromSelector(selector); switch (selector.pseudoType()) { case CSSSelector::PseudoChecked: setSelectRuleFeature(AffectedSelectorChecked); break; case CSSSelector::PseudoEnabled: setSelectRuleFeature(AffectedSelectorEnabled); break; case CSSSelector::PseudoDisabled: setSelectRuleFeature(AffectedSelectorDisabled); break; case CSSSelector::PseudoIndeterminate: setSelectRuleFeature(AffectedSelectorIndeterminate); break; case CSSSelector::PseudoLink: setSelectRuleFeature(AffectedSelectorLink); break; case CSSSelector::PseudoTarget: setSelectRuleFeature(AffectedSelectorTarget); break; case CSSSelector::PseudoVisited: setSelectRuleFeature(AffectedSelectorVisited); break; default: break; } }
static inline bool isSelectorMatchingHTMLBasedOnRuleHash(const CSSSelector& selector) { if (selector.m_match == CSSSelector::Tag) { const AtomicString& selectorNamespace = selector.tagQName().namespaceURI(); if (selectorNamespace != starAtom && selectorNamespace != xhtmlNamespaceURI) return false; if (selector.relation() == CSSSelector::SubSelector) { ASSERT(selector.tagHistory()); return isSelectorMatchingHTMLBasedOnRuleHash(*selector.tagHistory()); } return true; } if (SelectorChecker::isCommonPseudoClassSelector(selector)) return true; return selector.m_match == CSSSelector::Id || selector.m_match == CSSSelector::Class; }
static unsigned selectorSpecificity(const CSSSelector& firstSimpleSelector, bool isComputingMaximumSpecificity) { unsigned total = simpleSelectorSpecificityInternal(firstSimpleSelector, isComputingMaximumSpecificity); for (const CSSSelector* selector = firstSimpleSelector.tagHistory(); selector; selector = selector->tagHistory()) total = CSSSelector::addSpecificities(total, simpleSelectorSpecificityInternal(*selector, isComputingMaximumSpecificity)); return total; }
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 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; }
static void collectFeaturesFromRuleData(RuleFeatureSet& features, const RuleData& ruleData) { bool foundSiblingSelector = false; for (CSSSelector* selector = ruleData.selector(); selector; selector = selector->tagHistory()) { features.collectFeaturesFromSelector(selector); if (CSSSelectorList* selectorList = selector->selectorList()) { for (CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = CSSSelectorList::next(subSelector)) { if (!foundSiblingSelector && selector->isSiblingSelector()) foundSiblingSelector = true; features.collectFeaturesFromSelector(subSelector); } } else if (!foundSiblingSelector && selector->isSiblingSelector()) foundSiblingSelector = true; } if (foundSiblingSelector) features.siblingRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin())); if (ruleData.containsUncommonAttributeSelector()) features.uncommonAttributeRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin())); }
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 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; }
static bool validateSelector(CSSSelector* selector) { ASSERT(selector); if (!validateSubSelector(selector)) return false; CSSSelector* prevSubSelector = selector; CSSSelector* subSelector = selector->tagHistory(); while (subSelector) { if (prevSubSelector->relation() != CSSSelector::SubSelector) return false; if (!validateSubSelector(subSelector)) return false; prevSubSelector = subSelector; subSelector = subSelector->tagHistory(); } return true; }