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