1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Copyright (C) 2016 Apple Inc. All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
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
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.
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.
31 #include "CSSSelectorParser.h"
33 #include "CSSParserIdioms.h"
34 #include "CSSParserMode.h"
35 #include "CSSSelectorList.h"
36 #include "StyleSheetContents.h"
41 CSSSelectorList CSSSelectorParser::parseSelector(CSSParserTokenRange range, const CSSParserContext& context, StyleSheetContents* styleSheet)
43 CSSSelectorParser parser(context, styleSheet);
44 range.consumeWhitespace();
45 CSSSelectorList result = parser.consumeComplexSelectorList(range);
47 return CSSSelectorList();
51 CSSSelectorParser::CSSSelectorParser(const CSSParserContext& context, StyleSheetContents* styleSheet)
53 , m_styleSheet(styleSheet)
57 CSSSelectorList CSSSelectorParser::consumeComplexSelectorList(CSSParserTokenRange& range)
59 Vector<std::unique_ptr<CSSParserSelector>> selectorList;
60 std::unique_ptr<CSSParserSelector> selector = consumeComplexSelector(range);
62 return CSSSelectorList();
63 selectorList.append(WTFMove(selector));
64 while (!range.atEnd() && range.peek().type() == CommaToken) {
65 range.consumeIncludingWhitespace();
66 selector = consumeComplexSelector(range);
68 return CSSSelectorList();
69 selectorList.append(WTFMove(selector));
75 list.adoptSelectorVector(selectorList);
79 CSSSelectorList CSSSelectorParser::consumeCompoundSelectorList(CSSParserTokenRange& range)
81 Vector<std::unique_ptr<CSSParserSelector>> selectorList;
82 std::unique_ptr<CSSParserSelector> selector = consumeCompoundSelector(range);
83 range.consumeWhitespace();
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();
92 return CSSSelectorList();
93 selectorList.append(WTFMove(selector));
99 list.adoptSelectorVector(selectorList);
105 enum CompoundSelectorFlags {
106 HasPseudoElementForRightmostCompound = 1 << 0,
107 HasContentPseudoElement = 1 << 1
110 unsigned extractCompoundFlags(const CSSParserSelector& simpleSelector, CSSParserMode parserMode)
112 if (simpleSelector.match() != CSSSelector::PseudoElement)
114 // FIXME-NEWPARSER: These don't exist for us, so may need to revisit.
115 // if (simpleSelector.pseudoElementType() == CSSSelector::PseudoContent)
116 // return HasContentPseudoElement;
117 // if (simpleSelector.pseudoElementType() == CSSSelector::PseudoShadow)
120 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=161747
121 // The UASheetMode check is a work-around to allow this selector in mediaControls(New).css:
122 // input[type="range" i]::-webkit-media-slider-container > div {
123 if (parserMode == UASheetMode && simpleSelector.pseudoElementType() == CSSSelector::PseudoElementWebKitCustom)
125 return HasPseudoElementForRightmostCompound;
130 std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeComplexSelector(CSSParserTokenRange& range)
132 std::unique_ptr<CSSParserSelector> selector = consumeCompoundSelector(range);
136 unsigned previousCompoundFlags = 0;
138 for (CSSParserSelector* simple = selector.get(); simple && !previousCompoundFlags; simple = simple->tagHistory())
139 previousCompoundFlags |= extractCompoundFlags(*simple, m_context.mode);
141 while (auto combinator = consumeCombinator(range)) {
142 std::unique_ptr<CSSParserSelector> nextSelector = consumeCompoundSelector(range);
144 return combinator == CSSSelector::Descendant ? WTFMove(selector) : nullptr;
145 if (previousCompoundFlags & HasPseudoElementForRightmostCompound)
147 CSSParserSelector* end = nextSelector.get();
148 unsigned compoundFlags = extractCompoundFlags(*end, m_context.mode);
149 while (end->tagHistory()) {
150 end = end->tagHistory();
151 compoundFlags |= extractCompoundFlags(*end, m_context.mode);
153 end->setRelation(combinator);
154 // FIXME-NEWPARSER: Shadow stuff that we don't have.
155 // if (previousCompoundFlags & HasContentPseudoElement)
156 // end->setRelationIsAffectedByPseudoContent();
157 previousCompoundFlags = compoundFlags;
158 end->setTagHistory(WTFMove(selector));
160 selector = WTFMove(nextSelector);
168 bool isScrollbarPseudoClass(CSSSelector::PseudoClassType pseudo)
171 case CSSSelector::PseudoClassEnabled:
172 case CSSSelector::PseudoClassDisabled:
173 case CSSSelector::PseudoClassHover:
174 case CSSSelector::PseudoClassActive:
175 case CSSSelector::PseudoClassHorizontal:
176 case CSSSelector::PseudoClassVertical:
177 case CSSSelector::PseudoClassDecrement:
178 case CSSSelector::PseudoClassIncrement:
179 case CSSSelector::PseudoClassStart:
180 case CSSSelector::PseudoClassEnd:
181 case CSSSelector::PseudoClassDoubleButton:
182 case CSSSelector::PseudoClassSingleButton:
183 case CSSSelector::PseudoClassNoButton:
184 case CSSSelector::PseudoClassCornerPresent:
185 case CSSSelector::PseudoClassWindowInactive:
192 bool isUserActionPseudoClass(CSSSelector::PseudoClassType pseudo)
195 case CSSSelector::PseudoClassHover:
196 case CSSSelector::PseudoClassFocus:
197 case CSSSelector::PseudoClassActive:
204 bool isPseudoClassValidAfterPseudoElement(CSSSelector::PseudoClassType pseudoClass, CSSSelector::PseudoElementType compoundPseudoElement)
206 switch (compoundPseudoElement) {
207 case CSSSelector::PseudoElementResizer:
208 case CSSSelector::PseudoElementScrollbar:
209 case CSSSelector::PseudoElementScrollbarCorner:
210 case CSSSelector::PseudoElementScrollbarButton:
211 case CSSSelector::PseudoElementScrollbarThumb:
212 case CSSSelector::PseudoElementScrollbarTrack:
213 case CSSSelector::PseudoElementScrollbarTrackPiece:
214 return isScrollbarPseudoClass(pseudoClass);
215 case CSSSelector::PseudoElementSelection:
216 return pseudoClass == CSSSelector::PseudoClassWindowInactive;
217 case CSSSelector::PseudoElementWebKitCustom:
218 case CSSSelector::PseudoElementWebKitCustomLegacyPrefixed:
219 return isUserActionPseudoClass(pseudoClass);
225 bool isSimpleSelectorValidAfterPseudoElement(const CSSParserSelector& simpleSelector, CSSSelector::PseudoElementType compoundPseudoElement)
227 if (compoundPseudoElement == CSSSelector::PseudoElementUnknown)
229 // FIXME-NEWPARSER: This doesn't exist for us.
230 // if (compoundPseudoElement == CSSSelector::PseudoElementContent)
231 // return simpleSelector.match() != CSSSelector::PseudoElement;
232 if (simpleSelector.match() != CSSSelector::PseudoClass)
234 CSSSelector::PseudoClassType pseudo = simpleSelector.pseudoClassType();
235 if (pseudo == CSSSelector::PseudoClassNot) {
236 ASSERT(simpleSelector.selectorList());
237 ASSERT(simpleSelector.selectorList()->first());
238 ASSERT(!simpleSelector.selectorList()->first()->tagHistory());
239 pseudo = simpleSelector.selectorList()->first()->pseudoClassType();
241 return isPseudoClassValidAfterPseudoElement(pseudo, compoundPseudoElement);
246 std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeCompoundSelector(CSSParserTokenRange& range)
248 std::unique_ptr<CSSParserSelector> compoundSelector;
250 AtomicString namespacePrefix;
251 AtomicString elementName;
252 CSSSelector::PseudoElementType compoundPseudoElement = CSSSelector::PseudoElementUnknown;
253 if (!consumeName(range, elementName, namespacePrefix)) {
254 compoundSelector = consumeSimpleSelector(range);
255 if (!compoundSelector)
257 if (compoundSelector->match() == CSSSelector::PseudoElement)
258 compoundPseudoElement = compoundSelector->pseudoElementType();
260 if (m_context.isHTMLDocument)
261 elementName = elementName.convertToASCIILowercase();
263 while (std::unique_ptr<CSSParserSelector> simpleSelector = consumeSimpleSelector(range)) {
264 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=161747
265 // The UASheetMode check is a work-around to allow this selector in mediaControls(New).css:
266 // video::-webkit-media-text-track-region-container.scrolling
267 if (m_context.mode != UASheetMode && !isSimpleSelectorValidAfterPseudoElement(*simpleSelector.get(), compoundPseudoElement)) {
268 m_failedParsing = true;
271 if (simpleSelector->match() == CSSSelector::PseudoElement)
272 compoundPseudoElement = simpleSelector->pseudoElementType();
274 if (compoundSelector)
275 compoundSelector = addSimpleSelectorToCompound(WTFMove(compoundSelector), WTFMove(simpleSelector));
277 compoundSelector = WTFMove(simpleSelector);
280 if (!compoundSelector) {
281 AtomicString namespaceURI = determineNamespace(namespacePrefix);
282 if (namespaceURI.isNull()) {
283 m_failedParsing = true;
286 if (namespaceURI == defaultNamespace())
287 namespacePrefix = nullAtom;
289 CSSParserSelector* rawSelector = new CSSParserSelector(QualifiedName(namespacePrefix, elementName, namespaceURI));
290 std::unique_ptr<CSSParserSelector> selector = std::unique_ptr<CSSParserSelector>(rawSelector);
293 prependTypeSelectorIfNeeded(namespacePrefix, elementName, compoundSelector.get());
294 return splitCompoundAtImplicitShadowCrossingCombinator(WTFMove(compoundSelector));
297 std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeSimpleSelector(CSSParserTokenRange& range)
299 const CSSParserToken& token = range.peek();
300 std::unique_ptr<CSSParserSelector> selector;
301 if (token.type() == HashToken)
302 selector = consumeId(range);
303 else if (token.type() == DelimiterToken && token.delimiter() == '.')
304 selector = consumeClass(range);
305 else if (token.type() == LeftBracketToken)
306 selector = consumeAttribute(range);
307 else if (token.type() == ColonToken)
308 selector = consumePseudo(range);
312 m_failedParsing = true;
316 bool CSSSelectorParser::consumeName(CSSParserTokenRange& range, AtomicString& name, AtomicString& namespacePrefix)
319 namespacePrefix = nullAtom;
321 const CSSParserToken& firstToken = range.peek();
322 if (firstToken.type() == IdentToken) {
323 name = firstToken.value().toAtomicString();
325 } else if (firstToken.type() == DelimiterToken && firstToken.delimiter() == '*') {
328 } else if (firstToken.type() == DelimiterToken && firstToken.delimiter() == '|') {
329 // This is an empty namespace, which'll get assigned this value below
334 if (range.peek().type() != DelimiterToken || range.peek().delimiter() != '|')
338 namespacePrefix = name;
339 const CSSParserToken& nameToken = range.consume();
340 if (nameToken.type() == IdentToken) {
341 name = nameToken.value().toAtomicString();
342 } else if (nameToken.type() == DelimiterToken && nameToken.delimiter() == '*')
346 namespacePrefix = nullAtom;
353 std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeId(CSSParserTokenRange& range)
355 ASSERT(range.peek().type() == HashToken);
356 if (range.peek().getHashTokenType() != HashTokenId)
358 std::unique_ptr<CSSParserSelector> selector = std::unique_ptr<CSSParserSelector>(new CSSParserSelector());
359 selector->setMatch(CSSSelector::Id);
361 // FIXME-NEWPARSER: Avoid having to do this, but the old parser does and we need
362 // to be compatible for now.
363 StringView stringView = range.consume().value();
364 if (m_context.mode == HTMLQuirksMode)
365 convertToASCIILowercaseInPlace(stringView);
366 selector->setValue(stringView.toAtomicString());
371 std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeClass(CSSParserTokenRange& range)
373 ASSERT(range.peek().type() == DelimiterToken);
374 ASSERT(range.peek().delimiter() == '.');
376 if (range.peek().type() != IdentToken)
378 std::unique_ptr<CSSParserSelector> selector = std::unique_ptr<CSSParserSelector>(new CSSParserSelector());
379 selector->setMatch(CSSSelector::Class);
381 // FIXME-NEWPARSER: Avoid having to do this, but the old parser does and we need
382 // to be compatible for now.
383 StringView stringView = range.consume().value();
384 if (m_context.mode == HTMLQuirksMode)
385 convertToASCIILowercaseInPlace(stringView);
386 selector->setValue(stringView.toAtomicString());
391 std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumeAttribute(CSSParserTokenRange& range)
393 ASSERT(range.peek().type() == LeftBracketToken);
394 CSSParserTokenRange block = range.consumeBlock();
395 block.consumeWhitespace();
397 AtomicString namespacePrefix;
398 AtomicString attributeName;
399 if (!consumeName(block, attributeName, namespacePrefix))
401 block.consumeWhitespace();
403 if (m_context.isHTMLDocument)
404 attributeName = attributeName.convertToASCIILowercase();
406 AtomicString namespaceURI = determineNamespace(namespacePrefix);
407 if (namespaceURI.isNull())
410 QualifiedName qualifiedName = namespacePrefix.isNull()
411 ? QualifiedName(nullAtom, attributeName, nullAtom)
412 : QualifiedName(namespacePrefix, attributeName, namespaceURI);
414 std::unique_ptr<CSSParserSelector> selector = std::unique_ptr<CSSParserSelector>(new CSSParserSelector());
417 selector->setAttribute(qualifiedName, CSSSelector::CaseSensitive);
418 selector->setMatch(CSSSelector::Set);
422 selector->setMatch(consumeAttributeMatch(block));
424 const CSSParserToken& attributeValue = block.consumeIncludingWhitespace();
425 if (attributeValue.type() != IdentToken && attributeValue.type() != StringToken)
427 selector->setValue(attributeValue.value().toAtomicString());
428 selector->setAttribute(qualifiedName, consumeAttributeFlags(block));
435 std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumePseudo(CSSParserTokenRange& range)
437 ASSERT(range.peek().type() == ColonToken);
441 if (range.peek().type() == ColonToken) {
446 const CSSParserToken& token = range.peek();
447 if (token.type() != IdentToken && token.type() != FunctionToken)
450 std::unique_ptr<CSSParserSelector> selector;
451 StringView value = token.value();
453 selector = std::unique_ptr<CSSParserSelector>(CSSParserSelector::parsePseudoClassSelectorFromStringView(value));
455 selector = std::unique_ptr<CSSParserSelector>(CSSParserSelector::parsePseudoElementSelectorFromStringView(value));
457 if (!selector || (selector->match() == CSSSelector::PseudoElement && m_disallowPseudoElements))
460 if (token.type() == IdentToken) {
462 if ((selector->match() == CSSSelector::PseudoElement && selector->pseudoElementType() == CSSSelector::PseudoElementUnknown) || (selector->match() == CSSSelector::PseudoClass && selector->pseudoClassType() == CSSSelector::PseudoClassUnknown))
467 CSSParserTokenRange block = range.consumeBlock();
468 block.consumeWhitespace();
469 if (token.type() != FunctionToken)
472 switch (selector->pseudoClassType()) {
473 case CSSSelector::PseudoClassNot: {
474 std::unique_ptr<CSSParserSelector> innerSelector = consumeCompoundSelector(block);
475 block.consumeWhitespace();
476 if (!innerSelector || !block.atEnd())
478 Vector<std::unique_ptr<CSSParserSelector>> selectorVector;
479 selectorVector.append(WTFMove(innerSelector));
480 selector->adoptSelectorVector(selectorVector);
483 case CSSSelector::PseudoClassNthChild:
484 case CSSSelector::PseudoClassNthLastChild:
485 case CSSSelector::PseudoClassNthOfType:
486 case CSSSelector::PseudoClassNthLastOfType: {
487 std::pair<int, int> ab;
488 if (!consumeANPlusB(block, ab))
490 block.consumeWhitespace();
493 selector->setArgument(AtomicString::number(ab.first * ab.second));
496 case CSSSelector::PseudoClassLang: {
497 // FIXME: CSS Selectors Level 4 allows :lang(*-foo)
498 const CSSParserToken& ident = block.consumeIncludingWhitespace();
499 if (ident.type() != IdentToken || !block.atEnd())
501 selector->setArgument(ident.value().toAtomicString());
504 // FIXME-NEWPARSER: Support :host-context
505 case CSSSelector::PseudoClassAny:
506 case CSSSelector::PseudoClassHost: {
507 DisallowPseudoElementsScope scope(this);
508 std::unique_ptr<CSSSelectorList> selectorList = std::unique_ptr<CSSSelectorList>(new CSSSelectorList());
509 *selectorList = consumeCompoundSelectorList(block);
510 if (!selectorList->isValid() || !block.atEnd())
512 selector->setSelectorList(WTFMove(selectorList));
519 switch (selector->pseudoElementType()) {
520 case CSSSelector::PseudoElementCue: {
521 DisallowPseudoElementsScope scope(this);
522 std::unique_ptr<CSSSelectorList> selectorList = std::unique_ptr<CSSSelectorList>(new CSSSelectorList());
523 *selectorList = consumeCompoundSelectorList(block);
524 if (!selectorList->isValid() || !block.atEnd())
526 selector->setSelectorList(WTFMove(selectorList));
529 case CSSSelector::PseudoElementSlotted: {
530 DisallowPseudoElementsScope scope(this);
532 std::unique_ptr<CSSParserSelector> innerSelector = consumeCompoundSelector(block);
533 block.consumeWhitespace();
534 if (!innerSelector || !block.atEnd())
536 Vector<std::unique_ptr<CSSParserSelector>> selectorVector;
537 selectorVector.append(WTFMove(innerSelector));
538 selector->adoptSelectorVector(selectorVector);
548 CSSSelector::RelationType CSSSelectorParser::consumeCombinator(CSSParserTokenRange& range)
550 auto fallbackResult = CSSSelector::Subselector;
551 while (range.peek().type() == WhitespaceToken) {
553 fallbackResult = CSSSelector::Descendant;
556 if (range.peek().type() != DelimiterToken)
557 return fallbackResult;
559 UChar delimiter = range.peek().delimiter();
561 if (delimiter == '+' || delimiter == '~' || delimiter == '>') {
562 range.consumeIncludingWhitespace();
563 if (delimiter == '+')
564 return CSSSelector::DirectAdjacent;
565 if (delimiter == '~')
566 return CSSSelector::IndirectAdjacent;
567 return CSSSelector::Child;
571 if (delimiter != '/')
572 return fallbackResult;
574 const CSSParserToken& ident = range.consume();
575 if (ident.type() != IdentToken || !equalIgnoringASCIICase(ident.value(), "deep"))
576 m_failedParsing = true;
577 const CSSParserToken& slash = range.consumeIncludingWhitespace();
578 if (slash.type() != DelimiterToken || slash.delimiter() != '/')
579 m_failedParsing = true;
580 return CSSSelector::ShadowDeep;
583 CSSSelector::Match CSSSelectorParser::consumeAttributeMatch(CSSParserTokenRange& range)
585 const CSSParserToken& token = range.consumeIncludingWhitespace();
586 switch (token.type()) {
587 case IncludeMatchToken:
588 return CSSSelector::List;
590 return CSSSelector::Hyphen;
591 case PrefixMatchToken:
592 return CSSSelector::Begin;
593 case SuffixMatchToken:
594 return CSSSelector::End;
595 case SubstringMatchToken:
596 return CSSSelector::Contain;
598 if (token.delimiter() == '=')
599 return CSSSelector::Exact;
602 m_failedParsing = true;
603 return CSSSelector::Exact;
607 CSSSelector::AttributeMatchType CSSSelectorParser::consumeAttributeFlags(CSSParserTokenRange& range)
609 if (range.peek().type() != IdentToken)
610 return CSSSelector::CaseSensitive;
611 const CSSParserToken& flag = range.consumeIncludingWhitespace();
612 if (equalIgnoringASCIICase(flag.value(), "i"))
613 return CSSSelector::CaseInsensitive;
614 m_failedParsing = true;
615 return CSSSelector::CaseSensitive;
618 bool CSSSelectorParser::consumeANPlusB(CSSParserTokenRange& range, std::pair<int, int>& result)
620 const CSSParserToken& token = range.consume();
621 if (token.type() == NumberToken && token.numericValueType() == IntegerValueType) {
622 result = std::make_pair(0, static_cast<int>(token.numericValue()));
625 if (token.type() == IdentToken) {
626 if (equalIgnoringASCIICase(token.value(), "odd")) {
627 result = std::make_pair(2, 1);
630 if (equalIgnoringASCIICase(token.value(), "even")) {
631 result = std::make_pair(2, 0);
636 // The 'n' will end up as part of an ident or dimension. For a valid <an+b>,
637 // this will store a string of the form 'n', 'n-', or 'n-123'.
640 if (token.type() == DelimiterToken && token.delimiter() == '+' && range.peek().type() == IdentToken) {
642 nString = range.consume().value().toString();
643 } else if (token.type() == DimensionToken && token.numericValueType() == IntegerValueType) {
644 result.first = token.numericValue();
645 nString = token.value().toString();
646 } else if (token.type() == IdentToken) {
647 if (token.value()[0] == '-') {
649 nString = token.value().toString().substring(1);
652 nString = token.value().toString();
656 range.consumeWhitespace();
658 if (nString.isEmpty() || !isASCIIAlphaCaselessEqual(nString[0], 'n'))
660 if (nString.length() > 1 && nString[1] != '-')
663 if (nString.length() > 2) {
665 result.second = nString.substring(1).toIntStrict(&valid);
669 NumericSign sign = nString.length() == 1 ? NoSign : MinusSign;
670 if (sign == NoSign && range.peek().type() == DelimiterToken) {
671 char delimiterSign = range.consumeIncludingWhitespace().delimiter();
672 if (delimiterSign == '+')
674 else if (delimiterSign == '-')
680 if (sign == NoSign && range.peek().type() != NumberToken) {
685 const CSSParserToken& b = range.consume();
686 if (b.type() != NumberToken || b.numericValueType() != IntegerValueType)
688 if ((b.numericSign() == NoSign) == (sign == NoSign))
690 result.second = b.numericValue();
691 if (sign == MinusSign)
692 result.second = -result.second;
696 const AtomicString& CSSSelectorParser::defaultNamespace() const
700 return m_styleSheet->defaultNamespace();
703 const AtomicString& CSSSelectorParser::determineNamespace(const AtomicString& prefix)
706 return defaultNamespace();
707 if (prefix.isEmpty())
708 return emptyAtom; // No namespace. If an element/attribute has a namespace, we won't match it.
709 if (prefix == starAtom)
710 return starAtom; // We'll match any namespace.
712 return nullAtom; // Cannot resolve prefix to namespace without a stylesheet, syntax error.
713 return m_styleSheet->namespaceURIFromPrefix(prefix);
716 void CSSSelectorParser::prependTypeSelectorIfNeeded(const AtomicString& namespacePrefix, const AtomicString& elementName, CSSParserSelector* compoundSelector)
718 if (elementName.isNull() && defaultNamespace() == starAtom && !compoundSelector->needsImplicitShadowCombinatorForMatching())
721 AtomicString determinedElementName = elementName.isNull() ? starAtom : elementName;
722 AtomicString namespaceURI = determineNamespace(namespacePrefix);
723 if (namespaceURI.isNull()) {
724 m_failedParsing = true;
727 AtomicString determinedPrefix = namespacePrefix;
728 if (namespaceURI == defaultNamespace())
729 determinedPrefix = nullAtom;
730 QualifiedName tag = QualifiedName(determinedPrefix, determinedElementName, namespaceURI);
732 // *:host/*:host-context never matches, so we can't discard the *,
733 // otherwise we can't tell the difference between *:host and just :host.
735 // Also, selectors where we use a ShadowPseudo combinator between the
736 // element and the pseudo element for matching (custom pseudo elements,
737 // ::cue, ::shadow), we need a universal selector to set the combinator
738 // (relation) on in the cases where there are no simple selectors preceding
739 // the pseudo element.
740 bool explicitForHost = compoundSelector->isHostPseudoSelector() && !elementName.isNull();
741 if (tag != anyQName() || explicitForHost || compoundSelector->needsImplicitShadowCombinatorForMatching())
742 compoundSelector->prependTagSelector(tag, determinedPrefix == nullAtom && determinedElementName == starAtom && !explicitForHost);
745 std::unique_ptr<CSSParserSelector> CSSSelectorParser::addSimpleSelectorToCompound(std::unique_ptr<CSSParserSelector> compoundSelector, std::unique_ptr<CSSParserSelector> simpleSelector)
747 compoundSelector->appendTagHistory(CSSSelector::Subselector, WTFMove(simpleSelector));
748 return compoundSelector;
751 std::unique_ptr<CSSParserSelector> CSSSelectorParser::splitCompoundAtImplicitShadowCrossingCombinator(std::unique_ptr<CSSParserSelector> compoundSelector)
753 // The tagHistory is a linked list that stores combinator separated compound selectors
754 // from right-to-left. Yet, within a single compound selector, stores the simple selectors
755 // from left-to-right.
757 // ".a.b > div#id" is stored in a tagHistory as [div, #id, .a, .b], each element in the
758 // list stored with an associated relation (combinator or Subselector).
760 // ::cue, ::shadow, and custom pseudo elements have an implicit ShadowPseudo combinator
761 // to their left, which really makes for a new compound selector, yet it's consumed by
762 // the selector parser as a single compound selector.
764 // Example: input#x::-webkit-clear-button -> [ ::-webkit-clear-button, input, #x ]
766 // Likewise, ::slotted() pseudo element has an implicit ShadowSlot combinator to its left
767 // for finding matching slot element in other TreeScope.
769 // Example: slot[name=foo]::slotted(div) -> [ ::slotted(div), slot, [name=foo] ]
770 CSSParserSelector* splitAfter = compoundSelector.get();
772 while (splitAfter->tagHistory() && !splitAfter->tagHistory()->needsImplicitShadowCombinatorForMatching())
773 splitAfter = splitAfter->tagHistory();
775 if (!splitAfter || !splitAfter->tagHistory())
776 return compoundSelector;
778 std::unique_ptr<CSSParserSelector> secondCompound = splitAfter->releaseTagHistory();
779 secondCompound->appendTagHistory(secondCompound->pseudoElementType() == CSSSelector::PseudoElementSlotted ? CSSSelector::ShadowSlot : CSSSelector::ShadowPseudo, WTFMove(compoundSelector));
780 return secondCompound;
783 } // namespace WebCore