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

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

Issue 1094213002: Refactored compound selector parsing methods. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 5 years, 8 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 "config.h" 5 #include "config.h"
6 #include "core/css/parser/CSSSelectorParser.h" 6 #include "core/css/parser/CSSSelectorParser.h"
7 7
8 #include "core/css/CSSSelectorList.h" 8 #include "core/css/CSSSelectorList.h"
9 #include "core/css/StyleSheetContents.h" 9 #include "core/css/StyleSheetContents.h"
10 #include "core/frame/UseCounter.h" 10 #include "core/frame/UseCounter.h"
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
94 end->setTagHistory(selector.release()); 94 end->setTagHistory(selector.release());
95 95
96 selector = nextSelector.release(); 96 selector = nextSelector.release();
97 } 97 }
98 98
99 return selector.release(); 99 return selector.release();
100 } 100 }
101 101
102 PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeCompoundSelector(CSSPars erTokenRange& range) 102 PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeCompoundSelector(CSSPars erTokenRange& range)
103 { 103 {
104 OwnPtr<CSSParserSelector> selector; 104 OwnPtr<CSSParserSelector> compoundSelector;
105 105
106 AtomicString namespacePrefix; 106 AtomicString namespacePrefix;
107 AtomicString elementName; 107 AtomicString elementName;
108 bool hasNamespace; 108 bool hasNamespace;
109 if (!consumeName(range, elementName, namespacePrefix, hasNamespace)) { 109 if (!consumeName(range, elementName, namespacePrefix, hasNamespace)) {
110 selector = consumeSimpleSelector(range); 110 compoundSelector = consumeSimpleSelector(range);
111 if (!selector) 111 if (!compoundSelector)
112 return nullptr; 112 return nullptr;
113 } 113 }
114 if (m_context.isHTMLDocument()) 114 if (m_context.isHTMLDocument())
115 elementName = elementName.lower(); 115 elementName = elementName.lower();
116 116
117 while (OwnPtr<CSSParserSelector> nextSelector = consumeSimpleSelector(range) ) { 117 while (OwnPtr<CSSParserSelector> simpleSelector = consumeSimpleSelector(rang e)) {
118 if (selector) 118 if (compoundSelector)
119 selector = rewriteSpecifiers(selector.release(), nextSelector.releas e()); 119 compoundSelector = addSimpleSelectorToCompound(compoundSelector.rele ase(), simpleSelector.release());
120 else 120 else
121 selector = nextSelector.release(); 121 compoundSelector = simpleSelector.release();
122 } 122 }
123 123
124 if (!selector) { 124 if (!compoundSelector) {
125 if (hasNamespace) 125 if (hasNamespace)
126 return CSSParserSelector::create(determineNameInNamespace(namespaceP refix, elementName)); 126 return CSSParserSelector::create(determineNameInNamespace(namespaceP refix, elementName));
127 return CSSParserSelector::create(QualifiedName(nullAtom, elementName, m_ defaultNamespace)); 127 return CSSParserSelector::create(QualifiedName(nullAtom, elementName, m_ defaultNamespace));
128 } 128 }
129 if (elementName.isNull()) 129 prependTypeSelectorIfNeeded(namespacePrefix, elementName, compoundSelector.g et());
130 rewriteSpecifiersWithNamespaceIfNeeded(selector.get()); 130 return compoundSelector.release();
131 else
132 rewriteSpecifiersWithElementName(namespacePrefix, elementName, selector. get());
133 return selector.release();
134 } 131 }
135 132
136 PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeSimpleSelector(CSSParser TokenRange& range) 133 PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeSimpleSelector(CSSParser TokenRange& range)
137 { 134 {
138 const CSSParserToken& token = range.peek(); 135 const CSSParserToken& token = range.peek();
139 OwnPtr<CSSParserSelector> selector; 136 OwnPtr<CSSParserSelector> selector;
140 if (token.type() == HashToken) 137 if (token.type() == HashToken)
141 selector = consumeId(range); 138 selector = consumeId(range);
142 else if (token.type() == DelimiterToken && token.delimiter() == '.') 139 else if (token.type() == DelimiterToken && token.delimiter() == '.')
143 selector = consumeClass(range); 140 selector = consumeClass(range);
(...skipping 370 matching lines...) Expand 10 before | Expand all | Expand 10 after
514 return true; 511 return true;
515 } 512 }
516 513
517 QualifiedName CSSSelectorParser::determineNameInNamespace(const AtomicString& pr efix, const AtomicString& localName) 514 QualifiedName CSSSelectorParser::determineNameInNamespace(const AtomicString& pr efix, const AtomicString& localName)
518 { 515 {
519 if (!m_styleSheet) 516 if (!m_styleSheet)
520 return QualifiedName(prefix, localName, m_defaultNamespace); 517 return QualifiedName(prefix, localName, m_defaultNamespace);
521 return QualifiedName(prefix, localName, m_styleSheet->determineNamespace(pre fix)); 518 return QualifiedName(prefix, localName, m_styleSheet->determineNamespace(pre fix));
522 } 519 }
523 520
524 void CSSSelectorParser::rewriteSpecifiersWithNamespaceIfNeeded(CSSParserSelector * specifiers) 521 void CSSSelectorParser::prependTypeSelectorIfNeeded(const AtomicString& namespac ePrefix, const AtomicString& elementName, CSSParserSelector* compoundSelector)
525 { 522 {
526 if (m_defaultNamespace != starAtom || specifiers->crossesTreeScopes()) 523 if (elementName.isNull() && m_defaultNamespace == starAtom && !compoundSelec tor->crossesTreeScopes())
527 rewriteSpecifiersWithElementName(nullAtom, starAtom, specifiers, /*tagIs ForNamespaceRule*/true); 524 return;
528 }
529 525
530 void CSSSelectorParser::rewriteSpecifiersWithElementName(const AtomicString& nam espacePrefix, const AtomicString& elementName, CSSParserSelector* specifiers, bo ol tagIsForNamespaceRule) 526 AtomicString determinedElementName = elementName.isNull() ? starAtom : eleme ntName;
531 {
532 AtomicString determinedNamespace = namespacePrefix != nullAtom && m_styleShe et ? m_styleSheet->determineNamespace(namespacePrefix) : m_defaultNamespace; 527 AtomicString determinedNamespace = namespacePrefix != nullAtom && m_styleShe et ? m_styleSheet->determineNamespace(namespacePrefix) : m_defaultNamespace;
533 QualifiedName tag(namespacePrefix, elementName, determinedNamespace); 528 QualifiedName tag(namespacePrefix, determinedElementName, determinedNamespac e);
534 529
535 if (specifiers->crossesTreeScopes()) 530 if (compoundSelector->crossesTreeScopes())
536 return rewriteSpecifiersWithElementNameForCustomPseudoElement(tag, speci fiers, tagIsForNamespaceRule); 531 return rewriteSpecifiersWithElementNameForCustomPseudoElement(tag, compo undSelector, elementName.isNull());
537 532
538 if (specifiers->isContentPseudoElement()) 533 if (compoundSelector->isContentPseudoElement())
539 return rewriteSpecifiersWithElementNameForContentPseudoElement(tag, spec ifiers, tagIsForNamespaceRule); 534 return rewriteSpecifiersWithElementNameForContentPseudoElement(tag, comp oundSelector, elementName.isNull());
540 535
541 // *:host never matches, so we can't discard the * otherwise we can't tell t he 536 // *:host never matches, so we can't discard the * otherwise we can't tell t he
542 // difference between *:host and just :host. 537 // difference between *:host and just :host.
543 if (tag == anyQName() && !specifiers->hasHostPseudoSelector()) 538 if (tag == anyQName() && !compoundSelector->hasHostPseudoSelector())
544 return; 539 return;
545 if (specifiers->pseudoType() != CSSSelector::PseudoCue) 540 compoundSelector->prependTagSelector(tag, elementName.isNull());
546 specifiers->prependTagSelector(tag, tagIsForNamespaceRule);
547 } 541 }
548 542
549 void CSSSelectorParser::rewriteSpecifiersWithElementNameForCustomPseudoElement(c onst QualifiedName& tag, CSSParserSelector* specifiers, bool tagIsForNamespaceRu le) 543 void CSSSelectorParser::rewriteSpecifiersWithElementNameForCustomPseudoElement(c onst QualifiedName& tag, CSSParserSelector* specifiers, bool tagIsImplicit)
550 { 544 {
551 CSSParserSelector* lastShadowPseudo = specifiers; 545 CSSParserSelector* lastShadowPseudo = specifiers;
552 CSSParserSelector* history = specifiers; 546 CSSParserSelector* history = specifiers;
553 while (history->tagHistory()) { 547 while (history->tagHistory()) {
554 history = history->tagHistory(); 548 history = history->tagHistory();
555 if (history->crossesTreeScopes() || history->hasShadowPseudo()) 549 if (history->crossesTreeScopes() || history->hasShadowPseudo())
556 lastShadowPseudo = history; 550 lastShadowPseudo = history;
557 } 551 }
558 552
559 if (lastShadowPseudo->tagHistory()) { 553 if (lastShadowPseudo->tagHistory()) {
560 if (tag != anyQName()) 554 if (tag != anyQName())
561 lastShadowPseudo->tagHistory()->prependTagSelector(tag, tagIsForName spaceRule); 555 lastShadowPseudo->tagHistory()->prependTagSelector(tag, tagIsImplici t);
562 return; 556 return;
563 } 557 }
564 558
565 // For shadow-ID pseudo-elements to be correctly matched, the ShadowPseudo c ombinator has to be used. 559 // For shadow-ID pseudo-elements to be correctly matched, the ShadowPseudo c ombinator has to be used.
566 // We therefore create a new Selector with that combinator here in any case, even if matching any (host) element in any namespace (i.e. '*'). 560 // We therefore create a new Selector with that combinator here in any case, even if matching any (host) element in any namespace (i.e. '*').
567 OwnPtr<CSSParserSelector> elementNameSelector = adoptPtr(new CSSParserSelect or(tag)); 561 OwnPtr<CSSParserSelector> elementNameSelector = CSSParserSelector::create(ta g);
568 lastShadowPseudo->setTagHistory(elementNameSelector.release()); 562 lastShadowPseudo->setTagHistory(elementNameSelector.release());
569 lastShadowPseudo->setRelation(CSSSelector::ShadowPseudo); 563 lastShadowPseudo->setRelation(CSSSelector::ShadowPseudo);
570 } 564 }
571 565
572 void CSSSelectorParser::rewriteSpecifiersWithElementNameForContentPseudoElement( const QualifiedName& tag, CSSParserSelector* specifiers, bool tagIsForNamespaceR ule) 566 void CSSSelectorParser::rewriteSpecifiersWithElementNameForContentPseudoElement( const QualifiedName& tag, CSSParserSelector* specifiers, bool tagIsImplicit)
573 { 567 {
574 CSSParserSelector* last = specifiers; 568 CSSParserSelector* last = specifiers;
575 CSSParserSelector* history = specifiers; 569 CSSParserSelector* history = specifiers;
576 while (history->tagHistory()) { 570 while (history->tagHistory()) {
577 history = history->tagHistory(); 571 history = history->tagHistory();
578 if (history->isContentPseudoElement() || history->relationIsAffectedByPs eudoContent()) 572 if (history->isContentPseudoElement() || history->relationIsAffectedByPs eudoContent())
579 last = history; 573 last = history;
580 } 574 }
581 575
582 if (last->tagHistory()) { 576 if (last->tagHistory()) {
583 if (tag != anyQName()) 577 if (tag != anyQName())
584 last->tagHistory()->prependTagSelector(tag, tagIsForNamespaceRule); 578 last->tagHistory()->prependTagSelector(tag, tagIsImplicit);
585 return; 579 return;
586 } 580 }
587 581
588 // For shadow-ID pseudo-elements to be correctly matched, the ShadowPseudo c ombinator has to be used. 582 // For shadow-ID pseudo-elements to be correctly matched, the ShadowPseudo c ombinator has to be used.
589 // We therefore create a new Selector with that combinator here in any case, even if matching any (host) element in any namespace (i.e. '*'). 583 // We therefore create a new Selector with that combinator here in any case, even if matching any (host) element in any namespace (i.e. '*').
590 OwnPtr<CSSParserSelector> elementNameSelector = adoptPtr(new CSSParserSelect or(tag)); 584 OwnPtr<CSSParserSelector> elementNameSelector = CSSParserSelector::create(ta g);
591 last->setTagHistory(elementNameSelector.release()); 585 last->setTagHistory(elementNameSelector.release());
592 } 586 }
593 587
594 PassOwnPtr<CSSParserSelector> CSSSelectorParser::rewriteSpecifiers(PassOwnPtr<CS SParserSelector> specifiers, PassOwnPtr<CSSParserSelector> newSpecifier) 588 PassOwnPtr<CSSParserSelector> CSSSelectorParser::addSimpleSelectorToCompound(Pas sOwnPtr<CSSParserSelector> compoundSelector, PassOwnPtr<CSSParserSelector> simpl eSelector)
595 { 589 {
596 if (newSpecifier->crossesTreeScopes()) { 590 // The tagHistory is a linked list that stores combinator separated compound selectors
597 // Unknown pseudo element always goes at the top of selector chain. 591 // from right-to-left. Yet, within a single compound selector, stores the si mple selectors
598 newSpecifier->appendTagHistory(CSSSelector::ShadowPseudo, specifiers); 592 // from left-to-right.
599 return newSpecifier; 593 //
594 // ".a.b > div#id" is stored in a tagHistory as [div, #id, .a, .b], each ele ment in the
595 // list stored with an associated relation (combinator or SubSelector).
596 //
597 // ::cue, ::shadow, and custom pseudo elements have an implicit ShadowPseudo combinator
598 // to their left, which really makes for a new compound selector, yet it's c onsumed by
599 // the selector parser as a single compound selector.
600 //
601 // Example: input#x::-webkit-clear-button -> [ ::-webkit-clear-button, input , #x ]
602 //
603 // ::content is kept at the end of the compound in order easily know when to call
604 // setRelationIsAffectedByPseudoContent.
605 //
606 // We are currently not dropping selectors containing multiple instances of ::content,
607 // ::shadow, ::cue, and custom pseudo elements in arbitrary order. There are known
608 // issues like crbug.com/478563
609 //
610 // TODO(rune@opera.com): We could probably handle the implicit combinator as a combinator
611 // in the selector parser by pushing a token for it and exit the compound co nsumption.
Timothy Loh 2015/04/21 01:25:10 Not sure if this is a good idea. Maybe we can come
rune 2015/04/21 09:00:08 I've modified the comment to say that instead.
612 // That way, we could get rid of re-ordering tricks like the two remaining r ewrite*
613 // methods and the code below.
614 //
615 // The code below is to keep ::content at the end of the compound, and too k eep the
Timothy Loh 2015/04/21 01:25:10 typo: too instead of to
rune 2015/04/21 09:00:08 Done.
616 // tagHistory order correct for implicit ShadowPseudo and juggling multiple (two?)
617 // compounds.
618
619 CSSSelector::Relation relation = CSSSelector::SubSelector;
620
621 if (simpleSelector->crossesTreeScopes() || simpleSelector->isContentPseudoEl ement()) {
622 if (simpleSelector->crossesTreeScopes())
623 relation = CSSSelector::ShadowPseudo;
624 simpleSelector->appendTagHistory(relation, compoundSelector);
625 return simpleSelector;
600 } 626 }
601 if (newSpecifier->isContentPseudoElement()) { 627 if (compoundSelector->crossesTreeScopes() || compoundSelector->isContentPseu doElement()) {
602 newSpecifier->appendTagHistory(CSSSelector::SubSelector, specifiers); 628 if (compoundSelector->crossesTreeScopes())
603 return newSpecifier; 629 relation = CSSSelector::ShadowPseudo;
630 compoundSelector->insertTagHistory(CSSSelector::SubSelector, simpleSelec tor, relation);
631 return compoundSelector;
604 } 632 }
605 if (specifiers->crossesTreeScopes()) { 633
606 // Specifiers for unknown pseudo element go right behind it in the chain . 634 // All other simple selectors are added to the end of the compound.
607 specifiers->insertTagHistory(CSSSelector::SubSelector, newSpecifier, CSS Selector::ShadowPseudo); 635 compoundSelector->appendTagHistory(CSSSelector::SubSelector, simpleSelector) ;
608 return specifiers; 636 return compoundSelector;
609 }
610 if (specifiers->isContentPseudoElement()) {
611 specifiers->insertTagHistory(CSSSelector::SubSelector, newSpecifier, CSS Selector::SubSelector);
612 return specifiers;
613 }
614 specifiers->appendTagHistory(CSSSelector::SubSelector, newSpecifier);
615 return specifiers;
616 } 637 }
617 638
618 void CSSSelectorParser::recordSelectorStats(const CSSParserContext& context, con st CSSSelectorList& selectorList) 639 void CSSSelectorParser::recordSelectorStats(const CSSParserContext& context, con st CSSSelectorList& selectorList)
619 { 640 {
620 if (!context.useCounter()) 641 if (!context.useCounter())
621 return; 642 return;
622 643
623 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector)) { 644 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector)) {
624 for (const CSSSelector* current = selector; current ; current = current- >tagHistory()) { 645 for (const CSSSelector* current = selector; current ; current = current- >tagHistory()) {
625 UseCounter::Feature feature = UseCounter::NumberOfFeatures; 646 UseCounter::Feature feature = UseCounter::NumberOfFeatures;
(...skipping 29 matching lines...) Expand all
655 context.useCounter()->count(feature); 676 context.useCounter()->count(feature);
656 if (current->relation() == CSSSelector::ShadowDeep) 677 if (current->relation() == CSSSelector::ShadowDeep)
657 context.useCounter()->count(UseCounter::CSSDeepCombinator); 678 context.useCounter()->count(UseCounter::CSSDeepCombinator);
658 if (current->selectorList()) 679 if (current->selectorList())
659 recordSelectorStats(context, *current->selectorList()); 680 recordSelectorStats(context, *current->selectorList());
660 } 681 }
661 } 682 }
662 } 683 }
663 684
664 } // namespace blink 685 } // namespace blink
OLDNEW
« no previous file with comments | « Source/core/css/parser/CSSSelectorParser.h ('k') | Source/core/css/parser/CSSSelectorParserTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698