diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/RestrictChildrenAttribute.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/RestrictChildrenAttribute.cs index 3321e34471..e1eeb8365f 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/RestrictChildrenAttribute.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/RestrictChildrenAttribute.cs @@ -19,10 +19,10 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers /// Instantiates a new instance of the class. /// /// - /// The tag name of an element allowed as a child. Tag helpers must target the element. + /// The tag name of an element allowed as a child. /// /// - /// Additional names of elements allowed as children. Tag helpers must target all such elements. + /// Additional names of elements allowed as children. /// public RestrictChildrenAttribute(string childTag, params string[] childTags) { @@ -35,7 +35,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers } /// - /// Get the names of elements allowed as children. Tag helpers must target all such elements. + /// Get the names of elements allowed as children. /// public IEnumerable ChildTags { get; } } diff --git a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperParseTreeRewriter.cs b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperParseTreeRewriter.cs index 5c114bc789..80e4bad2be 100644 --- a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperParseTreeRewriter.cs +++ b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperParseTreeRewriter.cs @@ -81,10 +81,10 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal } else { - TrackTagBlock(childBlock); - // Non-TagHelper tag. - ValidateParentTagHelperAllowsPlainTag(childBlock, context.ErrorSink); + ValidateParentAllowsPlainTag(childBlock, context.ErrorSink); + + TrackTagBlock(childBlock); } // If we get to here it means that we're a normal html tag. No need to iterate any deeper into @@ -99,7 +99,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal } else { - ValidateParentTagHelperAllowsContent((Span)child, context.ErrorSink); + ValidateParentAllowsContent((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 @@ -197,7 +197,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal return false; } - ValidateParentTagHelperAllowsTagHelper(tagName, tagBlock, context.ErrorSink); + ValidateParentAllowsTagHelper(tagName, tagBlock, context.ErrorSink); ValidateDescriptors(descriptors, tagName, tagBlock, context.ErrorSink); // We're in a start TagHelper block. @@ -277,7 +277,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal // can't recover it means there was no corresponding tag helper start tag. if (TryRecoverTagHelper(tagName, tagBlock, context)) { - ValidateParentTagHelperAllowsTagHelper(tagName, tagBlock, context.ErrorSink); + ValidateParentAllowsTagHelper(tagName, tagBlock, context.ErrorSink); ValidateTagSyntax(tagName, tagBlock, context); // Successfully recovered, move onto the next element. @@ -335,10 +335,22 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal return attributeNames; } - private void ValidateParentTagHelperAllowsContent(Span child, ErrorSink errorSink) + private bool HasAllowedChildren() { - var allowedChildren = _currentTagHelperTracker?.AllowedChildren; - if (allowedChildren != null) + var currentTracker = _trackerStack.Count > 0 ? _trackerStack.Peek() : null; + + // If the current tracker is not a TagHelper then there's no AllowedChildren to enforce. + if (currentTracker == null || !currentTracker.IsTagHelper) + { + return false; + } + + return _currentTagHelperTracker.AllowedChildren != null; + } + + private void ValidateParentAllowsContent(Span child, ErrorSink errorSink) + { + if (HasAllowedChildren()) { var content = child.Content; if (!string.IsNullOrWhiteSpace(content)) @@ -347,6 +359,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal var whitespace = content.Substring(0, content.Length - trimmedStart.Length); var errorStart = SourceLocation.Advance(child.Start, whitespace); var length = trimmedStart.TrimEnd().Length; + var allowedChildren = _currentTagHelperTracker.AllowedChildren; var allowedChildrenString = string.Join(", ", allowedChildren); errorSink.OnError( errorStart, @@ -358,31 +371,32 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal } } - private void ValidateParentTagHelperAllowsPlainTag(Block tagBlock, ErrorSink errorSink) + private void ValidateParentAllowsPlainTag(Block tagBlock, ErrorSink errorSink) { - if (_currentTagHelperTracker?.AllowedChildren != null) + var tagName = GetTagName(tagBlock); + + // Treat partial tags such as ' 0 ? _trackerStack.Peek() : null; + if (HasAllowedChildren() && + !_currentTagHelperTracker.AllowedChildren.Contains(tagName, StringComparer.OrdinalIgnoreCase)) + { OnAllowedChildrenTagError(_currentTagHelperTracker, tagName, tagBlock, errorSink); } } - private void ValidateParentTagHelperAllowsTagHelper(string tagName, Block tagBlock, ErrorSink errorSink) + private void ValidateParentAllowsTagHelper(string tagName, Block tagBlock, ErrorSink errorSink) { - var currentlyAllowedChildren = _currentTagHelperTracker?.PrefixedAllowedChildren; - - if (currentlyAllowedChildren != null && - !currentlyAllowedChildren.Contains(tagName, StringComparer.OrdinalIgnoreCase)) + if (HasAllowedChildren() && + !_currentTagHelperTracker.PrefixedAllowedChildren.Contains(tagName, StringComparer.OrdinalIgnoreCase)) { OnAllowedChildrenTagError(_currentTagHelperTracker, tagName, tagBlock, errorSink); } diff --git a/src/Microsoft.AspNet.Razor/Properties/RazorResources.Designer.cs b/src/Microsoft.AspNet.Razor/Properties/RazorResources.Designer.cs index 38433e297a..317c929956 100644 --- a/src/Microsoft.AspNet.Razor/Properties/RazorResources.Designer.cs +++ b/src/Microsoft.AspNet.Razor/Properties/RazorResources.Designer.cs @@ -1531,7 +1531,7 @@ namespace Microsoft.AspNet.Razor } /// - /// The <{0}> tag is not allowed by parent <{1}> tag helper. Only child tag helper(s) targeting tag name(s) '{2}' are allowed. + /// The <{0}> tag is not allowed by parent <{1}> tag helper. Only child tags with name(s) '{2}' are allowed. /// internal static string TagHelperParseTreeRewriter_InvalidNestedTag { @@ -1539,7 +1539,7 @@ namespace Microsoft.AspNet.Razor } /// - /// The <{0}> tag is not allowed by parent <{1}> tag helper. Only child tag helper(s) targeting tag name(s) '{2}' are allowed. + /// The <{0}> tag is not allowed by parent <{1}> tag helper. Only child tags with name(s) '{2}' are allowed. /// internal static string FormatTagHelperParseTreeRewriter_InvalidNestedTag(object p0, object p1, object p2) { diff --git a/src/Microsoft.AspNet.Razor/RazorResources.resx b/src/Microsoft.AspNet.Razor/RazorResources.resx index 45c0228a83..a68e8bea39 100644 --- a/src/Microsoft.AspNet.Razor/RazorResources.resx +++ b/src/Microsoft.AspNet.Razor/RazorResources.resx @@ -423,7 +423,7 @@ Instead, wrap the contents of the block in "{{}}": The parent <{0}> tag helper does not allow non-tag content. Only child tag helper(s) targeting tag name(s) '{1}' are allowed. - The <{0}> tag is not allowed by parent <{1}> tag helper. Only child tag helper(s) targeting tag name(s) '{2}' are allowed. + The <{0}> tag is not allowed by parent <{1}> tag helper. Only child tags with name(s) '{2}' are allowed. The {0} directive is not supported. diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs index 78ea922aae..f4e58acff1 100644 --- a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs +++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs @@ -158,7 +158,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers } /// - /// Get the names of elements allowed as children. Tag helpers must target all such elements. + /// Get the names of elements allowed as children. /// /// null indicates all children are allowed. public IEnumerable AllowedChildren { get; set; } diff --git a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs index ec695afd9c..e2098c204c 100644 --- a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs @@ -661,18 +661,6 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers lineIndex: 1, columnIndex: 5, length: 6), - new RazorError( - RazorResources.FormatTagHelperParseTreeRewriter_CannotHaveNonTagContent("p", "br"), - absoluteIndex: 23 + newLineLength * 2, - lineIndex: 2, - columnIndex: 8, - length: 5), - new RazorError( - RazorResources.FormatTagHelperParseTreeRewriter_InvalidNestedTag("strong", "p", "br"), - absoluteIndex: 34 + newLineLength * 3, - lineIndex: 3, - columnIndex: 5, - length: 6), }; var expectedOutput = new MarkupBlock( new MarkupTagHelperBlock("p", @@ -711,13 +699,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers absoluteIndex: 18, lineIndex: 0, columnIndex: 18, - length: 6), - new RazorError( - RazorResources.FormatTagHelperParseTreeRewriter_InvalidNestedTag("strong", "strong", "br"), - absoluteIndex: 27, - lineIndex: 0, - columnIndex: 27, - length: 6), + length: 6) }; var expectedOutput = new MarkupBlock( new MarkupTagHelperBlock("strong", @@ -973,8 +955,6 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers nestedContentError("strong", "strong", 11, 6), nestedTagError("br", "strong", "strong", 18, 2), nestedTagError("em", "strong", "strong", 22, 2), - nestedContentError("strong", "strong", 25, 11), - nestedTagError("em", "strong", "strong", 38, 2), nestedTagError("br", "p", "strong", 51, 2), nestedContentError("p", "strong", 56, 9) } @@ -995,13 +975,6 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers factory.Markup("Something"))), new[] { - nestedTagError("custom", "p", "custom", 4, 6), - nestedContentError("p", "custom", 11, 6), - nestedTagError("br", "p", "custom", 18, 2), - nestedTagError("em", "p", "custom", 22, 2), - nestedContentError("p", "custom", 25, 11), - nestedTagError("em", "p", "custom", 38, 2), - nestedTagError("custom", "p", "custom", 43, 6), nestedTagError("br", "p", "custom", 51, 2), nestedContentError("p", "custom", 56, 9) } @@ -1027,7 +1000,26 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers { nestedContentError("p", "custom", 3, 1), } - } + }, + { + "


:Hello:

", + new[] { "custom", "strong" }, + new MarkupBlock( + new MarkupTagHelperBlock("p", + blockFactory.MarkupTagBlock(""), + new MarkupTagHelperBlock("br", TagMode.StartTagOnly), + factory.Markup(":"), + new MarkupTagHelperBlock("strong", + new MarkupTagHelperBlock("strong", + factory.Markup("Hello"))), + factory.Markup(":"), + blockFactory.MarkupTagBlock(""), + blockFactory.MarkupTagBlock(""))), + new[] + { + nestedContentError("strong", "custom, strong", 32, 5), + } + }, }; } }