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