diff --git a/src/Microsoft.AspNet.Mvc.Extensions/Rendering/Html/TagBuilder.cs b/src/Microsoft.AspNet.Mvc.Extensions/Rendering/Html/TagBuilder.cs index faacf3b274..718793d523 100644 --- a/src/Microsoft.AspNet.Mvc.Extensions/Rendering/Html/TagBuilder.cs +++ b/src/Microsoft.AspNet.Mvc.Extensions/Rendering/Html/TagBuilder.cs @@ -77,16 +77,40 @@ namespace Microsoft.AspNet.Mvc.Rendering return string.Empty; } + // If there are no invalid characters in the string, then we don't have to create the buffer. + var firstIndexOfInvalidCharacter = 1; + for (; firstIndexOfInvalidCharacter < name.Length; firstIndexOfInvalidCharacter++) + { + if (!Html401IdUtil.IsValidIdCharacter(name[firstIndexOfInvalidCharacter])) + { + break; + } + } + var firstChar = name[0]; - if (!Html401IdUtil.IsAsciiLetter(firstChar)) + var startsWithAsciiLetter = Html401IdUtil.IsAsciiLetter(firstChar); + if (!startsWithAsciiLetter) { // The first character must be a letter according to the HTML 4.01 specification. firstChar = 'z'; } + if (firstIndexOfInvalidCharacter == name.Length && startsWithAsciiLetter) + { + return name; + } + var stringBuffer = new StringBuilder(name.Length); stringBuffer.Append(firstChar); - for (var index = 1; index < name.Length; index++) + + // Characters until 'firstIndexOfInvalidCharacter' have already been checked for validity. + // So just copying them. This avoids running them through Html401IdUtil.IsValidIdCharacter again. + for (var index = 1; index < firstIndexOfInvalidCharacter; index++) + { + stringBuffer.Append(name[index]); + } + + for (var index = firstIndexOfInvalidCharacter; index < name.Length; index++) { var thisChar = name[index]; if (Html401IdUtil.IsValidIdCharacter(thisChar)) diff --git a/test/Microsoft.AspNet.Mvc.Extensions.Test/Rendering/TagBuilderTest.cs b/test/Microsoft.AspNet.Mvc.Extensions.Test/Rendering/TagBuilderTest.cs index bd028bd9d3..429e3b43bc 100644 --- a/test/Microsoft.AspNet.Mvc.Extensions.Test/Rendering/TagBuilderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Extensions.Test/Rendering/TagBuilderTest.cs @@ -116,5 +116,18 @@ namespace Microsoft.AspNet.Mvc.Core.Rendering // Assert Assert.Equal("HtmlEncode[[TestValue]]", tagBuilder.InnerHtml); } + + [Theory] + [InlineData("HelloWorld", "HelloWorld")] + [InlineData("ˇHelloWorld", "zHelloWorld")] + [InlineData("HelloˇWorld", "Hello-World")] + public void CreateSanitizedIdCreatesId(string input, string output) + { + // Arrange + var result = TagBuilder.CreateSanitizedId(input, "-"); + + // Assert + Assert.Equal(output, result); + } } } \ No newline at end of file