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_deleted(false)
48 , m_tag(anyQName())
49 {
50 }
51
CSSSelector(const QualifiedName & qName)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
~CSSSelector()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
pseudoType()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.
tagHistory()233 CSSSelector* tagHistory() const { return m_isLastInTagHistory ? 0 : const_cast<CSSSelector*>(this + 1); }
234
hasTag()235 bool hasTag() const { return m_tag != anyQName(); }
hasAttribute()236 bool hasAttribute() const { return m_match == Id || m_match == Class || (m_hasRareData && m_data.m_rareData->m_attribute != anyQName()); }
237
tag()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?
value()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;
argument()243 const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; }
selectorList()244 CSSSelectorList* selectorList() const { return m_hasRareData ? m_data.m_rareData->m_selectorList.get() : 0; }
245
setTag(const QualifiedName & value)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
relation()259 Relation relation() const { return static_cast<Relation>(m_relation); }
260
isLastInSelectorList()261 bool isLastInSelectorList() const { return m_isLastInSelectorList; }
setLastInSelectorList()262 void setLastInSelectorList() { m_isLastInSelectorList = true; }
isLastInTagHistory()263 bool isLastInTagHistory() const { return m_isLastInTagHistory; }
setNotLastInTagHistory()264 void setNotLastInTagHistory() { m_isLastInTagHistory = false; }
265
266 bool isSimple() const;
267
isForPage()268 bool isForPage() const { return m_isForPage; }
setForPage()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 {
DataUnion()307 DataUnion() : m_value(0) { }
308 AtomicStringImpl* m_value;
309 RareData* m_rareData;
310 } m_data;
311
312 QualifiedName m_tag;
313 };
314
matchesPseudoElement()315 inline bool CSSSelector::matchesPseudoElement() const
316 {
317 if (m_pseudoType == PseudoUnknown)
318 extractPseudoType();
319 return m_match == PseudoElement;
320 }
321
isUnknownPseudoElement()322 inline bool CSSSelector::isUnknownPseudoElement() const
323 {
324 return m_match == PseudoElement && m_pseudoType == PseudoUnknown;
325 }
326
isSiblingSelector()327 inline 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
setValue(const AtomicString & value)345 inline 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
move(PassOwnPtr<CSSSelector> from,CSSSelector * to)357 inline 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