Prevent newlines from being encoded in the output (dotnet/aspnetcore-tooling#374)
* Prevent newlines from being encoded in the output
\n\nCommit migrated from 5f74fae0d2
This commit is contained in:
parent
8a8e144653
commit
8bb16c28aa
|
|
@ -16,9 +16,9 @@ namespace __GeneratedComponent
|
||||||
{
|
{
|
||||||
builder.OpenElement(0, "div");
|
builder.OpenElement(0, "div");
|
||||||
builder.AddAttribute(1, "class", this.ToString());
|
builder.AddAttribute(1, "class", this.ToString());
|
||||||
builder.AddContent(2, "\r\n Hello world\r\n ");
|
builder.AddMarkupContent(2, "\r\n Hello world\r\n ");
|
||||||
builder.AddContent(3, string.Format("{0}", "Hello"));
|
builder.AddContent(3, string.Format("{0}", "Hello"));
|
||||||
builder.AddContent(4, "\r\n");
|
builder.AddMarkupContent(4, "\r\n");
|
||||||
builder.CloseElement();
|
builder.CloseElement();
|
||||||
}
|
}
|
||||||
#pragma warning restore 1998
|
#pragma warning restore 1998
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
// 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.Linq;
|
||||||
|
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||||
|
{
|
||||||
|
internal class ComponentMarkupEncodingPass : ComponentIntermediateNodePassBase, IRazorOptimizationPass
|
||||||
|
{
|
||||||
|
// Runs after ComponentMarkupBlockPass
|
||||||
|
public override int Order => 10010;
|
||||||
|
|
||||||
|
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||||
|
{
|
||||||
|
if (!IsComponentDocument(documentNode))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (documentNode.Options.DesignTime)
|
||||||
|
{
|
||||||
|
// Nothing to do during design time.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rewriter = new Rewriter();
|
||||||
|
rewriter.Visit(documentNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Rewriter : IntermediateNodeWalker
|
||||||
|
{
|
||||||
|
// Markup content in components are rendered in one of the following two ways,
|
||||||
|
// AddContent - we encode it when used with prerendering and inserted into the DOM in a safe way (low perf impact)
|
||||||
|
// AddMarkupContent - renders the content directly as markup (high perf impact)
|
||||||
|
// Because of this, we want to use AddContent as much as possible.
|
||||||
|
//
|
||||||
|
// We want to use AddMarkupContent to avoid aggresive encoding during prerendering.
|
||||||
|
// Specifically, when one of the following characters are in the content,
|
||||||
|
// 1. New lines (\r, \n), tabs(\t) - so they get rendered as actual new lines, tabs instead of 

|
||||||
|
// 2. Ampersands (&) - so that HTML entities are rendered correctly without getting encoded
|
||||||
|
// 3. Any character outside the ASCII range
|
||||||
|
|
||||||
|
private static readonly char[] EncodedCharacters = new[] { '\r', '\n', '\t', '&' };
|
||||||
|
|
||||||
|
public override void VisitHtml(HtmlContentIntermediateNode node)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < node.Children.Count; i++)
|
||||||
|
{
|
||||||
|
var child = node.Children[i];
|
||||||
|
if (!(child is IntermediateToken token) || !token.IsHtml)
|
||||||
|
{
|
||||||
|
// We only care about Html tokens.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var j = 0; j < token.Content.Length; j++)
|
||||||
|
{
|
||||||
|
var ch = token.Content[j];
|
||||||
|
// ASCII range is 0 - 127
|
||||||
|
if (ch > 127 || EncodedCharacters.Contains(ch))
|
||||||
|
{
|
||||||
|
node.SetEncoded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -98,7 +98,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||||
// Since we're not in the middle of writing an element, this must evaluate as some
|
// Since we're not in the middle of writing an element, this must evaluate as some
|
||||||
// text to display
|
// text to display
|
||||||
context.CodeWriter
|
context.CodeWriter
|
||||||
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(ComponentsApi.RenderTreeBuilder.AddContent)}")
|
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{ComponentsApi.RenderTreeBuilder.AddContent}")
|
||||||
.Write((_sourceSequence++).ToString())
|
.Write((_sourceSequence++).ToString())
|
||||||
.WriteParameterSeparator();
|
.WriteParameterSeparator();
|
||||||
|
|
||||||
|
|
@ -158,7 +158,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||||
}
|
}
|
||||||
|
|
||||||
context.CodeWriter
|
context.CodeWriter
|
||||||
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(ComponentsApi.RenderTreeBuilder.AddMarkupContent)}")
|
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{ComponentsApi.RenderTreeBuilder.AddMarkupContent}")
|
||||||
.Write((_sourceSequence++).ToString())
|
.Write((_sourceSequence++).ToString())
|
||||||
.WriteParameterSeparator()
|
.WriteParameterSeparator()
|
||||||
.WriteStringLiteral(node.Content)
|
.WriteStringLiteral(node.Content)
|
||||||
|
|
@ -178,7 +178,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||||
}
|
}
|
||||||
|
|
||||||
context.CodeWriter
|
context.CodeWriter
|
||||||
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(ComponentsApi.RenderTreeBuilder.OpenElement)}")
|
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{ComponentsApi.RenderTreeBuilder.OpenElement}")
|
||||||
.Write((_sourceSequence++).ToString())
|
.Write((_sourceSequence++).ToString())
|
||||||
.WriteParameterSeparator()
|
.WriteParameterSeparator()
|
||||||
.WriteStringLiteral(node.TagName)
|
.WriteStringLiteral(node.TagName)
|
||||||
|
|
@ -255,8 +255,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||||
|
|
||||||
// Text node
|
// Text node
|
||||||
var content = GetHtmlContent(node);
|
var content = GetHtmlContent(node);
|
||||||
|
var renderApi = ComponentsApi.RenderTreeBuilder.AddContent;
|
||||||
|
if (node.IsEncoded())
|
||||||
|
{
|
||||||
|
// This content is already encoded.
|
||||||
|
renderApi = ComponentsApi.RenderTreeBuilder.AddMarkupContent;
|
||||||
|
}
|
||||||
|
|
||||||
context.CodeWriter
|
context.CodeWriter
|
||||||
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(ComponentsApi.RenderTreeBuilder.AddContent)}")
|
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{renderApi}")
|
||||||
.Write((_sourceSequence++).ToString())
|
.Write((_sourceSequence++).ToString())
|
||||||
.WriteParameterSeparator()
|
.WriteParameterSeparator()
|
||||||
.WriteStringLiteral(content)
|
.WriteStringLiteral(content)
|
||||||
|
|
@ -644,8 +651,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||||
var codeWriter = context.CodeWriter;
|
var codeWriter = context.CodeWriter;
|
||||||
|
|
||||||
var methodName = node.IsComponentCapture
|
var methodName = node.IsComponentCapture
|
||||||
? nameof(ComponentsApi.RenderTreeBuilder.AddComponentReferenceCapture)
|
? ComponentsApi.RenderTreeBuilder.AddComponentReferenceCapture
|
||||||
: nameof(ComponentsApi.RenderTreeBuilder.AddElementReferenceCapture);
|
: ComponentsApi.RenderTreeBuilder.AddElementReferenceCapture;
|
||||||
codeWriter
|
codeWriter
|
||||||
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{methodName}")
|
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{methodName}")
|
||||||
.Write((_sourceSequence++).ToString())
|
.Write((_sourceSequence++).ToString())
|
||||||
|
|
@ -693,7 +700,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||||
protected override void BeginWriteAttribute(CodeWriter codeWriter, string key)
|
protected override void BeginWriteAttribute(CodeWriter codeWriter, string key)
|
||||||
{
|
{
|
||||||
codeWriter
|
codeWriter
|
||||||
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(ComponentsApi.RenderTreeBuilder.AddAttribute)}")
|
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{ComponentsApi.RenderTreeBuilder.AddAttribute}")
|
||||||
.Write((_sourceSequence++).ToString())
|
.Write((_sourceSequence++).ToString())
|
||||||
.WriteParameterSeparator()
|
.WriteParameterSeparator()
|
||||||
.WriteStringLiteral(key)
|
.WriteStringLiteral(key)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
||||||
|
{
|
||||||
|
internal static class HtmlContentIntermediateNodeExtensions
|
||||||
|
{
|
||||||
|
private static readonly string HasEncodedContent = "HasEncodedContent";
|
||||||
|
|
||||||
|
public static bool IsEncoded(this HtmlContentIntermediateNode node)
|
||||||
|
{
|
||||||
|
return ReferenceEquals(node.Annotations[HasEncodedContent], HasEncodedContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetEncoded(this HtmlContentIntermediateNode node)
|
||||||
|
{
|
||||||
|
if (node == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
node.Annotations[HasEncodedContent] = HasEncodedContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -232,6 +232,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
||||||
builder.Features.Add(new ComponentGenericTypePass());
|
builder.Features.Add(new ComponentGenericTypePass());
|
||||||
builder.Features.Add(new ComponentChildContentDiagnosticPass());
|
builder.Features.Add(new ComponentChildContentDiagnosticPass());
|
||||||
builder.Features.Add(new ComponentMarkupBlockPass());
|
builder.Features.Add(new ComponentMarkupBlockPass());
|
||||||
|
builder.Features.Add(new ComponentMarkupEncodingPass());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void LoadExtensions(RazorProjectEngineBuilder builder, IReadOnlyList<RazorExtension> extensions)
|
private static void LoadExtensions(RazorProjectEngineBuilder builder, IReadOnlyList<RazorExtension> extensions)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue