Do not HTML encode while checking for whitespace
- #760 - using a `NullHtmlEncoder` improves `TagHelperContent.IsEmptyOrWhiteSpace` semantics - e.g. a tab is still a tab after encoding - also avoids `NotImplementedException` - implement `Write(char)` for char-by-char `IHtmlContent` corner case
This commit is contained in:
parent
e14f4b095d
commit
44f0aa63c6
|
|
@ -288,6 +288,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
|||
var stringValue = entry as string;
|
||||
if (stringValue != null)
|
||||
{
|
||||
// Do not encode the string because encoded value remains whitespace from user's POV.
|
||||
if (!string.IsNullOrWhiteSpace(stringValue))
|
||||
{
|
||||
return false;
|
||||
|
|
@ -295,7 +296,8 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
|||
}
|
||||
else
|
||||
{
|
||||
((IHtmlContent)entry).WriteTo(writer, HtmlEncoder.Default);
|
||||
// Use NullHtmlEncoder to avoid treating encoded whitespace as non-whitespace e.g. "\t" as "	".
|
||||
((IHtmlContent)entry).WriteTo(writer, NullHtmlEncoder.Default);
|
||||
if (!writer.IsEmptyOrWhiteSpace)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -341,13 +343,13 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
|||
|
||||
public bool IsEmptyOrWhiteSpace { get; private set; } = true;
|
||||
|
||||
#if NETSTANDARD1_5
|
||||
// This is an abstract method in DNXCore
|
||||
public override void Write(char value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (IsEmptyOrWhiteSpace && !char.IsWhiteSpace(value))
|
||||
{
|
||||
IsEmptyOrWhiteSpace = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public override void Write(string value)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.Encodings.Web;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.Extensions.WebEncoders.Testing;
|
||||
using Xunit;
|
||||
|
|
@ -356,27 +357,42 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
|||
Assert.True(tagHelperContent.IsModified);
|
||||
}
|
||||
|
||||
public static TheoryData<string> EmptyOrWhiteSpaceData
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<string>
|
||||
{
|
||||
string.Empty,
|
||||
" ",
|
||||
"\n",
|
||||
"\t",
|
||||
"\r",
|
||||
"\r\n",
|
||||
"\u2000",
|
||||
"\u205f",
|
||||
"\u3000",
|
||||
" \u200a \t",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
[InlineData("\n")]
|
||||
[InlineData("\t")]
|
||||
[InlineData("\r")]
|
||||
public void CanIdentifyEmptyOrWhiteSpace(string data)
|
||||
[MemberData(nameof(EmptyOrWhiteSpaceData))]
|
||||
public void IsEmptyOrWhiteSpace_TrueAfterSetContent(string data)
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
|
||||
// Act
|
||||
tagHelperContent.SetContent(" ");
|
||||
tagHelperContent.Append(data);
|
||||
tagHelperContent.SetContent(data);
|
||||
|
||||
// Assert
|
||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanIdentifyWhiteSpace_WithoutIgnoringStrings()
|
||||
public void IsEmptyOrWhiteSpace_FalseAfterLaterAppend()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
|
|
@ -400,28 +416,59 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
|||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsEmptyOrWhiteSpace_TrueAfterSetEmptyContent()
|
||||
[Theory]
|
||||
[MemberData(nameof(EmptyOrWhiteSpaceData))]
|
||||
public void IsEmptyOrWhiteSpace_TrueAfterAppend(string data)
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
|
||||
// Act
|
||||
tagHelperContent.SetContent(string.Empty);
|
||||
tagHelperContent.Append(data);
|
||||
|
||||
// Assert
|
||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsEmptyOrWhiteSpace_TrueAfterAppendEmptyContent()
|
||||
[Theory]
|
||||
[MemberData(nameof(EmptyOrWhiteSpaceData))]
|
||||
public void IsEmptyOrWhiteSpace_TrueAfterAppendTwice(string data)
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
|
||||
// Act
|
||||
tagHelperContent.Append(string.Empty);
|
||||
tagHelperContent.Append(string.Empty);
|
||||
tagHelperContent.Append(data);
|
||||
tagHelperContent.Append(data);
|
||||
|
||||
// Assert
|
||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(EmptyOrWhiteSpaceData))]
|
||||
public void IsEmptyOrWhiteSpace_TrueAfterAppendHtml(string data)
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
|
||||
// Act
|
||||
tagHelperContent.AppendHtml(data);
|
||||
|
||||
// Assert
|
||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(EmptyOrWhiteSpaceData))]
|
||||
public void IsEmptyOrWhiteSpace_TrueAfterAppendHtmlTwice(string data)
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
|
||||
// Act
|
||||
tagHelperContent.AppendHtml(data);
|
||||
tagHelperContent.AppendHtml(data);
|
||||
|
||||
// Assert
|
||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||
|
|
@ -436,12 +483,136 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
|||
|
||||
// Act
|
||||
tagHelperContent.AppendHtml(copiedTagHelperContent);
|
||||
tagHelperContent.Append(string.Empty);
|
||||
|
||||
// Assert
|
||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsEmptyOrWhiteSpace_TrueAfterAppendEmptyTagHelperContentTwice()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
var copiedTagHelperContent = new DefaultTagHelperContent();
|
||||
|
||||
// Act
|
||||
tagHelperContent.AppendHtml(copiedTagHelperContent);
|
||||
tagHelperContent.AppendHtml(copiedTagHelperContent);
|
||||
|
||||
// Assert
|
||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(EmptyOrWhiteSpaceData))]
|
||||
public void IsEmptyOrWhiteSpace_TrueAfterAppendTagHelperContent(string data)
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
var copiedTagHelperContent = new DefaultTagHelperContent();
|
||||
copiedTagHelperContent.AppendHtml(data);
|
||||
|
||||
// Act
|
||||
tagHelperContent.AppendHtml(copiedTagHelperContent);
|
||||
|
||||
// Assert
|
||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(EmptyOrWhiteSpaceData))]
|
||||
public void IsEmptyOrWhiteSpace_TrueAfterAppendTagHelperContentTwice(string data)
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
var copiedTagHelperContent = new DefaultTagHelperContent();
|
||||
copiedTagHelperContent.AppendHtml(data);
|
||||
|
||||
// Act
|
||||
tagHelperContent.AppendHtml(copiedTagHelperContent);
|
||||
tagHelperContent.AppendHtml(copiedTagHelperContent);
|
||||
|
||||
// Assert
|
||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(EmptyOrWhiteSpaceData))]
|
||||
public void IsEmptyOrWhiteSpace_TrueAfterAppendTagHelperContent_WithDataToEncode(string data)
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
var copiedTagHelperContent = new DefaultTagHelperContent();
|
||||
copiedTagHelperContent.Append(data);
|
||||
|
||||
// Act
|
||||
tagHelperContent.AppendHtml(copiedTagHelperContent);
|
||||
|
||||
// Assert
|
||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(EmptyOrWhiteSpaceData))]
|
||||
public void IsEmptyOrWhiteSpace_TrueAfterAppendTagHelperContentTwice_WithDataToEncode(string data)
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
var copiedTagHelperContent = new DefaultTagHelperContent();
|
||||
copiedTagHelperContent.Append(data);
|
||||
|
||||
// Act
|
||||
tagHelperContent.AppendHtml(copiedTagHelperContent);
|
||||
tagHelperContent.AppendHtml(copiedTagHelperContent);
|
||||
|
||||
// Assert
|
||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsEmptyOrWhiteSpace_TrueAfterAppendTagHelperContent_WithCharByCharWriteTo()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
var copiedTagHelperContent = new CharByCharWhiteSpaceHtmlContent();
|
||||
|
||||
// Act
|
||||
tagHelperContent.AppendHtml(copiedTagHelperContent);
|
||||
|
||||
// Assert
|
||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsEmptyOrWhiteSpace_TrueAfterAppendTagHelperContentTwice_WithCharByCharWriteTo()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
var copiedTagHelperContent = new CharByCharWhiteSpaceHtmlContent();
|
||||
|
||||
// Act
|
||||
tagHelperContent.AppendHtml(copiedTagHelperContent);
|
||||
tagHelperContent.AppendHtml(copiedTagHelperContent);
|
||||
|
||||
// Assert
|
||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsEmptyOrWhiteSpace_FalseAfterAppendTagHelperContentTwice_WithCharByCharWriteTo()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
var copiedTagHelperContent = new CharByCharNonWhiteSpaceHtmlContent();
|
||||
|
||||
// Act
|
||||
tagHelperContent.AppendHtml(copiedTagHelperContent);
|
||||
tagHelperContent.AppendHtml(copiedTagHelperContent);
|
||||
|
||||
// Assert
|
||||
Assert.False(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsEmptyOrWhiteSpace_TrueAfterClear()
|
||||
{
|
||||
|
|
@ -483,7 +654,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void IsEmptyOrWhiteSpace_FalseAfterAppendTagHelper()
|
||||
public void IsEmptyOrWhiteSpace_FalseAfterAppendTagHelperContent()
|
||||
{
|
||||
// Arrange
|
||||
var tagHelperContent = new DefaultTagHelperContent();
|
||||
|
|
@ -633,5 +804,38 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
|||
// Assert
|
||||
Assert.Equal("Hi", writer.ToString());
|
||||
}
|
||||
|
||||
private class CharByCharWhiteSpaceHtmlContent : IHtmlContent
|
||||
{
|
||||
public void WriteTo(TextWriter writer, HtmlEncoder encoder)
|
||||
{
|
||||
writer.Write(' ');
|
||||
writer.Write('\n');
|
||||
writer.Write('\t');
|
||||
writer.Write('\r');
|
||||
writer.Write('\u2000');
|
||||
writer.Write('\u205f');
|
||||
writer.Write('\u3000');
|
||||
}
|
||||
}
|
||||
|
||||
private class CharByCharNonWhiteSpaceHtmlContent : IHtmlContent
|
||||
{
|
||||
public void WriteTo(TextWriter writer, HtmlEncoder encoder)
|
||||
{
|
||||
writer.Write('\u2000');
|
||||
writer.Write('h');
|
||||
writer.Write('e');
|
||||
writer.Write('l');
|
||||
writer.Write('l');
|
||||
writer.Write('o');
|
||||
writer.Write('\u200a');
|
||||
writer.Write('É');
|
||||
writer.Write('r');
|
||||
writer.Write('i');
|
||||
writer.Write('c');
|
||||
writer.Write('\u3000');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue