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 "core/dom/QualifiedName.h"
26#include "core/rendering/style/RenderStyleConstants.h"
27#include "wtf/OwnPtr.h"
28#include "wtf/PassOwnPtr.h"
29
30namespace WebCore {
31    class CSSSelectorList;
32
33    // this class represents a selector for a StyleRule
34    class CSSSelector {
35        WTF_MAKE_FAST_ALLOCATED;
36    public:
37        CSSSelector();
38        CSSSelector(const CSSSelector&);
39        explicit CSSSelector(const QualifiedName&, bool tagIsForNamespaceRule = false);
40
41        ~CSSSelector();
42
43        /**
44         * Re-create selector text from selector's data
45         */
46        String selectorText(const String& = "") const;
47
48        // checks if the 2 selectors (including sub selectors) agree.
49        bool operator==(const CSSSelector&) const;
50
51        // tag == -1 means apply to all elements (Selector = *)
52
53        unsigned specificity() const;
54
55        /* how the attribute value has to match.... Default is Exact */
56        enum Match {
57            Unknown = 0,
58            Tag,
59            Id,
60            Class,
61            Exact,
62            Set,
63            List,
64            Hyphen,
65            PseudoClass,
66            PseudoElement,
67            Contain, // css3: E[foo*="bar"]
68            Begin, // css3: E[foo^="bar"]
69            End, // css3: E[foo$="bar"]
70            PagePseudoClass
71        };
72
73        enum Relation {
74            Descendant = 0,
75            Child,
76            DirectAdjacent,
77            IndirectAdjacent,
78            SubSelector,
79            ShadowPseudo,
80            // FIXME: rename ChildTree and DescendantTree when the spec for this is written down.
81            ChildTree,
82            DescendantTree
83        };
84
85        enum PseudoType {
86            PseudoNotParsed = 0,
87            PseudoUnknown,
88            PseudoEmpty,
89            PseudoFirstChild,
90            PseudoFirstOfType,
91            PseudoLastChild,
92            PseudoLastOfType,
93            PseudoOnlyChild,
94            PseudoOnlyOfType,
95            PseudoFirstLine,
96            PseudoFirstLetter,
97            PseudoNthChild,
98            PseudoNthOfType,
99            PseudoNthLastChild,
100            PseudoNthLastOfType,
101            PseudoLink,
102            PseudoVisited,
103            PseudoAny,
104            PseudoAnyLink,
105            PseudoAutofill,
106            PseudoHover,
107            PseudoDrag,
108            PseudoFocus,
109            PseudoActive,
110            PseudoChecked,
111            PseudoEnabled,
112            PseudoFullPageMedia,
113            PseudoDefault,
114            PseudoDisabled,
115            PseudoOptional,
116            PseudoRequired,
117            PseudoReadOnly,
118            PseudoReadWrite,
119            PseudoValid,
120            PseudoInvalid,
121            PseudoIndeterminate,
122            PseudoTarget,
123            PseudoBefore,
124            PseudoAfter,
125            PseudoBackdrop,
126            PseudoLang,
127            PseudoNot,
128            PseudoResizer,
129            PseudoRoot,
130            PseudoScope,
131            PseudoScrollbar,
132            PseudoScrollbarBack,
133            PseudoScrollbarButton,
134            PseudoScrollbarCorner,
135            PseudoScrollbarForward,
136            PseudoScrollbarThumb,
137            PseudoScrollbarTrack,
138            PseudoScrollbarTrackPiece,
139            PseudoWindowInactive,
140            PseudoCornerPresent,
141            PseudoDecrement,
142            PseudoIncrement,
143            PseudoHorizontal,
144            PseudoVertical,
145            PseudoStart,
146            PseudoEnd,
147            PseudoDoubleButton,
148            PseudoSingleButton,
149            PseudoNoButton,
150            PseudoSelection,
151            PseudoLeftPage,
152            PseudoRightPage,
153            PseudoFirstPage,
154            PseudoFullScreen,
155            PseudoFullScreenDocument,
156            PseudoFullScreenAncestor,
157            PseudoInRange,
158            PseudoOutOfRange,
159            PseudoUserAgentCustomElement,
160            PseudoWebKitCustomElement,
161            PseudoCue,
162            PseudoFutureCue,
163            PseudoPastCue,
164            PseudoSeamlessDocument,
165            PseudoDistributed,
166            PseudoUnresolved,
167            PseudoContent,
168            PseudoHost
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
207        // WARNING: Use of QualifiedName by attribute() is a lie.
208        // attribute() will return a QualifiedName with prefix and namespaceURI
209        // set to starAtom to mean "matches any namespace". Be very careful
210        // how you use the returned QualifiedName.
211        // http://www.w3.org/TR/css3-selectors/#attrnmsp
212        const QualifiedName& attribute() const;
213        const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; }
214        const CSSSelectorList* selectorList() const { return m_hasRareData ? m_data.m_rareData->m_selectorList.get() : 0; }
215
216        void setValue(const AtomicString&);
217        void setAttribute(const QualifiedName&);
218        void setArgument(const AtomicString&);
219        void setSelectorList(PassOwnPtr<CSSSelectorList>);
220        void setMatchUserAgentOnly();
221
222        bool parseNth() const;
223        bool matchNth(int count) const;
224
225        bool matchesPseudoElement() const;
226        bool isUnknownPseudoElement() const;
227        bool isCustomPseudoElement() const;
228        bool isDirectAdjacentSelector() const { return m_relation == DirectAdjacent; }
229        bool isSiblingSelector() const;
230        bool isAttributeSelector() const;
231        bool isDistributedPseudoElement() const;
232        bool isContentPseudoElement() const;
233        bool isHostPseudoClass() const;
234
235        Relation relation() const { return static_cast<Relation>(m_relation); }
236
237        bool isLastInSelectorList() const { return m_isLastInSelectorList; }
238        void setLastInSelectorList() { m_isLastInSelectorList = true; }
239        bool isLastInTagHistory() const { return m_isLastInTagHistory; }
240        void setNotLastInTagHistory() { m_isLastInTagHistory = false; }
241
242        // http://dev.w3.org/csswg/selectors4/#compound
243        bool isCompound() const;
244
245        bool isForPage() const { return m_isForPage; }
246        void setForPage() { m_isForPage = true; }
247
248        bool relationIsAffectedByPseudoContent() const { return m_relationIsAffectedByPseudoContent; }
249        void setRelationIsAffectedByPseudoContent() { m_relationIsAffectedByPseudoContent = true; }
250
251        unsigned m_relation           : 3; // enum Relation
252        mutable unsigned m_match      : 4; // enum Match
253        mutable unsigned m_pseudoType : 8; // PseudoType
254
255    private:
256        mutable unsigned m_parsedNth      : 1; // Used for :nth-*
257        unsigned m_isLastInSelectorList   : 1;
258        unsigned m_isLastInTagHistory     : 1;
259        unsigned m_hasRareData            : 1;
260        unsigned m_isForPage              : 1;
261        unsigned m_tagIsForNamespaceRule  : 1;
262        unsigned m_relationIsAffectedByPseudoContent  : 1;
263
264        unsigned specificityForOneSelector() const;
265        unsigned specificityForPage() const;
266        void extractPseudoType() const;
267
268        // Hide.
269        CSSSelector& operator=(const CSSSelector&);
270
271        struct RareData : public RefCounted<RareData> {
272            static PassRefPtr<RareData> create(PassRefPtr<StringImpl> value) { return adoptRef(new RareData(value)); }
273            ~RareData();
274
275            bool parseNth();
276            bool matchNth(int count);
277
278            StringImpl* 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_argument; // Used for :contains, :lang, :nth-*
283            OwnPtr<CSSSelectorList> m_selectorList; // Used for :-webkit-any and :not
284
285        private:
286            RareData(PassRefPtr<StringImpl> value);
287        };
288        void createRareData();
289
290        union DataUnion {
291            DataUnion() : m_value(0) { }
292            StringImpl* m_value;
293            QualifiedName::QualifiedNameImpl* m_tagQName;
294            RareData* m_rareData;
295        } m_data;
296    };
297
298inline const QualifiedName& CSSSelector::attribute() const
299{
300    ASSERT(isAttributeSelector());
301    ASSERT(m_hasRareData);
302    return m_data.m_rareData->m_attribute;
303}
304
305inline bool CSSSelector::matchesPseudoElement() const
306{
307    if (m_pseudoType == PseudoUnknown)
308        extractPseudoType();
309    return m_match == PseudoElement;
310}
311
312inline bool CSSSelector::isUnknownPseudoElement() const
313{
314    return m_match == PseudoElement && m_pseudoType == PseudoUnknown;
315}
316
317inline bool CSSSelector::isCustomPseudoElement() const
318{
319    return m_match == PseudoElement && (m_pseudoType == PseudoUserAgentCustomElement || m_pseudoType == PseudoWebKitCustomElement);
320}
321
322inline bool CSSSelector::isHostPseudoClass() const
323{
324    return m_match == PseudoClass && m_pseudoType == PseudoHost;
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 bool CSSSelector::isAttributeSelector() const
346{
347    return m_match == CSSSelector::Exact
348        || m_match ==  CSSSelector::Set
349        || m_match == CSSSelector::List
350        || m_match == CSSSelector::Hyphen
351        || m_match == CSSSelector::Contain
352        || m_match == CSSSelector::Begin
353        || m_match == CSSSelector::End;
354}
355
356inline bool CSSSelector::isDistributedPseudoElement() const
357{
358    return m_match == PseudoElement && pseudoType() == PseudoDistributed;
359}
360
361inline bool CSSSelector::isContentPseudoElement() const
362{
363    return m_match == PseudoElement && pseudoType() == PseudoContent;
364}
365
366inline void CSSSelector::setValue(const AtomicString& value)
367{
368    ASSERT(m_match != Tag);
369    ASSERT(m_pseudoType == PseudoNotParsed);
370    // Need to do ref counting manually for the union.
371    if (m_hasRareData) {
372        if (m_data.m_rareData->m_value)
373            m_data.m_rareData->m_value->deref();
374        m_data.m_rareData->m_value = value.impl();
375        m_data.m_rareData->m_value->ref();
376        return;
377    }
378    if (m_data.m_value)
379        m_data.m_value->deref();
380    m_data.m_value = value.impl();
381    m_data.m_value->ref();
382}
383
384inline CSSSelector::CSSSelector()
385    : m_relation(Descendant)
386    , m_match(Unknown)
387    , m_pseudoType(PseudoNotParsed)
388    , m_parsedNth(false)
389    , m_isLastInSelectorList(false)
390    , m_isLastInTagHistory(true)
391    , m_hasRareData(false)
392    , m_isForPage(false)
393    , m_tagIsForNamespaceRule(false)
394    , m_relationIsAffectedByPseudoContent(false)
395{
396}
397
398inline CSSSelector::CSSSelector(const QualifiedName& tagQName, bool tagIsForNamespaceRule)
399    : m_relation(Descendant)
400    , m_match(Tag)
401    , m_pseudoType(PseudoNotParsed)
402    , m_parsedNth(false)
403    , m_isLastInSelectorList(false)
404    , m_isLastInTagHistory(true)
405    , m_hasRareData(false)
406    , m_isForPage(false)
407    , m_tagIsForNamespaceRule(tagIsForNamespaceRule)
408    , m_relationIsAffectedByPseudoContent(false)
409{
410    m_data.m_tagQName = tagQName.impl();
411    m_data.m_tagQName->ref();
412}
413
414inline CSSSelector::CSSSelector(const CSSSelector& o)
415    : m_relation(o.m_relation)
416    , m_match(o.m_match)
417    , m_pseudoType(o.m_pseudoType)
418    , m_parsedNth(o.m_parsedNth)
419    , m_isLastInSelectorList(o.m_isLastInSelectorList)
420    , m_isLastInTagHistory(o.m_isLastInTagHistory)
421    , m_hasRareData(o.m_hasRareData)
422    , m_isForPage(o.m_isForPage)
423    , m_tagIsForNamespaceRule(o.m_tagIsForNamespaceRule)
424    , m_relationIsAffectedByPseudoContent(o.m_relationIsAffectedByPseudoContent)
425{
426    if (o.m_match == Tag) {
427        m_data.m_tagQName = o.m_data.m_tagQName;
428        m_data.m_tagQName->ref();
429    } else if (o.m_hasRareData) {
430        m_data.m_rareData = o.m_data.m_rareData;
431        m_data.m_rareData->ref();
432    } else if (o.m_data.m_value) {
433        m_data.m_value = o.m_data.m_value;
434        m_data.m_value->ref();
435    }
436}
437
438inline CSSSelector::~CSSSelector()
439{
440    if (m_match == Tag)
441        m_data.m_tagQName->deref();
442    else if (m_hasRareData)
443        m_data.m_rareData->deref();
444    else if (m_data.m_value)
445        m_data.m_value->deref();
446}
447
448inline const QualifiedName& CSSSelector::tagQName() const
449{
450    ASSERT(m_match == Tag);
451    return *reinterpret_cast<const QualifiedName*>(&m_data.m_tagQName);
452}
453
454inline const AtomicString& CSSSelector::value() const
455{
456    ASSERT(m_match != Tag);
457    // AtomicString is really just a StringImpl* so the cast below is safe.
458    // FIXME: Perhaps call sites could be changed to accept StringImpl?
459    return *reinterpret_cast<const AtomicString*>(m_hasRareData ? &m_data.m_rareData->m_value : &m_data.m_value);
460}
461
462
463} // namespace WebCore
464
465#endif // CSSSelector_h
466