Commit 69b7d1c1 authored by Darren Shen's avatar Darren Shen Committed by Commit Bot

Do not treat escaped asterisk as universal selector.

This patch fixes an issue where an escaped asterisk character is being
treated as a universal selector. The problem was that \* and * were
represented as g_star_atom (the '*' character) in selector matching.

To differentiate between the two, we picked a different way to represent
universal selectors. The local name of a universal selector becomes
g_null_atom instead of g_star_atom. This way, \* and * will no longer
clash.

Bug: 682747
Change-Id: Ied14b468c8bfae6ed1022a565f4f46cea4c556a8
Reviewed-on: https://chromium-review.googlesource.com/765626Reviewed-by: default avatarRune Lillesveen <futhark@chromium.org>
Commit-Queue: Darren Shen <shend@chromium.org>
Cr-Commit-Position: refs/heads/master@{#521556}
parent a846ad7d
......@@ -4,6 +4,8 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
PASS parseThenSerializeRule('* { }') is '* { }'
PASS parseThenSerializeRule('\\* { }') is '\\* { }'
PASS parseThenSerializeRule('\\2a { }') is '\\* { }'
PASS parseThenSerializeRule('a { }') is 'a { }'
PASS parseThenSerializeRule('#a { }') is '#a { }'
PASS parseThenSerializeRule('.a { }') is '.a { }'
......
......@@ -30,6 +30,8 @@ function testSelectorRoundTrip(selector)
}
testSelectorRoundTrip('*');
testSelectorRoundTrip('\\\\*');
shouldBe("parseThenSerializeRule('\\\\2a { }')", "'\\\\* { }'");
testSelectorRoundTrip('a');
testSelectorRoundTrip('#a');
testSelectorRoundTrip('.a');
......
......@@ -131,7 +131,7 @@ inline unsigned CSSSelector::SpecificityForOneSelector() const {
case kAttributeEnd:
return 0x000100;
case kTag:
if (TagQName().LocalName() == g_star_atom)
if (TagQName().LocalName() == UniversalSelectorAtom())
return 0;
return 0x000001;
case kUnknown:
......@@ -149,7 +149,7 @@ unsigned CSSSelector::SpecificityForPage() const {
component = component->TagHistory()) {
switch (component->match_) {
case kTag:
s += TagQName().LocalName() == g_star_atom ? 0 : 4;
s += TagQName().LocalName() == UniversalSelectorAtom() ? 0 : 4;
break;
case kPagePseudoClass:
switch (component->GetPseudoType()) {
......@@ -664,26 +664,29 @@ bool CSSSelector::operator==(const CSSSelector& other) const {
}
static void SerializeIdentifierOrAny(const AtomicString& identifier,
const AtomicString& any,
StringBuilder& builder) {
if (identifier != g_star_atom)
if (identifier != any)
SerializeIdentifier(identifier, builder);
else
builder.Append(identifier);
builder.Append(g_star_atom);
}
static void SerializeNamespacePrefixIfNeeded(const AtomicString& prefix,
const AtomicString& any,
StringBuilder& builder) {
if (prefix.IsNull())
return;
SerializeIdentifierOrAny(prefix, builder);
SerializeIdentifierOrAny(prefix, any, builder);
builder.Append('|');
}
const CSSSelector* CSSSelector::SerializeCompound(
StringBuilder& builder) const {
if (match_ == kTag && !tag_is_implicit_) {
SerializeNamespacePrefixIfNeeded(TagQName().Prefix(), builder);
SerializeIdentifierOrAny(TagQName().LocalName(), builder);
SerializeNamespacePrefixIfNeeded(TagQName().Prefix(), g_star_atom, builder);
SerializeIdentifierOrAny(TagQName().LocalName(), UniversalSelectorAtom(),
builder);
}
for (const CSSSelector* simple_selector = this; simple_selector;
......@@ -744,7 +747,7 @@ const CSSSelector* CSSSelector::SerializeCompound(
} else if (simple_selector->IsAttributeSelector()) {
builder.Append('[');
SerializeNamespacePrefixIfNeeded(simple_selector->Attribute().Prefix(),
builder);
g_star_atom, builder);
SerializeIdentifier(simple_selector->Attribute().LocalName(), builder);
switch (simple_selector->match_) {
case kAttributeExact:
......
......@@ -263,6 +263,7 @@ class CORE_EXPORT CSSSelector {
: const_cast<CSSSelector*>(this + 1);
}
static const AtomicString& UniversalSelectorAtom() { return g_null_atom; }
const QualifiedName& TagQName() const;
const AtomicString& Value() const;
const AtomicString& SerializingValue() const;
......
......@@ -95,7 +95,8 @@ static bool CheckPageSelectorComponents(const CSSSelector* selector,
component = component->TagHistory()) {
if (component->Match() == CSSSelector::kTag) {
const AtomicString& local_name = component->TagQName().LocalName();
if (local_name != g_star_atom && local_name != page_name)
DCHECK_NE(local_name, CSSSelector::UniversalSelectorAtom());
if (local_name != page_name)
return false;
}
......
......@@ -383,7 +383,7 @@ void RuleFeatureSet::ExtractInvalidationSetFeaturesFromSimpleSelector(
const CSSSelector& selector,
InvalidationSetFeatures& features) {
if (selector.Match() == CSSSelector::kTag &&
selector.TagQName().LocalName() != g_star_atom) {
selector.TagQName().LocalName() != CSSSelector::UniversalSelectorAtom()) {
features.tag_names.push_back(selector.TagQName().LocalName());
return;
}
......
......@@ -107,7 +107,8 @@ static void ExtractSelectorValues(const CSSSelector* selector,
class_name = selector->Value();
break;
case CSSSelector::kTag:
if (selector->TagQName().LocalName() != g_star_atom)
if (selector->TagQName().LocalName() !=
CSSSelector::UniversalSelectorAtom())
tag_name = selector->TagQName().LocalName();
break;
case CSSSelector::kPseudoClass:
......
......@@ -89,7 +89,8 @@ static bool MatchesTagName(const Element& element,
if (tag_q_name == AnyQName())
return true;
const AtomicString& local_name = tag_q_name.LocalName();
if (local_name != g_star_atom && local_name != element.localName()) {
if (local_name != CSSSelector::UniversalSelectorAtom() &&
local_name != element.localName()) {
if (element.IsHTMLElement() || !element.GetDocument().IsHTMLDocument())
return false;
// Non-html elements in html documents are normalized to their camel-cased
......@@ -613,7 +614,7 @@ static bool AnyAttributeMatches(Element& element,
const CSSSelector& selector) {
const QualifiedName& selector_attr = selector.Attribute();
// Should not be possible from the CSS grammar.
DCHECK_NE(selector_attr.LocalName(), g_star_atom);
DCHECK_NE(selector_attr.LocalName(), CSSSelector::UniversalSelectorAtom());
// Synchronize the attribute in case it is lazy-computed.
// Currently all lazy properties have a null namespace, so only pass
......
......@@ -136,7 +136,8 @@ static inline void CollectDescendantSelectorIdentifierHashes(
selector.Value().Impl()->ExistingHash() * kClassAttributeSalt;
break;
case CSSSelector::kTag:
if (selector.TagQName().LocalName() != g_star_atom)
if (selector.TagQName().LocalName() !=
CSSSelector::UniversalSelectorAtom())
(*hash++) = selector.TagQName().LocalName().Impl()->ExistingHash() *
kTagNameSalt;
break;
......
......@@ -73,7 +73,8 @@ bool CSSParserSelector::IsSimple() const {
// Example:
// @namespace "http://www.w3.org/2000/svg";
// svg:not(:root) { ...
if (selector_->TagQName().LocalName() == g_star_atom)
if (selector_->TagQName().LocalName() ==
CSSSelector::UniversalSelectorAtom())
return tag_history_->IsSimple();
}
......
......@@ -284,7 +284,8 @@ std::unique_ptr<CSSParserSelector> CSSSelectorParser::ConsumeCompoundSelector(
AtomicString namespace_prefix;
AtomicString element_name;
CSSSelector::PseudoType compound_pseudo_element = CSSSelector::kPseudoUnknown;
if (!ConsumeName(range, element_name, namespace_prefix)) {
const bool has_q_name = ConsumeName(range, element_name, namespace_prefix);
if (!has_q_name) {
compound_selector = ConsumeSimpleSelector(range);
if (!compound_selector)
return nullptr;
......@@ -333,7 +334,7 @@ std::unique_ptr<CSSParserSelector> CSSSelectorParser::ConsumeCompoundSelector(
// :not(), since a type selector will be prepended at the top level of the
// selector if necessary. We need to propagate that context information here
// to tell if we are at the top level.
PrependTypeSelectorIfNeeded(namespace_prefix, element_name,
PrependTypeSelectorIfNeeded(namespace_prefix, has_q_name, element_name,
compound_selector.get());
return SplitCompoundAtImplicitShadowCrossingCombinator(
std::move(compound_selector));
......@@ -370,7 +371,7 @@ bool CSSSelectorParser::ConsumeName(CSSParserTokenRange& range,
range.Consume();
} else if (first_token.GetType() == kDelimiterToken &&
first_token.Delimiter() == '*') {
name = g_star_atom;
name = CSSSelector::UniversalSelectorAtom();
range.Consume();
} else if (first_token.GetType() == kDelimiterToken &&
first_token.Delimiter() == '|') {
......@@ -385,13 +386,14 @@ bool CSSSelectorParser::ConsumeName(CSSParserTokenRange& range,
return true;
range.Consume();
namespace_prefix = name;
namespace_prefix =
name == CSSSelector::UniversalSelectorAtom() ? g_star_atom : name;
const CSSParserToken& name_token = range.Consume();
if (name_token.GetType() == kIdentToken) {
name = name_token.Value().ToAtomicString();
} else if (name_token.GetType() == kDelimiterToken &&
name_token.Delimiter() == '*') {
name = g_star_atom;
name = CSSSelector::UniversalSelectorAtom();
} else {
name = g_null_atom;
namespace_prefix = g_null_atom;
......@@ -439,7 +441,7 @@ std::unique_ptr<CSSParserSelector> CSSSelectorParser::ConsumeAttribute(
AtomicString attribute_name;
if (!ConsumeName(block, attribute_name, namespace_prefix))
return nullptr;
if (attribute_name == g_star_atom)
if (attribute_name == CSSSelector::UniversalSelectorAtom())
return nullptr;
block.ConsumeWhitespace();
......@@ -799,14 +801,15 @@ const AtomicString& CSSSelectorParser::DetermineNamespace(
void CSSSelectorParser::PrependTypeSelectorIfNeeded(
const AtomicString& namespace_prefix,
bool has_q_name,
const AtomicString& element_name,
CSSParserSelector* compound_selector) {
if (element_name.IsNull() && DefaultNamespace() == g_star_atom &&
if (!has_q_name && DefaultNamespace() == g_star_atom &&
!compound_selector->NeedsImplicitShadowCombinatorForMatching())
return;
AtomicString determined_element_name =
element_name.IsNull() ? g_star_atom : element_name;
!has_q_name ? CSSSelector::UniversalSelectorAtom() : element_name;
AtomicString namespace_uri = DetermineNamespace(namespace_prefix);
if (namespace_uri.IsNull()) {
failed_parsing_ = true;
......@@ -827,13 +830,15 @@ void CSSSelectorParser::PrependTypeSelectorIfNeeded(
// (relation) on in the cases where there are no simple selectors preceding
// the pseudo element.
bool is_host_pseudo = compound_selector->IsHostPseudoSelector();
if (is_host_pseudo && element_name.IsNull() && namespace_prefix.IsNull())
if (is_host_pseudo && !has_q_name && namespace_prefix.IsNull())
return;
if (tag != AnyQName() || is_host_pseudo ||
compound_selector->NeedsImplicitShadowCombinatorForMatching()) {
compound_selector->PrependTagSelector(
tag, determined_prefix == g_null_atom &&
determined_element_name == g_star_atom && !is_host_pseudo);
tag,
determined_prefix == g_null_atom &&
determined_element_name == CSSSelector::UniversalSelectorAtom() &&
!is_host_pseudo);
}
}
......
......@@ -70,6 +70,7 @@ class CORE_EXPORT CSSSelectorParser {
const AtomicString& DefaultNamespace() const;
const AtomicString& DetermineNamespace(const AtomicString& prefix);
void PrependTypeSelectorIfNeeded(const AtomicString& namespace_prefix,
bool has_element_name,
const AtomicString& element_name,
CSSParserSelector*);
static std::unique_ptr<CSSParserSelector> AddSimpleSelectorToCompound(
......
......@@ -63,7 +63,10 @@ class NodeListsNodeData final : public GarbageCollected<NodeListsNodeData> {
struct NodeListAtomicCacheMapEntryHash {
STATIC_ONLY(NodeListAtomicCacheMapEntryHash);
static unsigned GetHash(const NamedNodeListKey& entry) {
return DefaultHash<AtomicString>::Hash::GetHash(entry.second) +
return DefaultHash<AtomicString>::Hash::GetHash(
entry.second == CSSSelector::UniversalSelectorAtom()
? g_star_atom
: entry.second) +
entry.first;
}
static bool Equal(const NamedNodeListKey& a, const NamedNodeListKey& b) {
......@@ -100,7 +103,8 @@ class NodeListsNodeData final : public GarbageCollected<NodeListsNodeData> {
T* AddCache(ContainerNode& node, CollectionType collection_type) {
DCHECK(ThreadState::Current()->IsGCForbidden());
NodeListAtomicNameCacheMap::AddResult result = atomic_name_caches_.insert(
NamedNodeListKey(collection_type, g_star_atom), nullptr);
NamedNodeListKey(collection_type, CSSSelector::UniversalSelectorAtom()),
nullptr);
if (!result.is_new_entry) {
return static_cast<T*>(result.stored_value->value.Get());
}
......@@ -112,8 +116,8 @@ class NodeListsNodeData final : public GarbageCollected<NodeListsNodeData> {
template <typename T>
T* Cached(CollectionType collection_type) {
return static_cast<T*>(
atomic_name_caches_.at(NamedNodeListKey(collection_type, g_star_atom)));
return static_cast<T*>(atomic_name_caches_.at(NamedNodeListKey(
collection_type, CSSSelector::UniversalSelectorAtom())));
}
TagCollectionNS* AddCache(ContainerNode& node,
......
......@@ -121,7 +121,7 @@ void QualifiedName::InitAndReserveCapacityForSize(unsigned size) {
GetQualifiedNameCache().ReserveCapacityForSize(
size + 2 /*g_star_atom and g_null_atom */);
new ((void*)&g_any_name)
QualifiedName(g_null_atom, g_star_atom, g_star_atom, true);
QualifiedName(g_null_atom, g_null_atom, g_star_atom, true);
new ((void*)&g_null_name)
QualifiedName(g_null_atom, g_null_atom, g_null_atom, true);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment