AngleSharp by AngleSharp

<PackageReference Include="AngleSharp" Version="0.9.11" />

 HtmlDomBuilder

sealed class HtmlDomBuilder
Represents the Tree construction as specified in 8.2.5 Tree construction, on the following page: http://www.w3.org/html/wg/drafts/html/master/syntax.html
using AngleSharp.Dom; using AngleSharp.Dom.Events; using AngleSharp.Dom.Html; using AngleSharp.Dom.Mathml; using AngleSharp.Dom.Svg; using AngleSharp.Extensions; using AngleSharp.Html; using AngleSharp.Network; using AngleSharp.Services; using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Threading.Tasks; namespace AngleSharp.Parser.Html { internal sealed class HtmlDomBuilder { private readonly HtmlTokenizer _tokenizer; private readonly HtmlDocument _document; private readonly List<Element> _openElements; private readonly List<Element> _formattingElements; private readonly Stack<HtmlTreeMode> _templateModes; private readonly IElementFactory<HtmlElement> _htmlFactory; private readonly IElementFactory<MathElement> _mathFactory; private readonly IElementFactory<SvgElement> _svgFactory; private HtmlFormElement _currentFormElement; private HtmlTreeMode _currentMode; private HtmlTreeMode _previousMode; private HtmlParserOptions _options; private Element _fragmentContext; private bool _foster; private bool _frameset; private Task _waiting; public bool IsFragmentCase => _fragmentContext != null; public Element AdjustedCurrentNode { get { if (_fragmentContext == null || _openElements.Count != 1) return CurrentNode; return _fragmentContext; } } public Element CurrentNode { get { if (_openElements.Count <= 0) return null; return _openElements[_openElements.Count - 1]; } } public HtmlDomBuilder(HtmlDocument document) { IConfiguration options = document.Options; IBrowsingContext context = document.Context; IEntityProvider resolver = options.GetProvider<IEntityProvider>() ?? HtmlEntityService.Resolver; _tokenizer = new HtmlTokenizer(document.Source, resolver); _tokenizer.Error += delegate(object _, HtmlErrorEvent error) { context.Fire(error); }; _document = document; _openElements = new List<Element>(); _templateModes = new Stack<HtmlTreeMode>(); _formattingElements = new List<Element>(); _frameset = true; _currentMode = HtmlTreeMode.Initial; _htmlFactory = options.GetFactory<IElementFactory<HtmlElement>>(); _mathFactory = options.GetFactory<IElementFactory<MathElement>>(); _svgFactory = options.GetFactory<IElementFactory<SvgElement>>(); } public async Task<HtmlDocument> ParseAsync(HtmlParserOptions options, CancellationToken cancelToken) { TextSource source = _document.Source; _tokenizer.IsStrictMode = options.IsStrictMode; _options = options; HtmlToken token; do { if (source.Length - source.Index < 1024) await source.PrefetchAsync(8192, cancelToken).ConfigureAwait(false); token = _tokenizer.Get(); Consume(token); if (_waiting != null) { await _waiting.ConfigureAwait(false); _waiting = null; } } while (token.Type != HtmlTokenType.EndOfFile); return _document; } public HtmlDocument Parse(HtmlParserOptions options) { HtmlToken htmlToken = null; _tokenizer.IsStrictMode = options.IsStrictMode; _options = options; do { htmlToken = _tokenizer.Get(); Consume(htmlToken); _waiting?.Wait(); _waiting = null; } while (htmlToken.Type != HtmlTokenType.EndOfFile); return _document; } public HtmlDocument ParseFragment(HtmlParserOptions options, Element context) { if (context == null) throw new ArgumentNullException("context"); _fragmentContext = context; string localName = context.LocalName; if (localName.IsOneOf(TagNames.Title, TagNames.Textarea)) _tokenizer.State = HtmlParseMode.RCData; else if (localName.IsOneOf(TagNames.Style, TagNames.Xmp, TagNames.Iframe, TagNames.NoEmbed, TagNames.NoFrames)) { _tokenizer.State = HtmlParseMode.Rawtext; } else if (localName.Is(TagNames.Script)) { _tokenizer.State = HtmlParseMode.Script; } else if (localName.Is(TagNames.Plaintext)) { _tokenizer.State = HtmlParseMode.Plaintext; } else if (localName.Is(TagNames.NoScript) && options.IsScripting) { _tokenizer.State = HtmlParseMode.Rawtext; } HtmlHtmlElement htmlHtmlElement = new HtmlHtmlElement(_document, null); _document.AddNode(htmlHtmlElement); _openElements.Add(htmlHtmlElement); if (context is HtmlTemplateElement) _templateModes.Push(HtmlTreeMode.InTemplate); Reset(); _tokenizer.IsAcceptingCharacterData = ((AdjustedCurrentNode.Flags & NodeFlags.HtmlMember) != NodeFlags.HtmlMember); do { if (context is HtmlFormElement) { _currentFormElement = (HtmlFormElement)context; break; } context = (context.ParentElement as Element); } while (context != null); return Parse(options); } private void Restart() { _currentMode = HtmlTreeMode.Initial; _tokenizer.State = HtmlParseMode.PCData; _document.ReplaceAll(null, true); _frameset = true; _openElements.Clear(); _formattingElements.Clear(); _templateModes.Clear(); } private void Reset() { int num = _openElements.Count - 1; HtmlTreeMode? nullable; while (true) { if (num < 0) return; Element element = _openElements[num]; bool flag = num == 0; if (flag && _fragmentContext != null) element = _fragmentContext; nullable = element.SelectMode(flag, _templateModes); if (nullable.HasValue) break; num--; } _currentMode = nullable.Value; } private void Consume(HtmlToken token) { Element adjustedCurrentNode = AdjustedCurrentNode; if (adjustedCurrentNode == null || token.Type == HtmlTokenType.EndOfFile || (adjustedCurrentNode.Flags & NodeFlags.HtmlMember) == NodeFlags.HtmlMember || ((adjustedCurrentNode.Flags & NodeFlags.HtmlTip) == NodeFlags.HtmlTip && token.IsHtmlCompatible) || ((adjustedCurrentNode.Flags & NodeFlags.MathTip) == NodeFlags.MathTip && token.IsMathCompatible) || ((adjustedCurrentNode.Flags & NodeFlags.MathMember) == NodeFlags.MathMember && token.IsSvg && adjustedCurrentNode.LocalName.Is(TagNames.AnnotationXml))) Home(token); else Foreign(token); } private void Home(HtmlToken token) { switch (_currentMode) { case HtmlTreeMode.Initial: Initial(token); break; case HtmlTreeMode.BeforeHtml: BeforeHtml(token); break; case HtmlTreeMode.BeforeHead: BeforeHead(token); break; case HtmlTreeMode.InHead: InHead(token); break; case HtmlTreeMode.InHeadNoScript: InHeadNoScript(token); break; case HtmlTreeMode.AfterHead: AfterHead(token); break; case HtmlTreeMode.InBody: InBody(token); break; case HtmlTreeMode.Text: Text(token); break; case HtmlTreeMode.InTable: InTable(token); break; case HtmlTreeMode.InCaption: InCaption(token); break; case HtmlTreeMode.InColumnGroup: InColumnGroup(token); break; case HtmlTreeMode.InTableBody: InTableBody(token); break; case HtmlTreeMode.InRow: InRow(token); break; case HtmlTreeMode.InCell: InCell(token); break; case HtmlTreeMode.InSelect: InSelect(token); break; case HtmlTreeMode.InSelectInTable: InSelectInTable(token); break; case HtmlTreeMode.InTemplate: InTemplate(token); break; case HtmlTreeMode.AfterBody: AfterBody(token); break; case HtmlTreeMode.InFrameset: InFrameset(token); break; case HtmlTreeMode.AfterFrameset: AfterFrameset(token); break; case HtmlTreeMode.AfterAfterBody: AfterAfterBody(token); break; case HtmlTreeMode.AfterAfterFrameset: AfterAfterFrameset(token); break; } } private void Initial(HtmlToken token) { switch (token.Type) { case HtmlTokenType.Doctype: { HtmlDoctypeToken htmlDoctypeToken = (HtmlDoctypeToken)token; if (!htmlDoctypeToken.IsValid) RaiseErrorOccurred(HtmlParseError.DoctypeInvalid, token); _document.AddNode(new DocumentType(_document, htmlDoctypeToken.Name ?? string.Empty) { SystemIdentifier = htmlDoctypeToken.SystemIdentifier, PublicIdentifier = htmlDoctypeToken.PublicIdentifier }); _document.QuirksMode = htmlDoctypeToken.GetQuirksMode(); _currentMode = HtmlTreeMode.BeforeHtml; return; } case HtmlTokenType.Character: token.TrimStart(); if (token.IsEmpty) return; break; case HtmlTokenType.Comment: _document.AddComment(token); return; } if (!_options.IsEmbedded) { RaiseErrorOccurred(HtmlParseError.DoctypeMissing, token); _document.QuirksMode = QuirksMode.On; } _currentMode = HtmlTreeMode.BeforeHtml; BeforeHtml(token); } private void BeforeHtml(HtmlToken token) { switch (token.Type) { case HtmlTokenType.Character: token.TrimStart(); if (token.IsEmpty) return; break; case HtmlTokenType.Comment: _document.AddComment(token); return; case HtmlTokenType.StartTag: if (token.Name.Is(TagNames.Html)) { AddRoot(token.AsTag()); _currentMode = HtmlTreeMode.BeforeHead; return; } break; case HtmlTokenType.EndTag: if (!TagNames.AllBeforeHead.Contains(token.Name)) { RaiseErrorOccurred(HtmlParseError.TagCannotEndHere, token); return; } break; case HtmlTokenType.Doctype: RaiseErrorOccurred(HtmlParseError.DoctypeTagInappropriate, token); return; } BeforeHtml(HtmlTagToken.Open(TagNames.Html)); BeforeHead(token); } private void BeforeHead(HtmlToken token) { switch (token.Type) { case HtmlTokenType.Character: token.TrimStart(); if (token.IsEmpty) return; break; case HtmlTokenType.StartTag: { string name = token.Name; if (name.Is(TagNames.Html)) { InBody(token); return; } if (name.Is(TagNames.Head)) { AddElement(new HtmlHeadElement(_document, null), token.AsTag(), false); _currentMode = HtmlTreeMode.InHead; return; } break; } case HtmlTokenType.EndTag: if (!TagNames.AllBeforeHead.Contains(token.Name)) { RaiseErrorOccurred(HtmlParseError.TagCannotEndHere, token); return; } break; case HtmlTokenType.Comment: CurrentNode.AddComment(token); return; case HtmlTokenType.Doctype: RaiseErrorOccurred(HtmlParseError.DoctypeTagInappropriate, token); return; } BeforeHead(HtmlTagToken.Open(TagNames.Head)); InHead(token); } private void InHead(HtmlToken token) { switch (token.Type) { case HtmlTokenType.Character: { string text = token.TrimStart(); AddCharacters(text); if (token.IsEmpty) return; break; } case HtmlTokenType.Comment: CurrentNode.AddComment(token); return; case HtmlTokenType.Doctype: RaiseErrorOccurred(HtmlParseError.DoctypeTagInappropriate, token); return; case HtmlTokenType.StartTag: { string name2 = token.Name; if (name2.Is(TagNames.Html)) { InBody(token); return; } if (name2.Is(TagNames.Meta)) { HtmlMetaElement htmlMetaElement = new HtmlMetaElement(_document, null); AddElement(htmlMetaElement, token.AsTag(), true); Encoding encoding = htmlMetaElement.GetEncoding(); CloseCurrentNode(); if (encoding != null) try { _document.Source.CurrentEncoding = encoding; } catch (NotSupportedException) { Restart(); } return; } if (TagNames.AllHeadBase.Contains(name2)) { AddElement(token.AsTag(), true); CloseCurrentNode(); return; } if (name2.Is(TagNames.Title)) { RCDataAlgorithm(token.AsTag()); return; } if (name2.IsOneOf(TagNames.Style, TagNames.NoFrames) || (_options.IsScripting && name2.Is(TagNames.NoScript))) { RawtextAlgorithm(token.AsTag()); return; } if (name2.Is(TagNames.NoScript)) { AddElement(token.AsTag(), false); _currentMode = HtmlTreeMode.InHeadNoScript; return; } if (name2.Is(TagNames.Script)) { HtmlScriptElement element = new HtmlScriptElement(_document, null, true, IsFragmentCase); AddElement(element, token.AsTag(), false); _tokenizer.State = HtmlParseMode.Script; _previousMode = _currentMode; _currentMode = HtmlTreeMode.Text; return; } if (name2.Is(TagNames.Head)) { RaiseErrorOccurred(HtmlParseError.HeadTagMisplaced, token); return; } if (name2.Is(TagNames.Template)) { AddElement(new HtmlTemplateElement(_document, null), token.AsTag(), false); _formattingElements.AddScopeMarker(); _frameset = false; _currentMode = HtmlTreeMode.InTemplate; _templateModes.Push(HtmlTreeMode.InTemplate); return; } break; } case HtmlTokenType.EndTag: { string name = token.Name; if (name.Is(TagNames.Head)) { CloseCurrentNode(); _currentMode = HtmlTreeMode.AfterHead; _waiting = _document.WaitForReadyAsync(); return; } if (name.Is(TagNames.Template)) { if (TagCurrentlyOpen(TagNames.Template)) { GenerateImpliedEndTags(); if (!CurrentNode.LocalName.Is(TagNames.Template)) RaiseErrorOccurred(HtmlParseError.TagClosingMismatch, token); CloseTemplate(); } else RaiseErrorOccurred(HtmlParseError.TagInappropriate, token); return; } if (!name.IsOneOf(TagNames.Html, TagNames.Body, TagNames.Br)) { RaiseErrorOccurred(HtmlParseError.TagCannotEndHere, token); return; } break; } } CloseCurrentNode(); _currentMode = HtmlTreeMode.AfterHead; AfterHead(token); } private void InHeadNoScript(HtmlToken token) { switch (token.Type) { case HtmlTokenType.Character: { string text = token.TrimStart(); AddCharacters(text); if (token.IsEmpty) return; break; } case HtmlTokenType.Comment: InHead(token); return; case HtmlTokenType.StartTag: { string name2 = token.Name; if (TagNames.AllNoScript.Contains(name2)) { InHead(token); return; } if (name2.Is(TagNames.Html)) { InBody(token); return; } if (name2.IsOneOf(TagNames.Head, TagNames.NoScript)) { RaiseErrorOccurred(HtmlParseError.TagInappropriate, token); return; } break; } case HtmlTokenType.EndTag: { string name = token.Name; if (name.Is(TagNames.NoScript)) { CloseCurrentNode(); _currentMode = HtmlTreeMode.InHead; return; } if (!name.Is(TagNames.Br)) { RaiseErrorOccurred(HtmlParseError.TagCannotEndHere, token); return; } break; } case HtmlTokenType.Doctype: RaiseErrorOccurred(HtmlParseError.DoctypeTagInappropriate, token); return; } RaiseErrorOccurred(HtmlParseError.TokenNotPossible, token); CloseCurrentNode(); _currentMode = HtmlTreeMode.InHead; InHead(token); } private void AfterHead(HtmlToken token) { switch (token.Type) { case HtmlTokenType.Character: { string text = token.TrimStart(); AddCharacters(text); if (token.IsEmpty) return; break; } case HtmlTokenType.Comment: CurrentNode.AddComment(token); return; case HtmlTokenType.Doctype: RaiseErrorOccurred(HtmlParseError.DoctypeTagInappropriate, token); return; case HtmlTokenType.StartTag: { string name = token.Name; if (name.Is(TagNames.Html)) { InBody(token); return; } if (name.Is(TagNames.Body)) { AfterHeadStartTagBody(token.AsTag()); return; } if (name.Is(TagNames.Frameset)) { AddElement(new HtmlFrameSetElement(_document, null), token.AsTag(), false); _currentMode = HtmlTreeMode.InFrameset; return; } if (TagNames.AllHeadNoTemplate.Contains(name)) { RaiseErrorOccurred(HtmlParseError.TagMustBeInHead, token); int count = _openElements.Count; Element element = _document.Head as Element; _openElements.Add(element); InHead(token); CloseNode(element); return; } if (name.Is(TagNames.Head)) { RaiseErrorOccurred(HtmlParseError.HeadTagMisplaced, token); return; } break; } case HtmlTokenType.EndTag: if (!token.Name.IsOneOf(TagNames.Html, TagNames.Body, TagNames.Br)) { RaiseErrorOccurred(HtmlParseError.TagCannotEndHere, token); return; } break; } AfterHeadStartTagBody(HtmlTagToken.Open(TagNames.Body)); _frameset = true; Home(token); } private void InBodyStartTag(HtmlTagToken tag) { string name = tag.Name; if (name.Is(TagNames.Div)) { if (IsInButtonScope()) InBodyEndTagParagraph(tag); AddElement(tag, false); } else if (name.Is(TagNames.A)) { for (int num = _formattingElements.Count - 1; num >= 0; num--) { if (_formattingElements[num] == null) break; if (_formattingElements[num].LocalName.Is(TagNames.A)) { Element element = _formattingElements[num]; RaiseErrorOccurred(HtmlParseError.AnchorNested, tag); HeisenbergAlgorithm(HtmlTagToken.Close(TagNames.A)); CloseNode(element); _formattingElements.Remove(element); break; } } ReconstructFormatting(); HtmlAnchorElement element2 = new HtmlAnchorElement(_document, null); AddElement(element2, tag, false); _formattingElements.AddFormatting(element2); } else if (name.Is(TagNames.Span)) { ReconstructFormatting(); AddElement(tag, false); } else if (name.Is(TagNames.Li)) { InBodyStartTagListItem(tag); } else if (name.Is(TagNames.Img)) { InBodyStartTagBreakrow(tag); } else if (name.IsOneOf(TagNames.Ul, TagNames.P)) { if (IsInButtonScope()) InBodyEndTagParagraph(tag); AddElement(tag, false); } else if (TagNames.AllSemanticFormatting.Contains(name)) { ReconstructFormatting(); _formattingElements.AddFormatting(AddElement(tag, false)); } else if (name.Is(TagNames.Script)) { InHead(tag); } else if (TagNames.AllHeadings.Contains(name)) { if (IsInButtonScope()) InBodyEndTagParagraph(tag); if (TagNames.AllHeadings.Contains(CurrentNode.LocalName)) { RaiseErrorOccurred(HtmlParseError.HeadingNested, tag); CloseCurrentNode(); } AddElement(new HtmlHeadingElement(_document, name, null), tag, false); } else if (name.Is(TagNames.Input)) { ReconstructFormatting(); AddElement(new HtmlInputElement(_document, null), tag, true); CloseCurrentNode(); if (!tag.GetAttribute(AttributeNames.Type).Isi(AttributeNames.Hidden)) _frameset = false; } else if (name.Is(TagNames.Form)) { if (_currentFormElement == null) { if (IsInButtonScope()) InBodyEndTagParagraph(tag); _currentFormElement = new HtmlFormElement(_document, null); AddElement(_currentFormElement, tag, false); } else RaiseErrorOccurred(HtmlParseError.FormAlreadyOpen, tag); } else if (TagNames.AllBody.Contains(name)) { if (IsInButtonScope()) InBodyEndTagParagraph(tag); AddElement(tag, false); } else if (TagNames.AllClassicFormatting.Contains(name)) { ReconstructFormatting(); _formattingElements.AddFormatting(AddElement(tag, false)); } else if (TagNames.AllHead.Contains(name)) { InHead(tag); } else if (name.IsOneOf(TagNames.Pre, TagNames.Listing)) { if (IsInButtonScope()) InBodyEndTagParagraph(tag); AddElement(tag, false); _frameset = false; PreventNewLine(); } else if (name.Is(TagNames.Button)) { if (IsInScope(TagNames.Button)) { RaiseErrorOccurred(HtmlParseError.ButtonInScope, tag); InBodyEndTagBlock(tag); InBody(tag); } else { ReconstructFormatting(); AddElement(new HtmlButtonElement(_document, null), tag, false); _frameset = false; } } else if (name.Is(TagNames.Table)) { if (_document.QuirksMode != QuirksMode.On && IsInButtonScope()) InBodyEndTagParagraph(tag); AddElement(new HtmlTableElement(_document, null), tag, false); _frameset = false; _currentMode = HtmlTreeMode.InTable; } else if (TagNames.AllBodyBreakrow.Contains(name)) { InBodyStartTagBreakrow(tag); } else if (TagNames.AllBodyClosed.Contains(name)) { AddElement(tag, true); CloseCurrentNode(); } else if (name.Is(TagNames.Hr)) { if (IsInButtonScope()) InBodyEndTagParagraph(tag); AddElement(new HtmlHrElement(_document, null), tag, true); CloseCurrentNode(); _frameset = false; } else if (name.Is(TagNames.Textarea)) { AddElement(new HtmlTextAreaElement(_document, null), tag, false); _tokenizer.State = HtmlParseMode.RCData; _previousMode = _currentMode; _frameset = false; _currentMode = HtmlTreeMode.Text; PreventNewLine(); } else if (name.Is(TagNames.Select)) { ReconstructFormatting(); AddElement(new HtmlSelectElement(_document, null), tag, false); _frameset = false; switch (_currentMode) { case HtmlTreeMode.InTable: case HtmlTreeMode.InCaption: case HtmlTreeMode.InTableBody: case HtmlTreeMode.InRow: case HtmlTreeMode.InCell: _currentMode = HtmlTreeMode.InSelectInTable; break; default: _currentMode = HtmlTreeMode.InSelect; break; } } else if (name.IsOneOf(TagNames.Optgroup, TagNames.Option)) { if (CurrentNode.LocalName.Is(TagNames.Option)) InBodyEndTagAnythingElse(HtmlTagToken.Close(TagNames.Option)); ReconstructFormatting(); AddElement(tag, false); } else if (name.IsOneOf(TagNames.Dd, TagNames.Dt)) { InBodyStartTagDefinitionItem(tag); } else if (name.Is(TagNames.Iframe)) { _frameset = false; RawtextAlgorithm(tag); } else if (TagNames.AllBodyObsolete.Contains(name)) { ReconstructFormatting(); AddElement(tag, false); _formattingElements.AddScopeMarker(); _frameset = false; } else if (name.Is(TagNames.Image)) { RaiseErrorOccurred(HtmlParseError.ImageTagNamedWrong, tag); tag.Name = TagNames.Img; InBodyStartTagBreakrow(tag); } else if (name.Is(TagNames.NoBr)) { ReconstructFormatting(); if (IsInScope(TagNames.NoBr)) { RaiseErrorOccurred(HtmlParseError.NobrInScope, tag); HeisenbergAlgorithm(tag); ReconstructFormatting(); } _formattingElements.AddFormatting(AddElement(tag, false)); } else if (name.Is(TagNames.Xmp)) { if (IsInButtonScope()) InBodyEndTagParagraph(tag); ReconstructFormatting(); _frameset = false; RawtextAlgorithm(tag); } else if (name.IsOneOf(TagNames.Rb, TagNames.Rtc)) { if (IsInScope(TagNames.Ruby)) { GenerateImpliedEndTags(); if (!CurrentNode.LocalName.Is(TagNames.Ruby)) RaiseErrorOccurred(HtmlParseError.TagDoesNotMatchCurrentNode, tag); } AddElement(tag, false); } else if (name.IsOneOf(TagNames.Rp, TagNames.Rt)) { if (IsInScope(TagNames.Ruby)) { GenerateImpliedEndTagsExceptFor(TagNames.Rtc); if (!CurrentNode.LocalName.IsOneOf(TagNames.Ruby, TagNames.Rtc)) RaiseErrorOccurred(HtmlParseError.TagDoesNotMatchCurrentNode, tag); } AddElement(tag, false); } else if (name.Is(TagNames.NoEmbed)) { RawtextAlgorithm(tag); } else if (name.Is(TagNames.NoScript)) { if (_options.IsScripting) RawtextAlgorithm(tag); else { ReconstructFormatting(); AddElement(tag, false); } } else if (name.Is(TagNames.Math)) { MathElement element3 = new MathElement(_document, name, null, NodeFlags.None); ReconstructFormatting(); AddElement(element3.Setup(tag)); if (tag.IsSelfClosing) CloseNode(element3); } else if (name.Is(TagNames.Svg)) { SvgElement element4 = new SvgElement(_document, name, null, NodeFlags.None); ReconstructFormatting(); AddElement(element4.Setup(tag)); if (tag.IsSelfClosing) CloseNode(element4); } else if (name.Is(TagNames.Plaintext)) { if (IsInButtonScope()) InBodyEndTagParagraph(tag); AddElement(tag, false); _tokenizer.State = HtmlParseMode.Plaintext; } else if (name.Is(TagNames.Frameset)) { RaiseErrorOccurred(HtmlParseError.FramesetMisplaced, tag); if (_openElements.Count != 1 && _openElements[1].LocalName.Is(TagNames.Body) && _frameset) { _openElements[1].RemoveFromParent(); while (_openElements.Count > 1) { CloseCurrentNode(); } AddElement(new HtmlFrameSetElement(_document, null), tag, false); _currentMode = HtmlTreeMode.InFrameset; } } else if (name.Is(TagNames.Html)) { RaiseErrorOccurred(HtmlParseError.HtmlTagMisplaced, tag); if (_templateModes.Count == 0) _openElements[0].SetUniqueAttributes(tag.Attributes); } else if (name.Is(TagNames.Body)) { RaiseErrorOccurred(HtmlParseError.BodyTagMisplaced, tag); if (_templateModes.Count == 0 && _openElements.Count > 1 && _openElements[1].LocalName.Is(TagNames.Body)) { _frameset = false; _openElements[1].SetUniqueAttributes(tag.Attributes); } } else if (name.Is(TagNames.IsIndex)) { RaiseErrorOccurred(HtmlParseError.TagInappropriate, tag); if (_currentFormElement == null) { InBody(HtmlTagToken.Open(TagNames.Form)); if (tag.GetAttribute(AttributeNames.Action).Length > 0) _currentFormElement.SetAttribute(AttributeNames.Action, tag.GetAttribute(AttributeNames.Action)); InBody(HtmlTagToken.Open(TagNames.Hr)); InBody(HtmlTagToken.Open(TagNames.Label)); if (tag.GetAttribute(AttributeNames.Prompt).Length > 0) AddCharacters(tag.GetAttribute(AttributeNames.Prompt)); else AddCharacters("This is a searchable index. Enter search keywords: "); HtmlTagToken htmlTagToken = HtmlTagToken.Open(TagNames.Input); htmlTagToken.AddAttribute(AttributeNames.Name, TagNames.IsIndex); for (int i = 0; i < tag.Attributes.Count; i++) { KeyValuePair<string, string> keyValuePair = tag.Attributes[i]; if (!keyValuePair.Key.IsOneOf(AttributeNames.Name, AttributeNames.Action, AttributeNames.Prompt)) { HtmlTagToken htmlTagToken2 = htmlTagToken; keyValuePair = tag.Attributes[i]; string key = keyValuePair.Key; keyValuePair = tag.Attributes[i]; htmlTagToken2.AddAttribute(key, keyValuePair.Value); } } InBody(htmlTagToken); InBody(HtmlTagToken.Close(TagNames.Label)); InBody(HtmlTagToken.Open(TagNames.Hr)); InBody(HtmlTagToken.Close(TagNames.Form)); } } else if (TagNames.AllNested.Contains(name)) { RaiseErrorOccurred(HtmlParseError.TagCannotStartHere, tag); } else { ReconstructFormatting(); AddElement(tag, false); } } private void InBodyEndTag(HtmlTagToken tag) { string name = tag.Name; if (name.Is(TagNames.Div)) InBodyEndTagBlock(tag); else if (name.Is(TagNames.A)) { HeisenbergAlgorithm(tag); } else if (name.Is(TagNames.Li)) { if (IsInListItemScope()) { GenerateImpliedEndTagsExceptFor(name); if (!CurrentNode.LocalName.Is(TagNames.Li)) RaiseErrorOccurred(HtmlParseError.TagDoesNotMatchCurrentNode, tag); ClearStackBackTo(TagNames.Li); CloseCurrentNode(); } else RaiseErrorOccurred(HtmlParseError.ListItemNotInScope, tag); } else if (name.Is(TagNames.P)) { InBodyEndTagParagraph(tag); } else if (TagNames.AllBlocks.Contains(name)) { InBodyEndTagBlock(tag); } else if (TagNames.AllFormatting.Contains(name)) { HeisenbergAlgorithm(tag); } else if (name.Is(TagNames.Form)) { HtmlFormElement currentFormElement = _currentFormElement; _currentFormElement = null; if (currentFormElement != null && IsInScope(currentFormElement.LocalName)) { GenerateImpliedEndTags(); if (CurrentNode != currentFormElement) RaiseErrorOccurred(HtmlParseError.FormClosedWrong, tag); CloseNode(currentFormElement); } else RaiseErrorOccurred(HtmlParseError.FormNotInScope, tag); } else if (name.Is(TagNames.Br)) { RaiseErrorOccurred(HtmlParseError.TagCannotEndHere, tag); InBodyStartTagBreakrow(HtmlTagToken.Open(TagNames.Br)); } else if (TagNames.AllHeadings.Contains(name)) { if (IsInScope(TagNames.AllHeadings)) { GenerateImpliedEndTags(); if (!CurrentNode.LocalName.Is(name)) RaiseErrorOccurred(HtmlParseError.TagDoesNotMatchCurrentNode, tag); ClearStackBackTo(TagNames.AllHeadings); CloseCurrentNode(); } else RaiseErrorOccurred(HtmlParseError.HeadingNotInScope, tag); } else if (name.IsOneOf(TagNames.Dd, TagNames.Dt)) { if (IsInScope(name)) { GenerateImpliedEndTagsExceptFor(name); if (!CurrentNode.LocalName.Is(name)) RaiseErrorOccurred(HtmlParseError.TagDoesNotMatchCurrentNode, tag); ClearStackBackTo(name); CloseCurrentNode(); } else RaiseErrorOccurred(HtmlParseError.ListItemNotInScope, tag); } else if (name.IsOneOf(TagNames.Applet, TagNames.Marquee, TagNames.Object)) { if (IsInScope(name)) { GenerateImpliedEndTags(); if (!CurrentNode.LocalName.Is(name)) RaiseErrorOccurred(HtmlParseError.TagDoesNotMatchCurrentNode, tag); ClearStackBackTo(name); CloseCurrentNode(); _formattingElements.ClearFormatting(); } else RaiseErrorOccurred(HtmlParseError.ObjectNotInScope, tag); } else if (name.Is(TagNames.Body)) { InBodyEndTagBody(tag); } else if (name.Is(TagNames.Html)) { if (InBodyEndTagBody(tag)) AfterBody(tag); } else if (name.Is(TagNames.Template)) { InHead(tag); } else { InBodyEndTagAnythingElse(tag); } } private void InBody(HtmlToken token) { switch (token.Type) { case HtmlTokenType.Character: ReconstructFormatting(); AddCharacters(token.Data); _frameset = (!token.HasContent && _frameset); break; case HtmlTokenType.StartTag: InBodyStartTag(token.AsTag()); break; case HtmlTokenType.EndTag: InBodyEndTag(token.AsTag()); break; case HtmlTokenType.Comment: CurrentNode.AddComment(token); break; case HtmlTokenType.Doctype: RaiseErrorOccurred(HtmlParseError.DoctypeTagInappropriate, token); break; case HtmlTokenType.EndOfFile: CheckBodyOnClosing(token); if (_templateModes.Count != 0) InTemplate(token); else End(); break; } } private void Text(HtmlToken token) { switch (token.Type) { case HtmlTokenType.Comment: break; case HtmlTokenType.Character: AddCharacters(token.Data); break; case HtmlTokenType.EndTag: if (!token.Name.Is(TagNames.Script)) { CloseCurrentNode(); _currentMode = _previousMode; } else HandleScript(CurrentNode as HtmlScriptElement); break; case HtmlTokenType.EndOfFile: RaiseErrorOccurred(HtmlParseError.EOF, token); CloseCurrentNode(); _currentMode = _previousMode; Consume(token); break; } } private void InTable(HtmlToken token) { switch (token.Type) { case HtmlTokenType.Comment: CurrentNode.AddComment(token); return; case HtmlTokenType.Doctype: RaiseErrorOccurred(HtmlParseError.DoctypeTagInappropriate, token); return; case HtmlTokenType.StartTag: { string name2 = token.Name; if (name2.Is(TagNames.Caption)) { ClearStackBackTo(TagNames.Table); _formattingElements.AddScopeMarker(); AddElement(new HtmlTableCaptionElement(_document, null), token.AsTag(), false); _currentMode = HtmlTreeMode.InCaption; } else if (name2.Is(TagNames.Colgroup)) { ClearStackBackTo(TagNames.Table); AddElement(new HtmlTableColgroupElement(_document, null), token.AsTag(), false); _currentMode = HtmlTreeMode.InColumnGroup; } else if (name2.Is(TagNames.Col)) { InTable(HtmlTagToken.Open(TagNames.Colgroup)); InColumnGroup(token); } else if (TagNames.AllTableSections.Contains(name2)) { ClearStackBackTo(TagNames.Table); AddElement(new HtmlTableSectionElement(_document, name2, null), token.AsTag(), false); _currentMode = HtmlTreeMode.InTableBody; } else if (TagNames.AllTableCellsRows.Contains(name2)) { InTable(HtmlTagToken.Open(TagNames.Tbody)); InTableBody(token); } else if (name2.Is(TagNames.Table)) { RaiseErrorOccurred(HtmlParseError.TableNesting, token); if (InTableEndTagTable(token)) Home(token); } else if (name2.Is(TagNames.Input)) { HtmlTagToken htmlTagToken = token.AsTag(); if (htmlTagToken.GetAttribute(AttributeNames.Type).Isi(AttributeNames.Hidden)) { RaiseErrorOccurred(HtmlParseError.InputUnexpected, token); AddElement(new HtmlInputElement(_document, null), htmlTagToken, true); CloseCurrentNode(); } else { RaiseErrorOccurred(HtmlParseError.TokenNotPossible, token); InBodyWithFoster(token); } } else if (name2.Is(TagNames.Form)) { RaiseErrorOccurred(HtmlParseError.FormInappropriate, token); if (_currentFormElement == null) { _currentFormElement = new HtmlFormElement(_document, null); AddElement(_currentFormElement, token.AsTag(), false); CloseCurrentNode(); } } else if (TagNames.AllTableHead.Contains(name2)) { InHead(token); } else { RaiseErrorOccurred(HtmlParseError.IllegalElementInTableDetected, token); InBodyWithFoster(token); } return; } case HtmlTokenType.EndTag: { string name = token.Name; if (name.Is(TagNames.Table)) InTableEndTagTable(token); else if (name.Is(TagNames.Template)) { InHead(token); } else if (TagNames.AllTableSpecial.Contains(name) || TagNames.AllTableInner.Contains(name)) { RaiseErrorOccurred(HtmlParseError.TagCannotEndHere, token); } else { RaiseErrorOccurred(HtmlParseError.IllegalElementInTableDetected, token); InBodyWithFoster(token); } return; } case HtmlTokenType.EndOfFile: InBody(token); return; case HtmlTokenType.Character: if (TagNames.AllTableMajor.Contains(CurrentNode.LocalName)) { InTableText(token); return; } break; } RaiseErrorOccurred(HtmlParseError.TokenNotPossible, token); InBodyWithFoster(token); } private void InTableText(HtmlToken token) { if (token.HasContent) { RaiseErrorOccurred(HtmlParseError.TokenNotPossible, token); InBodyWithFoster(token); } else AddCharacters(token.Data); } private void InCaption(HtmlToken token) { switch (token.Type) { case HtmlTokenType.EndTag: { string name2 = token.Name; if (name2.Is(TagNames.Caption)) { InCaptionEndTagCaption(token); return; } if (TagNames.AllCaptionStart.Contains(name2)) { RaiseErrorOccurred(HtmlParseError.TagCannotEndHere, token); return; } if (name2.Is(TagNames.Table)) { RaiseErrorOccurred(HtmlParseError.TableNesting, token); if (InCaptionEndTagCaption(token)) InTable(token); return; } break; } case HtmlTokenType.StartTag: { string name = token.Name; if (TagNames.AllCaptionEnd.Contains(name)) { RaiseErrorOccurred(HtmlParseError.TagCannotStartHere, token); if (InCaptionEndTagCaption(token)) InTable(token); return; } break; } } InBody(token); } private void InColumnGroup(HtmlToken token) { switch (token.Type) { case HtmlTokenType.Character: { string text = token.TrimStart(); AddCharacters(text); if (token.IsEmpty) return; break; } case HtmlTokenType.Comment: CurrentNode.AddComment(token); return; case HtmlTokenType.Doctype: RaiseErrorOccurred(HtmlParseError.DoctypeTagInappropriate, token); return; case HtmlTokenType.StartTag: { string name2 = token.Name; if (name2.Is(TagNames.Html)) { InBody(token); return; } if (name2.Is(TagNames.Col)) { AddElement(new HtmlTableColElement(_document, null), token.AsTag(), true); CloseCurrentNode(); return; } if (name2.Is(TagNames.Template)) { InHead(token); return; } break; } case HtmlTokenType.EndTag: { string name = token.Name; if (name.Is(TagNames.Colgroup)) { InColumnGroupEndTagColgroup(token); return; } if (name.Is(TagNames.Col)) { RaiseErrorOccurred(HtmlParseError.TagClosedWrong, token); return; } if (name.Is(TagNames.Template)) { InHead(token); return; } break; } case HtmlTokenType.EndOfFile: InBody(token); return; } if (InColumnGroupEndTagColgroup(token)) InTable(token); } private void InTableBody(HtmlToken token) { switch (token.Type) { case HtmlTokenType.StartTag: { string name2 = token.Name; if (name2.Is(TagNames.Tr)) { ClearStackBackTo(TagNames.AllTableSections); AddElement(new HtmlTableRowElement(_document, null), token.AsTag(), false); _currentMode = HtmlTreeMode.InRow; return; } if (TagNames.AllTableCells.Contains(name2)) { InTableBody(HtmlTagToken.Open(TagNames.Tr)); InRow(token); return; } if (TagNames.AllTableGeneral.Contains(name2)) { InTableBodyCloseTable(token.AsTag()); return; } break; } case HtmlTokenType.EndTag: { string name = token.Name; if (TagNames.AllTableSections.Contains(name)) { if (IsInTableScope(name)) { ClearStackBackTo(TagNames.AllTableSections); CloseCurrentNode(); _currentMode = HtmlTreeMode.InTable; } else RaiseErrorOccurred(HtmlParseError.TableSectionNotInScope, token); return; } if (name.Is(TagNames.Tr) || TagNames.AllTableSpecial.Contains(name)) { RaiseErrorOccurred(HtmlParseError.TagCannotEndHere, token); return; } if (name.Is(TagNames.Table)) { InTableBodyCloseTable(token.AsTag()); return; } break; } } InTable(token); } private void InRow(HtmlToken token) { switch (token.Type) { case HtmlTokenType.StartTag: { string name2 = token.Name; if (TagNames.AllTableCells.Contains(name2)) { ClearStackBackTo(TagNames.Tr); AddElement(token.AsTag(), false); _currentMode = HtmlTreeMode.InCell; _formattingElements.AddScopeMarker(); return; } if (name2.Is(TagNames.Tr) || TagNames.AllTableGeneral.Contains(name2)) { if (InRowEndTagTablerow(token)) InTableBody(token); return; } break; } case HtmlTokenType.EndTag: { string name = token.Name; if (name.Is(TagNames.Tr)) { InRowEndTagTablerow(token); return; } if (name.Is(TagNames.Table)) { if (InRowEndTagTablerow(token)) InTableBody(token); return; } if (TagNames.AllTableSections.Contains(name)) { if (IsInTableScope(name)) { InRowEndTagTablerow(token); InTableBody(token); } else RaiseErrorOccurred(HtmlParseError.TableSectionNotInScope, token); return; } if (TagNames.AllTableSpecial.Contains(name)) { RaiseErrorOccurred(HtmlParseError.TagCannotEndHere, token); return; } break; } } InTable(token); } private void InCell(HtmlToken token) { switch (token.Type) { case HtmlTokenType.StartTag: { string name2 = token.Name; if (TagNames.AllTableCellsRows.Contains(name2) || TagNames.AllTableGeneral.Contains(name2)) { if (IsInTableScope(TagNames.AllTableCells)) { InCellEndTagCell(token); Home(token); } else RaiseErrorOccurred(HtmlParseError.TableCellNotInScope, token); return; } break; } case HtmlTokenType.EndTag: { string name = token.Name; if (TagNames.AllTableCells.Contains(name)) InCellEndTagCell(token); else if (TagNames.AllTableCore.Contains(name)) { if (IsInTableScope(name)) { InCellEndTagCell(token); Home(token); } else RaiseErrorOccurred(HtmlParseError.TableNotInScope, token); } else if (!TagNames.AllTableSpecial.Contains(name)) { InBody(token); } else { RaiseErrorOccurred(HtmlParseError.TagCannotEndHere, token); } return; } } InBody(token); } private void InSelect(HtmlToken token) { switch (token.Type) { case HtmlTokenType.Character: AddCharacters(token.Data); break; case HtmlTokenType.Comment: CurrentNode.AddComment(token); break; case HtmlTokenType.Doctype: RaiseErrorOccurred(HtmlParseError.DoctypeTagInappropriate, token); break; case HtmlTokenType.StartTag: { string name2 = token.Name; if (name2.Is(TagNames.Html)) InBody(token); else if (name2.Is(TagNames.Option)) { if (CurrentNode.LocalName.Is(TagNames.Option)) InSelectEndTagOption(token); AddElement(new HtmlOptionElement(_document, null), token.AsTag(), false); } else if (name2.Is(TagNames.Optgroup)) { if (CurrentNode.LocalName.Is(TagNames.Option)) InSelectEndTagOption(token); if (CurrentNode.LocalName.Is(TagNames.Optgroup)) InSelectEndTagOptgroup(token); AddElement(new HtmlOptionsGroupElement(_document, null), token.AsTag(), false); } else if (name2.Is(TagNames.Select)) { RaiseErrorOccurred(HtmlParseError.SelectNesting, token); InSelectEndTagSelect(); } else if (TagNames.AllInput.Contains(name2)) { RaiseErrorOccurred(HtmlParseError.IllegalElementInSelectDetected, token); if (IsInSelectScope(TagNames.Select)) { InSelectEndTagSelect(); Home(token); } } else if (name2.IsOneOf(TagNames.Template, TagNames.Script)) { InHead(token); } else { RaiseErrorOccurred(HtmlParseError.IllegalElementInSelectDetected, token); } break; } case HtmlTokenType.EndTag: { string name = token.Name; if (name.Is(TagNames.Template)) InHead(token); else if (name.Is(TagNames.Optgroup)) { InSelectEndTagOptgroup(token); } else if (name.Is(TagNames.Option)) { InSelectEndTagOption(token); } else if (name.Is(TagNames.Select) && IsInSelectScope(TagNames.Select)) { InSelectEndTagSelect(); } else if (name.Is(TagNames.Select)) { RaiseErrorOccurred(HtmlParseError.SelectNotInScope, token); } else { RaiseErrorOccurred(HtmlParseError.TagCannotEndHere, token); } break; } case HtmlTokenType.EndOfFile: InBody(token); break; default: RaiseErrorOccurred(HtmlParseError.TokenNotPossible, token); break; } } private void InSelectInTable(HtmlToken token) { switch (token.Type) { case HtmlTokenType.StartTag: { string name2 = token.Name; if (TagNames.AllTableSelects.Contains(name2)) { RaiseErrorOccurred(HtmlParseError.IllegalElementInSelectDetected, token); InSelectEndTagSelect(); Home(token); return; } break; } case HtmlTokenType.EndTag: { string name = token.Name; if (TagNames.AllTableSelects.Contains(name)) { RaiseErrorOccurred(HtmlParseError.TagCannotEndHere, token); if (IsInTableScope(name)) { InSelectEndTagSelect(); Home(token); } return; } break; } } InSelect(token); } private void InTemplate(HtmlToken token) { switch (token.Type) { case HtmlTokenType.StartTag: { string name = token.Name; if (name.Is(TagNames.Script) || TagNames.AllHead.Contains(name)) InHead(token); else if (TagNames.AllTableRoot.Contains(name)) { TemplateStep(token, HtmlTreeMode.InTable); } else if (name.Is(TagNames.Col)) { TemplateStep(token, HtmlTreeMode.InColumnGroup); } else if (name.Is(TagNames.Tr)) { TemplateStep(token, HtmlTreeMode.InTableBody); } else if (TagNames.AllTableCells.Contains(name)) { TemplateStep(token, HtmlTreeMode.InRow); } else { TemplateStep(token, HtmlTreeMode.InBody); } break; } case HtmlTokenType.EndTag: if (token.Name.Is(TagNames.Template)) InHead(token); else RaiseErrorOccurred(HtmlParseError.TagCannotEndHere, token); break; case HtmlTokenType.EndOfFile: if (TagCurrentlyOpen(TagNames.Template)) { RaiseErrorOccurred(HtmlParseError.EOF, token); CloseTemplate(); Home(token); } else End(); break; default: InBody(token); break; } } private void AfterBody(HtmlToken token) { switch (token.Type) { case HtmlTokenType.Character: { string text = token.TrimStart(); ReconstructFormatting(); AddCharacters(text); if (token.IsEmpty) return; break; } case HtmlTokenType.Comment: _openElements[0].AddComment(token); return; case HtmlTokenType.Doctype: RaiseErrorOccurred(HtmlParseError.DoctypeTagInappropriate, token); return; case HtmlTokenType.StartTag: if (token.Name.Is(TagNames.Html)) { InBody(token); return; } break; case HtmlTokenType.EndTag: if (token.Name.Is(TagNames.Html)) { if (IsFragmentCase) RaiseErrorOccurred(HtmlParseError.TagInvalidInFragmentMode, token); else _currentMode = HtmlTreeMode.AfterAfterBody; return; } break; case HtmlTokenType.EndOfFile: End(); return; } RaiseErrorOccurred(HtmlParseError.TokenNotPossible, token); _currentMode = HtmlTreeMode.InBody; InBody(token); } private void InFrameset(HtmlToken token) { switch (token.Type) { case HtmlTokenType.Character: { string text = token.TrimStart(); AddCharacters(text); if (token.IsEmpty) return; break; } case HtmlTokenType.Comment: CurrentNode.AddComment(token); return; case HtmlTokenType.Doctype: RaiseErrorOccurred(HtmlParseError.DoctypeTagInappropriate, token); return; case HtmlTokenType.StartTag: { string name = token.Name; if (name.Is(TagNames.Html)) { InBody(token); return; } if (name.Is(TagNames.Frameset)) { AddElement(new HtmlFrameSetElement(_document, null), token.AsTag(), false); return; } if (name.Is(TagNames.Frame)) { AddElement(new HtmlFrameElement(_document, null), token.AsTag(), true); CloseCurrentNode(); return; } if (name.Is(TagNames.NoFrames)) { InHead(token); return; } break; } case HtmlTokenType.EndTag: if (token.Name.Is(TagNames.Frameset)) { if (CurrentNode != _openElements[0]) { CloseCurrentNode(); if (!IsFragmentCase && !CurrentNode.LocalName.Is(TagNames.Frameset)) _currentMode = HtmlTreeMode.AfterFrameset; } else RaiseErrorOccurred(HtmlParseError.CurrentNodeIsRoot, token); return; } break; case HtmlTokenType.EndOfFile: if (CurrentNode != _document.DocumentElement) RaiseErrorOccurred(HtmlParseError.CurrentNodeIsNotRoot, token); End(); return; } RaiseErrorOccurred(HtmlParseError.TokenNotPossible, token); } private void AfterFrameset(HtmlToken token) { switch (token.Type) { case HtmlTokenType.Character: { string text = token.TrimStart(); AddCharacters(text); if (token.IsEmpty) return; break; } case HtmlTokenType.Comment: CurrentNode.AddComment(token); return; case HtmlTokenType.Doctype: RaiseErrorOccurred(HtmlParseError.DoctypeTagInappropriate, token); return; case HtmlTokenType.StartTag: { string name = token.Name; if (name.Is(TagNames.Html)) { InBody(token); return; } if (name.Is(TagNames.NoFrames)) { InHead(token); return; } break; } case HtmlTokenType.EndTag: if (token.Name.Is(TagNames.Html)) { _currentMode = HtmlTreeMode.AfterAfterFrameset; return; } break; case HtmlTokenType.EndOfFile: End(); return; } RaiseErrorOccurred(HtmlParseError.TokenNotPossible, token); } private void AfterAfterBody(HtmlToken token) { switch (token.Type) { case HtmlTokenType.Character: { string text = token.TrimStart(); ReconstructFormatting(); AddCharacters(text); if (token.IsEmpty) return; break; } case HtmlTokenType.EndOfFile: End(); return; case HtmlTokenType.Comment: _document.AddComment(token); return; case HtmlTokenType.Doctype: InBody(token); return; case HtmlTokenType.StartTag: if (token.Name.Is(TagNames.Html)) { InBody(token); return; } break; } RaiseErrorOccurred(HtmlParseError.TokenNotPossible, token); _currentMode = HtmlTreeMode.InBody; InBody(token); } private void AfterAfterFrameset(HtmlToken token) { switch (token.Type) { case HtmlTokenType.Comment: _document.AddComment(token); return; case HtmlTokenType.Character: { string text = token.TrimStart(); ReconstructFormatting(); AddCharacters(text); if (token.IsEmpty) return; break; } case HtmlTokenType.Doctype: InBody(token); return; case HtmlTokenType.StartTag: { string name = token.Name; if (name.Is(TagNames.Html)) { InBody(token); return; } if (name.Is(TagNames.NoFrames)) { InHead(token); return; } break; } case HtmlTokenType.EndOfFile: End(); return; } RaiseErrorOccurred(HtmlParseError.TokenNotPossible, token); } private void TemplateStep(HtmlToken token, HtmlTreeMode mode) { _templateModes.Pop(); _templateModes.Push(mode); _currentMode = mode; Home(token); } private void CloseTemplate() { while (_openElements.Count > 0) { HtmlTemplateElement htmlTemplateElement = CurrentNode as HtmlTemplateElement; CloseCurrentNode(); if (htmlTemplateElement != null) { htmlTemplateElement.PopulateFragment(); break; } } _formattingElements.ClearFormatting(); _templateModes.Pop(); Reset(); } private void InTableBodyCloseTable(HtmlTagToken tag) { if (IsInTableScope(TagNames.AllTableSections)) { ClearStackBackTo(TagNames.AllTableSections); CloseCurrentNode(); _currentMode = HtmlTreeMode.InTable; InTable(tag); } else RaiseErrorOccurred(HtmlParseError.TableSectionNotInScope, tag); } private void InSelectEndTagOption(HtmlToken token) { if (CurrentNode.LocalName.Is(TagNames.Option)) CloseCurrentNode(); else RaiseErrorOccurred(HtmlParseError.TagDoesNotMatchCurrentNode, token); } private void InSelectEndTagOptgroup(HtmlToken token) { if (_openElements.Count > 1 && _openElements[_openElements.Count - 1].LocalName.Is(TagNames.Option) && _openElements[_openElements.Count - 2].LocalName.Is(TagNames.Optgroup)) CloseCurrentNode(); if (CurrentNode.LocalName.Is(TagNames.Optgroup)) CloseCurrentNode(); else RaiseErrorOccurred(HtmlParseError.TagDoesNotMatchCurrentNode, token); } private bool InColumnGroupEndTagColgroup(HtmlToken token) { if (CurrentNode.LocalName.Is(TagNames.Colgroup)) { CloseCurrentNode(); _currentMode = HtmlTreeMode.InTable; return true; } RaiseErrorOccurred(HtmlParseError.TagDoesNotMatchCurrentNode, token); return false; } private void AfterHeadStartTagBody(HtmlTagToken token) { AddElement(new HtmlBodyElement(_document, null), token, false); _frameset = false; _currentMode = HtmlTreeMode.InBody; } private void RawtextAlgorithm(HtmlTagToken tag) { AddElement(tag, false); _previousMode = _currentMode; _currentMode = HtmlTreeMode.Text; _tokenizer.State = HtmlParseMode.Rawtext; } private void RCDataAlgorithm(HtmlTagToken tag) { AddElement(tag, false); _previousMode = _currentMode; _currentMode = HtmlTreeMode.Text; _tokenizer.State = HtmlParseMode.RCData; } private void InBodyStartTagListItem(HtmlTagToken tag) { int num = _openElements.Count - 1; Element element = _openElements[num]; _frameset = false; while (true) { if (element.LocalName.Is(TagNames.Li)) { InBody(HtmlTagToken.Close(element.LocalName)); break; } if ((element.Flags & NodeFlags.Special) == NodeFlags.Special && !TagNames.AllBasicBlocks.Contains(element.LocalName)) break; element = _openElements[--num]; } if (IsInButtonScope()) InBodyEndTagParagraph(tag); AddElement(tag, false); } private void InBodyStartTagDefinitionItem(HtmlTagToken tag) { _frameset = false; int num = _openElements.Count - 1; Element element = _openElements[num]; while (true) { if (element.LocalName.IsOneOf(TagNames.Dd, TagNames.Dt)) { InBody(HtmlTagToken.Close(element.LocalName)); break; } if ((element.Flags & NodeFlags.Special) == NodeFlags.Special && !TagNames.AllBasicBlocks.Contains(element.LocalName)) break; element = _openElements[--num]; } if (IsInButtonScope()) InBodyEndTagParagraph(tag); AddElement(tag, false); } private bool InBodyEndTagBlock(HtmlTagToken tag) { if (IsInScope(tag.Name)) { GenerateImpliedEndTags(); if (!CurrentNode.LocalName.Is(tag.Name)) RaiseErrorOccurred(HtmlParseError.TagDoesNotMatchCurrentNode, tag); ClearStackBackTo(tag.Name); CloseCurrentNode(); return true; } RaiseErrorOccurred(HtmlParseError.BlockNotInScope, tag); return false; } private void HeisenbergAlgorithm(HtmlTagToken tag) { int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; Element element; Element element2; while (true) { if (num >= 8) return; element = null; element2 = null; num++; num4 = 0; num2 = 0; for (int num5 = _formattingElements.Count - 1; num5 >= 0; num5--) { if (_formattingElements[num5] == null) break; if (_formattingElements[num5].LocalName.Is(tag.Name)) { num4 = num5; element = _formattingElements[num5]; break; } } if (element == null) { InBodyEndTagAnythingElse(tag); return; } int num6 = _openElements.IndexOf(element); if (num6 == -1) { RaiseErrorOccurred(HtmlParseError.FormattingElementNotFound, tag); _formattingElements.Remove(element); return; } if (!IsInScope(element.LocalName)) { RaiseErrorOccurred(HtmlParseError.ElementNotInScope, tag); return; } if (num6 != _openElements.Count - 1) RaiseErrorOccurred(HtmlParseError.TagClosedWrong, tag); num3 = num4; for (int i = num6 + 1; i < _openElements.Count; i++) { if ((_openElements[i].Flags & NodeFlags.Special) == NodeFlags.Special) { num4 = i; element2 = _openElements[i]; break; } } if (element2 == null) break; Element element3 = _openElements[num6 - 1]; Element element4 = element2; Element element5 = element2; while (true) { num2++; element4 = _openElements[--num4]; if (element4 == element) break; if (num2 > 3 && _formattingElements.Contains(element4)) _formattingElements.Remove(element4); if (!_formattingElements.Contains(element4)) CloseNode(element4); else { Element element6 = CopyElement(element4); element3.AddNode(element6); _openElements[num4] = element6; for (int j = 0; j != _formattingElements.Count; j++) { if (_formattingElements[j] == element4) { _formattingElements[j] = element6; break; } } element4 = element6; if (element5 == element2) num3++; element5.Parent?.RemoveChild(element5); element4.AddNode(element5); element5 = element4; } } element5.Parent?.RemoveChild(element5); if (!TagNames.AllTableMajor.Contains(element3.LocalName)) element3.AddNode(element5); else AddElementWithFoster(element5); Element element7 = CopyElement(element); while (element2.ChildNodes.Length > 0) { Node node = element2.ChildNodes[0]; element2.RemoveNode(0, node); element7.AddNode(node); } element2.AddNode(element7); _formattingElements.Remove(element); _formattingElements.Insert(num3, element7); CloseNode(element); _openElements.Insert(_openElements.IndexOf(element2) + 1, element7); } do { element2 = CurrentNode; CloseCurrentNode(); } while (element2 != element); _formattingElements.Remove(element); } private Element CopyElement(Element element) { return (Element)element.Clone(false); } private void InBodyWithFoster(HtmlToken token) { _foster = true; InBody(token); _foster = false; } private void InBodyEndTagAnythingElse(HtmlTagToken tag) { int num = _openElements.Count - 1; Element element = CurrentNode; while (true) { if (element == null) return; if (element.LocalName.Is(tag.Name)) { GenerateImpliedEndTagsExceptFor(tag.Name); if (!element.LocalName.Is(tag.Name)) RaiseErrorOccurred(HtmlParseError.TagClosedWrong, tag); CloseNodesFrom(num); return; } if ((element.Flags & NodeFlags.Special) == NodeFlags.Special) break; element = _openElements[--num]; } RaiseErrorOccurred(HtmlParseError.TagClosedWrong, tag); } private bool InBodyEndTagBody(HtmlToken token) { if (IsInScope(TagNames.Body)) { CheckBodyOnClosing(token); _currentMode = HtmlTreeMode.AfterBody; return true; } RaiseErrorOccurred(HtmlParseError.BodyNotInScope, token); return false; } private void InBodyStartTagBreakrow(HtmlTagToken tag) { ReconstructFormatting(); AddElement(tag, true); CloseCurrentNode(); _frameset = false; } private bool InBodyEndTagParagraph(HtmlToken token) { if (IsInButtonScope()) { GenerateImpliedEndTagsExceptFor(TagNames.P); if (!CurrentNode.LocalName.Is(TagNames.P)) RaiseErrorOccurred(HtmlParseError.TagDoesNotMatchCurrentNode, token); ClearStackBackTo(TagNames.P); CloseCurrentNode(); return true; } RaiseErrorOccurred(HtmlParseError.ParagraphNotInScope, token); InBody(HtmlTagToken.Open(TagNames.P)); InBodyEndTagParagraph(token); return false; } private bool InTableEndTagTable(HtmlToken token) { if (IsInTableScope(TagNames.Table)) { ClearStackBackTo(TagNames.Table); CloseCurrentNode(); Reset(); return true; } RaiseErrorOccurred(HtmlParseError.TableNotInScope, token); return false; } private bool InRowEndTagTablerow(HtmlToken token) { if (IsInTableScope(TagNames.Tr)) { ClearStackBackTo(TagNames.Tr); CloseCurrentNode(); _currentMode = HtmlTreeMode.InTableBody; return true; } RaiseErrorOccurred(HtmlParseError.TableRowNotInScope, token); return false; } private void InSelectEndTagSelect() { ClearStackBackTo(TagNames.Select); CloseCurrentNode(); Reset(); } private bool InCaptionEndTagCaption(HtmlToken token) { if (IsInTableScope(TagNames.Caption)) { GenerateImpliedEndTags(); if (!CurrentNode.LocalName.Is(TagNames.Caption)) RaiseErrorOccurred(HtmlParseError.TagDoesNotMatchCurrentNode, token); ClearStackBackTo(TagNames.Caption); CloseCurrentNode(); _formattingElements.ClearFormatting(); _currentMode = HtmlTreeMode.InTable; return true; } RaiseErrorOccurred(HtmlParseError.CaptionNotInScope, token); return false; } private bool InCellEndTagCell(HtmlToken token) { if (IsInTableScope(TagNames.AllTableCells)) { GenerateImpliedEndTags(); if (!TagNames.AllTableCells.Contains(CurrentNode.LocalName)) RaiseErrorOccurred(HtmlParseError.TagDoesNotMatchCurrentNode, token); ClearStackBackTo(TagNames.AllTableCells); CloseCurrentNode(); _formattingElements.ClearFormatting(); _currentMode = HtmlTreeMode.InRow; return true; } RaiseErrorOccurred(HtmlParseError.TableCellNotInScope, token); return false; } private void Foreign(HtmlToken token) { switch (token.Type) { case HtmlTokenType.Character: AddCharacters(token.Data.Replace('', '�')); _frameset = (!token.HasContent && _frameset); break; case HtmlTokenType.StartTag: { string name2 = token.Name; HtmlTagToken htmlTagToken = token.AsTag(); if (name2.Is(TagNames.Font)) { for (int i = 0; i != htmlTagToken.Attributes.Count; i++) { if (htmlTagToken.Attributes[i].Key.IsOneOf(AttributeNames.Color, AttributeNames.Face, AttributeNames.Size)) { ForeignNormalTag(htmlTagToken); return; } } ForeignSpecialTag(htmlTagToken); } else if (TagNames.AllForeignExceptions.Contains(name2)) { ForeignNormalTag(htmlTagToken); } else { ForeignSpecialTag(htmlTagToken); } break; } case HtmlTokenType.EndTag: { string name = token.Name; Element element = CurrentNode; HtmlScriptElement htmlScriptElement = element as HtmlScriptElement; if (htmlScriptElement != null) HandleScript(htmlScriptElement); else { if (!element.LocalName.Is(name)) RaiseErrorOccurred(HtmlParseError.TagClosingMismatch, token); int num = _openElements.Count - 1; while (true) { if (num <= 0) return; if (element.LocalName.Isi(name)) { CloseNodesFrom(num); return; } element = _openElements[num - 1]; if ((element.Flags & NodeFlags.HtmlMember) == NodeFlags.HtmlMember) break; num--; } Home(token); } break; } case HtmlTokenType.Comment: CurrentNode.AddComment(token); break; case HtmlTokenType.Doctype: RaiseErrorOccurred(HtmlParseError.DoctypeTagInappropriate, token); break; } } private void ForeignSpecialTag(HtmlTagToken tag) { Element element = CreateForeignElementFrom(tag); if (element != null) { bool isSelfClosing = tag.IsSelfClosing; CurrentNode.AddNode(element); if (isSelfClosing) element.SetupElement(); if (!isSelfClosing) { _openElements.Add(element); _tokenizer.IsAcceptingCharacterData = true; } else if (tag.Name.Is(TagNames.Script)) { Foreign(HtmlTagToken.Close(TagNames.Script)); } } } private Element CreateForeignElementFrom(HtmlTagToken tag) { if ((AdjustedCurrentNode.Flags & NodeFlags.MathMember) == NodeFlags.MathMember) { string name = tag.Name; MathElement element = _mathFactory.Create(_document, name, null); AuxiliarySetupSteps(element, tag); return element.Setup(tag); } if ((AdjustedCurrentNode.Flags & NodeFlags.SvgMember) == NodeFlags.SvgMember) { string localName = tag.Name.SanatizeSvgTagName(); SvgElement element2 = _svgFactory.Create(_document, localName, null); AuxiliarySetupSteps(element2, tag); return element2.Setup(tag); } return null; } private void ForeignNormalTag(HtmlTagToken tag) { RaiseErrorOccurred(HtmlParseError.TagCannotStartHere, tag); if (!IsFragmentCase) { Element currentNode = CurrentNode; do { if (currentNode.LocalName.Is(TagNames.AnnotationXml)) { string attribute = currentNode.GetAttribute(null, AttributeNames.Encoding); if (attribute.Isi(MimeTypeNames.Html) || attribute.Isi(MimeTypeNames.ApplicationXHtml)) { AddElement(tag, false); return; } } CloseCurrentNode(); currentNode = CurrentNode; } while ((currentNode.Flags & (NodeFlags.HtmlMember | NodeFlags.HtmlTip | NodeFlags.MathTip)) == NodeFlags.None); Consume(tag); } else ForeignSpecialTag(tag); } private bool IsInScope(string tagName) { for (int num = _openElements.Count - 1; num >= 0; num--) { Element element = _openElements[num]; if (element.LocalName.Is(tagName)) return true; if ((element.Flags & NodeFlags.Scoped) == NodeFlags.Scoped) return false; } return false; } private bool IsInScope(HashSet<string> tags) { for (int num = _openElements.Count - 1; num >= 0; num--) { Element element = _openElements[num]; if (tags.Contains(element.LocalName)) return true; if ((element.Flags & NodeFlags.Scoped) == NodeFlags.Scoped) return false; } return false; } private bool IsInListItemScope() { for (int num = _openElements.Count - 1; num >= 0; num--) { Element element = _openElements[num]; if (element.LocalName.Is(TagNames.Li)) return true; if ((element.Flags & NodeFlags.HtmlListScoped) == NodeFlags.HtmlListScoped) return false; } return false; } private bool IsInButtonScope() { for (int num = _openElements.Count - 1; num >= 0; num--) { Element element = _openElements[num]; if (element.LocalName.Is(TagNames.P)) return true; if ((element.Flags & NodeFlags.Scoped) == NodeFlags.Scoped || element.LocalName.Is(TagNames.Button)) return false; } return false; } private bool IsInTableScope(HashSet<string> tags) { for (int num = _openElements.Count - 1; num >= 0; num--) { Element element = _openElements[num]; if (tags.Contains(element.LocalName)) return true; if ((element.Flags & NodeFlags.HtmlTableScoped) == NodeFlags.HtmlTableScoped) return false; } return false; } private bool IsInTableScope(string tagName) { for (int num = _openElements.Count - 1; num >= 0; num--) { Element element = _openElements[num]; if (element.LocalName.Is(tagName)) return true; if ((element.Flags & NodeFlags.HtmlTableScoped) == NodeFlags.HtmlTableScoped) return false; } return false; } private bool IsInSelectScope(string tagName) { for (int num = _openElements.Count - 1; num >= 0; num--) { Element element = _openElements[num]; if (element.LocalName.Is(tagName)) return true; if ((element.Flags & NodeFlags.HtmlSelectScoped) != NodeFlags.HtmlSelectScoped) return false; } return false; } private void HandleScript(HtmlScriptElement script) { if (script != null) { if (IsFragmentCase) { CloseCurrentNode(); _currentMode = _previousMode; } else { _document.PerformMicrotaskCheckpoint(); _document.ProvideStableState(); CloseCurrentNode(); _currentMode = _previousMode; if (script.Prepare(_document)) _waiting = RunScript(script); } } } private async Task RunScript(HtmlScriptElement script) { await _document.WaitForReadyAsync().ConfigureAwait(false); await script.RunAsync(CancellationToken.None).ConfigureAwait(false); } private void CheckBodyOnClosing(HtmlToken token) { int num = 0; while (true) { if (num >= _openElements.Count) return; if ((_openElements[num].Flags & NodeFlags.ImplicitelyClosed) != NodeFlags.ImplicitelyClosed) break; num++; } RaiseErrorOccurred(HtmlParseError.BodyClosedWrong, token); } private bool TagCurrentlyOpen(string tagName) { for (int i = 0; i < _openElements.Count; i++) { if (_openElements[i].LocalName.Is(tagName)) return true; } return false; } private void PreventNewLine() { HtmlToken htmlToken = _tokenizer.Get(); if (htmlToken.Type == HtmlTokenType.Character) htmlToken.RemoveNewLine(); Home(htmlToken); } private void End() { while (_openElements.Count != 0) { CloseCurrentNode(); } if (_document.IsLoading) _waiting = _document.FinishLoadingAsync(); } private void AddRoot(HtmlTagToken tag) { HtmlHtmlElement htmlHtmlElement = new HtmlHtmlElement(_document, null); _document.AddNode(htmlHtmlElement); SetupElement(htmlHtmlElement, tag, false); _openElements.Add(htmlHtmlElement); _tokenizer.IsAcceptingCharacterData = false; _document.ApplyManifest(); } private void CloseNode(Element element) { element.SetupElement(); _openElements.Remove(element); } private void CloseNodesFrom(int index) { for (int num = _openElements.Count - 1; num > index; num--) { _openElements[num].SetupElement(); _openElements.RemoveAt(num); } CloseCurrentNode(); } private void CloseCurrentNode() { if (_openElements.Count > 0) { int index = _openElements.Count - 1; _openElements[index].SetupElement(); _openElements.RemoveAt(index); Element adjustedCurrentNode = AdjustedCurrentNode; _tokenizer.IsAcceptingCharacterData = (adjustedCurrentNode != null && (adjustedCurrentNode.Flags & NodeFlags.HtmlMember) != NodeFlags.HtmlMember); } } private void SetupElement(Element element, HtmlTagToken tag, bool acknowledgeSelfClosing) { if (tag.IsSelfClosing && !acknowledgeSelfClosing) RaiseErrorOccurred(HtmlParseError.TagCannotBeSelfClosed, tag); AuxiliarySetupSteps(element, tag); element.SetAttributes(tag.Attributes); } private Element AddElement(HtmlTagToken tag, bool acknowledgeSelfClosing = false) { HtmlElement htmlElement = _htmlFactory.Create(_document, tag.Name, null); SetupElement(htmlElement, tag, acknowledgeSelfClosing); AddElement(htmlElement); return htmlElement; } private void AddElement(Element element, HtmlTagToken tag, bool acknowledgeSelfClosing = false) { SetupElement(element, tag, acknowledgeSelfClosing); AddElement(element); } private void AddElement(Element element) { Element currentNode = CurrentNode; if (_foster && TagNames.AllTableMajor.Contains(currentNode.LocalName)) AddElementWithFoster(element); else currentNode.AddNode(element); _openElements.Add(element); _tokenizer.IsAcceptingCharacterData = ((element.Flags & NodeFlags.HtmlMember) != NodeFlags.HtmlMember); } private void AddElementWithFoster(Element element) { bool flag = false; int num = _openElements.Count; while (--num != 0) { if (_openElements[num].LocalName.Is(TagNames.Template)) { _openElements[num].AddNode(element); return; } if (_openElements[num].LocalName.Is(TagNames.Table)) { flag = true; break; } } Node node = _openElements[num].Parent ?? _openElements[num + 1]; if (flag && _openElements[num].Parent != null) { int num2 = 0; while (true) { if (num2 >= node.ChildNodes.Length) return; if (node.ChildNodes[num2] == _openElements[num]) break; num2++; } node.InsertNode(num2, element); } else node.AddNode(element); } private void AddCharacters(string text) { if (!string.IsNullOrEmpty(text)) { Element currentNode = CurrentNode; if (_foster && TagNames.AllTableMajor.Contains(currentNode.LocalName)) AddCharactersWithFoster(text); else currentNode.AppendText(text); } } private void AddCharactersWithFoster(string text) { bool flag = false; int num = _openElements.Count; while (--num != 0) { if (_openElements[num].LocalName.Is(TagNames.Template)) { _openElements[num].AppendText(text); return; } if (_openElements[num].LocalName.Is(TagNames.Table)) { flag = true; break; } } Node node = _openElements[num].Parent ?? _openElements[num + 1]; if (flag && _openElements[num].Parent != null) { int num2 = 0; while (true) { if (num2 >= node.ChildNodes.Length) return; if (node.ChildNodes[num2] == _openElements[num]) break; num2++; } node.InsertText(num2, text); } else node.AppendText(text); } private void AuxiliarySetupSteps(Element element, HtmlTagToken tag) { if (_options.OnCreated != null) _options.OnCreated(element, tag.Position); } private void ClearStackBackTo(string tagName) { Element currentNode = CurrentNode; while (!currentNode.LocalName.IsOneOf(tagName, TagNames.Html, TagNames.Template)) { CloseCurrentNode(); currentNode = CurrentNode; } } private void ClearStackBackTo(HashSet<string> tags) { Element currentNode = CurrentNode; while (!tags.Contains(currentNode.LocalName) && !currentNode.LocalName.IsOneOf(TagNames.Html, TagNames.Template)) { CloseCurrentNode(); currentNode = CurrentNode; } } private void GenerateImpliedEndTagsExceptFor(string tagName) { Element currentNode = CurrentNode; while ((currentNode.Flags & NodeFlags.ImpliedEnd) == NodeFlags.ImpliedEnd && !currentNode.LocalName.Is(tagName)) { CloseCurrentNode(); currentNode = CurrentNode; } } private void GenerateImpliedEndTags() { while ((CurrentNode.Flags & NodeFlags.ImpliedEnd) == NodeFlags.ImpliedEnd) { CloseCurrentNode(); } } private void ReconstructFormatting() { if (_formattingElements.Count != 0) { int i = _formattingElements.Count - 1; Element element = _formattingElements[i]; if (element != null && !_openElements.Contains(element)) { while (i > 0) { element = _formattingElements[--i]; if (element == null || _openElements.Contains(element)) { i++; break; } } for (; i < _formattingElements.Count; i++) { Element element2 = CopyElement(_formattingElements[i]); AddElement(element2); _formattingElements[i] = element2; } } } } private void RaiseErrorOccurred(HtmlParseError code, HtmlToken token) { _tokenizer.RaiseErrorOccurred(code, token.Position); } } }