Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(48)

Side by Side Diff: third_party/WebKit/Source/core/css/parser/CSSSelectorParser.cpp

Issue 1574323003: Split compound selector after consume finished. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@upload-base
Patch Set: Removed obsolete serialization hack and fixed unit tests. Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698