From 47fc80bdb3e57d17dff5787589b764f1541f475b Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Mon, 16 Oct 2023 22:06:56 -0400 Subject: [PATCH] fix #3426: improve invalid `url()` token parsing --- internal/css_parser/css_parser.go | 31 +++++++++++++++++++++----- internal/css_parser/css_parser_test.go | 6 ++++- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/internal/css_parser/css_parser.go b/internal/css_parser/css_parser.go index ba84c8f177e..f39cfdd22d1 100644 --- a/internal/css_parser/css_parser.go +++ b/internal/css_parser/css_parser.go @@ -974,12 +974,31 @@ func (p *parser) parseURLOrString() (string, logger.Range, bool) { case css_lexer.TFunction: if strings.EqualFold(p.decoded(), "url") { matchingLoc := logger.Loc{Start: p.current().Range.End() - 1} - p.advance() - t = p.current() - text := p.decoded() - if p.expect(css_lexer.TString) { - p.expectWithMatchingLoc(css_lexer.TCloseParen, matchingLoc) - return text, t.Range, true + i := p.index + 1 + + // Skip over whitespace + for p.at(i).Kind == css_lexer.TWhitespace { + i++ + } + + // Consume a string + if p.at(i).Kind == css_lexer.TString { + stringIndex := i + i++ + + // Skip over whitespace + for p.at(i).Kind == css_lexer.TWhitespace { + i++ + } + + // Consume a closing parenthesis + if close := p.at(i).Kind; close == css_lexer.TCloseParen || close == css_lexer.TEndOfFile { + t := p.at(stringIndex) + text := t.DecodedText(p.source.Contents) + p.index = i + p.expectWithMatchingLoc(css_lexer.TCloseParen, matchingLoc) + return text, t.Range, true + } } } } diff --git a/internal/css_parser/css_parser_test.go b/internal/css_parser/css_parser_test.go index f0f62752885..f11c91632d2 100644 --- a/internal/css_parser/css_parser_test.go +++ b/internal/css_parser/css_parser_test.go @@ -1302,15 +1302,19 @@ func TestAtImport(t *testing.T) { expectPrinted(t, "@import url();", "@import \"\";\n", "") expectPrinted(t, "@import url(foo.css);", "@import \"foo.css\";\n", "") expectPrinted(t, "@import url(foo.css) ;", "@import \"foo.css\";\n", "") + expectPrinted(t, "@import url( foo.css );", "@import \"foo.css\";\n", "") expectPrinted(t, "@import url(\"foo.css\");", "@import \"foo.css\";\n", "") expectPrinted(t, "@import url(\"foo.css\") ;", "@import \"foo.css\";\n", "") + expectPrinted(t, "@import url( \"foo.css\" );", "@import \"foo.css\";\n", "") expectPrinted(t, "@import url(\"foo.css\") print;", "@import \"foo.css\" print;\n", "") expectPrinted(t, "@import url(\"foo.css\") screen and (orientation:landscape);", "@import \"foo.css\" screen and (orientation:landscape);\n", "") expectPrinted(t, "@import;", "@import;\n", ": WARNING: Expected URL token but found \";\"\n") expectPrinted(t, "@import ;", "@import;\n", ": WARNING: Expected URL token but found \";\"\n") expectPrinted(t, "@import \"foo.css\"", "@import \"foo.css\";\n", ": WARNING: Expected \";\" but found end of file\n") - expectPrinted(t, "@import url(\"foo.css\";", "@import \"foo.css\";\n", ": WARNING: Expected \")\" to go with \"(\"\n: NOTE: The unbalanced \"(\" is here:\n") + expectPrinted(t, "@import url(\"foo.css\" extra-stuff);", "@import url(\"foo.css\" extra-stuff);\n", ": WARNING: Expected URL token but found \"url(\"\n") + expectPrinted(t, "@import url(\"foo.css\";", "@import url(\"foo.css\";);\n", + ": WARNING: Expected URL token but found \"url(\"\n: WARNING: Expected \")\" to go with \"(\"\n: NOTE: The unbalanced \"(\" is here:\n") expectPrinted(t, "@import noturl(\"foo.css\");", "@import noturl(\"foo.css\");\n", ": WARNING: Expected URL token but found \"noturl(\"\n") expectPrinted(t, "@import url(foo.css", "@import \"foo.css\";\n", `: WARNING: Expected ")" to end URL token : NOTE: The unbalanced "(" is here: