1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 2004-2005 Allan Sandfeld Jensen (kde (at) carewolf.com) 4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit (at) nickshanks.com) 5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved. 6 * Copyright (C) 2007 Alexey Proskuryakov <ap (at) webkit.org> 7 * Copyright (C) 2007, 2008 Eric Seidel <eric (at) webkit.org> 8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved. 10 * Copyright (C) Research In Motion Limited 2011. All rights reserved. 11 * 12 * This library is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU Library General Public 14 * License as published by the Free Software Foundation; either 15 * version 2 of the License, or (at your option) any later version. 16 * 17 * This library is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 * Library General Public License for more details. 21 * 22 * You should have received a copy of the GNU Library General Public License 23 * along with this library; see the file COPYING.LIB. If not, write to 24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 25 * Boston, MA 02110-1301, USA. 26 */ 27 28 #include "config.h" 29 #include "core/css/SelectorChecker.h" 30 31 #include "core/HTMLNames.h" 32 #include "core/css/CSSSelectorList.h" 33 #include "core/css/SiblingTraversalStrategies.h" 34 #include "core/dom/Document.h" 35 #include "core/dom/ElementTraversal.h" 36 #include "core/dom/FullscreenElementStack.h" 37 #include "core/dom/NodeRenderStyle.h" 38 #include "core/dom/Text.h" 39 #include "core/dom/shadow/InsertionPoint.h" 40 #include "core/dom/shadow/ShadowRoot.h" 41 #include "core/editing/FrameSelection.h" 42 #include "core/frame/LocalFrame.h" 43 #include "core/html/HTMLDocument.h" 44 #include "core/html/HTMLFrameElementBase.h" 45 #include "core/html/HTMLInputElement.h" 46 #include "core/html/HTMLOptionElement.h" 47 #include "core/html/parser/HTMLParserIdioms.h" 48 #include "core/html/track/vtt/VTTElement.h" 49 #include "core/inspector/InspectorInstrumentation.h" 50 #include "core/page/FocusController.h" 51 #include "core/rendering/RenderObject.h" 52 #include "core/rendering/RenderScrollbar.h" 53 #include "core/rendering/style/RenderStyle.h" 54 #include "platform/scroll/ScrollableArea.h" 55 #include "platform/scroll/ScrollbarTheme.h" 56 57 namespace WebCore { 58 59 using namespace HTMLNames; 60 61 SelectorChecker::SelectorChecker(Document& document, Mode mode) 62 : m_strictParsing(!document.inQuirksMode()) 63 , m_documentIsHTML(document.isHTMLDocument()) 64 , m_mode(mode) 65 { 66 } 67 68 static bool matchesCustomPseudoElement(const Element* element, const CSSSelector& selector) 69 { 70 ShadowRoot* root = element->containingShadowRoot(); 71 if (!root || root->type() != ShadowRoot::UserAgentShadowRoot) 72 return false; 73 74 if (element->shadowPseudoId() != selector.value()) 75 return false; 76 77 return true; 78 } 79 80 Element* SelectorChecker::parentElement(const SelectorCheckingContext& context, bool allowToCrossBoundary) const 81 { 82 // CrossesBoundary means we don't care any context.scope. So we can walk up from a shadow root to its shadow host. 83 if (allowToCrossBoundary) 84 return context.element->parentOrShadowHostElement(); 85 86 // If context.scope is a shadow root, we should walk up to its shadow host. 87 if ((context.behaviorAtBoundary & SelectorChecker::ScopeIsShadowRoot) && context.scope == context.element->containingShadowRoot()) 88 return context.element->parentOrShadowHostElement(); 89 90 if ((context.behaviorAtBoundary & SelectorChecker::BoundaryBehaviorMask) != SelectorChecker::StaysWithinTreeScope) 91 return context.element->parentElement(); 92 93 // If context.scope is some element in some shadow tree and querySelector initialized the context, 94 // e.g. shadowRoot.querySelector(':host *'), 95 // (a) context.element has the same treescope as context.scope, need to walk up to its shadow host. 96 // (b) Otherwise, should not walk up from a shadow root to a shadow host. 97 if (context.scope && context.scope->treeScope() == context.element->treeScope()) 98 return context.element->parentOrShadowHostElement(); 99 100 return context.element->parentElement(); 101 } 102 103 bool SelectorChecker::scopeContainsLastMatchedElement(const SelectorCheckingContext& context) const 104 { 105 if (!(context.behaviorAtBoundary & SelectorChecker::ScopeContainsLastMatchedElement)) 106 return true; 107 108 ASSERT(context.scope); 109 if (context.scope->treeScope() == context.element->treeScope()) 110 return true; 111 112 // Because Blink treats a shadow host's TreeScope as a separate one from its descendent shadow roots, 113 // if the last matched element is a shadow host, the condition above isn't met, even though it 114 // should be. 115 return context.element == context.scope->shadowHost() && (!context.previousElement || context.previousElement->isInDescendantTreeOf(context.element)); 116 } 117 118 static inline bool nextSelectorExceedsScope(const SelectorChecker::SelectorCheckingContext& context) 119 { 120 if (context.scope && context.scope->isInShadowTree()) 121 return context.element == context.scope->shadowHost(); 122 123 return false; 124 } 125 126 // Recursive check of selectors and combinators 127 // It can return 4 different values: 128 // * SelectorMatches - the selector matches the element e 129 // * SelectorFailsLocally - the selector fails for the element e 130 // * SelectorFailsAllSiblings - the selector fails for e and any sibling of e 131 // * SelectorFailsCompletely - the selector fails for e and any sibling or ancestor of e 132 template<typename SiblingTraversalStrategy> 133 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const 134 { 135 // first selector has to match 136 unsigned specificity = 0; 137 if (!checkOne(context, siblingTraversalStrategy, &specificity)) 138 return SelectorFailsLocally; 139 140 if (context.selector->match() == CSSSelector::PseudoElement) { 141 if (context.selector->isCustomPseudoElement()) { 142 if (!matchesCustomPseudoElement(context.element, *context.selector)) 143 return SelectorFailsLocally; 144 } else if (context.selector->isContentPseudoElement()) { 145 if (!context.element->isInShadowTree() || !context.element->isInsertionPoint()) 146 return SelectorFailsLocally; 147 } else if (context.selector->isShadowPseudoElement()) { 148 if (!context.element->isInShadowTree() || !context.previousElement) 149 return SelectorFailsCompletely; 150 } else { 151 if ((!context.elementStyle && m_mode == ResolvingStyle) || m_mode == QueryingRules) 152 return SelectorFailsLocally; 153 154 PseudoId pseudoId = CSSSelector::pseudoId(context.selector->pseudoType()); 155 if (pseudoId == FIRST_LETTER) 156 context.element->document().styleEngine()->setUsesFirstLetterRules(true); 157 if (pseudoId != NOPSEUDO && m_mode != SharingRules && result) 158 result->dynamicPseudo = pseudoId; 159 } 160 } 161 162 // Prepare next selector 163 if (context.selector->isLastInTagHistory()) { 164 if (scopeContainsLastMatchedElement(context)) { 165 if (result) 166 result->specificity += specificity; 167 return SelectorMatches; 168 } 169 return SelectorFailsLocally; 170 } 171 172 Match match; 173 if (context.selector->relation() != CSSSelector::SubSelector) { 174 // Abort if the next selector would exceed the scope. 175 if (nextSelectorExceedsScope(context)) 176 return SelectorFailsCompletely; 177 178 // Bail-out if this selector is irrelevant for the pseudoId 179 if (context.pseudoId != NOPSEUDO && (!result || context.pseudoId != result->dynamicPseudo)) 180 return SelectorFailsCompletely; 181 182 if (result) { 183 TemporaryChange<PseudoId> dynamicPseudoScope(result->dynamicPseudo, NOPSEUDO); 184 match = matchForRelation(context, siblingTraversalStrategy, result); 185 } else { 186 return matchForRelation(context, siblingTraversalStrategy, 0); 187 } 188 } else { 189 match = matchForSubSelector(context, siblingTraversalStrategy, result); 190 } 191 if (match != SelectorMatches || !result) 192 return match; 193 194 result->specificity += specificity; 195 return SelectorMatches; 196 } 197 198 static inline SelectorChecker::SelectorCheckingContext prepareNextContextForRelation(const SelectorChecker::SelectorCheckingContext& context) 199 { 200 SelectorChecker::SelectorCheckingContext nextContext(context); 201 ASSERT(context.selector->tagHistory()); 202 nextContext.selector = context.selector->tagHistory(); 203 return nextContext; 204 } 205 206 static inline bool isAuthorShadowRoot(const Node* node) 207 { 208 return node && node->isShadowRoot() && toShadowRoot(node)->type() == ShadowRoot::AuthorShadowRoot; 209 } 210 211 template<typename SiblingTraversalStrategy> 212 SelectorChecker::Match SelectorChecker::matchForSubSelector(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const 213 { 214 SelectorCheckingContext nextContext = prepareNextContextForRelation(context); 215 216 PseudoId dynamicPseudo = result ? result->dynamicPseudo : NOPSEUDO; 217 // a selector is invalid if something follows a pseudo-element 218 // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else) 219 // to follow the pseudo elements. 220 nextContext.hasScrollbarPseudo = dynamicPseudo != NOPSEUDO && (context.scrollbar || dynamicPseudo == SCROLLBAR_CORNER || dynamicPseudo == RESIZER); 221 nextContext.hasSelectionPseudo = dynamicPseudo == SELECTION; 222 if ((context.elementStyle || m_mode == CollectingCSSRules || m_mode == CollectingStyleRules || m_mode == QueryingRules) && dynamicPseudo != NOPSEUDO 223 && !nextContext.hasSelectionPseudo 224 && !(nextContext.hasScrollbarPseudo && nextContext.selector->match() == CSSSelector::PseudoClass)) 225 return SelectorFailsCompletely; 226 227 nextContext.isSubSelector = true; 228 return match(nextContext, siblingTraversalStrategy, result); 229 } 230 231 static bool selectorMatchesShadowRoot(const CSSSelector* selector) 232 { 233 return selector && selector->isShadowPseudoElement(); 234 } 235 236 template<typename SiblingTraversalStrategy> 237 SelectorChecker::Match SelectorChecker::matchForPseudoShadow(const ContainerNode* node, const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const 238 { 239 if (!isAuthorShadowRoot(node)) 240 return SelectorFailsCompletely; 241 return match(context, siblingTraversalStrategy, result); 242 } 243 244 template<typename SiblingTraversalStrategy> 245 SelectorChecker::Match SelectorChecker::matchForRelation(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const 246 { 247 SelectorCheckingContext nextContext = prepareNextContextForRelation(context); 248 nextContext.previousElement = context.element; 249 250 CSSSelector::Relation relation = context.selector->relation(); 251 252 // Disable :visited matching when we see the first link or try to match anything else than an ancestors. 253 if (!context.isSubSelector && (context.element->isLink() || (relation != CSSSelector::Descendant && relation != CSSSelector::Child))) 254 nextContext.visitedMatchType = VisitedMatchDisabled; 255 256 nextContext.pseudoId = NOPSEUDO; 257 258 switch (relation) { 259 case CSSSelector::Descendant: 260 if (context.selector->relationIsAffectedByPseudoContent()) { 261 for (Element* element = context.element; element; element = element->parentElement()) { 262 if (matchForShadowDistributed(element, siblingTraversalStrategy, nextContext, result) == SelectorMatches) 263 return SelectorMatches; 264 } 265 return SelectorFailsCompletely; 266 } 267 nextContext.isSubSelector = false; 268 nextContext.elementStyle = 0; 269 270 if (selectorMatchesShadowRoot(nextContext.selector)) 271 return matchForPseudoShadow(context.element->containingShadowRoot(), nextContext, siblingTraversalStrategy, result); 272 273 for (nextContext.element = parentElement(context); nextContext.element; nextContext.element = parentElement(nextContext)) { 274 Match match = this->match(nextContext, siblingTraversalStrategy, result); 275 if (match == SelectorMatches || match == SelectorFailsCompletely) 276 return match; 277 if (nextSelectorExceedsScope(nextContext)) 278 return SelectorFailsCompletely; 279 } 280 return SelectorFailsCompletely; 281 case CSSSelector::Child: 282 { 283 if (context.selector->relationIsAffectedByPseudoContent()) 284 return matchForShadowDistributed(context.element, siblingTraversalStrategy, nextContext, result); 285 286 nextContext.isSubSelector = false; 287 nextContext.elementStyle = 0; 288 289 if (selectorMatchesShadowRoot(nextContext.selector)) 290 return matchForPseudoShadow(context.element->parentNode(), nextContext, siblingTraversalStrategy, result); 291 292 nextContext.element = parentElement(context); 293 if (!nextContext.element) 294 return SelectorFailsCompletely; 295 return match(nextContext, siblingTraversalStrategy, result); 296 } 297 case CSSSelector::DirectAdjacent: 298 // Shadow roots can't have sibling elements 299 if (selectorMatchesShadowRoot(nextContext.selector)) 300 return SelectorFailsCompletely; 301 302 if (m_mode == ResolvingStyle) { 303 if (ContainerNode* parent = context.element->parentElementOrShadowRoot()) 304 parent->setChildrenAffectedByDirectAdjacentRules(); 305 } 306 nextContext.element = ElementTraversal::previousSibling(*context.element); 307 if (!nextContext.element) 308 return SelectorFailsAllSiblings; 309 nextContext.isSubSelector = false; 310 nextContext.elementStyle = 0; 311 return match(nextContext, siblingTraversalStrategy, result); 312 313 case CSSSelector::IndirectAdjacent: 314 // Shadow roots can't have sibling elements 315 if (selectorMatchesShadowRoot(nextContext.selector)) 316 return SelectorFailsCompletely; 317 318 if (m_mode == ResolvingStyle) { 319 if (ContainerNode* parent = context.element->parentElementOrShadowRoot()) 320 parent->setChildrenAffectedByIndirectAdjacentRules(); 321 } 322 nextContext.element = ElementTraversal::previousSibling(*context.element); 323 nextContext.isSubSelector = false; 324 nextContext.elementStyle = 0; 325 for (; nextContext.element; nextContext.element = ElementTraversal::previousSibling(*nextContext.element)) { 326 Match match = this->match(nextContext, siblingTraversalStrategy, result); 327 if (match == SelectorMatches || match == SelectorFailsAllSiblings || match == SelectorFailsCompletely) 328 return match; 329 }; 330 return SelectorFailsAllSiblings; 331 332 case CSSSelector::ShadowPseudo: 333 { 334 // If we're in the same tree-scope as the scoping element, then following a shadow descendant combinator would escape that and thus the scope. 335 if (context.scope && context.scope->shadowHost() && context.scope->shadowHost()->treeScope() == context.element->treeScope() && (context.behaviorAtBoundary & BoundaryBehaviorMask) != StaysWithinTreeScope) 336 return SelectorFailsCompletely; 337 338 Element* shadowHost = context.element->shadowHost(); 339 if (!shadowHost) 340 return SelectorFailsCompletely; 341 nextContext.element = shadowHost; 342 nextContext.isSubSelector = false; 343 nextContext.elementStyle = 0; 344 return this->match(nextContext, siblingTraversalStrategy, result); 345 } 346 347 case CSSSelector::ShadowDeep: 348 { 349 nextContext.isSubSelector = false; 350 nextContext.elementStyle = 0; 351 for (nextContext.element = parentElement(context, true); nextContext.element; nextContext.element = parentElement(nextContext, true)) { 352 Match match = this->match(nextContext, siblingTraversalStrategy, result); 353 if (match == SelectorMatches || match == SelectorFailsCompletely) 354 return match; 355 if (nextSelectorExceedsScope(nextContext)) 356 return SelectorFailsCompletely; 357 } 358 return SelectorFailsCompletely; 359 } 360 361 case CSSSelector::SubSelector: 362 ASSERT_NOT_REACHED(); 363 } 364 365 ASSERT_NOT_REACHED(); 366 return SelectorFailsCompletely; 367 } 368 369 template<typename SiblingTraversalStrategy> 370 SelectorChecker::Match SelectorChecker::matchForShadowDistributed(const Element* element, const SiblingTraversalStrategy& siblingTraversalStrategy, SelectorCheckingContext& nextContext, MatchResult* result) const 371 { 372 ASSERT(element); 373 WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints; 374 375 const ContainerNode* scope = nextContext.scope; 376 BehaviorAtBoundary behaviorAtBoundary = nextContext.behaviorAtBoundary; 377 378 collectDestinationInsertionPoints(*element, insertionPoints); 379 for (size_t i = 0; i < insertionPoints.size(); ++i) { 380 nextContext.element = insertionPoints[i]; 381 382 // If a given scope is a shadow host of an insertion point but behaviorAtBoundary doesn't have ScopeIsShadowRoot, 383 // we need to update behaviorAtBoundary to make selectors like ":host > ::content" work correctly. 384 if (m_mode == SharingRules) { 385 nextContext.behaviorAtBoundary = static_cast<BehaviorAtBoundary>(behaviorAtBoundary | ScopeIsShadowRoot); 386 nextContext.scope = insertionPoints[i]->containingShadowRoot(); 387 } else if (scope == insertionPoints[i]->containingShadowRoot() && !(behaviorAtBoundary & ScopeIsShadowRoot)) { 388 nextContext.behaviorAtBoundary = static_cast<BehaviorAtBoundary>(behaviorAtBoundary | ScopeIsShadowRoot); 389 } else { 390 nextContext.behaviorAtBoundary = behaviorAtBoundary; 391 } 392 393 nextContext.isSubSelector = false; 394 nextContext.elementStyle = 0; 395 if (match(nextContext, siblingTraversalStrategy, result) == SelectorMatches) 396 return SelectorMatches; 397 } 398 return SelectorFailsLocally; 399 } 400 401 template<typename CharType> 402 static inline bool containsHTMLSpaceTemplate(const CharType* string, unsigned length) 403 { 404 for (unsigned i = 0; i < length; ++i) 405 if (isHTMLSpace<CharType>(string[i])) 406 return true; 407 return false; 408 } 409 410 static inline bool containsHTMLSpace(const AtomicString& string) 411 { 412 if (LIKELY(string.is8Bit())) 413 return containsHTMLSpaceTemplate<LChar>(string.characters8(), string.length()); 414 return containsHTMLSpaceTemplate<UChar>(string.characters16(), string.length()); 415 } 416 417 static bool attributeValueMatches(const Attribute& attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive) 418 { 419 const AtomicString& value = attributeItem.value(); 420 if (value.isNull()) 421 return false; 422 423 switch (match) { 424 case CSSSelector::Exact: 425 if (caseSensitive ? selectorValue != value : !equalIgnoringCase(selectorValue, value)) 426 return false; 427 break; 428 case CSSSelector::List: 429 { 430 // Ignore empty selectors or selectors containing HTML spaces 431 if (selectorValue.isEmpty() || containsHTMLSpace(selectorValue)) 432 return false; 433 434 unsigned startSearchAt = 0; 435 while (true) { 436 size_t foundPos = value.find(selectorValue, startSearchAt, caseSensitive); 437 if (foundPos == kNotFound) 438 return false; 439 if (!foundPos || isHTMLSpace<UChar>(value[foundPos - 1])) { 440 unsigned endStr = foundPos + selectorValue.length(); 441 if (endStr == value.length() || isHTMLSpace<UChar>(value[endStr])) 442 break; // We found a match. 443 } 444 445 // No match. Keep looking. 446 startSearchAt = foundPos + 1; 447 } 448 break; 449 } 450 case CSSSelector::Contain: 451 if (!value.contains(selectorValue, caseSensitive) || selectorValue.isEmpty()) 452 return false; 453 break; 454 case CSSSelector::Begin: 455 if (!value.startsWith(selectorValue, caseSensitive) || selectorValue.isEmpty()) 456 return false; 457 break; 458 case CSSSelector::End: 459 if (!value.endsWith(selectorValue, caseSensitive) || selectorValue.isEmpty()) 460 return false; 461 break; 462 case CSSSelector::Hyphen: 463 if (value.length() < selectorValue.length()) 464 return false; 465 if (!value.startsWith(selectorValue, caseSensitive)) 466 return false; 467 // It they start the same, check for exact match or following '-': 468 if (value.length() != selectorValue.length() && value[selectorValue.length()] != '-') 469 return false; 470 break; 471 case CSSSelector::PseudoClass: 472 case CSSSelector::PseudoElement: 473 default: 474 break; 475 } 476 477 return true; 478 } 479 480 static bool anyAttributeMatches(Element& element, CSSSelector::Match match, const CSSSelector& selector) 481 { 482 const QualifiedName& selectorAttr = selector.attribute(); 483 ASSERT(selectorAttr.localName() != starAtom); // Should not be possible from the CSS grammar. 484 485 // Synchronize the attribute in case it is lazy-computed. 486 // Currently all lazy properties have a null namespace, so only pass localName(). 487 element.synchronizeAttribute(selectorAttr.localName()); 488 489 if (!element.hasAttributesWithoutUpdate()) 490 return false; 491 492 const AtomicString& selectorValue = selector.value(); 493 494 AttributeCollection attributes = element.attributes(); 495 AttributeCollection::const_iterator end = attributes.end(); 496 for (AttributeCollection::const_iterator it = attributes.begin(); it != end; ++it) { 497 const Attribute& attributeItem = *it; 498 499 if (!attributeItem.matches(selectorAttr)) 500 continue; 501 502 if (attributeValueMatches(attributeItem, match, selectorValue, true)) 503 return true; 504 505 // Case sensitivity for attribute matching is looser than hasAttribute or 506 // Element::shouldIgnoreAttributeCase() for now. Unclear if that's correct. 507 bool caseSensitive = !element.document().isHTMLDocument() || HTMLDocument::isCaseSensitiveAttribute(selectorAttr); 508 509 // If case-insensitive, re-check, and count if result differs. 510 // See http://code.google.com/p/chromium/issues/detail?id=327060 511 if (!caseSensitive && attributeValueMatches(attributeItem, match, selectorValue, false)) { 512 UseCounter::count(element.document(), UseCounter::CaseInsensitiveAttrSelectorMatch); 513 return true; 514 } 515 } 516 517 return false; 518 } 519 520 template<typename SiblingTraversalStrategy> 521 bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, unsigned* specificity) const 522 { 523 ASSERT(context.element); 524 Element& element = *context.element; 525 ASSERT(context.selector); 526 const CSSSelector& selector = *context.selector; 527 528 bool elementIsHostInItsShadowTree = isHostInItsShadowTree(element, context.behaviorAtBoundary, context.scope); 529 530 // Only :host and :ancestor should match the host: http://drafts.csswg.org/css-scoping/#host-element 531 if (elementIsHostInItsShadowTree && !selector.isHostPseudoClass() 532 && !(context.behaviorAtBoundary & TreatShadowHostAsNormalScope)) 533 return false; 534 535 if (selector.match() == CSSSelector::Tag) 536 return SelectorChecker::tagMatches(element, selector.tagQName()); 537 538 if (selector.match() == CSSSelector::Class) 539 return element.hasClass() && element.classNames().contains(selector.value()); 540 541 if (selector.match() == CSSSelector::Id) 542 return element.hasID() && element.idForStyleResolution() == selector.value(); 543 544 if (selector.isAttributeSelector()) 545 return anyAttributeMatches(element, selector.match(), selector); 546 547 if (selector.match() == CSSSelector::PseudoClass) { 548 // Handle :not up front. 549 if (selector.pseudoType() == CSSSelector::PseudoNot) { 550 SelectorCheckingContext subContext(context); 551 subContext.isSubSelector = true; 552 ASSERT(selector.selectorList()); 553 for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = subContext.selector->tagHistory()) { 554 // :not cannot nest. I don't really know why this is a 555 // restriction in CSS3, but it is, so let's honor it. 556 // the parser enforces that this never occurs 557 ASSERT(subContext.selector->pseudoType() != CSSSelector::PseudoNot); 558 // We select between :visited and :link when applying. We don't know which one applied (or not) yet. 559 if (subContext.selector->pseudoType() == CSSSelector::PseudoVisited || (subContext.selector->pseudoType() == CSSSelector::PseudoLink && subContext.visitedMatchType == VisitedMatchEnabled)) 560 return true; 561 // context.scope is not available if m_mode == SharingRules. 562 // We cannot determine whether :host or :scope matches a given element or not. 563 if (m_mode == SharingRules && (subContext.selector->isHostPseudoClass() || subContext.selector->pseudoType() == CSSSelector::PseudoScope)) 564 return true; 565 if (!checkOne(subContext, DOMSiblingTraversalStrategy())) 566 return true; 567 } 568 } else if (context.hasScrollbarPseudo) { 569 // CSS scrollbars match a specific subset of pseudo classes, and they have specialized rules for each 570 // (since there are no elements involved). 571 return checkScrollbarPseudoClass(context, &element.document(), selector); 572 } else if (context.hasSelectionPseudo) { 573 if (selector.pseudoType() == CSSSelector::PseudoWindowInactive) 574 return !element.document().page()->focusController().isActive(); 575 } 576 577 // Normal element pseudo class checking. 578 switch (selector.pseudoType()) { 579 // Pseudo classes: 580 case CSSSelector::PseudoNot: 581 break; // Already handled up above. 582 case CSSSelector::PseudoEmpty: 583 { 584 bool result = true; 585 for (Node* n = element.firstChild(); n; n = n->nextSibling()) { 586 if (n->isElementNode()) { 587 result = false; 588 break; 589 } 590 if (n->isTextNode()) { 591 Text* textNode = toText(n); 592 if (!textNode->data().isEmpty()) { 593 result = false; 594 break; 595 } 596 } 597 } 598 if (m_mode == ResolvingStyle) { 599 element.setStyleAffectedByEmpty(); 600 if (context.elementStyle) 601 context.elementStyle->setEmptyState(result); 602 else if (element.renderStyle() && (element.document().styleEngine()->usesSiblingRules() || element.renderStyle()->unique())) 603 element.renderStyle()->setEmptyState(result); 604 } 605 return result; 606 } 607 case CSSSelector::PseudoFirstChild: 608 // first-child matches the first child that is an element 609 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) { 610 bool result = siblingTraversalStrategy.isFirstChild(element); 611 if (m_mode == ResolvingStyle) { 612 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle(); 613 parent->setChildrenAffectedByFirstChildRules(); 614 if (result && childStyle) 615 childStyle->setFirstChildState(); 616 } 617 return result; 618 } 619 break; 620 case CSSSelector::PseudoFirstOfType: 621 // first-of-type matches the first element of its type 622 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) { 623 bool result = siblingTraversalStrategy.isFirstOfType(element, element.tagQName()); 624 if (m_mode == ResolvingStyle) 625 parent->setChildrenAffectedByForwardPositionalRules(); 626 return result; 627 } 628 break; 629 case CSSSelector::PseudoLastChild: 630 // last-child matches the last child that is an element 631 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) { 632 bool result = parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element); 633 if (m_mode == ResolvingStyle) { 634 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle(); 635 parent->setChildrenAffectedByLastChildRules(); 636 if (result && childStyle) 637 childStyle->setLastChildState(); 638 } 639 return result; 640 } 641 break; 642 case CSSSelector::PseudoLastOfType: 643 // last-of-type matches the last element of its type 644 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) { 645 if (m_mode == ResolvingStyle) 646 parent->setChildrenAffectedByBackwardPositionalRules(); 647 if (!parent->isFinishedParsingChildren()) 648 return false; 649 return siblingTraversalStrategy.isLastOfType(element, element.tagQName()); 650 } 651 break; 652 case CSSSelector::PseudoOnlyChild: 653 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) { 654 bool firstChild = siblingTraversalStrategy.isFirstChild(element); 655 bool onlyChild = firstChild && parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element); 656 if (m_mode == ResolvingStyle) { 657 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle(); 658 parent->setChildrenAffectedByFirstChildRules(); 659 parent->setChildrenAffectedByLastChildRules(); 660 if (firstChild && childStyle) 661 childStyle->setFirstChildState(); 662 if (onlyChild && childStyle) 663 childStyle->setLastChildState(); 664 } 665 return onlyChild; 666 } 667 break; 668 case CSSSelector::PseudoOnlyOfType: 669 // FIXME: This selector is very slow. 670 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) { 671 if (m_mode == ResolvingStyle) { 672 parent->setChildrenAffectedByForwardPositionalRules(); 673 parent->setChildrenAffectedByBackwardPositionalRules(); 674 } 675 if (!parent->isFinishedParsingChildren()) 676 return false; 677 return siblingTraversalStrategy.isFirstOfType(element, element.tagQName()) && siblingTraversalStrategy.isLastOfType(element, element.tagQName()); 678 } 679 break; 680 case CSSSelector::PseudoNthChild: 681 if (!selector.parseNth()) 682 break; 683 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) { 684 int count = 1 + siblingTraversalStrategy.countElementsBefore(element); 685 if (m_mode == ResolvingStyle) { 686 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle(); 687 if (childStyle) 688 childStyle->setUnique(); 689 parent->setChildrenAffectedByForwardPositionalRules(); 690 } 691 692 if (selector.matchNth(count)) 693 return true; 694 } 695 break; 696 case CSSSelector::PseudoNthOfType: 697 if (!selector.parseNth()) 698 break; 699 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) { 700 int count = 1 + siblingTraversalStrategy.countElementsOfTypeBefore(element, element.tagQName()); 701 if (m_mode == ResolvingStyle) 702 parent->setChildrenAffectedByForwardPositionalRules(); 703 704 if (selector.matchNth(count)) 705 return true; 706 } 707 break; 708 case CSSSelector::PseudoNthLastChild: 709 if (!selector.parseNth()) 710 break; 711 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) { 712 if (m_mode == ResolvingStyle) 713 parent->setChildrenAffectedByBackwardPositionalRules(); 714 if (!parent->isFinishedParsingChildren()) 715 return false; 716 int count = 1 + siblingTraversalStrategy.countElementsAfter(element); 717 if (selector.matchNth(count)) 718 return true; 719 } 720 break; 721 case CSSSelector::PseudoNthLastOfType: 722 if (!selector.parseNth()) 723 break; 724 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) { 725 if (m_mode == ResolvingStyle) 726 parent->setChildrenAffectedByBackwardPositionalRules(); 727 if (!parent->isFinishedParsingChildren()) 728 return false; 729 730 int count = 1 + siblingTraversalStrategy.countElementsOfTypeAfter(element, element.tagQName()); 731 if (selector.matchNth(count)) 732 return true; 733 } 734 break; 735 case CSSSelector::PseudoTarget: 736 if (element == element.document().cssTarget()) 737 return true; 738 break; 739 case CSSSelector::PseudoAny: 740 { 741 SelectorCheckingContext subContext(context); 742 subContext.isSubSelector = true; 743 ASSERT(selector.selectorList()); 744 for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) { 745 if (match(subContext, siblingTraversalStrategy) == SelectorMatches) 746 return true; 747 } 748 } 749 break; 750 case CSSSelector::PseudoAutofill: 751 if (!element.isFormControlElement()) 752 break; 753 return toHTMLFormControlElement(element).isAutofilled(); 754 case CSSSelector::PseudoAnyLink: 755 case CSSSelector::PseudoLink: 756 // :visited and :link matches are separated later when applying the style. Here both classes match all links... 757 return element.isLink(); 758 case CSSSelector::PseudoVisited: 759 // ...except if :visited matching is disabled for ancestor/sibling matching. 760 return element.isLink() && context.visitedMatchType == VisitedMatchEnabled; 761 case CSSSelector::PseudoDrag: 762 if (m_mode == ResolvingStyle) { 763 if (context.elementStyle) 764 context.elementStyle->setAffectedByDrag(); 765 else 766 element.setChildrenOrSiblingsAffectedByDrag(); 767 } 768 if (element.renderer() && element.renderer()->isDragging()) 769 return true; 770 break; 771 case CSSSelector::PseudoFocus: 772 if (m_mode == ResolvingStyle) { 773 if (context.elementStyle) 774 context.elementStyle->setAffectedByFocus(); 775 else 776 element.setChildrenOrSiblingsAffectedByFocus(); 777 } 778 return matchesFocusPseudoClass(element); 779 case CSSSelector::PseudoHover: 780 // If we're in quirks mode, then hover should never match anchors with no 781 // href and *:hover should not match anything. This is important for sites like wsj.com. 782 if (m_strictParsing || context.isSubSelector || element.isLink()) { 783 if (m_mode == ResolvingStyle) { 784 if (context.elementStyle) 785 context.elementStyle->setAffectedByHover(); 786 else 787 element.setChildrenOrSiblingsAffectedByHover(); 788 } 789 if (element.hovered() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoHover)) 790 return true; 791 } 792 break; 793 case CSSSelector::PseudoActive: 794 // If we're in quirks mode, then :active should never match anchors with no 795 // href and *:active should not match anything. 796 if (m_strictParsing || context.isSubSelector || element.isLink()) { 797 if (m_mode == ResolvingStyle) { 798 if (context.elementStyle) 799 context.elementStyle->setAffectedByActive(); 800 else 801 element.setChildrenOrSiblingsAffectedByActive(); 802 } 803 if (element.active() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoActive)) 804 return true; 805 } 806 break; 807 case CSSSelector::PseudoEnabled: 808 if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element)) 809 return !element.isDisabledFormControl(); 810 break; 811 case CSSSelector::PseudoFullPageMedia: 812 return element.document().isMediaDocument(); 813 break; 814 case CSSSelector::PseudoDefault: 815 return element.isDefaultButtonForForm(); 816 case CSSSelector::PseudoDisabled: 817 if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element)) 818 return element.isDisabledFormControl(); 819 break; 820 case CSSSelector::PseudoReadOnly: 821 return element.matchesReadOnlyPseudoClass(); 822 case CSSSelector::PseudoReadWrite: 823 return element.matchesReadWritePseudoClass(); 824 case CSSSelector::PseudoOptional: 825 return element.isOptionalFormControl(); 826 case CSSSelector::PseudoRequired: 827 return element.isRequiredFormControl(); 828 case CSSSelector::PseudoValid: 829 element.document().setContainsValidityStyleRules(); 830 return element.willValidate() && element.isValidFormControlElement(); 831 case CSSSelector::PseudoInvalid: 832 element.document().setContainsValidityStyleRules(); 833 return element.willValidate() && !element.isValidFormControlElement(); 834 case CSSSelector::PseudoChecked: 835 { 836 if (isHTMLInputElement(element)) { 837 HTMLInputElement& inputElement = toHTMLInputElement(element); 838 // Even though WinIE allows checked and indeterminate to 839 // co-exist, the CSS selector spec says that you can't be 840 // both checked and indeterminate. We will behave like WinIE 841 // behind the scenes and just obey the CSS spec here in the 842 // test for matching the pseudo. 843 if (inputElement.shouldAppearChecked() && !inputElement.shouldAppearIndeterminate()) 844 return true; 845 } else if (isHTMLOptionElement(element) && toHTMLOptionElement(element).selected()) 846 return true; 847 break; 848 } 849 case CSSSelector::PseudoIndeterminate: 850 return element.shouldAppearIndeterminate(); 851 case CSSSelector::PseudoRoot: 852 if (element == element.document().documentElement()) 853 return true; 854 break; 855 case CSSSelector::PseudoLang: 856 { 857 AtomicString value; 858 if (element.isVTTElement()) 859 value = toVTTElement(element).language(); 860 else 861 value = element.computeInheritedLanguage(); 862 const AtomicString& argument = selector.argument(); 863 if (value.isEmpty() || !value.startsWith(argument, false)) 864 break; 865 if (value.length() != argument.length() && value[argument.length()] != '-') 866 break; 867 return true; 868 } 869 case CSSSelector::PseudoFullScreen: 870 // While a Document is in the fullscreen state, and the document's current fullscreen 871 // element is an element in the document, the 'full-screen' pseudoclass applies to 872 // that element. Also, an <iframe>, <object> or <embed> element whose child browsing 873 // context's Document is in the fullscreen state has the 'full-screen' pseudoclass applied. 874 if (isHTMLFrameElementBase(element) && element.containsFullScreenElement()) 875 return true; 876 if (FullscreenElementStack* fullscreen = FullscreenElementStack::fromIfExists(element.document())) { 877 if (!fullscreen->webkitIsFullScreen()) 878 return false; 879 return element == fullscreen->webkitCurrentFullScreenElement(); 880 } 881 return false; 882 case CSSSelector::PseudoFullScreenAncestor: 883 return element.containsFullScreenElement(); 884 case CSSSelector::PseudoFullScreenDocument: 885 // While a Document is in the fullscreen state, the 'full-screen-document' pseudoclass applies 886 // to all elements of that Document. 887 if (!FullscreenElementStack::isFullScreen(element.document())) 888 return false; 889 return true; 890 case CSSSelector::PseudoInRange: 891 element.document().setContainsValidityStyleRules(); 892 return element.isInRange(); 893 case CSSSelector::PseudoOutOfRange: 894 element.document().setContainsValidityStyleRules(); 895 return element.isOutOfRange(); 896 case CSSSelector::PseudoFutureCue: 897 return (element.isVTTElement() && !toVTTElement(element).isPastNode()); 898 case CSSSelector::PseudoPastCue: 899 return (element.isVTTElement() && toVTTElement(element).isPastNode()); 900 901 case CSSSelector::PseudoScope: 902 { 903 if (m_mode == SharingRules) 904 return true; 905 const Node* contextualReferenceNode = !context.scope ? element.document().documentElement() : context.scope; 906 if (element == contextualReferenceNode) 907 return true; 908 break; 909 } 910 911 case CSSSelector::PseudoUnresolved: 912 if (element.isUnresolvedCustomElement()) 913 return true; 914 break; 915 916 case CSSSelector::PseudoHost: 917 case CSSSelector::PseudoHostContext: 918 { 919 if (m_mode == SharingRules) 920 return true; 921 // :host only matches a shadow host when :host is in a shadow tree of the shadow host. 922 if (!context.scope) 923 return false; 924 const ContainerNode* shadowHost = context.scope->shadowHost(); 925 if (!shadowHost || shadowHost != element) 926 return false; 927 ASSERT(element.shadow()); 928 929 // For empty parameter case, i.e. just :host or :host(). 930 if (!selector.selectorList()) // Use *'s specificity. So just 0. 931 return true; 932 933 SelectorCheckingContext subContext(context); 934 subContext.isSubSelector = true; 935 936 bool matched = false; 937 unsigned maxSpecificity = 0; 938 939 // If one of simple selectors matches an element, returns SelectorMatches. Just "OR". 940 for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) { 941 subContext.behaviorAtBoundary = ScopeIsShadowHostInPseudoHostParameter; 942 subContext.scope = context.scope; 943 // Use NodeRenderingTraversal to traverse a composed ancestor list of a given element. 944 Element* nextElement = &element; 945 SelectorCheckingContext hostContext(subContext); 946 do { 947 MatchResult subResult; 948 hostContext.element = nextElement; 949 if (match(hostContext, siblingTraversalStrategy, &subResult) == SelectorMatches) { 950 matched = true; 951 // Consider div:host(div:host(div:host(div:host...))). 952 maxSpecificity = std::max(maxSpecificity, hostContext.selector->specificity() + subResult.specificity); 953 break; 954 } 955 hostContext.behaviorAtBoundary = DoesNotCrossBoundary; 956 hostContext.scope = 0; 957 958 if (selector.pseudoType() == CSSSelector::PseudoHost) 959 break; 960 961 hostContext.elementStyle = 0; 962 nextElement = NodeRenderingTraversal::parentElement(nextElement); 963 } while (nextElement); 964 } 965 if (matched) { 966 if (specificity) 967 *specificity = maxSpecificity; 968 return true; 969 } 970 } 971 break; 972 973 case CSSSelector::PseudoHorizontal: 974 case CSSSelector::PseudoVertical: 975 case CSSSelector::PseudoDecrement: 976 case CSSSelector::PseudoIncrement: 977 case CSSSelector::PseudoStart: 978 case CSSSelector::PseudoEnd: 979 case CSSSelector::PseudoDoubleButton: 980 case CSSSelector::PseudoSingleButton: 981 case CSSSelector::PseudoNoButton: 982 case CSSSelector::PseudoCornerPresent: 983 return false; 984 985 case CSSSelector::PseudoUnknown: 986 case CSSSelector::PseudoNotParsed: 987 default: 988 ASSERT_NOT_REACHED(); 989 break; 990 } 991 return false; 992 } else if (selector.match() == CSSSelector::PseudoElement && selector.pseudoType() == CSSSelector::PseudoCue) { 993 SelectorCheckingContext subContext(context); 994 subContext.isSubSelector = true; 995 subContext.behaviorAtBoundary = StaysWithinTreeScope; 996 997 const CSSSelector* contextSelector = context.selector; 998 ASSERT(contextSelector); 999 for (subContext.selector = contextSelector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) { 1000 if (match(subContext, siblingTraversalStrategy) == SelectorMatches) 1001 return true; 1002 } 1003 return false; 1004 } 1005 // ### add the rest of the checks... 1006 return true; 1007 } 1008 1009 bool SelectorChecker::checkScrollbarPseudoClass(const SelectorCheckingContext& context, Document* document, const CSSSelector& selector) const 1010 { 1011 RenderScrollbar* scrollbar = context.scrollbar; 1012 ScrollbarPart part = context.scrollbarPart; 1013 1014 // FIXME: This is a temporary hack for resizers and scrollbar corners. Eventually :window-inactive should become a real 1015 // pseudo class and just apply to everything. 1016 if (selector.pseudoType() == CSSSelector::PseudoWindowInactive) 1017 return !document->page()->focusController().isActive(); 1018 1019 if (!scrollbar) 1020 return false; 1021 1022 ASSERT(selector.match() == CSSSelector::PseudoClass); 1023 switch (selector.pseudoType()) { 1024 case CSSSelector::PseudoEnabled: 1025 return scrollbar->enabled(); 1026 case CSSSelector::PseudoDisabled: 1027 return !scrollbar->enabled(); 1028 case CSSSelector::PseudoHover: 1029 { 1030 ScrollbarPart hoveredPart = scrollbar->hoveredPart(); 1031 if (part == ScrollbarBGPart) 1032 return hoveredPart != NoPart; 1033 if (part == TrackBGPart) 1034 return hoveredPart == BackTrackPart || hoveredPart == ForwardTrackPart || hoveredPart == ThumbPart; 1035 return part == hoveredPart; 1036 } 1037 case CSSSelector::PseudoActive: 1038 { 1039 ScrollbarPart pressedPart = scrollbar->pressedPart(); 1040 if (part == ScrollbarBGPart) 1041 return pressedPart != NoPart; 1042 if (part == TrackBGPart) 1043 return pressedPart == BackTrackPart || pressedPart == ForwardTrackPart || pressedPart == ThumbPart; 1044 return part == pressedPart; 1045 } 1046 case CSSSelector::PseudoHorizontal: 1047 return scrollbar->orientation() == HorizontalScrollbar; 1048 case CSSSelector::PseudoVertical: 1049 return scrollbar->orientation() == VerticalScrollbar; 1050 case CSSSelector::PseudoDecrement: 1051 return part == BackButtonStartPart || part == BackButtonEndPart || part == BackTrackPart; 1052 case CSSSelector::PseudoIncrement: 1053 return part == ForwardButtonStartPart || part == ForwardButtonEndPart || part == ForwardTrackPart; 1054 case CSSSelector::PseudoStart: 1055 return part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart; 1056 case CSSSelector::PseudoEnd: 1057 return part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart; 1058 case CSSSelector::PseudoDoubleButton: 1059 { 1060 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement(); 1061 if (part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart) 1062 return buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth; 1063 if (part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart) 1064 return buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth; 1065 return false; 1066 } 1067 case CSSSelector::PseudoSingleButton: 1068 { 1069 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement(); 1070 if (part == BackButtonStartPart || part == ForwardButtonEndPart || part == BackTrackPart || part == ForwardTrackPart) 1071 return buttonsPlacement == ScrollbarButtonsSingle; 1072 return false; 1073 } 1074 case CSSSelector::PseudoNoButton: 1075 { 1076 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement(); 1077 if (part == BackTrackPart) 1078 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleEnd; 1079 if (part == ForwardTrackPart) 1080 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleStart; 1081 return false; 1082 } 1083 case CSSSelector::PseudoCornerPresent: 1084 return scrollbar->scrollableArea()->isScrollCornerVisible(); 1085 default: 1086 return false; 1087 } 1088 } 1089 1090 unsigned SelectorChecker::determineLinkMatchType(const CSSSelector& selector) 1091 { 1092 unsigned linkMatchType = MatchAll; 1093 1094 // Statically determine if this selector will match a link in visited, unvisited or any state, or never. 1095 // :visited never matches other elements than the innermost link element. 1096 for (const CSSSelector* current = &selector; current; current = current->tagHistory()) { 1097 switch (current->pseudoType()) { 1098 case CSSSelector::PseudoNot: 1099 { 1100 // :not(:visited) is equivalent to :link. Parser enforces that :not can't nest. 1101 ASSERT(current->selectorList()); 1102 for (const CSSSelector* subSelector = current->selectorList()->first(); subSelector; subSelector = subSelector->tagHistory()) { 1103 CSSSelector::PseudoType subType = subSelector->pseudoType(); 1104 if (subType == CSSSelector::PseudoVisited) 1105 linkMatchType &= ~SelectorChecker::MatchVisited; 1106 else if (subType == CSSSelector::PseudoLink) 1107 linkMatchType &= ~SelectorChecker::MatchLink; 1108 } 1109 } 1110 break; 1111 case CSSSelector::PseudoLink: 1112 linkMatchType &= ~SelectorChecker::MatchVisited; 1113 break; 1114 case CSSSelector::PseudoVisited: 1115 linkMatchType &= ~SelectorChecker::MatchLink; 1116 break; 1117 default: 1118 // We don't support :link and :visited inside :-webkit-any. 1119 break; 1120 } 1121 CSSSelector::Relation relation = current->relation(); 1122 if (relation == CSSSelector::SubSelector) 1123 continue; 1124 if (relation != CSSSelector::Descendant && relation != CSSSelector::Child) 1125 return linkMatchType; 1126 if (linkMatchType != MatchAll) 1127 return linkMatchType; 1128 } 1129 return linkMatchType; 1130 } 1131 1132 bool SelectorChecker::isFrameFocused(const Element& element) 1133 { 1134 return element.document().frame() && element.document().frame()->selection().isFocusedAndActive(); 1135 } 1136 1137 bool SelectorChecker::matchesFocusPseudoClass(const Element& element) 1138 { 1139 if (InspectorInstrumentation::forcePseudoState(const_cast<Element*>(&element), CSSSelector::PseudoFocus)) 1140 return true; 1141 return element.focused() && isFrameFocused(element); 1142 } 1143 1144 template 1145 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const DOMSiblingTraversalStrategy&, MatchResult*) const; 1146 1147 template 1148 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const ShadowDOMSiblingTraversalStrategy&, MatchResult*) const; 1149 1150 } 1151