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

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

Issue 18732004: Add fast path for querySelector(All) (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 7 years, 5 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 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)
« 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