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.
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 "core/css/SelectorChecker.h"
31 #include "core/HTMLNames.h"
32 #include "core/css/CSSSelectorList.h"
33 #include "core/css/SiblingTraversalStrategies.h"
34 #include "core/dom/Document.h"
35 #include "core/dom/ElementTraversal.h"
36 #include "core/dom/FullscreenElementStack.h"
37 #include "core/dom/NodeRenderStyle.h"
38 #include "core/dom/Text.h"
39 #include "core/dom/shadow/InsertionPoint.h"
40 #include "core/dom/shadow/ShadowRoot.h"
41 #include "core/editing/FrameSelection.h"
42 #include "core/frame/LocalFrame.h"
43 #include "core/html/HTMLDocument.h"
44 #include "core/html/HTMLFrameElementBase.h"
45 #include "core/html/HTMLInputElement.h"
46 #include "core/html/HTMLOptionElement.h"
47 #include "core/html/parser/HTMLParserIdioms.h"
48 #include "core/html/track/vtt/VTTElement.h"
49 #include "core/inspector/InspectorInstrumentation.h"
50 #include "core/page/FocusController.h"
51 #include "core/rendering/RenderObject.h"
52 #include "core/rendering/RenderScrollbar.h"
53 #include "core/rendering/style/RenderStyle.h"
54 #include "platform/scroll/ScrollableArea.h"
55 #include "platform/scroll/ScrollbarTheme.h"
59 using namespace HTMLNames;
61 SelectorChecker::SelectorChecker(Document& document, Mode mode)
62 : m_strictParsing(!document.inQuirksMode())
63 , m_documentIsHTML(document.isHTMLDocument())
68 static bool matchesCustomPseudoElement(const Element* element, const CSSSelector& selector)
70 ShadowRoot* root = element->containingShadowRoot();
71 if (!root || root->type() != ShadowRoot::UserAgentShadowRoot)
74 if (element->shadowPseudoId() != selector.value())
80 Element* SelectorChecker::parentElement(const SelectorCheckingContext& context, bool allowToCrossBoundary) const
82 // CrossesBoundary means we don't care any context.scope. So we can walk up from a shadow root to its shadow host.
83 if (allowToCrossBoundary)
84 return context.element->parentOrShadowHostElement();
86 // If context.scope is a shadow root, we should walk up to its shadow host.
87 if ((context.behaviorAtBoundary & SelectorChecker::ScopeIsShadowRoot) && context.scope == context.element->containingShadowRoot())
88 return context.element->parentOrShadowHostElement();
90 if ((context.behaviorAtBoundary & SelectorChecker::BoundaryBehaviorMask) != SelectorChecker::StaysWithinTreeScope)
91 return context.element->parentElement();
93 // If context.scope is some element in some shadow tree and querySelector initialized the context,
94 // e.g. shadowRoot.querySelector(':host *'),
95 // (a) context.element has the same treescope as context.scope, need to walk up to its shadow host.
96 // (b) Otherwise, should not walk up from a shadow root to a shadow host.
97 if (context.scope && context.scope->treeScope() == context.element->treeScope())
98 return context.element->parentOrShadowHostElement();
100 return context.element->parentElement();
103 bool SelectorChecker::scopeContainsLastMatchedElement(const SelectorCheckingContext& context) const
105 if (!(context.behaviorAtBoundary & SelectorChecker::ScopeContainsLastMatchedElement))
108 ASSERT(context.scope);
109 // If behaviorAtBoundary is not ScopeIsShadowRoot, we can use "contains".
110 if (!(context.behaviorAtBoundary & SelectorChecker::ScopeIsShadowRoot))
111 return context.scope->contains(context.element);
113 // If a given element is scope, i.e. shadow host, matches.
114 if (context.element == context.scope->shadowHost() && (!context.previousElement || context.previousElement->isInDescendantTreeOf(context.element)))
117 ShadowRoot* root = context.element->containingShadowRoot();
121 // If a host of the containing shadow root is scope, matches.
122 return root == context.scope;
125 static inline bool nextSelectorExceedsScope(const SelectorChecker::SelectorCheckingContext& context)
127 if (context.scope && context.scope->isInShadowTree())
128 return context.element == context.scope->shadowHost();
133 // Recursive check of selectors and combinators
134 // It can return 4 different values:
135 // * SelectorMatches - the selector matches the element e
136 // * SelectorFailsLocally - the selector fails for the element e
137 // * SelectorFailsAllSiblings - the selector fails for e and any sibling of e
138 // * SelectorFailsCompletely - the selector fails for e and any sibling or ancestor of e
139 template<typename SiblingTraversalStrategy>
140 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
142 // first selector has to match
143 unsigned specificity = 0;
144 if (!checkOne(context, siblingTraversalStrategy, &specificity))
145 return SelectorFailsLocally;
147 if (context.selector->match() == CSSSelector::PseudoElement) {
148 if (context.selector->isCustomPseudoElement()) {
149 if (!matchesCustomPseudoElement(context.element, *context.selector))
150 return SelectorFailsLocally;
151 } else if (context.selector->isContentPseudoElement()) {
152 if (!context.element->isInShadowTree() || !context.element->isInsertionPoint())
153 return SelectorFailsLocally;
154 } else if (context.selector->isShadowPseudoElement()) {
155 if (!context.element->isInShadowTree() || !context.previousElement)
156 return SelectorFailsCompletely;
158 if ((!context.elementStyle && m_mode == ResolvingStyle) || m_mode == QueryingRules)
159 return SelectorFailsLocally;
161 PseudoId pseudoId = CSSSelector::pseudoId(context.selector->pseudoType());
162 if (pseudoId == FIRST_LETTER)
163 context.element->document().styleEngine()->setUsesFirstLetterRules(true);
164 if (pseudoId != NOPSEUDO && m_mode != SharingRules && result)
165 result->dynamicPseudo = pseudoId;
169 // Prepare next selector
170 const CSSSelector* historySelector = context.selector->tagHistory();
171 if (!historySelector) {
172 if (scopeContainsLastMatchedElement(context)) {
174 result->specificity += specificity;
175 return SelectorMatches;
177 return SelectorFailsLocally;
181 if (context.selector->relation() != CSSSelector::SubSelector) {
182 // Abort if the next selector would exceed the scope.
183 if (nextSelectorExceedsScope(context))
184 return SelectorFailsCompletely;
186 // Bail-out if this selector is irrelevant for the pseudoId
187 if (context.pseudoId != NOPSEUDO && (!result || context.pseudoId != result->dynamicPseudo))
188 return SelectorFailsCompletely;
191 TemporaryChange<PseudoId> dynamicPseudoScope(result->dynamicPseudo, NOPSEUDO);
192 match = matchForRelation(context, siblingTraversalStrategy, result);
194 return matchForRelation(context, siblingTraversalStrategy, 0);
197 match = matchForSubSelector(context, siblingTraversalStrategy, result);
199 if (match != SelectorMatches || !result)
202 result->specificity += specificity;
203 return SelectorMatches;
206 static inline SelectorChecker::SelectorCheckingContext prepareNextContextForRelation(const SelectorChecker::SelectorCheckingContext& context)
208 SelectorChecker::SelectorCheckingContext nextContext(context);
209 ASSERT(context.selector->tagHistory());
210 nextContext.selector = context.selector->tagHistory();
214 static inline bool isAuthorShadowRoot(const Node* node)
216 return node && node->isShadowRoot() && toShadowRoot(node)->type() == ShadowRoot::AuthorShadowRoot;
219 template<typename SiblingTraversalStrategy>
220 SelectorChecker::Match SelectorChecker::matchForSubSelector(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
222 SelectorCheckingContext nextContext = prepareNextContextForRelation(context);
224 PseudoId dynamicPseudo = result ? result->dynamicPseudo : NOPSEUDO;
225 // a selector is invalid if something follows a pseudo-element
226 // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else)
227 // to follow the pseudo elements.
228 nextContext.hasScrollbarPseudo = dynamicPseudo != NOPSEUDO && (context.scrollbar || dynamicPseudo == SCROLLBAR_CORNER || dynamicPseudo == RESIZER);
229 nextContext.hasSelectionPseudo = dynamicPseudo == SELECTION;
230 if ((context.elementStyle || m_mode == CollectingCSSRules || m_mode == CollectingStyleRules || m_mode == QueryingRules) && dynamicPseudo != NOPSEUDO
231 && !nextContext.hasSelectionPseudo
232 && !(nextContext.hasScrollbarPseudo && nextContext.selector->match() == CSSSelector::PseudoClass))
233 return SelectorFailsCompletely;
235 nextContext.isSubSelector = true;
236 return match(nextContext, siblingTraversalStrategy, result);
239 static bool selectorMatchesShadowRoot(const CSSSelector* selector)
241 return selector && selector->isShadowPseudoElement();
244 template<typename SiblingTraversalStrategy>
245 SelectorChecker::Match SelectorChecker::matchForPseudoShadow(const ContainerNode* node, const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
247 if (!isAuthorShadowRoot(node))
248 return SelectorFailsCompletely;
249 return match(context, siblingTraversalStrategy, result);
252 template<typename SiblingTraversalStrategy>
253 SelectorChecker::Match SelectorChecker::matchForRelation(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
255 SelectorCheckingContext nextContext = prepareNextContextForRelation(context);
256 nextContext.previousElement = context.element;
258 CSSSelector::Relation relation = context.selector->relation();
260 // Disable :visited matching when we see the first link or try to match anything else than an ancestors.
261 if (!context.isSubSelector && (context.element->isLink() || (relation != CSSSelector::Descendant && relation != CSSSelector::Child)))
262 nextContext.visitedMatchType = VisitedMatchDisabled;
264 nextContext.pseudoId = NOPSEUDO;
267 case CSSSelector::Descendant:
268 if (context.selector->relationIsAffectedByPseudoContent()) {
269 for (Element* element = context.element; element; element = element->parentElement()) {
270 if (matchForShadowDistributed(element, siblingTraversalStrategy, nextContext, result) == SelectorMatches)
271 return SelectorMatches;
273 return SelectorFailsCompletely;
275 nextContext.isSubSelector = false;
276 nextContext.elementStyle = 0;
278 if (selectorMatchesShadowRoot(nextContext.selector))
279 return matchForPseudoShadow(context.element->containingShadowRoot(), nextContext, siblingTraversalStrategy, result);
281 for (nextContext.element = parentElement(context); nextContext.element; nextContext.element = parentElement(nextContext)) {
282 Match match = this->match(nextContext, siblingTraversalStrategy, result);
283 if (match == SelectorMatches || match == SelectorFailsCompletely)
285 if (nextSelectorExceedsScope(nextContext))
286 return SelectorFailsCompletely;
288 return SelectorFailsCompletely;
289 case CSSSelector::Child:
291 if (context.selector->relationIsAffectedByPseudoContent())
292 return matchForShadowDistributed(context.element, siblingTraversalStrategy, nextContext, result);
294 nextContext.isSubSelector = false;
295 nextContext.elementStyle = 0;
297 if (selectorMatchesShadowRoot(nextContext.selector))
298 return matchForPseudoShadow(context.element->parentNode(), nextContext, siblingTraversalStrategy, result);
300 nextContext.element = parentElement(context);
301 if (!nextContext.element)
302 return SelectorFailsCompletely;
303 return match(nextContext, siblingTraversalStrategy, result);
305 case CSSSelector::DirectAdjacent:
306 // Shadow roots can't have sibling elements
307 if (selectorMatchesShadowRoot(nextContext.selector))
308 return SelectorFailsCompletely;
310 if (m_mode == ResolvingStyle) {
311 if (ContainerNode* parent = context.element->parentElementOrShadowRoot())
312 parent->setChildrenAffectedByDirectAdjacentRules();
314 nextContext.element = ElementTraversal::previousSibling(*context.element);
315 if (!nextContext.element)
316 return SelectorFailsAllSiblings;
317 nextContext.isSubSelector = false;
318 nextContext.elementStyle = 0;
319 return match(nextContext, siblingTraversalStrategy, result);
321 case CSSSelector::IndirectAdjacent:
322 // Shadow roots can't have sibling elements
323 if (selectorMatchesShadowRoot(nextContext.selector))
324 return SelectorFailsCompletely;
326 if (m_mode == ResolvingStyle) {
327 if (ContainerNode* parent = context.element->parentElementOrShadowRoot())
328 parent->setChildrenAffectedByIndirectAdjacentRules();
330 nextContext.element = ElementTraversal::previousSibling(*context.element);
331 nextContext.isSubSelector = false;
332 nextContext.elementStyle = 0;
333 for (; nextContext.element; nextContext.element = ElementTraversal::previousSibling(*nextContext.element)) {
334 Match match = this->match(nextContext, siblingTraversalStrategy, result);
335 if (match == SelectorMatches || match == SelectorFailsAllSiblings || match == SelectorFailsCompletely)
338 return SelectorFailsAllSiblings;
340 case CSSSelector::ShadowPseudo:
342 // 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.
343 if (context.scope && context.scope->shadowHost() && context.scope->shadowHost()->treeScope() == context.element->treeScope() && (context.behaviorAtBoundary & BoundaryBehaviorMask) != StaysWithinTreeScope)
344 return SelectorFailsCompletely;
346 Element* shadowHost = context.element->shadowHost();
348 return SelectorFailsCompletely;
349 nextContext.element = shadowHost;
350 nextContext.isSubSelector = false;
351 nextContext.elementStyle = 0;
352 return this->match(nextContext, siblingTraversalStrategy, result);
355 case CSSSelector::ShadowDeep:
357 nextContext.isSubSelector = false;
358 nextContext.elementStyle = 0;
359 for (nextContext.element = parentElement(context, true); nextContext.element; nextContext.element = parentElement(nextContext, true)) {
360 Match match = this->match(nextContext, siblingTraversalStrategy, result);
361 if (match == SelectorMatches || match == SelectorFailsCompletely)
363 if (nextSelectorExceedsScope(nextContext))
364 return SelectorFailsCompletely;
366 return SelectorFailsCompletely;
369 case CSSSelector::SubSelector:
370 ASSERT_NOT_REACHED();
373 ASSERT_NOT_REACHED();
374 return SelectorFailsCompletely;
377 template<typename SiblingTraversalStrategy>
378 SelectorChecker::Match SelectorChecker::matchForShadowDistributed(const Element* element, const SiblingTraversalStrategy& siblingTraversalStrategy, SelectorCheckingContext& nextContext, MatchResult* result) const
381 WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints;
383 const ContainerNode* scope = nextContext.scope;
384 BehaviorAtBoundary behaviorAtBoundary = nextContext.behaviorAtBoundary;
386 collectDestinationInsertionPoints(*element, insertionPoints);
387 for (size_t i = 0; i < insertionPoints.size(); ++i) {
388 nextContext.element = insertionPoints[i];
390 // If a given scope is a shadow host of an insertion point but behaviorAtBoundary doesn't have ScopeIsShadowRoot,
391 // we need to update behaviorAtBoundary to make selectors like ":host > ::content" work correctly.
392 if (m_mode == SharingRules) {
393 nextContext.behaviorAtBoundary = static_cast<BehaviorAtBoundary>(behaviorAtBoundary | ScopeIsShadowRoot);
394 nextContext.scope = insertionPoints[i]->containingShadowRoot();
395 } else if (scope == insertionPoints[i]->containingShadowRoot() && !(behaviorAtBoundary & ScopeIsShadowRoot)) {
396 nextContext.behaviorAtBoundary = static_cast<BehaviorAtBoundary>(behaviorAtBoundary | ScopeIsShadowRoot);
398 nextContext.behaviorAtBoundary = behaviorAtBoundary;
401 nextContext.isSubSelector = false;
402 nextContext.elementStyle = 0;
403 if (match(nextContext, siblingTraversalStrategy, result) == SelectorMatches)
404 return SelectorMatches;
406 return SelectorFailsLocally;
409 template<typename CharType>
410 static inline bool containsHTMLSpaceTemplate(const CharType* string, unsigned length)
412 for (unsigned i = 0; i < length; ++i)
413 if (isHTMLSpace<CharType>(string[i]))
418 static inline bool containsHTMLSpace(const AtomicString& string)
420 if (LIKELY(string.is8Bit()))
421 return containsHTMLSpaceTemplate<LChar>(string.characters8(), string.length());
422 return containsHTMLSpaceTemplate<UChar>(string.characters16(), string.length());
425 static bool attributeValueMatches(const Attribute& attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
427 const AtomicString& value = attributeItem.value();
432 case CSSSelector::Exact:
433 if (caseSensitive ? selectorValue != value : !equalIgnoringCase(selectorValue, value))
436 case CSSSelector::List:
438 // Ignore empty selectors or selectors containing HTML spaces
439 if (selectorValue.isEmpty() || containsHTMLSpace(selectorValue))
442 unsigned startSearchAt = 0;
444 size_t foundPos = value.find(selectorValue, startSearchAt, caseSensitive);
445 if (foundPos == kNotFound)
447 if (!foundPos || isHTMLSpace<UChar>(value[foundPos - 1])) {
448 unsigned endStr = foundPos + selectorValue.length();
449 if (endStr == value.length() || isHTMLSpace<UChar>(value[endStr]))
450 break; // We found a match.
453 // No match. Keep looking.
454 startSearchAt = foundPos + 1;
458 case CSSSelector::Contain:
459 if (!value.contains(selectorValue, caseSensitive) || selectorValue.isEmpty())
462 case CSSSelector::Begin:
463 if (!value.startsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
466 case CSSSelector::End:
467 if (!value.endsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
470 case CSSSelector::Hyphen:
471 if (value.length() < selectorValue.length())
473 if (!value.startsWith(selectorValue, caseSensitive))
475 // It they start the same, check for exact match or following '-':
476 if (value.length() != selectorValue.length() && value[selectorValue.length()] != '-')
479 case CSSSelector::PseudoClass:
480 case CSSSelector::PseudoElement:
488 static bool anyAttributeMatches(Element& element, CSSSelector::Match match, const CSSSelector& selector)
490 const QualifiedName& selectorAttr = selector.attribute();
491 ASSERT(selectorAttr.localName() != starAtom); // Should not be possible from the CSS grammar.
493 // Synchronize the attribute in case it is lazy-computed.
494 // Currently all lazy properties have a null namespace, so only pass localName().
495 element.synchronizeAttribute(selectorAttr.localName());
497 if (!element.hasAttributesWithoutUpdate())
500 const AtomicString& selectorValue = selector.value();
502 AttributeCollection attributes = element.attributes();
503 AttributeCollection::const_iterator end = attributes.end();
504 for (AttributeCollection::const_iterator it = attributes.begin(); it != end; ++it) {
505 const Attribute& attributeItem = *it;
507 if (!attributeItem.matches(selectorAttr))
510 if (attributeValueMatches(attributeItem, match, selectorValue, true))
513 // Case sensitivity for attribute matching is looser than hasAttribute or
514 // Element::shouldIgnoreAttributeCase() for now. Unclear if that's correct.
515 bool caseSensitive = !element.document().isHTMLDocument() || HTMLDocument::isCaseSensitiveAttribute(selectorAttr);
517 // If case-insensitive, re-check, and count if result differs.
518 // See http://code.google.com/p/chromium/issues/detail?id=327060
519 if (!caseSensitive && attributeValueMatches(attributeItem, match, selectorValue, false)) {
520 UseCounter::count(element.document(), UseCounter::CaseInsensitiveAttrSelectorMatch);
528 template<typename SiblingTraversalStrategy>
529 bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, unsigned* specificity) const
531 ASSERT(context.element);
532 Element& element = *context.element;
533 ASSERT(context.selector);
534 const CSSSelector& selector = *context.selector;
536 bool elementIsHostInItsShadowTree = isHostInItsShadowTree(element, context.behaviorAtBoundary, context.scope);
538 // Only :host and :ancestor should match the host: http://drafts.csswg.org/css-scoping/#host-element
539 if (elementIsHostInItsShadowTree && !selector.isHostPseudoClass()
540 && !(context.behaviorAtBoundary & TreatShadowHostAsNormalScope))
543 if (selector.match() == CSSSelector::Tag)
544 return SelectorChecker::tagMatches(element, selector.tagQName());
546 if (selector.match() == CSSSelector::Class)
547 return element.hasClass() && element.classNames().contains(selector.value());
549 if (selector.match() == CSSSelector::Id)
550 return element.hasID() && element.idForStyleResolution() == selector.value();
552 if (selector.isAttributeSelector())
553 return anyAttributeMatches(element, selector.match(), selector);
555 if (selector.match() == CSSSelector::PseudoClass) {
556 // Handle :not up front.
557 if (selector.pseudoType() == CSSSelector::PseudoNot) {
558 SelectorCheckingContext subContext(context);
559 subContext.isSubSelector = true;
560 ASSERT(selector.selectorList());
561 for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = subContext.selector->tagHistory()) {
562 // :not cannot nest. I don't really know why this is a
563 // restriction in CSS3, but it is, so let's honor it.
564 // the parser enforces that this never occurs
565 ASSERT(subContext.selector->pseudoType() != CSSSelector::PseudoNot);
566 // We select between :visited and :link when applying. We don't know which one applied (or not) yet.
567 if (subContext.selector->pseudoType() == CSSSelector::PseudoVisited || (subContext.selector->pseudoType() == CSSSelector::PseudoLink && subContext.visitedMatchType == VisitedMatchEnabled))
569 // context.scope is not available if m_mode == SharingRules.
570 // We cannot determine whether :host or :scope matches a given element or not.
571 if (m_mode == SharingRules && (subContext.selector->isHostPseudoClass() || subContext.selector->pseudoType() == CSSSelector::PseudoScope))
573 if (!checkOne(subContext, DOMSiblingTraversalStrategy()))
576 } else if (context.hasScrollbarPseudo) {
577 // CSS scrollbars match a specific subset of pseudo classes, and they have specialized rules for each
578 // (since there are no elements involved).
579 return checkScrollbarPseudoClass(context, &element.document(), selector);
580 } else if (context.hasSelectionPseudo) {
581 if (selector.pseudoType() == CSSSelector::PseudoWindowInactive)
582 return !element.document().page()->focusController().isActive();
585 // Normal element pseudo class checking.
586 switch (selector.pseudoType()) {
588 case CSSSelector::PseudoNot:
589 break; // Already handled up above.
590 case CSSSelector::PseudoEmpty:
593 for (Node* n = element.firstChild(); n; n = n->nextSibling()) {
594 if (n->isElementNode()) {
598 if (n->isTextNode()) {
599 Text* textNode = toText(n);
600 if (!textNode->data().isEmpty()) {
606 if (m_mode == ResolvingStyle) {
607 element.setStyleAffectedByEmpty();
608 if (context.elementStyle)
609 context.elementStyle->setEmptyState(result);
610 else if (element.renderStyle() && (element.document().styleEngine()->usesSiblingRules() || element.renderStyle()->unique()))
611 element.renderStyle()->setEmptyState(result);
615 case CSSSelector::PseudoFirstChild:
616 // first-child matches the first child that is an element
617 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
618 bool result = siblingTraversalStrategy.isFirstChild(element);
619 if (m_mode == ResolvingStyle) {
620 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
621 parent->setChildrenAffectedByFirstChildRules();
622 if (result && childStyle)
623 childStyle->setFirstChildState();
628 case CSSSelector::PseudoFirstOfType:
629 // first-of-type matches the first element of its type
630 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
631 bool result = siblingTraversalStrategy.isFirstOfType(element, element.tagQName());
632 if (m_mode == ResolvingStyle)
633 parent->setChildrenAffectedByForwardPositionalRules();
637 case CSSSelector::PseudoLastChild:
638 // last-child matches the last child that is an element
639 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
640 bool result = parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
641 if (m_mode == ResolvingStyle) {
642 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
643 parent->setChildrenAffectedByLastChildRules();
644 if (result && childStyle)
645 childStyle->setLastChildState();
650 case CSSSelector::PseudoLastOfType:
651 // last-of-type matches the last element of its type
652 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
653 if (m_mode == ResolvingStyle)
654 parent->setChildrenAffectedByBackwardPositionalRules();
655 if (!parent->isFinishedParsingChildren())
657 return siblingTraversalStrategy.isLastOfType(element, element.tagQName());
660 case CSSSelector::PseudoOnlyChild:
661 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
662 bool firstChild = siblingTraversalStrategy.isFirstChild(element);
663 bool onlyChild = firstChild && parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
664 if (m_mode == ResolvingStyle) {
665 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
666 parent->setChildrenAffectedByFirstChildRules();
667 parent->setChildrenAffectedByLastChildRules();
668 if (firstChild && childStyle)
669 childStyle->setFirstChildState();
670 if (onlyChild && childStyle)
671 childStyle->setLastChildState();
676 case CSSSelector::PseudoOnlyOfType:
677 // FIXME: This selector is very slow.
678 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
679 if (m_mode == ResolvingStyle) {
680 parent->setChildrenAffectedByForwardPositionalRules();
681 parent->setChildrenAffectedByBackwardPositionalRules();
683 if (!parent->isFinishedParsingChildren())
685 return siblingTraversalStrategy.isFirstOfType(element, element.tagQName()) && siblingTraversalStrategy.isLastOfType(element, element.tagQName());
688 case CSSSelector::PseudoNthChild:
689 if (!selector.parseNth())
691 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
692 int count = 1 + siblingTraversalStrategy.countElementsBefore(element);
693 if (m_mode == ResolvingStyle) {
694 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
696 childStyle->setUnique();
697 parent->setChildrenAffectedByForwardPositionalRules();
700 if (selector.matchNth(count))
704 case CSSSelector::PseudoNthOfType:
705 if (!selector.parseNth())
707 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
708 int count = 1 + siblingTraversalStrategy.countElementsOfTypeBefore(element, element.tagQName());
709 if (m_mode == ResolvingStyle)
710 parent->setChildrenAffectedByForwardPositionalRules();
712 if (selector.matchNth(count))
716 case CSSSelector::PseudoNthLastChild:
717 if (!selector.parseNth())
719 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
720 if (m_mode == ResolvingStyle)
721 parent->setChildrenAffectedByBackwardPositionalRules();
722 if (!parent->isFinishedParsingChildren())
724 int count = 1 + siblingTraversalStrategy.countElementsAfter(element);
725 if (selector.matchNth(count))
729 case CSSSelector::PseudoNthLastOfType:
730 if (!selector.parseNth())
732 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
733 if (m_mode == ResolvingStyle)
734 parent->setChildrenAffectedByBackwardPositionalRules();
735 if (!parent->isFinishedParsingChildren())
738 int count = 1 + siblingTraversalStrategy.countElementsOfTypeAfter(element, element.tagQName());
739 if (selector.matchNth(count))
743 case CSSSelector::PseudoTarget:
744 if (element == element.document().cssTarget())
747 case CSSSelector::PseudoAny:
749 SelectorCheckingContext subContext(context);
750 subContext.isSubSelector = true;
751 ASSERT(selector.selectorList());
752 for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
753 if (match(subContext, siblingTraversalStrategy) == SelectorMatches)
758 case CSSSelector::PseudoAutofill:
759 if (!element.isFormControlElement())
761 return toHTMLFormControlElement(element).isAutofilled();
762 case CSSSelector::PseudoAnyLink:
763 case CSSSelector::PseudoLink:
764 // :visited and :link matches are separated later when applying the style. Here both classes match all links...
765 return element.isLink();
766 case CSSSelector::PseudoVisited:
767 // ...except if :visited matching is disabled for ancestor/sibling matching.
768 return element.isLink() && context.visitedMatchType == VisitedMatchEnabled;
769 case CSSSelector::PseudoDrag:
770 if (m_mode == ResolvingStyle) {
771 if (context.elementStyle)
772 context.elementStyle->setAffectedByDrag();
774 element.setChildrenOrSiblingsAffectedByDrag();
776 if (element.renderer() && element.renderer()->isDragging())
779 case CSSSelector::PseudoFocus:
780 if (m_mode == ResolvingStyle) {
781 if (context.elementStyle)
782 context.elementStyle->setAffectedByFocus();
784 element.setChildrenOrSiblingsAffectedByFocus();
786 return matchesFocusPseudoClass(element);
787 case CSSSelector::PseudoHover:
788 // If we're in quirks mode, then hover should never match anchors with no
789 // href and *:hover should not match anything. This is important for sites like wsj.com.
790 if (m_strictParsing || context.isSubSelector || element.isLink()) {
791 if (m_mode == ResolvingStyle) {
792 if (context.elementStyle)
793 context.elementStyle->setAffectedByHover();
795 element.setChildrenOrSiblingsAffectedByHover();
797 if (element.hovered() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoHover))
801 case CSSSelector::PseudoActive:
802 // If we're in quirks mode, then :active should never match anchors with no
803 // href and *:active should not match anything.
804 if (m_strictParsing || context.isSubSelector || element.isLink()) {
805 if (m_mode == ResolvingStyle) {
806 if (context.elementStyle)
807 context.elementStyle->setAffectedByActive();
809 element.setChildrenOrSiblingsAffectedByActive();
811 if (element.active() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoActive))
815 case CSSSelector::PseudoEnabled:
816 if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
817 return !element.isDisabledFormControl();
819 case CSSSelector::PseudoFullPageMedia:
820 return element.document().isMediaDocument();
822 case CSSSelector::PseudoDefault:
823 return element.isDefaultButtonForForm();
824 case CSSSelector::PseudoDisabled:
825 if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
826 return element.isDisabledFormControl();
828 case CSSSelector::PseudoReadOnly:
829 return element.matchesReadOnlyPseudoClass();
830 case CSSSelector::PseudoReadWrite:
831 return element.matchesReadWritePseudoClass();
832 case CSSSelector::PseudoOptional:
833 return element.isOptionalFormControl();
834 case CSSSelector::PseudoRequired:
835 return element.isRequiredFormControl();
836 case CSSSelector::PseudoValid:
837 element.document().setContainsValidityStyleRules();
838 return element.willValidate() && element.isValidFormControlElement();
839 case CSSSelector::PseudoInvalid:
840 element.document().setContainsValidityStyleRules();
841 return element.willValidate() && !element.isValidFormControlElement();
842 case CSSSelector::PseudoChecked:
844 if (isHTMLInputElement(element)) {
845 HTMLInputElement& inputElement = toHTMLInputElement(element);
846 // Even though WinIE allows checked and indeterminate to
847 // co-exist, the CSS selector spec says that you can't be
848 // both checked and indeterminate. We will behave like WinIE
849 // behind the scenes and just obey the CSS spec here in the
850 // test for matching the pseudo.
851 if (inputElement.shouldAppearChecked() && !inputElement.shouldAppearIndeterminate())
853 } else if (isHTMLOptionElement(element) && toHTMLOptionElement(element).selected())
857 case CSSSelector::PseudoIndeterminate:
858 return element.shouldAppearIndeterminate();
859 case CSSSelector::PseudoRoot:
860 if (element == element.document().documentElement())
863 case CSSSelector::PseudoLang:
866 if (element.isVTTElement())
867 value = toVTTElement(element).language();
869 value = element.computeInheritedLanguage();
870 const AtomicString& argument = selector.argument();
871 if (value.isEmpty() || !value.startsWith(argument, false))
873 if (value.length() != argument.length() && value[argument.length()] != '-')
877 case CSSSelector::PseudoFullScreen:
878 // While a Document is in the fullscreen state, and the document's current fullscreen
879 // element is an element in the document, the 'full-screen' pseudoclass applies to
880 // that element. Also, an <iframe>, <object> or <embed> element whose child browsing
881 // context's Document is in the fullscreen state has the 'full-screen' pseudoclass applied.
882 if (isHTMLFrameElementBase(element) && element.containsFullScreenElement())
884 if (FullscreenElementStack* fullscreen = FullscreenElementStack::fromIfExists(element.document())) {
885 if (!fullscreen->webkitIsFullScreen())
887 return element == fullscreen->webkitCurrentFullScreenElement();
890 case CSSSelector::PseudoFullScreenAncestor:
891 return element.containsFullScreenElement();
892 case CSSSelector::PseudoFullScreenDocument:
893 // While a Document is in the fullscreen state, the 'full-screen-document' pseudoclass applies
894 // to all elements of that Document.
895 if (!FullscreenElementStack::isFullScreen(element.document()))
898 case CSSSelector::PseudoInRange:
899 element.document().setContainsValidityStyleRules();
900 return element.isInRange();
901 case CSSSelector::PseudoOutOfRange:
902 element.document().setContainsValidityStyleRules();
903 return element.isOutOfRange();
904 case CSSSelector::PseudoFutureCue:
905 return (element.isVTTElement() && !toVTTElement(element).isPastNode());
906 case CSSSelector::PseudoPastCue:
907 return (element.isVTTElement() && toVTTElement(element).isPastNode());
909 case CSSSelector::PseudoScope:
911 if (m_mode == SharingRules)
913 const Node* contextualReferenceNode = !context.scope ? element.document().documentElement() : context.scope;
914 if (element == contextualReferenceNode)
919 case CSSSelector::PseudoUnresolved:
920 if (element.isUnresolvedCustomElement())
924 case CSSSelector::PseudoHost:
925 case CSSSelector::PseudoHostContext:
927 if (m_mode == SharingRules)
929 // :host only matches a shadow host when :host is in a shadow tree of the shadow host.
932 const ContainerNode* shadowHost = context.scope->shadowHost();
933 if (!shadowHost || shadowHost != element)
935 ASSERT(element.shadow());
937 // For empty parameter case, i.e. just :host or :host().
938 if (!selector.selectorList()) // Use *'s specificity. So just 0.
941 SelectorCheckingContext subContext(context);
942 subContext.isSubSelector = true;
944 bool matched = false;
945 unsigned maxSpecificity = 0;
947 // If one of simple selectors matches an element, returns SelectorMatches. Just "OR".
948 for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
949 subContext.behaviorAtBoundary = ScopeIsShadowHostInPseudoHostParameter;
950 subContext.scope = context.scope;
951 // Use NodeRenderingTraversal to traverse a composed ancestor list of a given element.
952 Element* nextElement = &element;
953 SelectorCheckingContext hostContext(subContext);
955 MatchResult subResult;
956 hostContext.element = nextElement;
957 if (match(hostContext, siblingTraversalStrategy, &subResult) == SelectorMatches) {
959 // Consider div:host(div:host(div:host(div:host...))).
960 maxSpecificity = std::max(maxSpecificity, hostContext.selector->specificity() + subResult.specificity);
963 hostContext.behaviorAtBoundary = DoesNotCrossBoundary;
964 hostContext.scope = 0;
966 if (selector.pseudoType() == CSSSelector::PseudoHost)
969 hostContext.elementStyle = 0;
970 nextElement = NodeRenderingTraversal::parentElement(nextElement);
971 } while (nextElement);
975 *specificity = maxSpecificity;
981 case CSSSelector::PseudoHorizontal:
982 case CSSSelector::PseudoVertical:
983 case CSSSelector::PseudoDecrement:
984 case CSSSelector::PseudoIncrement:
985 case CSSSelector::PseudoStart:
986 case CSSSelector::PseudoEnd:
987 case CSSSelector::PseudoDoubleButton:
988 case CSSSelector::PseudoSingleButton:
989 case CSSSelector::PseudoNoButton:
990 case CSSSelector::PseudoCornerPresent:
993 case CSSSelector::PseudoUnknown:
994 case CSSSelector::PseudoNotParsed:
996 ASSERT_NOT_REACHED();
1000 } else if (selector.match() == CSSSelector::PseudoElement && selector.pseudoType() == CSSSelector::PseudoCue) {
1001 SelectorCheckingContext subContext(context);
1002 subContext.isSubSelector = true;
1003 subContext.behaviorAtBoundary = StaysWithinTreeScope;
1005 const CSSSelector* contextSelector = context.selector;
1006 ASSERT(contextSelector);
1007 for (subContext.selector = contextSelector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
1008 if (match(subContext, siblingTraversalStrategy) == SelectorMatches)
1013 // ### add the rest of the checks...
1017 bool SelectorChecker::checkScrollbarPseudoClass(const SelectorCheckingContext& context, Document* document, const CSSSelector& selector) const
1019 RenderScrollbar* scrollbar = context.scrollbar;
1020 ScrollbarPart part = context.scrollbarPart;
1022 // FIXME: This is a temporary hack for resizers and scrollbar corners. Eventually :window-inactive should become a real
1023 // pseudo class and just apply to everything.
1024 if (selector.pseudoType() == CSSSelector::PseudoWindowInactive)
1025 return !document->page()->focusController().isActive();
1030 ASSERT(selector.match() == CSSSelector::PseudoClass);
1031 switch (selector.pseudoType()) {
1032 case CSSSelector::PseudoEnabled:
1033 return scrollbar->enabled();
1034 case CSSSelector::PseudoDisabled:
1035 return !scrollbar->enabled();
1036 case CSSSelector::PseudoHover:
1038 ScrollbarPart hoveredPart = scrollbar->hoveredPart();
1039 if (part == ScrollbarBGPart)
1040 return hoveredPart != NoPart;
1041 if (part == TrackBGPart)
1042 return hoveredPart == BackTrackPart || hoveredPart == ForwardTrackPart || hoveredPart == ThumbPart;
1043 return part == hoveredPart;
1045 case CSSSelector::PseudoActive:
1047 ScrollbarPart pressedPart = scrollbar->pressedPart();
1048 if (part == ScrollbarBGPart)
1049 return pressedPart != NoPart;
1050 if (part == TrackBGPart)
1051 return pressedPart == BackTrackPart || pressedPart == ForwardTrackPart || pressedPart == ThumbPart;
1052 return part == pressedPart;
1054 case CSSSelector::PseudoHorizontal:
1055 return scrollbar->orientation() == HorizontalScrollbar;
1056 case CSSSelector::PseudoVertical:
1057 return scrollbar->orientation() == VerticalScrollbar;
1058 case CSSSelector::PseudoDecrement:
1059 return part == BackButtonStartPart || part == BackButtonEndPart || part == BackTrackPart;
1060 case CSSSelector::PseudoIncrement:
1061 return part == ForwardButtonStartPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
1062 case CSSSelector::PseudoStart:
1063 return part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart;
1064 case CSSSelector::PseudoEnd:
1065 return part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
1066 case CSSSelector::PseudoDoubleButton:
1068 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1069 if (part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart)
1070 return buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth;
1071 if (part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart)
1072 return buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth;
1075 case CSSSelector::PseudoSingleButton:
1077 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1078 if (part == BackButtonStartPart || part == ForwardButtonEndPart || part == BackTrackPart || part == ForwardTrackPart)
1079 return buttonsPlacement == ScrollbarButtonsSingle;
1082 case CSSSelector::PseudoNoButton:
1084 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1085 if (part == BackTrackPart)
1086 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleEnd;
1087 if (part == ForwardTrackPart)
1088 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleStart;
1091 case CSSSelector::PseudoCornerPresent:
1092 return scrollbar->scrollableArea()->isScrollCornerVisible();
1098 unsigned SelectorChecker::determineLinkMatchType(const CSSSelector& selector)
1100 unsigned linkMatchType = MatchAll;
1102 // Statically determine if this selector will match a link in visited, unvisited or any state, or never.
1103 // :visited never matches other elements than the innermost link element.
1104 for (const CSSSelector* current = &selector; current; current = current->tagHistory()) {
1105 switch (current->pseudoType()) {
1106 case CSSSelector::PseudoNot:
1108 // :not(:visited) is equivalent to :link. Parser enforces that :not can't nest.
1109 ASSERT(current->selectorList());
1110 for (const CSSSelector* subSelector = current->selectorList()->first(); subSelector; subSelector = subSelector->tagHistory()) {
1111 CSSSelector::PseudoType subType = subSelector->pseudoType();
1112 if (subType == CSSSelector::PseudoVisited)
1113 linkMatchType &= ~SelectorChecker::MatchVisited;
1114 else if (subType == CSSSelector::PseudoLink)
1115 linkMatchType &= ~SelectorChecker::MatchLink;
1119 case CSSSelector::PseudoLink:
1120 linkMatchType &= ~SelectorChecker::MatchVisited;
1122 case CSSSelector::PseudoVisited:
1123 linkMatchType &= ~SelectorChecker::MatchLink;
1126 // We don't support :link and :visited inside :-webkit-any.
1129 CSSSelector::Relation relation = current->relation();
1130 if (relation == CSSSelector::SubSelector)
1132 if (relation != CSSSelector::Descendant && relation != CSSSelector::Child)
1133 return linkMatchType;
1134 if (linkMatchType != MatchAll)
1135 return linkMatchType;
1137 return linkMatchType;
1140 bool SelectorChecker::isFrameFocused(const Element& element)
1142 return element.document().frame() && element.document().frame()->selection().isFocusedAndActive();
1145 bool SelectorChecker::matchesFocusPseudoClass(const Element& element)
1147 if (InspectorInstrumentation::forcePseudoState(const_cast<Element*>(&element), CSSSelector::PseudoFocus))
1149 return element.focused() && isFrameFocused(element);
1153 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const DOMSiblingTraversalStrategy&, MatchResult*) const;
1156 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const ShadowDOMSiblingTraversalStrategy&, MatchResult*) const;