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

View File

@ -294,11 +294,10 @@ namespace Microsoft.AspNet.Mvc.Razor
foreach (var attribute in tagHelperOutput.Attributes)
{
var value = HtmlEncoder.HtmlEncode(attribute.Value);
writer.Write(' ');
writer.Write(attribute.Key);
writer.Write("=\"");
writer.Write(value);
WriteTo(writer, HtmlEncoder, attribute.Value, escapeQuotes: true);
writer.Write('"');
}
@ -366,26 +365,67 @@ namespace Microsoft.AspNet.Mvc.Razor
/// </remarks>
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;
if (helperResult != null)
{
helperResult.WriteTo(writer);
}
else
{
var htmlString = value as HtmlString;
if (htmlString != null)
{
writer.Write(htmlString);
}
else
{
WriteTo(writer, value.ToString());
}
}
return;
}
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>
@ -394,10 +434,15 @@ namespace Microsoft.AspNet.Mvc.Razor
/// <param name="writer">The <see cref="TextWriter"/> instance to write to.</param>
/// <param name="value">The <see cref="string"/> to write.</param>
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))
{
HtmlEncoder.HtmlEncode(value, writer);
encoder.HtmlEncode(value, writer);
}
}
@ -766,33 +811,5 @@ namespace Microsoft.AspNet.Mvc.Razor
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.
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;
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.
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(
attribute => attribute.Key.Substring(RouteAttributePrefix.Length),
attribute => (object)attribute.Value);
attribute => (object)attribute.Value.ToString());
}
return routeValues;

View File

@ -27,7 +27,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Protected to ensure subclasses are correctly activated. Internal for ease of use when testing.
[Activate]
protected internal IHtmlGenerator Generator { get; set; }
/// <summary>
/// The name of the action method.
/// </summary>
@ -41,12 +41,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
public string Controller { get; set; }
/// <summary>
/// The HTTP method for processing the form, either GET or POST.
/// </summary>
public string Method { get; set; }
/// <summary>
/// Whether the anti-forgery token should be generated.
/// Whether the anti-forgery token should be generated.
/// </summary>
/// <value>Defaults to <c>false</c> if user provides an <c>action</c> attribute; <c>true</c> otherwise.</value>
[HtmlAttributeName(AntiForgeryAttributeName)]
@ -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
// not force the anti-forgery token on the user.
antiForgeryDefault = false;
// Restore method attribute.
if (Method != null)
{
output.CopyHtmlAttribute(nameof(Method), context);
}
}
else
{
@ -98,7 +87,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Action,
Controller,
routeValues,
Method,
method: null,
htmlAttributes: 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.
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;
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.
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(
attribute => attribute.Key.Substring(RouteAttributePrefix.Length),
attribute => (object)attribute.Value);
attribute => (object)attribute.Value.ToString());
}
return routeValues;

View File

@ -8,7 +8,6 @@ using System.Linq;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.AspNet.Razor.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
// from a TagBuilder.
if (!string.IsNullOrEmpty(InputTypeName))
if (InputTypeName != null)
{
output.CopyHtmlAttribute("type", context);
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using Microsoft.AspNet.Hosting;
@ -10,7 +11,6 @@ using Microsoft.AspNet.Mvc.TagHelpers.Internal;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.Caching.Memory;
using Microsoft.Framework.Logging;
using Microsoft.Framework.Runtime;
using Microsoft.Framework.WebEncoders;
namespace Microsoft.AspNet.Mvc.TagHelpers
@ -98,6 +98,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
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>
/// 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.
@ -189,12 +198,21 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
[Activate]
protected internal IHtmlEncoder HtmlEncoder { get; set; }
[Activate]
protected internal IJavaScriptStringEncoder JavaScriptEncoder { get; set; }
// Internal for ease of use when testing.
protected internal GlobbingUrlBuilder GlobbingUrlBuilder { get; set; }
/// <inheritdoc />
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 logger = Logger ?? LoggerFactory.CreateLogger<LinkTagHelper>();
@ -210,8 +228,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Get the highest matched mode
var mode = modeResult.FullMatches.Select(match => match.Mode).Max();
// NOTE: Values in TagHelperOutput.Attributes are already HtmlEncoded
var attributes = new Dictionary<string, string>(output.Attributes);
// NOTE: Values in TagHelperOutput.Attributes may already be HTML-encoded.
var attributes = new Dictionary<string, object>(output.Attributes);
var builder = new DefaultTagHelperContent();
@ -236,17 +254,17 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
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();
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)
{
// "url" values come from bound attributes and globbing. Must always be non-null.
Debug.Assert(url != null);
attributes[HrefAttributeName] = url;
BuildLinkTag(attributes, builder);
}
@ -260,10 +278,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
if (fallbackHrefs.Length > 0)
{
if (ShouldAddFileVersion())
if (FileVersion == true)
{
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]);
}
}
@ -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
// <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.
builder.Append("<script>")
.Append(string.Format(CultureInfo.InvariantCulture,
JavaScriptResources.GetEmbeddedJavaScript(FallbackJavaScriptResourceName),
JavaScriptStringEncoder.Default.JavaScriptStringEncode(FallbackTestProperty),
JavaScriptStringEncoder.Default.JavaScriptStringEncode(FallbackTestValue),
JavaScriptStringArrayEncoder.Encode(JavaScriptStringEncoder.Default, fallbackHrefs)))
.Append("</script>");
builder
.Append("<script>")
.Append(string.Format(
CultureInfo.InvariantCulture,
JavaScriptResources.GetEmbeddedJavaScript(FallbackJavaScriptResourceName),
JavaScriptEncoder.JavaScriptStringEncode(FallbackTestProperty),
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();
builder.Append("<link ");
@ -319,27 +342,27 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
foreach (var attribute in attributes)
{
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(
ShouldAddFileVersion() ?
_fileVersionProvider.AddFileVersionToPath(attributeValue) :
attributeValue);
// "href" values come from bound attributes and globbing. So anything but a non-null string is
// unexpected but could happen if another helper targeting the same element does something odd.
// Pass through existing value in that case.
var attributeStringValue = attributeValue as string;
if (attributeStringValue != null)
{
attributeValue = _fileVersionProvider.AddFileVersionToPath(attributeStringValue);
}
}
builder
.Append(attribute.Key)
.Append("=\"")
.Append(attributeValue)
.Append(HtmlEncoder, ViewContext.Writer.Encoding, attributeValue)
.Append("\" ");
}
builder.Append("/>");
}
private bool ShouldAddFileVersion()
{
return FileVersion ?? false;
}
}
}

View File

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

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Hosting;
@ -10,7 +11,6 @@ using Microsoft.AspNet.Mvc.TagHelpers.Internal;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.Caching.Memory;
using Microsoft.Framework.Logging;
using Microsoft.Framework.Runtime;
using Microsoft.Framework.WebEncoders;
namespace Microsoft.AspNet.Mvc.TagHelpers
@ -87,6 +87,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
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>
/// 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.
@ -159,12 +168,21 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
[Activate]
protected internal IHtmlEncoder HtmlEncoder { get; set; }
[Activate]
protected internal IJavaScriptStringEncoder JavaScriptEncoder { get; set; }
// Internal for ease of use when testing.
protected internal GlobbingUrlBuilder GlobbingUrlBuilder { get; set; }
/// <inheritdoc />
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 logger = Logger ?? LoggerFactory.CreateLogger<ScriptTagHelper>();
@ -180,9 +198,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Get the highest matched mode
var mode = modeResult.FullMatches.Select(match => match.Mode).Max();
// NOTE: Values in TagHelperOutput.Attributes are already HtmlEncoded
var attributes = new Dictionary<string, string>(output.Attributes);
// NOTE: Values in TagHelperOutput.Attributes may already be HTML-encoded.
var attributes = new Dictionary<string, object>(output.Attributes);
var builder = new DefaultTagHelperContent();
var originalContent = await context.GetChildContentAsync();
@ -209,32 +227,36 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
private void BuildGlobbedScriptTags(
TagHelperContent originalContent,
IDictionary<string, string> attributes,
IDictionary<string, object> attributes,
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();
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)
{
// "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;
var content =
string.Equals(url, staticSrc, StringComparison.OrdinalIgnoreCase) ? originalContent : null;
BuildScriptTag(content, attributes, builder);
}
}
private void BuildFallbackBlock(IDictionary<string, string> attributes, DefaultTagHelperContent builder)
private void BuildFallbackBlock(IDictionary<string, object> attributes, DefaultTagHelperContent builder)
{
EnsureGlobbingUrlBuilder();
EnsureFileVersionProvider();
var fallbackSrcs = GlobbingUrlBuilder.BuildUrlList(FallbackSrc, FallbackSrcInclude, FallbackSrcExclude);
if (fallbackSrcs.Any())
{
// 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("||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)
{
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))
{
AppendAttribute(
builder,
SrcAttributeName,
HtmlEncoder.HtmlEncode(ShouldAddFileVersion() ? _fileVersionProvider.AddFileVersionToPath(src) : src),
escapteQuotes: true);
}
builder.Append("<script");
foreach (var attribute in attributes)
{
if (!attribute.Key.Equals(SrcAttributeName, StringComparison.OrdinalIgnoreCase))
{
var encodedKey = JavaScriptStringEncoder.Default.JavaScriptStringEncode(attribute.Key);
var encodedValue = JavaScriptStringEncoder.Default.JavaScriptStringEncode(attribute.Value);
var encodedKey = JavaScriptEncoder.JavaScriptStringEncode(attribute.Key);
var attributeValue = attribute.Value.ToString();
var encodedValue = JavaScriptEncoder.JavaScriptStringEncode(attributeValue);
AppendAttribute(builder, encodedKey, encodedValue, escapteQuotes: true);
AppendAttribute(builder, encodedKey, encodedValue, escapeQuotes: true);
}
else
{
AppendAttribute(
builder,
attribute.Key,
HtmlEncoder.HtmlEncode(
ShouldAddFileVersion() ? _fileVersionProvider.AddFileVersionToPath(src) : src),
escapteQuotes: true);
// Ignore attribute.Value; use src instead.
var attributeValue = src;
if (FileVersion == true)
{
attributeValue = _fileVersionProvider.AddFileVersionToPath(attributeValue);
}
// 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(
TagHelperContent content,
IDictionary<string, string> attributes,
IDictionary<string, object> attributes,
TagHelperContent builder)
{
EnsureFileVersionProvider();
@ -315,16 +344,21 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
foreach (var attribute in attributes)
{
string attributeValue = attribute.Value;
if (string.Equals(attribute.Key, SrcAttributeName, StringComparison.OrdinalIgnoreCase))
var attributeValue = attribute.Value;
if (FileVersion == true &&
string.Equals(attribute.Key, SrcAttributeName, StringComparison.OrdinalIgnoreCase))
{
attributeValue = HtmlEncoder.HtmlEncode(
ShouldAddFileVersion() ?
_fileVersionProvider.AddFileVersionToPath(attribute.Value) :
attributeValue);
// "src" values come from bound attributes and globbing. So anything but a non-null string is
// unexpected but could happen if another helper targeting the same element does something odd.
// Pass through existing value in that case.
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(">")
@ -332,20 +366,27 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
.Append("</script>");
}
private bool ShouldAddFileVersion()
{
return FileVersion ?? false;
}
private void AppendAttribute(TagHelperContent content, string key, string value, bool escapteQuotes)
private void AppendAttribute(TagHelperContent content, string key, object value, bool escapeQuotes)
{
content
.Append(" ")
.Append(key)
.Append(escapteQuotes ? "=\\\"" : "=\"")
.Append(value)
.Append(escapteQuotes ? "\\\"" : "\"");
.Append(key);
if (escapeQuotes)
{
// 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 Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.TagHelpers
{
@ -24,18 +25,19 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
/// <param name="context">The <see cref="TagHelperContext"/>.</param>
/// <remarks>Only copies the attribute if <paramref name="tagHelperOutput"/>'s
/// <see cref="TagHelperOutput.Attributes"/> does not contain an attribute with the given
/// <paramref name="attributeName"/></remarks>
public static void CopyHtmlAttribute(this TagHelperOutput tagHelperOutput,
string attributeName,
TagHelperContext context)
/// <paramref name="attributeName"/>.</remarks>
public static void CopyHtmlAttribute(
[NotNull] this TagHelperOutput tagHelperOutput,
[NotNull] string attributeName,
[NotNull] TagHelperContext context)
{
// We look for the original attribute so we can restore the exact attribute name the user typed.
var entry = context.AllAttributes.First(attribute =>
attribute.Key.Equals(attributeName, StringComparison.OrdinalIgnoreCase));
if (!tagHelperOutput.Attributes.ContainsKey(entry.Key))
if (!tagHelperOutput.Attributes.ContainsKey(attributeName))
{
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>
/// <returns><see cref="KeyValuePair{string, string}"/>s with <see cref="KeyValuePair{string, string}.Key"/>
/// starting with the given <paramref name="prefix"/>.</returns>
public static IEnumerable<KeyValuePair<string, string>> FindPrefixedAttributes(
this TagHelperOutput tagHelperOutput, string prefix)
public static IEnumerable<KeyValuePair<string, object>> FindPrefixedAttributes(
[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.
@ -68,7 +71,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
/// <param name="tagBuilder">The <see cref="TagBuilder"/> to merge attributes from.</param>
/// <remarks>Existing <see cref="TagHelperOutput.Attributes"/> on the given <paramref name="tagHelperOutput"/>
/// 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)
{
@ -90,7 +95,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/> this method extends.</param>
/// <param name="attributes">Attributes to remove.</param>
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)
{

View File

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

View File

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

View File

@ -7,19 +7,19 @@
<title>Link</title>
<!-- 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 -->
<link rel="stylesheet" href="/site.css" />
<link rel="stylesheet" title="&lt;the title>" href="/site.css" />
<!-- 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 -->
<link rel="stylesheet" />
<!-- 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 -->
@ -31,14 +31,14 @@
<!-- 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 -->
<link href="/site.css" rel="stylesheet" />
<link rel="stylesheet" href="/site.css" />
<!-- Fallback to static href -->
<link href="/site.min.css" rel="stylesheet" data-extra="test" />
<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>
<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?a=b\u0026c=d"]);</script>
<!-- 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>
<!-- 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>
<!-- 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>
<!-- 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>
<!-- 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>
<!-- 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>
<!-- 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>
<!-- 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>
<!-- 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>
<!-- 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>
<!-- 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>
<!-- 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>
<!-- 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 -->
<link href="/site.min.css" rel="stylesheet" data-extra="test" />
<link rel="stylesheet" data-extra="test" href="/site.min.css" />
<!-- 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 -->
<link href="/site.min.css" rel="stylesheet" data-extra="test" />
<link rel="stylesheet" data-extra="test" href="/site.min.css" />
<!-- 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 -->
<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 -->
<link rel="stylesheet" href="/site.css?v=XY7YsMemPf8AGU4SIX9ED9eOjK1LOQWu2dmCNmh-pQc" />
<!-- 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>
<!-- 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>
<body>

View File

@ -7,34 +7,34 @@
</head>
<body>
<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.
</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.
</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
</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">
// Fallback to globbed src with exclude
</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">
// Fallback to globbed and static src
</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">
// Fallback to globbed and static src should de-dupe
</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">
// Fallback to globbed src with missing include
@ -43,7 +43,7 @@
<script src="/blank.js">
// Fallback to static and globbed src with missing include
</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">
// Fallback to globbed src outside of webroot
@ -56,7 +56,7 @@
<script data-foo="foo-data3">
// Valid TagHelper (although no src is provided) script with comment in body, and extra properties.
</script>
<script>(false||document.write("<script 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">
// Invalid TagHelper script with comment in body.
@ -96,12 +96,12 @@
<script src="/blank.js">
// TagHelper script with comment in body, and file version.
</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">
// Fallback to globbed src with file version.
</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">
// 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.Mvc.TagHelpers;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.WebEncoders;
using MvcTagHelpersWebSite;
using Xunit;
@ -76,6 +77,50 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
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]
public async Task ValidationTagHelpers_GeneratesExpectedSpansAndDivs()
{
@ -347,7 +392,7 @@ Products: Laptops (3)";
// Arrange
var newServices = new ServiceCollection();
newServices.InitializeTagHelper<FormTagHelper>((helper, _) => helper.AntiForgery = optionsAntiForgery);
var server = TestHelper.CreateServer(_app, SiteName,
var server = TestHelper.CreateServer(_app, SiteName,
services =>
{
services.Add(newServices);

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",
"LowercaseUrlsWebSite": "1.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.Xml": "6.0.0-*",
"Microsoft.AspNet.TestHost": "1.0.0-*",

View File

@ -658,9 +658,10 @@ namespace Microsoft.AspNet.Mvc.Razor
// Assert
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.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
@ -672,21 +673,21 @@ namespace Microsoft.AspNet.Mvc.Razor
{
{
// 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>"
},
{
GetTagHelperOutput(null, new Dictionary<string, string>(), false, null, "Hello World!", null),
GetTagHelperOutput(null, new Dictionary<string, object>(), false, null, "Hello World!", null),
"Hello World!"
},
{
GetTagHelperOutput(" ", new Dictionary<string, string>(), false, null, "Hello World!", null),
GetTagHelperOutput(" ", new Dictionary<string, object>(), false, null, "Hello World!", null),
"Hello World!"
},
{
GetTagHelperOutput(
"p",
new Dictionary<string, string>() { { "test", "testVal" } },
new Dictionary<string, object>() { { "test", "testVal" } },
false,
null,
"Hello World!",
@ -696,7 +697,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{
GetTagHelperOutput(
"p",
new Dictionary<string, string>() { { "test", "testVal" }, { "something", " spaced " } },
new Dictionary<string, object>() { { "test", "testVal" }, { "something", " spaced " } },
false,
null,
"Hello World!",
@ -706,7 +707,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{
GetTagHelperOutput(
"p",
new Dictionary<string, string>() { { "test", "testVal" } },
new Dictionary<string, object>() { { "test", "testVal" } },
true,
null,
"Hello World!",
@ -716,7 +717,7 @@ namespace Microsoft.AspNet.Mvc.Razor
{
GetTagHelperOutput(
"p",
new Dictionary<string, string>() { { "test", "testVal" }, { "something", " spaced " } },
new Dictionary<string, object>() { { "test", "testVal" }, { "something", " spaced " } },
true,
null,
"Hello World!",
@ -724,31 +725,31 @@ namespace Microsoft.AspNet.Mvc.Razor
"<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>"
},
{
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>"
},
{
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>"
},
{
GetTagHelperOutput("p", new Dictionary<string, string>(), false, "Hello", "Test", "World!"),
GetTagHelperOutput("p", new Dictionary<string, object>(), false, "Hello", "Test", "World!"),
"<p>HelloTestWorld!</p>"
},
{
GetTagHelperOutput("p", new Dictionary<string, string>(), true, "Hello", "Test", "World!"),
GetTagHelperOutput("p", new Dictionary<string, object>(), true, "Hello", "Test", "World!"),
"<p />"
},
{
GetTagHelperOutput("custom", new Dictionary<string, string>(), false, "Hello", "Test", "World!"),
GetTagHelperOutput("custom", new Dictionary<string, object>(), false, "Hello", "Test", "World!"),
"<custom>HelloTestWorld!</custom>"
},
{
GetTagHelperOutput("random", new Dictionary<string, string>(), true, "Hello", "Test", "World!"),
GetTagHelperOutput("random", new Dictionary<string, object>(), true, "Hello", "Test", "World!"),
"<random />"
}
};
@ -807,8 +808,7 @@ namespace Microsoft.AspNet.Mvc.Razor
},
startTagHelperWritingScope: () => { },
endTagHelperWritingScope: () => defaultTagHelperContent);
tagHelperExecutionContext.Output =
new TagHelperOutput("p", new Dictionary<string, string>());
tagHelperExecutionContext.Output = new TagHelperOutput("p", new Dictionary<string, object>());
if (childContentRetrieved)
{
await tagHelperExecutionContext.GetChildContentAsync();
@ -840,8 +840,7 @@ namespace Microsoft.AspNet.Mvc.Razor
executeChildContentAsync: () => { return Task.FromResult(result: true); },
startTagHelperWritingScope: () => { },
endTagHelperWritingScope: () => new DefaultTagHelperContent());
tagHelperExecutionContext.Output =
new TagHelperOutput("p", new Dictionary<string, string>());
tagHelperExecutionContext.Output = new TagHelperOutput("p", new Dictionary<string, object>());
tagHelperExecutionContext.Output.Content.SetContent("Hello World!");
// Act
@ -886,7 +885,7 @@ namespace Microsoft.AspNet.Mvc.Razor
private static TagHelperOutput GetTagHelperOutput(
string tagName,
IDictionary<string, string> attributes,
IDictionary<string, object> attributes,
bool selfClosing,
string preContent,
string content,

View File

@ -43,7 +43,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
});
var output = new TagHelperOutput(
expectedTagName,
attributes: new Dictionary<string, string>
attributes: new Dictionary<string, object>
{
{ "id", "myanchor" },
{ "asp-route-foo", "bar" },
@ -97,7 +97,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
});
var output = new TagHelperOutput(
"a",
attributes: new Dictionary<string, string>());
attributes: new Dictionary<string, object>());
output.Content.SetContent(string.Empty);
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
@ -139,7 +139,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
});
var output = new TagHelperOutput(
"a",
attributes: new Dictionary<string, string>());
attributes: new Dictionary<string, object>());
output.Content.SetContent(string.Empty);
var generator = new Mock<IHtmlGenerator>();
@ -180,7 +180,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var anchorTagHelper = new AnchorTagHelper();
var output = new TagHelperOutput(
"a",
attributes: new Dictionary<string, string>()
attributes: new Dictionary<string, object>()
{
{ "href", "http://www.contoso.com" }
});
@ -218,7 +218,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
typeof(AnchorTagHelper).GetProperty(propertyName).SetValue(anchorTagHelper, "Home");
var output = new TagHelperOutput(
"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 " +
"'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.Infrastructure;
using Microsoft.Framework.Expiration.Interfaces;
using Microsoft.Framework.WebEncoders;
using Moq;
using Xunit;
@ -245,7 +244,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var childContent = "original-child-content";
var cache = new MemoryCache(new MemoryCacheOptions());
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
{
VaryByQuery = "key1,key2",
@ -266,7 +265,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Arrange - 2
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
{
VaryByQuery = "key1,key2",
@ -295,7 +294,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var cache = new MemoryCache(new MemoryCacheOptions());
var tagHelperContext1 = GetTagHelperContext(id, childContent1);
var tagHelperOutput1 = new TagHelperOutput("cache",
new Dictionary<string, string> { { "attr", "value" } });
new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput1.PreContent.Append("<cache>");
tagHelperOutput1.PostContent.SetContent("</cache>");
var cacheTagHelper1 = new CacheTagHelper
@ -320,7 +319,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelperContext2 = GetTagHelperContext(id, childContent2);
var tagHelperOutput2 = new TagHelperOutput(
"cache",
new Dictionary<string, string> { { "attr", "value" } });
new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput2.PreContent.SetContent("<cache>");
tagHelperOutput2.PostContent.SetContent("</cache>");
var cacheTagHelper2 = new CacheTagHelper
@ -530,7 +529,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var cache = new MemoryCache(new MemoryCacheOptions { Clock = clock.Object });
var tagHelperContext1 = GetTagHelperContext(id, childContent1);
var tagHelperOutput1 = new TagHelperOutput("cache",
new Dictionary<string, string> { { "attr", "value" } });
new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput1.PreContent.SetContent("<cache>");
tagHelperOutput1.PostContent.SetContent("</cache>");
var cacheTagHelper1 = new CacheTagHelper
@ -553,7 +552,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var childContent2 = "different-content";
var tagHelperContext2 = GetTagHelperContext(id, childContent2);
var tagHelperOutput2 = new TagHelperOutput("cache",
new Dictionary<string, string> { { "attr", "value" } });
new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput2.PreContent.SetContent("<cache>");
tagHelperOutput2.PostContent.SetContent("</cache>");
var cacheTagHelper2 = new CacheTagHelper
@ -587,7 +586,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var cache = new MemoryCache(new MemoryCacheOptions { Clock = clock.Object });
var tagHelperContext1 = GetTagHelperContext(id, childContent1);
var tagHelperOutput1 = new TagHelperOutput("cache",
new Dictionary<string, string> { { "attr", "value" } });
new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput1.PreContent.SetContent("<cache>");
tagHelperOutput1.PostContent.SetContent("</cache>");
var cacheTagHelper1 = new CacheTagHelper
@ -611,7 +610,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var childContent2 = "different-content";
var tagHelperContext2 = GetTagHelperContext(id, childContent2);
var tagHelperOutput2 = new TagHelperOutput("cache",
new Dictionary<string, string> { { "attr", "value" } });
new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput2.PreContent.SetContent("<cache>");
tagHelperOutput2.PostContent.SetContent("</cache>");
var cacheTagHelper2 = new CacheTagHelper
@ -644,7 +643,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var cache = new MemoryCache(new MemoryCacheOptions { Clock = clock.Object });
var tagHelperContext1 = GetTagHelperContext(id, childContent1);
var tagHelperOutput1 = new TagHelperOutput("cache",
new Dictionary<string, string> { { "attr", "value" } });
new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput1.PreContent.SetContent("<cache>");
tagHelperOutput1.PostContent.SetContent("</cache>");
var cacheTagHelper1 = new CacheTagHelper
@ -668,7 +667,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var childContent2 = "different-content";
var tagHelperContext2 = GetTagHelperContext(id, childContent2);
var tagHelperOutput2 = new TagHelperOutput("cache",
new Dictionary<string, string> { { "attr", "value" } });
new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput2.PreContent.SetContent("<cache>");
tagHelperOutput2.PostContent.SetContent("</cache>");
var cacheTagHelper2 = new CacheTagHelper
@ -712,7 +711,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
return Task.FromResult<TagHelperContent>(expectedContent);
});
var tagHelperOutput = new TagHelperOutput("cache",
new Dictionary<string, string> { { "attr", "value" } });
new Dictionary<string, object> { { "attr", "value" } });
tagHelperOutput.PreContent.SetContent("<cache>");
tagHelperOutput.PostContent.SetContent("</cache>");
var cacheTagHelper = new CacheTagHelper

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.WebEncoders;
using Moq;
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);
}

View File

@ -10,7 +10,6 @@ using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.OptionsModel;
using Microsoft.Framework.WebEncoders;
using Moq;
using Xunit;
@ -45,7 +44,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
});
var output = new TagHelperOutput(
expectedTagName,
attributes: new Dictionary<string, string>
attributes: new Dictionary<string, object>
{
{ "id", "myform" },
{ "asp-route-foo", "bar" },
@ -67,7 +66,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
AntiForgery = true,
Controller = "home",
Generator = htmlGenerator,
Method = "post",
ViewContext = viewContext,
};
@ -110,7 +108,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
});
var output = new TagHelperOutput(
"form",
attributes: new Dictionary<string, string>());
attributes: new Dictionary<string, object>());
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
generator
.Setup(mock => mock.GenerateForm(
@ -159,10 +157,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
tagHelperContent.SetContent("Something");
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(
"form",
attributes: new Dictionary<string, string>()
attributes: new Dictionary<string, object>()
{
{ "asp-route-val", "hello" },
{ "asp-roUte--Foo", "bar" }
@ -232,10 +230,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
});
var output = new TagHelperOutput(
"form",
attributes: new Dictionary<string, string>());
attributes: new Dictionary<string, object>());
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
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()))
.Verifiable();
var formTagHelper = new FormTagHelper
@ -244,7 +242,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
AntiForgery = false,
Controller = "Home",
Generator = generator.Object,
Method = "POST",
ViewContext = viewContext,
};
@ -260,52 +257,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
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]
[InlineData(true, "<input />")]
[InlineData(false, "")]
@ -328,7 +279,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
};
var output = new TagHelperOutput("form",
attributes: new Dictionary<string, string>
attributes: new Dictionary<string, object>
{
{ "aCTiON", "my-action" },
});
@ -351,7 +302,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Assert.Equal("form", output.TagName);
Assert.False(output.SelfClosing);
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.True(output.Content.IsEmpty);
Assert.Equal(expectedPostContent, output.PostContent.GetContent());
@ -364,13 +315,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
public async Task ProcessAsync_ThrowsIfActionConflictsWithBoundAttributes(string propertyName)
{
// Arrange
var formTagHelper = new FormTagHelper
{
Method = "POST"
};
var formTagHelper = new FormTagHelper();
var tagHelperOutput = new TagHelperOutput(
"form",
attributes: new Dictionary<string, string>
attributes: new Dictionary<string, object>
{
{ "action", "my-action" },
});

View File

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

View File

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

View File

@ -179,6 +179,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var helper = new LinkTagHelper
{
HtmlEncoder = new HtmlEncoder(),
JavaScriptEncoder = new JavaScriptStringEncoder(),
Logger = logger.Object,
HostingEnvironment = hostingEnvironment,
ViewContext = viewContext,
@ -202,8 +203,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var context = MakeTagHelperContext(
attributes: new Dictionary<string, object>
{
["rel"] = "stylesheet",
["data-extra"] = "something",
["rel"] = new HtmlString("stylesheet"),
["data-extra"] = new HtmlString("something"),
["href"] = "test.css",
["asp-fallback-href"] = "test.css",
["asp-fallback-test-class"] = "hidden",
@ -211,11 +212,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
["asp-fallback-test-value"] = "hidden"
});
var output = MakeTagHelperOutput("link",
attributes: new Dictionary<string, string>
attributes: new Dictionary<string, object>
{
["rel"] = "stylesheet",
["data-extra"] = "something",
["href"] = "test.css"
["rel"] = new HtmlString("stylesheet"),
["data-extra"] = new HtmlString("something"),
});
var logger = new Mock<ILogger<LinkTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment();
@ -223,6 +223,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var helper = new LinkTagHelper
{
HtmlEncoder = new HtmlEncoder(),
JavaScriptEncoder = new JavaScriptStringEncoder(),
Logger = logger.Object,
HostingEnvironment = hostingEnvironment,
ViewContext = viewContext,
@ -230,6 +231,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
FallbackTestClass = "hidden",
FallbackTestProperty = "visibility",
FallbackTestValue = "hidden",
Href = "test.css",
Cache = MakeCache(),
};
@ -374,14 +376,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var context = MakeTagHelperContext(
attributes: new Dictionary<string, object>
{
["rel"] = new HtmlString("stylesheet"),
["href"] = "/css/site.css",
["rel"] = "stylesheet",
["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"] = new HtmlString("stylesheet"),
});
var logger = new Mock<ILogger<LinkTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment();
@ -396,6 +397,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Logger = logger.Object,
HostingEnvironment = hostingEnvironment,
ViewContext = viewContext,
Href = "/css/site.css",
HrefInclude = "**/*.css",
Cache = MakeCache(),
};
@ -404,8 +406,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
helper.Process(context, output);
// Assert
Assert.Equal("<link href=\"/css/site.css\" rel=\"stylesheet\" />" +
"<link href=\"/base.css\" rel=\"stylesheet\" />", output.Content.GetContent());
Assert.Equal(
"<link rel=\"stylesheet\" href=\"/css/site.css\" />" +
"<link rel=\"stylesheet\" href=\"/base.css\" />",
output.Content.GetContent());
}
[Fact]
@ -415,14 +419,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var context = MakeTagHelperContext(
attributes: new Dictionary<string, object>
{
["href"] = "/css/site.css",
["rel"] = "stylesheet",
["href"] = "/css/site.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 hostingEnvironment = MakeHostingEnvironment();
@ -437,6 +440,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Logger = logger.Object,
HostingEnvironment = hostingEnvironment,
ViewContext = viewContext,
Href = "/css/site.css",
HrefInclude = "**/*.css",
Cache = MakeCache(),
};
@ -445,8 +449,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
helper.Process(context, output);
// Assert
Assert.Equal("<link href=\"HtmlEncode[[/css/site.css]]\" rel=\"stylesheet\" />" +
"<link href=\"HtmlEncode[[/base.css]]\" rel=\"stylesheet\" />", output.Content.GetContent());
Assert.Equal(
"<link rel=\"HtmlEncode[[stylesheet]]\" href=\"HtmlEncode[[/css/site.css]]\" />" +
"<link rel=\"HtmlEncode[[stylesheet]]\" href=\"HtmlEncode[[/base.css]]\" />",
output.Content.GetContent());
}
[Fact]
@ -456,14 +462,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var context = MakeTagHelperContext(
attributes: new Dictionary<string, object>
{
["rel"] = new HtmlString("stylesheet"),
["href"] = "/css/site.css",
["rel"] = "stylesheet",
["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"] = "stylesheet"
["rel"] = new HtmlString("stylesheet"),
});
var logger = new Mock<ILogger<LinkTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment();
@ -474,6 +479,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Logger = logger.Object,
HostingEnvironment = hostingEnvironment,
ViewContext = viewContext,
Href = "/css/site.css",
HrefInclude = "**/*.css",
FileVersion = true,
Cache = MakeCache(),
@ -483,8 +489,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
helper.Process(context, output);
// Assert
Assert.Equal("<link href=\"HtmlEncode[[/css/site.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\"" +
" rel=\"stylesheet\" />", output.Content.GetContent());
Assert.Equal(
"<link rel=\"stylesheet\" href=\"HtmlEncode[[/css/site.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\" />",
output.Content.GetContent());
}
[Fact]
@ -494,14 +501,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var context = MakeTagHelperContext(
attributes: new Dictionary<string, object>
{
["rel"] = new HtmlString("stylesheet"),
["href"] = "/bar/css/site.css",
["rel"] = "stylesheet",
["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"] = "stylesheet"
["rel"] = new HtmlString("stylesheet"),
});
var logger = new Mock<ILogger<LinkTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment();
@ -512,6 +518,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Logger = logger.Object,
HostingEnvironment = hostingEnvironment,
ViewContext = viewContext,
Href = "/bar/css/site.css",
HrefInclude = "**/*.css",
FileVersion = true,
Cache = MakeCache(),
@ -521,8 +528,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
helper.Process(context, output);
// Assert
Assert.Equal("<link href=\"HtmlEncode[[/bar/css/site.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-" +
"j1ncoSt3SABJtkGk]]\" rel=\"stylesheet\" />", output.Content.GetContent());
Assert.Equal(
"<link rel=\"stylesheet\" href=\"HtmlEncode[[/bar/css/site.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\" />",
output.Content.GetContent());
}
[Fact]
@ -532,15 +540,14 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var context = MakeTagHelperContext(
attributes: new Dictionary<string, object>
{
["rel"] = new HtmlString("stylesheet"),
["href"] = "/css/site.css",
["rel"] = "stylesheet",
["asp-href-include"] = "**/*.css",
["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"] = "stylesheet"
["rel"] = new HtmlString("stylesheet"),
});
var logger = new Mock<ILogger<LinkTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment();
@ -555,6 +562,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Logger = logger.Object,
HostingEnvironment = hostingEnvironment,
ViewContext = viewContext,
Href = "/css/site.css",
HrefInclude = "**/*.css",
FileVersion = true,
Cache = MakeCache(),
@ -562,11 +570,12 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Act
helper.Process(context, output);
// Assert
Assert.Equal("<link href=\"HtmlEncode[[/css/site.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\"" +
" rel=\"stylesheet\" /><link href=\"HtmlEncode[[/base.css" +
"?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\" rel=\"stylesheet\" />", output.Content.GetContent());
Assert.Equal(
"<link rel=\"stylesheet\" href=\"HtmlEncode[[/css/site.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\" />" +
"<link rel=\"stylesheet\" href=\"HtmlEncode[[/base.css?v=f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk]]\" />",
output.Content.GetContent());
}
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);
}
@ -671,27 +680,5 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
});
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,
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }
},
@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
null, string.Empty, "value", null,
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "value", "value" }, { "selected", "" }
},
@ -46,7 +46,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
null, "selected", "value", null,
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "value", "value" }, { "selected", "selected" }
},
@ -56,7 +56,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
null, null, "value", Enumerable.Empty<string>(),
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "value", "value" }
},
@ -66,7 +66,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
null, null, "value", new [] { string.Empty, },
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "value", "value" }
},
@ -76,7 +76,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
null, string.Empty, "value", new [] { string.Empty, },
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "value", "value" }, { "selected", "" }
},
@ -86,7 +86,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
null, null, "value", new [] { "value", },
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "value", "value" }, { "selected", "selected" }
},
@ -96,7 +96,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
null, null, "value", new [] { string.Empty, "value", },
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "value", "value" }, { "selected", "selected" }
},
@ -106,7 +106,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string.Empty, null, null, null,
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }
},
@ -116,7 +116,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string.Empty, string.Empty, null, null,
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "selected", "" }
},
@ -126,7 +126,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string.Empty, "selected", null, null,
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "selected", "selected" }
},
@ -136,7 +136,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string.Empty, null, null, Enumerable.Empty<string>(),
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }
},
@ -146,7 +146,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string.Empty, null, null, new [] { string.Empty, },
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "selected", "selected" }
},
@ -156,7 +156,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string.Empty, string.Empty, null, new [] { string.Empty, },
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "selected", "" }
},
@ -166,7 +166,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string.Empty, null, null, new [] { "text", },
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }
},
@ -176,18 +176,17 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string.Empty, null, null, new [] { string.Empty, "text", },
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "selected", "selected" }
},
"")
},
{
"text", null, null, null,
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }
},
@ -197,7 +196,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", string.Empty, null, null,
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "selected", "" }
},
@ -207,7 +206,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", "selected", null, null,
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "selected", "selected" }
},
@ -217,7 +216,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, null, Enumerable.Empty<string>(),
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }
},
@ -227,7 +226,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, null, new [] { string.Empty, },
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }
},
@ -237,7 +236,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, null, new [] { "text", },
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "selected", "selected" }
},
@ -247,7 +246,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", string.Empty, null, new [] { "text", },
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "selected", "" }
},
@ -257,18 +256,17 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, null, new [] { string.Empty, "text", },
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "selected", "selected" }
},
"text")
},
{
"text", string.Empty, "value", null,
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "value", "value" }, { "selected", "" }
},
@ -278,7 +276,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", "selected", "value", null,
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "value", "value" }, { "selected", "selected" }
},
@ -288,7 +286,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, "value", Enumerable.Empty<string>(),
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "value", "value" }
},
@ -298,7 +296,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, "value", new [] { string.Empty, },
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "value", "value" }
},
@ -308,7 +306,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", string.Empty, "value", new [] { string.Empty, },
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "value", "value" }, { "selected", "" }
},
@ -318,7 +316,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, "value", new [] { "text", },
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "value", "value" }
},
@ -328,7 +326,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, "value", new [] { "value", },
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "value", "value" }, { "selected", "selected" }
},
@ -338,7 +336,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
"text", null, "value", new [] { string.Empty, "value", },
GetTagHelperOutput(
"not-option",
new Dictionary<string, string>
new Dictionary<string, object>
{
{ "label", "my-label" }, { "value", "value" }, { "selected", "selected" }
},
@ -381,17 +379,21 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
TagHelperOutput expectedTagHelperOutput)
{
// Arrange
var originalAttributes = new Dictionary<string, string>
var originalAttributes = new Dictionary<string, object>
{
{ "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" },
{ "selected", selected },
{ "value", value },
};
contextAttributes.Add("value", value);
}
var tagHelperContext = new TagHelperContext(
contextAttributes,
items: new Dictionary<object, object>(),
@ -402,6 +404,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
tagHelperContent.SetContent(originalContent);
return Task.FromResult<TagHelperContent>(tagHelperContent);
});
var output = new TagHelperOutput(expectedTagHelperOutput.TagName, originalAttributes)
{
SelfClosing = false,
@ -418,7 +421,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelper = new OptionTagHelper
{
Generator = htmlGenerator,
Selected = selected,
Value = value,
ViewContext = viewContext,
};
@ -446,9 +448,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
TagHelperOutput ignored)
{
// Arrange
var originalAttributes = new Dictionary<string, string>
var originalAttributes = new Dictionary<string, object>
{
{ "label", "my-label" },
{ "selected", selected },
};
var originalTagName = "not-option";
@ -487,7 +490,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
viewContext.FormContext.FormData[SelectTagHelper.SelectedValuesFormDataKey] = selectedValues;
var tagHelper = new OptionTagHelper
{
Selected = selected,
Value = value,
ViewContext = viewContext,
};
@ -507,9 +509,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
TagHelperOutput ignoredOutput)
{
// Arrange
var originalAttributes = new Dictionary<string, string>
var originalAttributes = new Dictionary<string, object>
{
{ "label", "my-label" },
{ "selected", selected },
};
var originalTagName = "not-option";
@ -541,7 +544,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var tagHelper = new OptionTagHelper
{
Selected = selected,
Value = value,
};
@ -551,7 +553,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}
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);
tagHelperOutput.Content.SetContent(content);

View File

@ -223,6 +223,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var helper = new ScriptTagHelper
{
HtmlEncoder = new HtmlEncoder(),
JavaScriptEncoder = new JavaScriptStringEncoder(),
Logger = logger,
HostingEnvironment = hostingEnvironment,
ViewContext = viewContext,
@ -441,10 +442,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var viewContext = MakeViewContext();
var output = MakeTagHelperOutput("src",
attributes: new Dictionary<string, string>
attributes: new Dictionary<string, object>
{
["data-extra"] = "something",
["src"] = "/blank.js",
["data-more"] = "else",
});
@ -454,11 +454,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var helper = new ScriptTagHelper
{
HtmlEncoder = new HtmlEncoder(),
JavaScriptEncoder = new JavaScriptStringEncoder(),
Logger = logger,
ViewContext = viewContext,
HostingEnvironment = hostingEnvironment,
FallbackSrc = "~/blank.js",
FallbackTestExpression = "http://www.example.com/blank.js",
Src = "/blank.js",
Cache = MakeCache(),
};
@ -467,7 +469,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Assert
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);
}
@ -481,10 +483,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
["src"] = "/js/site.js",
["asp-src-include"] = "**/*.js"
});
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, string>
{
["src"] = "/js/site.js"
});
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, object>());
var logger = new Mock<ILogger<ScriptTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment();
var viewContext = MakeViewContext();
@ -497,6 +496,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Logger = logger.Object,
HostingEnvironment = hostingEnvironment,
ViewContext = viewContext,
Src = "/js/site.js",
SrcInclude = "**/*.js",
HtmlEncoder = new HtmlEncoder(),
Cache = MakeCache(),
@ -519,10 +519,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
["src"] = "/js/site.js",
["asp-src-include"] = "**/*.js"
});
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, string>
{
["src"] = "/js/site.js"
});
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, object>());
var logger = new Mock<ILogger<ScriptTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment();
var viewContext = MakeViewContext();
@ -535,8 +532,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Logger = logger.Object,
HostingEnvironment = hostingEnvironment,
ViewContext = viewContext,
Src = "/js/site.js",
SrcInclude = "**/*.js",
HtmlEncoder = new TestHtmlEncoder(),
JavaScriptEncoder = new TestJavaScriptEncoder(),
Cache = MakeCache(),
};
@ -558,10 +557,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
["src"] = "/js/site.js",
["asp-file-version"] = "true"
});
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, string>
{
["src"] = "/js/site.js"
});
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, object>());
var logger = new Mock<ILogger<ScriptTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment();
@ -574,6 +570,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
ViewContext = viewContext,
FileVersion = true,
HtmlEncoder = new TestHtmlEncoder(),
JavaScriptEncoder = new TestJavaScriptEncoder(),
Src = "/js/site.js",
Cache = MakeCache(),
};
@ -596,10 +594,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
["src"] = "/bar/js/site.js",
["asp-file-version"] = "true"
});
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, string>
{
["src"] = "/bar/js/site.js"
});
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, object>());
var logger = new Mock<ILogger<ScriptTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment();
@ -612,6 +607,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
ViewContext = viewContext,
FileVersion = true,
HtmlEncoder = new TestHtmlEncoder(),
JavaScriptEncoder = new TestJavaScriptEncoder(),
Src = "/bar/js/site.js",
Cache = MakeCache(),
};
@ -636,10 +633,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
["asp-fallback-test"] = "isavailable()",
["asp-file-version"] = "true"
});
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, string>
{
["src"] = "/js/site.js"
});
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, object>());
var logger = new Mock<ILogger<ScriptTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment();
@ -654,6 +648,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
FallbackTestExpression = "isavailable()",
FileVersion = true,
HtmlEncoder = new TestHtmlEncoder(),
JavaScriptEncoder = new TestJavaScriptEncoder(),
Src = "/js/site.js",
Cache = MakeCache(),
};
@ -663,7 +659,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
// Assert
Assert.Equal(
"<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>",
output.Content.GetContent());
}
@ -679,10 +675,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
["asp-src-include"] = "*.js",
["asp-file-version"] = "true"
});
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, string>
{
["src"] = "/js/site.js"
});
var output = MakeTagHelperOutput("script", attributes: new Dictionary<string, object>());
var logger = new Mock<ILogger<ScriptTagHelper>>();
var hostingEnvironment = MakeHostingEnvironment();
var viewContext = MakeViewContext();
@ -698,6 +691,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
SrcInclude = "*.js",
FileVersion = true,
HtmlEncoder = new TestHtmlEncoder(),
JavaScriptEncoder = new TestJavaScriptEncoder(),
Src = "/js/site.js",
Cache = MakeCache(),
};
@ -748,9 +743,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
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);
}
@ -817,27 +812,5 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
});
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.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.WebEncoders;
using Moq;
using Xunit;
@ -172,13 +171,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string ignored)
{
// Arrange
var originalAttributes = new Dictionary<string, string>
var originalAttributes = new Dictionary<string, object>
{
{ "class", "form-control" },
};
var originalPostContent = "original content";
var expectedAttributes = new Dictionary<string, string>(originalAttributes)
var expectedAttributes = new Dictionary<string, object>(originalAttributes)
{
{ "id", nameAndId.Id },
{ "name", nameAndId.Name },
@ -258,13 +257,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string expectedOptions)
{
// Arrange
var originalAttributes = new Dictionary<string, string>
var originalAttributes = new Dictionary<string, object>
{
{ "class", "form-control" },
};
var originalPostContent = "original content";
var expectedAttributes = new Dictionary<string, string>(originalAttributes)
var expectedAttributes = new Dictionary<string, object>(originalAttributes)
{
{ "id", nameAndId.Id },
{ "name", nameAndId.Name },
@ -359,13 +358,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
string expectedOptions)
{
// Arrange
var originalAttributes = new Dictionary<string, string>
var originalAttributes = new Dictionary<string, object>
{
{ "class", "form-control" },
};
var originalPostContent = "original content";
var expectedAttributes = new Dictionary<string, string>(originalAttributes)
var expectedAttributes = new Dictionary<string, object>(originalAttributes)
{
{ "id", nameAndId.Id },
{ "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.
{ attributeName, attributeValue },
};
var originalAttributes = new Dictionary<string, string>
var originalAttributes = new Dictionary<string, object>
{
{ attributeName, attributeValue },
};
@ -547,7 +546,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{
// Arrange
var contextAttributes = new Dictionary<string, object>();
var originalAttributes = new Dictionary<string, string>();
var originalAttributes = new Dictionary<string, object>();
var propertyName = "Property1";
var tagName = "select";

View File

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

View File

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

View File

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

View File

@ -55,7 +55,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
});
var output = new TagHelperOutput(
expectedTagName,
attributes: new Dictionary<string, string>
attributes: new Dictionary<string, object>
{
{ "class", "form-control" }
});
@ -102,7 +102,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var expectedPostContent = "original post-content";
var output = new TagHelperOutput(
"div",
attributes: new Dictionary<string, string>());
attributes: new Dictionary<string, object>());
output.PreContent.SetContent(expectedPreContent);
output.Content.SetContent(expectedContent);
output.PostContent.SetContent(expectedPostContent);
@ -144,7 +144,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var expectedContent = "original content";
var output = new TagHelperOutput(
"div",
attributes: new Dictionary<string, string>());
attributes: new Dictionary<string, object>());
output.PreContent.SetContent(expectedPreContent);
output.Content.SetContent(expectedContent);
output.PostContent.SetContent("Content of validation summary");
@ -201,7 +201,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var expectedPostContent = "original post-content";
var output = new TagHelperOutput(
"div",
attributes: new Dictionary<string, string>());
attributes: new Dictionary<string, object>());
output.PreContent.SetContent(expectedPreContent);
output.Content.SetContent(expectedContent);
output.PostContent.SetContent(expectedPostContent);
@ -236,7 +236,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var expectedContent = "original content";
var output = new TagHelperOutput(
"div",
attributes: new Dictionary<string, string>());
attributes: new Dictionary<string, object>());
output.PreContent.SetContent(expectedPreContent);
output.Content.SetContent(expectedContent);
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>
<body>
<div>
<a asp-controller="Product">Product Index</a>
<a asp-controller="Product" title="&lt;the title>">Product Index</a>
</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>
<a id="MvcTagHelperTestIndex">MvcTagHelperTest Index</a>

View File

@ -7,13 +7,13 @@
<title>Link</title>
<!-- 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 -->
<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 -->
<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 -->
<link asp-href-exclude="**/site2.css" rel="stylesheet" />
@ -37,8 +37,8 @@
<link href="~/site.css" asp-href-include="**/site.css" rel="stylesheet" />
<!-- Fallback to static href -->
<link href="~/site.min.css" rel="stylesheet" data-extra="test"
asp-fallback-href="~/site.css"
<link href="~/site.min.css?a=b&c=d" rel="stylesheet" data-extra="test" title='"the" title'
asp-fallback-href="~/site.css?a=b&c=d"
asp-fallback-test-class="hidden"
asp-fallback-test-property="visibility"
asp-fallback-test-value="hidden" />

View File

@ -11,15 +11,15 @@
</head>
<body>
<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.
</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.
</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
</script>

View File

@ -23,12 +23,14 @@ namespace TagHelpersWebSite.TagHelpers
public bool? MakePretty { get; set; }
public string Style { get; set; }
[Activate]
public ViewContext ViewContext { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
// Need to check if output.TagName == null in-case the ConditionTagHelper calls into SuppressOutput and
// Need to check if output.TagName == null in-case the ConditionTagHelper calls into SuppressOutput and
// therefore sets the TagName to null.
if (MakePretty.HasValue && !MakePretty.Value ||
output.TagName == null)
@ -40,9 +42,8 @@ namespace TagHelpersWebSite.TagHelpers
if (PrettyTagStyles.TryGetValue(output.TagName, out prettyStyle))
{
var style = string.Empty;
if (output.Attributes.TryGetValue("style", out style))
var style = Style ?? string.Empty;
if (!string.IsNullOrEmpty(style))
{
style += ";";
}