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.
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.
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.
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.
25 #include "core/dom/QualifiedName.h"
26 #include "core/rendering/style/RenderStyleConstants.h"
27 #include "wtf/OwnPtr.h"
28 #include "wtf/PassOwnPtr.h"
31 class CSSSelectorList;
33 // This class represents a selector for a StyleRule.
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.
38 // ** tagHistory() and relation():
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:
45 // selectorText(): .a.b .c
46 // --> (relation == Descendant)
47 // selectorText(): .a.b
48 // --> (relation == SubSelector)
51 // Note that currently a bare selector such as ".a" has a relation() of Descendant. This is a bug - instead the relation should be
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
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.
69 // ** value(), attribute():
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.
74 // ** Attribute selectors.
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"].
79 // ** isCustomPseudoElement():
81 // It appears this is used only for pseudo elements that appear in user-agent shadow DOM. They are not exposed to author-created
85 WTF_MAKE_FAST_ALLOCATED;
88 CSSSelector(const CSSSelector&);
89 explicit CSSSelector(const QualifiedName&, bool tagIsForNamespaceRule = false);
94 * Re-create selector text from selector's data
96 String selectorText(const String& = "") const;
98 // checks if the 2 selectors (including sub selectors) agree.
99 bool operator==(const CSSSelector&) const;
101 // tag == -1 means apply to all elements (Selector = *)
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;
107 /* how the attribute value has to match.... Default is Exact */
112 Class, // example: .class
113 Exact, // Example: E[foo="bar"]
114 Set, // Example: E[foo]
115 List, // Example: E[foo~="bar"]
116 Hyphen, // Example: E[foo|="bar"]
117 PseudoClass, // Example: :nth-child(2)
118 PseudoElement, // Example: ::first-line
119 Contain, // css3: E[foo*="bar"]
120 Begin, // css3: E[foo^="bar"]
121 End, // css3: E[foo$="bar"]
122 PagePseudoClass // ??
126 Descendant = 0, // "Space" combinator
127 Child, // > combinator
128 DirectAdjacent, // + combinator
129 IndirectAdjacent, // ~ combinator
130 SubSelector, // "No space" combinator
131 ShadowPseudo, // Special case of shadow DOM pseudo elements / shadow pseudo element
132 ShadowDeep // /shadow-deep/ combinator
183 PseudoScrollbarButton,
184 PseudoScrollbarCorner,
185 PseudoScrollbarForward,
186 PseudoScrollbarThumb,
187 PseudoScrollbarTrack,
188 PseudoScrollbarTrackPiece,
189 PseudoWindowInactive,
205 PseudoFullScreenDocument,
206 PseudoFullScreenAncestor,
209 PseudoUserAgentCustomElement,
210 PseudoWebKitCustomElement,
222 TopLeftCornerMarginBox,
226 TopRightCornerMarginBox,
227 BottomLeftCornerMarginBox,
229 BottomCenterMarginBox,
230 BottomRightMarginBox,
231 BottomRightCornerMarginBox,
236 RightMiddleMarginBox,
237 RightBottomMarginBox,
240 PseudoType pseudoType() const
242 if (m_pseudoType == PseudoNotParsed)
244 return static_cast<PseudoType>(m_pseudoType);
247 static PseudoType parsePseudoType(const AtomicString&);
248 static PseudoId pseudoId(PseudoType);
250 // Selectors are kept in an array by CSSSelectorList. The next component of the selector is
251 // the next item in the array.
252 const CSSSelector* tagHistory() const { return m_isLastInTagHistory ? 0 : const_cast<CSSSelector*>(this + 1); }
254 const QualifiedName& tagQName() const;
255 const AtomicString& value() const;
257 // WARNING: Use of QualifiedName by attribute() is a lie.
258 // attribute() will return a QualifiedName with prefix and namespaceURI
259 // set to starAtom to mean "matches any namespace". Be very careful
260 // how you use the returned QualifiedName.
261 // http://www.w3.org/TR/css3-selectors/#attrnmsp
262 const QualifiedName& attribute() const;
263 // Returns the argument of a parameterized selector. For example, nth-child(2) would have an argument of 2.
264 const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; }
265 const CSSSelectorList* selectorList() const { return m_hasRareData ? m_data.m_rareData->m_selectorList.get() : 0; }
269 void show(int indent) const;
272 void setValue(const AtomicString&);
273 void setAttribute(const QualifiedName&);
274 void setArgument(const AtomicString&);
275 void setSelectorList(PassOwnPtr<CSSSelectorList>);
276 void setMatchUserAgentOnly();
278 bool parseNth() const;
279 bool matchNth(int count) const;
281 bool matchesPseudoElement() const;
282 bool isUnknownPseudoElement() const;
283 bool isCustomPseudoElement() const;
284 bool isDirectAdjacentSelector() const { return m_relation == DirectAdjacent; }
285 bool isSiblingSelector() const;
286 bool isAttributeSelector() const;
287 bool isContentPseudoElement() const;
288 bool isShadowPseudoElement() const;
289 bool isHostPseudoClass() const;
291 // FIXME: selectors with no tagHistory() get a relation() of Descendant (and sometimes even SubSelector). It should instead be
293 Relation relation() const { return static_cast<Relation>(m_relation); }
295 bool isLastInSelectorList() const { return m_isLastInSelectorList; }
296 void setLastInSelectorList() { m_isLastInSelectorList = true; }
297 bool isLastInTagHistory() const { return m_isLastInTagHistory; }
298 void setNotLastInTagHistory() { m_isLastInTagHistory = false; }
300 // http://dev.w3.org/csswg/selectors4/#compound
301 bool isCompound() const;
303 bool isForPage() const { return m_isForPage; }
304 void setForPage() { m_isForPage = true; }
306 bool relationIsAffectedByPseudoContent() const { return m_relationIsAffectedByPseudoContent; }
307 void setRelationIsAffectedByPseudoContent() { m_relationIsAffectedByPseudoContent = true; }
309 unsigned m_relation : 3; // enum Relation
310 mutable unsigned m_match : 4; // enum Match
311 mutable unsigned m_pseudoType : 8; // PseudoType
314 mutable unsigned m_parsedNth : 1; // Used for :nth-*
315 unsigned m_isLastInSelectorList : 1;
316 unsigned m_isLastInTagHistory : 1;
317 unsigned m_hasRareData : 1;
318 unsigned m_isForPage : 1;
319 unsigned m_tagIsForNamespaceRule : 1;
320 unsigned m_relationIsAffectedByPseudoContent : 1;
322 unsigned specificityForOneSelector() const;
323 unsigned specificityForPage() const;
324 void extractPseudoType() const;
327 CSSSelector& operator=(const CSSSelector&);
329 struct RareData : public RefCounted<RareData> {
330 static PassRefPtr<RareData> create(const AtomicString& value) { return adoptRef(new RareData(value)); }
334 bool matchNth(int count);
336 AtomicString m_value;
337 int m_a; // Used for :nth-*
338 int m_b; // Used for :nth-*
339 QualifiedName m_attribute; // used for attribute selector
340 AtomicString m_argument; // Used for :contains, :lang, :nth-*
341 OwnPtr<CSSSelectorList> m_selectorList; // Used for :-webkit-any and :not
344 RareData(const AtomicString& value);
346 void createRareData();
349 DataUnion() : m_value(0) { }
351 QualifiedName::QualifiedNameImpl* m_tagQName;
352 RareData* m_rareData;
356 inline const QualifiedName& CSSSelector::attribute() const
358 ASSERT(isAttributeSelector());
359 ASSERT(m_hasRareData);
360 return m_data.m_rareData->m_attribute;
363 inline bool CSSSelector::matchesPseudoElement() const
365 if (m_pseudoType == PseudoUnknown)
367 return m_match == PseudoElement;
370 inline bool CSSSelector::isUnknownPseudoElement() const
372 return m_match == PseudoElement && m_pseudoType == PseudoUnknown;
375 inline bool CSSSelector::isCustomPseudoElement() const
377 return m_match == PseudoElement && (m_pseudoType == PseudoUserAgentCustomElement || m_pseudoType == PseudoWebKitCustomElement);
380 inline bool CSSSelector::isHostPseudoClass() const
382 return m_match == PseudoClass && (m_pseudoType == PseudoHost || m_pseudoType == PseudoHostContext);
385 inline bool CSSSelector::isSiblingSelector() const
387 PseudoType type = pseudoType();
388 return m_relation == DirectAdjacent
389 || m_relation == IndirectAdjacent
390 || type == PseudoEmpty
391 || type == PseudoFirstChild
392 || type == PseudoFirstOfType
393 || type == PseudoLastChild
394 || type == PseudoLastOfType
395 || type == PseudoOnlyChild
396 || type == PseudoOnlyOfType
397 || type == PseudoNthChild
398 || type == PseudoNthOfType
399 || type == PseudoNthLastChild
400 || type == PseudoNthLastOfType;
403 inline bool CSSSelector::isAttributeSelector() const
405 return m_match == CSSSelector::Exact
406 || m_match == CSSSelector::Set
407 || m_match == CSSSelector::List
408 || m_match == CSSSelector::Hyphen
409 || m_match == CSSSelector::Contain
410 || m_match == CSSSelector::Begin
411 || m_match == CSSSelector::End;
414 inline bool CSSSelector::isContentPseudoElement() const
416 return m_match == PseudoElement && pseudoType() == PseudoContent;
419 inline bool CSSSelector::isShadowPseudoElement() const
421 return m_match == PseudoElement && pseudoType() == PseudoShadow;
424 inline void CSSSelector::setValue(const AtomicString& value)
426 ASSERT(m_match != Tag);
427 ASSERT(m_pseudoType == PseudoNotParsed);
428 // Need to do ref counting manually for the union.
430 m_data.m_rareData->m_value = value;
434 m_data.m_value->deref();
435 m_data.m_value = value.impl();
436 m_data.m_value->ref();
439 inline CSSSelector::CSSSelector()
440 : m_relation(Descendant)
442 , m_pseudoType(PseudoNotParsed)
444 , m_isLastInSelectorList(false)
445 , m_isLastInTagHistory(true)
446 , m_hasRareData(false)
448 , m_tagIsForNamespaceRule(false)
449 , m_relationIsAffectedByPseudoContent(false)
453 inline CSSSelector::CSSSelector(const QualifiedName& tagQName, bool tagIsForNamespaceRule)
454 : m_relation(Descendant)
456 , m_pseudoType(PseudoNotParsed)
458 , m_isLastInSelectorList(false)
459 , m_isLastInTagHistory(true)
460 , m_hasRareData(false)
462 , m_tagIsForNamespaceRule(tagIsForNamespaceRule)
463 , m_relationIsAffectedByPseudoContent(false)
465 m_data.m_tagQName = tagQName.impl();
466 m_data.m_tagQName->ref();
469 inline CSSSelector::CSSSelector(const CSSSelector& o)
470 : m_relation(o.m_relation)
472 , m_pseudoType(o.m_pseudoType)
473 , m_parsedNth(o.m_parsedNth)
474 , m_isLastInSelectorList(o.m_isLastInSelectorList)
475 , m_isLastInTagHistory(o.m_isLastInTagHistory)
476 , m_hasRareData(o.m_hasRareData)
477 , m_isForPage(o.m_isForPage)
478 , m_tagIsForNamespaceRule(o.m_tagIsForNamespaceRule)
479 , m_relationIsAffectedByPseudoContent(o.m_relationIsAffectedByPseudoContent)
481 if (o.m_match == Tag) {
482 m_data.m_tagQName = o.m_data.m_tagQName;
483 m_data.m_tagQName->ref();
484 } else if (o.m_hasRareData) {
485 m_data.m_rareData = o.m_data.m_rareData;
486 m_data.m_rareData->ref();
487 } else if (o.m_data.m_value) {
488 m_data.m_value = o.m_data.m_value;
489 m_data.m_value->ref();
493 inline CSSSelector::~CSSSelector()
496 m_data.m_tagQName->deref();
497 else if (m_hasRareData)
498 m_data.m_rareData->deref();
499 else if (m_data.m_value)
500 m_data.m_value->deref();
503 inline const QualifiedName& CSSSelector::tagQName() const
505 ASSERT(m_match == Tag);
506 return *reinterpret_cast<const QualifiedName*>(&m_data.m_tagQName);
509 inline const AtomicString& CSSSelector::value() const
511 ASSERT(m_match != Tag);
513 return m_data.m_rareData->m_value;
514 // AtomicString is really just a StringImpl* so the cast below is safe.
515 // FIXME: Perhaps call sites could be changed to accept StringImpl?
516 return *reinterpret_cast<const AtomicString*>(&m_data.m_value);
519 } // namespace WebCore
521 #endif // CSSSelector_h