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