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