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
29namespace WebCore {
30    class CSSSelectorList;
31
32    // this class represents a selector for a StyleRule
33    class CSSSelector {
34        WTF_MAKE_FAST_ALLOCATED;
35    public:
36        CSSSelector();
37        CSSSelector(const CSSSelector&);
38        explicit CSSSelector(const QualifiedName&, bool tagIsForNamespaceRule = false);
39
40        ~CSSSelector();
41
42        /**
43         * Re-create selector text from selector's data
44         */
45        String selectorText(const String& = "") const;
46
47        // checks if the 2 selectors (including sub selectors) agree.
48        bool operator==(const CSSSelector&) const;
49
50        // tag == -1 means apply to all elements (Selector = *)
51
52        unsigned specificity() const;
53
54        /* how the attribute value has to match.... Default is Exact */
55        enum Match {
56            Unknown = 0,
57            Tag,
58            Id,
59            Class,
60            Exact,
61            Set,
62            List,
63            Hyphen,
64            PseudoClass,
65            PseudoElement,
66            Contain, // css3: E[foo*="bar"]
67            Begin, // css3: E[foo^="bar"]
68            End, // css3: E[foo$="bar"]
69            PagePseudoClass
70        };
71
72        enum Relation {
73            Descendant = 0,
74            Child,
75            DirectAdjacent,
76            IndirectAdjacent,
77            SubSelector,
78            ShadowDescendant,
79        };
80
81        enum PseudoClassType {
82            PseudoClassUnknown = 0,
83            PseudoClassEmpty,
84            PseudoClassFirstChild,
85            PseudoClassFirstOfType,
86            PseudoClassLastChild,
87            PseudoClassLastOfType,
88            PseudoClassOnlyChild,
89            PseudoClassOnlyOfType,
90            PseudoClassNthChild,
91            PseudoClassNthOfType,
92            PseudoClassNthLastChild,
93            PseudoClassNthLastOfType,
94            PseudoClassLink,
95            PseudoClassVisited,
96            PseudoClassAny,
97            PseudoClassAnyLink,
98            PseudoClassAutofill,
99            PseudoClassHover,
100            PseudoClassDrag,
101            PseudoClassFocus,
102            PseudoClassActive,
103            PseudoClassChecked,
104            PseudoClassEnabled,
105            PseudoClassFullPageMedia,
106            PseudoClassDefault,
107            PseudoClassDisabled,
108            PseudoClassOptional,
109            PseudoClassRequired,
110            PseudoClassReadOnly,
111            PseudoClassReadWrite,
112            PseudoClassValid,
113            PseudoClassInvalid,
114            PseudoClassIndeterminate,
115            PseudoClassTarget,
116            PseudoClassLang,
117            PseudoClassNot,
118            PseudoClassRoot,
119            PseudoClassScope,
120            PseudoClassWindowInactive,
121            PseudoClassCornerPresent,
122            PseudoClassDecrement,
123            PseudoClassIncrement,
124            PseudoClassHorizontal,
125            PseudoClassVertical,
126            PseudoClassStart,
127            PseudoClassEnd,
128            PseudoClassDoubleButton,
129            PseudoClassSingleButton,
130            PseudoClassNoButton,
131#if ENABLE(FULLSCREEN_API)
132            PseudoClassFullScreen,
133            PseudoClassFullScreenDocument,
134            PseudoClassFullScreenAncestor,
135            PseudoClassAnimatingFullScreenTransition,
136#endif
137            PseudoClassInRange,
138            PseudoClassOutOfRange,
139#if ENABLE(VIDEO_TRACK)
140            PseudoClassFuture,
141            PseudoClassPast,
142#endif
143        };
144
145        enum PseudoElementType {
146            PseudoElementUnknown = 0,
147            PseudoElementAfter,
148            PseudoElementBefore,
149#if ENABLE(VIDEO_TRACK)
150            PseudoElementCue,
151#endif
152            PseudoElementFirstLetter,
153            PseudoElementFirstLine,
154            PseudoElementResizer,
155            PseudoElementScrollbar,
156            PseudoElementScrollbarButton,
157            PseudoElementScrollbarCorner,
158            PseudoElementScrollbarThumb,
159            PseudoElementScrollbarTrack,
160            PseudoElementScrollbarTrackPiece,
161            PseudoElementSelection,
162            PseudoElementUserAgentCustom,
163            PseudoElementWebKitCustom,
164        };
165
166        enum PagePseudoClassType {
167            PagePseudoClassFirst = 1,
168            PagePseudoClassLeft,
169            PagePseudoClassRight,
170        };
171
172        enum MarginBoxType {
173            TopLeftCornerMarginBox,
174            TopLeftMarginBox,
175            TopCenterMarginBox,
176            TopRightMarginBox,
177            TopRightCornerMarginBox,
178            BottomLeftCornerMarginBox,
179            BottomLeftMarginBox,
180            BottomCenterMarginBox,
181            BottomRightMarginBox,
182            BottomRightCornerMarginBox,
183            LeftTopMarginBox,
184            LeftMiddleMarginBox,
185            LeftBottomMarginBox,
186            RightTopMarginBox,
187            RightMiddleMarginBox,
188            RightBottomMarginBox,
189        };
190
191        static PseudoElementType parsePseudoElementType(const String&);
192        static PseudoId pseudoId(PseudoElementType);
193
194        // Selectors are kept in an array by CSSSelectorList. The next component of the selector is
195        // the next item in the array.
196        const CSSSelector* tagHistory() const { return m_isLastInTagHistory ? 0 : const_cast<CSSSelector*>(this + 1); }
197
198        const QualifiedName& tagQName() const;
199        const AtomicString& value() const;
200        const QualifiedName& attribute() const;
201        const AtomicString& attributeCanonicalLocalName() const;
202        const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; }
203        const CSSSelectorList* selectorList() const { return m_hasRareData ? m_data.m_rareData->m_selectorList.get() : 0; }
204
205        void setPseudoElementType(PseudoElementType pseudoElementType) { m_pseudoType = pseudoElementType; }
206        void setPagePseudoType(PagePseudoClassType pagePseudoType) { m_pseudoType = pagePseudoType; }
207        void setValue(const AtomicString&);
208        void setAttribute(const QualifiedName&, bool isCaseInsensitive);
209        void setArgument(const AtomicString&);
210        void setSelectorList(std::unique_ptr<CSSSelectorList>);
211
212        bool parseNth() const;
213        bool matchNth(int count) const;
214        int nthA() const;
215        int nthB() const;
216
217        PseudoClassType pseudoClassType() const
218        {
219            ASSERT(m_match == PseudoClass);
220            return static_cast<PseudoClassType>(m_pseudoType);
221        }
222
223        PseudoElementType pseudoElementType() const
224        {
225            ASSERT(m_match == PseudoElement);
226            return static_cast<PseudoElementType>(m_pseudoType);
227        }
228
229        PagePseudoClassType pagePseudoClassType() const
230        {
231            ASSERT(m_match == PagePseudoClass);
232            return static_cast<PagePseudoClassType>(m_pseudoType);
233        }
234
235        bool matchesPseudoElement() const;
236        bool isUnknownPseudoElement() const;
237        bool isCustomPseudoElement() const;
238        bool isSiblingSelector() const;
239        bool isAttributeSelector() const;
240
241        Relation relation() const { return static_cast<Relation>(m_relation); }
242
243        bool isLastInSelectorList() const { return m_isLastInSelectorList; }
244        void setLastInSelectorList() { m_isLastInSelectorList = true; }
245        bool isLastInTagHistory() const { return m_isLastInTagHistory; }
246        void setNotLastInTagHistory() { m_isLastInTagHistory = false; }
247
248        bool isSimple() const;
249
250        bool isForPage() const { return m_isForPage; }
251        void setForPage() { m_isForPage = true; }
252
253        unsigned m_relation           : 3; // enum Relation
254        mutable unsigned m_match      : 4; // enum Match
255        mutable unsigned m_pseudoType : 8; // PseudoType
256
257    private:
258        mutable bool m_parsedNth      : 1; // Used for :nth-*
259        bool m_isLastInSelectorList   : 1;
260        bool m_isLastInTagHistory     : 1;
261        bool m_hasRareData            : 1;
262        bool m_isForPage              : 1;
263        bool m_tagIsForNamespaceRule  : 1;
264
265        unsigned specificityForOneSelector() const;
266        unsigned specificityForPage() const;
267
268        // Hide.
269        CSSSelector& operator=(const CSSSelector&);
270
271        struct RareData : public RefCounted<RareData> {
272            static PassRefPtr<RareData> create(PassRefPtr<AtomicStringImpl> value) { return adoptRef(new RareData(value)); }
273            ~RareData();
274
275            bool parseNth();
276            bool matchNth(int count);
277
278            AtomicStringImpl* m_value; // Plain pointer to keep things uniform with the union.
279            int m_a; // Used for :nth-*
280            int m_b; // Used for :nth-*
281            QualifiedName m_attribute; // used for attribute selector
282            AtomicString m_attributeCanonicalLocalName;
283            AtomicString m_argument; // Used for :contains, :lang and :nth-*
284            std::unique_ptr<CSSSelectorList> m_selectorList; // Used for :-webkit-any and :not
285
286        private:
287            RareData(PassRefPtr<AtomicStringImpl> value);
288        };
289        void createRareData();
290
291        union DataUnion {
292            DataUnion() : m_value(0) { }
293            AtomicStringImpl* m_value;
294            QualifiedName::QualifiedNameImpl* m_tagQName;
295            RareData* m_rareData;
296        } m_data;
297    };
298
299inline const QualifiedName& CSSSelector::attribute() const
300{
301    ASSERT(isAttributeSelector());
302    ASSERT(m_hasRareData);
303    return m_data.m_rareData->m_attribute;
304}
305
306inline const AtomicString& CSSSelector::attributeCanonicalLocalName() const
307{
308    ASSERT(isAttributeSelector());
309    ASSERT(m_hasRareData);
310    return m_data.m_rareData->m_attributeCanonicalLocalName;
311}
312
313inline bool CSSSelector::matchesPseudoElement() const
314{
315    return m_match == PseudoElement;
316}
317
318inline bool CSSSelector::isUnknownPseudoElement() const
319{
320    return m_match == PseudoElement && m_pseudoType == PseudoElementUnknown;
321}
322
323inline bool CSSSelector::isCustomPseudoElement() const
324{
325    return m_match == PseudoElement && (m_pseudoType == PseudoElementUserAgentCustom || m_pseudoType == PseudoElementWebKitCustom);
326}
327
328static inline bool pseudoClassIsRelativeToSiblings(CSSSelector::PseudoClassType type)
329{
330    return type == CSSSelector::PseudoClassEmpty
331        || type == CSSSelector::PseudoClassFirstChild
332        || type == CSSSelector::PseudoClassFirstOfType
333        || type == CSSSelector::PseudoClassLastChild
334        || type == CSSSelector::PseudoClassLastOfType
335        || type == CSSSelector::PseudoClassOnlyChild
336        || type == CSSSelector::PseudoClassOnlyOfType
337        || type == CSSSelector::PseudoClassNthChild
338        || type == CSSSelector::PseudoClassNthOfType
339        || type == CSSSelector::PseudoClassNthLastChild
340        || type == CSSSelector::PseudoClassNthLastOfType;
341}
342
343inline bool CSSSelector::isSiblingSelector() const
344{
345    return m_relation == DirectAdjacent
346        || m_relation == IndirectAdjacent
347        || (m_match == CSSSelector::PseudoClass && pseudoClassIsRelativeToSiblings(pseudoClassType()));
348}
349
350inline bool CSSSelector::isAttributeSelector() const
351{
352    return m_match == CSSSelector::Exact
353        || m_match ==  CSSSelector::Set
354        || m_match == CSSSelector::List
355        || m_match == CSSSelector::Hyphen
356        || m_match == CSSSelector::Contain
357        || m_match == CSSSelector::Begin
358        || m_match == CSSSelector::End;
359}
360
361inline void CSSSelector::setValue(const AtomicString& value)
362{
363    ASSERT(m_match != Tag);
364    // Need to do ref counting manually for the union.
365    if (m_hasRareData) {
366        if (m_data.m_rareData->m_value)
367            m_data.m_rareData->m_value->deref();
368        m_data.m_rareData->m_value = value.impl();
369        m_data.m_rareData->m_value->ref();
370        return;
371    }
372    if (m_data.m_value)
373        m_data.m_value->deref();
374    m_data.m_value = value.impl();
375    m_data.m_value->ref();
376}
377
378inline CSSSelector::CSSSelector()
379    : m_relation(Descendant)
380    , m_match(Unknown)
381    , m_pseudoType(0)
382    , m_parsedNth(false)
383    , m_isLastInSelectorList(false)
384    , m_isLastInTagHistory(true)
385    , m_hasRareData(false)
386    , m_isForPage(false)
387    , m_tagIsForNamespaceRule(false)
388{
389}
390
391inline CSSSelector::CSSSelector(const QualifiedName& tagQName, bool tagIsForNamespaceRule)
392    : m_relation(Descendant)
393    , m_match(Tag)
394    , m_pseudoType(0)
395    , m_parsedNth(false)
396    , m_isLastInSelectorList(false)
397    , m_isLastInTagHistory(true)
398    , m_hasRareData(false)
399    , m_isForPage(false)
400    , m_tagIsForNamespaceRule(tagIsForNamespaceRule)
401{
402    m_data.m_tagQName = tagQName.impl();
403    m_data.m_tagQName->ref();
404}
405
406inline CSSSelector::CSSSelector(const CSSSelector& o)
407    : m_relation(o.m_relation)
408    , m_match(o.m_match)
409    , m_pseudoType(o.m_pseudoType)
410    , m_parsedNth(o.m_parsedNth)
411    , m_isLastInSelectorList(o.m_isLastInSelectorList)
412    , m_isLastInTagHistory(o.m_isLastInTagHistory)
413    , m_hasRareData(o.m_hasRareData)
414    , m_isForPage(o.m_isForPage)
415    , m_tagIsForNamespaceRule(o.m_tagIsForNamespaceRule)
416{
417    if (o.m_match == Tag) {
418        m_data.m_tagQName = o.m_data.m_tagQName;
419        m_data.m_tagQName->ref();
420    } else if (o.m_hasRareData) {
421        m_data.m_rareData = o.m_data.m_rareData;
422        m_data.m_rareData->ref();
423    } else if (o.m_data.m_value) {
424        m_data.m_value = o.m_data.m_value;
425        m_data.m_value->ref();
426    }
427}
428
429inline CSSSelector::~CSSSelector()
430{
431    if (m_match == Tag)
432        m_data.m_tagQName->deref();
433    else if (m_hasRareData)
434        m_data.m_rareData->deref();
435    else if (m_data.m_value)
436        m_data.m_value->deref();
437}
438
439inline const QualifiedName& CSSSelector::tagQName() const
440{
441    ASSERT(m_match == Tag);
442    return *reinterpret_cast<const QualifiedName*>(&m_data.m_tagQName);
443}
444
445inline const AtomicString& CSSSelector::value() const
446{
447    ASSERT(m_match != Tag);
448    // AtomicString is really just an AtomicStringImpl* so the cast below is safe.
449    // FIXME: Perhaps call sites could be changed to accept AtomicStringImpl?
450    return *reinterpret_cast<const AtomicString*>(m_hasRareData ? &m_data.m_rareData->m_value : &m_data.m_value);
451}
452
453
454} // namespace WebCore
455
456#endif // CSSSelector_h
457