From 3191ce9a1bb8d9eea9c2602296f57019358dbe9f Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Mon, 15 Jun 2026 23:55:51 +0100 Subject: [PATCH] [BUGFIX] Allow CSS containing only whitespace or comments The tests added are functional rather than unit tests, as they are indirectly testing `CSSList` rather than `Parser` itself. Fixes #349 --- CHANGELOG.md | 1 + src/CSSList/CSSList.php | 4 +-- tests/Functional/ParserTest.php | 52 +++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da7751c34..fa59b9192 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Please also have a look at our ### Fixed +- Allow CSS containing only whitespace or comments (#1593) - Only allow strings as `LineName` components (#1590) ### Documentation diff --git a/src/CSSList/CSSList.php b/src/CSSList/CSSList.php index 007047d1a..a788c13b6 100644 --- a/src/CSSList/CSSList.php +++ b/src/CSSList/CSSList.php @@ -67,8 +67,8 @@ public static function parseList(ParserState $parserState, CSSList $list): void $isRoot = $list instanceof Document; $usesLenientParsing = $parserState->getSettings()->usesLenientParsing(); $comments = []; + $parserState->consumeWhiteSpace($comments); while (!$parserState->isEnd()) { - $parserState->consumeWhiteSpace($comments); $listItem = null; if ($usesLenientParsing) { try { @@ -77,7 +77,7 @@ public static function parseList(ParserState $parserState, CSSList $list): void } catch (UnexpectedTokenException $e) { $listItem = false; // If the failed parsing did not consume anything that was to come ... - if ($parserState->currentColumn() === $positionBeforeParse && !$parserState->isEnd()) { + if ($parserState->currentColumn() === $positionBeforeParse) { // ... the unexpected token needs to be skipped, otherwise there'll be an infinite loop. $parserState->consume(1); } diff --git a/tests/Functional/ParserTest.php b/tests/Functional/ParserTest.php index 982bb3a23..f84c639fd 100644 --- a/tests/Functional/ParserTest.php +++ b/tests/Functional/ParserTest.php @@ -7,6 +7,8 @@ use PHPUnit\Framework\TestCase; use Sabberworm\CSS\CSSList\Document; use Sabberworm\CSS\Parser; +use Sabberworm\CSS\Settings; +use TRegx\PhpUnit\DataProviders\DataProvider; /** * @covers \Sabberworm\CSS\Parser @@ -36,4 +38,54 @@ public function parseWithOneRuleSetReturnsDocument(): void self::assertInstanceOf(Document::class, $result); } + + /** + * @return array + */ + public static function provideEmptyCss(): array + { + return [ + 'empty string' => [''], + 'space' => [' '], + 'newline' => ["\n"], + 'carriage return' => ["\r"], + 'tab' => ["\t"], + 'Windows line ending' => ["\r\n"], + 'comment' => ['/* I get put in a separate property */'], + ]; + } + + /** + * @return array + */ + public static function provideLenientParsingFlag(): array + { + return [ + 'strict parsing' => [false], + 'lenient parsing' => [true], + ]; + } + + /** + * @return DataProvider + */ + public static function provideEmptyCssAndLenientParsingFlag(): DataProvider + { + return DataProvider::cross(static::provideEmptyCss(), static::provideLenientParsingFlag()); + } + + /** + * @test + * + * @dataProvider provideEmptyCssAndLenientParsingFlag + */ + public function parsesEmptyCss(string $css, bool $parseLeniently): void + { + $parser = new Parser($css, Settings::create()->withLenientParsing($parseLeniently)); + + $result = $parser->parse(); + + // Note: Comments for the document are accessed separately via `getComments()`. + self::assertSame([], $result->getContents()); + } }