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
30 namespace 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
pseudoType()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.
tagHistory()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;
argument()213 const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; }
selectorList()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;
isDirectAdjacentSelector()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
relation()235 Relation relation() const { return static_cast<Relation>(m_relation); }
236
isLastInSelectorList()237 bool isLastInSelectorList() const { return m_isLastInSelectorList; }
setLastInSelectorList()238 void setLastInSelectorList() { m_isLastInSelectorList = true; }
isLastInTagHistory()239 bool isLastInTagHistory() const { return m_isLastInTagHistory; }
setNotLastInTagHistory()240 void setNotLastInTagHistory() { m_isLastInTagHistory = false; }
241
242 // http://dev.w3.org/csswg/selectors4/#compound
243 bool isCompound() const;
244
isForPage()245 bool isForPage() const { return m_isForPage; }
setForPage()246 void setForPage() { m_isForPage = true; }
247
relationIsAffectedByPseudoContent()248 bool relationIsAffectedByPseudoContent() const { return m_relationIsAffectedByPseudoContent; }
setRelationIsAffectedByPseudoContent()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> {
createRareData272 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 {
DataUnion()291 DataUnion() : m_value(0) { }
292 StringImpl* m_value;
293 QualifiedName::QualifiedNameImpl* m_tagQName;
294 RareData* m_rareData;
295 } m_data;
296 };
297
attribute()298 inline const QualifiedName& CSSSelector::attribute() const
299 {
300 ASSERT(isAttributeSelector());
301 ASSERT(m_hasRareData);
302 return m_data.m_rareData->m_attribute;
303 }
304
matchesPseudoElement()305 inline bool CSSSelector::matchesPseudoElement() const
306 {
307 if (m_pseudoType == PseudoUnknown)
308 extractPseudoType();
309 return m_match == PseudoElement;
310 }
311
isUnknownPseudoElement()312 inline bool CSSSelector::isUnknownPseudoElement() const
313 {
314 return m_match == PseudoElement && m_pseudoType == PseudoUnknown;
315 }
316
isCustomPseudoElement()317 inline bool CSSSelector::isCustomPseudoElement() const
318 {
319 return m_match == PseudoElement && (m_pseudoType == PseudoUserAgentCustomElement || m_pseudoType == PseudoWebKitCustomElement);
320 }
321
isHostPseudoClass()322 inline bool CSSSelector::isHostPseudoClass() const
323 {
324 return m_match == PseudoClass && m_pseudoType == PseudoHost;
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
isAttributeSelector()345 inline 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
isDistributedPseudoElement()356 inline bool CSSSelector::isDistributedPseudoElement() const
357 {
358 return m_match == PseudoElement && pseudoType() == PseudoDistributed;
359 }
360
isContentPseudoElement()361 inline bool CSSSelector::isContentPseudoElement() const
362 {
363 return m_match == PseudoElement && pseudoType() == PseudoContent;
364 }
365
setValue(const AtomicString & value)366 inline 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
CSSSelector()384 inline 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
CSSSelector(const QualifiedName & tagQName,bool tagIsForNamespaceRule)398 inline 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
CSSSelector(const CSSSelector & o)414 inline 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
~CSSSelector()438 inline 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
tagQName()448 inline const QualifiedName& CSSSelector::tagQName() const
449 {
450 ASSERT(m_match == Tag);
451 return *reinterpret_cast<const QualifiedName*>(&m_data.m_tagQName);
452 }
453
value()454 inline 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