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