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