Commit 37fb300e authored by Anders Hartvoll Ruud's avatar Anders Hartvoll Ruud Committed by Commit Bot

Don't treat pipe as descendant combinator

There is currently a bug where selectors such as 'div | .c' matches
a .c-descendant of div. This is because of a bug in the parser, where
the '|' is consumed as part of the failing branch of CSSSelectorParser::
ConsumeName.

This CL changes the behavior to look ahead at the token after the pipe
delimiter: if it's not an <ident> or '*', we consume nothing and leave
the '|' unconsumed. This causes subsequent parsing attempts on the
CSSParserTokenRange to fail.

We had one test (non-WPT) that tried to use selectors such as
tns|#testInsertRule, but this is actually not valid. (Sidenote: the
test failed in Firefox). Added '*' to make it valid.

Bug: 1052847
Change-Id: I51e6a67a5e44cf3c0fe9d9fc7abea3772d0f4d9b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2058814Reviewed-by: default avatarRune Lillesveen <futhark@chromium.org>
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/master@{#742115}
parent 1c95f55d
...@@ -405,15 +405,16 @@ bool CSSSelectorParser::ConsumeName(CSSParserTokenRange& range, ...@@ -405,15 +405,16 @@ bool CSSSelectorParser::ConsumeName(CSSParserTokenRange& range,
if (range.Peek().GetType() != kDelimiterToken || if (range.Peek().GetType() != kDelimiterToken ||
range.Peek().Delimiter() != '|') range.Peek().Delimiter() != '|')
return true; return true;
range.Consume();
namespace_prefix = namespace_prefix =
name == CSSSelector::UniversalSelectorAtom() ? g_star_atom : name; name == CSSSelector::UniversalSelectorAtom() ? g_star_atom : name;
const CSSParserToken& name_token = range.Consume(); if (range.Peek(1).GetType() == kIdentToken) {
if (name_token.GetType() == kIdentToken) { range.Consume();
name = name_token.Value().ToAtomicString(); name = range.Consume().Value().ToAtomicString();
} else if (name_token.GetType() == kDelimiterToken && } else if (range.Peek(1).GetType() == kDelimiterToken &&
name_token.Delimiter() == '*') { range.Peek(1).Delimiter() == '*') {
range.Consume();
range.Consume();
name = CSSSelector::UniversalSelectorAtom(); name = CSSSelector::UniversalSelectorAtom();
} else { } else {
name = g_null_atom; name = g_null_atom;
......
...@@ -287,6 +287,23 @@ TEST(CSSSelectorParserTest, UnresolvedNamespacePrefix) { ...@@ -287,6 +287,23 @@ TEST(CSSSelectorParserTest, UnresolvedNamespacePrefix) {
} }
} }
TEST(CSSSelectorParserTest, UnexpectedPipe) {
const char* test_cases[] = {"div | .c", "| div", " | div"};
auto* context = MakeGarbageCollected<CSSParserContext>(
kHTMLStandardMode, SecureContextMode::kInsecureContext);
auto* sheet = MakeGarbageCollected<StyleSheetContents>(context);
for (auto* test_case : test_cases) {
CSSTokenizer tokenizer(test_case);
const auto tokens = tokenizer.TokenizeToEOF();
CSSParserTokenRange range(tokens);
CSSSelectorList list =
CSSSelectorParser::ParseSelector(range, context, sheet);
EXPECT_FALSE(list.IsValid());
}
}
TEST(CSSSelectorParserTest, SerializedUniversal) { TEST(CSSSelectorParserTest, SerializedUniversal) {
const char* test_cases[][2] = { const char* test_cases[][2] = {
{"*::-webkit-volume-slider", "::-webkit-volume-slider"}, {"*::-webkit-volume-slider", "::-webkit-volume-slider"},
......
...@@ -16,14 +16,14 @@ function assertColorGreen(id) { ...@@ -16,14 +16,14 @@ function assertColorGreen(id) {
} }
test(function() { test(function() {
style.sheet.insertRule('tns|#testInsertRule[tns|green] { color: green; }', style.sheet.cssRules.length); style.sheet.insertRule('tns|*#testInsertRule[tns|green] { color: green; }', style.sheet.cssRules.length);
assertColorGreen('testInsertRule'); assertColorGreen('testInsertRule');
}, 'Selectors added to CSSStyleSheets via insertRule() should use the @namespace mapping'); }, 'Selectors added to CSSStyleSheets via insertRule() should use the @namespace mapping');
test(function() { test(function() {
var mediaRule = style.sheet.rules[1]; var mediaRule = style.sheet.rules[1];
console.assert(mediaRule instanceof CSSMediaRule); // CSSMediaRule inherits from the CSSGroupingRule interface. console.assert(mediaRule instanceof CSSMediaRule); // CSSMediaRule inherits from the CSSGroupingRule interface.
mediaRule.insertRule('tns|#testMediaInsertRule[tns|green] { color: green; }', style.sheet.length); mediaRule.insertRule('tns|*#testMediaInsertRule[tns|green] { color: green; }', style.sheet.length);
assertColorGreen('testMediaInsertRule'); assertColorGreen('testMediaInsertRule');
}, 'Selectors added to CSSGroupingRules via insertRule() should use the @namespace mapping'); }, 'Selectors added to CSSGroupingRules via insertRule() should use the @namespace mapping');
</script> </script>
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