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