| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2011, 2013 Apple Inc. All rights reserved. | 2 * Copyright (C) 2011, 2013 Apple Inc. All rights reserved. |
| 3 * Copyright (C) 2014 Samsung Electronics. All rights reserved. | 3 * Copyright (C) 2014 Samsung Electronics. All rights reserved. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
| 7 * are met: | 7 * are met: |
| 8 * | 8 * |
| 9 * 1. Redistributions of source code must retain the above copyright | 9 * 1. Redistributions of source code must retain the above copyright |
| 10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 57 | 57 |
| 58 struct AllElementsSelectorQueryTrait { | 58 struct AllElementsSelectorQueryTrait { |
| 59 typedef HeapVector<Member<Element>> OutputType; | 59 typedef HeapVector<Member<Element>> OutputType; |
| 60 static const bool shouldOnlyMatchFirstElement = false; | 60 static const bool shouldOnlyMatchFirstElement = false; |
| 61 ALWAYS_INLINE static void appendElement(OutputType& output, | 61 ALWAYS_INLINE static void appendElement(OutputType& output, |
| 62 Element& element) { | 62 Element& element) { |
| 63 output.push_back(&element); | 63 output.push_back(&element); |
| 64 } | 64 } |
| 65 }; | 65 }; |
| 66 | 66 |
| 67 enum ClassElementListBehavior { AllElements, OnlyRoots }; | 67 // TODO(esprehn): Move this to Element and update callers elsewhere. |
| 68 | 68 inline bool hasClassName(const Element& element, |
| 69 template <ClassElementListBehavior onlyRoots> | 69 const AtomicString& className) { |
| 70 class ClassElementList { | 70 return element.hasClass() && element.classNames().contains(className); |
| 71 STACK_ALLOCATED(); | 71 } |
| 72 | |
| 73 public: | |
| 74 ClassElementList(ContainerNode& rootNode, const AtomicString& className) | |
| 75 : m_className(className), | |
| 76 m_rootNode(&rootNode), | |
| 77 m_currentElement( | |
| 78 nextInternal(ElementTraversal::firstWithin(rootNode))) {} | |
| 79 | |
| 80 bool isEmpty() const { return !m_currentElement; } | |
| 81 | |
| 82 Element* next() { | |
| 83 Element* current = m_currentElement; | |
| 84 DCHECK(current); | |
| 85 if (onlyRoots) | |
| 86 m_currentElement = nextInternal(ElementTraversal::nextSkippingChildren( | |
| 87 *m_currentElement, m_rootNode)); | |
| 88 else | |
| 89 m_currentElement = | |
| 90 nextInternal(ElementTraversal::next(*m_currentElement, m_rootNode)); | |
| 91 return current; | |
| 92 } | |
| 93 | |
| 94 private: | |
| 95 Element* nextInternal(Element* element) { | |
| 96 for (; element; element = ElementTraversal::next(*element, m_rootNode)) { | |
| 97 if (element->hasClass() && element->classNames().contains(m_className)) | |
| 98 return element; | |
| 99 } | |
| 100 return nullptr; | |
| 101 } | |
| 102 | |
| 103 const AtomicString& m_className; | |
| 104 Member<ContainerNode> m_rootNode; | |
| 105 Member<Element> m_currentElement; | |
| 106 }; | |
| 107 | 72 |
| 108 inline bool selectorMatches(const CSSSelector& selector, | 73 inline bool selectorMatches(const CSSSelector& selector, |
| 109 Element& element, | 74 Element& element, |
| 110 const ContainerNode& rootNode) { | 75 const ContainerNode& rootNode) { |
| 111 SelectorChecker::Init init; | 76 SelectorChecker::Init init; |
| 112 init.mode = SelectorChecker::QueryingRules; | 77 init.mode = SelectorChecker::QueryingRules; |
| 113 SelectorChecker checker(init); | 78 SelectorChecker checker(init); |
| 114 SelectorChecker::SelectorCheckingContext context( | 79 SelectorChecker::SelectorCheckingContext context( |
| 115 &element, SelectorChecker::VisitedMatchDisabled); | 80 &element, SelectorChecker::VisitedMatchDisabled); |
| 116 context.selector = &selector; | 81 context.selector = &selector; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 149 NthIndexCache nthIndexCache(rootNode.document()); | 114 NthIndexCache nthIndexCache(rootNode.document()); |
| 150 Element* matchedElement = nullptr; | 115 Element* matchedElement = nullptr; |
| 151 execute<SingleElementSelectorQueryTrait>(rootNode, matchedElement); | 116 execute<SingleElementSelectorQueryTrait>(rootNode, matchedElement); |
| 152 return matchedElement; | 117 return matchedElement; |
| 153 } | 118 } |
| 154 | 119 |
| 155 template <typename SelectorQueryTrait> | 120 template <typename SelectorQueryTrait> |
| 156 static void collectElementsByClassName( | 121 static void collectElementsByClassName( |
| 157 ContainerNode& rootNode, | 122 ContainerNode& rootNode, |
| 158 const AtomicString& className, | 123 const AtomicString& className, |
| 124 const CSSSelector* selector, |
| 159 typename SelectorQueryTrait::OutputType& output) { | 125 typename SelectorQueryTrait::OutputType& output) { |
| 160 for (Element& element : ElementTraversal::descendantsOf(rootNode)) { | 126 for (Element& element : ElementTraversal::descendantsOf(rootNode)) { |
| 161 if (element.hasClass() && element.classNames().contains(className)) { | 127 if (!hasClassName(element, className)) |
| 162 SelectorQueryTrait::appendElement(output, element); | 128 continue; |
| 163 if (SelectorQueryTrait::shouldOnlyMatchFirstElement) | 129 if (selector && !selectorMatches(*selector, element, rootNode)) |
| 164 return; | 130 continue; |
| 165 } | 131 SelectorQueryTrait::appendElement(output, element); |
| 132 if (SelectorQueryTrait::shouldOnlyMatchFirstElement) |
| 133 return; |
| 166 } | 134 } |
| 167 } | 135 } |
| 168 | 136 |
| 169 inline bool matchesTagName(const QualifiedName& tagName, | 137 inline bool matchesTagName(const QualifiedName& tagName, |
| 170 const Element& element) { | 138 const Element& element) { |
| 171 if (tagName == anyQName()) | 139 if (tagName == anyQName()) |
| 172 return true; | 140 return true; |
| 173 if (element.hasLocalName(tagName.localName())) | 141 if (element.hasLocalName(tagName.localName())) |
| 174 return true; | 142 return true; |
| 175 // Non-html elements in html documents are normalized to their camel-cased | 143 // Non-html elements in html documents are normalized to their camel-cased |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 209 return m_selectors.size() == 1; | 177 return m_selectors.size() == 1; |
| 210 } | 178 } |
| 211 | 179 |
| 212 inline bool ancestorHasClassName(ContainerNode& rootNode, | 180 inline bool ancestorHasClassName(ContainerNode& rootNode, |
| 213 const AtomicString& className) { | 181 const AtomicString& className) { |
| 214 if (!rootNode.isElementNode()) | 182 if (!rootNode.isElementNode()) |
| 215 return false; | 183 return false; |
| 216 | 184 |
| 217 for (Element* element = &toElement(rootNode); element; | 185 for (Element* element = &toElement(rootNode); element; |
| 218 element = element->parentElement()) { | 186 element = element->parentElement()) { |
| 219 if (element->hasClass() && element->classNames().contains(className)) | 187 if (hasClassName(*element, className)) |
| 220 return true; | 188 return true; |
| 221 } | 189 } |
| 222 return false; | 190 return false; |
| 223 } | 191 } |
| 224 | 192 |
| 225 template <typename SelectorQueryTrait> | 193 template <typename SelectorQueryTrait> |
| 226 void SelectorQuery::findTraverseRootsAndExecute( | 194 void SelectorQuery::findTraverseRootsAndExecute( |
| 227 ContainerNode& rootNode, | 195 ContainerNode& rootNode, |
| 228 typename SelectorQueryTrait::OutputType& output) const { | 196 typename SelectorQueryTrait::OutputType& output) const { |
| 229 // We need to return the matches in document order. To use id lookup while | 197 // We need to return the matches in document order. To use id lookup while |
| (...skipping 25 matching lines...) Expand all Loading... |
| 255 return; | 223 return; |
| 256 executeForTraverseRoot<SelectorQueryTrait>(*start, rootNode, output); | 224 executeForTraverseRoot<SelectorQueryTrait>(*start, rootNode, output); |
| 257 return; | 225 return; |
| 258 } | 226 } |
| 259 | 227 |
| 260 // If we have both CSSSelector::Id and CSSSelector::Class at the same time, | 228 // If we have both CSSSelector::Id and CSSSelector::Class at the same time, |
| 261 // we should use Id to find traverse root. | 229 // we should use Id to find traverse root. |
| 262 if (!SelectorQueryTrait::shouldOnlyMatchFirstElement && !startFromParent && | 230 if (!SelectorQueryTrait::shouldOnlyMatchFirstElement && !startFromParent && |
| 263 selector->match() == CSSSelector::Class) { | 231 selector->match() == CSSSelector::Class) { |
| 264 if (isRightmostSelector) { | 232 if (isRightmostSelector) { |
| 265 ClassElementList<AllElements> traverseRoots(rootNode, | 233 collectElementsByClassName<SelectorQueryTrait>( |
| 266 selector->value()); | 234 rootNode, selector->value(), m_selectors[0], output); |
| 267 while (!traverseRoots.isEmpty()) { | |
| 268 Element& element = *traverseRoots.next(); | |
| 269 if (selectorMatches(*m_selectors[0], element, rootNode)) | |
| 270 SelectorQueryTrait::appendElement(output, element); | |
| 271 } | |
| 272 return; | 235 return; |
| 273 } | 236 } |
| 274 // Since there exists some ancestor element which has the class name, we | 237 // Since there exists some ancestor element which has the class name, we |
| 275 // need to see all children of rootNode. | 238 // need to see all children of rootNode. |
| 276 if (ancestorHasClassName(rootNode, selector->value())) | 239 if (ancestorHasClassName(rootNode, selector->value())) |
| 277 break; | 240 break; |
| 278 | 241 |
| 279 ClassElementList<OnlyRoots> traverseRoots(rootNode, selector->value()); | 242 const AtomicString& className = selector->value(); |
| 280 while (!traverseRoots.isEmpty()) { | 243 Element* element = ElementTraversal::firstWithin(rootNode); |
| 281 executeForTraverseRoot<SelectorQueryTrait>(*traverseRoots.next(), | 244 while (element) { |
| 282 rootNode, output); | 245 if (hasClassName(*element, className)) { |
| 246 executeForTraverseRoot<SelectorQueryTrait>(*element, rootNode, |
| 247 output); |
| 248 element = ElementTraversal::nextSkippingChildren(*element, &rootNode); |
| 249 } else { |
| 250 element = ElementTraversal::next(*element, &rootNode); |
| 251 } |
| 283 } | 252 } |
| 284 return; | 253 return; |
| 285 } | 254 } |
| 286 | 255 |
| 287 if (selector->relation() == CSSSelector::SubSelector) | 256 if (selector->relation() == CSSSelector::SubSelector) |
| 288 continue; | 257 continue; |
| 289 isRightmostSelector = false; | 258 isRightmostSelector = false; |
| 290 if (selector->relation() == CSSSelector::DirectAdjacent || | 259 if (selector->relation() == CSSSelector::DirectAdjacent || |
| 291 selector->relation() == CSSSelector::IndirectAdjacent) | 260 selector->relation() == CSSSelector::IndirectAdjacent) |
| 292 startFromParent = true; | 261 startFromParent = true; |
| (...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 466 if (selectorMatches(selector, *element, rootNode)) | 435 if (selectorMatches(selector, *element, rootNode)) |
| 467 SelectorQueryTrait::appendElement(output, *element); | 436 SelectorQueryTrait::appendElement(output, *element); |
| 468 return; | 437 return; |
| 469 } | 438 } |
| 470 | 439 |
| 471 if (!firstSelector.tagHistory()) { | 440 if (!firstSelector.tagHistory()) { |
| 472 // Fast path for querySelector*('.foo'), and querySelector*('div'). | 441 // Fast path for querySelector*('.foo'), and querySelector*('div'). |
| 473 switch (firstSelector.match()) { | 442 switch (firstSelector.match()) { |
| 474 case CSSSelector::Class: | 443 case CSSSelector::Class: |
| 475 collectElementsByClassName<SelectorQueryTrait>( | 444 collectElementsByClassName<SelectorQueryTrait>( |
| 476 rootNode, firstSelector.value(), output); | 445 rootNode, firstSelector.value(), nullptr, output); |
| 477 return; | 446 return; |
| 478 case CSSSelector::Tag: | 447 case CSSSelector::Tag: |
| 479 if (firstSelector.tagQName().namespaceURI() == starAtom) { | 448 if (firstSelector.tagQName().namespaceURI() == starAtom) { |
| 480 collectElementsByTagName<SelectorQueryTrait>( | 449 collectElementsByTagName<SelectorQueryTrait>( |
| 481 rootNode, firstSelector.tagQName(), output); | 450 rootNode, firstSelector.tagQName(), output); |
| 482 return; | 451 return; |
| 483 } | 452 } |
| 484 // querySelector*() doesn't allow namespace prefix resolution and | 453 // querySelector*() doesn't allow namespace prefix resolution and |
| 485 // throws before we get here, but we still may have selectors for | 454 // throws before we get here, but we still may have selectors for |
| 486 // elements without a namespace. | 455 // elements without a namespace. |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 548 return m_entries | 517 return m_entries |
| 549 .insert(selectors, SelectorQuery::adopt(std::move(selectorList))) | 518 .insert(selectors, SelectorQuery::adopt(std::move(selectorList))) |
| 550 .storedValue->value.get(); | 519 .storedValue->value.get(); |
| 551 } | 520 } |
| 552 | 521 |
| 553 void SelectorQueryCache::invalidate() { | 522 void SelectorQueryCache::invalidate() { |
| 554 m_entries.clear(); | 523 m_entries.clear(); |
| 555 } | 524 } |
| 556 | 525 |
| 557 } // namespace blink | 526 } // namespace blink |
| OLD | NEW |