90730cc679baaa56d9df5e657b1683d23e3032e7
[WebKit-https.git] / Source / WebCore / css / parser / CSSSelectorParser.cpp
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Copyright (C) 2016 Apple Inc. All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //    * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //    * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //    * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "config.h"
31 #include "CSSSelectorParser.h"
32
33 #include "CSSParserMode.h"
34 #include "CSSSelectorList.h"
35 #include "StyleSheetContents.h"
36 #include <memory>
37
38 namespace WebCore {
39
40 CSSSelectorList CSSSelectorParser::parseSelector(CSSParserTokenRange range, const CSSParserContext& context, StyleSheetContents* styleSheet)
41 {
42     CSSSelectorParser parser(context, styleSheet);
43     range.consumeWhitespace();
44     CSSSelectorList result = parser.consumeComplexSelectorList(range);
45     if (!range.atEnd())
46         return CSSSelectorList();
47     return result;
48 }
49
50 CSSSelectorParser::CSSSelectorParser(const CSSParserContext& context, StyleSheetContents* styleSheet)
51     : m_context(context)
52     , m_styleSheet(styleSheet)
53 {
54 }
55
56 CSSSelectorList CSSSelectorParser::consumeComplexSelectorList(CSSParserTokenRange& range)
57 {
58     Vector<std::unique_ptr<CSSParserSelector>> selectorList;
59     std::unique_ptr<CSSParserSelector> selector = consumeComplexSelector(range);
60     if (!selector)
61         return CSSSelectorList();
62     selectorList.append(WTFMove(selector));
63     while (!range.atEnd() && range.peek().type() == CommaToken) {
64         range.consumeIncludingWhitespace();
65         selector = consumeComplexSelector(range);
66         if (!selector)
67             return CSSSelectorList();
68         selectorList.append(WTFMove(selector));
69     }
70
71     CSSSelectorList list;
72     if (m_failedParsing)
73         return list;
74     list.adoptSelectorVector(selectorList);
75     return list;
76 }
77
78 CSSSelectorList CSSSelectorParser::consumeCompoundSelectorList(CSSParserTokenRange& range)
79 {
80     Vector<std::unique_ptr<CSSParserSelector>> selectorList;
81     std::unique_ptr<CSSParserSelector> selector = consumeCompoundSelector(range);
82     range.consumeWhitespace();
83     if (!selector)
84         return CSSSelectorList();
85     selectorList.append(WTFMove(selector));
86     while (!range.atEnd() && range.peek().type() == CommaToken) {
87         range.consumeIncludingWhitespace();
88         selector = consumeCompoundSelector(range);
89         range.consumeWhitespace();
90         if (!selector)
91             return CSSSelectorList();
92         selectorList.append(WTFMove(selector));
93     }
94
95     CSSSelectorList list;
96     if (m_failedParsing)
97         return list;
98     list.adoptSelectorVector(selectorList);
99     return list;
100 }
101
102 static bool consumeLangArgumentList(std::unique_ptr<Vector<AtomicString>>& argumentList, CSSParserTokenRange& range)
103 {
104     const CSSParserToken& ident = range.consumeIncludingWhitespace();
105     if (ident.type() != IdentToken && ident.type() != StringToken)
106         return false;
107     StringView string = ident.value();
108     if (string.startsWith("--"))
109         return false;
110     argumentList->append(string.toAtomicString());
111     while (!range.atEnd() && range.peek().type() == CommaToken) {
112         range.consumeIncludingWhitespace();
113         const CSSParserToken& ident = range.consumeIncludingWhitespace();
114         if (ident.type() != IdentToken && ident.type() != StringToken)
115             return false;
116         StringView string = ident.value();
117         if (string.startsWith("--"))
118             return false;
119         argumentList->append(string.toAtomicString());
120     }
121     return range.atEnd();
122 }
123
124 namespace {
125
126 enum CompoundSelectorFlags {
127     HasPseudoElementForRightmostCompound = 1 << 0,
128     HasContentPseudoElement = 1 << 1
129 };
130
131 unsigned extractCompoundFlags(const CSSParserSelector& simpleSelector, CSSParserMode parserMode)
132 {
133     if (simpleSelector.match() != CSSSelector::PseudoElement)
134         return 0;
135     
136     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=161747
137     // The UASheetMode check is a work-around to allow this selector in mediaControls(New).css:
138     // input[type="range" i]::-webkit-media-slider-container > div {
139     if (parserMode == UASheetMode && simpleSelector.pseudoElementType() == CSSSelector::PseudoElementWebKitCustom)
140         return 0;
141     return HasPseudoElementForRightmostCompound;
142 }
143
144 } // namespace
145
146 static bool isDescendantCombinator(CSSSelector::RelationType relation)
147 {
148 #if ENABLE(CSS_SELECTORS_LEVEL4)
149     return relation == CSSSelector::DescendantSpace || relation == CSSSelector::DescendantDoubleChild;
150 #else
151     return relation == CSSSelector::DescendantSpace;
152 #endif
153 }
154 std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeComplexSelector(CSSParserTokenRange& range)
155 {
156     std::unique_ptr<CSSParserSelector> selector = consumeCompoundSelector(range);
157     if (!selector)
158         return nullptr;
159
160     unsigned previousCompoundFlags = 0;
161
162     for (CSSParserSelector* simple = selector.get(); simple && !previousCompoundFlags; simple = simple->tagHistory())
163         previousCompoundFlags |= extractCompoundFlags(*simple, m_context.mode);
164
165     while (auto combinator = consumeCombinator(range)) {
166         std::unique_ptr<CSSParserSelector> nextSelector = consumeCompoundSelector(range);
167         if (!nextSelector)
168             return isDescendantCombinator(combinator) ? WTFMove(selector) : nullptr;
169         if (previousCompoundFlags & HasPseudoElementForRightmostCompound)
170             return nullptr;
171         CSSParserSelector* end = nextSelector.get();
172         unsigned compoundFlags = extractCompoundFlags(*end, m_context.mode);
173         while (end->tagHistory()) {
174             end = end->tagHistory();
175             compoundFlags |= extractCompoundFlags(*end, m_context.mode);
176         }
177         end->setRelation(combinator);
178         previousCompoundFlags = compoundFlags;
179         end->setTagHistory(WTFMove(selector));
180
181         selector = WTFMove(nextSelector);
182     }
183
184     return selector;
185 }
186
187 namespace {
188
189 bool isScrollbarPseudoClass(CSSSelector::PseudoClassType pseudo)
190 {
191     switch (pseudo) {
192     case CSSSelector::PseudoClassEnabled:
193     case CSSSelector::PseudoClassDisabled:
194     case CSSSelector::PseudoClassHover:
195     case CSSSelector::PseudoClassActive:
196     case CSSSelector::PseudoClassHorizontal:
197     case CSSSelector::PseudoClassVertical:
198     case CSSSelector::PseudoClassDecrement:
199     case CSSSelector::PseudoClassIncrement:
200     case CSSSelector::PseudoClassStart:
201     case CSSSelector::PseudoClassEnd:
202     case CSSSelector::PseudoClassDoubleButton:
203     case CSSSelector::PseudoClassSingleButton:
204     case CSSSelector::PseudoClassNoButton:
205     case CSSSelector::PseudoClassCornerPresent:
206     case CSSSelector::PseudoClassWindowInactive:
207         return true;
208     default:
209         return false;
210     }
211 }
212
213 bool isUserActionPseudoClass(CSSSelector::PseudoClassType pseudo)
214 {
215     switch (pseudo) {
216     case CSSSelector::PseudoClassHover:
217     case CSSSelector::PseudoClassFocus:
218     case CSSSelector::PseudoClassActive:
219         return true;
220     default:
221         return false;
222     }
223 }
224
225 bool isPseudoClassValidAfterPseudoElement(CSSSelector::PseudoClassType pseudoClass, CSSSelector::PseudoElementType compoundPseudoElement)
226 {
227     switch (compoundPseudoElement) {
228     case CSSSelector::PseudoElementResizer:
229     case CSSSelector::PseudoElementScrollbar:
230     case CSSSelector::PseudoElementScrollbarCorner:
231     case CSSSelector::PseudoElementScrollbarButton:
232     case CSSSelector::PseudoElementScrollbarThumb:
233     case CSSSelector::PseudoElementScrollbarTrack:
234     case CSSSelector::PseudoElementScrollbarTrackPiece:
235         return isScrollbarPseudoClass(pseudoClass);
236     case CSSSelector::PseudoElementSelection:
237         return pseudoClass == CSSSelector::PseudoClassWindowInactive;
238     case CSSSelector::PseudoElementWebKitCustom:
239     case CSSSelector::PseudoElementWebKitCustomLegacyPrefixed:
240         return isUserActionPseudoClass(pseudoClass);
241     default:
242         return false;
243     }
244 }
245
246 bool isSimpleSelectorValidAfterPseudoElement(const CSSParserSelector& simpleSelector, CSSSelector::PseudoElementType compoundPseudoElement)
247 {
248     if (compoundPseudoElement == CSSSelector::PseudoElementUnknown)
249         return true;
250     // FIXME-NEWPARSER: This doesn't exist for us.
251     // if (compoundPseudoElement == CSSSelector::PseudoElementContent)
252     //    return simpleSelector.match() != CSSSelector::PseudoElement;
253     if (simpleSelector.match() != CSSSelector::PseudoClass)
254         return false;
255     CSSSelector::PseudoClassType pseudo = simpleSelector.pseudoClassType();
256     if (pseudo == CSSSelector::PseudoClassNot) {
257         ASSERT(simpleSelector.selectorList());
258         ASSERT(simpleSelector.selectorList()->first());
259         pseudo = simpleSelector.selectorList()->first()->pseudoClassType();
260     }
261     return isPseudoClassValidAfterPseudoElement(pseudo, compoundPseudoElement);
262 }
263
264 } // namespace
265
266 std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeCompoundSelector(CSSParserTokenRange& range)
267 {
268     std::unique_ptr<CSSParserSelector> compoundSelector;
269
270     AtomicString namespacePrefix;
271     AtomicString elementName;
272     CSSSelector::PseudoElementType compoundPseudoElement = CSSSelector::PseudoElementUnknown;
273     if (!consumeName(range, elementName, namespacePrefix)) {
274         compoundSelector = consumeSimpleSelector(range);
275         if (!compoundSelector)
276             return nullptr;
277         if (compoundSelector->match() == CSSSelector::PseudoElement)
278             compoundPseudoElement = compoundSelector->pseudoElementType();
279     }
280
281     while (std::unique_ptr<CSSParserSelector> simpleSelector = consumeSimpleSelector(range)) {
282         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=161747
283         // The UASheetMode check is a work-around to allow this selector in mediaControls(New).css:
284         // video::-webkit-media-text-track-region-container.scrolling
285         if (m_context.mode != UASheetMode && !isSimpleSelectorValidAfterPseudoElement(*simpleSelector.get(), compoundPseudoElement)) {
286             m_failedParsing = true;
287             return nullptr;
288         }
289         if (simpleSelector->match() == CSSSelector::PseudoElement)
290             compoundPseudoElement = simpleSelector->pseudoElementType();
291
292         if (compoundSelector)
293             compoundSelector = addSimpleSelectorToCompound(WTFMove(compoundSelector), WTFMove(simpleSelector));
294         else
295             compoundSelector = WTFMove(simpleSelector);
296     }
297
298     if (!compoundSelector) {
299         AtomicString namespaceURI = determineNamespace(namespacePrefix);
300         if (namespaceURI.isNull()) {
301             m_failedParsing = true;
302             return nullptr;
303         }
304         if (namespaceURI == defaultNamespace())
305             namespacePrefix = nullAtom;
306         
307         CSSParserSelector* rawSelector = new CSSParserSelector(QualifiedName(namespacePrefix, elementName, namespaceURI));
308         std::unique_ptr<CSSParserSelector> selector = std::unique_ptr<CSSParserSelector>(rawSelector);
309         return selector;
310     }
311     prependTypeSelectorIfNeeded(namespacePrefix, elementName, compoundSelector.get());
312     return splitCompoundAtImplicitShadowCrossingCombinator(WTFMove(compoundSelector), m_context);
313 }
314
315 std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeSimpleSelector(CSSParserTokenRange& range)
316 {
317     const CSSParserToken& token = range.peek();
318     std::unique_ptr<CSSParserSelector> selector;
319     if (token.type() == HashToken)
320         selector = consumeId(range);
321     else if (token.type() == DelimiterToken && token.delimiter() == '.')
322         selector = consumeClass(range);
323     else if (token.type() == LeftBracketToken)
324         selector = consumeAttribute(range);
325     else if (token.type() == ColonToken)
326         selector = consumePseudo(range);
327     else
328         return nullptr;
329     if (!selector)
330         m_failedParsing = true;
331     return selector;
332 }
333
334 bool CSSSelectorParser::consumeName(CSSParserTokenRange& range, AtomicString& name, AtomicString& namespacePrefix)
335 {
336     name = nullAtom;
337     namespacePrefix = nullAtom;
338
339     const CSSParserToken& firstToken = range.peek();
340     if (firstToken.type() == IdentToken) {
341         name = firstToken.value().toAtomicString();
342         range.consume();
343     } else if (firstToken.type() == DelimiterToken && firstToken.delimiter() == '*') {
344         name = starAtom;
345         range.consume();
346     } else if (firstToken.type() == DelimiterToken && firstToken.delimiter() == '|') {
347         // This is an empty namespace, which'll get assigned this value below
348         name = emptyAtom;
349     } else
350         return false;
351
352     if (range.peek().type() != DelimiterToken || range.peek().delimiter() != '|')
353         return true;
354     range.consume();
355
356     namespacePrefix = name;
357     const CSSParserToken& nameToken = range.consume();
358     if (nameToken.type() == IdentToken) {
359         name = nameToken.value().toAtomicString();
360     } else if (nameToken.type() == DelimiterToken && nameToken.delimiter() == '*')
361         name = starAtom;
362     else {
363         name = nullAtom;
364         namespacePrefix = nullAtom;
365         return false;
366     }
367
368     return true;
369 }
370
371 std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeId(CSSParserTokenRange& range)
372 {
373     ASSERT(range.peek().type() == HashToken);
374     if (range.peek().getHashTokenType() != HashTokenId)
375         return nullptr;
376     std::unique_ptr<CSSParserSelector> selector = std::unique_ptr<CSSParserSelector>(new CSSParserSelector());
377     selector->setMatch(CSSSelector::Id);
378     
379     // FIXME-NEWPARSER: Avoid having to do this, but the old parser does and we need
380     // to be compatible for now.
381     CSSParserToken token = range.consume();
382     selector->setValue(token.value().toAtomicString(), m_context.mode == HTMLQuirksMode);
383     return selector;
384 }
385
386 std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeClass(CSSParserTokenRange& range)
387 {
388     ASSERT(range.peek().type() == DelimiterToken);
389     ASSERT(range.peek().delimiter() == '.');
390     range.consume();
391     if (range.peek().type() != IdentToken)
392         return nullptr;
393     std::unique_ptr<CSSParserSelector> selector = std::unique_ptr<CSSParserSelector>(new CSSParserSelector());
394     selector->setMatch(CSSSelector::Class);
395     
396     // FIXME-NEWPARSER: Avoid having to do this, but the old parser does and we need
397     // to be compatible for now.
398     CSSParserToken token = range.consume();
399     selector->setValue(token.value().toAtomicString(), m_context.mode == HTMLQuirksMode);
400
401     return selector;
402 }
403
404 std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeAttribute(CSSParserTokenRange& range)
405 {
406     ASSERT(range.peek().type() == LeftBracketToken);
407     CSSParserTokenRange block = range.consumeBlock();
408     if (block.end() == range.end())
409         return nullptr; // No ] was found. Be strict about this.
410
411     block.consumeWhitespace();
412
413     AtomicString namespacePrefix;
414     AtomicString attributeName;
415     if (!consumeName(block, attributeName, namespacePrefix))
416         return nullptr;
417     block.consumeWhitespace();
418
419     AtomicString namespaceURI = determineNamespace(namespacePrefix);
420     if (namespaceURI.isNull())
421         return nullptr;
422
423     QualifiedName qualifiedName = namespacePrefix.isNull()
424         ? QualifiedName(nullAtom, attributeName, nullAtom)
425         : QualifiedName(namespacePrefix, attributeName, namespaceURI);
426
427     std::unique_ptr<CSSParserSelector> selector = std::unique_ptr<CSSParserSelector>(new CSSParserSelector());
428
429     if (block.atEnd()) {
430         selector->setAttribute(qualifiedName, m_context.isHTMLDocument, CSSSelector::CaseSensitive);
431         selector->setMatch(CSSSelector::Set);
432         return selector;
433     }
434
435     selector->setMatch(consumeAttributeMatch(block));
436
437     const CSSParserToken& attributeValue = block.consumeIncludingWhitespace();
438     if (attributeValue.type() != IdentToken && attributeValue.type() != StringToken)
439         return nullptr;
440     selector->setValue(attributeValue.value().toAtomicString());
441     
442     selector->setAttribute(qualifiedName, m_context.isHTMLDocument, consumeAttributeFlags(block));
443
444     if (!block.atEnd())
445         return nullptr;
446     return selector;
447 }
448
449 static bool isOnlyPseudoClassFunction(CSSSelector::PseudoClassType pseudoClassType)
450 {
451     switch (pseudoClassType) {
452     case CSSSelector::PseudoClassNot:
453     case CSSSelector::PseudoClassMatches:
454     case CSSSelector::PseudoClassNthChild:
455     case CSSSelector::PseudoClassNthLastChild:
456     case CSSSelector::PseudoClassNthOfType:
457     case CSSSelector::PseudoClassNthLastOfType:
458     case CSSSelector::PseudoClassLang:
459     case CSSSelector::PseudoClassAny:
460 #if ENABLE(CSS_SELECTORS_LEVEL4)
461     case CSSSelector::PseudoClassDir:
462     case CSSSelector::PseudoClassRole:
463 #endif
464         return true;
465     default:
466         break;
467     }
468     return false;
469 }
470     
471 static bool isOnlyPseudoElementFunction(CSSSelector::PseudoElementType pseudoElementType)
472 {
473     // Note that we omit cue since it can be both an ident or a function.
474     switch (pseudoElementType) {
475     case CSSSelector::PseudoElementSlotted:
476         return true;
477     default:
478         break;
479     }
480     return false;
481 }
482
483 std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumePseudo(CSSParserTokenRange& range)
484 {
485     ASSERT(range.peek().type() == ColonToken);
486     range.consume();
487
488     int colons = 1;
489     if (range.peek().type() == ColonToken) {
490         range.consume();
491         colons++;
492     }
493
494     const CSSParserToken& token = range.peek();
495     if (token.type() != IdentToken && token.type() != FunctionToken)
496         return nullptr;
497
498     std::unique_ptr<CSSParserSelector> selector;
499     
500     auto lowercasedValue = token.value().toString().convertToASCIILowercase();
501     auto value = StringView { lowercasedValue };
502
503     if (colons == 1)
504         selector = std::unique_ptr<CSSParserSelector>(CSSParserSelector::parsePseudoClassSelectorFromStringView(value));
505     else {
506         selector = std::unique_ptr<CSSParserSelector>(CSSParserSelector::parsePseudoElementSelectorFromStringView(value));
507 #if ENABLE(VIDEO_TRACK)
508         // Treat the ident version of cue as PseudoElementWebkitCustom.
509         if (token.type() == IdentToken && selector && selector->match() == CSSSelector::PseudoElement && selector->pseudoElementType() == CSSSelector::PseudoElementCue)
510             selector->setPseudoElementType(CSSSelector::PseudoElementWebKitCustom);
511 #endif
512     }
513
514     if (!selector || (selector->match() == CSSSelector::PseudoElement && m_disallowPseudoElements))
515         return nullptr;
516
517     if (token.type() == IdentToken) {
518         range.consume();
519         if ((selector->match() == CSSSelector::PseudoElement && (selector->pseudoElementType() == CSSSelector::PseudoElementUnknown || isOnlyPseudoElementFunction(selector->pseudoElementType())))
520             || (selector->match() == CSSSelector::PseudoClass && (selector->pseudoClassType() == CSSSelector::PseudoClassUnknown || isOnlyPseudoClassFunction(selector->pseudoClassType()))))
521             return nullptr;
522         return selector;
523     }
524
525     CSSParserTokenRange block = range.consumeBlock();
526     if (block.end() == range.end())
527         return nullptr; // No ) was found. Be strict about this.
528     block.consumeWhitespace();
529     if (token.type() != FunctionToken)
530         return nullptr;
531     
532     const auto& argumentStart = block.peek();
533     
534     if (selector->match() == CSSSelector::PseudoClass) {
535         switch (selector->pseudoClassType()) {
536         case CSSSelector::PseudoClassNot: {
537             DisallowPseudoElementsScope scope(this);
538             std::unique_ptr<CSSSelectorList> selectorList = std::unique_ptr<CSSSelectorList>(new CSSSelectorList());
539             *selectorList = consumeComplexSelectorList(block);
540             if (!selectorList->componentCount() || !block.atEnd())
541                 return nullptr;
542             selector->setSelectorList(WTFMove(selectorList));
543             return selector;
544         }
545         case CSSSelector::PseudoClassNthChild:
546         case CSSSelector::PseudoClassNthLastChild:
547         case CSSSelector::PseudoClassNthOfType:
548         case CSSSelector::PseudoClassNthLastOfType: {
549             std::pair<int, int> ab;
550             if (!consumeANPlusB(block, ab))
551                 return nullptr;
552             block.consumeWhitespace();
553             const auto& argumentEnd = block.peek();
554             auto rangeOfANPlusB = block.makeSubRange(&argumentStart, &argumentEnd);
555             auto argument = rangeOfANPlusB.serialize();
556             selector->setArgument(argument.stripWhiteSpace());
557             if (!block.atEnd()) {
558                 if (block.peek().type() != IdentToken)
559                     return nullptr;
560                 const CSSParserToken& ident = block.consume();
561                 if (!equalIgnoringASCIICase(ident.value(), "of"))
562                     return nullptr;
563                 if (block.peek().type() != WhitespaceToken)
564                     return nullptr;
565                 DisallowPseudoElementsScope scope(this);
566                 block.consumeWhitespace();
567                 std::unique_ptr<CSSSelectorList> selectorList = std::unique_ptr<CSSSelectorList>(new CSSSelectorList());
568                 *selectorList = consumeComplexSelectorList(block);
569                 if (!selectorList->componentCount() || !block.atEnd())
570                     return nullptr;
571                 selector->setSelectorList(WTFMove(selectorList));
572             }
573             selector->setNth(ab.first, ab.second);
574             return selector;
575         }
576         case CSSSelector::PseudoClassLang: {
577             // FIXME: CSS Selectors Level 4 allows :lang(*-foo)
578             auto argumentList = std::make_unique<Vector<AtomicString>>();
579             if (!consumeLangArgumentList(argumentList, block))
580                 return nullptr;
581             selector->setLangArgumentList(WTFMove(argumentList));
582             return selector;
583         }
584         case CSSSelector::PseudoClassMatches: {
585             std::unique_ptr<CSSSelectorList> selectorList = std::unique_ptr<CSSSelectorList>(new CSSSelectorList());
586             *selectorList = consumeComplexSelectorList(block);
587             if (!selectorList->componentCount() || !block.atEnd())
588                 return nullptr;
589             selector->setSelectorList(WTFMove(selectorList));
590             return selector;
591         }
592         case CSSSelector::PseudoClassAny:
593         case CSSSelector::PseudoClassHost: {
594             std::unique_ptr<CSSSelectorList> selectorList = std::unique_ptr<CSSSelectorList>(new CSSSelectorList());
595             *selectorList = consumeCompoundSelectorList(block);
596             if (!selectorList->componentCount() || !block.atEnd())
597                 return nullptr;
598             selector->setSelectorList(WTFMove(selectorList));
599             return selector;
600         }
601 #if ENABLE(CSS_SELECTORS_LEVEL4)
602         case CSSSelector::PseudoClassDir:
603         case CSSSelector::PseudoClassRole: {
604             const CSSParserToken& ident = block.consumeIncludingWhitespace();
605             if (ident.type() != IdentToken || !block.atEnd())
606                 return nullptr;
607             selector->setArgument(ident.value().toAtomicString());
608             return selector;
609         }
610 #endif
611         default:
612             break;
613         }
614
615     }
616     
617     if (selector->match() == CSSSelector::PseudoElement) {
618         switch (selector->pseudoElementType()) {
619 #if ENABLE(VIDEO_TRACK)
620         case CSSSelector::PseudoElementCue: {
621             DisallowPseudoElementsScope scope(this);
622             std::unique_ptr<CSSSelectorList> selectorList = std::unique_ptr<CSSSelectorList>(new CSSSelectorList());
623             *selectorList = consumeCompoundSelectorList(block);
624             if (!selectorList->isValid() || !block.atEnd())
625                 return nullptr;
626             selector->setSelectorList(WTFMove(selectorList));
627             return selector;
628         }
629 #endif
630         case CSSSelector::PseudoElementSlotted: {
631             DisallowPseudoElementsScope scope(this);
632
633             std::unique_ptr<CSSParserSelector> innerSelector = consumeCompoundSelector(block);
634             block.consumeWhitespace();
635             if (!innerSelector || !block.atEnd())
636                 return nullptr;
637             Vector<std::unique_ptr<CSSParserSelector>> selectorVector;
638             selectorVector.append(WTFMove(innerSelector));
639             selector->adoptSelectorVector(selectorVector);
640             return selector;
641         }
642         default:
643             break;
644         }
645     }
646
647     return nullptr;
648 }
649
650 CSSSelector::RelationType CSSSelectorParser::consumeCombinator(CSSParserTokenRange& range)
651 {
652     auto fallbackResult = CSSSelector::Subselector;
653     while (range.peek().type() == WhitespaceToken) {
654         range.consume();
655         fallbackResult = CSSSelector::DescendantSpace;
656     }
657
658     if (range.peek().type() != DelimiterToken)
659         return fallbackResult;
660
661     UChar delimiter = range.peek().delimiter();
662
663     if (delimiter == '+' || delimiter == '~' || delimiter == '>') {
664         if (delimiter == '+') {
665             range.consumeIncludingWhitespace();
666             return CSSSelector::DirectAdjacent;
667         }
668         
669         if (delimiter == '~') {
670             range.consumeIncludingWhitespace();
671             return CSSSelector::IndirectAdjacent;
672         }
673         
674 #if ENABLE(CSS_SELECTORS_LEVEL4)
675         range.consume();
676         if (range.peek().type() == DelimiterToken && range.peek().delimiter() == '>') {
677             range.consumeIncludingWhitespace();
678             return CSSSelector::DescendantDoubleChild;
679         }
680         range.consumeWhitespace();
681 #else
682         range.consumeIncludingWhitespace();
683 #endif
684         return CSSSelector::Child;
685     }
686
687     return fallbackResult;
688 }
689
690 CSSSelector::Match CSSSelectorParser::consumeAttributeMatch(CSSParserTokenRange& range)
691 {
692     const CSSParserToken& token = range.consumeIncludingWhitespace();
693     switch (token.type()) {
694     case IncludeMatchToken:
695         return CSSSelector::List;
696     case DashMatchToken:
697         return CSSSelector::Hyphen;
698     case PrefixMatchToken:
699         return CSSSelector::Begin;
700     case SuffixMatchToken:
701         return CSSSelector::End;
702     case SubstringMatchToken:
703         return CSSSelector::Contain;
704     case DelimiterToken:
705         if (token.delimiter() == '=')
706             return CSSSelector::Exact;
707         FALLTHROUGH;
708     default:
709         m_failedParsing = true;
710         return CSSSelector::Exact;
711     }
712 }
713
714 CSSSelector::AttributeMatchType CSSSelectorParser::consumeAttributeFlags(CSSParserTokenRange& range)
715 {
716     if (range.peek().type() != IdentToken)
717         return CSSSelector::CaseSensitive;
718     const CSSParserToken& flag = range.consumeIncludingWhitespace();
719     if (equalIgnoringASCIICase(flag.value(), "i"))
720         return CSSSelector::CaseInsensitive;
721     m_failedParsing = true;
722     return CSSSelector::CaseSensitive;
723 }
724
725 bool CSSSelectorParser::consumeANPlusB(CSSParserTokenRange& range, std::pair<int, int>& result)
726 {
727     const CSSParserToken& token = range.consume();
728     if (token.type() == NumberToken && token.numericValueType() == IntegerValueType) {
729         result = std::make_pair(0, static_cast<int>(token.numericValue()));
730         return true;
731     }
732     if (token.type() == IdentToken) {
733         if (equalIgnoringASCIICase(token.value(), "odd")) {
734             result = std::make_pair(2, 1);
735             return true;
736         }
737         if (equalIgnoringASCIICase(token.value(), "even")) {
738             result = std::make_pair(2, 0);
739             return true;
740         }
741     }
742
743     // The 'n' will end up as part of an ident or dimension. For a valid <an+b>,
744     // this will store a string of the form 'n', 'n-', or 'n-123'.
745     String nString;
746
747     if (token.type() == DelimiterToken && token.delimiter() == '+' && range.peek().type() == IdentToken) {
748         result.first = 1;
749         nString = range.consume().value().toString();
750     } else if (token.type() == DimensionToken && token.numericValueType() == IntegerValueType) {
751         result.first = token.numericValue();
752         nString = token.value().toString();
753     } else if (token.type() == IdentToken) {
754         if (token.value()[0] == '-') {
755             result.first = -1;
756             nString = token.value().toString().substring(1);
757         } else {
758             result.first = 1;
759             nString = token.value().toString();
760         }
761     }
762
763     range.consumeWhitespace();
764
765     if (nString.isEmpty() || !isASCIIAlphaCaselessEqual(nString[0], 'n'))
766         return false;
767     if (nString.length() > 1 && nString[1] != '-')
768         return false;
769
770     if (nString.length() > 2) {
771         bool valid;
772         result.second = nString.substring(1).toIntStrict(&valid);
773         return valid;
774     }
775
776     NumericSign sign = nString.length() == 1 ? NoSign : MinusSign;
777     if (sign == NoSign && range.peek().type() == DelimiterToken) {
778         char delimiterSign = range.consumeIncludingWhitespace().delimiter();
779         if (delimiterSign == '+')
780             sign = PlusSign;
781         else if (delimiterSign == '-')
782             sign = MinusSign;
783         else
784             return false;
785     }
786
787     if (sign == NoSign && range.peek().type() != NumberToken) {
788         result.second = 0;
789         return true;
790     }
791
792     const CSSParserToken& b = range.consume();
793     if (b.type() != NumberToken || b.numericValueType() != IntegerValueType)
794         return false;
795     if ((b.numericSign() == NoSign) == (sign == NoSign))
796         return false;
797     result.second = b.numericValue();
798     if (sign == MinusSign)
799         result.second = -result.second;
800     return true;
801 }
802
803 const AtomicString& CSSSelectorParser::defaultNamespace() const
804 {
805     if (!m_styleSheet)
806         return starAtom;
807     return m_styleSheet->defaultNamespace();
808 }
809
810 const AtomicString& CSSSelectorParser::determineNamespace(const AtomicString& prefix)
811 {
812     if (prefix.isNull())
813         return defaultNamespace();
814     if (prefix.isEmpty())
815         return emptyAtom; // No namespace. If an element/attribute has a namespace, we won't match it.
816     if (prefix == starAtom)
817         return starAtom; // We'll match any namespace.
818     if (!m_styleSheet)
819         return nullAtom; // Cannot resolve prefix to namespace without a stylesheet, syntax error.
820     return m_styleSheet->namespaceURIFromPrefix(prefix);
821 }
822
823 void CSSSelectorParser::prependTypeSelectorIfNeeded(const AtomicString& namespacePrefix, const AtomicString& elementName, CSSParserSelector* compoundSelector)
824 {
825     bool isShadowDOM = compoundSelector->needsImplicitShadowCombinatorForMatching();
826     
827     if (elementName.isNull() && defaultNamespace() == starAtom && !isShadowDOM)
828         return;
829
830     AtomicString determinedElementName = elementName.isNull() ? starAtom : elementName;
831     AtomicString namespaceURI = determineNamespace(namespacePrefix);
832     if (namespaceURI.isNull()) {
833         m_failedParsing = true;
834         return;
835     }
836     AtomicString determinedPrefix = namespacePrefix;
837     if (namespaceURI == defaultNamespace())
838         determinedPrefix = nullAtom;
839     QualifiedName tag = QualifiedName(determinedPrefix, determinedElementName, namespaceURI);
840
841     // *:host never matches, so we can't discard the *,
842     // otherwise we can't tell the difference between *:host and just :host.
843     //
844     // Also, selectors where we use a ShadowPseudo combinator between the
845     // element and the pseudo element for matching (custom pseudo elements,
846     // ::cue), we need a universal selector to set the combinator
847     // (relation) on in the cases where there are no simple selectors preceding
848     // the pseudo element.
849     bool explicitForHost = compoundSelector->isHostPseudoSelector() && !elementName.isNull();
850     if (tag != anyQName() || explicitForHost || isShadowDOM)
851         compoundSelector->prependTagSelector(tag, determinedPrefix == nullAtom && determinedElementName == starAtom && !explicitForHost);
852 }
853
854 std::unique_ptr<CSSParserSelector> CSSSelectorParser::addSimpleSelectorToCompound(std::unique_ptr<CSSParserSelector> compoundSelector, std::unique_ptr<CSSParserSelector> simpleSelector)
855 {
856     compoundSelector->appendTagHistory(CSSSelector::Subselector, WTFMove(simpleSelector));
857     return compoundSelector;
858 }
859
860 std::unique_ptr<CSSParserSelector> CSSSelectorParser::splitCompoundAtImplicitShadowCrossingCombinator(std::unique_ptr<CSSParserSelector> compoundSelector, const CSSParserContext& context)
861 {
862     // The tagHistory is a linked list that stores combinator separated compound selectors
863     // from right-to-left. Yet, within a single compound selector, stores the simple selectors
864     // from left-to-right.
865     //
866     // ".a.b > div#id" is stored in a tagHistory as [div, #id, .a, .b], each element in the
867     // list stored with an associated relation (combinator or Subselector).
868     //
869     // ::cue, ::shadow, and custom pseudo elements have an implicit ShadowPseudo combinator
870     // to their left, which really makes for a new compound selector, yet it's consumed by
871     // the selector parser as a single compound selector.
872     //
873     // Example: input#x::-webkit-clear-button -> [ ::-webkit-clear-button, input, #x ]
874     //
875     CSSParserSelector* splitAfter = compoundSelector.get();
876     while (splitAfter->tagHistory() && !splitAfter->tagHistory()->needsImplicitShadowCombinatorForMatching())
877         splitAfter = splitAfter->tagHistory();
878     
879     if (!splitAfter || !splitAfter->tagHistory())
880         return compoundSelector;
881
882     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=161747
883     // We have to recur, since we have rules in media controls like video::a::b. This should not be allowed, and
884     // we should remove this recursion once those rules are gone.
885     std::unique_ptr<CSSParserSelector> secondCompound = context.mode != UASheetMode ? splitAfter->releaseTagHistory() : splitCompoundAtImplicitShadowCrossingCombinator(splitAfter->releaseTagHistory(), context);
886     secondCompound->appendTagHistory(CSSSelector::ShadowDescendant, WTFMove(compoundSelector));
887     return secondCompound;
888 }
889
890 } // namespace WebCore