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