0b3edf3a9dc0de7090bff660307bfc77dfdf9a72
[WebKit-https.git] / Source / WebCore / css / SelectorChecker.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
4  * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
5  * Copyright (C) 2005, 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 "SelectorChecker.h"
30
31 #include "CSSSelector.h"
32 #include "CSSSelectorList.h"
33 #include "Document.h"
34 #include "FocusController.h"
35 #include "Frame.h"
36 #include "FrameSelection.h"
37 #include "HTMLDocument.h"
38 #include "HTMLFrameElementBase.h"
39 #include "HTMLInputElement.h"
40 #include "HTMLNames.h"
41 #include "HTMLOptionElement.h"
42 #include "HTMLProgressElement.h"
43 #include "HTMLStyleElement.h"
44 #include "InsertionPoint.h"
45 #include "InspectorInstrumentation.h"
46 #include "NodeRenderStyle.h"
47 #include "Page.h"
48 #include "RenderObject.h"
49 #include "RenderScrollbar.h"
50 #include "RenderStyle.h"
51 #include "ScrollableArea.h"
52 #include "ScrollbarTheme.h"
53 #include "ShadowRoot.h"
54 #include "SiblingTraversalStrategies.h"
55 #include "StyledElement.h"
56 #include "Text.h"
57
58 #if ENABLE(VIDEO_TRACK)
59 #include "WebVTTElement.h"
60 #endif
61
62 namespace WebCore {
63
64 using namespace HTMLNames;
65
66 SelectorChecker::SelectorChecker(Document* document, Mode mode)
67     : m_strictParsing(!document->inQuirksMode())
68     , m_documentIsHTML(document->isHTMLDocument())
69     , m_mode(mode)
70 {
71 }
72
73 // Recursive check of selectors and combinators
74 // It can return 4 different values:
75 // * SelectorMatches          - the selector matches the element e
76 // * SelectorFailsLocally     - the selector fails for the element e
77 // * SelectorFailsAllSiblings - the selector fails for e and any sibling of e
78 // * SelectorFailsCompletely  - the selector fails for e and any sibling or ancestor of e
79 template<typename SiblingTraversalStrategy>
80 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext& context, PseudoId& dynamicPseudo, const SiblingTraversalStrategy& siblingTraversalStrategy) const
81 {
82     // first selector has to match
83     if (!checkOne(context, siblingTraversalStrategy))
84         return SelectorFailsLocally;
85
86     if (context.selector->m_match == CSSSelector::PseudoElement) {
87         if (context.selector->isCustomPseudoElement()) {
88             if (ShadowRoot* root = context.element->containingShadowRoot()) {
89                 if (context.element->shadowPseudoId() != context.selector->value())
90                     return SelectorFailsLocally;
91
92                 if (context.selector->pseudoType() == CSSSelector::PseudoWebKitCustomElement && root->type() != ShadowRoot::UserAgentShadowRoot)
93                     return SelectorFailsLocally;
94             } else
95                 return SelectorFailsLocally;
96         } else {
97             if ((!context.elementStyle && m_mode == ResolvingStyle) || m_mode == QueryingRules)
98                 return SelectorFailsLocally;
99
100             PseudoId pseudoId = CSSSelector::pseudoId(context.selector->pseudoType());
101             if (pseudoId == FIRST_LETTER) {
102                 if (Document* document = context.element->document())
103                     document->styleSheetCollection()->setUsesFirstLetterRules(true);
104             }
105             if (pseudoId != NOPSEUDO && m_mode != SharingRules)
106                 dynamicPseudo = pseudoId;
107         }
108     }
109
110     // The rest of the selectors has to match
111     CSSSelector::Relation relation = context.selector->relation();
112
113     // Prepare next selector
114     const CSSSelector* historySelector = context.selector->tagHistory();
115     if (!historySelector) {
116         if (context.behaviorAtBoundary == CrossesBoundary) {
117             ASSERT(context.scope);
118             return context.scope->contains(context.element) ? SelectorMatches : SelectorFailsLocally;
119         }
120         return SelectorMatches;
121     }
122
123     SelectorCheckingContext nextContext(context);
124     nextContext.selector = historySelector;
125
126     PseudoId ignoreDynamicPseudo = NOPSEUDO;
127     if (relation != CSSSelector::SubSelector) {
128         // Abort if the next selector would exceed the scope.
129         if (context.element == context.scope && context.behaviorAtBoundary != StaysWithinTreeScope)
130             return SelectorFailsCompletely;
131
132         // Bail-out if this selector is irrelevant for the pseudoId
133         if (context.pseudoId != NOPSEUDO && context.pseudoId != dynamicPseudo)
134             return SelectorFailsCompletely;
135
136         // Disable :visited matching when we see the first link or try to match anything else than an ancestors.
137         if (!context.isSubSelector && (context.element->isLink() || (relation != CSSSelector::Descendant && relation != CSSSelector::Child)))
138             nextContext.visitedMatchType = VisitedMatchDisabled;
139
140         nextContext.pseudoId = NOPSEUDO;
141     }
142
143     switch (relation) {
144     case CSSSelector::Descendant:
145         nextContext.element = context.element->parentElement();
146         nextContext.isSubSelector = false;
147         nextContext.elementStyle = 0;
148         for (; nextContext.element; nextContext.element = nextContext.element->parentElement()) {
149             Match match = this->match(nextContext, ignoreDynamicPseudo, siblingTraversalStrategy);
150             if (match == SelectorMatches || match == SelectorFailsCompletely)
151                 return match;
152             if (nextContext.element == nextContext.scope && nextContext.behaviorAtBoundary != StaysWithinTreeScope)
153                 return SelectorFailsCompletely;
154         }
155         return SelectorFailsCompletely;
156
157     case CSSSelector::Child:
158         nextContext.element = context.element->parentElement();
159         if (!nextContext.element)
160             return SelectorFailsCompletely;
161         nextContext.isSubSelector = false;
162         nextContext.elementStyle = 0;
163         return match(nextContext, ignoreDynamicPseudo, siblingTraversalStrategy);
164
165     case CSSSelector::DirectAdjacent:
166         if (m_mode == ResolvingStyle) {
167             if (Element* parentElement = context.element->parentElement())
168                 parentElement->setChildrenAffectedByDirectAdjacentRules();
169         }
170         nextContext.element = context.element->previousElementSibling();
171         if (!nextContext.element)
172             return SelectorFailsAllSiblings;
173         nextContext.isSubSelector = false;
174         nextContext.elementStyle = 0;
175         return match(nextContext, ignoreDynamicPseudo, siblingTraversalStrategy);
176
177     case CSSSelector::IndirectAdjacent:
178         if (m_mode == ResolvingStyle) {
179             if (Element* parentElement = context.element->parentElement())
180                 parentElement->setChildrenAffectedByForwardPositionalRules();
181         }
182         nextContext.element = context.element->previousElementSibling();
183         nextContext.isSubSelector = false;
184         nextContext.elementStyle = 0;
185         for (; nextContext.element; nextContext.element = nextContext.element->previousElementSibling()) {
186             Match match = this->match(nextContext, ignoreDynamicPseudo, siblingTraversalStrategy);
187             if (match == SelectorMatches || match == SelectorFailsAllSiblings || match == SelectorFailsCompletely)
188                 return match;
189         };
190         return SelectorFailsAllSiblings;
191
192     case CSSSelector::SubSelector:
193         // a selector is invalid if something follows a pseudo-element
194         // We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else)
195         // to follow the pseudo elements.
196         nextContext.hasScrollbarPseudo = dynamicPseudo != NOPSEUDO && (context.scrollbar || dynamicPseudo == SCROLLBAR_CORNER || dynamicPseudo == RESIZER);
197         nextContext.hasSelectionPseudo = dynamicPseudo == SELECTION;
198         if ((context.elementStyle || m_mode == CollectingRules || m_mode == QueryingRules) && dynamicPseudo != NOPSEUDO
199             && !nextContext.hasSelectionPseudo
200             && !(nextContext.hasScrollbarPseudo && nextContext.selector->m_match == CSSSelector::PseudoClass))
201             return SelectorFailsCompletely;
202         nextContext.isSubSelector = true;
203         return match(nextContext, dynamicPseudo, siblingTraversalStrategy);
204
205     case CSSSelector::ShadowDescendant:
206         {
207             // 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.
208             if (context.scope && context.scope->treeScope() == context.element->treeScope() && context.behaviorAtBoundary != StaysWithinTreeScope)
209                 return SelectorFailsCompletely;
210             Element* shadowHostNode = context.element->shadowHost();
211             if (!shadowHostNode)
212                 return SelectorFailsCompletely;
213             nextContext.element = shadowHostNode;
214             nextContext.isSubSelector = false;
215             nextContext.elementStyle = 0;
216             return match(nextContext, ignoreDynamicPseudo, siblingTraversalStrategy);
217         }
218 #if ENABLE(SHADOW_DOM)
219     case CSSSelector::ShadowDistributed:
220         {
221             Vector<InsertionPoint*, 8> insertionPoints;
222             for (Element* element = context.element; element; element = element->parentElement()) {
223                 insertionPoints.clear();
224                 collectInsertionPointsWhereNodeIsDistributed(element, insertionPoints);
225                 for (size_t i = 0; i < insertionPoints.size(); ++i) {
226                     nextContext.element = insertionPoints[i];
227                     nextContext.isSubSelector = false;
228                     nextContext.elementStyle = 0;
229                     if (match(nextContext, ignoreDynamicPseudo, siblingTraversalStrategy) == SelectorMatches)
230                         return SelectorMatches;
231                 }
232             }
233             return SelectorFailsCompletely;
234         }
235 #endif
236     }
237
238     ASSERT_NOT_REACHED();
239     return SelectorFailsCompletely;
240 }
241
242 static bool attributeValueMatches(const Attribute* attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
243 {
244     const AtomicString& value = attributeItem->value();
245     if (value.isNull())
246         return false;
247
248     switch (match) {
249     case CSSSelector::Exact:
250         if (caseSensitive ? selectorValue != value : !equalIgnoringCase(selectorValue, value))
251             return false;
252         break;
253     case CSSSelector::List:
254         {
255             // Ignore empty selectors or selectors containing spaces
256             if (selectorValue.contains(' ') || selectorValue.isEmpty())
257                 return false;
258
259             unsigned startSearchAt = 0;
260             while (true) {
261                 size_t foundPos = value.find(selectorValue, startSearchAt, caseSensitive);
262                 if (foundPos == notFound)
263                     return false;
264                 if (!foundPos || value[foundPos - 1] == ' ') {
265                     unsigned endStr = foundPos + selectorValue.length();
266                     if (endStr == value.length() || value[endStr] == ' ')
267                         break; // We found a match.
268                 }
269
270                 // No match. Keep looking.
271                 startSearchAt = foundPos + 1;
272             }
273             break;
274         }
275     case CSSSelector::Contain:
276         if (!value.contains(selectorValue, caseSensitive) || selectorValue.isEmpty())
277             return false;
278         break;
279     case CSSSelector::Begin:
280         if (!value.startsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
281             return false;
282         break;
283     case CSSSelector::End:
284         if (!value.endsWith(selectorValue, caseSensitive) || selectorValue.isEmpty())
285             return false;
286         break;
287     case CSSSelector::Hyphen:
288         if (value.length() < selectorValue.length())
289             return false;
290         if (!value.startsWith(selectorValue, caseSensitive))
291             return false;
292         // It they start the same, check for exact match or following '-':
293         if (value.length() != selectorValue.length() && value[selectorValue.length()] != '-')
294             return false;
295         break;
296     case CSSSelector::PseudoClass:
297     case CSSSelector::PseudoElement:
298     default:
299         break;
300     }
301
302     return true;
303 }
304
305 static bool anyAttributeMatches(Element* element, CSSSelector::Match match, const QualifiedName& selectorAttr, const AtomicString& selectorValue, bool caseSensitive)
306 {
307     ASSERT(element->hasAttributesWithoutUpdate());
308     for (size_t i = 0; i < element->attributeCount(); ++i) {
309         const Attribute* attributeItem = element->attributeItem(i);
310
311         if (!attributeItem->matches(selectorAttr))
312             continue;
313
314         if (attributeValueMatches(attributeItem, match, selectorValue, caseSensitive))
315             return true;
316     }
317
318     return false;
319 }
320
321 template<typename SiblingTraversalStrategy>
322 bool SelectorChecker::checkOne(const SelectorCheckingContext& context, const SiblingTraversalStrategy& siblingTraversalStrategy) const
323 {
324     Element* const & element = context.element;
325     const CSSSelector* const & selector = context.selector;
326     ASSERT(element);
327     ASSERT(selector);
328
329     if (selector->m_match == CSSSelector::Tag)
330         return SelectorChecker::tagMatches(element, selector->tagQName());
331
332     if (selector->m_match == CSSSelector::Class)
333         return element->hasClass() && static_cast<StyledElement*>(element)->classNames().contains(selector->value());
334
335     if (selector->m_match == CSSSelector::Id)
336         return element->hasID() && element->idForStyleResolution() == selector->value();
337
338     if (selector->isAttributeSelector()) {
339         const QualifiedName& attr = selector->attribute();
340
341         if (!element->hasAttributes())
342             return false;
343
344         bool caseSensitive = !m_documentIsHTML || HTMLDocument::isCaseSensitiveAttribute(attr);
345
346         if (!anyAttributeMatches(element, static_cast<CSSSelector::Match>(selector->m_match), attr, selector->value(), caseSensitive))
347             return false;
348     }
349
350     if (selector->m_match == CSSSelector::PseudoClass) {
351         // Handle :not up front.
352         if (selector->pseudoType() == CSSSelector::PseudoNot) {
353             const CSSSelectorList* selectorList = selector->selectorList();
354
355             // FIXME: We probably should fix the parser and make it never produce :not rules with missing selector list.
356             if (!selectorList)
357                 return false;
358
359             SelectorCheckingContext subContext(context);
360             subContext.isSubSelector = true;
361             for (subContext.selector = selectorList->first(); subContext.selector; subContext.selector = subContext.selector->tagHistory()) {
362                 // :not cannot nest. I don't really know why this is a
363                 // restriction in CSS3, but it is, so let's honor it.
364                 // the parser enforces that this never occurs
365                 ASSERT(subContext.selector->pseudoType() != CSSSelector::PseudoNot);
366                 // We select between :visited and :link when applying. We don't know which one applied (or not) yet.
367                 if (subContext.selector->pseudoType() == CSSSelector::PseudoVisited || (subContext.selector->pseudoType() == CSSSelector::PseudoLink && subContext.visitedMatchType == VisitedMatchEnabled))
368                     return true;
369                 if (!checkOne(subContext, DOMSiblingTraversalStrategy()))
370                     return true;
371             }
372         } else if (context.hasScrollbarPseudo) {
373             // CSS scrollbars match a specific subset of pseudo classes, and they have specialized rules for each
374             // (since there are no elements involved).
375             return checkScrollbarPseudoClass(context, element->document(), selector);
376         } else if (context.hasSelectionPseudo) {
377             if (selector->pseudoType() == CSSSelector::PseudoWindowInactive)
378                 return !element->document()->page()->focusController()->isActive();
379         }
380
381         // Normal element pseudo class checking.
382         switch (selector->pseudoType()) {
383             // Pseudo classes:
384         case CSSSelector::PseudoNot:
385             break; // Already handled up above.
386         case CSSSelector::PseudoEmpty:
387             {
388                 bool result = true;
389                 for (Node* n = element->firstChild(); n; n = n->nextSibling()) {
390                     if (n->isElementNode()) {
391                         result = false;
392                         break;
393                     }
394                     if (n->isTextNode()) {
395                         Text* textNode = toText(n);
396                         if (!textNode->data().isEmpty()) {
397                             result = false;
398                             break;
399                         }
400                     }
401                 }
402                 if (m_mode == ResolvingStyle) {
403                     element->setStyleAffectedByEmpty();
404                     if (context.elementStyle)
405                         context.elementStyle->setEmptyState(result);
406                     else if (element->renderStyle() && (element->document()->styleSheetCollection()->usesSiblingRules() || element->renderStyle()->unique()))
407                         element->renderStyle()->setEmptyState(result);
408                 }
409                 return result;
410             }
411         case CSSSelector::PseudoFirstChild:
412             // first-child matches the first child that is an element
413             if (Element* parentElement = element->parentElement()) {
414                 bool result = siblingTraversalStrategy.isFirstChild(element);
415                 if (m_mode == ResolvingStyle) {
416                     RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle();
417                     parentElement->setChildrenAffectedByFirstChildRules();
418                     if (result && childStyle)
419                         childStyle->setFirstChildState();
420                 }
421                 return result;
422             }
423             break;
424         case CSSSelector::PseudoFirstOfType:
425             // first-of-type matches the first element of its type
426             if (Element* parentElement = element->parentElement()) {
427                 bool result = siblingTraversalStrategy.isFirstOfType(element, element->tagQName());
428                 if (m_mode == ResolvingStyle)
429                     parentElement->setChildrenAffectedByForwardPositionalRules();
430                 return result;
431             }
432             break;
433         case CSSSelector::PseudoLastChild:
434             // last-child matches the last child that is an element
435             if (Element* parentElement = element->parentElement()) {
436                 bool result = parentElement->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
437                 if (m_mode == ResolvingStyle) {
438                     RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle();
439                     parentElement->setChildrenAffectedByLastChildRules();
440                     if (result && childStyle)
441                         childStyle->setLastChildState();
442                 }
443                 return result;
444             }
445             break;
446         case CSSSelector::PseudoLastOfType:
447             // last-of-type matches the last element of its type
448             if (Element* parentElement = element->parentElement()) {
449                 if (m_mode == ResolvingStyle)
450                     parentElement->setChildrenAffectedByBackwardPositionalRules();
451                 if (!parentElement->isFinishedParsingChildren())
452                     return false;
453                 return siblingTraversalStrategy.isLastOfType(element, element->tagQName());
454             }
455             break;
456         case CSSSelector::PseudoOnlyChild:
457             if (Element* parentElement = element->parentElement()) {
458                 bool firstChild = siblingTraversalStrategy.isFirstChild(element);
459                 bool onlyChild = firstChild && parentElement->isFinishedParsingChildren() && siblingTraversalStrategy.isLastChild(element);
460                 if (m_mode == ResolvingStyle) {
461                     RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle();
462                     parentElement->setChildrenAffectedByFirstChildRules();
463                     parentElement->setChildrenAffectedByLastChildRules();
464                     if (firstChild && childStyle)
465                         childStyle->setFirstChildState();
466                     if (onlyChild && childStyle)
467                         childStyle->setLastChildState();
468                 }
469                 return onlyChild;
470             }
471             break;
472         case CSSSelector::PseudoOnlyOfType:
473             // FIXME: This selector is very slow.
474             if (Element* parentElement = element->parentElement()) {
475                 if (m_mode == ResolvingStyle) {
476                     parentElement->setChildrenAffectedByForwardPositionalRules();
477                     parentElement->setChildrenAffectedByBackwardPositionalRules();
478                 }
479                 if (!parentElement->isFinishedParsingChildren())
480                     return false;
481                 return siblingTraversalStrategy.isFirstOfType(element, element->tagQName()) && siblingTraversalStrategy.isLastOfType(element, element->tagQName());
482             }
483             break;
484         case CSSSelector::PseudoNthChild:
485             if (!selector->parseNth())
486                 break;
487             if (Element* parentElement = element->parentElement()) {
488                 int count = 1 + siblingTraversalStrategy.countElementsBefore(element);
489                 if (m_mode == ResolvingStyle) {
490                     RenderStyle* childStyle = context.elementStyle ? context.elementStyle : element->renderStyle();
491                     element->setChildIndex(count);
492                     if (childStyle)
493                         childStyle->setUnique();
494                     parentElement->setChildrenAffectedByForwardPositionalRules();
495                 }
496
497                 if (selector->matchNth(count))
498                     return true;
499             }
500             break;
501         case CSSSelector::PseudoNthOfType:
502             if (!selector->parseNth())
503                 break;
504             if (Element* parentElement = element->parentElement()) {
505                 int count = 1 + siblingTraversalStrategy.countElementsOfTypeBefore(element, element->tagQName());
506                 if (m_mode == ResolvingStyle)
507                     parentElement->setChildrenAffectedByForwardPositionalRules();
508
509                 if (selector->matchNth(count))
510                     return true;
511             }
512             break;
513         case CSSSelector::PseudoNthLastChild:
514             if (!selector->parseNth())
515                 break;
516             if (Element* parentElement = element->parentElement()) {
517                 if (m_mode == ResolvingStyle)
518                     parentElement->setChildrenAffectedByBackwardPositionalRules();
519                 if (!parentElement->isFinishedParsingChildren())
520                     return false;
521                 int count = 1 + siblingTraversalStrategy.countElementsAfter(element);
522                 if (selector->matchNth(count))
523                     return true;
524             }
525             break;
526         case CSSSelector::PseudoNthLastOfType:
527             if (!selector->parseNth())
528                 break;
529             if (Element* parentElement = element->parentElement()) {
530                 if (m_mode == ResolvingStyle)
531                     parentElement->setChildrenAffectedByBackwardPositionalRules();
532                 if (!parentElement->isFinishedParsingChildren())
533                     return false;
534
535                 int count = 1 + siblingTraversalStrategy.countElementsOfTypeAfter(element, element->tagQName());
536                 if (selector->matchNth(count))
537                     return true;
538             }
539             break;
540         case CSSSelector::PseudoTarget:
541             if (element == element->document()->cssTarget())
542                 return true;
543             break;
544         case CSSSelector::PseudoAny:
545             {
546                 SelectorCheckingContext subContext(context);
547                 subContext.isSubSelector = true;
548                 PseudoId ignoreDynamicPseudo = NOPSEUDO;
549                 for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(subContext.selector)) {
550                     if (match(subContext, ignoreDynamicPseudo, siblingTraversalStrategy) == SelectorMatches)
551                         return true;
552                 }
553             }
554             break;
555         case CSSSelector::PseudoAutofill:
556             if (!element || !element->isFormControlElement())
557                 break;
558             if (HTMLInputElement* inputElement = element->toInputElement())
559                 return inputElement->isAutofilled();
560             break;
561         case CSSSelector::PseudoAnyLink:
562         case CSSSelector::PseudoLink:
563             // :visited and :link matches are separated later when applying the style. Here both classes match all links...
564             return element->isLink();
565         case CSSSelector::PseudoVisited:
566             // ...except if :visited matching is disabled for ancestor/sibling matching.
567             return element->isLink() && context.visitedMatchType == VisitedMatchEnabled;
568         case CSSSelector::PseudoDrag:
569             if (m_mode == ResolvingStyle) {
570                 if (context.elementStyle)
571                     context.elementStyle->setAffectedByDrag();
572                 else
573                     element->setChildrenAffectedByDrag(true);
574             }
575             if (element->renderer() && element->renderer()->isDragging())
576                 return true;
577             break;
578         case CSSSelector::PseudoFocus:
579             return matchesFocusPseudoClass(element);
580         case CSSSelector::PseudoHover:
581             // If we're in quirks mode, then hover should never match anchors with no
582             // href and *:hover should not match anything. This is important for sites like wsj.com.
583             if (m_strictParsing || context.isSubSelector || (selector->m_match == CSSSelector::Tag && selector->tagQName() != anyQName() && !element->hasTagName(aTag)) || element->isLink()) {
584                 if (m_mode == ResolvingStyle) {
585                     if (context.elementStyle)
586                         context.elementStyle->setAffectedByHover();
587                     else
588                         element->setChildrenAffectedByHover(true);
589                 }
590                 if (element->hovered() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoHover))
591                     return true;
592             }
593             break;
594         case CSSSelector::PseudoActive:
595             // If we're in quirks mode, then :active should never match anchors with no
596             // href and *:active should not match anything.
597             if (m_strictParsing || context.isSubSelector || (selector->m_match == CSSSelector::Tag && selector->tagQName() != anyQName() && !element->hasTagName(aTag)) || element->isLink()) {
598                 if (m_mode == ResolvingStyle) {
599                     if (context.elementStyle)
600                         context.elementStyle->setAffectedByActive();
601                     else
602                         element->setChildrenAffectedByActive(true);
603                 }
604                 if (element->active() || InspectorInstrumentation::forcePseudoState(element, CSSSelector::PseudoActive))
605                     return true;
606             }
607             break;
608         case CSSSelector::PseudoEnabled:
609             if (element && (element->isFormControlElement() || element->hasTagName(optionTag) || element->hasTagName(optgroupTag)))
610                 return element->isEnabledFormControl();
611             break;
612         case CSSSelector::PseudoFullPageMedia:
613             return element && element->document() && element->document()->isMediaDocument();
614             break;
615         case CSSSelector::PseudoDefault:
616             return element && element->isDefaultButtonForForm();
617         case CSSSelector::PseudoDisabled:
618             if (element && (element->isFormControlElement() || element->hasTagName(optionTag) || element->hasTagName(optgroupTag)))
619                 return !element->isEnabledFormControl();
620             break;
621         case CSSSelector::PseudoReadOnly:
622             return element && element->matchesReadOnlyPseudoClass();
623         case CSSSelector::PseudoReadWrite:
624             return element && element->matchesReadWritePseudoClass();
625         case CSSSelector::PseudoOptional:
626             return element && element->isOptionalFormControl();
627         case CSSSelector::PseudoRequired:
628             return element && element->isRequiredFormControl();
629         case CSSSelector::PseudoValid:
630             if (!element)
631                 return false;
632             element->document()->setContainsValidityStyleRules();
633             return element->willValidate() && element->isValidFormControlElement();
634         case CSSSelector::PseudoInvalid:
635             if (!element)
636                 return false;
637             element->document()->setContainsValidityStyleRules();
638             return element->willValidate() && !element->isValidFormControlElement();
639         case CSSSelector::PseudoChecked:
640             {
641                 if (!element)
642                     break;
643                 // Even though WinIE allows checked and indeterminate to co-exist, the CSS selector spec says that
644                 // you can't be both checked and indeterminate. We will behave like WinIE behind the scenes and just
645                 // obey the CSS spec here in the test for matching the pseudo.
646                 HTMLInputElement* inputElement = element->toInputElement();
647                 if (inputElement && inputElement->shouldAppearChecked() && !inputElement->shouldAppearIndeterminate())
648                     return true;
649                 if (element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected())
650                     return true;
651                 break;
652             }
653         case CSSSelector::PseudoIndeterminate:
654             return element && element->shouldAppearIndeterminate();
655         case CSSSelector::PseudoRoot:
656             if (element == element->document()->documentElement())
657                 return true;
658             break;
659         case CSSSelector::PseudoLang:
660             {
661                 AtomicString value;
662 #if ENABLE(VIDEO_TRACK)
663                 if (element->isWebVTTElement())
664                     value = toWebVTTElement(element)->language();
665                 else
666 #endif
667                     value = element->computeInheritedLanguage();
668                 const AtomicString& argument = selector->argument();
669                 if (value.isEmpty() || !value.startsWith(argument, false))
670                     break;
671                 if (value.length() != argument.length() && value[argument.length()] != '-')
672                     break;
673                 return true;
674             }
675 #if ENABLE(FULLSCREEN_API)
676         case CSSSelector::PseudoFullScreen:
677             // While a Document is in the fullscreen state, and the document's current fullscreen
678             // element is an element in the document, the 'full-screen' pseudoclass applies to
679             // that element. Also, an <iframe>, <object> or <embed> element whose child browsing
680             // context's Document is in the fullscreen state has the 'full-screen' pseudoclass applied.
681             if (element->isFrameElementBase() && static_cast<HTMLFrameElementBase*>(element)->containsFullScreenElement())
682                 return true;
683             if (!element->document()->webkitIsFullScreen())
684                 return false;
685             return element == element->document()->webkitCurrentFullScreenElement();
686         case CSSSelector::PseudoAnimatingFullScreenTransition:
687             if (element != element->document()->webkitCurrentFullScreenElement())
688                 return false;
689             return element->document()->isAnimatingFullScreen();
690         case CSSSelector::PseudoFullScreenAncestor:
691             return element->containsFullScreenElement();
692         case CSSSelector::PseudoFullScreenDocument:
693             // While a Document is in the fullscreen state, the 'full-screen-document' pseudoclass applies
694             // to all elements of that Document.
695             if (!element->document()->webkitIsFullScreen())
696                 return false;
697             return true;
698 #endif
699 #if ENABLE(IFRAME_SEAMLESS)
700         case CSSSelector::PseudoSeamlessDocument:
701             // While a document is rendered in a seamless iframe, the 'seamless-document' pseudoclass applies
702             // to all elements of that Document.
703             return element->document()->shouldDisplaySeamlesslyWithParent();
704 #endif
705         case CSSSelector::PseudoInRange:
706             if (!element)
707                 return false;
708             element->document()->setContainsValidityStyleRules();
709             return element->isInRange();
710         case CSSSelector::PseudoOutOfRange:
711             if (!element)
712                 return false;
713             element->document()->setContainsValidityStyleRules();
714             return element->isOutOfRange();
715 #if ENABLE(VIDEO_TRACK)
716         case CSSSelector::PseudoFutureCue:
717             return (element->isWebVTTElement() && !toWebVTTElement(element)->isPastNode());
718         case CSSSelector::PseudoPastCue:
719             return (element->isWebVTTElement() && toWebVTTElement(element)->isPastNode());
720 #endif
721
722         case CSSSelector::PseudoScope:
723             {
724                 const Node* contextualReferenceNode = !context.scope || context.behaviorAtBoundary == CrossesBoundary ? element->document()->documentElement() : context.scope;
725                 if (element == contextualReferenceNode)
726                     return true;
727                 break;
728             }
729
730         case CSSSelector::PseudoHorizontal:
731         case CSSSelector::PseudoVertical:
732         case CSSSelector::PseudoDecrement:
733         case CSSSelector::PseudoIncrement:
734         case CSSSelector::PseudoStart:
735         case CSSSelector::PseudoEnd:
736         case CSSSelector::PseudoDoubleButton:
737         case CSSSelector::PseudoSingleButton:
738         case CSSSelector::PseudoNoButton:
739         case CSSSelector::PseudoCornerPresent:
740             return false;
741
742         case CSSSelector::PseudoUnknown:
743         case CSSSelector::PseudoNotParsed:
744         default:
745             ASSERT_NOT_REACHED();
746             break;
747         }
748         return false;
749     }
750 #if ENABLE(VIDEO_TRACK)
751     else if (selector->m_match == CSSSelector::PseudoElement && selector->pseudoType() == CSSSelector::PseudoCue) {
752         SelectorCheckingContext subContext(context);
753         subContext.isSubSelector = true;
754
755         PseudoId ignoreDynamicPseudo = NOPSEUDO;
756         const CSSSelector* const & selector = context.selector;
757         for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(subContext.selector)) {
758             if (match(subContext, ignoreDynamicPseudo, siblingTraversalStrategy) == SelectorMatches)
759                 return true;
760         }
761         return false;
762     }
763 #endif
764     // ### add the rest of the checks...
765     return true;
766 }
767
768 bool SelectorChecker::checkScrollbarPseudoClass(const SelectorCheckingContext& context, Document* document, const CSSSelector* selector) const
769 {
770     RenderScrollbar* scrollbar = context.scrollbar;
771     ScrollbarPart part = context.scrollbarPart;
772
773     // FIXME: This is a temporary hack for resizers and scrollbar corners. Eventually :window-inactive should become a real
774     // pseudo class and just apply to everything.
775     if (selector->pseudoType() == CSSSelector::PseudoWindowInactive)
776         return !document->page()->focusController()->isActive();
777
778     if (!scrollbar)
779         return false;
780
781     ASSERT(selector->m_match == CSSSelector::PseudoClass);
782     switch (selector->pseudoType()) {
783     case CSSSelector::PseudoEnabled:
784         return scrollbar->enabled();
785     case CSSSelector::PseudoDisabled:
786         return !scrollbar->enabled();
787     case CSSSelector::PseudoHover:
788         {
789             ScrollbarPart hoveredPart = scrollbar->hoveredPart();
790             if (part == ScrollbarBGPart)
791                 return hoveredPart != NoPart;
792             if (part == TrackBGPart)
793                 return hoveredPart == BackTrackPart || hoveredPart == ForwardTrackPart || hoveredPart == ThumbPart;
794             return part == hoveredPart;
795         }
796     case CSSSelector::PseudoActive:
797         {
798             ScrollbarPart pressedPart = scrollbar->pressedPart();
799             if (part == ScrollbarBGPart)
800                 return pressedPart != NoPart;
801             if (part == TrackBGPart)
802                 return pressedPart == BackTrackPart || pressedPart == ForwardTrackPart || pressedPart == ThumbPart;
803             return part == pressedPart;
804         }
805     case CSSSelector::PseudoHorizontal:
806         return scrollbar->orientation() == HorizontalScrollbar;
807     case CSSSelector::PseudoVertical:
808         return scrollbar->orientation() == VerticalScrollbar;
809     case CSSSelector::PseudoDecrement:
810         return part == BackButtonStartPart || part == BackButtonEndPart || part == BackTrackPart;
811     case CSSSelector::PseudoIncrement:
812         return part == ForwardButtonStartPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
813     case CSSSelector::PseudoStart:
814         return part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart;
815     case CSSSelector::PseudoEnd:
816         return part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart;
817     case CSSSelector::PseudoDoubleButton:
818         {
819             ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
820             if (part == BackButtonStartPart || part == ForwardButtonStartPart || part == BackTrackPart)
821                 return buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth;
822             if (part == BackButtonEndPart || part == ForwardButtonEndPart || part == ForwardTrackPart)
823                 return buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth;
824             return false;
825         }
826     case CSSSelector::PseudoSingleButton:
827         {
828             ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
829             if (part == BackButtonStartPart || part == ForwardButtonEndPart || part == BackTrackPart || part == ForwardTrackPart)
830                 return buttonsPlacement == ScrollbarButtonsSingle;
831             return false;
832         }
833     case CSSSelector::PseudoNoButton:
834         {
835             ScrollbarButtonsPlacement buttonsPlacement = scrollbar->theme()->buttonsPlacement();
836             if (part == BackTrackPart)
837                 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleEnd;
838             if (part == ForwardTrackPart)
839                 return buttonsPlacement == ScrollbarButtonsNone || buttonsPlacement == ScrollbarButtonsDoubleStart;
840             return false;
841         }
842     case CSSSelector::PseudoCornerPresent:
843         return scrollbar->scrollableArea()->isScrollCornerVisible();
844     default:
845         return false;
846     }
847 }
848
849 unsigned SelectorChecker::determineLinkMatchType(const CSSSelector* selector)
850 {
851     unsigned linkMatchType = MatchAll;
852
853     // Statically determine if this selector will match a link in visited, unvisited or any state, or never.
854     // :visited never matches other elements than the innermost link element.
855     for (; selector; selector = selector->tagHistory()) {
856         switch (selector->pseudoType()) {
857         case CSSSelector::PseudoNot:
858             {
859                 // :not(:visited) is equivalent to :link. Parser enforces that :not can't nest.
860                 const CSSSelectorList* selectorList = selector->selectorList();
861                 if (!selectorList)
862                     break;
863
864                 for (const CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = subSelector->tagHistory()) {
865                     CSSSelector::PseudoType subType = subSelector->pseudoType();
866                     if (subType == CSSSelector::PseudoVisited)
867                         linkMatchType &= ~SelectorChecker::MatchVisited;
868                     else if (subType == CSSSelector::PseudoLink)
869                         linkMatchType &= ~SelectorChecker::MatchLink;
870                 }
871             }
872             break;
873         case CSSSelector::PseudoLink:
874             linkMatchType &= ~SelectorChecker::MatchVisited;
875             break;
876         case CSSSelector::PseudoVisited:
877             linkMatchType &= ~SelectorChecker::MatchLink;
878             break;
879         default:
880             // We don't support :link and :visited inside :-webkit-any.
881             break;
882         }
883         CSSSelector::Relation relation = selector->relation();
884         if (relation == CSSSelector::SubSelector)
885             continue;
886         if (relation != CSSSelector::Descendant && relation != CSSSelector::Child)
887             return linkMatchType;
888         if (linkMatchType != MatchAll)
889             return linkMatchType;
890     }
891     return linkMatchType;
892 }
893
894 bool SelectorChecker::isFrameFocused(const Element* element)
895 {
896     return element->document()->frame() && element->document()->frame()->selection()->isFocusedAndActive();
897 }
898
899 bool SelectorChecker::matchesFocusPseudoClass(const Element* element)
900 {
901     if (InspectorInstrumentation::forcePseudoState(const_cast<Element*>(element), CSSSelector::PseudoFocus))
902         return true;
903     return element->focused() && isFrameFocused(element);
904 }
905
906 template
907 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, PseudoId&, const DOMSiblingTraversalStrategy&) const;
908
909 template
910 SelectorChecker::Match SelectorChecker::match(const SelectorCheckingContext&, PseudoId&, const ShadowDOMSiblingTraversalStrategy&) const;
911
912 }