Skip to content

Commit c2da39f

Browse files
committed
On normalization failure, stringify entire input as CSS
1 parent c83aa4e commit c2da39f

2 files changed

Lines changed: 28 additions & 20 deletions

File tree

src/wp-includes/fonts/class-wp-font-utils.php

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,29 +43,32 @@ private static function maybe_add_quotes( $item ) {
4343
/**
4444
* Normalize @font-face font-family CSS text.
4545
*
46-
* This function attempts to be generous in the allowed values:
46+
* The return value is always normalized to a quoted CSS string.
47+
*
4748
* - Valid @font-face font-family values must return a semantically equivalent result.
4849
* - Normalization must be idempotent.
49-
* - Common mistakes such as providing multiple comma-separated font-family values return a
50-
* normalization of the first item: `a, b` becomes `"a"`.
51-
* - Invalid trailing content is ignored: `"string" garbage` becomes `"string"`.
50+
* - If a CSS string is the first value, it will be used discarding subsequent text.
51+
* - The first valid value in a CSS comma-separated list will be used discarding subsequent text.
52+
* - If the value does not appear to be valid CSS or start with a valid CSS
53+
* @font-face font-family, treat the entire input as a plain string for normalization.
54+
*
55+
* Relevant notes from the CSS specification:
5256
*
5357
* > Syntax of <family-name>
5458
* > <family-name> = <string> | <custom-ident>+
55-
*
59+
* > …
5660
* > To avoid mistakes in escaping, it is recommended to quote font family names that contain
5761
* > white space, digits, or punctuation characters other than hyphens
5862
*
5963
* @see https://drafts.csswg.org/css-fonts/#family-name-syntax
6064
*
6165
* @param string $font_family CSS text @font-face font-family value.
62-
* @return string|null Normalized value or null if the value could not be normalized.
66+
* @return string Normalized value or null if the value could not be normalized.
6367
*/
64-
public static function normalize_css_font_face_font_family( string $font_family ): ?string {
65-
$processor = WP_CSS_Token_Processor::create( $font_family );
66-
if ( null === $processor ) {
67-
return null;
68-
}
68+
public static function normalize_css_font_face_font_family( string $font_family ): string {
69+
$font_family = wp_scrub_utf8( $font_family );
70+
$processor = WP_CSS_Token_Processor::create( $font_family );
71+
assert( null !== $processor, 'A valid processor must be created' );
6972

7073
// Ignore leading whitespace tokens.
7174
while ( $processor->next_token() && WP_CSS_Token_Processor::TOKEN_WHITESPACE === $processor->get_token_type() ) {
@@ -82,7 +85,7 @@ public static function normalize_css_font_face_font_family( string $font_family
8285
* font-family invalid.
8386
*/
8487
if ( WP_CSS_Token_Processor::TOKEN_IDENT !== $token_type ) {
85-
return null;
88+
return WP_CSS_Builder::string( $font_family );
8689
}
8790

8891
/**
@@ -111,7 +114,7 @@ public static function normalize_css_font_face_font_family( string $font_family
111114

112115
// Anything else is an error.
113116
default:
114-
return null;
117+
return WP_CSS_Builder::string( $font_family );
115118
}
116119
}
117120

tests/phpunit/tests/fonts/font-library/wpFontUtils/normalizeCssFontFaceFontFamily.php

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,20 @@ public function test_normalize_css_font_face_font_family( string $input, ?string
3030
* Data provider.
3131
*/
3232
public static function data_provider(): Generator {
33+
// Valid CSS font-family
3334
yield 'Already normalized' => array( '"Font"', '"Font"' );
3435
yield 'Unquoted' => array( 'Font', '"Font"' );
3536
yield 'Multiple idents' => array( 'Font Name', '"Font Name"' );
36-
yield 'Number' => array( 'Libre Barcode 128 Text', null );
37-
yield 'PX unit number' => array( '10px rest', null );
38-
yield '% unit number' => array( '10px rest', null );
39-
yield 'Weird unit number' => array( '20xYz rest', null );
40-
yield 'Negative number' => array( '-30deg rest', null );
41-
yield 'Positive number' => array( '+40rad rest', null );
42-
yield 'Multiple ident spaces normalized' => array( "A\nB\rC\r\nD\tE\fF", '"A B C D E F"' );
37+
yield 'Idents and whitespace normalized' => array( "A\nB\rC\r\nD\tE\fF", '"A B C D E F"' );
38+
39+
// Invalid CSS font-family is treated as a plain string.
40+
yield 'Input with stray single quote' => array( "O'er the rainbow", '"O\27 er the rainbow"' );
41+
yield 'Input with stray double quote' => array( 'Oop"sie', '"Oop\22 sie"' );
42+
yield 'Number' => array( 'Libre Barcode 128 Text', '"Libre Barcode 128 Text"' );
43+
yield 'PX unit number' => array( '10px rest', '"10px rest"' );
44+
yield '% unit number' => array( '10px rest', '"10px rest"' );
45+
yield 'Weird unit number' => array( '20xYz rest', '"20xYz rest"' );
46+
yield 'Negative number' => array( '-30deg rest', '"-30deg rest"' );
47+
yield 'Positive number' => array( '+40rad rest', '"+40rad rest"' );
4348
}
4449
}

0 commit comments

Comments
 (0)