• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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