Exemple #1
0
        public override void WriteHtmlElement(CodeRenderingContext context, HtmlElementIntermediateNode node)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (node == null)
            {
                throw new ArgumentNullException(nameof(node));
            }

            context.RenderChildren(node);
        }
Exemple #2
0
        private HtmlElementIntermediateNode RewriteAsElement(TagHelperIntermediateNode node)
        {
            var result = new HtmlElementIntermediateNode()
            {
                Source  = node.Source,
                TagName = node.TagName,
            };

            for (var i = 0; i < node.Diagnostics.Count; i++)
            {
                result.Diagnostics.Add(node.Diagnostics[i]);
            }

            var visitor = new ElementRewriteVisitor(result.Children);

            visitor.Visit(node);

            return(result);
        }
Exemple #3
0
 public abstract void WriteHtmlElement(CodeRenderingContext context, HtmlElementIntermediateNode node);
            private void RewriteChildren(RazorSourceDocument source, IntermediateNode node)
            {
                // We expect all of the immediate children of a node (together) to comprise
                // a well-formed tree of elements and components.
                var stack = new Stack <IntermediateNode>();

                stack.Push(node);

                // Make a copy, we will clear and rebuild the child collection of this node.
                var children = node.Children.ToArray();

                node.Children.Clear();

                // Due to the way Anglesharp parses HTML (tags at a time) we need to keep track of some state.
                // This handles cases like:
                //
                //  <foo bar="17" baz="@baz" />
                //
                // This will lower like:
                //
                //  HtmlContent <foo bar="17"
                //  HtmlAttribute baz=" - "
                //      CSharpAttributeValue baz
                //  HtmlContent  />
                //
                // We need to consume HTML until we see the 'end tag' for <foo /> and then we can
                // the attributes from the parsed HTML and the CSharpAttribute value.
                var parser     = new HtmlParser(source);
                var attributes = new List <HtmlAttributeIntermediateNode>();

                for (var i = 0; i < children.Length; i++)
                {
                    if (children[i] is HtmlContentIntermediateNode htmlNode)
                    {
                        parser.Push(htmlNode);
                        var tokens = parser.Get();
                        foreach (var token in tokens)
                        {
                            // We have to call this before get. Anglesharp doesn't return the start position
                            // of tokens.
                            var start = parser.GetCurrentLocation();

                            // We have to set the Location explicitly otherwise we would need to include
                            // the token in every call to the parser.
                            parser.SetLocation(token);

                            var end = parser.GetCurrentLocation();

                            if (token.Type == HtmlTokenType.EndOfFile)
                            {
                                break;
                            }

                            switch (token.Type)
                            {
                            case HtmlTokenType.Doctype:
                            {
                                // DocType isn't meaningful in Blazor. We don't process them in the runtime
                                // it wouldn't really mean much anyway since we build a DOM directly rather
                                // than letting the user-agent parse the document.
                                //
                                // For now, <!DOCTYPE html> and similar things will just be skipped by the compiler
                                // unless we come up with something more meaningful to do.
                                break;
                            }

                            case HtmlTokenType.Character:
                            {
                                // Text content
                                var span = new SourceSpan(start, end.AbsoluteIndex - start.AbsoluteIndex);
                                stack.Peek().Children.Add(new HtmlContentIntermediateNode()
                                    {
                                        Children =
                                        {
                                            new IntermediateToken()
                                            {
                                                Content = token.Data,
                                                Kind    = TokenKind.Html,
                                                Source  = span,
                                            }
                                        },
                                        Source = span,
                                    });
                                break;
                            }

                            case HtmlTokenType.StartTag:
                            {
                                var tag = token.AsTag();

                                if (token.Type == HtmlTokenType.StartTag)
                                {
                                    var elementNode = new HtmlElementIntermediateNode()
                                    {
                                        TagName = parser.GetTagNameOriginalCasing(tag),
                                        Source  = new SourceSpan(start, end.AbsoluteIndex - start.AbsoluteIndex),
                                    };

                                    stack.Peek().Children.Add(elementNode);
                                    stack.Push(elementNode);

                                    for (var j = 0; j < tag.Attributes.Count; j++)
                                    {
                                        // Unfortunately Anglesharp doesn't provide positions for attributes
                                        // so we can't record the spans here.
                                        var attribute = tag.Attributes[j];
                                        stack.Peek().Children.Add(CreateAttributeNode(attribute));
                                    }

                                    for (var j = 0; j < attributes.Count; j++)
                                    {
                                        stack.Peek().Children.Add(attributes[j]);
                                    }
                                    attributes.Clear();
                                }

                                if (tag.IsSelfClosing || VoidElements.Contains(tag.Data))
                                {
                                    // We can't possibly hit an error here since we just added an element node.
                                    stack.Pop();
                                }

                                break;
                            }

                            case HtmlTokenType.EndTag:
                            {
                                var tag = token.AsTag();

                                var popped = stack.Pop();
                                if (stack.Count == 0)
                                {
                                    // If we managed to 'bottom out' the stack then we have an unbalanced end tag.
                                    // Put back the current node so we don't crash.
                                    stack.Push(popped);

                                    var tagName = parser.GetTagNameOriginalCasing(tag);
                                    var span    = new SourceSpan(start, end.AbsoluteIndex - start.AbsoluteIndex);

                                    var diagnostic = VoidElements.Contains(tagName)
                                                ? BlazorDiagnosticFactory.Create_UnexpectedClosingTagForVoidElement(span, tagName)
                                                : BlazorDiagnosticFactory.Create_UnexpectedClosingTag(span, tagName);
                                    popped.Children.Add(new HtmlElementIntermediateNode()
                                        {
                                            Diagnostics =
                                            {
                                                diagnostic,
                                            },
                                            TagName = tagName,
                                            Source  = span,
                                        });
                                }
                                else if (!string.Equals(tag.Name, ((HtmlElementIntermediateNode)popped).TagName, StringComparison.OrdinalIgnoreCase))
                                {
                                    var span       = new SourceSpan(start, end.AbsoluteIndex - start.AbsoluteIndex);
                                    var diagnostic = BlazorDiagnosticFactory.Create_MismatchedClosingTag(span, ((HtmlElementIntermediateNode)popped).TagName, token.Data);
                                    popped.Diagnostics.Add(diagnostic);
                                }
                                else
                                {
                                    // Happy path.
                                    //
                                    // We need to compute a new source span because when we found the start tag before we knew
                                    // the end position of the tag.
                                    var length = end.AbsoluteIndex - popped.Source.Value.AbsoluteIndex;
                                    popped.Source = new SourceSpan(
                                        popped.Source.Value.FilePath,
                                        popped.Source.Value.AbsoluteIndex,
                                        popped.Source.Value.LineIndex,
                                        popped.Source.Value.CharacterIndex,
                                        length);
                                }

                                break;
                            }

                            case HtmlTokenType.Comment:
                                break;

                            default:
                                throw new InvalidCastException($"Unsupported token type: {token.Type.ToString()}");
                            }
                        }
                    }
                    else if (children[i] is HtmlAttributeIntermediateNode htmlAttribute)
                    {
                        // Buffer the attribute for now, it will get written out as part of a tag.
                        attributes.Add(htmlAttribute);
                    }
                    else
                    {
                        // not HTML, or already rewritten.
                        stack.Peek().Children.Add(children[i]);
                    }
                }

                var extraContent = parser.GetUnparsedContent();

                if (!string.IsNullOrEmpty(extraContent))
                {
                    // extra HTML - almost certainly invalid because it couldn't be parsed.
                    var start = parser.GetCurrentLocation();
                    var end   = parser.GetCurrentLocation(extraContent.Length);
                    var span  = new SourceSpan(start, end.AbsoluteIndex - start.AbsoluteIndex);
                    stack.Peek().Children.Add(new HtmlContentIntermediateNode()
                    {
                        Children =
                        {
                            new IntermediateToken()
                            {
                                Content = extraContent,
                                Kind    = TokenKind.Html,
                                Source  = span,
                            }
                        },
                        Diagnostics =
                        {
                            BlazorDiagnosticFactory.Create_InvalidHtmlContent(span, extraContent),
                        },
                        Source = span,
                    });
                }

                while (stack.Count > 1)
                {
                    // not balanced
                    var popped     = (HtmlElementIntermediateNode)stack.Pop();
                    var diagnostic = BlazorDiagnosticFactory.Create_UnclosedTag(popped.Source, popped.TagName);
                    popped.Diagnostics.Add(diagnostic);
                }
            }