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 "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/CSSLocalNameToLowerMaps.h" |
8 #include "core/css/CSSSelectorList.h" | 9 #include "core/css/CSSSelectorList.h" |
9 #include "core/css/StyleSheetContents.h" | 10 #include "core/css/StyleSheetContents.h" |
10 #include "core/frame/UseCounter.h" | 11 #include "core/frame/UseCounter.h" |
11 #include "platform/RuntimeEnabledFeatures.h" | 12 #include "platform/RuntimeEnabledFeatures.h" |
12 | 13 |
13 namespace blink { | 14 namespace blink { |
14 | 15 |
15 void CSSSelectorParser::parseSelector(CSSParserTokenRange range, const CSSParser
Context& context, const AtomicString& defaultNamespace, StyleSheetContents* styl
eSheet, CSSSelectorList& output) | 16 void CSSSelectorParser::parseSelector(CSSParserTokenRange range, const CSSParser
Context& context, const AtomicString& defaultNamespace, StyleSheetContents* styl
eSheet, CSSSelectorList& output) |
16 { | 17 { |
17 CSSSelectorParser parser(context, defaultNamespace, styleSheet); | 18 CSSSelectorParser parser(context, defaultNamespace, styleSheet); |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
99 return selector.release(); | 100 return selector.release(); |
100 } | 101 } |
101 | 102 |
102 PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeCompoundSelector(CSSPars
erTokenRange& range) | 103 PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeCompoundSelector(CSSPars
erTokenRange& range) |
103 { | 104 { |
104 OwnPtr<CSSParserSelector> compoundSelector; | 105 OwnPtr<CSSParserSelector> compoundSelector; |
105 | 106 |
106 AtomicString namespacePrefix; | 107 AtomicString namespacePrefix; |
107 AtomicString elementName; | 108 AtomicString elementName; |
108 bool hasNamespace; | 109 bool hasNamespace; |
| 110 TagSelectorCase tagSelectorCase = TagLowerCase; |
109 if (!consumeName(range, elementName, namespacePrefix, hasNamespace)) { | 111 if (!consumeName(range, elementName, namespacePrefix, hasNamespace)) { |
110 compoundSelector = consumeSimpleSelector(range); | 112 compoundSelector = consumeSimpleSelector(range); |
111 if (!compoundSelector) | 113 if (!compoundSelector) |
112 return nullptr; | 114 return nullptr; |
113 } | 115 } |
114 if (m_context.isHTMLDocument()) | 116 if (m_context.isHTMLDocument() && !elementName.isNull()) { |
115 elementName = elementName.lower(); | 117 if (CSSLocalNameToLowerMaps::elementToLower(elementName).isNull()) |
| 118 elementName = elementName.lower(); |
| 119 else |
| 120 tagSelectorCase = TagCamelCase; |
| 121 } |
116 | 122 |
117 while (OwnPtr<CSSParserSelector> simpleSelector = consumeSimpleSelector(rang
e)) { | 123 while (OwnPtr<CSSParserSelector> simpleSelector = consumeSimpleSelector(rang
e)) { |
118 if (compoundSelector) | 124 if (compoundSelector) |
119 compoundSelector = addSimpleSelectorToCompound(compoundSelector.rele
ase(), simpleSelector.release()); | 125 compoundSelector = addSimpleSelectorToCompound(compoundSelector.rele
ase(), simpleSelector.release()); |
120 else | 126 else |
121 compoundSelector = simpleSelector.release(); | 127 compoundSelector = simpleSelector.release(); |
122 } | 128 } |
123 | 129 |
124 if (!compoundSelector) { | 130 if (!compoundSelector) { |
125 if (hasNamespace) | 131 if (hasNamespace) |
126 return CSSParserSelector::create(determineNameInNamespace(namespaceP
refix, elementName)); | 132 return CSSParserSelector::create(determineNameInNamespace(namespaceP
refix, elementName), false, tagSelectorCase); |
127 return CSSParserSelector::create(QualifiedName(nullAtom, elementName, m_
defaultNamespace)); | 133 return CSSParserSelector::create(QualifiedName(nullAtom, elementName, m_
defaultNamespace), false, tagSelectorCase); |
128 } | 134 } |
129 prependTypeSelectorIfNeeded(namespacePrefix, elementName, compoundSelector.g
et()); | 135 prependTypeSelectorIfNeeded(namespacePrefix, elementName, compoundSelector.g
et(), tagSelectorCase); |
130 return compoundSelector.release(); | 136 return compoundSelector.release(); |
131 } | 137 } |
132 | 138 |
133 PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeSimpleSelector(CSSParser
TokenRange& range) | 139 PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeSimpleSelector(CSSParser
TokenRange& range) |
134 { | 140 { |
135 const CSSParserToken& token = range.peek(); | 141 const CSSParserToken& token = range.peek(); |
136 OwnPtr<CSSParserSelector> selector; | 142 OwnPtr<CSSParserSelector> selector; |
137 if (token.type() == HashToken) | 143 if (token.type() == HashToken) |
138 selector = consumeId(range); | 144 selector = consumeId(range); |
139 else if (token.type() == DelimiterToken && token.delimiter() == '.') | 145 else if (token.type() == DelimiterToken && token.delimiter() == '.') |
(...skipping 371 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
511 return true; | 517 return true; |
512 } | 518 } |
513 | 519 |
514 QualifiedName CSSSelectorParser::determineNameInNamespace(const AtomicString& pr
efix, const AtomicString& localName) | 520 QualifiedName CSSSelectorParser::determineNameInNamespace(const AtomicString& pr
efix, const AtomicString& localName) |
515 { | 521 { |
516 if (!m_styleSheet) | 522 if (!m_styleSheet) |
517 return QualifiedName(prefix, localName, m_defaultNamespace); | 523 return QualifiedName(prefix, localName, m_defaultNamespace); |
518 return QualifiedName(prefix, localName, m_styleSheet->determineNamespace(pre
fix)); | 524 return QualifiedName(prefix, localName, m_styleSheet->determineNamespace(pre
fix)); |
519 } | 525 } |
520 | 526 |
521 void CSSSelectorParser::prependTypeSelectorIfNeeded(const AtomicString& namespac
ePrefix, const AtomicString& elementName, CSSParserSelector* compoundSelector) | 527 void CSSSelectorParser::prependTypeSelectorIfNeeded(const AtomicString& namespac
ePrefix, const AtomicString& elementName, CSSParserSelector* compoundSelector, T
agSelectorCase tagSelectorCase) |
522 { | 528 { |
523 if (elementName.isNull() && m_defaultNamespace == starAtom && !compoundSelec
tor->crossesTreeScopes()) | 529 if (elementName.isNull() && m_defaultNamespace == starAtom && !compoundSelec
tor->crossesTreeScopes()) |
524 return; | 530 return; |
525 | 531 |
526 AtomicString determinedElementName = elementName.isNull() ? starAtom : eleme
ntName; | 532 AtomicString determinedElementName = elementName.isNull() ? starAtom : eleme
ntName; |
527 AtomicString determinedNamespace = namespacePrefix != nullAtom && m_styleShe
et ? m_styleSheet->determineNamespace(namespacePrefix) : m_defaultNamespace; | 533 AtomicString determinedNamespace = namespacePrefix != nullAtom && m_styleShe
et ? m_styleSheet->determineNamespace(namespacePrefix) : m_defaultNamespace; |
528 QualifiedName tag(namespacePrefix, determinedElementName, determinedNamespac
e); | 534 QualifiedName tag(namespacePrefix, determinedElementName, determinedNamespac
e); |
529 | 535 |
530 if (compoundSelector->crossesTreeScopes()) | 536 if (compoundSelector->crossesTreeScopes()) |
531 return rewriteSpecifiersWithElementNameForCustomPseudoElement(tag, compo
undSelector, elementName.isNull()); | 537 return rewriteSpecifiersWithElementNameForCustomPseudoElement(tag, compo
undSelector, elementName.isNull(), tagSelectorCase); |
532 | 538 |
533 if (compoundSelector->isContentPseudoElement()) | 539 if (compoundSelector->isContentPseudoElement()) |
534 return rewriteSpecifiersWithElementNameForContentPseudoElement(tag, comp
oundSelector, elementName.isNull()); | 540 return rewriteSpecifiersWithElementNameForContentPseudoElement(tag, comp
oundSelector, elementName.isNull(), tagSelectorCase); |
535 | 541 |
536 // *:host never matches, so we can't discard the * otherwise we can't tell t
he | 542 // *:host never matches, so we can't discard the * otherwise we can't tell t
he |
537 // difference between *:host and just :host. | 543 // difference between *:host and just :host. |
538 if (tag == anyQName() && !compoundSelector->hasHostPseudoSelector()) | 544 if (tag == anyQName() && !compoundSelector->hasHostPseudoSelector()) |
539 return; | 545 return; |
540 compoundSelector->prependTagSelector(tag, elementName.isNull()); | 546 compoundSelector->prependTagSelector(tag, elementName.isNull(), tagSelectorC
ase); |
541 } | 547 } |
542 | 548 |
543 void CSSSelectorParser::rewriteSpecifiersWithElementNameForCustomPseudoElement(c
onst QualifiedName& tag, CSSParserSelector* specifiers, bool tagIsImplicit) | 549 void CSSSelectorParser::rewriteSpecifiersWithElementNameForCustomPseudoElement(c
onst QualifiedName& tag, CSSParserSelector* specifiers, bool tagIsImplicit, TagS
electorCase tagSelectorCase) |
544 { | 550 { |
545 CSSParserSelector* lastShadowPseudo = specifiers; | 551 CSSParserSelector* lastShadowPseudo = specifiers; |
546 CSSParserSelector* history = specifiers; | 552 CSSParserSelector* history = specifiers; |
547 while (history->tagHistory()) { | 553 while (history->tagHistory()) { |
548 history = history->tagHistory(); | 554 history = history->tagHistory(); |
549 if (history->crossesTreeScopes() || history->hasShadowPseudo()) | 555 if (history->crossesTreeScopes() || history->hasShadowPseudo()) |
550 lastShadowPseudo = history; | 556 lastShadowPseudo = history; |
551 } | 557 } |
552 | 558 |
553 if (lastShadowPseudo->tagHistory()) { | 559 if (lastShadowPseudo->tagHistory()) { |
554 if (tag != anyQName()) | 560 if (tag != anyQName()) |
555 lastShadowPseudo->tagHistory()->prependTagSelector(tag, tagIsImplici
t); | 561 lastShadowPseudo->tagHistory()->prependTagSelector(tag, tagIsImplici
t, tagSelectorCase); |
556 return; | 562 return; |
557 } | 563 } |
558 | 564 |
559 // For shadow-ID pseudo-elements to be correctly matched, the ShadowPseudo c
ombinator has to be used. | 565 // For shadow-ID pseudo-elements to be correctly matched, the ShadowPseudo c
ombinator has to be used. |
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. '*'). | 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. '*'). |
561 OwnPtr<CSSParserSelector> elementNameSelector = CSSParserSelector::create(ta
g); | 567 OwnPtr<CSSParserSelector> elementNameSelector = CSSParserSelector::create(ta
g, false /* isImplicit */, tagSelectorCase); |
562 lastShadowPseudo->setTagHistory(elementNameSelector.release()); | 568 lastShadowPseudo->setTagHistory(elementNameSelector.release()); |
563 lastShadowPseudo->setRelation(CSSSelector::ShadowPseudo); | 569 lastShadowPseudo->setRelation(CSSSelector::ShadowPseudo); |
564 } | 570 } |
565 | 571 |
566 void CSSSelectorParser::rewriteSpecifiersWithElementNameForContentPseudoElement(
const QualifiedName& tag, CSSParserSelector* specifiers, bool tagIsImplicit) | 572 void CSSSelectorParser::rewriteSpecifiersWithElementNameForContentPseudoElement(
const QualifiedName& tag, CSSParserSelector* specifiers, bool tagIsImplicit, Tag
SelectorCase tagSelectorCase) |
567 { | 573 { |
568 CSSParserSelector* last = specifiers; | 574 CSSParserSelector* last = specifiers; |
569 CSSParserSelector* history = specifiers; | 575 CSSParserSelector* history = specifiers; |
570 while (history->tagHistory()) { | 576 while (history->tagHistory()) { |
571 history = history->tagHistory(); | 577 history = history->tagHistory(); |
572 if (history->isContentPseudoElement() || history->relationIsAffectedByPs
eudoContent()) | 578 if (history->isContentPseudoElement() || history->relationIsAffectedByPs
eudoContent()) |
573 last = history; | 579 last = history; |
574 } | 580 } |
575 | 581 |
576 if (last->tagHistory()) { | 582 if (last->tagHistory()) { |
577 if (tag != anyQName()) | 583 if (tag != anyQName()) |
578 last->tagHistory()->prependTagSelector(tag, tagIsImplicit); | 584 last->tagHistory()->prependTagSelector(tag, tagIsImplicit, tagSelect
orCase); |
579 return; | 585 return; |
580 } | 586 } |
581 | 587 |
582 // For shadow-ID pseudo-elements to be correctly matched, the ShadowPseudo c
ombinator has to be used. | 588 // For shadow-ID pseudo-elements to be correctly matched, the ShadowPseudo c
ombinator has to be used. |
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. '*'). | 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. '*'). |
584 OwnPtr<CSSParserSelector> elementNameSelector = CSSParserSelector::create(ta
g); | 590 OwnPtr<CSSParserSelector> elementNameSelector = CSSParserSelector::create(ta
g, false /* isImplicit */, tagSelectorCase); |
585 last->setTagHistory(elementNameSelector.release()); | 591 last->setTagHistory(elementNameSelector.release()); |
586 } | 592 } |
587 | 593 |
588 PassOwnPtr<CSSParserSelector> CSSSelectorParser::addSimpleSelectorToCompound(Pas
sOwnPtr<CSSParserSelector> compoundSelector, PassOwnPtr<CSSParserSelector> simpl
eSelector) | 594 PassOwnPtr<CSSParserSelector> CSSSelectorParser::addSimpleSelectorToCompound(Pas
sOwnPtr<CSSParserSelector> compoundSelector, PassOwnPtr<CSSParserSelector> simpl
eSelector) |
589 { | 595 { |
590 // The tagHistory is a linked list that stores combinator separated compound
selectors | 596 // The tagHistory is a linked list that stores combinator separated compound
selectors |
591 // from right-to-left. Yet, within a single compound selector, stores the si
mple selectors | 597 // from right-to-left. Yet, within a single compound selector, stores the si
mple selectors |
592 // from left-to-right. | 598 // from left-to-right. |
593 // | 599 // |
594 // ".a.b > div#id" is stored in a tagHistory as [div, #id, .a, .b], each ele
ment in the | 600 // ".a.b > div#id" is stored in a tagHistory as [div, #id, .a, .b], each ele
ment in the |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
675 context.useCounter()->count(feature); | 681 context.useCounter()->count(feature); |
676 if (current->relation() == CSSSelector::ShadowDeep) | 682 if (current->relation() == CSSSelector::ShadowDeep) |
677 context.useCounter()->count(UseCounter::CSSDeepCombinator); | 683 context.useCounter()->count(UseCounter::CSSDeepCombinator); |
678 if (current->selectorList()) | 684 if (current->selectorList()) |
679 recordSelectorStats(context, *current->selectorList()); | 685 recordSelectorStats(context, *current->selectorList()); |
680 } | 686 } |
681 } | 687 } |
682 } | 688 } |
683 | 689 |
684 } // namespace blink | 690 } // namespace blink |
OLD | NEW |