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