85227add23a6c7a9b49b8cf663a84a100bdff444
[WebKit-https.git] / Source / WebCore / css / CSSSelector.h
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, 2014 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 "QualifiedName.h"
26 #include "RenderStyleConstants.h"
27 #include <wtf/Noncopyable.h>
28
29 namespace WebCore {
30     class CSSSelectorList;
31
32     enum class SelectorSpecificityIncrement {
33         ClassA = 0x10000,
34         ClassB = 0x100,
35         ClassC = 1
36     };
37
38     // this class represents a selector for a StyleRule
39     class CSSSelector {
40         WTF_MAKE_FAST_ALLOCATED;
41     public:
42         CSSSelector();
43         CSSSelector(const CSSSelector&);
44         explicit CSSSelector(const QualifiedName&, bool tagIsForNamespaceRule = false);
45
46         ~CSSSelector();
47
48         /**
49          * Re-create selector text from selector's data
50          */
51         String selectorText(const String& = "") const;
52
53         // checks if the 2 selectors (including sub selectors) agree.
54         bool operator==(const CSSSelector&) const;
55
56         static const unsigned maxValueMask = 0xffffff;
57         static const unsigned idMask = 0xff0000;
58         static const unsigned classMask = 0xff00;
59         static const unsigned elementMask = 0xff;
60
61         unsigned staticSpecificity(bool& ok) const;
62         unsigned specificityForPage() const;
63         unsigned simpleSelectorSpecificity() const;
64         static unsigned addSpecificities(unsigned, unsigned);
65
66         /* how the attribute value has to match.... Default is Exact */
67         enum Match {
68             Unknown = 0,
69             Tag,
70             Id,
71             Class,
72             Exact,
73             Set,
74             List,
75             Hyphen,
76             PseudoClass,
77             PseudoElement,
78             Contain, // css3: E[foo*="bar"]
79             Begin, // css3: E[foo^="bar"]
80             End, // css3: E[foo$="bar"]
81             PagePseudoClass
82         };
83
84         enum Relation {
85             Descendant = 0,
86             Child,
87             DirectAdjacent,
88             IndirectAdjacent,
89             SubSelector,
90             ShadowDescendant,
91         };
92
93         enum PseudoClassType {
94             PseudoClassUnknown = 0,
95             PseudoClassEmpty,
96             PseudoClassFirstChild,
97             PseudoClassFirstOfType,
98             PseudoClassLastChild,
99             PseudoClassLastOfType,
100             PseudoClassOnlyChild,
101             PseudoClassOnlyOfType,
102             PseudoClassNthChild,
103             PseudoClassNthOfType,
104             PseudoClassNthLastChild,
105             PseudoClassNthLastOfType,
106             PseudoClassLink,
107             PseudoClassVisited,
108             PseudoClassAny,
109 #if ENABLE(CSS_SELECTORS_LEVEL4)
110             PseudoClassAnyLink,
111 #endif
112             PseudoClassAnyLinkDeprecated,
113             PseudoClassAutofill,
114             PseudoClassHover,
115             PseudoClassDrag,
116             PseudoClassFocus,
117             PseudoClassActive,
118             PseudoClassChecked,
119             PseudoClassEnabled,
120             PseudoClassFullPageMedia,
121             PseudoClassDefault,
122             PseudoClassDisabled,
123             PseudoClassOptional,
124 #if ENABLE(CSS_SELECTORS_LEVEL4)
125             PseudoClassPlaceholderShown,
126 #endif
127             PseudoClassMatches,
128             PseudoClassRequired,
129             PseudoClassReadOnly,
130             PseudoClassReadWrite,
131             PseudoClassValid,
132             PseudoClassInvalid,
133             PseudoClassIndeterminate,
134             PseudoClassTarget,
135             PseudoClassLang,
136             PseudoClassNot,
137             PseudoClassRoot,
138             PseudoClassScope,
139             PseudoClassWindowInactive,
140             PseudoClassCornerPresent,
141             PseudoClassDecrement,
142             PseudoClassIncrement,
143             PseudoClassHorizontal,
144             PseudoClassVertical,
145             PseudoClassStart,
146             PseudoClassEnd,
147             PseudoClassDoubleButton,
148             PseudoClassSingleButton,
149             PseudoClassNoButton,
150 #if ENABLE(FULLSCREEN_API)
151             PseudoClassFullScreen,
152             PseudoClassFullScreenDocument,
153             PseudoClassFullScreenAncestor,
154             PseudoClassAnimatingFullScreenTransition,
155 #endif
156             PseudoClassInRange,
157             PseudoClassOutOfRange,
158 #if ENABLE(VIDEO_TRACK)
159             PseudoClassFuture,
160             PseudoClassPast,
161 #endif
162 #if ENABLE(CSS_SELECTORS_LEVEL4)
163             PseudoClassRole,
164 #endif
165         };
166
167         enum PseudoElementType {
168             PseudoElementUnknown = 0,
169             PseudoElementAfter,
170             PseudoElementBefore,
171 #if ENABLE(VIDEO_TRACK)
172             PseudoElementCue,
173 #endif
174             PseudoElementFirstLetter,
175             PseudoElementFirstLine,
176             PseudoElementResizer,
177             PseudoElementScrollbar,
178             PseudoElementScrollbarButton,
179             PseudoElementScrollbarCorner,
180             PseudoElementScrollbarThumb,
181             PseudoElementScrollbarTrack,
182             PseudoElementScrollbarTrackPiece,
183             PseudoElementSelection,
184             PseudoElementUserAgentCustom,
185             PseudoElementWebKitCustom,
186         };
187
188         enum PagePseudoClassType {
189             PagePseudoClassFirst = 1,
190             PagePseudoClassLeft,
191             PagePseudoClassRight,
192         };
193
194         enum MarginBoxType {
195             TopLeftCornerMarginBox,
196             TopLeftMarginBox,
197             TopCenterMarginBox,
198             TopRightMarginBox,
199             TopRightCornerMarginBox,
200             BottomLeftCornerMarginBox,
201             BottomLeftMarginBox,
202             BottomCenterMarginBox,
203             BottomRightMarginBox,
204             BottomRightCornerMarginBox,
205             LeftTopMarginBox,
206             LeftMiddleMarginBox,
207             LeftBottomMarginBox,
208             RightTopMarginBox,
209             RightMiddleMarginBox,
210             RightBottomMarginBox,
211         };
212
213         static PseudoElementType parsePseudoElementType(const String&);
214         static PseudoId pseudoId(PseudoElementType);
215
216         // Selectors are kept in an array by CSSSelectorList. The next component of the selector is
217         // the next item in the array.
218         const CSSSelector* tagHistory() const { return m_isLastInTagHistory ? 0 : const_cast<CSSSelector*>(this + 1); }
219
220         const QualifiedName& tagQName() const;
221         const AtomicString& value() const;
222         const QualifiedName& attribute() const;
223         const AtomicString& attributeCanonicalLocalName() const;
224         const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; }
225 #if ENABLE(CSS_SELECTORS_LEVEL4)
226         const Vector<AtomicString>* argumentList() const { return m_hasRareData ? m_data.m_rareData->m_argumentList.get() : nullptr; }
227 #endif
228         const CSSSelectorList* selectorList() const { return m_hasRareData ? m_data.m_rareData->m_selectorList.get() : nullptr; }
229
230         void setValue(const AtomicString&);
231         void setAttribute(const QualifiedName&, bool isCaseInsensitive);
232         void setArgument(const AtomicString&);
233 #if ENABLE(CSS_SELECTORS_LEVEL4)
234         void setArgumentList(std::unique_ptr<Vector<AtomicString>>);
235 #endif
236         void setSelectorList(std::unique_ptr<CSSSelectorList>);
237
238         bool parseNth() const;
239         bool matchNth(int count) const;
240         int nthA() const;
241         int nthB() const;
242
243         PseudoClassType pseudoClassType() const
244         {
245             ASSERT(match() == PseudoClass);
246             return static_cast<PseudoClassType>(m_pseudoType);
247         }
248         void setPseudoClassType(PseudoClassType pseudoType)
249         {
250             m_pseudoType = pseudoType;
251             ASSERT(m_pseudoType == pseudoType);
252         }
253
254         PseudoElementType pseudoElementType() const
255         {
256             ASSERT(match() == PseudoElement);
257             return static_cast<PseudoElementType>(m_pseudoType);
258         }
259         void setPseudoElementType(PseudoElementType pseudoElementType)
260         {
261             m_pseudoType = pseudoElementType;
262             ASSERT(m_pseudoType == pseudoElementType);
263         }
264
265         PagePseudoClassType pagePseudoClassType() const
266         {
267             ASSERT(match() == PagePseudoClass);
268             return static_cast<PagePseudoClassType>(m_pseudoType);
269         }
270         void setPagePseudoType(PagePseudoClassType pagePseudoType)
271         {
272             m_pseudoType = pagePseudoType;
273             ASSERT(m_pseudoType == pagePseudoType);
274         }
275
276         bool matchesPseudoElement() const;
277         bool isUnknownPseudoElement() const;
278         bool isCustomPseudoElement() const;
279         bool isSiblingSelector() const;
280         bool isAttributeSelector() const;
281
282         Relation relation() const { return static_cast<Relation>(m_relation); }
283         void setRelation(Relation relation)
284         {
285             m_relation = relation;
286             ASSERT(m_relation == relation);
287         }
288
289         Match match() const { return static_cast<Match>(m_match); }
290         void setMatch(Match match)
291         {
292             m_match = match;
293             ASSERT(m_match == match);
294         }
295
296         bool isLastInSelectorList() const { return m_isLastInSelectorList; }
297         void setLastInSelectorList() { m_isLastInSelectorList = true; }
298         bool isLastInTagHistory() const { return m_isLastInTagHistory; }
299         void setNotLastInTagHistory() { m_isLastInTagHistory = false; }
300
301         bool isForPage() const { return m_isForPage; }
302         void setForPage() { m_isForPage = true; }
303
304     private:
305         unsigned m_relation              : 3; // enum Relation.
306         mutable unsigned m_match         : 4; // enum Match.
307         mutable unsigned m_pseudoType    : 8; // PseudoType.
308         mutable unsigned m_parsedNth     : 1; // Used for :nth-*.
309         unsigned m_isLastInSelectorList  : 1;
310         unsigned m_isLastInTagHistory    : 1;
311         unsigned m_hasRareData           : 1;
312         unsigned m_isForPage             : 1;
313         unsigned m_tagIsForNamespaceRule : 1;
314
315         unsigned simpleSelectorSpecificityForPage() const;
316
317         // Hide.
318         CSSSelector& operator=(const CSSSelector&);
319
320         struct RareData : public RefCounted<RareData> {
321             static PassRefPtr<RareData> create(PassRefPtr<AtomicStringImpl> value) { return adoptRef(new RareData(value)); }
322             ~RareData();
323
324             bool parseNth();
325             bool matchNth(int count);
326
327             AtomicStringImpl* m_value; // Plain pointer to keep things uniform with the union.
328             int m_a; // Used for :nth-*
329             int m_b; // Used for :nth-*
330             QualifiedName m_attribute; // used for attribute selector
331             AtomicString m_attributeCanonicalLocalName;
332             AtomicString m_argument; // Used for :contains and :nth-*
333 #if ENABLE(CSS_SELECTORS_LEVEL4)
334             std::unique_ptr<Vector<AtomicString>> m_argumentList;
335 #endif
336             std::unique_ptr<CSSSelectorList> m_selectorList; // Used for :-webkit-any and :not
337         
338         private:
339             RareData(PassRefPtr<AtomicStringImpl> value);
340         };
341         void createRareData();
342
343         union DataUnion {
344             DataUnion() : m_value(0) { }
345             AtomicStringImpl* m_value;
346             QualifiedName::QualifiedNameImpl* m_tagQName;
347             RareData* m_rareData;
348         } m_data;
349     };
350
351 inline const QualifiedName& CSSSelector::attribute() const
352 {
353     ASSERT(isAttributeSelector());
354     ASSERT(m_hasRareData);
355     return m_data.m_rareData->m_attribute;
356 }
357
358 inline const AtomicString& CSSSelector::attributeCanonicalLocalName() const
359 {
360     ASSERT(isAttributeSelector());
361     ASSERT(m_hasRareData);
362     return m_data.m_rareData->m_attributeCanonicalLocalName;
363 }
364
365 inline bool CSSSelector::matchesPseudoElement() const
366 {
367     return match() == PseudoElement;
368 }
369
370 inline bool CSSSelector::isUnknownPseudoElement() const
371 {
372     return match() == PseudoElement && pseudoElementType() == PseudoElementUnknown;
373 }
374
375 inline bool CSSSelector::isCustomPseudoElement() const
376 {
377     return match() == PseudoElement && (pseudoElementType() == PseudoElementUserAgentCustom || pseudoElementType() == PseudoElementWebKitCustom);
378 }
379
380 static inline bool pseudoClassIsRelativeToSiblings(CSSSelector::PseudoClassType type)
381 {
382     return type == CSSSelector::PseudoClassEmpty
383         || type == CSSSelector::PseudoClassFirstChild
384         || type == CSSSelector::PseudoClassFirstOfType
385         || type == CSSSelector::PseudoClassLastChild
386         || type == CSSSelector::PseudoClassLastOfType
387         || type == CSSSelector::PseudoClassOnlyChild
388         || type == CSSSelector::PseudoClassOnlyOfType
389         || type == CSSSelector::PseudoClassNthChild
390         || type == CSSSelector::PseudoClassNthOfType
391         || type == CSSSelector::PseudoClassNthLastChild
392         || type == CSSSelector::PseudoClassNthLastOfType;
393 }
394
395 inline bool CSSSelector::isSiblingSelector() const
396 {
397     return relation() == DirectAdjacent
398         || relation() == IndirectAdjacent
399         || (match() == CSSSelector::PseudoClass && pseudoClassIsRelativeToSiblings(pseudoClassType()));
400 }
401
402 inline bool CSSSelector::isAttributeSelector() const
403 {
404     return match() == CSSSelector::Exact
405         || match() ==  CSSSelector::Set
406         || match() == CSSSelector::List
407         || match() == CSSSelector::Hyphen
408         || match() == CSSSelector::Contain
409         || match() == CSSSelector::Begin
410         || match() == CSSSelector::End;
411 }
412
413 inline void CSSSelector::setValue(const AtomicString& value)
414 {
415     ASSERT(match() != Tag);
416     // Need to do ref counting manually for the union.
417     if (m_hasRareData) {
418         if (m_data.m_rareData->m_value)
419             m_data.m_rareData->m_value->deref();
420         m_data.m_rareData->m_value = value.impl();
421         m_data.m_rareData->m_value->ref();
422         return;
423     }
424     if (m_data.m_value)
425         m_data.m_value->deref();
426     m_data.m_value = value.impl();
427     m_data.m_value->ref();
428 }
429
430 inline CSSSelector::CSSSelector()
431     : m_relation(Descendant)
432     , m_match(Unknown)
433     , m_pseudoType(0)
434     , m_parsedNth(false)
435     , m_isLastInSelectorList(false)
436     , m_isLastInTagHistory(true)
437     , m_hasRareData(false)
438     , m_isForPage(false)
439     , m_tagIsForNamespaceRule(false)
440 {
441 }
442
443 inline CSSSelector::CSSSelector(const QualifiedName& tagQName, bool tagIsForNamespaceRule)
444     : m_relation(Descendant)
445     , m_match(Tag)
446     , m_pseudoType(0)
447     , m_parsedNth(false)
448     , m_isLastInSelectorList(false)
449     , m_isLastInTagHistory(true)
450     , m_hasRareData(false)
451     , m_isForPage(false)
452     , m_tagIsForNamespaceRule(tagIsForNamespaceRule)
453 {
454     m_data.m_tagQName = tagQName.impl();
455     m_data.m_tagQName->ref();
456 }
457
458 inline CSSSelector::CSSSelector(const CSSSelector& o)
459     : m_relation(o.m_relation)
460     , m_match(o.m_match)
461     , m_pseudoType(o.m_pseudoType)
462     , m_parsedNth(o.m_parsedNth)
463     , m_isLastInSelectorList(o.m_isLastInSelectorList)
464     , m_isLastInTagHistory(o.m_isLastInTagHistory)
465     , m_hasRareData(o.m_hasRareData)
466     , m_isForPage(o.m_isForPage)
467     , m_tagIsForNamespaceRule(o.m_tagIsForNamespaceRule)
468 {
469     if (o.match() == Tag) {
470         m_data.m_tagQName = o.m_data.m_tagQName;
471         m_data.m_tagQName->ref();
472     } else if (o.m_hasRareData) {
473         m_data.m_rareData = o.m_data.m_rareData;
474         m_data.m_rareData->ref();
475     } else if (o.m_data.m_value) {
476         m_data.m_value = o.m_data.m_value;
477         m_data.m_value->ref();
478     }
479 }
480
481 inline CSSSelector::~CSSSelector()
482 {
483     if (match() == Tag)
484         m_data.m_tagQName->deref();
485     else if (m_hasRareData)
486         m_data.m_rareData->deref();
487     else if (m_data.m_value)
488         m_data.m_value->deref();
489 }
490
491 inline const QualifiedName& CSSSelector::tagQName() const
492 {
493     ASSERT(match() == Tag);
494     return *reinterpret_cast<const QualifiedName*>(&m_data.m_tagQName);
495 }
496
497 inline const AtomicString& CSSSelector::value() const
498 {
499     ASSERT(match() != Tag);
500     // AtomicString is really just an AtomicStringImpl* so the cast below is safe.
501     // FIXME: Perhaps call sites could be changed to accept AtomicStringImpl?
502     return *reinterpret_cast<const AtomicString*>(m_hasRareData ? &m_data.m_rareData->m_value : &m_data.m_value);
503 }
504
505
506 } // namespace WebCore
507
508 #endif // CSSSelector_h