OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/css/parser/CSSSelectorParser.h" | 5 #include "core/css/parser/CSSSelectorParser.h" |
6 | 6 |
7 #include "core/css/CSSSelectorList.h" | 7 #include "core/css/CSSSelectorList.h" |
8 #include "core/css/StyleSheetContents.h" | 8 #include "core/css/StyleSheetContents.h" |
9 #include "core/frame/UseCounter.h" | 9 #include "core/frame/UseCounter.h" |
10 #include "platform/RuntimeEnabledFeatures.h" | 10 #include "platform/RuntimeEnabledFeatures.h" |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
128 return CSSSelectorList(); | 128 return CSSSelectorList(); |
129 | 129 |
130 return CSSSelectorList::adoptSelectorVector(selectorList); | 130 return CSSSelectorList::adoptSelectorVector(selectorList); |
131 } | 131 } |
132 | 132 |
133 PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeComplexSelector(CSSParse
rTokenRange& range) | 133 PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeComplexSelector(CSSParse
rTokenRange& range) |
134 { | 134 { |
135 OwnPtr<CSSParserSelector> selector = consumeCompoundSelector(range); | 135 OwnPtr<CSSParserSelector> selector = consumeCompoundSelector(range); |
136 if (!selector) | 136 if (!selector) |
137 return nullptr; | 137 return nullptr; |
| 138 |
| 139 bool previousCompoundHasContentPseudo = false; |
| 140 |
| 141 for (CSSParserSelector* simple = selector.get(); simple && !previousCompound
HasContentPseudo; simple = simple->tagHistory()) |
| 142 previousCompoundHasContentPseudo = simple->pseudoType() == CSSSelector::
PseudoContent; |
| 143 |
138 while (CSSSelector::Relation combinator = consumeCombinator(range)) { | 144 while (CSSSelector::Relation combinator = consumeCombinator(range)) { |
139 OwnPtr<CSSParserSelector> nextSelector = consumeCompoundSelector(range); | 145 OwnPtr<CSSParserSelector> nextSelector = consumeCompoundSelector(range); |
140 if (!nextSelector) | 146 if (!nextSelector) |
141 return combinator == CSSSelector::Descendant ? selector.release() :
nullptr; | 147 return combinator == CSSSelector::Descendant ? selector.release() :
nullptr; |
142 CSSParserSelector* end = nextSelector.get(); | 148 CSSParserSelector* end = nextSelector.get(); |
143 while (end->tagHistory()) | 149 bool compoundHasContentPseudo = end->pseudoType() == CSSSelector::Pseudo
Content; |
| 150 while (end->tagHistory()) { |
144 end = end->tagHistory(); | 151 end = end->tagHistory(); |
| 152 compoundHasContentPseudo |= end->pseudoType() == CSSSelector::Pseudo
Content; |
| 153 } |
145 end->setRelation(combinator); | 154 end->setRelation(combinator); |
146 if (selector->pseudoType() == CSSSelector::PseudoContent) | 155 if (previousCompoundHasContentPseudo) |
147 end->setRelationIsAffectedByPseudoContent(); | 156 end->setRelationIsAffectedByPseudoContent(); |
| 157 previousCompoundHasContentPseudo = compoundHasContentPseudo; |
148 end->setTagHistory(selector.release()); | 158 end->setTagHistory(selector.release()); |
149 | 159 |
150 selector = nextSelector.release(); | 160 selector = nextSelector.release(); |
151 } | 161 } |
152 | 162 |
153 return selector.release(); | 163 return selector.release(); |
154 } | 164 } |
155 | 165 |
156 PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeCompoundSelector(CSSPars
erTokenRange& range) | 166 PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeCompoundSelector(CSSPars
erTokenRange& range) |
157 { | 167 { |
(...skipping 16 matching lines...) Expand all Loading... |
174 compoundSelector = simpleSelector.release(); | 184 compoundSelector = simpleSelector.release(); |
175 } | 185 } |
176 | 186 |
177 if (!compoundSelector) { | 187 if (!compoundSelector) { |
178 AtomicString namespaceURI = determineNamespace(namespacePrefix); | 188 AtomicString namespaceURI = determineNamespace(namespacePrefix); |
179 if (namespaceURI.isNull()) | 189 if (namespaceURI.isNull()) |
180 return nullptr; | 190 return nullptr; |
181 return CSSParserSelector::create(QualifiedName(namespacePrefix, elementN
ame, namespaceURI)); | 191 return CSSParserSelector::create(QualifiedName(namespacePrefix, elementN
ame, namespaceURI)); |
182 } | 192 } |
183 prependTypeSelectorIfNeeded(namespacePrefix, elementName, compoundSelector.g
et()); | 193 prependTypeSelectorIfNeeded(namespacePrefix, elementName, compoundSelector.g
et()); |
184 return compoundSelector.release(); | 194 return splitCompoundAtImplicitShadowCrossingCombinator(compoundSelector.rele
ase()); |
185 } | 195 } |
186 | 196 |
187 PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeSimpleSelector(CSSParser
TokenRange& range) | 197 PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeSimpleSelector(CSSParser
TokenRange& range) |
188 { | 198 { |
189 const CSSParserToken& token = range.peek(); | 199 const CSSParserToken& token = range.peek(); |
190 OwnPtr<CSSParserSelector> selector; | 200 OwnPtr<CSSParserSelector> selector; |
191 if (token.type() == HashToken) | 201 if (token.type() == HashToken) |
192 selector = consumeId(range); | 202 selector = consumeId(range); |
193 else if (token.type() == DelimiterToken && token.delimiter() == '.') | 203 else if (token.type() == DelimiterToken && token.delimiter() == '.') |
194 selector = consumeClass(range); | 204 selector = consumeClass(range); |
(...skipping 374 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
569 { | 579 { |
570 if (elementName.isNull() && defaultNamespace() == starAtom && !compoundSelec
tor->needsImplicitShadowCrossingCombinatorForMatching()) | 580 if (elementName.isNull() && defaultNamespace() == starAtom && !compoundSelec
tor->needsImplicitShadowCrossingCombinatorForMatching()) |
571 return; | 581 return; |
572 | 582 |
573 AtomicString determinedElementName = elementName.isNull() ? starAtom : eleme
ntName; | 583 AtomicString determinedElementName = elementName.isNull() ? starAtom : eleme
ntName; |
574 AtomicString namespaceURI = determineNamespace(namespacePrefix); | 584 AtomicString namespaceURI = determineNamespace(namespacePrefix); |
575 if (namespaceURI.isNull()) | 585 if (namespaceURI.isNull()) |
576 return; | 586 return; |
577 QualifiedName tag = QualifiedName(namespacePrefix, determinedElementName, na
mespaceURI); | 587 QualifiedName tag = QualifiedName(namespacePrefix, determinedElementName, na
mespaceURI); |
578 | 588 |
579 if (compoundSelector->needsImplicitShadowCrossingCombinatorForMatching()) | 589 // *:host/*:host-context never matches, so we can't discard the *, |
580 return rewriteSpecifiersWithElementNameForCustomPseudoElement(tag, compo
undSelector, elementName.isNull()); | 590 // otherwise we can't tell the difference between *:host and just :host. |
581 | 591 // |
582 if (compoundSelector->pseudoType() == CSSSelector::PseudoContent) | 592 // Also, selectors where we use a ShadowPseudo combinator between the |
583 return rewriteSpecifiersWithElementNameForContentPseudoElement(tag, comp
oundSelector, elementName.isNull()); | 593 // element and the pseudo element for matching (custom pseudo elements, |
584 | 594 // ::cue, ::shadow), we need a universal selector to set the combinator |
585 // *:host never matches, so we can't discard the * otherwise we can't tell t
he | 595 // (relation) on in the cases where there are no simple selectors preceding |
586 // difference between *:host and just :host. | 596 // the pseudo element. |
587 if (tag == anyQName() && !compoundSelector->hasHostPseudoSelector()) | 597 if (tag != anyQName() || compoundSelector->isHostPseudoSelector() || compoun
dSelector->needsImplicitShadowCrossingCombinatorForMatching()) |
588 return; | 598 compoundSelector->prependTagSelector(tag, elementName.isNull()); |
589 compoundSelector->prependTagSelector(tag, elementName.isNull()); | |
590 } | |
591 | |
592 void CSSSelectorParser::rewriteSpecifiersWithElementNameForCustomPseudoElement(c
onst QualifiedName& tag, CSSParserSelector* specifiers, bool tagIsImplicit) | |
593 { | |
594 CSSParserSelector* lastShadowPseudo = specifiers; | |
595 CSSParserSelector* history = specifiers; | |
596 while (history->tagHistory()) { | |
597 history = history->tagHistory(); | |
598 if (history->needsImplicitShadowCrossingCombinatorForMatching() | |
599 || history->hasImplicitShadowCrossingCombinatorForMatching()) { | |
600 lastShadowPseudo = history; | |
601 } | |
602 } | |
603 | |
604 if (lastShadowPseudo->tagHistory()) { | |
605 ASSERT(lastShadowPseudo->hasImplicitShadowCrossingCombinatorForMatching(
)); | |
606 if (tag != anyQName()) | |
607 lastShadowPseudo->tagHistory()->prependTagSelector(tag, tagIsImplici
t); | |
608 return; | |
609 } | |
610 | |
611 // For shadow-ID pseudo-elements to be correctly matched, the ShadowPseudo c
ombinator has to be used. | |
612 // We therefore create a new Selector with that combinator here in any case,
even if matching any (host) element in any namespace (i.e. '*'). | |
613 OwnPtr<CSSParserSelector> elementNameSelector = CSSParserSelector::create(ta
g); | |
614 lastShadowPseudo->setTagHistory(elementNameSelector.release()); | |
615 lastShadowPseudo->setRelation(CSSSelector::ShadowPseudo); | |
616 } | |
617 | |
618 void CSSSelectorParser::rewriteSpecifiersWithElementNameForContentPseudoElement(
const QualifiedName& tag, CSSParserSelector* specifiers, bool tagIsImplicit) | |
619 { | |
620 CSSParserSelector* last = specifiers; | |
621 CSSParserSelector* history = specifiers; | |
622 while (history->tagHistory()) { | |
623 history = history->tagHistory(); | |
624 if (history->pseudoType() == CSSSelector::PseudoContent || history->rela
tionIsAffectedByPseudoContent()) | |
625 last = history; | |
626 } | |
627 | |
628 if (last->tagHistory()) { | |
629 if (tag != anyQName()) | |
630 last->tagHistory()->prependTagSelector(tag, tagIsImplicit); | |
631 return; | |
632 } | |
633 | |
634 // For shadow-ID pseudo-elements to be correctly matched, the ShadowPseudo c
ombinator has to be used. | |
635 // We therefore create a new Selector with that combinator here in any case,
even if matching any (host) element in any namespace (i.e. '*'). | |
636 OwnPtr<CSSParserSelector> elementNameSelector = CSSParserSelector::create(ta
g); | |
637 last->setTagHistory(elementNameSelector.release()); | |
638 } | 599 } |
639 | 600 |
640 PassOwnPtr<CSSParserSelector> CSSSelectorParser::addSimpleSelectorToCompound(Pas
sOwnPtr<CSSParserSelector> compoundSelector, PassOwnPtr<CSSParserSelector> simpl
eSelector) | 601 PassOwnPtr<CSSParserSelector> CSSSelectorParser::addSimpleSelectorToCompound(Pas
sOwnPtr<CSSParserSelector> compoundSelector, PassOwnPtr<CSSParserSelector> simpl
eSelector) |
641 { | 602 { |
| 603 compoundSelector->appendTagHistory(CSSSelector::SubSelector, simpleSelector)
; |
| 604 return compoundSelector; |
| 605 } |
| 606 |
| 607 PassOwnPtr<CSSParserSelector> CSSSelectorParser::splitCompoundAtImplicitShadowCr
ossingCombinator(PassOwnPtr<CSSParserSelector> compoundSelector) |
| 608 { |
642 // The tagHistory is a linked list that stores combinator separated compound
selectors | 609 // The tagHistory is a linked list that stores combinator separated compound
selectors |
643 // from right-to-left. Yet, within a single compound selector, stores the si
mple selectors | 610 // from right-to-left. Yet, within a single compound selector, stores the si
mple selectors |
644 // from left-to-right. | 611 // from left-to-right. |
645 // | 612 // |
646 // ".a.b > div#id" is stored in a tagHistory as [div, #id, .a, .b], each ele
ment in the | 613 // ".a.b > div#id" is stored in a tagHistory as [div, #id, .a, .b], each ele
ment in the |
647 // list stored with an associated relation (combinator or SubSelector). | 614 // list stored with an associated relation (combinator or SubSelector). |
648 // | 615 // |
649 // ::cue, ::shadow, and custom pseudo elements have an implicit ShadowPseudo
combinator | 616 // ::cue, ::shadow, and custom pseudo elements have an implicit ShadowPseudo
combinator |
650 // to their left, which really makes for a new compound selector, yet it's c
onsumed by | 617 // to their left, which really makes for a new compound selector, yet it's c
onsumed by |
651 // the selector parser as a single compound selector. | 618 // the selector parser as a single compound selector. |
652 // | 619 // |
653 // Example: input#x::-webkit-clear-button -> [ ::-webkit-clear-button, input
, #x ] | 620 // Example: input#x::-webkit-clear-button -> [ ::-webkit-clear-button, input
, #x ] |
654 // | |
655 // ::content is kept at the end of the compound in order easily know when to
call | |
656 // setRelationIsAffectedByPseudoContent. | |
657 // | |
658 // We are currently not dropping selectors containing multiple instances of
::content, | |
659 // ::shadow, ::cue, and custom pseudo elements in arbitrary order. There are
known | |
660 // issues like crbug.com/478563 | |
661 // | |
662 // TODO(rune@opera.com): We should try to remove the need for the re-orderin
g tricks | |
663 // below and in the remaining rewrite* methods by using a more suitable stor
age | |
664 // structure in CSSSelectorParser. | |
665 // | |
666 // The code below is to keep ::content at the end of the compound, and to ke
ep the | |
667 // tagHistory order correct for implicit ShadowPseudo and juggling multiple
(two?) | |
668 // compounds. | |
669 | 621 |
670 CSSSelector::Relation relation = CSSSelector::SubSelector; | 622 CSSParserSelector* splitAfter = compoundSelector.get(); |
671 | 623 |
672 if (simpleSelector->needsImplicitShadowCrossingCombinatorForMatching() || si
mpleSelector->pseudoType() == CSSSelector::PseudoContent) { | 624 while (splitAfter->tagHistory() && !splitAfter->tagHistory()->needsImplicitS
hadowCrossingCombinatorForMatching()) |
673 if (simpleSelector->needsImplicitShadowCrossingCombinatorForMatching()) | 625 splitAfter = splitAfter->tagHistory(); |
674 relation = CSSSelector::ShadowPseudo; | 626 |
675 simpleSelector->appendTagHistory(relation, compoundSelector); | 627 if (!splitAfter || !splitAfter->tagHistory()) |
676 return simpleSelector; | |
677 } | |
678 if (compoundSelector->needsImplicitShadowCrossingCombinatorForMatching() ||
compoundSelector->pseudoType() == CSSSelector::PseudoContent) { | |
679 if (compoundSelector->needsImplicitShadowCrossingCombinatorForMatching()
) | |
680 relation = CSSSelector::ShadowPseudo; | |
681 compoundSelector->insertTagHistory(CSSSelector::SubSelector, simpleSelec
tor, relation); | |
682 return compoundSelector; | 628 return compoundSelector; |
683 } | |
684 | 629 |
685 // All other simple selectors are added to the end of the compound. | 630 OwnPtr<CSSParserSelector> secondCompound = splitAfter->releaseTagHistory(); |
686 compoundSelector->appendTagHistory(CSSSelector::SubSelector, simpleSelector)
; | 631 secondCompound->appendTagHistory(CSSSelector::ShadowPseudo, compoundSelector
); |
687 return compoundSelector; | 632 return secondCompound.release(); |
688 } | 633 } |
689 | 634 |
690 } // namespace blink | 635 } // namespace blink |
OLD | NEW |