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