Allow invalid HTML to be in Razor pages.

- Modified the TagHelperParseTreeRewriter to not remove invalid HTML snippets.
- Added tests to validate invalid HTML is allowed.

#212
This commit is contained in:
N. Taylor Mullen 2014-10-28 12:52:56 -07:00
parent 39accef1ad
commit 8d4bdbdb84
4 changed files with 136 additions and 3 deletions

View File

@ -56,8 +56,10 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
// Get tag name of the current block (doesn't matter if it's an end or start tag) // Get tag name of the current block (doesn't matter if it's an end or start tag)
var tagName = GetTagName(childBlock); var tagName = GetTagName(childBlock);
// Could not determine tag name, it can't be a TagHelper, continue on and track the element.
if (tagName == null) if (tagName == null)
{ {
_currentBlock.Children.Add(child);
continue; continue;
} }
@ -224,9 +226,14 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
} }
var childSpan = (Span)child; var childSpan = (Span)child;
var textSymbol = childSpan.Symbols.FirstHtmlSymbolAs(HtmlSymbolType.Text); var textSymbol = childSpan.Symbols.FirstHtmlSymbolAs(HtmlSymbolType.WhiteSpace | HtmlSymbolType.Text);
return textSymbol != null ? textSymbol.Content : null; if (textSymbol == null)
{
return null;
}
return textSymbol.Type == HtmlSymbolType.WhiteSpace ? null : textSymbol.Content;
} }
private static bool IsSelfClosing(Block beginTagBlock) private static bool IsSelfClosing(Block beginTagBlock)

View File

@ -1,8 +1,11 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Razor.Tokenizer.Symbols namespace Microsoft.AspNet.Razor.Tokenizer.Symbols
{ {
[Flags]
public enum HtmlSymbolType public enum HtmlSymbolType
{ {
Unknown, Unknown,

View File

@ -49,7 +49,7 @@ namespace Microsoft.AspNet.Razor.Tokenizer.Symbols
/// <returns>The first <see cref="HtmlSymbol"/> of type <paramref name="type"/>.</returns> /// <returns>The first <see cref="HtmlSymbol"/> of type <paramref name="type"/>.</returns>
public static HtmlSymbol FirstHtmlSymbolAs(this IEnumerable<ISymbol> symbols, HtmlSymbolType type) public static HtmlSymbol FirstHtmlSymbolAs(this IEnumerable<ISymbol> symbols, HtmlSymbolType type)
{ {
return symbols.OfType<HtmlSymbol>().FirstOrDefault(sym => sym.Type == type); return symbols.OfType<HtmlSymbol>().FirstOrDefault(sym => (type & sym.Type) == sym.Type);
} }
} }
} }

View File

@ -92,6 +92,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
}; };
} }
} }
[Theory] [Theory]
[MemberData(nameof(IncompleteHelperBlockData))] [MemberData(nameof(IncompleteHelperBlockData))]
public void TagHelperParseTreeRewriter_CreatesErrorForIncompleteTagHelper( public void TagHelperParseTreeRewriter_CreatesErrorForIncompleteTagHelper(
@ -102,6 +103,128 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
RunParseTreeRewriterTest(documentContent, expectedOutput, new[] { expectedError }, "strong", "p"); RunParseTreeRewriterTest(documentContent, expectedOutput, new[] { expectedError }, "strong", "p");
} }
public static TheoryData<string, MarkupBlock> InvalidHtmlBlockData
{
get
{
var factory = CreateDefaultSpanFactory();
var blockFactory = new BlockFactory(factory);
var dateTimeNow = new ExpressionBlock(
factory.CodeTransition(),
factory.Code("DateTime.Now")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace));
return new TheoryData<string, MarkupBlock>
{
{
"<<<p>>></p>",
new MarkupBlock(
blockFactory.MarkupTagBlock("<"),
blockFactory.MarkupTagBlock("<"),
new MarkupTagHelperBlock("p",
factory.Markup(">>")))
},
{
"<<p />",
new MarkupBlock(
blockFactory.MarkupTagBlock("<"),
new MarkupTagHelperBlock("p"))
},
{
"< p />",
new MarkupBlock(
blockFactory.MarkupTagBlock("< p />"))
},
{
"<input <p />",
new MarkupBlock(
blockFactory.MarkupTagBlock("<input "),
new MarkupTagHelperBlock("p"))
},
{
"< class=\"foo\" <p />",
new MarkupBlock(
new MarkupTagBlock(
factory.Markup("<"),
new MarkupBlock(
new AttributeBlockCodeGenerator(
name: "class",
prefix: new LocationTagged<string>(" class=\"", 1, 0, 1),
suffix: new LocationTagged<string>("\"", 12, 0, 12)),
factory.Markup(" class=\"").With(SpanCodeGenerator.Null),
factory.Markup("foo").With(new LiteralAttributeCodeGenerator(
prefix: new LocationTagged<string>(string.Empty, 9, 0, 9),
value: new LocationTagged<string>("foo", 9, 0, 9))),
factory.Markup("\"").With(SpanCodeGenerator.Null)),
factory.Markup(" ")),
new MarkupTagHelperBlock("p"))
},
{
"</<<p>/></p>>",
new MarkupBlock(
blockFactory.MarkupTagBlock("</"),
blockFactory.MarkupTagBlock("<"),
new MarkupTagHelperBlock("p",
factory.Markup("/>")),
factory.Markup(">"))
},
{
"</<<p>/><strong></p>>",
new MarkupBlock(
blockFactory.MarkupTagBlock("</"),
blockFactory.MarkupTagBlock("<"),
new MarkupTagHelperBlock("p",
factory.Markup("/>"),
blockFactory.MarkupTagBlock("<strong>")),
factory.Markup(">"))
},
{
"</<<p>@DateTime.Now/><strong></p>>",
new MarkupBlock(
blockFactory.MarkupTagBlock("</"),
blockFactory.MarkupTagBlock("<"),
new MarkupTagHelperBlock("p",
dateTimeNow,
factory.Markup("/>"),
blockFactory.MarkupTagBlock("<strong>")),
factory.Markup(">"))
},
{
"</ /< ><p>@DateTime.Now / ><strong></p></ >",
new MarkupBlock(
blockFactory.MarkupTagBlock("</ "),
factory.Markup("/"),
blockFactory.MarkupTagBlock("< >"),
new MarkupTagHelperBlock("p",
dateTimeNow,
factory.Markup(" / >"),
blockFactory.MarkupTagBlock("<strong>")),
blockFactory.MarkupTagBlock("</ >"))
},
{
"<p>< @DateTime.Now ></ @DateTime.Now ></p>",
new MarkupBlock(
new MarkupTagHelperBlock("p",
new MarkupTagBlock(
factory.Markup("< "),
dateTimeNow,
factory.Markup(" >")),
blockFactory.MarkupTagBlock("</ "),
dateTimeNow,
factory.Markup(" >")))
}
};
}
}
[Theory]
[MemberData(nameof(InvalidHtmlBlockData))]
public void TagHelperParseTreeRewriter_AllowsInvalidHtml(string documentContent, MarkupBlock expectedOutput)
{
RunParseTreeRewriterTest(documentContent, expectedOutput, "p");
}
public static IEnumerable<object[]> TextTagsBlockData public static IEnumerable<object[]> TextTagsBlockData
{ {
get get