Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(534)

Unified Diff: Source/core/dom/SelectorQuery.cpp

Issue 142513003: Use Traits in SelectorQuery to avoid a lot of code duplication (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Take Adam's feedback into consideration Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Source/core/dom/SelectorQuery.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Source/core/dom/SelectorQuery.cpp
diff --git a/Source/core/dom/SelectorQuery.cpp b/Source/core/dom/SelectorQuery.cpp
index 9334ca52f0f986e2c164c37396d4cebb1c56e24e..f3370cccffc118be04e35c9e8b292fa0f3e091c6 100644
--- a/Source/core/dom/SelectorQuery.cpp
+++ b/Source/core/dom/SelectorQuery.cpp
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2014 Samsung Electronics. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -38,76 +39,45 @@
namespace WebCore {
-class SimpleNodeList {
-public:
- virtual ~SimpleNodeList() { }
- virtual bool isEmpty() const = 0;
- virtual Node* next() = 0;
-};
-
-class SingleNodeList FINAL : public SimpleNodeList {
-public:
- explicit SingleNodeList(Node* rootNode) : m_currentNode(rootNode) { }
-
- virtual bool isEmpty() const OVERRIDE { return !m_currentNode; }
-
- virtual Node* next() OVERRIDE
+struct SingleElementSelectorQueryTrait {
+ typedef Element* OutputType;
+ static const bool shouldOnlyMatchFirstElement = true;
+ ALWAYS_INLINE static void appendElement(OutputType& output, Element& element)
{
- Node* current = m_currentNode;
- m_currentNode = 0;
- return current;
+ ASSERT(!output);
+ output = &element;
}
-
-private:
- Node* m_currentNode;
};
-class ClassRootNodeList FINAL : public SimpleNodeList {
-public:
- ClassRootNodeList(Node& rootNode, const AtomicString& className)
- : m_className(className)
- , m_rootNode(rootNode)
- , m_currentElement(nextInternal(ElementTraversal::firstWithin(m_rootNode))) { }
-
- virtual bool isEmpty() const OVERRIDE { return !m_currentElement; }
-
- virtual Node* next() OVERRIDE
+struct AllElementsSelectorQueryTrait {
+ typedef Vector<RefPtr<Node> > OutputType;
+ static const bool shouldOnlyMatchFirstElement = false;
+ ALWAYS_INLINE static void appendElement(OutputType& output, Node& element)
{
- Node* current = m_currentElement;
- ASSERT(current);
- m_currentElement = nextInternal(ElementTraversal::nextSkippingChildren(*m_currentElement, &m_rootNode));
- return current;
+ output.append(RefPtr<Node>(element));
}
-
-private:
- Element* nextInternal(Element* element)
- {
- for (; element; element = ElementTraversal::next(*element, &m_rootNode)) {
- if (element->hasClass() && element->classNames().contains(m_className))
- return element;
- }
- return 0;
- }
-
- const AtomicString& m_className;
- Node& m_rootNode;
- Element* m_currentElement;
};
-class ClassElementList FINAL : public SimpleNodeList {
+enum ClassElementListBehavior { AllElements, OnlyRoots };
+
+template <ClassElementListBehavior onlyRoots>
+class ClassElementList {
public:
ClassElementList(Node& rootNode, const AtomicString& className)
: m_className(className)
, m_rootNode(rootNode)
, m_currentElement(nextInternal(ElementTraversal::firstWithin(rootNode))) { }
- virtual bool isEmpty() const OVERRIDE { return !m_currentElement; }
+ bool isEmpty() const { return !m_currentElement; }
- virtual Node* next() OVERRIDE
+ Node* next()
{
Node* current = m_currentElement;
ASSERT(current);
- m_currentElement = nextInternal(ElementTraversal::next(*m_currentElement, &m_rootNode));
+ if (onlyRoots)
+ m_currentElement = nextInternal(ElementTraversal::nextSkippingChildren(*m_currentElement, &m_rootNode));
+ else
+ m_currentElement = nextInternal(ElementTraversal::next(*m_currentElement, &m_rootNode));
return current;
}
@@ -169,47 +139,39 @@ bool SelectorDataList::matches(Element& targetElement) const
PassRefPtr<NodeList> SelectorDataList::queryAll(Node& rootNode) const
{
Vector<RefPtr<Node> > result;
- executeQueryAll(rootNode, result);
+ execute<AllElementsSelectorQueryTrait>(rootNode, result);
return StaticNodeList::adopt(result);
}
PassRefPtr<Element> SelectorDataList::queryFirst(Node& rootNode) const
{
- return executeQueryFirst(rootNode);
-}
-
-void SelectorDataList::collectElementsByClassName(Node& rootNode, const AtomicString& className, Vector<RefPtr<Node> >& traversalRoots) const
-{
- for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
- if (element->hasClass() && element->classNames().contains(className))
- traversalRoots.append(element);
- }
-}
-
-void SelectorDataList::collectElementsByTagName(Node& rootNode, const QualifiedName& tagName, Vector<RefPtr<Node> >& traversalRoots) const
-{
- for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
- if (SelectorChecker::tagMatches(*element, tagName))
- traversalRoots.append(element);
- }
+ Element* matchedElement = 0;
+ execute<SingleElementSelectorQueryTrait>(rootNode, matchedElement);
+ return matchedElement;
}
-Element* SelectorDataList::findElementByClassName(Node& rootNode, const AtomicString& className) const
+template <typename SelectorQueryTrait>
+void SelectorDataList::collectElementsByClassName(Node& rootNode, const AtomicString& className, typename SelectorQueryTrait::OutputType& output) const
{
for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
- if (element->hasClass() && element->classNames().contains(className))
- return element;
+ if (element->hasClass() && element->classNames().contains(className)) {
+ SelectorQueryTrait::appendElement(output, *element);
+ if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
+ return;
+ }
}
- return 0;
}
-Element* SelectorDataList::findElementByTagName(Node& rootNode, const QualifiedName& tagName) const
+template <typename SelectorQueryTrait>
+void SelectorDataList::collectElementsByTagName(Node& rootNode, const QualifiedName& tagName, typename SelectorQueryTrait::OutputType& output) const
{
for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
- if (SelectorChecker::tagMatches(*element, tagName))
- return element;
+ if (SelectorChecker::tagMatches(*element, tagName)) {
+ SelectorQueryTrait::appendElement(output, *element);
+ if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
+ return;
+ }
}
- return 0;
}
inline bool SelectorDataList::canUseFastQuery(const Node& rootNode) const
@@ -237,7 +199,8 @@ inline bool ancestorHasClassName(Node& rootNode, const AtomicString& className)
//
// 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
+template <typename SelectorQueryTrait>
+void SelectorDataList::findTraverseRootsAndExecute(Node& 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.
@@ -256,29 +219,34 @@ PassOwnPtr<SimpleNodeList> SelectorDataList::findTraverseRoots(Node& rootNode, b
else if (!element || isRightmostSelector)
adjustedNode = 0;
if (isRightmostSelector) {
- matchTraverseRoots = true;
- return adoptPtr(new SingleNodeList(adjustedNode));
+ executeForTraverseRoot<SelectorQueryTrait>(m_selectors[0], adjustedNode, MatchesTraverseRoots, rootNode, output);
+ return;
}
+
if (startFromParent && adjustedNode)
adjustedNode = adjustedNode->parentNode();
- matchTraverseRoots = false;
- return adoptPtr(new SingleNodeList(adjustedNode));
+ 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 (!startFromParent && selector->m_match == CSSSelector::Class) {
+ if (!SelectorQueryTrait::shouldOnlyMatchFirstElement && !startFromParent && selector->m_match == CSSSelector::Class) {
if (isRightmostSelector) {
- matchTraverseRoots = true;
- return adoptPtr(new ClassElementList(rootNode, selector->value()));
+ ClassElementList<AllElements> traverseRoots(rootNode, selector->value());
+ executeForTraverseRoots<SelectorQueryTrait>(m_selectors[0], traverseRoots, MatchesTraverseRoots, rootNode, output);
+ return;
}
- 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));
+ if (ancestorHasClassName(rootNode, selector->value())) {
+ executeForTraverseRoot<SelectorQueryTrait>(m_selectors[0], &rootNode, DoesNotMatchTraverseRoots, rootNode, output);
+ return;
+ }
- return adoptPtr(new ClassRootNodeList(rootNode, selector->value()));
+ ClassElementList<OnlyRoots> traverseRoots(rootNode, selector->value());
+ executeForTraverseRoots<SelectorQueryTrait>(m_selectors[0], traverseRoots, DoesNotMatchTraverseRoots, rootNode, output);
+ return;
}
if (selector->relation() == CSSSelector::SubSelector)
@@ -290,165 +258,95 @@ PassOwnPtr<SimpleNodeList> SelectorDataList::findTraverseRoots(Node& rootNode, b
startFromParent = false;
}
- matchTraverseRoots = false;
- return adoptPtr(new SingleNodeList(&rootNode));
+ executeForTraverseRoot<SelectorQueryTrait>(m_selectors[0], &rootNode, DoesNotMatchTraverseRoots, rootNode, output);
}
-void SelectorDataList::executeSlowQueryAll(Node& rootNode, Vector<RefPtr<Node> >& matchedElements) const
+template <typename SelectorQueryTrait>
+void SelectorDataList::executeForTraverseRoot(const SelectorData& selector, Node* traverseRoot, MatchTraverseRootState matchTraverseRoot, Node& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
- for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
- for (unsigned i = 0; i < m_selectors.size(); ++i) {
- if (selectorMatches(m_selectors[i], *element, rootNode)) {
- matchedElements.append(element);
- break;
- }
- }
- }
-}
-
-const CSSSelector* SelectorDataList::selectorForIdLookup(const CSSSelector* firstSelector) const
-{
- for (const CSSSelector* selector = firstSelector; selector; selector = selector->tagHistory()) {
- if (selector->m_match == CSSSelector::Id)
- return selector;
- if (selector->relation() != CSSSelector::SubSelector)
- break;
- }
- return 0;
-}
-
-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 SelectorData& selector = m_selectors[0];
- const CSSSelector* firstSelector = selector.selector;
+ if (!traverseRoot)
+ return;
- // Fast path for querySelectorAll('#id'), querySelectorAll('tag#id').
- if (const CSSSelector* idSelector = selectorForIdLookup(firstSelector)) {
- const AtomicString& idToMatch = idSelector->value();
- if (rootNode.treeScope().containsMultipleElementsWithId(idToMatch)) {
- 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];
- ASSERT(element);
- if (!(isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode)))
- continue;
- if (selectorMatches(selector, *element, rootNode))
- matchedElements.append(element);
- }
- return;
- }
- Element* element = rootNode.treeScope().getElementById(idToMatch);
- if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode)))
- return;
- if (selectorMatches(selector, *element, rootNode))
- matchedElements.append(element);
+ if (matchTraverseRoot) {
+ if (selectorMatches(selector, toElement(*traverseRoot), rootNode))
+ SelectorQueryTrait::appendElement(output, toElement(*traverseRoot));
return;
}
- if (!firstSelector->tagHistory()) {
- // Fast path for querySelectorAl('.foo'), and querySelectorAll('div').
- switch (firstSelector->m_match) {
- 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.
+ for (Element* element = ElementTraversal::firstWithin(*traverseRoot); element; element = ElementTraversal::next(*element, traverseRoot)) {
+ if (selectorMatches(selector, *element, rootNode)) {
+ SelectorQueryTrait::appendElement(output, *element);
+ if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
+ return;
}
}
+}
- bool matchTraverseRoots;
- OwnPtr<SimpleNodeList> traverseRoots = findTraverseRoots(rootNode, matchTraverseRoots);
- if (traverseRoots->isEmpty())
+template <typename SelectorQueryTrait, typename SimpleNodeListType>
+void SelectorDataList::executeForTraverseRoots(const SelectorData& selector, SimpleNodeListType& traverseRoots, MatchTraverseRootState matchTraverseRoots, Node& rootNode, typename SelectorQueryTrait::OutputType& output) const
+{
+ if (traverseRoots.isEmpty())
return;
if (matchTraverseRoots) {
- while (!traverseRoots->isEmpty()) {
- Node& node = *traverseRoots->next();
+ while (!traverseRoots.isEmpty()) {
+ Node& node = *traverseRoots.next();
Element& element = toElement(node);
- if (selectorMatches(selector, element, rootNode))
- matchedElements.append(&element);
+ if (selectorMatches(selector, element, rootNode)) {
+ SelectorQueryTrait::appendElement(output, element);
+ if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
+ return;
+ }
}
return;
}
- while (!traverseRoots->isEmpty()) {
- Node* traverseRoot = traverseRoots->next();
+ while (!traverseRoots.isEmpty()) {
+ Node* traverseRoot = traverseRoots.next();
ASSERT(traverseRoot);
for (Element* element = ElementTraversal::firstWithin(*traverseRoot); element; element = ElementTraversal::next(*element, traverseRoot)) {
- if (selectorMatches(selector, *element, rootNode))
- matchedElements.append(element);
+ if (selectorMatches(selector, *element, rootNode)) {
+ SelectorQueryTrait::appendElement(output, *element);
+ if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
+ return;
+ }
}
}
}
-// 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
+template <typename SelectorQueryTrait>
+void SelectorDataList::executeSlow(Node& 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);
- 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());
- Node* adjustedRootNode = &rootNode;
- if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode)))
- adjustedRootNode = element;
- else if (!element || matchSingleNode)
- adjustedRootNode = 0;
- if (matchSingleNode) {
- matchTraverseRoot = true;
- return adjustedRootNode;
+ for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
+ for (unsigned i = 0; i < m_selectors.size(); ++i) {
+ if (selectorMatches(m_selectors[i], *element, rootNode)) {
+ SelectorQueryTrait::appendElement(output, *element);
+ if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
+ return;
+ break;
}
- if (startFromParent && adjustedRootNode)
- adjustedRootNode = adjustedRootNode->parentNode();
- matchTraverseRoot = false;
- return adjustedRootNode;
}
- 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;
}
-Element* SelectorDataList::executeSlowQueryFirst(Node& rootNode) const
+const CSSSelector* SelectorDataList::selectorForIdLookup(const CSSSelector* firstSelector) const
{
- for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
- for (unsigned i = 0; i < m_selectors.size(); ++i) {
- if (selectorMatches(m_selectors[i], *element, rootNode))
- return element;
- }
+ for (const CSSSelector* selector = firstSelector; selector; selector = selector->tagHistory()) {
+ if (selector->m_match == CSSSelector::Id)
+ return selector;
+ if (selector->relation() != CSSSelector::SubSelector)
+ break;
}
return 0;
}
-Element* SelectorDataList::executeQueryFirst(Node& rootNode) const
+template <typename SelectorQueryTrait>
+void SelectorDataList::execute(Node& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
- if (!canUseFastQuery(rootNode))
- return executeSlowQueryFirst(rootNode);
+ if (!canUseFastQuery(rootNode)) {
+ executeSlow<SelectorQueryTrait>(rootNode, output);
+ return;
+ }
ASSERT(m_selectors.size() == 1);
ASSERT(m_selectors[0].selector);
@@ -456,56 +354,47 @@ Element* SelectorDataList::executeQueryFirst(Node& rootNode) const
const SelectorData& selector = m_selectors[0];
const CSSSelector* firstSelector = selector.selector;
- // Fast path for querySelectorAll('#id'), querySelectorAll('tag#id').
+ // 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 Vector<Element*>& elements = rootNode.treeScope().getAllElementsById(idToMatch);
size_t count = elements.size();
for (size_t i = 0; i < count; ++i) {
- Element* element = elements[i];
- ASSERT(element);
- if (!(isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode)))
+ Element& element = *elements[i];
+ if (!(isTreeScopeRoot(rootNode) || element.isDescendantOf(&rootNode)))
continue;
- if (selectorMatches(selector, *element, rootNode))
- return element;
+ if (selectorMatches(selector, element, rootNode)) {
+ SelectorQueryTrait::appendElement(output, element);
+ if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
+ return;
+ }
}
- return 0;
+ return;
}
Element* element = rootNode.treeScope().getElementById(idToMatch);
if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode)))
- return 0;
- return selectorMatches(selector, *element, rootNode) ? element : 0;
+ return;
+ if (selectorMatches(selector, *element, rootNode))
+ SelectorQueryTrait::appendElement(output, *element);
+ return;
}
if (!firstSelector->tagHistory()) {
- // Fast path for querySelector('.foo'), and querySelector('div').
- // Many web developers uses querySelector with these simple selectors.
+ // Fast path for querySelector*('.foo'), and querySelector*('div').
switch (firstSelector->m_match) {
case CSSSelector::Class:
- return findElementByClassName(rootNode, firstSelector->value());
+ collectElementsByClassName<SelectorQueryTrait>(rootNode, firstSelector->value(), output);
+ return;
case CSSSelector::Tag:
- return findElementByTagName(rootNode, firstSelector->tagQName());
+ collectElementsByTagName<SelectorQueryTrait>(rootNode, firstSelector->tagQName(), output);
+ return;
default:
break; // If we need another fast path, add here.
}
}
- bool matchTraverseRoot;
- Node* traverseRootNode = findTraverseRoot(rootNode, matchTraverseRoot);
- if (!traverseRootNode)
- return 0;
- if (matchTraverseRoot) {
- ASSERT(traverseRootNode->isElementNode());
- Element& element = toElement(*traverseRootNode);
- return selectorMatches(selector, element, rootNode) ? &element : 0;
- }
-
- for (Element* element = ElementTraversal::firstWithin(*traverseRootNode); element; element = ElementTraversal::next(*element, traverseRootNode)) {
- if (selectorMatches(selector, *element, rootNode))
- return element;
- }
- return 0;
+ findTraverseRootsAndExecute<SelectorQueryTrait>(rootNode, output);
}
SelectorQuery::SelectorQuery(const CSSSelectorList& selectorList)
« no previous file with comments | « Source/core/dom/SelectorQuery.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698