[CSS Parser] Eliminate in-place lowercasing in the parser.
[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/HashMap.h>
35 #include <wtf/StdLibExtras.h>
36 #include <wtf/Vector.h>
37 #include <wtf/text/AtomicStringHash.h>
38 #include <wtf/text/StringBuilder.h>
39
40 namespace WebCore {
41
42 using namespace HTMLNames;
43
44 struct SameSizeAsCSSSelector {
45     unsigned flags;
46     void* unionPointer;
47 };
48
49 static_assert(CSSSelector::RelationType::Subselector == 0, "Subselector must be 0 for consumeCombinator.");
50 static_assert(sizeof(CSSSelector) == sizeof(SameSizeAsCSSSelector), "CSSSelector should remain small.");
51
52 CSSSelector::CSSSelector(const QualifiedName& tagQName, bool tagIsForNamespaceRule)
53     : m_relation(DescendantSpace)
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     , m_caseInsensitiveAttributeValueMatching(false)
64 #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
65     , m_destructorHasBeenCalled(false)
66 #endif
67 {
68     const AtomicString& tagLocalName = tagQName.localName();
69     const AtomicString tagLocalNameASCIILowercase = tagLocalName.convertToASCIILowercase();
70
71     if (tagLocalName == tagLocalNameASCIILowercase) {
72         m_data.m_tagQName = tagQName.impl();
73         m_data.m_tagQName->ref();
74     } else {
75         m_data.m_nameWithCase = adoptRef(new NameWithCase(tagQName, tagLocalNameASCIILowercase)).leakRef();
76         m_hasNameWithCase = true;
77     }
78 }
79
80 void CSSSelector::createRareData()
81 {
82     ASSERT(match() != Tag);
83     ASSERT(!m_hasNameWithCase);
84     if (m_hasRareData)
85         return;
86     // Move the value to the rare data stucture.
87     AtomicString value { adoptRef(m_data.m_value) };
88     m_data.m_rareData = &RareData::create(WTFMove(value)).leakRef();
89     m_hasRareData = true;
90 }
91
92 static unsigned simpleSelectorSpecificityInternal(const CSSSelector& simpleSelector, bool isComputingMaximumSpecificity);
93
94 static unsigned selectorSpecificity(const CSSSelector& firstSimpleSelector, bool isComputingMaximumSpecificity)
95 {
96     unsigned total = simpleSelectorSpecificityInternal(firstSimpleSelector, isComputingMaximumSpecificity);
97
98     for (const CSSSelector* selector = firstSimpleSelector.tagHistory(); selector; selector = selector->tagHistory())
99         total = CSSSelector::addSpecificities(total, simpleSelectorSpecificityInternal(*selector, isComputingMaximumSpecificity));
100     return total;
101 }
102
103 static unsigned maxSpecificity(const CSSSelectorList& selectorList)
104 {
105     unsigned maxSpecificity = 0;
106     for (const CSSSelector* subSelector = selectorList.first(); subSelector; subSelector = CSSSelectorList::next(subSelector))
107         maxSpecificity = std::max(maxSpecificity, selectorSpecificity(*subSelector, true));
108     return maxSpecificity;
109 }
110
111 static unsigned simpleSelectorSpecificityInternal(const CSSSelector& simpleSelector, bool isComputingMaximumSpecificity)
112 {
113     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.");
114
115     switch (simpleSelector.match()) {
116     case CSSSelector::Id:
117         return static_cast<unsigned>(SelectorSpecificityIncrement::ClassA);
118
119     case CSSSelector::PagePseudoClass:
120         break;
121     case CSSSelector::PseudoClass:
122         if (simpleSelector.pseudoClassType() == CSSSelector::PseudoClassMatches) {
123             ASSERT_WITH_MESSAGE(simpleSelector.selectorList() && simpleSelector.selectorList()->first(), "The parser should never generate a valid selector for an empty :matches().");
124             if (!isComputingMaximumSpecificity)
125                 return 0;
126             return maxSpecificity(*simpleSelector.selectorList());
127         }
128
129         if (simpleSelector.pseudoClassType() == CSSSelector::PseudoClassNot) {
130             ASSERT_WITH_MESSAGE(simpleSelector.selectorList() && simpleSelector.selectorList()->first(), "The parser should never generate a valid selector for an empty :not().");
131             return maxSpecificity(*simpleSelector.selectorList());
132         }
133         FALLTHROUGH;
134     case CSSSelector::Exact:
135     case CSSSelector::Class:
136     case CSSSelector::Set:
137     case CSSSelector::List:
138     case CSSSelector::Hyphen:
139     case CSSSelector::Contain:
140     case CSSSelector::Begin:
141     case CSSSelector::End:
142         return static_cast<unsigned>(SelectorSpecificityIncrement::ClassB);
143     case CSSSelector::Tag:
144         return (simpleSelector.tagQName().localName() != starAtom) ? static_cast<unsigned>(SelectorSpecificityIncrement::ClassC) : 0;
145     case CSSSelector::PseudoElement:
146         return static_cast<unsigned>(SelectorSpecificityIncrement::ClassC);
147     case CSSSelector::Unknown:
148         return 0;
149     }
150     ASSERT_NOT_REACHED();
151     return 0;
152 }
153
154 unsigned CSSSelector::simpleSelectorSpecificity() const
155 {
156     return simpleSelectorSpecificityInternal(*this, false);
157 }
158
159 static unsigned staticSpecificityInternal(const CSSSelector& firstSimpleSelector, bool& ok);
160
161 static unsigned simpleSelectorFunctionalPseudoClassStaticSpecificity(const CSSSelector& simpleSelector, bool& ok)
162 {
163     if (simpleSelector.match() == CSSSelector::PseudoClass) {
164         CSSSelector::PseudoClassType pseudoClassType = simpleSelector.pseudoClassType();
165         if (pseudoClassType == CSSSelector::PseudoClassMatches || pseudoClassType == CSSSelector::PseudoClassNthChild || pseudoClassType == CSSSelector::PseudoClassNthLastChild) {
166             const CSSSelectorList* selectorList = simpleSelector.selectorList();
167             if (!selectorList) {
168                 ASSERT_WITH_MESSAGE(pseudoClassType != CSSSelector::PseudoClassMatches, ":matches() should never be created without a valid selector list.");
169                 return 0;
170             }
171
172             const CSSSelector& firstSubselector = *selectorList->first();
173
174             unsigned initialSpecificity = staticSpecificityInternal(firstSubselector, ok);
175             if (!ok)
176                 return 0;
177
178             const CSSSelector* subselector = &firstSubselector;
179             while ((subselector = CSSSelectorList::next(subselector))) {
180                 unsigned subSelectorSpecificity = staticSpecificityInternal(*subselector, ok);
181                 if (initialSpecificity != subSelectorSpecificity)
182                     ok = false;
183                 if (!ok)
184                     return 0;
185             }
186             return initialSpecificity;
187         }
188     }
189     return 0;
190 }
191
192 static unsigned functionalPseudoClassStaticSpecificity(const CSSSelector& firstSimpleSelector, bool& ok)
193 {
194     unsigned total = 0;
195     for (const CSSSelector* selector = &firstSimpleSelector; selector; selector = selector->tagHistory()) {
196         total = CSSSelector::addSpecificities(total, simpleSelectorFunctionalPseudoClassStaticSpecificity(*selector, ok));
197         if (!ok)
198             return 0;
199     }
200     return total;
201 }
202
203 static unsigned staticSpecificityInternal(const CSSSelector& firstSimpleSelector, bool& ok)
204 {
205     unsigned staticSpecificity = selectorSpecificity(firstSimpleSelector, false);
206     return CSSSelector::addSpecificities(staticSpecificity, functionalPseudoClassStaticSpecificity(firstSimpleSelector, ok));
207 }
208
209 unsigned CSSSelector::staticSpecificity(bool &ok) const
210 {
211     ok = true;
212     return staticSpecificityInternal(*this, ok);
213 }
214
215 unsigned CSSSelector::addSpecificities(unsigned a, unsigned b)
216 {
217     unsigned total = a;
218
219     unsigned newIdValue = (b & idMask);
220     if (((total & idMask) + newIdValue) & ~idMask)
221         total |= idMask;
222     else
223         total += newIdValue;
224
225     unsigned newClassValue = (b & classMask);
226     if (((total & classMask) + newClassValue) & ~classMask)
227         total |= classMask;
228     else
229         total += newClassValue;
230
231     unsigned newElementValue = (b & elementMask);
232     if (((total & elementMask) + newElementValue) & ~elementMask)
233         total |= elementMask;
234     else
235         total += newElementValue;
236
237     return total;
238 }
239
240 unsigned CSSSelector::specificityForPage() const
241 {
242     ASSERT(isForPage());
243
244     // See http://dev.w3.org/csswg/css3-page/#cascading-and-page-context
245     unsigned s = 0;
246
247     for (const CSSSelector* component = this; component; component = component->tagHistory()) {
248         switch (component->match()) {
249         case Tag:
250             s += tagQName().localName() == starAtom ? 0 : 4;
251             break;
252         case PagePseudoClass:
253             switch (component->pagePseudoClassType()) {
254             case PagePseudoClassFirst:
255                 s += 2;
256                 break;
257             case PagePseudoClassLeft:
258             case PagePseudoClassRight:
259                 s += 1;
260                 break;
261             }
262             break;
263         default:
264             break;
265         }
266     }
267     return s;
268 }
269
270 PseudoId CSSSelector::pseudoId(PseudoElementType type)
271 {
272     switch (type) {
273     case PseudoElementFirstLine:
274         return FIRST_LINE;
275     case PseudoElementFirstLetter:
276         return FIRST_LETTER;
277     case PseudoElementSelection:
278         return SELECTION;
279     case PseudoElementBefore:
280         return BEFORE;
281     case PseudoElementAfter:
282         return AFTER;
283     case PseudoElementScrollbar:
284         return SCROLLBAR;
285     case PseudoElementScrollbarButton:
286         return SCROLLBAR_BUTTON;
287     case PseudoElementScrollbarCorner:
288         return SCROLLBAR_CORNER;
289     case PseudoElementScrollbarThumb:
290         return SCROLLBAR_THUMB;
291     case PseudoElementScrollbarTrack:
292         return SCROLLBAR_TRACK;
293     case PseudoElementScrollbarTrackPiece:
294         return SCROLLBAR_TRACK_PIECE;
295     case PseudoElementResizer:
296         return RESIZER;
297 #if ENABLE(VIDEO_TRACK)
298     case PseudoElementCue:
299 #endif
300     case PseudoElementSlotted:
301     case PseudoElementUnknown:
302     case PseudoElementUserAgentCustom:
303     case PseudoElementWebKitCustom:
304     case PseudoElementWebKitCustomLegacyPrefixed:
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->serializingValue(), str);
411         } else if (cs->match() == CSSSelector::Class) {
412             str.append('.');
413             serializeIdentifier(cs->serializingValue(), 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             case CSSSelector::PseudoClassFocusWithin:
499                 str.appendLiteral(":focus-within");
500                 break;
501 #if ENABLE(VIDEO_TRACK)
502             case CSSSelector::PseudoClassFuture:
503                 str.appendLiteral(":future");
504                 break;
505 #endif
506             case CSSSelector::PseudoClassHorizontal:
507                 str.appendLiteral(":horizontal");
508                 break;
509             case CSSSelector::PseudoClassHover:
510                 str.appendLiteral(":hover");
511                 break;
512             case CSSSelector::PseudoClassInRange:
513                 str.appendLiteral(":in-range");
514                 break;
515             case CSSSelector::PseudoClassIncrement:
516                 str.appendLiteral(":increment");
517                 break;
518             case CSSSelector::PseudoClassIndeterminate:
519                 str.appendLiteral(":indeterminate");
520                 break;
521             case CSSSelector::PseudoClassInvalid:
522                 str.appendLiteral(":invalid");
523                 break;
524             case CSSSelector::PseudoClassLang:
525                 str.appendLiteral(":lang(");
526                 ASSERT_WITH_MESSAGE(cs->langArgumentList() && !cs->langArgumentList()->isEmpty(), "An empty :lang() is invalid and should never be generated by the parser.");
527                 appendLangArgumentList(str, *cs->langArgumentList());
528                 str.append(')');
529                 break;
530             case CSSSelector::PseudoClassLastChild:
531                 str.appendLiteral(":last-child");
532                 break;
533             case CSSSelector::PseudoClassLastOfType:
534                 str.appendLiteral(":last-of-type");
535                 break;
536             case CSSSelector::PseudoClassLink:
537                 str.appendLiteral(":link");
538                 break;
539             case CSSSelector::PseudoClassNoButton:
540                 str.appendLiteral(":no-button");
541                 break;
542             case CSSSelector::PseudoClassNot:
543                 str.appendLiteral(":not(");
544                 cs->selectorList()->buildSelectorsText(str);
545                 str.append(')');
546                 break;
547             case CSSSelector::PseudoClassNthChild:
548                 str.appendLiteral(":nth-child(");
549                 str.append(cs->argument());
550                 if (const CSSSelectorList* selectorList = cs->selectorList()) {
551                     str.appendLiteral(" of ");
552                     selectorList->buildSelectorsText(str);
553                 }
554                 str.append(')');
555                 break;
556             case CSSSelector::PseudoClassNthLastChild:
557                 str.appendLiteral(":nth-last-child(");
558                 str.append(cs->argument());
559                 if (const CSSSelectorList* selectorList = cs->selectorList()) {
560                     str.appendLiteral(" of ");
561                     selectorList->buildSelectorsText(str);
562                 }
563                 str.append(')');
564                 break;
565             case CSSSelector::PseudoClassNthLastOfType:
566                 str.appendLiteral(":nth-last-of-type(");
567                 appendPseudoClassFunctionTail(str, cs);
568                 break;
569             case CSSSelector::PseudoClassNthOfType:
570                 str.appendLiteral(":nth-of-type(");
571                 appendPseudoClassFunctionTail(str, cs);
572                 break;
573             case CSSSelector::PseudoClassOnlyChild:
574                 str.appendLiteral(":only-child");
575                 break;
576             case CSSSelector::PseudoClassOnlyOfType:
577                 str.appendLiteral(":only-of-type");
578                 break;
579             case CSSSelector::PseudoClassOptional:
580                 str.appendLiteral(":optional");
581                 break;
582             case CSSSelector::PseudoClassMatches: {
583                 str.appendLiteral(":matches(");
584                 cs->selectorList()->buildSelectorsText(str);
585                 str.append(')');
586                 break;
587             }
588             case CSSSelector::PseudoClassPlaceholderShown:
589                 str.appendLiteral(":placeholder-shown");
590                 break;
591             case CSSSelector::PseudoClassOutOfRange:
592                 str.appendLiteral(":out-of-range");
593                 break;
594 #if ENABLE(VIDEO_TRACK)
595             case CSSSelector::PseudoClassPast:
596                 str.appendLiteral(":past");
597                 break;
598 #endif
599             case CSSSelector::PseudoClassReadOnly:
600                 str.appendLiteral(":read-only");
601                 break;
602             case CSSSelector::PseudoClassReadWrite:
603                 str.appendLiteral(":read-write");
604                 break;
605             case CSSSelector::PseudoClassRequired:
606                 str.appendLiteral(":required");
607                 break;
608 #if ENABLE(CSS_SELECTORS_LEVEL4)
609             case CSSSelector::PseudoClassRole:
610                 str.appendLiteral(":role(");
611                 appendPseudoClassFunctionTail(str, cs);
612                 break;
613 #endif
614             case CSSSelector::PseudoClassRoot:
615                 str.appendLiteral(":root");
616                 break;
617             case CSSSelector::PseudoClassScope:
618                 str.appendLiteral(":scope");
619                 break;
620             case CSSSelector::PseudoClassSingleButton:
621                 str.appendLiteral(":single-button");
622                 break;
623             case CSSSelector::PseudoClassStart:
624                 str.appendLiteral(":start");
625                 break;
626             case CSSSelector::PseudoClassTarget:
627                 str.appendLiteral(":target");
628                 break;
629             case CSSSelector::PseudoClassValid:
630                 str.appendLiteral(":valid");
631                 break;
632             case CSSSelector::PseudoClassVertical:
633                 str.appendLiteral(":vertical");
634                 break;
635             case CSSSelector::PseudoClassVisited:
636                 str.appendLiteral(":visited");
637                 break;
638             case CSSSelector::PseudoClassWindowInactive:
639                 str.appendLiteral(":window-inactive");
640                 break;
641             case CSSSelector::PseudoClassHost:
642                 str.appendLiteral(":host");
643                 break;
644             case CSSSelector::PseudoClassDefined:
645                 str.appendLiteral(":defined");
646                 break;
647             case CSSSelector::PseudoClassUnknown:
648                 ASSERT_NOT_REACHED();
649             }
650         } else if (cs->match() == CSSSelector::PseudoElement) {
651             switch (cs->pseudoElementType()) {
652             case CSSSelector::PseudoElementSlotted:
653                 str.appendLiteral("::slotted(");
654                 cs->selectorList()->buildSelectorsText(str);
655                 str.append(')');
656                 break;
657             case CSSSelector::PseudoElementWebKitCustomLegacyPrefixed:
658                 if (cs->value() == "placeholder")
659                     str.appendLiteral("::-webkit-input-placeholder");
660                 break;
661             default:
662                 str.appendLiteral("::");
663                 str.append(cs->serializingValue());
664             }
665         } else if (cs->isAttributeSelector()) {
666             str.append('[');
667             const AtomicString& prefix = cs->attribute().prefix();
668             if (!prefix.isEmpty()) {
669                 str.append(prefix);
670                 str.append('|');
671             }
672             str.append(cs->attribute().localName());
673             switch (cs->match()) {
674                 case CSSSelector::Exact:
675                     str.append('=');
676                     break;
677                 case CSSSelector::Set:
678                     // set has no operator or value, just the attrName
679                     str.append(']');
680                     break;
681                 case CSSSelector::List:
682                     str.appendLiteral("~=");
683                     break;
684                 case CSSSelector::Hyphen:
685                     str.appendLiteral("|=");
686                     break;
687                 case CSSSelector::Begin:
688                     str.appendLiteral("^=");
689                     break;
690                 case CSSSelector::End:
691                     str.appendLiteral("$=");
692                     break;
693                 case CSSSelector::Contain:
694                     str.appendLiteral("*=");
695                     break;
696                 default:
697                     break;
698             }
699             if (cs->match() != CSSSelector::Set) {
700                 serializeString(cs->serializingValue(), str);
701                 if (cs->attributeValueMatchingIsCaseInsensitive())
702                     str.appendLiteral(" i]");
703                 else
704                     str.append(']');
705             }
706         } else if (cs->match() == CSSSelector::PagePseudoClass) {
707             switch (cs->pagePseudoClassType()) {
708             case PagePseudoClassFirst:
709                 str.appendLiteral(":first");
710                 break;
711             case PagePseudoClassLeft:
712                 str.appendLiteral(":left");
713                 break;
714             case PagePseudoClassRight:
715                 str.appendLiteral(":right");
716                 break;
717             }
718         }
719
720         if (cs->relation() != CSSSelector::Subselector || !cs->tagHistory())
721             break;
722         cs = cs->tagHistory();
723     }
724
725     if (const CSSSelector* tagHistory = cs->tagHistory()) {
726         switch (cs->relation()) {
727         case CSSSelector::DescendantSpace:
728             return tagHistory->selectorText(" " + str.toString() + rightSide);
729         case CSSSelector::Child:
730             return tagHistory->selectorText(" > " + str.toString() + rightSide);
731         case CSSSelector::DirectAdjacent:
732             return tagHistory->selectorText(" + " + str.toString() + rightSide);
733         case CSSSelector::IndirectAdjacent:
734             return tagHistory->selectorText(" ~ " + str.toString() + rightSide);
735 #if ENABLE(CSS_SELECTORS_LEVEL4)
736         case CSSSelector::DescendantDoubleChild:
737             return tagHistory->selectorText(" >> " + str.toString() + rightSide);
738 #endif
739         case CSSSelector::Subselector:
740             ASSERT_NOT_REACHED();
741 #if ASSERT_DISABLED
742             FALLTHROUGH;
743 #endif
744         case CSSSelector::ShadowDescendant:
745             return tagHistory->selectorText(str.toString() + rightSide);
746         }
747     }
748     return str.toString() + rightSide;
749 }
750
751 void CSSSelector::setAttribute(const QualifiedName& value, bool isCaseInsensitive)
752 {
753     createRareData();
754     m_data.m_rareData->m_attribute = value;
755     m_data.m_rareData->m_attributeCanonicalLocalName = isCaseInsensitive ? value.localName().convertToASCIILowercase() : value.localName();
756 }
757
758 void CSSSelector::setAttribute(const QualifiedName& value, bool convertToLowercase, AttributeMatchType matchType)
759 {
760     createRareData();
761     m_data.m_rareData->m_attribute = value;
762     m_data.m_rareData->m_attributeCanonicalLocalName = convertToLowercase ? value.localName().convertToASCIILowercase() : value.localName();
763     m_caseInsensitiveAttributeValueMatching = matchType == CaseInsensitive;
764 }
765     
766 void CSSSelector::setArgument(const AtomicString& value)
767 {
768     createRareData();
769     m_data.m_rareData->m_argument = value;
770 }
771
772 void CSSSelector::setLangArgumentList(std::unique_ptr<Vector<AtomicString>> argumentList)
773 {
774     createRareData();
775     m_data.m_rareData->m_langArgumentList = WTFMove(argumentList);
776 }
777
778 void CSSSelector::setSelectorList(std::unique_ptr<CSSSelectorList> selectorList)
779 {
780     createRareData();
781     m_data.m_rareData->m_selectorList = WTFMove(selectorList);
782 }
783
784 void CSSSelector::setNth(int a, int b)
785 {
786     createRareData();
787     m_parsedNth = true; // FIXME-NEWPARSER: Can remove this parsed boolean once old parser is gone.
788     m_data.m_rareData->m_a = a;
789     m_data.m_rareData->m_b = b;
790 }
791     
792 // FIXME-NEWPARSER: All the code to parse nth-child stuff can be removed when
793 // the new parser is enabled.
794 bool CSSSelector::parseNth() const
795 {
796     if (!m_hasRareData)
797         return false;
798     if (m_parsedNth)
799         return true;
800     m_parsedNth = m_data.m_rareData->parseNth();
801     return m_parsedNth;
802 }
803
804 bool CSSSelector::matchNth(int count) const
805 {
806     ASSERT(m_hasRareData);
807     return m_data.m_rareData->matchNth(count);
808 }
809
810 int CSSSelector::nthA() const
811 {
812     ASSERT(m_hasRareData);
813     ASSERT(m_parsedNth);
814     return m_data.m_rareData->m_a;
815 }
816
817 int CSSSelector::nthB() const
818 {
819     ASSERT(m_hasRareData);
820     ASSERT(m_parsedNth);
821     return m_data.m_rareData->m_b;
822 }
823
824 CSSSelector::RareData::RareData(AtomicString&& value)
825     : m_matchingValue(value)
826     , m_serializingValue(value)
827     , m_a(0)
828     , m_b(0)
829     , m_attribute(anyQName())
830     , m_argument(nullAtom)
831 {
832 }
833
834 CSSSelector::RareData::~RareData()
835 {
836 }
837
838 // a helper function for parsing nth-arguments
839 bool CSSSelector::RareData::parseNth()
840 {
841     if (m_argument.isEmpty())
842         return false;
843
844     if (equalLettersIgnoringASCIICase(m_argument, "odd")) {
845         m_a = 2;
846         m_b = 1;
847     } else if (equalLettersIgnoringASCIICase(m_argument, "even")) {
848         m_a = 2;
849         m_b = 0;
850     } else {
851         m_a = 0;
852         m_b = 0;
853
854         size_t n = std::min(m_argument.find('n'), m_argument.find('N'));
855         if (n != notFound) {
856             if (m_argument[0] == '-') {
857                 if (n == 1)
858                     m_a = -1; // -n == -1n
859                 else {
860                     bool ok;
861                     m_a = StringView(m_argument).substring(0, n).toIntStrict(ok);
862                     if (!ok)
863                         return false;
864                 }
865             } else if (!n)
866                 m_a = 1; // n == 1n
867             else {
868                 bool ok;
869                 m_a = StringView(m_argument).substring(0, n).toIntStrict(ok);
870                 if (!ok)
871                     return false;
872             }
873
874             size_t p = m_argument.find('+', n);
875             if (p != notFound) {
876                 bool ok;
877                 m_b = StringView(m_argument).substring(p + 1).toIntStrict(ok);
878                 if (!ok)
879                     return false;
880             } else {
881                 p = m_argument.find('-', n);
882                 if (p != notFound) {
883                     bool ok;
884                     m_b = -StringView(m_argument).substring(p + 1).toIntStrict(ok);
885                     if (!ok)
886                         return false;
887                 }
888             }
889         } else {
890             bool ok;
891             m_b = m_argument.string().toIntStrict(&ok);
892             if (!ok)
893                 return false;
894         }
895     }
896     return true;
897 }
898
899 // a helper function for checking nth-arguments
900 bool CSSSelector::RareData::matchNth(int count)
901 {
902     if (!m_a)
903         return count == m_b;
904     else if (m_a > 0) {
905         if (count < m_b)
906             return false;
907         return (count - m_b) % m_a == 0;
908     } else {
909         if (count > m_b)
910             return false;
911         return (m_b - count) % (-m_a) == 0;
912     }
913 }
914
915 } // namespace WebCore