diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Host/MvcRazorHost.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Host/MvcRazorHost.cs
index 4c97ecbea3..bb537aaa51 100644
--- a/src/Microsoft.AspNetCore.Mvc.Razor.Host/MvcRazorHost.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Razor.Host/MvcRazorHost.cs
@@ -88,8 +88,6 @@ internal MvcRazorHost(IChunkTreeCache chunkTreeCache, RazorPathNormalizer pathNo
ExecutionContextAddTagHelperAttributeMethodName =
nameof(TagHelperExecutionContext.AddTagHelperAttribute),
ExecutionContextAddHtmlAttributeMethodName = nameof(TagHelperExecutionContext.AddHtmlAttribute),
- ExecutionContextAddMinimizedHtmlAttributeMethodName =
- nameof(TagHelperExecutionContext.AddMinimizedHtmlAttribute),
ExecutionContextOutputPropertyName = nameof(TagHelperExecutionContext.Output),
RunnerTypeName = typeof(TagHelperRunner).FullName,
diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/RazorPage.cs b/src/Microsoft.AspNetCore.Mvc.Razor/RazorPage.cs
index 0e5581ee75..3fe6054600 100644
--- a/src/Microsoft.AspNetCore.Mvc.Razor/RazorPage.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Razor/RazorPage.cs
@@ -238,7 +238,7 @@ public TagHelperContent EndTagHelperWritingScope()
///
/// All writes to the or after calling this method will
/// be buffered until is called.
- /// The content will be buffered using a shared within this
+ /// The content will be buffered using a shared within this
/// Nesting of and method calls
/// is not supported.
///
@@ -267,7 +267,7 @@ public void BeginWriteTagHelperAttribute()
///
/// The content buffered by the shared of this .
///
- /// This method assumes that there will be no nesting of
+ /// This method assumes that there will be no nesting of
/// and method calls.
///
public string EndWriteTagHelperAttribute()
@@ -572,9 +572,14 @@ public virtual void EndWriteAttributeTo(TextWriter writer)
public void BeginAddHtmlAttributeValues(
TagHelperExecutionContext executionContext,
string attributeName,
- int attributeValuesCount)
+ int attributeValuesCount,
+ HtmlAttributeValueStyle attributeValueStyle)
{
- _tagHelperAttributeInfo = new TagHelperAttributeInfo(executionContext, attributeName, attributeValuesCount);
+ _tagHelperAttributeInfo = new TagHelperAttributeInfo(
+ executionContext,
+ attributeName,
+ attributeValuesCount,
+ attributeValueStyle);
}
public void AddHtmlAttributeValue(
@@ -596,7 +601,8 @@ public void AddHtmlAttributeValue(
// attribute was removed from TagHelperOutput.Attributes).
_tagHelperAttributeInfo.ExecutionContext.AddTagHelperAttribute(
_tagHelperAttributeInfo.Name,
- value?.ToString() ?? string.Empty);
+ value?.ToString() ?? string.Empty,
+ _tagHelperAttributeInfo.AttributeValueStyle);
_tagHelperAttributeInfo.Suppressed = true;
return;
}
@@ -604,7 +610,8 @@ public void AddHtmlAttributeValue(
{
_tagHelperAttributeInfo.ExecutionContext.AddHtmlAttribute(
_tagHelperAttributeInfo.Name,
- _tagHelperAttributeInfo.Name);
+ _tagHelperAttributeInfo.Name,
+ _tagHelperAttributeInfo.AttributeValueStyle);
_tagHelperAttributeInfo.Suppressed = true;
return;
}
@@ -637,7 +644,7 @@ public void EndAddHtmlAttributeValues(TagHelperExecutionContext executionContext
var content = _valueBuffer == null ? HtmlString.Empty : new HtmlString(_valueBuffer.ToString());
_valueBuffer?.GetStringBuilder().Clear();
- executionContext.AddHtmlAttribute(_tagHelperAttributeInfo.Name, content);
+ executionContext.AddHtmlAttribute(_tagHelperAttributeInfo.Name, content, _tagHelperAttributeInfo.AttributeValueStyle);
}
}
@@ -1081,11 +1088,13 @@ private struct TagHelperAttributeInfo
public TagHelperAttributeInfo(
TagHelperExecutionContext tagHelperExecutionContext,
string name,
- int attributeValuesCount)
+ int attributeValuesCount,
+ HtmlAttributeValueStyle attributeValueStyle)
{
ExecutionContext = tagHelperExecutionContext;
Name = name;
AttributeValuesCount = attributeValuesCount;
+ AttributeValueStyle = attributeValueStyle;
Suppressed = false;
}
@@ -1096,6 +1105,8 @@ public TagHelperAttributeInfo(
public int AttributeValuesCount { get; }
+ public HtmlAttributeValueStyle AttributeValueStyle { get; }
+
public bool Suppressed { get; set; }
}
diff --git a/src/Microsoft.AspNetCore.Mvc.TagHelpers/LinkTagHelper.cs b/src/Microsoft.AspNetCore.Mvc.TagHelpers/LinkTagHelper.cs
index 82a4bd40b3..2abec8a630 100644
--- a/src/Microsoft.AspNetCore.Mvc.TagHelpers/LinkTagHelper.cs
+++ b/src/Microsoft.AspNetCore.Mvc.TagHelpers/LinkTagHelper.cs
@@ -422,7 +422,8 @@ private void BuildLinkTag(string href, TagHelperAttributeList attributes, TagHel
}
else
{
- AppendAttribute(attribute.Name, attribute.Value, builder);
+ attribute.CopyTo(builder);
+ builder.AppendHtml(" ");
}
}
@@ -441,15 +442,10 @@ private void AppendVersionedHref(string hrefName, string hrefValue, TagHelperCon
hrefValue = _fileVersionProvider.AddFileVersionToPath(hrefValue);
}
- AppendAttribute(hrefName, hrefValue, builder);
- }
-
- private void AppendAttribute(string key, object value, TagHelperContent builder)
- {
builder
- .AppendHtml(key)
+ .AppendHtml(hrefName)
.AppendHtml("=\"")
- .Append(HtmlEncoder, value)
+ .Append(hrefValue)
.AppendHtml("\" ");
}
diff --git a/src/Microsoft.AspNetCore.Mvc.TagHelpers/ScriptTagHelper.cs b/src/Microsoft.AspNetCore.Mvc.TagHelpers/ScriptTagHelper.cs
index e821afd48a..afec945db1 100644
--- a/src/Microsoft.AspNetCore.Mvc.TagHelpers/ScriptTagHelper.cs
+++ b/src/Microsoft.AspNetCore.Mvc.TagHelpers/ScriptTagHelper.cs
@@ -4,6 +4,7 @@
using System;
using System.Diagnostics;
using System.IO;
+using System.Text;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Html;
@@ -235,7 +236,8 @@ public override void Process(TagHelperContext context, TagHelperOutput output)
var index = output.Attributes.IndexOfName(SrcAttributeName);
output.Attributes[index] = new TagHelperAttribute(
SrcAttributeName,
- _fileVersionProvider.AddFileVersionToPath(Src));
+ _fileVersionProvider.AddFileVersionToPath(Src),
+ output.Attributes[index].ValueStyle);
}
}
@@ -306,7 +308,7 @@ private void BuildFallbackBlock(TagHelperAttributeList attributes, TagHelperCont
// Fallback "src" values come from bound attributes and globbing. Must always be non-null.
Debug.Assert(src != null);
- builder.AppendHtml("");
}
+ var stringBuilder = StringWriter.GetStringBuilder();
+ var scriptTags = stringBuilder.ToString();
+ stringBuilder.Clear();
+ var encodedScriptTags = JavaScriptEncoder.Encode(scriptTags);
+ builder.AppendHtml(encodedScriptTags);
+
builder.AppendHtml("\"));");
}
}
- private string GetAttributeValue(object value)
+ private string GetVersionedSrc(string srcValue)
{
- string stringValue;
- var htmlString = value as HtmlString;
- if (htmlString != null)
+ if (AppendVersion == true)
{
- // Value likely came from an HTML context in the .cshtml file but may still contain double quotes
- // since attribute could have been enclosed in single quotes.
- stringValue = htmlString.Value;
- stringValue = stringValue.Replace("\"", """);
+ srcValue = _fileVersionProvider.AddFileVersionToPath(srcValue);
}
- else
- {
- var writer = StringWriter;
- RazorPage.WriteTo(writer, HtmlEncoder, value);
- // Value is now correctly HTML-encoded but may still contain double quotes since attribute could
- // have been enclosed in single quotes and portions that were HtmlStrings are not re-encoded.
- var builder = writer.GetStringBuilder();
- builder.Replace("\"", """);
+ return srcValue;
+ }
- stringValue = builder.ToString();
- builder.Clear();
- }
+ private void AppendVersionedSrc(
+ string srcName,
+ string srcValue,
+ HtmlAttributeValueStyle valueStyle,
+ IHtmlContentBuilder builder)
+ {
+ srcValue = GetVersionedSrc(srcValue);
- return stringValue;
+ builder.AppendHtml(" ");
+ var attribute = new TagHelperAttribute(srcName, srcValue, valueStyle);
+ attribute.CopyTo(builder);
}
- private void AppendEncodedVersionedSrc(
+ private void WriteVersionedSrc(
string srcName,
string srcValue,
- TagHelperContent builder,
- bool generateForDocumentWrite)
+ HtmlAttributeValueStyle valueStyle,
+ TextWriter writer)
{
- if (AppendVersion == true)
- {
- srcValue = _fileVersionProvider.AddFileVersionToPath(srcValue);
- }
-
- if (generateForDocumentWrite)
- {
- // srcValue comes from a C# context and globbing. Must HTML-encode it to ensure the
- // written element is valid. Must also JavaScript-encode that value to ensure
- // the document.write() statement is valid.
- srcValue = HtmlEncoder.Encode(srcValue);
- srcValue = JavaScriptEncoder.Encode(srcValue);
- }
+ srcValue = GetVersionedSrc(srcValue);
- AppendAttribute(builder, srcName, srcValue, escapeQuotes: generateForDocumentWrite);
+ writer.Write(' ');
+ var attribute = new TagHelperAttribute(srcName, srcValue, valueStyle);
+ attribute.WriteTo(writer, HtmlEncoder);
}
private void EnsureGlobbingUrlBuilder()
@@ -429,46 +419,24 @@ private void BuildScriptTag(
var attribute = attributes[i];
if (!attribute.Name.Equals(SrcAttributeName, StringComparison.OrdinalIgnoreCase))
{
- AppendAttribute(builder, attribute.Name, attribute.Value, escapeQuotes: false);
+ builder.AppendHtml(" ");
+ attribute.CopyTo(builder);
}
else
{
addSrc = false;
- AppendEncodedVersionedSrc(attribute.Name, src, builder, generateForDocumentWrite: false);
+ AppendVersionedSrc(attribute.Name, src, attribute.ValueStyle, builder);
}
}
if (addSrc)
{
- AppendEncodedVersionedSrc(SrcAttributeName, src, builder, generateForDocumentWrite: false);
+ AppendVersionedSrc(SrcAttributeName, src, HtmlAttributeValueStyle.DoubleQuotes, builder);
}
builder.AppendHtml(">");
}
- private void AppendAttribute(TagHelperContent content, string key, object value, bool escapeQuotes)
- {
- content
- .AppendHtml(" ")
- .AppendHtml(key);
- if (escapeQuotes)
- {
- // Passed only JavaScript-encoded strings in this case. Do not perform HTML-encoding as well.
- content
- .AppendHtml("=\\\"")
- .AppendHtml((string)value)
- .AppendHtml("\\\"");
- }
- else
- {
- // HTML-encode the given value if necessary.
- content
- .AppendHtml("=\"")
- .Append(HtmlEncoder, value)
- .AppendHtml("\"");
- }
- }
-
private enum Mode
{
///
diff --git a/src/Microsoft.AspNetCore.Mvc.TagHelpers/TagHelperContentExtensions.cs b/src/Microsoft.AspNetCore.Mvc.TagHelpers/TagHelperContentExtensions.cs
deleted file mode 100644
index cd6740bd8e..0000000000
--- a/src/Microsoft.AspNetCore.Mvc.TagHelpers/TagHelperContentExtensions.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.IO;
-using System.Text.Encodings.Web;
-using Microsoft.AspNetCore.Html;
-using Microsoft.AspNetCore.Mvc.Razor;
-using Microsoft.AspNetCore.Mvc.Rendering;
-using Microsoft.AspNetCore.Razor.TagHelpers;
-
-namespace Microsoft.AspNetCore.Mvc.TagHelpers
-{
- ///
- /// Extension methods for .
- ///
- public static class TagHelperContentExtensions
- {
- ///
- /// Writes the specified with HTML encoding to given .
- ///
- /// The to write to.
- /// The to use when encoding .
- /// The to write.
- /// after the write operation has completed.
- ///
- /// s of type are written using
- /// .
- /// For all other types, the encoded result of
- /// is written to the .
- ///
- public static TagHelperContent Append(this TagHelperContent content, HtmlEncoder encoder, object value)
- {
- if (content == null)
- {
- throw new ArgumentNullException(nameof(content));
- }
-
- if (encoder == null)
- {
- throw new ArgumentNullException(nameof(encoder));
- }
-
- if (value == null)
- {
- // No real action but touch content to ensure IsModified is true.
- content.Append((string)null);
- return content;
- }
-
- string stringValue;
- var htmlString = value as HtmlString;
- if (htmlString != null)
- {
- // No need for a StringWriter in this case.
- stringValue = htmlString.ToString();
- }
- else
- {
- using (var stringWriter = new StringWriter())
- {
- RazorPage.WriteTo(stringWriter, encoder, value);
- stringValue = stringWriter.ToString();
- }
- }
-
- // In this case the text likely came directly from the Razor source. Since the original string is
- // an attribute value that may have been quoted with single quotes, must handle any double quotes
- // in the value. Writing the value out surrounded by double quotes.
- content.AppendHtml(stringValue.Replace("\"", """));
-
- return content;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.TagHelpers/TagHelperOutputExtensions.cs b/src/Microsoft.AspNetCore.Mvc.TagHelpers/TagHelperOutputExtensions.cs
index dcd917675b..89168a6497 100644
--- a/src/Microsoft.AspNetCore.Mvc.TagHelpers/TagHelperOutputExtensions.cs
+++ b/src/Microsoft.AspNetCore.Mvc.TagHelpers/TagHelperOutputExtensions.cs
@@ -158,10 +158,6 @@ private static void CopyHtmlAttribute(
TagHelperContext context)
{
var existingAttribute = context.AllAttributes[allAttributeIndex];
- var copiedAttribute = new TagHelperAttribute(
- existingAttribute.Name,
- existingAttribute.Value,
- existingAttribute.Minimized);
// Move backwards through context.AllAttributes from the provided index until we find a familiar attribute
// in tagHelperOutput where we can insert the copied value after the familiar one.
@@ -171,7 +167,7 @@ private static void CopyHtmlAttribute(
var index = IndexOfFirstMatch(previousName, tagHelperOutput.Attributes);
if (index != -1)
{
- tagHelperOutput.Attributes.Insert(index + 1, copiedAttribute);
+ tagHelperOutput.Attributes.Insert(index + 1, existingAttribute);
return;
}
}
@@ -184,13 +180,13 @@ private static void CopyHtmlAttribute(
var index = IndexOfFirstMatch(nextName, tagHelperOutput.Attributes);
if (index != -1)
{
- tagHelperOutput.Attributes.Insert(index, copiedAttribute);
+ tagHelperOutput.Attributes.Insert(index, existingAttribute);
return;
}
}
// Couldn't determine the attribute's location, add it to the end.
- tagHelperOutput.Attributes.Add(copiedAttribute);
+ tagHelperOutput.Attributes.Add(existingAttribute);
}
private static int IndexOfFirstMatch(string name, TagHelperAttributeList attributes)
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Index.Encoded.html b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Index.Encoded.html
index 5d696d4c25..39438a1bd7 100644
--- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Index.Encoded.html
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Index.Encoded.html
@@ -4,7 +4,7 @@
Product Index