public void WriteHtmlContent_RendersContentCorrectly() { // Arrange var codeWriter = new CodeWriter(); var writer = new RuntimeNodeWriter(); var context = TestCodeRenderingContext.CreateRuntime(); var node = new HtmlContentIntermediateNode(); node.Children.Add(new IntermediateToken() { Content = "SomeContent", Kind = TokenKind.Html, }); // Act writer.WriteHtmlContent(context, node); // Assert var csharp = context.CodeWriter.GenerateCode(); Assert.Equal( @"WriteLiteral(""SomeContent""); ", csharp, ignoreLineEndingDifferences: true); }
public void WriteHtmlContent_LargeStringLiteral_UsesMultipleWrites() { // Arrange var codeWriter = new CodeWriter(); var writer = new RuntimeNodeWriter(); var context = TestCodeRenderingContext.CreateRuntime(); var node = new HtmlContentIntermediateNode(); node.Children.Add(new IntermediateToken() { Content = new string('*', 2000), Kind = TokenKind.Html, }); // Act writer.WriteHtmlContent(context, node); // Assert var csharp = context.CodeWriter.GenerateCode(); Assert.Equal(string.Format( @"WriteLiteral(@""{0}""); WriteLiteral(@""{1}""); ", new string('*', 1024), new string('*', 976)), csharp, ignoreLineEndingDifferences: true); }
public override void VisitHtml(HtmlContentIntermediateNode node) { // We need to restore the state after processing this node. // We might have found a leaf-block of HTML, but that shouldn't // affect our parent's state. var originalState = _foundNonHtml; _foundNonHtml = false; if (node.HasDiagnostics) { // Treat node with errors as non-HTML _foundNonHtml = true; } // Visit Children base.VisitDefault(node); if (!_foundNonHtml) { Trees.Add(new IntermediateNodeReference(Parent, node)); } _foundNonHtml = originalState |= _foundNonHtml; }
public void Push(HtmlContentIntermediateNode node) { var builder = new StringBuilder(); var offsets = new List <(int offset, int sourceOffset)>(); if (_content != null && _position < _content.Length) { offsets.Add((0, _offsets[0].sourceOffset + _position)); builder.Append(_content, _position, _content.Length - _position); } for (var i = 0; i < node.Children.Count; i++) { var token = node.Children[i] as IntermediateToken; if (token != null && token.IsHtml) { offsets.Add((builder.Length, token.Source.Value.AbsoluteIndex)); builder.Append(token.Content); } } _content = builder.ToString(); _offsets = offsets; _textSource = new TextSource(_content); _position = 0; }
public override void VisitHtml(HtmlContentIntermediateNode node) { if (node.Source != null) { Items.Add(new InstrumentationItem(node, Parent, isLiteral: true, source: node.Source.Value)); } VisitDefault(node); }
public static void SetEncoded(this HtmlContentIntermediateNode node) { if (node == null) { throw new ArgumentNullException(nameof(node)); } node.Annotations[HasEncodedContent] = HasEncodedContent; }
public override void VisitHtml(HtmlContentIntermediateNode node) { for (var i = 0; i < node.Children.Count; i++) { if (node.Children[i] is IntermediateToken token) { Builder.Append(token.Content); } } }
public override void VisitHtml(HtmlContentIntermediateNode node) { if (node.HasDiagnostics) { // Treat node with errors as non-HTML _foundNonHtml = true; } // Visit Children base.VisitDefault(node); }
private static string GetHtmlContent(HtmlContentIntermediateNode node) { var builder = new StringBuilder(); var htmlTokens = node.Children.OfType <IntermediateToken>().Where(t => t.IsHtml); foreach (var htmlToken in htmlTokens) { builder.Append(htmlToken.Content); } return(builder.ToString()); }
public override void VisitMarkupSpan(MarkupChunkGenerator chunkGenerator, Span span) { if (span.Symbols.Count == 1) { var symbol = span.Symbols[0] as HtmlSymbol; if (symbol != null && symbol.Type == HtmlSymbolType.Unknown && symbol.Content.Length == 0) { // We don't want to create IR nodes for marker symbols. return; } } var source = BuildSourceSpanFromNode(span); var currentChildren = _builder.Current.Children; if (currentChildren.Count > 0 && currentChildren[currentChildren.Count - 1] is HtmlContentIntermediateNode) { var existingHtmlContent = (HtmlContentIntermediateNode)currentChildren[currentChildren.Count - 1]; if (existingHtmlContent.Source == null && source == null) { Combine(existingHtmlContent, span); return; } if (source != null && existingHtmlContent.Source != null && existingHtmlContent.Source.Value.FilePath == source.Value.FilePath && existingHtmlContent.Source.Value.AbsoluteIndex + existingHtmlContent.Source.Value.Length == source.Value.AbsoluteIndex) { Combine(existingHtmlContent, span); return; } } var contentNode = new HtmlContentIntermediateNode() { Source = source }; _builder.Push(contentNode); _builder.Add(new IntermediateToken() { Content = span.Content, Kind = TokenKind.Html, Source = source, }); _builder.Pop(); }
public override void VisitTagHelperHtmlAttribute(TagHelperHtmlAttributeIntermediateNode node) { var attribute = new ComponentAttributeIntermediateNode(node); _children.Add(attribute); // Since we don't support complex content, we can rewrite the inside of this // node to the rather simpler form that property nodes usually have. for (var i = 0; i < attribute.Children.Count; i++) { if (attribute.Children[i] is HtmlAttributeValueIntermediateNode htmlValue) { var newNode = new HtmlContentIntermediateNode() { Source = htmlValue.Source, }; for (var j = 0; j < htmlValue.Children.Count; j++) { newNode.Children.Add(htmlValue.Children[j]); } attribute.Children[i] = newNode; } else if (attribute.Children[i] is CSharpExpressionAttributeValueIntermediateNode expressionValue) { var newNode = new CSharpExpressionIntermediateNode() { Source = expressionValue.Source, }; for (var j = 0; j < expressionValue.Children.Count; j++) { newNode.Children.Add(expressionValue.Children[j]); } attribute.Children[i] = newNode; } else if (attribute.Children[i] is CSharpCodeAttributeValueIntermediateNode codeValue) { var newNode = new CSharpExpressionIntermediateNode() { Source = codeValue.Source, }; for (var j = 0; j < codeValue.Children.Count; j++) { newNode.Children.Add(codeValue.Children[j]); } attribute.Children[i] = newNode; } } }
private string GetContent(HtmlContentIntermediateNode node) { var builder = new StringBuilder(); for (var i = 0; i < node.Children.Count; i++) { if (node.Children[i] is IntermediateToken token && token.IsHtml) { builder.Append(token.Content); } } return(builder.ToString()); }
private static string GetHtmlContent(HtmlContentIntermediateNode node) { var builder = new StringBuilder(); for (var i = 0; i < node.Children.Count; i++) { var token = node.Children[i] as IntermediateToken; if (token != null && token.IsHtml) { builder.Append(token.Content); } } return(builder.ToString()); }
private void ProcessElement(HtmlContentIntermediateNode node, string cssScope) { // Add a minimized attribute whose name is simply the CSS scope for (var i = 0; i < node.Children.Count; i++) { var child = node.Children[i]; if (child is IntermediateToken token && token.IsHtml) { if (IsValidElement(token)) { node.Children.Insert(i + 1, new IntermediateToken() { Content = cssScope, Kind = TokenKind.Html, Source = null }); i++; } } } bool IsValidElement(IntermediateToken token) { var content = token.Content; var isValidToken = content.StartsWith("<", StringComparison.Ordinal) && !content.StartsWith("</", StringComparison.Ordinal) && !content.StartsWith("<!", StringComparison.Ordinal); /// <remarks> /// We want to avoid adding the CSS scope to elements that do not appear /// within the body element of the document. When this pass executes over the /// nodes, we don't have the ability to store whether we are a descandant of a /// `head` or `body` element so it is not possible to discern whether the tag /// is valid this way. Instead, we go for a straight-forward check on the tag /// name that we are currently inspecting. /// </remarks> var isInvalidTag = content.IndexOf("head", StringComparison.OrdinalIgnoreCase) >= 0 || content.IndexOf("meta", StringComparison.OrdinalIgnoreCase) >= 0 || content.IndexOf("title", StringComparison.OrdinalIgnoreCase) >= 0 || content.IndexOf("link", StringComparison.OrdinalIgnoreCase) >= 0 || content.IndexOf("base", StringComparison.OrdinalIgnoreCase) >= 0 || content.IndexOf("script", StringComparison.OrdinalIgnoreCase) >= 0 || content.IndexOf("style", StringComparison.OrdinalIgnoreCase) >= 0 || content.IndexOf("html", StringComparison.OrdinalIgnoreCase) >= 0; return(isValidToken && !isInvalidTag); } }
private static void RewriteHtmlAttributeContent(List <IntermediateNode> nodes, TagHelperHtmlAttributeIntermediateNode node) { switch (node.AttributeStructure) { case AttributeStructure.Minimized: nodes.Add(new HtmlContentIntermediateNode() { Children = { new IntermediateToken() { Content = node.AttributeName + " ", Kind = TokenKind.Html, } } }); break; // Blazor doesn't really care about preserving the fidelity of the attributes. case AttributeStructure.NoQuotes: case AttributeStructure.SingleQuotes: case AttributeStructure.DoubleQuotes: var htmlNode = new HtmlContentIntermediateNode(); nodes.Add(htmlNode); htmlNode.Children.Add(new IntermediateToken() { Content = node.AttributeName + "=\"", Kind = TokenKind.Html, }); for (var i = 0; i < node.Children[0].Children.Count; i++) { htmlNode.Children.Add(node.Children[0].Children[i]); } htmlNode.Children.Add(new IntermediateToken() { Content = "\" ", Kind = TokenKind.Html, }); break; } }
private void Combine(HtmlContentIntermediateNode node, SyntaxNode item) { node.Children.Add(new IntermediateToken() { Content = item.GetContent(), Kind = TokenKind.Html, Source = BuildSourceSpanFromNode(item), }); if (node.Source != null) { Debug.Assert(node.Source.Value.FilePath != null); node.Source = new SourceSpan( node.Source.Value.FilePath, node.Source.Value.AbsoluteIndex, node.Source.Value.LineIndex, node.Source.Value.CharacterIndex, node.Source.Value.Length + item.FullWidth); } }
private void Combine(HtmlContentIntermediateNode node, Span span) { node.Children.Add(new IntermediateToken() { Content = span.Content, Kind = TokenKind.Html, Source = BuildSourceSpanFromNode(span), }); if (node.Source != null) { Debug.Assert(node.Source.Value.FilePath != null); node.Source = new SourceSpan( node.Source.Value.FilePath, node.Source.Value.AbsoluteIndex, node.Source.Value.LineIndex, node.Source.Value.CharacterIndex, node.Source.Value.Length + span.Content.Length); } }
private void ProcessElement(HtmlContentIntermediateNode node, string cssScope) { // Add a minimized attribute whose name is simply the CSS scope for (var i = 0; i < node.Children.Count; i++) { var child = node.Children[i]; if (child is IntermediateToken token && token.IsHtml) { var content = token.Content; if (content.StartsWith("<", StringComparison.Ordinal) && !content.StartsWith("</", StringComparison.Ordinal)) { node.Children.Insert(i + 1, new IntermediateToken() { Content = cssScope, Kind = TokenKind.Html, Source = null }); i++; } } } }
public static bool IsEncoded(this HtmlContentIntermediateNode node) { return(ReferenceEquals(node.Annotations[HasEncodedContent], HasEncodedContent)); }
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 || string.IsNullOrEmpty(token.Content)) { // 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; } } } // If we reach here, we don't have newlines, tabs or non-ascii characters in this node. // If we can successfully decode all HTML entities(if any) in this node, we can safely let it call AddContent. var decodedContent = new string[node.Children.Count]; for (var i = 0; i < node.Children.Count; i++) { var child = node.Children[i]; if (!(child is IntermediateToken token) || !token.IsHtml || string.IsNullOrEmpty(token.Content)) { // We only care about Html tokens. continue; } if (TryDecodeHtmlEntities(token.Content, out var decoded)) { decodedContent[i] = decoded; } else { node.SetEncoded(); return; } } // If we reach here, it means we have successfully decoded all content. // Replace all token content with the decoded value. for (var i = 0; i < node.Children.Count; i++) { var child = node.Children[i]; if (!(child is IntermediateToken token) || !token.IsHtml || string.IsNullOrEmpty(token.Content)) { // We only care about Html tokens. continue; } token.Content = decodedContent[i]; } }
public virtual void VisitHtml(HtmlContentIntermediateNode node) { VisitDefault(node); }
public override void VisitHtml(HtmlContentIntermediateNode node) { Builder.Append(Encode(node.Children)); }
public abstract void WriteHtmlContent(CodeRenderingContext context, HtmlContentIntermediateNode node);
public override void VisitHtml(HtmlContentIntermediateNode node) { // Visit Children base.VisitDefault(node); }
public override void VisitHtml(HtmlContentIntermediateNode node) { Context.NodeWriter.WriteHtmlContent(Context, node); }
public override void VisitMarkupTextLiteral(MarkupTextLiteralSyntax node) { var context = node.GetSpanContext(); if (context != null && context.ChunkGenerator == SpanChunkGenerator.Null) { base.VisitMarkupTextLiteral(node); return; } if (node.LiteralTokens.Count == 1) { var token = node.LiteralTokens[0]; if (token != null && token.Kind == SyntaxKind.Marker && token.Content.Length == 0) { // We don't want to create IR nodes for marker tokens. base.VisitMarkupTextLiteral(node); return; } } var source = BuildSourceSpanFromNode(node); var currentChildren = _builder.Current.Children; if (currentChildren.Count > 0 && currentChildren[currentChildren.Count - 1] is HtmlContentIntermediateNode) { var existingHtmlContent = (HtmlContentIntermediateNode)currentChildren[currentChildren.Count - 1]; if (existingHtmlContent.Source == null && source == null) { Combine(existingHtmlContent, node); base.VisitMarkupTextLiteral(node); return; } if (source != null && existingHtmlContent.Source != null && existingHtmlContent.Source.Value.FilePath == source.Value.FilePath && existingHtmlContent.Source.Value.AbsoluteIndex + existingHtmlContent.Source.Value.Length == source.Value.AbsoluteIndex) { Combine(existingHtmlContent, node); base.VisitMarkupTextLiteral(node); return; } } var contentNode = new HtmlContentIntermediateNode() { Source = source }; _builder.Push(contentNode); _builder.Add(new IntermediateToken() { Content = node.GetContent(), Kind = TokenKind.Html, Source = source, }); _builder.Pop(); base.VisitMarkupTextLiteral(node); }