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