• 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     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