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 (context.scope->treeScope() == context.element->treeScope())
112 // Because Blink treats a shadow host's TreeScope as a separate one from its descendent shadow roots,
113 // if the last matched element is a shadow host, the condition above isn't met, even though it
115 return context.element == context.scope->shadowHost() && (!context.previousElement || context.previousElement->isInDescendantTreeOf(context.element));
118 static inline bool nextSelectorExceedsScope(const SelectorChecker::SelectorCheckingContext& context)
120 if (context.scope && context.scope->isInShadowTree())
121 return context.element == context.scope->shadowHost();
126 // Recursive check of selectors and combinators
127 // It can return 4 different values:
128 // * SelectorMatches - the selector matches the element e
129 // * SelectorFailsLocally - the selector fails for the element e
130 // * SelectorFailsAllSiblings - the selector fails for e and any sibling of e
131 // * SelectorFailsCompletely - the selector fails for e and any sibling or ancestor of e
132 template<typename SiblingTraversalStrategy>
133 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
135 // first selector has to match
136 unsigned specificity = 0;
137 if (!checkOne(context, siblingTraversalStrategy, &specificity))
138 return SelectorFailsLocally;
140 if (context.selector->match() == CSSSelector::PseudoElement) {
141 if (context.selector->isCustomPseudoElement()) {
142 if (!matchesCustomPseudoElement(context.element, *context.selector))
143 return SelectorFailsLocally;
144 } else if (context.selector->isContentPseudoElement()) {
145 if (!context.element->isInShadowTree() || !context.element->isInsertionPoint())
146 return SelectorFailsLocally;
147 } else if (context.selector->isShadowPseudoElement()) {
148 if (!context.element->isInShadowTree() || !context.previousElement)
149 return SelectorFailsCompletely;
151 if ((!context.elementStyle && m_mode == ResolvingStyle) || m_mode == QueryingRules)
152 return SelectorFailsLocally;
154 PseudoId pseudoId = CSSSelector::pseudoId(context.selector->pseudoType());
155 if (pseudoId == FIRST_LETTER)
156 context.element->document().styleEngine()->setUsesFirstLetterRules(true);
157 if (pseudoId != NOPSEUDO && m_mode != SharingRules && result)
158 result->dynamicPseudo = pseudoId;
162 // Prepare next selector
163 if (context.selector->isLastInTagHistory()) {
164 if (scopeContainsLastMatchedElement(context)) {
166 result->specificity += specificity;
167 return SelectorMatches;
169 return SelectorFailsLocally;
173 if (context.selector->relation() != CSSSelector::SubSelector) {
174 // Abort if the next selector would exceed the scope.
175 if (nextSelectorExceedsScope(context))
176 return SelectorFailsCompletely;
178 // Bail-out if this selector is irrelevant for the pseudoId
179 if (context.pseudoId != NOPSEUDO && (!result || context.pseudoId != result->dynamicPseudo))
180 return SelectorFailsCompletely;
183 TemporaryChange<PseudoId> dynamicPseudoScope(result->dynamicPseudo, NOPSEUDO);
184 match = matchForRelation(context, siblingTraversalStrategy, result);
186 return matchForRelation(context, siblingTraversalStrategy, 0);
189 match = matchForSubSelector(context, siblingTraversalStrategy, result);
191 if (match != SelectorMatches || !result)
194 result->specificity += specificity;
195 return SelectorMatches;
198 static inline SelectorChecker::SelectorCheckingContext prepareNextContextForRelation(const SelectorChecker::SelectorCheckingContext& context)
200 SelectorChecker::SelectorCheckingContext nextContext(context);
201 ASSERT(context.selector->tagHistory());
202 nextContext.selector = context.selector->tagHistory();
206 static inline bool isAuthorShadowRoot(const Node* node)
208 return node && node->isShadowRoot() && toShadowRoot(node)->type() == ShadowRoot::AuthorShadowRoot;
211 template<typename SiblingTraversalStrategy>
212 SelectorChecker::Match SelectorChecker::matchForSubSelector(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
214 SelectorCheckingContext nextContext = prepareNextContextForRelation(context);
216 PseudoId dynamicPseudo = result ? result->dynamicPseudo : NOPSEUDO;
217 // a selector is invalid if something follows a pseudo-element
218 // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else)
219 // to follow the pseudo elements.
220 nextContext.hasScrollbarPseudo = dynamicPseudo != NOPSEUDO && (context.scrollbar || dynamicPseudo == SCROLLBAR_CORNER || dynamicPseudo == RESIZER);
221 nextContext.hasSelectionPseudo = dynamicPseudo == SELECTION;
222 if ((context.elementStyle || m_mode == CollectingCSSRules || m_mode == CollectingStyleRules || m_mode == QueryingRules) && dynamicPseudo != NOPSEUDO
223 && !nextContext.hasSelectionPseudo
224 && !(nextContext.hasScrollbarPseudo && nextContext.selector->match() == CSSSelector::PseudoClass))
225 return SelectorFailsCompletely;
227 nextContext.isSubSelector = true;
228 return match(nextContext, siblingTraversalStrategy, result);
231 static bool selectorMatchesShadowRoot(const CSSSelector* selector)
233 return selector && selector->isShadowPseudoElement();
236 template<typename SiblingTraversalStrategy>
237 SelectorChecker::Match SelectorChecker::matchForPseudoShadow(const ContainerNode* node, const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
239 if (!isAuthorShadowRoot(node))
240 return SelectorFailsCompletely;
241 return match(context, siblingTraversalStrategy, result);
244 template<typename SiblingTraversalStrategy>
245 SelectorChecker::Match SelectorChecker::matchForRelation(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
247 SelectorCheckingContext nextContext = prepareNextContextForRelation(context);
248 nextContext.previousElement = context.element;
250 CSSSelector::Relation relation = context.selector->relation();
252 // Disable :visited matching when we see the first link or try to match anything else than an ancestors.
253 if (!context.isSubSelector && (context.element->isLink() || (relation != CSSSelector::Descendant && relation != CSSSelector::Child)))
254 nextContext.visitedMatchType = VisitedMatchDisabled;
256 nextContext.pseudoId = NOPSEUDO;
259 case CSSSelector::Descendant:
260 if (context.selector->relationIsAffectedByPseudoContent()) {
261 for (Element* element = context.element; element; element = element->parentElement()) {
262 if (matchForShadowDistributed(element, siblingTraversalStrategy, nextContext, result) == SelectorMatches)
263 return SelectorMatches;
265 return SelectorFailsCompletely;
267 nextContext.isSubSelector = false;
268 nextContext.elementStyle = 0;
270 if (selectorMatchesShadowRoot(nextContext.selector))
271 return matchForPseudoShadow(context.element->containingShadowRoot(), nextContext, siblingTraversalStrategy, result);
273 for (nextContext.element = parentElement(context); nextContext.element; nextContext.element = parentElement(nextContext)) {
274 Match match = this->match(nextContext, siblingTraversalStrategy, result);
275 if (match == SelectorMatches || match == SelectorFailsCompletely)
277 if (nextSelectorExceedsScope(nextContext))
278 return SelectorFailsCompletely;
280 return SelectorFailsCompletely;
281 case CSSSelector::Child:
283 if (context.selector->relationIsAffectedByPseudoContent())
284 return matchForShadowDistributed(context.element, siblingTraversalStrategy, nextContext, result);
286 nextContext.isSubSelector = false;
287 nextContext.elementStyle = 0;
289 if (selectorMatchesShadowRoot(nextContext.selector))
290 return matchForPseudoShadow(context.element->parentNode(), nextContext, siblingTraversalStrategy, result);
292 nextContext.element = parentElement(context);
293 if (!nextContext.element)
294 return SelectorFailsCompletely;
295 return match(nextContext, siblingTraversalStrategy, result);
297 case CSSSelector::DirectAdjacent:
298 // Shadow roots can't have sibling elements
299 if (selectorMatchesShadowRoot(nextContext.selector))
300 return SelectorFailsCompletely;
302 if (m_mode == ResolvingStyle) {
303 if (ContainerNode* parent = context.element->parentElementOrShadowRoot())
304 parent->setChildrenAffectedByDirectAdjacentRules();
306 nextContext.element = ElementTraversal::previousSibling(*context.element);
307 if (!nextContext.element)
308 return SelectorFailsAllSiblings;
309 nextContext.isSubSelector = false;
310 nextContext.elementStyle = 0;
311 return match(nextContext, siblingTraversalStrategy, result);
313 case CSSSelector::IndirectAdjacent:
314 // Shadow roots can't have sibling elements
315 if (selectorMatchesShadowRoot(nextContext.selector))
316 return SelectorFailsCompletely;
318 if (m_mode == ResolvingStyle) {
319 if (ContainerNode* parent = context.element->parentElementOrShadowRoot())
320 parent->setChildrenAffectedByIndirectAdjacentRules();
322 nextContext.element = ElementTraversal::previousSibling(*context.element);
323 nextContext.isSubSelector = false;
324 nextContext.elementStyle = 0;
325 for (; nextContext.element; nextContext.element = ElementTraversal::previousSibling(*nextContext.element)) {
326 Match match = this->match(nextContext, siblingTraversalStrategy, result);
327 if (match == SelectorMatches || match == SelectorFailsAllSiblings || match == SelectorFailsCompletely)
330 return SelectorFailsAllSiblings;
332 case CSSSelector::ShadowPseudo:
334 // 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.
335 if (context.scope && context.scope->shadowHost() && context.scope->shadowHost()->treeScope() == context.element->treeScope() && (context.behaviorAtBoundary & BoundaryBehaviorMask) != StaysWithinTreeScope)
336 return SelectorFailsCompletely;
338 Element* shadowHost = context.element->shadowHost();
340 return SelectorFailsCompletely;
341 nextContext.element = shadowHost;
342 nextContext.isSubSelector = false;
343 nextContext.elementStyle = 0;
344 return this->match(nextContext, siblingTraversalStrategy, result);
347 case CSSSelector::ShadowDeep:
349 nextContext.isSubSelector = false;
350 nextContext.elementStyle = 0;
351 for (nextContext.element = parentElement(context, true); nextContext.element; nextContext.element = parentElement(nextContext, true)) {
352 Match match = this->match(nextContext, siblingTraversalStrategy, result);
353 if (match == SelectorMatches || match == SelectorFailsCompletely)
355 if (nextSelectorExceedsScope(nextContext))
356 return SelectorFailsCompletely;
358 return SelectorFailsCompletely;
361 case CSSSelector::SubSelector:
362 ASSERT_NOT_REACHED();
365 ASSERT_NOT_REACHED();
366 return SelectorFailsCompletely;
369 template<typename SiblingTraversalStrategy>
370 SelectorChecker::Match SelectorChecker::matchForShadowDistributed(const Element* element, const SiblingTraversalStrategy& siblingTraversalStrategy, SelectorCheckingContext& nextContext, MatchResult* result) const
373 WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints;
375 const ContainerNode* scope = nextContext.scope;
376 BehaviorAtBoundary behaviorAtBoundary = nextContext.behaviorAtBoundary;
378 collectDestinationInsertionPoints(*element, insertionPoints);
379 for (size_t i = 0; i < insertionPoints.size(); ++i) {
380 nextContext.element = insertionPoints[i];
382 // If a given scope is a shadow host of an insertion point but behaviorAtBoundary doesn't have ScopeIsShadowRoot,
383 // we need to update behaviorAtBoundary to make selectors like ":host > ::content" work correctly.
384 if (m_mode == SharingRules) {
385 nextContext.behaviorAtBoundary = static_cast<BehaviorAtBoundary>(behaviorAtBoundary | ScopeIsShadowRoot);
386 nextContext.scope = insertionPoints[i]->containingShadowRoot();
387 } else if (scope == insertionPoints[i]->containingShadowRoot() && !(behaviorAtBoundary & ScopeIsShadowRoot)) {
388 nextContext.behaviorAtBoundary = static_cast<BehaviorAtBoundary>(behaviorAtBoundary | ScopeIsShadowRoot);
390 nextContext.behaviorAtBoundary = behaviorAtBoundary;
393 nextContext.isSubSelector = false;
394 nextContext.elementStyle = 0;
395 if (match(nextContext, siblingTraversalStrategy, result) == SelectorMatches)
396 return SelectorMatches;
398 return SelectorFailsLocally;
401 template<typename CharType>
402 static inline bool containsHTMLSpaceTemplate(const CharType* string, unsigned length)
404 for (unsigned i = 0; i < length; ++i)
405 if (isHTMLSpace<CharType>(string[i]))
410 static inline bool containsHTMLSpace(const AtomicString& string)
412 if (LIKELY(string.is8Bit()))
413 return containsHTMLSpaceTemplate<LChar>(string.characters8(), string.length());
414 return containsHTMLSpaceTemplate<UChar>(string.characters16(), string.length());
417 static bool attributeValueMatches(const Attribute& attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
419 const AtomicString& value = attributeItem.value();
424 case CSSSelector::Exact:
425 if (caseSensitive ? selectorValue != value : !equalIgnoringCase(selectorValue, value))
428 case CSSSelector::List:
430 // Ignore empty selectors or selectors containing HTML spaces
431 if (selectorValue.isEmpty() || containsHTMLSpace(selectorValue))
434 unsigned startSearchAt = 0;
436 size_t foundPos = value.find(selectorValue, startSearchAt, caseSensitive);
437 if (foundPos == kNotFound)
439 if (!foundPos || isHTMLSpace<UChar>(value[foundPos - 1])) {
440 unsigned endStr = foundPos + selectorValue.length();
441 if (endStr == value.length() || isHTMLSpace<UChar>(value[endStr]))
442 break; // We found a match.
445 // No match. Keep looking.
446 startSearchAt = foundPos + 1;
450 case CSSSelector::Contain:
451 if (!value.contains(selectorValue, caseSensitive) || selectorValue.isEmpty())
454 case CSSSelector::Begin:
455 if (!value.startsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
458 case CSSSelector::End:
459 if (!value.endsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
462 case CSSSelector::Hyphen:
463 if (value.length() < selectorValue.length())
465 if (!value.startsWith(selectorValue, caseSensitive))
467 // It they start the same, check for exact match or following '-':
468 if (value.length() != selectorValue.length() && value[selectorValue.length()] != '-')
471 case CSSSelector::PseudoClass:
472 case CSSSelector::PseudoElement:
480 static bool anyAttributeMatches(Element& element, CSSSelector::Match match, const CSSSelector& selector)
482 const QualifiedName& selectorAttr = selector.attribute();
483 ASSERT(selectorAttr.localName() != starAtom); // Should not be possible from the CSS grammar.
485 // Synchronize the attribute in case it is lazy-computed.
486 // Currently all lazy properties have a null namespace, so only pass localName().
487 element.synchronizeAttribute(selectorAttr.localName());
489 if (!element.hasAttributesWithoutUpdate())
492 const AtomicString& selectorValue = selector.value();
494 AttributeCollection attributes = element.attributes();
495 AttributeCollection::const_iterator end = attributes.end();
496 for (AttributeCollection::const_iterator it = attributes.begin(); it != end; ++it) {
497 const Attribute& attributeItem = *it;
499 if (!attributeItem.matches(selectorAttr))
502 if (attributeValueMatches(attributeItem, match, selectorValue, true))
505 // Case sensitivity for attribute matching is looser than hasAttribute or
506 // Element::shouldIgnoreAttributeCase() for now. Unclear if that's correct.
507 bool caseSensitive = !element.document().isHTMLDocument() || HTMLDocument::isCaseSensitiveAttribute(selectorAttr);
509 // If case-insensitive, re-check, and count if result differs.
510 // See http://code.google.com/p/chromium/issues/detail?id=327060
511 if (!caseSensitive && attributeValueMatches(attributeItem, match, selectorValue, false)) {
512 UseCounter::count(element.document(), UseCounter::CaseInsensitiveAttrSelectorMatch);
520 template<typename SiblingTraversalStrategy>
521 bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, unsigned* specificity) const
523 ASSERT(context.element);
524 Element& element = *context.element;
525 ASSERT(context.selector);
526 const CSSSelector& selector = *context.selector;
528 bool elementIsHostInItsShadowTree = isHostInItsShadowTree(element, context.behaviorAtBoundary, context.scope);
530 // Only :host and :ancestor should match the host: http://drafts.csswg.org/css-scoping/#host-element
531 if (elementIsHostInItsShadowTree && !selector.isHostPseudoClass()
532 && !(context.behaviorAtBoundary & TreatShadowHostAsNormalScope))
535 if (selector.match() == CSSSelector::Tag)
536 return SelectorChecker::tagMatches(element, selector.tagQName());
538 if (selector.match() == CSSSelector::Class)
539 return element.hasClass() && element.classNames().contains(selector.value());
541 if (selector.match() == CSSSelector::Id)
542 return element.hasID() && element.idForStyleResolution() == selector.value();
544 if (selector.isAttributeSelector())
545 return anyAttributeMatches(element, selector.match(), selector);
547 if (selector.match() == CSSSelector::PseudoClass) {
548 // Handle :not up front.
549 if (selector.pseudoType() == CSSSelector::PseudoNot) {
550 SelectorCheckingContext subContext(context);
551 subContext.isSubSelector = true;
552 ASSERT(selector.selectorList());
553 for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = subContext.selector->tagHistory()) {
554 // :not cannot nest. I don't really know why this is a
555 // restriction in CSS3, but it is, so let's honor it.
556 // the parser enforces that this never occurs
557 ASSERT(subContext.selector->pseudoType() != CSSSelector::PseudoNot);
558 // We select between :visited and :link when applying. We don't know which one applied (or not) yet.
559 if (subContext.selector->pseudoType() == CSSSelector::PseudoVisited || (subContext.selector->pseudoType() == CSSSelector::PseudoLink && subContext.visitedMatchType == VisitedMatchEnabled))
561 // context.scope is not available if m_mode == SharingRules.
562 // We cannot determine whether :host or :scope matches a given element or not.
563 if (m_mode == SharingRules && (subContext.selector->isHostPseudoClass() || subContext.selector->pseudoType() == CSSSelector::PseudoScope))
565 if (!checkOne(subContext, DOMSiblingTraversalStrategy()))
568 } else if (context.hasScrollbarPseudo) {
569 // CSS scrollbars match a specific subset of pseudo classes, and they have specialized rules for each
570 // (since there are no elements involved).
571 return checkScrollbarPseudoClass(context, &element.document(), selector);
572 } else if (context.hasSelectionPseudo) {
573 if (selector.pseudoType() == CSSSelector::PseudoWindowInactive)
574 return !element.document().page()->focusController().isActive();
577 // Normal element pseudo class checking.
578 switch (selector.pseudoType()) {
580 case CSSSelector::PseudoNot:
581 break; // Already handled up above.
582 case CSSSelector::PseudoEmpty:
585 for (Node* n = element.firstChild(); n; n = n->nextSibling()) {
586 if (n->isElementNode()) {
590 if (n->isTextNode()) {
591 Text* textNode = toText(n);
592 if (!textNode->data().isEmpty()) {
598 if (m_mode == ResolvingStyle) {
599 element.setStyleAffectedByEmpty();
600 if (context.elementStyle)
601 context.elementStyle->setEmptyState(result);
602 else if (element.renderStyle() && (element.document().styleEngine()->usesSiblingRules() || element.renderStyle()->unique()))
603 element.renderStyle()->setEmptyState(result);
607 case CSSSelector::PseudoFirstChild:
608 // first-child matches the first child that is an element
609 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
610 bool result = siblingTraversalStrategy.isFirstChild(element);
611 if (m_mode == ResolvingStyle) {
612 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
613 parent->setChildrenAffectedByFirstChildRules();
614 if (result && childStyle)
615 childStyle->setFirstChildState();
620 case CSSSelector::PseudoFirstOfType:
621 // first-of-type matches the first element of its type
622 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
623 bool result = siblingTraversalStrategy.isFirstOfType(element, element.tagQName());
624 if (m_mode == ResolvingStyle)
625 parent->setChildrenAffectedByForwardPositionalRules();
629 case CSSSelector::PseudoLastChild:
630 // last-child matches the last child that is an element
631 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
632 bool result = parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
633 if (m_mode == ResolvingStyle) {
634 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
635 parent->setChildrenAffectedByLastChildRules();
636 if (result && childStyle)
637 childStyle->setLastChildState();
642 case CSSSelector::PseudoLastOfType:
643 // last-of-type matches the last element of its type
644 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
645 if (m_mode == ResolvingStyle)
646 parent->setChildrenAffectedByBackwardPositionalRules();
647 if (!parent->isFinishedParsingChildren())
649 return siblingTraversalStrategy.isLastOfType(element, element.tagQName());
652 case CSSSelector::PseudoOnlyChild:
653 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
654 bool firstChild = siblingTraversalStrategy.isFirstChild(element);
655 bool onlyChild = firstChild && parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
656 if (m_mode == ResolvingStyle) {
657 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
658 parent->setChildrenAffectedByFirstChildRules();
659 parent->setChildrenAffectedByLastChildRules();
660 if (firstChild && childStyle)
661 childStyle->setFirstChildState();
662 if (onlyChild && childStyle)
663 childStyle->setLastChildState();
668 case CSSSelector::PseudoOnlyOfType:
669 // FIXME: This selector is very slow.
670 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
671 if (m_mode == ResolvingStyle) {
672 parent->setChildrenAffectedByForwardPositionalRules();
673 parent->setChildrenAffectedByBackwardPositionalRules();
675 if (!parent->isFinishedParsingChildren())
677 return siblingTraversalStrategy.isFirstOfType(element, element.tagQName()) && siblingTraversalStrategy.isLastOfType(element, element.tagQName());
680 case CSSSelector::PseudoNthChild:
681 if (!selector.parseNth())
683 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
684 int count = 1 + siblingTraversalStrategy.countElementsBefore(element);
685 if (m_mode == ResolvingStyle) {
686 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
688 childStyle->setUnique();
689 parent->setChildrenAffectedByForwardPositionalRules();
692 if (selector.matchNth(count))
696 case CSSSelector::PseudoNthOfType:
697 if (!selector.parseNth())
699 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
700 int count = 1 + siblingTraversalStrategy.countElementsOfTypeBefore(element, element.tagQName());
701 if (m_mode == ResolvingStyle)
702 parent->setChildrenAffectedByForwardPositionalRules();
704 if (selector.matchNth(count))
708 case CSSSelector::PseudoNthLastChild:
709 if (!selector.parseNth())
711 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
712 if (m_mode == ResolvingStyle)
713 parent->setChildrenAffectedByBackwardPositionalRules();
714 if (!parent->isFinishedParsingChildren())
716 int count = 1 + siblingTraversalStrategy.countElementsAfter(element);
717 if (selector.matchNth(count))
721 case CSSSelector::PseudoNthLastOfType:
722 if (!selector.parseNth())
724 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
725 if (m_mode == ResolvingStyle)
726 parent->setChildrenAffectedByBackwardPositionalRules();
727 if (!parent->isFinishedParsingChildren())
730 int count = 1 + siblingTraversalStrategy.countElementsOfTypeAfter(element, element.tagQName());
731 if (selector.matchNth(count))
735 case CSSSelector::PseudoTarget:
736 if (element == element.document().cssTarget())
739 case CSSSelector::PseudoAny:
741 SelectorCheckingContext subContext(context);
742 subContext.isSubSelector = true;
743 ASSERT(selector.selectorList());
744 for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
745 if (match(subContext, siblingTraversalStrategy) == SelectorMatches)
750 case CSSSelector::PseudoAutofill:
751 if (!element.isFormControlElement())
753 return toHTMLFormControlElement(element).isAutofilled();
754 case CSSSelector::PseudoAnyLink:
755 case CSSSelector::PseudoLink:
756 // :visited and :link matches are separated later when applying the style. Here both classes match all links...
757 return element.isLink();
758 case CSSSelector::PseudoVisited:
759 // ...except if :visited matching is disabled for ancestor/sibling matching.
760 return element.isLink() && context.visitedMatchType == VisitedMatchEnabled;
761 case CSSSelector::PseudoDrag:
762 if (m_mode == ResolvingStyle) {
763 if (context.elementStyle)
764 context.elementStyle->setAffectedByDrag();
766 element.setChildrenOrSiblingsAffectedByDrag();
768 if (element.renderer() && element.renderer()->isDragging())
771 case CSSSelector::PseudoFocus:
772 if (m_mode == ResolvingStyle) {
773 if (context.elementStyle)
774 context.elementStyle->setAffectedByFocus();
776 element.setChildrenOrSiblingsAffectedByFocus();
778 return matchesFocusPseudoClass(element);
779 case CSSSelector::PseudoHover:
780 // If we're in quirks mode, then hover should never match anchors with no
781 // href and *:hover should not match anything. This is important for sites like wsj.com.
782 if (m_strictParsing || context.isSubSelector || element.isLink()) {
783 if (m_mode == ResolvingStyle) {
784 if (context.elementStyle)
785 context.elementStyle->setAffectedByHover();
787 element.setChildrenOrSiblingsAffectedByHover();
789 if (element.hovered() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoHover))
793 case CSSSelector::PseudoActive:
794 // If we're in quirks mode, then :active should never match anchors with no
795 // href and *:active should not match anything.
796 if (m_strictParsing || context.isSubSelector || element.isLink()) {
797 if (m_mode == ResolvingStyle) {
798 if (context.elementStyle)
799 context.elementStyle->setAffectedByActive();
801 element.setChildrenOrSiblingsAffectedByActive();
803 if (element.active() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoActive))
807 case CSSSelector::PseudoEnabled:
808 if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
809 return !element.isDisabledFormControl();
811 case CSSSelector::PseudoFullPageMedia:
812 return element.document().isMediaDocument();
814 case CSSSelector::PseudoDefault:
815 return element.isDefaultButtonForForm();
816 case CSSSelector::PseudoDisabled:
817 if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
818 return element.isDisabledFormControl();
820 case CSSSelector::PseudoReadOnly:
821 return element.matchesReadOnlyPseudoClass();
822 case CSSSelector::PseudoReadWrite:
823 return element.matchesReadWritePseudoClass();
824 case CSSSelector::PseudoOptional:
825 return element.isOptionalFormControl();
826 case CSSSelector::PseudoRequired:
827 return element.isRequiredFormControl();
828 case CSSSelector::PseudoValid:
829 element.document().setContainsValidityStyleRules();
830 return element.willValidate() && element.isValidFormControlElement();
831 case CSSSelector::PseudoInvalid:
832 element.document().setContainsValidityStyleRules();
833 return element.willValidate() && !element.isValidFormControlElement();
834 case CSSSelector::PseudoChecked:
836 if (isHTMLInputElement(element)) {
837 HTMLInputElement& inputElement = toHTMLInputElement(element);
838 // Even though WinIE allows checked and indeterminate to
839 // co-exist, the CSS selector spec says that you can't be
840 // both checked and indeterminate. We will behave like WinIE
841 // behind the scenes and just obey the CSS spec here in the
842 // test for matching the pseudo.
843 if (inputElement.shouldAppearChecked() && !inputElement.shouldAppearIndeterminate())
845 } else if (isHTMLOptionElement(element) && toHTMLOptionElement(element).selected())
849 case CSSSelector::PseudoIndeterminate:
850 return element.shouldAppearIndeterminate();
851 case CSSSelector::PseudoRoot:
852 if (element == element.document().documentElement())
855 case CSSSelector::PseudoLang:
858 if (element.isVTTElement())
859 value = toVTTElement(element).language();
861 value = element.computeInheritedLanguage();
862 const AtomicString& argument = selector.argument();
863 if (value.isEmpty() || !value.startsWith(argument, false))
865 if (value.length() != argument.length() && value[argument.length()] != '-')
869 case CSSSelector::PseudoFullScreen:
870 // While a Document is in the fullscreen state, and the document's current fullscreen
871 // element is an element in the document, the 'full-screen' pseudoclass applies to
872 // that element. Also, an <iframe>, <object> or <embed> element whose child browsing
873 // context's Document is in the fullscreen state has the 'full-screen' pseudoclass applied.
874 if (isHTMLFrameElementBase(element) && element.containsFullScreenElement())
876 if (FullscreenElementStack* fullscreen = FullscreenElementStack::fromIfExists(element.document())) {
877 if (!fullscreen->webkitIsFullScreen())
879 return element == fullscreen->webkitCurrentFullScreenElement();
882 case CSSSelector::PseudoFullScreenAncestor:
883 return element.containsFullScreenElement();
884 case CSSSelector::PseudoFullScreenDocument:
885 // While a Document is in the fullscreen state, the 'full-screen-document' pseudoclass applies
886 // to all elements of that Document.
887 if (!FullscreenElementStack::isFullScreen(element.document()))
890 case CSSSelector::PseudoInRange:
891 element.document().setContainsValidityStyleRules();
892 return element.isInRange();
893 case CSSSelector::PseudoOutOfRange:
894 element.document().setContainsValidityStyleRules();
895 return element.isOutOfRange();
896 case CSSSelector::PseudoFutureCue:
897 return (element.isVTTElement() && !toVTTElement(element).isPastNode());
898 case CSSSelector::PseudoPastCue:
899 return (element.isVTTElement() && toVTTElement(element).isPastNode());
901 case CSSSelector::PseudoScope:
903 if (m_mode == SharingRules)
905 const Node* contextualReferenceNode = !context.scope ? element.document().documentElement() : context.scope;
906 if (element == contextualReferenceNode)
911 case CSSSelector::PseudoUnresolved:
912 if (element.isUnresolvedCustomElement())
916 case CSSSelector::PseudoHost:
917 case CSSSelector::PseudoHostContext:
919 if (m_mode == SharingRules)
921 // :host only matches a shadow host when :host is in a shadow tree of the shadow host.
924 const ContainerNode* shadowHost = context.scope->shadowHost();
925 if (!shadowHost || shadowHost != element)
927 ASSERT(element.shadow());
929 // For empty parameter case, i.e. just :host or :host().
930 if (!selector.selectorList()) // Use *'s specificity. So just 0.
933 SelectorCheckingContext subContext(context);
934 subContext.isSubSelector = true;
936 bool matched = false;
937 unsigned maxSpecificity = 0;
939 // If one of simple selectors matches an element, returns SelectorMatches. Just "OR".
940 for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
941 subContext.behaviorAtBoundary = ScopeIsShadowHostInPseudoHostParameter;
942 subContext.scope = context.scope;
943 // Use NodeRenderingTraversal to traverse a composed ancestor list of a given element.
944 Element* nextElement = &element;
945 SelectorCheckingContext hostContext(subContext);
947 MatchResult subResult;
948 hostContext.element = nextElement;
949 if (match(hostContext, siblingTraversalStrategy, &subResult) == SelectorMatches) {
951 // Consider div:host(div:host(div:host(div:host...))).
952 maxSpecificity = std::max(maxSpecificity, hostContext.selector->specificity() + subResult.specificity);
955 hostContext.behaviorAtBoundary = DoesNotCrossBoundary;
956 hostContext.scope = 0;
958 if (selector.pseudoType() == CSSSelector::PseudoHost)
961 hostContext.elementStyle = 0;
962 nextElement = NodeRenderingTraversal::parentElement(nextElement);
963 } while (nextElement);
967 *specificity = maxSpecificity;
973 case CSSSelector::PseudoHorizontal:
974 case CSSSelector::PseudoVertical:
975 case CSSSelector::PseudoDecrement:
976 case CSSSelector::PseudoIncrement:
977 case CSSSelector::PseudoStart:
978 case CSSSelector::PseudoEnd:
979 case CSSSelector::PseudoDoubleButton:
980 case CSSSelector::PseudoSingleButton:
981 case CSSSelector::PseudoNoButton:
982 case CSSSelector::PseudoCornerPresent:
985 case CSSSelector::PseudoUnknown:
986 case CSSSelector::PseudoNotParsed:
988 ASSERT_NOT_REACHED();
992 } else if (selector.match() == CSSSelector::PseudoElement && selector.pseudoType() == CSSSelector::PseudoCue) {
993 SelectorCheckingContext subContext(context);
994 subContext.isSubSelector = true;
995 subContext.behaviorAtBoundary = StaysWithinTreeScope;
997 const CSSSelector* contextSelector = context.selector;
998 ASSERT(contextSelector);
999 for (subContext.selector = contextSelector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
1000 if (match(subContext, siblingTraversalStrategy) == SelectorMatches)
1005 // ### add the rest of the checks...
1009 bool SelectorChecker::checkScrollbarPseudoClass(const SelectorCheckingContext& context, Document* document, const CSSSelector& selector) const
1011 RenderScrollbar* scrollbar = context.scrollbar;
1012 ScrollbarPart part = context.scrollbarPart;
1014 // FIXME: This is a temporary hack for resizers and scrollbar corners. Eventually :window-inactive should become a real
1015 // pseudo class and just apply to everything.
1016 if (selector.pseudoType() == CSSSelector::PseudoWindowInactive)
1017 return !document->page()->focusController().isActive();
1022 ASSERT(selector.match() == CSSSelector::PseudoClass);
1023 switch (selector.pseudoType()) {
1024 case CSSSelector::PseudoEnabled:
1025 return scrollbar->enabled();
1026 case CSSSelector::PseudoDisabled:
1027 return !scrollbar->enabled();
1028 case CSSSelector::PseudoHover:
1030 ScrollbarPart hoveredPart = scrollbar->hoveredPart();
1031 if (part == ScrollbarBGPart)
1032 return hoveredPart != NoPart;
1033 if (part == TrackBGPart)
1034 return hoveredPart == BackTrackPart || hoveredPart == ForwardTrackPart || hoveredPart == ThumbPart;
1035 return part == hoveredPart;
1037 case CSSSelector::PseudoActive:
1039 ScrollbarPart pressedPart = scrollbar->pressedPart();
1040 if (part == ScrollbarBGPart)
1041 return pressedPart != NoPart;
1042 if (part == TrackBGPart)
1043 return pressedPart == BackTrackPart || pressedPart == ForwardTrackPart || pressedPart == ThumbPart;
1044 return part == pressedPart;
1046 case CSSSelector::PseudoHorizontal:
1047 return scrollbar->orientation() == HorizontalScrollbar;
1048 case CSSSelector::PseudoVertical:
1049 return scrollbar->orientation() == VerticalScrollbar;
1050 case CSSSelector::PseudoDecrement:
1051 return part == BackButtonStartPart || part == BackButtonEndPart || part == BackTrackPart;
1052 case CSSSelector::PseudoIncrement:
1053 return part == ForwardButtonStartPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
1054 case CSSSelector::PseudoStart:
1055 return part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart;
1056 case CSSSelector::PseudoEnd:
1057 return part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
1058 case CSSSelector::PseudoDoubleButton:
1060 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1061 if (part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart)
1062 return buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth;
1063 if (part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart)
1064 return buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth;
1067 case CSSSelector::PseudoSingleButton:
1069 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1070 if (part == BackButtonStartPart || part == ForwardButtonEndPart || part == BackTrackPart || part == ForwardTrackPart)
1071 return buttonsPlacement == ScrollbarButtonsSingle;
1074 case CSSSelector::PseudoNoButton:
1076 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1077 if (part == BackTrackPart)
1078 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleEnd;
1079 if (part == ForwardTrackPart)
1080 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleStart;
1083 case CSSSelector::PseudoCornerPresent:
1084 return scrollbar->scrollableArea()->isScrollCornerVisible();
1090 unsigned SelectorChecker::determineLinkMatchType(const CSSSelector& selector)
1092 unsigned linkMatchType = MatchAll;
1094 // Statically determine if this selector will match a link in visited, unvisited or any state, or never.
1095 // :visited never matches other elements than the innermost link element.
1096 for (const CSSSelector* current = &selector; current; current = current->tagHistory()) {
1097 switch (current->pseudoType()) {
1098 case CSSSelector::PseudoNot:
1100 // :not(:visited) is equivalent to :link. Parser enforces that :not can't nest.
1101 ASSERT(current->selectorList());
1102 for (const CSSSelector* subSelector = current->selectorList()->first(); subSelector; subSelector = subSelector->tagHistory()) {
1103 CSSSelector::PseudoType subType = subSelector->pseudoType();
1104 if (subType == CSSSelector::PseudoVisited)
1105 linkMatchType &= ~SelectorChecker::MatchVisited;
1106 else if (subType == CSSSelector::PseudoLink)
1107 linkMatchType &= ~SelectorChecker::MatchLink;
1111 case CSSSelector::PseudoLink:
1112 linkMatchType &= ~SelectorChecker::MatchVisited;
1114 case CSSSelector::PseudoVisited:
1115 linkMatchType &= ~SelectorChecker::MatchLink;
1118 // We don't support :link and :visited inside :-webkit-any.
1121 CSSSelector::Relation relation = current->relation();
1122 if (relation == CSSSelector::SubSelector)
1124 if (relation != CSSSelector::Descendant && relation != CSSSelector::Child)
1125 return linkMatchType;
1126 if (linkMatchType != MatchAll)
1127 return linkMatchType;
1129 return linkMatchType;
1132 bool SelectorChecker::isFrameFocused(const Element& element)
1134 return element.document().frame() && element.document().frame()->selection().isFocusedAndActive();
1137 bool SelectorChecker::matchesFocusPseudoClass(const Element& element)
1139 if (InspectorInstrumentation::forcePseudoState(const_cast<Element*>(&element), CSSSelector::PseudoFocus))
1141 return element.focused() && isFrameFocused(element);
1145 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const DOMSiblingTraversalStrategy&, MatchResult*) const;
1148 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const ShadowDOMSiblingTraversalStrategy&, MatchResult*) const;