Chromium Code Reviews| Index: Source/core/dom/SelectorQuery.cpp |
| diff --git a/Source/core/dom/SelectorQuery.cpp b/Source/core/dom/SelectorQuery.cpp |
| index 32ed0f30b0b86c7608837975df3f1f731ab591d6..be97d8f6b52014082cb66f02d769b37113a30cac 100644 |
| --- a/Source/core/dom/SelectorQuery.cpp |
| +++ b/Source/core/dom/SelectorQuery.cpp |
| @@ -104,74 +104,142 @@ static inline bool isTreeScopeRoot(Node* node) |
| return node->isDocumentNode() || node->isShadowRoot(); |
| } |
| -// If the first pair value is true, the returned Node is the single Element that may match the selector query. |
| +static bool hasNodeContains(DocumentOrderedList& rootNodes, Node* node) |
| +{ |
| + for (DocumentOrderedList::iterator it = rootNodes.begin(); it != rootNodes.end(); ++it) { |
| + Node* rootNode = *it; |
| + if (rootNode->contains(node)) |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +static void addWithRemoveNodesContainedBy(DocumentOrderedList& rootNodes, Node* rootNode) |
| +{ |
| + rootNodes.add(rootNode); |
| + |
| + // Remove nodes contained by a given rootNode, because rootNode is not given in document order. |
| + DocumentOrderedList::iterator it = rootNodes.end(); |
| + do { |
| + --it; |
| + if (rootNode == *it) |
| + break; |
| + if (rootNode->contains(*it)) |
| + rootNodes.remove(*it); |
| + } while (it != rootNodes.begin()); |
| +} |
| + |
| +// If returns true, traversalRoots has the elements 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 |
| +// If returns false, traversalRoots has the rootNode parameter or descendants of rootNode representing |
| // the subtree for which we can limit the querySelector traversal. |
| // |
| -// The returned Node may be 0, regardless of the returned bool value, if this method finds that the selectors won't |
| +// The travseralRoots may be empty, regardless of the returned bool value, if this method finds that the selectors won't |
| // match any element. |
| -std::pair<bool, Node*> SelectorDataList::findTraverseRoot(Node* rootNode) const |
| +bool SelectorDataList::findTraverseRoot(Node* rootNode, DocumentOrderedList& 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 (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; |
| + if (m_selectors.size() != 1 || !rootNode->inDocument() || rootNode->document()->inQuirksMode()) { |
| + traversalRoots.parserAdd(rootNode); |
| + return false; |
| + } |
| + |
| + bool matchTraverseRoots = 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) |
| + else if (!element || matchTraverseRoots) |
| rootNode = 0; |
| - if (matchSingleNode) |
| - return std::make_pair(true, rootNode); |
| + if (matchTraverseRoots) { |
| + if (rootNode) |
| + traversalRoots.parserAdd(rootNode); |
| + return true; |
| + } |
| if (startFromParent && rootNode) |
| rootNode = rootNode->parentNode(); |
| - return std::make_pair(false, rootNode); |
| + if (rootNode) |
| + traversalRoots.parserAdd(rootNode); |
| + return false; |
| + } |
| + if (selector->m_match == CSSSelector::Class) { |
| + RefPtr<NodeList> nodeList = rootNode->getElementsByClassName(selector->value()); |
| + if (startFromParent) { |
| + // Looking at parent nodes, we need to sort in document order. |
| + for (unsigned i = 0; i < nodeList->length(); ++i) { |
| + Node* node = nodeList->item(i); |
| + if (!node || (!isTreeScopeRoot(rootNode) && !node->isDescendantOf(rootNode))) |
| + continue; |
| + if (Node* parent = node->parentNode()) { |
| + if (!hasNodeContains(traversalRoots, parent)) |
| + addWithRemoveNodesContainedBy(traversalRoots, parent); |
| + } |
| + } |
| + } else if (matchTraverseRoots) { |
| + for (unsigned i = 0; i < nodeList->length(); ++i) { |
| + Node* node = nodeList->item(i); |
| + if (node && (isTreeScopeRoot(rootNode) || node->isDescendantOf(rootNode))) |
| + traversalRoots.parserAdd(node); |
| + } |
| + return true; |
| + } else { |
| + for (unsigned i = 0; i < nodeList->length(); ++i) { |
| + Node* node = nodeList->item(i); |
| + if (!node || (!isTreeScopeRoot(rootNode) && !node->isDescendantOf(rootNode))) |
| + continue; |
| + // Need to check whether the new node is contained by other nodes. |
| + if (!hasNodeContains(traversalRoots, node)) |
| + traversalRoots.add(node); |
| + } |
| + } |
| + return false; |
|
haraken
2013/07/08 06:13:16
This change looks correct, but we might want to ha
|
| } |
| + |
| if (selector->relation() == CSSSelector::SubSelector) |
| continue; |
| - matchSingleNode = false; |
| + matchTraverseRoots = false; |
| if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent) |
| startFromParent = true; |
| else |
| startFromParent = false; |
| } |
| - return std::make_pair(false, rootNode); |
| + if (rootNode) |
| + traversalRoots.parserAdd(rootNode); |
| + return false; |
| } |
| template <bool firstMatchOnly> |
| void SelectorDataList::execute(Node* rootNode, Vector<RefPtr<Node> >& matchedElements) const |
| { |
| - std::pair<bool, Node*> traverseRoot = findTraverseRoot(rootNode); |
| - if (!traverseRoot.second) |
| + DocumentOrderedList traverseRoots; |
| + bool matchTraverseRoots = findTraverseRoot(rootNode, traverseRoots); |
| + |
| + if (traverseRoots.isEmpty()) |
| return; |
| - Node* traverseRootNode = traverseRoot.second; |
| - if (traverseRoot.first) { |
| + if (matchTraverseRoots) { |
| ASSERT(m_selectors.size() == 1); |
| - ASSERT(traverseRootNode->isElementNode()); |
| - Element* element = toElement(traverseRootNode); |
| - if (selectorMatches(m_selectors[0], element, rootNode)) |
| - matchedElements.append(element); |
| + for (DocumentOrderedList::iterator it = traverseRoots.begin(); it != traverseRoots.end(); ++it) { |
| + Element* element = toElement(*it); |
| + 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; |
| + for (DocumentOrderedList::iterator it = traverseRoots.begin(); it != traverseRoots.end(); ++it) { |
| + Node* traverseRoot = *it; |
| + for (Element* element = ElementTraversal::firstWithin(traverseRoot); element; element = ElementTraversal::next(element, traverseRoot)) { |
| + if (selectorMatches(selector, element, rootNode)) { |
| + matchedElements.append(element); |
| + if (firstMatchOnly) |
| + return; |
| + } |
| } |
| } |
| return; |