Skip to content

Commit a05cb42

Browse files
authored
Reenable markup blocks (#1286)
* Reenable HtmlBlock unit tests * Add E2E tests for HTML Block cases * Remove harded GenerateBaselines=true * Fix #1193 This commit addresses the root cause of #1193. When we merge HTML text nodes into HTML blocks we need to re-encode any HTML entities that were encoded eariler. I did a bit of a deep dive on how HTML encoding is handled in Blazor and I think this is the best strategy. I think it's valuable that the BrowserRenderer uses document.createTextNode, which will always encode the text - this handles dynamic content. We want to keep this in place to avoid HTML injection attacks. * Fix #1265 Reenable MarkupBlock * test cleanup
1 parent 70a4bf7 commit a05cb42

File tree

36 files changed

+185
-110
lines changed

36 files changed

+185
-110
lines changed

src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorExtensionInitializer.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,7 @@ public static void Register(RazorProjectEngineBuilder builder)
7979
builder.Features.Add(new EventHandlerLoweringPass());
8080
builder.Features.Add(new RefLoweringPass());
8181
builder.Features.Add(new BindLoweringPass());
82-
83-
// Temporarily disabled for 0.5.1
84-
// builder.Features.Add(new HtmlBlockPass());
82+
builder.Features.Add(new HtmlBlockPass());
8583

8684
builder.Features.Add(new ComponentTagHelperDescriptorProvider());
8785
builder.Features.Add(new BindTagHelperDescriptorProvider());

src/Microsoft.AspNetCore.Blazor.Razor.Extensions/ComponentDocumentRewritePass.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,13 @@ public IEnumerable<HtmlToken> Get()
414414
throw new InvalidOperationException("You need to call Push first.");
415415
}
416416

417+
// This will decode any HTML entities into their textual equivalent.
418+
//
419+
// This is OK because an HtmlContent node is used with document.createTextNode
420+
// in the DOM, which can accept content that has decoded entities.
421+
//
422+
// In the event that we merge HtmlContent into an HtmlBlock, we need to
423+
// re-encode the entites. That's done in the HtmlBlock pass.
417424
var tokens = _textSource.Tokenize(HtmlEntityService.Resolver);
418425
return tokens;
419426
}

src/Microsoft.AspNetCore.Blazor.Razor.Extensions/HtmlBlockPass.cs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.Linq;
77
using System.Text;
8+
using AngleSharp.Html;
89
using Microsoft.AspNetCore.Razor.Language;
910
using Microsoft.AspNetCore.Razor.Language.Intermediate;
1011

@@ -166,11 +167,15 @@ private class RewriteVisitor :
166167
IntermediateNodeWalker,
167168
IExtensionIntermediateNodeVisitor<HtmlElementIntermediateNode>
168169
{
170+
private readonly StringBuilder _encodingBuilder;
171+
169172
private readonly List<IntermediateNodeReference> _trees;
170173

171174
public RewriteVisitor(List<IntermediateNodeReference> trees)
172175
{
173176
_trees = trees;
177+
178+
_encodingBuilder = new StringBuilder();
174179
}
175180

176181
public StringBuilder Builder { get; } = new StringBuilder();
@@ -251,19 +256,28 @@ public override void VisitHtmlAttribute(HtmlAttributeIntermediateNode node)
251256

252257
public override void VisitHtmlAttributeValue(HtmlAttributeValueIntermediateNode node)
253258
{
254-
// Visit Children
255-
base.VisitDefault(node);
259+
Builder.Append(Encode(node.Children));
256260
}
257261

258262
public override void VisitHtml(HtmlContentIntermediateNode node)
259263
{
260-
// Visit Children
261-
base.VisitDefault(node);
264+
Builder.Append(Encode(node.Children));
262265
}
263266

264-
public override void VisitToken(IntermediateToken node)
267+
private string Encode(IntermediateNodeCollection nodes)
265268
{
266-
Builder.Append(node.Content);
269+
// We need to HTML encode text content. We would have decoded HTML entities
270+
// earlier when we parsed the text into a tree, but since we're folding
271+
// this node into a block of pre-encoded HTML we need to be sure to
272+
// re-encode.
273+
_encodingBuilder.Clear();
274+
275+
for (var i = 0; i < nodes.Count; i++)
276+
{
277+
_encodingBuilder.Append(((IntermediateToken)nodes[i]).Content);
278+
}
279+
280+
return HtmlMarkupFormatter.Instance.Text(_encodingBuilder.ToString());
267281
}
268282
}
269283
}

test/Microsoft.AspNetCore.Blazor.Build.Test/ComponentRenderingRazorIntegrationTest.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,5 +472,21 @@ public void OnComponentHover(UIMouseEventArgs e)
472472
frame => AssertFrame.Attribute(frame, "onmouseover", 1),
473473
frame => AssertFrame.Attribute(frame, "style", "background: #FFFFFF;", 2));
474474
}
475+
476+
// Text nodes decode HTML entities
477+
[Fact]
478+
public void Render_Component_HtmlEncoded()
479+
{
480+
// Arrange
481+
var component = CompileToComponent(@"&lt;span&gt;Hi&lt/span&gt;");
482+
483+
// Act
484+
var frames = GetRenderTree(component);
485+
486+
// Assert
487+
Assert.Collection(
488+
frames,
489+
frame => AssertFrame.Text(frame, "<span>Hi</span>"));
490+
}
475491
}
476492
}

test/Microsoft.AspNetCore.Blazor.Build.Test/RuntimeCodeGenerationTest.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1256,7 +1256,6 @@ public class MyComponent : BlazorComponent
12561256
[Fact] // We don't process <!DOCTYPE ...> - we just skip them
12571257
public void Component_WithDocType()
12581258
{
1259-
GenerateBaselines = true;
12601259
// Arrange
12611260

12621261
// Act

test/Microsoft.AspNetCore.Blazor.Build.Test/TestFiles/RuntimeCodeGenerationTest/ChildComponent_WithChildContent/TestComponent.codegen.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,7 @@ protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.R
1919
builder.AddAttribute(1, "MyAttr", "abc");
2020
builder.AddAttribute(2, "ChildContent", (Microsoft.AspNetCore.Blazor.RenderFragment)((builder2) => {
2121
builder2.AddContent(3, "Some text");
22-
builder2.OpenElement(4, "some-child");
23-
builder2.AddAttribute(5, "a", "1");
24-
builder2.AddContent(6, "Nested text");
25-
builder2.CloseElement();
22+
builder2.AddMarkupContent(4, "<some-child a=\"1\">Nested text</some-child>");
2623
}
2724
));
2825
builder.CloseComponent();

test/Microsoft.AspNetCore.Blazor.Build.Test/TestFiles/RuntimeCodeGenerationTest/ChildComponent_WithChildContent/TestComponent.ir.txt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,7 @@ Document -
1313
ComponentExtensionNode - (31:1,0 [91] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent - Test.MyComponent
1414
HtmlContent - (57:1,26 [9] x:\dir\subdir\Test\TestComponent.cshtml)
1515
IntermediateToken - (57:1,26 [9] x:\dir\subdir\Test\TestComponent.cshtml) - Html - Some text
16-
HtmlElement - (66:1,35 [42] x:\dir\subdir\Test\TestComponent.cshtml) - some-child
17-
HtmlAttribute - - -
18-
HtmlAttributeValue - -
19-
IntermediateToken - - Html - 1
20-
HtmlContent - (84:1,53 [11] x:\dir\subdir\Test\TestComponent.cshtml)
21-
IntermediateToken - (84:1,53 [11] x:\dir\subdir\Test\TestComponent.cshtml) - Html - Nested text
16+
HtmlBlock - - <some-child a="1">Nested text</some-child>
2217
ComponentAttributeExtensionNode - (52:1,21 [3] x:\dir\subdir\Test\TestComponent.cshtml) - MyAttr - MyAttr
2318
HtmlContent - (52:1,21 [3] x:\dir\subdir\Test\TestComponent.cshtml)
2419
IntermediateToken - (52:1,21 [3] x:\dir\subdir\Test\TestComponent.cshtml) - Html - abc

test/Microsoft.AspNetCore.Blazor.Build.Test/TestFiles/RuntimeCodeGenerationTest/ChildComponent_WithElementOnlyChildContent/TestComponent.codegen.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.R
1717
base.BuildRenderTree(builder);
1818
builder.OpenComponent<Test.MyComponent>(0);
1919
builder.AddAttribute(1, "ChildContent", (Microsoft.AspNetCore.Blazor.RenderFragment)((builder2) => {
20-
builder2.OpenElement(2, "child");
21-
builder2.AddContent(3, "hello");
22-
builder2.CloseElement();
20+
builder2.AddMarkupContent(2, "<child>hello</child>");
2321
}
2422
));
2523
builder.CloseComponent();

test/Microsoft.AspNetCore.Blazor.Build.Test/TestFiles/RuntimeCodeGenerationTest/ChildComponent_WithElementOnlyChildContent/TestComponent.ir.txt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,4 @@ Document -
1111
CSharpCode -
1212
IntermediateToken - - CSharp - base.BuildRenderTree(builder);
1313
ComponentExtensionNode - (31:1,0 [47] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent - Test.MyComponent
14-
HtmlElement - (44:1,13 [20] x:\dir\subdir\Test\TestComponent.cshtml) - child
15-
HtmlContent - (51:1,20 [5] x:\dir\subdir\Test\TestComponent.cshtml)
16-
IntermediateToken - (51:1,20 [5] x:\dir\subdir\Test\TestComponent.cshtml) - Html - hello
14+
HtmlBlock - - <child>hello</child>

test/Microsoft.AspNetCore.Blazor.Build.Test/TestFiles/RuntimeCodeGenerationTest/Component_WithDocType/TestComponent.codegen.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ public class TestComponent : Microsoft.AspNetCore.Blazor.Components.BlazorCompon
1515
protected override void BuildRenderTree(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder)
1616
{
1717
base.BuildRenderTree(builder);
18-
builder.OpenElement(0, "div");
19-
builder.AddContent(1, "\n");
20-
builder.CloseElement();
18+
builder.AddMarkupContent(0, "<div>\n</div>");
2119
}
2220
#pragma warning restore 1998
2321
}

0 commit comments

Comments
 (0)