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
35 // CSS selector representation is somewhat complicated and subtle. A representative list of selectors is
36 // in CSSSelectorTest; run it in a debug build to see useful debugging output.
37 //
38 // ** tagHistory() and relation():
39 //
40 // Selectors are represented as a linked list of simple selectors (defined more or less according to
41 // http://www.w3.org/TR/css3-selectors/#simple-selectors-dfn). The tagHistory() method returns the next
42 // simple selector in the list. The relation() method returns the relationship of the current simple selector to
43 // the one in tagHistory(). For example, the CSS selector .a.b #c is represented as:
44 //
45 // selectorText(): .a.b #c
46 // --> (relation == Descendant)
47 // selectorText(): .a.b
48 // --> (relation == SubSelector)
49 // selectorText(): .b
50 //
51 // Note that currently a bare selector such as ".a" has a relation() of Descendant. This is a bug - instead the relation should be
52 // "None".
53 //
54 // The order of tagHistory() varies depending on the situation.
55 // * Relations using combinators (http://www.w3.org/TR/css3-selectors/#combinators), such as descendant, sibling, etc., are parsed
56 // right-to-left (in the example above, this is why .c is earlier in the tagHistory() chain than .a.b).
57 // * SubSelector relations are parsed left-to-right in most cases (such as the .a.b example above); a counter-example is the
58 // ::content pseudo-element. Most (all?) other pseudo elements and pseudo classes are parsed left-to-right.
59 // * ShadowPseudo relations are parsed right-to-left. Example: summary::-webkit-details-marker is parsed as:
60 // selectorText(): summary::-webkit-details-marker
61 // --> (relation == ShadowPseudo)
62 // selectorText(): summary
63 //
64 // ** match():
65 //
66 // The match of the current simple selector tells us the type of selector, such as class, id, tagname, or pseudo-class.
67 // Inline comments in the Match enum give examples of when each type would occur.
68 //
69 // ** value(), attribute():
70 //
71 // value() tells you the value of the simple selector. For example, for class selectors, value() will tell you the class string,
72 // and for id selectors it will tell you the id(). See below for the special case of attribute selectors.
73 //
74 // ** Attribute selectors.
75 //
76 // Attribute selectors return the attribute name in the attribute() method. The value() method returns the value matched against
77 // in case of selectors like [attr="value"].
78 //
79 // ** isCustomPseudoElement():
80 //
81 // It appears this is used only for pseudo elements that appear in user-agent shadow DOM. They are not exposed to author-created
82 // shadow DOM.
83
84 class CSSSelector {
85 WTF_MAKE_FAST_ALLOCATED;
86 public:
87 CSSSelector();
88 CSSSelector(const CSSSelector&);
89 explicit CSSSelector(const QualifiedName&, bool tagIsForNamespaceRule = false);
90
91 ~CSSSelector();
92
93 /**
94 * Re-create selector text from selector's data
95 */
96 String selectorText(const String& = "") const;
97
98 // checks if the 2 selectors (including sub selectors) agree.
99 bool operator==(const CSSSelector&) const;
100
101 // tag == -1 means apply to all elements (Selector = *)
102
103 // http://www.w3.org/TR/css3-selectors/#specificity
104 // We use 256 as the base of the specificity number system.
105 unsigned specificity() const;
106
107 /* how the attribute value has to match.... Default is Exact */
108 enum Match {
109 Unknown = 0,
110 Tag, // Example: div
111 Id, // Example: #id
112 Class, // example: .class
113 PseudoClass, // Example: :nth-child(2)
114 PseudoElement, // Example: ::first-line
115 PagePseudoClass, // ??
116 Exact, // Example: E[foo="bar"]
117 Set, // Example: E[foo]
118 Hyphen, // Example: E[foo|="bar"]
119 List, // Example: E[foo~="bar"]
120 Contain, // css3: E[foo*="bar"]
121 Begin, // css3: E[foo^="bar"]
122 End, // css3: E[foo$="bar"]
123 FirstAttributeSelectorMatch = Exact,
124 };
125
126 enum Relation {
127 Descendant = 0, // "Space" combinator
128 Child, // > combinator
129 DirectAdjacent, // + combinator
130 IndirectAdjacent, // ~ combinator
131 SubSelector, // "No space" combinator
132 ShadowPseudo, // Special case of shadow DOM pseudo elements / shadow pseudo element
133 ShadowDeep // /deep/ combinator
134 };
135
136 enum PseudoType {
137 PseudoNotParsed = 0,
138 PseudoUnknown,
139 PseudoEmpty,
140 PseudoFirstChild,
141 PseudoFirstOfType,
142 PseudoLastChild,
143 PseudoLastOfType,
144 PseudoOnlyChild,
145 PseudoOnlyOfType,
146 PseudoFirstLine,
147 PseudoFirstLetter,
148 PseudoNthChild,
149 PseudoNthOfType,
150 PseudoNthLastChild,
151 PseudoNthLastOfType,
152 PseudoLink,
153 PseudoVisited,
154 PseudoAny,
155 PseudoAnyLink,
156 PseudoAutofill,
157 PseudoHover,
158 PseudoDrag,
159 PseudoFocus,
160 PseudoActive,
161 PseudoChecked,
162 PseudoEnabled,
163 PseudoFullPageMedia,
164 PseudoDefault,
165 PseudoDisabled,
166 PseudoOptional,
167 PseudoRequired,
168 PseudoReadOnly,
169 PseudoReadWrite,
170 PseudoValid,
171 PseudoInvalid,
172 PseudoIndeterminate,
173 PseudoTarget,
174 PseudoBefore,
175 PseudoAfter,
176 PseudoBackdrop,
177 PseudoLang,
178 PseudoNot,
179 PseudoResizer,
180 PseudoRoot,
181 PseudoScope,
182 PseudoScrollbar,
183 PseudoScrollbarBack,
184 PseudoScrollbarButton,
185 PseudoScrollbarCorner,
186 PseudoScrollbarForward,
187 PseudoScrollbarThumb,
188 PseudoScrollbarTrack,
189 PseudoScrollbarTrackPiece,
190 PseudoWindowInactive,
191 PseudoCornerPresent,
192 PseudoDecrement,
193 PseudoIncrement,
194 PseudoHorizontal,
195 PseudoVertical,
196 PseudoStart,
197 PseudoEnd,
198 PseudoDoubleButton,
199 PseudoSingleButton,
200 PseudoNoButton,
201 PseudoSelection,
202 PseudoLeftPage,
203 PseudoRightPage,
204 PseudoFirstPage,
205 PseudoFullScreen,
206 PseudoFullScreenDocument,
207 PseudoFullScreenAncestor,
208 PseudoInRange,
209 PseudoOutOfRange,
210 PseudoUserAgentCustomElement,
211 PseudoWebKitCustomElement,
212 PseudoCue,
213 PseudoFutureCue,
214 PseudoPastCue,
215 PseudoUnresolved,
216 PseudoContent,
217 PseudoHost,
218 PseudoHostContext,
219 PseudoShadow
220 };
221
222 enum MarginBoxType {
223 TopLeftCornerMarginBox,
224 TopLeftMarginBox,
225 TopCenterMarginBox,
226 TopRightMarginBox,
227 TopRightCornerMarginBox,
228 BottomLeftCornerMarginBox,
229 BottomLeftMarginBox,
230 BottomCenterMarginBox,
231 BottomRightMarginBox,
232 BottomRightCornerMarginBox,
233 LeftTopMarginBox,
234 LeftMiddleMarginBox,
235 LeftBottomMarginBox,
236 RightTopMarginBox,
237 RightMiddleMarginBox,
238 RightBottomMarginBox,
239 };
240
pseudoType()241 PseudoType pseudoType() const
242 {
243 if (m_pseudoType == PseudoNotParsed)
244 extractPseudoType();
245 return static_cast<PseudoType>(m_pseudoType);
246 }
247
248 static PseudoType parsePseudoType(const AtomicString&);
249 static PseudoId pseudoId(PseudoType);
250
251 // Selectors are kept in an array by CSSSelectorList. The next component of the selector is
252 // the next item in the array.
tagHistory()253 const CSSSelector* tagHistory() const { return m_isLastInTagHistory ? 0 : const_cast<CSSSelector*>(this + 1); }
254
255 const QualifiedName& tagQName() const;
256 const AtomicString& value() const;
257
258 // WARNING: Use of QualifiedName by attribute() is a lie.
259 // attribute() will return a QualifiedName with prefix and namespaceURI
260 // set to starAtom to mean "matches any namespace". Be very careful
261 // how you use the returned QualifiedName.
262 // http://www.w3.org/TR/css3-selectors/#attrnmsp
263 const QualifiedName& attribute() const;
264 // Returns the argument of a parameterized selector. For example, nth-child(2) would have an argument of 2.
argument()265 const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; }
selectorList()266 const CSSSelectorList* selectorList() const { return m_hasRareData ? m_data.m_rareData->m_selectorList.get() : 0; }
267
268 #ifndef NDEBUG
269 void show() const;
270 void show(int indent) const;
271 #endif
272
273 void setValue(const AtomicString&);
274 void setAttribute(const QualifiedName&);
275 void setArgument(const AtomicString&);
276 void setSelectorList(PassOwnPtr<CSSSelectorList>);
277 void setMatchUserAgentOnly();
278
279 bool parseNth() const;
280 bool matchNth(int count) const;
281
282 bool matchesPseudoElement() const;
283 bool isUnknownPseudoElement() const;
284 bool isCustomPseudoElement() const;
isDirectAdjacentSelector()285 bool isDirectAdjacentSelector() const { return m_relation == DirectAdjacent; }
286 bool isSiblingSelector() const;
287 bool isAttributeSelector() const;
288 bool isContentPseudoElement() const;
289 bool isShadowPseudoElement() const;
290 bool isHostPseudoClass() const;
291
292 // FIXME: selectors with no tagHistory() get a relation() of Descendant (and sometimes even SubSelector). It should instead be
293 // None.
relation()294 Relation relation() const { return static_cast<Relation>(m_relation); }
setRelation(Relation relation)295 void setRelation(Relation relation)
296 {
297 m_relation = relation;
298 ASSERT(static_cast<Relation>(m_relation) == relation); // using a bitfield.
299 }
300
match()301 Match match() const { return static_cast<Match>(m_match); }
setMatch(Match match)302 void setMatch(Match match)
303 {
304 m_match = match;
305 ASSERT(static_cast<Match>(m_match) == match); // using a bitfield.
306 }
307
isLastInSelectorList()308 bool isLastInSelectorList() const { return m_isLastInSelectorList; }
setLastInSelectorList()309 void setLastInSelectorList() { m_isLastInSelectorList = true; }
isLastInTagHistory()310 bool isLastInTagHistory() const { return m_isLastInTagHistory; }
setNotLastInTagHistory()311 void setNotLastInTagHistory() { m_isLastInTagHistory = false; }
312
313 // http://dev.w3.org/csswg/selectors4/#compound
314 bool isCompound() const;
315
isForPage()316 bool isForPage() const { return m_isForPage; }
setForPage()317 void setForPage() { m_isForPage = true; }
318
relationIsAffectedByPseudoContent()319 bool relationIsAffectedByPseudoContent() const { return m_relationIsAffectedByPseudoContent; }
setRelationIsAffectedByPseudoContent()320 void setRelationIsAffectedByPseudoContent() { m_relationIsAffectedByPseudoContent = true; }
321
322 private:
323 unsigned m_relation : 3; // enum Relation
324 mutable unsigned m_match : 4; // enum Match
325 mutable unsigned m_pseudoType : 8; // PseudoType
326 mutable unsigned m_parsedNth : 1; // Used for :nth-*
327 unsigned m_isLastInSelectorList : 1;
328 unsigned m_isLastInTagHistory : 1;
329 unsigned m_hasRareData : 1;
330 unsigned m_isForPage : 1;
331 unsigned m_tagIsForNamespaceRule : 1;
332 unsigned m_relationIsAffectedByPseudoContent : 1;
333
334 unsigned specificityForOneSelector() const;
335 unsigned specificityForPage() const;
336 void extractPseudoType() const;
337
338 // Hide.
339 CSSSelector& operator=(const CSSSelector&);
340
341 struct RareData : public RefCounted<RareData> {
createRareData342 static PassRefPtr<RareData> create(const AtomicString& value) { return adoptRef(new RareData(value)); }
343 ~RareData();
344
345 bool parseNth();
346 bool matchNth(int count);
347
348 AtomicString m_value;
349 int m_a; // Used for :nth-*
350 int m_b; // Used for :nth-*
351 QualifiedName m_attribute; // used for attribute selector
352 AtomicString m_argument; // Used for :contains, :lang, :nth-*
353 OwnPtr<CSSSelectorList> m_selectorList; // Used for :-webkit-any and :not
354
355 private:
356 RareData(const AtomicString& value);
357 };
358 void createRareData();
359
360 union DataUnion {
DataUnion()361 DataUnion() : m_value(0) { }
362 StringImpl* m_value;
363 QualifiedName::QualifiedNameImpl* m_tagQName;
364 RareData* m_rareData;
365 } m_data;
366 };
367
attribute()368 inline const QualifiedName& CSSSelector::attribute() const
369 {
370 ASSERT(isAttributeSelector());
371 ASSERT(m_hasRareData);
372 return m_data.m_rareData->m_attribute;
373 }
374
matchesPseudoElement()375 inline bool CSSSelector::matchesPseudoElement() const
376 {
377 if (m_pseudoType == PseudoUnknown)
378 extractPseudoType();
379 return m_match == PseudoElement;
380 }
381
isUnknownPseudoElement()382 inline bool CSSSelector::isUnknownPseudoElement() const
383 {
384 return m_match == PseudoElement && m_pseudoType == PseudoUnknown;
385 }
386
isCustomPseudoElement()387 inline bool CSSSelector::isCustomPseudoElement() const
388 {
389 return m_match == PseudoElement && (m_pseudoType == PseudoUserAgentCustomElement || m_pseudoType == PseudoWebKitCustomElement);
390 }
391
isHostPseudoClass()392 inline bool CSSSelector::isHostPseudoClass() const
393 {
394 return m_match == PseudoClass && (m_pseudoType == PseudoHost || m_pseudoType == PseudoHostContext);
395 }
396
isSiblingSelector()397 inline bool CSSSelector::isSiblingSelector() const
398 {
399 PseudoType type = pseudoType();
400 return m_relation == DirectAdjacent
401 || m_relation == IndirectAdjacent
402 || type == PseudoEmpty
403 || type == PseudoFirstChild
404 || type == PseudoFirstOfType
405 || type == PseudoLastChild
406 || type == PseudoLastOfType
407 || type == PseudoOnlyChild
408 || type == PseudoOnlyOfType
409 || type == PseudoNthChild
410 || type == PseudoNthOfType
411 || type == PseudoNthLastChild
412 || type == PseudoNthLastOfType;
413 }
414
isAttributeSelector()415 inline bool CSSSelector::isAttributeSelector() const
416 {
417 return m_match >= FirstAttributeSelectorMatch;
418 }
419
isContentPseudoElement()420 inline bool CSSSelector::isContentPseudoElement() const
421 {
422 return m_match == PseudoElement && pseudoType() == PseudoContent;
423 }
424
isShadowPseudoElement()425 inline bool CSSSelector::isShadowPseudoElement() const
426 {
427 return m_match == PseudoElement && pseudoType() == PseudoShadow;
428 }
429
setValue(const AtomicString & value)430 inline void CSSSelector::setValue(const AtomicString& value)
431 {
432 ASSERT(m_match != Tag);
433 ASSERT(m_pseudoType == PseudoNotParsed);
434 // Need to do ref counting manually for the union.
435 if (m_hasRareData) {
436 m_data.m_rareData->m_value = value;
437 return;
438 }
439 if (m_data.m_value)
440 m_data.m_value->deref();
441 m_data.m_value = value.impl();
442 m_data.m_value->ref();
443 }
444
CSSSelector()445 inline CSSSelector::CSSSelector()
446 : m_relation(Descendant)
447 , m_match(Unknown)
448 , m_pseudoType(PseudoNotParsed)
449 , m_parsedNth(false)
450 , m_isLastInSelectorList(false)
451 , m_isLastInTagHistory(true)
452 , m_hasRareData(false)
453 , m_isForPage(false)
454 , m_tagIsForNamespaceRule(false)
455 , m_relationIsAffectedByPseudoContent(false)
456 {
457 }
458
CSSSelector(const QualifiedName & tagQName,bool tagIsForNamespaceRule)459 inline CSSSelector::CSSSelector(const QualifiedName& tagQName, bool tagIsForNamespaceRule)
460 : m_relation(Descendant)
461 , m_match(Tag)
462 , m_pseudoType(PseudoNotParsed)
463 , m_parsedNth(false)
464 , m_isLastInSelectorList(false)
465 , m_isLastInTagHistory(true)
466 , m_hasRareData(false)
467 , m_isForPage(false)
468 , m_tagIsForNamespaceRule(tagIsForNamespaceRule)
469 , m_relationIsAffectedByPseudoContent(false)
470 {
471 m_data.m_tagQName = tagQName.impl();
472 m_data.m_tagQName->ref();
473 }
474
CSSSelector(const CSSSelector & o)475 inline CSSSelector::CSSSelector(const CSSSelector& o)
476 : m_relation(o.m_relation)
477 , m_match(o.m_match)
478 , m_pseudoType(o.m_pseudoType)
479 , m_parsedNth(o.m_parsedNth)
480 , m_isLastInSelectorList(o.m_isLastInSelectorList)
481 , m_isLastInTagHistory(o.m_isLastInTagHistory)
482 , m_hasRareData(o.m_hasRareData)
483 , m_isForPage(o.m_isForPage)
484 , m_tagIsForNamespaceRule(o.m_tagIsForNamespaceRule)
485 , m_relationIsAffectedByPseudoContent(o.m_relationIsAffectedByPseudoContent)
486 {
487 if (o.m_match == Tag) {
488 m_data.m_tagQName = o.m_data.m_tagQName;
489 m_data.m_tagQName->ref();
490 } else if (o.m_hasRareData) {
491 m_data.m_rareData = o.m_data.m_rareData;
492 m_data.m_rareData->ref();
493 } else if (o.m_data.m_value) {
494 m_data.m_value = o.m_data.m_value;
495 m_data.m_value->ref();
496 }
497 }
498
~CSSSelector()499 inline CSSSelector::~CSSSelector()
500 {
501 if (m_match == Tag)
502 m_data.m_tagQName->deref();
503 else if (m_hasRareData)
504 m_data.m_rareData->deref();
505 else if (m_data.m_value)
506 m_data.m_value->deref();
507 }
508
tagQName()509 inline const QualifiedName& CSSSelector::tagQName() const
510 {
511 ASSERT(m_match == Tag);
512 return *reinterpret_cast<const QualifiedName*>(&m_data.m_tagQName);
513 }
514
value()515 inline const AtomicString& CSSSelector::value() const
516 {
517 ASSERT(m_match != Tag);
518 if (m_hasRareData)
519 return m_data.m_rareData->m_value;
520 // AtomicString is really just a StringImpl* so the cast below is safe.
521 // FIXME: Perhaps call sites could be changed to accept StringImpl?
522 return *reinterpret_cast<const AtomicString*>(&m_data.m_value);
523 }
524
525 } // namespace WebCore
526
527 #endif // CSSSelector_h
528