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/StyleEngine.h"
39 #include "core/dom/Text.h"
40 #include "core/dom/shadow/InsertionPoint.h"
41 #include "core/dom/shadow/ShadowRoot.h"
42 #include "core/editing/FrameSelection.h"
43 #include "core/frame/LocalFrame.h"
44 #include "core/html/HTMLDocument.h"
45 #include "core/html/HTMLFrameElementBase.h"
46 #include "core/html/HTMLInputElement.h"
47 #include "core/html/HTMLOptionElement.h"
48 #include "core/html/HTMLSelectElement.h"
49 #include "core/html/parser/HTMLParserIdioms.h"
50 #include "core/html/track/vtt/VTTElement.h"
51 #include "core/inspector/InspectorInstrumentation.h"
52 #include "core/page/FocusController.h"
53 #include "core/page/Page.h"
54 #include "core/rendering/RenderObject.h"
55 #include "core/rendering/RenderScrollbar.h"
56 #include "core/rendering/style/RenderStyle.h"
57 #include "platform/scroll/ScrollableArea.h"
58 #include "platform/scroll/ScrollbarTheme.h"
62 using namespace HTMLNames;
64 SelectorChecker::SelectorChecker(Document& document, Mode mode)
65 : m_strictParsing(!document.inQuirksMode())
66 , m_documentIsHTML(document.isHTMLDocument())
71 static bool matchesCustomPseudoElement(const Element* element, const CSSSelector& selector)
73 ShadowRoot* root = element->containingShadowRoot();
74 if (!root || root->type() != ShadowRoot::UserAgentShadowRoot)
77 if (element->shadowPseudoId() != selector.value())
83 static Element* parentElement(const SelectorChecker::SelectorCheckingContext& context)
85 // - If context.scope is a shadow root, we should walk up to its shadow host.
86 // - If context.scope is some element in some shadow tree and querySelector initialized the context,
87 // e.g. shadowRoot.querySelector(':host *'),
88 // (a) context.element has the same treescope as context.scope, need to walk up to its shadow host.
89 // (b) Otherwise, should not walk up from a shadow root to a shadow host.
90 if (context.scope && (context.scope == context.element->containingShadowRoot() || context.scope->treeScope() == context.element->treeScope()))
91 return context.element->parentOrShadowHostElement();
92 return context.element->parentElement();
95 static bool scopeContainsLastMatchedElement(const SelectorChecker::SelectorCheckingContext& context)
97 if (!(context.contextFlags & SelectorChecker::ScopeContainsLastMatchedElement))
100 ASSERT(context.scope);
101 if (context.scope->treeScope() == context.element->treeScope())
104 // Because Blink treats a shadow host's TreeScope as a separate one from its descendent shadow roots,
105 // if the last matched element is a shadow host, the condition above isn't met, even though it
107 return context.element == context.scope->shadowHost() && (!context.previousElement || context.previousElement->isInDescendantTreeOf(context.element));
110 static inline bool nextSelectorExceedsScope(const SelectorChecker::SelectorCheckingContext& context)
112 if (context.scope && context.scope->isInShadowTree())
113 return context.element == context.scope->shadowHost();
118 // Recursive check of selectors and combinators
119 // It can return 4 different values:
120 // * SelectorMatches - the selector matches the element e
121 // * SelectorFailsLocally - the selector fails for the element e
122 // * SelectorFailsAllSiblings - the selector fails for e and any sibling of e
123 // * SelectorFailsCompletely - the selector fails for e and any sibling or ancestor of e
124 template<typename SiblingTraversalStrategy>
125 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
127 // first selector has to match
128 unsigned specificity = 0;
129 if (!checkOne(context, siblingTraversalStrategy, &specificity))
130 return SelectorFailsLocally;
132 if (context.selector->match() == CSSSelector::PseudoElement) {
133 if (context.selector->isCustomPseudoElement()) {
134 if (!matchesCustomPseudoElement(context.element, *context.selector))
135 return SelectorFailsLocally;
136 } else if (context.selector->isContentPseudoElement()) {
137 if (!context.element->isInShadowTree() || !context.element->isInsertionPoint())
138 return SelectorFailsLocally;
139 } else if (context.selector->isShadowPseudoElement()) {
140 if (!context.element->isInShadowTree() || !context.previousElement)
141 return SelectorFailsCompletely;
143 if ((!context.elementStyle && m_mode == ResolvingStyle) || m_mode == QueryingRules)
144 return SelectorFailsLocally;
146 PseudoId pseudoId = CSSSelector::pseudoId(context.selector->pseudoType());
147 if (pseudoId == FIRST_LETTER)
148 context.element->document().styleEngine()->setUsesFirstLetterRules(true);
149 if (pseudoId != NOPSEUDO && m_mode != SharingRules && result)
150 result->dynamicPseudo = pseudoId;
154 // Prepare next selector
155 if (context.selector->isLastInTagHistory()) {
156 if (scopeContainsLastMatchedElement(context)) {
158 result->specificity += specificity;
159 return SelectorMatches;
161 return SelectorFailsLocally;
165 if (context.selector->relation() != CSSSelector::SubSelector) {
166 // Abort if the next selector would exceed the scope.
167 if (nextSelectorExceedsScope(context))
168 return SelectorFailsCompletely;
170 // Bail-out if this selector is irrelevant for the pseudoId
171 if (context.pseudoId != NOPSEUDO && (!result || context.pseudoId != result->dynamicPseudo))
172 return SelectorFailsCompletely;
175 TemporaryChange<PseudoId> dynamicPseudoScope(result->dynamicPseudo, NOPSEUDO);
176 match = matchForRelation(context, siblingTraversalStrategy, result);
178 return matchForRelation(context, siblingTraversalStrategy, 0);
181 match = matchForSubSelector(context, siblingTraversalStrategy, result);
183 if (match != SelectorMatches || !result)
186 result->specificity += specificity;
187 return SelectorMatches;
190 static inline SelectorChecker::SelectorCheckingContext prepareNextContextForRelation(const SelectorChecker::SelectorCheckingContext& context)
192 SelectorChecker::SelectorCheckingContext nextContext(context);
193 ASSERT(context.selector->tagHistory());
194 nextContext.selector = context.selector->tagHistory();
198 static inline bool isAuthorShadowRoot(const Node* node)
200 return node && node->isShadowRoot() && toShadowRoot(node)->type() == ShadowRoot::AuthorShadowRoot;
203 template<typename SiblingTraversalStrategy>
204 SelectorChecker::Match SelectorChecker::matchForSubSelector(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
206 SelectorCheckingContext nextContext = prepareNextContextForRelation(context);
208 PseudoId dynamicPseudo = result ? result->dynamicPseudo : NOPSEUDO;
209 // a selector is invalid if something follows a pseudo-element
210 // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else)
211 // to follow the pseudo elements.
212 nextContext.hasScrollbarPseudo = dynamicPseudo != NOPSEUDO && (context.scrollbar || dynamicPseudo == SCROLLBAR_CORNER || dynamicPseudo == RESIZER);
213 nextContext.hasSelectionPseudo = dynamicPseudo == SELECTION;
214 if ((context.elementStyle || m_mode == CollectingCSSRules || m_mode == CollectingStyleRules || m_mode == QueryingRules) && dynamicPseudo != NOPSEUDO
215 && !nextContext.hasSelectionPseudo
216 && !(nextContext.hasScrollbarPseudo && nextContext.selector->match() == CSSSelector::PseudoClass))
217 return SelectorFailsCompletely;
219 nextContext.isSubSelector = true;
220 return match(nextContext, siblingTraversalStrategy, result);
223 static bool selectorMatchesShadowRoot(const CSSSelector* selector)
225 return selector && selector->isShadowPseudoElement();
228 template<typename SiblingTraversalStrategy>
229 SelectorChecker::Match SelectorChecker::matchForPseudoShadow(const ContainerNode* node, const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
231 if (!isAuthorShadowRoot(node))
232 return SelectorFailsCompletely;
233 return match(context, siblingTraversalStrategy, result);
236 template<typename SiblingTraversalStrategy>
237 SelectorChecker::Match SelectorChecker::matchForRelation(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
239 SelectorCheckingContext nextContext = prepareNextContextForRelation(context);
240 nextContext.previousElement = context.element;
242 CSSSelector::Relation relation = context.selector->relation();
244 // Disable :visited matching when we see the first link or try to match anything else than an ancestors.
245 if (!context.isSubSelector && (context.element->isLink() || (relation != CSSSelector::Descendant && relation != CSSSelector::Child)))
246 nextContext.visitedMatchType = VisitedMatchDisabled;
248 nextContext.pseudoId = NOPSEUDO;
251 case CSSSelector::Descendant:
252 if (context.selector->relationIsAffectedByPseudoContent()) {
253 for (Element* element = context.element; element; element = element->parentElement()) {
254 if (matchForShadowDistributed(element, siblingTraversalStrategy, nextContext, result) == SelectorMatches)
255 return SelectorMatches;
257 return SelectorFailsCompletely;
259 nextContext.isSubSelector = false;
260 nextContext.elementStyle = 0;
262 if (selectorMatchesShadowRoot(nextContext.selector))
263 return matchForPseudoShadow(context.element->containingShadowRoot(), nextContext, siblingTraversalStrategy, result);
265 for (nextContext.element = parentElement(context); nextContext.element; nextContext.element = parentElement(nextContext)) {
266 Match match = this->match(nextContext, siblingTraversalStrategy, result);
267 if (match == SelectorMatches || match == SelectorFailsCompletely)
269 if (nextSelectorExceedsScope(nextContext))
270 return SelectorFailsCompletely;
272 return SelectorFailsCompletely;
273 case CSSSelector::Child:
275 if (context.selector->relationIsAffectedByPseudoContent())
276 return matchForShadowDistributed(context.element, siblingTraversalStrategy, nextContext, result);
278 nextContext.isSubSelector = false;
279 nextContext.elementStyle = 0;
281 if (selectorMatchesShadowRoot(nextContext.selector))
282 return matchForPseudoShadow(context.element->parentNode(), nextContext, siblingTraversalStrategy, result);
284 nextContext.element = parentElement(context);
285 if (!nextContext.element)
286 return SelectorFailsCompletely;
287 return match(nextContext, siblingTraversalStrategy, result);
289 case CSSSelector::DirectAdjacent:
290 // Shadow roots can't have sibling elements
291 if (selectorMatchesShadowRoot(nextContext.selector))
292 return SelectorFailsCompletely;
294 if (m_mode == ResolvingStyle) {
295 if (ContainerNode* parent = context.element->parentElementOrShadowRoot())
296 parent->setChildrenAffectedByDirectAdjacentRules();
298 nextContext.element = ElementTraversal::previousSibling(*context.element);
299 if (!nextContext.element)
300 return SelectorFailsAllSiblings;
301 nextContext.isSubSelector = false;
302 nextContext.elementStyle = 0;
303 return match(nextContext, siblingTraversalStrategy, result);
305 case CSSSelector::IndirectAdjacent:
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->setChildrenAffectedByIndirectAdjacentRules();
314 nextContext.element = ElementTraversal::previousSibling(*context.element);
315 nextContext.isSubSelector = false;
316 nextContext.elementStyle = 0;
317 for (; nextContext.element; nextContext.element = ElementTraversal::previousSibling(*nextContext.element)) {
318 Match match = this->match(nextContext, siblingTraversalStrategy, result);
319 if (match == SelectorMatches || match == SelectorFailsAllSiblings || match == SelectorFailsCompletely)
322 return SelectorFailsAllSiblings;
324 case CSSSelector::ShadowPseudo:
326 // 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.
327 if (context.scope && context.scope->shadowHost() && context.scope->shadowHost()->treeScope() == context.element->treeScope())
328 return SelectorFailsCompletely;
330 Element* shadowHost = context.element->shadowHost();
332 return SelectorFailsCompletely;
333 nextContext.element = shadowHost;
334 nextContext.isSubSelector = false;
335 nextContext.elementStyle = 0;
336 return this->match(nextContext, siblingTraversalStrategy, result);
339 case CSSSelector::ShadowDeep:
341 nextContext.isSubSelector = false;
342 nextContext.elementStyle = 0;
343 for (nextContext.element = context.element->parentOrShadowHostElement(); nextContext.element; nextContext.element = nextContext.element->parentOrShadowHostElement()) {
344 Match match = this->match(nextContext, siblingTraversalStrategy, result);
345 if (match == SelectorMatches || match == SelectorFailsCompletely)
347 if (nextSelectorExceedsScope(nextContext))
348 return SelectorFailsCompletely;
350 return SelectorFailsCompletely;
353 case CSSSelector::SubSelector:
354 ASSERT_NOT_REACHED();
357 ASSERT_NOT_REACHED();
358 return SelectorFailsCompletely;
361 template<typename SiblingTraversalStrategy>
362 SelectorChecker::Match SelectorChecker::matchForShadowDistributed(const Element* element, const SiblingTraversalStrategy& siblingTraversalStrategy, SelectorCheckingContext& nextContext, MatchResult* result) const
365 WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints;
366 collectDestinationInsertionPoints(*element, insertionPoints);
367 for (size_t i = 0; i < insertionPoints.size(); ++i) {
368 nextContext.element = insertionPoints[i];
369 if (m_mode == SharingRules)
370 nextContext.scope = insertionPoints[i]->containingShadowRoot();
371 nextContext.isSubSelector = false;
372 nextContext.elementStyle = 0;
373 if (match(nextContext, siblingTraversalStrategy, result) == SelectorMatches)
374 return SelectorMatches;
376 return SelectorFailsLocally;
379 template<typename CharType>
380 static inline bool containsHTMLSpaceTemplate(const CharType* string, unsigned length)
382 for (unsigned i = 0; i < length; ++i)
383 if (isHTMLSpace<CharType>(string[i]))
388 static inline bool containsHTMLSpace(const AtomicString& string)
390 if (LIKELY(string.is8Bit()))
391 return containsHTMLSpaceTemplate<LChar>(string.characters8(), string.length());
392 return containsHTMLSpaceTemplate<UChar>(string.characters16(), string.length());
395 static bool attributeValueMatches(const Attribute& attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
397 const AtomicString& value = attributeItem.value();
402 case CSSSelector::Exact:
403 if (caseSensitive ? selectorValue != value : !equalIgnoringCase(selectorValue, value))
406 case CSSSelector::List:
408 // Ignore empty selectors or selectors containing HTML spaces
409 if (selectorValue.isEmpty() || containsHTMLSpace(selectorValue))
412 unsigned startSearchAt = 0;
414 size_t foundPos = value.find(selectorValue, startSearchAt, caseSensitive);
415 if (foundPos == kNotFound)
417 if (!foundPos || isHTMLSpace<UChar>(value[foundPos - 1])) {
418 unsigned endStr = foundPos + selectorValue.length();
419 if (endStr == value.length() || isHTMLSpace<UChar>(value[endStr]))
420 break; // We found a match.
423 // No match. Keep looking.
424 startSearchAt = foundPos + 1;
428 case CSSSelector::Contain:
429 if (!value.contains(selectorValue, caseSensitive) || selectorValue.isEmpty())
432 case CSSSelector::Begin:
433 if (!value.startsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
436 case CSSSelector::End:
437 if (!value.endsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
440 case CSSSelector::Hyphen:
441 if (value.length() < selectorValue.length())
443 if (!value.startsWith(selectorValue, caseSensitive))
445 // It they start the same, check for exact match or following '-':
446 if (value.length() != selectorValue.length() && value[selectorValue.length()] != '-')
449 case CSSSelector::PseudoClass:
450 case CSSSelector::PseudoElement:
458 static bool anyAttributeMatches(Element& element, CSSSelector::Match match, const CSSSelector& selector)
460 const QualifiedName& selectorAttr = selector.attribute();
461 ASSERT(selectorAttr.localName() != starAtom); // Should not be possible from the CSS grammar.
463 // Synchronize the attribute in case it is lazy-computed.
464 // Currently all lazy properties have a null namespace, so only pass localName().
465 element.synchronizeAttribute(selectorAttr.localName());
467 const AtomicString& selectorValue = selector.value();
468 bool caseInsensitive = selector.attributeMatchType() == CSSSelector::CaseInsensitive;
470 AttributeCollection attributes = element.attributesWithoutUpdate();
471 AttributeCollection::iterator end = attributes.end();
472 for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) {
473 const Attribute& attributeItem = *it;
475 if (!attributeItem.matches(selectorAttr))
478 if (attributeValueMatches(attributeItem, match, selectorValue, !caseInsensitive))
484 // Legacy dictates that values of some attributes should be compared in
485 // a case-insensitive manner regardless of whether the case insensitive
486 // flag is set or not.
487 bool legacyCaseInsensitive = element.document().isHTMLDocument() && !HTMLDocument::isCaseSensitiveAttribute(selectorAttr);
489 // If case-insensitive, re-check, and count if result differs.
490 // See http://code.google.com/p/chromium/issues/detail?id=327060
491 if (legacyCaseInsensitive && attributeValueMatches(attributeItem, match, selectorValue, false)) {
492 UseCounter::count(element.document(), UseCounter::CaseInsensitiveAttrSelectorMatch);
500 template<typename SiblingTraversalStrategy>
501 bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, unsigned* specificity) const
503 ASSERT(context.element);
504 Element& element = *context.element;
505 ASSERT(context.selector);
506 const CSSSelector& selector = *context.selector;
508 bool elementIsHostInItsShadowTree = isHostInItsShadowTree(element, context.scope);
510 // Only :host and :ancestor should match the host: http://drafts.csswg.org/css-scoping/#host-element
511 if (elementIsHostInItsShadowTree && !selector.isHostPseudoClass()
512 && !(context.contextFlags & TreatShadowHostAsNormalScope))
515 if (selector.match() == CSSSelector::Tag)
516 return SelectorChecker::tagMatches(element, selector.tagQName());
518 if (selector.match() == CSSSelector::Class)
519 return element.hasClass() && element.classNames().contains(selector.value());
521 if (selector.match() == CSSSelector::Id)
522 return element.hasID() && element.idForStyleResolution() == selector.value();
524 if (selector.isAttributeSelector())
525 return anyAttributeMatches(element, selector.match(), selector);
527 if (selector.match() == CSSSelector::PseudoClass) {
528 // Handle :not up front.
529 if (selector.pseudoType() == CSSSelector::PseudoNot) {
530 SelectorCheckingContext subContext(context);
531 subContext.isSubSelector = true;
532 ASSERT(selector.selectorList());
533 for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = subContext.selector->tagHistory()) {
534 // :not cannot nest. I don't really know why this is a
535 // restriction in CSS3, but it is, so let's honor it.
536 // the parser enforces that this never occurs
537 ASSERT(subContext.selector->pseudoType() != CSSSelector::PseudoNot);
538 // We select between :visited and :link when applying. We don't know which one applied (or not) yet.
539 if (subContext.selector->pseudoType() == CSSSelector::PseudoVisited || (subContext.selector->pseudoType() == CSSSelector::PseudoLink && subContext.visitedMatchType == VisitedMatchEnabled))
541 // context.scope is not available if m_mode == SharingRules.
542 // We cannot determine whether :host or :scope matches a given element or not.
543 if (m_mode == SharingRules && (subContext.selector->isHostPseudoClass() || subContext.selector->pseudoType() == CSSSelector::PseudoScope))
545 if (!checkOne(subContext, DOMSiblingTraversalStrategy()))
548 } else if (context.hasScrollbarPseudo) {
549 // CSS scrollbars match a specific subset of pseudo classes, and they have specialized rules for each
550 // (since there are no elements involved).
551 return checkScrollbarPseudoClass(context, &element.document(), selector);
552 } else if (context.hasSelectionPseudo) {
553 if (selector.pseudoType() == CSSSelector::PseudoWindowInactive)
554 return !element.document().page()->focusController().isActive();
557 // Normal element pseudo class checking.
558 switch (selector.pseudoType()) {
560 case CSSSelector::PseudoNot:
561 break; // Already handled up above.
562 case CSSSelector::PseudoEmpty:
565 for (Node* n = element.firstChild(); n; n = n->nextSibling()) {
566 if (n->isElementNode()) {
570 if (n->isTextNode()) {
571 Text* textNode = toText(n);
572 if (!textNode->data().isEmpty()) {
578 if (m_mode == ResolvingStyle) {
579 element.setStyleAffectedByEmpty();
580 if (context.elementStyle)
581 context.elementStyle->setEmptyState(result);
582 else if (element.renderStyle() && (element.document().styleEngine()->usesSiblingRules() || element.renderStyle()->unique()))
583 element.renderStyle()->setEmptyState(result);
587 case CSSSelector::PseudoFirstChild:
588 // first-child matches the first child that is an element
589 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
590 bool result = siblingTraversalStrategy.isFirstChild(element);
591 if (m_mode == ResolvingStyle) {
592 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
593 parent->setChildrenAffectedByFirstChildRules();
594 if (result && childStyle)
595 childStyle->setFirstChildState();
600 case CSSSelector::PseudoFirstOfType:
601 // first-of-type matches the first element of its type
602 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
603 bool result = siblingTraversalStrategy.isFirstOfType(element, element.tagQName());
604 if (m_mode == ResolvingStyle)
605 parent->setChildrenAffectedByForwardPositionalRules();
609 case CSSSelector::PseudoLastChild:
610 // last-child matches the last child that is an element
611 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
612 bool result = parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
613 if (m_mode == ResolvingStyle) {
614 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
615 parent->setChildrenAffectedByLastChildRules();
616 if (result && childStyle)
617 childStyle->setLastChildState();
622 case CSSSelector::PseudoLastOfType:
623 // last-of-type matches the last element of its type
624 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
625 if (m_mode == ResolvingStyle)
626 parent->setChildrenAffectedByBackwardPositionalRules();
627 if (!parent->isFinishedParsingChildren())
629 return siblingTraversalStrategy.isLastOfType(element, element.tagQName());
632 case CSSSelector::PseudoOnlyChild:
633 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
634 bool firstChild = siblingTraversalStrategy.isFirstChild(element);
635 bool onlyChild = firstChild && parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
636 if (m_mode == ResolvingStyle) {
637 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
638 parent->setChildrenAffectedByFirstChildRules();
639 parent->setChildrenAffectedByLastChildRules();
640 if (firstChild && childStyle)
641 childStyle->setFirstChildState();
642 if (onlyChild && childStyle)
643 childStyle->setLastChildState();
648 case CSSSelector::PseudoOnlyOfType:
649 // FIXME: This selector is very slow.
650 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
651 if (m_mode == ResolvingStyle) {
652 parent->setChildrenAffectedByForwardPositionalRules();
653 parent->setChildrenAffectedByBackwardPositionalRules();
655 if (!parent->isFinishedParsingChildren())
657 return siblingTraversalStrategy.isFirstOfType(element, element.tagQName()) && siblingTraversalStrategy.isLastOfType(element, element.tagQName());
660 case CSSSelector::PseudoNthChild:
661 if (!selector.parseNth())
663 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
664 int count = 1 + siblingTraversalStrategy.countElementsBefore(element);
665 if (m_mode == ResolvingStyle) {
666 RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
668 childStyle->setUnique();
669 parent->setChildrenAffectedByForwardPositionalRules();
672 if (selector.matchNth(count))
676 case CSSSelector::PseudoNthOfType:
677 if (!selector.parseNth())
679 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
680 int count = 1 + siblingTraversalStrategy.countElementsOfTypeBefore(element, element.tagQName());
681 if (m_mode == ResolvingStyle)
682 parent->setChildrenAffectedByForwardPositionalRules();
684 if (selector.matchNth(count))
688 case CSSSelector::PseudoNthLastChild:
689 if (!selector.parseNth())
691 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
692 if (m_mode == ResolvingStyle)
693 parent->setChildrenAffectedByBackwardPositionalRules();
694 if (!parent->isFinishedParsingChildren())
696 int count = 1 + siblingTraversalStrategy.countElementsAfter(element);
697 if (selector.matchNth(count))
701 case CSSSelector::PseudoNthLastOfType:
702 if (!selector.parseNth())
704 if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
705 if (m_mode == ResolvingStyle)
706 parent->setChildrenAffectedByBackwardPositionalRules();
707 if (!parent->isFinishedParsingChildren())
710 int count = 1 + siblingTraversalStrategy.countElementsOfTypeAfter(element, element.tagQName());
711 if (selector.matchNth(count))
715 case CSSSelector::PseudoTarget:
716 if (element == element.document().cssTarget())
719 case CSSSelector::PseudoAny:
721 SelectorCheckingContext subContext(context);
722 subContext.isSubSelector = true;
723 ASSERT(selector.selectorList());
724 for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
725 if (match(subContext, siblingTraversalStrategy) == SelectorMatches)
730 case CSSSelector::PseudoAutofill:
731 if (!element.isFormControlElement())
733 return toHTMLFormControlElement(element).isAutofilled();
734 case CSSSelector::PseudoAnyLink:
735 case CSSSelector::PseudoLink:
736 // :visited and :link matches are separated later when applying the style. Here both classes match all links...
737 return element.isLink();
738 case CSSSelector::PseudoVisited:
739 // ...except if :visited matching is disabled for ancestor/sibling matching.
740 return element.isLink() && context.visitedMatchType == VisitedMatchEnabled;
741 case CSSSelector::PseudoDrag:
742 if (m_mode == ResolvingStyle) {
743 if (context.elementStyle)
744 context.elementStyle->setAffectedByDrag();
746 element.setChildrenOrSiblingsAffectedByDrag();
748 if (element.renderer() && element.renderer()->isDragging())
751 case CSSSelector::PseudoFocus:
752 if (m_mode == ResolvingStyle) {
753 if (context.elementStyle)
754 context.elementStyle->setAffectedByFocus();
756 element.setChildrenOrSiblingsAffectedByFocus();
758 return matchesFocusPseudoClass(element);
759 case CSSSelector::PseudoHover:
760 // If we're in quirks mode, then hover should never match anchors with no
761 // href and *:hover should not match anything. This is important for sites like wsj.com.
762 if (m_strictParsing || context.isSubSelector || element.isLink()) {
763 if (m_mode == ResolvingStyle) {
764 if (context.elementStyle)
765 context.elementStyle->setAffectedByHover();
767 element.setChildrenOrSiblingsAffectedByHover();
769 if (element.hovered() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoHover))
773 case CSSSelector::PseudoActive:
774 // If we're in quirks mode, then :active should never match anchors with no
775 // href and *:active should not match anything.
776 if (m_strictParsing || context.isSubSelector || element.isLink()) {
777 if (m_mode == ResolvingStyle) {
778 if (context.elementStyle)
779 context.elementStyle->setAffectedByActive();
781 element.setChildrenOrSiblingsAffectedByActive();
783 if (element.active() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoActive))
787 case CSSSelector::PseudoEnabled:
788 if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
789 return !element.isDisabledFormControl();
790 else if (isHTMLAnchorElement(element) || isHTMLAreaElement(element))
791 return element.isLink();
793 case CSSSelector::PseudoFullPageMedia:
794 return element.document().isMediaDocument();
796 case CSSSelector::PseudoDefault:
797 return element.isDefaultButtonForForm();
798 case CSSSelector::PseudoDisabled:
799 if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
800 return element.isDisabledFormControl();
802 case CSSSelector::PseudoReadOnly:
803 return element.matchesReadOnlyPseudoClass();
804 case CSSSelector::PseudoReadWrite:
805 return element.matchesReadWritePseudoClass();
806 case CSSSelector::PseudoOptional:
807 return element.isOptionalFormControl();
808 case CSSSelector::PseudoRequired:
809 return element.isRequiredFormControl();
810 case CSSSelector::PseudoValid:
811 element.document().setContainsValidityStyleRules();
812 return element.willValidate() && element.isValidFormControlElement();
813 case CSSSelector::PseudoInvalid:
814 element.document().setContainsValidityStyleRules();
815 return element.willValidate() && !element.isValidFormControlElement();
816 case CSSSelector::PseudoChecked:
818 if (isHTMLInputElement(element)) {
819 HTMLInputElement& inputElement = toHTMLInputElement(element);
820 // Even though WinIE allows checked and indeterminate to
821 // co-exist, the CSS selector spec says that you can't be
822 // both checked and indeterminate. We will behave like WinIE
823 // behind the scenes and just obey the CSS spec here in the
824 // test for matching the pseudo.
825 if (inputElement.shouldAppearChecked() && !inputElement.shouldAppearIndeterminate())
827 } else if (isHTMLOptionElement(element) && toHTMLOptionElement(element).selected())
831 case CSSSelector::PseudoIndeterminate:
832 return element.shouldAppearIndeterminate();
833 case CSSSelector::PseudoRoot:
834 if (element == element.document().documentElement())
837 case CSSSelector::PseudoLang:
840 if (element.isVTTElement())
841 value = toVTTElement(element).language();
843 value = element.computeInheritedLanguage();
844 const AtomicString& argument = selector.argument();
845 if (value.isEmpty() || !value.startsWith(argument, false))
847 if (value.length() != argument.length() && value[argument.length()] != '-')
851 case CSSSelector::PseudoFullScreen:
852 // While a Document is in the fullscreen state, and the document's current fullscreen
853 // element is an element in the document, the 'full-screen' pseudoclass applies to
854 // that element. Also, an <iframe>, <object> or <embed> element whose child browsing
855 // context's Document is in the fullscreen state has the 'full-screen' pseudoclass applied.
856 if (isHTMLFrameElementBase(element) && element.containsFullScreenElement())
858 if (FullscreenElementStack* fullscreen = FullscreenElementStack::fromIfExists(element.document())) {
859 if (!fullscreen->webkitIsFullScreen())
861 return element == fullscreen->webkitCurrentFullScreenElement();
864 case CSSSelector::PseudoFullScreenAncestor:
865 return element.containsFullScreenElement();
866 case CSSSelector::PseudoFullScreenDocument:
867 // While a Document is in the fullscreen state, the 'full-screen-document' pseudoclass applies
868 // to all elements of that Document.
869 if (!FullscreenElementStack::isFullScreen(element.document()))
872 case CSSSelector::PseudoInRange:
873 element.document().setContainsValidityStyleRules();
874 return element.isInRange();
875 case CSSSelector::PseudoOutOfRange:
876 element.document().setContainsValidityStyleRules();
877 return element.isOutOfRange();
878 case CSSSelector::PseudoFutureCue:
879 return (element.isVTTElement() && !toVTTElement(element).isPastNode());
880 case CSSSelector::PseudoPastCue:
881 return (element.isVTTElement() && toVTTElement(element).isPastNode());
883 case CSSSelector::PseudoScope:
885 if (m_mode == SharingRules)
887 const Node* contextualReferenceNode = !context.scope ? element.document().documentElement() : context.scope;
888 if (element == contextualReferenceNode)
893 case CSSSelector::PseudoUnresolved:
894 if (element.isUnresolvedCustomElement())
898 case CSSSelector::PseudoHost:
899 case CSSSelector::PseudoHostContext:
901 if (m_mode == SharingRules)
903 // :host only matches a shadow host when :host is in a shadow tree of the shadow host.
906 const ContainerNode* shadowHost = context.scope->shadowHost();
907 if (!shadowHost || shadowHost != element)
909 ASSERT(element.shadow());
911 // For empty parameter case, i.e. just :host or :host().
912 if (!selector.selectorList()) // Use *'s specificity. So just 0.
915 SelectorCheckingContext subContext(context);
916 subContext.isSubSelector = true;
918 bool matched = false;
919 unsigned maxSpecificity = 0;
921 // If one of simple selectors matches an element, returns SelectorMatches. Just "OR".
922 for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
923 subContext.contextFlags = TreatShadowHostAsNormalScope;
924 subContext.scope = context.scope;
925 // Use NodeRenderingTraversal to traverse a composed ancestor list of a given element.
926 Element* nextElement = &element;
927 SelectorCheckingContext hostContext(subContext);
929 MatchResult subResult;
930 hostContext.element = nextElement;
931 if (match(hostContext, siblingTraversalStrategy, &subResult) == SelectorMatches) {
933 // Consider div:host(div:host(div:host(div:host...))).
934 maxSpecificity = std::max(maxSpecificity, hostContext.selector->specificity() + subResult.specificity);
937 hostContext.contextFlags = DefaultBehavior;
938 hostContext.scope = nullptr;
940 if (selector.pseudoType() == CSSSelector::PseudoHost)
943 hostContext.elementStyle = 0;
944 nextElement = NodeRenderingTraversal::parentElement(nextElement);
945 } while (nextElement);
949 *specificity = maxSpecificity;
954 case CSSSelector::PseudoSpatialNavigationFocus:
955 return context.isUARule && matchesSpatialNavigationFocusPseudoClass(element);
956 case CSSSelector::PseudoListBox:
957 return context.isUARule && matchesListBoxPseudoClass(element);
959 case CSSSelector::PseudoHorizontal:
960 case CSSSelector::PseudoVertical:
961 case CSSSelector::PseudoDecrement:
962 case CSSSelector::PseudoIncrement:
963 case CSSSelector::PseudoStart:
964 case CSSSelector::PseudoEnd:
965 case CSSSelector::PseudoDoubleButton:
966 case CSSSelector::PseudoSingleButton:
967 case CSSSelector::PseudoNoButton:
968 case CSSSelector::PseudoCornerPresent:
971 case CSSSelector::PseudoUnknown:
972 case CSSSelector::PseudoNotParsed:
974 ASSERT_NOT_REACHED();
978 } else if (selector.match() == CSSSelector::PseudoElement && selector.pseudoType() == CSSSelector::PseudoCue) {
979 SelectorCheckingContext subContext(context);
980 subContext.isSubSelector = true;
981 subContext.contextFlags = DefaultBehavior;
983 const CSSSelector* contextSelector = context.selector;
984 ASSERT(contextSelector);
985 for (subContext.selector = contextSelector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
986 if (match(subContext, siblingTraversalStrategy) == SelectorMatches)
991 // ### add the rest of the checks...
995 bool SelectorChecker::checkScrollbarPseudoClass(const SelectorCheckingContext& context, Document* document, const CSSSelector& selector) const
997 RenderScrollbar* scrollbar = context.scrollbar;
998 ScrollbarPart part = context.scrollbarPart;
1000 // FIXME: This is a temporary hack for resizers and scrollbar corners. Eventually :window-inactive should become a real
1001 // pseudo class and just apply to everything.
1002 if (selector.pseudoType() == CSSSelector::PseudoWindowInactive)
1003 return !document->page()->focusController().isActive();
1008 ASSERT(selector.match() == CSSSelector::PseudoClass);
1009 switch (selector.pseudoType()) {
1010 case CSSSelector::PseudoEnabled:
1011 return scrollbar->enabled();
1012 case CSSSelector::PseudoDisabled:
1013 return !scrollbar->enabled();
1014 case CSSSelector::PseudoHover:
1016 ScrollbarPart hoveredPart = scrollbar->hoveredPart();
1017 if (part == ScrollbarBGPart)
1018 return hoveredPart != NoPart;
1019 if (part == TrackBGPart)
1020 return hoveredPart == BackTrackPart || hoveredPart == ForwardTrackPart || hoveredPart == ThumbPart;
1021 return part == hoveredPart;
1023 case CSSSelector::PseudoActive:
1025 ScrollbarPart pressedPart = scrollbar->pressedPart();
1026 if (part == ScrollbarBGPart)
1027 return pressedPart != NoPart;
1028 if (part == TrackBGPart)
1029 return pressedPart == BackTrackPart || pressedPart == ForwardTrackPart || pressedPart == ThumbPart;
1030 return part == pressedPart;
1032 case CSSSelector::PseudoHorizontal:
1033 return scrollbar->orientation() == HorizontalScrollbar;
1034 case CSSSelector::PseudoVertical:
1035 return scrollbar->orientation() == VerticalScrollbar;
1036 case CSSSelector::PseudoDecrement:
1037 return part == BackButtonStartPart || part == BackButtonEndPart || part == BackTrackPart;
1038 case CSSSelector::PseudoIncrement:
1039 return part == ForwardButtonStartPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
1040 case CSSSelector::PseudoStart:
1041 return part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart;
1042 case CSSSelector::PseudoEnd:
1043 return part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
1044 case CSSSelector::PseudoDoubleButton:
1046 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1047 if (part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart)
1048 return buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth;
1049 if (part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart)
1050 return buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth;
1053 case CSSSelector::PseudoSingleButton:
1055 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1056 if (part == BackButtonStartPart || part == ForwardButtonEndPart || part == BackTrackPart || part == ForwardTrackPart)
1057 return buttonsPlacement == ScrollbarButtonsSingle;
1060 case CSSSelector::PseudoNoButton:
1062 ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1063 if (part == BackTrackPart)
1064 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleEnd;
1065 if (part == ForwardTrackPart)
1066 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleStart;
1069 case CSSSelector::PseudoCornerPresent:
1070 return scrollbar->scrollableArea()->isScrollCornerVisible();
1076 unsigned SelectorChecker::determineLinkMatchType(const CSSSelector& selector)
1078 unsigned linkMatchType = MatchAll;
1080 // Statically determine if this selector will match a link in visited, unvisited or any state, or never.
1081 // :visited never matches other elements than the innermost link element.
1082 for (const CSSSelector* current = &selector; current; current = current->tagHistory()) {
1083 switch (current->pseudoType()) {
1084 case CSSSelector::PseudoNot:
1086 // :not(:visited) is equivalent to :link. Parser enforces that :not can't nest.
1087 ASSERT(current->selectorList());
1088 for (const CSSSelector* subSelector = current->selectorList()->first(); subSelector; subSelector = subSelector->tagHistory()) {
1089 CSSSelector::PseudoType subType = subSelector->pseudoType();
1090 if (subType == CSSSelector::PseudoVisited)
1091 linkMatchType &= ~SelectorChecker::MatchVisited;
1092 else if (subType == CSSSelector::PseudoLink)
1093 linkMatchType &= ~SelectorChecker::MatchLink;
1097 case CSSSelector::PseudoLink:
1098 linkMatchType &= ~SelectorChecker::MatchVisited;
1100 case CSSSelector::PseudoVisited:
1101 linkMatchType &= ~SelectorChecker::MatchLink;
1104 // We don't support :link and :visited inside :-webkit-any.
1107 CSSSelector::Relation relation = current->relation();
1108 if (relation == CSSSelector::SubSelector)
1110 if (relation != CSSSelector::Descendant && relation != CSSSelector::Child)
1111 return linkMatchType;
1112 if (linkMatchType != MatchAll)
1113 return linkMatchType;
1115 return linkMatchType;
1118 bool SelectorChecker::isFrameFocused(const Element& element)
1120 return element.document().frame() && element.document().frame()->selection().isFocusedAndActive();
1123 bool SelectorChecker::matchesFocusPseudoClass(const Element& element)
1125 if (InspectorInstrumentation::forcePseudoState(const_cast<Element*>(&element), CSSSelector::PseudoFocus))
1127 return element.focused() && isFrameFocused(element);
1130 bool SelectorChecker::matchesSpatialNavigationFocusPseudoClass(const Element& element)
1132 return isHTMLOptionElement(element) && toHTMLOptionElement(element).spatialNavigationFocused() && isFrameFocused(element);
1135 bool SelectorChecker::matchesListBoxPseudoClass(const Element& element)
1137 return isHTMLSelectElement(element) && !toHTMLSelectElement(element).usesMenuList();
1141 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const DOMSiblingTraversalStrategy&, MatchResult*) const;
1144 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const ShadowDOMSiblingTraversalStrategy&, MatchResult*) const;