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 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_NONCOPYABLE(CSSSelector); WTF_MAKE_FAST_ALLOCATED;
37    public:
38        CSSSelector()
39            : m_relation(Descendant)
40            , m_match(None)
41            , m_pseudoType(PseudoNotParsed)
42            , m_parsedNth(false)
43            , m_isLastInSelectorList(false)
44            , m_isLastInTagHistory(true)
45            , m_hasRareData(false)
46            , m_isForPage(false)
47            , m_deleted(false)
48            , m_tag(anyQName())
49        {
50        }
51
52        CSSSelector(const QualifiedName& qName)
53            : m_relation(Descendant)
54            , m_match(None)
55            , m_pseudoType(PseudoNotParsed)
56            , m_parsedNth(false)
57            , m_isLastInSelectorList(false)
58            , m_isLastInTagHistory(true)
59            , m_hasRareData(false)
60            , m_isForPage(false)
61            , m_deleted(false)
62            , m_tag(qName)
63        {
64        }
65
66        ~CSSSelector()
67        {
68            if (m_deleted)
69                CRASH();
70            m_deleted = true;
71            if (m_hasRareData)
72                delete m_data.m_rareData;
73            else if (m_data.m_value)
74                m_data.m_value->deref();
75        }
76
77        /**
78         * Re-create selector text from selector's data
79         */
80        String selectorText() const;
81
82        // checks if the 2 selectors (including sub selectors) agree.
83        bool operator==(const CSSSelector&);
84
85        // tag == -1 means apply to all elements (Selector = *)
86
87        unsigned specificity() const;
88
89        /* how the attribute value has to match.... Default is Exact */
90        enum Match {
91            None = 0,
92            Id,
93            Class,
94            Exact,
95            Set,
96            List,
97            Hyphen,
98            PseudoClass,
99            PseudoElement,
100            Contain, // css3: E[foo*="bar"]
101            Begin, // css3: E[foo^="bar"]
102            End, // css3: E[foo$="bar"]
103            PagePseudoClass
104        };
105
106        enum Relation {
107            Descendant = 0,
108            Child,
109            DirectAdjacent,
110            IndirectAdjacent,
111            SubSelector,
112            ShadowDescendant
113        };
114
115        enum PseudoType {
116            PseudoNotParsed = 0,
117            PseudoUnknown,
118            PseudoEmpty,
119            PseudoFirstChild,
120            PseudoFirstOfType,
121            PseudoLastChild,
122            PseudoLastOfType,
123            PseudoOnlyChild,
124            PseudoOnlyOfType,
125            PseudoFirstLine,
126            PseudoFirstLetter,
127            PseudoNthChild,
128            PseudoNthOfType,
129            PseudoNthLastChild,
130            PseudoNthLastOfType,
131            PseudoLink,
132            PseudoVisited,
133            PseudoAny,
134            PseudoAnyLink,
135            PseudoAutofill,
136            PseudoHover,
137            PseudoDrag,
138            PseudoFocus,
139            PseudoActive,
140            PseudoChecked,
141            PseudoEnabled,
142            PseudoFullPageMedia,
143            PseudoDefault,
144            PseudoDisabled,
145            PseudoInputPlaceholder,
146            PseudoOptional,
147            PseudoRequired,
148            PseudoReadOnly,
149            PseudoReadWrite,
150            PseudoValid,
151            PseudoInvalid,
152            PseudoIndeterminate,
153            PseudoTarget,
154            PseudoBefore,
155            PseudoAfter,
156            PseudoLang,
157            PseudoNot,
158            PseudoResizer,
159            PseudoRoot,
160            PseudoScrollbar,
161            PseudoScrollbarBack,
162            PseudoScrollbarButton,
163            PseudoScrollbarCorner,
164            PseudoScrollbarForward,
165            PseudoScrollbarThumb,
166            PseudoScrollbarTrack,
167            PseudoScrollbarTrackPiece,
168            PseudoWindowInactive,
169            PseudoCornerPresent,
170            PseudoDecrement,
171            PseudoIncrement,
172            PseudoHorizontal,
173            PseudoVertical,
174            PseudoStart,
175            PseudoEnd,
176            PseudoDoubleButton,
177            PseudoSingleButton,
178            PseudoNoButton,
179            PseudoSelection,
180            PseudoFileUploadButton,
181            PseudoSearchCancelButton,
182            PseudoSearchDecoration,
183            PseudoSearchResultsDecoration,
184            PseudoSearchResultsButton,
185            PseudoInputListButton,
186#if ENABLE(INPUT_SPEECH)
187            PseudoInputSpeechButton,
188#endif
189            PseudoInnerSpinButton,
190            PseudoOuterSpinButton,
191            PseudoLeftPage,
192            PseudoRightPage,
193            PseudoFirstPage,
194#if ENABLE(FULLSCREEN_API)
195            PseudoFullScreen,
196            PseudoFullScreenDocument,
197#endif
198            PseudoInRange,
199            PseudoOutOfRange,
200        };
201
202        enum MarginBoxType {
203            TopLeftCornerMarginBox,
204            TopLeftMarginBox,
205            TopCenterMarginBox,
206            TopRightMarginBox,
207            TopRightCornerMarginBox,
208            BottomLeftCornerMarginBox,
209            BottomLeftMarginBox,
210            BottomCenterMarginBox,
211            BottomRightMarginBox,
212            BottomRightCornerMarginBox,
213            LeftTopMarginBox,
214            LeftMiddleMarginBox,
215            LeftBottomMarginBox,
216            RightTopMarginBox,
217            RightMiddleMarginBox,
218            RightBottomMarginBox,
219        };
220
221        PseudoType pseudoType() const
222        {
223            if (m_pseudoType == PseudoNotParsed)
224                extractPseudoType();
225            return static_cast<PseudoType>(m_pseudoType);
226        }
227
228        static PseudoType parsePseudoType(const AtomicString&);
229        static PseudoId pseudoId(PseudoType);
230
231        // Selectors are kept in an array by CSSSelectorList. The next component of the selector is
232        // the next item in the array.
233        CSSSelector* tagHistory() const { return m_isLastInTagHistory ? 0 : const_cast<CSSSelector*>(this + 1); }
234
235        bool hasTag() const { return m_tag != anyQName(); }
236        bool hasAttribute() const { return m_match == Id || m_match == Class || (m_hasRareData && m_data.m_rareData->m_attribute != anyQName()); }
237
238        const QualifiedName& tag() const { return m_tag; }
239        // AtomicString is really just an AtomicStringImpl* so the cast below is safe.
240        // FIXME: Perhaps call sites could be changed to accept AtomicStringImpl?
241        const AtomicString& value() const { return *reinterpret_cast<const AtomicString*>(m_hasRareData ? &m_data.m_rareData->m_value : &m_data.m_value); }
242        const QualifiedName& attribute() const;
243        const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; }
244        CSSSelectorList* selectorList() const { return m_hasRareData ? m_data.m_rareData->m_selectorList.get() : 0; }
245
246        void setTag(const QualifiedName& value) { m_tag = value; }
247        void setValue(const AtomicString&);
248        void setAttribute(const QualifiedName&);
249        void setArgument(const AtomicString&);
250        void setSelectorList(PassOwnPtr<CSSSelectorList>);
251
252        bool parseNth();
253        bool matchNth(int count);
254
255        bool matchesPseudoElement() const;
256        bool isUnknownPseudoElement() const;
257        bool isSiblingSelector() const;
258
259        Relation relation() const { return static_cast<Relation>(m_relation); }
260
261        bool isLastInSelectorList() const { return m_isLastInSelectorList; }
262        void setLastInSelectorList() { m_isLastInSelectorList = true; }
263        bool isLastInTagHistory() const { return m_isLastInTagHistory; }
264        void setNotLastInTagHistory() { m_isLastInTagHistory = false; }
265
266        bool isSimple() const;
267
268        bool isForPage() const { return m_isForPage; }
269        void setForPage() { m_isForPage = true; }
270
271        unsigned m_relation           : 3; // enum Relation
272        mutable unsigned m_match      : 4; // enum Match
273        mutable unsigned m_pseudoType : 8; // PseudoType
274
275    private:
276        bool m_parsedNth              : 1; // Used for :nth-*
277        bool m_isLastInSelectorList   : 1;
278        bool m_isLastInTagHistory     : 1;
279        bool m_hasRareData            : 1;
280        bool m_isForPage              : 1;
281        // FIXME: Remove once http://webkit.org/b/56124 is fixed.
282        bool m_deleted                : 1;
283
284        unsigned specificityForOneSelector() const;
285        unsigned specificityForPage() const;
286        void extractPseudoType() const;
287
288        struct RareData {
289            WTF_MAKE_NONCOPYABLE(RareData); WTF_MAKE_FAST_ALLOCATED;
290        public:
291            RareData(PassRefPtr<AtomicStringImpl> value);
292            ~RareData();
293
294            bool parseNth();
295            bool matchNth(int count);
296
297            AtomicStringImpl* m_value; // Plain pointer to keep things uniform with the union.
298            int m_a; // Used for :nth-*
299            int m_b; // Used for :nth-*
300            QualifiedName m_attribute; // used for attribute selector
301            AtomicString m_argument; // Used for :contains, :lang and :nth-*
302            OwnPtr<CSSSelectorList> m_selectorList; // Used for :-webkit-any and :not
303        };
304        void createRareData();
305
306        union DataUnion {
307            DataUnion() : m_value(0) { }
308            AtomicStringImpl* m_value;
309            RareData* m_rareData;
310        } m_data;
311
312        QualifiedName m_tag;
313    };
314
315inline bool CSSSelector::matchesPseudoElement() const
316{
317    if (m_pseudoType == PseudoUnknown)
318        extractPseudoType();
319    return m_match == PseudoElement;
320}
321
322inline bool CSSSelector::isUnknownPseudoElement() const
323{
324    return m_match == PseudoElement && m_pseudoType == PseudoUnknown;
325}
326
327inline bool CSSSelector::isSiblingSelector() const
328{
329    PseudoType type = pseudoType();
330    return m_relation == DirectAdjacent
331        || m_relation == IndirectAdjacent
332        || type == PseudoEmpty
333        || type == PseudoFirstChild
334        || type == PseudoFirstOfType
335        || type == PseudoLastChild
336        || type == PseudoLastOfType
337        || type == PseudoOnlyChild
338        || type == PseudoOnlyOfType
339        || type == PseudoNthChild
340        || type == PseudoNthOfType
341        || type == PseudoNthLastChild
342        || type == PseudoNthLastOfType;
343}
344
345inline void CSSSelector::setValue(const AtomicString& value)
346{
347    // Need to do ref counting manually for the union.
348    if (m_hasRareData) {
349        m_data.m_rareData->m_value = value.impl();
350        m_data.m_rareData->m_value->ref();
351        return;
352    }
353    m_data.m_value = value.impl();
354    m_data.m_value->ref();
355}
356
357inline void move(PassOwnPtr<CSSSelector> from, CSSSelector* to)
358{
359    memcpy(to, from.get(), sizeof(CSSSelector));
360    // We want to free the memory (which was allocated with fastNew), but we
361    // don't want the destructor to run since it will affect the copy we've just made.
362    fastDeleteSkippingDestructor(from.leakPtr());
363}
364
365} // namespace WebCore
366
367#endif // CSSSelector_h
368