diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/BlockKindInternal.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/BlockKindInternal.cs index c515770ca7..9f9886164c 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/BlockKindInternal.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/BlockKindInternal.cs @@ -18,6 +18,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy // Special Comment = 8, - Tag = 9 + Tag = 9, + + HtmlComment = 10 } } diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs index 4fdee1322f..332023b775 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs @@ -207,6 +207,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy if (last != null) { Accept(last); + if (At(HtmlSymbolType.OpenAngle) && last.Type == HtmlSymbolType.Text) + { + Output(SpanKindInternal.Markup); + } } } @@ -494,26 +498,34 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy { if (CurrentSymbol.Type == HtmlSymbolType.DoubleHyphen) { - AcceptAndMoveNext(); - - Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any; - while (!EndOfFile) + using (Context.Builder.StartBlock(BlockKindInternal.HtmlComment)) { - SkipToAndParseCode(HtmlSymbolType.DoubleHyphen); - if (At(HtmlSymbolType.DoubleHyphen)) + AcceptAndMoveNext(); + + Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any; + while (!EndOfFile) { - AcceptWhile(HtmlSymbolType.DoubleHyphen); - - if (At(HtmlSymbolType.Text) && - string.Equals(CurrentSymbol.Content, "-", StringComparison.Ordinal)) + SkipToAndParseCode(HtmlSymbolType.DoubleHyphen); + if (At(HtmlSymbolType.DoubleHyphen)) { - AcceptAndMoveNext(); - } + AcceptWhile(HtmlSymbolType.DoubleHyphen); - if (At(HtmlSymbolType.CloseAngle)) - { - AcceptAndMoveNext(); - return true; + if (At(HtmlSymbolType.Text) && + string.Equals(CurrentSymbol.Content, "-", StringComparison.Ordinal)) + { + AcceptAndMoveNext(); + } + + if (At(HtmlSymbolType.CloseAngle)) + { + // This is the end of a comment block + Accept(this.CurrentSymbol); + Output(SpanKindInternal.Markup); + + NextToken(); + //AcceptAndMoveNext(); + return true; + } } } } @@ -1476,6 +1488,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy { AcceptAndMoveNext(); // Accept '<' BangTag(); + return; } diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperParseTreeRewriter.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperParseTreeRewriter.cs index 930b928949..6cac8b46ea 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperParseTreeRewriter.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperParseTreeRewriter.cs @@ -488,7 +488,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy private void ValidateParentAllowsContent(Span child, ErrorSink errorSink) { - if (HasAllowedChildren() && !IsComment(child)) + if (HasAllowedChildren() && !IsComment(child) && child.Kind != SpanKindInternal.Transition && child.Kind != SpanKindInternal.Code) { var content = child.Content; if (!string.IsNullOrWhiteSpace(content)) @@ -817,14 +817,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy return relevantSymbol.Type == HtmlSymbolType.ForwardSlash; } - private static bool IsComment(Span span) + internal static bool IsComment(Span span) { - bool isHtmlComment = span.Content?.StartsWith(""; + string commentOutput = ""; string expectedOutput = $"

{literal}{commentOutput}

"; var pTagHelperBuilder = TagHelperDescriptorBuilder @@ -1138,7 +1138,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy blockFactory.MarkupTagBlock(""), factory.Markup(literal), blockFactory.MarkupTagBlock(""), - factory.Markup(commentOutput))); + new HtmlCommentBlock(factory.Markup(commentOutput)))); // Act & Assert EvaluateData( @@ -1148,6 +1148,58 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Array.Empty()); } + [Fact] + public void Rewrite_FailsForContentWithCommentsAsChildren() + { + // Arrangestring documentContent, + Func nestedTagError = + (childName, parentName, allowed, location, length) => + RazorDiagnosticFactory.CreateTagHelper_InvalidNestedTag( + new SourceSpan(absoluteIndex: location, lineIndex: 0, characterIndex: location, length: length), childName, parentName, allowed); + Func nestedContentError = + (parentName, allowed, location, length) => + RazorDiagnosticFactory.CreateTagHelper_CannotHaveNonTagContent( + new SourceSpan(absoluteIndex: location, lineIndex: 0, characterIndex: location, length: length), parentName, allowed); + + IEnumerable allowedChildren = new List { "b" }; + string comment1 = "Hello"; + string literal = "asdf"; + string comment2 = "World"; + string expectedOutput = $"

{literal}

"; + + var pTagHelperBuilder = TagHelperDescriptorBuilder + .Create("PTagHelper", "SomeAssembly") + .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p")); + foreach (var childTag in allowedChildren) + { + pTagHelperBuilder.AllowChildTag(childTag); + } + + var descriptors = new TagHelperDescriptor[] + { + pTagHelperBuilder.Build() + }; + + var factory = new SpanFactory(); + var blockFactory = new BlockFactory(factory); + + var expectedMarkup = new MarkupBlock( + new MarkupTagHelperBlock("p", + new HtmlCommentBlock(factory.Markup($"")), + factory.Markup(literal), + new HtmlCommentBlock(factory.Markup($"")))); + + // Act & Assert + EvaluateData( + descriptors, + expectedOutput, + expectedMarkup, + new[] + { + nestedContentError("p", "b", 15, 4), + }); + } + [Fact] public void Rewrite_AllowsRazorCommentsAsChildren() { @@ -1193,6 +1245,54 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Array.Empty()); } + [Fact] + public void Rewrite_AllowsRazorMarkupInHtmlComment() + { + // Arrangestring documentContent, + IEnumerable allowedChildren = new List { "b" }; + string literal = "asdf"; + string part1 = ""; + string expectedOutput = $"

{literal}{part1}@{part2}{part3}

"; + + var pTagHelperBuilder = TagHelperDescriptorBuilder + .Create("PTagHelper", "SomeAssembly") + .TagMatchingRuleDescriptor(rule => rule.RequireTagName("p")); + foreach (var childTag in allowedChildren) + { + pTagHelperBuilder.AllowChildTag(childTag); + } + + var descriptors = new TagHelperDescriptor[] + { + pTagHelperBuilder.Build() + }; + + var factory = new SpanFactory(); + var blockFactory = new BlockFactory(factory); + + var expectedMarkup = new MarkupBlock( + new MarkupTagHelperBlock("p", + blockFactory.MarkupTagBlock(""), + factory.Markup(literal), + blockFactory.MarkupTagBlock(""), + new HtmlCommentBlock(factory.Markup(part1), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code(part2) + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), + factory.Markup(part3)))); + + // Act & Assert + EvaluateData( + descriptors, + expectedOutput, + expectedMarkup, + Array.Empty()); + } + [Fact] public void Rewrite_UnderstandsNullTagNameWithAllowedChildrenForCatchAll() { @@ -3993,7 +4093,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new MarkupBlock( new MarkupTagBlock( factory.Markup("")), - factory.Markup(""), + new HtmlCommentBlock( factory.Markup("")), new MarkupTagBlock( factory.Markup(""))) }; @@ -4003,13 +4103,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new MarkupBlock( new MarkupTagBlock( factory.Markup("")), - factory.Markup(""), + new HtmlCommentBlock(factory.Markup("")), new MarkupTagBlock( factory.Markup(""))) }; diff --git a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/BlockTypes.cs b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/BlockTypes.cs index c08e88d56f..56deb6b460 100644 --- a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/BlockTypes.cs +++ b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/BlockTypes.cs @@ -217,4 +217,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy { } } + + internal class HtmlCommentBlock : Block + { + private const BlockKindInternal ThisBlockKind = BlockKindInternal.HtmlComment; + + public HtmlCommentBlock(params SyntaxTreeNode[] children) + : base(ThisBlockKind, children, ParentChunkGenerator.Null) + { + } + } }