Chromium Code Reviews| Index: Source/core/dom/SelectorQuery.cpp | 
| diff --git a/Source/core/dom/SelectorQuery.cpp b/Source/core/dom/SelectorQuery.cpp | 
| index dd0440210c96f53dea60270f778f0b4469d59efc..e56652f3d962f3e8d9a707e7c8da111591851f9e 100644 | 
| --- a/Source/core/dom/SelectorQuery.cpp | 
| +++ b/Source/core/dom/SelectorQuery.cpp | 
| @@ -83,19 +83,13 @@ bool SelectorDataList::matches(Element* targetElement) const | 
| PassRefPtr<NodeList> SelectorDataList::queryAll(Node* rootNode) const | 
| { | 
| Vector<RefPtr<Node> > result; | 
| - execute<false>(rootNode, result); | 
| + executeQueryAll(rootNode, result); | 
| return StaticNodeList::adopt(result); | 
| } | 
| PassRefPtr<Element> SelectorDataList::queryFirst(Node* rootNode) const | 
| { | 
| - Vector<RefPtr<Node> > result; | 
| - execute<true>(rootNode, result); | 
| - if (result.isEmpty()) | 
| - return 0; | 
| - ASSERT(result.size() == 1); | 
| - ASSERT(result.first()->isElementNode()); | 
| - return toElement(result.first().get()); | 
| + return executeQueryFirst(rootNode); | 
| } | 
| static inline bool isTreeScopeRoot(Node* node) | 
| @@ -104,6 +98,132 @@ static inline bool isTreeScopeRoot(Node* node) | 
| return node->isDocumentNode() || node->isShadowRoot(); | 
| } | 
| +template<bool firstMatchOnly> | 
| +void SelectorDataList::collectElementsByClassName(Node* rootNode, const SpaceSplitString& classNames, Vector<Node*>& traversalRoots) const | 
| +{ | 
| + for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(element, rootNode)) { | 
| + if (element->hasClass() && element->classNames().containsAll(classNames)) { | 
| + traversalRoots.append(element); | 
| + if (firstMatchOnly) | 
| 
 
esprehn
2013/07/12 07:17:17
I'd rather you just created two methods.
Element*
 
tasak
2013/07/16 07:46:59
I see.
Done.
 
 | 
| + return; | 
| + } | 
| + } | 
| +} | 
| + | 
| +inline bool SelectorDataList::canUseFastQuery(Node* rootNode) const | 
| +{ | 
| + return m_selectors.size() == 1 && rootNode->inDocument() && !rootNode->document()->inQuirksMode(); | 
| 
 
esprehn
2013/07/12 07:17:17
Why does the root need to be inDocument?
 
tasak
2013/07/16 07:46:59
To use treeScope()->getElementById.
I guess, we h
 
 | 
| +} | 
| + | 
| +// 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 | 
| 
 
esprehn
2013/07/12 07:17:17
It seems like you're trying to do too much inside
 
tasak
2013/07/16 07:46:59
I see. I moved the codes for finding elements with
 
 | 
| +// match any element. | 
| +bool SelectorDataList::findTraverseRoots(Node* rootNode, Vector<Node*>& traversalRoots) 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. | 
| + if (!canUseFastQuery(rootNode)) { | 
| + traversalRoots.append(rootNode); | 
| + return false; | 
| + } | 
| + | 
| + bool matchTraverseRoots = true; | 
| + bool startFromParent = false; | 
| + | 
| + for (const CSSSelector* selector = m_selectors[0].selector; selector; selector = selector->tagHistory()) { | 
| 
 
esprehn
2013/07/12 07:17:17
Do you need an ASSERT at the start of this method?
 
tasak
2013/07/16 07:46:59
I see.
Done.
 
 | 
| + 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 || matchTraverseRoots) | 
| + rootNode = 0; | 
| + if (matchTraverseRoots) { | 
| + if (rootNode) | 
| + traversalRoots.append(rootNode); | 
| + return true; | 
| + } | 
| 
 
esprehn
2013/07/12 07:17:17
This loop is confusing, what is it doing?
 
tasak
2013/07/16 07:46:59
This loop is:
- looking at each selector from righ
 
 | 
| + if (startFromParent && rootNode) | 
| + rootNode = rootNode->parentNode(); | 
| + if (rootNode) | 
| + traversalRoots.append(rootNode); | 
| + return false; | 
| + } | 
| + | 
| + // 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) { | 
| + SpaceSplitString classNames(selector->value(), rootNode->document()->inQuirksMode()); | 
| + if (matchTraverseRoots) { | 
| + collectElementsByClassName<false>(rootNode, classNames, traversalRoots); | 
| + } else { | 
| + for (Element* element = ElementTraversal::firstWithin(rootNode); element; ) { | 
| + if (element->hasClass() && element->classNames().containsAll(classNames)) { | 
| + traversalRoots.append(element); | 
| + element = ElementTraversal::nextSkippingChildren(element, rootNode); | 
| 
 
esprehn
2013/07/12 07:17:17
Why doesn't this need to be recursive? If this ele
 
tasak
2013/07/16 07:46:59
Because this is for finding traversal roots (e.g.
 
 | 
| + } else { | 
| + element = ElementTraversal::next(element, rootNode); | 
| + } | 
| + } | 
| + } | 
| + return matchTraverseRoots; | 
| + } | 
| + | 
| + if (selector->relation() == CSSSelector::SubSelector) | 
| + continue; | 
| + matchTraverseRoots = false; | 
| + if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent) | 
| + startFromParent = true; | 
| + else | 
| + startFromParent = false; | 
| + } | 
| + if (rootNode) | 
| + traversalRoots.append(rootNode); | 
| + return false; | 
| +} | 
| + | 
| +void SelectorDataList::executeQueryAll(Node* rootNode, Vector<RefPtr<Node> >& matchedElements) const | 
| +{ | 
| + Vector<Node*> traverseRoots; | 
| + bool matchTraverseRoots = findTraverseRoots(rootNode, traverseRoots); | 
| + | 
| + if (traverseRoots.isEmpty()) | 
| + return; | 
| + if (matchTraverseRoots) { | 
| 
 
esprehn
2013/07/12 07:17:17
This is weird, even if matchTraverseRoots is false
 
tasak
2013/07/16 07:46:59
The matchTraverseRoots means whether elements foun
 
 | 
| + ASSERT(m_selectors.size() == 1); | 
| + for (unsigned i = 0; i < traverseRoots.size(); ++i) { | 
| + Element* element = toElement(traverseRoots.at(i)); | 
| + if (selectorMatches(m_selectors[0], element, rootNode)) | 
| + matchedElements.append(element); | 
| + } | 
| + return; | 
| + } | 
| + | 
| + unsigned selectorCount = m_selectors.size(); | 
| 
 
esprehn
2013/07/12 07:17:17
No need to cache this in a local
 
tasak
2013/07/16 07:46:59
Done.
 
 | 
| + if (selectorCount == 1) { | 
| + const SelectorData& selector = m_selectors[0]; | 
| + for (unsigned i = 0; i < traverseRoots.size(); ++i) { | 
| + Node* traverseRoot = traverseRoots.at(i); | 
| + for (Element* element = ElementTraversal::firstWithin(traverseRoot); element; element = ElementTraversal::next(element, traverseRoot)) { | 
| + if (selectorMatches(selector, element, rootNode)) | 
| + matchedElements.append(element); | 
| + } | 
| + } | 
| + 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); | 
| + break; | 
| + } | 
| + } | 
| + } | 
| +} | 
| + | 
| // If the first pair value is true, the returned Node is the single Element that may match the selector query. | 
| // | 
| // If the first value is false, the returned Node is the rootNode parameter or a descendant of rootNode representing | 
| @@ -115,13 +235,6 @@ std::pair<bool, Node*> SelectorDataList::findTraverseRoot(Node* rootNode) 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. | 
| - if (m_selectors.size() != 1) | 
| - return std::make_pair(false, rootNode); | 
| - if (!rootNode->inDocument()) | 
| - return std::make_pair(false, rootNode); | 
| - if (rootNode->document()->inQuirksMode()) | 
| - return std::make_pair(false, rootNode); | 
| - | 
| bool matchSingleNode = true; | 
| bool startFromParent = false; | 
| for (const CSSSelector* selector = m_selectors[0].selector; selector; selector = selector->tagHistory()) { | 
| @@ -148,44 +261,55 @@ std::pair<bool, Node*> SelectorDataList::findTraverseRoot(Node* rootNode) const | 
| return std::make_pair(false, rootNode); | 
| } | 
| -template <bool firstMatchOnly> | 
| -void SelectorDataList::execute(Node* rootNode, Vector<RefPtr<Node> >& matchedElements) const | 
| +Element* SelectorDataList::executeQueryFirst(Node* rootNode) const | 
| { | 
| - std::pair<bool, Node*> traverseRoot = findTraverseRoot(rootNode); | 
| - if (!traverseRoot.second) | 
| - return; | 
| - Node* traverseRootNode = traverseRoot.second; | 
| - if (traverseRoot.first) { | 
| - ASSERT(m_selectors.size() == 1); | 
| - ASSERT(traverseRootNode->isElementNode()); | 
| - Element* element = toElement(traverseRootNode); | 
| - if (selectorMatches(m_selectors[0], element, rootNode)) | 
| - matchedElements.append(element); | 
| - return; | 
| + // fast path | 
| + if (canUseFastQuery(rootNode)) { | 
| + if (m_selectors[0].selector && !m_selectors[0].selector->tagHistory()) { | 
| + const CSSSelector* selector = m_selectors[0].selector; | 
| + if (selector->m_match == CSSSelector::Id && !rootNode->document()->containsMultipleElementsWithId(selector->value())) { | 
| + // just getElementById | 
| + Element* element = rootNode->treeScope()->getElementById(selector->value()); | 
| + return element && (isTreeScopeRoot(rootNode) || element->isDescendantOf(rootNode)) ? element : 0; | 
| 
 
esprehn
2013/07/12 07:17:17
Remove the isTreeScopeRoot checks, isDescendantOf
 
tasak
2013/07/16 07:46:59
We need this check, because this doesn't use findT
 
 | 
| + } | 
| + if (selector->m_match == CSSSelector::Class) { | 
| + Vector<Node*> nodes; | 
| + SpaceSplitString classNames(selector->value(), rootNode->document()->inQuirksMode()); | 
| + collectElementsByClassName<true>(rootNode, classNames, nodes); | 
| + return nodes.size() > 0 ? toElement(nodes.at(0)) : 0; | 
| 
 
esprehn
2013/07/12 07:17:17
nodes.isEmpty() ? 0 : nodes.first();
 
tasak
2013/07/16 07:46:59
I added findElementByClassName and removed this co
 
 | 
| + } | 
| + } | 
| + | 
| + std::pair<bool, Node*> traverseRoot = findTraverseRoot(rootNode); | 
| 
 
esprehn
2013/07/12 07:17:17
I think you want to return a Node* and have an out
 
tasak
2013/07/16 07:46:59
Done.
 
 | 
| + if (!traverseRoot.second) | 
| + return 0; | 
| + Node* traverseRootNode = traverseRoot.second; | 
| + if (traverseRoot.first) { | 
| + ASSERT(m_selectors.size() == 1); | 
| + ASSERT(traverseRootNode->isElementNode()); | 
| + Element* element = toElement(traverseRootNode); | 
| + return selectorMatches(m_selectors[0], element, rootNode) ? element : 0; | 
| + } | 
| + rootNode = traverseRootNode; | 
| } | 
| 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; | 
| - } | 
| + if (selectorMatches(selector, element, rootNode)) | 
| + return element; | 
| } | 
| - return; | 
| + return 0; | 
| } | 
| + | 
| 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; | 
| - } | 
| + if (selectorMatches(m_selectors[i], element, rootNode)) | 
| + return element; | 
| } | 
| } | 
| + return 0; | 
| } | 
| SelectorQuery::SelectorQuery(const CSSSelectorList& selectorList) |