From 1f3f4b79dabcfdcaa7fc5031a878a5925ff56c24 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Tue, 20 Mar 2018 11:54:09 -0700 Subject: [PATCH 1/2] Revert "Make Razor HTML Comments aware (#2178)" This reverts commit b58986414e390339bfd5527ad24b1b2e52645613. --- .../Legacy/BlockKindInternal.cs | 5 +- .../Legacy/HtmlMarkupParser.cs | 179 ++---------- .../Legacy/HtmlSymbol.cs | 4 +- .../Legacy/TagHelperParseTreeRewriter.cs | 47 +-- .../Legacy/TokenizerBackedParser.cs | 69 +---- .../RazorParserFeatureFlags.cs | 13 +- .../BlockKind.cs | 6 +- .../Legacy/CSharpSectionTest.cs | 13 +- .../Legacy/HtmlBlockTest.cs | 39 +-- .../Legacy/HtmlDocumentTest.cs | 9 +- .../Legacy/HtmlMarkupParserTests.cs | 226 --------------- .../Legacy/HtmlTagsTest.cs | 4 +- .../Legacy/HtmlToCodeSwitchTest.cs | 17 +- .../Legacy/TagHelperBlockRewriterTest.cs | 15 +- .../Legacy/TagHelperParseTreeRewriterTest.cs | 272 ++---------------- .../Legacy/TokenizerLookaheadTest.cs | 105 +------ .../RazorParserFeatureFlagsTest.cs | 4 +- .../TagHelperParseTreeRewriterTests.cs | 26 -- ...lCommentWithQuote_Double_DesignTime.ir.txt | 5 +- ...HtmlCommentWithQuote_Double_Runtime.ir.txt | 5 +- ...lCommentWithQuote_Single_DesignTime.ir.txt | 5 +- ...HtmlCommentWithQuote_Single_Runtime.ir.txt | 5 +- .../Language/Legacy/BlockFactory.cs | 19 -- .../Language/Legacy/BlockTypes.cs | 12 +- 24 files changed, 108 insertions(+), 996 deletions(-) delete mode 100644 test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlMarkupParserTests.cs delete mode 100644 test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperParseTreeRewriterTests.cs diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/BlockKindInternal.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/BlockKindInternal.cs index 0b27d83f92..c515770ca7 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/BlockKindInternal.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/BlockKindInternal.cs @@ -18,7 +18,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy // Special Comment = 8, - Tag = 9, - HtmlComment = 10 + Tag = 9 } -} \ No newline at end of file +} diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs index 579cdc207c..4fdee1322f 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs @@ -12,9 +12,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy { private const string ScriptTagName = "script"; - private static readonly HtmlSymbol[] nonAllowedHtmlCommentEnding = new[] { HtmlSymbol.Hyphen, new HtmlSymbol("!", HtmlSymbolType.Bang), new HtmlSymbol("<", HtmlSymbolType.OpenAngle) }; - private static readonly HtmlSymbol[] singleHyphenArray = new[] { HtmlSymbol.Hyphen }; - private static readonly char[] ValidAfterTypeAttributeNameCharacters = { ' ', '\t', '\r', '\n', '\f', '=' }; private SourceLocation _lastTagStart = SourceLocation.Zero; private HtmlSymbol _bufferedOpenAngle; @@ -495,37 +492,33 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy if (AcceptAndMoveNext()) { - if (IsHtmlCommentAhead()) + if (CurrentSymbol.Type == HtmlSymbolType.DoubleHyphen) { - using (Context.Builder.StartBlock(BlockKindInternal.HtmlComment)) - { - // Accept the double-hyphen symbol at the beginning of the comment block. - AcceptAndMoveNext(); - Output(SpanKindInternal.Markup, AcceptedCharactersInternal.None); + AcceptAndMoveNext(); - Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.WhiteSpace; - while (!EndOfFile) + Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any; + while (!EndOfFile) + { + SkipToAndParseCode(HtmlSymbolType.DoubleHyphen); + if (At(HtmlSymbolType.DoubleHyphen)) { - SkipToAndParseCode(HtmlSymbolType.DoubleHyphen); - var lastDoubleHyphen = AcceptAllButLastDoubleHyphens(); + AcceptWhile(HtmlSymbolType.DoubleHyphen); + + if (At(HtmlSymbolType.Text) && + string.Equals(CurrentSymbol.Content, "-", StringComparison.Ordinal)) + { + AcceptAndMoveNext(); + } if (At(HtmlSymbolType.CloseAngle)) { - // Output the content in the comment block as a separate markup - Output(SpanKindInternal.Markup, AcceptedCharactersInternal.WhiteSpace); - - // This is the end of a comment block - Accept(lastDoubleHyphen); AcceptAndMoveNext(); - Output(SpanKindInternal.Markup, AcceptedCharactersInternal.None); return true; } - else if (lastDoubleHyphen != null) - { - Accept(lastDoubleHyphen); - } } } + + return false; } else if (CurrentSymbol.Type == HtmlSymbolType.LeftBracket) { @@ -544,138 +537,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy return false; } - protected HtmlSymbol AcceptAllButLastDoubleHyphens() - { - var lastDoubleHyphen = CurrentSymbol; - AcceptWhile(s => - { - if (NextIs(HtmlSymbolType.DoubleHyphen)) - { - lastDoubleHyphen = s; - return true; - } - - return false; - }); - - NextToken(); - - if (At(HtmlSymbolType.Text) && IsHyphen(CurrentSymbol)) - { - // Doing this here to maintain the order of symbols - if (!NextIs(HtmlSymbolType.CloseAngle)) - { - Accept(lastDoubleHyphen); - lastDoubleHyphen = null; - } - - AcceptAndMoveNext(); - } - - return lastDoubleHyphen; - } - - internal static bool IsHyphen(HtmlSymbol symbol) - { - return symbol.Equals(HtmlSymbol.Hyphen); - } - - protected bool IsHtmlCommentAhead() - { - /* - * From HTML5 Specification, available at http://www.w3.org/TR/html52/syntax.html#comments - * - * Comments must have the following format: - * 1. The string "" // As we will be treating this as a comment ending, there is no need to handle this case at all. - * 2.2.3 "--!>" - * 2.3 nor end with the string "" - * - * */ - - if (CurrentSymbol.Type != HtmlSymbolType.DoubleHyphen) - { - return false; - } - - // Check condition 2.1 - if (NextIs(HtmlSymbolType.CloseAngle) || NextIs(next => IsHyphen(next) && NextIs(HtmlSymbolType.CloseAngle))) - { - return false; - } - - // Check condition 2.2 - var isValidComment = false; - LookaheadUntil((symbol, prevSymbols) => - { - if (symbol.Type == HtmlSymbolType.DoubleHyphen) - { - if (NextIs(HtmlSymbolType.CloseAngle)) - { - // Check condition 2.3: We're at the end of a comment. Check to make sure the text ending is allowed. - isValidComment = !IsCommentContentEndingInvalid(prevSymbols); - return true; - } - else if (NextIs(ns => IsHyphen(ns) && NextIs(HtmlSymbolType.CloseAngle))) - { - // Check condition 2.3: we're at the end of a comment, which has an extra dash. - // Need to treat the dash as part of the content and check the ending. - // However, that case would have already been checked as part of check from 2.2.1 which - // would already fail this iteration and we wouldn't get here - isValidComment = true; - return true; - } - else if (NextIs(ns => ns.Type == HtmlSymbolType.Bang && NextIs(HtmlSymbolType.CloseAngle))) - { - // This is condition 2.2.3 - isValidComment = false; - return true; - } - } - else if (symbol.Type == HtmlSymbolType.OpenAngle) - { - // Checking condition 2.2.1 - if (NextIs(ns => ns.Type == HtmlSymbolType.Bang && NextIs(HtmlSymbolType.DoubleHyphen))) - { - isValidComment = false; - return true; - } - } - - return false; - }); - - return isValidComment; - } - - /// - /// Verifies, that the sequence doesn't end with the "<!-" HtmlSymbols. Note, the first symbol is an opening bracket symbol - /// - internal static bool IsCommentContentEndingInvalid(IEnumerable sequence) - { - var reversedSequence = sequence.Reverse(); - var index = 0; - foreach (var item in reversedSequence) - { - if (!item.Equals(nonAllowedHtmlCommentEnding[index++])) - { - return false; - } - - if (index == nonAllowedHtmlCommentEnding.Length) - { - return true; - } - } - - return false; - } - private bool CData() { if (CurrentSymbol.Type == HtmlSymbolType.Text && string.Equals(CurrentSymbol.Content, "cdata", StringComparison.OrdinalIgnoreCase)) @@ -1613,14 +1474,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy // Checking to see if we meet the conditions of a special '!' tag: { - internal static readonly HtmlSymbol Hyphen = new HtmlSymbol("-", HtmlSymbolType.Text); - public HtmlSymbol(string content, HtmlSymbolType type) : base(content, type, RazorDiagnostic.EmptyArray) { @@ -31,4 +29,4 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy } } } -} \ No newline at end of file +} diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperParseTreeRewriter.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperParseTreeRewriter.cs index d5d65c2985..2059522263 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperParseTreeRewriter.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperParseTreeRewriter.cs @@ -490,29 +490,20 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy { if (HasAllowedChildren()) { - var isDisallowedContent = true; - if (_featureFlags.AllowHtmlCommentsInTagHelpers) + var content = child.Content; + if (!string.IsNullOrWhiteSpace(content)) { - isDisallowedContent = !IsComment(child) && child.Kind != SpanKindInternal.Transition && child.Kind != SpanKindInternal.Code; - } - - if (isDisallowedContent) - { - var content = child.Content; - if (!string.IsNullOrWhiteSpace(content)) - { - var trimmedStart = content.TrimStart(); - var whitespace = content.Substring(0, content.Length - trimmedStart.Length); - var errorStart = SourceLocationTracker.Advance(child.Start, whitespace); - var length = trimmedStart.TrimEnd().Length; - var allowedChildren = _currentTagHelperTracker.AllowedChildren; - var allowedChildrenString = string.Join(", ", allowedChildren); - errorSink.OnError( - RazorDiagnosticFactory.CreateTagHelper_CannotHaveNonTagContent( - new SourceSpan(errorStart, length), - _currentTagHelperTracker.TagName, - allowedChildrenString)); - } + var trimmedStart = content.TrimStart(); + var whitespace = content.Substring(0, content.Length - trimmedStart.Length); + var errorStart = SourceLocationTracker.Advance(child.Start, whitespace); + var length = trimmedStart.TrimEnd().Length; + var allowedChildren = _currentTagHelperTracker.AllowedChildren; + var allowedChildrenString = string.Join(", ", allowedChildren); + errorSink.OnError( + RazorDiagnosticFactory.CreateTagHelper_CannotHaveNonTagContent( + new SourceSpan(errorStart, length), + _currentTagHelperTracker.TagName, + allowedChildrenString)); } } } @@ -826,18 +817,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy return relevantSymbol.Type == HtmlSymbolType.ForwardSlash; } - internal static bool IsComment(Span span) - { - Block currentBlock = span.Parent; - while (currentBlock != null && currentBlock.Type != BlockKindInternal.Comment && currentBlock.Type != BlockKindInternal.HtmlComment) - { - currentBlock = currentBlock.Parent; - } - - return currentBlock != null; - } - - private static void EnsureTagBlock(Block tagBlock) { Debug.Assert(tagBlock.Type == BlockKindInternal.Tag); diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs index 723f1cbf5a..6f564eb442 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs @@ -109,52 +109,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy return symbols[count]; } - /// - /// Looks forward until the specified condition is met. - /// - /// A predicate accepting the symbol being evaluated and the list of symbols which have been looped through. - /// true, if the condition was met. false - if the condition wasn't met and the last symbol has already been processed. - /// The list of previous symbols is passed in the reverse order. So the last processed element will be the first one in the list. - protected bool LookaheadUntil(Func, bool> condition) - { - if (condition == null) - { - throw new ArgumentNullException(nameof(condition)); - } - - var matchFound = false; - - var symbols = new List(); - symbols.Add(CurrentSymbol); - - while (true) - { - if (!NextToken()) - { - break; - } - - symbols.Add(CurrentSymbol); - if (condition(CurrentSymbol, symbols)) - { - matchFound = true; - break; - } - } - - // Restore Tokenizer's location to where it was pointing before the look-ahead. - for (var i = symbols.Count - 1; i >= 0; i--) - { - PutBack(symbols[i]); - } - - // The PutBacks above will set CurrentSymbol to null. EnsureCurrent will set our CurrentSymbol to the - // next symbol. - EnsureCurrent(); - - return matchFound; - } - protected internal bool NextToken() { PreviousSymbol = CurrentSymbol; @@ -300,21 +254,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy protected internal bool NextIs(Func condition) { var cur = CurrentSymbol; - if (NextToken()) - { - var result = condition(CurrentSymbol); - PutCurrentBack(); - PutBack(cur); - EnsureCurrent(); - return result; - } - else - { - PutBack(cur); - EnsureCurrent(); - } - - return false; + NextToken(); + var result = condition(CurrentSymbol); + PutCurrentBack(); + PutBack(cur); + EnsureCurrent(); + return result; } protected internal bool Was(TSymbolType type) @@ -676,4 +621,4 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Initialize(Span); } } -} \ No newline at end of file +} diff --git a/src/Microsoft.AspNetCore.Razor.Language/RazorParserFeatureFlags.cs b/src/Microsoft.AspNetCore.Razor.Language/RazorParserFeatureFlags.cs index 33324d00bd..0629eb9af8 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/RazorParserFeatureFlags.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/RazorParserFeatureFlags.cs @@ -8,33 +8,26 @@ namespace Microsoft.AspNetCore.Razor.Language public static RazorParserFeatureFlags Create(RazorLanguageVersion version) { var allowMinimizedBooleanTagHelperAttributes = false; - var allowHtmlCommentsInTagHelpers = false; if (version.CompareTo(RazorLanguageVersion.Version_2_1) >= 0) { // Added in 2.1 allowMinimizedBooleanTagHelperAttributes = true; - allowHtmlCommentsInTagHelpers = true; } - return new DefaultRazorParserFeatureFlags(allowMinimizedBooleanTagHelperAttributes, allowHtmlCommentsInTagHelpers); + return new DefaultRazorParserFeatureFlags(allowMinimizedBooleanTagHelperAttributes); } public abstract bool AllowMinimizedBooleanTagHelperAttributes { get; } - public abstract bool AllowHtmlCommentsInTagHelpers { get; } - private class DefaultRazorParserFeatureFlags : RazorParserFeatureFlags { - public DefaultRazorParserFeatureFlags(bool allowMinimizedBooleanTagHelperAttributes, bool allowHtmlCommentsInTagHelpers) + public DefaultRazorParserFeatureFlags(bool allowMinimizedBooleanTagHelperAttributes) { AllowMinimizedBooleanTagHelperAttributes = allowMinimizedBooleanTagHelperAttributes; - AllowHtmlCommentsInTagHelpers = allowHtmlCommentsInTagHelpers; } public override bool AllowMinimizedBooleanTagHelperAttributes { get; } - - public override bool AllowHtmlCommentsInTagHelpers { get; } } } -} \ No newline at end of file +} diff --git a/src/Microsoft.VisualStudio.Editor.Razor/BlockKind.cs b/src/Microsoft.VisualStudio.Editor.Razor/BlockKind.cs index 1a91221cbf..48ad2e4fb4 100644 --- a/src/Microsoft.VisualStudio.Editor.Razor/BlockKind.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/BlockKind.cs @@ -19,8 +19,6 @@ namespace Microsoft.VisualStudio.Editor.Razor // Special Comment, - Tag, - - HtmlComment + Tag } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/CSharpSectionTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/CSharpSectionTest.cs index 36f2aedb8d..3ae95b1994 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/CSharpSectionTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/CSharpSectionTest.cs @@ -367,7 +367,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy { // Act & Assert ParseDocumentTest( - "@section foo " + "@section foo " + Environment.NewLine + Environment.NewLine + Environment.NewLine @@ -606,8 +606,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( - BlockFactory.HtmlCommentBlock(" "), - Factory.EmptyHtml()), + Factory.Markup("")), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } @@ -631,8 +630,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( - BlockFactory.HtmlCommentBlock(" > \" '"), - Factory.EmptyHtml()), + Factory.Markup("")), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } @@ -657,8 +655,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Factory.Markup(Environment.NewLine), new MarkupTagBlock( Factory.Markup(" \" '"), - Factory.EmptyHtml()), + Factory.Markup("")), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } @@ -763,4 +760,4 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy ParseDocumentTest(input, new[] { SectionDirective.Directive }, (Block)expected); } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlBlockTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlBlockTest.cs index c70f4507f7..f54ba50d37 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlBlockTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlBlockTest.cs @@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Factory.Code(Environment.NewLine).AsStatement().AutoCompleteWith(null), new MarkupBlock( Factory.Markup(" "), - BlockFactory.HtmlCommentBlock(" Hello, I'm a comment that shouldn't break razor -"), + Factory.Markup("").Accepts(AcceptedCharactersInternal.None), Factory.Markup(Environment.NewLine).Accepts(AcceptedCharactersInternal.None)), Factory.EmptyCSharp().AsStatement(), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), @@ -333,13 +333,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy [Fact] public void ParseBlockSupportsCommentAsBlock() { - ParseBlockTest("", new MarkupBlock(BlockFactory.HtmlCommentBlock(" foo "))); - } - - [Fact] - public void ParseBlockSupportsCommentWithExtraDashAsBlock() - { - ParseBlockTest("", new MarkupBlock(BlockFactory.HtmlCommentBlock(" foo -"))); + SingleSpanBlockTest("", BlockKindInternal.Markup, SpanKindInternal.Markup, acceptedCharacters: AcceptedCharactersInternal.None); } [Fact] @@ -350,8 +344,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new MarkupTagBlock( Factory.Markup("").Accepts(AcceptedCharactersInternal.None)), Factory.Markup("bar"), - BlockFactory.HtmlCommentBlock(" zoop "), - Factory.Markup("baz").Accepts(AcceptedCharactersInternal.None), + Factory.Markup("").Accepts(AcceptedCharactersInternal.None), + Factory.Markup("baz"), new MarkupTagBlock( Factory.Markup("").Accepts(AcceptedCharactersInternal.None)))); } @@ -361,7 +355,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy get { var factory = new SpanFactory(); - var blockFactory = new BlockFactory(factory); + return new TheoryData { { @@ -369,7 +363,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new MarkupBlock( new MarkupTagBlock( factory.Markup("
").Accepts(AcceptedCharactersInternal.None)), - blockFactory.HtmlCommentBlock("- Hello World -"), + factory.Markup("").Accepts(AcceptedCharactersInternal.None), new MarkupTagBlock( factory.Markup("
").Accepts(AcceptedCharactersInternal.None))) }, @@ -378,7 +372,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new MarkupBlock( new MarkupTagBlock( factory.Markup("
").Accepts(AcceptedCharactersInternal.None)), - blockFactory.HtmlCommentBlock("-- Hello World --"), + factory.Markup("").Accepts(AcceptedCharactersInternal.None), new MarkupTagBlock( factory.Markup("
").Accepts(AcceptedCharactersInternal.None))) }, @@ -387,7 +381,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new MarkupBlock( new MarkupTagBlock( factory.Markup("
").Accepts(AcceptedCharactersInternal.None)), - blockFactory.HtmlCommentBlock("--- Hello World ---"), + factory.Markup("").Accepts(AcceptedCharactersInternal.None), new MarkupTagBlock( factory.Markup("
").Accepts(AcceptedCharactersInternal.None))) }, @@ -396,7 +390,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new MarkupBlock( new MarkupTagBlock( factory.Markup("
").Accepts(AcceptedCharactersInternal.None)), - blockFactory.HtmlCommentBlock("--- Hello < --- > World
---"), + factory.Markup("").Accepts(AcceptedCharactersInternal.None), new MarkupTagBlock( factory.Markup("").Accepts(AcceptedCharactersInternal.None))) }, @@ -416,22 +410,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy [Fact] public void ParseBlockProperlyBalancesCommentStartAndEndTags() { - ParseBlockTest("", new MarkupBlock(BlockFactory.HtmlCommentBlock(""))); + SingleSpanBlockTest("", BlockKindInternal.Markup, SpanKindInternal.Markup, acceptedCharacters: AcceptedCharactersInternal.None); } [Fact] public void ParseBlockTerminatesAtEOFWhenParsingComment() { - ParseBlockTest( - "", new MarkupBlock(BlockFactory.HtmlCommentBlock("--"))); + SingleSpanBlockTest("", BlockKindInternal.Markup, SpanKindInternal.Markup, acceptedCharacters: AcceptedCharactersInternal.None); } [Fact] @@ -441,8 +432,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new MarkupBlock( new MarkupTagBlock( Factory.Markup("").Accepts(AcceptedCharactersInternal.None)), - BlockFactory.HtmlCommentBlock("").Accepts(AcceptedCharactersInternal.None), + Factory.Markup("").Accepts(AcceptedCharactersInternal.None), + Factory.Markup("-->"), new MarkupTagBlock( Factory.Markup("").Accepts(AcceptedCharactersInternal.None)))); } @@ -654,4 +645,4 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Factory.Markup("").Accepts(AcceptedCharactersInternal.None)))); } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlDocumentTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlDocumentTest.cs index da9b30c878..0f06fe578a 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlDocumentTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlDocumentTest.cs @@ -214,12 +214,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy [Fact] public void ParseDocumentReturnsOneMarkupSegmentIfNoCodeBlocksEncountered() { - ParseDocumentTest("Foo BazBarBar"); - - // Act - var symbol = sut.AcceptAllButLastDoubleHyphens(); - - // Assert - Assert.Equal(doubleHyphenSymbol, symbol); - Assert.True(sut.At(HtmlSymbolType.CloseAngle)); - Assert.Equal(doubleHyphenSymbol, sut.PreviousSymbol); - } - - [Fact] - public void AcceptAllButLastDoubleHypens_ReturnsTheDoubleHyphenSymbolAfterAcceptingTheDash() - { - // Arrange - var sut = CreateTestParserForContent("--->"); - - // Act - var symbol = sut.AcceptAllButLastDoubleHyphens(); - - // Assert - Assert.Equal(doubleHyphenSymbol, symbol); - Assert.True(sut.At(HtmlSymbolType.CloseAngle)); - Assert.True(HtmlMarkupParser.IsHyphen(sut.PreviousSymbol)); - } - - [Fact] - public void IsHtmlCommentAhead_ReturnsTrueForEmptyCommentTag() - { - // Arrange - var sut = CreateTestParserForContent("---->"); - - // Act & Assert - Assert.True(sut.IsHtmlCommentAhead()); - } - - [Fact] - public void IsHtmlCommentAhead_ReturnsTrueForValidCommentTag() - { - // Arrange - var sut = CreateTestParserForContent("-- Some comment content in here -->"); - - // Act & Assert - Assert.True(sut.IsHtmlCommentAhead()); - } - - [Fact] - public void IsHtmlCommentAhead_ReturnsTrueForValidCommentTagWithExtraDashesAtClosingTag() - { - // Arrange - var sut = CreateTestParserForContent("-- Some comment content in here ----->"); - - // Act & Assert - Assert.True(sut.IsHtmlCommentAhead()); - } - - [Fact] - public void IsHtmlCommentAhead_ReturnsFalseForContentWithBadEndingAndExtraDash() - { - // Arrange - var sut = CreateTestParserForContent("-- Some comment content in here "); - - // Act & Assert - Assert.False(sut.IsHtmlCommentAhead()); - } - - [Fact] - public void IsHtmlCommentAhead_ReturnsTrueForValidCommentTagWithExtraInfoAfter() - { - // Arrange - var sut = CreateTestParserForContent("-- comment --> the first part is a valid comment without the Open angle and bang symbols"); - - // Act & Assert - Assert.True(sut.IsHtmlCommentAhead()); - } - - [Fact] - public void IsHtmlCommentAhead_ReturnsFalseForNotClosedComment() - { - // Arrange - var sut = CreateTestParserForContent("-- not closed comment"); - - // Act & Assert - Assert.False(sut.IsHtmlCommentAhead()); - } - - [Fact] - public void IsHtmlCommentAhead_ReturnsFalseForCommentWithoutLastClosingAngle() - { - // Arrange - var sut = CreateTestParserForContent("-- not closed comment--"); - - // Act & Assert - Assert.False(sut.IsHtmlCommentAhead()); - } - - [Fact] - public void IsHtmlCommentAhead_ReturnsTrueForCommentWithCodeInside() - { - // Arrange - var sut = CreateTestParserForContent("-- not closed @DateTime.Now comment-->"); - - // Act & Assert - Assert.True(sut.IsHtmlCommentAhead()); - } - - [Fact] - public void IsCommentContentEndingInvalid_ReturnsFalseForAllowedContent() - { - // Arrange - var expectedSymbol1 = new HtmlSymbol("a", HtmlSymbolType.Text); - var sequence = Enumerable.Range((int)'a', 26).Select(item => new HtmlSymbol(((char)item).ToString(), HtmlSymbolType.Text)); - - // Act & Assert - Assert.False(HtmlMarkupParser.IsCommentContentEndingInvalid(sequence)); - } - - [Fact] - public void IsCommentContentEndingInvalid_ReturnsTrueForDisallowedContent() - { - // Arrange - var expectedSymbol1 = new HtmlSymbol("a", HtmlSymbolType.Text); - var sequence = new[] { new HtmlSymbol("<", HtmlSymbolType.OpenAngle), new HtmlSymbol("!", HtmlSymbolType.Bang), new HtmlSymbol("-", HtmlSymbolType.Text) }; - - // Act & Assert - Assert.True(HtmlMarkupParser.IsCommentContentEndingInvalid(sequence)); - } - - [Fact] - public void IsCommentContentEndingInvalid_ReturnsFalseForEmptyContent() - { - // Arrange - var expectedSymbol1 = new HtmlSymbol("a", HtmlSymbolType.Text); - var sequence = Array.Empty(); - - // Act & Assert - Assert.False(HtmlMarkupParser.IsCommentContentEndingInvalid(sequence)); - } - - private class TestHtmlMarkupParser : HtmlMarkupParser - { - public new HtmlSymbol PreviousSymbol - { - get => base.PreviousSymbol; - } - - public new bool IsHtmlCommentAhead() - { - return base.IsHtmlCommentAhead(); - } - - public TestHtmlMarkupParser(ParserContext context) : base(context) - { - this.EnsureCurrent(); - } - - public new HtmlSymbol AcceptAllButLastDoubleHyphens() - { - return base.AcceptAllButLastDoubleHyphens(); - } - - public override void BuildSpan(SpanBuilder span, SourceLocation start, string content) - { - base.BuildSpan(span, start, content); - } - } - - private static TestHtmlMarkupParser CreateTestParserForContent(string content) - { - var source = TestRazorSourceDocument.Create(content); - var options = RazorParserOptions.CreateDefault(); - var context = new ParserContext(source, options); - - return new TestHtmlMarkupParser(context); - } - } -} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlTagsTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlTagsTest.cs index 9a956758f2..0a86c7a06b 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlTagsTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlTagsTest.cs @@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy { ParseBlockTest(" Bar", new MarkupBlock( - BlockFactory.HtmlCommentBlock("Foo"), + Factory.Markup("").Accepts(AcceptedCharactersInternal.None), Factory.Markup(" ").Accepts(AcceptedCharactersInternal.None))); } @@ -203,4 +203,4 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy BlockFactory.MarkupTagBlock("").Accepts(AcceptedCharactersInternal.None)), - BlockFactory.HtmlCommentBlock(Factory, f => new SyntaxTreeNode[] { - f.Markup(" ").Accepts(AcceptedCharactersInternal.WhiteSpace), - new ExpressionBlock( - f.CodeTransition(), - f.Code("foo") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - f.Markup(" ").Accepts(AcceptedCharactersInternal.WhiteSpace) }), + Factory.Markup("").Accepts(AcceptedCharactersInternal.None), new MarkupTagBlock( Factory.Markup("").Accepts(AcceptedCharactersInternal.None)))); } @@ -452,4 +451,4 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Factory.EmptyHtml())); } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperBlockRewriterTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperBlockRewriterTest.cs index a970b69539..9b2adc4d07 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperBlockRewriterTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperBlockRewriterTest.cs @@ -2950,7 +2950,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy intType), RazorDiagnosticFactory.CreateParsing_TagHelperIndexerAttributeNameMustIncludeKey( new SourceSpan(7, 0, 7, 11), - "int-prefix-", + "int-prefix-", "input"), } }, @@ -2973,7 +2973,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy stringType), RazorDiagnosticFactory.CreateParsing_TagHelperIndexerAttributeNameMustIncludeKey( new SourceSpan(7, 0, 7, 14), - "string-prefix-", + "string-prefix-", "input"), } }, @@ -3638,7 +3638,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy RazorDiagnosticFactory.CreateTagHelper_EmptyBoundAttribute( new SourceSpan(7, 0, 7, 21), "bound-required-string", - "input", + "input", stringType), } }, @@ -3962,7 +3962,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy .Build(), }; - var featureFlags = new TestRazorParserFeatureFlags(allowMinimizedBooleanTagHelperAttributes: false, allowHtmlCommentsInTagHelper: false); + var featureFlags = new TestRazorParserFeatureFlags(allowMinimizedBooleanTagHelperAttributes: false); var expectedOutput = new MarkupBlock( new MarkupTagHelperBlock( @@ -3994,15 +3994,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy private class TestRazorParserFeatureFlags : RazorParserFeatureFlags { - public TestRazorParserFeatureFlags(bool allowMinimizedBooleanTagHelperAttributes, bool allowHtmlCommentsInTagHelper) + public TestRazorParserFeatureFlags(bool allowMinimizedBooleanTagHelperAttributes) { AllowMinimizedBooleanTagHelperAttributes = allowMinimizedBooleanTagHelperAttributes; - AllowHtmlCommentsInTagHelpers = allowHtmlCommentsInTagHelper; } public override bool AllowMinimizedBooleanTagHelperAttributes { get; } - - public override bool AllowHtmlCommentsInTagHelpers { get; } } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperParseTreeRewriterTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperParseTreeRewriterTest.cs index 3f37c82c4d..2feccbe1a4 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperParseTreeRewriterTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperParseTreeRewriterTest.cs @@ -254,7 +254,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy var descriptors = new TagHelperDescriptor[] { TagHelperDescriptorBuilder.Create("InputTagHelper", "SomeAssembly") - .TagMatchingRuleDescriptor(rule => + .TagMatchingRuleDescriptor(rule => rule .RequireTagName("input") .RequireTagStructure(TagStructure.WithoutEndTag)) @@ -371,7 +371,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy .TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong")) .Build(), }; - + // Act & Assert EvaluateData( descriptors, @@ -793,7 +793,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy var descriptors = new TagHelperDescriptor[] { TagHelperDescriptorBuilder.Create("StrongTagHelper", "SomeAssembly") - .TagMatchingRuleDescriptor(rule => + .TagMatchingRuleDescriptor(rule => rule .RequireTagName("strong") .RequireAttributeDescriptor(attribute => attribute.Name("required"))) @@ -830,7 +830,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy .TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong")) .Build(), TagHelperDescriptorBuilder.Create("BRTagHelper", "SomeAssembly") - .TagMatchingRuleDescriptor(rule => + .TagMatchingRuleDescriptor(rule => rule .RequireTagName("br") .RequireTagStructure(TagStructure.WithoutEndTag)) @@ -1108,243 +1108,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy EvaluateData(descriptors, documentContent, (MarkupBlock)expectedOutput, (RazorDiagnostic[])expectedErrors); } - [Fact] - public void Rewrite_AllowsSimpleHtmlCommentsAsChildren() - { - // Arrange - IEnumerable allowedChildren = new List { "b" }; - string literal = "asdf"; - string commentOutput = "Hello 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", - blockFactory.MarkupTagBlock(""), - factory.Markup(literal), - blockFactory.MarkupTagBlock(""), - blockFactory.HtmlCommentBlock(commentOutput))); - - // Act & Assert - EvaluateData( - descriptors, - expectedOutput, - expectedMarkup, - Array.Empty()); - } - - [Fact] - public void Rewrite_DoesntAllowSimpleHtmlCommentsAsChildrenWhenFeatureFlagIsOff() - { - // Arrange - 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 expectedOutput = $"

"; - - 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.HtmlCommentBlock(comment1))); - - // Act & Assert - EvaluateData( - descriptors, - expectedOutput, - expectedMarkup, - new[] - { - nestedContentError("p", "b", 3, 4), - nestedContentError("p", "b", 7, 5), - nestedContentError("p", "b", 12, 3), - }, - featureFlags: RazorParserFeatureFlags.Create(RazorLanguageVersion.Version_2_0)); - } - - [Fact] - public void Rewrite_FailsForContentWithCommentsAsChildren() - { - // Arrange - 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", - blockFactory.HtmlCommentBlock(comment1), - factory.Markup(literal), - blockFactory.HtmlCommentBlock(comment2))); - - // Act & Assert - EvaluateData( - descriptors, - expectedOutput, - expectedMarkup, - new[] - { - nestedContentError("p", "b", 15, 4), - }); - } - - [Fact] - public void Rewrite_AllowsRazorCommentsAsChildren() - { - // Arrange - IEnumerable allowedChildren = new List { "b" }; - string literal = "asdf"; - string commentOutput = $"@*{literal}*@"; - string expectedOutput = $"

{literal}{commentOutput}

"; - - 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 CommentBlock( - Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition).Accepts(AcceptedCharactersInternal.None), - Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharactersInternal.None), - Factory.Span(SpanKindInternal.Comment, new HtmlSymbol(literal, HtmlSymbolType.RazorComment)).Accepts(AcceptedCharactersInternal.Any), - Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharactersInternal.None), - Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition).Accepts(AcceptedCharactersInternal.None)))); - - // Act & Assert - EvaluateData( - descriptors, - expectedOutput, - expectedMarkup, - Array.Empty()); - } - - [Fact] - public void Rewrite_AllowsRazorMarkupInHtmlComment() - { - // Arrange - IEnumerable allowedChildren = new List { "b" }; - string literal = "asdf"; - string part1 = "Hello "; - string part2 = "World"; - string commentStart = ""; - string expectedOutput = $"

{literal}{commentStart}{part1}@{part2}{commentEnd}

"; - - 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(""), - BlockFactory.HtmlCommentBlock(factory, f => new SyntaxTreeNode[] { - f.Markup(part1).Accepts(AcceptedCharactersInternal.WhiteSpace), - new ExpressionBlock( - f.CodeTransition(), - f.Code(part2) - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)) }))); - - // Act & Assert - EvaluateData( - descriptors, - expectedOutput, - expectedMarkup, - Array.Empty()); - } - [Fact] public void Rewrite_UnderstandsNullTagNameWithAllowedChildrenForCatchAll() { @@ -1410,7 +1173,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy var descriptors = new TagHelperDescriptor[] { TagHelperDescriptorBuilder.Create("InputTagHelper", "SomeAssembly") - .TagMatchingRuleDescriptor(rule => + .TagMatchingRuleDescriptor(rule => rule .RequireTagName("input") .RequireTagStructure(TagStructure.WithoutEndTag)) @@ -1883,7 +1646,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy var descriptors = new TagHelperDescriptor[] { TagHelperDescriptorBuilder.Create("pTagHelper", "SomeAssembly") - .TagMatchingRuleDescriptor(rule => + .TagMatchingRuleDescriptor(rule => rule .RequireTagName("p") .RequireAttributeDescriptor(attribute => attribute.Name("class"))) @@ -4138,14 +3901,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy get { var factory = new SpanFactory(); - var blockFactory = new BlockFactory(factory); + yield return new object[] { "", new MarkupBlock( new MarkupTagBlock( factory.Markup("")), - blockFactory.HtmlCommentBlock (" Hello World "), + factory.Markup(""), new MarkupTagBlock( factory.Markup(""))) }; @@ -4155,14 +3918,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new MarkupBlock( new MarkupTagBlock( factory.Markup("")), - BlockFactory.HtmlCommentBlock(factory, f=> new SyntaxTreeNode[]{ - f.Markup(" ").Accepts(AcceptedCharactersInternal.WhiteSpace), - new ExpressionBlock( - f.CodeTransition(), - f.Code("foo") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), - factory.Markup(" ").Accepts(AcceptedCharactersInternal.WhiteSpace) }), + factory.Markup(""), new MarkupTagBlock( factory.Markup(""))) }; @@ -4238,8 +4000,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new ExpressionBlock( factory.CodeTransition(), factory.Code("foo") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), factory.Markup(" ]]>"), new MarkupTagBlock( factory.Markup("
"))) diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TokenizerLookaheadTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TokenizerLookaheadTest.cs index 77ac033464..6abd70ad3e 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TokenizerLookaheadTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TokenizerLookaheadTest.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.IO; using System.Text; using Xunit; @@ -56,85 +56,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Assert.Equal("pre-existing values", tokenizer.Buffer.ToString(), StringComparer.Ordinal); } - [Fact] - public void LookaheadUntil_PassesThePreviousSymbolsInTheSameOrder() - { - // Arrange - var tokenizer = CreateContentTokenizer("asdf--fvd--<"); - - // Act - var i = 3; - IEnumerable previousSymbols = null; - var symbolFound = tokenizer.LookaheadUntil((s, p) => - { - previousSymbols = p; - return --i == 0; - }); - - // Assert - Assert.Equal(4, previousSymbols.Count()); - - // For the very first element, there will be no previous items, so null is expected - var orderIndex = 0; - Assert.Null(previousSymbols.ElementAt(orderIndex++)); - Assert.Equal(new HtmlSymbol("asdf", HtmlSymbolType.Text), previousSymbols.ElementAt(orderIndex++)); - Assert.Equal(new HtmlSymbol("--", HtmlSymbolType.DoubleHyphen), previousSymbols.ElementAt(orderIndex++)); - Assert.Equal(new HtmlSymbol("fvd", HtmlSymbolType.Text), previousSymbols.ElementAt(orderIndex++)); - } - - [Fact] - public void LookaheadUntil_ReturnsFalseAfterIteratingOverAllSymbolsIfConditionIsNotMet() - { - // Arrange - var tokenizer = CreateContentTokenizer("asdf--fvd"); - - // Act - var symbols = new Stack(); - var symbolFound = tokenizer.LookaheadUntil((s, p) => - { - symbols.Push(s); - return false; - }); - - // Assert - Assert.False(symbolFound); - Assert.Equal(3, symbols.Count); - Assert.Equal(new HtmlSymbol("fvd", HtmlSymbolType.Text), symbols.Pop()); - Assert.Equal(new HtmlSymbol("--", HtmlSymbolType.DoubleHyphen), symbols.Pop()); - Assert.Equal(new HtmlSymbol("asdf", HtmlSymbolType.Text), symbols.Pop()); - } - - [Fact] - public void LookaheadUntil_ReturnsTrueAndBreaksIteration() - { - // Arrange - var tokenizer = CreateContentTokenizer("asdf--fvd"); - - // Act - var symbols = new Stack(); - var symbolFound = tokenizer.LookaheadUntil((s, p) => - { - symbols.Push(s); - return s.Type == HtmlSymbolType.DoubleHyphen; - }); - - // Assert - Assert.True(symbolFound); - Assert.Equal(2, symbols.Count); - Assert.Equal(new HtmlSymbol("--", HtmlSymbolType.DoubleHyphen), symbols.Pop()); - Assert.Equal(new HtmlSymbol("asdf", HtmlSymbolType.Text), symbols.Pop()); - } - - private static TestTokenizerBackedParser CreateContentTokenizer(string content) - { - var source = TestRazorSourceDocument.Create(content); - var options = RazorParserOptions.CreateDefault(); - var context = new ParserContext(source, options); - - var tokenizer = new TestTokenizerBackedParser(HtmlLanguageCharacteristics.Instance, context); - return tokenizer; - } - private class ExposedTokenizer : Tokenizer { public ExposedTokenizer(string input) @@ -195,27 +116,5 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy throw new NotImplementedException(); } } - - private class TestTokenizerBackedParser : TokenizerBackedParser - { - internal TestTokenizerBackedParser(LanguageCharacteristics language, ParserContext context) : base(language, context) - { - } - - public override void ParseBlock() - { - throw new NotImplementedException(); - } - - protected override bool SymbolTypeEquals(HtmlSymbolType x, HtmlSymbolType y) - { - throw new NotImplementedException(); - } - - internal new bool LookaheadUntil(Func, bool> condition) - { - return base.LookaheadUntil(condition); - } - } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/RazorParserFeatureFlagsTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/RazorParserFeatureFlagsTest.cs index 1a6605b7a5..db57a33fdf 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/RazorParserFeatureFlagsTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/RazorParserFeatureFlagsTest.cs @@ -16,7 +16,6 @@ namespace Microsoft.AspNetCore.Razor.Language // Assert Assert.True(context.AllowMinimizedBooleanTagHelperAttributes); - Assert.True(context.AllowHtmlCommentsInTagHelpers); } [Fact] @@ -27,7 +26,6 @@ namespace Microsoft.AspNetCore.Razor.Language // Assert Assert.False(context.AllowMinimizedBooleanTagHelperAttributes); - Assert.False(context.AllowHtmlCommentsInTagHelpers); } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperParseTreeRewriterTests.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperParseTreeRewriterTests.cs deleted file mode 100644 index 6c16133467..0000000000 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperParseTreeRewriterTests.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Microsoft.AspNetCore.Razor.Language.Legacy; -using Xunit; - -namespace Microsoft.AspNetCore.Razor.Language.Test -{ - public class TagHelperParseTreeRewriterTests - { - public void IsComment_ReturnsTrueForSpanInHtmlCommentBlock() - { - // Arrange - SpanFactory spanFactory = new SpanFactory(); - - Span content = spanFactory.Markup(""); - Block commentBlock = new HtmlCommentBlock(content); - - // Act - bool actualResult = TagHelperParseTreeRewriter.IsComment(content); - - // Assert - Assert.True(actualResult); - } - } -} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Double_DesignTime.ir.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Double_DesignTime.ir.txt index 4523eae7a3..aa6a00864d 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Double_DesignTime.ir.txt +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Double_DesignTime.ir.txt @@ -10,10 +10,7 @@ Document - IntermediateToken - - CSharp - #pragma warning restore 0414 MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync HtmlContent - (0:0,0 [45] HtmlCommentWithQuote_Double.cshtml) - IntermediateToken - (0:0,0 [4] HtmlCommentWithQuote_Double.cshtml) - Html - - IntermediateToken - (10:0,10 [2] HtmlCommentWithQuote_Double.cshtml) - Html - \n + IntermediateToken - (0:0,0 [12] HtmlCommentWithQuote_Double.cshtml) - Html - \n IntermediateToken - (12:1,0 [4] HtmlCommentWithQuote_Double.cshtml) - Html - diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Double_Runtime.ir.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Double_Runtime.ir.txt index 046cad8407..343985c189 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Double_Runtime.ir.txt +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Double_Runtime.ir.txt @@ -5,10 +5,7 @@ Document - ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_HtmlCommentWithQuote_Double_Runtime - - MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync HtmlContent - (0:0,0 [45] HtmlCommentWithQuote_Double.cshtml) - IntermediateToken - (0:0,0 [4] HtmlCommentWithQuote_Double.cshtml) - Html - - IntermediateToken - (10:0,10 [2] HtmlCommentWithQuote_Double.cshtml) - Html - \n + IntermediateToken - (0:0,0 [12] HtmlCommentWithQuote_Double.cshtml) - Html - \n IntermediateToken - (12:1,0 [4] HtmlCommentWithQuote_Double.cshtml) - Html - diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Single_DesignTime.ir.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Single_DesignTime.ir.txt index 8b5f5b3ed5..04795676d1 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Single_DesignTime.ir.txt +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Single_DesignTime.ir.txt @@ -10,10 +10,7 @@ Document - IntermediateToken - - CSharp - #pragma warning restore 0414 MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync HtmlContent - (0:0,0 [45] HtmlCommentWithQuote_Single.cshtml) - IntermediateToken - (0:0,0 [4] HtmlCommentWithQuote_Single.cshtml) - Html - - IntermediateToken - (10:0,10 [2] HtmlCommentWithQuote_Single.cshtml) - Html - \n + IntermediateToken - (0:0,0 [12] HtmlCommentWithQuote_Single.cshtml) - Html - \n IntermediateToken - (12:1,0 [4] HtmlCommentWithQuote_Single.cshtml) - Html - diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Single_Runtime.ir.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Single_Runtime.ir.txt index 8596f51503..65aafb6fe7 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Single_Runtime.ir.txt +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Single_Runtime.ir.txt @@ -5,10 +5,7 @@ Document - ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_HtmlCommentWithQuote_Single_Runtime - - MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync HtmlContent - (0:0,0 [45] HtmlCommentWithQuote_Single.cshtml) - IntermediateToken - (0:0,0 [4] HtmlCommentWithQuote_Single.cshtml) - Html - - IntermediateToken - (10:0,10 [2] HtmlCommentWithQuote_Single.cshtml) - Html - \n + IntermediateToken - (0:0,0 [12] HtmlCommentWithQuote_Single.cshtml) - Html - \n IntermediateToken - (12:1,0 [4] HtmlCommentWithQuote_Single.cshtml) - Html - diff --git a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/BlockFactory.cs b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/BlockFactory.cs index 2551fee917..5c2ee95394 100644 --- a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/BlockFactory.cs +++ b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/BlockFactory.cs @@ -1,7 +1,6 @@ // 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.AspNetCore.Razor.Language.Legacy @@ -56,24 +55,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy ); } - public HtmlCommentBlock HtmlCommentBlock(string content) - { - return HtmlCommentBlock(_factory, f => new SyntaxTreeNode[] { f.Markup(content).Accepts(AcceptedCharactersInternal.WhiteSpace) }); - } - - public static HtmlCommentBlock HtmlCommentBlock(SpanFactory factory, Func> nodesBuilder = null) - { - var nodes = new List(); - nodes.Add(factory.Markup("").Accepts(AcceptedCharactersInternal.None)); - - return new HtmlCommentBlock(nodes.ToArray()); - } - public Block TagHelperBlock( string tagName, TagMode tagMode, 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 8fffcf0dd9..c08e88d56f 100644 --- a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/BlockTypes.cs +++ b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/BlockTypes.cs @@ -217,14 +217,4 @@ 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) - { - } - } -} \ No newline at end of file +} From c874f84c3dd21d967dc10e6fd7f751c46edddf9a Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Tue, 20 Mar 2018 15:23:53 -0700 Subject: [PATCH 2/2] Revert "Revert "Make Razor HTML Comments aware (#2178)"" This reverts commit 1f3f4b79dabcfdcaa7fc5031a878a5925ff56c24. --- .../Legacy/BlockKindInternal.cs | 5 +- .../Legacy/HtmlMarkupParser.cs | 179 ++++++++++-- .../Legacy/HtmlSymbol.cs | 4 +- .../Legacy/TagHelperParseTreeRewriter.cs | 47 ++- .../Legacy/TokenizerBackedParser.cs | 69 ++++- .../RazorParserFeatureFlags.cs | 13 +- .../BlockKind.cs | 6 +- .../Legacy/CSharpSectionTest.cs | 13 +- .../Legacy/HtmlBlockTest.cs | 39 ++- .../Legacy/HtmlDocumentTest.cs | 9 +- .../Legacy/HtmlMarkupParserTests.cs | 226 +++++++++++++++ .../Legacy/HtmlTagsTest.cs | 4 +- .../Legacy/HtmlToCodeSwitchTest.cs | 17 +- .../Legacy/TagHelperBlockRewriterTest.cs | 15 +- .../Legacy/TagHelperParseTreeRewriterTest.cs | 272 ++++++++++++++++-- .../Legacy/TokenizerLookaheadTest.cs | 105 ++++++- .../RazorParserFeatureFlagsTest.cs | 4 +- .../TagHelperParseTreeRewriterTests.cs | 26 ++ ...lCommentWithQuote_Double_DesignTime.ir.txt | 5 +- ...HtmlCommentWithQuote_Double_Runtime.ir.txt | 5 +- ...lCommentWithQuote_Single_DesignTime.ir.txt | 5 +- ...HtmlCommentWithQuote_Single_Runtime.ir.txt | 5 +- .../Language/Legacy/BlockFactory.cs | 19 ++ .../Language/Legacy/BlockTypes.cs | 12 +- 24 files changed, 996 insertions(+), 108 deletions(-) create mode 100644 test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlMarkupParserTests.cs create mode 100644 test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperParseTreeRewriterTests.cs diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/BlockKindInternal.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/BlockKindInternal.cs index c515770ca7..0b27d83f92 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/BlockKindInternal.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/BlockKindInternal.cs @@ -18,6 +18,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy // Special Comment = 8, - Tag = 9 + Tag = 9, + HtmlComment = 10 } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs index 4fdee1322f..579cdc207c 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs @@ -12,6 +12,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy { private const string ScriptTagName = "script"; + private static readonly HtmlSymbol[] nonAllowedHtmlCommentEnding = new[] { HtmlSymbol.Hyphen, new HtmlSymbol("!", HtmlSymbolType.Bang), new HtmlSymbol("<", HtmlSymbolType.OpenAngle) }; + private static readonly HtmlSymbol[] singleHyphenArray = new[] { HtmlSymbol.Hyphen }; + private static readonly char[] ValidAfterTypeAttributeNameCharacters = { ' ', '\t', '\r', '\n', '\f', '=' }; private SourceLocation _lastTagStart = SourceLocation.Zero; private HtmlSymbol _bufferedOpenAngle; @@ -492,33 +495,37 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy if (AcceptAndMoveNext()) { - if (CurrentSymbol.Type == HtmlSymbolType.DoubleHyphen) + if (IsHtmlCommentAhead()) { - AcceptAndMoveNext(); - - Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.Any; - while (!EndOfFile) + using (Context.Builder.StartBlock(BlockKindInternal.HtmlComment)) { - SkipToAndParseCode(HtmlSymbolType.DoubleHyphen); - if (At(HtmlSymbolType.DoubleHyphen)) - { - AcceptWhile(HtmlSymbolType.DoubleHyphen); + // Accept the double-hyphen symbol at the beginning of the comment block. + AcceptAndMoveNext(); + Output(SpanKindInternal.Markup, AcceptedCharactersInternal.None); - if (At(HtmlSymbolType.Text) && - string.Equals(CurrentSymbol.Content, "-", StringComparison.Ordinal)) - { - AcceptAndMoveNext(); - } + Span.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.WhiteSpace; + while (!EndOfFile) + { + SkipToAndParseCode(HtmlSymbolType.DoubleHyphen); + var lastDoubleHyphen = AcceptAllButLastDoubleHyphens(); if (At(HtmlSymbolType.CloseAngle)) { + // Output the content in the comment block as a separate markup + Output(SpanKindInternal.Markup, AcceptedCharactersInternal.WhiteSpace); + + // This is the end of a comment block + Accept(lastDoubleHyphen); AcceptAndMoveNext(); + Output(SpanKindInternal.Markup, AcceptedCharactersInternal.None); return true; } + else if (lastDoubleHyphen != null) + { + Accept(lastDoubleHyphen); + } } } - - return false; } else if (CurrentSymbol.Type == HtmlSymbolType.LeftBracket) { @@ -537,6 +544,138 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy return false; } + protected HtmlSymbol AcceptAllButLastDoubleHyphens() + { + var lastDoubleHyphen = CurrentSymbol; + AcceptWhile(s => + { + if (NextIs(HtmlSymbolType.DoubleHyphen)) + { + lastDoubleHyphen = s; + return true; + } + + return false; + }); + + NextToken(); + + if (At(HtmlSymbolType.Text) && IsHyphen(CurrentSymbol)) + { + // Doing this here to maintain the order of symbols + if (!NextIs(HtmlSymbolType.CloseAngle)) + { + Accept(lastDoubleHyphen); + lastDoubleHyphen = null; + } + + AcceptAndMoveNext(); + } + + return lastDoubleHyphen; + } + + internal static bool IsHyphen(HtmlSymbol symbol) + { + return symbol.Equals(HtmlSymbol.Hyphen); + } + + protected bool IsHtmlCommentAhead() + { + /* + * From HTML5 Specification, available at http://www.w3.org/TR/html52/syntax.html#comments + * + * Comments must have the following format: + * 1. The string "" // As we will be treating this as a comment ending, there is no need to handle this case at all. + * 2.2.3 "--!>" + * 2.3 nor end with the string "" + * + * */ + + if (CurrentSymbol.Type != HtmlSymbolType.DoubleHyphen) + { + return false; + } + + // Check condition 2.1 + if (NextIs(HtmlSymbolType.CloseAngle) || NextIs(next => IsHyphen(next) && NextIs(HtmlSymbolType.CloseAngle))) + { + return false; + } + + // Check condition 2.2 + var isValidComment = false; + LookaheadUntil((symbol, prevSymbols) => + { + if (symbol.Type == HtmlSymbolType.DoubleHyphen) + { + if (NextIs(HtmlSymbolType.CloseAngle)) + { + // Check condition 2.3: We're at the end of a comment. Check to make sure the text ending is allowed. + isValidComment = !IsCommentContentEndingInvalid(prevSymbols); + return true; + } + else if (NextIs(ns => IsHyphen(ns) && NextIs(HtmlSymbolType.CloseAngle))) + { + // Check condition 2.3: we're at the end of a comment, which has an extra dash. + // Need to treat the dash as part of the content and check the ending. + // However, that case would have already been checked as part of check from 2.2.1 which + // would already fail this iteration and we wouldn't get here + isValidComment = true; + return true; + } + else if (NextIs(ns => ns.Type == HtmlSymbolType.Bang && NextIs(HtmlSymbolType.CloseAngle))) + { + // This is condition 2.2.3 + isValidComment = false; + return true; + } + } + else if (symbol.Type == HtmlSymbolType.OpenAngle) + { + // Checking condition 2.2.1 + if (NextIs(ns => ns.Type == HtmlSymbolType.Bang && NextIs(HtmlSymbolType.DoubleHyphen))) + { + isValidComment = false; + return true; + } + } + + return false; + }); + + return isValidComment; + } + + /// + /// Verifies, that the sequence doesn't end with the "<!-" HtmlSymbols. Note, the first symbol is an opening bracket symbol + /// + internal static bool IsCommentContentEndingInvalid(IEnumerable sequence) + { + var reversedSequence = sequence.Reverse(); + var index = 0; + foreach (var item in reversedSequence) + { + if (!item.Equals(nonAllowedHtmlCommentEnding[index++])) + { + return false; + } + + if (index == nonAllowedHtmlCommentEnding.Length) + { + return true; + } + } + + return false; + } + private bool CData() { if (CurrentSymbol.Type == HtmlSymbolType.Text && string.Equals(CurrentSymbol.Content, "cdata", StringComparison.OrdinalIgnoreCase)) @@ -1474,8 +1613,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy // Checking to see if we meet the conditions of a special '!' tag: { + internal static readonly HtmlSymbol Hyphen = new HtmlSymbol("-", HtmlSymbolType.Text); + public HtmlSymbol(string content, HtmlSymbolType type) : base(content, type, RazorDiagnostic.EmptyArray) { @@ -29,4 +31,4 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy } } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperParseTreeRewriter.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperParseTreeRewriter.cs index 2059522263..d5d65c2985 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperParseTreeRewriter.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TagHelperParseTreeRewriter.cs @@ -490,20 +490,29 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy { if (HasAllowedChildren()) { - var content = child.Content; - if (!string.IsNullOrWhiteSpace(content)) + var isDisallowedContent = true; + if (_featureFlags.AllowHtmlCommentsInTagHelpers) { - var trimmedStart = content.TrimStart(); - var whitespace = content.Substring(0, content.Length - trimmedStart.Length); - var errorStart = SourceLocationTracker.Advance(child.Start, whitespace); - var length = trimmedStart.TrimEnd().Length; - var allowedChildren = _currentTagHelperTracker.AllowedChildren; - var allowedChildrenString = string.Join(", ", allowedChildren); - errorSink.OnError( - RazorDiagnosticFactory.CreateTagHelper_CannotHaveNonTagContent( - new SourceSpan(errorStart, length), - _currentTagHelperTracker.TagName, - allowedChildrenString)); + isDisallowedContent = !IsComment(child) && child.Kind != SpanKindInternal.Transition && child.Kind != SpanKindInternal.Code; + } + + if (isDisallowedContent) + { + var content = child.Content; + if (!string.IsNullOrWhiteSpace(content)) + { + var trimmedStart = content.TrimStart(); + var whitespace = content.Substring(0, content.Length - trimmedStart.Length); + var errorStart = SourceLocationTracker.Advance(child.Start, whitespace); + var length = trimmedStart.TrimEnd().Length; + var allowedChildren = _currentTagHelperTracker.AllowedChildren; + var allowedChildrenString = string.Join(", ", allowedChildren); + errorSink.OnError( + RazorDiagnosticFactory.CreateTagHelper_CannotHaveNonTagContent( + new SourceSpan(errorStart, length), + _currentTagHelperTracker.TagName, + allowedChildrenString)); + } } } } @@ -817,6 +826,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy return relevantSymbol.Type == HtmlSymbolType.ForwardSlash; } + internal static bool IsComment(Span span) + { + Block currentBlock = span.Parent; + while (currentBlock != null && currentBlock.Type != BlockKindInternal.Comment && currentBlock.Type != BlockKindInternal.HtmlComment) + { + currentBlock = currentBlock.Parent; + } + + return currentBlock != null; + } + + private static void EnsureTagBlock(Block tagBlock) { Debug.Assert(tagBlock.Type == BlockKindInternal.Tag); diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs index 6f564eb442..723f1cbf5a 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs @@ -109,6 +109,52 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy return symbols[count]; } + /// + /// Looks forward until the specified condition is met. + /// + /// A predicate accepting the symbol being evaluated and the list of symbols which have been looped through. + /// true, if the condition was met. false - if the condition wasn't met and the last symbol has already been processed. + /// The list of previous symbols is passed in the reverse order. So the last processed element will be the first one in the list. + protected bool LookaheadUntil(Func, bool> condition) + { + if (condition == null) + { + throw new ArgumentNullException(nameof(condition)); + } + + var matchFound = false; + + var symbols = new List(); + symbols.Add(CurrentSymbol); + + while (true) + { + if (!NextToken()) + { + break; + } + + symbols.Add(CurrentSymbol); + if (condition(CurrentSymbol, symbols)) + { + matchFound = true; + break; + } + } + + // Restore Tokenizer's location to where it was pointing before the look-ahead. + for (var i = symbols.Count - 1; i >= 0; i--) + { + PutBack(symbols[i]); + } + + // The PutBacks above will set CurrentSymbol to null. EnsureCurrent will set our CurrentSymbol to the + // next symbol. + EnsureCurrent(); + + return matchFound; + } + protected internal bool NextToken() { PreviousSymbol = CurrentSymbol; @@ -254,12 +300,21 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy protected internal bool NextIs(Func condition) { var cur = CurrentSymbol; - NextToken(); - var result = condition(CurrentSymbol); - PutCurrentBack(); - PutBack(cur); - EnsureCurrent(); - return result; + if (NextToken()) + { + var result = condition(CurrentSymbol); + PutCurrentBack(); + PutBack(cur); + EnsureCurrent(); + return result; + } + else + { + PutBack(cur); + EnsureCurrent(); + } + + return false; } protected internal bool Was(TSymbolType type) @@ -621,4 +676,4 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Initialize(Span); } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Razor.Language/RazorParserFeatureFlags.cs b/src/Microsoft.AspNetCore.Razor.Language/RazorParserFeatureFlags.cs index 0629eb9af8..33324d00bd 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/RazorParserFeatureFlags.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/RazorParserFeatureFlags.cs @@ -8,26 +8,33 @@ namespace Microsoft.AspNetCore.Razor.Language public static RazorParserFeatureFlags Create(RazorLanguageVersion version) { var allowMinimizedBooleanTagHelperAttributes = false; + var allowHtmlCommentsInTagHelpers = false; if (version.CompareTo(RazorLanguageVersion.Version_2_1) >= 0) { // Added in 2.1 allowMinimizedBooleanTagHelperAttributes = true; + allowHtmlCommentsInTagHelpers = true; } - return new DefaultRazorParserFeatureFlags(allowMinimizedBooleanTagHelperAttributes); + return new DefaultRazorParserFeatureFlags(allowMinimizedBooleanTagHelperAttributes, allowHtmlCommentsInTagHelpers); } public abstract bool AllowMinimizedBooleanTagHelperAttributes { get; } + public abstract bool AllowHtmlCommentsInTagHelpers { get; } + private class DefaultRazorParserFeatureFlags : RazorParserFeatureFlags { - public DefaultRazorParserFeatureFlags(bool allowMinimizedBooleanTagHelperAttributes) + public DefaultRazorParserFeatureFlags(bool allowMinimizedBooleanTagHelperAttributes, bool allowHtmlCommentsInTagHelpers) { AllowMinimizedBooleanTagHelperAttributes = allowMinimizedBooleanTagHelperAttributes; + AllowHtmlCommentsInTagHelpers = allowHtmlCommentsInTagHelpers; } public override bool AllowMinimizedBooleanTagHelperAttributes { get; } + + public override bool AllowHtmlCommentsInTagHelpers { get; } } } -} +} \ No newline at end of file diff --git a/src/Microsoft.VisualStudio.Editor.Razor/BlockKind.cs b/src/Microsoft.VisualStudio.Editor.Razor/BlockKind.cs index 48ad2e4fb4..1a91221cbf 100644 --- a/src/Microsoft.VisualStudio.Editor.Razor/BlockKind.cs +++ b/src/Microsoft.VisualStudio.Editor.Razor/BlockKind.cs @@ -19,6 +19,8 @@ namespace Microsoft.VisualStudio.Editor.Razor // Special Comment, - Tag + Tag, + + HtmlComment } -} +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/CSharpSectionTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/CSharpSectionTest.cs index 3ae95b1994..36f2aedb8d 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/CSharpSectionTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/CSharpSectionTest.cs @@ -367,7 +367,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy { // Act & Assert ParseDocumentTest( - "@section foo " + "@section foo " + Environment.NewLine + Environment.NewLine + Environment.NewLine @@ -606,7 +606,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( - Factory.Markup("")), + BlockFactory.HtmlCommentBlock(" "), + Factory.EmptyHtml()), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } @@ -630,7 +631,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( - Factory.Markup("")), + BlockFactory.HtmlCommentBlock(" > \" '"), + Factory.EmptyHtml()), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } @@ -655,7 +657,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Factory.Markup(Environment.NewLine), new MarkupTagBlock( Factory.Markup(" \" '-->")), + BlockFactory.HtmlCommentBlock(" > \" '"), + Factory.EmptyHtml()), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } @@ -760,4 +763,4 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy ParseDocumentTest(input, new[] { SectionDirective.Directive }, (Block)expected); } } -} +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlBlockTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlBlockTest.cs index f54ba50d37..c70f4507f7 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlBlockTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlBlockTest.cs @@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Factory.Code(Environment.NewLine).AsStatement().AutoCompleteWith(null), new MarkupBlock( Factory.Markup(" "), - Factory.Markup("").Accepts(AcceptedCharactersInternal.None), + BlockFactory.HtmlCommentBlock(" Hello, I'm a comment that shouldn't break razor -"), Factory.Markup(Environment.NewLine).Accepts(AcceptedCharactersInternal.None)), Factory.EmptyCSharp().AsStatement(), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), @@ -333,7 +333,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy [Fact] public void ParseBlockSupportsCommentAsBlock() { - SingleSpanBlockTest("", BlockKindInternal.Markup, SpanKindInternal.Markup, acceptedCharacters: AcceptedCharactersInternal.None); + ParseBlockTest("", new MarkupBlock(BlockFactory.HtmlCommentBlock(" foo "))); + } + + [Fact] + public void ParseBlockSupportsCommentWithExtraDashAsBlock() + { + ParseBlockTest("", new MarkupBlock(BlockFactory.HtmlCommentBlock(" foo -"))); } [Fact] @@ -344,8 +350,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new MarkupTagBlock( Factory.Markup("").Accepts(AcceptedCharactersInternal.None)), Factory.Markup("bar"), - Factory.Markup("").Accepts(AcceptedCharactersInternal.None), - Factory.Markup("baz"), + BlockFactory.HtmlCommentBlock(" zoop "), + Factory.Markup("baz").Accepts(AcceptedCharactersInternal.None), new MarkupTagBlock( Factory.Markup("").Accepts(AcceptedCharactersInternal.None)))); } @@ -355,7 +361,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy get { var factory = new SpanFactory(); - + var blockFactory = new BlockFactory(factory); return new TheoryData { { @@ -363,7 +369,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new MarkupBlock( new MarkupTagBlock( factory.Markup("
").Accepts(AcceptedCharactersInternal.None)), - factory.Markup("").Accepts(AcceptedCharactersInternal.None), + blockFactory.HtmlCommentBlock("- Hello World -"), new MarkupTagBlock( factory.Markup("
").Accepts(AcceptedCharactersInternal.None))) }, @@ -372,7 +378,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new MarkupBlock( new MarkupTagBlock( factory.Markup("
").Accepts(AcceptedCharactersInternal.None)), - factory.Markup("").Accepts(AcceptedCharactersInternal.None), + blockFactory.HtmlCommentBlock("-- Hello World --"), new MarkupTagBlock( factory.Markup("
").Accepts(AcceptedCharactersInternal.None))) }, @@ -381,7 +387,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new MarkupBlock( new MarkupTagBlock( factory.Markup("
").Accepts(AcceptedCharactersInternal.None)), - factory.Markup("").Accepts(AcceptedCharactersInternal.None), + blockFactory.HtmlCommentBlock("--- Hello World ---"), new MarkupTagBlock( factory.Markup("
").Accepts(AcceptedCharactersInternal.None))) }, @@ -390,7 +396,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new MarkupBlock( new MarkupTagBlock( factory.Markup("
").Accepts(AcceptedCharactersInternal.None)), - factory.Markup("").Accepts(AcceptedCharactersInternal.None), + blockFactory.HtmlCommentBlock("--- Hello < --- > World
---"), new MarkupTagBlock( factory.Markup("").Accepts(AcceptedCharactersInternal.None))) }, @@ -410,19 +416,22 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy [Fact] public void ParseBlockProperlyBalancesCommentStartAndEndTags() { - SingleSpanBlockTest("", BlockKindInternal.Markup, SpanKindInternal.Markup, acceptedCharacters: AcceptedCharactersInternal.None); + ParseBlockTest("", new MarkupBlock(BlockFactory.HtmlCommentBlock(""))); } [Fact] public void ParseBlockTerminatesAtEOFWhenParsingComment() { - SingleSpanBlockTest("", BlockKindInternal.Markup, SpanKindInternal.Markup, acceptedCharacters: AcceptedCharactersInternal.None); + ParseBlockTest("", new MarkupBlock(BlockFactory.HtmlCommentBlock("--"))); } [Fact] @@ -432,8 +441,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new MarkupBlock( new MarkupTagBlock( Factory.Markup("").Accepts(AcceptedCharactersInternal.None)), - Factory.Markup("").Accepts(AcceptedCharactersInternal.None), - Factory.Markup("-->"), + BlockFactory.HtmlCommentBlock("").Accepts(AcceptedCharactersInternal.None), new MarkupTagBlock( Factory.Markup("").Accepts(AcceptedCharactersInternal.None)))); } @@ -645,4 +654,4 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Factory.Markup("").Accepts(AcceptedCharactersInternal.None)))); } } -} +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlDocumentTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlDocumentTest.cs index 0f06fe578a..da9b30c878 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlDocumentTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlDocumentTest.cs @@ -214,7 +214,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy [Fact] public void ParseDocumentReturnsOneMarkupSegmentIfNoCodeBlocksEncountered() { - SingleSpanDocumentTest("Foo BazBarBar"); + + // Act + var symbol = sut.AcceptAllButLastDoubleHyphens(); + + // Assert + Assert.Equal(doubleHyphenSymbol, symbol); + Assert.True(sut.At(HtmlSymbolType.CloseAngle)); + Assert.Equal(doubleHyphenSymbol, sut.PreviousSymbol); + } + + [Fact] + public void AcceptAllButLastDoubleHypens_ReturnsTheDoubleHyphenSymbolAfterAcceptingTheDash() + { + // Arrange + var sut = CreateTestParserForContent("--->"); + + // Act + var symbol = sut.AcceptAllButLastDoubleHyphens(); + + // Assert + Assert.Equal(doubleHyphenSymbol, symbol); + Assert.True(sut.At(HtmlSymbolType.CloseAngle)); + Assert.True(HtmlMarkupParser.IsHyphen(sut.PreviousSymbol)); + } + + [Fact] + public void IsHtmlCommentAhead_ReturnsTrueForEmptyCommentTag() + { + // Arrange + var sut = CreateTestParserForContent("---->"); + + // Act & Assert + Assert.True(sut.IsHtmlCommentAhead()); + } + + [Fact] + public void IsHtmlCommentAhead_ReturnsTrueForValidCommentTag() + { + // Arrange + var sut = CreateTestParserForContent("-- Some comment content in here -->"); + + // Act & Assert + Assert.True(sut.IsHtmlCommentAhead()); + } + + [Fact] + public void IsHtmlCommentAhead_ReturnsTrueForValidCommentTagWithExtraDashesAtClosingTag() + { + // Arrange + var sut = CreateTestParserForContent("-- Some comment content in here ----->"); + + // Act & Assert + Assert.True(sut.IsHtmlCommentAhead()); + } + + [Fact] + public void IsHtmlCommentAhead_ReturnsFalseForContentWithBadEndingAndExtraDash() + { + // Arrange + var sut = CreateTestParserForContent("-- Some comment content in here "); + + // Act & Assert + Assert.False(sut.IsHtmlCommentAhead()); + } + + [Fact] + public void IsHtmlCommentAhead_ReturnsTrueForValidCommentTagWithExtraInfoAfter() + { + // Arrange + var sut = CreateTestParserForContent("-- comment --> the first part is a valid comment without the Open angle and bang symbols"); + + // Act & Assert + Assert.True(sut.IsHtmlCommentAhead()); + } + + [Fact] + public void IsHtmlCommentAhead_ReturnsFalseForNotClosedComment() + { + // Arrange + var sut = CreateTestParserForContent("-- not closed comment"); + + // Act & Assert + Assert.False(sut.IsHtmlCommentAhead()); + } + + [Fact] + public void IsHtmlCommentAhead_ReturnsFalseForCommentWithoutLastClosingAngle() + { + // Arrange + var sut = CreateTestParserForContent("-- not closed comment--"); + + // Act & Assert + Assert.False(sut.IsHtmlCommentAhead()); + } + + [Fact] + public void IsHtmlCommentAhead_ReturnsTrueForCommentWithCodeInside() + { + // Arrange + var sut = CreateTestParserForContent("-- not closed @DateTime.Now comment-->"); + + // Act & Assert + Assert.True(sut.IsHtmlCommentAhead()); + } + + [Fact] + public void IsCommentContentEndingInvalid_ReturnsFalseForAllowedContent() + { + // Arrange + var expectedSymbol1 = new HtmlSymbol("a", HtmlSymbolType.Text); + var sequence = Enumerable.Range((int)'a', 26).Select(item => new HtmlSymbol(((char)item).ToString(), HtmlSymbolType.Text)); + + // Act & Assert + Assert.False(HtmlMarkupParser.IsCommentContentEndingInvalid(sequence)); + } + + [Fact] + public void IsCommentContentEndingInvalid_ReturnsTrueForDisallowedContent() + { + // Arrange + var expectedSymbol1 = new HtmlSymbol("a", HtmlSymbolType.Text); + var sequence = new[] { new HtmlSymbol("<", HtmlSymbolType.OpenAngle), new HtmlSymbol("!", HtmlSymbolType.Bang), new HtmlSymbol("-", HtmlSymbolType.Text) }; + + // Act & Assert + Assert.True(HtmlMarkupParser.IsCommentContentEndingInvalid(sequence)); + } + + [Fact] + public void IsCommentContentEndingInvalid_ReturnsFalseForEmptyContent() + { + // Arrange + var expectedSymbol1 = new HtmlSymbol("a", HtmlSymbolType.Text); + var sequence = Array.Empty(); + + // Act & Assert + Assert.False(HtmlMarkupParser.IsCommentContentEndingInvalid(sequence)); + } + + private class TestHtmlMarkupParser : HtmlMarkupParser + { + public new HtmlSymbol PreviousSymbol + { + get => base.PreviousSymbol; + } + + public new bool IsHtmlCommentAhead() + { + return base.IsHtmlCommentAhead(); + } + + public TestHtmlMarkupParser(ParserContext context) : base(context) + { + this.EnsureCurrent(); + } + + public new HtmlSymbol AcceptAllButLastDoubleHyphens() + { + return base.AcceptAllButLastDoubleHyphens(); + } + + public override void BuildSpan(SpanBuilder span, SourceLocation start, string content) + { + base.BuildSpan(span, start, content); + } + } + + private static TestHtmlMarkupParser CreateTestParserForContent(string content) + { + var source = TestRazorSourceDocument.Create(content); + var options = RazorParserOptions.CreateDefault(); + var context = new ParserContext(source, options); + + return new TestHtmlMarkupParser(context); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlTagsTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlTagsTest.cs index 0a86c7a06b..9a956758f2 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlTagsTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlTagsTest.cs @@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy { ParseBlockTest(" Bar", new MarkupBlock( - Factory.Markup("").Accepts(AcceptedCharactersInternal.None), + BlockFactory.HtmlCommentBlock("Foo"), Factory.Markup(" ").Accepts(AcceptedCharactersInternal.None))); } @@ -203,4 +203,4 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy BlockFactory.MarkupTagBlock("").Accepts(AcceptedCharactersInternal.None)), - Factory.Markup("").Accepts(AcceptedCharactersInternal.None), + BlockFactory.HtmlCommentBlock(Factory, f => new SyntaxTreeNode[] { + f.Markup(" ").Accepts(AcceptedCharactersInternal.WhiteSpace), + new ExpressionBlock( + f.CodeTransition(), + f.Code("foo") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), + f.Markup(" ").Accepts(AcceptedCharactersInternal.WhiteSpace) }), new MarkupTagBlock( Factory.Markup("").Accepts(AcceptedCharactersInternal.None)))); } @@ -451,4 +452,4 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Factory.EmptyHtml())); } } -} +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperBlockRewriterTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperBlockRewriterTest.cs index 9b2adc4d07..a970b69539 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperBlockRewriterTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperBlockRewriterTest.cs @@ -2950,7 +2950,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy intType), RazorDiagnosticFactory.CreateParsing_TagHelperIndexerAttributeNameMustIncludeKey( new SourceSpan(7, 0, 7, 11), - "int-prefix-", + "int-prefix-", "input"), } }, @@ -2973,7 +2973,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy stringType), RazorDiagnosticFactory.CreateParsing_TagHelperIndexerAttributeNameMustIncludeKey( new SourceSpan(7, 0, 7, 14), - "string-prefix-", + "string-prefix-", "input"), } }, @@ -3638,7 +3638,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy RazorDiagnosticFactory.CreateTagHelper_EmptyBoundAttribute( new SourceSpan(7, 0, 7, 21), "bound-required-string", - "input", + "input", stringType), } }, @@ -3962,7 +3962,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy .Build(), }; - var featureFlags = new TestRazorParserFeatureFlags(allowMinimizedBooleanTagHelperAttributes: false); + var featureFlags = new TestRazorParserFeatureFlags(allowMinimizedBooleanTagHelperAttributes: false, allowHtmlCommentsInTagHelper: false); var expectedOutput = new MarkupBlock( new MarkupTagHelperBlock( @@ -3994,12 +3994,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy private class TestRazorParserFeatureFlags : RazorParserFeatureFlags { - public TestRazorParserFeatureFlags(bool allowMinimizedBooleanTagHelperAttributes) + public TestRazorParserFeatureFlags(bool allowMinimizedBooleanTagHelperAttributes, bool allowHtmlCommentsInTagHelper) { AllowMinimizedBooleanTagHelperAttributes = allowMinimizedBooleanTagHelperAttributes; + AllowHtmlCommentsInTagHelpers = allowHtmlCommentsInTagHelper; } public override bool AllowMinimizedBooleanTagHelperAttributes { get; } + + public override bool AllowHtmlCommentsInTagHelpers { get; } } } -} +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperParseTreeRewriterTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperParseTreeRewriterTest.cs index 2feccbe1a4..3f37c82c4d 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperParseTreeRewriterTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TagHelperParseTreeRewriterTest.cs @@ -254,7 +254,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy var descriptors = new TagHelperDescriptor[] { TagHelperDescriptorBuilder.Create("InputTagHelper", "SomeAssembly") - .TagMatchingRuleDescriptor(rule => + .TagMatchingRuleDescriptor(rule => rule .RequireTagName("input") .RequireTagStructure(TagStructure.WithoutEndTag)) @@ -371,7 +371,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy .TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong")) .Build(), }; - + // Act & Assert EvaluateData( descriptors, @@ -793,7 +793,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy var descriptors = new TagHelperDescriptor[] { TagHelperDescriptorBuilder.Create("StrongTagHelper", "SomeAssembly") - .TagMatchingRuleDescriptor(rule => + .TagMatchingRuleDescriptor(rule => rule .RequireTagName("strong") .RequireAttributeDescriptor(attribute => attribute.Name("required"))) @@ -830,7 +830,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy .TagMatchingRuleDescriptor(rule => rule.RequireTagName("strong")) .Build(), TagHelperDescriptorBuilder.Create("BRTagHelper", "SomeAssembly") - .TagMatchingRuleDescriptor(rule => + .TagMatchingRuleDescriptor(rule => rule .RequireTagName("br") .RequireTagStructure(TagStructure.WithoutEndTag)) @@ -1108,6 +1108,243 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy EvaluateData(descriptors, documentContent, (MarkupBlock)expectedOutput, (RazorDiagnostic[])expectedErrors); } + [Fact] + public void Rewrite_AllowsSimpleHtmlCommentsAsChildren() + { + // Arrange + IEnumerable allowedChildren = new List { "b" }; + string literal = "asdf"; + string commentOutput = "Hello 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", + blockFactory.MarkupTagBlock(""), + factory.Markup(literal), + blockFactory.MarkupTagBlock(""), + blockFactory.HtmlCommentBlock(commentOutput))); + + // Act & Assert + EvaluateData( + descriptors, + expectedOutput, + expectedMarkup, + Array.Empty()); + } + + [Fact] + public void Rewrite_DoesntAllowSimpleHtmlCommentsAsChildrenWhenFeatureFlagIsOff() + { + // Arrange + 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 expectedOutput = $"

"; + + 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.HtmlCommentBlock(comment1))); + + // Act & Assert + EvaluateData( + descriptors, + expectedOutput, + expectedMarkup, + new[] + { + nestedContentError("p", "b", 3, 4), + nestedContentError("p", "b", 7, 5), + nestedContentError("p", "b", 12, 3), + }, + featureFlags: RazorParserFeatureFlags.Create(RazorLanguageVersion.Version_2_0)); + } + + [Fact] + public void Rewrite_FailsForContentWithCommentsAsChildren() + { + // Arrange + 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", + blockFactory.HtmlCommentBlock(comment1), + factory.Markup(literal), + blockFactory.HtmlCommentBlock(comment2))); + + // Act & Assert + EvaluateData( + descriptors, + expectedOutput, + expectedMarkup, + new[] + { + nestedContentError("p", "b", 15, 4), + }); + } + + [Fact] + public void Rewrite_AllowsRazorCommentsAsChildren() + { + // Arrange + IEnumerable allowedChildren = new List { "b" }; + string literal = "asdf"; + string commentOutput = $"@*{literal}*@"; + string expectedOutput = $"

{literal}{commentOutput}

"; + + 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 CommentBlock( + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition).Accepts(AcceptedCharactersInternal.None), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharactersInternal.None), + Factory.Span(SpanKindInternal.Comment, new HtmlSymbol(literal, HtmlSymbolType.RazorComment)).Accepts(AcceptedCharactersInternal.Any), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharactersInternal.None), + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition).Accepts(AcceptedCharactersInternal.None)))); + + // Act & Assert + EvaluateData( + descriptors, + expectedOutput, + expectedMarkup, + Array.Empty()); + } + + [Fact] + public void Rewrite_AllowsRazorMarkupInHtmlComment() + { + // Arrange + IEnumerable allowedChildren = new List { "b" }; + string literal = "asdf"; + string part1 = "Hello "; + string part2 = "World"; + string commentStart = ""; + string expectedOutput = $"

{literal}{commentStart}{part1}@{part2}{commentEnd}

"; + + 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(""), + BlockFactory.HtmlCommentBlock(factory, f => new SyntaxTreeNode[] { + f.Markup(part1).Accepts(AcceptedCharactersInternal.WhiteSpace), + new ExpressionBlock( + f.CodeTransition(), + f.Code(part2) + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharactersInternal.NonWhiteSpace)) }))); + + // Act & Assert + EvaluateData( + descriptors, + expectedOutput, + expectedMarkup, + Array.Empty()); + } + [Fact] public void Rewrite_UnderstandsNullTagNameWithAllowedChildrenForCatchAll() { @@ -1173,7 +1410,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy var descriptors = new TagHelperDescriptor[] { TagHelperDescriptorBuilder.Create("InputTagHelper", "SomeAssembly") - .TagMatchingRuleDescriptor(rule => + .TagMatchingRuleDescriptor(rule => rule .RequireTagName("input") .RequireTagStructure(TagStructure.WithoutEndTag)) @@ -1646,7 +1883,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy var descriptors = new TagHelperDescriptor[] { TagHelperDescriptorBuilder.Create("pTagHelper", "SomeAssembly") - .TagMatchingRuleDescriptor(rule => + .TagMatchingRuleDescriptor(rule => rule .RequireTagName("p") .RequireAttributeDescriptor(attribute => attribute.Name("class"))) @@ -3901,14 +4138,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy get { var factory = new SpanFactory(); - + var blockFactory = new BlockFactory(factory); yield return new object[] { "", new MarkupBlock( new MarkupTagBlock( factory.Markup("")), - factory.Markup(""), + blockFactory.HtmlCommentBlock (" Hello World "), new MarkupTagBlock( factory.Markup(""))) }; @@ -3918,13 +4155,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new MarkupBlock( new MarkupTagBlock( factory.Markup("")), - factory.Markup(""), + BlockFactory.HtmlCommentBlock(factory, f=> new SyntaxTreeNode[]{ + f.Markup(" ").Accepts(AcceptedCharactersInternal.WhiteSpace), + new ExpressionBlock( + f.CodeTransition(), + f.Code("foo") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), + factory.Markup(" ").Accepts(AcceptedCharactersInternal.WhiteSpace) }), new MarkupTagBlock( factory.Markup(""))) }; @@ -4000,8 +4238,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy new ExpressionBlock( factory.CodeTransition(), factory.Code("foo") - .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) - .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), factory.Markup(" ]]>"), new MarkupTagBlock( factory.Markup("
"))) diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TokenizerLookaheadTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TokenizerLookaheadTest.cs index 6abd70ad3e..77ac033464 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TokenizerLookaheadTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/TokenizerLookaheadTest.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; -using System.IO; +using System.Linq; using System.Text; using Xunit; @@ -56,6 +56,85 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Assert.Equal("pre-existing values", tokenizer.Buffer.ToString(), StringComparer.Ordinal); } + [Fact] + public void LookaheadUntil_PassesThePreviousSymbolsInTheSameOrder() + { + // Arrange + var tokenizer = CreateContentTokenizer("asdf--fvd--<"); + + // Act + var i = 3; + IEnumerable previousSymbols = null; + var symbolFound = tokenizer.LookaheadUntil((s, p) => + { + previousSymbols = p; + return --i == 0; + }); + + // Assert + Assert.Equal(4, previousSymbols.Count()); + + // For the very first element, there will be no previous items, so null is expected + var orderIndex = 0; + Assert.Null(previousSymbols.ElementAt(orderIndex++)); + Assert.Equal(new HtmlSymbol("asdf", HtmlSymbolType.Text), previousSymbols.ElementAt(orderIndex++)); + Assert.Equal(new HtmlSymbol("--", HtmlSymbolType.DoubleHyphen), previousSymbols.ElementAt(orderIndex++)); + Assert.Equal(new HtmlSymbol("fvd", HtmlSymbolType.Text), previousSymbols.ElementAt(orderIndex++)); + } + + [Fact] + public void LookaheadUntil_ReturnsFalseAfterIteratingOverAllSymbolsIfConditionIsNotMet() + { + // Arrange + var tokenizer = CreateContentTokenizer("asdf--fvd"); + + // Act + var symbols = new Stack(); + var symbolFound = tokenizer.LookaheadUntil((s, p) => + { + symbols.Push(s); + return false; + }); + + // Assert + Assert.False(symbolFound); + Assert.Equal(3, symbols.Count); + Assert.Equal(new HtmlSymbol("fvd", HtmlSymbolType.Text), symbols.Pop()); + Assert.Equal(new HtmlSymbol("--", HtmlSymbolType.DoubleHyphen), symbols.Pop()); + Assert.Equal(new HtmlSymbol("asdf", HtmlSymbolType.Text), symbols.Pop()); + } + + [Fact] + public void LookaheadUntil_ReturnsTrueAndBreaksIteration() + { + // Arrange + var tokenizer = CreateContentTokenizer("asdf--fvd"); + + // Act + var symbols = new Stack(); + var symbolFound = tokenizer.LookaheadUntil((s, p) => + { + symbols.Push(s); + return s.Type == HtmlSymbolType.DoubleHyphen; + }); + + // Assert + Assert.True(symbolFound); + Assert.Equal(2, symbols.Count); + Assert.Equal(new HtmlSymbol("--", HtmlSymbolType.DoubleHyphen), symbols.Pop()); + Assert.Equal(new HtmlSymbol("asdf", HtmlSymbolType.Text), symbols.Pop()); + } + + private static TestTokenizerBackedParser CreateContentTokenizer(string content) + { + var source = TestRazorSourceDocument.Create(content); + var options = RazorParserOptions.CreateDefault(); + var context = new ParserContext(source, options); + + var tokenizer = new TestTokenizerBackedParser(HtmlLanguageCharacteristics.Instance, context); + return tokenizer; + } + private class ExposedTokenizer : Tokenizer { public ExposedTokenizer(string input) @@ -116,5 +195,27 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy throw new NotImplementedException(); } } + + private class TestTokenizerBackedParser : TokenizerBackedParser + { + internal TestTokenizerBackedParser(LanguageCharacteristics language, ParserContext context) : base(language, context) + { + } + + public override void ParseBlock() + { + throw new NotImplementedException(); + } + + protected override bool SymbolTypeEquals(HtmlSymbolType x, HtmlSymbolType y) + { + throw new NotImplementedException(); + } + + internal new bool LookaheadUntil(Func, bool> condition) + { + return base.LookaheadUntil(condition); + } + } } -} +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/RazorParserFeatureFlagsTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/RazorParserFeatureFlagsTest.cs index db57a33fdf..1a6605b7a5 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/RazorParserFeatureFlagsTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/RazorParserFeatureFlagsTest.cs @@ -16,6 +16,7 @@ namespace Microsoft.AspNetCore.Razor.Language // Assert Assert.True(context.AllowMinimizedBooleanTagHelperAttributes); + Assert.True(context.AllowHtmlCommentsInTagHelpers); } [Fact] @@ -26,6 +27,7 @@ namespace Microsoft.AspNetCore.Razor.Language // Assert Assert.False(context.AllowMinimizedBooleanTagHelperAttributes); + Assert.False(context.AllowHtmlCommentsInTagHelpers); } } -} +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperParseTreeRewriterTests.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperParseTreeRewriterTests.cs new file mode 100644 index 0000000000..6c16133467 --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TagHelperParseTreeRewriterTests.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.AspNetCore.Razor.Language.Legacy; +using Xunit; + +namespace Microsoft.AspNetCore.Razor.Language.Test +{ + public class TagHelperParseTreeRewriterTests + { + public void IsComment_ReturnsTrueForSpanInHtmlCommentBlock() + { + // Arrange + SpanFactory spanFactory = new SpanFactory(); + + Span content = spanFactory.Markup(""); + Block commentBlock = new HtmlCommentBlock(content); + + // Act + bool actualResult = TagHelperParseTreeRewriter.IsComment(content); + + // Assert + Assert.True(actualResult); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Double_DesignTime.ir.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Double_DesignTime.ir.txt index aa6a00864d..4523eae7a3 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Double_DesignTime.ir.txt +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Double_DesignTime.ir.txt @@ -10,7 +10,10 @@ Document - IntermediateToken - - CSharp - #pragma warning restore 0414 MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync HtmlContent - (0:0,0 [45] HtmlCommentWithQuote_Double.cshtml) - IntermediateToken - (0:0,0 [12] HtmlCommentWithQuote_Double.cshtml) - Html - \n + IntermediateToken - (0:0,0 [4] HtmlCommentWithQuote_Double.cshtml) - Html - + IntermediateToken - (10:0,10 [2] HtmlCommentWithQuote_Double.cshtml) - Html - \n IntermediateToken - (12:1,0 [4] HtmlCommentWithQuote_Double.cshtml) - Html - diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Double_Runtime.ir.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Double_Runtime.ir.txt index 343985c189..046cad8407 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Double_Runtime.ir.txt +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Double_Runtime.ir.txt @@ -5,7 +5,10 @@ Document - ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_HtmlCommentWithQuote_Double_Runtime - - MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync HtmlContent - (0:0,0 [45] HtmlCommentWithQuote_Double.cshtml) - IntermediateToken - (0:0,0 [12] HtmlCommentWithQuote_Double.cshtml) - Html - \n + IntermediateToken - (0:0,0 [4] HtmlCommentWithQuote_Double.cshtml) - Html - + IntermediateToken - (10:0,10 [2] HtmlCommentWithQuote_Double.cshtml) - Html - \n IntermediateToken - (12:1,0 [4] HtmlCommentWithQuote_Double.cshtml) - Html - diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Single_DesignTime.ir.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Single_DesignTime.ir.txt index 04795676d1..8b5f5b3ed5 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Single_DesignTime.ir.txt +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Single_DesignTime.ir.txt @@ -10,7 +10,10 @@ Document - IntermediateToken - - CSharp - #pragma warning restore 0414 MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync HtmlContent - (0:0,0 [45] HtmlCommentWithQuote_Single.cshtml) - IntermediateToken - (0:0,0 [12] HtmlCommentWithQuote_Single.cshtml) - Html - \n + IntermediateToken - (0:0,0 [4] HtmlCommentWithQuote_Single.cshtml) - Html - + IntermediateToken - (10:0,10 [2] HtmlCommentWithQuote_Single.cshtml) - Html - \n IntermediateToken - (12:1,0 [4] HtmlCommentWithQuote_Single.cshtml) - Html - diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Single_Runtime.ir.txt b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Single_Runtime.ir.txt index 65aafb6fe7..8596f51503 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Single_Runtime.ir.txt +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/HtmlCommentWithQuote_Single_Runtime.ir.txt @@ -5,7 +5,10 @@ Document - ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_HtmlCommentWithQuote_Single_Runtime - - MethodDeclaration - - public async - System.Threading.Tasks.Task - ExecuteAsync HtmlContent - (0:0,0 [45] HtmlCommentWithQuote_Single.cshtml) - IntermediateToken - (0:0,0 [12] HtmlCommentWithQuote_Single.cshtml) - Html - \n + IntermediateToken - (0:0,0 [4] HtmlCommentWithQuote_Single.cshtml) - Html - + IntermediateToken - (10:0,10 [2] HtmlCommentWithQuote_Single.cshtml) - Html - \n IntermediateToken - (12:1,0 [4] HtmlCommentWithQuote_Single.cshtml) - Html - diff --git a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/BlockFactory.cs b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/BlockFactory.cs index 5c2ee95394..2551fee917 100644 --- a/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/BlockFactory.cs +++ b/test/Microsoft.AspNetCore.Razor.Test.Common/Language/Legacy/BlockFactory.cs @@ -1,6 +1,7 @@ // 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.AspNetCore.Razor.Language.Legacy @@ -55,6 +56,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy ); } + public HtmlCommentBlock HtmlCommentBlock(string content) + { + return HtmlCommentBlock(_factory, f => new SyntaxTreeNode[] { f.Markup(content).Accepts(AcceptedCharactersInternal.WhiteSpace) }); + } + + public static HtmlCommentBlock HtmlCommentBlock(SpanFactory factory, Func> nodesBuilder = null) + { + var nodes = new List(); + nodes.Add(factory.Markup("").Accepts(AcceptedCharactersInternal.None)); + + return new HtmlCommentBlock(nodes.ToArray()); + } + public Block TagHelperBlock( string tagName, TagMode tagMode, 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..8fffcf0dd9 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) + { + } + } +} \ No newline at end of file