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 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 "FocusController.h" 35#include "Frame.h" 36#include "FrameSelection.h" 37#include "HTMLDocument.h" 38#include "HTMLFrameElementBase.h" 39#include "HTMLInputElement.h" 40#include "HTMLNames.h" 41#include "HTMLOptionElement.h" 42#include "HTMLProgressElement.h" 43#include "HTMLStyleElement.h" 44#include "InsertionPoint.h" 45#include "InspectorInstrumentation.h" 46#include "NodeRenderStyle.h" 47#include "Page.h" 48#include "RenderObject.h" 49#include "RenderScrollbar.h" 50#include "RenderStyle.h" 51#include "ScrollableArea.h" 52#include "ScrollbarTheme.h" 53#include "ShadowRoot.h" 54#include "StyledElement.h" 55#include "Text.h" 56 57#if ENABLE(VIDEO_TRACK) 58#include "WebVTTElement.h" 59#endif 60 61namespace WebCore { 62 63using namespace HTMLNames; 64 65static inline bool isFirstChildElement(const Element* element) 66{ 67 return !element->previousElementSibling(); 68} 69 70static inline bool isLastChildElement(const Element* element) 71{ 72 return !element->nextElementSibling(); 73} 74 75static inline bool isFirstOfType(const Element* element, const QualifiedName& type) 76{ 77 for (const Element* sibling = element->previousElementSibling(); sibling; sibling = sibling->previousElementSibling()) { 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 = element->nextElementSibling(); sibling; sibling = sibling->nextElementSibling()) { 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 = element->previousElementSibling(); sibling; sibling = sibling->previousElementSibling()) { 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 = element->previousElementSibling(); sibling; sibling = sibling->previousElementSibling()) { 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 = element->nextElementSibling(); sibling; sibling = sibling->nextElementSibling()) 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 = element->nextElementSibling(); sibling; sibling = sibling->nextElementSibling()) { 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 142// Recursive check of selectors and combinators 143// It can return 4 different values: 144// * SelectorMatches - the selector matches the element e 145// * SelectorFailsLocally - the selector fails for the element e 146// * SelectorFailsAllSiblings - the selector fails for e and any sibling of e 147// * SelectorFailsCompletely - the selector fails for e and any sibling or ancestor of e 148SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext& context, PseudoId& dynamicPseudo) const 149{ 150 // first selector has to match 151 if (!checkOne(context)) 152 return SelectorFailsLocally; 153 154 if (context.selector->m_match == CSSSelector::PseudoElement) { 155 if (context.selector->isCustomPseudoElement()) { 156 if (ShadowRoot* root = context.element->containingShadowRoot()) { 157 if (context.element->shadowPseudoId() != context.selector->value()) 158 return SelectorFailsLocally; 159 160 if (context.selector->pseudoType() == CSSSelector::PseudoWebKitCustomElement && root->type() != ShadowRoot::UserAgentShadowRoot) 161 return SelectorFailsLocally; 162 } else 163 return SelectorFailsLocally; 164 } else { 165 if ((!context.elementStyle && m_mode == ResolvingStyle) || m_mode == QueryingRules) 166 return SelectorFailsLocally; 167 168 PseudoId pseudoId = CSSSelector::pseudoId(context.selector->pseudoType()); 169 if (pseudoId == FIRST_LETTER) { 170 if (Document* document = context.element->document()) 171 document->styleSheetCollection()->setUsesFirstLetterRules(true); 172 } 173 if (pseudoId != NOPSEUDO && m_mode != SharingRules) 174 dynamicPseudo = pseudoId; 175 } 176 } 177 178 // The rest of the selectors has to match 179 CSSSelector::Relation relation = context.selector->relation(); 180 181 // Prepare next selector 182 const CSSSelector* historySelector = context.selector->tagHistory(); 183 if (!historySelector) { 184 if (context.behaviorAtBoundary == CrossesBoundary) { 185 ASSERT(context.scope); 186 return context.scope->contains(context.element) ? SelectorMatches : SelectorFailsLocally; 187 } 188 return SelectorMatches; 189 } 190 191 SelectorCheckingContext nextContext(context); 192 nextContext.selector = historySelector; 193 194 PseudoId ignoreDynamicPseudo = NOPSEUDO; 195 if (relation != CSSSelector::SubSelector) { 196 // Abort if the next selector would exceed the scope. 197 if (context.element == context.scope && context.behaviorAtBoundary != StaysWithinTreeScope) 198 return SelectorFailsCompletely; 199 200 // Bail-out if this selector is irrelevant for the pseudoId 201 if (context.pseudoId != NOPSEUDO && context.pseudoId != dynamicPseudo) 202 return SelectorFailsCompletely; 203 204 // Disable :visited matching when we see the first link or try to match anything else than an ancestors. 205 if (!context.isSubSelector && (context.element->isLink() || (relation != CSSSelector::Descendant && relation != CSSSelector::Child))) 206 nextContext.visitedMatchType = VisitedMatchDisabled; 207 208 nextContext.pseudoId = NOPSEUDO; 209 } 210 211 switch (relation) { 212 case CSSSelector::Descendant: 213 nextContext.element = context.element->parentElement(); 214 nextContext.isSubSelector = false; 215 nextContext.elementStyle = 0; 216 for (; nextContext.element; nextContext.element = nextContext.element->parentElement()) { 217 Match match = this->match(nextContext, ignoreDynamicPseudo); 218 if (match == SelectorMatches || match == SelectorFailsCompletely) 219 return match; 220 if (nextContext.element == nextContext.scope && nextContext.behaviorAtBoundary != StaysWithinTreeScope) 221 return SelectorFailsCompletely; 222 } 223 return SelectorFailsCompletely; 224 225 case CSSSelector::Child: 226 nextContext.element = context.element->parentElement(); 227 if (!nextContext.element) 228 return SelectorFailsCompletely; 229 nextContext.isSubSelector = false; 230 nextContext.elementStyle = 0; 231 return match(nextContext, ignoreDynamicPseudo); 232 233 case CSSSelector::DirectAdjacent: 234 if (m_mode == ResolvingStyle) { 235 if (Element* parentElement = context.element->parentElement()) 236 parentElement->setChildrenAffectedByDirectAdjacentRules(); 237 } 238 nextContext.element = context.element->previousElementSibling(); 239 if (!nextContext.element) 240 return SelectorFailsAllSiblings; 241 nextContext.isSubSelector = false; 242 nextContext.elementStyle = 0; 243 return match(nextContext, ignoreDynamicPseudo); 244 245 case CSSSelector::IndirectAdjacent: 246 if (m_mode == ResolvingStyle) { 247 if (Element* parentElement = context.element->parentElement()) 248 parentElement->setChildrenAffectedByForwardPositionalRules(); 249 } 250 nextContext.element = context.element->previousElementSibling(); 251 nextContext.isSubSelector = false; 252 nextContext.elementStyle = 0; 253 for (; nextContext.element; nextContext.element = nextContext.element->previousElementSibling()) { 254 Match match = this->match(nextContext, ignoreDynamicPseudo); 255 if (match == SelectorMatches || match == SelectorFailsAllSiblings || match == SelectorFailsCompletely) 256 return match; 257 }; 258 return SelectorFailsAllSiblings; 259 260 case CSSSelector::SubSelector: 261 // a selector is invalid if something follows a pseudo-element 262 // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else) 263 // to follow the pseudo elements. 264 nextContext.hasScrollbarPseudo = dynamicPseudo != NOPSEUDO && (context.scrollbar || dynamicPseudo == SCROLLBAR_CORNER || dynamicPseudo == RESIZER); 265 nextContext.hasSelectionPseudo = dynamicPseudo == SELECTION; 266 if ((context.elementStyle || m_mode == CollectingRules || m_mode == QueryingRules) && dynamicPseudo != NOPSEUDO 267 && !nextContext.hasSelectionPseudo 268 && !(nextContext.hasScrollbarPseudo && nextContext.selector->m_match == CSSSelector::PseudoClass)) 269 return SelectorFailsCompletely; 270 nextContext.isSubSelector = true; 271 return match(nextContext, dynamicPseudo); 272 273 case CSSSelector::ShadowDescendant: 274 { 275 // 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. 276 if (context.scope && context.scope->treeScope() == context.element->treeScope() && context.behaviorAtBoundary != StaysWithinTreeScope) 277 return SelectorFailsCompletely; 278 Element* shadowHostNode = context.element->shadowHost(); 279 if (!shadowHostNode) 280 return SelectorFailsCompletely; 281 nextContext.element = shadowHostNode; 282 nextContext.isSubSelector = false; 283 nextContext.elementStyle = 0; 284 return match(nextContext, ignoreDynamicPseudo); 285 } 286 } 287 288 ASSERT_NOT_REACHED(); 289 return SelectorFailsCompletely; 290} 291 292static bool attributeValueMatches(const Attribute* attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive) 293{ 294 const AtomicString& value = attributeItem->value(); 295 if (value.isNull()) 296 return false; 297 298 switch (match) { 299 case CSSSelector::Exact: 300 if (caseSensitive ? selectorValue != value : !equalIgnoringCase(selectorValue, value)) 301 return false; 302 break; 303 case CSSSelector::List: 304 { 305 // Ignore empty selectors or selectors containing spaces 306 if (selectorValue.contains(' ') || selectorValue.isEmpty()) 307 return false; 308 309 unsigned startSearchAt = 0; 310 while (true) { 311 size_t foundPos = value.find(selectorValue, startSearchAt, caseSensitive); 312 if (foundPos == notFound) 313 return false; 314 if (!foundPos || value[foundPos - 1] == ' ') { 315 unsigned endStr = foundPos + selectorValue.length(); 316 if (endStr == value.length() || value[endStr] == ' ') 317 break; // We found a match. 318 } 319 320 // No match. Keep looking. 321 startSearchAt = foundPos + 1; 322 } 323 break; 324 } 325 case CSSSelector::Contain: 326 if (!value.contains(selectorValue, caseSensitive) || selectorValue.isEmpty()) 327 return false; 328 break; 329 case CSSSelector::Begin: 330 if (!value.startsWith(selectorValue, caseSensitive) || selectorValue.isEmpty()) 331 return false; 332 break; 333 case CSSSelector::End: 334 if (!value.endsWith(selectorValue, caseSensitive) || selectorValue.isEmpty()) 335 return false; 336 break; 337 case CSSSelector::Hyphen: 338 if (value.length() < selectorValue.length()) 339 return false; 340 if (!value.startsWith(selectorValue, caseSensitive)) 341 return false; 342 // It they start the same, check for exact match or following '-': 343 if (value.length() != selectorValue.length() && value[selectorValue.length()] != '-') 344 return false; 345 break; 346 case CSSSelector::PseudoClass: 347 case CSSSelector::PseudoElement: 348 default: 349 break; 350 } 351 352 return true; 353} 354 355static bool anyAttributeMatches(Element* element, CSSSelector::Match match, const QualifiedName& selectorAttr, const AtomicString& selectorValue, bool caseSensitive) 356{ 357 ASSERT(element->hasAttributesWithoutUpdate()); 358 for (size_t i = 0; i < element->attributeCount(); ++i) { 359 const Attribute* attributeItem = element->attributeItem(i); 360 361 if (!attributeItem->matches(selectorAttr)) 362 continue; 363 364 if (attributeValueMatches(attributeItem, match, selectorValue, caseSensitive)) 365 return true; 366 } 367 368 return false; 369} 370 371bool SelectorChecker::checkOne(const SelectorCheckingContext& context) const 372{ 373 Element* const & element = context.element; 374 const CSSSelector* const & selector = context.selector; 375 ASSERT(element); 376 ASSERT(selector); 377 378 if (selector->m_match == CSSSelector::Tag) 379 return SelectorChecker::tagMatches(element, selector->tagQName()); 380 381 if (selector->m_match == CSSSelector::Class) 382 return element->hasClass() && static_cast<StyledElement*>(element)->classNames().contains(selector->value()); 383 384 if (selector->m_match == CSSSelector::Id) 385 return element->hasID() && element->idForStyleResolution() == selector->value(); 386 387 if (selector->isAttributeSelector()) { 388 const QualifiedName& attr = selector->attribute(); 389 390 if (!element->hasAttributes()) 391 return false; 392 393 bool caseSensitive = !m_documentIsHTML || HTMLDocument::isCaseSensitiveAttribute(attr); 394 395 if (!anyAttributeMatches(element, static_cast<CSSSelector::Match>(selector->m_match), attr, selector->value(), caseSensitive)) 396 return false; 397 } 398 399 if (selector->m_match == CSSSelector::PseudoClass) { 400 // Handle :not up front. 401 if (selector->pseudoType() == CSSSelector::PseudoNot) { 402 const CSSSelectorList* selectorList = selector->selectorList(); 403 404 // FIXME: We probably should fix the parser and make it never produce :not rules with missing selector list. 405 if (!selectorList) 406 return false; 407 408 SelectorCheckingContext subContext(context); 409 subContext.isSubSelector = true; 410 for (subContext.selector = selectorList->first(); subContext.selector; subContext.selector = subContext.selector->tagHistory()) { 411 // :not cannot nest. I don't really know why this is a 412 // restriction in CSS3, but it is, so let's honor it. 413 // the parser enforces that this never occurs 414 ASSERT(subContext.selector->pseudoType() != CSSSelector::PseudoNot); 415 // We select between :visited and :link when applying. We don't know which one applied (or not) yet. 416 if (subContext.selector->pseudoType() == CSSSelector::PseudoVisited || (subContext.selector->pseudoType() == CSSSelector::PseudoLink && subContext.visitedMatchType == VisitedMatchEnabled)) 417 return true; 418 if (!checkOne(subContext)) 419 return true; 420 } 421 } else if (context.hasScrollbarPseudo) { 422 // CSS scrollbars match a specific subset of pseudo classes, and they have specialized rules for each 423 // (since there are no elements involved). 424 return checkScrollbarPseudoClass(context, element->document(), selector); 425 } else if (context.hasSelectionPseudo) { 426 if (selector->pseudoType() == CSSSelector::PseudoWindowInactive) 427 return !element->document()->page()->focusController()->isActive(); 428 } 429 430 // Normal element pseudo class checking. 431 switch (selector->pseudoType()) { 432 // Pseudo classes: 433 case CSSSelector::PseudoNot: 434 break; // Already handled up above. 435 case CSSSelector::PseudoEmpty: 436 { 437 bool result = true; 438 for (Node* n = element->firstChild(); n; n = n->nextSibling()) { 439 if (n->isElementNode()) { 440 result = false; 441 break; 442 } 443 if (n->isTextNode()) { 444 Text* textNode = toText(n); 445 if (!textNode->data().isEmpty()) { 446 result = false; 447 break; 448 } 449 } 450 } 451 if (m_mode == ResolvingStyle) { 452 element->setStyleAffectedByEmpty(); 453 if (context.elementStyle) 454 context.elementStyle->setEmptyState(result); 455 else if (element->renderStyle() && (element->document()->styleSheetCollection()->usesSiblingRules() || element->renderStyle()->unique())) 456 element->renderStyle()->setEmptyState(result); 457 } 458 return result; 459 } 460 case CSSSelector::PseudoFirstChild: 461 // first-child matches the first child that is an element 462 if (Element* parentElement = element->parentElement()) { 463 bool result = isFirstChildElement(element); 464 if (m_mode == ResolvingStyle) { 465 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle(); 466 parentElement->setChildrenAffectedByFirstChildRules(); 467 if (result && childStyle) 468 childStyle->setFirstChildState(); 469 } 470 return result; 471 } 472 break; 473 case CSSSelector::PseudoFirstOfType: 474 // first-of-type matches the first element of its type 475 if (Element* parentElement = element->parentElement()) { 476 bool result = isFirstOfType(element, element->tagQName()); 477 if (m_mode == ResolvingStyle) 478 parentElement->setChildrenAffectedByForwardPositionalRules(); 479 return result; 480 } 481 break; 482 case CSSSelector::PseudoLastChild: 483 // last-child matches the last child that is an element 484 if (Element* parentElement = element->parentElement()) { 485 bool result = parentElement->isFinishedParsingChildren() && isLastChildElement(element); 486 if (m_mode == ResolvingStyle) { 487 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle(); 488 parentElement->setChildrenAffectedByLastChildRules(); 489 if (result && childStyle) 490 childStyle->setLastChildState(); 491 } 492 return result; 493 } 494 break; 495 case CSSSelector::PseudoLastOfType: 496 // last-of-type matches the last element of its type 497 if (Element* parentElement = element->parentElement()) { 498 if (m_mode == ResolvingStyle) 499 parentElement->setChildrenAffectedByBackwardPositionalRules(); 500 if (!parentElement->isFinishedParsingChildren()) 501 return false; 502 return isLastOfType(element, element->tagQName()); 503 } 504 break; 505 case CSSSelector::PseudoOnlyChild: 506 if (Element* parentElement = element->parentElement()) { 507 bool firstChild = isFirstChildElement(element); 508 bool onlyChild = firstChild && parentElement->isFinishedParsingChildren() && isLastChildElement(element); 509 if (m_mode == ResolvingStyle) { 510 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle(); 511 parentElement->setChildrenAffectedByFirstChildRules(); 512 parentElement->setChildrenAffectedByLastChildRules(); 513 if (firstChild && childStyle) 514 childStyle->setFirstChildState(); 515 if (onlyChild && childStyle) 516 childStyle->setLastChildState(); 517 } 518 return onlyChild; 519 } 520 break; 521 case CSSSelector::PseudoOnlyOfType: 522 // FIXME: This selector is very slow. 523 if (Element* parentElement = element->parentElement()) { 524 if (m_mode == ResolvingStyle) { 525 parentElement->setChildrenAffectedByForwardPositionalRules(); 526 parentElement->setChildrenAffectedByBackwardPositionalRules(); 527 } 528 if (!parentElement->isFinishedParsingChildren()) 529 return false; 530 return isFirstOfType(element, element->tagQName()) && isLastOfType(element, element->tagQName()); 531 } 532 break; 533 case CSSSelector::PseudoNthChild: 534 if (!selector->parseNth()) 535 break; 536 if (Element* parentElement = element->parentElement()) { 537 int count = 1 + countElementsBefore(element); 538 if (m_mode == ResolvingStyle) { 539 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle(); 540 element->setChildIndex(count); 541 if (childStyle) 542 childStyle->setUnique(); 543 parentElement->setChildrenAffectedByForwardPositionalRules(); 544 } 545 546 if (selector->matchNth(count)) 547 return true; 548 } 549 break; 550 case CSSSelector::PseudoNthOfType: 551 if (!selector->parseNth()) 552 break; 553 if (Element* parentElement = element->parentElement()) { 554 int count = 1 + countElementsOfTypeBefore(element, element->tagQName()); 555 if (m_mode == ResolvingStyle) 556 parentElement->setChildrenAffectedByForwardPositionalRules(); 557 558 if (selector->matchNth(count)) 559 return true; 560 } 561 break; 562 case CSSSelector::PseudoNthLastChild: 563 if (!selector->parseNth()) 564 break; 565 if (Element* parentElement = element->parentElement()) { 566 if (m_mode == ResolvingStyle) 567 parentElement->setChildrenAffectedByBackwardPositionalRules(); 568 if (!parentElement->isFinishedParsingChildren()) 569 return false; 570 int count = 1 + countElementsAfter(element); 571 if (selector->matchNth(count)) 572 return true; 573 } 574 break; 575 case CSSSelector::PseudoNthLastOfType: 576 if (!selector->parseNth()) 577 break; 578 if (Element* parentElement = element->parentElement()) { 579 if (m_mode == ResolvingStyle) 580 parentElement->setChildrenAffectedByBackwardPositionalRules(); 581 if (!parentElement->isFinishedParsingChildren()) 582 return false; 583 584 int count = 1 + countElementsOfTypeAfter(element, element->tagQName()); 585 if (selector->matchNth(count)) 586 return true; 587 } 588 break; 589 case CSSSelector::PseudoTarget: 590 if (element == element->document()->cssTarget()) 591 return true; 592 break; 593 case CSSSelector::PseudoAny: 594 { 595 SelectorCheckingContext subContext(context); 596 subContext.isSubSelector = true; 597 PseudoId ignoreDynamicPseudo = NOPSEUDO; 598 for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(subContext.selector)) { 599 if (match(subContext, ignoreDynamicPseudo) == SelectorMatches) 600 return true; 601 } 602 } 603 break; 604 case CSSSelector::PseudoAutofill: 605 if (!element->isFormControlElement()) 606 break; 607 if (HTMLInputElement* inputElement = element->toInputElement()) 608 return inputElement->isAutofilled(); 609 break; 610 case CSSSelector::PseudoAnyLink: 611 case CSSSelector::PseudoLink: 612 // :visited and :link matches are separated later when applying the style. Here both classes match all links... 613 return element->isLink(); 614 case CSSSelector::PseudoVisited: 615 // ...except if :visited matching is disabled for ancestor/sibling matching. 616 return element->isLink() && context.visitedMatchType == VisitedMatchEnabled; 617 case CSSSelector::PseudoDrag: 618 if (m_mode == ResolvingStyle) { 619 if (context.elementStyle) 620 context.elementStyle->setAffectedByDrag(); 621 else 622 element->setChildrenAffectedByDrag(true); 623 } 624 if (element->renderer() && element->renderer()->isDragging()) 625 return true; 626 break; 627 case CSSSelector::PseudoFocus: 628 return matchesFocusPseudoClass(element); 629 case CSSSelector::PseudoHover: 630 // If we're in quirks mode, then hover should never match anchors with no 631 // href and *:hover should not match anything. This is important for sites like wsj.com. 632 if (m_strictParsing || context.isSubSelector || (selector->m_match == CSSSelector::Tag && selector->tagQName() != anyQName() && !element->hasTagName(aTag)) || element->isLink()) { 633 if (m_mode == ResolvingStyle) { 634 if (context.elementStyle) 635 context.elementStyle->setAffectedByHover(); 636 else 637 element->setChildrenAffectedByHover(true); 638 } 639 if (element->hovered() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoHover)) 640 return true; 641 } 642 break; 643 case CSSSelector::PseudoActive: 644 // If we're in quirks mode, then :active should never match anchors with no 645 // href and *:active should not match anything. 646 if (m_strictParsing || context.isSubSelector || (selector->m_match == CSSSelector::Tag && selector->tagQName() != anyQName() && !element->hasTagName(aTag)) || element->isLink()) { 647 if (m_mode == ResolvingStyle) { 648 if (context.elementStyle) 649 context.elementStyle->setAffectedByActive(); 650 else 651 element->setChildrenAffectedByActive(true); 652 } 653 if (element->active() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoActive)) 654 return true; 655 } 656 break; 657 case CSSSelector::PseudoEnabled: 658 if (element->isFormControlElement() || element->hasTagName(optionTag) || element->hasTagName(optgroupTag)) 659 return !element->isDisabledFormControl(); 660 break; 661 case CSSSelector::PseudoFullPageMedia: 662 return element->document() && element->document()->isMediaDocument(); 663 break; 664 case CSSSelector::PseudoDefault: 665 return element->isDefaultButtonForForm(); 666 case CSSSelector::PseudoDisabled: 667 if (element->isFormControlElement() || element->hasTagName(optionTag) || element->hasTagName(optgroupTag)) 668 return element->isDisabledFormControl(); 669 break; 670 case CSSSelector::PseudoReadOnly: 671 return element->matchesReadOnlyPseudoClass(); 672 case CSSSelector::PseudoReadWrite: 673 return element->matchesReadWritePseudoClass(); 674 case CSSSelector::PseudoOptional: 675 return element->isOptionalFormControl(); 676 case CSSSelector::PseudoRequired: 677 return element->isRequiredFormControl(); 678 case CSSSelector::PseudoValid: 679 element->document()->setContainsValidityStyleRules(); 680 return element->willValidate() && element->isValidFormControlElement(); 681 case CSSSelector::PseudoInvalid: 682 element->document()->setContainsValidityStyleRules(); 683 return element->willValidate() && !element->isValidFormControlElement(); 684 case CSSSelector::PseudoChecked: 685 { 686 // Even though WinIE allows checked and indeterminate to co-exist, the CSS selector spec says that 687 // you can't be both checked and indeterminate. We will behave like WinIE behind the scenes and just 688 // obey the CSS spec here in the test for matching the pseudo. 689 HTMLInputElement* inputElement = element->toInputElement(); 690 if (inputElement && inputElement->shouldAppearChecked() && !inputElement->shouldAppearIndeterminate()) 691 return true; 692 if (element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected()) 693 return true; 694 break; 695 } 696 case CSSSelector::PseudoIndeterminate: 697 return element->shouldAppearIndeterminate(); 698 case CSSSelector::PseudoRoot: 699 if (element == element->document()->documentElement()) 700 return true; 701 break; 702 case CSSSelector::PseudoLang: 703 { 704 AtomicString value; 705#if ENABLE(VIDEO_TRACK) 706 if (element->isWebVTTElement()) 707 value = toWebVTTElement(element)->language(); 708 else 709#endif 710 value = element->computeInheritedLanguage(); 711 const AtomicString& argument = selector->argument(); 712 if (value.isEmpty() || !value.startsWith(argument, false)) 713 break; 714 if (value.length() != argument.length() && value[argument.length()] != '-') 715 break; 716 return true; 717 } 718#if ENABLE(FULLSCREEN_API) 719 case CSSSelector::PseudoFullScreen: 720 // While a Document is in the fullscreen state, and the document's current fullscreen 721 // element is an element in the document, the 'full-screen' pseudoclass applies to 722 // that element. Also, an <iframe>, <object> or <embed> element whose child browsing 723 // context's Document is in the fullscreen state has the 'full-screen' pseudoclass applied. 724 if (element->isFrameElementBase() && static_cast<HTMLFrameElementBase*>(element)->containsFullScreenElement()) 725 return true; 726 if (!element->document()->webkitIsFullScreen()) 727 return false; 728 return element == element->document()->webkitCurrentFullScreenElement(); 729 case CSSSelector::PseudoAnimatingFullScreenTransition: 730 if (element != element->document()->webkitCurrentFullScreenElement()) 731 return false; 732 return element->document()->isAnimatingFullScreen(); 733 case CSSSelector::PseudoFullScreenAncestor: 734 return element->containsFullScreenElement(); 735 case CSSSelector::PseudoFullScreenDocument: 736 // While a Document is in the fullscreen state, the 'full-screen-document' pseudoclass applies 737 // to all elements of that Document. 738 if (!element->document()->webkitIsFullScreen()) 739 return false; 740 return true; 741#endif 742#if ENABLE(IFRAME_SEAMLESS) 743 case CSSSelector::PseudoSeamlessDocument: 744 // While a document is rendered in a seamless iframe, the 'seamless-document' pseudoclass applies 745 // to all elements of that Document. 746 return element->document()->shouldDisplaySeamlesslyWithParent(); 747#endif 748 case CSSSelector::PseudoInRange: 749 element->document()->setContainsValidityStyleRules(); 750 return element->isInRange(); 751 case CSSSelector::PseudoOutOfRange: 752 element->document()->setContainsValidityStyleRules(); 753 return element->isOutOfRange(); 754#if ENABLE(VIDEO_TRACK) 755 case CSSSelector::PseudoFutureCue: 756 return (element->isWebVTTElement() && !toWebVTTElement(element)->isPastNode()); 757 case CSSSelector::PseudoPastCue: 758 return (element->isWebVTTElement() && toWebVTTElement(element)->isPastNode()); 759#endif 760 761 case CSSSelector::PseudoScope: 762 { 763 const Node* contextualReferenceNode = !context.scope || context.behaviorAtBoundary == CrossesBoundary ? element->document()->documentElement() : context.scope; 764 if (element == contextualReferenceNode) 765 return true; 766 break; 767 } 768 769 case CSSSelector::PseudoHorizontal: 770 case CSSSelector::PseudoVertical: 771 case CSSSelector::PseudoDecrement: 772 case CSSSelector::PseudoIncrement: 773 case CSSSelector::PseudoStart: 774 case CSSSelector::PseudoEnd: 775 case CSSSelector::PseudoDoubleButton: 776 case CSSSelector::PseudoSingleButton: 777 case CSSSelector::PseudoNoButton: 778 case CSSSelector::PseudoCornerPresent: 779 return false; 780 781 case CSSSelector::PseudoUnknown: 782 case CSSSelector::PseudoNotParsed: 783 default: 784 ASSERT_NOT_REACHED(); 785 break; 786 } 787 return false; 788 } 789#if ENABLE(VIDEO_TRACK) 790 else if (selector->m_match == CSSSelector::PseudoElement && selector->pseudoType() == CSSSelector::PseudoCue) { 791 SelectorCheckingContext subContext(context); 792 subContext.isSubSelector = true; 793 794 PseudoId ignoreDynamicPseudo = NOPSEUDO; 795 const CSSSelector* const & selector = context.selector; 796 for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(subContext.selector)) { 797 if (match(subContext, ignoreDynamicPseudo) == SelectorMatches) 798 return true; 799 } 800 return false; 801 } 802#endif 803 // ### add the rest of the checks... 804 return true; 805} 806 807bool SelectorChecker::checkScrollbarPseudoClass(const SelectorCheckingContext& context, Document* document, const CSSSelector* selector) const 808{ 809 RenderScrollbar* scrollbar = context.scrollbar; 810 ScrollbarPart part = context.scrollbarPart; 811 812 // FIXME: This is a temporary hack for resizers and scrollbar corners. Eventually :window-inactive should become a real 813 // pseudo class and just apply to everything. 814 if (selector->pseudoType() == CSSSelector::PseudoWindowInactive) 815 return !document->page()->focusController()->isActive(); 816 817 if (!scrollbar) 818 return false; 819 820 ASSERT(selector->m_match == CSSSelector::PseudoClass); 821 switch (selector->pseudoType()) { 822 case CSSSelector::PseudoEnabled: 823 return scrollbar->enabled(); 824 case CSSSelector::PseudoDisabled: 825 return !scrollbar->enabled(); 826 case CSSSelector::PseudoHover: 827 { 828 ScrollbarPart hoveredPart = scrollbar->hoveredPart(); 829 if (part == ScrollbarBGPart) 830 return hoveredPart != NoPart; 831 if (part == TrackBGPart) 832 return hoveredPart == BackTrackPart || hoveredPart == ForwardTrackPart || hoveredPart == ThumbPart; 833 return part == hoveredPart; 834 } 835 case CSSSelector::PseudoActive: 836 { 837 ScrollbarPart pressedPart = scrollbar->pressedPart(); 838 if (part == ScrollbarBGPart) 839 return pressedPart != NoPart; 840 if (part == TrackBGPart) 841 return pressedPart == BackTrackPart || pressedPart == ForwardTrackPart || pressedPart == ThumbPart; 842 return part == pressedPart; 843 } 844 case CSSSelector::PseudoHorizontal: 845 return scrollbar->orientation() == HorizontalScrollbar; 846 case CSSSelector::PseudoVertical: 847 return scrollbar->orientation() == VerticalScrollbar; 848 case CSSSelector::PseudoDecrement: 849 return part == BackButtonStartPart || part == BackButtonEndPart || part == BackTrackPart; 850 case CSSSelector::PseudoIncrement: 851 return part == ForwardButtonStartPart || part == ForwardButtonEndPart || part == ForwardTrackPart; 852 case CSSSelector::PseudoStart: 853 return part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart; 854 case CSSSelector::PseudoEnd: 855 return part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart; 856 case CSSSelector::PseudoDoubleButton: 857 { 858 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement(); 859 if (part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart) 860 return buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth; 861 if (part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart) 862 return buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth; 863 return false; 864 } 865 case CSSSelector::PseudoSingleButton: 866 { 867 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement(); 868 if (part == BackButtonStartPart || part == ForwardButtonEndPart || part == BackTrackPart || part == ForwardTrackPart) 869 return buttonsPlacement == ScrollbarButtonsSingle; 870 return false; 871 } 872 case CSSSelector::PseudoNoButton: 873 { 874 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement(); 875 if (part == BackTrackPart) 876 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleEnd; 877 if (part == ForwardTrackPart) 878 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleStart; 879 return false; 880 } 881 case CSSSelector::PseudoCornerPresent: 882 return scrollbar->scrollableArea()->isScrollCornerVisible(); 883 default: 884 return false; 885 } 886} 887 888unsigned SelectorChecker::determineLinkMatchType(const CSSSelector* selector) 889{ 890 unsigned linkMatchType = MatchAll; 891 892 // Statically determine if this selector will match a link in visited, unvisited or any state, or never. 893 // :visited never matches other elements than the innermost link element. 894 for (; selector; selector = selector->tagHistory()) { 895 switch (selector->pseudoType()) { 896 case CSSSelector::PseudoNot: 897 { 898 // :not(:visited) is equivalent to :link. Parser enforces that :not can't nest. 899 const CSSSelectorList* selectorList = selector->selectorList(); 900 if (!selectorList) 901 break; 902 903 for (const CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = subSelector->tagHistory()) { 904 CSSSelector::PseudoType subType = subSelector->pseudoType(); 905 if (subType == CSSSelector::PseudoVisited) 906 linkMatchType &= ~SelectorChecker::MatchVisited; 907 else if (subType == CSSSelector::PseudoLink) 908 linkMatchType &= ~SelectorChecker::MatchLink; 909 } 910 } 911 break; 912 case CSSSelector::PseudoLink: 913 linkMatchType &= ~SelectorChecker::MatchVisited; 914 break; 915 case CSSSelector::PseudoVisited: 916 linkMatchType &= ~SelectorChecker::MatchLink; 917 break; 918 default: 919 // We don't support :link and :visited inside :-webkit-any. 920 break; 921 } 922 CSSSelector::Relation relation = selector->relation(); 923 if (relation == CSSSelector::SubSelector) 924 continue; 925 if (relation != CSSSelector::Descendant && relation != CSSSelector::Child) 926 return linkMatchType; 927 if (linkMatchType != MatchAll) 928 return linkMatchType; 929 } 930 return linkMatchType; 931} 932 933bool SelectorChecker::isFrameFocused(const Element* element) 934{ 935 return element->document()->frame() && element->document()->frame()->selection()->isFocusedAndActive(); 936} 937 938bool SelectorChecker::matchesFocusPseudoClass(const Element* element) 939{ 940 if (InspectorInstrumentation::forcePseudoState(const_cast<Element*>(element), CSSSelector::PseudoFocus)) 941 return true; 942 return element->focused() && isFrameFocused(element); 943} 944 945} 946