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 blink {
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 PseudoSpatialNavigationFocus,
221 PseudoListBox
222 };
223
224 enum MarginBoxType {
225 TopLeftCornerMarginBox,
226 TopLeftMarginBox,
227 TopCenterMarginBox,
228 TopRightMarginBox,
229 TopRightCornerMarginBox,
230 BottomLeftCornerMarginBox,
231 BottomLeftMarginBox,
232 BottomCenterMarginBox,
233 BottomRightMarginBox,
234 BottomRightCornerMarginBox,
235 LeftTopMarginBox,
236 LeftMiddleMarginBox,
237 LeftBottomMarginBox,
238 RightTopMarginBox,
239 RightMiddleMarginBox,
240 RightBottomMarginBox,
241 };
242
243 enum AttributeMatchType {
244 CaseSensitive,
245 CaseInsensitive,
246 };
247
pseudoType()248 PseudoType pseudoType() const
249 {
250 if (m_pseudoType == PseudoNotParsed)
251 extractPseudoType();
252 return static_cast<PseudoType>(m_pseudoType);
253 }
254
255 static PseudoType parsePseudoType(const AtomicString&, bool hasArguments);
256 static PseudoId pseudoId(PseudoType);
257
258 // Selectors are kept in an array by CSSSelectorList. The next component of the selector is
259 // the next item in the array.
tagHistory()260 const CSSSelector* tagHistory() const { return m_isLastInTagHistory ? 0 : const_cast<CSSSelector*>(this + 1); }
261
262 const QualifiedName& tagQName() const;
263 const AtomicString& value() const;
264
265 // WARNING: Use of QualifiedName by attribute() is a lie.
266 // attribute() will return a QualifiedName with prefix and namespaceURI
267 // set to starAtom to mean "matches any namespace". Be very careful
268 // how you use the returned QualifiedName.
269 // http://www.w3.org/TR/css3-selectors/#attrnmsp
270 const QualifiedName& attribute() const;
271 AttributeMatchType attributeMatchType() const;
272 // Returns the argument of a parameterized selector. For example, nth-child(2) would have an argument of 2.
argument()273 const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; }
selectorList()274 const CSSSelectorList* selectorList() const { return m_hasRareData ? m_data.m_rareData->m_selectorList.get() : 0; }
275
276 #ifndef NDEBUG
277 void show() const;
278 void show(int indent) const;
279 #endif
280
281 void setValue(const AtomicString&);
282 void setAttribute(const QualifiedName&, AttributeMatchType);
283 void setArgument(const AtomicString&);
284 void setSelectorList(PassOwnPtr<CSSSelectorList>);
285 void setMatchUserAgentOnly();
286
287 bool parseNth() const;
288 bool matchNth(int count) const;
289
290 bool matchesPseudoElement() const;
291 bool isCustomPseudoElement() const;
isDirectAdjacentSelector()292 bool isDirectAdjacentSelector() const { return m_relation == DirectAdjacent; }
293 bool isSiblingSelector() const;
294 bool isAttributeSelector() const;
295 bool isContentPseudoElement() const;
296 bool isShadowPseudoElement() const;
297 bool isHostPseudoClass() const;
298
299 // FIXME: selectors with no tagHistory() get a relation() of Descendant (and sometimes even SubSelector). It should instead be
300 // None.
relation()301 Relation relation() const { return static_cast<Relation>(m_relation); }
setRelation(Relation relation)302 void setRelation(Relation relation)
303 {
304 m_relation = relation;
305 ASSERT(static_cast<Relation>(m_relation) == relation); // using a bitfield.
306 }
307
match()308 Match match() const { return static_cast<Match>(m_match); }
setMatch(Match match)309 void setMatch(Match match)
310 {
311 m_match = match;
312 ASSERT(static_cast<Match>(m_match) == match); // using a bitfield.
313 }
314
isLastInSelectorList()315 bool isLastInSelectorList() const { return m_isLastInSelectorList; }
setLastInSelectorList()316 void setLastInSelectorList() { m_isLastInSelectorList = true; }
isLastInTagHistory()317 bool isLastInTagHistory() const { return m_isLastInTagHistory; }
setNotLastInTagHistory()318 void setNotLastInTagHistory() { m_isLastInTagHistory = false; }
319
320 // http://dev.w3.org/csswg/selectors4/#compound
321 bool isCompound() const;
322
isForPage()323 bool isForPage() const { return m_isForPage; }
setForPage()324 void setForPage() { m_isForPage = true; }
325
relationIsAffectedByPseudoContent()326 bool relationIsAffectedByPseudoContent() const { return m_relationIsAffectedByPseudoContent; }
setRelationIsAffectedByPseudoContent()327 void setRelationIsAffectedByPseudoContent() { m_relationIsAffectedByPseudoContent = true; }
328
329 private:
330 unsigned m_relation : 3; // enum Relation
331 mutable unsigned m_match : 4; // enum Match
332 mutable unsigned m_pseudoType : 8; // PseudoType
333 mutable unsigned m_parsedNth : 1; // Used for :nth-*
334 unsigned m_isLastInSelectorList : 1;
335 unsigned m_isLastInTagHistory : 1;
336 unsigned m_hasRareData : 1;
337 unsigned m_isForPage : 1;
338 unsigned m_tagIsForNamespaceRule : 1;
339 unsigned m_relationIsAffectedByPseudoContent : 1;
340
341 unsigned specificityForOneSelector() const;
342 unsigned specificityForPage() const;
343 void extractPseudoType() const;
344
345 // Hide.
346 CSSSelector& operator=(const CSSSelector&);
347
348 struct RareData : public RefCounted<RareData> {
createRareData349 static PassRefPtr<RareData> create(const AtomicString& value) { return adoptRef(new RareData(value)); }
350 ~RareData();
351
352 bool parseNth();
353 bool matchNth(int count);
nthAValueRareData354 int nthAValue() const { return m_bits.m_nth.m_a; }
setNthAValueRareData355 void setNthAValue(int nthA) { m_bits.m_nth.m_a = nthA; }
nthBValueRareData356 int nthBValue() const { return m_bits.m_nth.m_b; }
setNthBValueRareData357 void setNthBValue(int nthB) { m_bits.m_nth.m_b = nthB; }
358
359 AtomicString m_value;
360 union {
361 struct {
362 int m_a; // Used for :nth-*
363 int m_b; // Used for :nth-*
364 } m_nth;
365 AttributeMatchType m_attributeMatchType; // used for attribute selector (with value)
366 } m_bits;
367 QualifiedName m_attribute; // used for attribute selector
368 AtomicString m_argument; // Used for :contains, :lang, :nth-*
369 OwnPtr<CSSSelectorList> m_selectorList; // Used for :-webkit-any and :not
370
371 private:
372 RareData(const AtomicString& value);
373 };
374 void createRareData();
375
376 union DataUnion {
DataUnion()377 DataUnion() : m_value(0) { }
378 StringImpl* m_value;
379 QualifiedName::QualifiedNameImpl* m_tagQName;
380 RareData* m_rareData;
381 } m_data;
382 };
383
attribute()384 inline const QualifiedName& CSSSelector::attribute() const
385 {
386 ASSERT(isAttributeSelector());
387 ASSERT(m_hasRareData);
388 return m_data.m_rareData->m_attribute;
389 }
390
attributeMatchType()391 inline CSSSelector::AttributeMatchType CSSSelector::attributeMatchType() const
392 {
393 ASSERT(isAttributeSelector());
394 ASSERT(m_hasRareData);
395 return m_data.m_rareData->m_bits.m_attributeMatchType;
396 }
397
matchesPseudoElement()398 inline bool CSSSelector::matchesPseudoElement() const
399 {
400 if (m_pseudoType == PseudoUnknown)
401 extractPseudoType();
402 return m_match == PseudoElement;
403 }
404
isCustomPseudoElement()405 inline bool CSSSelector::isCustomPseudoElement() const
406 {
407 return m_match == PseudoElement && (m_pseudoType == PseudoUserAgentCustomElement || m_pseudoType == PseudoWebKitCustomElement);
408 }
409
isHostPseudoClass()410 inline bool CSSSelector::isHostPseudoClass() const
411 {
412 return m_match == PseudoClass && (m_pseudoType == PseudoHost || m_pseudoType == PseudoHostContext);
413 }
414
isSiblingSelector()415 inline bool CSSSelector::isSiblingSelector() const
416 {
417 PseudoType type = pseudoType();
418 return m_relation == DirectAdjacent
419 || m_relation == IndirectAdjacent
420 || type == PseudoEmpty
421 || type == PseudoFirstChild
422 || type == PseudoFirstOfType
423 || type == PseudoLastChild
424 || type == PseudoLastOfType
425 || type == PseudoOnlyChild
426 || type == PseudoOnlyOfType
427 || type == PseudoNthChild
428 || type == PseudoNthOfType
429 || type == PseudoNthLastChild
430 || type == PseudoNthLastOfType;
431 }
432
isAttributeSelector()433 inline bool CSSSelector::isAttributeSelector() const
434 {
435 return m_match >= FirstAttributeSelectorMatch;
436 }
437
isContentPseudoElement()438 inline bool CSSSelector::isContentPseudoElement() const
439 {
440 return m_match == PseudoElement && pseudoType() == PseudoContent;
441 }
442
isShadowPseudoElement()443 inline bool CSSSelector::isShadowPseudoElement() const
444 {
445 return m_match == PseudoElement && pseudoType() == PseudoShadow;
446 }
447
setValue(const AtomicString & value)448 inline void CSSSelector::setValue(const AtomicString& value)
449 {
450 ASSERT(m_match != Tag);
451 ASSERT(m_pseudoType == PseudoNotParsed);
452 // Need to do ref counting manually for the union.
453 if (m_hasRareData) {
454 m_data.m_rareData->m_value = value;
455 return;
456 }
457 if (m_data.m_value)
458 m_data.m_value->deref();
459 m_data.m_value = value.impl();
460 m_data.m_value->ref();
461 }
462
CSSSelector()463 inline CSSSelector::CSSSelector()
464 : m_relation(Descendant)
465 , m_match(Unknown)
466 , m_pseudoType(PseudoNotParsed)
467 , m_parsedNth(false)
468 , m_isLastInSelectorList(false)
469 , m_isLastInTagHistory(true)
470 , m_hasRareData(false)
471 , m_isForPage(false)
472 , m_tagIsForNamespaceRule(false)
473 , m_relationIsAffectedByPseudoContent(false)
474 {
475 }
476
CSSSelector(const QualifiedName & tagQName,bool tagIsForNamespaceRule)477 inline CSSSelector::CSSSelector(const QualifiedName& tagQName, bool tagIsForNamespaceRule)
478 : m_relation(Descendant)
479 , m_match(Tag)
480 , m_pseudoType(PseudoNotParsed)
481 , m_parsedNth(false)
482 , m_isLastInSelectorList(false)
483 , m_isLastInTagHistory(true)
484 , m_hasRareData(false)
485 , m_isForPage(false)
486 , m_tagIsForNamespaceRule(tagIsForNamespaceRule)
487 , m_relationIsAffectedByPseudoContent(false)
488 {
489 m_data.m_tagQName = tagQName.impl();
490 m_data.m_tagQName->ref();
491 }
492
CSSSelector(const CSSSelector & o)493 inline CSSSelector::CSSSelector(const CSSSelector& o)
494 : m_relation(o.m_relation)
495 , m_match(o.m_match)
496 , m_pseudoType(o.m_pseudoType)
497 , m_parsedNth(o.m_parsedNth)
498 , m_isLastInSelectorList(o.m_isLastInSelectorList)
499 , m_isLastInTagHistory(o.m_isLastInTagHistory)
500 , m_hasRareData(o.m_hasRareData)
501 , m_isForPage(o.m_isForPage)
502 , m_tagIsForNamespaceRule(o.m_tagIsForNamespaceRule)
503 , m_relationIsAffectedByPseudoContent(o.m_relationIsAffectedByPseudoContent)
504 {
505 if (o.m_match == Tag) {
506 m_data.m_tagQName = o.m_data.m_tagQName;
507 m_data.m_tagQName->ref();
508 } else if (o.m_hasRareData) {
509 m_data.m_rareData = o.m_data.m_rareData;
510 m_data.m_rareData->ref();
511 } else if (o.m_data.m_value) {
512 m_data.m_value = o.m_data.m_value;
513 m_data.m_value->ref();
514 }
515 }
516
~CSSSelector()517 inline CSSSelector::~CSSSelector()
518 {
519 if (m_match == Tag)
520 m_data.m_tagQName->deref();
521 else if (m_hasRareData)
522 m_data.m_rareData->deref();
523 else if (m_data.m_value)
524 m_data.m_value->deref();
525 }
526
tagQName()527 inline const QualifiedName& CSSSelector::tagQName() const
528 {
529 ASSERT(m_match == Tag);
530 return *reinterpret_cast<const QualifiedName*>(&m_data.m_tagQName);
531 }
532
value()533 inline const AtomicString& CSSSelector::value() const
534 {
535 ASSERT(m_match != Tag);
536 if (m_hasRareData)
537 return m_data.m_rareData->m_value;
538 // AtomicString is really just a StringImpl* so the cast below is safe.
539 // FIXME: Perhaps call sites could be changed to accept StringImpl?
540 return *reinterpret_cast<const AtomicString*>(&m_data.m_value);
541 }
542
543 } // namespace blink
544
545 #endif // CSSSelector_h
546