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.
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.
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.
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.
29 #include "SelectorChecker.h"
31 #include "CSSSelector.h"
32 #include "CSSSelectorList.h"
34 #include "ElementTraversal.h"
35 #include "FocusController.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"
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"
63 using namespace HTMLNames;
65 static inline bool isFirstChildElement(const Element* element)
67 return !ElementTraversal::previousSibling(element);
70 static inline bool isLastChildElement(const Element* element)
72 return !ElementTraversal::nextSibling(element);
75 static inline bool isFirstOfType(const Element* element, const QualifiedName& type)
77 for (const Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(sibling)) {
78 if (sibling->hasTagName(type))
84 static inline bool isLastOfType(const Element* element, const QualifiedName& type)
86 for (const Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(sibling)) {
87 if (sibling->hasTagName(type))
93 static inline int countElementsBefore(const Element* element)
96 for (const Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(sibling)) {
97 unsigned index = sibling->childIndex();
107 static inline int countElementsOfTypeBefore(const Element* element, const QualifiedName& type)
110 for (const Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(sibling)) {
111 if (sibling->hasTagName(type))
117 static inline int countElementsAfter(const Element* element)
120 for (const Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(sibling))
125 static inline int countElementsOfTypeAfter(const Element* element, const QualifiedName& type)
128 for (const Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(sibling)) {
129 if (sibling->hasTagName(type))
135 SelectorChecker::SelectorChecker(Document& document, Mode mode)
136 : m_strictParsing(!document.inQuirksMode())
137 , m_documentIsHTML(document.isHTMLDocument())
142 bool SelectorChecker::match(const SelectorCheckingContext& context) const
144 PseudoId pseudoId = NOPSEUDO;
145 if (matchRecursively(context, pseudoId) != SelectorMatches)
147 if (context.pseudoId != NOPSEUDO && context.pseudoId != pseudoId)
149 if (context.pseudoId == NOPSEUDO && pseudoId != NOPSEUDO) {
150 if (m_mode == ResolvingStyle && pseudoId < FIRST_INTERNAL_PSEUDOID)
151 context.elementStyle->setHasPseudoStyle(pseudoId);
153 // For SharingRules testing, any match is good enough, we don't care what is matched.
154 return m_mode == SharingRules || m_mode == StyleInvalidation;
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
165 SelectorChecker::Match SelectorChecker::matchRecursively(const SelectorCheckingContext& context, PseudoId& dynamicPseudo) const
167 // The first selector has to match.
168 if (!checkOne(context))
169 return SelectorFailsLocally;
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;
177 if (context.selector->pseudoElementType() == CSSSelector::PseudoElementWebKitCustom && root->type() != ShadowRoot::UserAgentShadowRoot)
178 return SelectorFailsLocally;
179 } else if (m_mode != StyleInvalidation)
180 return SelectorFailsLocally;
182 if ((!context.elementStyle && m_mode == ResolvingStyle) || m_mode == QueryingRules)
183 return SelectorFailsLocally;
185 // When invalidating style all pseudo elements need to match.
186 PseudoId pseudoId = m_mode == StyleInvalidation ? NOPSEUDO : CSSSelector::pseudoId(context.selector->pseudoElementType());
187 if (pseudoId != NOPSEUDO) {
188 dynamicPseudo = pseudoId;
190 if (pseudoId == FIRST_LETTER)
191 context.element->document().styleSheetCollection().setUsesFirstLetterRules(true);
196 // The rest of the selectors has to match
197 CSSSelector::Relation relation = context.selector->relation();
199 // Prepare next selector
200 const CSSSelector* historySelector = context.selector->tagHistory();
201 if (!historySelector)
202 return SelectorMatches;
204 SelectorCheckingContext nextContext(context);
205 nextContext.selector = historySelector;
207 PseudoId ignoreDynamicPseudo = NOPSEUDO;
208 if (relation != CSSSelector::SubSelector) {
209 // Bail-out if this selector is irrelevant for the pseudoId
210 if (context.pseudoId != NOPSEUDO && context.pseudoId != dynamicPseudo)
211 return SelectorFailsCompletely;
213 // Disable :visited matching when we see the first link or try to match anything else than an ancestors.
214 if (context.firstSelectorOfTheFragment == context.selector && (context.element->isLink() || (relation != CSSSelector::Descendant && relation != CSSSelector::Child)))
215 nextContext.visitedMatchType = VisitedMatchDisabled;
217 nextContext.pseudoId = NOPSEUDO;
221 case CSSSelector::Descendant:
222 nextContext.element = context.element->parentElement();
223 nextContext.firstSelectorOfTheFragment = nextContext.selector;
224 nextContext.elementStyle = 0;
225 for (; nextContext.element; nextContext.element = nextContext.element->parentElement()) {
226 Match match = this->matchRecursively(nextContext, ignoreDynamicPseudo);
227 if (match == SelectorMatches || match == SelectorFailsCompletely)
230 return SelectorFailsCompletely;
232 case CSSSelector::Child:
234 nextContext.element = context.element->parentElement();
235 if (!nextContext.element)
236 return SelectorFailsCompletely;
237 nextContext.firstSelectorOfTheFragment = nextContext.selector;
238 nextContext.elementStyle = nullptr;
239 Match match = matchRecursively(nextContext, ignoreDynamicPseudo);
240 if (match == SelectorMatches || match == SelectorFailsCompletely)
242 return SelectorFailsAllSiblings;
245 case CSSSelector::DirectAdjacent:
246 if (m_mode == ResolvingStyle) {
247 if (Element* parentElement = context.element->parentElement())
248 parentElement->setChildrenAffectedByDirectAdjacentRules();
250 nextContext.element = context.element->previousElementSibling();
251 if (!nextContext.element)
252 return SelectorFailsAllSiblings;
253 nextContext.firstSelectorOfTheFragment = nextContext.selector;
254 nextContext.elementStyle = 0;
255 return matchRecursively(nextContext, ignoreDynamicPseudo);
257 case CSSSelector::IndirectAdjacent:
258 if (m_mode == ResolvingStyle) {
259 if (Element* parentElement = context.element->parentElement())
260 parentElement->setChildrenAffectedByForwardPositionalRules();
262 nextContext.element = context.element->previousElementSibling();
263 nextContext.firstSelectorOfTheFragment = nextContext.selector;
264 nextContext.elementStyle = 0;
265 for (; nextContext.element; nextContext.element = nextContext.element->previousElementSibling()) {
266 Match match = this->matchRecursively(nextContext, ignoreDynamicPseudo);
267 if (match == SelectorMatches || match == SelectorFailsAllSiblings || match == SelectorFailsCompletely)
270 return SelectorFailsAllSiblings;
272 case CSSSelector::SubSelector:
273 // a selector is invalid if something follows a pseudo-element
274 // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else)
275 // to follow the pseudo elements.
276 nextContext.hasScrollbarPseudo = dynamicPseudo != NOPSEUDO && (context.scrollbar || dynamicPseudo == SCROLLBAR_CORNER || dynamicPseudo == RESIZER);
277 nextContext.hasSelectionPseudo = dynamicPseudo == SELECTION;
278 if ((context.elementStyle || m_mode == CollectingRules || m_mode == QueryingRules) && dynamicPseudo != NOPSEUDO
279 && !nextContext.hasSelectionPseudo
280 && !(nextContext.hasScrollbarPseudo && nextContext.selector->m_match == CSSSelector::PseudoClass))
281 return SelectorFailsCompletely;
282 return matchRecursively(nextContext, dynamicPseudo);
284 case CSSSelector::ShadowDescendant:
286 Element* shadowHostNode = context.element->shadowHost();
288 return SelectorFailsCompletely;
289 nextContext.element = shadowHostNode;
290 nextContext.firstSelectorOfTheFragment = nextContext.selector;
291 nextContext.elementStyle = 0;
292 return matchRecursively(nextContext, ignoreDynamicPseudo);
296 ASSERT_NOT_REACHED();
297 return SelectorFailsCompletely;
300 static bool attributeValueMatches(const Attribute& attribute, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
302 const AtomicString& value = attribute.value();
303 ASSERT(!value.isNull());
306 case CSSSelector::Set:
308 case CSSSelector::Exact:
309 if (caseSensitive ? selectorValue != value : !equalIgnoringCase(selectorValue, value))
312 case CSSSelector::List:
314 // Ignore empty selectors or selectors containing spaces
315 if (selectorValue.contains(' ') || selectorValue.isEmpty())
318 unsigned startSearchAt = 0;
320 size_t foundPos = value.find(selectorValue, startSearchAt, caseSensitive);
321 if (foundPos == notFound)
323 if (!foundPos || value[foundPos - 1] == ' ') {
324 unsigned endStr = foundPos + selectorValue.length();
325 if (endStr == value.length() || value[endStr] == ' ')
326 break; // We found a match.
329 // No match. Keep looking.
330 startSearchAt = foundPos + 1;
334 case CSSSelector::Contain:
335 if (!value.contains(selectorValue, caseSensitive) || selectorValue.isEmpty())
338 case CSSSelector::Begin:
339 if (!value.startsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
342 case CSSSelector::End:
343 if (!value.endsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
346 case CSSSelector::Hyphen:
347 if (value.length() < selectorValue.length())
349 if (!value.startsWith(selectorValue, caseSensitive))
351 // It they start the same, check for exact match or following '-':
352 if (value.length() != selectorValue.length() && value[selectorValue.length()] != '-')
356 ASSERT_NOT_REACHED();
363 static bool anyAttributeMatches(Element* element, const CSSSelector* selector, const QualifiedName& selectorAttr, bool caseSensitive)
365 ASSERT(element->hasAttributesWithoutUpdate());
366 for (const Attribute& attribute : element->attributesIterator()) {
367 if (!attribute.matches(selectorAttr.prefix(), element->isHTMLElement() ? selector->attributeCanonicalLocalName() : selectorAttr.localName(), selectorAttr.namespaceURI()))
370 if (attributeValueMatches(attribute, static_cast<CSSSelector::Match>(selector->m_match), selector->value(), caseSensitive))
377 static bool canMatchHoverOrActiveInQuirksMode(const SelectorChecker::SelectorCheckingContext& context)
379 // For quirks mode, follow this: http://quirks.spec.whatwg.org/#the-:active-and-:hover-quirk
380 // 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.
382 // selector uses the ':active' or ':hover' pseudo-classes.
383 // selector does not use a type selector.
384 // selector does not use an attribute selector.
385 // selector does not use an ID selector.
386 // selector does not use a class selector.
387 // selector does not use a pseudo-class selector other than ':active' and ':hover'.
388 // selector does not use a pseudo-element selector.
389 // selector is not part of an argument to a functional pseudo-class or pseudo-element.
390 if (context.inFunctionalPseudoClass)
393 for (const CSSSelector* selector = context.firstSelectorOfTheFragment; selector; selector = selector->tagHistory()) {
394 switch (selector->m_match) {
395 case CSSSelector::Tag:
396 if (selector->tagQName() != anyQName())
399 case CSSSelector::PseudoClass: {
400 CSSSelector::PseudoClassType pseudoClassType = selector->pseudoClassType();
401 if (pseudoClassType != CSSSelector::PseudoClassHover && pseudoClassType != CSSSelector::PseudoClassActive)
405 case CSSSelector::Id:
406 case CSSSelector::Class:
407 case CSSSelector::Exact:
408 case CSSSelector::Set:
409 case CSSSelector::List:
410 case CSSSelector::Hyphen:
411 case CSSSelector::Contain:
412 case CSSSelector::Begin:
413 case CSSSelector::End:
414 case CSSSelector::PagePseudoClass:
415 case CSSSelector::PseudoElement:
417 case CSSSelector::Unknown:
418 ASSERT_NOT_REACHED();
422 CSSSelector::Relation relation = selector->relation();
423 if (relation == CSSSelector::ShadowDescendant)
426 if (relation != CSSSelector::SubSelector)
432 bool SelectorChecker::checkOne(const SelectorCheckingContext& context) const
434 Element* const & element = context.element;
435 const CSSSelector* const & selector = context.selector;
439 if (selector->m_match == CSSSelector::Tag)
440 return SelectorChecker::tagMatches(element, selector->tagQName());
442 if (selector->m_match == CSSSelector::Class)
443 return element->hasClass() && element->classNames().contains(selector->value());
445 if (selector->m_match == CSSSelector::Id)
446 return element->hasID() && element->idForStyleResolution() == selector->value();
448 if (selector->isAttributeSelector()) {
449 if (!element->hasAttributes())
452 const QualifiedName& attr = selector->attribute();
453 bool caseSensitive = !(m_documentIsHTML && element->isHTMLElement()) || HTMLDocument::isCaseSensitiveAttribute(attr);
455 if (!anyAttributeMatches(element, selector, attr, caseSensitive))
459 if (selector->m_match == CSSSelector::PseudoClass) {
460 // Handle :not up front.
461 if (selector->pseudoClassType() == CSSSelector::PseudoClassNot) {
462 const CSSSelectorList* selectorList = selector->selectorList();
464 // FIXME: We probably should fix the parser and make it never produce :not rules with missing selector list.
468 SelectorCheckingContext subContext(context);
469 subContext.inFunctionalPseudoClass = true;
470 subContext.firstSelectorOfTheFragment = selectorList->first();
471 for (subContext.selector = selectorList->first(); subContext.selector; subContext.selector = subContext.selector->tagHistory()) {
472 if (subContext.selector->m_match == CSSSelector::PseudoClass) {
473 // :not cannot nest. I don't really know why this is a
474 // restriction in CSS3, but it is, so let's honor it.
475 // the parser enforces that this never occurs
476 ASSERT(subContext.selector->pseudoClassType() != CSSSelector::PseudoClassNot);
477 // We select between :visited and :link when applying. We don't know which one applied (or not) yet.
478 if (subContext.selector->pseudoClassType() == CSSSelector::PseudoClassVisited || (subContext.selector->pseudoClassType() == CSSSelector::PseudoClassLink && subContext.visitedMatchType == VisitedMatchEnabled))
481 if (!checkOne(subContext))
484 } else if (context.hasScrollbarPseudo) {
485 // CSS scrollbars match a specific subset of pseudo classes, and they have specialized rules for each
486 // (since there are no elements involved).
487 return checkScrollbarPseudoClass(context, &element->document(), selector);
488 } else if (context.hasSelectionPseudo) {
489 if (selector->pseudoClassType() == CSSSelector::PseudoClassWindowInactive)
490 return !element->document().page()->focusController().isActive();
493 // Normal element pseudo class checking.
494 switch (selector->pseudoClassType()) {
496 case CSSSelector::PseudoClassNot:
497 break; // Already handled up above.
498 case CSSSelector::PseudoClassEmpty:
501 for (Node* n = element->firstChild(); n; n = n->nextSibling()) {
502 if (n->isElementNode()) {
506 if (n->isTextNode()) {
507 Text* textNode = toText(n);
508 if (!textNode->data().isEmpty()) {
514 if (m_mode == ResolvingStyle) {
515 element->setStyleAffectedByEmpty();
516 if (context.elementStyle)
517 context.elementStyle->setEmptyState(result);
518 else if (element->renderStyle() && (element->document().styleSheetCollection().usesSiblingRules() || element->renderStyle()->unique()))
519 element->renderStyle()->setEmptyState(result);
523 case CSSSelector::PseudoClassFirstChild:
524 // first-child matches the first child that is an element
525 if (Element* parentElement = element->parentElement()) {
526 bool result = isFirstChildElement(element);
527 if (m_mode == ResolvingStyle) {
528 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle();
529 parentElement->setChildrenAffectedByFirstChildRules();
530 if (result && childStyle)
531 childStyle->setFirstChildState();
536 case CSSSelector::PseudoClassFirstOfType:
537 // first-of-type matches the first element of its type
538 if (Element* parentElement = element->parentElement()) {
539 bool result = isFirstOfType(element, element->tagQName());
540 if (m_mode == ResolvingStyle)
541 parentElement->setChildrenAffectedByForwardPositionalRules();
545 case CSSSelector::PseudoClassLastChild:
546 // last-child matches the last child that is an element
547 if (Element* parentElement = element->parentElement()) {
548 bool result = parentElement->isFinishedParsingChildren() && isLastChildElement(element);
549 if (m_mode == ResolvingStyle) {
550 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle();
551 parentElement->setChildrenAffectedByLastChildRules();
552 if (result && childStyle)
553 childStyle->setLastChildState();
558 case CSSSelector::PseudoClassLastOfType:
559 // last-of-type matches the last element of its type
560 if (Element* parentElement = element->parentElement()) {
561 if (m_mode == ResolvingStyle)
562 parentElement->setChildrenAffectedByBackwardPositionalRules();
563 if (!parentElement->isFinishedParsingChildren())
565 return isLastOfType(element, element->tagQName());
568 case CSSSelector::PseudoClassOnlyChild:
569 if (Element* parentElement = element->parentElement()) {
570 bool firstChild = isFirstChildElement(element);
571 bool onlyChild = firstChild && parentElement->isFinishedParsingChildren() && isLastChildElement(element);
572 if (m_mode == ResolvingStyle) {
573 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle();
574 parentElement->setChildrenAffectedByFirstChildRules();
575 parentElement->setChildrenAffectedByLastChildRules();
576 if (firstChild && childStyle)
577 childStyle->setFirstChildState();
578 if (onlyChild && childStyle)
579 childStyle->setLastChildState();
584 case CSSSelector::PseudoClassOnlyOfType:
585 // FIXME: This selector is very slow.
586 if (Element* parentElement = element->parentElement()) {
587 if (m_mode == ResolvingStyle) {
588 parentElement->setChildrenAffectedByForwardPositionalRules();
589 parentElement->setChildrenAffectedByBackwardPositionalRules();
591 if (!parentElement->isFinishedParsingChildren())
593 return isFirstOfType(element, element->tagQName()) && isLastOfType(element, element->tagQName());
596 case CSSSelector::PseudoClassNthChild:
597 if (!selector->parseNth())
599 if (Element* parentElement = element->parentElement()) {
600 int count = 1 + countElementsBefore(element);
601 if (m_mode == ResolvingStyle) {
602 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle();
603 element->setChildIndex(count);
605 childStyle->setUnique();
606 parentElement->setChildrenAffectedByForwardPositionalRules();
609 if (selector->matchNth(count))
613 case CSSSelector::PseudoClassNthOfType:
614 if (!selector->parseNth())
616 if (Element* parentElement = element->parentElement()) {
617 int count = 1 + countElementsOfTypeBefore(element, element->tagQName());
618 if (m_mode == ResolvingStyle)
619 parentElement->setChildrenAffectedByForwardPositionalRules();
621 if (selector->matchNth(count))
625 case CSSSelector::PseudoClassNthLastChild:
626 if (!selector->parseNth())
628 if (Element* parentElement = element->parentElement()) {
629 if (m_mode == ResolvingStyle)
630 parentElement->setChildrenAffectedByBackwardPositionalRules();
631 if (!parentElement->isFinishedParsingChildren())
633 int count = 1 + countElementsAfter(element);
634 if (selector->matchNth(count))
638 case CSSSelector::PseudoClassNthLastOfType:
639 if (!selector->parseNth())
641 if (Element* parentElement = element->parentElement()) {
642 if (m_mode == ResolvingStyle)
643 parentElement->setChildrenAffectedByBackwardPositionalRules();
644 if (!parentElement->isFinishedParsingChildren())
647 int count = 1 + countElementsOfTypeAfter(element, element->tagQName());
648 if (selector->matchNth(count))
652 case CSSSelector::PseudoClassTarget:
653 if (element == element->document().cssTarget())
656 case CSSSelector::PseudoClassAny:
658 SelectorCheckingContext subContext(context);
659 subContext.inFunctionalPseudoClass = true;
660 PseudoId ignoreDynamicPseudo = NOPSEUDO;
661 for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(subContext.selector)) {
662 subContext.firstSelectorOfTheFragment = subContext.selector;
663 if (matchRecursively(subContext, ignoreDynamicPseudo) == SelectorMatches)
668 case CSSSelector::PseudoClassAutofill:
669 return isAutofilled(element);
670 case CSSSelector::PseudoClassAnyLink:
671 case CSSSelector::PseudoClassLink:
672 // :visited and :link matches are separated later when applying the style. Here both classes match all links...
673 return element->isLink();
674 case CSSSelector::PseudoClassVisited:
675 // ...except if :visited matching is disabled for ancestor/sibling matching.
676 return element->isLink() && context.visitedMatchType == VisitedMatchEnabled;
677 case CSSSelector::PseudoClassDrag:
678 if (m_mode == ResolvingStyle) {
679 if (context.elementStyle)
680 context.elementStyle->setAffectedByDrag();
682 element->setChildrenAffectedByDrag();
684 if (element->renderer() && element->renderer()->isDragging())
687 case CSSSelector::PseudoClassFocus:
688 return matchesFocusPseudoClass(element);
689 case CSSSelector::PseudoClassHover:
690 if (m_strictParsing || element->isLink() || canMatchHoverOrActiveInQuirksMode(context)) {
691 if (m_mode == ResolvingStyle) {
692 if (context.elementStyle)
693 context.elementStyle->setAffectedByHover();
695 element->setChildrenAffectedByHover();
697 if (element->hovered() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassHover))
701 case CSSSelector::PseudoClassActive:
702 if (m_strictParsing || element->isLink() || canMatchHoverOrActiveInQuirksMode(context)) {
703 if (m_mode == ResolvingStyle) {
704 if (context.elementStyle)
705 context.elementStyle->setAffectedByActive();
707 element->setChildrenAffectedByActive();
709 if (element->active() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassActive))
713 case CSSSelector::PseudoClassEnabled:
714 return isEnabled(element);
715 case CSSSelector::PseudoClassFullPageMedia:
716 return element->document().isMediaDocument();
717 case CSSSelector::PseudoClassDefault:
718 return isDefaultButtonForForm(element);
719 case CSSSelector::PseudoClassDisabled:
720 return isDisabled(element);
721 case CSSSelector::PseudoClassReadOnly:
722 return matchesReadOnlyPseudoClass(element);
723 case CSSSelector::PseudoClassReadWrite:
724 return matchesReadWritePseudoClass(element);
725 case CSSSelector::PseudoClassOptional:
726 return isOptionalFormControl(element);
727 case CSSSelector::PseudoClassRequired:
728 return isRequiredFormControl(element);
729 case CSSSelector::PseudoClassValid:
730 return isValid(element);
731 case CSSSelector::PseudoClassInvalid:
732 return isInvalid(element);
733 case CSSSelector::PseudoClassChecked:
734 return isChecked(element);
735 case CSSSelector::PseudoClassIndeterminate:
736 return shouldAppearIndeterminate(element);
737 case CSSSelector::PseudoClassRoot:
738 if (element == element->document().documentElement())
741 case CSSSelector::PseudoClassLang:
743 const AtomicString& argument = selector->argument();
744 if (argument.isNull())
746 return matchesLangPseudoClass(element, argument.impl());
748 #if ENABLE(FULLSCREEN_API)
749 case CSSSelector::PseudoClassFullScreen:
750 return matchesFullScreenPseudoClass(element);
751 case CSSSelector::PseudoClassAnimatingFullScreenTransition:
752 if (element != element->document().webkitCurrentFullScreenElement())
754 return element->document().isAnimatingFullScreen();
755 case CSSSelector::PseudoClassFullScreenAncestor:
756 return element->containsFullScreenElement();
757 case CSSSelector::PseudoClassFullScreenDocument:
758 // While a Document is in the fullscreen state, the 'full-screen-document' pseudoclass applies
759 // to all elements of that Document.
760 if (!element->document().webkitIsFullScreen())
764 case CSSSelector::PseudoClassInRange:
765 return isInRange(element);
766 case CSSSelector::PseudoClassOutOfRange:
767 return isOutOfRange(element);
768 #if ENABLE(VIDEO_TRACK)
769 case CSSSelector::PseudoClassFuture:
770 return matchesFutureCuePseudoClass(element);
771 case CSSSelector::PseudoClassPast:
772 return matchesPastCuePseudoClass(element);
775 case CSSSelector::PseudoClassScope:
777 const Node* contextualReferenceNode = !context.scope ? element->document().documentElement() : context.scope;
778 if (element == contextualReferenceNode)
783 case CSSSelector::PseudoClassHorizontal:
784 case CSSSelector::PseudoClassVertical:
785 case CSSSelector::PseudoClassDecrement:
786 case CSSSelector::PseudoClassIncrement:
787 case CSSSelector::PseudoClassStart:
788 case CSSSelector::PseudoClassEnd:
789 case CSSSelector::PseudoClassDoubleButton:
790 case CSSSelector::PseudoClassSingleButton:
791 case CSSSelector::PseudoClassNoButton:
792 case CSSSelector::PseudoClassCornerPresent:
795 case CSSSelector::PseudoClassUnknown:
797 ASSERT_NOT_REACHED();
802 #if ENABLE(VIDEO_TRACK)
803 else if (selector->m_match == CSSSelector::PseudoElement && selector->pseudoElementType() == CSSSelector::PseudoElementCue) {
804 SelectorCheckingContext subContext(context);
806 PseudoId ignoreDynamicPseudo = NOPSEUDO;
807 const CSSSelector* const & selector = context.selector;
808 for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(subContext.selector)) {
809 subContext.firstSelectorOfTheFragment = subContext.selector;
810 subContext.inFunctionalPseudoClass = true;
811 if (matchRecursively(subContext, ignoreDynamicPseudo) == SelectorMatches)
817 // ### add the rest of the checks...
821 bool SelectorChecker::checkScrollbarPseudoClass(const SelectorCheckingContext& context, Document* document, const CSSSelector* selector) const
823 ASSERT(selector->m_match == CSSSelector::PseudoClass);
825 RenderScrollbar* scrollbar = context.scrollbar;
826 ScrollbarPart part = context.scrollbarPart;
828 // FIXME: This is a temporary hack for resizers and scrollbar corners. Eventually :window-inactive should become a real
829 // pseudo class and just apply to everything.
830 if (selector->pseudoClassType() == CSSSelector::PseudoClassWindowInactive)
831 return !document->page()->focusController().isActive();
836 switch (selector->pseudoClassType()) {
837 case CSSSelector::PseudoClassEnabled:
838 return scrollbar->enabled();
839 case CSSSelector::PseudoClassDisabled:
840 return !scrollbar->enabled();
841 case CSSSelector::PseudoClassHover:
843 ScrollbarPart hoveredPart = scrollbar->hoveredPart();
844 if (part == ScrollbarBGPart)
845 return hoveredPart != NoPart;
846 if (part == TrackBGPart)
847 return hoveredPart == BackTrackPart || hoveredPart == ForwardTrackPart || hoveredPart == ThumbPart;
848 return part == hoveredPart;
850 case CSSSelector::PseudoClassActive:
852 ScrollbarPart pressedPart = scrollbar->pressedPart();
853 if (part == ScrollbarBGPart)
854 return pressedPart != NoPart;
855 if (part == TrackBGPart)
856 return pressedPart == BackTrackPart || pressedPart == ForwardTrackPart || pressedPart == ThumbPart;
857 return part == pressedPart;
859 case CSSSelector::PseudoClassHorizontal:
860 return scrollbar->orientation() == HorizontalScrollbar;
861 case CSSSelector::PseudoClassVertical:
862 return scrollbar->orientation() == VerticalScrollbar;
863 case CSSSelector::PseudoClassDecrement:
864 return part == BackButtonStartPart || part == BackButtonEndPart || part == BackTrackPart;
865 case CSSSelector::PseudoClassIncrement:
866 return part == ForwardButtonStartPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
867 case CSSSelector::PseudoClassStart:
868 return part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart;
869 case CSSSelector::PseudoClassEnd:
870 return part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
871 case CSSSelector::PseudoClassDoubleButton:
873 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
874 if (part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart)
875 return buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth;
876 if (part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart)
877 return buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth;
880 case CSSSelector::PseudoClassSingleButton:
882 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
883 if (part == BackButtonStartPart || part == ForwardButtonEndPart || part == BackTrackPart || part == ForwardTrackPart)
884 return buttonsPlacement == ScrollbarButtonsSingle;
887 case CSSSelector::PseudoClassNoButton:
889 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
890 if (part == BackTrackPart)
891 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleEnd;
892 if (part == ForwardTrackPart)
893 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleStart;
896 case CSSSelector::PseudoClassCornerPresent:
897 return scrollbar->scrollableArea()->isScrollCornerVisible();
903 unsigned SelectorChecker::determineLinkMatchType(const CSSSelector* selector)
905 unsigned linkMatchType = MatchAll;
907 // Statically determine if this selector will match a link in visited, unvisited or any state, or never.
908 // :visited never matches other elements than the innermost link element.
909 for (; selector; selector = selector->tagHistory()) {
910 if (selector->m_match == CSSSelector::PseudoClass) {
911 switch (selector->pseudoClassType()) {
912 case CSSSelector::PseudoClassNot:
914 // :not(:visited) is equivalent to :link. Parser enforces that :not can't nest.
915 const CSSSelectorList* selectorList = selector->selectorList();
919 for (const CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = subSelector->tagHistory()) {
920 if (subSelector->m_match == CSSSelector::PseudoClass) {
921 CSSSelector::PseudoClassType subType = subSelector->pseudoClassType();
922 if (subType == CSSSelector::PseudoClassVisited)
923 linkMatchType &= ~SelectorChecker::MatchVisited;
924 else if (subType == CSSSelector::PseudoClassLink)
925 linkMatchType &= ~SelectorChecker::MatchLink;
930 case CSSSelector::PseudoClassLink:
931 linkMatchType &= ~SelectorChecker::MatchVisited;
933 case CSSSelector::PseudoClassVisited:
934 linkMatchType &= ~SelectorChecker::MatchLink;
937 // We don't support :link and :visited inside :-webkit-any.
941 CSSSelector::Relation relation = selector->relation();
942 if (relation == CSSSelector::SubSelector)
944 if (relation != CSSSelector::Descendant && relation != CSSSelector::Child)
945 return linkMatchType;
946 if (linkMatchType != MatchAll)
947 return linkMatchType;
949 return linkMatchType;
952 static bool isFrameFocused(const Element* element)
954 return element->document().frame() && element->document().frame()->selection().isFocusedAndActive();
957 bool SelectorChecker::matchesFocusPseudoClass(const Element* element)
959 if (InspectorInstrumentation::forcePseudoState(const_cast<Element*>(element), CSSSelector::PseudoClassFocus))
961 return element->focused() && isFrameFocused(element);