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 checkOneSelector(const CSSSelector& selector, const Vector<RawPtr<Node>, 32>& siblings, int nth) { Element* element = toElement(siblings[nth]); SelectorChecker selectorChecker(element->document(), SelectorChecker::QueryingRules); SelectorChecker::SelectorCheckingContext context(selector, element); return selectorChecker.match(context); }
static inline bool checkOneSelector(const CSSSelector& selector, const WillBeHeapVector<RawPtrWillBeMember<Node>, 32>& siblings, int nth) { Element* element = toElement(siblings[nth]); SelectorChecker selectorChecker(element->document(), SelectorChecker::CollectingCSSRules); SelectorChecker::SelectorCheckingContext context(selector, element, SelectorChecker::VisitedMatchEnabled); ShadowDOMSiblingTraversalStrategy strategy(siblings, nth); return selectorChecker.match(context, strategy) == SelectorChecker::SelectorMatches; }
inline bool SelectorDataList::selectorMatches(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const { SelectorChecker selectorChecker(element.document()); SelectorChecker::CheckingContext selectorCheckingContext(SelectorChecker::Mode::QueryingRules); selectorCheckingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode; unsigned ignoredSpecificity; return selectorChecker.match(*selectorData.selector, element, selectorCheckingContext, ignoredSpecificity); }
inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData, const ContainerNode* scope, SelectorChecker::ContextFlags contextFlags) { SelectorChecker selectorChecker(m_context.element()->document(), m_mode); SelectorChecker::SelectorCheckingContext context(ruleData.selector(), m_context.element()); context.elementStyle = m_style.get(); context.scope = scope; context.contextFlags = contextFlags; return selectorChecker.match(context); }
inline bool SelectorDataList::selectorMatches(const CSSSelector& selector, Element& element, const ContainerNode& rootNode) const { SelectorChecker selectorChecker(SelectorChecker::QueryingRules); SelectorChecker::SelectorCheckingContext selectorCheckingContext(&element, SelectorChecker::VisitedMatchDisabled); selectorCheckingContext.selector = &selector; selectorCheckingContext.scope = !rootNode.isDocumentNode() ? &rootNode : nullptr; if (selectorCheckingContext.scope) selectorCheckingContext.scopeContainsLastMatchedElement = true; return selectorChecker.match(selectorCheckingContext); }
inline bool SelectorDataList::selectorMatches(const CSSSelector& selector, Element& element, const ContainerNode& rootNode) const { SelectorChecker selectorChecker(element.document(), SelectorChecker::QueryingRules); SelectorChecker::SelectorCheckingContext selectorCheckingContext(selector, &element, SelectorChecker::VisitedMatchDisabled); selectorCheckingContext.behaviorAtBoundary = SelectorChecker::StaysWithinTreeScope; selectorCheckingContext.scope = !rootNode.isDocumentNode() ? &rootNode : 0; if (selectorCheckingContext.scope) selectorCheckingContext.behaviorAtBoundary = static_cast<SelectorChecker::BehaviorAtBoundary>(SelectorChecker::StaysWithinTreeScope | SelectorChecker::ScopeContainsLastMatchedElement); return selectorChecker.match(selectorCheckingContext, DOMSiblingTraversalStrategy()) == SelectorChecker::SelectorMatches; }
inline Element* SelectorDataList::selectorClosest(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const { SelectorChecker selectorChecker(element.document()); SelectorChecker::CheckingContext selectorCheckingContext(SelectorChecker::Mode::QueryingRules); selectorCheckingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode; Element* currentNode = &element; unsigned ignoredSpecificity; if (!selectorChecker.match(selectorData.selector, currentNode, selectorCheckingContext, ignoredSpecificity)) return nullptr; return currentNode; }
bool SelectorDataList::matches(Element* targetElement) const { ASSERT(targetElement); SelectorChecker selectorChecker(targetElement->document(), SelectorChecker::ResolvingStyle); unsigned selectorCount = m_selectors.size(); for (unsigned i = 0; i < selectorCount; ++i) { if (selectorChecker.matches(m_selectors[i].selector, targetElement, m_selectors[i].isFastCheckable)) return true; } return false; }
inline bool SelectorDataList::selectorMatches(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const { if (selectorData.isFastCheckable && !element.isSVGElement()) { SelectorCheckerFastPath selectorCheckerFastPath(selectorData.selector, &element); if (!selectorCheckerFastPath.matchesRightmostSelector(SelectorChecker::VisitedMatchDisabled)) return false; return selectorCheckerFastPath.matches(); } SelectorChecker selectorChecker(element.document(), SelectorChecker::QueryingRules); SelectorChecker::SelectorCheckingContext selectorCheckingContext(selectorData.selector, &element, SelectorChecker::VisitedMatchDisabled); selectorCheckingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode; return selectorChecker.match(selectorCheckingContext); }
inline bool SelectorDataList::selectorMatches(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const { if (selectorData.isFastCheckable && !element.isSVGElement()) { SelectorCheckerFastPath selectorCheckerFastPath(selectorData.selector, element); if (!selectorCheckerFastPath.matchesRightmostSelector(SelectorChecker::VisitedMatchDisabled)) return false; return selectorCheckerFastPath.matches(); } SelectorChecker selectorChecker(element.document(), SelectorChecker::QueryingRules); SelectorChecker::SelectorCheckingContext selectorCheckingContext(selectorData.selector, &element, SelectorChecker::VisitedMatchDisabled); selectorCheckingContext.behaviorAtBoundary = SelectorChecker::StaysWithinTreeScope; selectorCheckingContext.scope = !rootNode.isDocumentNode() ? &rootNode : 0; return selectorChecker.match(selectorCheckingContext, DOMSiblingTraversalStrategy()) == SelectorChecker::SelectorMatches; }
inline bool SelectorDataList::selectorMatches(const SelectorData& selectorData, Element* element, const Node* rootNode) const { if (selectorData.isFastCheckable && !element->isSVGElement()) { SelectorCheckerFastPath selectorCheckerFastPath(selectorData.selector, element); if (!selectorCheckerFastPath.matchesRightmostSelector(SelectorChecker::VisitedMatchDisabled)) return false; return selectorCheckerFastPath.matches(); } SelectorChecker selectorChecker(element->document(), SelectorChecker::QueryingRules); SelectorChecker::SelectorCheckingContext selectorCheckingContext(selectorData.selector, element, SelectorChecker::VisitedMatchDisabled); selectorCheckingContext.behaviorAtBoundary = SelectorChecker::StaysWithinTreeScope; selectorCheckingContext.scope = !rootNode->isDocumentNode() && rootNode->isContainerNode() ? toContainerNode(rootNode) : 0; PseudoId ignoreDynamicPseudo = NOPSEUDO; return selectorChecker.match(selectorCheckingContext, ignoreDynamicPseudo) == SelectorChecker::SelectorMatches; }
inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData, const ContainerNode* scope, SelectorChecker::MatchResult* result) { SelectorChecker selectorChecker(m_context.element()->document(), m_mode); SelectorChecker::SelectorCheckingContext context(ruleData.selector(), m_context.element(), SelectorChecker::VisitedMatchEnabled); context.elementStyle = m_style.get(); context.scope = scope; context.pseudoId = m_pseudoStyleRequest.pseudoId; context.scrollbar = m_pseudoStyleRequest.scrollbar; context.scrollbarPart = m_pseudoStyleRequest.scrollbarPart; context.isUARule = m_matchingUARules; context.scopeContainsLastMatchedElement = m_scopeContainsLastMatchedElement; SelectorChecker::Match match = selectorChecker.match(context, DOMSiblingTraversalStrategy(), result); if (match != SelectorChecker::SelectorMatches) return false; if (m_pseudoStyleRequest.pseudoId != NOPSEUDO && m_pseudoStyleRequest.pseudoId != result->dynamicPseudo) return false; return true; }
void SelectorDataList::execute(Node* rootNode, Vector<RefPtr<Node> >& matchedElements) const { SelectorChecker selectorChecker(rootNode->document(), SelectorChecker::QueryingRules); if (canUseIdLookup(rootNode)) { ASSERT(m_selectors.size() == 1); const 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(); } }
inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData, const ContainerNode* scope, PseudoId& dynamicPseudo, SelectorChecker::BehaviorAtBoundary behaviorAtBoundary) { // They can't match because the fast path uses a pool of tag/class/ids, collected from // elements in that tree and those will never match the host, since it's in a different pool. // So when adding scoped rules to RuleSet, RuleCanUseFastCheckSelector is not used. if (ruleData.hasFastCheckableSelector()) { // We know this selector does not include any pseudo elements. if (m_pseudoStyleRequest.pseudoId != NOPSEUDO) return false; // We know a sufficiently simple single part selector matches simply because we found it from the rule hash. // This is limited to HTML only so we don't need to check the namespace. ASSERT(m_context.element()); if (ruleData.hasRightmostSelectorMatchingHTMLBasedOnRuleHash() && m_context.element()->isHTMLElement()) { if (!ruleData.hasMultipartSelector()) return true; } if (ruleData.selector()->m_match == CSSSelector::Tag && !SelectorChecker::tagMatches(*m_context.element(), ruleData.selector()->tagQName())) return false; SelectorCheckerFastPath selectorCheckerFastPath(ruleData.selector(), *m_context.element()); if (!selectorCheckerFastPath.matchesRightmostAttributeSelector()) return false; return selectorCheckerFastPath.matches(); } // Slow path. SelectorChecker selectorChecker(m_context.element()->document(), m_mode); SelectorChecker::SelectorCheckingContext context(ruleData.selector(), m_context.element(), SelectorChecker::VisitedMatchEnabled); context.elementStyle = m_style.get(); context.scope = scope; context.pseudoId = m_pseudoStyleRequest.pseudoId; context.scrollbar = m_pseudoStyleRequest.scrollbar; context.scrollbarPart = m_pseudoStyleRequest.scrollbarPart; context.behaviorAtBoundary = behaviorAtBoundary; SelectorChecker::Match match = selectorChecker.match(context, dynamicPseudo, DOMSiblingTraversalStrategy()); if (match != SelectorChecker::SelectorMatches) return false; if (m_pseudoStyleRequest.pseudoId != NOPSEUDO && m_pseudoStyleRequest.pseudoId != dynamicPseudo) return false; return true; }
inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData, const ContainerNode* scope, PseudoId& dynamicPseudo) { const StyleResolver::State& state = m_state; if (ruleData.hasFastCheckableSelector()) { // We know this selector does not include any pseudo elements. if (m_pseudoStyleRequest.pseudoId != NOPSEUDO) return false; // We know a sufficiently simple single part selector matches simply because we found it from the rule hash. // This is limited to HTML only so we don't need to check the namespace. if (ruleData.hasRightmostSelectorMatchingHTMLBasedOnRuleHash() && state.element()->isHTMLElement()) { if (!ruleData.hasMultipartSelector()) return true; } if (ruleData.selector()->m_match == CSSSelector::Tag && !SelectorChecker::tagMatches(state.element(), ruleData.selector()->tagQName())) return false; SelectorCheckerFastPath selectorCheckerFastPath(ruleData.selector(), state.element()); if (!selectorCheckerFastPath.matchesRightmostAttributeSelector()) return false; return selectorCheckerFastPath.matches(); } // Slow path. SelectorChecker selectorChecker(document(), m_mode); SelectorChecker::SelectorCheckingContext context(ruleData.selector(), state.element(), SelectorChecker::VisitedMatchEnabled); context.elementStyle = state.style(); context.scope = scope; context.pseudoId = m_pseudoStyleRequest.pseudoId; context.scrollbar = m_pseudoStyleRequest.scrollbar; context.scrollbarPart = m_pseudoStyleRequest.scrollbarPart; context.behaviorAtBoundary = m_behaviorAtBoundary; SelectorChecker::Match match = selectorChecker.match(context, dynamicPseudo); if (match != SelectorChecker::SelectorMatches) return false; if (m_pseudoStyleRequest.pseudoId != NOPSEUDO && m_pseudoStyleRequest.pseudoId != dynamicPseudo) return false; return true; }
void ElementRuleCollector::matchHostPseudoClassRules(MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange) { ASSERT(m_element.shadowRoot()); matchRequest.treeContextOrdinal++; auto& shadowAuthorStyle = m_element.shadowRoot()->styleScope().resolver().ruleSets().authorStyle(); auto& shadowHostRules = shadowAuthorStyle.hostPseudoClassRules(); if (shadowHostRules.isEmpty()) return; SelectorChecker::CheckingContext context(m_mode); SelectorChecker selectorChecker(m_element.document()); for (auto& ruleData : shadowHostRules) { if (ruleData.rule()->properties().isEmpty() && !matchRequest.includeEmptyRules) continue; auto& selector = *ruleData.selector(); unsigned specificity = 0; if (!selectorChecker.matchHostPseudoClass(selector, m_element, context, specificity)) continue; addMatchedRule(ruleData, specificity, matchRequest.treeContextOrdinal, ruleRange); } }
PassRefPtr<Element> SelectorQuery::queryFirst(Node* rootNode) const { SelectorChecker selectorChecker(rootNode->document()); selectorChecker.setMode(SelectorChecker::QueryingRules); return m_selectors.queryFirst(selectorChecker, rootNode); }
bool SelectorQuery::matches(Element* element) const { SelectorChecker selectorChecker(element->document()); return m_selectors.matches(selectorChecker, element); }
inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData, unsigned& specificity) { // We know a sufficiently simple single part selector matches simply because we found it from the rule hash when filtering the RuleSet. // This is limited to HTML only so we don't need to check the namespace (because of tag name match). MatchBasedOnRuleHash matchBasedOnRuleHash = ruleData.matchBasedOnRuleHash(); if (matchBasedOnRuleHash != MatchBasedOnRuleHash::None && m_element.isHTMLElement()) { ASSERT_WITH_MESSAGE(m_pseudoStyleRequest.pseudoId == NOPSEUDO, "If we match based on the rule hash while collecting for a particular pseudo element ID, we would add incorrect rules for that pseudo element ID. We should never end in ruleMatches() with a pseudo element if the ruleData cannot match any pseudo element."); switch (matchBasedOnRuleHash) { case MatchBasedOnRuleHash::None: ASSERT_NOT_REACHED(); break; case MatchBasedOnRuleHash::Universal: specificity = 0; break; case MatchBasedOnRuleHash::ClassA: specificity = static_cast<unsigned>(SelectorSpecificityIncrement::ClassA); break; case MatchBasedOnRuleHash::ClassB: specificity = static_cast<unsigned>(SelectorSpecificityIncrement::ClassB); break; case MatchBasedOnRuleHash::ClassC: specificity = static_cast<unsigned>(SelectorSpecificityIncrement::ClassC); break; } return true; } #if ENABLE(CSS_SELECTOR_JIT) void* compiledSelectorChecker = ruleData.compiledSelectorCodeRef().code().executableAddress(); if (!compiledSelectorChecker && ruleData.compilationStatus() == SelectorCompilationStatus::NotCompiled) { JSC::VM& vm = m_element.document().scriptExecutionContext()->vm(); SelectorCompilationStatus compilationStatus; JSC::MacroAssemblerCodeRef compiledSelectorCodeRef; compilationStatus = SelectorCompiler::compileSelector(ruleData.selector(), &vm, SelectorCompiler::SelectorContext::RuleCollector, compiledSelectorCodeRef); ruleData.setCompiledSelector(compilationStatus, compiledSelectorCodeRef); compiledSelectorChecker = ruleData.compiledSelectorCodeRef().code().executableAddress(); } if (compiledSelectorChecker && ruleData.compilationStatus() == SelectorCompilationStatus::SimpleSelectorChecker) { SelectorCompiler::RuleCollectorSimpleSelectorChecker selectorChecker = SelectorCompiler::ruleCollectorSimpleSelectorCheckerFunction(compiledSelectorChecker, ruleData.compilationStatus()); #if !ASSERT_MSG_DISABLED unsigned ignoreSpecificity; ASSERT_WITH_MESSAGE(!selectorChecker(&m_element, &ignoreSpecificity) || m_pseudoStyleRequest.pseudoId == NOPSEUDO, "When matching pseudo elements, we should never compile a selector checker without context unless it cannot match anything."); #endif #if CSS_SELECTOR_JIT_PROFILING ruleData.compiledSelectorUsed(); #endif bool selectorMatches = selectorChecker(&m_element, &specificity); if (selectorMatches && ruleData.containsUncommonAttributeSelector()) m_didMatchUncommonAttributeSelector = true; return selectorMatches; } #endif // ENABLE(CSS_SELECTOR_JIT) SelectorChecker::CheckingContext context(m_mode); context.pseudoId = m_pseudoStyleRequest.pseudoId; context.scrollbar = m_pseudoStyleRequest.scrollbar; context.scrollbarPart = m_pseudoStyleRequest.scrollbarPart; bool selectorMatches; #if ENABLE(CSS_SELECTOR_JIT) if (compiledSelectorChecker) { ASSERT(ruleData.compilationStatus() == SelectorCompilationStatus::SelectorCheckerWithCheckingContext); SelectorCompiler::RuleCollectorSelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::ruleCollectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, ruleData.compilationStatus()); #if CSS_SELECTOR_JIT_PROFILING ruleData.compiledSelectorUsed(); #endif selectorMatches = selectorChecker(&m_element, &context, &specificity); } else #endif // ENABLE(CSS_SELECTOR_JIT) { auto* selector = ruleData.selector(); if (m_isMatchingSlottedPseudoElements) { selector = findSlottedPseudoElementSelector(ruleData.selector()); if (!selector) return false; } // Slow path. SelectorChecker selectorChecker(m_element.document()); selectorMatches = selectorChecker.match(*selector, m_element, context, specificity); } if (ruleData.containsUncommonAttributeSelector()) { if (selectorMatches || context.pseudoIDSet) m_didMatchUncommonAttributeSelector = true; } m_matchedPseudoElementIds.merge(context.pseudoIDSet); m_styleRelations.appendVector(context.styleRelations); return selectorMatches; }
inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData, PseudoId& dynamicPseudo) { bool fastCheckableSelector = ruleData.hasFastCheckableSelector(); if (fastCheckableSelector) { // We know this selector does not include any pseudo elements. if (m_pseudoStyleRequest.pseudoId != NOPSEUDO) return false; // We know a sufficiently simple single part selector matches simply because we found it from the rule hash. // This is limited to HTML only so we don't need to check the namespace. if (ruleData.hasRightmostSelectorMatchingHTMLBasedOnRuleHash() && m_element.isHTMLElement()) { if (!ruleData.hasMultipartSelector()) return true; } } #if ENABLE(CSS_SELECTOR_JIT) void* compiledSelectorChecker = ruleData.compiledSelectorCodeRef().code().executableAddress(); if (!compiledSelectorChecker && ruleData.compilationStatus() == SelectorCompilationStatus::NotCompiled) { JSC::VM* vm = m_element.document().scriptExecutionContext()->vm(); SelectorCompilationStatus compilationStatus; JSC::MacroAssemblerCodeRef compiledSelectorCodeRef; compilationStatus = SelectorCompiler::compileSelector(ruleData.selector(), vm, SelectorCompiler::SelectorContext::RuleCollector, compiledSelectorCodeRef); ruleData.setCompiledSelector(compilationStatus, compiledSelectorCodeRef); compiledSelectorChecker = ruleData.compiledSelectorCodeRef().code().executableAddress(); } if (compiledSelectorChecker) { if (m_pseudoStyleRequest.pseudoId != NOPSEUDO) return false; if (ruleData.compilationStatus() == SelectorCompilationStatus::SimpleSelectorChecker) { SelectorCompiler::SimpleSelectorChecker selectorChecker = SelectorCompiler::simpleSelectorCheckerFunction(compiledSelectorChecker, ruleData.compilationStatus()); return selectorChecker(&m_element); } ASSERT(ruleData.compilationStatus() == SelectorCompilationStatus::SelectorCheckerWithCheckingContext); SelectorCompiler::SelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::selectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, ruleData.compilationStatus()); SelectorCompiler::CheckingContext context; context.elementStyle = m_style; context.resolvingMode = m_mode; return selectorChecker(&m_element, &context); } #endif // ENABLE(CSS_SELECTOR_JIT) if (fastCheckableSelector) { if (ruleData.selector()->m_match == CSSSelector::Tag && !SelectorChecker::tagMatches(&m_element, ruleData.selector()->tagQName())) return false; SelectorCheckerFastPath selectorCheckerFastPath(ruleData.selector(), &m_element); if (!selectorCheckerFastPath.matchesRightmostAttributeSelector()) return false; return selectorCheckerFastPath.matches(); } // Slow path. SelectorChecker selectorChecker(m_element.document(), m_mode); SelectorChecker::SelectorCheckingContext context(ruleData.selector(), &m_element, SelectorChecker::VisitedMatchEnabled); context.elementStyle = m_style; context.pseudoId = m_pseudoStyleRequest.pseudoId; context.scrollbar = m_pseudoStyleRequest.scrollbar; context.scrollbarPart = m_pseudoStyleRequest.scrollbarPart; if (!selectorChecker.match(context, dynamicPseudo)) return false; if (m_pseudoStyleRequest.pseudoId != NOPSEUDO && m_pseudoStyleRequest.pseudoId != dynamicPseudo) return false; return true; }
PassRefPtr<NodeList> SelectorQuery::queryAll(Node* rootNode) const { SelectorChecker selectorChecker(rootNode->document(), !rootNode->document()->inQuirksMode()); selectorChecker.setMode(SelectorChecker::QueryingRules); return m_selectors.queryAll(selectorChecker, rootNode); }