diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs index dcd0254c29..e9dcf36d5c 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/HtmlMarkupParser.cs @@ -541,7 +541,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy return false; } - private HtmlSymbol AcceptAllButLastDoubleHypens() + protected HtmlSymbol AcceptAllButLastDoubleHypens() { var lastDoubleHyphen = CurrentSymbol; AcceptWhile(s => @@ -552,11 +552,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy return true; } - NextToken(); - EnsureCurrent(); return false; }); + NextToken(); + EnsureCurrent(); + if (At(HtmlSymbolType.Text) && IsDashSymbol(CurrentSymbol)) { // Doing this here to maintain the order of symbols @@ -572,12 +573,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy return lastDoubleHyphen; } - private static bool IsDashSymbol(HtmlSymbol symbol) + internal static bool IsDashSymbol(HtmlSymbol symbol) { return string.Equals(symbol.Content, "-", StringComparison.Ordinal); } - private bool IsHtmlCommentAhead() + protected bool IsHtmlCommentAhead() { /* * From HTML5 Specification, available at http://www.w3.org/TR/html52/syntax.html#comments diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs index 98763ad0a3..2b6e55090a 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/TokenizerBackedParser.cs @@ -300,12 +300,16 @@ 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; + } + + return false; } protected internal bool Was(TSymbolType type) diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlMarkupParserTests.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlMarkupParserTests.cs new file mode 100644 index 0000000000..db47d47b73 --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/HtmlMarkupParserTests.cs @@ -0,0 +1,171 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Razor.Language.Legacy; +using Xunit; + +namespace Microsoft.AspNetCore.Razor.Language.Test.Legacy +{ + public class HtmlMarkupParserTests + { + private static readonly HtmlSymbol doubleHyphenSymbol = new HtmlSymbol("--", HtmlSymbolType.DoubleHyphen); + + public static IEnumerable NonDashSymbols + { + get + { + yield return new[] { new HtmlSymbol("--", HtmlSymbolType.DoubleHyphen) }; + yield return new[] { new HtmlSymbol("asdf", HtmlSymbolType.Text) }; + yield return new[] { new HtmlSymbol(">", HtmlSymbolType.CloseAngle) }; + yield return new[] { new HtmlSymbol("<", HtmlSymbolType.OpenAngle) }; + yield return new[] { new HtmlSymbol("!", HtmlSymbolType.Bang) }; + } + } + + [Theory] + [MemberData(nameof(NonDashSymbols))] + public void IsDashSymbol_ReturnsFalseForNonDashSymbol(object symbol) + { + // Arrange + HtmlSymbol convertedSymbol = (HtmlSymbol)symbol; + + // Act & Assert + Assert.False(HtmlMarkupParser.IsDashSymbol(convertedSymbol)); + } + + [Fact] + public void IsDashSymbol_ReturnsTrueForADashSymbol() + { + // Arrange + HtmlSymbol dashSymbol = new HtmlSymbol("-", HtmlSymbolType.Text); + + // Act & Assert + Assert.True(HtmlMarkupParser.IsDashSymbol(dashSymbol)); + } + + [Fact] + public void AcceptAllButLastDoubleHypens_ReturnsTheOnlyDoubleHyphenSymbol() + { + // Arrange + TestHtmlMarkupParser sut = CreateTestParserForContent("-->"); + + // Act + HtmlSymbol symbol = sut.AcceptAllButLastDoubleHypens(); + + // Assert + Assert.Equal(doubleHyphenSymbol, symbol); + Assert.True(sut.At(HtmlSymbolType.CloseAngle)); + Assert.Equal(doubleHyphenSymbol, sut.PreviousSymbol); + } + + [Fact] + public void AcceptAllButLastDoubleHypens_ReturnsTheDoubleHyphenSymbolAfterAcceptingTheDash() + { + // Arrange + TestHtmlMarkupParser sut = CreateTestParserForContent("--->"); + + // Act + HtmlSymbol symbol = sut.AcceptAllButLastDoubleHypens(); + + // Assert + Assert.Equal(doubleHyphenSymbol, symbol); + Assert.True(sut.At(HtmlSymbolType.CloseAngle)); + Assert.True(HtmlMarkupParser.IsDashSymbol(sut.PreviousSymbol)); + } + + [Fact] + public void IsHtmlCommentAhead_ReturnsTrueForEmptyCommentTag() + { + // Arrange + TestHtmlMarkupParser sut = CreateTestParserForContent("---->"); + + // Act & Assert + Assert.True(sut.IsHtmlCommentAhead()); + } + + [Fact] + public void IsHtmlCommentAhead_ReturnsTrueForValidCommentTag() + { + // Arrange + TestHtmlMarkupParser sut = CreateTestParserForContent("-- Some comment content in here -->"); + + // Act & Assert + Assert.True(sut.IsHtmlCommentAhead()); + } + + [Fact] + public void IsHtmlCommentAhead_ReturnsTrueForValidCommentTagWithExtraDashesAtClosingTag() + { + // Arrange + TestHtmlMarkupParser sut = CreateTestParserForContent("-- Some comment content in here ----->"); + + // Act & Assert + Assert.True(sut.IsHtmlCommentAhead()); + } + + [Fact] + public void IsHtmlCommentAhead_ReturnsTrueForValidCommentTagWithExtraInfoAfter() + { + // Arrange + TestHtmlMarkupParser 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 + TestHtmlMarkupParser sut = CreateTestParserForContent("-- not closed comment"); + + // Act & Assert + Assert.False(sut.IsHtmlCommentAhead()); + } + + [Fact] + public void IsHtmlCommentAhead_ReturnsFalseForCommentWithoutLastClosingAngle() + { + // Arrange + TestHtmlMarkupParser sut = CreateTestParserForContent("-- not closed comment--"); + + // Act & Assert + Assert.False(sut.IsHtmlCommentAhead()); + } + + 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 AcceptAllButLastDoubleHypens() + { + return base.AcceptAllButLastDoubleHypens(); + } + + 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); + } + } +}