| 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 |