Home | History | Annotate | Download | only in css
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 2004-2005 Allan Sandfeld Jensen (kde (at) carewolf.com)
      4  * Copyright (C) 2006, 2007 Nicholas Shanks (webkit (at) 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 (at) webkit.org>
      7  * Copyright (C) 2007, 2008 Eric Seidel <eric (at) 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 
     60 namespace blink {
     61 
     62 using namespace HTMLNames;
     63 
     64 SelectorChecker::SelectorChecker(Document& document, Mode mode)
     65     : m_strictParsing(!document.inQuirksMode())
     66     , m_mode(mode)
     67 {
     68 }
     69 
     70 static 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 
     82 static 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 
     94 static 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 
    109 static 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
    123 template<typename SiblingTraversalStrategy>
    124 SelectorChecker::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 
    189 static 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 
    197 static inline bool isAuthorShadowRoot(const Node* node)
    198 {
    199     return node && node->isShadowRoot() && toShadowRoot(node)->type() == ShadowRoot::AuthorShadowRoot;
    200 }
    201 
    202 template<typename SiblingTraversalStrategy>
    203 SelectorChecker::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 
    222 static bool selectorMatchesShadowRoot(const CSSSelector* selector)
    223 {
    224     return selector && selector->isShadowPseudoElement();
    225 }
    226 
    227 template<typename SiblingTraversalStrategy>
    228 SelectorChecker::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 
    235 template<typename SiblingTraversalStrategy>
    236 SelectorChecker::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 
    360 template<typename SiblingTraversalStrategy>
    361 SelectorChecker::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 
    378 template<typename CharType>
    379 static 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 
    387 static 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 
    394 static 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 
    457 static 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 
    499 template<typename SiblingTraversalStrategy>
    500 bool 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 
    990 bool 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 
   1071 unsigned 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 
   1113 bool SelectorChecker::isFrameFocused(const Element& element)
   1114 {
   1115     return element.document().frame() && element.document().frame()->selection().isFocusedAndActive();
   1116 }
   1117 
   1118 bool 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 
   1125 bool SelectorChecker::matchesSpatialNavigationFocusPseudoClass(const Element& element)
   1126 {
   1127     return isHTMLOptionElement(element) && toHTMLOptionElement(element).spatialNavigationFocused() && isFrameFocused(element);
   1128 }
   1129 
   1130 bool SelectorChecker::matchesListBoxPseudoClass(const Element& element)
   1131 {
   1132     return isHTMLSelectElement(element) && !toHTMLSelectElement(element).usesMenuList();
   1133 }
   1134 
   1135 template
   1136 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const DOMSiblingTraversalStrategy&, MatchResult*) const;
   1137 
   1138 template
   1139 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, const ShadowDOMSiblingTraversalStrategy&, MatchResult*) const;
   1140 
   1141 }
   1142