| Index: Source/core/css/parser/CSSSelectorParser.cpp
|
| diff --git a/Source/core/css/parser/CSSSelectorParser.cpp b/Source/core/css/parser/CSSSelectorParser.cpp
|
| index 4e0e0965f8627611325636949d96da26755a890d..284a0a114014391975101ef7a4ee15537aec8551 100644
|
| --- a/Source/core/css/parser/CSSSelectorParser.cpp
|
| +++ b/Source/core/css/parser/CSSSelectorParser.cpp
|
| @@ -217,8 +217,43 @@ PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeClass()
|
|
|
| PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeAttribute()
|
| {
|
| - // FIXME: Implement attribute parsing
|
| - return nullptr;
|
| + ASSERT(m_tokenRange.peek().type() == LeftBracketToken);
|
| + m_tokenRange.consumeIncludingWhitespaceAndComments();
|
| +
|
| + AtomicString namespacePrefix;
|
| + AtomicString attributeName;
|
| + bool hasNamespace;
|
| + if (!consumeName(attributeName, namespacePrefix, hasNamespace))
|
| + return nullptr;
|
| +
|
| + if (m_context.isHTMLDocument())
|
| + attributeName = attributeName.lower();
|
| +
|
| + QualifiedName qualifiedName = hasNamespace
|
| + ? determineNameInNamespace(namespacePrefix, attributeName)
|
| + : QualifiedName(nullAtom, attributeName, nullAtom);
|
| +
|
| + OwnPtr<CSSParserSelector> selector = CSSParserSelector::create();
|
| +
|
| + if (m_tokenRange.atEnd() || m_tokenRange.peek().type() == RightBracketToken) {
|
| + m_tokenRange.consumeIncludingComments();
|
| + selector->setAttribute(qualifiedName, CSSSelector::CaseSensitive);
|
| + selector->setMatch(CSSSelector::AttributeSet);
|
| + return selector.release();
|
| + }
|
| +
|
| + selector->setMatch(consumeAttributeMatch());
|
| +
|
| + const CSSParserToken& attributeValue = m_tokenRange.consumeIncludingWhitespaceAndComments();
|
| + if (attributeValue.type() != IdentToken && attributeValue.type() != StringToken)
|
| + return nullptr;
|
| + selector->setValue(AtomicString(attributeValue.value()));
|
| + selector->setAttribute(qualifiedName, consumeAttributeFlags());
|
| +
|
| + if (!m_tokenRange.atEnd() && m_tokenRange.consumeIncludingComments().type() != RightBracketToken)
|
| + return nullptr;
|
| +
|
| + return selector.release();
|
| }
|
|
|
| PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumePseudo()
|
| @@ -262,6 +297,42 @@ CSSSelector::Relation CSSSelectorParser::consumeCombinator()
|
| return CSSSelector::ShadowDeep;
|
| }
|
|
|
| +CSSSelector::Match CSSSelectorParser::consumeAttributeMatch()
|
| +{
|
| + const CSSParserToken& token = m_tokenRange.consumeIncludingWhitespaceAndComments();
|
| + switch (token.type()) {
|
| + case IncludeMatchToken:
|
| + return CSSSelector::AttributeList;
|
| + case DashMatchToken:
|
| + return CSSSelector::AttributeHyphen;
|
| + case PrefixMatchToken:
|
| + return CSSSelector::AttributeBegin;
|
| + case SuffixMatchToken:
|
| + return CSSSelector::AttributeEnd;
|
| + case SubstringMatchToken:
|
| + return CSSSelector::AttributeContain;
|
| + case DelimiterToken:
|
| + if (token.delimiter() == '=')
|
| + return CSSSelector::AttributeExact;
|
| + default:
|
| + m_failedParsing = true;
|
| + return CSSSelector::AttributeExact;
|
| + }
|
| +}
|
| +
|
| +CSSSelector::AttributeMatchType CSSSelectorParser::consumeAttributeFlags()
|
| +{
|
| + if (m_tokenRange.peek().type() != IdentToken)
|
| + return CSSSelector::CaseSensitive;
|
| + const CSSParserToken& flag = m_tokenRange.consumeIncludingWhitespaceAndComments();
|
| + if (flag.value() == "i") {
|
| + if (RuntimeEnabledFeatures::cssAttributeCaseSensitivityEnabled() || isUASheetBehavior(m_context.mode()))
|
| + return CSSSelector::CaseInsensitive;
|
| + }
|
| + m_failedParsing = true;
|
| + return CSSSelector::CaseSensitive;
|
| +}
|
| +
|
| QualifiedName CSSSelectorParser::determineNameInNamespace(const AtomicString& prefix, const AtomicString& localName)
|
| {
|
| if (!m_styleSheet)
|
|
|