Index: Source/core/dom/SelectorQuery.cpp |
diff --git a/Source/core/dom/SelectorQuery.cpp b/Source/core/dom/SelectorQuery.cpp |
index 5fcf2ac55a2693a1504cb64febc5832e164ba1d9..e5283e4dee54b35291c7a48ee5bf001779be33fb 100644 |
--- a/Source/core/dom/SelectorQuery.cpp |
+++ b/Source/core/dom/SelectorQuery.cpp |
@@ -306,6 +306,17 @@ void SelectorDataList::executeSlowQueryAll(Node& rootNode, Vector<RefPtr<Node> > |
} |
} |
+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)) |
@@ -314,22 +325,34 @@ void SelectorDataList::executeQueryAll(Node& rootNode, Vector<RefPtr<Node> >& ma |
ASSERT(m_selectors.size() == 1); |
ASSERT(m_selectors[0].selector); |
- const CSSSelector* firstSelector = m_selectors[0].selector; |
+ const SelectorData& selector = m_selectors[0]; |
+ const CSSSelector* firstSelector = selector.selector; |
+ |
+ // 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 (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); |
+ return; |
+ } |
if (!firstSelector->tagHistory()) { |
- // Fast path for querySelectorAll('#id'), querySelectorAl('.foo'), and querySelectorAll('div'). |
+ // Fast path for 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: |
@@ -344,7 +367,6 @@ void SelectorDataList::executeQueryAll(Node& rootNode, Vector<RefPtr<Node> >& ma |
if (traverseRoots->isEmpty()) |
return; |
- const SelectorData& selector = m_selectors[0]; |
if (matchTraverseRoots) { |
while (!traverseRoots->isEmpty()) { |
Node& node = *traverseRoots->next(); |
@@ -426,25 +448,40 @@ Element* SelectorDataList::executeQueryFirst(Node& rootNode) const |
if (!canUseFastQuery(rootNode)) |
return executeSlowQueryFirst(rootNode); |
+ ASSERT(m_selectors.size() == 1); |
+ ASSERT(m_selectors[0].selector); |
- const CSSSelector* selector = m_selectors[0].selector; |
- ASSERT(selector); |
+ const SelectorData& selector = m_selectors[0]; |
+ const CSSSelector* firstSelector = selector.selector; |
+ |
+ // 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 (selectorMatches(selector, *element, rootNode)) |
+ return element; |
+ } |
+ return 0; |
+ } |
+ Element* element = rootNode.treeScope().getElementById(idToMatch); |
+ if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode))) |
+ return 0; |
+ return selectorMatches(selector, *element, rootNode) ? element : 0; |
+ } |
- if (!selector->tagHistory()) { |
- // Fast path for querySelector('#id'), querySelector('.foo'), and querySelector('div'). |
+ if (!firstSelector->tagHistory()) { |
+ // Fast path for 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; |
- } |
+ switch (firstSelector->m_match) { |
case CSSSelector::Class: |
- return findElementByClassName(rootNode, selector->value()); |
+ return findElementByClassName(rootNode, firstSelector->value()); |
case CSSSelector::Tag: |
- return findElementByTagName(rootNode, selector->tagQName()); |
+ return findElementByTagName(rootNode, firstSelector->tagQName()); |
default: |
break; // If we need another fast path, add here. |
} |
@@ -455,14 +492,13 @@ Element* SelectorDataList::executeQueryFirst(Node& rootNode) const |
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; |
+ return selectorMatches(selector, element, rootNode) ? &element : 0; |
} |
for (Element* element = ElementTraversal::firstWithin(*traverseRootNode); element; element = ElementTraversal::next(*element, traverseRootNode)) { |
- if (selectorMatches(m_selectors[0], *element, rootNode)) |
+ if (selectorMatches(selector, *element, rootNode)) |
return element; |
} |
return 0; |