bc747ea610a068ff53b679cf307934cc24ebb6a0
[WebKit.git] / Source / WebCore / css / SelectorChecker.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
4  * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
5  * Copyright (C) 2005-2016 Apple Inc. All rights reserved.
6  * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
7  * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
8  * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
9  * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
10  * Copyright (C) Research In Motion Limited 2011. All rights reserved.
11  * Copyright (C) 2014 Yusuke Suzuki <utatane.tea@gmail.com>
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Library General Public
15  * License as published by the Free Software Foundation; either
16  * version 2 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Library General Public License for more details.
22  *
23  * You should have received a copy of the GNU Library General Public License
24  * along with this library; see the file COPYING.LIB.  If not, write to
25  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26  * Boston, MA 02110-1301, USA.
27  */
28
29 #include "config.h"
30 #include "SelectorChecker.h"
31
32 #include "CSSSelector.h"
33 #include "CSSSelectorList.h"
34 #include "Document.h"
35 #include "ElementTraversal.h"
36 #include "Frame.h"
37 #include "FrameSelection.h"
38 #include "HTMLAnchorElement.h"
39 #include "HTMLDocument.h"
40 #include "HTMLFrameElementBase.h"
41 #include "HTMLInputElement.h"
42 #include "HTMLNames.h"
43 #include "HTMLOptGroupElement.h"
44 #include "HTMLOptionElement.h"
45 #include "HTMLParserIdioms.h"
46 #include "HTMLProgressElement.h"
47 #include "HTMLSlotElement.h"
48 #include "HTMLStyleElement.h"
49 #include "InspectorInstrumentation.h"
50 #include "Page.h"
51 #include "RenderElement.h"
52 #include "SelectorCheckerTestFunctions.h"
53 #include "ShadowRoot.h"
54 #include "StyledElement.h"
55 #include "Text.h"
56
57 namespace WebCore {
58
59 using namespace HTMLNames;
60
61 enum class VisitedMatchType : unsigned char {
62     Disabled, Enabled
63 };
64
65 struct SelectorChecker::LocalContext {
66     LocalContext(const CSSSelector& selector, const Element& element, VisitedMatchType visitedMatchType, PseudoId pseudoId)
67         : selector(&selector)
68         , element(&element)
69         , visitedMatchType(visitedMatchType)
70         , firstSelectorOfTheFragment(&selector)
71         , pseudoId(pseudoId)
72     { }
73
74     const CSSSelector* selector;
75     const Element* element;
76     VisitedMatchType visitedMatchType;
77     const CSSSelector* firstSelectorOfTheFragment;
78     PseudoId pseudoId;
79     bool isMatchElement { true };
80     bool inFunctionalPseudoClass { false };
81     bool pseudoElementEffective { true };
82     bool hasScrollbarPseudo { false };
83     bool hasSelectionPseudo { false };
84
85 };
86
87 static inline void addStyleRelation(SelectorChecker::CheckingContext& checkingContext, const Element& element, Style::Relation::Type type, unsigned value = 1)
88 {
89     ASSERT(value == 1 || type == Style::Relation::NthChildIndex || type == Style::Relation::AffectedByEmpty);
90     if (checkingContext.resolvingMode != SelectorChecker::Mode::ResolvingStyle)
91         return;
92     if (type == Style::Relation::AffectsNextSibling && !checkingContext.styleRelations.isEmpty()) {
93         auto& last = checkingContext.styleRelations.last();
94         if (last.type == Style::Relation::AffectsNextSibling && last.element == element.nextElementSibling()) {
95             ++last.value;
96             last.element = &element;
97             return;
98         }
99     }
100     checkingContext.styleRelations.append({ element, type, value });
101 }
102
103 static inline bool isFirstChildElement(const Element& element)
104 {
105     return !ElementTraversal::previousSibling(element);
106 }
107
108 static inline bool isLastChildElement(const Element& element)
109 {
110     return !ElementTraversal::nextSibling(element);
111 }
112
113 static inline bool isFirstOfType(SelectorChecker::CheckingContext& checkingContext, const Element& element, const QualifiedName& type)
114 {
115     for (const Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(*sibling)) {
116         addStyleRelation(checkingContext, *sibling, Style::Relation::AffectsNextSibling);
117
118         if (sibling->hasTagName(type))
119             return false;
120     }
121     return true;
122 }
123
124 static inline bool isLastOfType(const Element& element, const QualifiedName& type)
125 {
126     for (const Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(*sibling)) {
127         if (sibling->hasTagName(type))
128             return false;
129     }
130     return true;
131 }
132
133 static inline int countElementsBefore(SelectorChecker::CheckingContext& checkingContext, const Element& element)
134 {
135     int count = 0;
136     for (const Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(*sibling)) {
137
138         addStyleRelation(checkingContext, *sibling, Style::Relation::AffectsNextSibling);
139
140         unsigned index = sibling->childIndex();
141         if (index) {
142             count += index;
143             break;
144         }
145         count++;
146     }
147     return count;
148 }
149
150 static inline int countElementsOfTypeBefore(SelectorChecker::CheckingContext& checkingContext, const Element& element, const QualifiedName& type)
151 {
152     int count = 0;
153     for (const Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(*sibling)) {
154         addStyleRelation(checkingContext, *sibling, Style::Relation::AffectsNextSibling);
155
156         if (sibling->hasTagName(type))
157             ++count;
158     }
159     return count;
160 }
161
162 static inline int countElementsAfter(const Element& element)
163 {
164     int count = 0;
165     for (const Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(*sibling))
166         ++count;
167     return count;
168 }
169
170 static inline int countElementsOfTypeAfter(const Element& element, const QualifiedName& type)
171 {
172     int count = 0;
173     for (const Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(*sibling)) {
174         if (sibling->hasTagName(type))
175             ++count;
176     }
177     return count;
178 }
179
180 SelectorChecker::SelectorChecker(Document& document)
181     : m_strictParsing(!document.inQuirksMode())
182     , m_documentIsHTML(document.isHTMLDocument())
183 {
184 }
185
186 bool SelectorChecker::match(const CSSSelector& selector, const Element& element, CheckingContext& checkingContext, unsigned& specificity) const
187 {
188     specificity = 0;
189
190     LocalContext context(selector, element, checkingContext.resolvingMode == SelectorChecker::Mode::QueryingRules ? VisitedMatchType::Disabled : VisitedMatchType::Enabled, checkingContext.pseudoId);
191     PseudoIdSet pseudoIdSet;
192     MatchResult result = matchRecursively(checkingContext, context, pseudoIdSet, specificity);
193     if (result.match != Match::SelectorMatches)
194         return false;
195     if (checkingContext.pseudoId != NOPSEUDO && !pseudoIdSet.has(checkingContext.pseudoId))
196         return false;
197
198     if (checkingContext.pseudoId == NOPSEUDO && pseudoIdSet) {
199         PseudoIdSet publicPseudoIdSet = pseudoIdSet & PseudoIdSet::fromMask(PUBLIC_PSEUDOID_MASK);
200         if (checkingContext.resolvingMode == Mode::ResolvingStyle && publicPseudoIdSet)
201             checkingContext.pseudoIDSet = publicPseudoIdSet;
202
203         // When ignoring virtual pseudo elements, the context's pseudo should also be NOPSEUDO but that does
204         // not cause a failure.
205         return checkingContext.resolvingMode == Mode::CollectingRulesIgnoringVirtualPseudoElements || result.matchType == MatchType::Element;
206     }
207     return true;
208 }
209
210 bool SelectorChecker::matchHostPseudoClass(const CSSSelector& selector, const Element& element, CheckingContext& checkingContext, unsigned& specificity) const
211 {
212     ASSERT(element.shadowRoot());
213     ASSERT(selector.match() == CSSSelector::PseudoClass && selector.pseudoClassType() == CSSSelector::PseudoClassHost);
214     ASSERT(checkingContext.resolvingMode != SelectorChecker::Mode::QueryingRules);
215
216     specificity = selector.simpleSelectorSpecificity();
217
218     // :host doesn't combine with any other selectors.
219     if (selector.tagHistory())
220         return false;
221     
222     if (auto* selectorList = selector.selectorList()) {
223         LocalContext context(*selectorList->first(), element, VisitedMatchType::Enabled, NOPSEUDO);
224         context.inFunctionalPseudoClass = true;
225         context.pseudoElementEffective = false;
226         PseudoIdSet ignoreDynamicPseudo;
227         unsigned subselectorSpecificity = 0;
228         if (matchRecursively(checkingContext, context, ignoreDynamicPseudo, subselectorSpecificity).match != Match::SelectorMatches)
229             return false;
230         specificity = CSSSelector::addSpecificities(specificity, subselectorSpecificity);
231     }
232     return true;
233 }
234
235 inline static bool hasScrollbarPseudoElement(const PseudoIdSet& dynamicPseudoIdSet)
236 {
237     PseudoIdSet scrollbarIdSet = { SCROLLBAR, SCROLLBAR_THUMB, SCROLLBAR_BUTTON, SCROLLBAR_TRACK, SCROLLBAR_TRACK_PIECE, SCROLLBAR_CORNER };
238     if (dynamicPseudoIdSet & scrollbarIdSet)
239         return true;
240
241     // RESIZER does not always have a scrollbar but it is a scrollbar-like pseudo element
242     // because it can have more than one pseudo element.
243     return dynamicPseudoIdSet.has(RESIZER);
244 }
245
246 static SelectorChecker::LocalContext localContextForParent(const SelectorChecker::LocalContext& context)
247 {
248     SelectorChecker::LocalContext updatedContext(context);
249     // Disable :visited matching when we see the first link.
250     if (context.element->isLink())
251         updatedContext.visitedMatchType = VisitedMatchType::Disabled;
252     updatedContext.element = context.element->parentElement();
253     updatedContext.isMatchElement = false;
254     return updatedContext;
255 }
256
257 // Recursive check of selectors and combinators
258 // It can return 4 different values:
259 // * SelectorMatches          - the selector matches the element e
260 // * SelectorFailsLocally     - the selector fails for the element e
261 // * SelectorFailsAllSiblings - the selector fails for e and any sibling of e
262 // * SelectorFailsCompletely  - the selector fails for e and any sibling or ancestor of e
263 SelectorChecker::MatchResult SelectorChecker::matchRecursively(CheckingContext& checkingContext, const LocalContext& context, PseudoIdSet& dynamicPseudoIdSet, unsigned& specificity) const
264 {
265     MatchType matchType = MatchType::Element;
266
267     // The first selector has to match.
268     if (!checkOne(checkingContext, context, dynamicPseudoIdSet, matchType, specificity))
269         return MatchResult::fails(Match::SelectorFailsLocally);
270
271     if (context.selector->match() == CSSSelector::PseudoElement) {
272         if (context.selector->isCustomPseudoElement()) {
273             // In functional pseudo class, custom pseudo elements are always disabled.
274             // FIXME: We should accept custom pseudo elements inside :matches().
275             if (context.inFunctionalPseudoClass)
276                 return MatchResult::fails(Match::SelectorFailsCompletely);
277             if (ShadowRoot* root = context.element->containingShadowRoot()) {
278                 if (context.element->shadowPseudoId() != context.selector->value())
279                     return MatchResult::fails(Match::SelectorFailsLocally);
280
281                 if (context.selector->isWebKitCustomPseudoElement() && root->type() != ShadowRoot::Type::UserAgent)
282                     return MatchResult::fails(Match::SelectorFailsLocally);
283             } else
284                 return MatchResult::fails(Match::SelectorFailsLocally);
285         } else {
286             if (!context.pseudoElementEffective)
287                 return MatchResult::fails(Match::SelectorFailsCompletely);
288
289             if (checkingContext.resolvingMode == Mode::QueryingRules)
290                 return MatchResult::fails(Match::SelectorFailsCompletely);
291
292             PseudoId pseudoId = CSSSelector::pseudoId(context.selector->pseudoElementType());
293             if (pseudoId != NOPSEUDO)
294                 dynamicPseudoIdSet.add(pseudoId);
295             matchType = MatchType::VirtualPseudoElementOnly;
296         }
297     }
298
299     // The rest of the selectors has to match
300     CSSSelector::Relation relation = context.selector->relation();
301
302     // Prepare next selector
303     const CSSSelector* historySelector = context.selector->tagHistory();
304     if (!historySelector)
305         return MatchResult::matches(matchType);
306
307     LocalContext nextContext(context);
308     nextContext.selector = historySelector;
309
310     if (relation != CSSSelector::SubSelector) {
311         // Bail-out if this selector is irrelevant for the pseudoId
312         if (context.pseudoId != NOPSEUDO && !dynamicPseudoIdSet.has(context.pseudoId))
313             return MatchResult::fails(Match::SelectorFailsCompletely);
314
315         // Disable :visited matching when we try to match anything else than an ancestors.
316         if (relation != CSSSelector::Descendant && relation != CSSSelector::Child)
317             nextContext.visitedMatchType = VisitedMatchType::Disabled;
318
319         nextContext.pseudoId = NOPSEUDO;
320         // Virtual pseudo element is only effective in the rightmost fragment.
321         nextContext.pseudoElementEffective = false;
322         nextContext.isMatchElement = false;
323     }
324
325     switch (relation) {
326     case CSSSelector::Descendant:
327         nextContext = localContextForParent(nextContext);
328         nextContext.firstSelectorOfTheFragment = nextContext.selector;
329         for (; nextContext.element; nextContext = localContextForParent(nextContext)) {
330             PseudoIdSet ignoreDynamicPseudo;
331             unsigned descendantsSpecificity = 0;
332             MatchResult result = matchRecursively(checkingContext, nextContext, ignoreDynamicPseudo, descendantsSpecificity);
333             ASSERT(!nextContext.pseudoElementEffective && !ignoreDynamicPseudo);
334
335             if (result.match == Match::SelectorMatches)
336                 specificity = CSSSelector::addSpecificities(specificity, descendantsSpecificity);
337
338             if (result.match == Match::SelectorMatches || result.match == Match::SelectorFailsCompletely)
339                 return MatchResult::updateWithMatchType(result, matchType);
340         }
341         return MatchResult::fails(Match::SelectorFailsCompletely);
342
343     case CSSSelector::Child:
344         {
345             nextContext = localContextForParent(nextContext);
346             if (!nextContext.element)
347                 return MatchResult::fails(Match::SelectorFailsCompletely);
348             nextContext.firstSelectorOfTheFragment = nextContext.selector;
349             PseudoIdSet ignoreDynamicPseudo;
350             unsigned childSpecificity = 0;
351             MatchResult result = matchRecursively(checkingContext, nextContext, ignoreDynamicPseudo, childSpecificity);
352             ASSERT(!nextContext.pseudoElementEffective && !ignoreDynamicPseudo);
353
354             if (result.match == Match::SelectorMatches)
355                 specificity = CSSSelector::addSpecificities(specificity, childSpecificity);
356
357             if (result.match == Match::SelectorMatches || result.match == Match::SelectorFailsCompletely)
358                 return MatchResult::updateWithMatchType(result, matchType);
359             return MatchResult::fails(Match::SelectorFailsAllSiblings);
360         }
361
362     case CSSSelector::DirectAdjacent:
363         {
364             addStyleRelation(checkingContext, *context.element, Style::Relation::AffectedByPreviousSibling);
365
366             Element* previousElement = context.element->previousElementSibling();
367             if (!previousElement)
368                 return MatchResult::fails(Match::SelectorFailsAllSiblings);
369
370             addStyleRelation(checkingContext, *previousElement, Style::Relation::AffectsNextSibling);
371
372             nextContext.element = previousElement;
373             nextContext.firstSelectorOfTheFragment = nextContext.selector;
374             PseudoIdSet ignoreDynamicPseudo;
375             unsigned adjacentSpecificity = 0;
376             MatchResult result = matchRecursively(checkingContext, nextContext, ignoreDynamicPseudo, adjacentSpecificity);
377             ASSERT(!nextContext.pseudoElementEffective && !ignoreDynamicPseudo);
378
379             if (result.match == Match::SelectorMatches)
380                 specificity = CSSSelector::addSpecificities(specificity, adjacentSpecificity);
381
382             return MatchResult::updateWithMatchType(result, matchType);
383         }
384     case CSSSelector::IndirectAdjacent:
385         addStyleRelation(checkingContext, *context.element, Style::Relation::AffectedByPreviousSibling);
386
387         nextContext.element = context.element->previousElementSibling();
388         nextContext.firstSelectorOfTheFragment = nextContext.selector;
389         for (; nextContext.element; nextContext.element = nextContext.element->previousElementSibling()) {
390             addStyleRelation(checkingContext, *nextContext.element, Style::Relation::AffectsNextSibling);
391
392             PseudoIdSet ignoreDynamicPseudo;
393             unsigned indirectAdjacentSpecificity = 0;
394             MatchResult result = matchRecursively(checkingContext, nextContext, ignoreDynamicPseudo, indirectAdjacentSpecificity);
395             ASSERT(!nextContext.pseudoElementEffective && !ignoreDynamicPseudo);
396
397             if (result.match == Match::SelectorMatches)
398                 specificity = CSSSelector::addSpecificities(specificity, indirectAdjacentSpecificity);
399
400             if (result.match == Match::SelectorMatches || result.match == Match::SelectorFailsAllSiblings || result.match == Match::SelectorFailsCompletely)
401                 return MatchResult::updateWithMatchType(result, matchType);
402         };
403         return MatchResult::fails(Match::SelectorFailsAllSiblings);
404
405     case CSSSelector::SubSelector:
406         {
407             // a selector is invalid if something follows a pseudo-element
408             // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else)
409             // to follow the pseudo elements.
410             nextContext.hasScrollbarPseudo = hasScrollbarPseudoElement(dynamicPseudoIdSet);
411             nextContext.hasSelectionPseudo = dynamicPseudoIdSet.has(SELECTION);
412             if ((context.isMatchElement || checkingContext.resolvingMode == Mode::CollectingRules) && dynamicPseudoIdSet
413                 && !nextContext.hasSelectionPseudo
414                 && !(nextContext.hasScrollbarPseudo && nextContext.selector->match() == CSSSelector::PseudoClass))
415                 return MatchResult::fails(Match::SelectorFailsCompletely);
416
417             unsigned subselectorSpecificity = 0;
418             MatchResult result = matchRecursively(checkingContext, nextContext, dynamicPseudoIdSet, subselectorSpecificity);
419
420             if (result.match == Match::SelectorMatches)
421                 specificity = CSSSelector::addSpecificities(specificity, subselectorSpecificity);
422
423             return MatchResult::updateWithMatchType(result, matchType);
424         }
425     case CSSSelector::ShadowDescendant:
426         {
427             Element* shadowHostNode = context.element->shadowHost();
428             if (!shadowHostNode)
429                 return MatchResult::fails(Match::SelectorFailsCompletely);
430             nextContext.element = shadowHostNode;
431             nextContext.firstSelectorOfTheFragment = nextContext.selector;
432             PseudoIdSet ignoreDynamicPseudo;
433             unsigned shadowDescendantSpecificity = 0;
434             MatchResult result = matchRecursively(checkingContext, nextContext, ignoreDynamicPseudo, shadowDescendantSpecificity);
435
436             if (result.match == Match::SelectorMatches)
437                 specificity = CSSSelector::addSpecificities(specificity, shadowDescendantSpecificity);
438
439             return MatchResult::updateWithMatchType(result, matchType);
440         }
441     }
442
443     ASSERT_NOT_REACHED();
444     return MatchResult::fails(Match::SelectorFailsCompletely);
445 }
446
447 static bool attributeValueMatches(const Attribute& attribute, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
448 {
449     const AtomicString& value = attribute.value();
450     ASSERT(!value.isNull());
451
452     switch (match) {
453     case CSSSelector::Set:
454         break;
455     case CSSSelector::Exact:
456         if (caseSensitive ? selectorValue != value : !equalIgnoringASCIICase(selectorValue, value))
457             return false;
458         break;
459     case CSSSelector::List:
460         {
461             // Ignore empty selectors or selectors containing spaces.
462             if (selectorValue.isEmpty() || selectorValue.find(isHTMLSpace<UChar>) != notFound)
463                 return false;
464
465             unsigned startSearchAt = 0;
466             while (true) {
467                 size_t foundPos;
468                 if (caseSensitive)
469                     foundPos = value.find(selectorValue, startSearchAt);
470                 else
471                     foundPos = value.findIgnoringASCIICase(selectorValue, startSearchAt);
472                 if (foundPos == notFound)
473                     return false;
474                 if (!foundPos || isHTMLSpace(value[foundPos - 1])) {
475                     unsigned endStr = foundPos + selectorValue.length();
476                     if (endStr == value.length() || isHTMLSpace(value[endStr]))
477                         break; // We found a match.
478                 }
479
480                 // No match. Keep looking.
481                 startSearchAt = foundPos + 1;
482             }
483             break;
484         }
485     case CSSSelector::Contain: {
486         bool valueContainsSelectorValue;
487         if (caseSensitive)
488             valueContainsSelectorValue = value.contains(selectorValue);
489         else
490             valueContainsSelectorValue = value.containsIgnoringASCIICase(selectorValue);
491
492         if (!valueContainsSelectorValue || selectorValue.isEmpty())
493             return false;
494
495         break;
496     }
497     case CSSSelector::Begin:
498         if (selectorValue.isEmpty())
499             return false;
500         if (caseSensitive) {
501             if (!value.startsWith(selectorValue))
502                 return false;
503         } else {
504             if (!value.startsWithIgnoringASCIICase(selectorValue))
505                 return false;
506         }
507         break;
508     case CSSSelector::End:
509         if (selectorValue.isEmpty())
510             return false;
511         if (caseSensitive) {
512             if (!value.endsWith(selectorValue))
513                 return false;
514         } else {
515             if (!value.endsWithIgnoringASCIICase(selectorValue))
516                 return false;
517         }
518         break;
519     case CSSSelector::Hyphen:
520         if (value.length() < selectorValue.length())
521             return false;
522         if (caseSensitive) {
523             if (!value.startsWith(selectorValue))
524                 return false;
525         } else {
526             if (!value.startsWithIgnoringASCIICase(selectorValue))
527                 return false;
528         }
529         // It they start the same, check for exact match or following '-':
530         if (value.length() != selectorValue.length() && value[selectorValue.length()] != '-')
531             return false;
532         break;
533     default:
534         ASSERT_NOT_REACHED();
535         return false;
536     }
537
538     return true;
539 }
540
541 static bool anyAttributeMatches(const Element& element, const CSSSelector& selector, const QualifiedName& selectorAttr, bool caseSensitive)
542 {
543     ASSERT(element.hasAttributesWithoutUpdate());
544     for (const Attribute& attribute : element.attributesIterator()) {
545         if (!attribute.matches(selectorAttr.prefix(), element.isHTMLElement() ? selector.attributeCanonicalLocalName() : selectorAttr.localName(), selectorAttr.namespaceURI()))
546             continue;
547
548         if (attributeValueMatches(attribute, selector.match(), selector.value(), caseSensitive))
549             return true;
550     }
551
552     return false;
553 }
554
555 bool SelectorChecker::attributeSelectorMatches(const Element& element, const QualifiedName& attributeName, const AtomicString& attributeValue, const CSSSelector& selector)
556 {
557     ASSERT(selector.isAttributeSelector());
558     auto& selectorAttribute = selector.attribute();
559     auto& selectorName = element.isHTMLElement() ? selector.attributeCanonicalLocalName() : selectorAttribute.localName();
560     if (!Attribute::nameMatchesFilter(attributeName, selectorAttribute.prefix(), selectorName, selectorAttribute.namespaceURI()))
561         return false;
562     bool caseSensitive = true;
563     if (selector.attributeValueMatchingIsCaseInsensitive())
564         caseSensitive = false;
565     else if (element.document().isHTMLDocument() && element.isHTMLElement() && !HTMLDocument::isCaseSensitiveAttribute(selector.attribute()))
566         caseSensitive = false;
567     return attributeValueMatches(Attribute(attributeName, attributeValue), selector.match(), selector.value(), caseSensitive);
568 }
569
570 static bool canMatchHoverOrActiveInQuirksMode(const SelectorChecker::LocalContext& context)
571 {
572     // For quirks mode, follow this: http://quirks.spec.whatwg.org/#the-:active-and-:hover-quirk
573     // In quirks mode, a compound selector 'selector' that matches the following conditions must not match elements that would not also match the ':any-link' selector.
574     //
575     //    selector uses the ':active' or ':hover' pseudo-classes.
576     //    selector does not use a type selector.
577     //    selector does not use an attribute selector.
578     //    selector does not use an ID selector.
579     //    selector does not use a class selector.
580     //    selector does not use a pseudo-class selector other than ':active' and ':hover'.
581     //    selector does not use a pseudo-element selector.
582     //    selector is not part of an argument to a functional pseudo-class or pseudo-element.
583     if (context.inFunctionalPseudoClass)
584         return true;
585
586     for (const CSSSelector* selector = context.firstSelectorOfTheFragment; selector; selector = selector->tagHistory()) {
587         switch (selector->match()) {
588         case CSSSelector::Tag:
589             if (selector->tagQName() != anyQName())
590                 return true;
591             break;
592         case CSSSelector::PseudoClass: {
593             CSSSelector::PseudoClassType pseudoClassType = selector->pseudoClassType();
594             if (pseudoClassType != CSSSelector::PseudoClassHover && pseudoClassType != CSSSelector::PseudoClassActive)
595                 return true;
596             break;
597         }
598         case CSSSelector::Id:
599         case CSSSelector::Class:
600         case CSSSelector::Exact:
601         case CSSSelector::Set:
602         case CSSSelector::List:
603         case CSSSelector::Hyphen:
604         case CSSSelector::Contain:
605         case CSSSelector::Begin:
606         case CSSSelector::End:
607         case CSSSelector::PagePseudoClass:
608         case CSSSelector::PseudoElement:
609             return true;
610         case CSSSelector::Unknown:
611             ASSERT_NOT_REACHED();
612             break;
613         }
614
615         CSSSelector::Relation relation = selector->relation();
616         if (relation == CSSSelector::ShadowDescendant)
617             return true;
618
619         if (relation != CSSSelector::SubSelector)
620             return false;
621     }
622     return false;
623 }
624
625 static inline bool tagMatches(const Element& element, const CSSSelector& simpleSelector)
626 {
627     const QualifiedName& tagQName = simpleSelector.tagQName();
628
629     if (tagQName == anyQName())
630         return true;
631
632     const AtomicString& localName = (element.isHTMLElement() && element.document().isHTMLDocument()) ? simpleSelector.tagLowercaseLocalName() : tagQName.localName();
633
634     if (localName != starAtom && localName != element.localName())
635         return false;
636     const AtomicString& namespaceURI = tagQName.namespaceURI();
637     return namespaceURI == starAtom || namespaceURI == element.namespaceURI();
638 }
639
640 bool SelectorChecker::checkOne(CheckingContext& checkingContext, const LocalContext& context, PseudoIdSet& dynamicPseudoIdSet, MatchType& matchType, unsigned& specificity) const
641 {
642     const Element& element = *context.element;
643     const CSSSelector& selector = *context.selector;
644
645     specificity = CSSSelector::addSpecificities(specificity, selector.simpleSelectorSpecificity());
646
647     if (selector.match() == CSSSelector::Tag)
648         return tagMatches(element, selector);
649
650     if (selector.match() == CSSSelector::Class)
651         return element.hasClass() && element.classNames().contains(selector.value());
652
653     if (selector.match() == CSSSelector::Id) {
654         ASSERT(!selector.value().isNull());
655         return element.idForStyleResolution() == selector.value();
656     }
657
658     if (selector.isAttributeSelector()) {
659         if (!element.hasAttributes())
660             return false;
661
662         const QualifiedName& attr = selector.attribute();
663         bool caseSensitive = true;
664         if (selector.attributeValueMatchingIsCaseInsensitive())
665             caseSensitive = false;
666         else if (m_documentIsHTML && element.isHTMLElement() && !HTMLDocument::isCaseSensitiveAttribute(attr))
667             caseSensitive = false;
668
669         return anyAttributeMatches(element, selector, attr, caseSensitive);
670     }
671
672     if (selector.match() == CSSSelector::PseudoClass) {
673         // Handle :not up front.
674         if (selector.pseudoClassType() == CSSSelector::PseudoClassNot) {
675             const CSSSelectorList* selectorList = selector.selectorList();
676
677             for (const CSSSelector* subselector = selectorList->first(); subselector; subselector = CSSSelectorList::next(subselector)) {
678                 LocalContext subcontext(context);
679                 subcontext.inFunctionalPseudoClass = true;
680                 subcontext.pseudoElementEffective = false;
681                 subcontext.selector = subselector;
682                 subcontext.firstSelectorOfTheFragment = selectorList->first();
683                 PseudoIdSet ignoreDynamicPseudo;
684
685                 unsigned ignoredSpecificity;
686                 if (matchRecursively(checkingContext, subcontext, ignoreDynamicPseudo, ignoredSpecificity).match == Match::SelectorMatches) {
687                     ASSERT(!ignoreDynamicPseudo);
688                     return false;
689                 }
690             }
691             return true;
692         }
693         if (context.hasScrollbarPseudo) {
694             // CSS scrollbars match a specific subset of pseudo classes, and they have specialized rules for each
695             // (since there are no elements involved except with window-inactive).
696             return checkScrollbarPseudoClass(checkingContext, element, selector);
697         }
698
699         // Normal element pseudo class checking.
700         switch (selector.pseudoClassType()) {
701             // Pseudo classes:
702         case CSSSelector::PseudoClassNot:
703             break; // Already handled up above.
704         case CSSSelector::PseudoClassEmpty:
705             {
706                 bool result = true;
707                 for (Node* node = element.firstChild(); node; node = node->nextSibling()) {
708                     if (is<Element>(*node)) {
709                         result = false;
710                         break;
711                     }
712                     if (is<Text>(*node)) {
713                         Text& textNode = downcast<Text>(*node);
714                         if (!textNode.data().isEmpty()) {
715                             result = false;
716                             break;
717                         }
718                     }
719                 }
720                 addStyleRelation(checkingContext, *context.element, Style::Relation::AffectedByEmpty, result);
721
722                 return result;
723             }
724         case CSSSelector::PseudoClassFirstChild:
725             // first-child matches the first child that is an element
726             if (const Element* parentElement = element.parentElement()) {
727                 bool isFirstChild = isFirstChildElement(element);
728                 if (isFirstChild)
729                     addStyleRelation(checkingContext, element, Style::Relation::FirstChild);
730                 addStyleRelation(checkingContext, *parentElement, Style::Relation::ChildrenAffectedByFirstChildRules);
731                 return isFirstChild;
732             }
733             break;
734         case CSSSelector::PseudoClassFirstOfType:
735             // first-of-type matches the first element of its type
736             if (element.parentElement()) {
737                 addStyleRelation(checkingContext, element, Style::Relation::AffectedByPreviousSibling);
738                 return isFirstOfType(checkingContext, element, element.tagQName());
739             }
740             break;
741         case CSSSelector::PseudoClassLastChild:
742             // last-child matches the last child that is an element
743             if (const Element* parentElement = element.parentElement()) {
744                 bool isLastChild = parentElement->isFinishedParsingChildren() && isLastChildElement(element);
745                 if (isLastChild)
746                     addStyleRelation(checkingContext, element, Style::Relation::LastChild);
747                 addStyleRelation(checkingContext, *parentElement, Style::Relation::ChildrenAffectedByLastChildRules);
748                 return isLastChild;
749             }
750             break;
751         case CSSSelector::PseudoClassLastOfType:
752             // last-of-type matches the last element of its type
753             if (Element* parentElement = element.parentElement()) {
754                 addStyleRelation(checkingContext, *parentElement, Style::Relation::ChildrenAffectedByBackwardPositionalRules);
755                 if (!parentElement->isFinishedParsingChildren())
756                     return false;
757                 return isLastOfType(element, element.tagQName());
758             }
759             break;
760         case CSSSelector::PseudoClassOnlyChild:
761             if (Element* parentElement = element.parentElement()) {
762                 bool firstChild = isFirstChildElement(element);
763                 bool onlyChild = firstChild && parentElement->isFinishedParsingChildren() && isLastChildElement(element);
764                 addStyleRelation(checkingContext, *parentElement, Style::Relation::ChildrenAffectedByFirstChildRules);
765                 addStyleRelation(checkingContext, *parentElement, Style::Relation::ChildrenAffectedByLastChildRules);
766                 if (firstChild)
767                     addStyleRelation(checkingContext, element, Style::Relation::FirstChild);
768                 if (onlyChild)
769                     addStyleRelation(checkingContext, element, Style::Relation::LastChild);
770                 return onlyChild;
771             }
772             break;
773         case CSSSelector::PseudoClassOnlyOfType:
774             // FIXME: This selector is very slow.
775             if (Element* parentElement = element.parentElement()) {
776                 addStyleRelation(checkingContext, element, Style::Relation::AffectedByPreviousSibling);
777                 addStyleRelation(checkingContext, *parentElement, Style::Relation::ChildrenAffectedByBackwardPositionalRules);
778                 if (!parentElement->isFinishedParsingChildren())
779                     return false;
780                 return isFirstOfType(checkingContext, element, element.tagQName()) && isLastOfType(element, element.tagQName());
781             }
782             break;
783         case CSSSelector::PseudoClassMatches:
784             {
785                 bool hasMatchedAnything = false;
786                 unsigned maxSpecificity = 0;
787
788                 MatchType localMatchType = MatchType::VirtualPseudoElementOnly;
789                 for (const CSSSelector* subselector = selector.selectorList()->first(); subselector; subselector = CSSSelectorList::next(subselector)) {
790                     LocalContext subcontext(context);
791                     subcontext.inFunctionalPseudoClass = true;
792                     subcontext.pseudoElementEffective = context.pseudoElementEffective;
793                     subcontext.selector = subselector;
794                     subcontext.firstSelectorOfTheFragment = subselector;
795                     PseudoIdSet localDynamicPseudoIdSet;
796                     unsigned localSpecificity = 0;
797                     MatchResult result = matchRecursively(checkingContext, subcontext, localDynamicPseudoIdSet, localSpecificity);
798                     if (result.match == Match::SelectorMatches) {
799                         maxSpecificity = std::max(maxSpecificity, localSpecificity);
800
801                         if (result.matchType == MatchType::Element)
802                             localMatchType = MatchType::Element;
803
804                         dynamicPseudoIdSet.merge(localDynamicPseudoIdSet);
805                         hasMatchedAnything = true;
806                     }
807                 }
808                 if (hasMatchedAnything) {
809                     matchType = localMatchType;
810                     specificity = CSSSelector::addSpecificities(specificity, maxSpecificity);
811                 }
812                 return hasMatchedAnything;
813             }
814         case CSSSelector::PseudoClassPlaceholderShown:
815             if (is<HTMLTextFormControlElement>(element)) {
816                 addStyleRelation(checkingContext, element, Style::Relation::Unique);
817                 return downcast<HTMLTextFormControlElement>(element).isPlaceholderVisible();
818             }
819             return false;
820         case CSSSelector::PseudoClassNthChild:
821             if (!selector.parseNth())
822                 break;
823             if (element.parentElement()) {
824                 if (const CSSSelectorList* selectorList = selector.selectorList()) {
825                     unsigned selectorListSpecificity;
826                     if (!matchSelectorList(checkingContext, context, element, *selectorList, selectorListSpecificity))
827                         return false;
828                     specificity = CSSSelector::addSpecificities(specificity, selectorListSpecificity);
829                 }
830
831                 addStyleRelation(checkingContext, element, Style::Relation::AffectedByPreviousSibling);
832
833                 int count = 1;
834                 if (const CSSSelectorList* selectorList = selector.selectorList()) {
835                     for (Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(*sibling)) {
836                         addStyleRelation(checkingContext, *sibling, Style::Relation::AffectsNextSibling);
837
838                         unsigned ignoredSpecificity;
839                         if (matchSelectorList(checkingContext, context, *sibling, *selectorList, ignoredSpecificity))
840                             ++count;
841                     }
842                 } else {
843                     count += countElementsBefore(checkingContext, element);
844                     addStyleRelation(checkingContext, element, Style::Relation::NthChildIndex, count);
845                 }
846
847                 if (selector.matchNth(count))
848                     return true;
849             }
850             break;
851         case CSSSelector::PseudoClassNthOfType:
852             if (!selector.parseNth())
853                 break;
854
855             if (element.parentElement()) {
856                 addStyleRelation(checkingContext, element, Style::Relation::AffectedByPreviousSibling);
857
858                 int count = 1 + countElementsOfTypeBefore(checkingContext, element, element.tagQName());
859                 if (selector.matchNth(count))
860                     return true;
861             }
862             break;
863         case CSSSelector::PseudoClassNthLastChild:
864             if (!selector.parseNth())
865                 break;
866             if (Element* parentElement = element.parentElement()) {
867                 if (const CSSSelectorList* selectorList = selector.selectorList()) {
868                     unsigned selectorListSpecificity;
869                     if (!matchSelectorList(checkingContext, context, element, *selectorList, selectorListSpecificity))
870                         return false;
871                     specificity = CSSSelector::addSpecificities(specificity, selectorListSpecificity);
872
873                     addStyleRelation(checkingContext, *parentElement, Style::Relation::ChildrenAffectedByPropertyBasedBackwardPositionalRules);
874                 } else
875                     addStyleRelation(checkingContext, *parentElement, Style::Relation::ChildrenAffectedByBackwardPositionalRules);
876
877                 if (!parentElement->isFinishedParsingChildren())
878                     return false;
879
880                 int count = 1;
881                 if (const CSSSelectorList* selectorList = selector.selectorList()) {
882                     for (Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(*sibling)) {
883                         unsigned ignoredSpecificity;
884                         if (matchSelectorList(checkingContext, context, *sibling, *selectorList, ignoredSpecificity))
885                             ++count;
886                     }
887                 } else
888                     count += countElementsAfter(element);
889
890                 if (selector.matchNth(count))
891                     return true;
892             }
893             break;
894         case CSSSelector::PseudoClassNthLastOfType:
895             if (!selector.parseNth())
896                 break;
897             if (Element* parentElement = element.parentElement()) {
898                 addStyleRelation(checkingContext, *parentElement, Style::Relation::ChildrenAffectedByBackwardPositionalRules);
899
900                 if (!parentElement->isFinishedParsingChildren())
901                     return false;
902
903                 int count = 1 + countElementsOfTypeAfter(element, element.tagQName());
904                 if (selector.matchNth(count))
905                     return true;
906             }
907             break;
908         case CSSSelector::PseudoClassTarget:
909             if (&element == element.document().cssTarget())
910                 return true;
911             break;
912         case CSSSelector::PseudoClassAny:
913             {
914                 LocalContext subcontext(context);
915                 subcontext.inFunctionalPseudoClass = true;
916                 subcontext.pseudoElementEffective = false;
917                 for (subcontext.selector = selector.selectorList()->first(); subcontext.selector; subcontext.selector = CSSSelectorList::next(subcontext.selector)) {
918                     subcontext.firstSelectorOfTheFragment = subcontext.selector;
919                     PseudoIdSet ignoreDynamicPseudo;
920                     unsigned ingoredSpecificity = 0;
921                     if (matchRecursively(checkingContext, subcontext, ignoreDynamicPseudo, ingoredSpecificity).match == Match::SelectorMatches)
922                         return true;
923                 }
924             }
925             break;
926         case CSSSelector::PseudoClassAutofill:
927             return isAutofilled(element);
928         case CSSSelector::PseudoClassAnyLink:
929         case CSSSelector::PseudoClassAnyLinkDeprecated:
930         case CSSSelector::PseudoClassLink:
931             // :visited and :link matches are separated later when applying the style. Here both classes match all links...
932             return element.isLink();
933         case CSSSelector::PseudoClassVisited:
934             // ...except if :visited matching is disabled for ancestor/sibling matching.
935             // Inside functional pseudo class except for :not, :visited never matches.
936             if (context.inFunctionalPseudoClass)
937                 return false;
938             return element.isLink() && context.visitedMatchType == VisitedMatchType::Enabled;
939         case CSSSelector::PseudoClassDrag:
940             addStyleRelation(checkingContext, element, Style::Relation::AffectedByDrag);
941
942             if (element.renderer() && element.renderer()->isDragging())
943                 return true;
944             break;
945         case CSSSelector::PseudoClassFocus:
946             return matchesFocusPseudoClass(element);
947         case CSSSelector::PseudoClassFocusWithin:
948             addStyleRelation(checkingContext, element, Style::Relation::AffectedByFocusWithin);
949             return element.hasFocusWithin();
950         case CSSSelector::PseudoClassHover:
951             if (m_strictParsing || element.isLink() || canMatchHoverOrActiveInQuirksMode(context)) {
952                 addStyleRelation(checkingContext, element, Style::Relation::AffectedByHover);
953
954                 if (element.hovered() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassHover))
955                     return true;
956             }
957             break;
958         case CSSSelector::PseudoClassActive:
959             if (m_strictParsing || element.isLink() || canMatchHoverOrActiveInQuirksMode(context)) {
960                 addStyleRelation(checkingContext, element, Style::Relation::AffectedByActive);
961
962                 if (element.active() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassActive))
963                     return true;
964             }
965             break;
966         case CSSSelector::PseudoClassEnabled:
967             return isEnabled(element);
968         case CSSSelector::PseudoClassFullPageMedia:
969             return isMediaDocument(element);
970         case CSSSelector::PseudoClassDefault:
971             return matchesDefaultPseudoClass(element);
972         case CSSSelector::PseudoClassDisabled:
973             return isDisabled(element);
974         case CSSSelector::PseudoClassReadOnly:
975             return matchesReadOnlyPseudoClass(element);
976         case CSSSelector::PseudoClassReadWrite:
977             return matchesReadWritePseudoClass(element);
978         case CSSSelector::PseudoClassOptional:
979             return isOptionalFormControl(element);
980         case CSSSelector::PseudoClassRequired:
981             return isRequiredFormControl(element);
982         case CSSSelector::PseudoClassValid:
983             return isValid(element);
984         case CSSSelector::PseudoClassInvalid:
985             return isInvalid(element);
986         case CSSSelector::PseudoClassChecked:
987             return isChecked(element);
988         case CSSSelector::PseudoClassIndeterminate:
989             return matchesIndeterminatePseudoClass(element);
990         case CSSSelector::PseudoClassRoot:
991             if (&element == element.document().documentElement())
992                 return true;
993             break;
994         case CSSSelector::PseudoClassLang:
995             {
996                 ASSERT(selector.langArgumentList() && !selector.langArgumentList()->isEmpty());
997                 return matchesLangPseudoClass(element, *selector.langArgumentList());
998             }
999 #if ENABLE(FULLSCREEN_API)
1000         case CSSSelector::PseudoClassFullScreen:
1001             return matchesFullScreenPseudoClass(element);
1002         case CSSSelector::PseudoClassAnimatingFullScreenTransition:
1003             return matchesFullScreenAnimatingFullScreenTransitionPseudoClass(element);
1004         case CSSSelector::PseudoClassFullScreenAncestor:
1005             return matchesFullScreenAncestorPseudoClass(element);
1006         case CSSSelector::PseudoClassFullScreenDocument:
1007             return matchesFullScreenDocumentPseudoClass(element);
1008 #endif
1009         case CSSSelector::PseudoClassInRange:
1010             return isInRange(element);
1011         case CSSSelector::PseudoClassOutOfRange:
1012             return isOutOfRange(element);
1013 #if ENABLE(VIDEO_TRACK)
1014         case CSSSelector::PseudoClassFuture:
1015             return matchesFutureCuePseudoClass(element);
1016         case CSSSelector::PseudoClassPast:
1017             return matchesPastCuePseudoClass(element);
1018 #endif
1019
1020         case CSSSelector::PseudoClassScope:
1021             {
1022                 const Node* contextualReferenceNode = !checkingContext.scope ? element.document().documentElement() : checkingContext.scope;
1023                 if (&element == contextualReferenceNode)
1024                     return true;
1025                 break;
1026             }
1027         case CSSSelector::PseudoClassHost:
1028             // :host matches based on context. Cases that reach selector checker don't match.
1029             return false;
1030 #if ENABLE(CUSTOM_ELEMENTS)
1031         case CSSSelector::PseudoClassDefined:
1032             return isDefinedElement(element);
1033 #endif
1034         case CSSSelector::PseudoClassWindowInactive:
1035             return isWindowInactive(element);
1036
1037         case CSSSelector::PseudoClassHorizontal:
1038         case CSSSelector::PseudoClassVertical:
1039         case CSSSelector::PseudoClassDecrement:
1040         case CSSSelector::PseudoClassIncrement:
1041         case CSSSelector::PseudoClassStart:
1042         case CSSSelector::PseudoClassEnd:
1043         case CSSSelector::PseudoClassDoubleButton:
1044         case CSSSelector::PseudoClassSingleButton:
1045         case CSSSelector::PseudoClassNoButton:
1046         case CSSSelector::PseudoClassCornerPresent:
1047             return false;
1048
1049 #if ENABLE(CSS_SELECTORS_LEVEL4)
1050         // FIXME: Implement :dir() selector.
1051         case CSSSelector::PseudoClassDir:
1052             return false;
1053
1054         // FIXME: Implement :role() selector.
1055         case CSSSelector::PseudoClassRole:
1056             return false;
1057 #endif
1058
1059         case CSSSelector::PseudoClassUnknown:
1060             ASSERT_NOT_REACHED();
1061             break;
1062         }
1063         return false;
1064     }
1065 #if ENABLE(VIDEO_TRACK)
1066     if (selector.match() == CSSSelector::PseudoElement && selector.pseudoElementType() == CSSSelector::PseudoElementCue) {
1067         LocalContext subcontext(context);
1068
1069         const CSSSelector* const & selector = context.selector;
1070         for (subcontext.selector = selector->selectorList()->first(); subcontext.selector; subcontext.selector = CSSSelectorList::next(subcontext.selector)) {
1071             subcontext.firstSelectorOfTheFragment = subcontext.selector;
1072             subcontext.inFunctionalPseudoClass = true;
1073             subcontext.pseudoElementEffective = false;
1074             PseudoIdSet ignoredDynamicPseudo;
1075             unsigned ignoredSpecificity = 0;
1076             if (matchRecursively(checkingContext, subcontext, ignoredDynamicPseudo, ignoredSpecificity).match == Match::SelectorMatches)
1077                 return true;
1078         }
1079         return false;
1080     }
1081 #endif
1082     if (selector.match() == CSSSelector::PseudoElement && selector.pseudoElementType() == CSSSelector::PseudoElementSlotted) {
1083         // We see ::slotted() pseudo elements when collecting slotted rules from the slot shadow tree only.
1084         ASSERT(checkingContext.resolvingMode == Mode::CollectingRules);
1085         return is<HTMLSlotElement>(element);
1086     }
1087     return true;
1088 }
1089
1090 bool SelectorChecker::matchSelectorList(CheckingContext& checkingContext, const LocalContext& context, const Element& element, const CSSSelectorList& selectorList, unsigned& specificity) const
1091 {
1092     specificity = 0;
1093     bool hasMatchedAnything = false;
1094
1095     for (const CSSSelector* subselector = selectorList.first(); subselector; subselector = CSSSelectorList::next(subselector)) {
1096         LocalContext subcontext(context);
1097         subcontext.element = &element;
1098         subcontext.selector = subselector;
1099         subcontext.inFunctionalPseudoClass = true;
1100         subcontext.pseudoElementEffective = false;
1101         subcontext.firstSelectorOfTheFragment = subselector;
1102         PseudoIdSet ignoreDynamicPseudo;
1103         unsigned localSpecificity = 0;
1104         if (matchRecursively(checkingContext, subcontext, ignoreDynamicPseudo, localSpecificity).match == Match::SelectorMatches) {
1105             ASSERT(!ignoreDynamicPseudo);
1106
1107             hasMatchedAnything = true;
1108             specificity = std::max(specificity, localSpecificity);
1109         }
1110     }
1111     return hasMatchedAnything;
1112 }
1113
1114 bool SelectorChecker::checkScrollbarPseudoClass(const CheckingContext& checkingContext, const Element& element, const CSSSelector& selector) const
1115 {
1116     ASSERT(selector.match() == CSSSelector::PseudoClass);
1117
1118     switch (selector.pseudoClassType()) {
1119     case CSSSelector::PseudoClassWindowInactive:
1120         return isWindowInactive(element);
1121     case CSSSelector::PseudoClassEnabled:
1122         return scrollbarMatchesEnabledPseudoClass(checkingContext);
1123     case CSSSelector::PseudoClassDisabled:
1124         return scrollbarMatchesDisabledPseudoClass(checkingContext);
1125     case CSSSelector::PseudoClassHover:
1126         return scrollbarMatchesHoverPseudoClass(checkingContext);
1127     case CSSSelector::PseudoClassActive:
1128         return scrollbarMatchesActivePseudoClass(checkingContext);
1129     case CSSSelector::PseudoClassHorizontal:
1130         return scrollbarMatchesHorizontalPseudoClass(checkingContext);
1131     case CSSSelector::PseudoClassVertical:
1132         return scrollbarMatchesVerticalPseudoClass(checkingContext);
1133     case CSSSelector::PseudoClassDecrement:
1134         return scrollbarMatchesDecrementPseudoClass(checkingContext);
1135     case CSSSelector::PseudoClassIncrement:
1136         return scrollbarMatchesIncrementPseudoClass(checkingContext);
1137     case CSSSelector::PseudoClassStart:
1138         return scrollbarMatchesStartPseudoClass(checkingContext);
1139     case CSSSelector::PseudoClassEnd:
1140         return scrollbarMatchesEndPseudoClass(checkingContext);
1141     case CSSSelector::PseudoClassDoubleButton:
1142         return scrollbarMatchesDoubleButtonPseudoClass(checkingContext);
1143     case CSSSelector::PseudoClassSingleButton:
1144         return scrollbarMatchesSingleButtonPseudoClass(checkingContext);
1145     case CSSSelector::PseudoClassNoButton:
1146         return scrollbarMatchesNoButtonPseudoClass(checkingContext);
1147     case CSSSelector::PseudoClassCornerPresent:
1148         return scrollbarMatchesCornerPresentPseudoClass(checkingContext);
1149     default:
1150         return false;
1151     }
1152 }
1153
1154 unsigned SelectorChecker::determineLinkMatchType(const CSSSelector* selector)
1155 {
1156     unsigned linkMatchType = MatchAll;
1157
1158     // Statically determine if this selector will match a link in visited, unvisited or any state, or never.
1159     // :visited never matches other elements than the innermost link element.
1160     for (; selector; selector = selector->tagHistory()) {
1161         if (selector->match() == CSSSelector::PseudoClass) {
1162             switch (selector->pseudoClassType()) {
1163             case CSSSelector::PseudoClassLink:
1164                 linkMatchType &= ~SelectorChecker::MatchVisited;
1165                 break;
1166             case CSSSelector::PseudoClassVisited:
1167                 linkMatchType &= ~SelectorChecker::MatchLink;
1168                 break;
1169             default:
1170                 break;
1171             }
1172         }
1173         CSSSelector::Relation relation = selector->relation();
1174         if (relation == CSSSelector::SubSelector)
1175             continue;
1176         if (relation != CSSSelector::Descendant && relation != CSSSelector::Child)
1177             return linkMatchType;
1178         if (linkMatchType != MatchAll)
1179             return linkMatchType;
1180     }
1181     return linkMatchType;
1182 }
1183
1184 static bool isFrameFocused(const Element& element)
1185 {
1186     return element.document().frame() && element.document().frame()->selection().isFocusedAndActive();
1187 }
1188
1189 bool SelectorChecker::matchesFocusPseudoClass(const Element& element)
1190 {
1191     if (InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoClassFocus))
1192         return true;
1193     return element.focused() && isFrameFocused(element);
1194 }
1195
1196 }