2 * Copyright (C) 2011, 2013 Apple Inc. All rights reserved.
3 * Copyright (C) 2014 Samsung Electronics. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "core/dom/SelectorQuery.h"
30 #include "bindings/v8/ExceptionState.h"
31 #include "core/css/parser/BisonCSSParser.h"
32 #include "core/css/SelectorChecker.h"
33 #include "core/css/SiblingTraversalStrategies.h"
34 #include "core/dom/Document.h"
35 #include "core/dom/ElementTraversal.h"
36 #include "core/dom/Node.h"
37 #include "core/dom/StaticNodeList.h"
38 #include "core/dom/shadow/ElementShadow.h"
39 #include "core/dom/shadow/ShadowRoot.h"
43 struct SingleElementSelectorQueryTrait {
44 typedef Element* OutputType;
45 static const bool shouldOnlyMatchFirstElement = true;
46 ALWAYS_INLINE static void appendElement(OutputType& output, Element& element)
53 struct AllElementsSelectorQueryTrait {
54 typedef WillBeHeapVector<RefPtrWillBeMember<Node> > OutputType;
55 static const bool shouldOnlyMatchFirstElement = false;
56 ALWAYS_INLINE static void appendElement(OutputType& output, Node& element)
58 output.append(&element);
62 enum ClassElementListBehavior { AllElements, OnlyRoots };
64 template <ClassElementListBehavior onlyRoots>
65 class ClassElementList {
68 ClassElementList(ContainerNode& rootNode, const AtomicString& className)
69 : m_className(className)
70 , m_rootNode(&rootNode)
71 , m_currentElement(nextInternal(ElementTraversal::firstWithin(rootNode))) { }
73 bool isEmpty() const { return !m_currentElement; }
77 Element* current = m_currentElement;
80 m_currentElement = nextInternal(ElementTraversal::nextSkippingChildren(*m_currentElement, m_rootNode));
82 m_currentElement = nextInternal(ElementTraversal::next(*m_currentElement, m_rootNode));
87 Element* nextInternal(Element* element)
89 for (; element; element = ElementTraversal::next(*element, m_rootNode)) {
90 if (element->hasClass() && element->classNames().contains(m_className))
96 const AtomicString& m_className;
97 RawPtrWillBeMember<ContainerNode> m_rootNode;
98 RawPtrWillBeMember<Element> m_currentElement;
101 void SelectorDataList::initialize(const CSSSelectorList& selectorList)
103 ASSERT(m_selectors.isEmpty());
105 unsigned selectorCount = 0;
106 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector))
109 m_crossesTreeBoundary = false;
110 m_selectors.reserveInitialCapacity(selectorCount);
112 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector), ++index) {
113 m_selectors.uncheckedAppend(selector);
114 m_crossesTreeBoundary |= selectorList.selectorCrossesTreeScopes(index);
118 inline bool SelectorDataList::selectorMatches(const CSSSelector& selector, Element& element, const ContainerNode& rootNode) const
120 SelectorChecker selectorChecker(element.document(), SelectorChecker::QueryingRules);
121 SelectorChecker::SelectorCheckingContext selectorCheckingContext(selector, &element, SelectorChecker::VisitedMatchDisabled);
122 selectorCheckingContext.behaviorAtBoundary = SelectorChecker::StaysWithinTreeScope;
123 selectorCheckingContext.scope = !rootNode.isDocumentNode() ? &rootNode : 0;
124 if (selectorCheckingContext.scope)
125 selectorCheckingContext.behaviorAtBoundary = static_cast<SelectorChecker::BehaviorAtBoundary>(SelectorChecker::StaysWithinTreeScope | SelectorChecker::ScopeContainsLastMatchedElement);
126 return selectorChecker.match(selectorCheckingContext, DOMSiblingTraversalStrategy()) == SelectorChecker::SelectorMatches;
129 bool SelectorDataList::matches(Element& targetElement) const
131 unsigned selectorCount = m_selectors.size();
132 for (unsigned i = 0; i < selectorCount; ++i) {
133 if (selectorMatches(*m_selectors[i], targetElement, targetElement))
140 PassRefPtrWillBeRawPtr<StaticNodeList> SelectorDataList::queryAll(ContainerNode& rootNode) const
142 WillBeHeapVector<RefPtrWillBeMember<Node> > result;
143 execute<AllElementsSelectorQueryTrait>(rootNode, result);
144 return StaticNodeList::adopt(result);
147 PassRefPtrWillBeRawPtr<Element> SelectorDataList::queryFirst(ContainerNode& rootNode) const
149 Element* matchedElement = 0;
150 execute<SingleElementSelectorQueryTrait>(rootNode, matchedElement);
151 return matchedElement;
154 template <typename SelectorQueryTrait>
155 void SelectorDataList::collectElementsByClassName(ContainerNode& rootNode, const AtomicString& className, typename SelectorQueryTrait::OutputType& output) const
157 for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
158 if (element->hasClass() && element->classNames().contains(className)) {
159 SelectorQueryTrait::appendElement(output, *element);
160 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
166 template <typename SelectorQueryTrait>
167 void SelectorDataList::collectElementsByTagName(ContainerNode& rootNode, const QualifiedName& tagName, typename SelectorQueryTrait::OutputType& output) const
169 for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
170 if (SelectorChecker::tagMatches(*element, tagName)) {
171 SelectorQueryTrait::appendElement(output, *element);
172 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
178 inline bool SelectorDataList::canUseFastQuery(const ContainerNode& rootNode) const
180 return m_selectors.size() == 1 && !m_crossesTreeBoundary && rootNode.inDocument() && !rootNode.document().inQuirksMode();
183 inline bool ancestorHasClassName(ContainerNode& rootNode, const AtomicString& className)
185 if (!rootNode.isElementNode())
188 for (Element* element = &toElement(rootNode); element; element = element->parentElement()) {
189 if (element->hasClass() && element->classNames().contains(className))
196 // If returns true, traversalRoots has the elements that may match the selector query.
198 // If returns false, traversalRoots has the rootNode parameter or descendants of rootNode representing
199 // the subtree for which we can limit the querySelector traversal.
201 // The travseralRoots may be empty, regardless of the returned bool value, if this method finds that the selectors won't
202 // match any element.
203 template <typename SelectorQueryTrait>
204 void SelectorDataList::findTraverseRootsAndExecute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
206 // We need to return the matches in document order. To use id lookup while there is possiblity of multiple matches
207 // we would need to sort the results. For now, just traverse the document in that case.
208 ASSERT(m_selectors.size() == 1);
210 bool isRightmostSelector = true;
211 bool startFromParent = false;
213 for (const CSSSelector* selector = m_selectors[0]; selector; selector = selector->tagHistory()) {
214 if (selector->match() == CSSSelector::Id && !rootNode.document().containsMultipleElementsWithId(selector->value())) {
215 Element* element = rootNode.treeScope().getElementById(selector->value());
216 ContainerNode* adjustedNode = &rootNode;
217 if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode)))
218 adjustedNode = element;
219 else if (!element || isRightmostSelector)
221 if (isRightmostSelector) {
222 executeForTraverseRoot<SelectorQueryTrait>(*m_selectors[0], adjustedNode, MatchesTraverseRoots, rootNode, output);
226 if (startFromParent && adjustedNode)
227 adjustedNode = adjustedNode->parentNode();
229 executeForTraverseRoot<SelectorQueryTrait>(*m_selectors[0], adjustedNode, DoesNotMatchTraverseRoots, rootNode, output);
233 // If we have both CSSSelector::Id and CSSSelector::Class at the same time, we should use Id
234 // to find traverse root.
235 if (!SelectorQueryTrait::shouldOnlyMatchFirstElement && !startFromParent && selector->match() == CSSSelector::Class) {
236 if (isRightmostSelector) {
237 ClassElementList<AllElements> traverseRoots(rootNode, selector->value());
238 executeForTraverseRoots<SelectorQueryTrait>(*m_selectors[0], traverseRoots, MatchesTraverseRoots, rootNode, output);
241 // Since there exists some ancestor element which has the class name, we need to see all children of rootNode.
242 if (ancestorHasClassName(rootNode, selector->value())) {
243 executeForTraverseRoot<SelectorQueryTrait>(*m_selectors[0], &rootNode, DoesNotMatchTraverseRoots, rootNode, output);
247 ClassElementList<OnlyRoots> traverseRoots(rootNode, selector->value());
248 executeForTraverseRoots<SelectorQueryTrait>(*m_selectors[0], traverseRoots, DoesNotMatchTraverseRoots, rootNode, output);
252 if (selector->relation() == CSSSelector::SubSelector)
254 isRightmostSelector = false;
255 if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent)
256 startFromParent = true;
258 startFromParent = false;
261 executeForTraverseRoot<SelectorQueryTrait>(*m_selectors[0], &rootNode, DoesNotMatchTraverseRoots, rootNode, output);
264 template <typename SelectorQueryTrait>
265 void SelectorDataList::executeForTraverseRoot(const CSSSelector& selector, ContainerNode* traverseRoot, MatchTraverseRootState matchTraverseRoot, ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
270 if (matchTraverseRoot) {
271 if (selectorMatches(selector, toElement(*traverseRoot), rootNode))
272 SelectorQueryTrait::appendElement(output, toElement(*traverseRoot));
276 for (Element* element = ElementTraversal::firstWithin(*traverseRoot); element; element = ElementTraversal::next(*element, traverseRoot)) {
277 if (selectorMatches(selector, *element, rootNode)) {
278 SelectorQueryTrait::appendElement(output, *element);
279 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
285 template <typename SelectorQueryTrait, typename SimpleElementListType>
286 void SelectorDataList::executeForTraverseRoots(const CSSSelector& selector, SimpleElementListType& traverseRoots, MatchTraverseRootState matchTraverseRoots, ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
288 if (traverseRoots.isEmpty())
291 if (matchTraverseRoots) {
292 while (!traverseRoots.isEmpty()) {
293 Element& element = *traverseRoots.next();
294 if (selectorMatches(selector, element, rootNode)) {
295 SelectorQueryTrait::appendElement(output, element);
296 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
303 while (!traverseRoots.isEmpty()) {
304 Element& traverseRoot = *traverseRoots.next();
305 for (Element* element = ElementTraversal::firstWithin(traverseRoot); element; element = ElementTraversal::next(*element, &traverseRoot)) {
306 if (selectorMatches(selector, *element, rootNode)) {
307 SelectorQueryTrait::appendElement(output, *element);
308 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
315 template <typename SelectorQueryTrait>
316 bool SelectorDataList::selectorListMatches(ContainerNode& rootNode, Element& element, typename SelectorQueryTrait::OutputType& output) const
318 for (unsigned i = 0; i < m_selectors.size(); ++i) {
319 if (selectorMatches(*m_selectors[i], element, rootNode)) {
320 SelectorQueryTrait::appendElement(output, element);
327 template <typename SelectorQueryTrait>
328 void SelectorDataList::executeSlow(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
330 for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) {
331 if (selectorListMatches<SelectorQueryTrait>(rootNode, *element, output) && SelectorQueryTrait::shouldOnlyMatchFirstElement)
336 // FIXME: Move the following helper functions, authorShadowRootOf, firstWithinTraversingShadowTree,
337 // nextTraversingShadowTree to the best place, e.g. NodeTraversal.
338 static ShadowRoot* authorShadowRootOf(const ContainerNode& node)
340 if (!node.isElementNode() || !isShadowHost(&node))
343 ElementShadow* shadow = toElement(node).shadow();
345 for (ShadowRoot* shadowRoot = shadow->oldestShadowRoot(); shadowRoot; shadowRoot = shadowRoot->youngerShadowRoot()) {
346 if (shadowRoot->type() == ShadowRoot::AuthorShadowRoot)
352 static ContainerNode* firstWithinTraversingShadowTree(const ContainerNode& rootNode)
354 if (ShadowRoot* shadowRoot = authorShadowRootOf(rootNode))
356 return ElementTraversal::firstWithin(rootNode);
359 static ContainerNode* nextTraversingShadowTree(const ContainerNode& node, const ContainerNode* rootNode)
361 if (ShadowRoot* shadowRoot = authorShadowRootOf(node))
364 const ContainerNode* current = &node;
366 if (Element* next = ElementTraversal::next(*current, rootNode))
369 if (!current->isInShadowTree())
372 ShadowRoot* shadowRoot = current->containingShadowRoot();
373 if (shadowRoot == rootNode)
375 if (ShadowRoot* youngerShadowRoot = shadowRoot->youngerShadowRoot()) {
376 // Should not obtain any elements in user-agent shadow root.
377 ASSERT(youngerShadowRoot->type() == ShadowRoot::AuthorShadowRoot);
378 return youngerShadowRoot;
381 current = shadowRoot->host();
386 template <typename SelectorQueryTrait>
387 void SelectorDataList::executeSlowTraversingShadowTree(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
389 for (ContainerNode* node = firstWithinTraversingShadowTree(rootNode); node; node = nextTraversingShadowTree(*node, &rootNode)) {
390 if (!node->isElementNode())
392 Element* element = toElement(node);
393 if (selectorListMatches<SelectorQueryTrait>(rootNode, *element, output) && SelectorQueryTrait::shouldOnlyMatchFirstElement)
398 const CSSSelector* SelectorDataList::selectorForIdLookup(const CSSSelector& firstSelector) const
400 for (const CSSSelector* selector = &firstSelector; selector; selector = selector->tagHistory()) {
401 if (selector->match() == CSSSelector::Id)
403 if (selector->relation() != CSSSelector::SubSelector)
409 template <typename SelectorQueryTrait>
410 void SelectorDataList::execute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
412 if (!canUseFastQuery(rootNode)) {
413 if (m_crossesTreeBoundary) {
414 rootNode.document().updateDistributionForNodeIfNeeded(&rootNode);
415 executeSlowTraversingShadowTree<SelectorQueryTrait>(rootNode, output);
417 executeSlow<SelectorQueryTrait>(rootNode, output);
422 ASSERT(m_selectors.size() == 1);
424 const CSSSelector& selector = *m_selectors[0];
425 const CSSSelector& firstSelector = selector;
427 // Fast path for querySelector*('#id'), querySelector*('tag#id').
428 if (const CSSSelector* idSelector = selectorForIdLookup(firstSelector)) {
429 const AtomicString& idToMatch = idSelector->value();
430 if (rootNode.treeScope().containsMultipleElementsWithId(idToMatch)) {
431 const Vector<Element*>& elements = rootNode.treeScope().getAllElementsById(idToMatch);
432 size_t count = elements.size();
433 for (size_t i = 0; i < count; ++i) {
434 Element& element = *elements[i];
435 if (!(isTreeScopeRoot(rootNode) || element.isDescendantOf(&rootNode)))
437 if (selectorMatches(selector, element, rootNode)) {
438 SelectorQueryTrait::appendElement(output, element);
439 if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
445 Element* element = rootNode.treeScope().getElementById(idToMatch);
446 if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode)))
448 if (selectorMatches(selector, *element, rootNode))
449 SelectorQueryTrait::appendElement(output, *element);
453 if (!firstSelector.tagHistory()) {
454 // Fast path for querySelector*('.foo'), and querySelector*('div').
455 switch (firstSelector.match()) {
456 case CSSSelector::Class:
457 collectElementsByClassName<SelectorQueryTrait>(rootNode, firstSelector.value(), output);
459 case CSSSelector::Tag:
460 collectElementsByTagName<SelectorQueryTrait>(rootNode, firstSelector.tagQName(), output);
463 break; // If we need another fast path, add here.
467 findTraverseRootsAndExecute<SelectorQueryTrait>(rootNode, output);
470 PassOwnPtr<SelectorQuery> SelectorQuery::adopt(CSSSelectorList& selectorList)
472 return adoptPtr(new SelectorQuery(selectorList));
475 SelectorQuery::SelectorQuery(CSSSelectorList& selectorList)
477 m_selectorList.adopt(selectorList);
478 m_selectors.initialize(m_selectorList);
481 bool SelectorQuery::matches(Element& element) const
483 return m_selectors.matches(element);
486 PassRefPtrWillBeRawPtr<StaticNodeList> SelectorQuery::queryAll(ContainerNode& rootNode) const
488 return m_selectors.queryAll(rootNode);
491 PassRefPtrWillBeRawPtr<Element> SelectorQuery::queryFirst(ContainerNode& rootNode) const
493 return m_selectors.queryFirst(rootNode);
496 SelectorQuery* SelectorQueryCache::add(const AtomicString& selectors, const Document& document, ExceptionState& exceptionState)
498 HashMap<AtomicString, OwnPtr<SelectorQuery> >::iterator it = m_entries.find(selectors);
499 if (it != m_entries.end())
500 return it->value.get();
502 BisonCSSParser parser(CSSParserContext(document, 0));
503 CSSSelectorList selectorList;
504 parser.parseSelector(selectors, selectorList);
506 if (!selectorList.first()) {
507 exceptionState.throwDOMException(SyntaxError, "'" + selectors + "' is not a valid selector.");
511 // throw a NamespaceError if the selector includes any namespace prefixes.
512 if (selectorList.selectorsNeedNamespaceResolution()) {
513 exceptionState.throwDOMException(NamespaceError, "'" + selectors + "' contains namespaces, which are not supported.");
517 const unsigned maximumSelectorQueryCacheSize = 256;
518 if (m_entries.size() == maximumSelectorQueryCacheSize)
519 m_entries.remove(m_entries.begin());
521 return m_entries.add(selectors, SelectorQuery::adopt(selectorList)).storedValue->value.get();
524 void SelectorQueryCache::invalidate()