/// <summary> /// Switches to the fragment algorithm with the specified context element. /// </summary> /// <param name="context">The context element where the algorithm is applied to.</param> public void SwitchToFragment(Node context) { if (started) throw new InvalidOperationException("Fragment mode has to be activated before running the parser!"); switch (context.NodeName) { case HTMLTitleElement.Tag: case HTMLTextAreaElement.Tag: { tokenizer.Switch(HtmlParseMode.RCData); break; } case HTMLStyleElement.Tag: case HTMLSemanticElement.XmpTag: case HTMLIFrameElement.Tag: case HTMLNoElement.NoEmbedTag: case HTMLNoElement.NoFramesTag: { tokenizer.Switch(HtmlParseMode.Rawtext); break; } case HTMLScriptElement.Tag: { tokenizer.Switch(HtmlParseMode.Script); break; } case HTMLNoElement.NoScriptTag: { if (doc.IsScripting) tokenizer.Switch(HtmlParseMode.Rawtext); break; } case HTMLSemanticElement.PlaintextTag: { tokenizer.Switch(HtmlParseMode.Plaintext); break; } } var root = new HTMLHtmlElement(); doc.AppendChild(root); open.Add(root); Reset(context); fragmentContext = context; tokenizer.AcceptsCharacterData = !AdjustedCurrentNode.IsInHtml; do { if (context is HTMLFormElement) { form = (HTMLFormElement)context; break; } context = context.ParentNode; } while (context != null); }
/// <summary> /// See 8.2.5.4.9 The "in table" insertion mode. /// </summary> /// <param name="token">The passed token.</param> void InTable(HtmlToken token) { if (token.Type == HtmlTokenType.Comment) { AddComment(CurrentNode, token); } else if (token.Type == HtmlTokenType.DOCTYPE) { RaiseErrorOccurred(ErrorCode.DoctypeTagInappropriate); } else if (token.Type == HtmlTokenType.StartTag) { var tag = (HtmlTagToken)token; switch (tag.Name) { case HTMLTableCaptionElement.Tag: { ClearStackBackToTable(); InsertScopeMarker(); var element = new HTMLTableCaptionElement(); AddElementToCurrentNode(element, token); insert = HtmlTreeMode.InCaption; break; } case HTMLTableColElement.ColgroupTag: { ClearStackBackToTable(); var element = new HTMLTableColElement(); AddElementToCurrentNode(element, token); insert = HtmlTreeMode.InColumnGroup; break; } case HTMLTableColElement.ColTag: { InTable(HtmlToken.OpenTag(HTMLTableColElement.ColgroupTag)); InColumnGroup(token); break; } case HTMLTableSectionElement.BodyTag: case HTMLTableSectionElement.HeadTag: case HTMLTableSectionElement.FootTag: { ClearStackBackToTable(); var element = new HTMLTableSectionElement(); AddElementToCurrentNode(element, token); insert = HtmlTreeMode.InTableBody; break; } case HTMLTableCellElement.NormalTag: case HTMLTableCellElement.HeadTag: case HTMLTableRowElement.Tag: { InTable(HtmlToken.OpenTag(HTMLTableSectionElement.BodyTag)); InTableBody(token); break; } case HTMLTableElement.Tag: { RaiseErrorOccurred(ErrorCode.TableNesting); if (InTableEndTagTable()) Consume(token); break; } case HTMLScriptElement.Tag: case HTMLStyleElement.Tag: { InHead(token); break; } case HTMLInputElement.Tag: { if (tag.GetAttribute("type").Equals("hidden", StringComparison.OrdinalIgnoreCase)) { RaiseErrorOccurred(ErrorCode.InputUnexpected); var element = new HTMLInputElement(); AddElementToCurrentNode(element, token, true); CloseCurrentNode(); } else { RaiseErrorOccurred(ErrorCode.TokenNotPossible); InBodyWithFoster(token); } break; } case HTMLFormElement.Tag: { RaiseErrorOccurred(ErrorCode.FormInappropriate); if (form == null) { var element = new HTMLFormElement(); AddElementToCurrentNode(element, token); form = element; CloseCurrentNode(); } break; } default: { RaiseErrorOccurred(ErrorCode.IllegalElementInTableDetected); InBodyWithFoster(token); break; } } } else if (token.Type == HtmlTokenType.EndTag) { var tag = (HtmlTagToken)token; switch (tag.Name) { case HTMLTableElement.Tag: { InTableEndTagTable(); break; } case HTMLBodyElement.Tag: case HTMLTableColElement.ColgroupTag: case HTMLTableColElement.ColTag: case HTMLTableCaptionElement.Tag: case HTMLHtmlElement.Tag: case HTMLTableSectionElement.BodyTag: case HTMLTableRowElement.Tag: case HTMLTableSectionElement.HeadTag: case HTMLTableCellElement.HeadTag: case HTMLTableSectionElement.FootTag: case HTMLTableCellElement.NormalTag: { RaiseErrorOccurred(ErrorCode.TagCannotEndHere); break; } default: { RaiseErrorOccurred(ErrorCode.IllegalElementInTableDetected); InBodyWithFoster(token); break; } } } else if (token.Type == HtmlTokenType.Character && CurrentNode != null && CurrentNode.IsTableElement()) { InTableText((HtmlCharacterToken)token); } else if (token.Type == HtmlTokenType.EOF) { if (CurrentNode != doc.DocumentElement) RaiseErrorOccurred(ErrorCode.CurrentNodeIsNotRoot); End(); } else { RaiseErrorOccurred(ErrorCode.TokenNotPossible); InBodyWithFoster(token); } }
/// <summary> /// See 8.2.5.4.7 The "in body" insertion mode. /// </summary> /// <param name="token">The passed token.</param> void InBody(HtmlToken token) { if (token.Type == HtmlTokenType.Character) { var chrs = (HtmlCharacterToken)token; ReconstructFormatting(); InsertCharacters(chrs.Data); if(chrs.HasContent) frameset = false; } else if (token.Type == HtmlTokenType.Comment) AddComment(CurrentNode, token); else if (token.Type == HtmlTokenType.DOCTYPE) RaiseErrorOccurred(ErrorCode.DoctypeTagInappropriate); else if (token.Type == HtmlTokenType.StartTag) { var tag = (HtmlTagToken)token; switch (tag.Name) { case HTMLHtmlElement.Tag: { RaiseErrorOccurred(ErrorCode.HtmlTagMisplaced); AppendAttributes(tag, open[0]); break; } case HTMLBaseElement.Tag: case HTMLBaseFontElement.Tag: case HTMLBgsoundElement.Tag: case HTMLLinkElement.Tag: case HTMLMenuItemElement.Tag: case HTMLMetaElement.Tag: case HTMLNoElement.NoFramesTag: case HTMLScriptElement.Tag: case HTMLStyleElement.Tag: case HTMLTitleElement.Tag: { InHead(token); break; } case HTMLBodyElement.Tag: { RaiseErrorOccurred(ErrorCode.BodyTagMisplaced); if (open.Count > 1 && open[1] is HTMLBodyElement) { frameset = false; AppendAttributes(tag, open[1]); } break; } case HTMLFrameSetElement.Tag: { RaiseErrorOccurred(ErrorCode.FramesetMisplaced); if (open.Count != 1 && open[1] is HTMLBodyElement && frameset) { open[1].ParentNode.RemoveChild(open[1]); while (open.Count > 1) CloseCurrentNode(); var element = new HTMLFrameSetElement(); AddElementToCurrentNode(element, token); insert = HtmlTreeMode.InFrameset; } break; } case HTMLSemanticElement.AddressTag: case HTMLSemanticElement.ArticleTag: case HTMLSemanticElement.AsideTag: case HTMLQuoteElement.BlockTag: case HTMLSemanticElement.CenterTag: case HTMLDetailsElement.Tag: case HTMLDialogElement.Tag: case HTMLDirectoryElement.Tag: case HTMLDivElement.Tag: case HTMLDListElement.Tag: case HTMLFieldSetElement.Tag: case HTMLSemanticElement.FigcaptionTag: case HTMLSemanticElement.FigureTag: case HTMLSemanticElement.FooterTag: case HTMLSemanticElement.HeaderTag: case HTMLSemanticElement.HgroupTag: case HTMLMenuElement.Tag: case HTMLSemanticElement.NavTag: case HTMLOListElement.Tag: case HTMLParagraphElement.Tag: case HTMLSemanticElement.SectionTag: case HTMLSemanticElement.SummaryTag: case HTMLUListElement.Tag: { if (IsInButtonScope(HTMLParagraphElement.Tag)) InBodyEndTagParagraph(); var element = HTMLElement.Factory(tag.Name); AddElementToCurrentNode(element, token); break; } case HTMLHeadingElement.ChapterTag: case HTMLHeadingElement.SubSubSubSubSectionTag: case HTMLHeadingElement.SubSubSubSectionTag: case HTMLHeadingElement.SubSubSectionTag: case HTMLHeadingElement.SubSectionTag: case HTMLHeadingElement.SectionTag: { if (IsInButtonScope(HTMLParagraphElement.Tag)) InBodyEndTagParagraph(); if (CurrentNode is HTMLHeadingElement) { RaiseErrorOccurred(ErrorCode.HeadingNested); CloseCurrentNode(); } var element = new HTMLHeadingElement(); AddElementToCurrentNode(element, token); break; } case HTMLPreElement.Tag: case HTMLSemanticElement.ListingTag: { if (IsInButtonScope(HTMLParagraphElement.Tag)) InBodyEndTagParagraph(); var element = new HTMLPreElement(); AddElementToCurrentNode(element, token); frameset = false; PreventNewLine(); break; } case HTMLFormElement.Tag: { if (form == null) { if (IsInButtonScope(HTMLParagraphElement.Tag)) InBodyEndTagParagraph(); var element = new HTMLFormElement(); AddElementToCurrentNode(element, token); form = element; } else RaiseErrorOccurred(ErrorCode.FormAlreadyOpen); break; } case HTMLLIElement.ItemTag: { InBodyStartTagListItem(tag); break; } case HTMLLIElement.DefinitionTag: case HTMLLIElement.DescriptionTag: { InBodyStartTagDefinitionItem(tag); break; } case HTMLSemanticElement.PlaintextTag: { if (IsInButtonScope(HTMLParagraphElement.Tag)) InBodyEndTagParagraph(); var plaintext = new HTMLElement(); AddElementToCurrentNode(plaintext, token); tokenizer.Switch(HtmlParseMode.Plaintext); break; } case HTMLButtonElement.Tag: { if (IsInScope(tag.Name)) { RaiseErrorOccurred(ErrorCode.ButtonInScope); InBodyEndTagBlock(tag.Name); InBody(token); } else { ReconstructFormatting(); var element = new HTMLButtonElement(); AddElementToCurrentNode(element, token); frameset = false; } break; } case HTMLAnchorElement.Tag: { for (var i = formatting.Count - 1; i >= 0; i--) { if (formatting[i] is ScopeMarkerNode) break; else if (formatting[i].NodeName == HTMLAnchorElement.Tag) { var format = formatting[i]; RaiseErrorOccurred(ErrorCode.AnchorNested); HeisenbergAlgorithm(HtmlToken.CloseTag(HTMLAnchorElement.Tag)); if(open.Contains(format)) open.Remove(format); if(formatting.Contains(format)) formatting.RemoveAt(i); break; } } ReconstructFormatting(); var element = new HTMLAnchorElement(); AddElementToCurrentNode(element, token); AddFormattingElement(element); break; } case HTMLFormattingElement.BTag: case HTMLFormattingElement.BigTag: case HTMLFormattingElement.CodeTag: case HTMLFormattingElement.EmTag: case HTMLFontElement.Tag: case HTMLFormattingElement.ITag: case HTMLFormattingElement.STag: case HTMLFormattingElement.SmallTag: case HTMLFormattingElement.StrikeTag: case HTMLFormattingElement.StrongTag: case HTMLFormattingElement.TtTag: case HTMLFormattingElement.UTag: { ReconstructFormatting(); var element = HTMLElement.Factory(tag.Name); AddElementToCurrentNode(element, token); AddFormattingElement(element); break; } case HTMLFormattingElement.NobrTag: { ReconstructFormatting(); if (IsInScope(HTMLFormattingElement.NobrTag)) { RaiseErrorOccurred(ErrorCode.NobrInScope); HeisenbergAlgorithm(tag); ReconstructFormatting(); } var element = new HTMLElement(); AddElementToCurrentNode(element, token); AddFormattingElement(element); break; } case HTMLAppletElement.Tag: case HTMLMarqueeElement.Tag: case HTMLObjectElement.Tag: { ReconstructFormatting(); var element = HTMLElement.Factory(tag.Name); AddElementToCurrentNode(element, token); InsertScopeMarker(); frameset = false; break; } case HTMLTableElement.Tag: { if (doc.QuirksMode == QuirksMode.Off && IsInButtonScope(HTMLParagraphElement.Tag)) InBodyEndTagParagraph(); var element = new HTMLTableElement(); AddElementToCurrentNode(element, token); frameset = false; insert = HtmlTreeMode.InTable; break; } case HTMLAreaElement.Tag: case HTMLBRElement.Tag: case HTMLEmbedElement.Tag: case HTMLImageElement.Tag: case HTMLKeygenElement.Tag: case HTMLWbrElement.Tag: { InBodyStartTagBreakrow(tag); break; } case HTMLInputElement.Tag: { ReconstructFormatting(); var element = new HTMLInputElement(); AddElementToCurrentNode(element, token, true); CloseCurrentNode(); if (!tag.GetAttribute("type").Equals("hidden", StringComparison.OrdinalIgnoreCase)) frameset = false; break; } case HTMLParamElement.Tag: case HTMLSourceElement.Tag: case HTMLTrackElement.Tag: { var element = HTMLElement.Factory(tag.Name); AddElementToCurrentNode(element, token, true); CloseCurrentNode(); break; } case HTMLHRElement.Tag: { if (IsInButtonScope(HTMLParagraphElement.Tag)) InBodyEndTagParagraph(); var element = new HTMLHRElement(); AddElementToCurrentNode(element, token, true); CloseCurrentNode(); frameset = false; break; } case HTMLImageElement.FalseTag: { RaiseErrorOccurred(ErrorCode.ImageTagNamedWrong); tag.Name = HTMLImageElement.Tag; goto case HTMLImageElement.Tag; } case HTMLIsIndexElement.Tag: { RaiseErrorOccurred(ErrorCode.TagInappropriate); if (form == null) { InBody(HtmlToken.OpenTag(HTMLFormElement.Tag)); if (tag.GetAttribute("action") != String.Empty) form.SetAttribute("action", tag.GetAttribute("action")); InBody(HtmlToken.OpenTag(HTMLHRElement.Tag)); InBody(HtmlToken.OpenTag(HTMLLabelElement.Tag)); if (tag.GetAttribute("prompt") != String.Empty) InsertCharacters(tag.GetAttribute("prompt")); else InsertCharacters("This is a searchable index. Enter search keywords:"); var input = HtmlToken.OpenTag(HTMLInputElement.Tag); input.AddAttribute("name", HTMLIsIndexElement.Tag); for (int i = 0; i < tag.Attributes.Count; i++) { if (tag.Attributes[i].Key == "name" || tag.Attributes[i].Key == "action" || tag.Attributes[i].Key == "prompt") continue; input.AddAttribute(tag.Attributes[i].Key, tag.Attributes[i].Value); } InBody(input); InBody(HtmlToken.CloseTag(HTMLLabelElement.Tag)); InBody(HtmlToken.OpenTag(HTMLHRElement.Tag)); InBody(HtmlToken.CloseTag(HTMLFormElement.Tag)); } break; } case HTMLTextAreaElement.Tag: { var element = new HTMLTextAreaElement(); AddElementToCurrentNode(element, token); tokenizer.Switch(HtmlParseMode.RCData); originalInsert = insert; frameset = false; insert = HtmlTreeMode.Text; PreventNewLine(); break; } case HTMLSemanticElement.XmpTag: { if (IsInButtonScope(HTMLParagraphElement.Tag)) InBodyEndTagParagraph(); ReconstructFormatting(); frameset = false; RawtextAlgorithm(tag); break; } case HTMLIFrameElement.Tag: { frameset = false; RawtextAlgorithm(tag); break; } case HTMLSelectElement.Tag: { ReconstructFormatting(); var element = new HTMLSelectElement(); AddElementToCurrentNode(element, token); frameset = false; switch (insert) { case HtmlTreeMode.InTable: case HtmlTreeMode.InCaption: case HtmlTreeMode.InRow: case HtmlTreeMode.InCell: insert = HtmlTreeMode.InSelectInTable; break; default: insert = HtmlTreeMode.InSelect; break; } break; } case HTMLOptGroupElement.Tag: case HTMLOptionElement.Tag: { if (CurrentNode.NodeName == HTMLOptionElement.Tag) InBodyEndTagAnythingElse(HtmlToken.CloseTag(HTMLOptionElement.Tag)); ReconstructFormatting(); var element = HTMLElement.Factory(tag.Name); AddElementToCurrentNode(element, token); break; } case "rp": case "rt": { if (IsInScope("ruby")) { GenerateImpliedEndTags(); if (CurrentNode.NodeName != "ruby") RaiseErrorOccurred(ErrorCode.TagDoesNotMatchCurrentNode); } var element = HTMLElement.Factory(tag.Name); AddElementToCurrentNode(element, token); break; } case HTMLNoElement.NoEmbedTag: { RawtextAlgorithm(tag); break; } case HTMLNoElement.NoScriptTag: { if (!doc.IsScripting) goto default; RawtextAlgorithm(tag); break; } case MathMLElement.RootTag: { var element = new MathMLElement(); element.NodeName = tag.Name; ReconstructFormatting(); for (int i = 0; i < tag.Attributes.Count; i++) { var name = tag.Attributes[i].Key; var value = tag.Attributes[i].Value; element.SetAttribute(ForeignHelpers.AdjustAttributeName(MathMLHelpers.AdjustAttributeName(name)), value); } CurrentNode.AppendChild(element); if (!tag.IsSelfClosing) open.Add(element); break; } case SVGElement.RootTag: { var element = new SVGElement(); element.NodeName = tag.Name; ReconstructFormatting(); for (int i = 0; i < tag.Attributes.Count; i++) { var name = tag.Attributes[i].Key; var value = tag.Attributes[i].Value; element.SetAttribute(ForeignHelpers.AdjustAttributeName(MathMLHelpers.AdjustAttributeName(name)), value); } CurrentNode.AppendChild(element); if (!tag.IsSelfClosing) { open.Add(element); tokenizer.AcceptsCharacterData = true; } break; } case HTMLTableCaptionElement.Tag: case HTMLTableColElement.ColTag: case HTMLTableColElement.ColgroupTag: case HTMLFrameElement.Tag: case HTMLHeadElement.Tag: case HTMLTableSectionElement.BodyTag: case HTMLTableCellElement.NormalTag: case HTMLTableSectionElement.FootTag: case HTMLTableCellElement.HeadTag: case HTMLTableSectionElement.HeadTag: case HTMLTableRowElement.Tag: { RaiseErrorOccurred(ErrorCode.TagCannotStartHere); break; } default: { ReconstructFormatting(); var element = new HTMLUnknownElement(); AddElementToCurrentNode(element, token); break; } } } else if (token.Type == HtmlTokenType.EndTag) { var tag = (HtmlTagToken)token; switch (tag.Name) { case HTMLBodyElement.Tag: { InBodyEndTagBody(); break; } case HTMLHtmlElement.Tag: { if (InBodyEndTagBody()) AfterBody(token); break; } case HTMLSemanticElement.AddressTag: case HTMLSemanticElement.ArticleTag: case HTMLSemanticElement.AsideTag: case HTMLQuoteElement.BlockTag: case HTMLButtonElement.Tag: case HTMLSemanticElement.CenterTag: case HTMLDetailsElement.Tag: case HTMLDialogElement.Tag: case HTMLDirectoryElement.Tag: case HTMLDivElement.Tag: case HTMLDListElement.Tag: case HTMLFieldSetElement.Tag: case HTMLSemanticElement.FigcaptionTag: case HTMLSemanticElement.FigureTag: case HTMLSemanticElement.FooterTag: case HTMLSemanticElement.HeaderTag: case HTMLSemanticElement.HgroupTag: case HTMLSemanticElement.ListingTag: case HTMLSemanticElement.MainTag: case HTMLMenuElement.Tag: case HTMLSemanticElement.NavTag: case HTMLOListElement.Tag: case HTMLPreElement.Tag: case HTMLSemanticElement.SectionTag: case HTMLSemanticElement.SummaryTag: case HTMLUListElement.Tag: { InBodyEndTagBlock(tag.Name); break; } case HTMLFormElement.Tag: { var node = form; form = null; if (node != null && IsInScope(node.NodeName)) { GenerateImpliedEndTags(); if (CurrentNode != node) RaiseErrorOccurred(ErrorCode.FormClosedWrong); open.Remove(node); } else RaiseErrorOccurred(ErrorCode.FormNotInScope); break; } case HTMLParagraphElement.Tag: { InBodyEndTagParagraph(); break; } case HTMLLIElement.ItemTag: { if (IsInListItemScope(tag.Name)) { GenerateImpliedEndTagsExceptFor(tag.Name); if (CurrentNode.NodeName != tag.Name) RaiseErrorOccurred(ErrorCode.TagDoesNotMatchCurrentNode); ClearStackBackTo(tag.Name); CloseCurrentNode(); } else RaiseErrorOccurred(ErrorCode.ListItemNotInScope); break; } case HTMLLIElement.DefinitionTag: case HTMLLIElement.DescriptionTag: { if (IsInScope(tag.Name)) { GenerateImpliedEndTagsExceptFor(tag.Name); if (CurrentNode.NodeName != tag.Name) RaiseErrorOccurred(ErrorCode.TagDoesNotMatchCurrentNode); ClearStackBackTo(tag.Name); CloseCurrentNode(); } else RaiseErrorOccurred(ErrorCode.ListItemNotInScope); break; } case HTMLHeadingElement.ChapterTag: case HTMLHeadingElement.SubSubSubSubSectionTag: case HTMLHeadingElement.SubSubSubSectionTag: case HTMLHeadingElement.SubSubSectionTag: case HTMLHeadingElement.SubSectionTag: case HTMLHeadingElement.SectionTag: { if (IsHeadingInScope()) { GenerateImpliedEndTags(); if (CurrentNode.NodeName != tag.Name) RaiseErrorOccurred(ErrorCode.TagDoesNotMatchCurrentNode); ClearStackBackToHeading(); CloseCurrentNode(); } else RaiseErrorOccurred(ErrorCode.HeadingNotInScope); break; } case HTMLAnchorElement.Tag: case HTMLFormattingElement.BTag: case HTMLFormattingElement.BigTag: case HTMLFormattingElement.CodeTag: case HTMLFormattingElement.EmTag: case HTMLFontElement.Tag: case HTMLFormattingElement.ITag: case HTMLFormattingElement.NobrTag: case HTMLFormattingElement.STag: case HTMLFormattingElement.SmallTag: case HTMLFormattingElement.StrikeTag: case HTMLFormattingElement.StrongTag: case HTMLFormattingElement.TtTag: case HTMLFormattingElement.UTag: { HeisenbergAlgorithm(tag); break; } case HTMLAppletElement.Tag: case HTMLMarqueeElement.Tag: case HTMLObjectElement.Tag: { if (IsInScope(tag.Name)) { GenerateImpliedEndTags(); if (CurrentNode.NodeName != tag.Name) RaiseErrorOccurred(ErrorCode.TagDoesNotMatchCurrentNode); ClearStackBackTo(tag.Name); CloseCurrentNode(); ClearFormattingElements(); } else RaiseErrorOccurred(ErrorCode.ObjectNotInScope); break; } case HTMLBRElement.Tag: { RaiseErrorOccurred(ErrorCode.TagCannotEndHere); InBodyStartTagBreakrow(HtmlToken.OpenTag(HTMLBRElement.Tag)); break; } default: { InBodyEndTagAnythingElse(tag); break; } } } else if (token.Type == HtmlTokenType.EOF) { for (var i = 0; i < open.Count; i++) { switch (open[i].NodeName) { case HTMLLIElement.DescriptionTag: case HTMLLIElement.DefinitionTag: case HTMLLIElement.ItemTag: case HTMLParagraphElement.Tag: case HTMLTableSectionElement.BodyTag: case HTMLTableCellElement.HeadTag: case HTMLTableSectionElement.FootTag: case HTMLTableCellElement.NormalTag: case HTMLTableSectionElement.HeadTag: case HTMLTableRowElement.Tag: case HTMLBodyElement.Tag: case HTMLHtmlElement.Tag: break; default: RaiseErrorOccurred(ErrorCode.BodyClosedWrong); i = open.Count; break; } } End(); } }