diff --git a/Source/HtmlRenderer/Core/CssEngine/Extensions/CharExtensions.cs b/Source/HtmlRenderer/Core/CssEngine/Extensions/CharExtensions.cs index 26ef23e5b..b2054ec40 100644 --- a/Source/HtmlRenderer/Core/CssEngine/Extensions/CharExtensions.cs +++ b/Source/HtmlRenderer/Core/CssEngine/Extensions/CharExtensions.cs @@ -109,7 +109,7 @@ public static bool IsDigit(this char c) public static bool IsInvalid(this int c) { return c == 0 || c > Symbols.MaximumCodepoint || - (c > Symbols.UTF16SurrogateMin && c < Symbols.UTF16SurrogateMax); + (c >= Symbols.UTF16SurrogateMin && c <= Symbols.UTF16SurrogateMax); } public static bool IsOneOf(this char c, char a, char b) diff --git a/Source/Test/HtmlRenderer.Test/Css/TokenizationTests.cs b/Source/Test/HtmlRenderer.Test/Css/TokenizationTests.cs index 4a1b323c2..e9a444573 100644 --- a/Source/Test/HtmlRenderer.Test/Css/TokenizationTests.cs +++ b/Source/Test/HtmlRenderer.Test/Css/TokenizationTests.cs @@ -152,6 +152,32 @@ static void Check(string input, TokenType expectedType, string expectedData) Assert.AreEqual(TokenType.Delim, delim.Get().Type); } + // CSS Syntax 3 §4.3.7: zero, a surrogate or a code point above U+10FFFF becomes U+FFFD + [TestMethod] + [DataRow(@"'\0'", "�")] + [DataRow(@"'\D800'", "�")] + [DataRow(@"'\DABC'", "�")] + [DataRow(@"'\DFFF'", "�")] + [DataRow(@"'\110000'", "�")] + [DataRow(@"'\D7FF\E000'", "퟿")] + [DataRow(@"'\10FFFF'", "􏿿")] + public void LexerInvalidEscapedCodePointInString(string input, string expected) + { + var token = new Lexer(new TextSource(input)).Get(); + Assert.AreEqual(TokenType.String, token.Type); + Assert.AreEqual(expected, token.Data); + } + + [TestMethod] + [DataRow(@"a\D800 b")] + [DataRow(@"a\DFFF b")] + public void LexerInvalidEscapedCodePointInIdent(string input) + { + var token = new Lexer(new TextSource(input)).Get(); + Assert.AreEqual(TokenType.Ident, token.Type); + Assert.AreEqual("a�b", token.Data); + } + [TestMethod] public void LexerOnlyCarriageReturn() {