1/*
2 * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
3 *               1999 Waldo Bastian (bastian@kde.org)
4 * Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB.  If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#ifndef CSSSelector_h
23#define CSSSelector_h
24
25#include "QualifiedName.h"
26#include "RenderStyleConstants.h"
27#include <wtf/Noncopyable.h>
28#include <wtf/OwnPtr.h>
29#include <wtf/PassOwnPtr.h>
30
31namespace WebCore {
32    class CSSSelectorList;
33
34    // this class represents a selector for a StyleRule
35    class CSSSelector {
36        WTF_MAKE_FAST_ALLOCATED;
37    public:
38        CSSSelector();
39        CSSSelector(const CSSSelector&);
40        explicit CSSSelector(const QualifiedName&, bool tagIsForNamespaceRule = false);
41
42        ~CSSSelector();
43
44        /**
45         * Re-create selector text from selector's data
46         */
47        String selectorText(const String& = "") const;
48
49        // checks if the 2 selectors (including sub selectors) agree.
50        bool operator==(const CSSSelector&) const;
51
52        // tag == -1 means apply to all elements (Selector = *)
53
54        unsigned specificity() const;
55
56        /* how the attribute value has to match.... Default is Exact */
57        enum Match {
58            Unknown = 0,
59            Tag,
60            Id,
61            Class,
62            Exact,
63            Set,
64            List,
65            Hyphen,
66            PseudoClass,
67            PseudoElement,
68            Contain, // css3: E[foo*="bar"]
69            Begin, // css3: E[foo^="bar"]
70            End, // css3: E[foo$="bar"]
71            PagePseudoClass
72        };
73
74        enum Relation {
75            Descendant = 0,
76            Child,
77            DirectAdjacent,
78            IndirectAdjacent,
79            SubSelector,
80            ShadowDescendant,
81        };
82
83        enum PseudoType {
84            PseudoNotParsed = 0,
85            PseudoUnknown,
86            PseudoEmpty,
87            PseudoFirstChild,
88            PseudoFirstOfType,
89            PseudoLastChild,
90            PseudoLastOfType,
91            PseudoOnlyChild,
92            PseudoOnlyOfType,
93            PseudoFirstLine,
94            PseudoFirstLetter,
95            PseudoNthChild,
96            PseudoNthOfType,
97            PseudoNthLastChild,
98            PseudoNthLastOfType,
99            PseudoLink,
100            PseudoVisited,
101            PseudoAny,
102            PseudoAnyLink,
103            PseudoAutofill,
104            PseudoHover,
105            PseudoDrag,
106            PseudoFocus,
107            PseudoActive,
108            PseudoChecked,
109            PseudoEnabled,
110            PseudoFullPageMedia,
111            PseudoDefault,
112            PseudoDisabled,
113            PseudoOptional,
114            PseudoRequired,
115            PseudoReadOnly,
116            PseudoReadWrite,
117            PseudoValid,
118            PseudoInvalid,
119            PseudoIndeterminate,
120            PseudoTarget,
121            PseudoBefore,
122            PseudoAfter,
123            PseudoLang,
124            PseudoNot,
125            PseudoResizer,
126            PseudoRoot,
127            PseudoScope,
128            PseudoScrollbar,
129            PseudoScrollbarBack,
130            PseudoScrollbarButton,
131            PseudoScrollbarCorner,
132            PseudoScrollbarForward,
133            PseudoScrollbarThumb,
134            PseudoScrollbarTrack,
135            PseudoScrollbarTrackPiece,
136            PseudoWindowInactive,
137            PseudoCornerPresent,
138            PseudoDecrement,
139            PseudoIncrement,
140            PseudoHorizontal,
141            PseudoVertical,
142            PseudoStart,
143            PseudoEnd,
144            PseudoDoubleButton,
145            PseudoSingleButton,
146            PseudoNoButton,
147            PseudoSelection,
148            PseudoLeftPage,
149            PseudoRightPage,
150            PseudoFirstPage,
151#if ENABLE(FULLSCREEN_API)
152            PseudoFullScreen,
153            PseudoFullScreenDocument,
154            PseudoFullScreenAncestor,
155            PseudoAnimatingFullScreenTransition,
156#endif
157            PseudoInRange,
158            PseudoOutOfRange,
159            PseudoUserAgentCustomElement,
160            PseudoWebKitCustomElement,
161#if ENABLE(VIDEO_TRACK)
162            PseudoCue,
163            PseudoFutureCue,
164            PseudoPastCue,
165#endif
166#if ENABLE(IFRAME_SEAMLESS)
167            PseudoSeamlessDocument,
168#endif
169        };
170
171        enum MarginBoxType {
172            TopLeftCornerMarginBox,
173            TopLeftMarginBox,
174            TopCenterMarginBox,
175            TopRightMarginBox,
176            TopRightCornerMarginBox,
177            BottomLeftCornerMarginBox,
178            BottomLeftMarginBox,
179            BottomCenterMarginBox,
180            BottomRightMarginBox,
181            BottomRightCornerMarginBox,
182            LeftTopMarginBox,
183            LeftMiddleMarginBox,
184            LeftBottomMarginBox,
185            RightTopMarginBox,
186            RightMiddleMarginBox,
187            RightBottomMarginBox,
188        };
189
190        PseudoType pseudoType() const
191        {
192            if (m_pseudoType == PseudoNotParsed)
193                extractPseudoType();
194            return static_cast<PseudoType>(m_pseudoType);
195        }
196
197        static PseudoType parsePseudoType(const AtomicString&);
198        static PseudoId pseudoId(PseudoType);
199
200        // Selectors are kept in an array by CSSSelectorList. The next component of the selector is
201        // the next item in the array.
202        const CSSSelector* tagHistory() const { return m_isLastInTagHistory ? 0 : const_cast<CSSSelector*>(this + 1); }
203
204        const QualifiedName& tagQName() const;
205        const AtomicString& value() const;
206        const QualifiedName& attribute() const;
207        const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; }
208        const CSSSelectorList* selectorList() const { return m_hasRareData ? m_data.m_rareData->m_selectorList.get() : 0; }
209
210        void setValue(const AtomicString&);
211        void setAttribute(const QualifiedName&);
212        void setArgument(const AtomicString&);
213        void setSelectorList(PassOwnPtr<CSSSelectorList>);
214
215        bool parseNth() const;
216        bool matchNth(int count) const;
217
218        bool matchesPseudoElement() const;
219        bool isUnknownPseudoElement() const;
220        bool isCustomPseudoElement() const;
221        bool isSiblingSelector() const;
222        bool isAttributeSelector() const;
223
224        Relation relation() const { return static_cast<Relation>(m_relation); }
225
226        bool isLastInSelectorList() const { return m_isLastInSelectorList; }
227        void setLastInSelectorList() { m_isLastInSelectorList = true; }
228        bool isLastInTagHistory() const { return m_isLastInTagHistory; }
229        void setNotLastInTagHistory() { m_isLastInTagHistory = false; }
230
231        bool isSimple() const;
232
233        bool isForPage() const { return m_isForPage; }
234        void setForPage() { m_isForPage = true; }
235
236        unsigned m_relation           : 3; // enum Relation
237        mutable unsigned m_match      : 4; // enum Match
238        mutable unsigned m_pseudoType : 8; // PseudoType
239
240    private:
241        mutable bool m_parsedNth      : 1; // Used for :nth-*
242        bool m_isLastInSelectorList   : 1;
243        bool m_isLastInTagHistory     : 1;
244        bool m_hasRareData            : 1;
245        bool m_isForPage              : 1;
246        bool m_tagIsForNamespaceRule  : 1;
247
248        unsigned specificityForOneSelector() const;
249        unsigned specificityForPage() const;
250        void extractPseudoType() const;
251
252        // Hide.
253        CSSSelector& operator=(const CSSSelector&);
254
255        struct RareData : public RefCounted<RareData> {
256            static PassRefPtr<RareData> create(PassRefPtr<AtomicStringImpl> value) { return adoptRef(new RareData(value)); }
257            ~RareData();
258
259            bool parseNth();
260            bool matchNth(int count);
261
262            AtomicStringImpl* m_value; // Plain pointer to keep things uniform with the union.
263            int m_a; // Used for :nth-*
264            int m_b; // Used for :nth-*
265            QualifiedName m_attribute; // used for attribute selector
266            AtomicString m_argument; // Used for :contains, :lang and :nth-*
267            OwnPtr<CSSSelectorList> m_selectorList; // Used for :-webkit-any and :not
268
269        private:
270            RareData(PassRefPtr<AtomicStringImpl> value);
271        };
272        void createRareData();
273
274        union DataUnion {
275            DataUnion() : m_value(0) { }
276            AtomicStringImpl* m_value;
277            QualifiedName::QualifiedNameImpl* m_tagQName;
278            RareData* m_rareData;
279        } m_data;
280    };
281
282inline const QualifiedName& CSSSelector::attribute() const
283{
284    ASSERT(isAttributeSelector());
285    ASSERT(m_hasRareData);
286    return m_data.m_rareData->m_attribute;
287}
288
289inline bool CSSSelector::matchesPseudoElement() const
290{
291    if (m_pseudoType == PseudoUnknown)
292        extractPseudoType();
293    return m_match == PseudoElement;
294}
295
296inline bool CSSSelector::isUnknownPseudoElement() const
297{
298    return m_match == PseudoElement && m_pseudoType == PseudoUnknown;
299}
300
301inline bool CSSSelector::isCustomPseudoElement() const
302{
303    return m_match == PseudoElement && (m_pseudoType == PseudoUserAgentCustomElement || m_pseudoType == PseudoWebKitCustomElement);
304}
305
306inline bool CSSSelector::isSiblingSelector() const
307{
308    PseudoType type = pseudoType();
309    return m_relation == DirectAdjacent
310        || m_relation == IndirectAdjacent
311        || type == PseudoEmpty
312        || type == PseudoFirstChild
313        || type == PseudoFirstOfType
314        || type == PseudoLastChild
315        || type == PseudoLastOfType
316        || type == PseudoOnlyChild
317        || type == PseudoOnlyOfType
318        || type == PseudoNthChild
319        || type == PseudoNthOfType
320        || type == PseudoNthLastChild
321        || type == PseudoNthLastOfType;
322}
323
324inline bool CSSSelector::isAttributeSelector() const
325{
326    return m_match == CSSSelector::Exact
327        || m_match ==  CSSSelector::Set
328        || m_match == CSSSelector::List
329        || m_match == CSSSelector::Hyphen
330        || m_match == CSSSelector::Contain
331        || m_match == CSSSelector::Begin
332        || m_match == CSSSelector::End;
333}
334
335inline void CSSSelector::setValue(const AtomicString& value)
336{
337    ASSERT(m_match != Tag);
338    ASSERT(m_pseudoType == PseudoNotParsed);
339    // Need to do ref counting manually for the union.
340    if (m_hasRareData) {
341        if (m_data.m_rareData->m_value)
342            m_data.m_rareData->m_value->deref();
343        m_data.m_rareData->m_value = value.impl();
344        m_data.m_rareData->m_value->ref();
345        return;
346    }
347    if (m_data.m_value)
348        m_data.m_value->deref();
349    m_data.m_value = value.impl();
350    m_data.m_value->ref();
351}
352
353inline CSSSelector::CSSSelector()
354    : m_relation(Descendant)
355    , m_match(Unknown)
356    , m_pseudoType(PseudoNotParsed)
357    , m_parsedNth(false)
358    , m_isLastInSelectorList(false)
359    , m_isLastInTagHistory(true)
360    , m_hasRareData(false)
361    , m_isForPage(false)
362    , m_tagIsForNamespaceRule(false)
363{
364}
365
366inline CSSSelector::CSSSelector(const QualifiedName& tagQName, bool tagIsForNamespaceRule)
367    : m_relation(Descendant)
368    , m_match(Tag)
369    , m_pseudoType(PseudoNotParsed)
370    , m_parsedNth(false)
371    , m_isLastInSelectorList(false)
372    , m_isLastInTagHistory(true)
373    , m_hasRareData(false)
374    , m_isForPage(false)
375    , m_tagIsForNamespaceRule(tagIsForNamespaceRule)
376{
377    m_data.m_tagQName = tagQName.impl();
378    m_data.m_tagQName->ref();
379}
380
381inline CSSSelector::CSSSelector(const CSSSelector& o)
382    : m_relation(o.m_relation)
383    , m_match(o.m_match)
384    , m_pseudoType(o.m_pseudoType)
385    , m_parsedNth(o.m_parsedNth)
386    , m_isLastInSelectorList(o.m_isLastInSelectorList)
387    , m_isLastInTagHistory(o.m_isLastInTagHistory)
388    , m_hasRareData(o.m_hasRareData)
389    , m_isForPage(o.m_isForPage)
390    , m_tagIsForNamespaceRule(o.m_tagIsForNamespaceRule)
391{
392    if (o.m_match == Tag) {
393        m_data.m_tagQName = o.m_data.m_tagQName;
394        m_data.m_tagQName->ref();
395    } else if (o.m_hasRareData) {
396        m_data.m_rareData = o.m_data.m_rareData;
397        m_data.m_rareData->ref();
398    } else if (o.m_data.m_value) {
399        m_data.m_value = o.m_data.m_value;
400        m_data.m_value->ref();
401    }
402}
403
404inline CSSSelector::~CSSSelector()
405{
406    if (m_match == Tag)
407        m_data.m_tagQName->deref();
408    else if (m_hasRareData)
409        m_data.m_rareData->deref();
410    else if (m_data.m_value)
411        m_data.m_value->deref();
412}
413
414inline const QualifiedName& CSSSelector::tagQName() const
415{
416    ASSERT(m_match == Tag);
417    return *reinterpret_cast<const QualifiedName*>(&m_data.m_tagQName);
418}
419
420inline const AtomicString& CSSSelector::value() const
421{
422    ASSERT(m_match != Tag);
423    // AtomicString is really just an AtomicStringImpl* so the cast below is safe.
424    // FIXME: Perhaps call sites could be changed to accept AtomicStringImpl?
425    return *reinterpret_cast<const AtomicString*>(m_hasRareData ? &m_data.m_rareData->m_value : &m_data.m_value);
426}
427
428
429} // namespace WebCore
430
431#endif // CSSSelector_h
432