void SelectorDataList::execute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const { if (!canUseFastQuery(rootNode)) { if (m_needsUpdatedDistribution) rootNode.updateDistribution(); if (m_crossesTreeBoundary) { executeSlowTraversingShadowTree<SelectorQueryTrait>(rootNode, output); } else { executeSlow<SelectorQueryTrait>(rootNode, output); } return; } ASSERT(m_selectors.size() == 1); const CSSSelector& selector = *m_selectors[0]; const CSSSelector& firstSelector = selector; // Fast path for querySelector*('#id'), querySelector*('tag#id'). if (const CSSSelector* idSelector = selectorForIdLookup(firstSelector)) { const AtomicString& idToMatch = idSelector->value(); if (rootNode.treeScope().containsMultipleElementsWithId(idToMatch)) { const WillBeHeapVector<RawPtrWillBeMember<Element>>& elements = rootNode.treeScope().getAllElementsById(idToMatch); size_t count = elements.size(); for (size_t i = 0; i < count; ++i) { Element& element = *elements[i]; if (!(isTreeScopeRoot(rootNode) || element.isDescendantOf(&rootNode))) continue; if (selectorMatches(selector, element, rootNode)) { SelectorQueryTrait::appendElement(output, element); if (SelectorQueryTrait::shouldOnlyMatchFirstElement) return; } } return; } Element* element = rootNode.treeScope().getElementById(idToMatch); if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode))) return; if (selectorMatches(selector, *element, rootNode)) SelectorQueryTrait::appendElement(output, *element); return; } if (!firstSelector.tagHistory()) { // Fast path for querySelector*('.foo'), and querySelector*('div'). switch (firstSelector.match()) { case CSSSelector::Class: collectElementsByClassName<SelectorQueryTrait>(rootNode, firstSelector.value(), output); return; case CSSSelector::Tag: collectElementsByTagName<SelectorQueryTrait>(rootNode, firstSelector.tagQName(), output); return; default: break; // If we need another fast path, add here. } } findTraverseRootsAndExecute<SelectorQueryTrait>(rootNode, output); }
ALWAYS_INLINE void SelectorDataList::executeFastPathForIdSelector(const Node* rootNode, const SelectorData& selectorData, const CSSSelector* idSelector, typename SelectorQueryTrait::OutputType& output) const { ASSERT(m_selectors.size() == 1); ASSERT(idSelector); const AtomicString& idToMatch = idSelector->value(); if (UNLIKELY(rootNode->treeScope()->containsMultipleElementsWithId(idToMatch))) { const Vector<Element*>* elements = rootNode->treeScope()->getAllElementsById(idToMatch); ASSERT(elements); size_t count = elements->size(); bool rootNodeIsTreeScopeRoot = isTreeScopeRoot(rootNode); for (size_t i = 0; i < count; ++i) { Element* element = elements->at(i); if ((rootNodeIsTreeScopeRoot || element->isDescendantOf(rootNode)) && selectorMatches(selectorData, element, rootNode)) { SelectorQueryTrait::appendOutputForElement(output, element); if (SelectorQueryTrait::shouldOnlyMatchFirstElement) return; } } return; } Element* element = rootNode->treeScope()->getElementById(idToMatch); if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(rootNode))) return; if (selectorMatches(selectorData, element, rootNode)) SelectorQueryTrait::appendOutputForElement(output, element); }
ALWAYS_INLINE void SelectorDataList::executeFastPathForIdSelector(const Node* rootNode, const SelectorData& selectorData, const CSSSelector* idSelector, Vector<RefPtr<Node> >& matchedElements) const { ASSERT(m_selectors.size() == 1); ASSERT(idSelector); const AtomicString& idToMatch = idSelector->value(); if (UNLIKELY(rootNode->treeScope()->containsMultipleElementsWithId(idToMatch))) { const Vector<Element*>* elements = rootNode->treeScope()->getAllElementsById(idToMatch); ASSERT(elements); size_t count = elements->size(); bool rootNodeIsTreeScopeRoot = isTreeScopeRoot(rootNode); for (size_t i = 0; i < count; ++i) { Element* element = elements->at(i); if ((rootNodeIsTreeScopeRoot || element->isDescendantOf(rootNode)) && selectorMatches(selectorData, element, rootNode)) { matchedElements.append(element); if (firstMatchOnly) return; } } return; } Element* element = rootNode->treeScope()->getElementById(idToMatch); if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(rootNode))) return; if (selectorMatches(selectorData, element, rootNode)) matchedElements.append(element); }
void SelectorDataList::execute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const { if (!canUseFastQuery(rootNode)) { executeSlow<SelectorQueryTrait>(rootNode, output); return; } ASSERT(m_selectors.size() == 1); const SelectorData& selector = m_selectors[0]; const CSSSelector& firstSelector = selector.selector; // Fast path for querySelector*('#id'), querySelector*('tag#id'). if (const CSSSelector* idSelector = selectorForIdLookup(firstSelector)) { const AtomicString& idToMatch = idSelector->value(); Element* singleMatchingElement = 0; if (rootNode.treeScope().getNumberOfElementsWithId(idToMatch, singleMatchingElement) > 1) { const Vector<Element*>& elements = rootNode.treeScope().getAllElementsById(idToMatch); size_t count = elements.size(); for (size_t i = 0; i < count; ++i) { Element& element = *elements[i]; if (!(isTreeScopeRoot(rootNode) || element.isDescendantOf(&rootNode))) continue; if (selectorMatches(selector, element, rootNode)) { SelectorQueryTrait::appendElement(output, element); if (SelectorQueryTrait::shouldOnlyMatchFirstElement) return; } } return; } if (!singleMatchingElement || !(isTreeScopeRoot(rootNode) || singleMatchingElement->isDescendantOf(&rootNode))) return; if (selectorMatches(selector, *singleMatchingElement, rootNode)) SelectorQueryTrait::appendElement(output, *singleMatchingElement); return; } if (!firstSelector.tagHistory()) { // Fast path for querySelector*('.foo'), and querySelector*('div'). switch (firstSelector.m_match) { case CSSSelector::Class: collectElementsByClassName<SelectorQueryTrait>(rootNode, firstSelector.value(), output); return; case CSSSelector::Tag: collectElementsByTagName<SelectorQueryTrait>(rootNode, firstSelector.tagQName(), output); return; default: break; // If we need another fast path, add here. } } findTraverseRootsAndExecute<SelectorQueryTrait>(rootNode, output); }
void SelectorDataList::findTraverseRootsAndExecute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const { // We need to return the matches in document order. To use id lookup while there is possiblity of multiple matches // we would need to sort the results. For now, just traverse the document in that case. ASSERT(m_selectors.size() == 1); bool isRightmostSelector = true; bool startFromParent = false; Element* singleMatchingElement = 0; for (const CSSSelector* selector = &m_selectors[0].selector; selector; selector = selector->tagHistory()) { if (selector->m_match == CSSSelector::Id && (rootNode.document().getNumberOfElementsWithId(selector->value(), singleMatchingElement) <= 1)) { ContainerNode* adjustedNode = &rootNode; if (singleMatchingElement && (isTreeScopeRoot(rootNode) || singleMatchingElement->isDescendantOf(&rootNode))) adjustedNode = singleMatchingElement; else if (!singleMatchingElement || isRightmostSelector) adjustedNode = 0; if (isRightmostSelector) { executeForTraverseRoot<SelectorQueryTrait>(m_selectors[0], adjustedNode, MatchesTraverseRoots, rootNode, output); return; } if (startFromParent && adjustedNode) adjustedNode = adjustedNode->parentNode(); executeForTraverseRoot<SelectorQueryTrait>(m_selectors[0], adjustedNode, DoesNotMatchTraverseRoots, rootNode, output); return; } // If we have both CSSSelector::Id and CSSSelector::Class at the same time, we should use Id // to find traverse root. if (!SelectorQueryTrait::shouldOnlyMatchFirstElement && !startFromParent && selector->m_match == CSSSelector::Class) { if (isRightmostSelector) { ClassElementList<AllElements> traverseRoots(rootNode, selector->value()); executeForTraverseRoots<SelectorQueryTrait>(m_selectors[0], traverseRoots, MatchesTraverseRoots, rootNode, output); return; } // Since there exists some ancestor element which has the class name, we need to see all children of rootNode. if (ancestorHasClassName(rootNode, selector->value())) { executeForTraverseRoot<SelectorQueryTrait>(m_selectors[0], &rootNode, DoesNotMatchTraverseRoots, rootNode, output); return; } ClassElementList<OnlyRoots> traverseRoots(rootNode, selector->value()); executeForTraverseRoots<SelectorQueryTrait>(m_selectors[0], traverseRoots, DoesNotMatchTraverseRoots, rootNode, output); return; } if (selector->relation() == CSSSelector::SubSelector) continue; isRightmostSelector = false; if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent) startFromParent = true; else startFromParent = false; } executeForTraverseRoot<SelectorQueryTrait>(m_selectors[0], &rootNode, DoesNotMatchTraverseRoots, rootNode, output); }
void SelectorDataList::executeQueryAll(Node* rootNode, Vector<RefPtr<Node> >& matchedElements) const { if (!canUseFastQuery(rootNode)) return executeSlowQueryAll(rootNode, matchedElements); ASSERT(m_selectors.size() == 1); ASSERT(m_selectors[0].selector); const CSSSelector* firstSelector = m_selectors[0].selector; if (!firstSelector->tagHistory()) { // Fast path for querySelectorAll('#id'), querySelectorAl('.foo'), and querySelectorAll('div'). switch (firstSelector->m_match) { case CSSSelector::Id: { if (rootNode->document()->containsMultipleElementsWithId(firstSelector->value())) break; // Just the same as getElementById. Element* element = rootNode->treeScope()->getElementById(firstSelector->value()); if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf(rootNode))) matchedElements.append(element); return; } case CSSSelector::Class: return collectElementsByClassName(rootNode, firstSelector->value(), matchedElements); case CSSSelector::Tag: return collectElementsByTagName(rootNode, firstSelector->tagQName(), matchedElements); default: break; // If we need another fast path, add here. } } bool matchTraverseRoots; OwnPtr<SimpleNodeList> traverseRoots = findTraverseRoots(rootNode, matchTraverseRoots); if (traverseRoots->isEmpty()) return; const SelectorData& selector = m_selectors[0]; if (matchTraverseRoots) { while (!traverseRoots->isEmpty()) { Node* node = traverseRoots->next(); Element* element = toElement(node); if (selectorMatches(selector, element, rootNode)) matchedElements.append(element); } return; } while (!traverseRoots->isEmpty()) { Node* traverseRoot = traverseRoots->next(); for (Element* element = ElementTraversal::firstWithin(traverseRoot); element; element = ElementTraversal::next(element, traverseRoot)) { if (selectorMatches(selector, element, rootNode)) matchedElements.append(element); } } }
// If returns true, traversalRoots has the elements that may match the selector query. // // If returns false, traversalRoots has the rootNode parameter or descendants of rootNode representing // the subtree for which we can limit the querySelector traversal. // // The travseralRoots may be empty, regardless of the returned bool value, if this method finds that the selectors won't // match any element. PassOwnPtr<SimpleNodeList> SelectorDataList::findTraverseRoots(Node* rootNode, bool& matchTraverseRoots) const { // We need to return the matches in document order. To use id lookup while there is possiblity of multiple matches // we would need to sort the results. For now, just traverse the document in that case. ASSERT(rootNode); ASSERT(m_selectors.size() == 1); ASSERT(m_selectors[0].selector); bool isRightmostSelector = true; bool startFromParent = false; for (const CSSSelector* selector = m_selectors[0].selector; selector; selector = selector->tagHistory()) { if (selector->m_match == CSSSelector::Id && !rootNode->document().containsMultipleElementsWithId(selector->value())) { Element* element = rootNode->treeScope().getElementById(selector->value()); if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf(rootNode))) rootNode = element; else if (!element || isRightmostSelector) rootNode = 0; if (isRightmostSelector) { matchTraverseRoots = true; return adoptPtr(new SingleNodeList(rootNode)); } if (startFromParent && rootNode) rootNode = rootNode->parentNode(); matchTraverseRoots = false; return adoptPtr(new SingleNodeList(rootNode)); } // If we have both CSSSelector::Id and CSSSelector::Class at the same time, we should use Id // to find traverse root. if (!startFromParent && selector->m_match == CSSSelector::Class) { if (isRightmostSelector) { matchTraverseRoots = true; return adoptPtr(new ClassElementList(rootNode, selector->value())); } matchTraverseRoots = false; // Since there exists some ancestor element which has the class name, we need to see all children of rootNode. if (ancestorHasClassName(rootNode, selector->value())) return adoptPtr(new SingleNodeList(rootNode)); return adoptPtr(new ClassRootNodeList(rootNode, selector->value())); } if (selector->relation() == CSSSelector::SubSelector) continue; isRightmostSelector = false; if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent) startFromParent = true; else startFromParent = false; } matchTraverseRoots = false; return adoptPtr(new SingleNodeList(rootNode)); }
void StyleSheetScopingNodeList::remove(ContainerNode* node) { if (isTreeScopeRoot(node) || !m_scopingNodes) return; m_scopingNodes->remove(node); if (node->inDocument() && node->numberOfScopedHTMLStyleChildren()) return; if (!m_scopingNodesRemoved) m_scopingNodesRemoved = adoptPtr(new ListHashSet<Node*, 4>()); m_scopingNodesRemoved->add(node); }
void StyleSheetScopingNodeList::add(ContainerNode* node) { ASSERT(node && node->inDocument()); if (isTreeScopeRoot(node)) return; if (!m_scopingNodes) m_scopingNodes = adoptPtr(new DocumentOrderedList()); m_scopingNodes->add(node); if (m_scopingNodesRemoved) m_scopingNodesRemoved->remove(node); }
void SelectorDataList::execute(Node* rootNode, Vector<RefPtr<Node> >& matchedElements) const { if (const CSSSelector* idSelector = selectorForIdLookup(rootNode)) { ASSERT(m_selectors.size() == 1); const AtomicString& idToMatch = idSelector->value(); if (UNLIKELY(rootNode->treeScope()->containsMultipleElementsWithId(idToMatch))) { const Vector<Element*>* elements = rootNode->treeScope()->getAllElementsById(idToMatch); ASSERT(elements); size_t count = elements->size(); for (size_t i = 0; i < count; ++i) { Element* element = elements->at(i); if (selectorMatches(m_selectors[0], element, rootNode)) { matchedElements.append(element); if (firstMatchOnly) return; } } return; } Element* element = rootNode->treeScope()->getElementById(idToMatch); if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(rootNode))) return; if (selectorMatches(m_selectors[0], element, rootNode)) matchedElements.append(element); return; } unsigned selectorCount = m_selectors.size(); if (selectorCount == 1) { const SelectorData& selector = m_selectors[0]; for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(element, rootNode)) { if (selectorMatches(selector, element, rootNode)) { matchedElements.append(element); if (firstMatchOnly) return; } } return; } for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(element, rootNode)) { for (unsigned i = 0; i < selectorCount; ++i) { if (selectorMatches(m_selectors[i], element, rootNode)) { matchedElements.append(element); if (firstMatchOnly) return; break; } } } }
void StyleSheetScopingNodeList::remove(ContainerNode* node) { if (isTreeScopeRoot(node) || !m_scopingNodes) return; // If the node is still working as a scoping node, we cannot remove. if (node->inDocument()) return; m_scopingNodes->remove(node); if (!m_scopingNodesRemoved) m_scopingNodesRemoved = adoptPtr(new ListHashSet<Node*, 4>()); m_scopingNodesRemoved->add(node); }
Element* SelectorDataList::executeQueryFirst(Node* rootNode) const { if (!canUseFastQuery(rootNode)) return executeSlowQueryFirst(rootNode); const CSSSelector* selector = m_selectors[0].selector; ASSERT(selector); if (!selector->tagHistory()) { // Fast path for querySelector('#id'), querySelector('.foo'), and querySelector('div'). // Many web developers uses querySelector with these simple selectors. switch (selector->m_match) { case CSSSelector::Id: { if (rootNode->document()->containsMultipleElementsWithId(selector->value())) break; Element* element = rootNode->treeScope()->getElementById(selector->value()); return element && (isTreeScopeRoot(rootNode) || element->isDescendantOf(rootNode)) ? element : 0; } case CSSSelector::Class: return findElementByClassName(rootNode, selector->value()); case CSSSelector::Tag: return findElementByTagName(rootNode, selector->tagQName()); default: break; // If we need another fast path, add here. } } bool matchTraverseRoot; Node* traverseRootNode = findTraverseRoot(rootNode, matchTraverseRoot); if (!traverseRootNode) return 0; if (matchTraverseRoot) { ASSERT(m_selectors.size() == 1); ASSERT(traverseRootNode->isElementNode()); Element* element = toElement(traverseRootNode); return selectorMatches(m_selectors[0], element, rootNode) ? element : 0; } for (Element* element = ElementTraversal::firstWithin(traverseRootNode); element; element = ElementTraversal::next(element, traverseRootNode)) { if (selectorMatches(m_selectors[0], element, rootNode)) return element; } return 0; }
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(); } }
static ContainerNode& filterRootById(ContainerNode& rootNode, const CSSSelector& firstSelector) { if (!rootNode.inDocument()) return rootNode; if (rootNode.document().inQuirksMode()) return rootNode; // If there was an Id match in the rightmost Simple Selector, we should be in a RightMostWithIdMatch, not in filter. // Thus we can skip the rightmost match. const CSSSelector* selector = &firstSelector; do { ASSERT(!canBeUsedForIdFastPath(*selector)); if (selector->relation() != CSSSelector::SubSelector) break; selector = selector->tagHistory(); } while (selector); bool inAdjacentChain = false; for (; selector; selector = selector->tagHistory()) { if (canBeUsedForIdFastPath(*selector)) { const AtomicString& idToMatch = selector->value(); if (ContainerNode* searchRoot = rootNode.treeScope().getElementById(idToMatch)) { if (LIKELY(!rootNode.treeScope().containsMultipleElementsWithId(idToMatch))) { if (inAdjacentChain) searchRoot = searchRoot->parentNode(); if (searchRoot && (isTreeScopeRoot(rootNode) || searchRoot == &rootNode || searchRoot->isDescendantOf(&rootNode))) return *searchRoot; } } } if (selector->relation() == CSSSelector::SubSelector) continue; if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent) inAdjacentChain = true; else inAdjacentChain = false; } return rootNode; }
// If matchTraverseRoot is true, the returned Node is the single Element that may match the selector query. // // If matchTraverseRoot is false, the returned Node is the rootNode parameter or a descendant of rootNode representing // the subtree for which we can limit the querySelector traversal. // // The returned Node may be 0, regardless of matchTraverseRoot, if this method finds that the selectors won't // match any element. Node* SelectorDataList::findTraverseRoot(Node* rootNode, bool& matchTraverseRoot) const { // We need to return the matches in document order. To use id lookup while there is possiblity of multiple matches // we would need to sort the results. For now, just traverse the document in that case. ASSERT(rootNode); ASSERT(m_selectors.size() == 1); ASSERT(m_selectors[0].selector); bool matchSingleNode = true; bool startFromParent = false; for (const CSSSelector* selector = m_selectors[0].selector; selector; selector = selector->tagHistory()) { if (selector->m_match == CSSSelector::Id && !rootNode->document()->containsMultipleElementsWithId(selector->value())) { Element* element = rootNode->treeScope()->getElementById(selector->value()); if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf(rootNode))) rootNode = element; else if (!element || matchSingleNode) rootNode = 0; if (matchSingleNode) { matchTraverseRoot = true; return rootNode; } if (startFromParent && rootNode) rootNode = rootNode->parentNode(); matchTraverseRoot = false; return rootNode; } if (selector->relation() == CSSSelector::SubSelector) continue; matchSingleNode = false; if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent) startFromParent = true; else startFromParent = false; } matchTraverseRoot = false; return rootNode; }