| 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 10 matching lines...) Expand all Loading... |
| 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | 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 | 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 | 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. | 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 25 */ | 25 */ |
| 26 | 26 |
| 27 #include "config.h" | 27 #include "config.h" |
| 28 #include "core/dom/SelectorQuery.h" | 28 #include "core/dom/SelectorQuery.h" |
| 29 | 29 |
| 30 #include "bindings/core/v8/ExceptionState.h" | 30 #include "bindings/core/v8/ExceptionState.h" |
| 31 #include "core/css/SelectorChecker.h" | |
| 32 #include "core/css/parser/CSSParser.h" | 31 #include "core/css/parser/CSSParser.h" |
| 32 #include "core/dom/ContainerNode.h" |
| 33 #include "core/dom/Document.h" | 33 #include "core/dom/Document.h" |
| 34 #include "core/dom/ElementTraversal.h" | 34 #include "core/dom/Element.h" |
| 35 #include "core/dom/ExceptionCode.h" | 35 #include "core/dom/ExceptionCode.h" |
| 36 #include "core/dom/Node.h" | |
| 37 #include "core/dom/StaticNodeList.h" | 36 #include "core/dom/StaticNodeList.h" |
| 38 #include "core/dom/shadow/ElementShadow.h" | 37 #include "core/dom/query/SelectorAnyMatcher.h" |
| 39 #include "core/dom/shadow/ShadowRoot.h" | 38 #include "core/dom/query/SelectorCheckerMatcher.h" |
| 39 #include "core/dom/query/SelectorClassMatcher.h" |
| 40 #include "core/dom/query/SelectorIdMatcher.h" |
| 41 #include "core/dom/query/SelectorMatcher.h" |
| 42 #include "core/dom/query/SelectorTagMatcher.h" |
| 40 | 43 |
| 41 namespace blink { | 44 namespace blink { |
| 42 | 45 |
| 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::nextSkippingChildr
en(*m_currentElement, m_rootNode)); | |
| 81 else | |
| 82 m_currentElement = nextInternal(ElementTraversal::next(*m_currentEle
ment, 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_classNam
e)) | |
| 91 return element; | |
| 92 } | |
| 93 return nullptr; | |
| 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_usesDeepCombinatorOrShadowPseudo = false; | |
| 110 m_needsUpdatedDistribution = false; | |
| 111 m_selectors.reserveInitialCapacity(selectorCount); | |
| 112 unsigned index = 0; | |
| 113 for (const CSSSelector* selector = selectorList.first(); selector; selector
= CSSSelectorList::next(*selector), ++index) { | |
| 114 m_selectors.uncheckedAppend(selector); | |
| 115 m_usesDeepCombinatorOrShadowPseudo |= selectorList.selectorUsesDeepCombi
natorOrShadowPseudo(index); | |
| 116 m_needsUpdatedDistribution |= selectorList.selectorNeedsUpdatedDistribut
ion(index); | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 inline bool SelectorDataList::selectorMatches(const CSSSelector& selector, Eleme
nt& element, const ContainerNode& rootNode) const | |
| 121 { | |
| 122 SelectorChecker selectorChecker(SelectorChecker::QueryingRules); | |
| 123 SelectorChecker::SelectorCheckingContext selectorCheckingContext(&element, S
electorChecker::VisitedMatchDisabled); | |
| 124 selectorCheckingContext.selector = &selector; | |
| 125 selectorCheckingContext.scope = &rootNode; | |
| 126 return selectorChecker.match(selectorCheckingContext); | |
| 127 } | |
| 128 | |
| 129 bool SelectorDataList::matches(Element& targetElement) const | |
| 130 { | |
| 131 if (m_needsUpdatedDistribution) | |
| 132 targetElement.updateDistribution(); | |
| 133 | |
| 134 unsigned selectorCount = m_selectors.size(); | |
| 135 for (unsigned i = 0; i < selectorCount; ++i) { | |
| 136 if (selectorMatches(*m_selectors[i], targetElement, targetElement)) | |
| 137 return true; | |
| 138 } | |
| 139 | |
| 140 return false; | |
| 141 } | |
| 142 | |
| 143 Element* SelectorDataList::closest(Element& targetElement) const | |
| 144 { | |
| 145 if (m_needsUpdatedDistribution) | |
| 146 targetElement.updateDistribution(); | |
| 147 | |
| 148 unsigned selectorCount = m_selectors.size(); | |
| 149 for (Element* currentElement = &targetElement; currentElement; currentElemen
t = currentElement->parentElement()) { | |
| 150 for (unsigned i = 0; i < selectorCount; ++i) { | |
| 151 if (selectorMatches(*m_selectors[i], *currentElement, targetElement)
) | |
| 152 return currentElement; | |
| 153 } | |
| 154 } | |
| 155 return nullptr; | |
| 156 } | |
| 157 | |
| 158 PassRefPtrWillBeRawPtr<StaticElementList> SelectorDataList::queryAll(ContainerNo
de& rootNode) const | |
| 159 { | |
| 160 WillBeHeapVector<RefPtrWillBeMember<Element>> result; | |
| 161 execute<AllElementsSelectorQueryTrait>(rootNode, result); | |
| 162 return StaticElementList::adopt(result); | |
| 163 } | |
| 164 | |
| 165 PassRefPtrWillBeRawPtr<Element> SelectorDataList::queryFirst(ContainerNode& root
Node) const | |
| 166 { | |
| 167 Element* matchedElement = nullptr; | |
| 168 execute<SingleElementSelectorQueryTrait>(rootNode, matchedElement); | |
| 169 return matchedElement; | |
| 170 } | |
| 171 | |
| 172 template <typename SelectorQueryTrait> | |
| 173 void SelectorDataList::collectElementsByClassName(ContainerNode& rootNode, const
AtomicString& className, typename SelectorQueryTrait::OutputType& output) cons
t | |
| 174 { | |
| 175 for (Element& element : ElementTraversal::descendantsOf(rootNode)) { | |
| 176 if (element.hasClass() && element.classNames().contains(className)) { | |
| 177 SelectorQueryTrait::appendElement(output, element); | |
| 178 if (SelectorQueryTrait::shouldOnlyMatchFirstElement) | |
| 179 return; | |
| 180 } | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 inline bool matchesTagName(const QualifiedName& tagName, const Element& element) | |
| 185 { | |
| 186 if (tagName == anyQName()) | |
| 187 return true; | |
| 188 if (element.hasLocalName(tagName.localName())) | |
| 189 return true; | |
| 190 // Non-html elements in html documents are normalized to their camel-cased | |
| 191 // version during parsing if applicable. Yet, type selectors are lower-cased | |
| 192 // for selectors in html documents. Compare the upper case converted names | |
| 193 // instead to allow matching SVG elements like foreignObject. | |
| 194 if (!element.isHTMLElement() && element.document().isHTMLDocument()) | |
| 195 return element.tagQName().localNameUpper() == tagName.localNameUpper(); | |
| 196 return false; | |
| 197 } | |
| 198 | |
| 199 template <typename SelectorQueryTrait> | |
| 200 void SelectorDataList::collectElementsByTagName(ContainerNode& rootNode, const Q
ualifiedName& tagName, typename SelectorQueryTrait::OutputType& output) const | |
| 201 { | |
| 202 for (Element& element : ElementTraversal::descendantsOf(rootNode)) { | |
| 203 // querySelector*() doesn't allow namespaces and throws before it gets | |
| 204 // here so we can ignore them. | |
| 205 ASSERT(tagName.namespaceURI() == starAtom); | |
| 206 if (matchesTagName(tagName, element)) { | |
| 207 SelectorQueryTrait::appendElement(output, element); | |
| 208 if (SelectorQueryTrait::shouldOnlyMatchFirstElement) | |
| 209 return; | |
| 210 } | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 inline bool SelectorDataList::canUseFastQuery(const ContainerNode& rootNode) con
st | |
| 215 { | |
| 216 if (m_usesDeepCombinatorOrShadowPseudo) | |
| 217 return false; | |
| 218 if (m_needsUpdatedDistribution) | |
| 219 return false; | |
| 220 if (rootNode.document().inQuirksMode()) | |
| 221 return false; | |
| 222 if (!rootNode.inDocument()) | |
| 223 return false; | |
| 224 return m_selectors.size() == 1; | |
| 225 } | |
| 226 | |
| 227 inline bool ancestorHasClassName(ContainerNode& rootNode, const AtomicString& cl
assName) | |
| 228 { | |
| 229 if (!rootNode.isElementNode()) | |
| 230 return false; | |
| 231 | |
| 232 for (Element* element = &toElement(rootNode); element; element = element->pa
rentElement()) { | |
| 233 if (element->hasClass() && element->classNames().contains(className)) | |
| 234 return true; | |
| 235 } | |
| 236 return false; | |
| 237 } | |
| 238 | |
| 239 | |
| 240 // If returns true, traversalRoots has the elements that may match the selector
query. | |
| 241 // | |
| 242 // If returns false, traversalRoots has the rootNode parameter or descendants of
rootNode representing | |
| 243 // the subtree for which we can limit the querySelector traversal. | |
| 244 // | |
| 245 // The travseralRoots may be empty, regardless of the returned bool value, if th
is method finds that the selectors won't | |
| 246 // match any element. | |
| 247 template <typename SelectorQueryTrait> | |
| 248 void SelectorDataList::findTraverseRootsAndExecute(ContainerNode& rootNode, type
name SelectorQueryTrait::OutputType& output) const | |
| 249 { | |
| 250 // We need to return the matches in document order. To use id lookup while t
here is possiblity of multiple matches | |
| 251 // we would need to sort the results. For now, just traverse the document in
that case. | |
| 252 ASSERT(m_selectors.size() == 1); | |
| 253 | |
| 254 bool isRightmostSelector = true; | |
| 255 bool startFromParent = false; | |
| 256 | |
| 257 for (const CSSSelector* selector = m_selectors[0]; selector; selector = sele
ctor->tagHistory()) { | |
| 258 if (selector->match() == CSSSelector::Id && !rootNode.document().contain
sMultipleElementsWithId(selector->value())) { | |
| 259 Element* element = rootNode.treeScope().getElementById(selector->val
ue()); | |
| 260 ContainerNode* adjustedNode = &rootNode; | |
| 261 if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf
(&rootNode))) | |
| 262 adjustedNode = element; | |
| 263 else if (!element || isRightmostSelector) | |
| 264 adjustedNode = nullptr; | |
| 265 if (isRightmostSelector) { | |
| 266 executeForTraverseRoot<SelectorQueryTrait>(*m_selectors[0], adju
stedNode, MatchesTraverseRoots, rootNode, output); | |
| 267 return; | |
| 268 } | |
| 269 | |
| 270 if (startFromParent && adjustedNode) | |
| 271 adjustedNode = adjustedNode->parentNode(); | |
| 272 | |
| 273 executeForTraverseRoot<SelectorQueryTrait>(*m_selectors[0], adjusted
Node, DoesNotMatchTraverseRoots, rootNode, output); | |
| 274 return; | |
| 275 } | |
| 276 | |
| 277 // If we have both CSSSelector::Id and CSSSelector::Class at the same ti
me, we should use Id | |
| 278 // to find traverse root. | |
| 279 if (!SelectorQueryTrait::shouldOnlyMatchFirstElement && !startFromParent
&& selector->match() == CSSSelector::Class) { | |
| 280 if (isRightmostSelector) { | |
| 281 ClassElementList<AllElements> traverseRoots(rootNode, selector->
value()); | |
| 282 executeForTraverseRoots<SelectorQueryTrait>(*m_selectors[0], tra
verseRoots, MatchesTraverseRoots, rootNode, output); | |
| 283 return; | |
| 284 } | |
| 285 // Since there exists some ancestor element which has the class name
, we need to see all children of rootNode. | |
| 286 if (ancestorHasClassName(rootNode, selector->value())) { | |
| 287 executeForTraverseRoot<SelectorQueryTrait>(*m_selectors[0], &roo
tNode, DoesNotMatchTraverseRoots, rootNode, output); | |
| 288 return; | |
| 289 } | |
| 290 | |
| 291 ClassElementList<OnlyRoots> traverseRoots(rootNode, selector->value(
)); | |
| 292 executeForTraverseRoots<SelectorQueryTrait>(*m_selectors[0], travers
eRoots, DoesNotMatchTraverseRoots, rootNode, output); | |
| 293 return; | |
| 294 } | |
| 295 | |
| 296 if (selector->relation() == CSSSelector::SubSelector) | |
| 297 continue; | |
| 298 isRightmostSelector = false; | |
| 299 if (selector->relation() == CSSSelector::DirectAdjacent || selector->rel
ation() == CSSSelector::IndirectAdjacent) | |
| 300 startFromParent = true; | |
| 301 else | |
| 302 startFromParent = false; | |
| 303 } | |
| 304 | |
| 305 executeForTraverseRoot<SelectorQueryTrait>(*m_selectors[0], &rootNode, DoesN
otMatchTraverseRoots, rootNode, output); | |
| 306 } | |
| 307 | |
| 308 template <typename SelectorQueryTrait> | |
| 309 void SelectorDataList::executeForTraverseRoot(const CSSSelector& selector, Conta
inerNode* traverseRoot, MatchTraverseRootState matchTraverseRoot, ContainerNode&
rootNode, typename SelectorQueryTrait::OutputType& output) const | |
| 310 { | |
| 311 if (!traverseRoot) | |
| 312 return; | |
| 313 | |
| 314 if (matchTraverseRoot) { | |
| 315 if (selectorMatches(selector, toElement(*traverseRoot), rootNode)) | |
| 316 SelectorQueryTrait::appendElement(output, toElement(*traverseRoot)); | |
| 317 return; | |
| 318 } | |
| 319 | |
| 320 for (Element& element : ElementTraversal::descendantsOf(*traverseRoot)) { | |
| 321 if (selectorMatches(selector, element, rootNode)) { | |
| 322 SelectorQueryTrait::appendElement(output, element); | |
| 323 if (SelectorQueryTrait::shouldOnlyMatchFirstElement) | |
| 324 return; | |
| 325 } | |
| 326 } | |
| 327 } | |
| 328 | |
| 329 template <typename SelectorQueryTrait, typename SimpleElementListType> | |
| 330 void SelectorDataList::executeForTraverseRoots(const CSSSelector& selector, Simp
leElementListType& traverseRoots, MatchTraverseRootState matchTraverseRoots, Con
tainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const | |
| 331 { | |
| 332 if (traverseRoots.isEmpty()) | |
| 333 return; | |
| 334 | |
| 335 if (matchTraverseRoots) { | |
| 336 while (!traverseRoots.isEmpty()) { | |
| 337 Element& element = *traverseRoots.next(); | |
| 338 if (selectorMatches(selector, element, rootNode)) { | |
| 339 SelectorQueryTrait::appendElement(output, element); | |
| 340 if (SelectorQueryTrait::shouldOnlyMatchFirstElement) | |
| 341 return; | |
| 342 } | |
| 343 } | |
| 344 return; | |
| 345 } | |
| 346 | |
| 347 while (!traverseRoots.isEmpty()) { | |
| 348 for (Element& element : ElementTraversal::descendantsOf(*traverseRoots.n
ext())) { | |
| 349 if (selectorMatches(selector, element, rootNode)) { | |
| 350 SelectorQueryTrait::appendElement(output, element); | |
| 351 if (SelectorQueryTrait::shouldOnlyMatchFirstElement) | |
| 352 return; | |
| 353 } | |
| 354 } | |
| 355 } | |
| 356 } | |
| 357 | |
| 358 template <typename SelectorQueryTrait> | |
| 359 bool SelectorDataList::selectorListMatches(ContainerNode& rootNode, Element& ele
ment, typename SelectorQueryTrait::OutputType& output) const | |
| 360 { | |
| 361 for (unsigned i = 0; i < m_selectors.size(); ++i) { | |
| 362 if (selectorMatches(*m_selectors[i], element, rootNode)) { | |
| 363 SelectorQueryTrait::appendElement(output, element); | |
| 364 return true; | |
| 365 } | |
| 366 } | |
| 367 return false; | |
| 368 } | |
| 369 | |
| 370 template <typename SelectorQueryTrait> | |
| 371 void SelectorDataList::executeSlow(ContainerNode& rootNode, typename SelectorQue
ryTrait::OutputType& output) const | |
| 372 { | |
| 373 for (Element& element : ElementTraversal::descendantsOf(rootNode)) { | |
| 374 if (selectorListMatches<SelectorQueryTrait>(rootNode, element, output) &
& SelectorQueryTrait::shouldOnlyMatchFirstElement) | |
| 375 return; | |
| 376 } | |
| 377 } | |
| 378 | |
| 379 // FIXME: Move the following helper functions, authorShadowRootOf, firstWithinTr
aversingShadowTree, | |
| 380 // nextTraversingShadowTree to the best place, e.g. NodeTraversal. | |
| 381 static ShadowRoot* authorShadowRootOf(const ContainerNode& node) | |
| 382 { | |
| 383 if (!node.isElementNode() || !isShadowHost(&node)) | |
| 384 return nullptr; | |
| 385 | |
| 386 ElementShadow* shadow = toElement(node).shadow(); | |
| 387 ASSERT(shadow); | |
| 388 for (ShadowRoot* shadowRoot = shadow->oldestShadowRoot(); shadowRoot; shadow
Root = shadowRoot->youngerShadowRoot()) { | |
| 389 if (shadowRoot->type() == ShadowRootType::V0 || shadowRoot->type() == Sh
adowRootType::Open) | |
| 390 return shadowRoot; | |
| 391 } | |
| 392 return nullptr; | |
| 393 } | |
| 394 | |
| 395 static ContainerNode* firstWithinTraversingShadowTree(const ContainerNode& rootN
ode) | |
| 396 { | |
| 397 if (ShadowRoot* shadowRoot = authorShadowRootOf(rootNode)) | |
| 398 return shadowRoot; | |
| 399 return ElementTraversal::firstWithin(rootNode); | |
| 400 } | |
| 401 | |
| 402 static ContainerNode* nextTraversingShadowTree(const ContainerNode& node, const
ContainerNode* rootNode) | |
| 403 { | |
| 404 if (ShadowRoot* shadowRoot = authorShadowRootOf(node)) | |
| 405 return shadowRoot; | |
| 406 | |
| 407 const ContainerNode* current = &node; | |
| 408 while (current) { | |
| 409 if (Element* next = ElementTraversal::next(*current, rootNode)) | |
| 410 return next; | |
| 411 | |
| 412 if (!current->isInShadowTree()) | |
| 413 return nullptr; | |
| 414 | |
| 415 ShadowRoot* shadowRoot = current->containingShadowRoot(); | |
| 416 if (shadowRoot == rootNode) | |
| 417 return nullptr; | |
| 418 if (ShadowRoot* youngerShadowRoot = shadowRoot->youngerShadowRoot()) { | |
| 419 // Should not obtain any elements in closed or user-agent shadow roo
t. | |
| 420 ASSERT(youngerShadowRoot->type() == ShadowRootType::V0 || youngerSha
dowRoot->type() == ShadowRootType::Open); | |
| 421 return youngerShadowRoot; | |
| 422 } | |
| 423 | |
| 424 current = shadowRoot->host(); | |
| 425 } | |
| 426 return nullptr; | |
| 427 } | |
| 428 | |
| 429 template <typename SelectorQueryTrait> | |
| 430 void SelectorDataList::executeSlowTraversingShadowTree(ContainerNode& rootNode,
typename SelectorQueryTrait::OutputType& output) const | |
| 431 { | |
| 432 for (ContainerNode* node = firstWithinTraversingShadowTree(rootNode); node;
node = nextTraversingShadowTree(*node, &rootNode)) { | |
| 433 if (!node->isElementNode()) | |
| 434 continue; | |
| 435 Element* element = toElement(node); | |
| 436 if (selectorListMatches<SelectorQueryTrait>(rootNode, *element, output)
&& SelectorQueryTrait::shouldOnlyMatchFirstElement) | |
| 437 return; | |
| 438 } | |
| 439 } | |
| 440 | |
| 441 const CSSSelector* SelectorDataList::selectorForIdLookup(const CSSSelector& firs
tSelector) const | |
| 442 { | |
| 443 for (const CSSSelector* selector = &firstSelector; selector; selector = sele
ctor->tagHistory()) { | |
| 444 if (selector->match() == CSSSelector::Id) | |
| 445 return selector; | |
| 446 if (selector->relation() != CSSSelector::SubSelector) | |
| 447 break; | |
| 448 } | |
| 449 return nullptr; | |
| 450 } | |
| 451 | |
| 452 template <typename SelectorQueryTrait> | |
| 453 void SelectorDataList::execute(ContainerNode& rootNode, typename SelectorQueryTr
ait::OutputType& output) const | |
| 454 { | |
| 455 if (!canUseFastQuery(rootNode)) { | |
| 456 if (m_needsUpdatedDistribution) | |
| 457 rootNode.updateDistribution(); | |
| 458 if (m_usesDeepCombinatorOrShadowPseudo) { | |
| 459 executeSlowTraversingShadowTree<SelectorQueryTrait>(rootNode, output
); | |
| 460 } else { | |
| 461 executeSlow<SelectorQueryTrait>(rootNode, output); | |
| 462 } | |
| 463 return; | |
| 464 } | |
| 465 | |
| 466 ASSERT(m_selectors.size() == 1); | |
| 467 | |
| 468 const CSSSelector& selector = *m_selectors[0]; | |
| 469 const CSSSelector& firstSelector = selector; | |
| 470 | |
| 471 // Fast path for querySelector*('#id'), querySelector*('tag#id'). | |
| 472 if (const CSSSelector* idSelector = selectorForIdLookup(firstSelector)) { | |
| 473 const AtomicString& idToMatch = idSelector->value(); | |
| 474 if (rootNode.treeScope().containsMultipleElementsWithId(idToMatch)) { | |
| 475 const WillBeHeapVector<RawPtrWillBeMember<Element>>& elements = root
Node.treeScope().getAllElementsById(idToMatch); | |
| 476 size_t count = elements.size(); | |
| 477 for (size_t i = 0; i < count; ++i) { | |
| 478 Element& element = *elements[i]; | |
| 479 if (!(isTreeScopeRoot(rootNode) || element.isDescendantOf(&rootN
ode))) | |
| 480 continue; | |
| 481 if (selectorMatches(selector, element, rootNode)) { | |
| 482 SelectorQueryTrait::appendElement(output, element); | |
| 483 if (SelectorQueryTrait::shouldOnlyMatchFirstElement) | |
| 484 return; | |
| 485 } | |
| 486 } | |
| 487 return; | |
| 488 } | |
| 489 Element* element = rootNode.treeScope().getElementById(idToMatch); | |
| 490 if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(&
rootNode))) | |
| 491 return; | |
| 492 if (selectorMatches(selector, *element, rootNode)) | |
| 493 SelectorQueryTrait::appendElement(output, *element); | |
| 494 return; | |
| 495 } | |
| 496 | |
| 497 if (!firstSelector.tagHistory()) { | |
| 498 // Fast path for querySelector*('.foo'), and querySelector*('div'). | |
| 499 switch (firstSelector.match()) { | |
| 500 case CSSSelector::Class: | |
| 501 collectElementsByClassName<SelectorQueryTrait>(rootNode, firstSelect
or.value(), output); | |
| 502 return; | |
| 503 case CSSSelector::Tag: | |
| 504 collectElementsByTagName<SelectorQueryTrait>(rootNode, firstSelector
.tagQName(), output); | |
| 505 return; | |
| 506 default: | |
| 507 break; // If we need another fast path, add here. | |
| 508 } | |
| 509 } | |
| 510 | |
| 511 findTraverseRootsAndExecute<SelectorQueryTrait>(rootNode, output); | |
| 512 } | |
| 513 | |
| 514 PassOwnPtr<SelectorQuery> SelectorQuery::adopt(CSSSelectorList& selectorList) | 46 PassOwnPtr<SelectorQuery> SelectorQuery::adopt(CSSSelectorList& selectorList) |
| 515 { | 47 { |
| 516 return adoptPtr(new SelectorQuery(selectorList)); | 48 return adoptPtr(new SelectorQuery(selectorList)); |
| 517 } | 49 } |
| 518 | 50 |
| 51 SelectorQuery::~SelectorQuery() |
| 52 { |
| 53 } |
| 54 |
| 55 static PassOwnPtr<SelectorMatcher> createMatcher(CSSSelectorList& selectorList) |
| 56 { |
| 57 if (selectorList.hasOneSelector() && !selectorList.first()->tagHistory()) { |
| 58 const CSSSelector* selector = selectorList.first(); |
| 59 switch (selector->match()) { |
| 60 case CSSSelector::Id: |
| 61 return adoptPtr(new SelectorIdMatcher(selector->value())); |
| 62 case CSSSelector::Class: |
| 63 return adoptPtr(new SelectorClassMatcher(selector->value())); |
| 64 case CSSSelector::Tag: |
| 65 if (selector->tagQName() == anyQName()) |
| 66 return adoptPtr(new SelectorAnyMatcher()); |
| 67 return adoptPtr(new SelectorTagMatcher(selector->tagQName())); |
| 68 default: |
| 69 break; |
| 70 } |
| 71 } |
| 72 return adoptPtr(new SelectorCheckerMatcher(selectorList)); |
| 73 } |
| 74 |
| 519 SelectorQuery::SelectorQuery(CSSSelectorList& selectorList) | 75 SelectorQuery::SelectorQuery(CSSSelectorList& selectorList) |
| 76 : m_matcher(createMatcher(selectorList)) |
| 520 { | 77 { |
| 521 m_selectorList.adopt(selectorList); | 78 ASSERT(m_matcher); |
| 522 m_selectors.initialize(m_selectorList); | |
| 523 } | 79 } |
| 524 | 80 |
| 525 bool SelectorQuery::matches(Element& element) const | 81 bool SelectorQuery::matches(Element& element) const |
| 526 { | 82 { |
| 527 return m_selectors.matches(element); | 83 return m_matcher->match(element, element); |
| 528 } | 84 } |
| 529 | 85 |
| 530 Element* SelectorQuery::closest(Element& element) const | 86 Element* SelectorQuery::closest(Element& element) const |
| 531 { | 87 { |
| 532 return m_selectors.closest(element); | 88 for (Element* current = &element; current; current = current->parentElement(
)) { |
| 89 if (m_matcher->match(*current, element)) |
| 90 return current; |
| 91 } |
| 92 return nullptr; |
| 533 } | 93 } |
| 534 | 94 |
| 535 PassRefPtrWillBeRawPtr<StaticElementList> SelectorQuery::queryAll(ContainerNode&
rootNode) const | 95 PassRefPtrWillBeRawPtr<StaticElementList> SelectorQuery::queryAll(ContainerNode&
rootNode) const |
| 536 { | 96 { |
| 537 return m_selectors.queryAll(rootNode); | 97 WillBeHeapVector<RefPtrWillBeMember<Element>> result; |
| 98 m_matcher->findAll(rootNode, result); |
| 99 return StaticElementList::adopt(result); |
| 538 } | 100 } |
| 539 | 101 |
| 540 PassRefPtrWillBeRawPtr<Element> SelectorQuery::queryFirst(ContainerNode& rootNod
e) const | 102 Element* SelectorQuery::queryFirst(ContainerNode& rootNode) const |
| 541 { | 103 { |
| 542 return m_selectors.queryFirst(rootNode); | 104 return m_matcher->findFirst(rootNode); |
| 543 } | 105 } |
| 544 | 106 |
| 545 SelectorQuery* SelectorQueryCache::add(const AtomicString& selectors, const Docu
ment& document, ExceptionState& exceptionState) | 107 SelectorQuery* SelectorQueryCache::add(const AtomicString& selectors, const Docu
ment& document, ExceptionState& exceptionState) |
| 546 { | 108 { |
| 547 HashMap<AtomicString, OwnPtr<SelectorQuery>>::iterator it = m_entries.find(s
electors); | 109 HashMap<AtomicString, OwnPtr<SelectorQuery>>::iterator it = m_entries.find(s
electors); |
| 548 if (it != m_entries.end()) | 110 if (it != m_entries.end()) |
| 549 return it->value.get(); | 111 return it->value.get(); |
| 550 | 112 |
| 551 CSSSelectorList selectorList; | 113 CSSSelectorList selectorList; |
| 552 CSSParser::parseSelector(CSSParserContext(document, nullptr), selectors, sel
ectorList); | 114 CSSParser::parseSelector(CSSParserContext(document, nullptr), selectors, sel
ectorList); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 568 | 130 |
| 569 return m_entries.add(selectors, SelectorQuery::adopt(selectorList)).storedVa
lue->value.get(); | 131 return m_entries.add(selectors, SelectorQuery::adopt(selectorList)).storedVa
lue->value.get(); |
| 570 } | 132 } |
| 571 | 133 |
| 572 void SelectorQueryCache::invalidate() | 134 void SelectorQueryCache::invalidate() |
| 573 { | 135 { |
| 574 m_entries.clear(); | 136 m_entries.clear(); |
| 575 } | 137 } |
| 576 | 138 |
| 577 } | 139 } |
| OLD | NEW |