From d37c017994d2771b41209f571874b096c77e785d Mon Sep 17 00:00:00 2001 From: Fomafix Date: Wed, 11 Apr 2018 06:23:50 +0200 Subject: [PATCH] CSSMin::serializeStringValue: Update implementation to new specification The current version of https://www.w3.org/TR/cssom/ is https://www.w3.org/TR/2016/WD-cssom-1-20160317/ The new specification for CSS string serialization https://www.w3.org/TR/2016/WD-cssom-1-20160317/#serialize-a-string has some changes compared to the old specification https://www.w3.org/TR/2013/WD-cssom-20131205/#serialize-a-string * U+0000 get replaced by the REPLACEMENT CHARACTER (U+FFFD) instead of throwing an exception. * U+0080 to U+009F are not escaped. The old implementation has a bug because it selects the byte range [\x7f-\x9f] and not unicode codepoint range [\u007f-\u009f]. This breaks the encoding because CSS is in UTF-8 not in ISO 8859-x. Also add tests to cover CSSMin::serializeStringValue. Bug: T192048 Change-Id: I894824c216b95dbba461308488fba33121ffea54 --- includes/libs/CSSMin.php | 10 ++---- tests/phpunit/includes/libs/CSSMinTest.php | 37 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/includes/libs/CSSMin.php b/includes/libs/CSSMin.php index 1cbcbde1e0..73825e82e3 100644 --- a/includes/libs/CSSMin.php +++ b/includes/libs/CSSMin.php @@ -173,18 +173,14 @@ class CSSMin { /** * Serialize a string (escape and quote) for use as a CSS string value. - * http://www.w3.org/TR/2013/WD-cssom-20131205/#serialize-a-string + * https://www.w3.org/TR/2016/WD-cssom-1-20160317/#serialize-a-string * * @param string $value * @return string - * @throws Exception */ public static function serializeStringValue( $value ) { - if ( strstr( $value, "\0" ) ) { - throw new Exception( "Invalid character in CSS string" ); - } - $value = strtr( $value, [ '\\' => '\\\\', '"' => '\\"' ] ); - $value = preg_replace_callback( '/[\x01-\x1f\x7f-\x9f]/', function ( $match ) { + $value = strtr( $value, [ "\0" => "\\fffd ", '\\' => '\\\\', '"' => '\\"' ] ); + $value = preg_replace_callback( '/[\x01-\x1f\x7f]/', function ( $match ) { return '\\' . base_convert( ord( $match[0] ), 10, 16 ) . ' '; }, $value ); return '"' . $value . '"'; diff --git a/tests/phpunit/includes/libs/CSSMinTest.php b/tests/phpunit/includes/libs/CSSMinTest.php index f2d5ef379b..59a1c7c9b7 100644 --- a/tests/phpunit/includes/libs/CSSMinTest.php +++ b/tests/phpunit/includes/libs/CSSMinTest.php @@ -22,6 +22,43 @@ class CSSMinTest extends MediaWikiTestCase { ] ); } + /** + * @dataProvider serializeStringValueProvider + * @covers CSSMin::serializeStringValue + */ + public function testSerializeStringValue( $input, $expected ) { + $output = CSSMin::serializeStringValue( $input ); + $this->assertEquals( + $expected, + $output, + 'Serialized output must be in the expected form.' + ); + } + + public function serializeStringValueProvider() { + return [ + [ 'Hello World!', '"Hello World!"' ], + [ "Null\0Null", "\"Null\\fffd Null\"" ], + [ '"', '"\\""' ], + [ "'", '"\'"' ], + [ "\\", '"\\\\"' ], + [ "Tab\tTab", '"Tab\\9 Tab"' ], + [ "Space tab \t space", '"Space tab \\9 space"' ], + [ "Line\nfeed", '"Line\\a feed"' ], + [ "Return\rreturn", '"Return\\d return"' ], + [ "Next\xc2\x85line", "\"Next\xc2\x85line\"" ], + [ "Del\x7fDel", '"Del\\7f Del"' ], + [ "nb\xc2\xa0sp", "\"nb\xc2\xa0sp\"" ], + [ "AMP&AMP", "\"AMP&AMP\"" ], + [ '!"#$%&\'()*+,-./0123456789:;<=>?', '"!\\"#$%&\'()*+,-./0123456789:;<=>?"' ], + [ '@[\\]^_`{|}~', '"@[\\\\]^_`{|}~"' ], + [ 'ä', '"ä"' ], + [ 'Ä', '"Ä"' ], + [ '€', '"€"' ], + [ '𝒞', '"𝒞"' ], // U+1D49E 'MATHEMATICAL SCRIPT CAPITAL C' + ]; + } + /** * @dataProvider mimeTypeProvider * @covers CSSMin::getMimeType -- 2.20.1