1/* 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 3 * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) 4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com) 5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Apple Inc. All rights reserved. 6 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> 7 * Copyright (C) 2007, 2008 Eric Seidel <eric@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 "SelectorChecker.h" 30 31#include "CSSSelector.h" 32#include "CSSSelectorList.h" 33#include "Document.h" 34#include "ElementTraversal.h" 35#include "FocusController.h" 36#include "Frame.h" 37#include "FrameSelection.h" 38#include "HTMLAnchorElement.h" 39#include "HTMLDocument.h" 40#include "HTMLFrameElementBase.h" 41#include "HTMLInputElement.h" 42#include "HTMLNames.h" 43#include "HTMLOptGroupElement.h" 44#include "HTMLOptionElement.h" 45#include "HTMLProgressElement.h" 46#include "HTMLStyleElement.h" 47#include "InsertionPoint.h" 48#include "InspectorInstrumentation.h" 49#include "NodeRenderStyle.h" 50#include "Page.h" 51#include "RenderElement.h" 52#include "RenderScrollbar.h" 53#include "RenderStyle.h" 54#include "ScrollableArea.h" 55#include "ScrollbarTheme.h" 56#include "SelectorCheckerTestFunctions.h" 57#include "ShadowRoot.h" 58#include "StyledElement.h" 59#include "Text.h" 60 61namespace WebCore { 62 63using namespace HTMLNames; 64 65static inline bool isFirstChildElement(const Element* element) 66{ 67 return !ElementTraversal::previousSibling(element); 68} 69 70static inline bool isLastChildElement(const Element* element) 71{ 72 return !ElementTraversal::nextSibling(element); 73} 74 75static inline bool isFirstOfType(const Element* element, const QualifiedName& type) 76{ 77 for (const Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(sibling)) { 78 if (sibling->hasTagName(type)) 79 return false; 80 } 81 return true; 82} 83 84static inline bool isLastOfType(const Element* element, const QualifiedName& type) 85{ 86 for (const Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(sibling)) { 87 if (sibling->hasTagName(type)) 88 return false; 89 } 90 return true; 91} 92 93static inline int countElementsBefore(const Element* element) 94{ 95 int count = 0; 96 for (const Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(sibling)) { 97 unsigned index = sibling->childIndex(); 98 if (index) { 99 count += index; 100 break; 101 } 102 count++; 103 } 104 return count; 105} 106 107static inline int countElementsOfTypeBefore(const Element* element, const QualifiedName& type) 108{ 109 int count = 0; 110 for (const Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(sibling)) { 111 if (sibling->hasTagName(type)) 112 ++count; 113 } 114 return count; 115} 116 117static inline int countElementsAfter(const Element* element) 118{ 119 int count = 0; 120 for (const Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(sibling)) 121 ++count; 122 return count; 123} 124 125static inline int countElementsOfTypeAfter(const Element* element, const QualifiedName& type) 126{ 127 int count = 0; 128 for (const Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(sibling)) { 129 if (sibling->hasTagName(type)) 130 ++count; 131 } 132 return count; 133} 134 135SelectorChecker::SelectorChecker(Document& document, Mode mode) 136 : m_strictParsing(!document.inQuirksMode()) 137 , m_documentIsHTML(document.isHTMLDocument()) 138 , m_mode(mode) 139{ 140} 141 142bool SelectorChecker::match(const SelectorCheckingContext& context) const 143{ 144 PseudoId pseudoId = NOPSEUDO; 145 if (matchRecursively(context, pseudoId) != SelectorMatches) 146 return false; 147 if (context.pseudoId != NOPSEUDO && context.pseudoId != pseudoId) 148 return false; 149 if (context.pseudoId == NOPSEUDO && pseudoId != NOPSEUDO) { 150 if (m_mode == Mode::ResolvingStyle && pseudoId < FIRST_INTERNAL_PSEUDOID) 151 context.elementStyle->setHasPseudoStyle(pseudoId); 152 153 // For SharingRules testing, any match is good enough, we don't care what is matched. 154 return m_mode == Mode::SharingRules || m_mode == Mode::StyleInvalidation; 155 } 156 return true; 157} 158 159// Recursive check of selectors and combinators 160// It can return 4 different values: 161// * SelectorMatches - the selector matches the element e 162// * SelectorFailsLocally - the selector fails for the element e 163// * SelectorFailsAllSiblings - the selector fails for e and any sibling of e 164// * SelectorFailsCompletely - the selector fails for e and any sibling or ancestor of e 165SelectorChecker::Match SelectorChecker::matchRecursively(const SelectorCheckingContext& context, PseudoId& dynamicPseudo) const 166{ 167 // The first selector has to match. 168 if (!checkOne(context)) 169 return SelectorFailsLocally; 170 171 if (context.selector->m_match == CSSSelector::PseudoElement) { 172 if (context.selector->isCustomPseudoElement()) { 173 if (ShadowRoot* root = context.element->containingShadowRoot()) { 174 if (context.element->shadowPseudoId() != context.selector->value()) 175 return SelectorFailsLocally; 176 177 if (context.selector->pseudoElementType() == CSSSelector::PseudoElementWebKitCustom && root->type() != ShadowRoot::UserAgentShadowRoot) 178 return SelectorFailsLocally; 179 } else if (m_mode != Mode::StyleInvalidation) 180 return SelectorFailsLocally; 181 } else { 182 if ((!context.elementStyle && m_mode == Mode::ResolvingStyle) || m_mode == Mode::QueryingRules) 183 return SelectorFailsLocally; 184 185 // When invalidating style all pseudo elements need to match. 186 PseudoId pseudoId = m_mode == Mode::StyleInvalidation ? NOPSEUDO : CSSSelector::pseudoId(context.selector->pseudoElementType()); 187 if (pseudoId != NOPSEUDO) 188 dynamicPseudo = pseudoId; 189 } 190 } 191 192 // The rest of the selectors has to match 193 CSSSelector::Relation relation = context.selector->relation(); 194 195 // Prepare next selector 196 const CSSSelector* historySelector = context.selector->tagHistory(); 197 if (!historySelector) 198 return SelectorMatches; 199 200 SelectorCheckingContext nextContext(context); 201 nextContext.selector = historySelector; 202 203 PseudoId ignoreDynamicPseudo = NOPSEUDO; 204 if (relation != CSSSelector::SubSelector) { 205 // Bail-out if this selector is irrelevant for the pseudoId 206 if (context.pseudoId != NOPSEUDO && context.pseudoId != dynamicPseudo) 207 return SelectorFailsCompletely; 208 209 // Disable :visited matching when we see the first link or try to match anything else than an ancestors. 210 if (context.element->isLink() || (relation != CSSSelector::Descendant && relation != CSSSelector::Child)) 211 nextContext.visitedMatchType = VisitedMatchDisabled; 212 213 nextContext.pseudoId = NOPSEUDO; 214 } 215 216 switch (relation) { 217 case CSSSelector::Descendant: 218 nextContext.element = context.element->parentElement(); 219 nextContext.firstSelectorOfTheFragment = nextContext.selector; 220 nextContext.elementStyle = 0; 221 for (; nextContext.element; nextContext.element = nextContext.element->parentElement()) { 222 Match match = this->matchRecursively(nextContext, ignoreDynamicPseudo); 223 if (match == SelectorMatches || match == SelectorFailsCompletely) 224 return match; 225 } 226 return SelectorFailsCompletely; 227 228 case CSSSelector::Child: 229 { 230 nextContext.element = context.element->parentElement(); 231 if (!nextContext.element) 232 return SelectorFailsCompletely; 233 nextContext.firstSelectorOfTheFragment = nextContext.selector; 234 nextContext.elementStyle = nullptr; 235 Match match = matchRecursively(nextContext, ignoreDynamicPseudo); 236 if (match == SelectorMatches || match == SelectorFailsCompletely) 237 return match; 238 return SelectorFailsAllSiblings; 239 } 240 241 case CSSSelector::DirectAdjacent: 242 if (m_mode == Mode::ResolvingStyle) { 243 if (Element* parentElement = context.element->parentElement()) 244 parentElement->setChildrenAffectedByDirectAdjacentRules(); 245 } 246 nextContext.element = context.element->previousElementSibling(); 247 if (!nextContext.element) 248 return SelectorFailsAllSiblings; 249 nextContext.firstSelectorOfTheFragment = nextContext.selector; 250 nextContext.elementStyle = 0; 251 return matchRecursively(nextContext, ignoreDynamicPseudo); 252 253 case CSSSelector::IndirectAdjacent: 254 if (m_mode == Mode::ResolvingStyle) { 255 if (Element* parentElement = context.element->parentElement()) 256 parentElement->setChildrenAffectedByForwardPositionalRules(); 257 } 258 nextContext.element = context.element->previousElementSibling(); 259 nextContext.firstSelectorOfTheFragment = nextContext.selector; 260 nextContext.elementStyle = 0; 261 for (; nextContext.element; nextContext.element = nextContext.element->previousElementSibling()) { 262 Match match = this->matchRecursively(nextContext, ignoreDynamicPseudo); 263 if (match == SelectorMatches || match == SelectorFailsAllSiblings || match == SelectorFailsCompletely) 264 return match; 265 }; 266 return SelectorFailsAllSiblings; 267 268 case CSSSelector::SubSelector: 269 // a selector is invalid if something follows a pseudo-element 270 // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else) 271 // to follow the pseudo elements. 272 nextContext.hasScrollbarPseudo = dynamicPseudo != NOPSEUDO && (context.scrollbar || dynamicPseudo == SCROLLBAR_CORNER || dynamicPseudo == RESIZER); 273 nextContext.hasSelectionPseudo = dynamicPseudo == SELECTION; 274 if ((context.elementStyle || m_mode == Mode::CollectingRules) && dynamicPseudo != NOPSEUDO 275 && !nextContext.hasSelectionPseudo 276 && !(nextContext.hasScrollbarPseudo && nextContext.selector->m_match == CSSSelector::PseudoClass)) 277 return SelectorFailsCompletely; 278 return matchRecursively(nextContext, dynamicPseudo); 279 280 case CSSSelector::ShadowDescendant: 281 { 282 Element* shadowHostNode = context.element->shadowHost(); 283 if (!shadowHostNode) 284 return SelectorFailsCompletely; 285 nextContext.element = shadowHostNode; 286 nextContext.firstSelectorOfTheFragment = nextContext.selector; 287 nextContext.elementStyle = 0; 288 return matchRecursively(nextContext, ignoreDynamicPseudo); 289 } 290 } 291 292 ASSERT_NOT_REACHED(); 293 return SelectorFailsCompletely; 294} 295 296static bool attributeValueMatches(const Attribute& attribute, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive) 297{ 298 const AtomicString& value = attribute.value(); 299 ASSERT(!value.isNull()); 300 301 switch (match) { 302 case CSSSelector::Set: 303 break; 304 case CSSSelector::Exact: 305 if (caseSensitive ? selectorValue != value : !equalIgnoringCase(selectorValue, value)) 306 return false; 307 break; 308 case CSSSelector::List: 309 { 310 // Ignore empty selectors or selectors containing spaces 311 if (selectorValue.contains(' ') || selectorValue.isEmpty()) 312 return false; 313 314 unsigned startSearchAt = 0; 315 while (true) { 316 size_t foundPos = value.find(selectorValue, startSearchAt, caseSensitive); 317 if (foundPos == notFound) 318 return false; 319 if (!foundPos || value[foundPos - 1] == ' ') { 320 unsigned endStr = foundPos + selectorValue.length(); 321 if (endStr == value.length() || value[endStr] == ' ') 322 break; // We found a match. 323 } 324 325 // No match. Keep looking. 326 startSearchAt = foundPos + 1; 327 } 328 break; 329 } 330 case CSSSelector::Contain: 331 if (!value.contains(selectorValue, caseSensitive) || selectorValue.isEmpty()) 332 return false; 333 break; 334 case CSSSelector::Begin: 335 if (!value.startsWith(selectorValue, caseSensitive) || selectorValue.isEmpty()) 336 return false; 337 break; 338 case CSSSelector::End: 339 if (!value.endsWith(selectorValue, caseSensitive) || selectorValue.isEmpty()) 340 return false; 341 break; 342 case CSSSelector::Hyphen: 343 if (value.length() < selectorValue.length()) 344 return false; 345 if (!value.startsWith(selectorValue, caseSensitive)) 346 return false; 347 // It they start the same, check for exact match or following '-': 348 if (value.length() != selectorValue.length() && value[selectorValue.length()] != '-') 349 return false; 350 break; 351 default: 352 ASSERT_NOT_REACHED(); 353 return false; 354 } 355 356 return true; 357} 358 359static bool anyAttributeMatches(Element* element, const CSSSelector* selector, const QualifiedName& selectorAttr, bool caseSensitive) 360{ 361 ASSERT(element->hasAttributesWithoutUpdate()); 362 for (const Attribute& attribute : element->attributesIterator()) { 363 if (!attribute.matches(selectorAttr.prefix(), element->isHTMLElement() ? selector->attributeCanonicalLocalName() : selectorAttr.localName(), selectorAttr.namespaceURI())) 364 continue; 365 366 if (attributeValueMatches(attribute, static_cast<CSSSelector::Match>(selector->m_match), selector->value(), caseSensitive)) 367 return true; 368 } 369 370 return false; 371} 372 373static bool canMatchHoverOrActiveInQuirksMode(const SelectorChecker::SelectorCheckingContext& context) 374{ 375 // For quirks mode, follow this: http://quirks.spec.whatwg.org/#the-:active-and-:hover-quirk 376 // In quirks mode, a compound selector 'selector' that matches the following conditions must not match elements that would not also match the ':any-link' selector. 377 // 378 // selector uses the ':active' or ':hover' pseudo-classes. 379 // selector does not use a type selector. 380 // selector does not use an attribute selector. 381 // selector does not use an ID selector. 382 // selector does not use a class selector. 383 // selector does not use a pseudo-class selector other than ':active' and ':hover'. 384 // selector does not use a pseudo-element selector. 385 // selector is not part of an argument to a functional pseudo-class or pseudo-element. 386 if (context.inFunctionalPseudoClass) 387 return true; 388 389 for (const CSSSelector* selector = context.firstSelectorOfTheFragment; selector; selector = selector->tagHistory()) { 390 switch (selector->m_match) { 391 case CSSSelector::Tag: 392 if (selector->tagQName() != anyQName()) 393 return true; 394 break; 395 case CSSSelector::PseudoClass: { 396 CSSSelector::PseudoClassType pseudoClassType = selector->pseudoClassType(); 397 if (pseudoClassType != CSSSelector::PseudoClassHover && pseudoClassType != CSSSelector::PseudoClassActive) 398 return true; 399 break; 400 } 401 case CSSSelector::Id: 402 case CSSSelector::Class: 403 case CSSSelector::Exact: 404 case CSSSelector::Set: 405 case CSSSelector::List: 406 case CSSSelector::Hyphen: 407 case CSSSelector::Contain: 408 case CSSSelector::Begin: 409 case CSSSelector::End: 410 case CSSSelector::PagePseudoClass: 411 case CSSSelector::PseudoElement: 412 return true; 413 case CSSSelector::Unknown: 414 ASSERT_NOT_REACHED(); 415 break; 416 } 417 418 CSSSelector::Relation relation = selector->relation(); 419 if (relation == CSSSelector::ShadowDescendant) 420 return true; 421 422 if (relation != CSSSelector::SubSelector) 423 return false; 424 } 425 return false; 426} 427 428bool SelectorChecker::checkOne(const SelectorCheckingContext& context) const 429{ 430 Element* const & element = context.element; 431 const CSSSelector* const & selector = context.selector; 432 ASSERT(element); 433 ASSERT(selector); 434 435 if (selector->m_match == CSSSelector::Tag) 436 return SelectorChecker::tagMatches(element, selector->tagQName()); 437 438 if (selector->m_match == CSSSelector::Class) 439 return element->hasClass() && element->classNames().contains(selector->value()); 440 441 if (selector->m_match == CSSSelector::Id) 442 return element->hasID() && element->idForStyleResolution() == selector->value(); 443 444 if (selector->isAttributeSelector()) { 445 if (!element->hasAttributes()) 446 return false; 447 448 const QualifiedName& attr = selector->attribute(); 449 bool caseSensitive = !(m_documentIsHTML && element->isHTMLElement()) || HTMLDocument::isCaseSensitiveAttribute(attr); 450 451 if (!anyAttributeMatches(element, selector, attr, caseSensitive)) 452 return false; 453 } 454 455 if (selector->m_match == CSSSelector::PseudoClass) { 456 // Handle :not up front. 457 if (selector->pseudoClassType() == CSSSelector::PseudoClassNot) { 458 const CSSSelectorList* selectorList = selector->selectorList(); 459 460 // FIXME: We probably should fix the parser and make it never produce :not rules with missing selector list. 461 if (!selectorList) 462 return false; 463 464 SelectorCheckingContext subContext(context); 465 subContext.inFunctionalPseudoClass = true; 466 subContext.firstSelectorOfTheFragment = selectorList->first(); 467 for (subContext.selector = selectorList->first(); subContext.selector; subContext.selector = subContext.selector->tagHistory()) { 468 if (subContext.selector->m_match == CSSSelector::PseudoClass) { 469 // :not cannot nest. I don't really know why this is a 470 // restriction in CSS3, but it is, so let's honor it. 471 // the parser enforces that this never occurs 472 ASSERT(subContext.selector->pseudoClassType() != CSSSelector::PseudoClassNot); 473 // We select between :visited and :link when applying. We don't know which one applied (or not) yet. 474 if (subContext.selector->pseudoClassType() == CSSSelector::PseudoClassVisited || (subContext.selector->pseudoClassType() == CSSSelector::PseudoClassLink && subContext.visitedMatchType == VisitedMatchEnabled)) 475 return true; 476 } 477 if (!checkOne(subContext)) 478 return true; 479 } 480 } else if (context.hasScrollbarPseudo) { 481 // CSS scrollbars match a specific subset of pseudo classes, and they have specialized rules for each 482 // (since there are no elements involved). 483 return checkScrollbarPseudoClass(context, &element->document(), selector); 484 } else if (context.hasSelectionPseudo) { 485 if (selector->pseudoClassType() == CSSSelector::PseudoClassWindowInactive) 486 return !element->document().page()->focusController().isActive(); 487 } 488 489 // Normal element pseudo class checking. 490 switch (selector->pseudoClassType()) { 491 // Pseudo classes: 492 case CSSSelector::PseudoClassNot: 493 break; // Already handled up above. 494 case CSSSelector::PseudoClassEmpty: 495 { 496 bool result = true; 497 for (Node* n = element->firstChild(); n; n = n->nextSibling()) { 498 if (n->isElementNode()) { 499 result = false; 500 break; 501 } 502 if (n->isTextNode()) { 503 Text* textNode = toText(n); 504 if (!textNode->data().isEmpty()) { 505 result = false; 506 break; 507 } 508 } 509 } 510 if (m_mode == Mode::ResolvingStyle) { 511 element->setStyleAffectedByEmpty(); 512 if (context.elementStyle) 513 context.elementStyle->setEmptyState(result); 514 else if (element->renderStyle() && (element->document().styleSheetCollection().usesSiblingRules() || element->renderStyle()->unique())) 515 element->renderStyle()->setEmptyState(result); 516 } 517 return result; 518 } 519 case CSSSelector::PseudoClassFirstChild: 520 // first-child matches the first child that is an element 521 if (Element* parentElement = element->parentElement()) { 522 bool result = isFirstChildElement(element); 523 if (m_mode == Mode::ResolvingStyle) { 524 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle(); 525 parentElement->setChildrenAffectedByFirstChildRules(); 526 if (result && childStyle) 527 childStyle->setFirstChildState(); 528 } 529 return result; 530 } 531 break; 532 case CSSSelector::PseudoClassFirstOfType: 533 // first-of-type matches the first element of its type 534 if (Element* parentElement = element->parentElement()) { 535 bool result = isFirstOfType(element, element->tagQName()); 536 if (m_mode == Mode::ResolvingStyle) 537 parentElement->setChildrenAffectedByForwardPositionalRules(); 538 return result; 539 } 540 break; 541 case CSSSelector::PseudoClassLastChild: 542 // last-child matches the last child that is an element 543 if (Element* parentElement = element->parentElement()) { 544 bool result = parentElement->isFinishedParsingChildren() && isLastChildElement(element); 545 if (m_mode == Mode::ResolvingStyle) { 546 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle(); 547 parentElement->setChildrenAffectedByLastChildRules(); 548 if (result && childStyle) 549 childStyle->setLastChildState(); 550 } 551 return result; 552 } 553 break; 554 case CSSSelector::PseudoClassLastOfType: 555 // last-of-type matches the last element of its type 556 if (Element* parentElement = element->parentElement()) { 557 if (m_mode == Mode::ResolvingStyle) 558 parentElement->setChildrenAffectedByBackwardPositionalRules(); 559 if (!parentElement->isFinishedParsingChildren()) 560 return false; 561 return isLastOfType(element, element->tagQName()); 562 } 563 break; 564 case CSSSelector::PseudoClassOnlyChild: 565 if (Element* parentElement = element->parentElement()) { 566 bool firstChild = isFirstChildElement(element); 567 bool onlyChild = firstChild && parentElement->isFinishedParsingChildren() && isLastChildElement(element); 568 if (m_mode == Mode::ResolvingStyle) { 569 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle(); 570 parentElement->setChildrenAffectedByFirstChildRules(); 571 parentElement->setChildrenAffectedByLastChildRules(); 572 if (firstChild && childStyle) 573 childStyle->setFirstChildState(); 574 if (onlyChild && childStyle) 575 childStyle->setLastChildState(); 576 } 577 return onlyChild; 578 } 579 break; 580 case CSSSelector::PseudoClassOnlyOfType: 581 // FIXME: This selector is very slow. 582 if (Element* parentElement = element->parentElement()) { 583 if (m_mode == Mode::ResolvingStyle) { 584 parentElement->setChildrenAffectedByForwardPositionalRules(); 585 parentElement->setChildrenAffectedByBackwardPositionalRules(); 586 } 587 if (!parentElement->isFinishedParsingChildren()) 588 return false; 589 return isFirstOfType(element, element->tagQName()) && isLastOfType(element, element->tagQName()); 590 } 591 break; 592 case CSSSelector::PseudoClassNthChild: 593 if (!selector->parseNth()) 594 break; 595 if (Element* parentElement = element->parentElement()) { 596 int count = 1 + countElementsBefore(element); 597 if (m_mode == Mode::ResolvingStyle) { 598 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle(); 599 element->setChildIndex(count); 600 if (childStyle) 601 childStyle->setUnique(); 602 parentElement->setChildrenAffectedByForwardPositionalRules(); 603 } 604 605 if (selector->matchNth(count)) 606 return true; 607 } 608 break; 609 case CSSSelector::PseudoClassNthOfType: 610 if (!selector->parseNth()) 611 break; 612 if (Element* parentElement = element->parentElement()) { 613 int count = 1 + countElementsOfTypeBefore(element, element->tagQName()); 614 if (m_mode == Mode::ResolvingStyle) 615 parentElement->setChildrenAffectedByForwardPositionalRules(); 616 617 if (selector->matchNth(count)) 618 return true; 619 } 620 break; 621 case CSSSelector::PseudoClassNthLastChild: 622 if (!selector->parseNth()) 623 break; 624 if (Element* parentElement = element->parentElement()) { 625 if (m_mode == Mode::ResolvingStyle) 626 parentElement->setChildrenAffectedByBackwardPositionalRules(); 627 if (!parentElement->isFinishedParsingChildren()) 628 return false; 629 int count = 1 + countElementsAfter(element); 630 if (selector->matchNth(count)) 631 return true; 632 } 633 break; 634 case CSSSelector::PseudoClassNthLastOfType: 635 if (!selector->parseNth()) 636 break; 637 if (Element* parentElement = element->parentElement()) { 638 if (m_mode == Mode::ResolvingStyle) 639 parentElement->setChildrenAffectedByBackwardPositionalRules(); 640 if (!parentElement->isFinishedParsingChildren()) 641 return false; 642 643 int count = 1 + countElementsOfTypeAfter(element, element->tagQName()); 644 if (selector->matchNth(count)) 645 return true; 646 } 647 break; 648 case CSSSelector::PseudoClassTarget: 649 if (element == element->document().cssTarget()) 650 return true; 651 break; 652 case CSSSelector::PseudoClassAny: 653 { 654 SelectorCheckingContext subContext(context); 655 subContext.inFunctionalPseudoClass = true; 656 PseudoId ignoreDynamicPseudo = NOPSEUDO; 657 for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(subContext.selector)) { 658 subContext.firstSelectorOfTheFragment = subContext.selector; 659 if (matchRecursively(subContext, ignoreDynamicPseudo) == SelectorMatches) 660 return true; 661 } 662 } 663 break; 664 case CSSSelector::PseudoClassAutofill: 665 return isAutofilled(element); 666 case CSSSelector::PseudoClassAnyLink: 667 case CSSSelector::PseudoClassLink: 668 // :visited and :link matches are separated later when applying the style. Here both classes match all links... 669 return element->isLink(); 670 case CSSSelector::PseudoClassVisited: 671 // ...except if :visited matching is disabled for ancestor/sibling matching. 672 return element->isLink() && context.visitedMatchType == VisitedMatchEnabled; 673 case CSSSelector::PseudoClassDrag: 674 if (m_mode == Mode::ResolvingStyle) { 675 if (context.elementStyle) 676 context.elementStyle->setAffectedByDrag(); 677 else 678 element->setChildrenAffectedByDrag(); 679 } 680 if (element->renderer() && element->renderer()->isDragging()) 681 return true; 682 break; 683 case CSSSelector::PseudoClassFocus: 684 return matchesFocusPseudoClass(element); 685 case CSSSelector::PseudoClassHover: 686 if (m_strictParsing || element->isLink() || canMatchHoverOrActiveInQuirksMode(context)) { 687 if (m_mode == Mode::ResolvingStyle) { 688 if (context.elementStyle) 689 context.elementStyle->setAffectedByHover(); 690 else 691 element->setChildrenAffectedByHover(); 692 } 693 if (element->hovered() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassHover)) 694 return true; 695 } 696 break; 697 case CSSSelector::PseudoClassActive: 698 if (m_strictParsing || element->isLink() || canMatchHoverOrActiveInQuirksMode(context)) { 699 if (m_mode == Mode::ResolvingStyle) { 700 if (context.elementStyle) 701 context.elementStyle->setAffectedByActive(); 702 else 703 element->setChildrenAffectedByActive(); 704 } 705 if (element->active() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassActive)) 706 return true; 707 } 708 break; 709 case CSSSelector::PseudoClassEnabled: 710 return isEnabled(element); 711 case CSSSelector::PseudoClassFullPageMedia: 712 return element->document().isMediaDocument(); 713 case CSSSelector::PseudoClassDefault: 714 return isDefaultButtonForForm(element); 715 case CSSSelector::PseudoClassDisabled: 716 return isDisabled(element); 717 case CSSSelector::PseudoClassReadOnly: 718 return matchesReadOnlyPseudoClass(element); 719 case CSSSelector::PseudoClassReadWrite: 720 return matchesReadWritePseudoClass(element); 721 case CSSSelector::PseudoClassOptional: 722 return isOptionalFormControl(element); 723 case CSSSelector::PseudoClassRequired: 724 return isRequiredFormControl(element); 725 case CSSSelector::PseudoClassValid: 726 return isValid(element); 727 case CSSSelector::PseudoClassInvalid: 728 return isInvalid(element); 729 case CSSSelector::PseudoClassChecked: 730 return isChecked(element); 731 case CSSSelector::PseudoClassIndeterminate: 732 return shouldAppearIndeterminate(element); 733 case CSSSelector::PseudoClassRoot: 734 if (element == element->document().documentElement()) 735 return true; 736 break; 737 case CSSSelector::PseudoClassLang: 738 { 739 const AtomicString& argument = selector->argument(); 740 if (argument.isNull()) 741 return false; 742 return matchesLangPseudoClass(element, argument.impl()); 743 } 744#if ENABLE(FULLSCREEN_API) 745 case CSSSelector::PseudoClassFullScreen: 746 return matchesFullScreenPseudoClass(element); 747 case CSSSelector::PseudoClassAnimatingFullScreenTransition: 748 if (element != element->document().webkitCurrentFullScreenElement()) 749 return false; 750 return element->document().isAnimatingFullScreen(); 751 case CSSSelector::PseudoClassFullScreenAncestor: 752 return element->containsFullScreenElement(); 753 case CSSSelector::PseudoClassFullScreenDocument: 754 // While a Document is in the fullscreen state, the 'full-screen-document' pseudoclass applies 755 // to all elements of that Document. 756 if (!element->document().webkitIsFullScreen()) 757 return false; 758 return true; 759#endif 760 case CSSSelector::PseudoClassInRange: 761 return isInRange(element); 762 case CSSSelector::PseudoClassOutOfRange: 763 return isOutOfRange(element); 764#if ENABLE(VIDEO_TRACK) 765 case CSSSelector::PseudoClassFuture: 766 return matchesFutureCuePseudoClass(element); 767 case CSSSelector::PseudoClassPast: 768 return matchesPastCuePseudoClass(element); 769#endif 770 771 case CSSSelector::PseudoClassScope: 772 { 773 const Node* contextualReferenceNode = !context.scope ? element->document().documentElement() : context.scope; 774 if (element == contextualReferenceNode) 775 return true; 776 break; 777 } 778 779 case CSSSelector::PseudoClassHorizontal: 780 case CSSSelector::PseudoClassVertical: 781 case CSSSelector::PseudoClassDecrement: 782 case CSSSelector::PseudoClassIncrement: 783 case CSSSelector::PseudoClassStart: 784 case CSSSelector::PseudoClassEnd: 785 case CSSSelector::PseudoClassDoubleButton: 786 case CSSSelector::PseudoClassSingleButton: 787 case CSSSelector::PseudoClassNoButton: 788 case CSSSelector::PseudoClassCornerPresent: 789 return false; 790 791 case CSSSelector::PseudoClassUnknown: 792 default: 793 ASSERT_NOT_REACHED(); 794 break; 795 } 796 return false; 797 } 798#if ENABLE(VIDEO_TRACK) 799 else if (selector->m_match == CSSSelector::PseudoElement && selector->pseudoElementType() == CSSSelector::PseudoElementCue) { 800 SelectorCheckingContext subContext(context); 801 802 PseudoId ignoreDynamicPseudo = NOPSEUDO; 803 const CSSSelector* const & selector = context.selector; 804 for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(subContext.selector)) { 805 subContext.firstSelectorOfTheFragment = subContext.selector; 806 subContext.inFunctionalPseudoClass = true; 807 if (matchRecursively(subContext, ignoreDynamicPseudo) == SelectorMatches) 808 return true; 809 } 810 return false; 811 } 812#endif 813 // ### add the rest of the checks... 814 return true; 815} 816 817bool SelectorChecker::checkScrollbarPseudoClass(const SelectorCheckingContext& context, Document* document, const CSSSelector* selector) const 818{ 819 ASSERT(selector->m_match == CSSSelector::PseudoClass); 820 821 RenderScrollbar* scrollbar = context.scrollbar; 822 ScrollbarPart part = context.scrollbarPart; 823 824 // FIXME: This is a temporary hack for resizers and scrollbar corners. Eventually :window-inactive should become a real 825 // pseudo class and just apply to everything. 826 if (selector->pseudoClassType() == CSSSelector::PseudoClassWindowInactive) 827 return !document->page()->focusController().isActive(); 828 829 if (!scrollbar) 830 return false; 831 832 switch (selector->pseudoClassType()) { 833 case CSSSelector::PseudoClassEnabled: 834 return scrollbar->enabled(); 835 case CSSSelector::PseudoClassDisabled: 836 return !scrollbar->enabled(); 837 case CSSSelector::PseudoClassHover: 838 { 839 ScrollbarPart hoveredPart = scrollbar->hoveredPart(); 840 if (part == ScrollbarBGPart) 841 return hoveredPart != NoPart; 842 if (part == TrackBGPart) 843 return hoveredPart == BackTrackPart || hoveredPart == ForwardTrackPart || hoveredPart == ThumbPart; 844 return part == hoveredPart; 845 } 846 case CSSSelector::PseudoClassActive: 847 { 848 ScrollbarPart pressedPart = scrollbar->pressedPart(); 849 if (part == ScrollbarBGPart) 850 return pressedPart != NoPart; 851 if (part == TrackBGPart) 852 return pressedPart == BackTrackPart || pressedPart == ForwardTrackPart || pressedPart == ThumbPart; 853 return part == pressedPart; 854 } 855 case CSSSelector::PseudoClassHorizontal: 856 return scrollbar->orientation() == HorizontalScrollbar; 857 case CSSSelector::PseudoClassVertical: 858 return scrollbar->orientation() == VerticalScrollbar; 859 case CSSSelector::PseudoClassDecrement: 860 return part == BackButtonStartPart || part == BackButtonEndPart || part == BackTrackPart; 861 case CSSSelector::PseudoClassIncrement: 862 return part == ForwardButtonStartPart || part == ForwardButtonEndPart || part == ForwardTrackPart; 863 case CSSSelector::PseudoClassStart: 864 return part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart; 865 case CSSSelector::PseudoClassEnd: 866 return part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart; 867 case CSSSelector::PseudoClassDoubleButton: 868 { 869 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement(); 870 if (part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart) 871 return buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth; 872 if (part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart) 873 return buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth; 874 return false; 875 } 876 case CSSSelector::PseudoClassSingleButton: 877 { 878 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement(); 879 if (part == BackButtonStartPart || part == ForwardButtonEndPart || part == BackTrackPart || part == ForwardTrackPart) 880 return buttonsPlacement == ScrollbarButtonsSingle; 881 return false; 882 } 883 case CSSSelector::PseudoClassNoButton: 884 { 885 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement(); 886 if (part == BackTrackPart) 887 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleEnd; 888 if (part == ForwardTrackPart) 889 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleStart; 890 return false; 891 } 892 case CSSSelector::PseudoClassCornerPresent: 893 return scrollbar->scrollableArea()->isScrollCornerVisible(); 894 default: 895 return false; 896 } 897} 898 899unsigned SelectorChecker::determineLinkMatchType(const CSSSelector* selector) 900{ 901 unsigned linkMatchType = MatchAll; 902 903 // Statically determine if this selector will match a link in visited, unvisited or any state, or never. 904 // :visited never matches other elements than the innermost link element. 905 for (; selector; selector = selector->tagHistory()) { 906 if (selector->m_match == CSSSelector::PseudoClass) { 907 switch (selector->pseudoClassType()) { 908 case CSSSelector::PseudoClassNot: 909 { 910 // :not(:visited) is equivalent to :link. Parser enforces that :not can't nest. 911 const CSSSelectorList* selectorList = selector->selectorList(); 912 if (!selectorList) 913 break; 914 915 for (const CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = subSelector->tagHistory()) { 916 if (subSelector->m_match == CSSSelector::PseudoClass) { 917 CSSSelector::PseudoClassType subType = subSelector->pseudoClassType(); 918 if (subType == CSSSelector::PseudoClassVisited) 919 linkMatchType &= ~SelectorChecker::MatchVisited; 920 else if (subType == CSSSelector::PseudoClassLink) 921 linkMatchType &= ~SelectorChecker::MatchLink; 922 } 923 } 924 } 925 break; 926 case CSSSelector::PseudoClassLink: 927 linkMatchType &= ~SelectorChecker::MatchVisited; 928 break; 929 case CSSSelector::PseudoClassVisited: 930 linkMatchType &= ~SelectorChecker::MatchLink; 931 break; 932 default: 933 // We don't support :link and :visited inside :-webkit-any. 934 break; 935 } 936 } 937 CSSSelector::Relation relation = selector->relation(); 938 if (relation == CSSSelector::SubSelector) 939 continue; 940 if (relation != CSSSelector::Descendant && relation != CSSSelector::Child) 941 return linkMatchType; 942 if (linkMatchType != MatchAll) 943 return linkMatchType; 944 } 945 return linkMatchType; 946} 947 948static bool isFrameFocused(const Element* element) 949{ 950 return element->document().frame() && element->document().frame()->selection().isFocusedAndActive(); 951} 952 953bool SelectorChecker::matchesFocusPseudoClass(const Element* element) 954{ 955 if (InspectorInstrumentation::forcePseudoState(const_cast<Element*>(element), CSSSelector::PseudoClassFocus)) 956 return true; 957 return element->focused() && isFrameFocused(element); 958} 959 960} 961