Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / css / CSSSelector.cpp
1 /*
2  * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
3  *               1999 Waldo Bastian (bastian@kde.org)
4  *               2001 Andreas Schlapbach (schlpbch@iam.unibe.ch)
5  *               2001-2003 Dirk Mueller (mueller@kde.org)
6  * Copyright (C) 2002, 2006, 2007, 2008, 2009, 2010, 2013, 2014 Apple Inc. All rights reserved.
7  * Copyright (C) 2008 David Smith (catfish.man@gmail.com)
8  * Copyright (C) 2010 Google Inc. All rights reserved.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25
26 #include "config.h"
27 #include "CSSSelector.h"
28
29 #include "CSSOMUtils.h"
30 #include "CSSSelectorList.h"
31 #include "HTMLNames.h"
32 #include "SelectorPseudoTypeMap.h"
33 #include <wtf/Assertions.h>
34 #include <wtf/HashMap.h>
35 #include <wtf/NeverDestroyed.h>
36 #include <wtf/StdLibExtras.h>
37 #include <wtf/Vector.h>
38 #include <wtf/text/AtomicStringHash.h>
39 #include <wtf/text/StringBuilder.h>
40
41 namespace WebCore {
42
43 using namespace HTMLNames;
44
45 struct SameSizeAsCSSSelector {
46     unsigned flags;
47     void* unionPointer;
48 };
49
50 static_assert(sizeof(CSSSelector) == sizeof(SameSizeAsCSSSelector), "CSSSelector should remain small.");
51
52 CSSSelector::CSSSelector(const QualifiedName& tagQName, bool tagIsForNamespaceRule)
53     : m_relation(Descendant)
54     , m_match(Tag)
55     , m_pseudoType(0)
56     , m_parsedNth(false)
57     , m_isLastInSelectorList(false)
58     , m_isLastInTagHistory(true)
59     , m_hasRareData(false)
60     , m_hasNameWithCase(false)
61     , m_isForPage(false)
62     , m_tagIsForNamespaceRule(tagIsForNamespaceRule)
63 #if ENABLE(CSS_SELECTORS_LEVEL4)
64     , m_descendantDoubleChildSyntax(false)
65 #endif
66     , m_caseInsensitiveAttributeValueMatching(false)
67 #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
68     , m_destructorHasBeenCalled(false)
69 #endif
70 {
71     const AtomicString& tagLocalName = tagQName.localName();
72     const AtomicString tagLocalNameASCIILowercase = tagLocalName.convertToASCIILowercase();
73
74     if (tagLocalName == tagLocalNameASCIILowercase) {
75         m_data.m_tagQName = tagQName.impl();
76         m_data.m_tagQName->ref();
77     } else {
78         m_data.m_nameWithCase = adoptRef(new NameWithCase(tagQName, tagLocalNameASCIILowercase)).leakRef();
79         m_hasNameWithCase = true;
80     }
81 }
82
83 void CSSSelector::createRareData()
84 {
85     ASSERT(match() != Tag);
86     ASSERT(!m_hasNameWithCase);
87     if (m_hasRareData)
88         return;
89     // Move the value to the rare data stucture.
90     m_data.m_rareData = &RareData::create(adoptRef(m_data.m_value)).leakRef();
91     m_hasRareData = true;
92 }
93
94 static unsigned simpleSelectorSpecificityInternal(const CSSSelector& simpleSelector, bool isComputingMaximumSpecificity);
95
96 static unsigned selectorSpecificity(const CSSSelector& firstSimpleSelector, bool isComputingMaximumSpecificity)
97 {
98     unsigned total = simpleSelectorSpecificityInternal(firstSimpleSelector, isComputingMaximumSpecificity);
99
100     for (const CSSSelector* selector = firstSimpleSelector.tagHistory(); selector; selector = selector->tagHistory())
101         total = CSSSelector::addSpecificities(total, simpleSelectorSpecificityInternal(*selector, isComputingMaximumSpecificity));
102     return total;
103 }
104
105 static unsigned maxSpecificity(const CSSSelectorList& selectorList)
106 {
107     unsigned maxSpecificity = 0;
108     for (const CSSSelector* subSelector = selectorList.first(); subSelector; subSelector = CSSSelectorList::next(subSelector))
109         maxSpecificity = std::max(maxSpecificity, selectorSpecificity(*subSelector, true));
110     return maxSpecificity;
111 }
112
113 static unsigned simpleSelectorSpecificityInternal(const CSSSelector& simpleSelector, bool isComputingMaximumSpecificity)
114 {
115     ASSERT_WITH_MESSAGE(!simpleSelector.isForPage(), "At the time of this writing, page selectors are not treated as real selectors that are matched. The value computed here only account for real selectors.");
116
117     switch (simpleSelector.match()) {
118     case CSSSelector::Id:
119         return static_cast<unsigned>(SelectorSpecificityIncrement::ClassA);
120
121     case CSSSelector::PagePseudoClass:
122         break;
123     case CSSSelector::PseudoClass:
124         if (simpleSelector.pseudoClassType() == CSSSelector::PseudoClassMatches) {
125             ASSERT_WITH_MESSAGE(simpleSelector.selectorList() && simpleSelector.selectorList()->first(), "The parser should never generate a valid selector for an empty :matches().");
126             if (!isComputingMaximumSpecificity)
127                 return 0;
128             return maxSpecificity(*simpleSelector.selectorList());
129         }
130
131         if (simpleSelector.pseudoClassType() == CSSSelector::PseudoClassNot) {
132             ASSERT_WITH_MESSAGE(simpleSelector.selectorList() && simpleSelector.selectorList()->first(), "The parser should never generate a valid selector for an empty :not().");
133             return maxSpecificity(*simpleSelector.selectorList());
134         }
135         FALLTHROUGH;
136     case CSSSelector::Exact:
137     case CSSSelector::Class:
138     case CSSSelector::Set:
139     case CSSSelector::List:
140     case CSSSelector::Hyphen:
141     case CSSSelector::Contain:
142     case CSSSelector::Begin:
143     case CSSSelector::End:
144         return static_cast<unsigned>(SelectorSpecificityIncrement::ClassB);
145     case CSSSelector::Tag:
146         return (simpleSelector.tagQName().localName() != starAtom) ? static_cast<unsigned>(SelectorSpecificityIncrement::ClassC) : 0;
147     case CSSSelector::PseudoElement:
148         return static_cast<unsigned>(SelectorSpecificityIncrement::ClassC);
149     case CSSSelector::Unknown:
150         return 0;
151     }
152     ASSERT_NOT_REACHED();
153     return 0;
154 }
155
156 unsigned CSSSelector::simpleSelectorSpecificity() const
157 {
158     return simpleSelectorSpecificityInternal(*this, false);
159 }
160
161 static unsigned staticSpecificityInternal(const CSSSelector& firstSimpleSelector, bool& ok);
162
163 static unsigned simpleSelectorFunctionalPseudoClassStaticSpecificity(const CSSSelector& simpleSelector, bool& ok)
164 {
165     if (simpleSelector.match() == CSSSelector::PseudoClass) {
166         CSSSelector::PseudoClassType pseudoClassType = simpleSelector.pseudoClassType();
167         if (pseudoClassType == CSSSelector::PseudoClassMatches || pseudoClassType == CSSSelector::PseudoClassNthChild || pseudoClassType == CSSSelector::PseudoClassNthLastChild) {
168             const CSSSelectorList* selectorList = simpleSelector.selectorList();
169             if (!selectorList) {
170                 ASSERT_WITH_MESSAGE(pseudoClassType != CSSSelector::PseudoClassMatches, ":matches() should never be created without a valid selector list.");
171                 return 0;
172             }
173
174             const CSSSelector& firstSubselector = *selectorList->first();
175
176             unsigned initialSpecificity = staticSpecificityInternal(firstSubselector, ok);
177             if (!ok)
178                 return 0;
179
180             const CSSSelector* subselector = &firstSubselector;
181             while ((subselector = CSSSelectorList::next(subselector))) {
182                 unsigned subSelectorSpecificity = staticSpecificityInternal(*subselector, ok);
183                 if (initialSpecificity != subSelectorSpecificity)
184                     ok = false;
185                 if (!ok)
186                     return 0;
187             }
188             return initialSpecificity;
189         }
190     }
191     return 0;
192 }
193
194 static unsigned functionalPseudoClassStaticSpecificity(const CSSSelector& firstSimpleSelector, bool& ok)
195 {
196     unsigned total = 0;
197     for (const CSSSelector* selector = &firstSimpleSelector; selector; selector = selector->tagHistory()) {
198         total = CSSSelector::addSpecificities(total, simpleSelectorFunctionalPseudoClassStaticSpecificity(*selector, ok));
199         if (!ok)
200             return 0;
201     }
202     return total;
203 }
204
205 static unsigned staticSpecificityInternal(const CSSSelector& firstSimpleSelector, bool& ok)
206 {
207     unsigned staticSpecificity = selectorSpecificity(firstSimpleSelector, false);
208     return CSSSelector::addSpecificities(staticSpecificity, functionalPseudoClassStaticSpecificity(firstSimpleSelector, ok));
209 }
210
211 unsigned CSSSelector::staticSpecificity(bool &ok) const
212 {
213     ok = true;
214     return staticSpecificityInternal(*this, ok);
215 }
216
217 unsigned CSSSelector::addSpecificities(unsigned a, unsigned b)
218 {
219     unsigned total = a;
220
221     unsigned newIdValue = (b & idMask);
222     if (((total & idMask) + newIdValue) & ~idMask)
223         total |= idMask;
224     else
225         total += newIdValue;
226
227     unsigned newClassValue = (b & classMask);
228     if (((total & classMask) + newClassValue) & ~classMask)
229         total |= classMask;
230     else
231         total += newClassValue;
232
233     unsigned newElementValue = (b & elementMask);
234     if (((total & elementMask) + newElementValue) & ~elementMask)
235         total |= elementMask;
236     else
237         total += newElementValue;
238
239     return total;
240 }
241
242 unsigned CSSSelector::specificityForPage() const
243 {
244     ASSERT(isForPage());
245
246     // See http://dev.w3.org/csswg/css3-page/#cascading-and-page-context
247     unsigned s = 0;
248
249     for (const CSSSelector* component = this; component; component = component->tagHistory()) {
250         switch (component->match()) {
251         case Tag:
252             s += tagQName().localName() == starAtom ? 0 : 4;
253             break;
254         case PagePseudoClass:
255             switch (component->pagePseudoClassType()) {
256             case PagePseudoClassFirst:
257                 s += 2;
258                 break;
259             case PagePseudoClassLeft:
260             case PagePseudoClassRight:
261                 s += 1;
262                 break;
263             }
264             break;
265         default:
266             break;
267         }
268     }
269     return s;
270 }
271
272 PseudoId CSSSelector::pseudoId(PseudoElementType type)
273 {
274     switch (type) {
275     case PseudoElementFirstLine:
276         return FIRST_LINE;
277     case PseudoElementFirstLetter:
278         return FIRST_LETTER;
279     case PseudoElementSelection:
280         return SELECTION;
281     case PseudoElementBefore:
282         return BEFORE;
283     case PseudoElementAfter:
284         return AFTER;
285     case PseudoElementScrollbar:
286         return SCROLLBAR;
287     case PseudoElementScrollbarButton:
288         return SCROLLBAR_BUTTON;
289     case PseudoElementScrollbarCorner:
290         return SCROLLBAR_CORNER;
291     case PseudoElementScrollbarThumb:
292         return SCROLLBAR_THUMB;
293     case PseudoElementScrollbarTrack:
294         return SCROLLBAR_TRACK;
295     case PseudoElementScrollbarTrackPiece:
296         return SCROLLBAR_TRACK_PIECE;
297     case PseudoElementResizer:
298         return RESIZER;
299 #if ENABLE(VIDEO_TRACK)
300     case PseudoElementCue:
301 #endif
302     case PseudoElementUnknown:
303     case PseudoElementUserAgentCustom:
304     case PseudoElementWebKitCustom:
305         return NOPSEUDO;
306     }
307
308     ASSERT_NOT_REACHED();
309     return NOPSEUDO;
310 }
311
312 CSSSelector::PseudoElementType CSSSelector::parsePseudoElementType(const String& name)
313 {
314     if (name.isNull())
315         return PseudoElementUnknown;
316
317     PseudoElementType type = parsePseudoElementString(*name.impl());
318     if (type == PseudoElementUnknown) {
319         if (name.startsWith("-webkit-"))
320             type = PseudoElementWebKitCustom;
321
322         if (name.startsWith("x-"))
323             type = PseudoElementUserAgentCustom;
324     }
325     return type;
326 }
327
328
329 bool CSSSelector::operator==(const CSSSelector& other) const
330 {
331     const CSSSelector* sel1 = this;
332     const CSSSelector* sel2 = &other;
333
334     while (sel1 && sel2) {
335         if (sel1->attribute() != sel2->attribute()
336             || sel1->relation() != sel2->relation()
337             || sel1->match() != sel2->match()
338             || sel1->value() != sel2->value()
339             || sel1->m_pseudoType != sel2->m_pseudoType
340             || sel1->argument() != sel2->argument()) {
341             return false;
342         }
343         if (sel1->match() == Tag) {
344             if (sel1->tagQName() != sel2->tagQName())
345                 return false;
346         }
347         sel1 = sel1->tagHistory();
348         sel2 = sel2->tagHistory();
349     }
350
351     if (sel1 || sel2)
352         return false;
353
354     return true;
355 }
356
357 static void appendPseudoClassFunctionTail(StringBuilder& str, const CSSSelector* selector)
358 {
359     switch (selector->pseudoClassType()) {
360 #if ENABLE(CSS_SELECTORS_LEVEL4)
361     case CSSSelector::PseudoClassDir:
362 #endif
363     case CSSSelector::PseudoClassLang:
364     case CSSSelector::PseudoClassNthChild:
365     case CSSSelector::PseudoClassNthLastChild:
366     case CSSSelector::PseudoClassNthOfType:
367     case CSSSelector::PseudoClassNthLastOfType:
368 #if ENABLE(CSS_SELECTORS_LEVEL4)
369     case CSSSelector::PseudoClassRole:
370 #endif
371         str.append(selector->argument());
372         str.append(')');
373         break;
374     default:
375         break;
376     }
377
378 }
379
380 static void appendLangArgumentList(StringBuilder& str, const Vector<AtomicString>& argumentList)
381 {
382     unsigned argumentListSize = argumentList.size();
383     for (unsigned i = 0; i < argumentListSize; ++i) {
384         str.append('"');
385         str.append(argumentList[i]);
386         str.append('"');
387         if (i != argumentListSize - 1)
388             str.appendLiteral(", ");
389     }
390 }
391
392 String CSSSelector::selectorText(const String& rightSide) const
393 {
394     StringBuilder str;
395
396     if (match() == CSSSelector::Tag && !m_tagIsForNamespaceRule) {
397         if (tagQName().prefix().isNull())
398             str.append(tagQName().localName());
399         else {
400             str.append(tagQName().prefix().string());
401             str.append('|');
402             str.append(tagQName().localName());
403         }
404     }
405
406     const CSSSelector* cs = this;
407     while (true) {
408         if (cs->match() == CSSSelector::Id) {
409             str.append('#');
410             serializeIdentifier(cs->value(), str);
411         } else if (cs->match() == CSSSelector::Class) {
412             str.append('.');
413             serializeIdentifier(cs->value(), str);
414         } else if (cs->match() == CSSSelector::PseudoClass) {
415             switch (cs->pseudoClassType()) {
416 #if ENABLE(FULLSCREEN_API)
417             case CSSSelector::PseudoClassAnimatingFullScreenTransition:
418                 str.appendLiteral(":-webkit-animating-full-screen-transition");
419                 break;
420 #endif
421             case CSSSelector::PseudoClassAny: {
422                 str.appendLiteral(":-webkit-any(");
423                 cs->selectorList()->buildSelectorsText(str);
424                 str.append(')');
425                 break;
426             }
427             case CSSSelector::PseudoClassAnyLink:
428                 str.appendLiteral(":any-link");
429                 break;
430             case CSSSelector::PseudoClassAnyLinkDeprecated:
431                 str.appendLiteral(":-webkit-any-link");
432                 break;
433             case CSSSelector::PseudoClassAutofill:
434                 str.appendLiteral(":-webkit-autofill");
435                 break;
436             case CSSSelector::PseudoClassDrag:
437                 str.appendLiteral(":-webkit-drag");
438                 break;
439             case CSSSelector::PseudoClassFullPageMedia:
440                 str.appendLiteral(":-webkit-full-page-media");
441                 break;
442 #if ENABLE(FULLSCREEN_API)
443             case CSSSelector::PseudoClassFullScreen:
444                 str.appendLiteral(":-webkit-full-screen");
445                 break;
446             case CSSSelector::PseudoClassFullScreenAncestor:
447                 str.appendLiteral(":-webkit-full-screen-ancestor");
448                 break;
449             case CSSSelector::PseudoClassFullScreenDocument:
450                 str.appendLiteral(":-webkit-full-screen-document");
451                 break;
452 #endif
453             case CSSSelector::PseudoClassActive:
454                 str.appendLiteral(":active");
455                 break;
456             case CSSSelector::PseudoClassChecked:
457                 str.appendLiteral(":checked");
458                 break;
459             case CSSSelector::PseudoClassCornerPresent:
460                 str.appendLiteral(":corner-present");
461                 break;
462             case CSSSelector::PseudoClassDecrement:
463                 str.appendLiteral(":decrement");
464                 break;
465             case CSSSelector::PseudoClassDefault:
466                 str.appendLiteral(":default");
467                 break;
468 #if ENABLE(CSS_SELECTORS_LEVEL4)
469             case CSSSelector::PseudoClassDir:
470                 str.appendLiteral(":dir(");
471                 appendPseudoClassFunctionTail(str, cs);
472                 break;
473 #endif
474             case CSSSelector::PseudoClassDisabled:
475                 str.appendLiteral(":disabled");
476                 break;
477             case CSSSelector::PseudoClassDoubleButton:
478                 str.appendLiteral(":double-button");
479                 break;
480             case CSSSelector::PseudoClassEmpty:
481                 str.appendLiteral(":empty");
482                 break;
483             case CSSSelector::PseudoClassEnabled:
484                 str.appendLiteral(":enabled");
485                 break;
486             case CSSSelector::PseudoClassEnd:
487                 str.appendLiteral(":end");
488                 break;
489             case CSSSelector::PseudoClassFirstChild:
490                 str.appendLiteral(":first-child");
491                 break;
492             case CSSSelector::PseudoClassFirstOfType:
493                 str.appendLiteral(":first-of-type");
494                 break;
495             case CSSSelector::PseudoClassFocus:
496                 str.appendLiteral(":focus");
497                 break;
498 #if ENABLE(VIDEO_TRACK)
499             case CSSSelector::PseudoClassFuture:
500                 str.appendLiteral(":future");
501                 break;
502 #endif
503             case CSSSelector::PseudoClassHorizontal:
504                 str.appendLiteral(":horizontal");
505                 break;
506             case CSSSelector::PseudoClassHover:
507                 str.appendLiteral(":hover");
508                 break;
509             case CSSSelector::PseudoClassInRange:
510                 str.appendLiteral(":in-range");
511                 break;
512             case CSSSelector::PseudoClassIncrement:
513                 str.appendLiteral(":increment");
514                 break;
515             case CSSSelector::PseudoClassIndeterminate:
516                 str.appendLiteral(":indeterminate");
517                 break;
518             case CSSSelector::PseudoClassInvalid:
519                 str.appendLiteral(":invalid");
520                 break;
521             case CSSSelector::PseudoClassLang:
522                 str.appendLiteral(":lang(");
523                 ASSERT_WITH_MESSAGE(cs->langArgumentList() && !cs->langArgumentList()->isEmpty(), "An empty :lang() is invalid and should never be generated by the parser.");
524                 appendLangArgumentList(str, *cs->langArgumentList());
525                 str.append(')');
526                 break;
527             case CSSSelector::PseudoClassLastChild:
528                 str.appendLiteral(":last-child");
529                 break;
530             case CSSSelector::PseudoClassLastOfType:
531                 str.appendLiteral(":last-of-type");
532                 break;
533             case CSSSelector::PseudoClassLink:
534                 str.appendLiteral(":link");
535                 break;
536             case CSSSelector::PseudoClassNoButton:
537                 str.appendLiteral(":no-button");
538                 break;
539             case CSSSelector::PseudoClassNot:
540                 str.appendLiteral(":not(");
541                 cs->selectorList()->buildSelectorsText(str);
542                 str.append(')');
543                 break;
544             case CSSSelector::PseudoClassNthChild:
545                 str.appendLiteral(":nth-child(");
546                 str.append(cs->argument());
547                 if (const CSSSelectorList* selectorList = cs->selectorList()) {
548                     str.appendLiteral(" of ");
549                     selectorList->buildSelectorsText(str);
550                 }
551                 str.append(')');
552                 break;
553             case CSSSelector::PseudoClassNthLastChild:
554                 str.appendLiteral(":nth-last-child(");
555                 str.append(cs->argument());
556                 if (const CSSSelectorList* selectorList = cs->selectorList()) {
557                     str.appendLiteral(" of ");
558                     selectorList->buildSelectorsText(str);
559                 }
560                 str.append(')');
561                 break;
562             case CSSSelector::PseudoClassNthLastOfType:
563                 str.appendLiteral(":nth-last-of-type(");
564                 appendPseudoClassFunctionTail(str, cs);
565                 break;
566             case CSSSelector::PseudoClassNthOfType:
567                 str.appendLiteral(":nth-of-type(");
568                 appendPseudoClassFunctionTail(str, cs);
569                 break;
570             case CSSSelector::PseudoClassOnlyChild:
571                 str.appendLiteral(":only-child");
572                 break;
573             case CSSSelector::PseudoClassOnlyOfType:
574                 str.appendLiteral(":only-of-type");
575                 break;
576             case CSSSelector::PseudoClassOptional:
577                 str.appendLiteral(":optional");
578                 break;
579             case CSSSelector::PseudoClassMatches: {
580                 str.appendLiteral(":matches(");
581                 cs->selectorList()->buildSelectorsText(str);
582                 str.append(')');
583                 break;
584             }
585             case CSSSelector::PseudoClassPlaceholderShown:
586                 str.appendLiteral(":placeholder-shown");
587                 break;
588             case CSSSelector::PseudoClassOutOfRange:
589                 str.appendLiteral(":out-of-range");
590                 break;
591 #if ENABLE(VIDEO_TRACK)
592             case CSSSelector::PseudoClassPast:
593                 str.appendLiteral(":past");
594                 break;
595 #endif
596             case CSSSelector::PseudoClassReadOnly:
597                 str.appendLiteral(":read-only");
598                 break;
599             case CSSSelector::PseudoClassReadWrite:
600                 str.appendLiteral(":read-write");
601                 break;
602             case CSSSelector::PseudoClassRequired:
603                 str.appendLiteral(":required");
604                 break;
605 #if ENABLE(CSS_SELECTORS_LEVEL4)
606             case CSSSelector::PseudoClassRole:
607                 str.appendLiteral(":role(");
608                 appendPseudoClassFunctionTail(str, cs);
609                 break;
610 #endif
611             case CSSSelector::PseudoClassRoot:
612                 str.appendLiteral(":root");
613                 break;
614             case CSSSelector::PseudoClassScope:
615                 str.appendLiteral(":scope");
616                 break;
617             case CSSSelector::PseudoClassSingleButton:
618                 str.appendLiteral(":single-button");
619                 break;
620             case CSSSelector::PseudoClassStart:
621                 str.appendLiteral(":start");
622                 break;
623             case CSSSelector::PseudoClassTarget:
624                 str.appendLiteral(":target");
625                 break;
626             case CSSSelector::PseudoClassValid:
627                 str.appendLiteral(":valid");
628                 break;
629             case CSSSelector::PseudoClassVertical:
630                 str.appendLiteral(":vertical");
631                 break;
632             case CSSSelector::PseudoClassVisited:
633                 str.appendLiteral(":visited");
634                 break;
635             case CSSSelector::PseudoClassWindowInactive:
636                 str.appendLiteral(":window-inactive");
637                 break;
638 #if ENABLE(SHADOW_DOM)
639             case CSSSelector::PseudoClassHost:
640                 str.appendLiteral(":host");
641                 break;
642 #endif
643             case CSSSelector::PseudoClassUnknown:
644                 ASSERT_NOT_REACHED();
645             }
646         } else if (cs->match() == CSSSelector::PseudoElement) {
647             str.appendLiteral("::");
648             str.append(cs->value());
649         } else if (cs->isAttributeSelector()) {
650             str.append('[');
651             const AtomicString& prefix = cs->attribute().prefix();
652             if (!prefix.isNull()) {
653                 str.append(prefix);
654                 str.append('|');
655             }
656             str.append(cs->attribute().localName());
657             switch (cs->match()) {
658                 case CSSSelector::Exact:
659                     str.append('=');
660                     break;
661                 case CSSSelector::Set:
662                     // set has no operator or value, just the attrName
663                     str.append(']');
664                     break;
665                 case CSSSelector::List:
666                     str.appendLiteral("~=");
667                     break;
668                 case CSSSelector::Hyphen:
669                     str.appendLiteral("|=");
670                     break;
671                 case CSSSelector::Begin:
672                     str.appendLiteral("^=");
673                     break;
674                 case CSSSelector::End:
675                     str.appendLiteral("$=");
676                     break;
677                 case CSSSelector::Contain:
678                     str.appendLiteral("*=");
679                     break;
680                 default:
681                     break;
682             }
683             if (cs->match() != CSSSelector::Set) {
684                 serializeString(cs->value(), str);
685                 if (cs->attributeValueMatchingIsCaseInsensitive())
686                     str.appendLiteral(" i]");
687                 else
688                     str.append(']');
689             }
690         } else if (cs->match() == CSSSelector::PagePseudoClass) {
691             switch (cs->pagePseudoClassType()) {
692             case PagePseudoClassFirst:
693                 str.appendLiteral(":first");
694                 break;
695             case PagePseudoClassLeft:
696                 str.appendLiteral(":left");
697                 break;
698             case PagePseudoClassRight:
699                 str.appendLiteral(":right");
700                 break;
701             }
702         }
703
704         if (cs->relation() != CSSSelector::SubSelector || !cs->tagHistory())
705             break;
706         cs = cs->tagHistory();
707     }
708
709     if (const CSSSelector* tagHistory = cs->tagHistory()) {
710         switch (cs->relation()) {
711         case CSSSelector::Descendant:
712 #if ENABLE(CSS_SELECTORS_LEVEL4)
713             if (cs->m_descendantDoubleChildSyntax)
714                 return tagHistory->selectorText(" >> " + str.toString() + rightSide);
715 #endif
716             return tagHistory->selectorText(" " + str.toString() + rightSide);
717         case CSSSelector::Child:
718             return tagHistory->selectorText(" > " + str.toString() + rightSide);
719         case CSSSelector::DirectAdjacent:
720             return tagHistory->selectorText(" + " + str.toString() + rightSide);
721         case CSSSelector::IndirectAdjacent:
722             return tagHistory->selectorText(" ~ " + str.toString() + rightSide);
723         case CSSSelector::SubSelector:
724             ASSERT_NOT_REACHED();
725 #if ASSERT_DISABLED
726             FALLTHROUGH;
727 #endif
728         case CSSSelector::ShadowDescendant:
729             return tagHistory->selectorText(str.toString() + rightSide);
730         }
731     }
732     return str.toString() + rightSide;
733 }
734
735 void CSSSelector::setAttribute(const QualifiedName& value, bool isCaseInsensitive)
736 {
737     createRareData();
738     m_data.m_rareData->m_attribute = value;
739     m_data.m_rareData->m_attributeCanonicalLocalName = isCaseInsensitive ? value.localName().convertToASCIILowercase() : value.localName();
740 }
741
742 void CSSSelector::setArgument(const AtomicString& value)
743 {
744     createRareData();
745     m_data.m_rareData->m_argument = value;
746 }
747
748 void CSSSelector::setLangArgumentList(std::unique_ptr<Vector<AtomicString>> argumentList)
749 {
750     createRareData();
751     m_data.m_rareData->m_langArgumentList = WTFMove(argumentList);
752 }
753
754 void CSSSelector::setSelectorList(std::unique_ptr<CSSSelectorList> selectorList)
755 {
756     createRareData();
757     m_data.m_rareData->m_selectorList = WTFMove(selectorList);
758 }
759
760 bool CSSSelector::parseNth() const
761 {
762     if (!m_hasRareData)
763         return false;
764     if (m_parsedNth)
765         return true;
766     m_parsedNth = m_data.m_rareData->parseNth();
767     return m_parsedNth;
768 }
769
770 bool CSSSelector::matchNth(int count) const
771 {
772     ASSERT(m_hasRareData);
773     return m_data.m_rareData->matchNth(count);
774 }
775
776 int CSSSelector::nthA() const
777 {
778     ASSERT(m_hasRareData);
779     ASSERT(m_parsedNth);
780     return m_data.m_rareData->m_a;
781 }
782
783 int CSSSelector::nthB() const
784 {
785     ASSERT(m_hasRareData);
786     ASSERT(m_parsedNth);
787     return m_data.m_rareData->m_b;
788 }
789
790 CSSSelector::RareData::RareData(PassRefPtr<AtomicStringImpl> value)
791     : m_value(value.leakRef())
792     , m_a(0)
793     , m_b(0)
794     , m_attribute(anyQName())
795     , m_argument(nullAtom)
796 {
797 }
798
799 CSSSelector::RareData::~RareData()
800 {
801     if (m_value)
802         m_value->deref();
803 }
804
805 // a helper function for parsing nth-arguments
806 bool CSSSelector::RareData::parseNth()
807 {
808     String argument = m_argument.lower();
809
810     if (argument.isEmpty())
811         return false;
812
813     m_a = 0;
814     m_b = 0;
815     if (argument == "odd") {
816         m_a = 2;
817         m_b = 1;
818     } else if (argument == "even") {
819         m_a = 2;
820         m_b = 0;
821     } else {
822         size_t n = argument.find('n');
823         if (n != notFound) {
824             if (argument[0] == '-') {
825                 if (n == 1)
826                     m_a = -1; // -n == -1n
827                 else {
828                     bool ok;
829                     m_a = argument.substringSharingImpl(0, n).toIntStrict(&ok);
830                     if (!ok)
831                         return false;
832                 }
833             } else if (!n)
834                 m_a = 1; // n == 1n
835             else {
836                 bool ok;
837                 m_a = argument.substringSharingImpl(0, n).toIntStrict(&ok);
838                 if (!ok)
839                     return false;
840             }
841
842             size_t p = argument.find('+', n);
843             if (p != notFound) {
844                 bool ok;
845                 m_b = argument.substringSharingImpl(p + 1, argument.length() - p - 1).toIntStrict(&ok);
846                 if (!ok)
847                     return false;
848             } else {
849                 p = argument.find('-', n);
850                 if (p != notFound) {
851                     bool ok;
852                     m_b = -argument.substringSharingImpl(p + 1, argument.length() - p - 1).toIntStrict(&ok);
853                     if (!ok)
854                         return false;
855                 }
856             }
857         } else {
858             bool ok;
859             m_b = argument.toIntStrict(&ok);
860             if (!ok)
861                 return false;
862         }
863     }
864     return true;
865 }
866
867 // a helper function for checking nth-arguments
868 bool CSSSelector::RareData::matchNth(int count)
869 {
870     if (!m_a)
871         return count == m_b;
872     else if (m_a > 0) {
873         if (count < m_b)
874             return false;
875         return (count - m_b) % m_a == 0;
876     } else {
877         if (count > m_b)
878             return false;
879         return (m_b - count) % (-m_a) == 0;
880     }
881 }
882
883 } // namespace WebCore