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