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