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