8fd6955c438b1920fb95be9aeb1556d3e71b3901
[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& = emptyString()) 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 RelationType {
85             Subselector,
86             DescendantSpace,
87             Child,
88             DirectAdjacent,
89             IndirectAdjacent,
90 #if ENABLE(CSS_SELECTORS_LEVEL4)
91             DescendantDoubleChild,
92 #endif
93             ShadowDescendant
94         };
95
96         enum PseudoClassType {
97             PseudoClassUnknown = 0,
98             PseudoClassEmpty,
99             PseudoClassFirstChild,
100             PseudoClassFirstOfType,
101             PseudoClassLastChild,
102             PseudoClassLastOfType,
103             PseudoClassOnlyChild,
104             PseudoClassOnlyOfType,
105             PseudoClassNthChild,
106             PseudoClassNthOfType,
107             PseudoClassNthLastChild,
108             PseudoClassNthLastOfType,
109             PseudoClassLink,
110             PseudoClassVisited,
111             PseudoClassAny,
112             PseudoClassAnyLink,
113             PseudoClassAnyLinkDeprecated,
114             PseudoClassAutofill,
115             PseudoClassHover,
116             PseudoClassDrag,
117             PseudoClassFocus,
118             PseudoClassFocusWithin,
119             PseudoClassActive,
120             PseudoClassChecked,
121             PseudoClassEnabled,
122             PseudoClassFullPageMedia,
123             PseudoClassDefault,
124             PseudoClassDisabled,
125             PseudoClassMatches,
126             PseudoClassOptional,
127             PseudoClassPlaceholderShown,
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             PseudoClassDir,
164             PseudoClassRole,
165 #endif
166             PseudoClassHost,
167             PseudoClassDefined,
168         };
169
170         enum PseudoElementType {
171             PseudoElementUnknown = 0,
172             PseudoElementAfter,
173             PseudoElementBefore,
174 #if ENABLE(VIDEO_TRACK)
175             PseudoElementCue,
176 #endif
177             PseudoElementFirstLetter,
178             PseudoElementFirstLine,
179             PseudoElementResizer,
180             PseudoElementScrollbar,
181             PseudoElementScrollbarButton,
182             PseudoElementScrollbarCorner,
183             PseudoElementScrollbarThumb,
184             PseudoElementScrollbarTrack,
185             PseudoElementScrollbarTrackPiece,
186             PseudoElementSelection,
187             PseudoElementSlotted,
188             PseudoElementUserAgentCustom,
189             PseudoElementWebKitCustom,
190
191             // WebKitCustom that appeared in an old prefixed form
192             // and need special handling.
193             PseudoElementWebKitCustomLegacyPrefixed,
194         };
195
196         enum PagePseudoClassType {
197             PagePseudoClassFirst = 1,
198             PagePseudoClassLeft,
199             PagePseudoClassRight,
200         };
201
202         enum MarginBoxType {
203             TopLeftCornerMarginBox,
204             TopLeftMarginBox,
205             TopCenterMarginBox,
206             TopRightMarginBox,
207             TopRightCornerMarginBox,
208             BottomLeftCornerMarginBox,
209             BottomLeftMarginBox,
210             BottomCenterMarginBox,
211             BottomRightMarginBox,
212             BottomRightCornerMarginBox,
213             LeftTopMarginBox,
214             LeftMiddleMarginBox,
215             LeftBottomMarginBox,
216             RightTopMarginBox,
217             RightMiddleMarginBox,
218             RightBottomMarginBox,
219         };
220
221         enum AttributeMatchType {
222             CaseSensitive,
223             CaseInsensitive,
224         };
225
226         static PseudoElementType parsePseudoElementType(const String&);
227         static PseudoId pseudoId(PseudoElementType);
228
229         // Selectors are kept in an array by CSSSelectorList. The next component of the selector is
230         // the next item in the array.
231         const CSSSelector* tagHistory() const { return m_isLastInTagHistory ? 0 : const_cast<CSSSelector*>(this + 1); }
232
233         const QualifiedName& tagQName() const;
234         const AtomicString& tagLowercaseLocalName() const;
235
236         const AtomicString& value() const;
237         const QualifiedName& attribute() const;
238         const AtomicString& attributeCanonicalLocalName() const;
239         const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; }
240         bool attributeValueMatchingIsCaseInsensitive() const;
241         const Vector<AtomicString>* langArgumentList() const { return m_hasRareData ? m_data.m_rareData->m_langArgumentList.get() : nullptr; }
242         const CSSSelectorList* selectorList() const { return m_hasRareData ? m_data.m_rareData->m_selectorList.get() : nullptr; }
243
244         void setValue(const AtomicString&);
245         
246         // FIXME-NEWPARSER: These two methods can go away once the old parser is gone.
247         void setAttribute(const QualifiedName&, bool);
248         void setAttributeValueMatchingIsCaseInsensitive(bool);
249         void setAttribute(const QualifiedName&, bool convertToLowercase, AttributeMatchType);
250         void setNth(int a, int b);
251         void setArgument(const AtomicString&);
252         void setLangArgumentList(std::unique_ptr<Vector<AtomicString>>);
253         void setSelectorList(std::unique_ptr<CSSSelectorList>);
254
255         bool parseNth() const;
256         bool matchNth(int count) const;
257         int nthA() const;
258         int nthB() const;
259
260 #if ENABLE(CSS_SELECTORS_LEVEL4)
261         bool hasDescendantRelation() const { return relation() == DescendantSpace || relation() == DescendantDoubleChild; }
262 #else
263         bool hasDescendantRelation() const { return relation() == DescendantSpace; }
264 #endif
265
266         bool hasDescendantOrChildRelation() const { return relation() == Child || hasDescendantRelation(); }
267
268         PseudoClassType pseudoClassType() const
269         {
270             ASSERT(match() == PseudoClass);
271             return static_cast<PseudoClassType>(m_pseudoType);
272         }
273         void setPseudoClassType(PseudoClassType pseudoType)
274         {
275             m_pseudoType = pseudoType;
276             ASSERT(m_pseudoType == pseudoType);
277         }
278
279         PseudoElementType pseudoElementType() const
280         {
281             ASSERT(match() == PseudoElement);
282             return static_cast<PseudoElementType>(m_pseudoType);
283         }
284         void setPseudoElementType(PseudoElementType pseudoElementType)
285         {
286             m_pseudoType = pseudoElementType;
287             ASSERT(m_pseudoType == pseudoElementType);
288         }
289
290         PagePseudoClassType pagePseudoClassType() const
291         {
292             ASSERT(match() == PagePseudoClass);
293             return static_cast<PagePseudoClassType>(m_pseudoType);
294         }
295         void setPagePseudoType(PagePseudoClassType pagePseudoType)
296         {
297             m_pseudoType = pagePseudoType;
298             ASSERT(m_pseudoType == pagePseudoType);
299         }
300
301         bool matchesPseudoElement() const;
302         bool isUnknownPseudoElement() const;
303         bool isCustomPseudoElement() const;
304         bool isWebKitCustomPseudoElement() const;
305         bool isSiblingSelector() const;
306         bool isAttributeSelector() const;
307
308         RelationType relation() const { return static_cast<RelationType>(m_relation); }
309         void setRelation(RelationType relation)
310         {
311             m_relation = relation;
312             ASSERT(m_relation == relation);
313         }
314
315         Match match() const { return static_cast<Match>(m_match); }
316         void setMatch(Match match)
317         {
318             m_match = match;
319             ASSERT(m_match == match);
320         }
321
322         bool isLastInSelectorList() const { return m_isLastInSelectorList; }
323         void setLastInSelectorList() { m_isLastInSelectorList = true; }
324         bool isLastInTagHistory() const { return m_isLastInTagHistory; }
325         void setNotLastInTagHistory() { m_isLastInTagHistory = false; }
326
327         bool isForPage() const { return m_isForPage; }
328         void setForPage() { m_isForPage = true; }
329
330     private:
331         unsigned m_relation              : 4; // enum RelationType.
332         mutable unsigned m_match         : 4; // enum Match.
333         mutable unsigned m_pseudoType    : 8; // PseudoType.
334         mutable unsigned m_parsedNth     : 1; // Used for :nth-*.
335         unsigned m_isLastInSelectorList  : 1;
336         unsigned m_isLastInTagHistory    : 1;
337         unsigned m_hasRareData           : 1;
338         unsigned m_hasNameWithCase       : 1;
339         unsigned m_isForPage             : 1;
340         unsigned m_tagIsForNamespaceRule : 1;
341         unsigned m_caseInsensitiveAttributeValueMatching : 1;
342 #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
343         unsigned m_destructorHasBeenCalled : 1;
344 #endif
345
346         unsigned simpleSelectorSpecificityForPage() const;
347
348         // Hide.
349         CSSSelector& operator=(const CSSSelector&);
350
351         struct RareData : public RefCounted<RareData> {
352             static Ref<RareData> create(PassRefPtr<AtomicStringImpl> value) { return adoptRef(*new RareData(value)); }
353             ~RareData();
354
355             bool parseNth();
356             bool matchNth(int count);
357
358             AtomicStringImpl* m_value; // Plain pointer to keep things uniform with the union.
359             int m_a; // Used for :nth-*
360             int m_b; // Used for :nth-*
361             QualifiedName m_attribute; // used for attribute selector
362             AtomicString m_attributeCanonicalLocalName;
363             AtomicString m_argument; // Used for :contains and :nth-*
364             std::unique_ptr<Vector<AtomicString>> m_langArgumentList; // Used for :lang arguments.
365             std::unique_ptr<CSSSelectorList> m_selectorList; // Used for :matches() and :not().
366         
367         private:
368             RareData(PassRefPtr<AtomicStringImpl> value);
369         };
370         void createRareData();
371
372         struct NameWithCase : public RefCounted<NameWithCase> {
373             NameWithCase(const QualifiedName& originalName, const AtomicString& lowercaseName)
374                 : m_originalName(originalName)
375                 , m_lowercaseLocalName(lowercaseName)
376             {
377                 ASSERT(originalName.localName() != lowercaseName);
378             }
379
380             const QualifiedName m_originalName;
381             const AtomicString m_lowercaseLocalName;
382         };
383
384         union DataUnion {
385             DataUnion() : m_value(0) { }
386             AtomicStringImpl* m_value;
387             QualifiedName::QualifiedNameImpl* m_tagQName;
388             RareData* m_rareData;
389             NameWithCase* m_nameWithCase;
390         } m_data;
391     };
392
393 inline const QualifiedName& CSSSelector::attribute() const
394 {
395     ASSERT(isAttributeSelector());
396     ASSERT(m_hasRareData);
397     return m_data.m_rareData->m_attribute;
398 }
399
400 inline const AtomicString& CSSSelector::attributeCanonicalLocalName() const
401 {
402     ASSERT(isAttributeSelector());
403     ASSERT(m_hasRareData);
404     return m_data.m_rareData->m_attributeCanonicalLocalName;
405 }
406
407 inline bool CSSSelector::matchesPseudoElement() const
408 {
409     return match() == PseudoElement;
410 }
411
412 inline bool CSSSelector::isUnknownPseudoElement() const
413 {
414     return match() == PseudoElement && pseudoElementType() == PseudoElementUnknown;
415 }
416
417 inline bool CSSSelector::isCustomPseudoElement() const
418 {
419     return match() == PseudoElement
420         && (pseudoElementType() == PseudoElementUserAgentCustom
421             || pseudoElementType() == PseudoElementWebKitCustom
422             || pseudoElementType() == PseudoElementWebKitCustomLegacyPrefixed);
423 }
424
425 inline bool CSSSelector::isWebKitCustomPseudoElement() const
426 {
427     return pseudoElementType() == PseudoElementWebKitCustom || pseudoElementType() == PseudoElementWebKitCustomLegacyPrefixed;
428 }
429
430 static inline bool pseudoClassIsRelativeToSiblings(CSSSelector::PseudoClassType type)
431 {
432     return type == CSSSelector::PseudoClassEmpty
433         || type == CSSSelector::PseudoClassFirstChild
434         || type == CSSSelector::PseudoClassFirstOfType
435         || type == CSSSelector::PseudoClassLastChild
436         || type == CSSSelector::PseudoClassLastOfType
437         || type == CSSSelector::PseudoClassOnlyChild
438         || type == CSSSelector::PseudoClassOnlyOfType
439         || type == CSSSelector::PseudoClassNthChild
440         || type == CSSSelector::PseudoClassNthOfType
441         || type == CSSSelector::PseudoClassNthLastChild
442         || type == CSSSelector::PseudoClassNthLastOfType;
443 }
444
445 inline bool CSSSelector::isSiblingSelector() const
446 {
447     return relation() == DirectAdjacent
448         || relation() == IndirectAdjacent
449         || (match() == CSSSelector::PseudoClass && pseudoClassIsRelativeToSiblings(pseudoClassType()));
450 }
451
452 inline bool CSSSelector::isAttributeSelector() const
453 {
454     return match() == CSSSelector::Exact
455         || match() ==  CSSSelector::Set
456         || match() == CSSSelector::List
457         || match() == CSSSelector::Hyphen
458         || match() == CSSSelector::Contain
459         || match() == CSSSelector::Begin
460         || match() == CSSSelector::End;
461 }
462
463 inline void CSSSelector::setValue(const AtomicString& value)
464 {
465     ASSERT(match() != Tag);
466     // Need to do ref counting manually for the union.
467     if (m_hasRareData) {
468         if (m_data.m_rareData->m_value)
469             m_data.m_rareData->m_value->deref();
470         m_data.m_rareData->m_value = value.impl();
471         m_data.m_rareData->m_value->ref();
472         return;
473     }
474     if (m_data.m_value)
475         m_data.m_value->deref();
476     m_data.m_value = value.impl();
477     m_data.m_value->ref();
478 }
479
480 inline CSSSelector::CSSSelector()
481     : m_relation(DescendantSpace)
482     , m_match(Unknown)
483     , m_pseudoType(0)
484     , m_parsedNth(false)
485     , m_isLastInSelectorList(false)
486     , m_isLastInTagHistory(true)
487     , m_hasRareData(false)
488     , m_hasNameWithCase(false)
489     , m_isForPage(false)
490     , m_tagIsForNamespaceRule(false)
491     , m_caseInsensitiveAttributeValueMatching(false)
492 #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
493     , m_destructorHasBeenCalled(false)
494 #endif
495 {
496 }
497
498 inline CSSSelector::CSSSelector(const CSSSelector& o)
499     : m_relation(o.m_relation)
500     , m_match(o.m_match)
501     , m_pseudoType(o.m_pseudoType)
502     , m_parsedNth(o.m_parsedNth)
503     , m_isLastInSelectorList(o.m_isLastInSelectorList)
504     , m_isLastInTagHistory(o.m_isLastInTagHistory)
505     , m_hasRareData(o.m_hasRareData)
506     , m_hasNameWithCase(o.m_hasNameWithCase)
507     , m_isForPage(o.m_isForPage)
508     , m_tagIsForNamespaceRule(o.m_tagIsForNamespaceRule)
509     , m_caseInsensitiveAttributeValueMatching(o.m_caseInsensitiveAttributeValueMatching)
510 #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
511     , m_destructorHasBeenCalled(false)
512 #endif
513 {
514     if (o.m_hasRareData) {
515         m_data.m_rareData = o.m_data.m_rareData;
516         m_data.m_rareData->ref();
517     } else if (o.m_hasNameWithCase) {
518         m_data.m_nameWithCase = o.m_data.m_nameWithCase;
519         m_data.m_nameWithCase->ref();
520     } if (o.match() == Tag) {
521         m_data.m_tagQName = o.m_data.m_tagQName;
522         m_data.m_tagQName->ref();
523     } else if (o.m_data.m_value) {
524         m_data.m_value = o.m_data.m_value;
525         m_data.m_value->ref();
526     }
527 }
528
529 inline CSSSelector::~CSSSelector()
530 {
531     ASSERT_WITH_SECURITY_IMPLICATION(!m_destructorHasBeenCalled);
532 #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
533     m_destructorHasBeenCalled = true;
534 #endif
535     if (m_hasRareData) {
536         m_data.m_rareData->deref();
537         m_data.m_rareData = nullptr;
538         m_hasRareData = false;
539     } else if (m_hasNameWithCase) {
540         m_data.m_nameWithCase->deref();
541         m_data.m_nameWithCase = nullptr;
542         m_hasNameWithCase = false;
543     } else if (match() == Tag) {
544         m_data.m_tagQName->deref();
545         m_data.m_tagQName = nullptr;
546         m_match = Unknown;
547     } else if (m_data.m_value) {
548         m_data.m_value->deref();
549         m_data.m_value = nullptr;
550     }
551 }
552
553 inline const QualifiedName& CSSSelector::tagQName() const
554 {
555     ASSERT(match() == Tag);
556     if (m_hasNameWithCase)
557         return m_data.m_nameWithCase->m_originalName;
558     return *reinterpret_cast<const QualifiedName*>(&m_data.m_tagQName);
559 }
560
561 inline const AtomicString& CSSSelector::tagLowercaseLocalName() const
562 {
563     if (m_hasNameWithCase)
564         return m_data.m_nameWithCase->m_lowercaseLocalName;
565     return m_data.m_tagQName->m_localName;
566 }
567
568 inline const AtomicString& CSSSelector::value() const
569 {
570     ASSERT(match() != Tag);
571     // AtomicString is really just an AtomicStringImpl* so the cast below is safe.
572     // FIXME: Perhaps call sites could be changed to accept AtomicStringImpl?
573     return *reinterpret_cast<const AtomicString*>(m_hasRareData ? &m_data.m_rareData->m_value : &m_data.m_value);
574 }
575
576 inline void CSSSelector::setAttributeValueMatchingIsCaseInsensitive(bool isCaseInsensitive)
577 {
578     ASSERT(isAttributeSelector() && match() != CSSSelector::Set);
579     m_caseInsensitiveAttributeValueMatching = isCaseInsensitive;
580 }
581     
582 inline bool CSSSelector::attributeValueMatchingIsCaseInsensitive() const
583 {
584     return m_caseInsensitiveAttributeValueMatching;
585 }
586
587 } // namespace WebCore
588
589 #endif // CSSSelector_h