Allow `TagHelper`s inside of text/html typed script tags.
- To limit the impact of the change ensured that we only do extra work in the case that we detect a script tag with a `type` attribute. - The parsing changes include normal HTML parsing behaviors when we detect that a script tag has a `type` attribute with value `text/html`. - Added unit and code generation tests to validate `text/html` script tag behavior. #502
This commit is contained in:
parent
d3835455d2
commit
fb6a08d5de
|
|
@ -5,8 +5,8 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNet.Razor.Editor;
|
|
||||||
using Microsoft.AspNet.Razor.Chunks.Generators;
|
using Microsoft.AspNet.Razor.Chunks.Generators;
|
||||||
|
using Microsoft.AspNet.Razor.Editor;
|
||||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||||
using Microsoft.AspNet.Razor.Text;
|
using Microsoft.AspNet.Razor.Text;
|
||||||
using Microsoft.AspNet.Razor.Tokenizer.Symbols;
|
using Microsoft.AspNet.Razor.Tokenizer.Symbols;
|
||||||
|
|
@ -937,9 +937,17 @@ namespace Microsoft.AspNet.Razor.Parser
|
||||||
}
|
}
|
||||||
else if (string.Equals(tagName, ScriptTagName, StringComparison.OrdinalIgnoreCase))
|
else if (string.Equals(tagName, ScriptTagName, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
CompleteTagBlockWithSpan(tagBlockWrapper, AcceptedCharacters.None, SpanKind.Markup);
|
if (!CurrentScriptTagExpectsHtml())
|
||||||
|
{
|
||||||
|
CompleteTagBlockWithSpan(tagBlockWrapper, AcceptedCharacters.None, SpanKind.Markup);
|
||||||
|
|
||||||
SkipToEndScriptAndParseCode(endTagAcceptedCharacters: AcceptedCharacters.None);
|
SkipToEndScriptAndParseCode(endTagAcceptedCharacters: AcceptedCharacters.None);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Push the script tag onto the tag stack, it should be treated like all other HTML tags.
|
||||||
|
tags.Push(tag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@
|
||||||
// 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;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNet.Razor.Chunks.Generators;
|
||||||
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
||||||
using Microsoft.AspNet.Razor.Tokenizer.Symbols;
|
using Microsoft.AspNet.Razor.Tokenizer.Symbols;
|
||||||
|
|
||||||
|
|
@ -9,6 +12,9 @@ namespace Microsoft.AspNet.Razor.Parser
|
||||||
{
|
{
|
||||||
public partial class HtmlMarkupParser
|
public partial class HtmlMarkupParser
|
||||||
{
|
{
|
||||||
|
private static readonly char[] ValidAfterTypeAttributeNameCharacters =
|
||||||
|
new[] { ' ', '\t', '\r', '\n', '\f', '=' };
|
||||||
|
|
||||||
public override void ParseDocument()
|
public override void ParseDocument()
|
||||||
{
|
{
|
||||||
if (Context == null)
|
if (Context == null)
|
||||||
|
|
@ -50,7 +56,7 @@ namespace Microsoft.AspNet.Razor.Parser
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should behave like a normal tag that has a parser escape, fall through to the normal
|
// We should behave like a normal tag that has a parser escape, fall through to the normal
|
||||||
// tag logic.
|
// tag logic.
|
||||||
}
|
}
|
||||||
else if (NextIs(HtmlSymbolType.QuestionMark))
|
else if (NextIs(HtmlSymbolType.QuestionMark))
|
||||||
|
|
@ -79,7 +85,9 @@ namespace Microsoft.AspNet.Razor.Parser
|
||||||
Optional(HtmlSymbolType.ForwardSlash);
|
Optional(HtmlSymbolType.ForwardSlash);
|
||||||
Optional(HtmlSymbolType.CloseAngle);
|
Optional(HtmlSymbolType.CloseAngle);
|
||||||
|
|
||||||
if (scriptTag)
|
// If the script tag expects javascript content then we should do minimal parsing until we reach
|
||||||
|
// the end script tag. Don't want to incorrectly parse a "var tag = '<input />';" as an HTML tag.
|
||||||
|
if (scriptTag && !CurrentScriptTagExpectsHtml())
|
||||||
{
|
{
|
||||||
Output(SpanKind.Markup);
|
Output(SpanKind.Markup);
|
||||||
tagBlock.Dispose();
|
tagBlock.Dispose();
|
||||||
|
|
@ -107,5 +115,54 @@ namespace Microsoft.AspNet.Razor.Parser
|
||||||
tagBlock.Dispose();
|
tagBlock.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool CurrentScriptTagExpectsHtml()
|
||||||
|
{
|
||||||
|
var blockBuilder = Context.CurrentBlock;
|
||||||
|
|
||||||
|
Debug.Assert(blockBuilder != null);
|
||||||
|
|
||||||
|
var typeAttribute = blockBuilder.Children
|
||||||
|
.OfType<Block>()
|
||||||
|
.Where(block =>
|
||||||
|
block.ChunkGenerator is AttributeBlockChunkGenerator &&
|
||||||
|
block.Children.Count() >= 2)
|
||||||
|
.FirstOrDefault(IsTypeAttribute);
|
||||||
|
|
||||||
|
if (typeAttribute != null)
|
||||||
|
{
|
||||||
|
var contentValues = typeAttribute.Children
|
||||||
|
.OfType<Span>()
|
||||||
|
.Where(childSpan => childSpan.ChunkGenerator is LiteralAttributeChunkGenerator)
|
||||||
|
.Select(childSpan => childSpan.Content);
|
||||||
|
|
||||||
|
var scriptType = string.Concat(contentValues).Trim();
|
||||||
|
|
||||||
|
// Does not allow charset parameter (or any other parameters).
|
||||||
|
return string.Equals(scriptType, "text/html", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsTypeAttribute(Block block)
|
||||||
|
{
|
||||||
|
var span = block.Children.First() as Span;
|
||||||
|
|
||||||
|
if (span == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var trimmedStartContent = span.Content.TrimStart();
|
||||||
|
if (trimmedStartContent.StartsWith("type", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
(trimmedStartContent.Length == 4 ||
|
||||||
|
ValidAfterTypeAttributeNameCharacters.Contains(trimmedStartContent[4])))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1459,6 +1459,58 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
||||||
contentLength: 1),
|
contentLength: 1),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"NestedScriptTagTagHelpers",
|
||||||
|
"NestedScriptTagTagHelpers.DesignTime",
|
||||||
|
DefaultPAndInputTagHelperDescriptors,
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
BuildLineMapping(
|
||||||
|
documentAbsoluteIndex: 14,
|
||||||
|
documentLineIndex: 0,
|
||||||
|
generatedAbsoluteIndex: 495,
|
||||||
|
generatedLineIndex: 15,
|
||||||
|
characterOffsetIndex: 14,
|
||||||
|
contentLength: 17),
|
||||||
|
BuildLineMapping(
|
||||||
|
documentAbsoluteIndex: 182,
|
||||||
|
documentLineIndex: 5,
|
||||||
|
generatedAbsoluteIndex: 1033,
|
||||||
|
generatedLineIndex: 35,
|
||||||
|
characterOffsetIndex: 0,
|
||||||
|
contentLength: 12),
|
||||||
|
BuildLineMapping(
|
||||||
|
documentAbsoluteIndex: 195,
|
||||||
|
documentLineIndex: 5,
|
||||||
|
documentCharacterOffsetIndex: 13,
|
||||||
|
generatedAbsoluteIndex: 1136,
|
||||||
|
generatedLineIndex: 41,
|
||||||
|
generatedCharacterOffsetIndex: 12,
|
||||||
|
contentLength: 30),
|
||||||
|
BuildLineMapping(
|
||||||
|
documentAbsoluteIndex: 339,
|
||||||
|
documentLineIndex: 7,
|
||||||
|
documentCharacterOffsetIndex: 50,
|
||||||
|
generatedAbsoluteIndex: 1385,
|
||||||
|
generatedLineIndex: 49,
|
||||||
|
generatedCharacterOffsetIndex: 6,
|
||||||
|
contentLength: 23),
|
||||||
|
BuildLineMapping(
|
||||||
|
documentAbsoluteIndex: 389,
|
||||||
|
documentLineIndex: 7,
|
||||||
|
generatedAbsoluteIndex: 1692,
|
||||||
|
generatedLineIndex: 56,
|
||||||
|
characterOffsetIndex: 100,
|
||||||
|
contentLength: 4),
|
||||||
|
BuildLineMapping(
|
||||||
|
documentAbsoluteIndex: 424,
|
||||||
|
documentLineIndex: 9,
|
||||||
|
generatedAbsoluteIndex: 1775,
|
||||||
|
generatedLineIndex: 61,
|
||||||
|
characterOffsetIndex: 0,
|
||||||
|
contentLength: 15),
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1505,7 +1557,8 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
||||||
},
|
},
|
||||||
{ "DuplicateAttributeTagHelpers", null, DefaultPAndInputTagHelperDescriptors },
|
{ "DuplicateAttributeTagHelpers", null, DefaultPAndInputTagHelperDescriptors },
|
||||||
{ "DynamicAttributeTagHelpers", null, DynamicAttributeTagHelpers_Descriptors },
|
{ "DynamicAttributeTagHelpers", null, DynamicAttributeTagHelpers_Descriptors },
|
||||||
{ "TransitionsInTagHelperAttributes", null, DefaultPAndInputTagHelperDescriptors }
|
{ "TransitionsInTagHelperAttributes", null, DefaultPAndInputTagHelperDescriptors },
|
||||||
|
{ "NestedScriptTagTagHelpers", null, DefaultPAndInputTagHelperDescriptors },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,273 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
||||||
{
|
{
|
||||||
public class TagHelperParseTreeRewriterTest : TagHelperRewritingTestBase
|
public class TagHelperParseTreeRewriterTest : TagHelperRewritingTestBase
|
||||||
{
|
{
|
||||||
|
public static TheoryData InvalidHtmlScriptBlockData
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var factory = CreateDefaultSpanFactory();
|
||||||
|
var blockFactory = new BlockFactory(factory);
|
||||||
|
|
||||||
|
return new TheoryData<string, MarkupBlock>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"<script type><input /></script>",
|
||||||
|
new MarkupBlock(
|
||||||
|
new MarkupTagBlock(
|
||||||
|
factory.Markup("<script"),
|
||||||
|
new MarkupBlock(factory.Markup(" type")),
|
||||||
|
factory.Markup(">")),
|
||||||
|
factory.Markup("<input />"),
|
||||||
|
blockFactory.MarkupTagBlock("</script>"))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<script types='text/html'><input /></script>",
|
||||||
|
new MarkupBlock(
|
||||||
|
new MarkupTagBlock(
|
||||||
|
factory.Markup("<script"),
|
||||||
|
new MarkupBlock(
|
||||||
|
new AttributeBlockChunkGenerator(
|
||||||
|
name: "types",
|
||||||
|
prefix: new LocationTagged<string>(" types='", 7, 0, 7),
|
||||||
|
suffix: new LocationTagged<string>("'", 24, 0, 24)),
|
||||||
|
factory.Markup(" types='").With(SpanChunkGenerator.Null),
|
||||||
|
factory.Markup("text/html").With(
|
||||||
|
new LiteralAttributeChunkGenerator(
|
||||||
|
prefix: new LocationTagged<string>(string.Empty, 15, 0, 15),
|
||||||
|
value: new LocationTagged<string>("text/html", 15, 0, 15))),
|
||||||
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
||||||
|
factory.Markup(">")),
|
||||||
|
factory.Markup("<input />"),
|
||||||
|
blockFactory.MarkupTagBlock("</script>"))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<script type='text/html invalid'><input /></script>",
|
||||||
|
new MarkupBlock(
|
||||||
|
new MarkupTagBlock(
|
||||||
|
factory.Markup("<script"),
|
||||||
|
new MarkupBlock(
|
||||||
|
new AttributeBlockChunkGenerator(
|
||||||
|
name: "type",
|
||||||
|
prefix: new LocationTagged<string>(" type='", 7, 0, 7),
|
||||||
|
suffix: new LocationTagged<string>("'", 31, 0, 31)),
|
||||||
|
factory.Markup(" type='").With(SpanChunkGenerator.Null),
|
||||||
|
factory.Markup("text/html").With(
|
||||||
|
new LiteralAttributeChunkGenerator(
|
||||||
|
prefix: new LocationTagged<string>(string.Empty, 14, 0, 14),
|
||||||
|
value: new LocationTagged<string>("text/html", 14, 0, 14))),
|
||||||
|
factory.Markup(" invalid").With(
|
||||||
|
new LiteralAttributeChunkGenerator(
|
||||||
|
prefix: new LocationTagged<string>(" ", 23, 0, 23),
|
||||||
|
value: new LocationTagged<string>("invalid", 24, 0, 24))),
|
||||||
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
||||||
|
factory.Markup(">")),
|
||||||
|
factory.Markup("<input />"),
|
||||||
|
blockFactory.MarkupTagBlock("</script>"))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<script type='text/ng-*' type='text/html'><input /></script>",
|
||||||
|
new MarkupBlock(
|
||||||
|
new MarkupTagBlock(
|
||||||
|
factory.Markup("<script"),
|
||||||
|
new MarkupBlock(
|
||||||
|
new AttributeBlockChunkGenerator(
|
||||||
|
name: "type",
|
||||||
|
prefix: new LocationTagged<string>(" type='", 7, 0, 7),
|
||||||
|
suffix: new LocationTagged<string>("'", 23, 0, 23)),
|
||||||
|
factory.Markup(" type='").With(SpanChunkGenerator.Null),
|
||||||
|
factory.Markup("text/ng-*").With(
|
||||||
|
new LiteralAttributeChunkGenerator(
|
||||||
|
prefix: new LocationTagged<string>(string.Empty, 14, 0, 14),
|
||||||
|
value: new LocationTagged<string>("text/ng-*", 14, 0, 14))),
|
||||||
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
||||||
|
new MarkupBlock(
|
||||||
|
new AttributeBlockChunkGenerator(
|
||||||
|
name: "type",
|
||||||
|
prefix: new LocationTagged<string>(" type='", 24, 0, 24),
|
||||||
|
suffix: new LocationTagged<string>("'", 40, 0, 40)),
|
||||||
|
factory.Markup(" type='").With(SpanChunkGenerator.Null),
|
||||||
|
factory.Markup("text/html").With(
|
||||||
|
new LiteralAttributeChunkGenerator(
|
||||||
|
prefix: new LocationTagged<string>(string.Empty, 31, 0, 31),
|
||||||
|
value: new LocationTagged<string>("text/html", 31, 0, 31))),
|
||||||
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
||||||
|
factory.Markup(">")),
|
||||||
|
factory.Markup("<input />"),
|
||||||
|
blockFactory.MarkupTagBlock("</script>"))
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(InvalidHtmlScriptBlockData))]
|
||||||
|
public void TagHelperParseTreeRewriter_DoesNotUnderstandTagHelpersInInvalidHtmlTypedScriptTags(
|
||||||
|
string documentContent,
|
||||||
|
MarkupBlock expectedOutput)
|
||||||
|
{
|
||||||
|
RunParseTreeRewriterTest(documentContent, expectedOutput, "input");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TheoryData HtmlScriptBlockData
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var factory = CreateDefaultSpanFactory();
|
||||||
|
var blockFactory = new BlockFactory(factory);
|
||||||
|
|
||||||
|
return new TheoryData<string, MarkupBlock>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"<script type='text/html'><input /></script>",
|
||||||
|
new MarkupBlock(
|
||||||
|
new MarkupTagBlock(
|
||||||
|
factory.Markup("<script"),
|
||||||
|
new MarkupBlock(
|
||||||
|
new AttributeBlockChunkGenerator(
|
||||||
|
name: "type",
|
||||||
|
prefix: new LocationTagged<string>(" type='", 7, 0, 7),
|
||||||
|
suffix: new LocationTagged<string>("'", 23, 0, 23)),
|
||||||
|
factory.Markup(" type='").With(SpanChunkGenerator.Null),
|
||||||
|
factory.Markup("text/html").With(
|
||||||
|
new LiteralAttributeChunkGenerator(
|
||||||
|
prefix: new LocationTagged<string>(string.Empty, 14, 0, 14),
|
||||||
|
value: new LocationTagged<string>("text/html", 14, 0, 14))),
|
||||||
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
||||||
|
factory.Markup(">")),
|
||||||
|
new MarkupTagHelperBlock("input", TagMode.SelfClosing),
|
||||||
|
blockFactory.MarkupTagBlock("</script>"))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<script id='scriptTag' type='text/html' class='something'><input /></script>",
|
||||||
|
new MarkupBlock(
|
||||||
|
new MarkupTagBlock(
|
||||||
|
factory.Markup("<script"),
|
||||||
|
new MarkupBlock(
|
||||||
|
new AttributeBlockChunkGenerator(
|
||||||
|
name: "id",
|
||||||
|
prefix: new LocationTagged<string>(" id='", 7, 0, 7),
|
||||||
|
suffix: new LocationTagged<string>("'", 21, 0, 21)),
|
||||||
|
factory.Markup(" id='").With(SpanChunkGenerator.Null),
|
||||||
|
factory.Markup("scriptTag").With(
|
||||||
|
new LiteralAttributeChunkGenerator(
|
||||||
|
prefix: new LocationTagged<string>(string.Empty, 12, 0, 12),
|
||||||
|
value: new LocationTagged<string>("scriptTag", 12, 0, 12))),
|
||||||
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
||||||
|
new MarkupBlock(
|
||||||
|
new AttributeBlockChunkGenerator(
|
||||||
|
name: "type",
|
||||||
|
prefix: new LocationTagged<string>(" type='", 22, 0, 22),
|
||||||
|
suffix: new LocationTagged<string>("'", 38, 0, 38)),
|
||||||
|
factory.Markup(" type='").With(SpanChunkGenerator.Null),
|
||||||
|
factory.Markup("text/html").With(
|
||||||
|
new LiteralAttributeChunkGenerator(
|
||||||
|
prefix: new LocationTagged<string>(string.Empty, 29, 0, 29),
|
||||||
|
value: new LocationTagged<string>("text/html", 29, 0, 29))),
|
||||||
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
||||||
|
new MarkupBlock(
|
||||||
|
new AttributeBlockChunkGenerator(
|
||||||
|
name: "class",
|
||||||
|
prefix: new LocationTagged<string>(" class='", 39, 0, 39),
|
||||||
|
suffix: new LocationTagged<string>("'", 56, 0, 56)),
|
||||||
|
factory.Markup(" class='").With(SpanChunkGenerator.Null),
|
||||||
|
factory.Markup("something").With(
|
||||||
|
new LiteralAttributeChunkGenerator(
|
||||||
|
prefix: new LocationTagged<string>(string.Empty, 47, 0, 47),
|
||||||
|
value: new LocationTagged<string>("something", 47, 0, 47))),
|
||||||
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
||||||
|
factory.Markup(">")),
|
||||||
|
new MarkupTagHelperBlock("input", TagMode.SelfClosing),
|
||||||
|
blockFactory.MarkupTagBlock("</script>"))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<script type='text/html'><p><script type='text/html'><input /></script></p></script>",
|
||||||
|
new MarkupBlock(
|
||||||
|
new MarkupTagBlock(
|
||||||
|
factory.Markup("<script"),
|
||||||
|
new MarkupBlock(
|
||||||
|
new AttributeBlockChunkGenerator(
|
||||||
|
name: "type",
|
||||||
|
prefix: new LocationTagged<string>(" type='", 7, 0, 7),
|
||||||
|
suffix: new LocationTagged<string>("'", 23, 0, 23)),
|
||||||
|
factory.Markup(" type='").With(SpanChunkGenerator.Null),
|
||||||
|
factory.Markup("text/html").With(
|
||||||
|
new LiteralAttributeChunkGenerator(
|
||||||
|
prefix: new LocationTagged<string>(string.Empty, 14, 0, 14),
|
||||||
|
value: new LocationTagged<string>("text/html", 14, 0, 14))),
|
||||||
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
||||||
|
factory.Markup(">")),
|
||||||
|
new MarkupTagHelperBlock("p",
|
||||||
|
new MarkupTagBlock(
|
||||||
|
factory.Markup("<script"),
|
||||||
|
new MarkupBlock(
|
||||||
|
new AttributeBlockChunkGenerator(
|
||||||
|
name: "type",
|
||||||
|
prefix: new LocationTagged<string>(" type='", 35, 0, 35),
|
||||||
|
suffix: new LocationTagged<string>("'", 51, 0, 51)),
|
||||||
|
factory.Markup(" type='").With(SpanChunkGenerator.Null),
|
||||||
|
factory.Markup("text/html").With(
|
||||||
|
new LiteralAttributeChunkGenerator(
|
||||||
|
prefix: new LocationTagged<string>(string.Empty, 42, 0, 42),
|
||||||
|
value: new LocationTagged<string>("text/html", 42, 0, 42))),
|
||||||
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
||||||
|
factory.Markup(">")),
|
||||||
|
new MarkupTagHelperBlock("input", TagMode.SelfClosing),
|
||||||
|
blockFactory.MarkupTagBlock("</script>")),
|
||||||
|
blockFactory.MarkupTagBlock("</script>"))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<script type='text/html'><p><script type='text/ html'><input /></script></p></script>",
|
||||||
|
new MarkupBlock(
|
||||||
|
new MarkupTagBlock(
|
||||||
|
factory.Markup("<script"),
|
||||||
|
new MarkupBlock(
|
||||||
|
new AttributeBlockChunkGenerator(
|
||||||
|
name: "type",
|
||||||
|
prefix: new LocationTagged<string>(" type='", 7, 0, 7),
|
||||||
|
suffix: new LocationTagged<string>("'", 23, 0, 23)),
|
||||||
|
factory.Markup(" type='").With(SpanChunkGenerator.Null),
|
||||||
|
factory.Markup("text/html").With(
|
||||||
|
new LiteralAttributeChunkGenerator(
|
||||||
|
prefix: new LocationTagged<string>(string.Empty, 14, 0, 14),
|
||||||
|
value: new LocationTagged<string>("text/html", 14, 0, 14))),
|
||||||
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
||||||
|
factory.Markup(">")),
|
||||||
|
new MarkupTagHelperBlock("p",
|
||||||
|
new MarkupTagBlock(
|
||||||
|
factory.Markup("<script"),
|
||||||
|
new MarkupBlock(
|
||||||
|
new AttributeBlockChunkGenerator(
|
||||||
|
name: "type",
|
||||||
|
prefix: new LocationTagged<string>(" type='", 35, 0, 35),
|
||||||
|
suffix: new LocationTagged<string>("'", 52, 0, 52)),
|
||||||
|
factory.Markup(" type='").With(SpanChunkGenerator.Null),
|
||||||
|
factory.Markup("text/").With(
|
||||||
|
new LiteralAttributeChunkGenerator(
|
||||||
|
prefix: new LocationTagged<string>(string.Empty, 42, 0, 42),
|
||||||
|
value: new LocationTagged<string>("text/", 42, 0, 42))),
|
||||||
|
factory.Markup(" html").With(
|
||||||
|
new LiteralAttributeChunkGenerator(
|
||||||
|
prefix: new LocationTagged<string>(" ", 47, 0, 47),
|
||||||
|
value: new LocationTagged<string>("html", 48, 0, 48))),
|
||||||
|
factory.Markup("'").With(SpanChunkGenerator.Null)),
|
||||||
|
factory.Markup(">")),
|
||||||
|
factory.Markup("<input />"),
|
||||||
|
blockFactory.MarkupTagBlock("</script>")),
|
||||||
|
blockFactory.MarkupTagBlock("</script>"))
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(HtmlScriptBlockData))]
|
||||||
|
public void TagHelperParseTreeRewriter_UnderstandsTagHelpersInHtmlTypedScriptTags(
|
||||||
|
string documentContent,
|
||||||
|
MarkupBlock expectedOutput)
|
||||||
|
{
|
||||||
|
RunParseTreeRewriterTest(documentContent, expectedOutput, "p", "input");
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Rewrite_CanHandleInvalidChildrenWithWhitespace()
|
public void Rewrite_CanHandleInvalidChildrenWithWhitespace()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
namespace TestOutput
|
||||||
|
{
|
||||||
|
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
public class NestedScriptTagTagHelpers
|
||||||
|
{
|
||||||
|
private static object @__o;
|
||||||
|
private void @__RazorDesignTimeHelpers__()
|
||||||
|
{
|
||||||
|
#pragma warning disable 219
|
||||||
|
string __tagHelperDirectiveSyntaxHelper = null;
|
||||||
|
__tagHelperDirectiveSyntaxHelper =
|
||||||
|
#line 1 "NestedScriptTagTagHelpers.cshtml"
|
||||||
|
"something, nice"
|
||||||
|
|
||||||
|
#line default
|
||||||
|
#line hidden
|
||||||
|
;
|
||||||
|
#pragma warning restore 219
|
||||||
|
}
|
||||||
|
#line hidden
|
||||||
|
private PTagHelper __PTagHelper = null;
|
||||||
|
private InputTagHelper __InputTagHelper = null;
|
||||||
|
private InputTagHelper2 __InputTagHelper2 = null;
|
||||||
|
#line hidden
|
||||||
|
public NestedScriptTagTagHelpers()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning disable 1998
|
||||||
|
public override async Task ExecuteAsync()
|
||||||
|
{
|
||||||
|
#line 6 "NestedScriptTagTagHelpers.cshtml"
|
||||||
|
|
||||||
|
|
||||||
|
#line default
|
||||||
|
#line hidden
|
||||||
|
|
||||||
|
#line 6 "NestedScriptTagTagHelpers.cshtml"
|
||||||
|
for(var i = 0; i < 5; i++) {
|
||||||
|
|
||||||
|
#line default
|
||||||
|
#line hidden
|
||||||
|
|
||||||
|
__InputTagHelper = CreateTagHelper<InputTagHelper>();
|
||||||
|
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||||
|
#line 8 "NestedScriptTagTagHelpers.cshtml"
|
||||||
|
__o = ViewBag.DefaultInterval;
|
||||||
|
|
||||||
|
#line default
|
||||||
|
#line hidden
|
||||||
|
__InputTagHelper.Type = "text";
|
||||||
|
__InputTagHelper2.Type = __InputTagHelper.Type;
|
||||||
|
#line 8 "NestedScriptTagTagHelpers.cshtml"
|
||||||
|
__InputTagHelper2.Checked = true;
|
||||||
|
|
||||||
|
#line default
|
||||||
|
#line hidden
|
||||||
|
#line 10 "NestedScriptTagTagHelpers.cshtml"
|
||||||
|
}
|
||||||
|
|
||||||
|
#line default
|
||||||
|
#line hidden
|
||||||
|
|
||||||
|
__PTagHelper = CreateTagHelper<PTagHelper>();
|
||||||
|
}
|
||||||
|
#pragma warning restore 1998
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// !!! Do not check in. Instead paste content into test method. !!!
|
||||||
|
|
||||||
|
var expectedLineMappings = new[]
|
||||||
|
{
|
||||||
|
BuildLineMapping(
|
||||||
|
documentAbsoluteIndex: 14,
|
||||||
|
documentLineIndex: 0,
|
||||||
|
generatedAbsoluteIndex: 495,
|
||||||
|
generatedLineIndex: 15,
|
||||||
|
characterOffsetIndex: 14,
|
||||||
|
contentLength: 17),
|
||||||
|
BuildLineMapping(
|
||||||
|
documentAbsoluteIndex: 182,
|
||||||
|
documentLineIndex: 5,
|
||||||
|
generatedAbsoluteIndex: 1033,
|
||||||
|
generatedLineIndex: 35,
|
||||||
|
characterOffsetIndex: 0,
|
||||||
|
contentLength: 12),
|
||||||
|
BuildLineMapping(
|
||||||
|
documentAbsoluteIndex: 195,
|
||||||
|
documentLineIndex: 5,
|
||||||
|
documentCharacterOffsetIndex: 13,
|
||||||
|
generatedAbsoluteIndex: 1136,
|
||||||
|
generatedLineIndex: 41,
|
||||||
|
generatedCharacterOffsetIndex: 12,
|
||||||
|
contentLength: 30),
|
||||||
|
BuildLineMapping(
|
||||||
|
documentAbsoluteIndex: 339,
|
||||||
|
documentLineIndex: 7,
|
||||||
|
documentCharacterOffsetIndex: 50,
|
||||||
|
generatedAbsoluteIndex: 1385,
|
||||||
|
generatedLineIndex: 49,
|
||||||
|
generatedCharacterOffsetIndex: 6,
|
||||||
|
contentLength: 23),
|
||||||
|
BuildLineMapping(
|
||||||
|
documentAbsoluteIndex: 389,
|
||||||
|
documentLineIndex: 7,
|
||||||
|
generatedAbsoluteIndex: 1692,
|
||||||
|
generatedLineIndex: 56,
|
||||||
|
characterOffsetIndex: 100,
|
||||||
|
contentLength: 4),
|
||||||
|
BuildLineMapping(
|
||||||
|
documentAbsoluteIndex: 424,
|
||||||
|
documentLineIndex: 9,
|
||||||
|
generatedAbsoluteIndex: 1775,
|
||||||
|
generatedLineIndex: 61,
|
||||||
|
characterOffsetIndex: 0,
|
||||||
|
contentLength: 15),
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
#pragma checksum "NestedScriptTagTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "9e6bc8d09df124eda650118b208b7c5e6e058f6b"
|
||||||
|
namespace TestOutput
|
||||||
|
{
|
||||||
|
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
public class NestedScriptTagTagHelpers
|
||||||
|
{
|
||||||
|
#line hidden
|
||||||
|
#pragma warning disable 0414
|
||||||
|
private TagHelperContent __tagHelperStringValueBuffer = null;
|
||||||
|
#pragma warning restore 0414
|
||||||
|
private TagHelperExecutionContext __tagHelperExecutionContext = null;
|
||||||
|
private TagHelperRunner __tagHelperRunner = null;
|
||||||
|
private TagHelperScopeManager __tagHelperScopeManager = new TagHelperScopeManager();
|
||||||
|
private PTagHelper __PTagHelper = null;
|
||||||
|
private InputTagHelper __InputTagHelper = null;
|
||||||
|
private InputTagHelper2 __InputTagHelper2 = null;
|
||||||
|
#line hidden
|
||||||
|
public NestedScriptTagTagHelpers()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning disable 1998
|
||||||
|
public override async Task ExecuteAsync()
|
||||||
|
{
|
||||||
|
__tagHelperRunner = __tagHelperRunner ?? new TagHelperRunner();
|
||||||
|
Instrumentation.BeginContext(33, 106, true);
|
||||||
|
WriteLiteral("\r\n<script type=\"text/html\">\r\n <div data-animation=\"fade\" class=\"randomNonTagHe" +
|
||||||
|
"lperAttribute\">\r\n ");
|
||||||
|
Instrumentation.EndContext();
|
||||||
|
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", TagMode.StartTagAndEndTag, "test", async() => {
|
||||||
|
Instrumentation.BeginContext(180, 2, true);
|
||||||
|
WriteLiteral("\r\n");
|
||||||
|
Instrumentation.EndContext();
|
||||||
|
#line 6 "NestedScriptTagTagHelpers.cshtml"
|
||||||
|
|
||||||
|
|
||||||
|
#line default
|
||||||
|
#line hidden
|
||||||
|
|
||||||
|
#line 6 "NestedScriptTagTagHelpers.cshtml"
|
||||||
|
for(var i = 0; i < 5; i++) {
|
||||||
|
|
||||||
|
#line default
|
||||||
|
#line hidden
|
||||||
|
|
||||||
|
Instrumentation.BeginContext(225, 84, true);
|
||||||
|
WriteLiteral(" <script id=\"nestedScriptTag\" type=\"text/html\">\r\n " +
|
||||||
|
" ");
|
||||||
|
Instrumentation.EndContext();
|
||||||
|
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", TagMode.StartTagOnly, "test", async() => {
|
||||||
|
}
|
||||||
|
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||||
|
__InputTagHelper = CreateTagHelper<InputTagHelper>();
|
||||||
|
__tagHelperExecutionContext.Add(__InputTagHelper);
|
||||||
|
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||||
|
__tagHelperExecutionContext.Add(__InputTagHelper2);
|
||||||
|
StartTagHelperWritingScope();
|
||||||
|
WriteLiteral("2000 + ");
|
||||||
|
#line 8 "NestedScriptTagTagHelpers.cshtml"
|
||||||
|
Write(ViewBag.DefaultInterval);
|
||||||
|
|
||||||
|
#line default
|
||||||
|
#line hidden
|
||||||
|
WriteLiteral(" + 1");
|
||||||
|
__tagHelperStringValueBuffer = EndTagHelperWritingScope();
|
||||||
|
__tagHelperExecutionContext.AddHtmlAttribute("data-interval", Html.Raw(__tagHelperStringValueBuffer.ToString()));
|
||||||
|
__InputTagHelper.Type = "text";
|
||||||
|
__tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type);
|
||||||
|
__InputTagHelper2.Type = __InputTagHelper.Type;
|
||||||
|
#line 8 "NestedScriptTagTagHelpers.cshtml"
|
||||||
|
__InputTagHelper2.Checked = true;
|
||||||
|
|
||||||
|
#line default
|
||||||
|
#line hidden
|
||||||
|
__tagHelperExecutionContext.AddTagHelperAttribute("checked", __InputTagHelper2.Checked);
|
||||||
|
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||||
|
Instrumentation.BeginContext(309, 86, false);
|
||||||
|
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||||
|
Instrumentation.EndContext();
|
||||||
|
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||||
|
Instrumentation.BeginContext(395, 29, true);
|
||||||
|
WriteLiteral("\r\n </script>\r\n");
|
||||||
|
Instrumentation.EndContext();
|
||||||
|
#line 10 "NestedScriptTagTagHelpers.cshtml"
|
||||||
|
}
|
||||||
|
|
||||||
|
#line default
|
||||||
|
#line hidden
|
||||||
|
|
||||||
|
Instrumentation.BeginContext(439, 129, true);
|
||||||
|
WriteLiteral(" <script type=\"text/javascript\">\r\n var tag = \'<input ch" +
|
||||||
|
"ecked=\"true\">\';\r\n </script>\r\n ");
|
||||||
|
Instrumentation.EndContext();
|
||||||
|
}
|
||||||
|
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||||
|
__PTagHelper = CreateTagHelper<PTagHelper>();
|
||||||
|
__tagHelperExecutionContext.Add(__PTagHelper);
|
||||||
|
__tagHelperExecutionContext.AddHtmlAttribute("class", Html.Raw("Hello World"));
|
||||||
|
__tagHelperExecutionContext.AddHtmlAttribute("data-delay", Html.Raw("1000"));
|
||||||
|
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||||
|
Instrumentation.BeginContext(139, 433, false);
|
||||||
|
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||||
|
Instrumentation.EndContext();
|
||||||
|
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||||
|
Instrumentation.BeginContext(572, 23, true);
|
||||||
|
WriteLiteral("\r\n </div>\r\n</script>");
|
||||||
|
Instrumentation.EndContext();
|
||||||
|
}
|
||||||
|
#pragma warning restore 1998
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
@addTagHelper "something, nice"
|
||||||
|
|
||||||
|
<script type="text/html">
|
||||||
|
<div data-animation="fade" class="randomNonTagHelperAttribute">
|
||||||
|
<p class="Hello World" data-delay="1000">
|
||||||
|
@for(var i = 0; i < 5; i++) {
|
||||||
|
<script id="nestedScriptTag" type="text/html">
|
||||||
|
<input data-interval="2000 + @ViewBag.DefaultInterval + 1" type="text" checked="true">
|
||||||
|
</script>
|
||||||
|
}
|
||||||
|
<script type="text/javascript">
|
||||||
|
var tag = '<input checked="true">';
|
||||||
|
</script>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
Loading…
Reference in New Issue