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;
|
var stringValue = entry as string;
|
||||||
if (stringValue != null)
|
if (stringValue != null)
|
||||||
{
|
{
|
||||||
|
// Do not encode the string because encoded value remains whitespace from user's POV.
|
||||||
if (!string.IsNullOrWhiteSpace(stringValue))
|
if (!string.IsNullOrWhiteSpace(stringValue))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -295,7 +296,8 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
||||||
}
|
}
|
||||||
else
|
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)
|
if (!writer.IsEmptyOrWhiteSpace)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -341,13 +343,13 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
||||||
|
|
||||||
public bool IsEmptyOrWhiteSpace { get; private set; } = true;
|
public bool IsEmptyOrWhiteSpace { get; private set; } = true;
|
||||||
|
|
||||||
#if NETSTANDARD1_5
|
|
||||||
// This is an abstract method in DNXCore
|
|
||||||
public override void Write(char value)
|
public override void Write(char value)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
if (IsEmptyOrWhiteSpace && !char.IsWhiteSpace(value))
|
||||||
|
{
|
||||||
|
IsEmptyOrWhiteSpace = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
public override void Write(string value)
|
public override void Write(string value)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
using Microsoft.AspNetCore.Html;
|
using Microsoft.AspNetCore.Html;
|
||||||
using Microsoft.Extensions.WebEncoders.Testing;
|
using Microsoft.Extensions.WebEncoders.Testing;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -356,27 +357,42 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
||||||
Assert.True(tagHelperContent.IsModified);
|
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]
|
[Theory]
|
||||||
[InlineData("")]
|
[MemberData(nameof(EmptyOrWhiteSpaceData))]
|
||||||
[InlineData(" ")]
|
public void IsEmptyOrWhiteSpace_TrueAfterSetContent(string data)
|
||||||
[InlineData("\n")]
|
|
||||||
[InlineData("\t")]
|
|
||||||
[InlineData("\r")]
|
|
||||||
public void CanIdentifyEmptyOrWhiteSpace(string data)
|
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var tagHelperContent = new DefaultTagHelperContent();
|
var tagHelperContent = new DefaultTagHelperContent();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
tagHelperContent.SetContent(" ");
|
tagHelperContent.SetContent(data);
|
||||||
tagHelperContent.Append(data);
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CanIdentifyWhiteSpace_WithoutIgnoringStrings()
|
public void IsEmptyOrWhiteSpace_FalseAfterLaterAppend()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var tagHelperContent = new DefaultTagHelperContent();
|
var tagHelperContent = new DefaultTagHelperContent();
|
||||||
|
|
@ -400,28 +416,59 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
||||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public void IsEmptyOrWhiteSpace_TrueAfterSetEmptyContent()
|
[MemberData(nameof(EmptyOrWhiteSpaceData))]
|
||||||
|
public void IsEmptyOrWhiteSpace_TrueAfterAppend(string data)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var tagHelperContent = new DefaultTagHelperContent();
|
var tagHelperContent = new DefaultTagHelperContent();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
tagHelperContent.SetContent(string.Empty);
|
tagHelperContent.Append(data);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public void IsEmptyOrWhiteSpace_TrueAfterAppendEmptyContent()
|
[MemberData(nameof(EmptyOrWhiteSpaceData))]
|
||||||
|
public void IsEmptyOrWhiteSpace_TrueAfterAppendTwice(string data)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var tagHelperContent = new DefaultTagHelperContent();
|
var tagHelperContent = new DefaultTagHelperContent();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
tagHelperContent.Append(string.Empty);
|
tagHelperContent.Append(data);
|
||||||
tagHelperContent.Append(string.Empty);
|
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
|
||||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
||||||
|
|
@ -436,12 +483,136 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
tagHelperContent.AppendHtml(copiedTagHelperContent);
|
tagHelperContent.AppendHtml(copiedTagHelperContent);
|
||||||
tagHelperContent.Append(string.Empty);
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(tagHelperContent.IsEmptyOrWhiteSpace);
|
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]
|
[Fact]
|
||||||
public void IsEmptyOrWhiteSpace_TrueAfterClear()
|
public void IsEmptyOrWhiteSpace_TrueAfterClear()
|
||||||
{
|
{
|
||||||
|
|
@ -483,7 +654,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void IsEmptyOrWhiteSpace_FalseAfterAppendTagHelper()
|
public void IsEmptyOrWhiteSpace_FalseAfterAppendTagHelperContent()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var tagHelperContent = new DefaultTagHelperContent();
|
var tagHelperContent = new DefaultTagHelperContent();
|
||||||
|
|
@ -633,5 +804,38 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal("Hi", writer.ToString());
|
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