Add support for '*' at the end of required attributes.
- [TargetElement(Attributes ="prefix-*")] is now supported. - Added '*' to the list of invalid non whitespace characters in TagHelperDescriptorFactory. - Modified TagHelperDescriptorProvider to respect suffixed wildcards in TagHelperAttributeDescriptor.Attributes. - Added tests to validate wildcard required attributes #361
This commit is contained in:
parent
60c47c8874
commit
0e61b49881
|
|
@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
// https://github.com/aspnet/Razor/issues/165
|
// https://github.com/aspnet/Razor/issues/165
|
||||||
|
|
||||||
public static ICollection<char> InvalidNonWhitespaceNameCharacters { get; } = new HashSet<char>(
|
public static ICollection<char> InvalidNonWhitespaceNameCharacters { get; } = new HashSet<char>(
|
||||||
new[] { '@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'' });
|
new[] { '@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*' });
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="TagHelperDescriptor"/> from the given <paramref name="type"/>.
|
/// Creates a <see cref="TagHelperDescriptor"/> from the given <paramref name="type"/>.
|
||||||
|
|
@ -169,6 +169,25 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
bool targetingAttributes,
|
bool targetingAttributes,
|
||||||
ErrorSink errorSink)
|
ErrorSink errorSink)
|
||||||
{
|
{
|
||||||
|
if (!targetingAttributes &&
|
||||||
|
string.Equals(
|
||||||
|
name,
|
||||||
|
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
|
StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
// '*' as the entire name is OK in the TargetElement catch-all case.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (targetingAttributes &&
|
||||||
|
name.EndsWith(
|
||||||
|
TagHelperDescriptorProvider.RequiredAttributeWildcardSuffix,
|
||||||
|
StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
// A single '*' at the end of a required attribute is valid; everywhere else is invalid. Strip it from
|
||||||
|
// the end so we can validate the rest of the name.
|
||||||
|
name = name.Substring(0, name.Length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
var targetName = targetingAttributes ?
|
var targetName = targetingAttributes ?
|
||||||
Resources.TagHelperDescriptorFactory_Attribute :
|
Resources.TagHelperDescriptorFactory_Attribute :
|
||||||
Resources.TagHelperDescriptorFactory_Tag;
|
Resources.TagHelperDescriptorFactory_Tag;
|
||||||
|
|
|
||||||
|
|
@ -12,16 +12,16 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
|
||||||
public sealed class TargetElementAttribute : Attribute
|
public sealed class TargetElementAttribute : Attribute
|
||||||
{
|
{
|
||||||
public const string CatchAllDescriptorTarget = TagHelperDescriptorProvider.CatchAllDescriptorTarget;
|
public const string ElementCatchAllTarget = TagHelperDescriptorProvider.ElementCatchAllTarget;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Instantiates a new instance of the <see cref="TargetElementAttribute"/> class with <see cref="Tag"/>
|
/// Instantiates a new instance of the <see cref="TargetElementAttribute"/> class with <see cref="Tag"/>
|
||||||
/// set to <c>*</c>.
|
/// set to <c>*</c>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>A <c>*</c> <see cref="Tag"/> value indicates an <see cref="ITagHelper"/>
|
/// <remarks>A <c>*</c> <see cref="Tag"/> value indicates an <see cref="ITagHelper"/>
|
||||||
/// that targets all HTML elements with the required <see cref="Attributes"/>.</remarks>
|
/// that targets all HTML elements with the required <see cref="Attributes"/>.</remarks>
|
||||||
public TargetElementAttribute()
|
public TargetElementAttribute()
|
||||||
: this(CatchAllDescriptorTarget)
|
: this(ElementCatchAllTarget)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
/// <param name="tag">
|
/// <param name="tag">
|
||||||
/// The HTML tag the <see cref="ITagHelper"/> targets.
|
/// The HTML tag the <see cref="ITagHelper"/> targets.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <remarks>A <c>*</c> <paramref name="tag"/> value indicates an <see cref="ITagHelper"/>
|
/// <remarks>A <c>*</c> <paramref name="tag"/> value indicates an <see cref="ITagHelper"/>
|
||||||
/// that targets all HTML elements with the required <see cref="Attributes"/>.</remarks>
|
/// that targets all HTML elements with the required <see cref="Attributes"/>.</remarks>
|
||||||
public TargetElementAttribute(string tag)
|
public TargetElementAttribute(string tag)
|
||||||
{
|
{
|
||||||
|
|
@ -41,14 +41,17 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The HTML tag the <see cref="ITagHelper"/> targets.
|
/// The HTML tag the <see cref="ITagHelper"/> targets.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>A <c>*</c> <see cref="Tag"/> value indicates an <see cref="ITagHelper"/>
|
/// <remarks>A <c>*</c> <see cref="Tag"/> value indicates an <see cref="ITagHelper"/>
|
||||||
/// that targets all HTML elements with the required <see cref="Attributes"/>.</remarks>
|
/// that targets all HTML elements with the required <see cref="Attributes"/>.</remarks>
|
||||||
public string Tag { get; }
|
public string Tag { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A comma-separated <see cref="string"/> of attributes the HTML element must contain for the
|
/// A comma-separated <see cref="string"/> of attribute names the HTML element must contain for the
|
||||||
/// <see cref="ITagHelper"/> to run.
|
/// <see cref="ITagHelper"/> to run.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <c>*</c> at the end of an attribute name acts as a prefix match.
|
||||||
|
/// </remarks>
|
||||||
public string Attributes { get; set; }
|
public string Attributes { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -133,6 +133,9 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The list of required attribute names the tag helper expects to target an element.
|
/// The list of required attribute names the tag helper expects to target an element.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <c>*</c> at the end of an attribute name acts as a prefix match.
|
||||||
|
/// </remarks>
|
||||||
public IReadOnlyList<string> RequiredAttributes { get; private set; }
|
public IReadOnlyList<string> RequiredAttributes { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -12,7 +12,9 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TagHelperDescriptorProvider
|
public class TagHelperDescriptorProvider
|
||||||
{
|
{
|
||||||
public const string CatchAllDescriptorTarget = "*";
|
public const string ElementCatchAllTarget = "*";
|
||||||
|
|
||||||
|
public static readonly string RequiredAttributeWildcardSuffix = "*";
|
||||||
|
|
||||||
private IDictionary<string, HashSet<TagHelperDescriptor>> _registrations;
|
private IDictionary<string, HashSet<TagHelperDescriptor>> _registrations;
|
||||||
private string _tagHelperPrefix;
|
private string _tagHelperPrefix;
|
||||||
|
|
@ -55,7 +57,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
IEnumerable<TagHelperDescriptor> descriptors;
|
IEnumerable<TagHelperDescriptor> descriptors;
|
||||||
|
|
||||||
// Ensure there's a HashSet to use.
|
// Ensure there's a HashSet to use.
|
||||||
if (!_registrations.TryGetValue(CatchAllDescriptorTarget, out catchAllDescriptors))
|
if (!_registrations.TryGetValue(ElementCatchAllTarget, out catchAllDescriptors))
|
||||||
{
|
{
|
||||||
descriptors = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
|
descriptors = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
|
||||||
}
|
}
|
||||||
|
|
@ -66,9 +68,9 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
|
|
||||||
// If the requested tag name is the catch-all target, we shouldn't do the work of concatenating extra
|
// If the requested tag name is the catch-all target, we shouldn't do the work of concatenating extra
|
||||||
// descriptors.
|
// descriptors.
|
||||||
if (!tagName.Equals(CatchAllDescriptorTarget, StringComparison.OrdinalIgnoreCase))
|
if (!tagName.Equals(ElementCatchAllTarget, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
// If we have a tag name associated with the requested name, we need to combine matchingDescriptors
|
// If we have a tag name associated with the requested name, we need to combine matchingDescriptors
|
||||||
// with all the catch-all descriptors.
|
// with all the catch-all descriptors.
|
||||||
HashSet<TagHelperDescriptor> matchingDescriptors;
|
HashSet<TagHelperDescriptor> matchingDescriptors;
|
||||||
if (_registrations.TryGetValue(tagName, out matchingDescriptors))
|
if (_registrations.TryGetValue(tagName, out matchingDescriptors))
|
||||||
|
|
@ -91,7 +93,23 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
{
|
{
|
||||||
foreach (var requiredAttribute in descriptor.RequiredAttributes)
|
foreach (var requiredAttribute in descriptor.RequiredAttributes)
|
||||||
{
|
{
|
||||||
if (!attributeNames.Contains(requiredAttribute, StringComparer.OrdinalIgnoreCase))
|
// '*' at the end of a required attribute indicates: apply to attributes prefixed with the
|
||||||
|
// required attribute value.
|
||||||
|
if (requiredAttribute.EndsWith(
|
||||||
|
RequiredAttributeWildcardSuffix,
|
||||||
|
StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var prefix = requiredAttribute.Substring(0, requiredAttribute.Length - 1);
|
||||||
|
|
||||||
|
if (!attributeNames.Any(
|
||||||
|
attributeName =>
|
||||||
|
attributeName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!string.Equals(attributeName, prefix, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!attributeNames.Contains(requiredAttribute, StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -111,8 +129,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
var registrationKey =
|
var registrationKey =
|
||||||
string.Equals(descriptor.TagName, CatchAllDescriptorTarget, StringComparison.Ordinal) ?
|
string.Equals(descriptor.TagName, ElementCatchAllTarget, StringComparison.Ordinal) ?
|
||||||
CatchAllDescriptorTarget :
|
ElementCatchAllTarget :
|
||||||
descriptor.FullTagName;
|
descriptor.FullTagName;
|
||||||
|
|
||||||
// Ensure there's a HashSet to add the descriptor to.
|
// Ensure there's a HashSet to add the descriptor to.
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNet.Razor.TagHelpers;
|
using Microsoft.AspNet.Razor.TagHelpers;
|
||||||
|
|
@ -29,7 +30,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
new[]
|
new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor(
|
new TagHelperDescriptor(
|
||||||
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
|
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
typeof(AttributeTargetingTagHelper).FullName,
|
typeof(AttributeTargetingTagHelper).FullName,
|
||||||
AssemblyName,
|
AssemblyName,
|
||||||
attributes,
|
attributes,
|
||||||
|
|
@ -41,7 +42,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
new[]
|
new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor(
|
new TagHelperDescriptor(
|
||||||
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
|
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
typeof(MultiAttributeTargetingTagHelper).FullName,
|
typeof(MultiAttributeTargetingTagHelper).FullName,
|
||||||
AssemblyName,
|
AssemblyName,
|
||||||
attributes,
|
attributes,
|
||||||
|
|
@ -53,13 +54,13 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
new[]
|
new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor(
|
new TagHelperDescriptor(
|
||||||
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
|
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
typeof(MultiAttributeAttributeTargetingTagHelper).FullName,
|
typeof(MultiAttributeAttributeTargetingTagHelper).FullName,
|
||||||
AssemblyName,
|
AssemblyName,
|
||||||
attributes,
|
attributes,
|
||||||
requiredAttributes: new[] { "custom" }),
|
requiredAttributes: new[] { "custom" }),
|
||||||
new TagHelperDescriptor(
|
new TagHelperDescriptor(
|
||||||
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
|
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
typeof(MultiAttributeAttributeTargetingTagHelper).FullName,
|
typeof(MultiAttributeAttributeTargetingTagHelper).FullName,
|
||||||
AssemblyName,
|
AssemblyName,
|
||||||
attributes,
|
attributes,
|
||||||
|
|
@ -71,7 +72,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
new[]
|
new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor(
|
new TagHelperDescriptor(
|
||||||
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
|
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
typeof(InheritedAttributeTargetingTagHelper).FullName,
|
typeof(InheritedAttributeTargetingTagHelper).FullName,
|
||||||
AssemblyName,
|
AssemblyName,
|
||||||
attributes,
|
attributes,
|
||||||
|
|
@ -168,6 +169,30 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
requiredAttributes: new[] { "class", "style" }),
|
requiredAttributes: new[] { "class", "style" }),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
typeof(AttributeWildcardTargetingTagHelper),
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new TagHelperDescriptor(
|
||||||
|
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
|
typeof(AttributeWildcardTargetingTagHelper).FullName,
|
||||||
|
AssemblyName,
|
||||||
|
attributes,
|
||||||
|
requiredAttributes: new[] { "class*" })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
typeof(MultiAttributeWildcardTargetingTagHelper),
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new TagHelperDescriptor(
|
||||||
|
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
|
typeof(MultiAttributeWildcardTargetingTagHelper).FullName,
|
||||||
|
AssemblyName,
|
||||||
|
attributes,
|
||||||
|
requiredAttributes: new[] { "class*", "style*" })
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1381,51 +1406,65 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
onNameError("'he'lo'", "'"),
|
onNameError("'he'lo'", "'"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{ "hello*", new[] { onNameError("hello*", "*") } },
|
||||||
|
{ "*hello", new[] { onNameError("*hello", "*") } },
|
||||||
|
{ "he*lo", new[] { onNameError("he*lo", "*") } },
|
||||||
|
{
|
||||||
|
"*he*lo*",
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
onNameError("*he*lo*", "*"),
|
||||||
|
onNameError("*he*lo*", "*"),
|
||||||
|
onNameError("*he*lo*", "*"),
|
||||||
|
}
|
||||||
|
},
|
||||||
{ Environment.NewLine, new[] { whitespaceErrorString } },
|
{ Environment.NewLine, new[] { whitespaceErrorString } },
|
||||||
{ "\t", new[] { whitespaceErrorString } },
|
{ "\t", new[] { whitespaceErrorString } },
|
||||||
{ " \t ", new[] { whitespaceErrorString } },
|
{ " \t ", new[] { whitespaceErrorString } },
|
||||||
{ " ", new[] { whitespaceErrorString } },
|
{ " ", new[] { whitespaceErrorString } },
|
||||||
{ Environment.NewLine + " ", new[] { whitespaceErrorString } },
|
{ Environment.NewLine + " ", new[] { whitespaceErrorString } },
|
||||||
{
|
{
|
||||||
"! \t\r\n@/<>?[]=\"'",
|
"! \t\r\n@/<>?[]=\"'*",
|
||||||
new[]
|
new[]
|
||||||
{
|
{
|
||||||
onNameError("! \t\r\n@/<>?[]=\"'", "!"),
|
onNameError("! \t\r\n@/<>?[]=\"'*", "!"),
|
||||||
onNameError("! \t\r\n@/<>?[]=\"'", " "),
|
onNameError("! \t\r\n@/<>?[]=\"'*", " "),
|
||||||
onNameError("! \t\r\n@/<>?[]=\"'", "\t"),
|
onNameError("! \t\r\n@/<>?[]=\"'*", "\t"),
|
||||||
onNameError("! \t\r\n@/<>?[]=\"'", "\r"),
|
onNameError("! \t\r\n@/<>?[]=\"'*", "\r"),
|
||||||
onNameError("! \t\r\n@/<>?[]=\"'", "\n"),
|
onNameError("! \t\r\n@/<>?[]=\"'*", "\n"),
|
||||||
onNameError("! \t\r\n@/<>?[]=\"'", "@"),
|
onNameError("! \t\r\n@/<>?[]=\"'*", "@"),
|
||||||
onNameError("! \t\r\n@/<>?[]=\"'", "/"),
|
onNameError("! \t\r\n@/<>?[]=\"'*", "/"),
|
||||||
onNameError("! \t\r\n@/<>?[]=\"'", "<"),
|
onNameError("! \t\r\n@/<>?[]=\"'*", "<"),
|
||||||
onNameError("! \t\r\n@/<>?[]=\"'", ">"),
|
onNameError("! \t\r\n@/<>?[]=\"'*", ">"),
|
||||||
onNameError("! \t\r\n@/<>?[]=\"'", "?"),
|
onNameError("! \t\r\n@/<>?[]=\"'*", "?"),
|
||||||
onNameError("! \t\r\n@/<>?[]=\"'", "["),
|
onNameError("! \t\r\n@/<>?[]=\"'*", "["),
|
||||||
onNameError("! \t\r\n@/<>?[]=\"'", "]"),
|
onNameError("! \t\r\n@/<>?[]=\"'*", "]"),
|
||||||
onNameError("! \t\r\n@/<>?[]=\"'", "="),
|
onNameError("! \t\r\n@/<>?[]=\"'*", "="),
|
||||||
onNameError("! \t\r\n@/<>?[]=\"'", "\""),
|
onNameError("! \t\r\n@/<>?[]=\"'*", "\""),
|
||||||
onNameError("! \t\r\n@/<>?[]=\"'", "'"),
|
onNameError("! \t\r\n@/<>?[]=\"'*", "'"),
|
||||||
|
onNameError("! \t\r\n@/<>?[]=\"'*", "*"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"! \tv\ra\nl@i/d<>?[]=\"'",
|
"! \tv\ra\nl@i/d<>?[]=\"'*",
|
||||||
new[]
|
new[]
|
||||||
{
|
{
|
||||||
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "!"),
|
onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", "!"),
|
||||||
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", " "),
|
onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", " "),
|
||||||
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "\t"),
|
onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", "\t"),
|
||||||
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "\r"),
|
onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", "\r"),
|
||||||
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "\n"),
|
onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", "\n"),
|
||||||
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "@"),
|
onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", "@"),
|
||||||
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "/"),
|
onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", "/"),
|
||||||
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "<"),
|
onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", "<"),
|
||||||
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", ">"),
|
onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", ">"),
|
||||||
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "?"),
|
onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", "?"),
|
||||||
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "["),
|
onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", "["),
|
||||||
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "]"),
|
onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", "]"),
|
||||||
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "="),
|
onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", "="),
|
||||||
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "\""),
|
onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", "\""),
|
||||||
onNameError("! \tv\ra\nl@i/d<>?[]=\"'", "'"),
|
onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", "'"),
|
||||||
|
onNameError("! \tv\ra\nl@i/d<>?[]=\"'*", "*"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -1441,6 +1480,16 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TargetElement(Attributes = "class*")]
|
||||||
|
private class AttributeWildcardTargetingTagHelper : TagHelper
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[TargetElement(Attributes = "class*,style*")]
|
||||||
|
private class MultiAttributeWildcardTargetingTagHelper : TagHelper
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
[TargetElement(Attributes = "class")]
|
[TargetElement(Attributes = "class")]
|
||||||
private class AttributeTargetingTagHelper : TagHelper
|
private class AttributeTargetingTagHelper : TagHelper
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -25,20 +25,34 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
assemblyName: "SomeAssembly",
|
assemblyName: "SomeAssembly",
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: new[] { "class", "style" });
|
requiredAttributes: new[] { "class", "style" });
|
||||||
|
var inputWildcardPrefixDescriptor = new TagHelperDescriptor(
|
||||||
|
tagName: "input",
|
||||||
|
typeName: "InputWildCardAttribute",
|
||||||
|
assemblyName: "SomeAssembly",
|
||||||
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
|
requiredAttributes: new[] { "nodashprefix*" });
|
||||||
var catchAllDescriptor = new TagHelperDescriptor(
|
var catchAllDescriptor = new TagHelperDescriptor(
|
||||||
tagName: TagHelperDescriptorProvider.CatchAllDescriptorTarget,
|
tagName: TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
typeName: "CatchAllTagHelper",
|
typeName: "CatchAllTagHelper",
|
||||||
assemblyName: "SomeAssembly",
|
assemblyName: "SomeAssembly",
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: new[] { "class" });
|
requiredAttributes: new[] { "class" });
|
||||||
var catchAllDescriptor2 = new TagHelperDescriptor(
|
var catchAllDescriptor2 = new TagHelperDescriptor(
|
||||||
tagName: TagHelperDescriptorProvider.CatchAllDescriptorTarget,
|
tagName: TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
typeName: "CatchAllTagHelper",
|
typeName: "CatchAllTagHelper",
|
||||||
assemblyName: "SomeAssembly",
|
assemblyName: "SomeAssembly",
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: new[] { "custom", "class" });
|
requiredAttributes: new[] { "custom", "class" });
|
||||||
|
var catchAllWildcardPrefixDescriptor = new TagHelperDescriptor(
|
||||||
|
tagName: TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
|
typeName: "CatchAllWildCardAttribute",
|
||||||
|
assemblyName: "SomeAssembly",
|
||||||
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
|
requiredAttributes: new[] { "prefix-*" });
|
||||||
var defaultAvailableDescriptors =
|
var defaultAvailableDescriptors =
|
||||||
new[] { divDescriptor, inputDescriptor, catchAllDescriptor, catchAllDescriptor2 };
|
new[] { divDescriptor, inputDescriptor, catchAllDescriptor, catchAllDescriptor2 };
|
||||||
|
var defaultWildcardDescriptors =
|
||||||
|
new[] { inputWildcardPrefixDescriptor, catchAllWildcardPrefixDescriptor };
|
||||||
|
|
||||||
return new TheoryData<
|
return new TheoryData<
|
||||||
string, // tagName
|
string, // tagName
|
||||||
|
|
@ -73,29 +87,83 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
new[] { inputDescriptor, catchAllDescriptor }
|
new[] { inputDescriptor, catchAllDescriptor }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
|
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
new[] { "custom" },
|
new[] { "custom" },
|
||||||
defaultAvailableDescriptors,
|
defaultAvailableDescriptors,
|
||||||
Enumerable.Empty<TagHelperDescriptor>()
|
Enumerable.Empty<TagHelperDescriptor>()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
|
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
new[] { "class" },
|
new[] { "class" },
|
||||||
defaultAvailableDescriptors,
|
defaultAvailableDescriptors,
|
||||||
new[] { catchAllDescriptor }
|
new[] { catchAllDescriptor }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
|
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
new[] { "class", "style" },
|
new[] { "class", "style" },
|
||||||
defaultAvailableDescriptors,
|
defaultAvailableDescriptors,
|
||||||
new[] { catchAllDescriptor }
|
new[] { catchAllDescriptor }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
|
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
new[] { "class", "custom" },
|
new[] { "class", "custom" },
|
||||||
defaultAvailableDescriptors,
|
defaultAvailableDescriptors,
|
||||||
new[] { catchAllDescriptor, catchAllDescriptor2 }
|
new[] { catchAllDescriptor, catchAllDescriptor2 }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"input",
|
||||||
|
new[] { "nodashprefixA" },
|
||||||
|
defaultWildcardDescriptors,
|
||||||
|
new[] { inputWildcardPrefixDescriptor }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input",
|
||||||
|
new[] { "nodashprefix-ABC-DEF", "random" },
|
||||||
|
defaultWildcardDescriptors,
|
||||||
|
new[] { inputWildcardPrefixDescriptor }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input",
|
||||||
|
new[] { "prefixABCnodashprefix" },
|
||||||
|
defaultWildcardDescriptors,
|
||||||
|
Enumerable.Empty<TagHelperDescriptor>()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input",
|
||||||
|
new[] { "prefix-" },
|
||||||
|
defaultWildcardDescriptors,
|
||||||
|
Enumerable.Empty<TagHelperDescriptor>()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input",
|
||||||
|
new[] { "nodashprefix" },
|
||||||
|
defaultWildcardDescriptors,
|
||||||
|
Enumerable.Empty<TagHelperDescriptor>()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input",
|
||||||
|
new[] { "prefix-A" },
|
||||||
|
defaultWildcardDescriptors,
|
||||||
|
new[] { catchAllWildcardPrefixDescriptor }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input",
|
||||||
|
new[] { "prefix-ABC-DEF", "random" },
|
||||||
|
defaultWildcardDescriptors,
|
||||||
|
new[] { catchAllWildcardPrefixDescriptor }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input",
|
||||||
|
new[] { "prefix-abc", "nodashprefix-def" },
|
||||||
|
defaultWildcardDescriptors,
|
||||||
|
new[] { inputWildcardPrefixDescriptor, catchAllWildcardPrefixDescriptor }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input",
|
||||||
|
new[] { "class", "prefix-abc", "onclick", "nodashprefix-def", "style" },
|
||||||
|
defaultWildcardDescriptors,
|
||||||
|
new[] { inputWildcardPrefixDescriptor, catchAllWildcardPrefixDescriptor }
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -124,7 +192,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
// Arrange
|
// Arrange
|
||||||
var catchAllDescriptor = CreatePrefixedDescriptor(
|
var catchAllDescriptor = CreatePrefixedDescriptor(
|
||||||
"th",
|
"th",
|
||||||
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
|
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
"foo1");
|
"foo1");
|
||||||
var descriptors = new[] { catchAllDescriptor };
|
var descriptors = new[] { catchAllDescriptor };
|
||||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
var provider = new TagHelperDescriptorProvider(descriptors);
|
||||||
|
|
@ -159,7 +227,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
public void GetDescriptors_ReturnsCatchAllDescriptorsForPrefixedTags()
|
public void GetDescriptors_ReturnsCatchAllDescriptorsForPrefixedTags()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var catchAllDescriptor = CreatePrefixedDescriptor("th:", TagHelperDescriptorProvider.CatchAllDescriptorTarget, "foo1");
|
var catchAllDescriptor = CreatePrefixedDescriptor("th:", TagHelperDescriptorProvider.ElementCatchAllTarget, "foo1");
|
||||||
var descriptors = new[] { catchAllDescriptor };
|
var descriptors = new[] { catchAllDescriptor };
|
||||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
var provider = new TagHelperDescriptorProvider(descriptors);
|
||||||
|
|
||||||
|
|
@ -230,14 +298,14 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly");
|
var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly");
|
||||||
var spanDescriptor = new TagHelperDescriptor("span", "foo2", "SomeAssembly");
|
var spanDescriptor = new TagHelperDescriptor("span", "foo2", "SomeAssembly");
|
||||||
var catchAllDescriptor = new TagHelperDescriptor(
|
var catchAllDescriptor = new TagHelperDescriptor(
|
||||||
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
|
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
"foo3",
|
"foo3",
|
||||||
"SomeAssembly");
|
"SomeAssembly");
|
||||||
var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor };
|
var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor };
|
||||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
var provider = new TagHelperDescriptorProvider(descriptors);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var retrievedDescriptors = provider.GetDescriptors(TagHelperDescriptorProvider.CatchAllDescriptorTarget, attributeNames: Enumerable.Empty<string>());
|
var retrievedDescriptors = provider.GetDescriptors(TagHelperDescriptorProvider.ElementCatchAllTarget, attributeNames: Enumerable.Empty<string>());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var descriptor = Assert.Single(retrievedDescriptors);
|
var descriptor = Assert.Single(retrievedDescriptors);
|
||||||
|
|
@ -251,7 +319,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly");
|
var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly");
|
||||||
var spanDescriptor = new TagHelperDescriptor("span", "foo2", "SomeAssembly");
|
var spanDescriptor = new TagHelperDescriptor("span", "foo2", "SomeAssembly");
|
||||||
var catchAllDescriptor = new TagHelperDescriptor(
|
var catchAllDescriptor = new TagHelperDescriptor(
|
||||||
TagHelperDescriptorProvider.CatchAllDescriptorTarget,
|
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||||
"foo3",
|
"foo3",
|
||||||
"SomeAssembly");
|
"SomeAssembly");
|
||||||
var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor };
|
var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor };
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue