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