[CSS Parser] Eliminate in-place lowercasing in the parser.
[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 AtomicString& serializingValue() 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&, bool matchLowerCase = false);
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(AtomicString&& value) { return adoptRef(*new RareData(WTFMove(value))); }
353             ~RareData();
354
355             bool parseNth();
356             bool matchNth(int count);
357
358             // For quirks mode, class and id are case-insensitive. In the case where uppercase
359             // letters are used in quirks mode, |m_matchingValue| holds the lowercase class/id
360             // and |m_serializingValue| holds the original string.
361             AtomicString m_matchingValue;
362             AtomicString m_serializingValue;
363             
364             int m_a; // Used for :nth-*
365             int m_b; // Used for :nth-*
366             QualifiedName m_attribute; // used for attribute selector
367             AtomicString m_attributeCanonicalLocalName;
368             AtomicString m_argument; // Used for :contains and :nth-*
369             std::unique_ptr<Vector<AtomicString>> m_langArgumentList; // Used for :lang arguments.
370             std::unique_ptr<CSSSelectorList> m_selectorList; // Used for :matches() and :not().
371         
372         private:
373             RareData(AtomicString&& value);
374         };
375         void createRareData();
376
377         struct NameWithCase : public RefCounted<NameWithCase> {
378             NameWithCase(const QualifiedName& originalName, const AtomicString& lowercaseName)
379                 : m_originalName(originalName)
380                 , m_lowercaseLocalName(lowercaseName)
381             {
382                 ASSERT(originalName.localName() != lowercaseName);
383             }
384
385             const QualifiedName m_originalName;
386             const AtomicString m_lowercaseLocalName;
387         };
388
389         union DataUnion {
390             DataUnion() : m_value(0) { }
391             AtomicStringImpl* m_value;
392             QualifiedName::QualifiedNameImpl* m_tagQName;
393             RareData* m_rareData;
394             NameWithCase* m_nameWithCase;
395         } m_data;
396     };
397
398 inline const QualifiedName& CSSSelector::attribute() const
399 {
400     ASSERT(isAttributeSelector());
401     ASSERT(m_hasRareData);
402     return m_data.m_rareData->m_attribute;
403 }
404
405 inline const AtomicString& CSSSelector::attributeCanonicalLocalName() const
406 {
407     ASSERT(isAttributeSelector());
408     ASSERT(m_hasRareData);
409     return m_data.m_rareData->m_attributeCanonicalLocalName;
410 }
411
412 inline bool CSSSelector::matchesPseudoElement() const
413 {
414     return match() == PseudoElement;
415 }
416
417 inline bool CSSSelector::isUnknownPseudoElement() const
418 {
419     return match() == PseudoElement && pseudoElementType() == PseudoElementUnknown;
420 }
421
422 inline bool CSSSelector::isCustomPseudoElement() const
423 {
424     return match() == PseudoElement
425         && (pseudoElementType() == PseudoElementUserAgentCustom
426             || pseudoElementType() == PseudoElementWebKitCustom
427             || pseudoElementType() == PseudoElementWebKitCustomLegacyPrefixed);
428 }
429
430 inline bool CSSSelector::isWebKitCustomPseudoElement() const
431 {
432     return pseudoElementType() == PseudoElementWebKitCustom || pseudoElementType() == PseudoElementWebKitCustomLegacyPrefixed;
433 }
434
435 static inline bool pseudoClassIsRelativeToSiblings(CSSSelector::PseudoClassType type)
436 {
437     return type == CSSSelector::PseudoClassEmpty
438         || type == CSSSelector::PseudoClassFirstChild
439         || type == CSSSelector::PseudoClassFirstOfType
440         || type == CSSSelector::PseudoClassLastChild
441         || type == CSSSelector::PseudoClassLastOfType
442         || type == CSSSelector::PseudoClassOnlyChild
443         || type == CSSSelector::PseudoClassOnlyOfType
444         || type == CSSSelector::PseudoClassNthChild
445         || type == CSSSelector::PseudoClassNthOfType
446         || type == CSSSelector::PseudoClassNthLastChild
447         || type == CSSSelector::PseudoClassNthLastOfType;
448 }
449
450 inline bool CSSSelector::isSiblingSelector() const
451 {
452     return relation() == DirectAdjacent
453         || relation() == IndirectAdjacent
454         || (match() == CSSSelector::PseudoClass && pseudoClassIsRelativeToSiblings(pseudoClassType()));
455 }
456
457 inline bool CSSSelector::isAttributeSelector() const
458 {
459     return match() == CSSSelector::Exact
460         || match() ==  CSSSelector::Set
461         || match() == CSSSelector::List
462         || match() == CSSSelector::Hyphen
463         || match() == CSSSelector::Contain
464         || match() == CSSSelector::Begin
465         || match() == CSSSelector::End;
466 }
467
468 inline void CSSSelector::setValue(const AtomicString& value, bool matchLowerCase)
469 {
470     ASSERT(match() != Tag);
471     AtomicString matchingValue = matchLowerCase ? value.convertToASCIILowercase() : value;
472     if (!m_hasRareData && matchingValue != value)
473         createRareData();
474     
475     // Need to do ref counting manually for the union.
476     if (!m_hasRareData) {
477         if (m_data.m_value)
478             m_data.m_value->deref();
479         m_data.m_value = value.impl();
480         m_data.m_value->ref();
481         return;
482     }
483
484     m_data.m_rareData->m_matchingValue = WTFMove(matchingValue);
485     m_data.m_rareData->m_serializingValue = value;
486 }
487
488 inline CSSSelector::CSSSelector()
489     : m_relation(DescendantSpace)
490     , m_match(Unknown)
491     , m_pseudoType(0)
492     , m_parsedNth(false)
493     , m_isLastInSelectorList(false)
494     , m_isLastInTagHistory(true)
495     , m_hasRareData(false)
496     , m_hasNameWithCase(false)
497     , m_isForPage(false)
498     , m_tagIsForNamespaceRule(false)
499     , m_caseInsensitiveAttributeValueMatching(false)
500 #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
501     , m_destructorHasBeenCalled(false)
502 #endif
503 {
504 }
505
506 inline CSSSelector::CSSSelector(const CSSSelector& o)
507     : m_relation(o.m_relation)
508     , m_match(o.m_match)
509     , m_pseudoType(o.m_pseudoType)
510     , m_parsedNth(o.m_parsedNth)
511     , m_isLastInSelectorList(o.m_isLastInSelectorList)
512     , m_isLastInTagHistory(o.m_isLastInTagHistory)
513     , m_hasRareData(o.m_hasRareData)
514     , m_hasNameWithCase(o.m_hasNameWithCase)
515     , m_isForPage(o.m_isForPage)
516     , m_tagIsForNamespaceRule(o.m_tagIsForNamespaceRule)
517     , m_caseInsensitiveAttributeValueMatching(o.m_caseInsensitiveAttributeValueMatching)
518 #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
519     , m_destructorHasBeenCalled(false)
520 #endif
521 {
522     if (o.m_hasRareData) {
523         m_data.m_rareData = o.m_data.m_rareData;
524         m_data.m_rareData->ref();
525     } else if (o.m_hasNameWithCase) {
526         m_data.m_nameWithCase = o.m_data.m_nameWithCase;
527         m_data.m_nameWithCase->ref();
528     } if (o.match() == Tag) {
529         m_data.m_tagQName = o.m_data.m_tagQName;
530         m_data.m_tagQName->ref();
531     } else if (o.m_data.m_value) {
532         m_data.m_value = o.m_data.m_value;
533         m_data.m_value->ref();
534     }
535 }
536
537 inline CSSSelector::~CSSSelector()
538 {
539     ASSERT_WITH_SECURITY_IMPLICATION(!m_destructorHasBeenCalled);
540 #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
541     m_destructorHasBeenCalled = true;
542 #endif
543     if (m_hasRareData) {
544         m_data.m_rareData->deref();
545         m_data.m_rareData = nullptr;
546         m_hasRareData = false;
547     } else if (m_hasNameWithCase) {
548         m_data.m_nameWithCase->deref();
549         m_data.m_nameWithCase = nullptr;
550         m_hasNameWithCase = false;
551     } else if (match() == Tag) {
552         m_data.m_tagQName->deref();
553         m_data.m_tagQName = nullptr;
554         m_match = Unknown;
555     } else if (m_data.m_value) {
556         m_data.m_value->deref();
557         m_data.m_value = nullptr;
558     }
559 }
560
561 inline const QualifiedName& CSSSelector::tagQName() const
562 {
563     ASSERT(match() == Tag);
564     if (m_hasNameWithCase)
565         return m_data.m_nameWithCase->m_originalName;
566     return *reinterpret_cast<const QualifiedName*>(&m_data.m_tagQName);
567 }
568
569 inline const AtomicString& CSSSelector::tagLowercaseLocalName() const
570 {
571     if (m_hasNameWithCase)
572         return m_data.m_nameWithCase->m_lowercaseLocalName;
573     return m_data.m_tagQName->m_localName;
574 }
575
576 inline const AtomicString& CSSSelector::value() const
577 {
578     ASSERT(match() != Tag);
579     if (m_hasRareData)
580         return m_data.m_rareData->m_matchingValue;
581
582     // AtomicString is really just an AtomicStringImpl* so the cast below is safe.
583     return *reinterpret_cast<const AtomicString*>(&m_data.m_value);
584 }
585
586 inline const AtomicString& CSSSelector::serializingValue() const
587 {
588     ASSERT(match() != Tag);
589     if (m_hasRareData)
590         return m_data.m_rareData->m_serializingValue;
591     
592     // AtomicString is really just an AtomicStringImpl* so the cast below is safe.
593     return *reinterpret_cast<const AtomicString*>(&m_data.m_value);
594 }
595
596 inline void CSSSelector::setAttributeValueMatchingIsCaseInsensitive(bool isCaseInsensitive)
597 {
598     ASSERT(isAttributeSelector() && match() != CSSSelector::Set);
599     m_caseInsensitiveAttributeValueMatching = isCaseInsensitive;
600 }
601     
602 inline bool CSSSelector::attributeValueMatchingIsCaseInsensitive() const
603 {
604     return m_caseInsensitiveAttributeValueMatching;
605 }
606
607 } // namespace WebCore