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, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
6 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
7 * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
10 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
11 *
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
16 *
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 * Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public License
23 * along with this library; see the file COPYING.LIB.  If not, write to
24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 * Boston, MA 02110-1301, USA.
26 */
27
28#include "config.h"
29#include "core/css/SelectorChecker.h"
30
31#include "core/HTMLNames.h"
32#include "core/css/CSSSelectorList.h"
33#include "core/css/SiblingTraversalStrategies.h"
34#include "core/dom/Document.h"
35#include "core/dom/ElementTraversal.h"
36#include "core/dom/Fullscreen.h"
37#include "core/dom/NodeRenderStyle.h"
38#include "core/dom/StyleEngine.h"
39#include "core/dom/Text.h"
40#include "core/dom/shadow/InsertionPoint.h"
41#include "core/dom/shadow/ShadowRoot.h"
42#include "core/editing/FrameSelection.h"
43#include "core/frame/LocalFrame.h"
44#include "core/html/HTMLDocument.h"
45#include "core/html/HTMLFrameElementBase.h"
46#include "core/html/HTMLInputElement.h"
47#include "core/html/HTMLOptionElement.h"
48#include "core/html/HTMLSelectElement.h"
49#include "core/html/parser/HTMLParserIdioms.h"
50#include "core/html/track/vtt/VTTElement.h"
51#include "core/inspector/InspectorInstrumentation.h"
52#include "core/page/FocusController.h"
53#include "core/page/Page.h"
54#include "core/rendering/RenderObject.h"
55#include "core/rendering/RenderScrollbar.h"
56#include "core/rendering/style/RenderStyle.h"
57#include "platform/scroll/ScrollableArea.h"
58#include "platform/scroll/ScrollbarTheme.h"
59
60namespace blink {
61
62using namespace HTMLNames;
63
64SelectorChecker::SelectorChecker(Document& document, Mode mode)
65    : m_strictParsing(!document.inQuirksMode())
66    , m_mode(mode)
67{
68}
69
70static bool matchesCustomPseudoElement(const Element* element, const CSSSelector& selector)
71{
72    ShadowRoot* root = element->containingShadowRoot();
73    if (!root || root->type() != ShadowRoot::UserAgentShadowRoot)
74        return false;
75
76    if (element->shadowPseudoId() != selector.value())
77        return false;
78
79    return true;
80}
81
82static Element* parentElement(const SelectorChecker::SelectorCheckingContext& context)
83{
84    // - If context.scope is a shadow root, we should walk up to its shadow host.
85    // - If context.scope is some element in some shadow tree and querySelector initialized the context,
86    //   e.g. shadowRoot.querySelector(':host *'),
87    //   (a) context.element has the same treescope as context.scope, need to walk up to its shadow host.
88    //   (b) Otherwise, should not walk up from a shadow root to a shadow host.
89    if (context.scope && (context.scope == context.element->containingShadowRoot() || context.scope->treeScope() == context.element->treeScope()))
90        return context.element->parentOrShadowHostElement();
91    return context.element->parentElement();
92}
93
94static bool scopeContainsLastMatchedElement(const SelectorChecker::SelectorCheckingContext& context)
95{
96    if (!(context.contextFlags & SelectorChecker::ScopeContainsLastMatchedElement))
97        return true;
98
99    ASSERT(context.scope);
100    if (context.scope->treeScope() == context.element->treeScope())
101        return true;
102
103    // Because Blink treats a shadow host's TreeScope as a separate one from its descendent shadow roots,
104    // if the last matched element is a shadow host, the condition above isn't met, even though it
105    // should be.
106    return context.element == context.scope->shadowHost() && (!context.previousElement || context.previousElement->isInDescendantTreeOf(context.element));
107}
108
109static inline bool nextSelectorExceedsScope(const SelectorChecker::SelectorCheckingContext& context)
110{
111    if (context.scope && context.scope->isInShadowTree())
112        return context.element == context.scope->shadowHost();
113
114    return false;
115}
116
117// Recursive check of selectors and combinators
118// It can return 4 different values:
119// * SelectorMatches          - the selector matches the element e
120// * SelectorFailsLocally     - the selector fails for the element e
121// * SelectorFailsAllSiblings - the selector fails for e and any sibling of e
122// * SelectorFailsCompletely  - the selector fails for e and any sibling or ancestor of e
123template<typename SiblingTraversalStrategy>
124SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
125{
126    // first selector has to match
127    unsigned specificity = 0;
128    if (!checkOne(context, siblingTraversalStrategy, &specificity))
129        return SelectorFailsLocally;
130
131    if (context.selector->match() == CSSSelector::PseudoElement) {
132        if (context.selector->isCustomPseudoElement()) {
133            if (!matchesCustomPseudoElement(context.element, *context.selector))
134                return SelectorFailsLocally;
135        } else if (context.selector->isContentPseudoElement()) {
136            if (!context.element->isInShadowTree() || !context.element->isInsertionPoint())
137                return SelectorFailsLocally;
138        } else if (context.selector->isShadowPseudoElement()) {
139            if (!context.element->isInShadowTree() || !context.previousElement)
140                return SelectorFailsCompletely;
141        } else {
142            if ((!context.elementStyle && m_mode == ResolvingStyle) || m_mode == QueryingRules)
143                return SelectorFailsLocally;
144
145            PseudoId pseudoId = CSSSelector::pseudoId(context.selector->pseudoType());
146            if (pseudoId == FIRST_LETTER)
147                context.element->document().styleEngine()->setUsesFirstLetterRules(true);
148            if (pseudoId != NOPSEUDO && m_mode != SharingRules && result)
149                result->dynamicPseudo = pseudoId;
150        }
151    }
152
153    // Prepare next selector
154    if (context.selector->isLastInTagHistory()) {
155        if (scopeContainsLastMatchedElement(context)) {
156            if (result)
157                result->specificity += specificity;
158            return SelectorMatches;
159        }
160        return SelectorFailsLocally;
161    }
162
163    Match match;
164    if (context.selector->relation() != CSSSelector::SubSelector) {
165        // Abort if the next selector would exceed the scope.
166        if (nextSelectorExceedsScope(context))
167            return SelectorFailsCompletely;
168
169        // Bail-out if this selector is irrelevant for the pseudoId
170        if (context.pseudoId != NOPSEUDO && (!result || context.pseudoId != result->dynamicPseudo))
171            return SelectorFailsCompletely;
172
173        if (result) {
174            TemporaryChange<PseudoId> dynamicPseudoScope(result->dynamicPseudo, NOPSEUDO);
175            match = matchForRelation(context, siblingTraversalStrategy, result);
176        } else {
177            return matchForRelation(context, siblingTraversalStrategy, 0);
178        }
179    } else {
180        match = matchForSubSelector(context, siblingTraversalStrategy, result);
181    }
182    if (match != SelectorMatches || !result)
183        return match;
184
185    result->specificity += specificity;
186    return SelectorMatches;
187}
188
189static inline SelectorChecker::SelectorCheckingContext prepareNextContextForRelation(const SelectorChecker::SelectorCheckingContext& context)
190{
191    SelectorChecker::SelectorCheckingContext nextContext(context);
192    ASSERT(context.selector->tagHistory());
193    nextContext.selector = context.selector->tagHistory();
194    return nextContext;
195}
196
197static inline bool isAuthorShadowRoot(const Node* node)
198{
199    return node && node->isShadowRoot() && toShadowRoot(node)->type() == ShadowRoot::AuthorShadowRoot;
200}
201
202template<typename SiblingTraversalStrategy>
203SelectorChecker::Match SelectorChecker::matchForSubSelector(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
204{
205    SelectorCheckingContext nextContext = prepareNextContextForRelation(context);
206
207    PseudoId dynamicPseudo = result ? result->dynamicPseudo : NOPSEUDO;
208    // a selector is invalid if something follows a pseudo-element
209    // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else)
210    // to follow the pseudo elements.
211    nextContext.hasScrollbarPseudo = dynamicPseudo != NOPSEUDO && (context.scrollbar || dynamicPseudo == SCROLLBAR_CORNER || dynamicPseudo == RESIZER);
212    nextContext.hasSelectionPseudo = dynamicPseudo == SELECTION;
213    if ((context.elementStyle || m_mode == CollectingCSSRules || m_mode == CollectingStyleRules || m_mode == QueryingRules) && dynamicPseudo != NOPSEUDO
214        && !nextContext.hasSelectionPseudo
215        && !(nextContext.hasScrollbarPseudo && nextContext.selector->match() == CSSSelector::PseudoClass))
216        return SelectorFailsCompletely;
217
218    nextContext.isSubSelector = true;
219    return match(nextContext, siblingTraversalStrategy, result);
220}
221
222static bool selectorMatchesShadowRoot(const CSSSelector* selector)
223{
224    return selector && selector->isShadowPseudoElement();
225}
226
227template<typename SiblingTraversalStrategy>
228SelectorChecker::Match SelectorChecker::matchForPseudoShadow(const ContainerNode* node, const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
229{
230    if (!isAuthorShadowRoot(node))
231        return SelectorFailsCompletely;
232    return match(context, siblingTraversalStrategy, result);
233}
234
235template<typename SiblingTraversalStrategy>
236SelectorChecker::Match SelectorChecker::matchForRelation(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, MatchResult* result) const
237{
238    SelectorCheckingContext nextContext = prepareNextContextForRelation(context);
239    nextContext.previousElement = context.element;
240
241    CSSSelector::Relation relation = context.selector->relation();
242
243    // Disable :visited matching when we see the first link or try to match anything else than an ancestors.
244    if (!context.isSubSelector && (context.element->isLink() || (relation != CSSSelector::Descendant && relation != CSSSelector::Child)))
245        nextContext.visitedMatchType = VisitedMatchDisabled;
246
247    nextContext.pseudoId = NOPSEUDO;
248
249    switch (relation) {
250    case CSSSelector::Descendant:
251        if (context.selector->relationIsAffectedByPseudoContent()) {
252            for (Element* element = context.element; element; element = element->parentElement()) {
253                if (matchForShadowDistributed(element, siblingTraversalStrategy, nextContext, result) == SelectorMatches)
254                    return SelectorMatches;
255            }
256            return SelectorFailsCompletely;
257        }
258        nextContext.isSubSelector = false;
259        nextContext.elementStyle = 0;
260
261        if (selectorMatchesShadowRoot(nextContext.selector))
262            return matchForPseudoShadow(context.element->containingShadowRoot(), nextContext, siblingTraversalStrategy, result);
263
264        for (nextContext.element = parentElement(context); nextContext.element; nextContext.element = parentElement(nextContext)) {
265            Match match = this->match(nextContext, siblingTraversalStrategy, result);
266            if (match == SelectorMatches || match == SelectorFailsCompletely)
267                return match;
268            if (nextSelectorExceedsScope(nextContext))
269                return SelectorFailsCompletely;
270        }
271        return SelectorFailsCompletely;
272    case CSSSelector::Child:
273        {
274            if (context.selector->relationIsAffectedByPseudoContent())
275                return matchForShadowDistributed(context.element, siblingTraversalStrategy, nextContext, result);
276
277            nextContext.isSubSelector = false;
278            nextContext.elementStyle = 0;
279
280            if (selectorMatchesShadowRoot(nextContext.selector))
281                return matchForPseudoShadow(context.element->parentNode(), nextContext, siblingTraversalStrategy, result);
282
283            nextContext.element = parentElement(context);
284            if (!nextContext.element)
285                return SelectorFailsCompletely;
286            return match(nextContext, siblingTraversalStrategy, result);
287        }
288    case CSSSelector::DirectAdjacent:
289        // Shadow roots can't have sibling elements
290        if (selectorMatchesShadowRoot(nextContext.selector))
291            return SelectorFailsCompletely;
292
293        if (m_mode == ResolvingStyle) {
294            if (ContainerNode* parent = context.element->parentElementOrShadowRoot())
295                parent->setChildrenAffectedByDirectAdjacentRules();
296        }
297        nextContext.element = ElementTraversal::previousSibling(*context.element);
298        if (!nextContext.element)
299            return SelectorFailsAllSiblings;
300        nextContext.isSubSelector = false;
301        nextContext.elementStyle = 0;
302        return match(nextContext, siblingTraversalStrategy, result);
303
304    case CSSSelector::IndirectAdjacent:
305        // Shadow roots can't have sibling elements
306        if (selectorMatchesShadowRoot(nextContext.selector))
307            return SelectorFailsCompletely;
308
309        if (m_mode == ResolvingStyle) {
310            if (ContainerNode* parent = context.element->parentElementOrShadowRoot())
311                parent->setChildrenAffectedByIndirectAdjacentRules();
312        }
313        nextContext.element = ElementTraversal::previousSibling(*context.element);
314        nextContext.isSubSelector = false;
315        nextContext.elementStyle = 0;
316        for (; nextContext.element; nextContext.element = ElementTraversal::previousSibling(*nextContext.element)) {
317            Match match = this->match(nextContext, siblingTraversalStrategy, result);
318            if (match == SelectorMatches || match == SelectorFailsAllSiblings || match == SelectorFailsCompletely)
319                return match;
320        };
321        return SelectorFailsAllSiblings;
322
323    case CSSSelector::ShadowPseudo:
324        {
325            // If we're in the same tree-scope as the scoping element, then following a shadow descendant combinator would escape that and thus the scope.
326            if (context.scope && context.scope->shadowHost() && context.scope->shadowHost()->treeScope() == context.element->treeScope())
327                return SelectorFailsCompletely;
328
329            Element* shadowHost = context.element->shadowHost();
330            if (!shadowHost)
331                return SelectorFailsCompletely;
332            nextContext.element = shadowHost;
333            nextContext.isSubSelector = false;
334            nextContext.elementStyle = 0;
335            return this->match(nextContext, siblingTraversalStrategy, result);
336        }
337
338    case CSSSelector::ShadowDeep:
339        {
340            nextContext.isSubSelector = false;
341            nextContext.elementStyle = 0;
342            for (nextContext.element = context.element->parentOrShadowHostElement(); nextContext.element; nextContext.element = nextContext.element->parentOrShadowHostElement()) {
343                Match match = this->match(nextContext, siblingTraversalStrategy, result);
344                if (match == SelectorMatches || match == SelectorFailsCompletely)
345                    return match;
346                if (nextSelectorExceedsScope(nextContext))
347                    return SelectorFailsCompletely;
348            }
349            return SelectorFailsCompletely;
350        }
351
352    case CSSSelector::SubSelector:
353        ASSERT_NOT_REACHED();
354    }
355
356    ASSERT_NOT_REACHED();
357    return SelectorFailsCompletely;
358}
359
360template<typename SiblingTraversalStrategy>
361SelectorChecker::Match SelectorChecker::matchForShadowDistributed(const Element* element, const SiblingTraversalStrategy& siblingTraversalStrategy, SelectorCheckingContext& nextContext, MatchResult* result) const
362{
363    ASSERT(element);
364    WillBeHeapVector<RawPtrWillBeMember<InsertionPoint>, 8> insertionPoints;
365    collectDestinationInsertionPoints(*element, insertionPoints);
366    for (size_t i = 0; i < insertionPoints.size(); ++i) {
367        nextContext.element = insertionPoints[i];
368        if (m_mode == SharingRules)
369            nextContext.scope = insertionPoints[i]->containingShadowRoot();
370        nextContext.isSubSelector = false;
371        nextContext.elementStyle = 0;
372        if (match(nextContext, siblingTraversalStrategy, result) == SelectorMatches)
373            return SelectorMatches;
374    }
375    return SelectorFailsLocally;
376}
377
378template<typename CharType>
379static inline bool containsHTMLSpaceTemplate(const CharType* string, unsigned length)
380{
381    for (unsigned i = 0; i < length; ++i)
382        if (isHTMLSpace<CharType>(string[i]))
383            return true;
384    return false;
385}
386
387static inline bool containsHTMLSpace(const AtomicString& string)
388{
389    if (LIKELY(string.is8Bit()))
390        return containsHTMLSpaceTemplate<LChar>(string.characters8(), string.length());
391    return containsHTMLSpaceTemplate<UChar>(string.characters16(), string.length());
392}
393
394static bool attributeValueMatches(const Attribute& attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
395{
396    const AtomicString& value = attributeItem.value();
397    if (value.isNull())
398        return false;
399
400    switch (match) {
401    case CSSSelector::Exact:
402        if (caseSensitive ? selectorValue != value : !equalIgnoringCase(selectorValue, value))
403            return false;
404        break;
405    case CSSSelector::List:
406        {
407            // Ignore empty selectors or selectors containing HTML spaces
408            if (selectorValue.isEmpty() || containsHTMLSpace(selectorValue))
409                return false;
410
411            unsigned startSearchAt = 0;
412            while (true) {
413                size_t foundPos = value.find(selectorValue, startSearchAt, caseSensitive);
414                if (foundPos == kNotFound)
415                    return false;
416                if (!foundPos || isHTMLSpace<UChar>(value[foundPos - 1])) {
417                    unsigned endStr = foundPos + selectorValue.length();
418                    if (endStr == value.length() || isHTMLSpace<UChar>(value[endStr]))
419                        break; // We found a match.
420                }
421
422                // No match. Keep looking.
423                startSearchAt = foundPos + 1;
424            }
425            break;
426        }
427    case CSSSelector::Contain:
428        if (!value.contains(selectorValue, caseSensitive) || selectorValue.isEmpty())
429            return false;
430        break;
431    case CSSSelector::Begin:
432        if (!value.startsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
433            return false;
434        break;
435    case CSSSelector::End:
436        if (!value.endsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
437            return false;
438        break;
439    case CSSSelector::Hyphen:
440        if (value.length() < selectorValue.length())
441            return false;
442        if (!value.startsWith(selectorValue, caseSensitive))
443            return false;
444        // It they start the same, check for exact match or following '-':
445        if (value.length() != selectorValue.length() && value[selectorValue.length()] != '-')
446            return false;
447        break;
448    case CSSSelector::PseudoClass:
449    case CSSSelector::PseudoElement:
450    default:
451        break;
452    }
453
454    return true;
455}
456
457static bool anyAttributeMatches(Element& element, CSSSelector::Match match, const CSSSelector& selector)
458{
459    const QualifiedName& selectorAttr = selector.attribute();
460    ASSERT(selectorAttr.localName() != starAtom); // Should not be possible from the CSS grammar.
461
462    // Synchronize the attribute in case it is lazy-computed.
463    // Currently all lazy properties have a null namespace, so only pass localName().
464    element.synchronizeAttribute(selectorAttr.localName());
465
466    const AtomicString& selectorValue = selector.value();
467    bool caseInsensitive = selector.attributeMatchType() == CSSSelector::CaseInsensitive;
468
469    AttributeCollection attributes = element.attributesWithoutUpdate();
470    AttributeCollection::iterator end = attributes.end();
471    for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) {
472        const Attribute& attributeItem = *it;
473
474        if (!attributeItem.matches(selectorAttr))
475            continue;
476
477        if (attributeValueMatches(attributeItem, match, selectorValue, !caseInsensitive))
478            return true;
479
480        if (caseInsensitive)
481            continue;
482
483        // Legacy dictates that values of some attributes should be compared in
484        // a case-insensitive manner regardless of whether the case insensitive
485        // flag is set or not.
486        bool legacyCaseInsensitive = element.document().isHTMLDocument() && !HTMLDocument::isCaseSensitiveAttribute(selectorAttr);
487
488        // If case-insensitive, re-check, and count if result differs.
489        // See http://code.google.com/p/chromium/issues/detail?id=327060
490        if (legacyCaseInsensitive && attributeValueMatches(attributeItem, match, selectorValue, false)) {
491            UseCounter::count(element.document(), UseCounter::CaseInsensitiveAttrSelectorMatch);
492            return true;
493        }
494    }
495
496    return false;
497}
498
499template<typename SiblingTraversalStrategy>
500bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy, unsigned* specificity) const
501{
502    ASSERT(context.element);
503    Element& element = *context.element;
504    ASSERT(context.selector);
505    const CSSSelector& selector = *context.selector;
506
507    bool elementIsHostInItsShadowTree = isHostInItsShadowTree(element, context.scope);
508
509    // Only :host and :host-context() should match the host: http://drafts.csswg.org/css-scoping/#host-element
510    if (elementIsHostInItsShadowTree && (!selector.isHostPseudoClass()
511        && !(context.contextFlags & TreatShadowHostAsNormalScope)
512        && selector.match() != CSSSelector::PseudoElement))
513            return false;
514
515    if (selector.match() == CSSSelector::Tag)
516        return SelectorChecker::tagMatches(element, selector.tagQName());
517
518    if (selector.match() == CSSSelector::Class)
519        return element.hasClass() && element.classNames().contains(selector.value());
520
521    if (selector.match() == CSSSelector::Id)
522        return element.hasID() && element.idForStyleResolution() == selector.value();
523
524    if (selector.isAttributeSelector())
525        return anyAttributeMatches(element, selector.match(), selector);
526
527    if (selector.match() == CSSSelector::PseudoClass) {
528        // Handle :not up front.
529        if (selector.pseudoType() == CSSSelector::PseudoNot) {
530            SelectorCheckingContext subContext(context);
531            subContext.isSubSelector = true;
532            ASSERT(selector.selectorList());
533            for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = subContext.selector->tagHistory()) {
534                // :not cannot nest. I don't really know why this is a
535                // restriction in CSS3, but it is, so let's honor it.
536                // the parser enforces that this never occurs
537                ASSERT(subContext.selector->pseudoType() != CSSSelector::PseudoNot);
538                // We select between :visited and :link when applying. We don't know which one applied (or not) yet.
539                if (subContext.selector->pseudoType() == CSSSelector::PseudoVisited || (subContext.selector->pseudoType() == CSSSelector::PseudoLink && subContext.visitedMatchType == VisitedMatchEnabled))
540                    return true;
541                // context.scope is not available if m_mode == SharingRules.
542                // We cannot determine whether :host or :scope matches a given element or not.
543                if (m_mode == SharingRules && (subContext.selector->isHostPseudoClass() || subContext.selector->pseudoType() == CSSSelector::PseudoScope))
544                    return true;
545                if (!checkOne(subContext, DOMSiblingTraversalStrategy()))
546                    return true;
547            }
548        } else if (context.hasScrollbarPseudo) {
549            // CSS scrollbars match a specific subset of pseudo classes, and they have specialized rules for each
550            // (since there are no elements involved).
551            return checkScrollbarPseudoClass(context, &element.document(), selector);
552        } else if (context.hasSelectionPseudo) {
553            if (selector.pseudoType() == CSSSelector::PseudoWindowInactive)
554                return !element.document().page()->focusController().isActive();
555        }
556
557        // Normal element pseudo class checking.
558        switch (selector.pseudoType()) {
559            // Pseudo classes:
560        case CSSSelector::PseudoNot:
561            break; // Already handled up above.
562        case CSSSelector::PseudoEmpty:
563            {
564                bool result = true;
565                for (Node* n = element.firstChild(); n; n = n->nextSibling()) {
566                    if (n->isElementNode()) {
567                        result = false;
568                        break;
569                    }
570                    if (n->isTextNode()) {
571                        Text* textNode = toText(n);
572                        if (!textNode->data().isEmpty()) {
573                            result = false;
574                            break;
575                        }
576                    }
577                }
578                if (m_mode == ResolvingStyle) {
579                    element.setStyleAffectedByEmpty();
580                    if (context.elementStyle)
581                        context.elementStyle->setEmptyState(result);
582                    else if (element.renderStyle() && (element.document().styleEngine()->usesSiblingRules() || element.renderStyle()->unique()))
583                        element.renderStyle()->setEmptyState(result);
584                }
585                return result;
586            }
587        case CSSSelector::PseudoFirstChild:
588            // first-child matches the first child that is an element
589            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
590                bool result = siblingTraversalStrategy.isFirstChild(element);
591                if (m_mode == ResolvingStyle) {
592                    RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
593                    parent->setChildrenAffectedByFirstChildRules();
594                    if (result && childStyle)
595                        childStyle->setFirstChildState();
596                }
597                return result;
598            }
599            break;
600        case CSSSelector::PseudoFirstOfType:
601            // first-of-type matches the first element of its type
602            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
603                bool result = siblingTraversalStrategy.isFirstOfType(element, element.tagQName());
604                if (m_mode == ResolvingStyle)
605                    parent->setChildrenAffectedByForwardPositionalRules();
606                return result;
607            }
608            break;
609        case CSSSelector::PseudoLastChild:
610            // last-child matches the last child that is an element
611            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
612                bool result = parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
613                if (m_mode == ResolvingStyle) {
614                    RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
615                    parent->setChildrenAffectedByLastChildRules();
616                    if (result && childStyle)
617                        childStyle->setLastChildState();
618                }
619                return result;
620            }
621            break;
622        case CSSSelector::PseudoLastOfType:
623            // last-of-type matches the last element of its type
624            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
625                if (m_mode == ResolvingStyle)
626                    parent->setChildrenAffectedByBackwardPositionalRules();
627                if (!parent->isFinishedParsingChildren())
628                    return false;
629                return siblingTraversalStrategy.isLastOfType(element, element.tagQName());
630            }
631            break;
632        case CSSSelector::PseudoOnlyChild:
633            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
634                bool firstChild = siblingTraversalStrategy.isFirstChild(element);
635                bool onlyChild = firstChild && parent->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
636                if (m_mode == ResolvingStyle) {
637                    RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
638                    parent->setChildrenAffectedByFirstChildRules();
639                    parent->setChildrenAffectedByLastChildRules();
640                    if (firstChild && childStyle)
641                        childStyle->setFirstChildState();
642                    if (onlyChild && childStyle)
643                        childStyle->setLastChildState();
644                }
645                return onlyChild;
646            }
647            break;
648        case CSSSelector::PseudoOnlyOfType:
649            // FIXME: This selector is very slow.
650            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
651                if (m_mode == ResolvingStyle) {
652                    parent->setChildrenAffectedByForwardPositionalRules();
653                    parent->setChildrenAffectedByBackwardPositionalRules();
654                }
655                if (!parent->isFinishedParsingChildren())
656                    return false;
657                return siblingTraversalStrategy.isFirstOfType(element, element.tagQName()) && siblingTraversalStrategy.isLastOfType(element, element.tagQName());
658            }
659            break;
660        case CSSSelector::PseudoNthChild:
661            if (!selector.parseNth())
662                break;
663            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
664                int count = 1 + siblingTraversalStrategy.countElementsBefore(element);
665                if (m_mode == ResolvingStyle) {
666                    RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element.renderStyle();
667                    if (childStyle)
668                        childStyle->setUnique();
669                    parent->setChildrenAffectedByForwardPositionalRules();
670                }
671
672                if (selector.matchNth(count))
673                    return true;
674            }
675            break;
676        case CSSSelector::PseudoNthOfType:
677            if (!selector.parseNth())
678                break;
679            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
680                int count = 1 + siblingTraversalStrategy.countElementsOfTypeBefore(element, element.tagQName());
681                if (m_mode == ResolvingStyle)
682                    parent->setChildrenAffectedByForwardPositionalRules();
683
684                if (selector.matchNth(count))
685                    return true;
686            }
687            break;
688        case CSSSelector::PseudoNthLastChild:
689            if (!selector.parseNth())
690                break;
691            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
692                if (m_mode == ResolvingStyle)
693                    parent->setChildrenAffectedByBackwardPositionalRules();
694                if (!parent->isFinishedParsingChildren())
695                    return false;
696                int count = 1 + siblingTraversalStrategy.countElementsAfter(element);
697                if (selector.matchNth(count))
698                    return true;
699            }
700            break;
701        case CSSSelector::PseudoNthLastOfType:
702            if (!selector.parseNth())
703                break;
704            if (ContainerNode* parent = element.parentElementOrDocumentFragment()) {
705                if (m_mode == ResolvingStyle)
706                    parent->setChildrenAffectedByBackwardPositionalRules();
707                if (!parent->isFinishedParsingChildren())
708                    return false;
709
710                int count = 1 + siblingTraversalStrategy.countElementsOfTypeAfter(element, element.tagQName());
711                if (selector.matchNth(count))
712                    return true;
713            }
714            break;
715        case CSSSelector::PseudoTarget:
716            if (element == element.document().cssTarget())
717                return true;
718            break;
719        case CSSSelector::PseudoAny:
720            {
721                SelectorCheckingContext subContext(context);
722                subContext.isSubSelector = true;
723                ASSERT(selector.selectorList());
724                for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
725                    if (match(subContext, siblingTraversalStrategy) == SelectorMatches)
726                        return true;
727                }
728            }
729            break;
730        case CSSSelector::PseudoAutofill:
731            if (!element.isFormControlElement())
732                break;
733            return toHTMLFormControlElement(element).isAutofilled();
734        case CSSSelector::PseudoAnyLink:
735        case CSSSelector::PseudoLink:
736            // :visited and :link matches are separated later when applying the style. Here both classes match all links...
737            return element.isLink();
738        case CSSSelector::PseudoVisited:
739            // ...except if :visited matching is disabled for ancestor/sibling matching.
740            return element.isLink() && context.visitedMatchType == VisitedMatchEnabled;
741        case CSSSelector::PseudoDrag:
742            if (m_mode == ResolvingStyle) {
743                if (context.elementStyle)
744                    context.elementStyle->setAffectedByDrag();
745                else
746                    element.setChildrenOrSiblingsAffectedByDrag();
747            }
748            if (element.renderer() && element.renderer()->isDragging())
749                return true;
750            break;
751        case CSSSelector::PseudoFocus:
752            if (m_mode == ResolvingStyle) {
753                if (context.elementStyle)
754                    context.elementStyle->setAffectedByFocus();
755                else
756                    element.setChildrenOrSiblingsAffectedByFocus();
757            }
758            return matchesFocusPseudoClass(element);
759        case CSSSelector::PseudoHover:
760            // If we're in quirks mode, then hover should never match anchors with no
761            // href and *:hover should not match anything. This is important for sites like wsj.com.
762            if (m_strictParsing || context.isSubSelector || element.isLink()) {
763                if (m_mode == ResolvingStyle) {
764                    if (context.elementStyle)
765                        context.elementStyle->setAffectedByHover();
766                    else
767                        element.setChildrenOrSiblingsAffectedByHover();
768                }
769                if (element.hovered() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoHover))
770                    return true;
771            }
772            break;
773        case CSSSelector::PseudoActive:
774            // If we're in quirks mode, then :active should never match anchors with no
775            // href and *:active should not match anything.
776            if (m_strictParsing || context.isSubSelector || element.isLink()) {
777                if (m_mode == ResolvingStyle) {
778                    if (context.elementStyle)
779                        context.elementStyle->setAffectedByActive();
780                    else
781                        element.setChildrenOrSiblingsAffectedByActive();
782                }
783                if (element.active() || InspectorInstrumentation::forcePseudoState(&element, CSSSelector::PseudoActive))
784                    return true;
785            }
786            break;
787        case CSSSelector::PseudoEnabled:
788            if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
789                return !element.isDisabledFormControl();
790            else if (isHTMLAnchorElement(element) || isHTMLAreaElement(element))
791                return element.isLink();
792            break;
793        case CSSSelector::PseudoFullPageMedia:
794            return element.document().isMediaDocument();
795            break;
796        case CSSSelector::PseudoDefault:
797            return element.isDefaultButtonForForm();
798        case CSSSelector::PseudoDisabled:
799            if (element.isFormControlElement() || isHTMLOptionElement(element) || isHTMLOptGroupElement(element))
800                return element.isDisabledFormControl();
801            break;
802        case CSSSelector::PseudoReadOnly:
803            return element.matchesReadOnlyPseudoClass();
804        case CSSSelector::PseudoReadWrite:
805            return element.matchesReadWritePseudoClass();
806        case CSSSelector::PseudoOptional:
807            return element.isOptionalFormControl();
808        case CSSSelector::PseudoRequired:
809            return element.isRequiredFormControl();
810        case CSSSelector::PseudoValid:
811            element.document().setContainsValidityStyleRules();
812            return element.willValidate() && element.isValidFormControlElement();
813        case CSSSelector::PseudoInvalid:
814            element.document().setContainsValidityStyleRules();
815            return element.willValidate() && !element.isValidFormControlElement();
816        case CSSSelector::PseudoChecked:
817            {
818                if (isHTMLInputElement(element)) {
819                    HTMLInputElement& inputElement = toHTMLInputElement(element);
820                    // Even though WinIE allows checked and indeterminate to
821                    // co-exist, the CSS selector spec says that you can't be
822                    // both checked and indeterminate. We will behave like WinIE
823                    // behind the scenes and just obey the CSS spec here in the
824                    // test for matching the pseudo.
825                    if (inputElement.shouldAppearChecked() && !inputElement.shouldAppearIndeterminate())
826                        return true;
827                } else if (isHTMLOptionElement(element) && toHTMLOptionElement(element).selected())
828                    return true;
829                break;
830            }
831        case CSSSelector::PseudoIndeterminate:
832            return element.shouldAppearIndeterminate();
833        case CSSSelector::PseudoRoot:
834            if (element == element.document().documentElement())
835                return true;
836            break;
837        case CSSSelector::PseudoLang:
838            {
839                AtomicString value;
840                if (element.isVTTElement())
841                    value = toVTTElement(element).language();
842                else
843                    value = element.computeInheritedLanguage();
844                const AtomicString& argument = selector.argument();
845                if (value.isEmpty() || !value.startsWith(argument, false))
846                    break;
847                if (value.length() != argument.length() && value[argument.length()] != '-')
848                    break;
849                return true;
850            }
851        case CSSSelector::PseudoFullScreen:
852            // While a Document is in the fullscreen state, and the document's current fullscreen
853            // element is an element in the document, the 'full-screen' pseudoclass applies to
854            // that element. Also, an <iframe>, <object> or <embed> element whose child browsing
855            // context's Document is in the fullscreen state has the 'full-screen' pseudoclass applied.
856            if (isHTMLFrameElementBase(element) && element.containsFullScreenElement())
857                return true;
858            return Fullscreen::isActiveFullScreenElement(element);
859        case CSSSelector::PseudoFullScreenAncestor:
860            return element.containsFullScreenElement();
861        case CSSSelector::PseudoFullScreenDocument:
862            // While a Document is in the fullscreen state, the 'full-screen-document' pseudoclass applies
863            // to all elements of that Document.
864            if (!Fullscreen::isFullScreen(element.document()))
865                return false;
866            return true;
867        case CSSSelector::PseudoInRange:
868            element.document().setContainsValidityStyleRules();
869            return element.isInRange();
870        case CSSSelector::PseudoOutOfRange:
871            element.document().setContainsValidityStyleRules();
872            return element.isOutOfRange();
873        case CSSSelector::PseudoFutureCue:
874            return (element.isVTTElement() && !toVTTElement(element).isPastNode());
875        case CSSSelector::PseudoPastCue:
876            return (element.isVTTElement() && toVTTElement(element).isPastNode());
877
878        case CSSSelector::PseudoScope:
879            {
880                if (m_mode == SharingRules)
881                    return true;
882                const Node* contextualReferenceNode = !context.scope ? element.document().documentElement() : context.scope;
883                if (element == contextualReferenceNode)
884                    return true;
885                break;
886            }
887
888        case CSSSelector::PseudoUnresolved:
889            if (element.isUnresolvedCustomElement())
890                return true;
891            break;
892
893        case CSSSelector::PseudoHost:
894        case CSSSelector::PseudoHostContext:
895            {
896                if (m_mode == SharingRules)
897                    return true;
898                // :host only matches a shadow host when :host is in a shadow tree of the shadow host.
899                if (!context.scope)
900                    return false;
901                const ContainerNode* shadowHost = context.scope->shadowHost();
902                if (!shadowHost || shadowHost != element)
903                    return false;
904                ASSERT(element.shadow());
905
906                // For empty parameter case, i.e. just :host or :host().
907                if (!selector.selectorList()) // Use *'s specificity. So just 0.
908                    return true;
909
910                SelectorCheckingContext subContext(context);
911                subContext.isSubSelector = true;
912
913                bool matched = false;
914                unsigned maxSpecificity = 0;
915
916                // If one of simple selectors matches an element, returns SelectorMatches. Just "OR".
917                for (subContext.selector = selector.selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
918                    subContext.contextFlags = TreatShadowHostAsNormalScope;
919                    subContext.scope = context.scope;
920                    // Use NodeRenderingTraversal to traverse a composed ancestor list of a given element.
921                    Element* nextElement = &element;
922                    SelectorCheckingContext hostContext(subContext);
923                    do {
924                        MatchResult subResult;
925                        hostContext.element = nextElement;
926                        if (match(hostContext, siblingTraversalStrategy, &subResult) == SelectorMatches) {
927                            matched = true;
928                            // Consider div:host(div:host(div:host(div:host...))).
929                            maxSpecificity = std::max(maxSpecificity, hostContext.selector->specificity() + subResult.specificity);
930                            break;
931                        }
932                        hostContext.contextFlags = DefaultBehavior;
933                        hostContext.scope = nullptr;
934
935                        if (selector.pseudoType() == CSSSelector::PseudoHost)
936                            break;
937
938                        hostContext.elementStyle = 0;
939                        nextElement = NodeRenderingTraversal::parentElement(nextElement);
940                    } while (nextElement);
941                }
942                if (matched) {
943                    if (specificity)
944                        *specificity = maxSpecificity;
945                    return true;
946                }
947            }
948            break;
949        case CSSSelector::PseudoSpatialNavigationFocus:
950            return context.isUARule && matchesSpatialNavigationFocusPseudoClass(element);
951        case CSSSelector::PseudoListBox:
952            return context.isUARule && matchesListBoxPseudoClass(element);
953
954        case CSSSelector::PseudoHorizontal:
955        case CSSSelector::PseudoVertical:
956        case CSSSelector::PseudoDecrement:
957        case CSSSelector::PseudoIncrement:
958        case CSSSelector::PseudoStart:
959        case CSSSelector::PseudoEnd:
960        case CSSSelector::PseudoDoubleButton:
961        case CSSSelector::PseudoSingleButton:
962        case CSSSelector::PseudoNoButton:
963        case CSSSelector::PseudoCornerPresent:
964            return false;
965
966        case CSSSelector::PseudoUnknown:
967        case CSSSelector::PseudoNotParsed:
968        default:
969            ASSERT_NOT_REACHED();
970            break;
971        }
972        return false;
973    } else if (selector.match() == CSSSelector::PseudoElement && selector.pseudoType() == CSSSelector::PseudoCue) {
974        SelectorCheckingContext subContext(context);
975        subContext.isSubSelector = true;
976        subContext.contextFlags = DefaultBehavior;
977
978        const CSSSelector* contextSelector = context.selector;
979        ASSERT(contextSelector);
980        for (subContext.selector = contextSelector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(*subContext.selector)) {
981            if (match(subContext, siblingTraversalStrategy) == SelectorMatches)
982                return true;
983        }
984        return false;
985    }
986    // ### add the rest of the checks...
987    return true;
988}
989
990bool SelectorChecker::checkScrollbarPseudoClass(const SelectorCheckingContext& context, Document* document, const CSSSelector& selector) const
991{
992    RenderScrollbar* scrollbar = context.scrollbar;
993    ScrollbarPart part = context.scrollbarPart;
994
995    // FIXME: This is a temporary hack for resizers and scrollbar corners. Eventually :window-inactive should become a real
996    // pseudo class and just apply to everything.
997    if (selector.pseudoType() == CSSSelector::PseudoWindowInactive)
998        return !document->page()->focusController().isActive();
999
1000    if (!scrollbar)
1001        return false;
1002
1003    ASSERT(selector.match() == CSSSelector::PseudoClass);
1004    switch (selector.pseudoType()) {
1005    case CSSSelector::PseudoEnabled:
1006        return scrollbar->enabled();
1007    case CSSSelector::PseudoDisabled:
1008        return !scrollbar->enabled();
1009    case CSSSelector::PseudoHover:
1010        {
1011            ScrollbarPart hoveredPart = scrollbar->hoveredPart();
1012            if (part == ScrollbarBGPart)
1013                return hoveredPart != NoPart;
1014            if (part == TrackBGPart)
1015                return hoveredPart == BackTrackPart || hoveredPart == ForwardTrackPart || hoveredPart == ThumbPart;
1016            return part == hoveredPart;
1017        }
1018    case CSSSelector::PseudoActive:
1019        {
1020            ScrollbarPart pressedPart = scrollbar->pressedPart();
1021            if (part == ScrollbarBGPart)
1022                return pressedPart != NoPart;
1023            if (part == TrackBGPart)
1024                return pressedPart == BackTrackPart || pressedPart == ForwardTrackPart || pressedPart == ThumbPart;
1025            return part == pressedPart;
1026        }
1027    case CSSSelector::PseudoHorizontal:
1028        return scrollbar->orientation() == HorizontalScrollbar;
1029    case CSSSelector::PseudoVertical:
1030        return scrollbar->orientation() == VerticalScrollbar;
1031    case CSSSelector::PseudoDecrement:
1032        return part == BackButtonStartPart || part == BackButtonEndPart || part == BackTrackPart;
1033    case CSSSelector::PseudoIncrement:
1034        return part == ForwardButtonStartPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
1035    case CSSSelector::PseudoStart:
1036        return part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart;
1037    case CSSSelector::PseudoEnd:
1038        return part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
1039    case CSSSelector::PseudoDoubleButton:
1040        {
1041            ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1042            if (part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart)
1043                return buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth;
1044            if (part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart)
1045                return buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth;
1046            return false;
1047        }
1048    case CSSSelector::PseudoSingleButton:
1049        {
1050            ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1051            if (part == BackButtonStartPart || part == ForwardButtonEndPart || part == BackTrackPart || part == ForwardTrackPart)
1052                return buttonsPlacement == ScrollbarButtonsSingle;
1053            return false;
1054        }
1055    case CSSSelector::PseudoNoButton:
1056        {
1057            ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
1058            if (part == BackTrackPart)
1059                return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleEnd;
1060            if (part == ForwardTrackPart)
1061                return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleStart;
1062            return false;
1063        }
1064    case CSSSelector::PseudoCornerPresent:
1065        return scrollbar->scrollableArea()->isScrollCornerVisible();
1066    default:
1067        return false;
1068    }
1069}
1070
1071unsigned SelectorChecker::determineLinkMatchType(const CSSSelector& selector)
1072{
1073    unsigned linkMatchType = MatchAll;
1074
1075    // Statically determine if this selector will match a link in visited, unvisited or any state, or never.
1076    // :visited never matches other elements than the innermost link element.
1077    for (const CSSSelector* current = &selector; current; current = current->tagHistory()) {
1078        switch (current->pseudoType()) {
1079        case CSSSelector::PseudoNot:
1080            {
1081                // :not(:visited) is equivalent to :link. Parser enforces that :not can't nest.
1082                ASSERT(current->selectorList());
1083                for (const CSSSelector* subSelector = current->selectorList()->first(); subSelector; subSelector = subSelector->tagHistory()) {
1084                    CSSSelector::PseudoType subType = subSelector->pseudoType();
1085                    if (subType == CSSSelector::PseudoVisited)
1086                        linkMatchType &= ~SelectorChecker::MatchVisited;
1087                    else if (subType == CSSSelector::PseudoLink)
1088                        linkMatchType &= ~SelectorChecker::MatchLink;
1089                }
1090            }
1091            break;
1092        case CSSSelector::PseudoLink:
1093            linkMatchType &= ~SelectorChecker::MatchVisited;
1094            break;
1095        case CSSSelector::PseudoVisited:
1096            linkMatchType &= ~SelectorChecker::MatchLink;
1097            break;
1098        default:
1099            // We don't support :link and :visited inside :-webkit-any.
1100            break;
1101        }
1102        CSSSelector::Relation relation = current->relation();
1103        if (relation == CSSSelector::SubSelector)
1104            continue;
1105        if (relation != CSSSelector::Descendant && relation != CSSSelector::Child)
1106            return linkMatchType;
1107        if (linkMatchType != MatchAll)
1108            return linkMatchType;
1109    }
1110    return linkMatchType;
1111}
1112
1113bool SelectorChecker::isFrameFocused(const Element& element)
1114{
1115    return element.document().frame() && element.document().frame()->selection().isFocusedAndActive();
1116}
1117
1118bool SelectorChecker::matchesFocusPseudoClass(const Element& element)
1119{
1120    if (InspectorInstrumentation::forcePseudoState(const_cast<Element*>(&element), CSSSelector::PseudoFocus))
1121        return true;
1122    return element.focused() && isFrameFocused(element);
1123}
1124
1125bool SelectorChecker::matchesSpatialNavigationFocusPseudoClass(const Element& element)
1126{
1127    return isHTMLOptionElement(element) && toHTMLOptionElement(element).spatialNavigationFocused() && isFrameFocused(element);
1128}
1129
1130bool SelectorChecker::matchesListBoxPseudoClass(const Element& element)
1131{
1132    return isHTMLSelectElement(element) && !toHTMLSelectElement(element).usesMenuList();
1133}
1134
1135template
1136SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const DOMSiblingTraversalStrategy&, MatchResult*) const;
1137
1138template
1139SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const ShadowDOMSiblingTraversalStrategy&, MatchResult*) const;
1140
1141}
1142