Add ability for `TagHelper`s to specify restricted children.
- Specifying the `RestrictChildrenAttribute` enables `TagHelper`s to only allow other `TagHelper`s targeting specified names to be in the children. - Used the `null` value to indicate that `AllowedChildren` was not specified and therefore everything is allowed. This is the default. - Added name verification to name values to ensure that no bad values pass through the system. - Added parsing tests to validate a mixture of content generates errors when expected. #255
This commit is contained in:
parent
3361507c29
commit
465ff9713d
|
|
@ -394,6 +394,38 @@ namespace Microsoft.AspNet.Razor.Runtime
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperAttributeList_CannotAddAttribute"), p0, p1, p2, p3);
|
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperAttributeList_CannotAddAttribute"), p0, p1, p2, p3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.
|
||||||
|
/// </summary>
|
||||||
|
internal static string TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName
|
||||||
|
{
|
||||||
|
get { return GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName(object p0, object p1, object p2, object p3)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName"), p0, p1, p2, p3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid '{0}' tag name for tag helper '{1}'. Name cannot be null or whitespace.
|
||||||
|
/// </summary>
|
||||||
|
internal static string TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace
|
||||||
|
{
|
||||||
|
get { return GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid '{0}' tag name for tag helper '{1}'. Name cannot be null or whitespace.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace(object p0, object p1)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace"), p0, p1);
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -189,4 +189,10 @@
|
||||||
<data name="TagHelperAttributeList_CannotAddAttribute" xml:space="preserve">
|
<data name="TagHelperAttributeList_CannotAddAttribute" xml:space="preserve">
|
||||||
<value>Cannot add a {0} with inconsistent names. The {1} property '{2}' must match the location '{3}'.</value>
|
<value>Cannot add a {0} with inconsistent names. The {1} property '{2}' must match the location '{3}'.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName" xml:space="preserve">
|
||||||
|
<value>Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace" xml:space="preserve">
|
||||||
|
<value>Invalid '{0}' tag name for tag helper '{1}'. Name cannot be null or whitespace.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Restricts children of the <see cref="ITagHelper"/>'s element.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Combining this attribute with a <see cref="TargetElementAttribute"/> that specifies its
|
||||||
|
/// <see cref="TargetElementAttribute.TagStructure"/> as <see cref="TagStructure.WithoutEndTag"/> will result in
|
||||||
|
/// this attribute being ignored.</remarks>
|
||||||
|
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||||
|
public class RestrictChildrenAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Instantiates a new instance of the <see cref="RestrictChildrenAttribute"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tagName">
|
||||||
|
/// The tag name of an element allowed as a child. Tag helpers must target the element.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="tagNames">
|
||||||
|
/// Additional names of elements allowed as children. Tag helpers must target all such elements.
|
||||||
|
/// </param>
|
||||||
|
public RestrictChildrenAttribute(string tagName, params string[] tagNames)
|
||||||
|
{
|
||||||
|
var concatenatedNames = new string[1 + tagNames.Length];
|
||||||
|
concatenatedNames[0] = tagName;
|
||||||
|
|
||||||
|
tagNames.CopyTo(concatenatedNames, 1);
|
||||||
|
|
||||||
|
ChildTagNames = concatenatedNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the names of elements allowed as children. Tag helpers must target all such elements.
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<string> ChildTagNames { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -61,6 +61,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
|
|
||||||
var attributeDescriptors = GetAttributeDescriptors(type, designTime, errorSink);
|
var attributeDescriptors = GetAttributeDescriptors(type, designTime, errorSink);
|
||||||
var targetElementAttributes = GetValidTargetElementAttributes(typeInfo, errorSink);
|
var targetElementAttributes = GetValidTargetElementAttributes(typeInfo, errorSink);
|
||||||
|
var allowedChildren = GetAllowedChildren(typeInfo, errorSink);
|
||||||
|
|
||||||
var tagHelperDescriptors =
|
var tagHelperDescriptors =
|
||||||
BuildTagHelperDescriptors(
|
BuildTagHelperDescriptors(
|
||||||
|
|
@ -68,6 +69,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName,
|
assemblyName,
|
||||||
attributeDescriptors,
|
attributeDescriptors,
|
||||||
targetElementAttributes,
|
targetElementAttributes,
|
||||||
|
allowedChildren,
|
||||||
designTime);
|
designTime);
|
||||||
|
|
||||||
return tagHelperDescriptors.Distinct(TagHelperDescriptorComparer.Default);
|
return tagHelperDescriptors.Distinct(TagHelperDescriptorComparer.Default);
|
||||||
|
|
@ -87,6 +89,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
string assemblyName,
|
string assemblyName,
|
||||||
IEnumerable<TagHelperAttributeDescriptor> attributeDescriptors,
|
IEnumerable<TagHelperAttributeDescriptor> attributeDescriptors,
|
||||||
IEnumerable<TargetElementAttribute> targetElementAttributes,
|
IEnumerable<TargetElementAttribute> targetElementAttributes,
|
||||||
|
IEnumerable<string> allowedChildren,
|
||||||
bool designTime)
|
bool designTime)
|
||||||
{
|
{
|
||||||
TagHelperDesignTimeDescriptor typeDesignTimeDescriptor = null;
|
TagHelperDesignTimeDescriptor typeDesignTimeDescriptor = null;
|
||||||
|
|
@ -118,6 +121,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName,
|
assemblyName,
|
||||||
attributeDescriptors,
|
attributeDescriptors,
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: allowedChildren,
|
||||||
tagStructure: default(TagStructure),
|
tagStructure: default(TagStructure),
|
||||||
designTimeDescriptor: typeDesignTimeDescriptor)
|
designTimeDescriptor: typeDesignTimeDescriptor)
|
||||||
};
|
};
|
||||||
|
|
@ -130,14 +134,70 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName,
|
assemblyName,
|
||||||
attributeDescriptors,
|
attributeDescriptors,
|
||||||
attribute,
|
attribute,
|
||||||
|
allowedChildren,
|
||||||
typeDesignTimeDescriptor));
|
typeDesignTimeDescriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<string> GetAllowedChildren(TypeInfo typeInfo, ErrorSink errorSink)
|
||||||
|
{
|
||||||
|
var restrictChildrenAttribute = typeInfo.GetCustomAttribute<RestrictChildrenAttribute>(inherit: false);
|
||||||
|
if (restrictChildrenAttribute == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var allowedChildren = restrictChildrenAttribute.ChildTagNames;
|
||||||
|
var validAllowedChildren = GetValidAllowedChildren(allowedChildren, typeInfo.FullName, errorSink);
|
||||||
|
|
||||||
|
if (validAllowedChildren.Any())
|
||||||
|
{
|
||||||
|
return validAllowedChildren;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// All allowed children were invalid, return null to indicate that any child is acceptable.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal for unit testing
|
||||||
|
internal static IEnumerable<string> GetValidAllowedChildren(
|
||||||
|
IEnumerable<string> allowedChildren,
|
||||||
|
string tagHelperName,
|
||||||
|
ErrorSink errorSink)
|
||||||
|
{
|
||||||
|
var validAllowedChildren = new List<string>();
|
||||||
|
|
||||||
|
foreach (var name in allowedChildren)
|
||||||
|
{
|
||||||
|
var valid = TryValidateName(
|
||||||
|
name,
|
||||||
|
whitespaceError: Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace(
|
||||||
|
nameof(RestrictChildrenAttribute),
|
||||||
|
tagHelperName),
|
||||||
|
characterErrorBuilder: (invalidCharacter) =>
|
||||||
|
Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName(
|
||||||
|
nameof(RestrictChildrenAttribute),
|
||||||
|
name,
|
||||||
|
tagHelperName,
|
||||||
|
invalidCharacter),
|
||||||
|
errorSink: errorSink);
|
||||||
|
|
||||||
|
if (valid)
|
||||||
|
{
|
||||||
|
validAllowedChildren.Add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return validAllowedChildren;
|
||||||
|
}
|
||||||
|
|
||||||
private static TagHelperDescriptor BuildTagHelperDescriptor(
|
private static TagHelperDescriptor BuildTagHelperDescriptor(
|
||||||
string typeName,
|
string typeName,
|
||||||
string assemblyName,
|
string assemblyName,
|
||||||
IEnumerable<TagHelperAttributeDescriptor> attributeDescriptors,
|
IEnumerable<TagHelperAttributeDescriptor> attributeDescriptors,
|
||||||
TargetElementAttribute targetElementAttribute,
|
TargetElementAttribute targetElementAttribute,
|
||||||
|
IEnumerable<string> allowedChildren,
|
||||||
TagHelperDesignTimeDescriptor designTimeDescriptor)
|
TagHelperDesignTimeDescriptor designTimeDescriptor)
|
||||||
{
|
{
|
||||||
var requiredAttributes = GetCommaSeparatedValues(targetElementAttribute.Attributes);
|
var requiredAttributes = GetCommaSeparatedValues(targetElementAttribute.Attributes);
|
||||||
|
|
@ -148,6 +208,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName,
|
assemblyName,
|
||||||
attributeDescriptors,
|
attributeDescriptors,
|
||||||
requiredAttributes,
|
requiredAttributes,
|
||||||
|
allowedChildren,
|
||||||
targetElementAttribute.TagStructure,
|
targetElementAttribute.TagStructure,
|
||||||
designTimeDescriptor);
|
designTimeDescriptor);
|
||||||
}
|
}
|
||||||
|
|
@ -158,6 +219,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
string assemblyName,
|
string assemblyName,
|
||||||
IEnumerable<TagHelperAttributeDescriptor> attributeDescriptors,
|
IEnumerable<TagHelperAttributeDescriptor> attributeDescriptors,
|
||||||
IEnumerable<string> requiredAttributes,
|
IEnumerable<string> requiredAttributes,
|
||||||
|
IEnumerable<string> allowedChildren,
|
||||||
TagStructure tagStructure,
|
TagStructure tagStructure,
|
||||||
TagHelperDesignTimeDescriptor designTimeDescriptor)
|
TagHelperDesignTimeDescriptor designTimeDescriptor)
|
||||||
{
|
{
|
||||||
|
|
@ -168,6 +230,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName: assemblyName,
|
assemblyName: assemblyName,
|
||||||
attributes: attributeDescriptors,
|
attributes: attributeDescriptors,
|
||||||
requiredAttributes: requiredAttributes,
|
requiredAttributes: requiredAttributes,
|
||||||
|
allowedChildren: allowedChildren,
|
||||||
tagStructure: tagStructure,
|
tagStructure: tagStructure,
|
||||||
designTimeDescriptor: designTimeDescriptor);
|
designTimeDescriptor: designTimeDescriptor);
|
||||||
}
|
}
|
||||||
|
|
@ -230,13 +293,28 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
var targetName = targetingAttributes ?
|
var targetName = targetingAttributes ?
|
||||||
Resources.TagHelperDescriptorFactory_Attribute :
|
Resources.TagHelperDescriptorFactory_Attribute :
|
||||||
Resources.TagHelperDescriptorFactory_Tag;
|
Resources.TagHelperDescriptorFactory_Tag;
|
||||||
|
|
||||||
|
var validName = TryValidateName(
|
||||||
|
name,
|
||||||
|
whitespaceError: Resources.FormatTargetElementAttribute_NameCannotBeNullOrWhitespace(targetName),
|
||||||
|
characterErrorBuilder: (invalidCharacter) =>
|
||||||
|
Resources.FormatTargetElementAttribute_InvalidName(targetName.ToLower(), name, invalidCharacter),
|
||||||
|
errorSink: errorSink);
|
||||||
|
|
||||||
|
return validName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryValidateName(
|
||||||
|
string name,
|
||||||
|
string whitespaceError,
|
||||||
|
Func<char, string> characterErrorBuilder,
|
||||||
|
ErrorSink errorSink)
|
||||||
|
{
|
||||||
var validName = true;
|
var validName = true;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
{
|
{
|
||||||
errorSink.OnError(
|
errorSink.OnError(SourceLocation.Zero, whitespaceError);
|
||||||
SourceLocation.Zero,
|
|
||||||
Resources.FormatTargetElementAttribute_NameCannotBeNullOrWhitespace(targetName));
|
|
||||||
|
|
||||||
validName = false;
|
validName = false;
|
||||||
}
|
}
|
||||||
|
|
@ -247,12 +325,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
if (char.IsWhiteSpace(character) ||
|
if (char.IsWhiteSpace(character) ||
|
||||||
InvalidNonWhitespaceNameCharacters.Contains(character))
|
InvalidNonWhitespaceNameCharacters.Contains(character))
|
||||||
{
|
{
|
||||||
errorSink.OnError(
|
var error = characterErrorBuilder(character);
|
||||||
SourceLocation.Zero,
|
errorSink.OnError(SourceLocation.Zero, error);
|
||||||
Resources.FormatTargetElementAttribute_InvalidName(
|
|
||||||
targetName.ToLower(),
|
|
||||||
name,
|
|
||||||
character));
|
|
||||||
|
|
||||||
validName = false;
|
validName = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
descriptor.AssemblyName,
|
descriptor.AssemblyName,
|
||||||
descriptor.Attributes,
|
descriptor.Attributes,
|
||||||
descriptor.RequiredAttributes,
|
descriptor.RequiredAttributes,
|
||||||
|
descriptor.AllowedChildren,
|
||||||
descriptor.TagStructure,
|
descriptor.TagStructure,
|
||||||
descriptor.DesignTimeDescriptor));
|
descriptor.DesignTimeDescriptor));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,19 @@ namespace Microsoft.AspNet.Razor.Test.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.Equals(descriptorX, descriptorY) &&
|
return base.Equals(descriptorX, descriptorY) &&
|
||||||
// Normal comparer doesn't care about the case, required attribute order, attributes or prefixes.
|
// Normal comparer doesn't care about the case, required attribute order, allowed children order,
|
||||||
// In tests we do.
|
// attributes or prefixes. In tests we do.
|
||||||
string.Equals(descriptorX.TagName, descriptorY.TagName, StringComparison.Ordinal) &&
|
string.Equals(descriptorX.TagName, descriptorY.TagName, StringComparison.Ordinal) &&
|
||||||
string.Equals(descriptorX.Prefix, descriptorY.Prefix, StringComparison.Ordinal) &&
|
string.Equals(descriptorX.Prefix, descriptorY.Prefix, StringComparison.Ordinal) &&
|
||||||
Enumerable.SequenceEqual(
|
Enumerable.SequenceEqual(
|
||||||
descriptorX.RequiredAttributes,
|
descriptorX.RequiredAttributes,
|
||||||
descriptorY.RequiredAttributes,
|
descriptorY.RequiredAttributes,
|
||||||
StringComparer.Ordinal) &&
|
StringComparer.Ordinal) &&
|
||||||
|
(descriptorX.AllowedChildren == descriptorY.AllowedChildren ||
|
||||||
|
Enumerable.SequenceEqual(
|
||||||
|
descriptorX.AllowedChildren,
|
||||||
|
descriptorY.AllowedChildren,
|
||||||
|
StringComparer.Ordinal)) &&
|
||||||
descriptorX.Attributes.SequenceEqual(
|
descriptorX.Attributes.SequenceEqual(
|
||||||
descriptorY.Attributes,
|
descriptorY.Attributes,
|
||||||
TagHelperAttributeDescriptorComparer.Default) &&
|
TagHelperAttributeDescriptorComparer.Default) &&
|
||||||
|
|
@ -60,6 +65,14 @@ namespace Microsoft.AspNet.Razor.Test.Internal
|
||||||
hashCodeCombiner.Add(requiredAttribute, StringComparer.Ordinal);
|
hashCodeCombiner.Add(requiredAttribute, StringComparer.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (descriptor.AllowedChildren != null)
|
||||||
|
{
|
||||||
|
foreach (var child in descriptor.AllowedChildren)
|
||||||
|
{
|
||||||
|
hashCodeCombiner.Add(child, StringComparer.Ordinal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var attribute in descriptor.Attributes)
|
foreach (var attribute in descriptor.Attributes)
|
||||||
{
|
{
|
||||||
hashCodeCombiner.Add(TagHelperAttributeDescriptorComparer.Default.GetHashCode(attribute));
|
hashCodeCombiner.Add(TagHelperAttributeDescriptorComparer.Default.GetHashCode(attribute));
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||||
|
|
@ -16,6 +17,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
||||||
{
|
{
|
||||||
private TagHelperDescriptorProvider _provider;
|
private TagHelperDescriptorProvider _provider;
|
||||||
private Stack<TagHelperBlockTracker> _trackerStack;
|
private Stack<TagHelperBlockTracker> _trackerStack;
|
||||||
|
private TagHelperBlockTracker _currentTagHelperTracker;
|
||||||
private Stack<BlockBuilder> _blockStack;
|
private Stack<BlockBuilder> _blockStack;
|
||||||
private BlockBuilder _currentBlock;
|
private BlockBuilder _currentBlock;
|
||||||
|
|
||||||
|
|
@ -56,6 +58,11 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Non-TagHelper tag.
|
||||||
|
ValidateParentTagHelperAllowsPlainTag(childBlock, context.ErrorSink);
|
||||||
|
}
|
||||||
|
|
||||||
// If we get to here it means that we're a normal html tag. No need to iterate any deeper into
|
// If we get to here it means that we're a normal html tag. No need to iterate any deeper into
|
||||||
// the children of it because they wont be tag helpers.
|
// the children of it because they wont be tag helpers.
|
||||||
|
|
@ -67,6 +74,10 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ValidateParentTagHelperAllowsContent((Span)child, context.ErrorSink);
|
||||||
|
}
|
||||||
|
|
||||||
// At this point the child is a Span or Block with Type BlockType.Tag that doesn't happen to be a
|
// At this point the child is a Span or Block with Type BlockType.Tag that doesn't happen to be a
|
||||||
// tag helper.
|
// tag helper.
|
||||||
|
|
@ -108,7 +119,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tracker = _trackerStack.Count > 0 ? _trackerStack.Peek() : null;
|
var tracker = _currentTagHelperTracker;
|
||||||
var tagNameScope = tracker?.Builder.TagName ?? string.Empty;
|
var tagNameScope = tracker?.Builder.TagName ?? string.Empty;
|
||||||
|
|
||||||
if (!IsEndTag(tagBlock))
|
if (!IsEndTag(tagBlock))
|
||||||
|
|
@ -135,6 +146,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ValidateParentTagHelperAllowsTagHelper(tagName, tagBlock, context.ErrorSink);
|
||||||
ValidateDescriptors(descriptors, tagName, tagBlock, context.ErrorSink);
|
ValidateDescriptors(descriptors, tagName, tagBlock, context.ErrorSink);
|
||||||
|
|
||||||
// We're in a start TagHelper block.
|
// We're in a start TagHelper block.
|
||||||
|
|
@ -211,6 +223,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
||||||
// can't recover it means there was no corresponding tag helper start tag.
|
// can't recover it means there was no corresponding tag helper start tag.
|
||||||
if (TryRecoverTagHelper(tagName, tagBlock, context))
|
if (TryRecoverTagHelper(tagName, tagBlock, context))
|
||||||
{
|
{
|
||||||
|
ValidateParentTagHelperAllowsTagHelper(tagName, tagBlock, context.ErrorSink);
|
||||||
ValidateTagSyntax(tagName, tagBlock, context);
|
ValidateTagSyntax(tagName, tagBlock, context);
|
||||||
|
|
||||||
// Successfully recovered, move onto the next element.
|
// Successfully recovered, move onto the next element.
|
||||||
|
|
@ -267,6 +280,63 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
||||||
return attributeNames;
|
return attributeNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ValidateParentTagHelperAllowsContent(Span child, ErrorSink errorSink)
|
||||||
|
{
|
||||||
|
var allowedChildren = _currentTagHelperTracker?.AllowedChildren;
|
||||||
|
if (allowedChildren != null)
|
||||||
|
{
|
||||||
|
var content = child.Content;
|
||||||
|
if (!string.IsNullOrWhiteSpace(content))
|
||||||
|
{
|
||||||
|
var trimmedStart = content.TrimStart();
|
||||||
|
var whitespace = content.Substring(0, content.Length - trimmedStart.Length);
|
||||||
|
var errorStart = SourceLocation.Advance(child.Start, whitespace);
|
||||||
|
var length = trimmedStart.TrimEnd().Length;
|
||||||
|
var allowedChildrenString = string.Join(", ", allowedChildren);
|
||||||
|
errorSink.OnError(
|
||||||
|
errorStart,
|
||||||
|
RazorResources.FormatTagHelperParseTreeRewriter_CannotHaveNonTagContent(
|
||||||
|
_currentTagHelperTracker.Builder.TagName,
|
||||||
|
allowedChildrenString),
|
||||||
|
length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ValidateParentTagHelperAllowsPlainTag(Block tagBlock, ErrorSink errorSink)
|
||||||
|
{
|
||||||
|
if (_currentTagHelperTracker?.AllowedChildren != null)
|
||||||
|
{
|
||||||
|
OnAllowedChildrenTagError(_currentTagHelperTracker, tagBlock, errorSink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ValidateParentTagHelperAllowsTagHelper(string tagName, Block tagBlock, ErrorSink errorSink)
|
||||||
|
{
|
||||||
|
var currentlyAllowedChildren = _currentTagHelperTracker?.AllowedChildren;
|
||||||
|
|
||||||
|
if (currentlyAllowedChildren != null &&
|
||||||
|
!currentlyAllowedChildren.Contains(tagName, StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
OnAllowedChildrenTagError(_currentTagHelperTracker, tagBlock, errorSink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnAllowedChildrenTagError(
|
||||||
|
TagHelperBlockTracker tracker,
|
||||||
|
Block tagBlock,
|
||||||
|
ErrorSink errorSink)
|
||||||
|
{
|
||||||
|
var tagName = GetTagName(tagBlock);
|
||||||
|
var allowedChildrenString = string.Join(", ", tracker.AllowedChildren);
|
||||||
|
var errorMessage = RazorResources.FormatTagHelperParseTreeRewriter_InvalidNestedTag(
|
||||||
|
tagName,
|
||||||
|
tracker.Builder.TagName,
|
||||||
|
allowedChildrenString);
|
||||||
|
|
||||||
|
errorSink.OnError(tagBlock.Start, errorMessage, tagBlock.Length);
|
||||||
|
}
|
||||||
|
|
||||||
private static void ValidateDescriptors(
|
private static void ValidateDescriptors(
|
||||||
IEnumerable<TagHelperDescriptor> descriptors,
|
IEnumerable<TagHelperDescriptor> descriptors,
|
||||||
string tagName,
|
string tagName,
|
||||||
|
|
@ -361,6 +431,8 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
||||||
// for formatting.
|
// for formatting.
|
||||||
_trackerStack.Pop().Builder.SourceEndTag = endTag;
|
_trackerStack.Pop().Builder.SourceEndTag = endTag;
|
||||||
|
|
||||||
|
_currentTagHelperTracker = _trackerStack.Count > 0 ? _trackerStack.Peek() : null;
|
||||||
|
|
||||||
BuildCurrentlyTrackedBlock();
|
BuildCurrentlyTrackedBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -385,7 +457,8 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
||||||
|
|
||||||
private void TrackTagHelperBlock(TagHelperBlockBuilder builder)
|
private void TrackTagHelperBlock(TagHelperBlockBuilder builder)
|
||||||
{
|
{
|
||||||
_trackerStack.Push(new TagHelperBlockTracker(builder));
|
_currentTagHelperTracker = new TagHelperBlockTracker(builder);
|
||||||
|
_trackerStack.Push(_currentTagHelperTracker);
|
||||||
|
|
||||||
TrackBlock(builder);
|
TrackBlock(builder);
|
||||||
}
|
}
|
||||||
|
|
@ -478,11 +551,20 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
||||||
public TagHelperBlockTracker(TagHelperBlockBuilder builder)
|
public TagHelperBlockTracker(TagHelperBlockBuilder builder)
|
||||||
{
|
{
|
||||||
Builder = builder;
|
Builder = builder;
|
||||||
|
|
||||||
|
if (Builder.Descriptors.Any(descriptor => descriptor.AllowedChildren != null))
|
||||||
|
{
|
||||||
|
AllowedChildren = Builder.Descriptors
|
||||||
|
.SelectMany(descriptor => descriptor.AllowedChildren)
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TagHelperBlockBuilder Builder { get; }
|
public TagHelperBlockBuilder Builder { get; }
|
||||||
|
|
||||||
public uint OpenMatchingTags { get; set; }
|
public uint OpenMatchingTags { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<string> AllowedChildren { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1514,6 +1514,38 @@ namespace Microsoft.AspNet.Razor
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag"), p0, p1, p2);
|
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag"), p0, p1, p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parent &lt;{0}&gt; tag helper does not allow non-tag content. Only child tag helper(s) targeting tag name(s) '{1}' are allowed.
|
||||||
|
/// </summary>
|
||||||
|
internal static string TagHelperParseTreeRewriter_CannotHaveNonTagContent
|
||||||
|
{
|
||||||
|
get { return GetString("TagHelperParseTreeRewriter_CannotHaveNonTagContent"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parent &lt;{0}&gt; tag helper does not allow non-tag content. Only child tag helper(s) targeting tag name(s) '{1}' are allowed.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatTagHelperParseTreeRewriter_CannotHaveNonTagContent(object p0, object p1)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperParseTreeRewriter_CannotHaveNonTagContent"), p0, p1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The &lt;{0}&gt; tag is not allowed by parent &lt;{1}&gt; tag helper. Only child tag helper(s) targeting tag name(s) '{2}' are allowed.
|
||||||
|
/// </summary>
|
||||||
|
internal static string TagHelperParseTreeRewriter_InvalidNestedTag
|
||||||
|
{
|
||||||
|
get { return GetString("TagHelperParseTreeRewriter_InvalidNestedTag"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The &lt;{0}&gt; tag is not allowed by parent &lt;{1}&gt; tag helper. Only child tag helper(s) targeting tag name(s) '{2}' are allowed.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatTagHelperParseTreeRewriter_InvalidNestedTag(object p0, object p1, object p2)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperParseTreeRewriter_InvalidNestedTag"), p0, p1, p2);
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -419,4 +419,10 @@ Instead, wrap the contents of the block in "{{}}":
|
||||||
<data name="TagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag" xml:space="preserve">
|
<data name="TagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag" xml:space="preserve">
|
||||||
<value>Found an end tag (&lt;/{0}&gt;) for tag helper '{1}' with tag structure that disallows an end tag ('{2}').</value>
|
<value>Found an end tag (&lt;/{0}&gt;) for tag helper '{1}' with tag structure that disallows an end tag ('{2}').</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TagHelperParseTreeRewriter_CannotHaveNonTagContent" xml:space="preserve">
|
||||||
|
<value>The parent &lt;{0}&gt; tag helper does not allow non-tag content. Only child tag helper(s) targeting tag name(s) '{1}' are allowed.</value>
|
||||||
|
</data>
|
||||||
|
<data name="TagHelperParseTreeRewriter_InvalidNestedTag" xml:space="preserve">
|
||||||
|
<value>The &lt;{0}&gt; tag is not allowed by parent &lt;{1}&gt; tag helper. Only child tag helper(s) targeting tag name(s) '{2}' are allowed.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -61,6 +61,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
assemblyName: assemblyName,
|
assemblyName: assemblyName,
|
||||||
attributes: attributes,
|
attributes: attributes,
|
||||||
requiredAttributes: requiredAttributes,
|
requiredAttributes: requiredAttributes,
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.Unspecified,
|
tagStructure: TagStructure.Unspecified,
|
||||||
designTimeDescriptor: null)
|
designTimeDescriptor: null)
|
||||||
{
|
{
|
||||||
|
|
@ -84,6 +85,10 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
/// <param name="requiredAttributes">
|
/// <param name="requiredAttributes">
|
||||||
/// The attribute names required for the tag helper to target the HTML tag.
|
/// The attribute names required for the tag helper to target the HTML tag.
|
||||||
/// </param>
|
/// </param>
|
||||||
|
/// <param name="allowedChildren">
|
||||||
|
/// The names of elements allowed as children. Tag helpers must target all such elements.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="tagStructure">The expected tag structure.</param>
|
||||||
/// <param name="designTimeDescriptor">The <see cref="TagHelperDesignTimeDescriptor"/> that contains design
|
/// <param name="designTimeDescriptor">The <see cref="TagHelperDesignTimeDescriptor"/> that contains design
|
||||||
/// time information about the tag helper.</param>
|
/// time information about the tag helper.</param>
|
||||||
public TagHelperDescriptor(
|
public TagHelperDescriptor(
|
||||||
|
|
@ -93,6 +98,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
[NotNull] string assemblyName,
|
[NotNull] string assemblyName,
|
||||||
[NotNull] IEnumerable<TagHelperAttributeDescriptor> attributes,
|
[NotNull] IEnumerable<TagHelperAttributeDescriptor> attributes,
|
||||||
[NotNull] IEnumerable<string> requiredAttributes,
|
[NotNull] IEnumerable<string> requiredAttributes,
|
||||||
|
IEnumerable<string> allowedChildren,
|
||||||
TagStructure tagStructure,
|
TagStructure tagStructure,
|
||||||
TagHelperDesignTimeDescriptor designTimeDescriptor)
|
TagHelperDesignTimeDescriptor designTimeDescriptor)
|
||||||
{
|
{
|
||||||
|
|
@ -105,6 +111,11 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
RequiredAttributes = new List<string>(requiredAttributes);
|
RequiredAttributes = new List<string>(requiredAttributes);
|
||||||
TagStructure = tagStructure;
|
TagStructure = tagStructure;
|
||||||
DesignTimeDescriptor = designTimeDescriptor;
|
DesignTimeDescriptor = designTimeDescriptor;
|
||||||
|
|
||||||
|
if (allowedChildren != null)
|
||||||
|
{
|
||||||
|
AllowedChildren = new List<string>(allowedChildren);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -147,6 +158,12 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public IReadOnlyList<string> RequiredAttributes { get; }
|
public IReadOnlyList<string> RequiredAttributes { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the names of elements allowed as children. Tag helpers must target all such elements.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks><c>null</c> indicates all children are allowed.</remarks>
|
||||||
|
public IReadOnlyList<string> AllowedChildren { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The expected tag structure.
|
/// The expected tag structure.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Determines equality based on <see cref="TagHelperDescriptor.TypeName"/>,
|
/// Determines equality based on <see cref="TagHelperDescriptor.TypeName"/>,
|
||||||
/// <see cref="TagHelperDescriptor.AssemblyName"/>, <see cref="TagHelperDescriptor.TagName"/>,
|
/// <see cref="TagHelperDescriptor.AssemblyName"/>, <see cref="TagHelperDescriptor.TagName"/>,
|
||||||
/// <see cref="TagHelperDescriptor.RequiredAttributes"/>, and <see cref="TagHelperDescriptor.TagStructure"/>.
|
/// <see cref="TagHelperDescriptor.RequiredAttributes"/>, <see cref="TagHelperDescriptor.AllowedChildren"/>,
|
||||||
|
/// and <see cref="TagHelperDescriptor.TagStructure"/>.
|
||||||
/// Ignores <see cref="TagHelperDescriptor.DesignTimeDescriptor"/> because it can be inferred directly from
|
/// Ignores <see cref="TagHelperDescriptor.DesignTimeDescriptor"/> because it can be inferred directly from
|
||||||
/// <see cref="TagHelperDescriptor.TypeName"/> and <see cref="TagHelperDescriptor.AssemblyName"/>.
|
/// <see cref="TagHelperDescriptor.TypeName"/> and <see cref="TagHelperDescriptor.AssemblyName"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
|
@ -49,6 +50,13 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
descriptorX.RequiredAttributes.OrderBy(attribute => attribute, StringComparer.OrdinalIgnoreCase),
|
descriptorX.RequiredAttributes.OrderBy(attribute => attribute, StringComparer.OrdinalIgnoreCase),
|
||||||
descriptorY.RequiredAttributes.OrderBy(attribute => attribute, StringComparer.OrdinalIgnoreCase),
|
descriptorY.RequiredAttributes.OrderBy(attribute => attribute, StringComparer.OrdinalIgnoreCase),
|
||||||
StringComparer.OrdinalIgnoreCase) &&
|
StringComparer.OrdinalIgnoreCase) &&
|
||||||
|
(descriptorX.AllowedChildren == descriptorY.AllowedChildren ||
|
||||||
|
(descriptorX.AllowedChildren != null &&
|
||||||
|
descriptorY.AllowedChildren != null &&
|
||||||
|
Enumerable.SequenceEqual(
|
||||||
|
descriptorX.AllowedChildren.OrderBy(child => child, StringComparer.OrdinalIgnoreCase),
|
||||||
|
descriptorY.AllowedChildren.OrderBy(child => child, StringComparer.OrdinalIgnoreCase),
|
||||||
|
StringComparer.OrdinalIgnoreCase))) &&
|
||||||
descriptorX.TagStructure == descriptorY.TagStructure;
|
descriptorX.TagStructure == descriptorY.TagStructure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,6 +77,15 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
hashCodeCombiner.Add(attribute, StringComparer.OrdinalIgnoreCase);
|
hashCodeCombiner.Add(attribute, StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (descriptor.AllowedChildren != null)
|
||||||
|
{
|
||||||
|
var allowedChildren = descriptor.AllowedChildren.OrderBy(child => child, StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var child in allowedChildren)
|
||||||
|
{
|
||||||
|
hashCodeCombiner.Add(child, StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return hashCodeCombiner.CombinedHash;
|
return hashCodeCombiner.CombinedHash;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName: AssemblyName,
|
assemblyName: AssemblyName,
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.WithoutEndTag,
|
tagStructure: TagStructure.WithoutEndTag,
|
||||||
designTimeDescriptor: null)
|
designTimeDescriptor: null)
|
||||||
}
|
}
|
||||||
|
|
@ -50,6 +51,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName: AssemblyName,
|
assemblyName: AssemblyName,
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.WithoutEndTag,
|
tagStructure: TagStructure.WithoutEndTag,
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
new TagHelperDescriptor(
|
new TagHelperDescriptor(
|
||||||
|
|
@ -59,6 +61,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName: AssemblyName,
|
assemblyName: AssemblyName,
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.NormalOrSelfClosing,
|
tagStructure: TagStructure.NormalOrSelfClosing,
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
}
|
}
|
||||||
|
|
@ -74,6 +77,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName: AssemblyName,
|
assemblyName: AssemblyName,
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.WithoutEndTag,
|
tagStructure: TagStructure.WithoutEndTag,
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
new TagHelperDescriptor(
|
new TagHelperDescriptor(
|
||||||
|
|
@ -83,6 +87,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName: AssemblyName,
|
assemblyName: AssemblyName,
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.Unspecified,
|
tagStructure: TagStructure.Unspecified,
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
}
|
}
|
||||||
|
|
@ -137,6 +142,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName: AssemblyName,
|
assemblyName: AssemblyName,
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: default(TagStructure),
|
tagStructure: default(TagStructure),
|
||||||
designTimeDescriptor: new TagHelperDesignTimeDescriptor
|
designTimeDescriptor: new TagHelperDesignTimeDescriptor
|
||||||
{
|
{
|
||||||
|
|
@ -155,6 +161,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName: AssemblyName,
|
assemblyName: AssemblyName,
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: default(TagStructure),
|
tagStructure: default(TagStructure),
|
||||||
designTimeDescriptor: new TagHelperDesignTimeDescriptor
|
designTimeDescriptor: new TagHelperDesignTimeDescriptor
|
||||||
{
|
{
|
||||||
|
|
@ -167,6 +174,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName: AssemblyName,
|
assemblyName: AssemblyName,
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: default(TagStructure),
|
tagStructure: default(TagStructure),
|
||||||
designTimeDescriptor: new TagHelperDesignTimeDescriptor
|
designTimeDescriptor: new TagHelperDesignTimeDescriptor
|
||||||
{
|
{
|
||||||
|
|
@ -1734,6 +1742,42 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static TheoryData<string, string[]> InvalidRestrictChildrenNameData
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var nullOrWhiteSpaceError =
|
||||||
|
Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace(
|
||||||
|
nameof(RestrictChildrenAttribute),
|
||||||
|
"SomeTagHelper");
|
||||||
|
|
||||||
|
return GetInvalidNameOrPrefixData(
|
||||||
|
onNameError: (invalidInput, invalidCharacter) =>
|
||||||
|
Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName(
|
||||||
|
nameof(RestrictChildrenAttribute),
|
||||||
|
invalidInput,
|
||||||
|
"SomeTagHelper",
|
||||||
|
invalidCharacter),
|
||||||
|
whitespaceErrorString: nullOrWhiteSpaceError,
|
||||||
|
onDataError: null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(InvalidRestrictChildrenNameData))]
|
||||||
|
public void GetValidAllowedChildren_AddsExpectedErrors(string name, string[] expectedErrorMessages)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var errorSink = new ErrorSink();
|
||||||
|
var expectedErrors = expectedErrorMessages.Select(message => new RazorError(message, SourceLocation.Zero));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
TagHelperDescriptorFactory.GetValidAllowedChildren(new[] { name }, "SomeTagHelper", errorSink);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedErrors, errorSink.Errors);
|
||||||
|
}
|
||||||
|
|
||||||
private static TheoryData<string, string[]> GetInvalidNameOrPrefixData(
|
private static TheoryData<string, string[]> GetInvalidNameOrPrefixData(
|
||||||
Func<string, string, string> onNameError,
|
Func<string, string, string> onNameError,
|
||||||
string whitespaceErrorString,
|
string whitespaceErrorString,
|
||||||
|
|
@ -1973,6 +2017,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName: assemblyName,
|
assemblyName: assemblyName,
|
||||||
attributes: attributes ?? Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: attributes ?? Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: requiredAttributes ?? Enumerable.Empty<string>(),
|
requiredAttributes: requiredAttributes ?? Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: default(TagStructure),
|
tagStructure: default(TagStructure),
|
||||||
designTimeDescriptor: null);
|
designTimeDescriptor: null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName: AssemblyName,
|
assemblyName: AssemblyName,
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: default(TagStructure),
|
tagStructure: default(TagStructure),
|
||||||
designTimeDescriptor: null);
|
designTimeDescriptor: null);
|
||||||
}
|
}
|
||||||
|
|
@ -48,6 +49,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName: AssemblyName,
|
assemblyName: AssemblyName,
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: default(TagStructure),
|
tagStructure: default(TagStructure),
|
||||||
designTimeDescriptor: null);
|
designTimeDescriptor: null);
|
||||||
}
|
}
|
||||||
|
|
@ -609,6 +611,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName: assemblyB,
|
assemblyName: assemblyB,
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: default(TagStructure),
|
tagStructure: default(TagStructure),
|
||||||
designTimeDescriptor: null);
|
designTimeDescriptor: null);
|
||||||
|
|
||||||
|
|
@ -1045,6 +1048,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName: assemblyB,
|
assemblyName: assemblyB,
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: default(TagStructure),
|
tagStructure: default(TagStructure),
|
||||||
designTimeDescriptor: null);
|
designTimeDescriptor: null);
|
||||||
|
|
||||||
|
|
@ -1444,6 +1448,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
assemblyName,
|
assemblyName,
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: default(TagStructure),
|
tagStructure: default(TagStructure),
|
||||||
designTimeDescriptor: null);
|
designTimeDescriptor: null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1550,6 +1550,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
||||||
new TagHelperAttributeDescriptor("age", pAgePropertyInfo)
|
new TagHelperAttributeDescriptor("age", pAgePropertyInfo)
|
||||||
},
|
},
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.NormalOrSelfClosing,
|
tagStructure: TagStructure.NormalOrSelfClosing,
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
new TagHelperDescriptor(
|
new TagHelperDescriptor(
|
||||||
|
|
@ -1562,6 +1563,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
||||||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo)
|
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo)
|
||||||
},
|
},
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.WithoutEndTag,
|
tagStructure: TagStructure.WithoutEndTag,
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
new TagHelperDescriptor(
|
new TagHelperDescriptor(
|
||||||
|
|
@ -1575,6 +1577,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
||||||
new TagHelperAttributeDescriptor("checked", checkedPropertyInfo)
|
new TagHelperAttributeDescriptor("checked", checkedPropertyInfo)
|
||||||
},
|
},
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.Unspecified,
|
tagStructure: TagStructure.Unspecified,
|
||||||
designTimeDescriptor: null)
|
designTimeDescriptor: null)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
||||||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo)
|
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo)
|
||||||
},
|
},
|
||||||
requiredAttributes: new string[0],
|
requiredAttributes: new string[0],
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.WithoutEndTag,
|
tagStructure: TagStructure.WithoutEndTag,
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
new TagHelperDescriptor(
|
new TagHelperDescriptor(
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
assemblyName: "SomeAssembly",
|
assemblyName: "SomeAssembly",
|
||||||
attributes: new TagHelperAttributeDescriptor[0],
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.WithoutEndTag,
|
tagStructure: TagStructure.WithoutEndTag,
|
||||||
designTimeDescriptor: null)
|
designTimeDescriptor: null)
|
||||||
};
|
};
|
||||||
|
|
@ -189,6 +190,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
assemblyName: "SomeAssembly",
|
assemblyName: "SomeAssembly",
|
||||||
attributes: new TagHelperAttributeDescriptor[0],
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: structure1,
|
tagStructure: structure1,
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
new TagHelperDescriptor(
|
new TagHelperDescriptor(
|
||||||
|
|
@ -198,6 +200,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
assemblyName: "SomeAssembly",
|
assemblyName: "SomeAssembly",
|
||||||
attributes: new TagHelperAttributeDescriptor[0],
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: structure2,
|
tagStructure: structure2,
|
||||||
designTimeDescriptor: null)
|
designTimeDescriptor: null)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
assemblyName: "SomeAssembly",
|
assemblyName: "SomeAssembly",
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: default(TagStructure),
|
tagStructure: default(TagStructure),
|
||||||
designTimeDescriptor: null);
|
designTimeDescriptor: null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
assemblyName: "assembly name",
|
assemblyName: "assembly name",
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: new[] { "required attribute one", "required attribute two" },
|
requiredAttributes: new[] { "required attribute one", "required attribute two" },
|
||||||
|
allowedChildren: new[] { "allowed child one" },
|
||||||
tagStructure: TagStructure.Unspecified,
|
tagStructure: TagStructure.Unspecified,
|
||||||
designTimeDescriptor: new TagHelperDesignTimeDescriptor
|
designTimeDescriptor: new TagHelperDesignTimeDescriptor
|
||||||
{
|
{
|
||||||
|
|
@ -40,6 +41,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
$"\"{ nameof(TagHelperDescriptor.Attributes) }\":[]," +
|
$"\"{ nameof(TagHelperDescriptor.Attributes) }\":[]," +
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":" +
|
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":" +
|
||||||
"[\"required attribute one\",\"required attribute two\"]," +
|
"[\"required attribute one\",\"required attribute two\"]," +
|
||||||
|
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":[\"allowed child one\"]," +
|
||||||
$"\"{ nameof(TagHelperDescriptor.TagStructure) }\":0," +
|
$"\"{ nameof(TagHelperDescriptor.TagStructure) }\":0," +
|
||||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":{{"+
|
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":{{"+
|
||||||
$"\"{ nameof(TagHelperDesignTimeDescriptor.Summary) }\":\"usage summary\"," +
|
$"\"{ nameof(TagHelperDesignTimeDescriptor.Summary) }\":\"usage summary\"," +
|
||||||
|
|
@ -78,6 +80,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
},
|
},
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.NormalOrSelfClosing,
|
tagStructure: TagStructure.NormalOrSelfClosing,
|
||||||
designTimeDescriptor: null);
|
designTimeDescriptor: null);
|
||||||
var expectedSerializedDescriptor =
|
var expectedSerializedDescriptor =
|
||||||
|
|
@ -100,6 +103,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," +
|
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," +
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," +
|
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," +
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," +
|
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," +
|
||||||
|
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":null," +
|
||||||
$"\"{ nameof(TagHelperDescriptor.TagStructure) }\":1," +
|
$"\"{ nameof(TagHelperDescriptor.TagStructure) }\":1," +
|
||||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}";
|
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}";
|
||||||
|
|
||||||
|
|
@ -135,6 +139,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
},
|
},
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: new[] { "allowed child one", "allowed child two" },
|
||||||
tagStructure: default(TagStructure),
|
tagStructure: default(TagStructure),
|
||||||
designTimeDescriptor: null);
|
designTimeDescriptor: null);
|
||||||
var expectedSerializedDescriptor =
|
var expectedSerializedDescriptor =
|
||||||
|
|
@ -157,6 +162,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," +
|
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," +
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," +
|
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," +
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," +
|
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," +
|
||||||
|
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":[\"allowed child one\",\"allowed child two\"]," +
|
||||||
$"\"{ nameof(TagHelperDescriptor.TagStructure) }\":0," +
|
$"\"{ nameof(TagHelperDescriptor.TagStructure) }\":0," +
|
||||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}";
|
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}";
|
||||||
|
|
||||||
|
|
@ -180,6 +186,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
$"\"{nameof(TagHelperDescriptor.Attributes)}\":[]," +
|
$"\"{nameof(TagHelperDescriptor.Attributes)}\":[]," +
|
||||||
$"\"{nameof(TagHelperDescriptor.RequiredAttributes)}\":" +
|
$"\"{nameof(TagHelperDescriptor.RequiredAttributes)}\":" +
|
||||||
"[\"required attribute one\",\"required attribute two\"]," +
|
"[\"required attribute one\",\"required attribute two\"]," +
|
||||||
|
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":[\"allowed child one\",\"allowed child two\"]," +
|
||||||
$"\"{nameof(TagHelperDescriptor.TagStructure)}\":2," +
|
$"\"{nameof(TagHelperDescriptor.TagStructure)}\":2," +
|
||||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":{{" +
|
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":{{" +
|
||||||
$"\"{ nameof(TagHelperDesignTimeDescriptor.Summary) }\":\"usage summary\"," +
|
$"\"{ nameof(TagHelperDesignTimeDescriptor.Summary) }\":\"usage summary\"," +
|
||||||
|
|
@ -192,6 +199,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
assemblyName: "assembly name",
|
assemblyName: "assembly name",
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: new[] { "required attribute one", "required attribute two" },
|
requiredAttributes: new[] { "required attribute one", "required attribute two" },
|
||||||
|
allowedChildren: new[] { "allowed child one", "allowed child two" },
|
||||||
tagStructure: TagStructure.WithoutEndTag,
|
tagStructure: TagStructure.WithoutEndTag,
|
||||||
designTimeDescriptor: new TagHelperDesignTimeDescriptor
|
designTimeDescriptor: new TagHelperDesignTimeDescriptor
|
||||||
{
|
{
|
||||||
|
|
@ -242,6 +250,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," +
|
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," +
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," +
|
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," +
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," +
|
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," +
|
||||||
|
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":null," +
|
||||||
$"\"{nameof(TagHelperDescriptor.TagStructure)}\":0," +
|
$"\"{nameof(TagHelperDescriptor.TagStructure)}\":0," +
|
||||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}";
|
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}";
|
||||||
var expectedDescriptor = new TagHelperDescriptor(
|
var expectedDescriptor = new TagHelperDescriptor(
|
||||||
|
|
@ -265,6 +274,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
},
|
},
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.Unspecified,
|
tagStructure: TagStructure.Unspecified,
|
||||||
designTimeDescriptor: null);
|
designTimeDescriptor: null);
|
||||||
|
|
||||||
|
|
@ -331,6 +341,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," +
|
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," +
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," +
|
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," +
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," +
|
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," +
|
||||||
|
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":null," +
|
||||||
$"\"{nameof(TagHelperDescriptor.TagStructure)}\":1," +
|
$"\"{nameof(TagHelperDescriptor.TagStructure)}\":1," +
|
||||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}";
|
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}";
|
||||||
var expectedDescriptor = new TagHelperDescriptor(
|
var expectedDescriptor = new TagHelperDescriptor(
|
||||||
|
|
@ -354,6 +365,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
},
|
},
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.NormalOrSelfClosing,
|
tagStructure: TagStructure.NormalOrSelfClosing,
|
||||||
designTimeDescriptor: null);
|
designTimeDescriptor: null);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,450 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
||||||
{
|
{
|
||||||
public class TagHelperParseTreeRewriterTest : TagHelperRewritingTestBase
|
public class TagHelperParseTreeRewriterTest : TagHelperRewritingTestBase
|
||||||
{
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Rewrite_CanHandleInvalidChildrenWithWhitespace()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var factory = CreateDefaultSpanFactory();
|
||||||
|
var blockFactory = new BlockFactory(factory);
|
||||||
|
var documentContent = $"<p>{Environment.NewLine} <strong>{Environment.NewLine} Hello" +
|
||||||
|
$"{Environment.NewLine} </strong>{Environment.NewLine}</p>";
|
||||||
|
var expectedErrors = new[] {
|
||||||
|
new RazorError(
|
||||||
|
RazorResources.FormatTagHelperParseTreeRewriter_InvalidNestedTag("strong", "p", "br"),
|
||||||
|
absoluteIndex: 9,
|
||||||
|
lineIndex: 1,
|
||||||
|
columnIndex: 9,
|
||||||
|
length: 8),
|
||||||
|
new RazorError(
|
||||||
|
RazorResources.FormatTagHelperParseTreeRewriter_CannotHaveNonTagContent("p", "br"),
|
||||||
|
absoluteIndex: 27,
|
||||||
|
lineIndex: 2,
|
||||||
|
columnIndex: 27,
|
||||||
|
length: 5),
|
||||||
|
new RazorError(
|
||||||
|
RazorResources.FormatTagHelperParseTreeRewriter_InvalidNestedTag("strong", "p", "br"),
|
||||||
|
absoluteIndex: 38,
|
||||||
|
lineIndex: 3,
|
||||||
|
columnIndex: 38,
|
||||||
|
length: 9),
|
||||||
|
};
|
||||||
|
var expectedOutput = new MarkupBlock(
|
||||||
|
new MarkupTagHelperBlock("p",
|
||||||
|
factory.Markup(Environment.NewLine + " "),
|
||||||
|
blockFactory.MarkupTagBlock("<strong>"),
|
||||||
|
factory.Markup(Environment.NewLine + " Hello" + Environment.NewLine + " "),
|
||||||
|
blockFactory.MarkupTagBlock("</strong>"),
|
||||||
|
factory.Markup(Environment.NewLine)));
|
||||||
|
var descriptors = new TagHelperDescriptor[]
|
||||||
|
{
|
||||||
|
new TagHelperDescriptor(
|
||||||
|
prefix: string.Empty,
|
||||||
|
tagName: "p",
|
||||||
|
typeName: "PTagHelper",
|
||||||
|
assemblyName: "SomeAssembly",
|
||||||
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: new[] { "br" },
|
||||||
|
tagStructure: TagStructure.Unspecified,
|
||||||
|
designTimeDescriptor: null),
|
||||||
|
};
|
||||||
|
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Rewrite_RecoversWhenRequiredAttributeMismatchAndRestrictedChildren()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var factory = CreateDefaultSpanFactory();
|
||||||
|
var blockFactory = new BlockFactory(factory);
|
||||||
|
var documentContent = "<strong required><strong></strong></strong>";
|
||||||
|
|
||||||
|
var expectedErrors = new[] {
|
||||||
|
new RazorError(
|
||||||
|
RazorResources.FormatTagHelperParseTreeRewriter_InvalidNestedTag("strong", "strong", "br"),
|
||||||
|
absoluteIndex: 17,
|
||||||
|
lineIndex: 0,
|
||||||
|
columnIndex: 17,
|
||||||
|
length: 8),
|
||||||
|
new RazorError(
|
||||||
|
RazorResources.FormatTagHelperParseTreeRewriter_InvalidNestedTag("strong", "strong", "br"),
|
||||||
|
absoluteIndex: 25,
|
||||||
|
lineIndex: 0,
|
||||||
|
columnIndex: 25,
|
||||||
|
length: 9),
|
||||||
|
};
|
||||||
|
var expectedOutput = new MarkupBlock(
|
||||||
|
new MarkupTagHelperBlock("strong",
|
||||||
|
new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, SyntaxTreeNode>("required", null)
|
||||||
|
},
|
||||||
|
blockFactory.MarkupTagBlock("<strong>"),
|
||||||
|
blockFactory.MarkupTagBlock("</strong>")));
|
||||||
|
var descriptors = new TagHelperDescriptor[]
|
||||||
|
{
|
||||||
|
new TagHelperDescriptor(
|
||||||
|
prefix: string.Empty,
|
||||||
|
tagName: "strong",
|
||||||
|
typeName: "StrongTagHelper",
|
||||||
|
assemblyName: "SomeAssembly",
|
||||||
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
|
requiredAttributes: new[] { "required" },
|
||||||
|
allowedChildren: new[] { "br" },
|
||||||
|
tagStructure: TagStructure.Unspecified,
|
||||||
|
designTimeDescriptor: null),
|
||||||
|
};
|
||||||
|
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Rewrite_CanHandleMultipleTagHelpersWithAllowedChildren_OneNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var factory = CreateDefaultSpanFactory();
|
||||||
|
var documentContent = "<p><strong>Hello World</strong><br></p>";
|
||||||
|
var expectedOutput = new MarkupBlock(
|
||||||
|
new MarkupTagHelperBlock("p",
|
||||||
|
new MarkupTagHelperBlock("strong",
|
||||||
|
factory.Markup("Hello World")),
|
||||||
|
new MarkupTagHelperBlock("br", TagMode.StartTagOnly)));
|
||||||
|
var descriptors = new TagHelperDescriptor[]
|
||||||
|
{
|
||||||
|
new TagHelperDescriptor(
|
||||||
|
prefix: string.Empty,
|
||||||
|
tagName: "p",
|
||||||
|
typeName: "PTagHelper1",
|
||||||
|
assemblyName: "SomeAssembly",
|
||||||
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: new[] { "strong", "br" },
|
||||||
|
tagStructure: TagStructure.Unspecified,
|
||||||
|
designTimeDescriptor: null),
|
||||||
|
new TagHelperDescriptor(
|
||||||
|
prefix: string.Empty,
|
||||||
|
tagName: "p",
|
||||||
|
typeName: "PTagHelper2",
|
||||||
|
assemblyName: "SomeAssembly",
|
||||||
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
|
tagStructure: TagStructure.Unspecified,
|
||||||
|
designTimeDescriptor: null),
|
||||||
|
new TagHelperDescriptor(
|
||||||
|
prefix: string.Empty,
|
||||||
|
tagName: "strong",
|
||||||
|
typeName: "StrongTagHelper",
|
||||||
|
assemblyName: "SomeAssembly",
|
||||||
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
|
tagStructure: TagStructure.Unspecified,
|
||||||
|
designTimeDescriptor: null),
|
||||||
|
new TagHelperDescriptor(
|
||||||
|
prefix: string.Empty,
|
||||||
|
tagName: "br",
|
||||||
|
typeName: "BRTagHelper",
|
||||||
|
assemblyName: "SomeAssembly",
|
||||||
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
|
tagStructure: TagStructure.WithoutEndTag,
|
||||||
|
designTimeDescriptor: null),
|
||||||
|
};
|
||||||
|
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors: new RazorError[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Rewrite_CanHandleMultipleTagHelpersWithAllowedChildren()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var factory = CreateDefaultSpanFactory();
|
||||||
|
var documentContent = "<p><strong>Hello World</strong><br></p>";
|
||||||
|
var expectedOutput = new MarkupBlock(
|
||||||
|
new MarkupTagHelperBlock("p",
|
||||||
|
new MarkupTagHelperBlock("strong",
|
||||||
|
factory.Markup("Hello World")),
|
||||||
|
new MarkupTagHelperBlock("br", TagMode.StartTagOnly)));
|
||||||
|
var descriptors = new TagHelperDescriptor[]
|
||||||
|
{
|
||||||
|
new TagHelperDescriptor(
|
||||||
|
prefix: string.Empty,
|
||||||
|
tagName: "p",
|
||||||
|
typeName: "PTagHelper1",
|
||||||
|
assemblyName: "SomeAssembly",
|
||||||
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: new[] { "strong" },
|
||||||
|
tagStructure: TagStructure.Unspecified,
|
||||||
|
designTimeDescriptor: null),
|
||||||
|
new TagHelperDescriptor(
|
||||||
|
prefix: string.Empty,
|
||||||
|
tagName: "p",
|
||||||
|
typeName: "PTagHelper2",
|
||||||
|
assemblyName: "SomeAssembly",
|
||||||
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: new[] { "br" },
|
||||||
|
tagStructure: TagStructure.Unspecified,
|
||||||
|
designTimeDescriptor: null),
|
||||||
|
new TagHelperDescriptor(
|
||||||
|
prefix: string.Empty,
|
||||||
|
tagName: "strong",
|
||||||
|
typeName: "StrongTagHelper",
|
||||||
|
assemblyName: "SomeAssembly",
|
||||||
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
|
tagStructure: TagStructure.Unspecified,
|
||||||
|
designTimeDescriptor: null),
|
||||||
|
new TagHelperDescriptor(
|
||||||
|
prefix: string.Empty,
|
||||||
|
tagName: "br",
|
||||||
|
typeName: "BRTagHelper",
|
||||||
|
assemblyName: "SomeAssembly",
|
||||||
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
|
tagStructure: TagStructure.WithoutEndTag,
|
||||||
|
designTimeDescriptor: null),
|
||||||
|
};
|
||||||
|
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors: new RazorError[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TheoryData AllowedChildrenData
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var factory = CreateDefaultSpanFactory();
|
||||||
|
var blockFactory = new BlockFactory(factory);
|
||||||
|
Func<string, string, string, int, int, RazorError> nestedTagError =
|
||||||
|
(childName, parentName, allowed, location, length) => new RazorError(
|
||||||
|
RazorResources.FormatTagHelperParseTreeRewriter_InvalidNestedTag(
|
||||||
|
childName,
|
||||||
|
parentName,
|
||||||
|
allowed),
|
||||||
|
absoluteIndex: location,
|
||||||
|
lineIndex: 0,
|
||||||
|
columnIndex: location,
|
||||||
|
length: length);
|
||||||
|
Func<string, string, int, int, RazorError> nestedContentError =
|
||||||
|
(parentName, allowed, location, length) => new RazorError(
|
||||||
|
RazorResources.FormatTagHelperParseTreeRewriter_CannotHaveNonTagContent(parentName, allowed),
|
||||||
|
absoluteIndex: location,
|
||||||
|
lineIndex: 0,
|
||||||
|
columnIndex: location,
|
||||||
|
length: length);
|
||||||
|
|
||||||
|
return new TheoryData<string, IEnumerable<string>, MarkupBlock, RazorError[]>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"<p><br /></p>",
|
||||||
|
new[] { "br" },
|
||||||
|
new MarkupBlock(
|
||||||
|
new MarkupTagHelperBlock("p",
|
||||||
|
new MarkupTagHelperBlock("br", TagMode.SelfClosing))),
|
||||||
|
new RazorError[0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$"<p>{Environment.NewLine}<br />{Environment.NewLine}</p>",
|
||||||
|
new[] { "br" },
|
||||||
|
new MarkupBlock(
|
||||||
|
new MarkupTagHelperBlock("p",
|
||||||
|
factory.Markup(Environment.NewLine),
|
||||||
|
new MarkupTagHelperBlock("br", TagMode.SelfClosing),
|
||||||
|
factory.Markup(Environment.NewLine))),
|
||||||
|
new RazorError[0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<p><br></p>",
|
||||||
|
new[] { "strong" },
|
||||||
|
new MarkupBlock(
|
||||||
|
new MarkupTagHelperBlock("p",
|
||||||
|
new MarkupTagHelperBlock("br", TagMode.StartTagOnly))),
|
||||||
|
new[] { nestedTagError("br", "p", "strong", 3, 4) }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<p>Hello</p>",
|
||||||
|
new[] { "strong" },
|
||||||
|
new MarkupBlock(new MarkupTagHelperBlock("p", factory.Markup("Hello"))),
|
||||||
|
new[] { nestedContentError("p", "strong", 3, 5) }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<p><hr /></p>",
|
||||||
|
new[] { "br", "strong" },
|
||||||
|
new MarkupBlock(new MarkupTagHelperBlock("p", blockFactory.MarkupTagBlock("<hr />"))),
|
||||||
|
new[] { nestedTagError("hr", "p", "br, strong", 3, 6) }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<p><br>Hello</p>",
|
||||||
|
new[] { "strong" },
|
||||||
|
new MarkupBlock(
|
||||||
|
new MarkupTagHelperBlock("p",
|
||||||
|
new MarkupTagHelperBlock("br", TagMode.StartTagOnly),
|
||||||
|
factory.Markup("Hello"))),
|
||||||
|
new[] { nestedTagError("br", "p", "strong", 3, 4), nestedContentError("p", "strong", 7, 5) }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<p><strong>Title:</strong><br />Something</p>",
|
||||||
|
new[] { "strong" },
|
||||||
|
new MarkupBlock(
|
||||||
|
new MarkupTagHelperBlock("p",
|
||||||
|
new MarkupTagHelperBlock("strong", factory.Markup("Title:")),
|
||||||
|
new MarkupTagHelperBlock("br", TagMode.SelfClosing),
|
||||||
|
factory.Markup("Something"))),
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
nestedContentError("strong", "strong", 11, 6),
|
||||||
|
nestedTagError("br", "p", "strong", 26, 6),
|
||||||
|
nestedContentError("p", "strong", 32, 9),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<p><strong>Title:</strong><br />Something</p>",
|
||||||
|
new[] { "strong", "br" },
|
||||||
|
new MarkupBlock(
|
||||||
|
new MarkupTagHelperBlock("p",
|
||||||
|
new MarkupTagHelperBlock("strong", factory.Markup("Title:")),
|
||||||
|
new MarkupTagHelperBlock("br", TagMode.SelfClosing),
|
||||||
|
factory.Markup("Something"))),
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
nestedContentError("strong", "strong, br", 11, 6),
|
||||||
|
nestedContentError("p", "strong, br", 32, 9),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<p> <strong>Title:</strong> <br /> Something</p>",
|
||||||
|
new[] { "strong", "br" },
|
||||||
|
new MarkupBlock(
|
||||||
|
new MarkupTagHelperBlock("p",
|
||||||
|
factory.Markup(" "),
|
||||||
|
new MarkupTagHelperBlock("strong", factory.Markup("Title:")),
|
||||||
|
factory.Markup(" "),
|
||||||
|
new MarkupTagHelperBlock("br", TagMode.SelfClosing),
|
||||||
|
factory.Markup(" Something"))),
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
nestedContentError("strong", "strong, br", 13, 6),
|
||||||
|
nestedContentError("p", "strong, br", 38, 9),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<p><strong>Title:<br><em>A Very Cool</em></strong><br />Something</p>",
|
||||||
|
new[] { "strong" },
|
||||||
|
new MarkupBlock(
|
||||||
|
new MarkupTagHelperBlock("p",
|
||||||
|
new MarkupTagHelperBlock("strong",
|
||||||
|
factory.Markup("Title:"),
|
||||||
|
new MarkupTagHelperBlock("br", TagMode.StartTagOnly),
|
||||||
|
blockFactory.MarkupTagBlock("<em>"),
|
||||||
|
factory.Markup("A Very Cool"),
|
||||||
|
blockFactory.MarkupTagBlock("</em>")),
|
||||||
|
new MarkupTagHelperBlock("br", TagMode.SelfClosing),
|
||||||
|
factory.Markup("Something"))),
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
nestedContentError("strong", "strong", 11, 6),
|
||||||
|
nestedTagError("br", "strong", "strong", 17, 4),
|
||||||
|
nestedTagError("em", "strong", "strong", 21, 4),
|
||||||
|
nestedContentError("strong", "strong", 25, 11),
|
||||||
|
nestedTagError("em", "strong", "strong", 36, 5),
|
||||||
|
nestedTagError("br", "p", "strong", 50, 6),
|
||||||
|
nestedContentError("p", "strong", 56, 9)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<p><custom>Title:<br><em>A Very Cool</em></custom><br />Something</p>",
|
||||||
|
new[] { "custom" },
|
||||||
|
new MarkupBlock(
|
||||||
|
new MarkupTagHelperBlock("p",
|
||||||
|
blockFactory.MarkupTagBlock("<custom>"),
|
||||||
|
factory.Markup("Title:"),
|
||||||
|
new MarkupTagHelperBlock("br", TagMode.StartTagOnly),
|
||||||
|
blockFactory.MarkupTagBlock("<em>"),
|
||||||
|
factory.Markup("A Very Cool"),
|
||||||
|
blockFactory.MarkupTagBlock("</em>"),
|
||||||
|
blockFactory.MarkupTagBlock("</custom>"),
|
||||||
|
new MarkupTagHelperBlock("br", TagMode.SelfClosing),
|
||||||
|
factory.Markup("Something"))),
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
nestedTagError("custom", "p", "custom", 3, 8),
|
||||||
|
nestedContentError("p", "custom", 11, 6),
|
||||||
|
nestedTagError("br", "p", "custom", 17, 4),
|
||||||
|
nestedTagError("em", "p", "custom", 21, 4),
|
||||||
|
nestedContentError("p", "custom", 25, 11),
|
||||||
|
nestedTagError("em", "p", "custom", 36, 5),
|
||||||
|
nestedTagError("custom", "p", "custom", 41, 9),
|
||||||
|
nestedTagError("br", "p", "custom", 50, 6),
|
||||||
|
nestedContentError("p", "custom", 56, 9)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(AllowedChildrenData))]
|
||||||
|
public void Rewrite_UnderstandsAllowedChildren(
|
||||||
|
string documentContent,
|
||||||
|
IEnumerable<string> allowedChildren,
|
||||||
|
MarkupBlock expectedOutput,
|
||||||
|
RazorError[] expectedErrors)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptors = new TagHelperDescriptor[]
|
||||||
|
{
|
||||||
|
new TagHelperDescriptor(
|
||||||
|
prefix: string.Empty,
|
||||||
|
tagName: "p",
|
||||||
|
typeName: "PTagHelper",
|
||||||
|
assemblyName: "SomeAssembly",
|
||||||
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: allowedChildren,
|
||||||
|
tagStructure: TagStructure.Unspecified,
|
||||||
|
designTimeDescriptor: null),
|
||||||
|
new TagHelperDescriptor(
|
||||||
|
prefix: string.Empty,
|
||||||
|
tagName: "strong",
|
||||||
|
typeName: "StrongTagHelper",
|
||||||
|
assemblyName: "SomeAssembly",
|
||||||
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: allowedChildren,
|
||||||
|
tagStructure: TagStructure.Unspecified,
|
||||||
|
designTimeDescriptor: null),
|
||||||
|
new TagHelperDescriptor(
|
||||||
|
prefix: string.Empty,
|
||||||
|
tagName: "br",
|
||||||
|
typeName: "BRTagHelper",
|
||||||
|
assemblyName: "SomeAssembly",
|
||||||
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
|
tagStructure: TagStructure.WithoutEndTag,
|
||||||
|
designTimeDescriptor: null),
|
||||||
|
};
|
||||||
|
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Rewrite_CanHandleStartTagOnlyTagTagMode()
|
public void Rewrite_CanHandleStartTagOnlyTagTagMode()
|
||||||
{
|
{
|
||||||
|
|
@ -33,6 +477,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
||||||
assemblyName: "SomeAssembly",
|
assemblyName: "SomeAssembly",
|
||||||
attributes: new TagHelperAttributeDescriptor[0],
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.WithoutEndTag,
|
tagStructure: TagStructure.WithoutEndTag,
|
||||||
designTimeDescriptor: null)
|
designTimeDescriptor: null)
|
||||||
};
|
};
|
||||||
|
|
@ -68,6 +513,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
||||||
assemblyName: "SomeAssembly",
|
assemblyName: "SomeAssembly",
|
||||||
attributes: new TagHelperAttributeDescriptor[0],
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.WithoutEndTag,
|
tagStructure: TagStructure.WithoutEndTag,
|
||||||
designTimeDescriptor: null)
|
designTimeDescriptor: null)
|
||||||
};
|
};
|
||||||
|
|
@ -104,6 +550,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
||||||
assemblyName: "SomeAssembly",
|
assemblyName: "SomeAssembly",
|
||||||
attributes: new TagHelperAttributeDescriptor[0],
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.WithoutEndTag,
|
tagStructure: TagStructure.WithoutEndTag,
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
new TagHelperDescriptor(
|
new TagHelperDescriptor(
|
||||||
|
|
@ -113,6 +560,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
||||||
assemblyName: "SomeAssembly",
|
assemblyName: "SomeAssembly",
|
||||||
attributes: new TagHelperAttributeDescriptor[0],
|
attributes: new TagHelperAttributeDescriptor[0],
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: TagStructure.NormalOrSelfClosing,
|
tagStructure: TagStructure.NormalOrSelfClosing,
|
||||||
designTimeDescriptor: null)
|
designTimeDescriptor: null)
|
||||||
};
|
};
|
||||||
|
|
@ -1022,6 +1470,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
||||||
assemblyName: "SomeAssembly",
|
assemblyName: "SomeAssembly",
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: default(TagStructure),
|
tagStructure: default(TagStructure),
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
new TagHelperDescriptor(
|
new TagHelperDescriptor(
|
||||||
|
|
@ -1039,6 +1488,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
},
|
},
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: default(TagStructure),
|
tagStructure: default(TagStructure),
|
||||||
designTimeDescriptor: null)
|
designTimeDescriptor: null)
|
||||||
};
|
};
|
||||||
|
|
@ -1051,6 +1501,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
||||||
assemblyName: "SomeAssembly",
|
assemblyName: "SomeAssembly",
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: default(TagStructure),
|
tagStructure: default(TagStructure),
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
new TagHelperDescriptor(
|
new TagHelperDescriptor(
|
||||||
|
|
@ -1068,6 +1519,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
},
|
},
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: default(TagStructure),
|
tagStructure: default(TagStructure),
|
||||||
designTimeDescriptor: null)
|
designTimeDescriptor: null)
|
||||||
};
|
};
|
||||||
|
|
@ -1080,6 +1532,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
||||||
assemblyName: "SomeAssembly",
|
assemblyName: "SomeAssembly",
|
||||||
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
attributes: Enumerable.Empty<TagHelperAttributeDescriptor>(),
|
||||||
requiredAttributes: Enumerable.Empty<string>(),
|
requiredAttributes: Enumerable.Empty<string>(),
|
||||||
|
allowedChildren: null,
|
||||||
tagStructure: default(TagStructure),
|
tagStructure: default(TagStructure),
|
||||||
designTimeDescriptor: null),
|
designTimeDescriptor: null),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue