1 /* 2 * Copyright (C) 2011, 2013 Apple Inc. All rights reserved. 3 * Copyright (C) 2014 Samsung Electronics. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 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. 14 * 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. 25 */ 26 27 #include "config.h" 28 #include "core/dom/SelectorQuery.h" 29 30 #include "bindings/core/v8/ExceptionState.h" 31 #include "core/css/SelectorChecker.h" 32 #include "core/css/SiblingTraversalStrategies.h" 33 #include "core/css/parser/CSSParser.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" 40 41 namespace blink { 42 43 struct SingleElementSelectorQueryTrait { 44 typedef Element* OutputType; 45 static const bool shouldOnlyMatchFirstElement = true; 46 ALWAYS_INLINE static void appendElement(OutputType& output, Element& element) 47 { 48 ASSERT(!output); 49 output = &element; 50 } 51 }; 52 53 struct AllElementsSelectorQueryTrait { 54 typedef WillBeHeapVector<RefPtrWillBeMember<Element> > OutputType; 55 static const bool shouldOnlyMatchFirstElement = false; 56 ALWAYS_INLINE static void appendElement(OutputType& output, Element& element) 57 { 58 output.append(&element); 59 } 60 }; 61 62 enum ClassElementListBehavior { AllElements, OnlyRoots }; 63 64 template <ClassElementListBehavior onlyRoots> 65 class ClassElementList { 66 STACK_ALLOCATED(); 67 public: 68 ClassElementList(ContainerNode& rootNode, const AtomicString& className) 69 : m_className(className) 70 , m_rootNode(&rootNode) 71 , m_currentElement(nextInternal(ElementTraversal::firstWithin(rootNode))) { } 72 73 bool isEmpty() const { return !m_currentElement; } 74 75 Element* next() 76 { 77 Element* current = m_currentElement; 78 ASSERT(current); 79 if (onlyRoots) 80 m_currentElement = nextInternal(ElementTraversal::nextSkippingChildren(*m_currentElement, m_rootNode)); 81 else 82 m_currentElement = nextInternal(ElementTraversal::next(*m_currentElement, m_rootNode)); 83 return current; 84 } 85 86 private: 87 Element* nextInternal(Element* element) 88 { 89 for (; element; element = ElementTraversal::next(*element, m_rootNode)) { 90 if (element->hasClass() && element->classNames().contains(m_className)) 91 return element; 92 } 93 return 0; 94 } 95 96 const AtomicString& m_className; 97 RawPtrWillBeMember<ContainerNode> m_rootNode; 98 RawPtrWillBeMember<Element> m_currentElement; 99 }; 100 101 void SelectorDataList::initialize(const CSSSelectorList& selectorList) 102 { 103 ASSERT(m_selectors.isEmpty()); 104 105 unsigned selectorCount = 0; 106 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector)) 107 selectorCount++; 108 109 m_crossesTreeBoundary = false; 110 m_selectors.reserveInitialCapacity(selectorCount); 111 unsigned index = 0; 112 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector), ++index) { 113 m_selectors.uncheckedAppend(selector); 114 m_crossesTreeBoundary |= selectorList.selectorCrossesTreeScopes(index); 115 } 116 } 117 118 inline bool SelectorDataList::selectorMatches(const CSSSelector& selector, Element& element, const ContainerNode& rootNode) const 119 { 120 SelectorChecker selectorChecker(element.document(), SelectorChecker::QueryingRules); 121 SelectorChecker::SelectorCheckingContext selectorCheckingContext(selector, &element, SelectorChecker::VisitedMatchDisabled); 122 selectorCheckingContext.scope = !rootNode.isDocumentNode() ? &rootNode : 0; 123 if (selectorCheckingContext.scope) 124 selectorCheckingContext.contextFlags = SelectorChecker::ScopeContainsLastMatchedElement; 125 return selectorChecker.match(selectorCheckingContext, DOMSiblingTraversalStrategy()) == SelectorChecker::SelectorMatches; 126 } 127 128 bool SelectorDataList::matches(Element& targetElement) const 129 { 130 unsigned selectorCount = m_selectors.size(); 131 for (unsigned i = 0; i < selectorCount; ++i) { 132 if (selectorMatches(*m_selectors[i], targetElement, targetElement)) 133 return true; 134 } 135 136 return false; 137 } 138 139 PassRefPtrWillBeRawPtr<StaticElementList> SelectorDataList::queryAll(ContainerNode& rootNode) const 140 { 141 WillBeHeapVector<RefPtrWillBeMember<Element> > result; 142 execute<AllElementsSelectorQueryTrait>(rootNode, result); 143 return StaticElementList::adopt(result); 144 } 145 146 PassRefPtrWillBeRawPtr<Element> SelectorDataList::queryFirst(ContainerNode& rootNode) const 147 { 148 Element* matchedElement = 0; 149 execute<SingleElementSelectorQueryTrait>(rootNode, matchedElement); 150 return matchedElement; 151 } 152 153 template <typename SelectorQueryTrait> 154 void SelectorDataList::collectElementsByClassName(ContainerNode& rootNode, const AtomicString& className, typename SelectorQueryTrait::OutputType& output) const 155 { 156 for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) { 157 if (element->hasClass() && element->classNames().contains(className)) { 158 SelectorQueryTrait::appendElement(output, *element); 159 if (SelectorQueryTrait::shouldOnlyMatchFirstElement) 160 return; 161 } 162 } 163 } 164 165 template <typename SelectorQueryTrait> 166 void SelectorDataList::collectElementsByTagName(ContainerNode& rootNode, const QualifiedName& tagName, typename SelectorQueryTrait::OutputType& output) const 167 { 168 for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) { 169 if (SelectorChecker::tagMatches(*element, tagName)) { 170 SelectorQueryTrait::appendElement(output, *element); 171 if (SelectorQueryTrait::shouldOnlyMatchFirstElement) 172 return; 173 } 174 } 175 } 176 177 inline bool SelectorDataList::canUseFastQuery(const ContainerNode& rootNode) const 178 { 179 return m_selectors.size() == 1 && !m_crossesTreeBoundary && rootNode.inDocument() && !rootNode.document().inQuirksMode(); 180 } 181 182 inline bool ancestorHasClassName(ContainerNode& rootNode, const AtomicString& className) 183 { 184 if (!rootNode.isElementNode()) 185 return false; 186 187 for (Element* element = &toElement(rootNode); element; element = element->parentElement()) { 188 if (element->hasClass() && element->classNames().contains(className)) 189 return true; 190 } 191 return false; 192 } 193 194 195 // If returns true, traversalRoots has the elements that may match the selector query. 196 // 197 // If returns false, traversalRoots has the rootNode parameter or descendants of rootNode representing 198 // the subtree for which we can limit the querySelector traversal. 199 // 200 // The travseralRoots may be empty, regardless of the returned bool value, if this method finds that the selectors won't 201 // match any element. 202 template <typename SelectorQueryTrait> 203 void SelectorDataList::findTraverseRootsAndExecute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const 204 { 205 // We need to return the matches in document order. To use id lookup while there is possiblity of multiple matches 206 // we would need to sort the results. For now, just traverse the document in that case. 207 ASSERT(m_selectors.size() == 1); 208 209 bool isRightmostSelector = true; 210 bool startFromParent = false; 211 212 for (const CSSSelector* selector = m_selectors[0]; selector; selector = selector->tagHistory()) { 213 if (selector->match() == CSSSelector::Id && !rootNode.document().containsMultipleElementsWithId(selector->value())) { 214 Element* element = rootNode.treeScope().getElementById(selector->value()); 215 ContainerNode* adjustedNode = &rootNode; 216 if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode))) 217 adjustedNode = element; 218 else if (!element || isRightmostSelector) 219 adjustedNode = 0; 220 if (isRightmostSelector) { 221 executeForTraverseRoot<SelectorQueryTrait>(*m_selectors[0], adjustedNode, MatchesTraverseRoots, rootNode, output); 222 return; 223 } 224 225 if (startFromParent && adjustedNode) 226 adjustedNode = adjustedNode->parentNode(); 227 228 executeForTraverseRoot<SelectorQueryTrait>(*m_selectors[0], adjustedNode, DoesNotMatchTraverseRoots, rootNode, output); 229 return; 230 } 231 232 // If we have both CSSSelector::Id and CSSSelector::Class at the same time, we should use Id 233 // to find traverse root. 234 if (!SelectorQueryTrait::shouldOnlyMatchFirstElement && !startFromParent && selector->match() == CSSSelector::Class) { 235 if (isRightmostSelector) { 236 ClassElementList<AllElements> traverseRoots(rootNode, selector->value()); 237 executeForTraverseRoots<SelectorQueryTrait>(*m_selectors[0], traverseRoots, MatchesTraverseRoots, rootNode, output); 238 return; 239 } 240 // Since there exists some ancestor element which has the class name, we need to see all children of rootNode. 241 if (ancestorHasClassName(rootNode, selector->value())) { 242 executeForTraverseRoot<SelectorQueryTrait>(*m_selectors[0], &rootNode, DoesNotMatchTraverseRoots, rootNode, output); 243 return; 244 } 245 246 ClassElementList<OnlyRoots> traverseRoots(rootNode, selector->value()); 247 executeForTraverseRoots<SelectorQueryTrait>(*m_selectors[0], traverseRoots, DoesNotMatchTraverseRoots, rootNode, output); 248 return; 249 } 250 251 if (selector->relation() == CSSSelector::SubSelector) 252 continue; 253 isRightmostSelector = false; 254 if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent) 255 startFromParent = true; 256 else 257 startFromParent = false; 258 } 259 260 executeForTraverseRoot<SelectorQueryTrait>(*m_selectors[0], &rootNode, DoesNotMatchTraverseRoots, rootNode, output); 261 } 262 263 template <typename SelectorQueryTrait> 264 void SelectorDataList::executeForTraverseRoot(const CSSSelector& selector, ContainerNode* traverseRoot, MatchTraverseRootState matchTraverseRoot, ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const 265 { 266 if (!traverseRoot) 267 return; 268 269 if (matchTraverseRoot) { 270 if (selectorMatches(selector, toElement(*traverseRoot), rootNode)) 271 SelectorQueryTrait::appendElement(output, toElement(*traverseRoot)); 272 return; 273 } 274 275 for (Element* element = ElementTraversal::firstWithin(*traverseRoot); element; element = ElementTraversal::next(*element, traverseRoot)) { 276 if (selectorMatches(selector, *element, rootNode)) { 277 SelectorQueryTrait::appendElement(output, *element); 278 if (SelectorQueryTrait::shouldOnlyMatchFirstElement) 279 return; 280 } 281 } 282 } 283 284 template <typename SelectorQueryTrait, typename SimpleElementListType> 285 void SelectorDataList::executeForTraverseRoots(const CSSSelector& selector, SimpleElementListType& traverseRoots, MatchTraverseRootState matchTraverseRoots, ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const 286 { 287 if (traverseRoots.isEmpty()) 288 return; 289 290 if (matchTraverseRoots) { 291 while (!traverseRoots.isEmpty()) { 292 Element& element = *traverseRoots.next(); 293 if (selectorMatches(selector, element, rootNode)) { 294 SelectorQueryTrait::appendElement(output, element); 295 if (SelectorQueryTrait::shouldOnlyMatchFirstElement) 296 return; 297 } 298 } 299 return; 300 } 301 302 while (!traverseRoots.isEmpty()) { 303 Element& traverseRoot = *traverseRoots.next(); 304 for (Element* element = ElementTraversal::firstWithin(traverseRoot); element; element = ElementTraversal::next(*element, &traverseRoot)) { 305 if (selectorMatches(selector, *element, rootNode)) { 306 SelectorQueryTrait::appendElement(output, *element); 307 if (SelectorQueryTrait::shouldOnlyMatchFirstElement) 308 return; 309 } 310 } 311 } 312 } 313 314 template <typename SelectorQueryTrait> 315 bool SelectorDataList::selectorListMatches(ContainerNode& rootNode, Element& element, typename SelectorQueryTrait::OutputType& output) const 316 { 317 for (unsigned i = 0; i < m_selectors.size(); ++i) { 318 if (selectorMatches(*m_selectors[i], element, rootNode)) { 319 SelectorQueryTrait::appendElement(output, element); 320 return true; 321 } 322 } 323 return false; 324 } 325 326 template <typename SelectorQueryTrait> 327 void SelectorDataList::executeSlow(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const 328 { 329 for (Element* element = ElementTraversal::firstWithin(rootNode); element; element = ElementTraversal::next(*element, &rootNode)) { 330 if (selectorListMatches<SelectorQueryTrait>(rootNode, *element, output) && SelectorQueryTrait::shouldOnlyMatchFirstElement) 331 return; 332 } 333 } 334 335 // FIXME: Move the following helper functions, authorShadowRootOf, firstWithinTraversingShadowTree, 336 // nextTraversingShadowTree to the best place, e.g. NodeTraversal. 337 static ShadowRoot* authorShadowRootOf(const ContainerNode& node) 338 { 339 if (!node.isElementNode() || !isShadowHost(&node)) 340 return 0; 341 342 ElementShadow* shadow = toElement(node).shadow(); 343 ASSERT(shadow); 344 for (ShadowRoot* shadowRoot = shadow->oldestShadowRoot(); shadowRoot; shadowRoot = shadowRoot->youngerShadowRoot()) { 345 if (shadowRoot->type() == ShadowRoot::AuthorShadowRoot) 346 return shadowRoot; 347 } 348 return 0; 349 } 350 351 static ContainerNode* firstWithinTraversingShadowTree(const ContainerNode& rootNode) 352 { 353 if (ShadowRoot* shadowRoot = authorShadowRootOf(rootNode)) 354 return shadowRoot; 355 return ElementTraversal::firstWithin(rootNode); 356 } 357 358 static ContainerNode* nextTraversingShadowTree(const ContainerNode& node, const ContainerNode* rootNode) 359 { 360 if (ShadowRoot* shadowRoot = authorShadowRootOf(node)) 361 return shadowRoot; 362 363 const ContainerNode* current = &node; 364 while (current) { 365 if (Element* next = ElementTraversal::next(*current, rootNode)) 366 return next; 367 368 if (!current->isInShadowTree()) 369 return 0; 370 371 ShadowRoot* shadowRoot = current->containingShadowRoot(); 372 if (shadowRoot == rootNode) 373 return 0; 374 if (ShadowRoot* youngerShadowRoot = shadowRoot->youngerShadowRoot()) { 375 // Should not obtain any elements in user-agent shadow root. 376 ASSERT(youngerShadowRoot->type() == ShadowRoot::AuthorShadowRoot); 377 return youngerShadowRoot; 378 } 379 380 current = shadowRoot->host(); 381 } 382 return 0; 383 } 384 385 template <typename SelectorQueryTrait> 386 void SelectorDataList::executeSlowTraversingShadowTree(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const 387 { 388 for (ContainerNode* node = firstWithinTraversingShadowTree(rootNode); node; node = nextTraversingShadowTree(*node, &rootNode)) { 389 if (!node->isElementNode()) 390 continue; 391 Element* element = toElement(node); 392 if (selectorListMatches<SelectorQueryTrait>(rootNode, *element, output) && SelectorQueryTrait::shouldOnlyMatchFirstElement) 393 return; 394 } 395 } 396 397 const CSSSelector* SelectorDataList::selectorForIdLookup(const CSSSelector& firstSelector) const 398 { 399 for (const CSSSelector* selector = &firstSelector; selector; selector = selector->tagHistory()) { 400 if (selector->match() == CSSSelector::Id) 401 return selector; 402 if (selector->relation() != CSSSelector::SubSelector) 403 break; 404 } 405 return 0; 406 } 407 408 template <typename SelectorQueryTrait> 409 void SelectorDataList::execute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const 410 { 411 if (!canUseFastQuery(rootNode)) { 412 if (m_crossesTreeBoundary) { 413 rootNode.document().updateDistributionForNodeIfNeeded(&rootNode); 414 executeSlowTraversingShadowTree<SelectorQueryTrait>(rootNode, output); 415 } else { 416 executeSlow<SelectorQueryTrait>(rootNode, output); 417 } 418 return; 419 } 420 421 ASSERT(m_selectors.size() == 1); 422 423 const CSSSelector& selector = *m_selectors[0]; 424 const CSSSelector& firstSelector = selector; 425 426 // Fast path for querySelector*('#id'), querySelector*('tag#id'). 427 if (const CSSSelector* idSelector = selectorForIdLookup(firstSelector)) { 428 const AtomicString& idToMatch = idSelector->value(); 429 if (rootNode.treeScope().containsMultipleElementsWithId(idToMatch)) { 430 const WillBeHeapVector<RawPtrWillBeMember<Element> >& elements = rootNode.treeScope().getAllElementsById(idToMatch); 431 size_t count = elements.size(); 432 for (size_t i = 0; i < count; ++i) { 433 Element& element = *elements[i]; 434 if (!(isTreeScopeRoot(rootNode) || element.isDescendantOf(&rootNode))) 435 continue; 436 if (selectorMatches(selector, element, rootNode)) { 437 SelectorQueryTrait::appendElement(output, element); 438 if (SelectorQueryTrait::shouldOnlyMatchFirstElement) 439 return; 440 } 441 } 442 return; 443 } 444 Element* element = rootNode.treeScope().getElementById(idToMatch); 445 if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode))) 446 return; 447 if (selectorMatches(selector, *element, rootNode)) 448 SelectorQueryTrait::appendElement(output, *element); 449 return; 450 } 451 452 if (!firstSelector.tagHistory()) { 453 // Fast path for querySelector*('.foo'), and querySelector*('div'). 454 switch (firstSelector.match()) { 455 case CSSSelector::Class: 456 collectElementsByClassName<SelectorQueryTrait>(rootNode, firstSelector.value(), output); 457 return; 458 case CSSSelector::Tag: 459 collectElementsByTagName<SelectorQueryTrait>(rootNode, firstSelector.tagQName(), output); 460 return; 461 default: 462 break; // If we need another fast path, add here. 463 } 464 } 465 466 findTraverseRootsAndExecute<SelectorQueryTrait>(rootNode, output); 467 } 468 469 PassOwnPtr<SelectorQuery> SelectorQuery::adopt(CSSSelectorList& selectorList) 470 { 471 return adoptPtr(new SelectorQuery(selectorList)); 472 } 473 474 SelectorQuery::SelectorQuery(CSSSelectorList& selectorList) 475 { 476 m_selectorList.adopt(selectorList); 477 m_selectors.initialize(m_selectorList); 478 } 479 480 bool SelectorQuery::matches(Element& element) const 481 { 482 return m_selectors.matches(element); 483 } 484 485 PassRefPtrWillBeRawPtr<StaticElementList> SelectorQuery::queryAll(ContainerNode& rootNode) const 486 { 487 return m_selectors.queryAll(rootNode); 488 } 489 490 PassRefPtrWillBeRawPtr<Element> SelectorQuery::queryFirst(ContainerNode& rootNode) const 491 { 492 return m_selectors.queryFirst(rootNode); 493 } 494 495 SelectorQuery* SelectorQueryCache::add(const AtomicString& selectors, const Document& document, ExceptionState& exceptionState) 496 { 497 HashMap<AtomicString, OwnPtr<SelectorQuery> >::iterator it = m_entries.find(selectors); 498 if (it != m_entries.end()) 499 return it->value.get(); 500 501 CSSParser parser(CSSParserContext(document, 0)); 502 CSSSelectorList selectorList; 503 parser.parseSelector(selectors, selectorList); 504 505 if (!selectorList.first()) { 506 exceptionState.throwDOMException(SyntaxError, "'" + selectors + "' is not a valid selector."); 507 return 0; 508 } 509 510 // throw a NamespaceError if the selector includes any namespace prefixes. 511 if (selectorList.selectorsNeedNamespaceResolution()) { 512 exceptionState.throwDOMException(NamespaceError, "'" + selectors + "' contains namespaces, which are not supported."); 513 return 0; 514 } 515 516 const unsigned maximumSelectorQueryCacheSize = 256; 517 if (m_entries.size() == maximumSelectorQueryCacheSize) 518 m_entries.remove(m_entries.begin()); 519 520 return m_entries.add(selectors, SelectorQuery::adopt(selectorList)).storedValue->value.get(); 521 } 522 523 void SelectorQueryCache::invalidate() 524 { 525 m_entries.clear(); 526 } 527 528 } 529