- set correct `MarkAsHtmlEncodedMethodName` value in `MvcRazorHost`
- handle `HtmlString` values in `TagHelperOutput.Attributes` in `RazorPage`
 - special-case double-quotes in `HtmlString` values
 - add `static WriteTo()` method for use in tag helpers
- handle non-`string` `output.Attributes` values in tag helpers
- make `TagHelperContentWrapperTextWriter` a `public` class
- provide a `TagHelperContent.Append(object, ...)` extension method
- add `LinkTagHelper.Href` and `ScriptTagHelper.Src` properties
 - avoid Razor HTML-encoding these attribute values before their use
- add `JavaScriptEncoder` properties in `LinkTagHelper` and `ScriptTagHelper`
 - allow encoding testing without unit testing the default encoder
- handle MVC and Razor changes for this bug in existing tests
- add functional tests of encodings
- add test encoders to TestCommon project

nits:
- correct `InputTagHelper` to pass `type=""` through unchanged
- set correct `TagHelperContentTypeName` value in `MvcRazorHost`
- remove unnecessary `FormTagHelper.Method` and `OptionTagHelper.Selected` properties
- remove complex ternaries and `ShouldAddFileVersion()` methods
- add a few debug assertions
- fix some odd wrapping
- remove or `#if`-out unused `using`s
- remove trailing whitespace
This commit is contained in:
Doug Bunting 2015-03-29 15:54:53 -07:00
parent 9402f1485c
commit 5882cdb03f
46 changed files with 1342 additions and 564 deletions

View File

@ -3,7 +3,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
#if NET45
using Microsoft.AspNet.FileProviders; using Microsoft.AspNet.FileProviders;
#endif
using Microsoft.AspNet.Mvc.Razor.Directives; using Microsoft.AspNet.Mvc.Razor.Directives;
using Microsoft.AspNet.Mvc.Razor.Internal; using Microsoft.AspNet.Mvc.Razor.Internal;
using Microsoft.AspNet.Razor; using Microsoft.AspNet.Razor;
@ -18,6 +20,8 @@ namespace Microsoft.AspNet.Mvc.Razor
public class MvcRazorHost : RazorEngineHost, IMvcRazorHost public class MvcRazorHost : RazorEngineHost, IMvcRazorHost
{ {
private const string BaseType = "Microsoft.AspNet.Mvc.Razor.RazorPage"; private const string BaseType = "Microsoft.AspNet.Mvc.Razor.RazorPage";
private const string HtmlHelperPropertyName = "Html";
private static readonly string[] _defaultNamespaces = new[] private static readonly string[] _defaultNamespaces = new[]
{ {
"System", "System",
@ -28,7 +32,7 @@ namespace Microsoft.AspNet.Mvc.Razor
}; };
private static readonly Chunk[] _defaultInheritedChunks = new[] private static readonly Chunk[] _defaultInheritedChunks = new[]
{ {
new InjectChunk("Microsoft.AspNet.Mvc.Rendering.IHtmlHelper<TModel>", "Html"), new InjectChunk("Microsoft.AspNet.Mvc.Rendering.IHtmlHelper<TModel>", HtmlHelperPropertyName),
new InjectChunk("Microsoft.AspNet.Mvc.IViewComponentHelper", "Component"), new InjectChunk("Microsoft.AspNet.Mvc.IViewComponentHelper", "Component"),
new InjectChunk("Microsoft.AspNet.Mvc.IUrlHelper", "Url"), new InjectChunk("Microsoft.AspNet.Mvc.IUrlHelper", "Url"),
}; };
@ -76,6 +80,8 @@ namespace Microsoft.AspNet.Mvc.Razor
ScopeManagerBeginMethodName = nameof(TagHelperScopeManager.Begin), ScopeManagerBeginMethodName = nameof(TagHelperScopeManager.Begin),
ScopeManagerEndMethodName = nameof(TagHelperScopeManager.End), ScopeManagerEndMethodName = nameof(TagHelperScopeManager.End),
TagHelperContentTypeName = nameof(TagHelperContent),
// Can't use nameof because RazorPage is not accessible here. // Can't use nameof because RazorPage is not accessible here.
CreateTagHelperMethodName = "CreateTagHelper", CreateTagHelperMethodName = "CreateTagHelper",
StartTagHelperWritingScopeMethodName = "StartTagHelperWritingScope", StartTagHelperWritingScopeMethodName = "StartTagHelperWritingScope",
@ -83,6 +89,9 @@ namespace Microsoft.AspNet.Mvc.Razor
WriteTagHelperAsyncMethodName = "WriteTagHelperAsync", WriteTagHelperAsyncMethodName = "WriteTagHelperAsync",
WriteTagHelperToAsyncMethodName = "WriteTagHelperToAsync", WriteTagHelperToAsyncMethodName = "WriteTagHelperToAsync",
// Can't use nameof because IHtmlHelper is (also) not accessible here.
MarkAsHtmlEncodedMethodName = HtmlHelperPropertyName + ".Raw",
}) })
{ {
ResolveUrlMethodName = "Href", ResolveUrlMethodName = "Href",

View File

@ -294,11 +294,10 @@ namespace Microsoft.AspNet.Mvc.Razor
foreach (var attribute in tagHelperOutput.Attributes) foreach (var attribute in tagHelperOutput.Attributes)
{ {
var value = HtmlEncoder.HtmlEncode(attribute.Value);
writer.Write(' '); writer.Write(' ');
writer.Write(attribute.Key); writer.Write(attribute.Key);
writer.Write("=\""); writer.Write("=\"");
writer.Write(value); WriteTo(writer, HtmlEncoder, attribute.Value, escapeQuotes: true);
writer.Write('"'); writer.Write('"');
} }
@ -366,26 +365,67 @@ namespace Microsoft.AspNet.Mvc.Razor
/// </remarks> /// </remarks>
public virtual void WriteTo([NotNull] TextWriter writer, object value) public virtual void WriteTo([NotNull] TextWriter writer, object value)
{ {
if (value != null && value != HtmlString.Empty) WriteTo(writer, HtmlEncoder, value, escapeQuotes: false);
}
/// <summary>
/// Writes the specified <paramref name="value"/> with HTML encoding to given <paramref name="writer"/>.
/// </summary>
/// <param name="writer">The <see cref="TextWriter"/> instance to write to.</param>
/// <param name="encoder">The <see cref="IHtmlEncoder"/> to use when encoding <paramref name="value"/>.</param>
/// <param name="value">The <see cref="object"/> to write.</param>
/// <param name="escapeQuotes">
/// If <c>true</c> escapes double quotes in a <paramref name="value"/> of type <see cref="HtmlString"/>.
/// Otherwise writes <see cref="HtmlString"/> values as-is.
/// </param>
/// <remarks>
/// <paramref name="value"/>s of type <see cref="HtmlString"/> are written without encoding and the
/// <see cref="HelperResult.WriteTo(TextWriter)"/> is invoked for <see cref="HelperResult"/> types.
/// For all other types, the encoded result of <see cref="object.ToString"/> is written to the
/// <paramref name="writer"/>.
/// </remarks>
public static void WriteTo(
[NotNull] TextWriter writer,
[NotNull] IHtmlEncoder encoder,
object value,
bool escapeQuotes)
{
if (value == null || value == HtmlString.Empty)
{ {
var helperResult = value as HelperResult; return;
if (helperResult != null)
{
helperResult.WriteTo(writer);
}
else
{
var htmlString = value as HtmlString;
if (htmlString != null)
{
writer.Write(htmlString);
}
else
{
WriteTo(writer, value.ToString());
}
}
} }
var helperResult = value as HelperResult;
if (helperResult != null)
{
helperResult.WriteTo(writer);
return;
}
var htmlString = value as HtmlString;
if (htmlString != null)
{
if (escapeQuotes)
{
// 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.
//
// Do not combine following condition with check of escapeQuotes; htmlString.ToString() can be
// expensive when the HtmlString is created with a StringCollectionTextWriter.
var stringValue = htmlString.ToString();
if (stringValue.Contains("\""))
{
writer.Write(stringValue.Replace("\"", "&quot;"));
return;
}
}
htmlString.WriteTo(writer);
return;
}
WriteTo(writer, encoder, value.ToString());
} }
/// <summary> /// <summary>
@ -394,10 +434,15 @@ namespace Microsoft.AspNet.Mvc.Razor
/// <param name="writer">The <see cref="TextWriter"/> instance to write to.</param> /// <param name="writer">The <see cref="TextWriter"/> instance to write to.</param>
/// <param name="value">The <see cref="string"/> to write.</param> /// <param name="value">The <see cref="string"/> to write.</param>
public virtual void WriteTo([NotNull] TextWriter writer, string value) public virtual void WriteTo([NotNull] TextWriter writer, string value)
{
WriteTo(writer, HtmlEncoder, value);
}
private static void WriteTo(TextWriter writer, IHtmlEncoder encoder, string value)
{ {
if (!string.IsNullOrEmpty(value)) if (!string.IsNullOrEmpty(value))
{ {
HtmlEncoder.HtmlEncode(value, writer); encoder.HtmlEncode(value, writer);
} }
} }
@ -766,33 +811,5 @@ namespace Microsoft.AspNet.Mvc.Razor
throw new InvalidOperationException(Resources.FormatRazorPage_MethodCannotBeCalled(methodName)); throw new InvalidOperationException(Resources.FormatRazorPage_MethodCannotBeCalled(methodName));
} }
} }
private class TagHelperContentWrapperTextWriter : TextWriter
{
public TagHelperContentWrapperTextWriter(Encoding encoding)
{
Content = new DefaultTagHelperContent();
Encoding = encoding;
}
public TagHelperContent Content { get; }
public override Encoding Encoding { get; }
public override void Write(string value)
{
Content.Append(value);
}
public override void Write(char value)
{
Content.Append(value.ToString());
}
public override string ToString()
{
return Content.ToString();
}
}
} }
} }

View File

@ -0,0 +1,62 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Text;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.Razor
{
/// <summary>
/// <see cref="TextWriter"/> implementation which writes to a <see cref="TagHelperContent"/> instance.
/// </summary>
public class TagHelperContentWrapperTextWriter : TextWriter
{
/// <summary>
/// Initializes a new instance of the <see cref="TagHelperContentWrapperTextWriter"/> class.
/// </summary>
/// <param name="encoding">The <see cref="Encoding"/> in which output is written.</param>
public TagHelperContentWrapperTextWriter([NotNull] Encoding encoding)
: this(encoding, new DefaultTagHelperContent())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TagHelperContentWrapperTextWriter"/> class.
/// </summary>
/// <param name="encoding">The <see cref="Encoding"/> in which output is written.</param>
/// <param name="content">The <see cref="TagHelperContent"/> to write to.</param>
public TagHelperContentWrapperTextWriter([NotNull] Encoding encoding, [NotNull] TagHelperContent content)
{
Content = content;
Encoding = encoding;
}
/// <summary>
/// The <see cref="TagHelperContent"/> this <see cref="TagHelperContentWrapperTextWriter"/> writes to.
/// </summary>
public TagHelperContent Content { get; }
/// <inheritdoc />
public override Encoding Encoding { get; }
/// <inheritdoc />
public override void Write(string value)
{
Content.Append(value);
}
/// <inheritdoc />
public override void Write(char value)
{
Content.Append(value.ToString());
}
/// <inheritdoc />
public override string ToString()
{
return Content.ToString();
}
}
}

View File

@ -153,7 +153,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// TODO: https://github.com/aspnet/Razor/issues/89 - We will not need this method once #89 is completed. // TODO: https://github.com/aspnet/Razor/issues/89 - We will not need this method once #89 is completed.
private static Dictionary<string, object> GetRouteValues( private static Dictionary<string, object> GetRouteValues(
TagHelperOutput output, IEnumerable<KeyValuePair<string, string>> routePrefixedAttributes) TagHelperOutput output,
IEnumerable<KeyValuePair<string, object>> routePrefixedAttributes)
{ {
Dictionary<string, object> routeValues = null; Dictionary<string, object> routeValues = null;
if (routePrefixedAttributes.Any()) if (routePrefixedAttributes.Any())
@ -161,10 +162,11 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Prefixed values should be treated as bound attributes, remove them from the output. // Prefixed values should be treated as bound attributes, remove them from the output.
output.RemoveRange(routePrefixedAttributes); output.RemoveRange(routePrefixedAttributes);
// Generator.GenerateForm does not accept a Dictionary<string, string> for route values. // Remove prefix from keys and convert all values to strings. HtmlString and similar classes are not
// meaningful to routing.
routeValues = routePrefixedAttributes.ToDictionary( routeValues = routePrefixedAttributes.ToDictionary(
attribute => attribute.Key.Substring(RouteAttributePrefix.Length), attribute => attribute.Key.Substring(RouteAttributePrefix.Length),
attribute => (object)attribute.Value); attribute => (object)attribute.Value.ToString());
} }
return routeValues; return routeValues;

View File

@ -40,11 +40,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
[HtmlAttributeName(ControllerAttributeName)] [HtmlAttributeName(ControllerAttributeName)]
public string Controller { get; set; } public string Controller { get; set; }
/// <summary>
/// The HTTP method for processing the form, either GET or POST.
/// </summary>
public string Method { get; set; }
/// <summary> /// <summary>
/// Whether the anti-forgery token should be generated. /// Whether the anti-forgery token should be generated.
/// </summary> /// </summary>
@ -84,12 +79,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// User is using the FormTagHelper like a normal <form> tag. Anti-forgery default should be false to // User is using the FormTagHelper like a normal <form> tag. Anti-forgery default should be false to
// not force the anti-forgery token on the user. // not force the anti-forgery token on the user.
antiForgeryDefault = false; antiForgeryDefault = false;
// Restore method attribute.
if (Method != null)
{
output.CopyHtmlAttribute(nameof(Method), context);
}
} }
else else
{ {
@ -98,7 +87,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Action, Action,
Controller, Controller,
routeValues, routeValues,
Method, method: null,
htmlAttributes: null); htmlAttributes: null);
if (tagBuilder != null) if (tagBuilder != null)
@ -120,7 +109,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// TODO: https://github.com/aspnet/Razor/issues/89 - We will not need this method once #89 is completed. // TODO: https://github.com/aspnet/Razor/issues/89 - We will not need this method once #89 is completed.
private static Dictionary<string, object> GetRouteValues( private static Dictionary<string, object> GetRouteValues(
TagHelperOutput output, IEnumerable<KeyValuePair<string, string>> routePrefixedAttributes) TagHelperOutput output,
IEnumerable<KeyValuePair<string, object>> routePrefixedAttributes)
{ {
Dictionary<string, object> routeValues = null; Dictionary<string, object> routeValues = null;
if (routePrefixedAttributes.Any()) if (routePrefixedAttributes.Any())
@ -128,10 +118,11 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Prefixed values should be treated as bound attributes, remove them from the output. // Prefixed values should be treated as bound attributes, remove them from the output.
output.RemoveRange(routePrefixedAttributes); output.RemoveRange(routePrefixedAttributes);
// Generator.GenerateForm does not accept a Dictionary<string, string> for route values. // Remove prefix from keys and convert all values to strings. HtmlString and similar classes are not
// meaningful to routing.
routeValues = routePrefixedAttributes.ToDictionary( routeValues = routePrefixedAttributes.ToDictionary(
attribute => attribute.Key.Substring(RouteAttributePrefix.Length), attribute => attribute.Key.Substring(RouteAttributePrefix.Length),
attribute => (object)attribute.Value); attribute => (object)attribute.Value.ToString());
} }
return routeValues; return routeValues;

View File

@ -8,7 +8,6 @@ using System.Linq;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers; using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.AspNet.Razor.TagHelpers;
namespace Microsoft.AspNet.Mvc.TagHelpers namespace Microsoft.AspNet.Mvc.TagHelpers
{ {
@ -112,7 +111,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{ {
// Pass through attributes that are also well-known HTML attributes. Must be done prior to any copying // Pass through attributes that are also well-known HTML attributes. Must be done prior to any copying
// from a TagBuilder. // from a TagBuilder.
if (!string.IsNullOrEmpty(InputTypeName)) if (InputTypeName != null)
{ {
output.CopyHtmlAttribute("type", context); output.CopyHtmlAttribute("type", context);
} }

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Hosting;
@ -10,7 +11,6 @@ using Microsoft.AspNet.Mvc.TagHelpers.Internal;
using Microsoft.AspNet.Razor.Runtime.TagHelpers; using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.Caching.Memory; using Microsoft.Framework.Caching.Memory;
using Microsoft.Framework.Logging; using Microsoft.Framework.Logging;
using Microsoft.Framework.Runtime;
using Microsoft.Framework.WebEncoders; using Microsoft.Framework.WebEncoders;
namespace Microsoft.AspNet.Mvc.TagHelpers namespace Microsoft.AspNet.Mvc.TagHelpers
@ -98,6 +98,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Fallback = 2, Fallback = 2,
} }
/// <summary>
/// Address of the linked resource.
/// </summary>
/// <remarks>
/// Passed through to the generated HTML in all cases.
/// </remarks>
[HtmlAttributeName(HrefAttributeName)]
public string Href { get; set; }
/// <summary> /// <summary>
/// A comma separated list of globbed file patterns of CSS stylesheets to load. /// A comma separated list of globbed file patterns of CSS stylesheets to load.
/// The glob patterns are assessed relative to the application's 'webroot' setting. /// The glob patterns are assessed relative to the application's 'webroot' setting.
@ -189,12 +198,21 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
[Activate] [Activate]
protected internal IHtmlEncoder HtmlEncoder { get; set; } protected internal IHtmlEncoder HtmlEncoder { get; set; }
[Activate]
protected internal IJavaScriptStringEncoder JavaScriptEncoder { get; set; }
// Internal for ease of use when testing. // Internal for ease of use when testing.
protected internal GlobbingUrlBuilder GlobbingUrlBuilder { get; set; } protected internal GlobbingUrlBuilder GlobbingUrlBuilder { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public override void Process(TagHelperContext context, TagHelperOutput output) public override void Process(TagHelperContext context, TagHelperOutput output)
{ {
// Pass through attribute that is also a well-known HTML attribute.
if (Href != null)
{
output.CopyHtmlAttribute(HrefAttributeName, context);
}
var modeResult = AttributeMatcher.DetermineMode(context, ModeDetails); var modeResult = AttributeMatcher.DetermineMode(context, ModeDetails);
var logger = Logger ?? LoggerFactory.CreateLogger<LinkTagHelper>(); var logger = Logger ?? LoggerFactory.CreateLogger<LinkTagHelper>();
@ -210,8 +228,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Get the highest matched mode // Get the highest matched mode
var mode = modeResult.FullMatches.Select(match => match.Mode).Max(); var mode = modeResult.FullMatches.Select(match => match.Mode).Max();
// NOTE: Values in TagHelperOutput.Attributes are already HtmlEncoded // NOTE: Values in TagHelperOutput.Attributes may already be HTML-encoded.
var attributes = new Dictionary<string, string>(output.Attributes); var attributes = new Dictionary<string, object>(output.Attributes);
var builder = new DefaultTagHelperContent(); var builder = new DefaultTagHelperContent();
@ -236,17 +254,17 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
output.Content.SetContent(builder); output.Content.SetContent(builder);
} }
private void BuildGlobbedLinkTags(IDictionary<string, string> attributes, TagHelperContent builder) private void BuildGlobbedLinkTags(IDictionary<string, object> attributes, TagHelperContent builder)
{ {
// Build a <link /> tag for each matched href as well as the original one in the source file
string staticHref;
attributes.TryGetValue(HrefAttributeName, out staticHref);
EnsureGlobbingUrlBuilder(); EnsureGlobbingUrlBuilder();
var urls = GlobbingUrlBuilder.BuildUrlList(staticHref, HrefInclude, HrefExclude);
// Build a <link /> tag for each matched href as well as the original one in the source file
var urls = GlobbingUrlBuilder.BuildUrlList(Href, HrefInclude, HrefExclude);
foreach (var url in urls) foreach (var url in urls)
{ {
// "url" values come from bound attributes and globbing. Must always be non-null.
Debug.Assert(url != null);
attributes[HrefAttributeName] = url; attributes[HrefAttributeName] = url;
BuildLinkTag(attributes, builder); BuildLinkTag(attributes, builder);
} }
@ -260,10 +278,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
if (fallbackHrefs.Length > 0) if (fallbackHrefs.Length > 0)
{ {
if (ShouldAddFileVersion()) if (FileVersion == true)
{ {
for (var i=0; i < fallbackHrefs.Length; i++) for (var i=0; i < fallbackHrefs.Length; i++)
{ {
// fallbackHrefs come from bound attributes and globbing. Must always be non-null.
Debug.Assert(fallbackHrefs[i] != null);
fallbackHrefs[i] = _fileVersionProvider.AddFileVersionToPath(fallbackHrefs[i]); fallbackHrefs[i] = _fileVersionProvider.AddFileVersionToPath(fallbackHrefs[i]);
} }
} }
@ -279,13 +300,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Build the <script /> tag that checks the effective style of <meta /> tag above and renders the extra // Build the <script /> tag that checks the effective style of <meta /> tag above and renders the extra
// <link /> tag to load the fallback stylesheet if the test CSS property value is found to be false, // <link /> tag to load the fallback stylesheet if the test CSS property value is found to be false,
// indicating that the primary stylesheet failed to load. // indicating that the primary stylesheet failed to load.
builder.Append("<script>") builder
.Append(string.Format(CultureInfo.InvariantCulture, .Append("<script>")
JavaScriptResources.GetEmbeddedJavaScript(FallbackJavaScriptResourceName), .Append(string.Format(
JavaScriptStringEncoder.Default.JavaScriptStringEncode(FallbackTestProperty), CultureInfo.InvariantCulture,
JavaScriptStringEncoder.Default.JavaScriptStringEncode(FallbackTestValue), JavaScriptResources.GetEmbeddedJavaScript(FallbackJavaScriptResourceName),
JavaScriptStringArrayEncoder.Encode(JavaScriptStringEncoder.Default, fallbackHrefs))) JavaScriptEncoder.JavaScriptStringEncode(FallbackTestProperty),
.Append("</script>"); JavaScriptEncoder.JavaScriptStringEncode(FallbackTestValue),
JavaScriptStringArrayEncoder.Encode(JavaScriptEncoder, fallbackHrefs)))
.Append("</script>");
} }
} }
@ -311,7 +334,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
} }
} }
private void BuildLinkTag(IDictionary<string, string> attributes, TagHelperContent builder) private void BuildLinkTag(IDictionary<string, object> attributes, TagHelperContent builder)
{ {
EnsureFileVersionProvider(); EnsureFileVersionProvider();
builder.Append("<link "); builder.Append("<link ");
@ -319,27 +342,27 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
foreach (var attribute in attributes) foreach (var attribute in attributes)
{ {
var attributeValue = attribute.Value; var attributeValue = attribute.Value;
if (string.Equals(attribute.Key, HrefAttributeName, StringComparison.OrdinalIgnoreCase)) if (FileVersion == true &&
string.Equals(attribute.Key, HrefAttributeName, StringComparison.OrdinalIgnoreCase))
{ {
attributeValue = HtmlEncoder.HtmlEncode( // "href" values come from bound attributes and globbing. So anything but a non-null string is
ShouldAddFileVersion() ? // unexpected but could happen if another helper targeting the same element does something odd.
_fileVersionProvider.AddFileVersionToPath(attributeValue) : // Pass through existing value in that case.
attributeValue); var attributeStringValue = attributeValue as string;
if (attributeStringValue != null)
{
attributeValue = _fileVersionProvider.AddFileVersionToPath(attributeStringValue);
}
} }
builder builder
.Append(attribute.Key) .Append(attribute.Key)
.Append("=\"") .Append("=\"")
.Append(attributeValue) .Append(HtmlEncoder, ViewContext.Writer.Encoding, attributeValue)
.Append("\" "); .Append("\" ");
} }
builder.Append("/>"); builder.Append("/>");
} }
private bool ShouldAddFileVersion()
{
return FileVersion ?? false;
}
} }
} }

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers; using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.AspNet.Razor.TagHelpers;
namespace Microsoft.AspNet.Mvc.TagHelpers namespace Microsoft.AspNet.Mvc.TagHelpers
{ {
@ -28,14 +27,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
[Activate] [Activate]
protected internal ViewContext ViewContext { get; set; } protected internal ViewContext ViewContext { get; set; }
/// <summary>
/// Specifies that this &lt;option&gt; is pre-selected.
/// </summary>
/// <remarks>
/// Passed through to the generated HTML in all cases.
/// </remarks>
public string Selected { get; set; }
/// <summary> /// <summary>
/// Specifies a value for the &lt;option&gt; element. /// Specifies a value for the &lt;option&gt; element.
/// </summary> /// </summary>
@ -59,12 +50,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
output.CopyHtmlAttribute(nameof(Value), context); output.CopyHtmlAttribute(nameof(Value), context);
} }
if (Selected != null) // Nothing to do if this <option/> is already selected.
{ if (!output.Attributes.ContainsKey("selected"))
// This <option/> will always be selected.
output.CopyHtmlAttribute(nameof(Selected), context);
}
else
{ {
// Is this <option/> element a child of a <select/> element the SelectTagHelper targeted? // Is this <option/> element a child of a <select/> element the SelectTagHelper targeted?
object formDataEntry; object formDataEntry;

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Hosting;
@ -10,7 +11,6 @@ using Microsoft.AspNet.Mvc.TagHelpers.Internal;
using Microsoft.AspNet.Razor.Runtime.TagHelpers; using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.Caching.Memory; using Microsoft.Framework.Caching.Memory;
using Microsoft.Framework.Logging; using Microsoft.Framework.Logging;
using Microsoft.Framework.Runtime;
using Microsoft.Framework.WebEncoders; using Microsoft.Framework.WebEncoders;
namespace Microsoft.AspNet.Mvc.TagHelpers namespace Microsoft.AspNet.Mvc.TagHelpers
@ -87,6 +87,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Fallback = 2 Fallback = 2
} }
/// <summary>
/// Address of the external script to use.
/// </summary>
/// <remarks>
/// Passed through to the generated HTML in all cases.
/// </remarks>
[HtmlAttributeName(SrcAttributeName)]
public string Src { get; set; }
/// <summary> /// <summary>
/// A comma separated list of globbed file patterns of JavaScript scripts to load. /// A comma separated list of globbed file patterns of JavaScript scripts to load.
/// The glob patterns are assessed relative to the application's 'webroot' setting. /// The glob patterns are assessed relative to the application's 'webroot' setting.
@ -159,12 +168,21 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
[Activate] [Activate]
protected internal IHtmlEncoder HtmlEncoder { get; set; } protected internal IHtmlEncoder HtmlEncoder { get; set; }
[Activate]
protected internal IJavaScriptStringEncoder JavaScriptEncoder { get; set; }
// Internal for ease of use when testing. // Internal for ease of use when testing.
protected internal GlobbingUrlBuilder GlobbingUrlBuilder { get; set; } protected internal GlobbingUrlBuilder GlobbingUrlBuilder { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{ {
// Pass through attribute that is also a well-known HTML attribute.
if (Src != null)
{
output.CopyHtmlAttribute(SrcAttributeName, context);
}
var modeResult = AttributeMatcher.DetermineMode(context, ModeDetails); var modeResult = AttributeMatcher.DetermineMode(context, ModeDetails);
var logger = Logger ?? LoggerFactory.CreateLogger<ScriptTagHelper>(); var logger = Logger ?? LoggerFactory.CreateLogger<ScriptTagHelper>();
@ -180,8 +198,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Get the highest matched mode // Get the highest matched mode
var mode = modeResult.FullMatches.Select(match => match.Mode).Max(); var mode = modeResult.FullMatches.Select(match => match.Mode).Max();
// NOTE: Values in TagHelperOutput.Attributes are already HtmlEncoded // NOTE: Values in TagHelperOutput.Attributes may already be HTML-encoded.
var attributes = new Dictionary<string, string>(output.Attributes); var attributes = new Dictionary<string, object>(output.Attributes);
var builder = new DefaultTagHelperContent(); var builder = new DefaultTagHelperContent();
var originalContent = await context.GetChildContentAsync(); var originalContent = await context.GetChildContentAsync();
@ -209,32 +227,36 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
private void BuildGlobbedScriptTags( private void BuildGlobbedScriptTags(
TagHelperContent originalContent, TagHelperContent originalContent,
IDictionary<string, string> attributes, IDictionary<string, object> attributes,
TagHelperContent builder) TagHelperContent builder)
{ {
// Build a <script> tag for each matched src as well as the original one in the source file
string staticSrc;
attributes.TryGetValue(SrcAttributeName, out staticSrc);
EnsureGlobbingUrlBuilder(); EnsureGlobbingUrlBuilder();
var urls = GlobbingUrlBuilder.BuildUrlList(staticSrc, SrcInclude, SrcExclude);
// Build a <script> tag for each matched src as well as the original one in the source file
var urls = GlobbingUrlBuilder.BuildUrlList(Src, SrcInclude, SrcExclude);
foreach (var url in urls) foreach (var url in urls)
{ {
// "url" values come from bound attributes and globbing. Must always be non-null.
Debug.Assert(url != null);
var content = originalContent;
if (!string.Equals(url, Src, StringComparison.OrdinalIgnoreCase))
{
// Do not copy content into added <script/> elements.
content = null;
}
attributes[SrcAttributeName] = url; attributes[SrcAttributeName] = url;
var content =
string.Equals(url, staticSrc, StringComparison.OrdinalIgnoreCase) ? originalContent : null;
BuildScriptTag(content, attributes, builder); BuildScriptTag(content, attributes, builder);
} }
} }
private void BuildFallbackBlock(IDictionary<string, string> attributes, DefaultTagHelperContent builder) private void BuildFallbackBlock(IDictionary<string, object> attributes, DefaultTagHelperContent builder)
{ {
EnsureGlobbingUrlBuilder(); EnsureGlobbingUrlBuilder();
EnsureFileVersionProvider(); EnsureFileVersionProvider();
var fallbackSrcs = GlobbingUrlBuilder.BuildUrlList(FallbackSrc, FallbackSrcInclude, FallbackSrcExclude); var fallbackSrcs = GlobbingUrlBuilder.BuildUrlList(FallbackSrc, FallbackSrcInclude, FallbackSrcExclude);
if (fallbackSrcs.Any()) if (fallbackSrcs.Any())
{ {
// Build the <script> tag that checks the test method and if it fails, renders the extra script. // Build the <script> tag that checks the test method and if it fails, renders the extra script.
@ -243,36 +265,43 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
.Append(FallbackTestExpression) .Append(FallbackTestExpression)
.Append("||document.write(\""); .Append("||document.write(\"");
// May have no "src" attribute in the dictionary e.g. if Src and SrcInclude were not bound.
if (!attributes.ContainsKey(SrcAttributeName))
{
// Need this entry to place each fallback source.
attributes.Add(SrcAttributeName, null);
}
foreach (var src in fallbackSrcs) foreach (var src in fallbackSrcs)
{ {
builder.Append("<script"); // Fallback "src" values come from bound attributes and globbing. Must always be non-null.
Debug.Assert(src != null);
if (!attributes.ContainsKey(SrcAttributeName)) builder.Append("<script");
{
AppendAttribute(
builder,
SrcAttributeName,
HtmlEncoder.HtmlEncode(ShouldAddFileVersion() ? _fileVersionProvider.AddFileVersionToPath(src) : src),
escapteQuotes: true);
}
foreach (var attribute in attributes) foreach (var attribute in attributes)
{ {
if (!attribute.Key.Equals(SrcAttributeName, StringComparison.OrdinalIgnoreCase)) if (!attribute.Key.Equals(SrcAttributeName, StringComparison.OrdinalIgnoreCase))
{ {
var encodedKey = JavaScriptStringEncoder.Default.JavaScriptStringEncode(attribute.Key); var encodedKey = JavaScriptEncoder.JavaScriptStringEncode(attribute.Key);
var encodedValue = JavaScriptStringEncoder.Default.JavaScriptStringEncode(attribute.Value); var attributeValue = attribute.Value.ToString();
var encodedValue = JavaScriptEncoder.JavaScriptStringEncode(attributeValue);
AppendAttribute(builder, encodedKey, encodedValue, escapteQuotes: true); AppendAttribute(builder, encodedKey, encodedValue, escapeQuotes: true);
} }
else else
{ {
AppendAttribute( // Ignore attribute.Value; use src instead.
builder, var attributeValue = src;
attribute.Key, if (FileVersion == true)
HtmlEncoder.HtmlEncode( {
ShouldAddFileVersion() ? _fileVersionProvider.AddFileVersionToPath(src) : src), attributeValue = _fileVersionProvider.AddFileVersionToPath(attributeValue);
escapteQuotes: true); }
// attribute.Key ("src") does not need to be JavaScript-encoded.
var encodedValue = JavaScriptEncoder.JavaScriptStringEncode(attributeValue);
AppendAttribute(builder, attribute.Key, encodedValue, escapeQuotes: true);
} }
} }
@ -307,7 +336,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
private void BuildScriptTag( private void BuildScriptTag(
TagHelperContent content, TagHelperContent content,
IDictionary<string, string> attributes, IDictionary<string, object> attributes,
TagHelperContent builder) TagHelperContent builder)
{ {
EnsureFileVersionProvider(); EnsureFileVersionProvider();
@ -315,16 +344,21 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
foreach (var attribute in attributes) foreach (var attribute in attributes)
{ {
string attributeValue = attribute.Value; var attributeValue = attribute.Value;
if (string.Equals(attribute.Key, SrcAttributeName, StringComparison.OrdinalIgnoreCase)) if (FileVersion == true &&
string.Equals(attribute.Key, SrcAttributeName, StringComparison.OrdinalIgnoreCase))
{ {
attributeValue = HtmlEncoder.HtmlEncode( // "src" values come from bound attributes and globbing. So anything but a non-null string is
ShouldAddFileVersion() ? // unexpected but could happen if another helper targeting the same element does something odd.
_fileVersionProvider.AddFileVersionToPath(attribute.Value) : // Pass through existing value in that case.
attributeValue); var attributeStringValue = attributeValue as string;
if (attributeStringValue != null)
{
attributeValue = _fileVersionProvider.AddFileVersionToPath(attributeStringValue);
}
} }
AppendAttribute(builder, attribute.Key, attributeValue, escapteQuotes: false); AppendAttribute(builder, attribute.Key, attributeValue, escapeQuotes: false);
} }
builder.Append(">") builder.Append(">")
@ -332,20 +366,27 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
.Append("</script>"); .Append("</script>");
} }
private bool ShouldAddFileVersion() private void AppendAttribute(TagHelperContent content, string key, object value, bool escapeQuotes)
{
return FileVersion ?? false;
}
private void AppendAttribute(TagHelperContent content, string key, string value, bool escapteQuotes)
{ {
content content
.Append(" ") .Append(" ")
.Append(key) .Append(key);
.Append(escapteQuotes ? "=\\\"" : "=\"") if (escapeQuotes)
.Append(value) {
.Append(escapteQuotes ? "\\\"" : "\""); // Passed only JavaScript-encoded strings in this case. Do not perform HTML-encoding as well.
content
.Append("=\\\"")
.Append((string)value)
.Append("\\\"");
}
else
{
// HTML-encoded the given value if necessary.
content
.Append("=\"")
.Append(HtmlEncoder, ViewContext.Writer.Encoding, value)
.Append("\"");
}
} }
} }
} }

View File

@ -0,0 +1,44 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Text;
using Microsoft.AspNet.Mvc.Razor;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.Internal;
using Microsoft.Framework.WebEncoders;
namespace Microsoft.AspNet.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="IHtmlEncoder"/> to use when encoding <paramref name="value"/>.</param>
/// <param name="encoding">The character encoding in which the <paramref name="value"/> is written.</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="Rendering.HtmlString"/> are written without encoding and
/// <see cref="HelperResult.WriteTo"/> is invoked for <see cref="HelperResult"/> types. For all other types,
/// the encoded result of <see cref="object.ToString"/> is written to the <paramref name="content"/>.
/// </remarks>
public static TagHelperContent Append(
[NotNull] this TagHelperContent content,
[NotNull] IHtmlEncoder encoder,
[NotNull] Encoding encoding,
object value)
{
using (var writer = new TagHelperContentWrapperTextWriter(encoding, content))
{
RazorPage.WriteTo(writer, encoder, value, escapeQuotes: true);
}
return content;
}
}
}

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers; using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.TagHelpers namespace Microsoft.AspNet.Mvc.TagHelpers
{ {
@ -24,18 +25,19 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
/// <param name="context">The <see cref="TagHelperContext"/>.</param> /// <param name="context">The <see cref="TagHelperContext"/>.</param>
/// <remarks>Only copies the attribute if <paramref name="tagHelperOutput"/>'s /// <remarks>Only copies the attribute if <paramref name="tagHelperOutput"/>'s
/// <see cref="TagHelperOutput.Attributes"/> does not contain an attribute with the given /// <see cref="TagHelperOutput.Attributes"/> does not contain an attribute with the given
/// <paramref name="attributeName"/></remarks> /// <paramref name="attributeName"/>.</remarks>
public static void CopyHtmlAttribute(this TagHelperOutput tagHelperOutput, public static void CopyHtmlAttribute(
string attributeName, [NotNull] this TagHelperOutput tagHelperOutput,
TagHelperContext context) [NotNull] string attributeName,
[NotNull] TagHelperContext context)
{ {
// We look for the original attribute so we can restore the exact attribute name the user typed. if (!tagHelperOutput.Attributes.ContainsKey(attributeName))
var entry = context.AllAttributes.First(attribute =>
attribute.Key.Equals(attributeName, StringComparison.OrdinalIgnoreCase));
if (!tagHelperOutput.Attributes.ContainsKey(entry.Key))
{ {
tagHelperOutput.Attributes.Add(entry.Key, entry.Value.ToString()); // We look for the original attribute so we can restore the exact attribute name the user typed.
// Approach also ignores changes made to tagHelperOutput[attributeName].
var entry = context.AllAttributes.First(
attribute => attribute.Key.Equals(attributeName, StringComparison.OrdinalIgnoreCase));
tagHelperOutput.Attributes.Add(entry.Key, entry.Value);
} }
} }
@ -47,8 +49,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
/// <param name="prefix">A prefix to look for.</param> /// <param name="prefix">A prefix to look for.</param>
/// <returns><see cref="KeyValuePair{string, string}"/>s with <see cref="KeyValuePair{string, string}.Key"/> /// <returns><see cref="KeyValuePair{string, string}"/>s with <see cref="KeyValuePair{string, string}.Key"/>
/// starting with the given <paramref name="prefix"/>.</returns> /// starting with the given <paramref name="prefix"/>.</returns>
public static IEnumerable<KeyValuePair<string, string>> FindPrefixedAttributes( public static IEnumerable<KeyValuePair<string, object>> FindPrefixedAttributes(
this TagHelperOutput tagHelperOutput, string prefix) [NotNull] this TagHelperOutput tagHelperOutput,
[NotNull] string prefix)
{ {
// TODO: https://github.com/aspnet/Razor/issues/89 - We will not need this method once #89 is completed. // TODO: https://github.com/aspnet/Razor/issues/89 - We will not need this method once #89 is completed.
@ -68,7 +71,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
/// <param name="tagBuilder">The <see cref="TagBuilder"/> to merge attributes from.</param> /// <param name="tagBuilder">The <see cref="TagBuilder"/> to merge attributes from.</param>
/// <remarks>Existing <see cref="TagHelperOutput.Attributes"/> on the given <paramref name="tagHelperOutput"/> /// <remarks>Existing <see cref="TagHelperOutput.Attributes"/> on the given <paramref name="tagHelperOutput"/>
/// are not overridden; "class" attributes are merged with spaces.</remarks> /// are not overridden; "class" attributes are merged with spaces.</remarks>
public static void MergeAttributes(this TagHelperOutput tagHelperOutput, TagBuilder tagBuilder) public static void MergeAttributes(
[NotNull] this TagHelperOutput tagHelperOutput,
[NotNull] TagBuilder tagBuilder)
{ {
foreach (var attribute in tagBuilder.Attributes) foreach (var attribute in tagBuilder.Attributes)
{ {
@ -90,7 +95,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/> this method extends.</param> /// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/> this method extends.</param>
/// <param name="attributes">Attributes to remove.</param> /// <param name="attributes">Attributes to remove.</param>
public static void RemoveRange( public static void RemoveRange(
this TagHelperOutput tagHelperOutput, IEnumerable<KeyValuePair<string, string>> attributes) [NotNull] this TagHelperOutput tagHelperOutput,
[NotNull] IEnumerable<KeyValuePair<string, object>> attributes)
{ {
foreach (var attribute in attributes) foreach (var attribute in attributes)
{ {

View File

@ -16,7 +16,8 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
{ {
return RetrieveAntiForgeryTokens( return RetrieveAntiForgeryTokens(
htmlContent, htmlContent,
attribute => attribute.Value.EndsWith(actionUrl, StringComparison.OrdinalIgnoreCase)) attribute => attribute.Value.EndsWith(actionUrl, StringComparison.OrdinalIgnoreCase) ||
attribute.Value.EndsWith($"HtmlEncode[[{ actionUrl }]]", StringComparison.OrdinalIgnoreCase))
.FirstOrDefault(); .FirstOrDefault();
} }
@ -40,8 +41,10 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
{ {
if (input.Attribute("name") != null && if (input.Attribute("name") != null &&
input.Attribute("type") != null && input.Attribute("type") != null &&
input.Attribute("name").Value == "__RequestVerificationToken" && (input.Attribute("name").Value == "__RequestVerificationToken" &&
input.Attribute("type").Value == "hidden") input.Attribute("type").Value == "hidden" ||
input.Attribute("name").Value == "HtmlEncode[[__RequestVerificationToken]]" &&
input.Attribute("type").Value == "HtmlEncode[[hidden]]"))
{ {
yield return input.Attributes("value").First().Value; yield return input.Attributes("value").First().Value;
} }

View File

@ -4,10 +4,10 @@
<html> <html>
<body> <body>
<div> <div>
<a href="">Product Index</a> <a title="&lt;the title>" href="">Product Index</a>
</div> </div>
<div> <div>
<a href="">Product List</a> <a title="&quot;the&quot; title" href="">Product List</a>
</div> </div>
<div> <div>
<a id="MvcTagHelperTestIndex" href="/">MvcTagHelperTest Index</a> <a id="MvcTagHelperTestIndex" href="/">MvcTagHelperTest Index</a>

View File

@ -7,19 +7,19 @@
<title>Link</title> <title>Link</title>
<!-- Plain link tag --> <!-- Plain link tag -->
<link href="/site.css" rel="stylesheet" /> <link href="/site.css" rel="stylesheet" title="&lt;the title>" />
<!-- Globbed link tag with existing file --> <!-- Globbed link tag with existing file -->
<link rel="stylesheet" href="/site.css" /> <link rel="stylesheet" title="&lt;the title>" href="/site.css" />
<!-- Globbed link tag with existing file and exclude --> <!-- Globbed link tag with existing file and exclude -->
<link rel="stylesheet" href="/site.css" /><link rel="stylesheet" href="/sub/site2.css" /> <link rel="stylesheet" title="&quot;the&quot; title" href="/site.css" /><link rel="stylesheet" title="&quot;the&quot; title" href="/sub/site2.css" />
<!-- Globbed link tag missing include --> <!-- Globbed link tag missing include -->
<link rel="stylesheet" /> <link rel="stylesheet" />
<!-- Globbed link tag missing include but with static href --> <!-- Globbed link tag missing include but with static href -->
<link href="/site.css" rel="stylesheet" /> <link rel="stylesheet" href="/site.css" />
<!-- Globbed link tag with missing file --> <!-- Globbed link tag with missing file -->
@ -31,14 +31,14 @@
<!-- Globbed link tag with existing file and static href --> <!-- Globbed link tag with existing file and static href -->
<link href="/site.css" rel="stylesheet" /><link href="/sub/site2.css" rel="stylesheet" /> <link rel="stylesheet" href="/site.css" /><link rel="stylesheet" href="/sub/site2.css" />
<!-- Globbed link tag with existing file and static href should dedupe --> <!-- Globbed link tag with existing file and static href should dedupe -->
<link href="/site.css" rel="stylesheet" /> <link rel="stylesheet" href="/site.css" />
<!-- Fallback to static href --> <!-- Fallback to static href -->
<link href="/site.min.css" rel="stylesheet" data-extra="test" /> <link rel="stylesheet" data-extra="test" title="&quot;the&quot; title" href="/site.min.css?a=b&amp;c=d" />
<meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script> <meta name="x-stylesheet-fallback-test" 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",["\/site.css?a=b\u0026c=d"]);</script>
<!-- Fallback from globbed href to static href --> <!-- Fallback from globbed href to static href -->
@ -49,11 +49,11 @@
<meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script> <meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script>
<!-- Fallback from globbed and static href to static href --> <!-- Fallback from globbed and static href to static href -->
<link href="site.min.css" rel="stylesheet" data-extra="test" /><link href="/site.css" rel="stylesheet" data-extra="test" /> <link rel="stylesheet" data-extra="test" href="site.min.css" /><link rel="stylesheet" data-extra="test" href="/site.css" />
<meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script> <meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script>
<!-- Fallback from globbed and static href with exclude to static href --> <!-- Fallback from globbed and static href with exclude to static href -->
<link href="site.min.css" rel="stylesheet" data-extra="test" /> <link rel="stylesheet" data-extra="test" href="site.min.css" />
<meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script> <meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script>
<!-- Fallback to static href with no primary href --> <!-- Fallback to static href with no primary href -->
@ -61,19 +61,19 @@
<meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script> <meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script>
<!-- Fallback to globbed href --> <!-- Fallback to globbed href -->
<link href="/site.min.css" rel="stylesheet" data-extra="test" /> <link rel="stylesheet" data-extra="test" href="/site.min.css" />
<meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script> <meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script>
<!-- Fallback to static and globbed href --> <!-- Fallback to static and globbed href -->
<link href="/site.min.css" rel="stylesheet" data-extra="test" /> <link rel="stylesheet" data-extra="test" href="/site.min.css" />
<meta name="x-stylesheet-fallback-test" 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",["\/site.css","\/sub\/site2.css"]);</script> <meta name="x-stylesheet-fallback-test" 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",["\/site.css","\/sub\/site2.css"]);</script>
<!-- Fallback to static and globbed href should dedupe --> <!-- Fallback to static and globbed href should dedupe -->
<link href="/site.min.css" rel="stylesheet" data-extra="test" /> <link rel="stylesheet" data-extra="test" href="/site.min.css" />
<meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script> <meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script>
<!-- Fallback to static and globbed href with exclude --> <!-- Fallback to static and globbed href with exclude -->
<link href="/site.min.css" rel="stylesheet" data-extra="test" /> <link rel="stylesheet" data-extra="test" href="/site.min.css" />
<meta name="x-stylesheet-fallback-test" 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",["\/site.css","\/sub\/site2.css"]);</script> <meta name="x-stylesheet-fallback-test" 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",["\/site.css","\/sub\/site2.css"]);</script>
<!-- Fallback from globbed href to glbobed href --> <!-- Fallback from globbed href to glbobed href -->
@ -85,44 +85,44 @@
<meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script> <meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script>
<!-- Fallback from globbed and static href to globbed href --> <!-- Fallback from globbed and static href to globbed href -->
<link href="site.min.css" rel="stylesheet" data-extra="test" /><link href="/site.css" rel="stylesheet" data-extra="test" /> <link rel="stylesheet" data-extra="test" href="site.min.css" /><link rel="stylesheet" data-extra="test" href="/site.css" />
<meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script> <meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script>
<!-- Fallback from globbed and static href with exclude to globbed href --> <!-- Fallback from globbed and static href with exclude to globbed href -->
<link href="site.min.css" rel="stylesheet" data-extra="test" /> <link rel="stylesheet" data-extra="test" href="site.min.css" />
<meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script> <meta name="x-stylesheet-fallback-test" 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",["\/site.css"]);</script>
<!-- Kitchen sink, all the attributes --> <!-- Kitchen sink, all the attributes -->
<link href="site.min.css" rel="stylesheet" data-extra="test" /> <link rel="stylesheet" data-extra="test" href="site.min.css" />
<meta name="x-stylesheet-fallback-test" 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",["\/site.css","\/sub\/site2.css"]);</script> <meta name="x-stylesheet-fallback-test" 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",["\/site.css","\/sub\/site2.css"]);</script>
<!-- Fallback to globbed href that doesn't exist --> <!-- Fallback to globbed href that doesn't exist -->
<link href="/site.min.css" rel="stylesheet" data-extra="test" /> <link rel="stylesheet" data-extra="test" href="/site.min.css" />
<!-- Fallback to globbed href outside webroot --> <!-- Fallback to globbed href outside webroot -->
<link href="/site.min.css" rel="stylesheet" data-extra="test" /> <link rel="stylesheet" data-extra="test" href="/site.min.css" />
<!-- Fallback with missing attribute --> <!-- Fallback with missing attribute -->
<link href="/site.min.css" rel="stylesheet" data-extra="test" /> <link rel="stylesheet" data-extra="test" href="/site.min.css" />
<!-- Fallback with missing attribute --> <!-- Fallback with missing attribute -->
<link href="/site.min.css" rel="stylesheet" data-extra="test" /> <link rel="stylesheet" data-extra="test" href="/site.min.css" />
<!-- Fallback with missing attribute --> <!-- Fallback with missing attribute -->
<link href="/site.min.css" rel="stylesheet" data-extra="test" /> <link rel="stylesheet" data-extra="test" href="/site.min.css" />
<!-- Plain link tag with file version --> <!-- Plain link tag with file version -->
<link href="/site.css?v=XY7YsMemPf8AGU4SIX9ED9eOjK1LOQWu2dmCNmh-pQc" rel="stylesheet" /> <link rel="stylesheet" href="/site.css?v=XY7YsMemPf8AGU4SIX9ED9eOjK1LOQWu2dmCNmh-pQc" />
<!-- Globbed link tag with existing file and file version --> <!-- Globbed link tag with existing file and file version -->
<link rel="stylesheet" href="/site.css?v=XY7YsMemPf8AGU4SIX9ED9eOjK1LOQWu2dmCNmh-pQc" /> <link rel="stylesheet" href="/site.css?v=XY7YsMemPf8AGU4SIX9ED9eOjK1LOQWu2dmCNmh-pQc" />
<!-- Fallback with file version --> <!-- Fallback with file version -->
<link href="/site.min.css" rel="stylesheet" data-extra="test" /> <link rel="stylesheet" data-extra="test" href="/site.min.css" />
<meta name="x-stylesheet-fallback-test" 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",["\/site.css?v=XY7YsMemPf8AGU4SIX9ED9eOjK1LOQWu2dmCNmh-pQc"]);</script> <meta name="x-stylesheet-fallback-test" 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",["\/site.css?v=XY7YsMemPf8AGU4SIX9ED9eOjK1LOQWu2dmCNmh-pQc"]);</script>
<!-- Globbed link tag with existing file, static href and file version --> <!-- Globbed link tag with existing file, static href and file version -->
<link href="/site.css?v=XY7YsMemPf8AGU4SIX9ED9eOjK1LOQWu2dmCNmh-pQc" rel="stylesheet" /><link href="/sub/site2.css?v=30cxPex0tA9xEatW7f1Qhnn8tVLAHgE6xwIZhESq0y0" rel="stylesheet" /><link href="/sub/site3.css?v=fSxxOr1Q4Dq2uPuzlju5UYGuK0SKABI-ghvaIGEsZDc" rel="stylesheet" /><link href="/sub/site3.min.css?v=s8JMmAZxBn0dzuhRtQ0wgOvNBK4XRJRWEC2wfzsVF9M" rel="stylesheet" /> <link rel="stylesheet" href="/site.css?v=XY7YsMemPf8AGU4SIX9ED9eOjK1LOQWu2dmCNmh-pQc" /><link rel="stylesheet" href="/sub/site2.css?v=30cxPex0tA9xEatW7f1Qhnn8tVLAHgE6xwIZhESq0y0" /><link rel="stylesheet" href="/sub/site3.css?v=fSxxOr1Q4Dq2uPuzlju5UYGuK0SKABI-ghvaIGEsZDc" /><link rel="stylesheet" href="/sub/site3.min.css?v=s8JMmAZxBn0dzuhRtQ0wgOvNBK4XRJRWEC2wfzsVF9M" />
</head> </head>
<body> <body>

View File

@ -7,34 +7,34 @@
</head> </head>
<body> <body>
<h2>Script tag helper test</h2> <h2>Script tag helper test</h2>
<script src="/site.js" data-foo="foo-data1"> <script src="/site.js" data-foo="foo-data1" title="&lt;the title>">
// Regular script with comment in body, and extra properties. // Regular script with comment in body, and extra properties.
</script> </script>
<script src="/blank.js" data-foo="foo-data2"> <script data-foo="foo-data2" title="&lt;the title>" src="/blank.js?a=b&amp;c=d">
// TagHelper script with comment in body, and extra properties. // TagHelper script with comment in body, and extra properties.
</script> </script>
<script>(false||document.write("<script src=\"/site.js\" data-foo=\"foo-data2\"><\/script>"));</script> <script>(false||document.write("<script data-foo=\"foo-data2\" title=\"\u0026lt;the title\u003E\" src=\"\/site.js?a=b\u0026c=d\"><\/script>"));</script>
<script src="/blank.js"> <script title="&quot;the&quot; title" src="/blank.js">
// Fallback to globbed src // Fallback to globbed src
</script> </script>
<script>(false||document.write("<script src=\"/site.js\"><\/script>"));</script> <script>(false||document.write("<script title=\"\u0022the\u0022 title\" src=\"\/site.js\"><\/script>"));</script>
<script src="/blank.js"> <script src="/blank.js">
// Fallback to globbed src with exclude // Fallback to globbed src with exclude
</script> </script>
<script>(false||document.write("<script src=\"/site.js\"><\/script><script src=\"/sub/site2.js\"><\/script>"));</script> <script>(false||document.write("<script src=\"\/site.js\"><\/script><script src=\"\/sub\/site2.js\"><\/script>"));</script>
<script src="/blank.js"> <script src="/blank.js">
// Fallback to globbed and static src // Fallback to globbed and static src
</script> </script>
<script>(false||document.write("<script src=\"/site.js\"><\/script><script src=\"/sub/site2.js\"><\/script>"));</script> <script>(false||document.write("<script src=\"\/site.js\"><\/script><script src=\"\/sub\/site2.js\"><\/script>"));</script>
<script src="/blank.js"> <script src="/blank.js">
// Fallback to globbed and static src should de-dupe // Fallback to globbed and static src should de-dupe
</script> </script>
<script>(false||document.write("<script src=\"/site.js\"><\/script>"));</script> <script>(false||document.write("<script src=\"\/site.js\"><\/script>"));</script>
<script src="/blank.js"> <script src="/blank.js">
// Fallback to globbed src with missing include // Fallback to globbed src with missing include
@ -43,7 +43,7 @@
<script src="/blank.js"> <script src="/blank.js">
// Fallback to static and globbed src with missing include // Fallback to static and globbed src with missing include
</script> </script>
<script>(false||document.write("<script src=\"/site.js\"><\/script>"));</script> <script>(false||document.write("<script src=\"\/site.js\"><\/script>"));</script>
<script src="/blank.js"> <script src="/blank.js">
// Fallback to globbed src outside of webroot // Fallback to globbed src outside of webroot
@ -56,7 +56,7 @@
<script data-foo="foo-data3"> <script data-foo="foo-data3">
// Valid TagHelper (although no src is provided) script with comment in body, and extra properties. // Valid TagHelper (although no src is provided) script with comment in body, and extra properties.
</script> </script>
<script>(false||document.write("<script src=\"/site.js\" data-foo=\"foo-data3\"><\/script>"));</script> <script>(false||document.write("<script data-foo=\"foo-data3\" src=\"\/site.js\"><\/script>"));</script>
<script src="/blank.js"> <script src="/blank.js">
// Invalid TagHelper script with comment in body. // Invalid TagHelper script with comment in body.
@ -96,12 +96,12 @@
<script src="/blank.js"> <script src="/blank.js">
// TagHelper script with comment in body, and file version. // TagHelper script with comment in body, and file version.
</script> </script>
<script>(false||document.write("<script src=\"/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I\"><\/script>"));</script> <script>(false||document.write("<script src=\"\/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I\"><\/script>"));</script>
<script src="/blank.js"> <script src="/blank.js">
// Fallback to globbed src with file version. // Fallback to globbed src with file version.
</script> </script>
<script>(false||document.write("<script src=\"/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I\"><\/script>"));</script> <script>(false||document.write("<script src=\"\/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I\"><\/script>"));</script>
<script src="/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I"> <script src="/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I">
// Regular script with comment in body, and file version. // Regular script with comment in body, and file version.

View File

@ -12,6 +12,7 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Builder; using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Mvc.TagHelpers; using Microsoft.AspNet.Mvc.TagHelpers;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.WebEncoders;
using MvcTagHelpersWebSite; using MvcTagHelpersWebSite;
using Xunit; using Xunit;
@ -76,6 +77,50 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.Equal(expectedContent.Trim(), responseContent.Trim()); Assert.Equal(expectedContent.Trim(), responseContent.Trim());
} }
[Theory]
[InlineData("EditWarehouse", null)]
[InlineData("Index", null)]
[InlineData("Link", null)]
[InlineData("Order", "/MvcTagHelper_Order/Submit")]
[InlineData("OrderUsingHtmlHelpers", "/MvcTagHelper_Order/Submit")]
[InlineData("Product", null)]
[InlineData("Script", null)]
public async Task MvcTagHelpers_GenerateEncodedResults(string action, string antiForgeryPath)
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName, services =>
{
_configureServices(services);
services.AddTransient<IHtmlEncoder, TestHtmlEncoder>();
services.AddTransient<IJavaScriptStringEncoder, TestJavaScriptEncoder>();
services.AddTransient<IUrlEncoder, TestUrlEncoder>();
});
var client = server.CreateClient();
var expectedMediaType = MediaTypeHeaderValue.Parse("text/html; charset=utf-8");
// The K runtime compiles every file under compiler/resources as a resource at runtime with the same name
// as the file name, in order to update a baseline you just need to change the file in that folder.
var expectedContent = await _resourcesAssembly.ReadResourceAsStringAsync(
"compiler/resources/MvcTagHelpersWebSite.MvcTagHelper_Home." + action + ".Encoded.html");
// Act
// The host is not important as everything runs in memory and tests are isolated from each other.
var response = await client.GetAsync("http://localhost/MvcTagHelper_Home/" + action);
var responseContent = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expectedMediaType, response.Content.Headers.ContentType);
if (antiForgeryPath != null)
{
var forgeryToken = AntiForgeryTestHelper.RetrieveAntiForgeryToken(responseContent, antiForgeryPath);
expectedContent = string.Format(expectedContent, forgeryToken);
}
Assert.Equal(expectedContent.Trim(), responseContent.Trim());
}
[Fact] [Fact]
public async Task ValidationTagHelpers_GeneratesExpectedSpansAndDivs() public async Task ValidationTagHelpers_GeneratesExpectedSpansAndDivs()
{ {

View File

@ -0,0 +1,47 @@

<html>
<body>
<form action="HtmlEncode[[/MvcTagHelper_Home/EditWarehouse]]" method="HtmlEncode[[post]]">
<div>
HtmlEncode[[City_1]]
<input type="HtmlEncode[[text]]" data-val="HtmlEncode[[true]]" data-val-minlength="HtmlEncode[[The field City must be a string or array type with a minimum length of '2'.]]" data-val-minlength-min="HtmlEncode[[2]]" id="HtmlEncode[[City]]" name="HtmlEncode[[City]]" value="HtmlEncode[[City_1]]" />
</div>
<div>
<label>HtmlEncode[[Address]]</label>
<textarea id="HtmlEncode[[Employee_Address]]" name="HtmlEncode[[Employee.Address]]">
HtmlEncode[[Address_1]]</textarea>;
</div>
<div>
<label for="HtmlEncode[[Employee_Password]]">HtmlEncode[[Password]]</label>
<input data-val="HtmlEncode[[true]]" data-val-required="HtmlEncode[[The Password field is required.]]" id="HtmlEncode[[Employee_Password]]" name="HtmlEncode[[Employee.Password]]" type="HtmlEncode[[password]]" />
</div>
<div>
<label>HtmlEncode[[PhoneNumber]]</label>
<input id="HtmlEncode[[Employee_PhoneNumber]]" name="HtmlEncode[[Employee.PhoneNumber]]" type="HtmlEncode[[text]]" value="HtmlEncode[[PhoneNumber_1]]" />
</div>
<div>
<label for="HtmlEncode[[Employee_Name]]">HtmlEncode[[Employee.Name]]</label>
<input type="HtmlEncode[[text]]" id="HtmlEncode[[Employee_Name]]" name="HtmlEncode[[Employee.Name]]" value="HtmlEncode[[EmployeeName_1]]" />
<span class="HtmlEncode[[field-validation-valid]]" data-valmsg-for="HtmlEncode[[Employee.Name]]" data-valmsg-replace="HtmlEncode[[true]]"></span>
</div>
<div>
<label for="HtmlEncode[[Employee_Gender]]">HtmlEncode[[Gender]]</label>
<input checked="HtmlEncode[[checked]]" id="HtmlEncode[[Employee_Gender]]" name="HtmlEncode[[Employee.Gender]]" type="HtmlEncode[[radio]]" value="HtmlEncode[[Female]]" /> Female
<input id="HtmlEncode[[Employee_Gender]]" name="HtmlEncode[[Employee.Gender]]" type="HtmlEncode[[radio]]" value="HtmlEncode[[Male]]" /> Male
</div>
<div>
<label for="HtmlEncode[[Employee_OfficeNumber]]">HtmlEncode[[OfficeNumber]]</label>
<select id="HtmlEncode[[Employee_OfficeNumber]]" name="HtmlEncode[[Employee.OfficeNumber]]"><option>HtmlEncode[[1001]]</option>
<option>HtmlEncode[[1002]]</option>
</select>
</div>
<input data-val="HtmlEncode[[true]]" data-val-range="HtmlEncode[[The field Number must be between 1 and 100.]]" data-val-range-max="HtmlEncode[[100]]" data-val-range-min="HtmlEncode[[1]]" id="HtmlEncode[[Employee_Number]]" name="HtmlEncode[[Employee.Number]]" type="HtmlEncode[[hidden]]" value="HtmlEncode[[1]]" />
<div class="HtmlEncode[[validation-summary-valid]]" data-valmsg-summary="HtmlEncode[[true]]"><ul><li style="display:none"></li>
</ul></div>
<input type="submit" />
</form>
</body>
</html>

View File

@ -0,0 +1,76 @@

<html>
<body>
<div>
<a title="&lt;the title>" href="">Product Index</a>
</div>
<div>
<a title="&quot;the&quot; title" href="">Product List</a>
</div>
<div>
<a id="MvcTagHelperTestIndex" href="HtmlEncode[[/]]">MvcTagHelperTest Index</a>
</div>
<div>
<a href="HtmlEncode[[/]]">Default Controller</a>
</div>
<div>
<a href="">Product Index Fragment</a>
</div>
<div>
<a href="">Produt Submit Fragment</a>
</div>
<div>
<a id="MvcTagHelperTestIndexFragment" href="HtmlEncode[[/#fragment]]">
MvcTagHelperTest Index Fragment
</a>
</div>
<div>
<a href="HtmlEncode[[ftp://localhost/#fragment]]">
FTP MvcTagHelperTest Index Fragment
</a>
</div>
<div>
<a href="">
Unknown Protocol Product List
</a>
</div>
<div>
<a id="MvcTagHelperTestIndexProtocol" href="HtmlEncode[[/]]">
Empty Protocol MvcTagHelperTest Index
</a>
</div>
<div>
<a href="">Customer Area Customer Index</a>
</div>
<div>
<a href="/Order/List">Href Order List</a>
</div>
<div>
<a href="">Non-existent Controller</a>
</div>
<div>
<a href="">
Non-existent Controller Fragment
</a>
</div>
<div>
<a href="">Non-existent Action</a>
</div>
<div>
<a id="Id" href="HtmlEncode[[http://somewhere/]]">Some Where</a>
</div>
<div>
<a href="">
Unknown Protocol Non-existent Controller Fragment
</a>
</div>
<div>
<a href="">Product Route Non-existent Area Parameter</a>
</div>
<div>
<a href="">Non-existent Area</a>
</div>
</body>
</html>

View File

@ -0,0 +1,131 @@

<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Link</title>
<!-- Plain link tag -->
<link href="HtmlEncode[[/site.css]]" rel="stylesheet" title="&lt;the title>" />
<!-- Globbed link tag with existing file -->
<link rel="stylesheet" title="&lt;the title>" href="HtmlEncode[[/site.css]]" />
<!-- Globbed link tag with existing file and exclude -->
<link rel="stylesheet" title="&quot;the&quot; title" href="HtmlEncode[[/site.css]]" /><link rel="stylesheet" title="&quot;the&quot; title" href="HtmlEncode[[/sub/site2.css]]" />
<!-- Globbed link tag missing include -->
<link rel="stylesheet" />
<!-- Globbed link tag missing include but with static href -->
<link rel="stylesheet" href="HtmlEncode[[/site.css]]" />
<!-- Globbed link tag with missing file -->
<!-- Globbed link tag with file outside of webroot -->
<!-- Globbed link tag with file outside of webroot -->
<!-- Globbed link tag with existing file and static href -->
<link rel="stylesheet" href="HtmlEncode[[/site.css]]" /><link rel="stylesheet" href="HtmlEncode[[/sub/site2.css]]" />
<!-- Globbed link tag with existing file and static href should dedupe -->
<link rel="stylesheet" href="HtmlEncode[[/site.css]]" />
<!-- Fallback to static href -->
<link rel="stylesheet" data-extra="test" title="&quot;the&quot; title" href="HtmlEncode[[/site.min.css?a=b&c=d]]" />
<meta name="x-stylesheet-fallback-test" 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[[/site.css?a=b&c=d]]"]);</script>
<!-- Fallback from globbed href to static href -->
<meta name="x-stylesheet-fallback-test" 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[[/site.css]]"]);</script>
<!-- Fallback from globbed href with exclude to static href -->
<meta name="x-stylesheet-fallback-test" 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[[/site.css]]"]);</script>
<!-- Fallback from globbed and static href to static href -->
<link rel="stylesheet" data-extra="test" href="HtmlEncode[[site.min.css]]" /><link rel="stylesheet" data-extra="test" href="HtmlEncode[[/site.css]]" />
<meta name="x-stylesheet-fallback-test" 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[[/site.css]]"]);</script>
<!-- Fallback from globbed and static href with exclude to static href -->
<link rel="stylesheet" data-extra="test" href="HtmlEncode[[site.min.css]]" />
<meta name="x-stylesheet-fallback-test" 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[[/site.css]]"]);</script>
<!-- Fallback to static href with no primary href -->
<link rel="stylesheet" data-extra="test" />
<meta name="x-stylesheet-fallback-test" 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[[/site.css]]"]);</script>
<!-- Fallback to globbed href -->
<link rel="stylesheet" data-extra="test" href="HtmlEncode[[/site.min.css]]" />
<meta name="x-stylesheet-fallback-test" 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[[/site.css]]"]);</script>
<!-- Fallback to static and globbed href -->
<link rel="stylesheet" data-extra="test" href="HtmlEncode[[/site.min.css]]" />
<meta name="x-stylesheet-fallback-test" 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[[/site.css]]","JavaScriptEncode[[/sub/site2.css]]"]);</script>
<!-- Fallback to static and globbed href should dedupe -->
<link rel="stylesheet" data-extra="test" href="HtmlEncode[[/site.min.css]]" />
<meta name="x-stylesheet-fallback-test" 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[[/site.css]]"]);</script>
<!-- Fallback to static and globbed href with exclude -->
<link rel="stylesheet" data-extra="test" href="HtmlEncode[[/site.min.css]]" />
<meta name="x-stylesheet-fallback-test" 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[[/site.css]]","JavaScriptEncode[[/sub/site2.css]]"]);</script>
<!-- Fallback from globbed href to glbobed href -->
<meta name="x-stylesheet-fallback-test" 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[[/site.css]]"]);</script>
<!-- Fallback from globbed href with exclude to globbed href -->
<meta name="x-stylesheet-fallback-test" 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[[/site.css]]"]);</script>
<!-- Fallback from globbed and static href to globbed href -->
<link rel="stylesheet" data-extra="test" href="HtmlEncode[[site.min.css]]" /><link rel="stylesheet" data-extra="test" href="HtmlEncode[[/site.css]]" />
<meta name="x-stylesheet-fallback-test" 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[[/site.css]]"]);</script>
<!-- Fallback from globbed and static href with exclude to globbed href -->
<link rel="stylesheet" data-extra="test" href="HtmlEncode[[site.min.css]]" />
<meta name="x-stylesheet-fallback-test" 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[[/site.css]]"]);</script>
<!-- Kitchen sink, all the attributes -->
<link rel="stylesheet" data-extra="test" href="HtmlEncode[[site.min.css]]" />
<meta name="x-stylesheet-fallback-test" 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[[/site.css]]","JavaScriptEncode[[/sub/site2.css]]"]);</script>
<!-- Fallback to globbed href that doesn't exist -->
<link rel="stylesheet" data-extra="test" href="HtmlEncode[[/site.min.css]]" />
<!-- Fallback to globbed href outside webroot -->
<link rel="stylesheet" data-extra="test" href="HtmlEncode[[/site.min.css]]" />
<!-- Fallback with missing attribute -->
<link rel="stylesheet" data-extra="test" href="HtmlEncode[[/site.min.css]]" />
<!-- Fallback with missing attribute -->
<link rel="stylesheet" data-extra="test" href="HtmlEncode[[/site.min.css]]" />
<!-- Fallback with missing attribute -->
<link rel="stylesheet" data-extra="test" href="HtmlEncode[[/site.min.css]]" />
<!-- Plain link tag with file version -->
<link rel="stylesheet" href="HtmlEncode[[/site.css?v=XY7YsMemPf8AGU4SIX9ED9eOjK1LOQWu2dmCNmh-pQc]]" />
<!-- Globbed link tag with existing file and file version -->
<link rel="stylesheet" href="HtmlEncode[[/site.css?v=XY7YsMemPf8AGU4SIX9ED9eOjK1LOQWu2dmCNmh-pQc]]" />
<!-- Fallback with file version -->
<link rel="stylesheet" data-extra="test" href="HtmlEncode[[/site.min.css]]" />
<meta name="x-stylesheet-fallback-test" 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[[/site.css?v=XY7YsMemPf8AGU4SIX9ED9eOjK1LOQWu2dmCNmh-pQc]]"]);</script>
<!-- Globbed link tag with existing file, static href and file version -->
<link rel="stylesheet" href="HtmlEncode[[/site.css?v=XY7YsMemPf8AGU4SIX9ED9eOjK1LOQWu2dmCNmh-pQc]]" /><link rel="stylesheet" href="HtmlEncode[[/sub/site2.css?v=30cxPex0tA9xEatW7f1Qhnn8tVLAHgE6xwIZhESq0y0]]" /><link rel="stylesheet" href="HtmlEncode[[/sub/site3.css?v=fSxxOr1Q4Dq2uPuzlju5UYGuK0SKABI-ghvaIGEsZDc]]" /><link rel="stylesheet" href="HtmlEncode[[/sub/site3.min.css?v=s8JMmAZxBn0dzuhRtQ0wgOvNBK4XRJRWEC2wfzsVF9M]]" />
</head>
<body>
<h2>Link Tag Helper Test</h2>
</body>
</html>

View File

@ -0,0 +1,85 @@

<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<form action="HtmlEncode[[/MvcTagHelper_Order/Submit]]" method="HtmlEncode[[post]]">
<div>
<label class="order" for="HtmlEncode[[Shipping]]">HtmlEncode[[Shipping]]</label>
<input size="50" type="HtmlEncode[[text]]" id="HtmlEncode[[Shipping]]" name="HtmlEncode[[Shipping]]" value="HtmlEncode[[Your shipping method is UPSP]]" />
</div>
<div>
<label class="order" for="HtmlEncode[[ShippingDateTime]]">HtmlEncode[[ShippingDateTime]]</label>
<input type="HtmlEncode[[datetime-local]]" id="HtmlEncode[[ShippingDateTime]]" name="HtmlEncode[[ShippingDateTime]]" value="HtmlEncode[[01/01/0001 00:00:00]]" />
</div>
<div>
<label class="order" for="HtmlEncode[[Products]]">HtmlEncode[[Products]]</label>
<select multiple="HtmlEncode[[multiple]]" id="HtmlEncode[[Products]]" name="HtmlEncode[[Products]]"><option selected="HtmlEncode[[selected]]" value="HtmlEncode[[0]]">HtmlEncode[[Product_0]]</option>
<option selected="HtmlEncode[[selected]]" value="HtmlEncode[[1]]">HtmlEncode[[Product_1]]</option>
<option value="HtmlEncode[[2]]">HtmlEncode[[Product_2]]</option>
</select>
</div>
<div>
<label class="order" for="HtmlEncode[[SubstituteProducts]]">HtmlEncode[[SubstituteProducts]]</label>
<select id="HtmlEncode[[SubstituteProducts]]" multiple="HtmlEncode[[multiple]]" name="HtmlEncode[[SubstituteProducts]]"><option value="HtmlEncode[[0]]">HtmlEncode[[Product_0]]</option>
<option value="HtmlEncode[[1]]">HtmlEncode[[Product_1]]</option>
<option selected="HtmlEncode[[selected]]" value="HtmlEncode[[2]]">HtmlEncode[[Product_2]]</option>
</select>
</div>
<div>
<label class="order" for="HtmlEncode[[OrderDate]]">HtmlEncode[[OrderDate]]</label>
<input type="HtmlEncode[[datetime]]" id="HtmlEncode[[OrderDate]]" name="HtmlEncode[[OrderDate]]" value="HtmlEncode[[0001/01/01/ A.D.]]" />
</div>
<div>
<label class="order" for="HtmlEncode[[NeedSpecialHandle]]">HtmlEncode[[NeedSpecialHandle]]</label>
<input checked="HtmlEncode[[checked]]" id="HtmlEncode[[NeedSpecialHandle]]" name="HtmlEncode[[NeedSpecialHandle]]" type="HtmlEncode[[checkbox]]" value="HtmlEncode[[true]]" /><input name="HtmlEncode[[NeedSpecialHandle]]" type="HtmlEncode[[hidden]]" value="HtmlEncode[[false]]" />
</div>
<div>
<label class="order" for="HtmlEncode[[PaymentMethod]]">HtmlEncode[[PaymentMethod]]</label>
<select id="HtmlEncode[[PaymentMethod]]" multiple="HtmlEncode[[multiple]]" name="HtmlEncode[[PaymentMethod]]">
<option value="HtmlEncode[[Credit]]">Credit</option>
<option value="HtmlEncode[[Check]]" selected="HtmlEncode[[selected]]">Check</option>
</select>
</div>
<div>
<label class="order" for="HtmlEncode[[Customer_Number]]">HtmlEncode[[Number]]</label>
<input class="form-control" type="HtmlEncode[[number]]" data-val="HtmlEncode[[true]]" data-val-range="HtmlEncode[[The field Number must be between 1 and 100.]]" data-val-range-max="HtmlEncode[[100]]" data-val-range-min="HtmlEncode[[1]]" id="HtmlEncode[[Customer_Number]]" name="HtmlEncode[[Customer.Number]]" value="HtmlEncode[[1]]" />
<span class="HtmlEncode[[field-validation-valid]]" data-valmsg-for="HtmlEncode[[Customer.Number]]" data-valmsg-replace="HtmlEncode[[true]]"></span>
</div>
<div>
<label class="order" for="HtmlEncode[[Customer_Name]]">HtmlEncode[[Name]]</label>
<input type="HtmlEncode[[text]]" id="HtmlEncode[[Customer_Name]]" name="HtmlEncode[[Customer.Name]]" value="HtmlEncode[[NameStringValue]]" />
</div>
<div>
<label class="order" for="HtmlEncode[[Customer_Email]]">HtmlEncode[[Email]]</label>
<input type="HtmlEncode[[email]]" id="HtmlEncode[[Customer_Email]]" name="HtmlEncode[[Customer.Email]]" value="" />
<span class="HtmlEncode[[field-validation-valid]]" data-valmsg-for="HtmlEncode[[Customer.Email]]" data-valmsg-replace="HtmlEncode[[true]]"></span>
</div>
<div>
<label class="order" for="HtmlEncode[[Customer_PhoneNumber]]">HtmlEncode[[PhoneNumber]]</label>
<input type="HtmlEncode[[tel]]" id="HtmlEncode[[Customer_PhoneNumber]]" name="HtmlEncode[[Customer.PhoneNumber]]" value="" />
</div>
<div>
<label class="order" for="HtmlEncode[[Customer_Password]]">HtmlEncode[[Password]]</label>
<input class="form-control" type="HtmlEncode[[password]]" data-val="HtmlEncode[[true]]" data-val-required="HtmlEncode[[The Password field is required.]]" id="HtmlEncode[[Customer_Password]]" name="HtmlEncode[[Customer.Password]]" />
<span class="HtmlEncode[[field-validation-valid]]" data-valmsg-for="HtmlEncode[[Customer.Password]]" data-valmsg-replace="HtmlEncode[[true]]"></span>
</div>
<div>
<label class="order" for="HtmlEncode[[Customer_Gender]]">HtmlEncode[[Gender]]</label>
<input type="HtmlEncode[[radio]]" value="HtmlEncode[[Male]]" id="HtmlEncode[[Customer_Gender]]" name="HtmlEncode[[Customer.Gender]]" /> Male
<input type="HtmlEncode[[radio]]" value="HtmlEncode[[Female]]" checked="HtmlEncode[[checked]]" id="HtmlEncode[[Customer_Gender]]" name="HtmlEncode[[Customer.Gender]]" /> Female
<span class="HtmlEncode[[field-validation-valid]]" data-valmsg-for="HtmlEncode[[Customer.Gender]]" data-valmsg-replace="HtmlEncode[[true]]"></span>
</div>
<div class="HtmlEncode[[order validation-summary-valid]]" data-valmsg-summary="HtmlEncode[[true]]"><ul><li style="display:none"></li>
</ul></div>
<input type="HtmlEncode[[hidden]]" id="HtmlEncode[[Customer_Key]]" name="HtmlEncode[[Customer.Key]]" value="HtmlEncode[[KeyA]]" />
<input type="submit" />
<input name="HtmlEncode[[__RequestVerificationToken]]" type="HtmlEncode[[hidden]]" value="{0}" /></form>
</body>
</html>

View File

@ -0,0 +1,84 @@

<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<form action="HtmlEncode[[/MvcTagHelper_Order/Submit]]" method="HtmlEncode[[post]]">
<div>
<label class="HtmlEncode[[order]]" for="HtmlEncode[[Shipping]]">HtmlEncode[[Shipping]]</label>
<input id="HtmlEncode[[Shipping]]" name="HtmlEncode[[Shipping]]" size="HtmlEncode[[50]]" type="HtmlEncode[[text]]" value="HtmlEncode[[Your shipping method is UPSP]]" />
</div>
<div>
<label class="HtmlEncode[[order]]" for="HtmlEncode[[ShippingDateTime]]">HtmlEncode[[ShippingDateTime]]</label>
<input id="HtmlEncode[[ShippingDateTime]]" name="HtmlEncode[[ShippingDateTime]]" type="HtmlEncode[[datetime-local]]" value="HtmlEncode[[01/01/0001 00:00:00]]" />
</div>
<div>
<label class="HtmlEncode[[order]]" for="HtmlEncode[[Products]]">HtmlEncode[[Products]]</label>
<select id="HtmlEncode[[Products]]" multiple="HtmlEncode[[multiple]]" name="HtmlEncode[[Products]]"><option selected="HtmlEncode[[selected]]" value="HtmlEncode[[0]]">HtmlEncode[[Product_0]]</option>
<option selected="HtmlEncode[[selected]]" value="HtmlEncode[[1]]">HtmlEncode[[Product_1]]</option>
<option value="HtmlEncode[[2]]">HtmlEncode[[Product_2]]</option>
</select>
</div>
<div>
<label class="HtmlEncode[[order]]" for="HtmlEncode[[SubstituteProducts]]">HtmlEncode[[SubstituteProducts]]</label>
<select id="HtmlEncode[[SubstituteProducts]]" multiple="HtmlEncode[[multiple]]" name="HtmlEncode[[SubstituteProducts]]"><option value="HtmlEncode[[0]]">HtmlEncode[[Product_0]]</option>
<option value="HtmlEncode[[1]]">HtmlEncode[[Product_1]]</option>
<option selected="HtmlEncode[[selected]]" value="HtmlEncode[[2]]">HtmlEncode[[Product_2]]</option>
</select>
</div>
<div>
<label class="HtmlEncode[[order]]" for="HtmlEncode[[OrderDate]]">HtmlEncode[[OrderDate]]</label>
<input id="HtmlEncode[[OrderDate]]" name="HtmlEncode[[OrderDate]]" type="HtmlEncode[[datetime]]" value="HtmlEncode[[0001/01/01/ A.D.]]" />
</div>
<div>
<label class="HtmlEncode[[order]]" for="HtmlEncode[[NeedSpecialHandle]]">HtmlEncode[[NeedSpecialHandle]]</label>
<input checked="HtmlEncode[[checked]]" id="HtmlEncode[[NeedSpecialHandle]]" name="HtmlEncode[[NeedSpecialHandle]]" type="HtmlEncode[[checkbox]]" value="HtmlEncode[[true]]" /><input name="HtmlEncode[[NeedSpecialHandle]]" type="HtmlEncode[[hidden]]" value="HtmlEncode[[false]]" />
</div>
<div>
<label class="HtmlEncode[[order]]" for="HtmlEncode[[PaymentMethod]]">HtmlEncode[[PaymentMethod]]</label>
<select id="HtmlEncode[[PaymentMethod]]" multiple="HtmlEncode[[multiple]]" name="HtmlEncode[[PaymentMethod]]"><option value="HtmlEncode[[Credit]]">HtmlEncode[[Credit]]</option>
<option selected="HtmlEncode[[selected]]" value="HtmlEncode[[Check]]">HtmlEncode[[Check]]</option>
</select>
</div>
<div>
<label class="HtmlEncode[[order]]" for="HtmlEncode[[Customer_Number]]">HtmlEncode[[Number]]</label>
<input class="HtmlEncode[[form-control]]" data-val="HtmlEncode[[true]]" data-val-range="HtmlEncode[[The field Number must be between 1 and 100.]]" data-val-range-max="HtmlEncode[[100]]" data-val-range-min="HtmlEncode[[1]]" id="HtmlEncode[[Customer_Number]]" name="HtmlEncode[[Customer.Number]]" type="HtmlEncode[[number]]" value="HtmlEncode[[1]]" />
<span class="HtmlEncode[[field-validation-valid]]" data-valmsg-for="HtmlEncode[[Customer.Number]]" data-valmsg-replace="HtmlEncode[[true]]"></span>
</div>
<div>
<label class="HtmlEncode[[order]]" for="HtmlEncode[[Customer_Name]]">HtmlEncode[[Name]]</label>
<input id="HtmlEncode[[Customer_Name]]" name="HtmlEncode[[Customer.Name]]" type="HtmlEncode[[text]]" value="HtmlEncode[[NameStringValue]]" />
</div>
<div>
<label class="HtmlEncode[[order]]" for="HtmlEncode[[Customer_Email]]">HtmlEncode[[Email]]</label>
<input id="HtmlEncode[[Customer_Email]]" name="HtmlEncode[[Customer.Email]]" type="HtmlEncode[[email]]" value="" />
<span class="HtmlEncode[[field-validation-valid]]" data-valmsg-for="HtmlEncode[[Customer.Email]]" data-valmsg-replace="HtmlEncode[[true]]"></span>
</div>
<div>
<label class="HtmlEncode[[order]]" for="HtmlEncode[[Customer_PhoneNumber]]">HtmlEncode[[PhoneNumber]]</label>
<input id="HtmlEncode[[Customer_PhoneNumber]]" name="HtmlEncode[[Customer.PhoneNumber]]" type="HtmlEncode[[tel]]" value="" />
</div>
<div>
<label class="HtmlEncode[[order]]" for="HtmlEncode[[Customer_Password]]">HtmlEncode[[Password]]</label>
<input class="HtmlEncode[[form-control]]" data-val="HtmlEncode[[true]]" data-val-required="HtmlEncode[[The Password field is required.]]" id="HtmlEncode[[Customer_Password]]" name="HtmlEncode[[Customer.Password]]" type="HtmlEncode[[password]]" />
<span class="HtmlEncode[[field-validation-valid]]" data-valmsg-for="HtmlEncode[[Customer.Password]]" data-valmsg-replace="HtmlEncode[[true]]"></span>
</div>
<div>
<label class="HtmlEncode[[order]]" for="HtmlEncode[[Customer_Gender]]">HtmlEncode[[Gender]]</label>
<input id="HtmlEncode[[Customer_Gender]]" name="HtmlEncode[[Customer.Gender]]" type="HtmlEncode[[radio]]" value="HtmlEncode[[Male]]" /> Male
<input checked="HtmlEncode[[checked]]" id="HtmlEncode[[Customer_Gender]]" name="HtmlEncode[[Customer.Gender]]" type="HtmlEncode[[radio]]" value="HtmlEncode[[Female]]" /> Female
<span class="HtmlEncode[[field-validation-valid]]" data-valmsg-for="HtmlEncode[[Customer.Gender]]" data-valmsg-replace="HtmlEncode[[true]]"></span>
</div>
<div class="HtmlEncode[[validation-summary-valid order]]" data-valmsg-summary="HtmlEncode[[true]]"><ul><li style="display:none"></li>
</ul></div>
<input id="HtmlEncode[[Customer_Key]]" name="HtmlEncode[[Customer.Key]]" type="HtmlEncode[[hidden]]" value="HtmlEncode[[KeyA]]" />
<input type="submit"/>
<input name="HtmlEncode[[__RequestVerificationToken]]" type="HtmlEncode[[hidden]]" value="{0}" /></form>
</body>
</html>

View File

@ -0,0 +1,23 @@

<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<form action="HtmlEncode[[/MvcTagHelper_Home/ProductSubmit]]" method="get">
<div>
<label class="product" for="HtmlEncode[[HomePage]]">HtmlEncode[[HomePage]]</label>
<input size="50" type="HtmlEncode[[url]]" id="HtmlEncode[[HomePage]]" name="HtmlEncode[[HomePage]]" value="HtmlEncode[[http://www.contoso.com/]]" />
</div>
<div>
<label class="product" for="HtmlEncode[[Description]]">HtmlEncode[[Description]]</label>
<textarea rows="4" cols="50" class="product" id="HtmlEncode[[Description]]" name="HtmlEncode[[Description]]">
HtmlEncode[[Type the product description]]</textarea>
</div>
<input type="submit" />
</form>
</body>
</html>

View File

@ -0,0 +1,116 @@

<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Script</title>
</head>
<body>
<h2>Script tag helper test</h2>
<script src="HtmlEncode[[/site.js]]" data-foo="foo-data1" title="&lt;the title>">
// Regular script with comment in body, and extra properties.
</script>
<script data-foo="foo-data2" title="&lt;the title>" src="HtmlEncode[[/blank.js?a=b&c=d]]">
// TagHelper script with comment in body, and extra properties.
</script>
<script>(false||document.write("<script JavaScriptEncode[[data-foo]]=\"JavaScriptEncode[[foo-data2]]\" JavaScriptEncode[[title]]=\"JavaScriptEncode[[&lt;the title>]]\" src=\"JavaScriptEncode[[/site.js?a=b&c=d]]\"><\/script>"));</script>
<script title="&quot;the&quot; title" src="HtmlEncode[[/blank.js]]">
// Fallback to globbed src
</script>
<script>(false||document.write("<script JavaScriptEncode[[title]]=\"JavaScriptEncode[["the" title]]\" src=\"JavaScriptEncode[[/site.js]]\"><\/script>"));</script>
<script src="HtmlEncode[[/blank.js]]">
// Fallback to globbed src with exclude
</script>
<script>(false||document.write("<script src=\"JavaScriptEncode[[/site.js]]\"><\/script><script src=\"JavaScriptEncode[[/sub/site2.js]]\"><\/script>"));</script>
<script src="HtmlEncode[[/blank.js]]">
// Fallback to globbed and static src
</script>
<script>(false||document.write("<script src=\"JavaScriptEncode[[/site.js]]\"><\/script><script src=\"JavaScriptEncode[[/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[[/site.js]]\"><\/script>"));</script>
<script src="HtmlEncode[[/blank.js]]">
// Fallback to globbed src with missing include
</script>
<script src="HtmlEncode[[/blank.js]]">
// Fallback to static and globbed src with missing include
</script>
<script>(false||document.write("<script src=\"JavaScriptEncode[[/site.js]]\"><\/script>"));</script>
<script src="HtmlEncode[[/blank.js]]">
// Fallback to globbed src outside of webroot
</script>
<script src="HtmlEncode[[/blank.js]]">
// Fallback to globbed src outside of webroot
</script>
<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[[/site.js]]\"><\/script>"));</script>
<script src="HtmlEncode[[/blank.js]]">
// Invalid TagHelper script with comment in body.
</script>
<!-- Globbed script tag with existing file -->
<script src="HtmlEncode[[/site.js]]"></script>
<!-- Globbed script tag with existing file and exclude -->
<script src="HtmlEncode[[/site.js]]"></script><script src="HtmlEncode[[/sub/site2.js]]"></script>
<script>
// Globbed script tag missing include
</script>
<script src="HtmlEncode[[/site.js]]">
// Globbed script tag missing include but with static src
</script>
<!-- Globbed script tag with missing file -->
<!-- Globbed script tag with file outside of webroot -->
<!-- Globbed script tag with file outside of webroot -->
<script src="HtmlEncode[[/site.js]]">
// Globbed script tag with existing file and static src
</script><script src="HtmlEncode[[/sub/site2.js]]"></script>
<script src="HtmlEncode[[/site.js]]">
// Globbed script tag with existing file and static src should dedupe
</script>
<script src="HtmlEncode[[/blank.js]]">
// TagHelper script with comment in body, and file version.
</script>
<script>(false||document.write("<script src=\"JavaScriptEncode[[/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[[/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I]]\"><\/script>"));</script>
<script src="HtmlEncode[[/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I]]">
// Regular script with comment in body, and file version.
</script>
<!-- Globbed script tag with existing files and version -->
<script src="HtmlEncode[[/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I]]"></script><script src="HtmlEncode[[/sub/site2.js?v=pwJaxaQxnb-rPAdF2JlAp4xiPNq1XuJFd6TyOOfNF-0]]"></script><script src="HtmlEncode[[/sub/site3.js?v=lmeAMiqm76lnGyqHhu6PIBHAC0Vt46mgVB_KaG_gGdA]]"></script>
<!-- Globbed script tag with existing file, exclude and version -->
<script src="HtmlEncode[[/site.js?v=jx1PJjLX32-xgQQx2BxnckU9QH9DVKkm4-M5bSK869I]]"></script><script src="HtmlEncode[[/sub/site2.js?v=pwJaxaQxnb-rPAdF2JlAp4xiPNq1XuJFd6TyOOfNF-0]]"></script>
</body>
</html>

View File

@ -32,6 +32,7 @@
"LoggingWebSite": "1.0.0", "LoggingWebSite": "1.0.0",
"LowercaseUrlsWebSite": "1.0.0-*", "LowercaseUrlsWebSite": "1.0.0-*",
"Microsoft.AspNet.Mvc": "6.0.0-*", "Microsoft.AspNet.Mvc": "6.0.0-*",
"Microsoft.AspNet.Mvc.TestCommon": { "version": "6.0.0-*", "type": "build" },
"Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0", "Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0",
"Microsoft.AspNet.Mvc.Xml": "6.0.0-*", "Microsoft.AspNet.Mvc.Xml": "6.0.0-*",
"Microsoft.AspNet.TestHost": "1.0.0-*", "Microsoft.AspNet.TestHost": "1.0.0-*",

View File

@ -658,9 +658,10 @@ namespace Microsoft.AspNet.Mvc.Razor
// Assert // Assert
var buffer = writer.BufferedWriter.Buffer; var buffer = writer.BufferedWriter.Buffer;
Assert.Equal(2, buffer.BufferEntries.Count); Assert.Equal(3, buffer.BufferEntries.Count);
Assert.Equal("Hello world", buffer.BufferEntries[0]); Assert.Equal("Hello world", buffer.BufferEntries[0]);
Assert.Same(stringCollectionWriter.Buffer.BufferEntries, buffer.BufferEntries[1]); Assert.Equal("text1", buffer.BufferEntries[1]);
Assert.Equal("text2", buffer.BufferEntries[2]);
} }
public static TheoryData<TagHelperOutput, string> WriteTagHelper_InputData public static TheoryData<TagHelperOutput, string> WriteTagHelper_InputData
@ -672,21 +673,21 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
{ {
// parameters: TagName, Attributes, SelfClosing, PreContent, Content, PostContent // parameters: TagName, Attributes, SelfClosing, PreContent, Content, PostContent
GetTagHelperOutput("div", new Dictionary<string, string>(), false, null, "Hello World!", null), GetTagHelperOutput("div", new Dictionary<string, object>(), false, null, "Hello World!", null),
"<div>Hello World!</div>" "<div>Hello World!</div>"
}, },
{ {
GetTagHelperOutput(null, new Dictionary<string, string>(), false, null, "Hello World!", null), GetTagHelperOutput(null, new Dictionary<string, object>(), false, null, "Hello World!", null),
"Hello World!" "Hello World!"
}, },
{ {
GetTagHelperOutput(" ", new Dictionary<string, string>(), false, null, "Hello World!", null), GetTagHelperOutput(" ", new Dictionary<string, object>(), false, null, "Hello World!", null),
"Hello World!" "Hello World!"
}, },
{ {
GetTagHelperOutput( GetTagHelperOutput(
"p", "p",
new Dictionary<string, string>() { { "test", "testVal" } }, new Dictionary<string, object>() { { "test", "testVal" } },
false, false,
null, null,
"Hello World!", "Hello World!",
@ -696,7 +697,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
GetTagHelperOutput( GetTagHelperOutput(
"p", "p",
new Dictionary<string, string>() { { "test", "testVal" }, { "something", " spaced " } }, new Dictionary<string, object>() { { "test", "testVal" }, { "something", " spaced " } },
false, false,
null, null,
"Hello World!", "Hello World!",
@ -706,7 +707,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
GetTagHelperOutput( GetTagHelperOutput(
"p", "p",
new Dictionary<string, string>() { { "test", "testVal" } }, new Dictionary<string, object>() { { "test", "testVal" } },
true, true,
null, null,
"Hello World!", "Hello World!",
@ -716,7 +717,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{ {
GetTagHelperOutput( GetTagHelperOutput(
"p", "p",
new Dictionary<string, string>() { { "test", "testVal" }, { "something", " spaced " } }, new Dictionary<string, object>() { { "test", "testVal" }, { "something", " spaced " } },
true, true,
null, null,
"Hello World!", "Hello World!",
@ -724,31 +725,31 @@ namespace Microsoft.AspNet.Mvc.Razor
"<p test=\"testVal\" something=\" spaced \" />" "<p test=\"testVal\" something=\" spaced \" />"
}, },
{ {
GetTagHelperOutput("p", new Dictionary<string, string>(), false, "Hello World!", null, null), GetTagHelperOutput("p", new Dictionary<string, object>(), false, "Hello World!", null, null),
"<p>Hello World!</p>" "<p>Hello World!</p>"
}, },
{ {
GetTagHelperOutput("p", new Dictionary<string, string>(), false, null, "Hello World!", null), GetTagHelperOutput("p", new Dictionary<string, object>(), false, null, "Hello World!", null),
"<p>Hello World!</p>" "<p>Hello World!</p>"
}, },
{ {
GetTagHelperOutput("p", new Dictionary<string, string>(), false, null, null, "Hello World!"), GetTagHelperOutput("p", new Dictionary<string, object>(), false, null, null, "Hello World!"),
"<p>Hello World!</p>" "<p>Hello World!</p>"
}, },
{ {
GetTagHelperOutput("p", new Dictionary<string, string>(), false, "Hello", "Test", "World!"), GetTagHelperOutput("p", new Dictionary<string, object>(), false, "Hello", "Test", "World!"),
"<p>HelloTestWorld!</p>" "<p>HelloTestWorld!</p>"
}, },
{ {
GetTagHelperOutput("p", new Dictionary<string, string>(), true, "Hello", "Test", "World!"), GetTagHelperOutput("p", new Dictionary<string, object>(), true, "Hello", "Test", "World!"),
"<p />" "<p />"
}, },
{ {
GetTagHelperOutput("custom", new Dictionary<string, string>(), false, "Hello", "Test", "World!"), GetTagHelperOutput("custom", new Dictionary<string, object>(), false, "Hello", "Test", "World!"),
"<custom>HelloTestWorld!</custom>" "<custom>HelloTestWorld!</custom>"
}, },
{ {
GetTagHelperOutput("random", new Dictionary<string, string>(), true, "Hello", "Test", "World!"), GetTagHelperOutput("random", new Dictionary<string, object>(), true, "Hello", "Test", "World!"),
"<random />" "<random />"
} }
}; };
@ -807,8 +808,7 @@ namespace Microsoft.AspNet.Mvc.Razor
}, },
startTagHelperWritingScope: () => { }, startTagHelperWritingScope: () => { },
endTagHelperWritingScope: () => defaultTagHelperContent); endTagHelperWritingScope: () => defaultTagHelperContent);
tagHelperExecutionContext.Output = tagHelperExecutionContext.Output = new TagHelperOutput("p", new Dictionary<string, object>());
new TagHelperOutput("p", new Dictionary<string, string>());
if (childContentRetrieved) if (childContentRetrieved)
{ {
await tagHelperExecutionContext.GetChildContentAsync(); await tagHelperExecutionContext.GetChildContentAsync();
@ -840,8 +840,7 @@ namespace Microsoft.AspNet.Mvc.Razor
executeChildContentAsync: () => { return Task.FromResult(result: true); }, executeChildContentAsync: () => { return Task.FromResult(result: true); },
startTagHelperWritingScope: () => { }, startTagHelperWritingScope: () => { },
endTagHelperWritingScope: () => new DefaultTagHelperContent()); endTagHelperWritingScope: () => new DefaultTagHelperContent());
tagHelperExecutionContext.Output = tagHelperExecutionContext.Output = new TagHelperOutput("p", new Dictionary<string, object>());
new TagHelperOutput("p", new Dictionary<string, string>());
tagHelperExecutionContext.Output.Content.SetContent("Hello World!"); tagHelperExecutionContext.Output.Content.SetContent("Hello World!");
// Act // Act
@ -886,7 +885,7 @@ namespace Microsoft.AspNet.Mvc.Razor
private static TagHelperOutput GetTagHelperOutput( private static TagHelperOutput GetTagHelperOutput(
string tagName, string tagName,
IDictionary<string, string> attributes, IDictionary<string, object> attributes,
bool selfClosing, bool selfClosing,
string preContent, string preContent,
string content, string content,

View File

@ -43,7 +43,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}); });
var output = new TagHelperOutput( var output = new TagHelperOutput(
expectedTagName, expectedTagName,
attributes: new Dictionary<string, string> attributes: new Dictionary<string, object>
{ {
{ "id", "myanchor" }, { "id", "myanchor" },
{ "asp-route-foo", "bar" }, { "asp-route-foo", "bar" },
@ -97,7 +97,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}); });
var output = new TagHelperOutput( var output = new TagHelperOutput(
"a", "a",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
output.Content.SetContent(string.Empty); output.Content.SetContent(string.Empty);
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict); var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
@ -139,7 +139,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}); });
var output = new TagHelperOutput( var output = new TagHelperOutput(
"a", "a",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
output.Content.SetContent(string.Empty); output.Content.SetContent(string.Empty);
var generator = new Mock<IHtmlGenerator>(); var generator = new Mock<IHtmlGenerator>();
@ -180,7 +180,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var anchorTagHelper = new AnchorTagHelper(); var anchorTagHelper = new AnchorTagHelper();
var output = new TagHelperOutput( var output = new TagHelperOutput(
"a", "a",
attributes: new Dictionary<string, string>() attributes: new Dictionary<string, object>()
{ {
{ "href", "http://www.contoso.com" } { "href", "http://www.contoso.com" }
}); });
@ -218,7 +218,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
typeof(AnchorTagHelper).GetProperty(propertyName).SetValue(anchorTagHelper, "Home"); typeof(AnchorTagHelper).GetProperty(propertyName).SetValue(anchorTagHelper, "Home");
var output = new TagHelperOutput( var output = new TagHelperOutput(
"a", "a",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
var expectedErrorMessage = "Cannot determine an 'href' attribute for <a>. An <a> with a specified " + var expectedErrorMessage = "Cannot determine an 'href' attribute for <a>. An <a> with a specified " +
"'asp-route' must not have an 'asp-action' or 'asp-controller' attribute."; "'asp-route' must not have an 'asp-action' or 'asp-controller' attribute.";

View File

@ -17,7 +17,6 @@ using Microsoft.AspNet.Routing;
using Microsoft.Framework.Caching.Memory; using Microsoft.Framework.Caching.Memory;
using Microsoft.Framework.Caching.Memory.Infrastructure; using Microsoft.Framework.Caching.Memory.Infrastructure;
using Microsoft.Framework.Expiration.Interfaces; using Microsoft.Framework.Expiration.Interfaces;
using Microsoft.Framework.WebEncoders;
using Moq; using Moq;
using Xunit; using Xunit;
@ -245,7 +244,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var childContent = "original-child-content"; var childContent = "original-child-content";
var cache = new MemoryCache(new MemoryCacheOptions()); var cache = new MemoryCache(new MemoryCacheOptions());
var tagHelperContext1 = GetTagHelperContext(id, childContent); var tagHelperContext1 = GetTagHelperContext(id, childContent);
var tagHelperOutput1 = new TagHelperOutput("cache", new Dictionary<string, string>()); var tagHelperOutput1 = new TagHelperOutput("cache", new Dictionary<string, object>());
var cacheTagHelper1 = new CacheTagHelper var cacheTagHelper1 = new CacheTagHelper
{ {
VaryByQuery = "key1,key2", VaryByQuery = "key1,key2",
@ -266,7 +265,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Arrange - 2 // Arrange - 2
var tagHelperContext2 = GetTagHelperContext(id, "different-content"); var tagHelperContext2 = GetTagHelperContext(id, "different-content");
var tagHelperOutput2 = new TagHelperOutput("cache", new Dictionary<string, string>()); var tagHelperOutput2 = new TagHelperOutput("cache", new Dictionary<string, object>());
var cacheTagHelper2 = new CacheTagHelper var cacheTagHelper2 = new CacheTagHelper
{ {
VaryByQuery = "key1,key2", VaryByQuery = "key1,key2",
@ -295,7 +294,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var cache = new MemoryCache(new MemoryCacheOptions()); var cache = new MemoryCache(new MemoryCacheOptions());
var tagHelperContext1 = GetTagHelperContext(id, childContent1); var tagHelperContext1 = GetTagHelperContext(id, childContent1);
var tagHelperOutput1 = new TagHelperOutput("cache", var tagHelperOutput1 = new TagHelperOutput("cache",
new Dictionary<string, string> { { "attr", "value" } }); new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput1.PreContent.Append("<cache>"); tagHelperOutput1.PreContent.Append("<cache>");
tagHelperOutput1.PostContent.SetContent("</cache>"); tagHelperOutput1.PostContent.SetContent("</cache>");
var cacheTagHelper1 = new CacheTagHelper var cacheTagHelper1 = new CacheTagHelper
@ -320,7 +319,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelperContext2 = GetTagHelperContext(id, childContent2); var tagHelperContext2 = GetTagHelperContext(id, childContent2);
var tagHelperOutput2 = new TagHelperOutput( var tagHelperOutput2 = new TagHelperOutput(
"cache", "cache",
new Dictionary<string, string> { { "attr", "value" } }); new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput2.PreContent.SetContent("<cache>"); tagHelperOutput2.PreContent.SetContent("<cache>");
tagHelperOutput2.PostContent.SetContent("</cache>"); tagHelperOutput2.PostContent.SetContent("</cache>");
var cacheTagHelper2 = new CacheTagHelper var cacheTagHelper2 = new CacheTagHelper
@ -530,7 +529,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var cache = new MemoryCache(new MemoryCacheOptions { Clock = clock.Object }); var cache = new MemoryCache(new MemoryCacheOptions { Clock = clock.Object });
var tagHelperContext1 = GetTagHelperContext(id, childContent1); var tagHelperContext1 = GetTagHelperContext(id, childContent1);
var tagHelperOutput1 = new TagHelperOutput("cache", var tagHelperOutput1 = new TagHelperOutput("cache",
new Dictionary<string, string> { { "attr", "value" } }); new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput1.PreContent.SetContent("<cache>"); tagHelperOutput1.PreContent.SetContent("<cache>");
tagHelperOutput1.PostContent.SetContent("</cache>"); tagHelperOutput1.PostContent.SetContent("</cache>");
var cacheTagHelper1 = new CacheTagHelper var cacheTagHelper1 = new CacheTagHelper
@ -553,7 +552,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var childContent2 = "different-content"; var childContent2 = "different-content";
var tagHelperContext2 = GetTagHelperContext(id, childContent2); var tagHelperContext2 = GetTagHelperContext(id, childContent2);
var tagHelperOutput2 = new TagHelperOutput("cache", var tagHelperOutput2 = new TagHelperOutput("cache",
new Dictionary<string, string> { { "attr", "value" } }); new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput2.PreContent.SetContent("<cache>"); tagHelperOutput2.PreContent.SetContent("<cache>");
tagHelperOutput2.PostContent.SetContent("</cache>"); tagHelperOutput2.PostContent.SetContent("</cache>");
var cacheTagHelper2 = new CacheTagHelper var cacheTagHelper2 = new CacheTagHelper
@ -587,7 +586,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var cache = new MemoryCache(new MemoryCacheOptions { Clock = clock.Object }); var cache = new MemoryCache(new MemoryCacheOptions { Clock = clock.Object });
var tagHelperContext1 = GetTagHelperContext(id, childContent1); var tagHelperContext1 = GetTagHelperContext(id, childContent1);
var tagHelperOutput1 = new TagHelperOutput("cache", var tagHelperOutput1 = new TagHelperOutput("cache",
new Dictionary<string, string> { { "attr", "value" } }); new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput1.PreContent.SetContent("<cache>"); tagHelperOutput1.PreContent.SetContent("<cache>");
tagHelperOutput1.PostContent.SetContent("</cache>"); tagHelperOutput1.PostContent.SetContent("</cache>");
var cacheTagHelper1 = new CacheTagHelper var cacheTagHelper1 = new CacheTagHelper
@ -611,7 +610,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var childContent2 = "different-content"; var childContent2 = "different-content";
var tagHelperContext2 = GetTagHelperContext(id, childContent2); var tagHelperContext2 = GetTagHelperContext(id, childContent2);
var tagHelperOutput2 = new TagHelperOutput("cache", var tagHelperOutput2 = new TagHelperOutput("cache",
new Dictionary<string, string> { { "attr", "value" } }); new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput2.PreContent.SetContent("<cache>"); tagHelperOutput2.PreContent.SetContent("<cache>");
tagHelperOutput2.PostContent.SetContent("</cache>"); tagHelperOutput2.PostContent.SetContent("</cache>");
var cacheTagHelper2 = new CacheTagHelper var cacheTagHelper2 = new CacheTagHelper
@ -644,7 +643,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var cache = new MemoryCache(new MemoryCacheOptions { Clock = clock.Object }); var cache = new MemoryCache(new MemoryCacheOptions { Clock = clock.Object });
var tagHelperContext1 = GetTagHelperContext(id, childContent1); var tagHelperContext1 = GetTagHelperContext(id, childContent1);
var tagHelperOutput1 = new TagHelperOutput("cache", var tagHelperOutput1 = new TagHelperOutput("cache",
new Dictionary<string, string> { { "attr", "value" } }); new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput1.PreContent.SetContent("<cache>"); tagHelperOutput1.PreContent.SetContent("<cache>");
tagHelperOutput1.PostContent.SetContent("</cache>"); tagHelperOutput1.PostContent.SetContent("</cache>");
var cacheTagHelper1 = new CacheTagHelper var cacheTagHelper1 = new CacheTagHelper
@ -668,7 +667,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var childContent2 = "different-content"; var childContent2 = "different-content";
var tagHelperContext2 = GetTagHelperContext(id, childContent2); var tagHelperContext2 = GetTagHelperContext(id, childContent2);
var tagHelperOutput2 = new TagHelperOutput("cache", var tagHelperOutput2 = new TagHelperOutput("cache",
new Dictionary<string, string> { { "attr", "value" } }); new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput2.PreContent.SetContent("<cache>"); tagHelperOutput2.PreContent.SetContent("<cache>");
tagHelperOutput2.PostContent.SetContent("</cache>"); tagHelperOutput2.PostContent.SetContent("</cache>");
var cacheTagHelper2 = new CacheTagHelper var cacheTagHelper2 = new CacheTagHelper
@ -712,7 +711,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
return Task.FromResult<TagHelperContent>(expectedContent); return Task.FromResult<TagHelperContent>(expectedContent);
}); });
var tagHelperOutput = new TagHelperOutput("cache", var tagHelperOutput = new TagHelperOutput("cache",
new Dictionary<string, string> { { "attr", "value" } }); new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput.PreContent.SetContent("<cache>"); tagHelperOutput.PreContent.SetContent("<cache>");
tagHelperOutput.PostContent.SetContent("</cache>"); tagHelperOutput.PostContent.SetContent("</cache>");
var cacheTagHelper = new CacheTagHelper var cacheTagHelper = new CacheTagHelper

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Razor.Runtime.TagHelpers; using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.WebEncoders;
using Moq; using Moq;
using Xunit; using Xunit;
@ -147,9 +146,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Test
}); });
} }
private TagHelperOutput MakeTagHelperOutput(string tagName, IDictionary<string, string> attributes = null) private TagHelperOutput MakeTagHelperOutput(string tagName, IDictionary<string, object> attributes = null)
{ {
attributes = attributes ?? new Dictionary<string, string>(); attributes = attributes ?? new Dictionary<string, object>();
return new TagHelperOutput(tagName, attributes); return new TagHelperOutput(tagName, attributes);
} }

View File

@ -10,7 +10,6 @@ using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers; using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.AspNet.Routing; using Microsoft.AspNet.Routing;
using Microsoft.Framework.OptionsModel;
using Microsoft.Framework.WebEncoders; using Microsoft.Framework.WebEncoders;
using Moq; using Moq;
using Xunit; using Xunit;
@ -45,7 +44,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}); });
var output = new TagHelperOutput( var output = new TagHelperOutput(
expectedTagName, expectedTagName,
attributes: new Dictionary<string, string> attributes: new Dictionary<string, object>
{ {
{ "id", "myform" }, { "id", "myform" },
{ "asp-route-foo", "bar" }, { "asp-route-foo", "bar" },
@ -67,7 +66,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
AntiForgery = true, AntiForgery = true,
Controller = "home", Controller = "home",
Generator = htmlGenerator, Generator = htmlGenerator,
Method = "post",
ViewContext = viewContext, ViewContext = viewContext,
}; };
@ -110,7 +108,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}); });
var output = new TagHelperOutput( var output = new TagHelperOutput(
"form", "form",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict); var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
generator generator
.Setup(mock => mock.GenerateForm( .Setup(mock => mock.GenerateForm(
@ -159,10 +157,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
tagHelperContent.SetContent("Something"); tagHelperContent.SetContent("Something");
return Task.FromResult<TagHelperContent>(tagHelperContent); return Task.FromResult<TagHelperContent>(tagHelperContent);
}); });
var expectedAttribute = new KeyValuePair<string, string>("asp-ROUTEE-NotRoute", "something"); var expectedAttribute = new KeyValuePair<string, object>("asp-ROUTEE-NotRoute", "something");
var output = new TagHelperOutput( var output = new TagHelperOutput(
"form", "form",
attributes: new Dictionary<string, string>() attributes: new Dictionary<string, object>()
{ {
{ "asp-route-val", "hello" }, { "asp-route-val", "hello" },
{ "asp-roUte--Foo", "bar" } { "asp-roUte--Foo", "bar" }
@ -232,10 +230,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}); });
var output = new TagHelperOutput( var output = new TagHelperOutput(
"form", "form",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict); var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
generator generator
.Setup(mock => mock.GenerateForm(viewContext, "Index", "Home", null, "POST", null)) .Setup(mock => mock.GenerateForm(viewContext, "Index", "Home", null, null, null))
.Returns(new TagBuilder("form", new HtmlEncoder())) .Returns(new TagBuilder("form", new HtmlEncoder()))
.Verifiable(); .Verifiable();
var formTagHelper = new FormTagHelper var formTagHelper = new FormTagHelper
@ -244,7 +242,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
AntiForgery = false, AntiForgery = false,
Controller = "Home", Controller = "Home",
Generator = generator.Object, Generator = generator.Object,
Method = "POST",
ViewContext = viewContext, ViewContext = viewContext,
}; };
@ -260,52 +257,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Assert.Empty(output.PostContent.GetContent()); Assert.Empty(output.PostContent.GetContent());
} }
[Theory]
[InlineData("my-action")]
[InlineData("http://www.contoso.com")]
[InlineData("my/action")]
public async Task ProcessAsync_RestoresBoundAttributesIfActionIsSpecified(string htmlAction)
{
// Arrange
var formTagHelper = new FormTagHelper
{
Method = "POST"
};
var output = new TagHelperOutput("form",
attributes: new Dictionary<string, string>
{
{ "aCTiON", htmlAction },
});
var context = new TagHelperContext(
allAttributes: new Dictionary<string, object>()
{
{ "METhod", "POST" }
},
items: new Dictionary<object, object>(),
uniqueId: "test",
getChildContentAsync: () =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
return Task.FromResult<TagHelperContent>(tagHelperContent);
});
// Act
await formTagHelper.ProcessAsync(context, output);
// Assert
Assert.Equal("form", output.TagName);
Assert.Equal(2, output.Attributes.Count);
var attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("aCTiON"));
Assert.Equal(htmlAction, attribute.Value);
attribute = Assert.Single(output.Attributes, kvp => kvp.Key.Equals("METhod"));
Assert.Equal("POST", attribute.Value);
Assert.Empty(output.PreContent.GetContent());
Assert.True(output.Content.IsEmpty);
Assert.Empty(output.PostContent.GetContent());
}
[Theory] [Theory]
[InlineData(true, "<input />")] [InlineData(true, "<input />")]
[InlineData(false, "")] [InlineData(false, "")]
@ -328,7 +279,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}; };
var output = new TagHelperOutput("form", var output = new TagHelperOutput("form",
attributes: new Dictionary<string, string> attributes: new Dictionary<string, object>
{ {
{ "aCTiON", "my-action" }, { "aCTiON", "my-action" },
}); });
@ -351,7 +302,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Assert.Equal("form", output.TagName); Assert.Equal("form", output.TagName);
Assert.False(output.SelfClosing); Assert.False(output.SelfClosing);
var attribute = Assert.Single(output.Attributes); var attribute = Assert.Single(output.Attributes);
Assert.Equal(new KeyValuePair<string, string>("aCTiON", "my-action"), attribute); Assert.Equal(new KeyValuePair<string, object>("aCTiON", "my-action"), attribute);
Assert.Empty(output.PreContent.GetContent()); Assert.Empty(output.PreContent.GetContent());
Assert.True(output.Content.IsEmpty); Assert.True(output.Content.IsEmpty);
Assert.Equal(expectedPostContent, output.PostContent.GetContent()); Assert.Equal(expectedPostContent, output.PostContent.GetContent());
@ -364,13 +315,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
public async Task ProcessAsync_ThrowsIfActionConflictsWithBoundAttributes(string propertyName) public async Task ProcessAsync_ThrowsIfActionConflictsWithBoundAttributes(string propertyName)
{ {
// Arrange // Arrange
var formTagHelper = new FormTagHelper var formTagHelper = new FormTagHelper();
{
Method = "POST"
};
var tagHelperOutput = new TagHelperOutput( var tagHelperOutput = new TagHelperOutput(
"form", "form",
attributes: new Dictionary<string, string> attributes: new Dictionary<string, object>
{ {
{ "action", "my-action" }, { "action", "my-action" },
}); });

View File

@ -81,7 +81,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string expectedValue) string expectedValue)
{ {
// Arrange // Arrange
var expectedAttributes = new Dictionary<string, string> var expectedAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control" }, { "class", "form-control" },
{ "type", "text" }, { "type", "text" },
@ -105,7 +105,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
tagHelperContent.SetContent("Something"); tagHelperContent.SetContent("Something");
return Task.FromResult<TagHelperContent>(tagHelperContent); return Task.FromResult<TagHelperContent>(tagHelperContent);
}); });
var originalAttributes = new Dictionary<string, string> var originalAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control" }, { "class", "form-control" },
}; };
@ -166,7 +166,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
tagHelperContent.SetContent("Something"); tagHelperContent.SetContent("Something");
return Task.FromResult<TagHelperContent>(tagHelperContent); return Task.FromResult<TagHelperContent>(tagHelperContent);
}); });
var originalAttributes = new Dictionary<string, string> var originalAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control" }, { "class", "form-control" },
}; };
@ -242,7 +242,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
contextAttributes["type"] = inputTypeName; // Support restoration of type attribute, if any. contextAttributes["type"] = inputTypeName; // Support restoration of type attribute, if any.
} }
var expectedAttributes = new Dictionary<string, string> var expectedAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control hidden-control" }, { "class", "form-control hidden-control" },
{ "type", inputTypeName ?? "hidden" }, // Generator restores type attribute; adds "hidden" if none. { "type", inputTypeName ?? "hidden" }, // Generator restores type attribute; adds "hidden" if none.
@ -262,7 +262,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
tagHelperContent.SetContent("Something"); tagHelperContent.SetContent("Something");
return Task.FromResult<TagHelperContent>(tagHelperContent); return Task.FromResult<TagHelperContent>(tagHelperContent);
}); });
var originalAttributes = new Dictionary<string, string> var originalAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control" }, { "class", "form-control" },
}; };
@ -341,7 +341,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
contextAttributes["type"] = inputTypeName; // Support restoration of type attribute, if any. contextAttributes["type"] = inputTypeName; // Support restoration of type attribute, if any.
} }
var expectedAttributes = new Dictionary<string, string> var expectedAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control password-control" }, { "class", "form-control password-control" },
{ "type", inputTypeName ?? "password" }, // Generator restores type attribute; adds "password" if none. { "type", inputTypeName ?? "password" }, // Generator restores type attribute; adds "password" if none.
@ -361,7 +361,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
tagHelperContent.SetContent("Something"); tagHelperContent.SetContent("Something");
return Task.FromResult<TagHelperContent>(tagHelperContent); return Task.FromResult<TagHelperContent>(tagHelperContent);
}); });
var originalAttributes = new Dictionary<string, string> var originalAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control" }, { "class", "form-control" },
}; };
@ -436,7 +436,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
contextAttributes["type"] = inputTypeName; // Support restoration of type attribute, if any. contextAttributes["type"] = inputTypeName; // Support restoration of type attribute, if any.
} }
var expectedAttributes = new Dictionary<string, string> var expectedAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control radio-control" }, { "class", "form-control radio-control" },
{ "type", inputTypeName ?? "radio" }, // Generator restores type attribute; adds "radio" if none. { "type", inputTypeName ?? "radio" }, // Generator restores type attribute; adds "radio" if none.
@ -457,7 +457,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
tagHelperContent.SetContent("Something"); tagHelperContent.SetContent("Something");
return Task.FromResult<TagHelperContent>(tagHelperContent); return Task.FromResult<TagHelperContent>(tagHelperContent);
}); });
var originalAttributes = new Dictionary<string, string> var originalAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control" }, { "class", "form-control" },
}; };
@ -542,7 +542,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
contextAttributes["type"] = inputTypeName; // Support restoration of type attribute, if any. contextAttributes["type"] = inputTypeName; // Support restoration of type attribute, if any.
} }
var expectedAttributes = new Dictionary<string, string> var expectedAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control text-control" }, { "class", "form-control text-control" },
{ "type", inputTypeName ?? "text" }, // Generator restores type attribute; adds "text" if none. { "type", inputTypeName ?? "text" }, // Generator restores type attribute; adds "text" if none.
@ -562,7 +562,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
tagHelperContent.SetContent("Something"); tagHelperContent.SetContent("Something");
return Task.FromResult<TagHelperContent>(tagHelperContent); return Task.FromResult<TagHelperContent>(tagHelperContent);
}); });
var originalAttributes = new Dictionary<string, string> var originalAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control" }, { "class", "form-control" },
}; };

View File

@ -7,7 +7,6 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers; using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.WebEncoders;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Mvc.TagHelpers namespace Microsoft.AspNet.Mvc.TagHelpers
@ -165,7 +164,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{ {
// Arrange // Arrange
var expectedTagName = "not-label"; var expectedTagName = "not-label";
var expectedAttributes = new Dictionary<string, string> var expectedAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control" }, { "class", "form-control" },
{ "for", tagHelperOutputContent.ExpectedId } { "for", tagHelperOutputContent.ExpectedId }
@ -196,7 +195,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
tagHelperContent.SetContent(tagHelperOutputContent.OriginalChildContent); tagHelperContent.SetContent(tagHelperOutputContent.OriginalChildContent);
return Task.FromResult<TagHelperContent>(tagHelperContent); return Task.FromResult<TagHelperContent>(tagHelperContent);
}); });
var htmlAttributes = new Dictionary<string, string> var htmlAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control" }, { "class", "form-control" },
}; };

View File

@ -179,6 +179,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var helper = new LinkTagHelper var helper = new LinkTagHelper
{ {
HtmlEncoder = new HtmlEncoder(), HtmlEncoder = new HtmlEncoder(),
JavaScriptEncoder = new JavaScriptStringEncoder(),
Logger = logger.Object, Logger = logger.Object,
HostingEnvironment = hostingEnvironment, HostingEnvironment = hostingEnvironment,
ViewContext = viewContext, ViewContext = viewContext,
@ -202,8 +203,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var context = MakeTagHelperContext( var context = MakeTagHelperContext(
attributes: new Dictionary<string, object> attributes: new Dictionary<string, object>
{ {
["rel"] = "stylesheet", ["rel"] = new HtmlString("stylesheet"),
["data-extra"] = "something", ["data-extra"] = new HtmlString("something"),
["href"] = "test.css", ["href"] = "test.css",
["asp-fallback-href"] = "test.css", ["asp-fallback-href"] = "test.css",
["asp-fallback-test-class"] = "hidden", ["asp-fallback-test-class"] = "hidden",
@ -211,11 +212,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
["asp-fallback-test-value"] = "hidden" ["asp-fallback-test-value"] = "hidden"
}); });
var output = MakeTagHelperOutput("link", var output = MakeTagHelperOutput("link",
attributes: new Dictionary<string, string> attributes: new Dictionary<string, object>
{ {
["rel"] = "stylesheet", ["rel"] = new HtmlString("stylesheet"),
["data-extra"] = "something", ["data-extra"] = new HtmlString("something"),
["href"] = "test.css"
}); });
var logger = new Mock<ILogger<LinkTagHelper>>(); var logger = new Mock<ILogger<LinkTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment(); var hostingEnvironment = MakeHostingEnvironment();
@ -223,6 +223,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var helper = new LinkTagHelper var helper = new LinkTagHelper
{ {
HtmlEncoder = new HtmlEncoder(), HtmlEncoder = new HtmlEncoder(),
JavaScriptEncoder = new JavaScriptStringEncoder(),
Logger = logger.Object, Logger = logger.Object,
HostingEnvironment = hostingEnvironment, HostingEnvironment = hostingEnvironment,
ViewContext = viewContext, ViewContext = viewContext,
@ -230,6 +231,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
FallbackTestClass = "hidden", FallbackTestClass = "hidden",
FallbackTestProperty = "visibility", FallbackTestProperty = "visibility",
FallbackTestValue = "hidden", FallbackTestValue = "hidden",
Href = "test.css",
Cache = MakeCache(), Cache = MakeCache(),
}; };
@ -374,14 +376,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var context = MakeTagHelperContext( var context = MakeTagHelperContext(
attributes: new Dictionary<string, object> attributes: new Dictionary<string, object>
{ {
["rel"] = new HtmlString("stylesheet"),
["href"] = "/css/site.css", ["href"] = "/css/site.css",
["rel"] = "stylesheet",
["asp-href-include"] = "**/*.css" ["asp-href-include"] = "**/*.css"
}); });
var output = MakeTagHelperOutput("link", attributes: new Dictionary<string, string> var output = MakeTagHelperOutput("link", attributes: new Dictionary<string, object>
{ {
["href"] = "/css/site.css", ["rel"] = new HtmlString("stylesheet"),
["rel"] = "stylesheet"
}); });
var logger = new Mock<ILogger<LinkTagHelper>>(); var logger = new Mock<ILogger<LinkTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment(); var hostingEnvironment = MakeHostingEnvironment();
@ -396,6 +397,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Logger = logger.Object, Logger = logger.Object,
HostingEnvironment = hostingEnvironment, HostingEnvironment = hostingEnvironment,
ViewContext = viewContext, ViewContext = viewContext,
Href = "/css/site.css",
HrefInclude = "**/*.css", HrefInclude = "**/*.css",
Cache = MakeCache(), Cache = MakeCache(),
}; };
@ -404,8 +406,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
helper.Process(context, output); helper.Process(context, output);
// Assert // Assert
Assert.Equal("<link href=\"/css/site.css\" rel=\"stylesheet\" />" + Assert.Equal(
"<link href=\"/base.css\" rel=\"stylesheet\" />", output.Content.GetContent()); "<link rel=\"stylesheet\" href=\"/css/site.css\" />" +
"<link rel=\"stylesheet\" href=\"/base.css\" />",
output.Content.GetContent());
} }
[Fact] [Fact]
@ -415,14 +419,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var context = MakeTagHelperContext( var context = MakeTagHelperContext(
attributes: new Dictionary<string, object> attributes: new Dictionary<string, object>
{ {
["href"] = "/css/site.css",
["rel"] = "stylesheet", ["rel"] = "stylesheet",
["href"] = "/css/site.css",
["asp-href-include"] = "**/*.css" ["asp-href-include"] = "**/*.css"
}); });
var output = MakeTagHelperOutput("link", attributes: new Dictionary<string, string> var output = MakeTagHelperOutput("link", attributes: new Dictionary<string, object>
{ {
["href"] = "/css/site.css", ["rel"] = "stylesheet",
["rel"] = "stylesheet"
}); });
var logger = new Mock<ILogger<LinkTagHelper>>(); var logger = new Mock<ILogger<LinkTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment(); var hostingEnvironment = MakeHostingEnvironment();
@ -437,6 +440,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Logger = logger.Object, Logger = logger.Object,
HostingEnvironment = hostingEnvironment, HostingEnvironment = hostingEnvironment,
ViewContext = viewContext, ViewContext = viewContext,
Href = "/css/site.css",
HrefInclude = "**/*.css", HrefInclude = "**/*.css",
Cache = MakeCache(), Cache = MakeCache(),
}; };
@ -445,8 +449,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
helper.Process(context, output); helper.Process(context, output);
// Assert // Assert
Assert.Equal("<link href=\"HtmlEncode[[/css/site.css]]\" rel=\"stylesheet\" />" + Assert.Equal(
"<link href=\"HtmlEncode[[/base.css]]\" rel=\"stylesheet\" />", output.Content.GetContent()); "<link rel=\"HtmlEncode[[stylesheet]]\" href=\"HtmlEncode[[/css/site.css]]\" />" +
"<link rel=\"HtmlEncode[[stylesheet]]\" href=\"HtmlEncode[[/base.css]]\" />",
output.Content.GetContent());
} }
[Fact] [Fact]
@ -456,14 +462,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var context = MakeTagHelperContext( var context = MakeTagHelperContext(
attributes: new Dictionary<string, object> attributes: new Dictionary<string, object>
{ {
["rel"] = new HtmlString("stylesheet"),
["href"] = "/css/site.css", ["href"] = "/css/site.css",
["rel"] = "stylesheet",
["asp-file-version"] = "true" ["asp-file-version"] = "true"
}); });
var output = MakeTagHelperOutput("link", attributes: new Dictionary<string, string> var output = MakeTagHelperOutput("link", attributes: new Dictionary<string, object>
{ {
["href"] = "/css/site.css", ["rel"] = new HtmlString("stylesheet"),
["rel"] = "stylesheet"
}); });
var logger = new Mock<ILogger<LinkTagHelper>>(); var logger = new Mock<ILogger<LinkTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment(); var hostingEnvironment = MakeHostingEnvironment();
@ -474,6 +479,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Logger = logger.Object, Logger = logger.Object,
HostingEnvironment = hostingEnvironment, HostingEnvironment = hostingEnvironment,
ViewContext = viewContext, ViewContext = viewContext,
Href = "/css/site.css",
HrefInclude = "**/*.css", HrefInclude = "**/*.css",
FileVersion = true, FileVersion = true,
Cache = MakeCache(), Cache = MakeCache(),
@ -483,8 +489,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
helper.Process(context, output); helper.Process(context, output);
// Assert // Assert
Assert.Equal("<link href=\"HtmlEncode[[/css/site.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\"" + Assert.Equal(
" rel=\"stylesheet\" />", output.Content.GetContent()); "<link rel=\"stylesheet\" href=\"HtmlEncode[[/css/site.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\" />",
output.Content.GetContent());
} }
[Fact] [Fact]
@ -494,14 +501,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var context = MakeTagHelperContext( var context = MakeTagHelperContext(
attributes: new Dictionary<string, object> attributes: new Dictionary<string, object>
{ {
["rel"] = new HtmlString("stylesheet"),
["href"] = "/bar/css/site.css", ["href"] = "/bar/css/site.css",
["rel"] = "stylesheet",
["asp-file-version"] = "true" ["asp-file-version"] = "true"
}); });
var output = MakeTagHelperOutput("link", attributes: new Dictionary<string, string> var output = MakeTagHelperOutput("link", attributes: new Dictionary<string, object>
{ {
["href"] = "/bar/css/site.css", ["rel"] = new HtmlString("stylesheet"),
["rel"] = "stylesheet"
}); });
var logger = new Mock<ILogger<LinkTagHelper>>(); var logger = new Mock<ILogger<LinkTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment(); var hostingEnvironment = MakeHostingEnvironment();
@ -512,6 +518,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Logger = logger.Object, Logger = logger.Object,
HostingEnvironment = hostingEnvironment, HostingEnvironment = hostingEnvironment,
ViewContext = viewContext, ViewContext = viewContext,
Href = "/bar/css/site.css",
HrefInclude = "**/*.css", HrefInclude = "**/*.css",
FileVersion = true, FileVersion = true,
Cache = MakeCache(), Cache = MakeCache(),
@ -521,8 +528,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
helper.Process(context, output); helper.Process(context, output);
// Assert // Assert
Assert.Equal("<link href=\"HtmlEncode[[/bar/css/site.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-" + Assert.Equal(
"j1ncoSt3SABJtkGk]]\" rel=\"stylesheet\" />", output.Content.GetContent()); "<link rel=\"stylesheet\" href=\"HtmlEncode[[/bar/css/site.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\" />",
output.Content.GetContent());
} }
[Fact] [Fact]
@ -532,15 +540,14 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var context = MakeTagHelperContext( var context = MakeTagHelperContext(
attributes: new Dictionary<string, object> attributes: new Dictionary<string, object>
{ {
["rel"] = new HtmlString("stylesheet"),
["href"] = "/css/site.css", ["href"] = "/css/site.css",
["rel"] = "stylesheet",
["asp-href-include"] = "**/*.css", ["asp-href-include"] = "**/*.css",
["asp-file-version"] = "true" ["asp-file-version"] = "true"
}); });
var output = MakeTagHelperOutput("link", attributes: new Dictionary<string, string> var output = MakeTagHelperOutput("link", attributes: new Dictionary<string, object>
{ {
["href"] = "/css/site.css", ["rel"] = new HtmlString("stylesheet"),
["rel"] = "stylesheet"
}); });
var logger = new Mock<ILogger<LinkTagHelper>>(); var logger = new Mock<ILogger<LinkTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment(); var hostingEnvironment = MakeHostingEnvironment();
@ -555,6 +562,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Logger = logger.Object, Logger = logger.Object,
HostingEnvironment = hostingEnvironment, HostingEnvironment = hostingEnvironment,
ViewContext = viewContext, ViewContext = viewContext,
Href = "/css/site.css",
HrefInclude = "**/*.css", HrefInclude = "**/*.css",
FileVersion = true, FileVersion = true,
Cache = MakeCache(), Cache = MakeCache(),
@ -564,9 +572,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
helper.Process(context, output); helper.Process(context, output);
// Assert // Assert
Assert.Equal("<link href=\"HtmlEncode[[/css/site.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\"" + Assert.Equal(
" rel=\"stylesheet\" /><link href=\"HtmlEncode[[/base.css" + "<link rel=\"stylesheet\" href=\"HtmlEncode[[/css/site.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\" />" +
"?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\" rel=\"stylesheet\" />", output.Content.GetContent()); "<link rel=\"stylesheet\" href=\"HtmlEncode[[/base.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\" />",
output.Content.GetContent());
} }
private static ViewContext MakeViewContext(string requestPathBase = null) private static ViewContext MakeViewContext(string requestPathBase = null)
@ -607,9 +616,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}); });
} }
private static TagHelperOutput MakeTagHelperOutput(string tagName, IDictionary<string, string> attributes = null) private static TagHelperOutput MakeTagHelperOutput(string tagName, IDictionary<string, object> attributes = null)
{ {
attributes = attributes ?? new Dictionary<string, string>(); attributes = attributes ?? new Dictionary<string, object>();
return new TagHelperOutput(tagName, attributes); return new TagHelperOutput(tagName, attributes);
} }
@ -671,27 +680,5 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}); });
return cache.Object; return cache.Object;
} }
private class TestHtmlEncoder : IHtmlEncoder
{
public string HtmlEncode(string value)
{
return "HtmlEncode[[" + value + "]]";
}
public void HtmlEncode(string value, int startIndex, int charCount, TextWriter output)
{
output.Write("HtmlEncode[[");
output.Write(value.Substring(startIndex, charCount));
output.Write("]]");
}
public void HtmlEncode(char[] value, int startIndex, int charCount, TextWriter output)
{
output.Write("HtmlEncode[[");
output.Write(value, startIndex, charCount);
output.Write("]]");
}
}
} }
} }

View File

@ -26,7 +26,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
null, null, null, null, null, null, null, null,
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" } { "label", "my-label" }
}, },
@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
null, string.Empty, "value", null, null, string.Empty, "value", null,
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "value", "value" }, { "selected", "" } { "label", "my-label" }, { "value", "value" }, { "selected", "" }
}, },
@ -46,7 +46,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
null, "selected", "value", null, null, "selected", "value", null,
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "value", "value" }, { "selected", "selected" } { "label", "my-label" }, { "value", "value" }, { "selected", "selected" }
}, },
@ -56,7 +56,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
null, null, "value", Enumerable.Empty<string>(), null, null, "value", Enumerable.Empty<string>(),
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "value", "value" } { "label", "my-label" }, { "value", "value" }
}, },
@ -66,7 +66,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
null, null, "value", new [] { string.Empty, }, null, null, "value", new [] { string.Empty, },
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "value", "value" } { "label", "my-label" }, { "value", "value" }
}, },
@ -76,7 +76,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
null, string.Empty, "value", new [] { string.Empty, }, null, string.Empty, "value", new [] { string.Empty, },
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "value", "value" }, { "selected", "" } { "label", "my-label" }, { "value", "value" }, { "selected", "" }
}, },
@ -86,7 +86,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
null, null, "value", new [] { "value", }, null, null, "value", new [] { "value", },
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "value", "value" }, { "selected", "selected" } { "label", "my-label" }, { "value", "value" }, { "selected", "selected" }
}, },
@ -96,7 +96,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
null, null, "value", new [] { string.Empty, "value", }, null, null, "value", new [] { string.Empty, "value", },
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "value", "value" }, { "selected", "selected" } { "label", "my-label" }, { "value", "value" }, { "selected", "selected" }
}, },
@ -106,7 +106,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string.Empty, null, null, null, string.Empty, null, null, null,
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" } { "label", "my-label" }
}, },
@ -116,7 +116,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string.Empty, string.Empty, null, null, string.Empty, string.Empty, null, null,
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "selected", "" } { "label", "my-label" }, { "selected", "" }
}, },
@ -126,7 +126,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string.Empty, "selected", null, null, string.Empty, "selected", null, null,
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "selected", "selected" } { "label", "my-label" }, { "selected", "selected" }
}, },
@ -136,7 +136,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string.Empty, null, null, Enumerable.Empty<string>(), string.Empty, null, null, Enumerable.Empty<string>(),
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" } { "label", "my-label" }
}, },
@ -146,7 +146,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string.Empty, null, null, new [] { string.Empty, }, string.Empty, null, null, new [] { string.Empty, },
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "selected", "selected" } { "label", "my-label" }, { "selected", "selected" }
}, },
@ -156,7 +156,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string.Empty, string.Empty, null, new [] { string.Empty, }, string.Empty, string.Empty, null, new [] { string.Empty, },
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "selected", "" } { "label", "my-label" }, { "selected", "" }
}, },
@ -166,7 +166,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string.Empty, null, null, new [] { "text", }, string.Empty, null, null, new [] { "text", },
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" } { "label", "my-label" }
}, },
@ -176,18 +176,17 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string.Empty, null, null, new [] { string.Empty, "text", }, string.Empty, null, null, new [] { string.Empty, "text", },
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "selected", "selected" } { "label", "my-label" }, { "selected", "selected" }
}, },
"") "")
}, },
{ {
"text", null, null, null, "text", null, null, null,
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" } { "label", "my-label" }
}, },
@ -197,7 +196,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", string.Empty, null, null, "text", string.Empty, null, null,
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "selected", "" } { "label", "my-label" }, { "selected", "" }
}, },
@ -207,7 +206,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", "selected", null, null, "text", "selected", null, null,
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "selected", "selected" } { "label", "my-label" }, { "selected", "selected" }
}, },
@ -217,7 +216,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, null, Enumerable.Empty<string>(), "text", null, null, Enumerable.Empty<string>(),
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" } { "label", "my-label" }
}, },
@ -227,7 +226,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, null, new [] { string.Empty, }, "text", null, null, new [] { string.Empty, },
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" } { "label", "my-label" }
}, },
@ -237,7 +236,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, null, new [] { "text", }, "text", null, null, new [] { "text", },
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "selected", "selected" } { "label", "my-label" }, { "selected", "selected" }
}, },
@ -247,7 +246,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", string.Empty, null, new [] { "text", }, "text", string.Empty, null, new [] { "text", },
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "selected", "" } { "label", "my-label" }, { "selected", "" }
}, },
@ -257,18 +256,17 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, null, new [] { string.Empty, "text", }, "text", null, null, new [] { string.Empty, "text", },
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "selected", "selected" } { "label", "my-label" }, { "selected", "selected" }
}, },
"text") "text")
}, },
{ {
"text", string.Empty, "value", null, "text", string.Empty, "value", null,
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "value", "value" }, { "selected", "" } { "label", "my-label" }, { "value", "value" }, { "selected", "" }
}, },
@ -278,7 +276,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", "selected", "value", null, "text", "selected", "value", null,
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "value", "value" }, { "selected", "selected" } { "label", "my-label" }, { "value", "value" }, { "selected", "selected" }
}, },
@ -288,7 +286,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, "value", Enumerable.Empty<string>(), "text", null, "value", Enumerable.Empty<string>(),
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "value", "value" } { "label", "my-label" }, { "value", "value" }
}, },
@ -298,7 +296,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, "value", new [] { string.Empty, }, "text", null, "value", new [] { string.Empty, },
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "value", "value" } { "label", "my-label" }, { "value", "value" }
}, },
@ -308,7 +306,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", string.Empty, "value", new [] { string.Empty, }, "text", string.Empty, "value", new [] { string.Empty, },
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "value", "value" }, { "selected", "" } { "label", "my-label" }, { "value", "value" }, { "selected", "" }
}, },
@ -318,7 +316,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, "value", new [] { "text", }, "text", null, "value", new [] { "text", },
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "value", "value" } { "label", "my-label" }, { "value", "value" }
}, },
@ -328,7 +326,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, "value", new [] { "value", }, "text", null, "value", new [] { "value", },
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "value", "value" }, { "selected", "selected" } { "label", "my-label" }, { "value", "value" }, { "selected", "selected" }
}, },
@ -338,7 +336,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, "value", new [] { string.Empty, "value", }, "text", null, "value", new [] { string.Empty, "value", },
GetTagHelperOutput( GetTagHelperOutput(
"not-option", "not-option",
new Dictionary<string, string> new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "value", "value" }, { "selected", "selected" } { "label", "my-label" }, { "value", "value" }, { "selected", "selected" }
}, },
@ -381,17 +379,21 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
TagHelperOutput expectedTagHelperOutput) TagHelperOutput expectedTagHelperOutput)
{ {
// Arrange // Arrange
var originalAttributes = new Dictionary<string, string> var originalAttributes = new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "label", "my-label" },
}; };
if (selected != null)
{
originalAttributes.Add("selected", selected);
}
var contextAttributes = new Dictionary<string, object> var contextAttributes = new Dictionary<string, object>(originalAttributes);
if (value != null)
{ {
{ "label", "my-label" }, contextAttributes.Add("value", value);
{ "selected", selected }, }
{ "value", value },
};
var tagHelperContext = new TagHelperContext( var tagHelperContext = new TagHelperContext(
contextAttributes, contextAttributes,
items: new Dictionary<object, object>(), items: new Dictionary<object, object>(),
@ -402,6 +404,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
tagHelperContent.SetContent(originalContent); tagHelperContent.SetContent(originalContent);
return Task.FromResult<TagHelperContent>(tagHelperContent); return Task.FromResult<TagHelperContent>(tagHelperContent);
}); });
var output = new TagHelperOutput(expectedTagHelperOutput.TagName, originalAttributes) var output = new TagHelperOutput(expectedTagHelperOutput.TagName, originalAttributes)
{ {
SelfClosing = false, SelfClosing = false,
@ -418,7 +421,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelper = new OptionTagHelper var tagHelper = new OptionTagHelper
{ {
Generator = htmlGenerator, Generator = htmlGenerator,
Selected = selected,
Value = value, Value = value,
ViewContext = viewContext, ViewContext = viewContext,
}; };
@ -446,9 +448,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
TagHelperOutput ignored) TagHelperOutput ignored)
{ {
// Arrange // Arrange
var originalAttributes = new Dictionary<string, string> var originalAttributes = new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "label", "my-label" },
{ "selected", selected },
}; };
var originalTagName = "not-option"; var originalTagName = "not-option";
@ -487,7 +490,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
viewContext.FormContext.FormData[SelectTagHelper.SelectedValuesFormDataKey] = selectedValues; viewContext.FormContext.FormData[SelectTagHelper.SelectedValuesFormDataKey] = selectedValues;
var tagHelper = new OptionTagHelper var tagHelper = new OptionTagHelper
{ {
Selected = selected,
Value = value, Value = value,
ViewContext = viewContext, ViewContext = viewContext,
}; };
@ -507,9 +509,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
TagHelperOutput ignoredOutput) TagHelperOutput ignoredOutput)
{ {
// Arrange // Arrange
var originalAttributes = new Dictionary<string, string> var originalAttributes = new Dictionary<string, object>
{ {
{ "label", "my-label" }, { "label", "my-label" },
{ "selected", selected },
}; };
var originalTagName = "not-option"; var originalTagName = "not-option";
@ -541,7 +544,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelper = new OptionTagHelper var tagHelper = new OptionTagHelper
{ {
Selected = selected,
Value = value, Value = value,
}; };
@ -551,7 +553,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
} }
private static TagHelperOutput GetTagHelperOutput( private static TagHelperOutput GetTagHelperOutput(
string tagName, IDictionary<string, string> attributes, string content) string tagName, IDictionary<string, object> attributes, string content)
{ {
var tagHelperOutput = new TagHelperOutput(tagName, attributes); var tagHelperOutput = new TagHelperOutput(tagName, attributes);
tagHelperOutput.Content.SetContent(content); tagHelperOutput.Content.SetContent(content);

View File

@ -223,6 +223,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var helper = new ScriptTagHelper var helper = new ScriptTagHelper
{ {
HtmlEncoder = new HtmlEncoder(), HtmlEncoder = new HtmlEncoder(),
JavaScriptEncoder = new JavaScriptStringEncoder(),
Logger = logger, Logger = logger,
HostingEnvironment = hostingEnvironment, HostingEnvironment = hostingEnvironment,
ViewContext = viewContext, ViewContext = viewContext,
@ -441,10 +442,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var viewContext = MakeViewContext(); var viewContext = MakeViewContext();
var output = MakeTagHelperOutput("src", var output = MakeTagHelperOutput("src",
attributes: new Dictionary<string, string> attributes: new Dictionary<string, object>
{ {
["data-extra"] = "something", ["data-extra"] = "something",
["src"] = "/blank.js",
["data-more"] = "else", ["data-more"] = "else",
}); });
@ -454,11 +454,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var helper = new ScriptTagHelper var helper = new ScriptTagHelper
{ {
HtmlEncoder = new HtmlEncoder(), HtmlEncoder = new HtmlEncoder(),
JavaScriptEncoder = new JavaScriptStringEncoder(),
Logger = logger, Logger = logger,
ViewContext = viewContext, ViewContext = viewContext,
HostingEnvironment = hostingEnvironment, HostingEnvironment = hostingEnvironment,
FallbackSrc = "~/blank.js", FallbackSrc = "~/blank.js",
FallbackTestExpression = "http://www.example.com/blank.js", FallbackTestExpression = "http://www.example.com/blank.js",
Src = "/blank.js",
Cache = MakeCache(), Cache = MakeCache(),
}; };
@ -467,7 +469,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Assert // Assert
Assert.StartsWith( Assert.StartsWith(
"<script data-extra=\"something\" src=\"/blank.js\" data-more=\"else\"", output.Content.GetContent()); "<script data-extra=\"something\" data-more=\"else\" src=\"/blank.js\"", output.Content.GetContent());
Assert.Empty(logger.Logged); Assert.Empty(logger.Logged);
} }
@ -481,10 +483,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
["src"] = "/js/site.js", ["src"] = "/js/site.js",
["asp-src-include"] = "**/*.js" ["asp-src-include"] = "**/*.js"
}); });
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, string> var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, object>());
{
["src"] = "/js/site.js"
});
var logger = new Mock<ILogger<ScriptTagHelper>>(); var logger = new Mock<ILogger<ScriptTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment(); var hostingEnvironment = MakeHostingEnvironment();
var viewContext = MakeViewContext(); var viewContext = MakeViewContext();
@ -497,6 +496,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Logger = logger.Object, Logger = logger.Object,
HostingEnvironment = hostingEnvironment, HostingEnvironment = hostingEnvironment,
ViewContext = viewContext, ViewContext = viewContext,
Src = "/js/site.js",
SrcInclude = "**/*.js", SrcInclude = "**/*.js",
HtmlEncoder = new HtmlEncoder(), HtmlEncoder = new HtmlEncoder(),
Cache = MakeCache(), Cache = MakeCache(),
@ -519,10 +519,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
["src"] = "/js/site.js", ["src"] = "/js/site.js",
["asp-src-include"] = "**/*.js" ["asp-src-include"] = "**/*.js"
}); });
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, string> var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, object>());
{
["src"] = "/js/site.js"
});
var logger = new Mock<ILogger<ScriptTagHelper>>(); var logger = new Mock<ILogger<ScriptTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment(); var hostingEnvironment = MakeHostingEnvironment();
var viewContext = MakeViewContext(); var viewContext = MakeViewContext();
@ -535,8 +532,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Logger = logger.Object, Logger = logger.Object,
HostingEnvironment = hostingEnvironment, HostingEnvironment = hostingEnvironment,
ViewContext = viewContext, ViewContext = viewContext,
Src = "/js/site.js",
SrcInclude = "**/*.js", SrcInclude = "**/*.js",
HtmlEncoder = new TestHtmlEncoder(), HtmlEncoder = new TestHtmlEncoder(),
JavaScriptEncoder = new TestJavaScriptEncoder(),
Cache = MakeCache(), Cache = MakeCache(),
}; };
@ -558,10 +557,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
["src"] = "/js/site.js", ["src"] = "/js/site.js",
["asp-file-version"] = "true" ["asp-file-version"] = "true"
}); });
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, string> var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, object>());
{
["src"] = "/js/site.js"
});
var logger = new Mock<ILogger<ScriptTagHelper>>(); var logger = new Mock<ILogger<ScriptTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment(); var hostingEnvironment = MakeHostingEnvironment();
@ -574,6 +570,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
ViewContext = viewContext, ViewContext = viewContext,
FileVersion = true, FileVersion = true,
HtmlEncoder = new TestHtmlEncoder(), HtmlEncoder = new TestHtmlEncoder(),
JavaScriptEncoder = new TestJavaScriptEncoder(),
Src = "/js/site.js",
Cache = MakeCache(), Cache = MakeCache(),
}; };
@ -596,10 +594,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
["src"] = "/bar/js/site.js", ["src"] = "/bar/js/site.js",
["asp-file-version"] = "true" ["asp-file-version"] = "true"
}); });
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, string> var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, object>());
{
["src"] = "/bar/js/site.js"
});
var logger = new Mock<ILogger<ScriptTagHelper>>(); var logger = new Mock<ILogger<ScriptTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment(); var hostingEnvironment = MakeHostingEnvironment();
@ -612,6 +607,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
ViewContext = viewContext, ViewContext = viewContext,
FileVersion = true, FileVersion = true,
HtmlEncoder = new TestHtmlEncoder(), HtmlEncoder = new TestHtmlEncoder(),
JavaScriptEncoder = new TestJavaScriptEncoder(),
Src = "/bar/js/site.js",
Cache = MakeCache(), Cache = MakeCache(),
}; };
@ -636,10 +633,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
["asp-fallback-test"] = "isavailable()", ["asp-fallback-test"] = "isavailable()",
["asp-file-version"] = "true" ["asp-file-version"] = "true"
}); });
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, string> var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, object>());
{
["src"] = "/js/site.js"
});
var logger = new Mock<ILogger<ScriptTagHelper>>(); var logger = new Mock<ILogger<ScriptTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment(); var hostingEnvironment = MakeHostingEnvironment();
@ -654,6 +648,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
FallbackTestExpression = "isavailable()", FallbackTestExpression = "isavailable()",
FileVersion = true, FileVersion = true,
HtmlEncoder = new TestHtmlEncoder(), HtmlEncoder = new TestHtmlEncoder(),
JavaScriptEncoder = new TestJavaScriptEncoder(),
Src = "/js/site.js",
Cache = MakeCache(), Cache = MakeCache(),
}; };
@ -663,7 +659,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Assert // Assert
Assert.Equal( Assert.Equal(
"<script src=\"HtmlEncode[[/js/site.js?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\">" + "<script src=\"HtmlEncode[[/js/site.js?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\">" +
"</script>\r\n<script>(isavailable()||document.write(\"<script src=\\\"HtmlEncode[[fallback.js" + "</script>\r\n<script>(isavailable()||document.write(\"<script src=\\\"JavaScriptEncode[[fallback.js" +
"?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\\\"><\\/script>\"));</script>", "?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\\\"><\\/script>\"));</script>",
output.Content.GetContent()); output.Content.GetContent());
} }
@ -679,10 +675,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
["asp-src-include"] = "*.js", ["asp-src-include"] = "*.js",
["asp-file-version"] = "true" ["asp-file-version"] = "true"
}); });
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, string> var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, object>());
{
["src"] = "/js/site.js"
});
var logger = new Mock<ILogger<ScriptTagHelper>>(); var logger = new Mock<ILogger<ScriptTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment(); var hostingEnvironment = MakeHostingEnvironment();
var viewContext = MakeViewContext(); var viewContext = MakeViewContext();
@ -698,6 +691,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
SrcInclude = "*.js", SrcInclude = "*.js",
FileVersion = true, FileVersion = true,
HtmlEncoder = new TestHtmlEncoder(), HtmlEncoder = new TestHtmlEncoder(),
JavaScriptEncoder = new TestJavaScriptEncoder(),
Src = "/js/site.js",
Cache = MakeCache(), Cache = MakeCache(),
}; };
@ -748,9 +743,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
return viewContext; return viewContext;
} }
private TagHelperOutput MakeTagHelperOutput(string tagName, IDictionary<string, string> attributes = null) private TagHelperOutput MakeTagHelperOutput(string tagName, IDictionary<string, object> attributes = null)
{ {
attributes = attributes ?? new Dictionary<string, string>(); attributes = attributes ?? new Dictionary<string, object>();
return new TagHelperOutput(tagName, attributes); return new TagHelperOutput(tagName, attributes);
} }
@ -817,27 +812,5 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}); });
return cache.Object; return cache.Object;
} }
private class TestHtmlEncoder : IHtmlEncoder
{
public string HtmlEncode(string value)
{
return "HtmlEncode[[" + value + "]]";
}
public void HtmlEncode(string value, int startIndex, int charCount, TextWriter output)
{
output.Write("HtmlEncode[[");
output.Write(value.Substring(startIndex, charCount));
output.Write("]]");
}
public void HtmlEncode(char[] value, int startIndex, int charCount, TextWriter output)
{
output.Write("HtmlEncode[[");
output.Write(value, startIndex, charCount);
output.Write("]]");
}
}
} }
} }

View File

@ -9,7 +9,6 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers; using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.WebEncoders;
using Moq; using Moq;
using Xunit; using Xunit;
@ -172,13 +171,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string ignored) string ignored)
{ {
// Arrange // Arrange
var originalAttributes = new Dictionary<string, string> var originalAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control" }, { "class", "form-control" },
}; };
var originalPostContent = "original content"; var originalPostContent = "original content";
var expectedAttributes = new Dictionary<string, string>(originalAttributes) var expectedAttributes = new Dictionary<string, object>(originalAttributes)
{ {
{ "id", nameAndId.Id }, { "id", nameAndId.Id },
{ "name", nameAndId.Name }, { "name", nameAndId.Name },
@ -258,13 +257,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string expectedOptions) string expectedOptions)
{ {
// Arrange // Arrange
var originalAttributes = new Dictionary<string, string> var originalAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control" }, { "class", "form-control" },
}; };
var originalPostContent = "original content"; var originalPostContent = "original content";
var expectedAttributes = new Dictionary<string, string>(originalAttributes) var expectedAttributes = new Dictionary<string, object>(originalAttributes)
{ {
{ "id", nameAndId.Id }, { "id", nameAndId.Id },
{ "name", nameAndId.Name }, { "name", nameAndId.Name },
@ -359,13 +358,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string expectedOptions) string expectedOptions)
{ {
// Arrange // Arrange
var originalAttributes = new Dictionary<string, string> var originalAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control" }, { "class", "form-control" },
}; };
var originalPostContent = "original content"; var originalPostContent = "original content";
var expectedAttributes = new Dictionary<string, string>(originalAttributes) var expectedAttributes = new Dictionary<string, object>(originalAttributes)
{ {
{ "id", nameAndId.Id }, { "id", nameAndId.Id },
{ "name", nameAndId.Name }, { "name", nameAndId.Name },
@ -465,7 +464,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Provided for completeness. Select tag helper does not confirm AllAttributes set is consistent. // Provided for completeness. Select tag helper does not confirm AllAttributes set is consistent.
{ attributeName, attributeValue }, { attributeName, attributeValue },
}; };
var originalAttributes = new Dictionary<string, string> var originalAttributes = new Dictionary<string, object>
{ {
{ attributeName, attributeValue }, { attributeName, attributeValue },
}; };
@ -547,7 +546,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{ {
// Arrange // Arrange
var contextAttributes = new Dictionary<string, object>(); var contextAttributes = new Dictionary<string, object>();
var originalAttributes = new Dictionary<string, string>(); var originalAttributes = new Dictionary<string, object>();
var propertyName = "Property1"; var propertyName = "Property1";
var tagName = "select"; var tagName = "select";

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Arrange // Arrange
var tagHelperOutput = new TagHelperOutput( var tagHelperOutput = new TagHelperOutput(
"p", "p",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
var tagHelperContext = new TagHelperContext( var tagHelperContext = new TagHelperContext(
allAttributes: new Dictionary<string, object>(StringComparer.Ordinal) allAttributes: new Dictionary<string, object>(StringComparer.Ordinal)
{ {
@ -35,7 +35,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
tagHelperContent.Append("Something"); tagHelperContent.Append("Something");
return Task.FromResult<TagHelperContent>(tagHelperContent); return Task.FromResult<TagHelperContent>(tagHelperContent);
}); });
var expectedAttribute = new KeyValuePair<string, string>(attributeName, attributeValue); var expectedAttribute = new KeyValuePair<string, object>(attributeName, attributeValue);
// Act // Act
tagHelperOutput.CopyHtmlAttribute("hello", tagHelperContext); tagHelperOutput.CopyHtmlAttribute("hello", tagHelperContext);
@ -52,11 +52,11 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var attributeName = "hello"; var attributeName = "hello";
var tagHelperOutput = new TagHelperOutput( var tagHelperOutput = new TagHelperOutput(
"p", "p",
attributes: new Dictionary<string, string>() attributes: new Dictionary<string, object>()
{ {
{ attributeName, "world2" } { attributeName, "world2" }
}); });
var expectedAttribute = new KeyValuePair<string, string>(attributeName, "world2"); var expectedAttribute = new KeyValuePair<string, object>(attributeName, "world2");
var tagHelperContext = new TagHelperContext( var tagHelperContext = new TagHelperContext(
allAttributes: new Dictionary<string, object>(StringComparer.Ordinal) allAttributes: new Dictionary<string, object>(StringComparer.Ordinal)
{ {
@ -85,12 +85,12 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Arrange // Arrange
var tagHelperOutput = new TagHelperOutput( var tagHelperOutput = new TagHelperOutput(
"p", "p",
attributes: new Dictionary<string, string>() attributes: new Dictionary<string, object>()
{ {
{ "route-Hello", "World" }, { "route-Hello", "World" },
{ "Route-I", "Am" } { "Route-I", "Am" }
}); });
var expectedAttribute = new KeyValuePair<string, string>("type", "btn"); var expectedAttribute = new KeyValuePair<string, object>("type", "btn");
tagHelperOutput.Attributes.Add(expectedAttribute); tagHelperOutput.Attributes.Add(expectedAttribute);
var attributes = tagHelperOutput.FindPrefixedAttributes("route-"); var attributes = tagHelperOutput.FindPrefixedAttributes("route-");
@ -108,7 +108,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Arrange // Arrange
var tagHelperOutput = new TagHelperOutput( var tagHelperOutput = new TagHelperOutput(
"p", "p",
attributes: new Dictionary<string, string>() attributes: new Dictionary<string, object>()
{ {
{ "routeHello", "World" }, { "routeHello", "World" },
{ "Routee-I", "Am" } { "Routee-I", "Am" }
@ -131,8 +131,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Arrange // Arrange
var tagHelperOutput = new TagHelperOutput( var tagHelperOutput = new TagHelperOutput(
"p", "p",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
var expectedAttribute = new KeyValuePair<string, string>("type", "btn"); var expectedAttribute = new KeyValuePair<string, object>("type", "btn");
tagHelperOutput.Attributes.Add(expectedAttribute); tagHelperOutput.Attributes.Add(expectedAttribute);
var tagBuilder = new TagBuilder("p", new HtmlEncoder()); var tagBuilder = new TagBuilder("p", new HtmlEncoder());
@ -152,13 +152,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Arrange // Arrange
var tagHelperOutput = new TagHelperOutput( var tagHelperOutput = new TagHelperOutput(
"p", "p",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
tagHelperOutput.Attributes.Add("class", "Hello"); tagHelperOutput.Attributes.Add("class", "Hello");
var tagBuilder = new TagBuilder("p", new HtmlEncoder()); var tagBuilder = new TagBuilder("p", new HtmlEncoder());
tagBuilder.Attributes.Add("class", "btn"); tagBuilder.Attributes.Add("class", "btn");
var expectedAttribute = new KeyValuePair<string, string>("class", "Hello btn"); var expectedAttribute = new KeyValuePair<string, object>("class", "Hello btn");
// Act // Act
tagHelperOutput.MergeAttributes(tagBuilder); tagHelperOutput.MergeAttributes(tagBuilder);
@ -178,7 +178,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Arrange // Arrange
var tagHelperOutput = new TagHelperOutput( var tagHelperOutput = new TagHelperOutput(
"p", "p",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
tagHelperOutput.Attributes.Add(originalName, "Hello"); tagHelperOutput.Attributes.Add(originalName, "Hello");
var tagBuilder = new TagBuilder("p", new HtmlEncoder()); var tagBuilder = new TagBuilder("p", new HtmlEncoder());
@ -189,7 +189,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Assert // Assert
var attribute = Assert.Single(tagHelperOutput.Attributes); var attribute = Assert.Single(tagHelperOutput.Attributes);
Assert.Equal(new KeyValuePair<string, string>(originalName, "Hello btn"), attribute); Assert.Equal(new KeyValuePair<string, object>(originalName, "Hello btn"), attribute);
} }
[Fact] [Fact]
@ -198,11 +198,11 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Arrange // Arrange
var tagHelperOutput = new TagHelperOutput( var tagHelperOutput = new TagHelperOutput(
"p", "p",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
var tagBuilder = new TagBuilder("p", new HtmlEncoder()); var tagBuilder = new TagBuilder("p", new HtmlEncoder());
var expectedAttribute = new KeyValuePair<string, string>("visible", "val < 3"); var expectedAttribute = new KeyValuePair<string, object>("visible", "val < 3");
tagBuilder.Attributes.Add(expectedAttribute); tagBuilder.Attributes.Add("visible", "val < 3");
// Act // Act
tagHelperOutput.MergeAttributes(tagBuilder); tagHelperOutput.MergeAttributes(tagBuilder);
@ -218,13 +218,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Arrange // Arrange
var tagHelperOutput = new TagHelperOutput( var tagHelperOutput = new TagHelperOutput(
"p", "p",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
var tagBuilder = new TagBuilder("p", new HtmlEncoder()); var tagBuilder = new TagBuilder("p", new HtmlEncoder());
var expectedAttribute1 = new KeyValuePair<string, string>("class", "btn"); var expectedAttribute1 = new KeyValuePair<string, object>("class", "btn");
var expectedAttribute2 = new KeyValuePair<string, string>("class2", "btn"); var expectedAttribute2 = new KeyValuePair<string, object>("class2", "btn");
tagBuilder.Attributes.Add(expectedAttribute1); tagBuilder.Attributes.Add("class", "btn");
tagBuilder.Attributes.Add(expectedAttribute2); tagBuilder.Attributes.Add("class2", "btn");
// Act // Act
tagHelperOutput.MergeAttributes(tagBuilder); tagHelperOutput.MergeAttributes(tagBuilder);
@ -243,8 +243,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Arrange // Arrange
var tagHelperOutput = new TagHelperOutput( var tagHelperOutput = new TagHelperOutput(
"p", "p",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
var expectedAttribute = new KeyValuePair<string, string>("class", "btn"); var expectedAttribute = new KeyValuePair<string, object>("class", "btn");
tagHelperOutput.Attributes.Add(expectedAttribute); tagHelperOutput.Attributes.Add(expectedAttribute);
var tagBuilder = new TagBuilder("p", new HtmlEncoder()); var tagBuilder = new TagBuilder("p", new HtmlEncoder());
@ -263,13 +263,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Arrange // Arrange
var tagHelperOutput = new TagHelperOutput( var tagHelperOutput = new TagHelperOutput(
"p", "p",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
var expectedOutputAttribute = new KeyValuePair<string, string>("class", "btn"); var expectedOutputAttribute = new KeyValuePair<string, object>("class", "btn");
tagHelperOutput.Attributes.Add(expectedOutputAttribute); tagHelperOutput.Attributes.Add(expectedOutputAttribute);
var tagBuilder = new TagBuilder("p", new HtmlEncoder()); var tagBuilder = new TagBuilder("p", new HtmlEncoder());
var expectedBuilderAttribute = new KeyValuePair<string, string>("for", "hello"); var expectedBuilderAttribute = new KeyValuePair<string, object>("for", "hello");
tagBuilder.Attributes.Add(expectedBuilderAttribute); tagBuilder.Attributes.Add("for", "hello");
// Act // Act
tagHelperOutput.MergeAttributes(tagBuilder); tagHelperOutput.MergeAttributes(tagBuilder);

View File

@ -7,7 +7,6 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers; using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.WebEncoders;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Mvc.TagHelpers namespace Microsoft.AspNet.Mvc.TagHelpers
@ -89,7 +88,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string expectedContent) string expectedContent)
{ {
// Arrange // Arrange
var expectedAttributes = new Dictionary<string, string> var expectedAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control" }, { "class", "form-control" },
{ "id", nameAndId.Id }, { "id", nameAndId.Id },
@ -123,7 +122,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
tagHelperContent.SetContent("Something"); tagHelperContent.SetContent("Something");
return Task.FromResult<TagHelperContent>(tagHelperContent); return Task.FromResult<TagHelperContent>(tagHelperContent);
}); });
var htmlAttributes = new Dictionary<string, string> var htmlAttributes = new Dictionary<string, object>
{ {
{ "class", "form-control" }, { "class", "form-control" },
}; };

View File

@ -49,7 +49,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}); });
var output = new TagHelperOutput( var output = new TagHelperOutput(
expectedTagName, expectedTagName,
attributes: new Dictionary<string, string> attributes: new Dictionary<string, object>
{ {
{ "id", "myvalidationmessage" } { "id", "myvalidationmessage" }
}); });
@ -105,7 +105,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}); });
var output = new TagHelperOutput( var output = new TagHelperOutput(
"span", "span",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
output.PreContent.SetContent(expectedPreContent); output.PreContent.SetContent(expectedPreContent);
output.Content.SetContent(expectedContent); output.Content.SetContent(expectedContent);
output.PostContent.SetContent(expectedPostContent); output.PostContent.SetContent(expectedPostContent);
@ -145,7 +145,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}; };
var output = new TagHelperOutput( var output = new TagHelperOutput(
"span", "span",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
output.Content.SetContent(outputContent); output.Content.SetContent(outputContent);
var context = new TagHelperContext( var context = new TagHelperContext(
@ -204,7 +204,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}; };
var output = new TagHelperOutput( var output = new TagHelperOutput(
"span", "span",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
var context = new TagHelperContext( var context = new TagHelperContext(
allAttributes: new Dictionary<string, object>(), allAttributes: new Dictionary<string, object>(),
@ -259,7 +259,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var expectedPostContent = "original post-content"; var expectedPostContent = "original post-content";
var output = new TagHelperOutput( var output = new TagHelperOutput(
"span", "span",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
output.PreContent.SetContent(expectedPreContent); output.PreContent.SetContent(expectedPreContent);
output.Content.SetContent(expectedContent); output.Content.SetContent(expectedContent);
output.PostContent.SetContent(expectedPostContent); output.PostContent.SetContent(expectedPostContent);

View File

@ -55,7 +55,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}); });
var output = new TagHelperOutput( var output = new TagHelperOutput(
expectedTagName, expectedTagName,
attributes: new Dictionary<string, string> attributes: new Dictionary<string, object>
{ {
{ "class", "form-control" } { "class", "form-control" }
}); });
@ -102,7 +102,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var expectedPostContent = "original post-content"; var expectedPostContent = "original post-content";
var output = new TagHelperOutput( var output = new TagHelperOutput(
"div", "div",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
output.PreContent.SetContent(expectedPreContent); output.PreContent.SetContent(expectedPreContent);
output.Content.SetContent(expectedContent); output.Content.SetContent(expectedContent);
output.PostContent.SetContent(expectedPostContent); output.PostContent.SetContent(expectedPostContent);
@ -144,7 +144,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var expectedContent = "original content"; var expectedContent = "original content";
var output = new TagHelperOutput( var output = new TagHelperOutput(
"div", "div",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
output.PreContent.SetContent(expectedPreContent); output.PreContent.SetContent(expectedPreContent);
output.Content.SetContent(expectedContent); output.Content.SetContent(expectedContent);
output.PostContent.SetContent("Content of validation summary"); output.PostContent.SetContent("Content of validation summary");
@ -201,7 +201,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var expectedPostContent = "original post-content"; var expectedPostContent = "original post-content";
var output = new TagHelperOutput( var output = new TagHelperOutput(
"div", "div",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
output.PreContent.SetContent(expectedPreContent); output.PreContent.SetContent(expectedPreContent);
output.Content.SetContent(expectedContent); output.Content.SetContent(expectedContent);
output.PostContent.SetContent(expectedPostContent); output.PostContent.SetContent(expectedPostContent);
@ -236,7 +236,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var expectedContent = "original content"; var expectedContent = "original content";
var output = new TagHelperOutput( var output = new TagHelperOutput(
"div", "div",
attributes: new Dictionary<string, string>()); attributes: new Dictionary<string, object>());
output.PreContent.SetContent(expectedPreContent); output.PreContent.SetContent(expectedPreContent);
output.Content.SetContent(expectedContent); output.Content.SetContent(expectedContent);
output.PostContent.SetContent("Content of validation message"); output.PostContent.SetContent("Content of validation message");

View File

@ -0,0 +1,27 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
namespace Microsoft.Framework.WebEncoders
{
internal class TestHtmlEncoder : IHtmlEncoder
{
public string HtmlEncode(string value)
{
return $"HtmlEncode[[{ value }]]";
}
public void HtmlEncode(string value, int startIndex, int charCount, TextWriter output)
{
output.Write($"HtmlEncode[[{ value.Substring(startIndex, charCount) }]]");
}
public void HtmlEncode(char[] value, int startIndex, int charCount, TextWriter output)
{
output.Write("HtmlEncode[[");
output.Write(value, startIndex, charCount);
output.Write("]]");
}
}
}

View File

@ -0,0 +1,27 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
namespace Microsoft.Framework.WebEncoders
{
internal class TestJavaScriptEncoder : IJavaScriptStringEncoder
{
public string JavaScriptStringEncode(string value)
{
return $"JavaScriptEncode[[{ value }]]";
}
public void JavaScriptStringEncode(string value, int startIndex, int charCount, TextWriter output)
{
output.Write($"JavaScriptEncode[[{ value.Substring(startIndex, charCount) }]]");
}
public void JavaScriptStringEncode(char[] value, int startIndex, int charCount, TextWriter output)
{
output.Write("JavaScriptEncode[[");
output.Write(value, startIndex, charCount);
output.Write("]]");
}
}
}

View File

@ -0,0 +1,27 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
namespace Microsoft.Framework.WebEncoders
{
internal class TestUrlEncoder : IUrlEncoder
{
public string UrlEncode(string value)
{
return $"UrlEncode[[{ value }]]";
}
public void UrlEncode(string value, int startIndex, int charCount, TextWriter output)
{
output.Write($"UrlEncode[[{ value.Substring(startIndex, charCount) }]]");
}
public void UrlEncode(char[] value, int startIndex, int charCount, TextWriter output)
{
output.Write("UrlEncode[[");
output.Write(value, startIndex, charCount);
output.Write("]]");
}
}
}

View File

@ -7,10 +7,10 @@
<html> <html>
<body> <body>
<div> <div>
<a asp-controller="Product">Product Index</a> <a asp-controller="Product" title="&lt;the title>">Product Index</a>
</div> </div>
<div> <div>
<a asp-controller="Product" asp-action="List">Product List</a> <a asp-controller="Product" asp-action="List" title='"the" title'>Product List</a>
</div> </div>
<div> <div>
<a id="MvcTagHelperTestIndex">MvcTagHelperTest Index</a> <a id="MvcTagHelperTestIndex">MvcTagHelperTest Index</a>

View File

@ -7,13 +7,13 @@
<title>Link</title> <title>Link</title>
<!-- Plain link tag --> <!-- Plain link tag -->
<link href="~/site.css" rel="stylesheet" /> <link href="~/site.css" rel="stylesheet" title="&lt;the title>" />
<!-- Globbed link tag with existing file --> <!-- Globbed link tag with existing file -->
<link asp-href-include="**/site.css" rel="stylesheet" /> <link asp-href-include="**/site.css" rel="stylesheet" title="&lt;the title>" />
<!-- Globbed link tag with existing file and exclude --> <!-- Globbed link tag with existing file and exclude -->
<link asp-href-include="**/*.css" asp-href-exclude="**/site3*.css" rel="stylesheet" /> <link asp-href-include="**/*.css" asp-href-exclude="**/site3*.css" rel="stylesheet" title='"the" title' />
<!-- Globbed link tag missing include --> <!-- Globbed link tag missing include -->
<link asp-href-exclude="**/site2.css" rel="stylesheet" /> <link asp-href-exclude="**/site2.css" rel="stylesheet" />
@ -37,8 +37,8 @@
<link href="~/site.css" asp-href-include="**/site.css" rel="stylesheet" /> <link href="~/site.css" asp-href-include="**/site.css" rel="stylesheet" />
<!-- Fallback to static href --> <!-- Fallback to static href -->
<link href="~/site.min.css" rel="stylesheet" data-extra="test" <link href="~/site.min.css?a=b&c=d" rel="stylesheet" data-extra="test" title='"the" title'
asp-fallback-href="~/site.css" asp-fallback-href="~/site.css?a=b&c=d"
asp-fallback-test-class="hidden" asp-fallback-test-class="hidden"
asp-fallback-test-property="visibility" asp-fallback-test-property="visibility"
asp-fallback-test-value="hidden" /> asp-fallback-test-value="hidden" />

View File

@ -11,15 +11,15 @@
</head> </head>
<body> <body>
<h2>Script tag helper test</h2> <h2>Script tag helper test</h2>
<script src="~/site.js" data-foo="foo-data1"> <script src="~/site.js" data-foo="foo-data1" title="&lt;the title>">
// Regular script with comment in body, and extra properties. // Regular script with comment in body, and extra properties.
</script> </script>
<script src="~/blank.js" asp-fallback-src="~/site.js" asp-fallback-test="false" data-foo="foo-data2"> <script src="~/blank.js?a=b&c=d" asp-fallback-src="~/site.js?a=b&c=d" asp-fallback-test="false" data-foo="foo-data2" title="&lt;the title>">
// TagHelper script with comment in body, and extra properties. // TagHelper script with comment in body, and extra properties.
</script> </script>
<script src="~/blank.js" asp-fallback-src-include="**/site.js" asp-fallback-test="false"> <script src="~/blank.js" asp-fallback-src-include="**/site.js" asp-fallback-test="false" title='"the" title'>
// Fallback to globbed src // Fallback to globbed src
</script> </script>

View File

@ -23,6 +23,8 @@ namespace TagHelpersWebSite.TagHelpers
public bool? MakePretty { get; set; } public bool? MakePretty { get; set; }
public string Style { get; set; }
[Activate] [Activate]
public ViewContext ViewContext { get; set; } public ViewContext ViewContext { get; set; }
@ -40,9 +42,8 @@ namespace TagHelpersWebSite.TagHelpers
if (PrettyTagStyles.TryGetValue(output.TagName, out prettyStyle)) if (PrettyTagStyles.TryGetValue(output.TagName, out prettyStyle))
{ {
var style = string.Empty; var style = Style ?? string.Empty;
if (!string.IsNullOrEmpty(style))
if (output.Attributes.TryGetValue("style", out style))
{ {
style += ";"; style += ";";
} }