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