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#ifndef SelectorChecker_h
29#define SelectorChecker_h
30
31#include "core/css/CSSSelector.h"
32#include "core/dom/Element.h"
33#include "platform/scroll/ScrollTypes.h"
34
35namespace blink {
36
37class CSSSelector;
38class ContainerNode;
39class Element;
40class RenderScrollbar;
41class RenderStyle;
42
43class SelectorChecker {
44    WTF_MAKE_NONCOPYABLE(SelectorChecker);
45public:
46    enum Match { SelectorMatches, SelectorFailsLocally, SelectorFailsAllSiblings, SelectorFailsCompletely };
47    enum VisitedMatchType { VisitedMatchDisabled, VisitedMatchEnabled };
48    enum Mode { ResolvingStyle = 0, CollectingStyleRules, CollectingCSSRules, QueryingRules, SharingRules };
49    explicit SelectorChecker(Document&, Mode);
50    enum ContextFlags {
51        // FIXME: Revmoe DefaultBehavior.
52        DefaultBehavior = 0,
53        ScopeContainsLastMatchedElement = 1,
54        TreatShadowHostAsNormalScope = 2,
55    };
56
57    struct SelectorCheckingContext {
58        STACK_ALLOCATED();
59    public:
60        // Initial selector constructor
61        SelectorCheckingContext(const CSSSelector& selector, Element* element, VisitedMatchType visitedMatchType)
62            : selector(&selector)
63            , element(element)
64            , previousElement(nullptr)
65            , scope(nullptr)
66            , visitedMatchType(visitedMatchType)
67            , pseudoId(NOPSEUDO)
68            , elementStyle(0)
69            , scrollbar(0)
70            , scrollbarPart(NoPart)
71            , isSubSelector(false)
72            , hasScrollbarPseudo(false)
73            , hasSelectionPseudo(false)
74            , isUARule(false)
75            , contextFlags(DefaultBehavior)
76        {
77        }
78
79        const CSSSelector* selector;
80        RawPtrWillBeMember<Element> element;
81        RawPtrWillBeMember<Element> previousElement;
82        RawPtrWillBeMember<const ContainerNode> scope;
83        VisitedMatchType visitedMatchType;
84        PseudoId pseudoId;
85        RenderStyle* elementStyle;
86        RenderScrollbar* scrollbar;
87        ScrollbarPart scrollbarPart;
88        bool isSubSelector;
89        bool hasScrollbarPseudo;
90        bool hasSelectionPseudo;
91        bool isUARule;
92        ContextFlags contextFlags;
93    };
94
95    struct MatchResult {
96        MatchResult()
97            : dynamicPseudo(NOPSEUDO)
98            , specificity(0) { }
99
100        PseudoId dynamicPseudo;
101        unsigned specificity;
102    };
103
104    template<typename SiblingTraversalStrategy>
105    Match match(const SelectorCheckingContext&, const SiblingTraversalStrategy&, MatchResult* = 0) const;
106
107    template<typename SiblingTraversalStrategy>
108    bool checkOne(const SelectorCheckingContext&, const SiblingTraversalStrategy&, unsigned* specificity = 0) const;
109
110    bool strictParsing() const { return m_strictParsing; }
111
112    Mode mode() const { return m_mode; }
113
114    static bool tagMatches(const Element&, const QualifiedName&);
115    static bool isCommonPseudoClassSelector(const CSSSelector&);
116    static bool matchesFocusPseudoClass(const Element&);
117    static bool matchesSpatialNavigationFocusPseudoClass(const Element&);
118    static bool matchesListBoxPseudoClass(const Element&);
119    static bool checkExactAttribute(const Element&, const QualifiedName& selectorAttributeName, const StringImpl* value);
120
121    enum LinkMatchMask { MatchLink = 1, MatchVisited = 2, MatchAll = MatchLink | MatchVisited };
122    static unsigned determineLinkMatchType(const CSSSelector&);
123
124    static bool isHostInItsShadowTree(const Element&, const ContainerNode* scope);
125
126private:
127    template<typename SiblingTraversalStrategy>
128    Match matchForSubSelector(const SelectorCheckingContext&, const SiblingTraversalStrategy&, MatchResult*) const;
129    template<typename SiblingTraversalStrategy>
130    Match matchForRelation(const SelectorCheckingContext&, const SiblingTraversalStrategy&, MatchResult*) const;
131    template<typename SiblingTraversalStrategy>
132    Match matchForShadowDistributed(const Element*, const SiblingTraversalStrategy&, SelectorCheckingContext& nextContext, MatchResult* = 0) const;
133    template<typename SiblingTraversalStrategy>
134    Match matchForPseudoShadow(const ContainerNode*, const SelectorCheckingContext&, const SiblingTraversalStrategy&, MatchResult*) const;
135
136    bool checkScrollbarPseudoClass(const SelectorCheckingContext&, Document*, const CSSSelector&) const;
137
138    static bool isFrameFocused(const Element&);
139
140    bool m_strictParsing;
141    Mode m_mode;
142};
143
144inline bool SelectorChecker::isCommonPseudoClassSelector(const CSSSelector& selector)
145{
146    if (selector.match() != CSSSelector::PseudoClass)
147        return false;
148    CSSSelector::PseudoType pseudoType = selector.pseudoType();
149    return pseudoType == CSSSelector::PseudoLink
150        || pseudoType == CSSSelector::PseudoAnyLink
151        || pseudoType == CSSSelector::PseudoVisited
152        || pseudoType == CSSSelector::PseudoFocus;
153}
154
155inline bool SelectorChecker::tagMatches(const Element& element, const QualifiedName& tagQName)
156{
157    if (tagQName == anyQName())
158        return true;
159    const AtomicString& localName = tagQName.localName();
160    if (localName != starAtom && localName != element.localName())
161        return false;
162    const AtomicString& namespaceURI = tagQName.namespaceURI();
163    return namespaceURI == starAtom || namespaceURI == element.namespaceURI();
164}
165
166inline bool SelectorChecker::checkExactAttribute(const Element& element, const QualifiedName& selectorAttributeName, const StringImpl* value)
167{
168    AttributeCollection attributes = element.attributesWithoutUpdate();
169    AttributeCollection::iterator end = attributes.end();
170    for (AttributeCollection::iterator it = attributes.begin(); it != end; ++it) {
171        if (it->matches(selectorAttributeName) && (!value || it->value().impl() == value))
172            return true;
173    }
174    return false;
175}
176
177inline bool SelectorChecker::isHostInItsShadowTree(const Element& element, const ContainerNode* scope)
178{
179    return scope && scope->isInShadowTree() && scope->shadowHost() == element;
180}
181
182}
183
184#endif
185