public void InsertInFosterParent(DomNode input) { DomContainer fosterParent = null; var lastTable = GetFromStack("table"); bool isLastTableParent = false; if (lastTable != null) { if (lastTable.Parent != null) { fosterParent = lastTable.Parent; isLastTableParent = true; } else { fosterParent = AboveOnStack(lastTable); } } else // no table == frag { fosterParent = stack.ElementAt(0); } if (isLastTableParent) { // last table cannot be null by this point. if (lastTable == null) { HtmlWarning.FosterParentTableUnexpectedlyNull(); } lastTable.Before(input); } else { fosterParent.Append(input); } }
private bool?HandleEndTag(Token t, HtmlTreeBuilder tb) { Token.EndTag endTag = t.AsEndTag(); string name = endTag.Name; if (name.Equals("body")) { if (!tb.InScope("body")) { tb.Error(this); return(false); } else { // TODO: error if stack contains something not dd, dt, li, optgroup, option, p, rp, rt, tbody, td, tfoot, th, thead, tr, body, html tb.Transition(AfterBody); } } else if (name.Equals("html")) { bool notIgnored = tb.Process(new Token.EndTag("body")); if (notIgnored) { return(tb.Process(endTag)); } } else if (StringSet.Create(@"address article aside blockquote button center details dir div dl fieldset figcaption figure footer header hgroup listing menu nav ol pre section summary ul").Contains(name)) { // TODO: refactor these lookups if (!tb.InScope(name)) { // nothing to close tb.Error(this); return(false); } else { tb.GenerateImpliedEndTags(); if (!tb.CurrentElement.NodeName.Equals(name)) { tb.Error(this); } tb.PopStackToClose(name); } } else if (name.Equals("form")) { HtmlElement currentForm = tb.FormElement; tb.FormElement = null; if (currentForm == null || !tb.InScope(name)) { tb.Error(this); return(false); } else { tb.GenerateImpliedEndTags(); if (!tb.CurrentElement.NodeName.Equals(name)) { tb.Error(this); } // remove currentForm from stack. will shift anything under up. tb.RemoveFromStack(currentForm); } } else if (name.Equals("p")) { if (!tb.InButtonScope(name)) { tb.Error(this); tb.Process(new Token.StartTag(name)); // if no p to close, creates an empty <p></p> return(tb.Process(endTag)); } else { tb.GenerateImpliedEndTags(name); if (!tb.CurrentElement.NodeName.Equals(name)) { tb.Error(this); } tb.PopStackToClose(name); } } else if (name.Equals("li")) { if (!tb.InListItemScope(name)) { tb.Error(this); return(false); } else { tb.GenerateImpliedEndTags(name); if (!tb.CurrentElement.NodeName.Equals(name)) { tb.Error(this); } tb.PopStackToClose(name); } } else if (StringUtil.In(name, "dd", "dt")) { if (!tb.InScope(name)) { tb.Error(this); return(false); } else { tb.GenerateImpliedEndTags(name); if (!tb.CurrentElement.NodeName.Equals(name)) { tb.Error(this); } tb.PopStackToClose(name); } } else if (HeadingTags.Contains(name)) { if (!tb.InScope(HeadingTags)) { tb.Error(this); return(false); } else { tb.GenerateImpliedEndTags(name); if (!tb.CurrentElement.NodeName.Equals(name)) { tb.Error(this); } tb.PopStackToClose(HeadingTags); } } else if (name.Equals("sarcasm")) { // *sigh* return(AnyOtherEndTag(t, tb)); } else if (StringSet.Create("a b big code em font i nobr s small strike strong tt u").Contains(name)) { // Adoption Agency Algorithm. OUTER: for (int i = 0; i < 8; i++) { var formatEl = tb.GetActiveFormattingElement(name); if (formatEl == null) { return(AnyOtherEndTag(t, tb)); } else if (!tb.OnStack(formatEl)) { tb.Error(this); tb.RemoveFromActiveFormattingElements(formatEl); return(true); } else if (!tb.InScope(formatEl.NodeName)) { tb.Error(this); return(false); } else if (tb.CurrentElement != formatEl) { tb.Error(this); } DomContainer furthestBlock = null; DomContainer commonAncestor = null; bool seenFormattingElement = false; DescendableLinkedList <DomContainer> stack = tb.Stack; for (int si = 0; si < stack.Count; si++) { DomContainer el = stack.ElementAt(si); if (el == formatEl) { commonAncestor = stack.ElementAt(si - 1); seenFormattingElement = true; } else if (seenFormattingElement && tb.IsSpecial(el)) { furthestBlock = el; break; } } if (furthestBlock == null) { tb.PopStackToClose(formatEl.NodeName); tb.RemoveFromActiveFormattingElements(formatEl); return(true); } // TODO: Let a bookmark note the position of the formatting element in the list of active formatting elements relative to the elements on either side of it in the list. // does that mean: int pos of format el in list? DomContainer node = furthestBlock; DomContainer lastNode = furthestBlock; INNER: for (int j = 0; j < 3; j++) { continueINNER: if (tb.OnStack(node)) { node = tb.AboveOnStack(node); } if (!tb.IsInActiveFormattingElements(node)) // note no bookmark check { tb.RemoveFromStack(node); goto continueINNER; } else if (node == formatEl) { goto breakINNER; } HtmlElement replacement = new HtmlElement(node.NodeName); tb.ReplaceActiveFormattingElement(node, replacement); tb.ReplaceOnStack(node, replacement); node = replacement; if (lastNode == furthestBlock) { // TODO: move the aforementioned bookmark to be immediately after the new node in the list of active formatting elements. // not getting how this bookmark both straddles the element above, but is inbetween here... } if (lastNode.Parent != null) { lastNode.Remove(); } node.Append(lastNode); lastNode = node; } breakINNER: if (StringUtil.In(commonAncestor.NodeName, "table", "tbody", "tfoot", "thead", "tr")) { if (lastNode.Parent != null) { lastNode.Remove(); } tb.InsertInFosterParent(lastNode); } else { if (lastNode.Parent != null) { lastNode.Remove(); } commonAncestor.Append(lastNode); } HtmlElement adopter = new HtmlElement(name); var childNodes = furthestBlock.ChildNodes.ToArray(); foreach (var childNode in childNodes) { adopter.Append(childNode); // append will reparent. thus the clone to avvoid concurrent mod. } furthestBlock.Append(adopter); tb.RemoveFromActiveFormattingElements(formatEl); // TODO: insert the new element into the list of active formatting elements at the position of the aforementioned bookmark. tb.RemoveFromStack(formatEl); tb.InsertOnStackAfter(furthestBlock, adopter); } } else if (StringUtil.In(name, "applet", "marquee", "object")) { if (!tb.InScope("name")) { if (!tb.InScope(name)) { tb.Error(this); return(false); } tb.GenerateImpliedEndTags(); if (!tb.CurrentElement.NodeName.Equals(name)) { tb.Error(this); } tb.PopStackToClose(name); tb.ClearFormattingElementsToLastMarker(); } } else if (name.Equals("br")) { tb.Error(this); tb.Process(new Token.StartTag("br")); return(false); } else { return(AnyOtherEndTag(t, tb)); } return(null); }