• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
60 namespace blink {
61 
62 using namespace HTMLNames;
63 
SelectorChecker(Document & document,Mode mode)64 SelectorChecker::SelectorChecker(Document& document, Mode mode)
65     : m_strictParsing(!document.inQuirksMode())
66     , m_mode(mode)
67 {
68 }
69 
matchesCustomPseudoElement(const Element * element,const CSSSelector & selector)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 
parentElement(const SelectorChecker::SelectorCheckingContext & context)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 
scopeContainsLastMatchedElement(const SelectorChecker::SelectorCheckingContext & context)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 
nextSelectorExceedsScope(const SelectorChecker::SelectorCheckingContext & context)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>
match(const SelectorCheckingContext & context,const SiblingTraversalStrategy & siblingTraversalStrategy,MatchResult * result) const124 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 
prepareNextContextForRelation(const SelectorChecker::SelectorCheckingContext & context)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 
isAuthorShadowRoot(const Node * node)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>
matchForSubSelector(const SelectorCheckingContext & context,const SiblingTraversalStrategy & siblingTraversalStrategy,MatchResult * result) const203 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 
selectorMatchesShadowRoot(const CSSSelector * selector)222 static bool selectorMatchesShadowRoot(const CSSSelector* selector)
223 {
224     return selector && selector->isShadowPseudoElement();
225 }
226 
227 template<typename SiblingTraversalStrategy>
matchForPseudoShadow(const ContainerNode * node,const SelectorCheckingContext & context,const SiblingTraversalStrategy & siblingTraversalStrategy,MatchResult * result) const228 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>
matchForRelation(const SelectorCheckingContext & context,const SiblingTraversalStrategy & siblingTraversalStrategy,MatchResult * result) const236 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>
matchForShadowDistributed(const Element * element,const SiblingTraversalStrategy & siblingTraversalStrategy,SelectorCheckingContext & nextContext,MatchResult * result) const361 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>
containsHTMLSpaceTemplate(const CharType * string,unsigned length)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 
containsHTMLSpace(const AtomicString & string)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 
attributeValueMatches(const Attribute & attributeItem,CSSSelector::Match match,const AtomicString & selectorValue,bool caseSensitive)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 
anyAttributeMatches(Element & element,CSSSelector::Match match,const CSSSelector & selector)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>
checkOne(const SelectorCheckingContext & context,const SiblingTraversalStrategy & siblingTraversalStrategy,unsigned * specificity) const500 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 
checkScrollbarPseudoClass(const SelectorCheckingContext & context,Document * document,const CSSSelector & selector) const990 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 
determineLinkMatchType(const CSSSelector & selector)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 
isFrameFocused(const Element & element)1113 bool SelectorChecker::isFrameFocused(const Element& element)
1114 {
1115     return element.document().frame() && element.document().frame()->selection().isFocusedAndActive();
1116 }
1117 
matchesFocusPseudoClass(const Element & element)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 
matchesSpatialNavigationFocusPseudoClass(const Element & element)1125 bool SelectorChecker::matchesSpatialNavigationFocusPseudoClass(const Element& element)
1126 {
1127     return isHTMLOptionElement(element) && toHTMLOptionElement(element).spatialNavigationFocused() && isFrameFocused(element);
1128 }
1129 
matchesListBoxPseudoClass(const Element & element)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