- Updated `ScriptTagHelper` and `LinkTagHelper` to maintain their quotes on generated script tags.
- Removed `TagHelperOutputExtensions`. It's no longer needed.
- We no longer string replace quotes directly. We rely on encoding and the initial representation of an attribute to ensure quotes don't break generated attributes.
- Updated tests.
This commit is contained in:
N. Taylor Mullen 2016-05-03 14:08:51 -07:00
parent 5eed7b9b88
commit cfd58f1747
19 changed files with 160 additions and 259 deletions

View File

@ -88,8 +88,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor
ExecutionContextAddTagHelperAttributeMethodName =
nameof(TagHelperExecutionContext.AddTagHelperAttribute),
ExecutionContextAddHtmlAttributeMethodName = nameof(TagHelperExecutionContext.AddHtmlAttribute),
ExecutionContextAddMinimizedHtmlAttributeMethodName =
nameof(TagHelperExecutionContext.AddMinimizedHtmlAttribute),
ExecutionContextOutputPropertyName = nameof(TagHelperExecutionContext.Output),
RunnerTypeName = typeof(TagHelperRunner).FullName,

View File

@ -238,7 +238,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
/// <remarks>
/// All writes to the <see cref="Output"/> or <see cref="ViewContext.Writer"/> after calling this method will
/// be buffered until <see cref="EndWriteTagHelperAttribute"/> is called.
/// The content will be buffered using a shared <see cref="StringWriter"/> within this <see cref="RazorPage"/>
/// The content will be buffered using a shared <see cref="StringWriter"/> within this <see cref="RazorPage"/>
/// Nesting of <see cref="BeginWriteTagHelperAttribute"/> and <see cref="EndWriteTagHelperAttribute"/> method calls
/// is not supported.
/// </remarks>
@ -267,7 +267,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
/// </summary>
/// <returns>The content buffered by the shared <see cref="StringWriter"/> of this <see cref="RazorPage"/>.</returns>
/// <remarks>
/// This method assumes that there will be no nesting of <see cref="BeginWriteTagHelperAttribute"/>
/// This method assumes that there will be no nesting of <see cref="BeginWriteTagHelperAttribute"/>
/// and <see cref="EndWriteTagHelperAttribute"/> method calls.
/// </remarks>
public string EndWriteTagHelperAttribute()
@ -572,9 +572,14 @@ namespace Microsoft.AspNetCore.Mvc.Razor
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 @@ namespace Microsoft.AspNetCore.Mvc.Razor
// 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 @@ namespace Microsoft.AspNetCore.Mvc.Razor
{
_tagHelperAttributeInfo.ExecutionContext.AddHtmlAttribute(
_tagHelperAttributeInfo.Name,
_tagHelperAttributeInfo.Name);
_tagHelperAttributeInfo.Name,
_tagHelperAttributeInfo.AttributeValueStyle);
_tagHelperAttributeInfo.Suppressed = true;
return;
}
@ -637,7 +644,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
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 @@ namespace Microsoft.AspNetCore.Mvc.Razor
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 @@ namespace Microsoft.AspNetCore.Mvc.Razor
public int AttributeValuesCount { get; }
public HtmlAttributeValueStyle AttributeValueStyle { get; }
public bool Suppressed { get; set; }
}

View File

@ -422,7 +422,8 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
}
else
{
AppendAttribute(attribute.Name, attribute.Value, builder);
attribute.CopyTo(builder);
builder.AppendHtml(" ");
}
}
@ -441,15 +442,10 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
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("\" ");
}

View File

@ -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 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
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 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
// Fallback "src" values come from bound attributes and globbing. Must always be non-null.
Debug.Assert(src != null);
builder.AppendHtml("<script");
StringWriter.Write("<script");
var addSrc = true;
@ -316,80 +318,68 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
var attribute = attributes[i];
if (!attribute.Name.Equals(SrcAttributeName, StringComparison.OrdinalIgnoreCase))
{
var encodedKey = JavaScriptEncoder.Encode(attribute.Name);
var attributeValue = GetAttributeValue(attribute.Value);
var encodedValue = JavaScriptEncoder.Encode(attributeValue);
AppendAttribute(builder, encodedKey, encodedValue, escapeQuotes: true);
StringWriter.Write(' ');
attribute.WriteTo(StringWriter, HtmlEncoder);
}
else
{
addSrc = false;
AppendEncodedVersionedSrc(attribute.Name, src, builder, generateForDocumentWrite: true);
WriteVersionedSrc(attribute.Name, src, attribute.ValueStyle, StringWriter);
}
}
if (addSrc)
{
AppendEncodedVersionedSrc(SrcAttributeName, src, builder, generateForDocumentWrite: true);
WriteVersionedSrc(SrcAttributeName, src, HtmlAttributeValueStyle.DoubleQuotes, StringWriter);
}
builder.AppendHtml("><\\/script>");
StringWriter.Write("></script>");
}
var stringBuilder = StringWriter.GetStringBuilder();
var scriptTags = stringBuilder.ToString();
stringBuilder.Clear();
var encodedScriptTags = JavaScriptEncoder.Encode(scriptTags);
builder.AppendHtml(encodedScriptTags);
builder.AppendHtml("\"));</script>");
}
}
private string GetAttributeValue(object value)
{
string stringValue;
var htmlString = value as HtmlString;
if (htmlString != null)
{
// 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("\"", "&quot;");
}
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("\"", "&quot;");
stringValue = builder.ToString();
builder.Clear();
}
return stringValue;
}
private void AppendEncodedVersionedSrc(
string srcName,
string srcValue,
TagHelperContent builder,
bool generateForDocumentWrite)
private string GetVersionedSrc(string srcValue)
{
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 <script/> 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);
}
return srcValue;
}
AppendAttribute(builder, srcName, srcValue, escapeQuotes: generateForDocumentWrite);
private void AppendVersionedSrc(
string srcName,
string srcValue,
HtmlAttributeValueStyle valueStyle,
IHtmlContentBuilder builder)
{
srcValue = GetVersionedSrc(srcValue);
builder.AppendHtml(" ");
var attribute = new TagHelperAttribute(srcName, srcValue, valueStyle);
attribute.CopyTo(builder);
}
private void WriteVersionedSrc(
string srcName,
string srcValue,
HtmlAttributeValueStyle valueStyle,
TextWriter writer)
{
srcValue = GetVersionedSrc(srcValue);
writer.Write(' ');
var attribute = new TagHelperAttribute(srcName, srcValue, valueStyle);
attribute.WriteTo(writer, HtmlEncoder);
}
private void EnsureGlobbingUrlBuilder()
@ -429,46 +419,24 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
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("></script>");
}
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
{
/// <summary>

View File

@ -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
{
/// <summary>
/// Extension methods for <see cref="TagHelperContent"/>.
/// </summary>
public static class TagHelperContentExtensions
{
/// <summary>
/// Writes the specified <paramref name="value"/> with HTML encoding to given <paramref name="content"/>.
/// </summary>
/// <param name="content">The <see cref="TagHelperContent"/> to write to.</param>
/// <param name="encoder">The <see cref="HtmlEncoder"/> to use when encoding <paramref name="value"/>.</param>
/// <param name="value">The <see cref="object"/> to write.</param>
/// <returns><paramref name="content"/> after the write operation has completed.</returns>
/// <remarks>
/// <paramref name="value"/>s of type <see cref="Html.IHtmlContent"/> are written using
/// <see cref="Html.IHtmlContent.WriteTo(TextWriter, HtmlEncoder)"/>.
/// For all other types, the encoded result of <see cref="object.ToString"/>
/// is written to the <paramref name="content"/>.
/// </remarks>
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("\"", "&quot;"));
return content;
}
}
}

View File

@ -158,10 +158,6 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
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 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
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 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
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)

View File

@ -4,7 +4,7 @@
<a title="&lt;the title>" href="HtmlEncode[[/UrlEncode[[HtmlGeneration_Product]]]]">Product Index</a>
</div>
<div>
<a title="&quot;the&quot; title" href="HtmlEncode[[/UrlEncode[[HtmlGeneration_Product]]/UrlEncode[[List]]]]">Product List</a>
<a title='"the" title' href="HtmlEncode[[/UrlEncode[[HtmlGeneration_Product]]/UrlEncode[[List]]]]">Product List</a>
</div>
<div>
<a id="HtmlGenerationWebSite_Index">HtmlGenerationWebSite Index</a>

View File

@ -4,7 +4,7 @@
<a title="&lt;the title>" href="/HtmlGeneration_Product">Product Index</a>
</div>
<div>
<a title="&quot;the&quot; title" href="/HtmlGeneration_Product/List">Product List</a>
<a title='"the" title' href="/HtmlGeneration_Product/List">Product List</a>
</div>
<div>
<a id="HtmlGenerationWebSite_Index">HtmlGenerationWebSite Index</a>

View File

@ -14,7 +14,7 @@
<link rel="stylesheet" title="&lt;the title" href="HtmlEncode[[/styles/site.css]]" /><link rel="stylesheet" title="&lt;the title" href="HtmlEncode[[/styles/sub/site2.css]]" /><link rel="stylesheet" title="&lt;the title" href="HtmlEncode[[/styles/sub/site3.css]]" /><link rel="stylesheet" title="&lt;the title" href="HtmlEncode[[/styles/sub/site3.min.css]]" />
<!-- Globbed link tag with existing file and exclude -->
<link rel="stylesheet" title="&quot;the&quot; title" href="HtmlEncode[[/styles/site.css]]" /><link rel="stylesheet" title="&quot;the&quot; title" href="HtmlEncode[[/styles/sub/site2.css]]" />
<link rel="stylesheet" title='"the" title' href="HtmlEncode[[/styles/site.css]]" /><link rel="stylesheet" title='"the" title' href="HtmlEncode[[/styles/sub/site2.css]]" />
<!-- Globbed link tag missing include -->
<link rel="stylesheet">
@ -38,7 +38,7 @@
<link href="HtmlEncode[[/styles/site.css]]" rel="stylesheet" />
<!-- Fallback to static href -->
<link href="HtmlEncode[[/styles/site.min.css?a=b&c=d]]" rel="stylesheet" data-extra="test" title="&quot;the&quot; title" />
<link href="HtmlEncode[[/styles/site.min.css?a=b&c=d]]" rel="stylesheet" data-extra="test" title='"the" title' />
<meta name="x-stylesheet-fallback-test" content="" class="HtmlEncode[[hidden]]" /><script>!function(a,b,c){var d,e=document,f=e.getElementsByTagName("SCRIPT"),g=f[f.length-1].previousElementSibling,h=e.defaultView&&e.defaultView.getComputedStyle?e.defaultView.getComputedStyle(g):g.currentStyle;if(h&&h[a]!==b)for(d=0;d<c.length;d++)e.write('<link rel="stylesheet" href="'+c[d]+'"/>')}("JavaScriptEncode[[visibility]]","JavaScriptEncode[[hidden]]",["JavaScriptEncode[[HtmlEncode[[/styles/site.css?a=b&c=d]]]]"]);</script>
<!-- Fallback from globbed href to static href -->

View File

@ -14,7 +14,7 @@
<link rel="stylesheet" title="&lt;the title" href="/styles/site.css" /><link rel="stylesheet" title="&lt;the title" href="/styles/sub/site2.css" /><link rel="stylesheet" title="&lt;the title" href="/styles/sub/site3.css" /><link rel="stylesheet" title="&lt;the title" href="/styles/sub/site3.min.css" />
<!-- Globbed link tag with existing file and exclude -->
<link rel="stylesheet" title="&quot;the&quot; title" href="/styles/site.css" /><link rel="stylesheet" title="&quot;the&quot; title" href="/styles/sub/site2.css" />
<link rel="stylesheet" title='"the" title' href="/styles/site.css" /><link rel="stylesheet" title='"the" title' href="/styles/sub/site2.css" />
<!-- Globbed link tag missing include -->
<link rel="stylesheet">
@ -38,7 +38,7 @@
<link href="/styles/site.css" rel="stylesheet" />
<!-- Fallback to static href -->
<link href="/styles/site.min.css?a=b&amp;c=d" rel="stylesheet" data-extra="test" title="&quot;the&quot; title" />
<link href="/styles/site.min.css?a=b&amp;c=d" rel="stylesheet" data-extra="test" title='"the" title' />
<meta name="x-stylesheet-fallback-test" content="" class="hidden" /><script>!function(a,b,c){var d,e=document,f=e.getElementsByTagName("SCRIPT"),g=f[f.length-1].previousElementSibling,h=e.defaultView&&e.defaultView.getComputedStyle?e.defaultView.getComputedStyle(g):g.currentStyle;if(h&&h[a]!==b)for(d=0;d<c.length;d++)e.write('<link rel="stylesheet" href="'+c[d]+'"/>')}("visibility","hidden",["\/styles\/site.css?a=b\u0026amp;c=d"]);</script>
<!-- Fallback from globbed href to static href -->

View File

@ -13,27 +13,27 @@
<script src="HtmlEncode[[/blank.js?a=b&c=d]]" data-foo="foo-data2" title="&lt;the title>">
// TagHelper script with comment in body, and extra properties.
</script>
<script>(false||document.write("<script src=\"JavaScriptEncode[[HtmlEncode[[/styles/site.js?a=b&c=d]]]]\" JavaScriptEncode[[data-foo]]=\"JavaScriptEncode[[foo-data2]]\" JavaScriptEncode[[title]]=\"JavaScriptEncode[[&lt;the title>]]\"><\/script>"));</script>
<script>(false||document.write("JavaScriptEncode[[<script src="HtmlEncode[[/styles/site.js?a=b&c=d]]" data-foo="foo-data2" title="&lt;the title>"></script>]]"));</script>
<script src="HtmlEncode[[/blank.js]]" title="&quot;the&quot; title">
<script src="HtmlEncode[[/blank.js]]" title='"the" title'>
// Fallback to globbed src
</script>
<script>(false||document.write("<script src=\"JavaScriptEncode[[HtmlEncode[[/styles/site.js]]]]\" JavaScriptEncode[[title]]=\"JavaScriptEncode[[&quot;the&quot; title]]\"><\/script>"));</script>
<script>(false||document.write("JavaScriptEncode[[<script src="HtmlEncode[[/styles/site.js]]" title='"the" title'></script>]]"));</script>
<script src="HtmlEncode[[/blank.js]]">
// Fallback to globbed src with exclude
</script>
<script>(false||document.write("<script src=\"JavaScriptEncode[[HtmlEncode[[/styles/site.js]]]]\"><\/script><script src=\"JavaScriptEncode[[HtmlEncode[[/styles/sub/site2.js]]]]\"><\/script>"));</script>
<script>(false||document.write("JavaScriptEncode[[<script src="HtmlEncode[[/styles/site.js]]"></script><script src="HtmlEncode[[/styles/sub/site2.js]]"></script>]]"));</script>
<script src="HtmlEncode[[/blank.js]]">
// Fallback to globbed and static src
</script>
<script>(false||document.write("<script src=\"JavaScriptEncode[[HtmlEncode[[/styles/site.js]]]]\"><\/script><script src=\"JavaScriptEncode[[HtmlEncode[[/styles/sub/site2.js]]]]\"><\/script>"));</script>
<script>(false||document.write("JavaScriptEncode[[<script src="HtmlEncode[[/styles/site.js]]"></script><script src="HtmlEncode[[/styles/sub/site2.js]]"></script>]]"));</script>
<script src="HtmlEncode[[/blank.js]]">
// Fallback to globbed and static src should de-dupe
</script>
<script>(false||document.write("<script src=\"JavaScriptEncode[[HtmlEncode[[/styles/site.js]]]]\"><\/script>"));</script>
<script>(false||document.write("JavaScriptEncode[[<script src="HtmlEncode[[/styles/site.js]]"></script>]]"));</script>
<script src="HtmlEncode[[/blank.js]]">
// Fallback to globbed src with missing include
@ -42,7 +42,7 @@
<script src="HtmlEncode[[/blank.js]]">
// Fallback to static and globbed src with missing include
</script>
<script>(false||document.write("<script src=\"JavaScriptEncode[[HtmlEncode[[/styles/site.js]]]]\"><\/script>"));</script>
<script>(false||document.write("JavaScriptEncode[[<script src="HtmlEncode[[/styles/site.js]]"></script>]]"));</script>
<script src="HtmlEncode[[/blank.js]]">
// Fallback to globbed src outside of webroot
@ -55,7 +55,7 @@
<script data-foo="foo-data3">
// Valid TagHelper (although no src is provided) script with comment in body, and extra properties.
</script>
<script>(false||document.write("<script JavaScriptEncode[[data-foo]]=\"JavaScriptEncode[[foo-data3]]\" src=\"JavaScriptEncode[[HtmlEncode[[/styles/site.js]]]]\"><\/script>"));</script>
<script>(false||document.write("JavaScriptEncode[[<script data-foo="foo-data3" src="HtmlEncode[[/styles/site.js]]"></script>]]"));</script>
<script src="HtmlEncode[[/blank.js]]">
// Invalid TagHelper script with comment in body.
@ -98,12 +98,12 @@
<script src="HtmlEncode[[/blank.js]]">
// TagHelper script with comment in body, and file version.
</script>
<script>(false||document.write("<script src=\"JavaScriptEncode[[HtmlEncode[[/styles/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I]]]]\"><\/script>"));</script>
<script>(false||document.write("JavaScriptEncode[[<script src="HtmlEncode[[/styles/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I]]"></script>]]"));</script>
<script src="HtmlEncode[[/blank.js]]">
// Fallback to globbed src with file version.
</script>
<script>(false||document.write("<script src=\"JavaScriptEncode[[HtmlEncode[[/styles/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I]]]]\"><\/script>"));</script>
<script>(false||document.write("JavaScriptEncode[[<script src="HtmlEncode[[/styles/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I]]"></script>]]"));</script>
<script src="HtmlEncode[[/styles/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I]]">
// Regular script with comment in body, and file version.

View File

@ -13,27 +13,27 @@
<script src="/blank.js?a=b&amp;c=d" data-foo="foo-data2" title="&lt;the title>">
// TagHelper script with comment in body, and extra properties.
</script>
<script>(false||document.write("<script src=\"\/styles\/site.js?a=b\u0026amp;c=d\" data-foo=\"foo-data2\" title=\"\u0026lt;the title\u003E\"><\/script>"));</script>
<script>(false||document.write("\u003Cscript src=\u0022\/styles\/site.js?a=b\u0026amp;c=d\u0022 data-foo=\u0022foo-data2\u0022 title=\u0022\u0026lt;the title\u003E\u0022\u003E\u003C\/script\u003E"));</script>
<script src="/blank.js" title="&quot;the&quot; title">
<script src="/blank.js" title='"the" title'>
// Fallback to globbed src
</script>
<script>(false||document.write("<script src=\"\/styles\/site.js\" title=\"\u0026quot;the\u0026quot; title\"><\/script>"));</script>
<script>(false||document.write("\u003Cscript src=\u0022\/styles\/site.js\u0022 title=\u0027\u0022the\u0022 title\u0027\u003E\u003C\/script\u003E"));</script>
<script src="/blank.js">
// Fallback to globbed src with exclude
</script>
<script>(false||document.write("<script src=\"\/styles\/site.js\"><\/script><script src=\"\/styles\/sub\/site2.js\"><\/script>"));</script>
<script>(false||document.write("\u003Cscript src=\u0022\/styles\/site.js\u0022\u003E\u003C\/script\u003E\u003Cscript src=\u0022\/styles\/sub\/site2.js\u0022\u003E\u003C\/script\u003E"));</script>
<script src="/blank.js">
// Fallback to globbed and static src
</script>
<script>(false||document.write("<script src=\"\/styles\/site.js\"><\/script><script src=\"\/styles\/sub\/site2.js\"><\/script>"));</script>
<script>(false||document.write("\u003Cscript src=\u0022\/styles\/site.js\u0022\u003E\u003C\/script\u003E\u003Cscript src=\u0022\/styles\/sub\/site2.js\u0022\u003E\u003C\/script\u003E"));</script>
<script src="/blank.js">
// Fallback to globbed and static src should de-dupe
</script>
<script>(false||document.write("<script src=\"\/styles\/site.js\"><\/script>"));</script>
<script>(false||document.write("\u003Cscript src=\u0022\/styles\/site.js\u0022\u003E\u003C\/script\u003E"));</script>
<script src="/blank.js">
// Fallback to globbed src with missing include
@ -42,7 +42,7 @@
<script src="/blank.js">
// Fallback to static and globbed src with missing include
</script>
<script>(false||document.write("<script src=\"\/styles\/site.js\"><\/script>"));</script>
<script>(false||document.write("\u003Cscript src=\u0022\/styles\/site.js\u0022\u003E\u003C\/script\u003E"));</script>
<script src="/blank.js">
// Fallback to globbed src outside of webroot
@ -55,7 +55,7 @@
<script data-foo="foo-data3">
// Valid TagHelper (although no src is provided) script with comment in body, and extra properties.
</script>
<script>(false||document.write("<script data-foo=\"foo-data3\" src=\"\/styles\/site.js\"><\/script>"));</script>
<script>(false||document.write("\u003Cscript data-foo=\u0022foo-data3\u0022 src=\u0022\/styles\/site.js\u0022\u003E\u003C\/script\u003E"));</script>
<script src="/blank.js">
// Invalid TagHelper script with comment in body.
@ -98,12 +98,12 @@
<script src="/blank.js">
// TagHelper script with comment in body, and file version.
</script>
<script>(false||document.write("<script src=\"\/styles\/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I\"><\/script>"));</script>
<script>(false||document.write("\u003Cscript src=\u0022\/styles\/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I\u0022\u003E\u003C\/script\u003E"));</script>
<script src="/blank.js">
// Fallback to globbed src with file version.
</script>
<script>(false||document.write("<script src=\"\/styles\/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I\"><\/script>"));</script>
<script>(false||document.write("\u003Cscript src=\u0022\/styles\/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I\u0022\u003E\u003C\/script\u003E"));</script>
<script src="/styles/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I">
// Regular script with comment in body, and file version.

View File

@ -2,5 +2,4 @@
<body>
<form action="/Employee/DuplicateAntiforgeryTokenRegistration" method="post"><input name="__RequestVerificationToken" type="hidden" value="{0}" /></form>
</body>
</html>
</html>

View File

@ -55,7 +55,7 @@ __Microsoft_AspNetCore_Mvc_Razor_InputTestTagHelper.For = ModelExpressionProvide
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("for", __Microsoft_AspNetCore_Mvc_Razor_InputTestTagHelper.For);
__tagHelperExecutionContext.AddTagHelperAttribute("for", __Microsoft_AspNetCore_Mvc_Razor_InputTestTagHelper.For, global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.DoubleQuotes);
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
BeginContext(128, 24, false);
Write(__tagHelperExecutionContext.Output);
@ -74,7 +74,7 @@ __Microsoft_AspNetCore_Mvc_Razor_InputTestTagHelper.For = ModelExpressionProvide
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("for", __Microsoft_AspNetCore_Mvc_Razor_InputTestTagHelper.For);
__tagHelperExecutionContext.AddTagHelperAttribute("for", __Microsoft_AspNetCore_Mvc_Razor_InputTestTagHelper.For, global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.DoubleQuotes);
await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
BeginContext(154, 27, false);
Write(__tagHelperExecutionContext.Output);

View File

@ -1248,7 +1248,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
endTagHelperWritingScope: () => new DefaultTagHelperContent());
// Act
page.BeginAddHtmlAttributeValues(executionContext, "someattr", attributeValues.Length);
page.BeginAddHtmlAttributeValues(executionContext, "someattr", attributeValues.Length, HtmlAttributeValueStyle.SingleQuotes);
foreach (var value in attributeValues)
{
page.AddHtmlAttributeValue(value.Item1, value.Item2, value.Item3, value.Item4, 0, value.Item5);
@ -1261,14 +1261,14 @@ namespace Microsoft.AspNetCore.Mvc.Razor
Assert.Equal("someattr", htmlAttribute.Name, StringComparer.Ordinal);
var htmlContent = Assert.IsAssignableFrom<IHtmlContent>(htmlAttribute.Value);
Assert.Equal(expectedValue, HtmlContentUtilities.HtmlContentToString(htmlContent), StringComparer.Ordinal);
Assert.False(htmlAttribute.Minimized);
Assert.Equal(HtmlAttributeValueStyle.SingleQuotes, htmlAttribute.ValueStyle);
var context = executionContext.Context;
var allAttribute = Assert.Single(context.AllAttributes);
Assert.Equal("someattr", allAttribute.Name, StringComparer.Ordinal);
htmlContent = Assert.IsAssignableFrom<IHtmlContent>(allAttribute.Value);
Assert.Equal(expectedValue, HtmlContentUtilities.HtmlContentToString(htmlContent), StringComparer.Ordinal);
Assert.False(allAttribute.Minimized);
Assert.Equal(HtmlAttributeValueStyle.SingleQuotes, allAttribute.ValueStyle);
}
[Theory]
@ -1291,7 +1291,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
endTagHelperWritingScope: () => new DefaultTagHelperContent());
// Act
page.BeginAddHtmlAttributeValues(executionContext, "someattr", 1);
page.BeginAddHtmlAttributeValues(executionContext, "someattr", 1, HtmlAttributeValueStyle.DoubleQuotes);
page.AddHtmlAttributeValue(string.Empty, 9, attributeValue, 9, valueLength: 0, isLiteral: false);
page.EndAddHtmlAttributeValues(executionContext);
@ -1302,7 +1302,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
var attribute = Assert.Single(context.AllAttributes);
Assert.Equal("someattr", attribute.Name, StringComparer.Ordinal);
Assert.Equal(expectedValue, (string)attribute.Value, StringComparer.Ordinal);
Assert.False(attribute.Minimized);
Assert.Equal(HtmlAttributeValueStyle.DoubleQuotes, attribute.ValueStyle);
}
[Fact]
@ -1321,7 +1321,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
endTagHelperWritingScope: () => new DefaultTagHelperContent());
// Act
page.BeginAddHtmlAttributeValues(executionContext, "someattr", 1);
page.BeginAddHtmlAttributeValues(executionContext, "someattr", 1, HtmlAttributeValueStyle.NoQuotes);
page.AddHtmlAttributeValue(string.Empty, 9, true, 9, valueLength: 0, isLiteral: false);
page.EndAddHtmlAttributeValues(executionContext);
@ -1330,12 +1330,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor
var htmlAttribute = Assert.Single(output.Attributes);
Assert.Equal("someattr", htmlAttribute.Name, StringComparer.Ordinal);
Assert.Equal("someattr", (string)htmlAttribute.Value, StringComparer.Ordinal);
Assert.False(htmlAttribute.Minimized);
Assert.Equal(HtmlAttributeValueStyle.NoQuotes, htmlAttribute.ValueStyle);
var context = executionContext.Context;
var allAttribute = Assert.Single(context.AllAttributes);
Assert.Equal("someattr", allAttribute.Name, StringComparer.Ordinal);
Assert.Equal("someattr", (string)allAttribute.Value, StringComparer.Ordinal);
Assert.False(allAttribute.Minimized);
Assert.Equal(HtmlAttributeValueStyle.NoQuotes, allAttribute.ValueStyle);
}
public static TheoryData WriteAttributeData

View File

@ -101,7 +101,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers
Assert.Equal("href", attribute.Name, StringComparer.Ordinal);
var attributeValue = Assert.IsType<string>(attribute.Value);
Assert.Equal(expectedHref, attributeValue, StringComparer.Ordinal);
Assert.False(attribute.Minimized);
Assert.Equal(HtmlAttributeValueStyle.DoubleQuotes, attribute.ValueStyle);
}
public static TheoryData ResolvableUrlHtmlStringData
@ -157,7 +157,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers
Assert.Equal("href", attribute.Name, StringComparer.Ordinal);
var htmlContent = Assert.IsAssignableFrom<IHtmlContent>(attribute.Value);
Assert.Equal(expectedHref, HtmlContentUtilities.HtmlContentToString(htmlContent), StringComparer.Ordinal);
Assert.False(attribute.Minimized);
Assert.Equal(HtmlAttributeValueStyle.DoubleQuotes, attribute.ValueStyle);
}
public static TheoryData UnresolvableUrlData
@ -212,7 +212,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers
Assert.Equal("href", attribute.Name, StringComparer.Ordinal);
var attributeValue = Assert.IsType<string>(attribute.Value);
Assert.Equal(url, attributeValue, StringComparer.Ordinal);
Assert.False(attribute.Minimized);
Assert.Equal(HtmlAttributeValueStyle.DoubleQuotes, attribute.ValueStyle);
}
public static TheoryData UnresolvableUrlHtmlStringData
@ -267,7 +267,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers
Assert.Equal("href", attribute.Name, StringComparer.Ordinal);
var attributeValue = Assert.IsType<HtmlString>(attribute.Value);
Assert.Equal(url.ToString(), attributeValue.ToString(), StringComparer.Ordinal);
Assert.False(attribute.Minimized);
Assert.Equal(HtmlAttributeValueStyle.DoubleQuotes, attribute.ValueStyle);
}
[Fact]
@ -296,7 +296,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers
var attribute = Assert.Single(tagHelperOutput.Attributes);
Assert.Equal("href", attribute.Name, StringComparer.Ordinal);
Assert.Equal(true, attribute.Value);
Assert.False(attribute.Minimized);
Assert.Equal(HtmlAttributeValueStyle.DoubleQuotes, attribute.ValueStyle);
}
[Fact]

View File

@ -646,12 +646,12 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
{
// Arrange
var expectedContent =
"<link encoded=\"contains &quot;quotes&quot;\" href=\"HtmlEncode[[/css/site.css]]\" " +
"<link encoded='contains \"quotes\"' href=\"HtmlEncode[[/css/site.css]]\" " +
"literal=\"HtmlEncode[[all HTML encoded]]\" " +
"mixed=\"HtmlEncode[[HTML encoded]] and contains &quot;quotes&quot;\" />" +
"<link encoded=\"contains &quot;quotes&quot;\" href=\"HtmlEncode[[/base.css]]\" " +
"mixed='HtmlEncode[[HTML encoded]] and contains \"quotes\"' />" +
"<link encoded='contains \"quotes\"' href=\"HtmlEncode[[/base.css]]\" " +
"literal=\"HtmlEncode[[all HTML encoded]]\" " +
"mixed=\"HtmlEncode[[HTML encoded]] and contains &quot;quotes&quot;\" />";
"mixed='HtmlEncode[[HTML encoded]] and contains \"quotes\"' />";
var mixed = new DefaultTagHelperContent();
mixed.Append("HTML encoded");
mixed.AppendHtml(" and contains \"quotes\"");
@ -659,18 +659,18 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
attributes: new TagHelperAttributeList
{
{ "asp-href-include", "**/*.css" },
{ "encoded", new HtmlString("contains \"quotes\"") },
{ new TagHelperAttribute("encoded", new HtmlString("contains \"quotes\""), HtmlAttributeValueStyle.SingleQuotes) },
{ "href", "/css/site.css" },
{ "literal", "all HTML encoded" },
{ "mixed", mixed },
{ new TagHelperAttribute("mixed", mixed, HtmlAttributeValueStyle.SingleQuotes) },
});
var output = MakeTagHelperOutput(
"link",
attributes: new TagHelperAttributeList
{
{ "encoded", new HtmlString("contains \"quotes\"") },
{ new TagHelperAttribute("encoded", new HtmlString("contains \"quotes\""), HtmlAttributeValueStyle.SingleQuotes) },
{ "literal", "all HTML encoded" },
{ "mixed", mixed },
{ new TagHelperAttribute("mixed", mixed, HtmlAttributeValueStyle.SingleQuotes) },
});
var hostingEnvironment = MakeHostingEnvironment();
var viewContext = MakeViewContext();
@ -843,10 +843,10 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
public void RenderLinkTags_FallbackHref_WithFileVersion_EncodesAsExpected()
{
// Arrange
var expectedContent = "<link encoded=\"contains &quot;quotes&quot;\" " +
var expectedContent = "<link encoded=\"contains \"quotes\"\" " +
"href=\"HtmlEncode[[/css/site.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\" " +
"literal=\"HtmlEncode[[all HTML encoded]]\" " +
"mixed=\"HtmlEncode[[HTML encoded]] and contains &quot;quotes&quot;\" />" +
"mixed=\"HtmlEncode[[HTML encoded]] and contains \"quotes\"\" />" +
Environment.NewLine +
"<meta name=\"x-stylesheet-fallback-test\" content=\"\" class=\"HtmlEncode[[hidden]]\" />" +
"<script>!function(a,b,c){var d,e=document,f=e.getElementsByTagName(\"SCRIPT\")," +
@ -961,8 +961,10 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
// Assert
Assert.Equal("link", output.TagName);
Assert.Equal("/css/site.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk", output.Attributes["href"].Value);
Assert.Equal("<link rel=\"stylesheet\" href=\"HtmlEncode[[/base.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\" />",
output.PostElement.GetContent());
var content = HtmlContentUtilities.HtmlContentToString(output.PostElement, new HtmlTestEncoder());
Assert.Equal(
"<link rel=\"stylesheet\" href=\"HtmlEncode[[/base.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\" />",
content);
}
private static ViewContext MakeViewContext(string requestPathBase = null)

View File

@ -623,11 +623,11 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
{
// Arrange
var expectedContent =
"<script encoded=\"contains &quot;quotes&quot;\" literal=\"HtmlEncode[[all HTML encoded]]\" " +
"mixed=\"HtmlEncode[[HTML encoded]] and contains &quot;quotes&quot;\" " +
"<script encoded='contains \"quotes\"' literal=\"HtmlEncode[[all HTML encoded]]\" " +
"mixed='HtmlEncode[[HTML encoded]] and contains \"quotes\"' " +
"src=\"HtmlEncode[[/js/site.js]]\"></script>" +
"<script encoded=\"contains &quot;quotes&quot;\" literal=\"HtmlEncode[[all HTML encoded]]\" " +
"mixed=\"HtmlEncode[[HTML encoded]] and contains &quot;quotes&quot;\" " +
"<script encoded='contains \"quotes\"' literal=\"HtmlEncode[[all HTML encoded]]\" " +
"mixed='HtmlEncode[[HTML encoded]] and contains \"quotes\"' " +
"src=\"HtmlEncode[[/common.js]]\"></script>";
var mixed = new DefaultTagHelperContent();
mixed.Append("HTML encoded");
@ -636,18 +636,18 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
attributes: new TagHelperAttributeList
{
{ "asp-src-include", "**/*.js" },
{ "encoded", new HtmlString("contains \"quotes\"") },
{ new TagHelperAttribute("encoded", new HtmlString("contains \"quotes\""), HtmlAttributeValueStyle.SingleQuotes) },
{ "literal", "all HTML encoded" },
{ "mixed", mixed },
{ new TagHelperAttribute("mixed", mixed, HtmlAttributeValueStyle.SingleQuotes) },
{ "src", "/js/site.js" },
});
var output = MakeTagHelperOutput(
"script",
attributes: new TagHelperAttributeList
{
{ "encoded", new HtmlString("contains \"quotes\"") },
{ new TagHelperAttribute("encoded", new HtmlString("contains \"quotes\""), HtmlAttributeValueStyle.SingleQuotes) },
{ "literal", "all HTML encoded"},
{ "mixed", mixed},
{ new TagHelperAttribute("mixed", mixed, HtmlAttributeValueStyle.SingleQuotes) },
});
var hostingEnvironment = MakeHostingEnvironment();
var viewContext = MakeViewContext();
@ -786,9 +786,9 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
// Assert
Assert.Equal("script", output.TagName);
Assert.Equal("/js/site.js?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk", output.Attributes["src"].Value);
Assert.Equal(Environment.NewLine + "<script>(isavailable()||document.write(\"<script src=" +
"\\\"JavaScriptEncode[[HtmlEncode[[fallback.js?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]]]\\\"" +
"><\\/script>\"));</script>", output.PostElement.GetContent());
Assert.Equal(Environment.NewLine + "<script>(isavailable()||document.write(\"JavaScriptEncode[[<script " +
"src=\"HtmlEncode[[fallback.js?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\">" +
"</script>]]\"));</script>", output.PostElement.GetContent());
}
[Fact]
@ -796,16 +796,14 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
{
// Arrange
var expectedContent =
"<script encoded=\"contains &quot;quotes&quot;\" literal=\"HtmlEncode[[all HTML encoded]]\" " +
"mixed=\"HtmlEncode[[HTML encoded]] and contains &quot;quotes&quot;\" " +
"<script encoded='contains \"quotes\"' literal=\"HtmlEncode[[all HTML encoded]]\" " +
"mixed='HtmlEncode[[HTML encoded]] and contains \"quotes\"' " +
"src=\"HtmlEncode[[/js/site.js?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\"></script>" +
Environment.NewLine +
"<script>(isavailable()||document.write(\"<script " +
"JavaScriptEncode[[encoded]]=\\\"JavaScriptEncode[[contains &quot;quotes&quot;]]\\\" " +
"JavaScriptEncode[[literal]]=\\\"JavaScriptEncode[[HtmlEncode[[all HTML encoded]]]]\\\" " +
"JavaScriptEncode[[mixed]]=\\\"JavaScriptEncode[[HtmlEncode[[HTML encoded]] and contains &quot;quotes&quot;]]\\\" " +
"src=\\\"JavaScriptEncode[[HtmlEncode[[fallback.js?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]]]\\\">" +
"<\\/script>\"));</script>";
"<script>(isavailable()||document.write(\"JavaScriptEncode[[<script encoded=\'contains \"quotes\"\' " +
"literal=\"HtmlEncode[[all HTML encoded]]\" mixed=\'HtmlEncode[[HTML encoded]] and contains " +
"\"quotes\"' src=\"HtmlEncode[[fallback.js?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\">" +
"</script>]]\"));</script>";
var mixed = new DefaultTagHelperContent();
mixed.Append("HTML encoded");
mixed.AppendHtml(" and contains \"quotes\"");
@ -815,18 +813,18 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
{ "asp-append-version", "true" },
{ "asp-fallback-src-include", "fallback.js" },
{ "asp-fallback-test", "isavailable()" },
{ "encoded", new HtmlString("contains \"quotes\"") },
{ new TagHelperAttribute("encoded", new HtmlString("contains \"quotes\""), HtmlAttributeValueStyle.SingleQuotes) },
{ "literal", "all HTML encoded" },
{ "mixed", mixed },
{ new TagHelperAttribute("mixed", mixed, HtmlAttributeValueStyle.SingleQuotes) },
{ "src", "/js/site.js" },
});
var output = MakeTagHelperOutput(
"script",
attributes: new TagHelperAttributeList
{
{ "encoded", new HtmlString("contains \"quotes\"") },
{ new TagHelperAttribute("encoded", new HtmlString("contains \"quotes\""), HtmlAttributeValueStyle.SingleQuotes) },
{ "literal", "all HTML encoded" },
{ "mixed", mixed },
{ new TagHelperAttribute("mixed", mixed, HtmlAttributeValueStyle.SingleQuotes) },
});
var hostingEnvironment = MakeHostingEnvironment();
var viewContext = MakeViewContext();

View File

@ -979,13 +979,21 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
// Normal comparer (TagHelperAttribute.Equals()) doesn't care about the Name case, in tests we do.
return attributeX != null &&
string.Equals(attributeX.Name, attributeY.Name, StringComparison.Ordinal) &&
attributeX.Minimized == attributeY.Minimized &&
(attributeX.Minimized || Equals(attributeX.Value, attributeY.Value));
attributeX.ValueStyle == attributeY.ValueStyle &&
(attributeX.ValueStyle == HtmlAttributeValueStyle.Minimized || Equals(attributeX.Value, attributeY.Value));
}
public int GetHashCode(TagHelperAttribute attribute)
{
return attribute.GetHashCode();
// Manually combine hash codes here. We can't reference HashCodeCombiner because we have internals visible
// from Mvc.Core and Mvc.TagHelpers; both of which reference HashCodeCombiner.
var baseHashCode = 0x1505L;
var attributeHashCode = attribute.GetHashCode();
var combinedHash = ((baseHashCode << 5) + baseHashCode) ^ attributeHashCode;
var nameHashCode = StringComparer.Ordinal.GetHashCode(attribute.Name);
combinedHash = ((combinedHash << 5) + combinedHash) ^ nameHashCode;
return combinedHash.GetHashCode();
}
}
}