Refine fixing of disallowed ancestors
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Sun, 19 Jun 2011 09:40:04 -0600
changeset 284 86105d64418d
parent 283 fbfbbbb3cbff
child 285 5813f549e4d2
Refine fixing of disallowed ancestors
autoimplementation.html
editcommands.html
implementation.js
manualtest.js
source.html
tests.js
--- a/autoimplementation.html	Thu Jun 16 14:11:22 2011 -0600
+++ b/autoimplementation.html	Sun Jun 19 09:40:04 2011 -0600
@@ -193,7 +193,8 @@
 				+ compareDiv1.innerHTML + " vs. " + compareDiv2.innerHTML;
 		}
 		if (!compareDiv1.isEqualNode(compareDiv2)) {
-			throw "DOM does not round-trip through serialization!";
+			throw "DOM does not round-trip through serialization (although innerHTML is the same)!  "
+				+ compareDiv1.innerHTML;
 		}
 
 		if (browserCell.firstChild.attributes.length) {
--- a/editcommands.html	Thu Jun 16 14:11:22 2011 -0600
+++ b/editcommands.html	Sun Jun 19 09:40:04 2011 -0600
@@ -38,7 +38,7 @@
 <body class=draft>
 <div class=head id=head>
 <h1>HTML Editing Commands</h1>
-<h2 class="no-num no-toc" id=work-in-progress-&mdash;-last-update-16-june-2011>Work in Progress &mdash; Last Update 16 June 2011</h2>
+<h2 class="no-num no-toc" id=work-in-progress-&mdash;-last-update-19-june-2011>Work in Progress &mdash; Last Update 19 June 2011</h2>
 <dl>
  <dt>Editor
  <dd>Aryeh Gregor &lt;[email protected]&gt;
@@ -635,7 +635,7 @@
     <tr><td>tbody, tfoot, thead <td>td, th, tr
     <tr><td>tr <td>td, th
     <tr><td>dl <td>dt, dd
-    <tr><td>dir, ol, ul <td>li, ol, ul
+    <tr><td>dir, ol, ul <td>dir, li, ol, ul
     <tr><td>hgroup <td>h1, h2, h3, h4, h5, h6
   </table>
 
@@ -703,6 +703,9 @@
 <p>To <dfn id=set-the-tag-name>set the tag name</dfn> of an <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element>Element</a></code> <var title="">element</var> to
 <var title="">new name</var>:
 
+<p class=XXX>Do we want to get rid of attributes that are no longer allowed
+here?
+
 <ol>
   <li>If <var title="">element</var> is an <a href=#html-element>HTML element</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a>
   equal to <var title="">new name</var>, return <var title="">element</var>.
@@ -764,6 +767,8 @@
 if the parent had styles, classes, etc.  There's not going to be any general
 way to handle this, but we should at least try to handle the special case of
 inline styles, because Firefox does actually add them to arbitrary elements.
+Also, when splitting out of an inline parent, it might be good to wrap all the
+inline descendants in a clone of the former parent.
 
 <ol>
   <li>Let <var title="">original parent</var> be the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> of the first member of
@@ -845,8 +850,9 @@
 
   <li>If the first member of <var title="">node list</var> is an <a href=#inline-node>inline
   node</a>, and <var title="">original parent</var>'s <code class=external data-anolis-spec=domcore title=dom-Node-previousSibling><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-previoussibling>previousSibling</a></code> is an
-  <a href=#inline-node>inline node</a> other than a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, call <code class=external data-anolis-spec=domcore title=dom-Document-createElement><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement>createElement("br")</a></code> on
-  the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>, then insert the result into the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> of
+  <a href=#inline-node>inline node</a> other than a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, and <var title="">original parent</var>
+  is not an <a href=#inline-node>inline node</a>, call <code class=external data-anolis-spec=domcore title=dom-Document-createElement><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement>createElement("br")</a></code> on the
+  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>, then insert the result into the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> of
   <var title="">original parent</var> immediately before <var title="">original parent</var>.
 
   <li>For each <var title="">node</var> in <var title="">node list</var>, insert <var title="">node</var>
@@ -861,8 +867,9 @@
 
   <li>If the last member of <var title="">node list</var> is an <a href=#inline-node>inline node</a>
   other than a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, and the first <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> of <var title="">original parent</var> is
-  a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, remove the first <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> of <var title="">original parent</var> from
-  <var title="">original parent</var>.
+  a <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code>, and <var title="">original parent</var> is not an <a href=#inline-node>inline node</a>,
+  remove the first <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> of <var title="">original parent</var> from <var title="">original
+  parent</var>.
 
   <li>If <var title="">original parent</var> has no <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, remove it from its
   <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
@@ -2783,7 +2790,32 @@
 
 <h3 id=the-inserthtml-command><span class=secno>6.15 </span><dfn>The <code title="">insertHTML</code> command</dfn></h3>
 
-<!-- Not supported by IE9. -->
+<!--
+Not supported by IE9.  Handling of disallowed children is interesting:
+
+Firefox 5.0a2: Will allow <dt> inside <dt> (doesn't serialize).  If you try
+inserting dir/ol/ul inside an existing dir/ol/ul, it will strip the list
+element and leave only the li's, so inserting <ul><li>abc</ul> into
+<ol><li>f[o]o</ol> creates <ol><li>f<li>abc<li>o</ol>.  <dt>/<dd>/<li> that
+don't descend from a list will be left alone, not converted to <p>.  Empty
+elements seem not to be inserted.  <li> will get put inside <p>, which breaks
+serialization.  Nothing is allowed inside <xmp>, not even text.
+
+Chrome 13 dev: Inserting a <p> into a <p> or <li> or such will remove the child
+<p>, adding its contents to the parent instead.  Adding an <li> or <hr> as the
+child of a <p> works, as does an <a> inside an <a>, <h2> inside <h1>, <li>
+inside <li>, <nobr> inside <nobr>, <b> inside <xmp>, etc. (all unserializable).
+But <dt> and <dd> seem to get converted to their contents like <p>.
+<ol><li>abc</ol> inside <ol><li>f[o]o</ol> becomes
+<ol><li>f<li>abc<li><li>o</ol>, interestingly (note the empty <li>).  I don't
+understand how it works, but it doesn't seem to make much sense.
+
+Opera 11.11: Seems to do almost no validity or serialization checks, except
+that it prevents <a> inside <a>, <nobr> inside <nobr>, and block elements
+inside inline elements.  Interestingly, most of the places where it's
+non-serializable per HTML parsing are actually serializable in Opera's own
+parser.
+-->
 
 <p><a href=#action>Action</a>:
 
@@ -3192,19 +3224,17 @@
 
 <p>To <dfn id=fix-disallowed-ancestors>fix disallowed ancestors</dfn> of a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> <var title="">node</var>:
 
+<p class=XXX>Do we want to get rid of attributes that are no longer allowed
+here?
+
 <ol>
-  <li>If <var title="">node</var> is an <code class=external data-anolis-spec=html title="the li element"><a href=http://www.whatwg.org/html/#the-li-element>li</a></code> and its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> is not an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code>,
-  unset its <code class=external data-anolis-spec=html title=attr-li-value><a href=http://www.whatwg.org/html/#attr-li-value>value</a></code>
-  attribute, if set.
-  <!-- IE9, Firefox 4.0, and Opera 11.10 keep the value attribute even if the
-  parent is now a ul.  Chrome 12 dev strips it even if the parent is now an ol.
-  The spec makes more sense. -->
-
-  <li>If <var title="">node</var> is an <code class=external data-anolis-spec=html title="the li element"><a href=http://www.whatwg.org/html/#the-li-element>li</a></code> and its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> is not an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> or
-  <code class=external data-anolis-spec=html title="the ul element"><a href=http://www.whatwg.org/html/#the-ul-element>ul</a></code>, or <var title="">node</var> is a <code class=external data-anolis-spec=html title="the dt element"><a href=http://www.whatwg.org/html/#the-dt-element>dt</a></code> or <code class=external data-anolis-spec=html title="the dd element"><a href=http://www.whatwg.org/html/#the-dd-element>dd</a></code> and its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> is not a
-  <code class=external data-anolis-spec=html title="the dl element"><a href=http://www.whatwg.org/html/#the-dl-element>dl</a></code>:
+  <li>If <var title="">node</var> is not an <a href=#allowed-child>allowed child</a> of any of its
+  <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-ancestor title=concept-tree-ancestor>ancestors</a> <a href=#in-the-same-editing-host>in the same editing host</a>:
 
   <ol>
+    <li>If <var title="">node</var> is not a <a href=#prohibited-paragraph-child>prohibited paragraph child</a>,
+    abort these steps.
+
     <li><a href=#set-the-tag-name>Set the tag name</a> of <var title="">node</var> to the <a href=#default-single-line-container-name>default
     single-line container name</a>, and let <var title="">node</var> be the result.
 
@@ -3212,15 +3242,14 @@
     <!-- Because maybe it somehow wound up as the child of a p, like via
     insertHTML. -->
 
-    <li><a href=#fix-prohibited-paragraph-descendants>Fix prohibited paragraph descendants</a> of <var title="">node</var>.
+    <li>Let <var title="">descendants</var> be all <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-descendant title=concept-tree-descendant>descendants</a> of <var title="">node</var>.
+
+    <li><a href=#fix-disallowed-ancestors>Fix disallowed ancestors</a> of each member of
+    <var title="">descendants</var>.
 
     <li>Abort these steps.
   </ol>
 
-  <li>If <var title="">node</var> is not an <a href=#allowed-child>allowed child</a> of any of its
-  <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-ancestor title=concept-tree-ancestor>ancestors</a> <a href=#in-the-same-editing-host>in the same editing host</a>, abort these steps and do
-  nothing.
-
   <li>While <var title="">node</var> is not an <a href=#allowed-child>allowed child</a> of its
   <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>, <a href=#split-the-parent>split the parent</a> of the one-<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> list consisting
   of <var title="">node</var>.
--- a/implementation.js	Thu Jun 16 14:11:22 2011 -0600
+++ b/implementation.js	Sun Jun 19 09:40:04 2011 -0600
@@ -825,7 +825,7 @@
 		case "dir":
 		case "ol":
 		case "ul":
-			return ["li", "ol", "ul"].indexOf(child) != -1;
+			return ["dir", "li", "ol", "ul"].indexOf(child) != -1;
 		case "hgroup":
 			return /^h[1-6]$/.test(child);
 	}
@@ -1100,12 +1100,14 @@
 	}
 
 	// "If the first member of node list is an inline node, and original
-	// parent's previousSibling is an inline node other than a br, call
-	// createElement("br") on the context object, then insert the result into
-	// the parent of original parent immediately before original parent."
+	// parent's previousSibling is an inline node other than a br, and original
+	// parent is not an inline node, call createElement("br") on the context
+	// object, then insert the result into the parent of original parent
+	// immediately before original parent."
 	if (isInlineNode(nodeList[0])
 	&& isInlineNode(originalParent.previousSibling)
-	&& !isHtmlElement(originalParent.previousSibling, "br")) {
+	&& !isHtmlElement(originalParent.previousSibling, "br")
+	&& !isInlineNode(originalParent)) {
 		originalParent.parentNode.insertBefore(document.createElement("br"), originalParent);
 	}
 
@@ -1116,11 +1118,13 @@
 	}
 
 	// "If the last member of node list is an inline node other than a br, and
-	// the first child of original parent is a br, remove the first child of
-	// original parent from original parent."
+	// the first child of original parent is a br, and original parent is not
+	// an inline node, remove the first child of original parent from original
+	// parent."
 	if (isInlineNode(nodeList[nodeList.length - 1])
-	&& !isHtmlElement(nodeList[nodeList.length - 1])
-	&& isHtmlElement(originalParent.firstChild, "BR")) {
+	&& !isHtmlElement(nodeList[nodeList.length - 1], "br")
+	&& isHtmlElement(originalParent.firstChild, "br")
+	&& !isInlineNode(originalParent)) {
 		originalParent.removeChild(originalParent.firstChild);
 	}
 
@@ -3691,19 +3695,21 @@
 //@{
 
 function fixDisallowedAncestors(node) {
-	// "If node is an li and its parent is not an ol, unset its value
-	// attribute, if set."
-	if (isHtmlElement(node, "li")
-	&& !isHtmlElement(node.parentNode, "ol")) {
-		node.removeAttribute("value");
-	}
-
-	// "If node is an li and its parent is not an ol or ul, or node is a dt or
-	// dd and its parent is not a dl:"
-	if ((isHtmlElement(node, "li")
-	&& !isHtmlElement(node.parentNode, ["ol", "ul"]))
-	|| (isHtmlElement(node, ["dt", "dd"])
-	&& !isHtmlElement(node.parentNode, "dl"))) {
+	// "If node is not an allowed child of any of its ancestors in the same
+	// editing host:"
+	var hasAllowedAncestor = false;
+	for (var ancestor = node.parentNode; inSameEditingHost(node, ancestor); ancestor = ancestor.parentNode) {
+		if (isAllowedChild(node, ancestor)) {
+			hasAllowedAncestor = true;
+			break;
+		}
+	}
+	if (!hasAllowedAncestor) {
+		// "If node is not a prohibited paragraph child, abort these steps."
+		if (!isProhibitedParagraphChild(node)) {
+			return;
+		}
+
 		// "Set the tag name of node to the default single-line container name,
 		// and let node be the result."
 		node = setTagName(node, defaultSingleLineContainerName);
@@ -3711,32 +3717,18 @@
 		// "Fix disallowed ancestors of node."
 		fixDisallowedAncestors(node);
 
-		// "Fix prohibited paragraph descendants of node."
-		fixProhibitedParagraphDescendants(node);
+		// "Let descendants be all descendants of node."
+		var descendants = getDescendants(node);
+
+		// "Fix disallowed ancestors of each member of descendants."
+		for (var i = 0; i < descendants.length; i++) {
+			fixDisallowedAncestors(descendants[i]);
+		}
 
 		// "Abort these steps."
 		return;
 	}
 
-	// "If node is an allowed child of its parent, or it is not an allowed
-	// child of any of its ancestors in the same editing host, abort these
-	// steps and do nothing."
-	if (isAllowedChild(node, node.parentNode)) {
-		return;
-	}
-	var ancestor = node.parentNode;
-	var hasAllowedAncestor = false;
-	while (inSameEditingHost(node, ancestor)) {
-		if (isAllowedChild(node, ancestor)) {
-			hasAllowedAncestor = true;
-			break;
-		}
-		ancestor = ancestor.parentNode;
-	}
-	if (!hasAllowedAncestor) {
-		return;
-	}
-
 	// "While node is not an allowed child of its parent, split the parent of
 	// the one-node list consisting of node."
 	while (!isAllowedChild(node, node.parentNode)) {
--- a/manualtest.js	Thu Jun 16 14:11:22 2011 -0600
+++ b/manualtest.js	Sun Jun 19 09:40:04 2011 -0600
@@ -149,7 +149,7 @@
 		}
 		if (!compareDiv1.isEqualNode(compareDiv2)) {
 			throw "DOM does not round-trip through serialization (although innerHTML is the same)!  "
-				+ testDiv.innerHTML;
+				+ compareDiv1.innerHTML;
 		}
 
 		browserCell.lastChild.textContent = browserCell.firstChild.innerHTML;
--- a/source.html	Thu Jun 16 14:11:22 2011 -0600
+++ b/source.html	Sun Jun 19 09:40:04 2011 -0600
@@ -580,7 +580,7 @@
     <tr><td>tbody, tfoot, thead <td>td, th, tr
     <tr><td>tr <td>td, th
     <tr><td>dl <td>dt, dd
-    <tr><td>dir, ol, ul <td>li, ol, ul
+    <tr><td>dir, ol, ul <td>dir, li, ol, ul
     <tr><td>hgroup <td>h1, h2, h3, h4, h5, h6
   </table>
 
@@ -648,6 +648,9 @@
 <p>To <dfn>set the tag name</dfn> of an [[element]] <var>element</var> to
 <var>new name</var>:
 
+<p class=XXX>Do we want to get rid of attributes that are no longer allowed
+here?
+
 <ol>
   <li>If <var>element</var> is an <span>HTML element</span> with [[localname]]
   equal to <var>new name</var>, return <var>element</var>.
@@ -711,6 +714,8 @@
 if the parent had styles, classes, etc.  There's not going to be any general
 way to handle this, but we should at least try to handle the special case of
 inline styles, because Firefox does actually add them to arbitrary elements.
+Also, when splitting out of an inline parent, it might be good to wrap all the
+inline descendants in a clone of the former parent.
 
 <ol>
   <li>Let <var>original parent</var> be the [[parent]] of the first member of
@@ -795,8 +800,9 @@
 
   <li>If the first member of <var>node list</var> is an <span>inline
   node</span>, and <var>original parent</var>'s [[previoussibling]] is an
-  <span>inline node</span> other than a [[br]], call [[createelement|"br"]] on
-  the [[contextobject]], then insert the result into the [[parent]] of
+  <span>inline node</span> other than a [[br]], and <var>original parent</var>
+  is not an <span>inline node</span>, call [[createelement|"br"]] on the
+  [[contextobject]], then insert the result into the [[parent]] of
   <var>original parent</var> immediately before <var>original parent</var>.
 
   <li>For each <var>node</var> in <var>node list</var>, insert <var>node</var>
@@ -811,8 +817,9 @@
 
   <li>If the last member of <var>node list</var> is an <span>inline node</span>
   other than a [[br]], and the first [[child]] of <var>original parent</var> is
-  a [[br]], remove the first [[child]] of <var>original parent</var> from
-  <var>original parent</var>.
+  a [[br]], and <var>original parent</var> is not an <span>inline node</span>,
+  remove the first [[child]] of <var>original parent</var> from <var>original
+  parent</var>.
 
   <li>If <var>original parent</var> has no [[children]], remove it from its
   [[parent]].
@@ -2762,7 +2769,32 @@
 
 <h3><dfn>The <code title>insertHTML</code> command</dfn></h3>
 <!-- @{ -->
-<!-- Not supported by IE9. -->
+<!--
+Not supported by IE9.  Handling of disallowed children is interesting:
+
+Firefox 5.0a2: Will allow <dt> inside <dt> (doesn't serialize).  If you try
+inserting dir/ol/ul inside an existing dir/ol/ul, it will strip the list
+element and leave only the li's, so inserting <ul><li>abc</ul> into
+<ol><li>f[o]o</ol> creates <ol><li>f<li>abc<li>o</ol>.  <dt>/<dd>/<li> that
+don't descend from a list will be left alone, not converted to <p>.  Empty
+elements seem not to be inserted.  <li> will get put inside <p>, which breaks
+serialization.  Nothing is allowed inside <xmp>, not even text.
+
+Chrome 13 dev: Inserting a <p> into a <p> or <li> or such will remove the child
+<p>, adding its contents to the parent instead.  Adding an <li> or <hr> as the
+child of a <p> works, as does an <a> inside an <a>, <h2> inside <h1>, <li>
+inside <li>, <nobr> inside <nobr>, <b> inside <xmp>, etc. (all unserializable).
+But <dt> and <dd> seem to get converted to their contents like <p>.
+<ol><li>abc</ol> inside <ol><li>f[o]o</ol> becomes
+<ol><li>f<li>abc<li><li>o</ol>, interestingly (note the empty <li>).  I don't
+understand how it works, but it doesn't seem to make much sense.
+
+Opera 11.11: Seems to do almost no validity or serialization checks, except
+that it prevents <a> inside <a>, <nobr> inside <nobr>, and block elements
+inside inline elements.  Interestingly, most of the places where it's
+non-serializable per HTML parsing are actually serializable in Opera's own
+parser.
+-->
 
 <p><span>Action</span>:
 
@@ -3175,19 +3207,17 @@
 <!-- @{ -->
 <p>To <dfn>fix disallowed ancestors</dfn> of a [[node]] <var>node</var>:
 
+<p class=XXX>Do we want to get rid of attributes that are no longer allowed
+here?
+
 <ol>
-  <li>If <var>node</var> is an [[li]] and its [[parent]] is not an [[ol]],
-  unset its <code data-anolis-spec=html title=attr-li-value>value</code>
-  attribute, if set.
-  <!-- IE9, Firefox 4.0, and Opera 11.10 keep the value attribute even if the
-  parent is now a ul.  Chrome 12 dev strips it even if the parent is now an ol.
-  The spec makes more sense. -->
-
-  <li>If <var>node</var> is an [[li]] and its [[parent]] is not an [[ol]] or
-  [[ul]], or <var>node</var> is a [[dt]] or [[dd]] and its [[parent]] is not a
-  [[dl]]:
+  <li>If <var>node</var> is not an <span>allowed child</span> of any of its
+  [[ancestors]] <span>in the same editing host</span>:
 
   <ol>
+    <li>If <var>node</var> is not a <span>prohibited paragraph child</span>,
+    abort these steps.
+
     <li><span>Set the tag name</span> of <var>node</var> to the <span>default
     single-line container name</span>, and let <var>node</var> be the result.
 
@@ -3195,15 +3225,14 @@
     <!-- Because maybe it somehow wound up as the child of a p, like via
     insertHTML. -->
 
-    <li><span>Fix prohibited paragraph descendants</span> of <var>node</var>.
+    <li>Let <var>descendants</var> be all [[descendants]] of <var>node</var>.
+
+    <li><span>Fix disallowed ancestors</span> of each member of
+    <var>descendants</var>.
 
     <li>Abort these steps.
   </ol>
 
-  <li>If <var>node</var> is not an <span>allowed child</span> of any of its
-  [[ancestors]] <span>in the same editing host</span>, abort these steps and do
-  nothing.
-
   <li>While <var>node</var> is not an <span>allowed child</span> of its
   [[parent]], <span>split the parent</span> of the one-[[node]] list consisting
   of <var>node</var>.
--- a/tests.js	Thu Jun 16 14:11:22 2011 -0600
+++ b/tests.js	Sun Jun 19 09:40:04 2011 -0600
@@ -1352,6 +1352,42 @@
 		['<p>abc', '<ol>{<li>foo</li>}<li>bar</ol>'],
 		['<p>abc', '<ol><li>foo</li>{<li>bar</li>}<li>baz</ol>'],
 		['<p>abc', '<ol><li>[foo]</li><li>bar</ol>'],
+
+		['abc', '<xmp>f[o]o</xmp>'],
+		['<b>abc</b>', '<xmp>f[o]o</xmp>'],
+		['<a>abc</a>', '<a>f[o]o</a>'],
+		['<a href=/>abc</a>', '<a href=.>f[o]o</a>'],
+		['<hr>', '<p>f[o]o'],
+		['<hr>', '<b>f[o]o</b>'],
+		['<h2>abc</h2>', '<h1>f[o]o</h1>'],
+		['<td>abc</td>', '<table><tr><td>f[o]o</table>'],
+		['<td>abc</td>', 'f[o]o'],
+
+		['<dt>abc</dt>', '<dl><dt>f[o]o<dd>bar</dl>'],
+		['<dt>abc</dt>', '<dl><dt>foo<dd>b[a]r</dl>'],
+		['<dd>abc</dd>', '<dl><dt>f[o]o<dd>bar</dl>'],
+		['<dd>abc</dd>', '<dl><dt>foo<dd>b[a]r</dl>'],
+		['<dt>abc</dt>', 'f[o]o'],
+		['<dt>abc</dt>', '<ol><li>f[o]o</ol>'],
+		['<dd>abc</dd>', 'f[o]o'],
+		['<dd>abc</dd>', '<ol><li>f[o]o</ol>'],
+
+		['<li>abc</li>', '<dir><li>f[o]o</dir>'],
+		['<li>abc</li>', '<ol><li>f[o]o</ol>'],
+		['<li>abc</li>', '<ul><li>f[o]o</ul>'],
+		['<dir><li>abc</dir>', '<dir><li>f[o]o</dir>'],
+		['<dir><li>abc</dir>', '<ol><li>f[o]o</ol>'],
+		['<dir><li>abc</dir>', '<ul><li>f[o]o</ul>'],
+		['<ol><li>abc</ol>', '<dir><li>f[o]o</dir>'],
+		['<ol><li>abc</ol>', '<ol><li>f[o]o</ol>'],
+		['<ol><li>abc</ol>', '<ul><li>f[o]o</ul>'],
+		['<ul><li>abc</ul>', '<dir><li>f[o]o</dir>'],
+		['<ul><li>abc</ul>', '<ol><li>f[o]o</ol>'],
+		['<ul><li>abc</ul>', '<ul><li>f[o]o</ul>'],
+		['<li>abc</li>', 'f[o]o'],
+
+		['<nobr>abc</nobr>', '<nobr>f[o]o</nobr>'],
+		['<nobr>abc</nobr>', 'f[o]o'],
 	],
 	insertimage: [
 		'foo[]bar',
@@ -2765,7 +2801,8 @@
 				+ compareDiv1.innerHTML + " vs. " + compareDiv2.innerHTML;
 		}
 		if (!compareDiv1.isEqualNode(compareDiv2)) {
-			throw "DOM does not round-trip through serialization!";
+			throw "DOM does not round-trip through serialization (although innerHTML is the same)!  "
+				+ compareDiv1.innerHTML;
 		}
 
 		if (specCell.firstChild.attributes.length) {