Tweak formatBlock and related algorithms some more
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Wed, 25 May 2011 10:18:04 -0600
changeset 168 cb6aa0868ea5
parent 167 17f5d951b956
child 169 d08f8d6ea8cb
Tweak formatBlock and related algorithms some more
autoimplementation.html
editcommands.html
implementation.js
source.html
--- a/autoimplementation.html	Wed May 25 08:59:52 2011 -0600
+++ b/autoimplementation.html	Wed May 25 10:18:04 2011 -0600
@@ -644,9 +644,45 @@
 		['<p>', '<h1>[foo]<br>bar</h1>'],
 		['<p>', '<h1>foo<br>[bar]</h1>'],
 		['<p>', '<h1>[foo<br>bar]</h1>'],
+		['<address>', '<h1>[foo]<br>bar</h1>'],
+		['<address>', '<h1>foo<br>[bar]</h1>'],
+		['<address>', '<h1>[foo<br>bar]</h1>'],
+		['<pre>', '<h1>[foo]<br>bar</h1>'],
+		['<pre>', '<h1>foo<br>[bar]</h1>'],
+		['<pre>', '<h1>[foo<br>bar]</h1>'],
+		['<h2>', '<h1>[foo]<br>bar</h1>'],
+		['<h2>', '<h1>foo<br>[bar]</h1>'],
+		['<h2>', '<h1>[foo<br>bar]</h1>'],
+
 		['<h1>', '<p>[foo]<br>bar</p>'],
 		['<h1>', '<p>foo<br>[bar]</p>'],
 		['<h1>', '<p>[foo<br>bar]</p>'],
+		['<address>', '<p>[foo]<br>bar</p>'],
+		['<address>', '<p>foo<br>[bar]</p>'],
+		['<address>', '<p>[foo<br>bar]</p>'],
+		['<pre>', '<p>[foo]<br>bar</p>'],
+		['<pre>', '<p>foo<br>[bar]</p>'],
+		['<pre>', '<p>[foo<br>bar]</p>'],
+
+		['<p>', '<address>[foo]<br>bar</address>'],
+		['<p>', '<address>foo<br>[bar]</address>'],
+		['<p>', '<address>[foo<br>bar]</address>'],
+		['<pre>', '<address>[foo]<br>bar</address>'],
+		['<pre>', '<address>foo<br>[bar]</address>'],
+		['<pre>', '<address>[foo<br>bar]</address>'],
+		['<h1>', '<address>[foo]<br>bar</address>'],
+		['<h1>', '<address>foo<br>[bar]</address>'],
+		['<h1>', '<address>[foo<br>bar]</address>'],
+
+		['<p>', '<pre>[foo]<br>bar</pre>'],
+		['<p>', '<pre>foo<br>[bar]</pre>'],
+		['<p>', '<pre>[foo<br>bar]</pre>'],
+		['<address>', '<pre>[foo]<br>bar</pre>'],
+		['<address>', '<pre>foo<br>[bar]</pre>'],
+		['<address>', '<pre>[foo<br>bar]</pre>'],
+		['<h1>', '<pre>[foo]<br>bar</pre>'],
+		['<h1>', '<pre>foo<br>[bar]</pre>'],
+		['<h1>', '<pre>[foo<br>bar]</pre>'],
 	],
 	hilitecolor: [
 		'foo[]bar',
--- a/editcommands.html	Wed May 25 08:59:52 2011 -0600
+++ b/editcommands.html	Wed May 25 10:18:04 2011 -0600
@@ -293,6 +293,43 @@
 editable, so authors are assured that editing commands will only modify the
 editing host's contents and not the editing host itself.
 
+<p>The <dfn id=editing-host-of>editing host of</dfn> <var title="">node</var> is null if <var title="">node</var> is
+neither <a href=#editable>editable</a> nor an <a href=#editing-host>editing host</a>; <var title="">node</var>
+itself, if <var title="">node</var> is an <a href=#editing-host>editing host</a>; or the nearest
+<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>ancestor</a> of <var title="">node</var> that is an <a href=#editing-host>editing host</a>, if
+<var title="">node</var> is <a href=#editable>editable</a>.
+
+<p>Two <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a> are <dfn id=in-the-same-editing-host>in the same editing host</dfn> if both are
+<a href=#editable>editable</a> and the <a href=#editing-host-of>editing host of</a> both is the same.
+
+<p>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="">child</var> is an <dfn id=allowed-child>allowed child</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="">parent</var> unless one of the following conditions is met:
+
+<p class=XXX>This is very ad hoc and might need to be rethought.  We can't use
+the HTML spec's definitions because those are too complicated, they don't take
+obsolete elements into account, and they're sometimes too restrictive.  (We
+don't like having to contort the DOM to ensure that it's valid, because it can
+have unwanted side effects, so we want to minimize the number of cases we
+disallow.)  Mostly this list covers only things that don't serialize as
+text/html.
+
+<ul>
+  <li><var title="">child</var> is an <code class=external data-anolis-spec=html title="the a element"><a href=http://www.whatwg.org/html/#the-a-element>a</a></code>, and <var title="">parent</var> or some <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>ancestor</a>
+  of <var title="">parent</var> is an <code class=external data-anolis-spec=html title="the a element"><a href=http://www.whatwg.org/html/#the-a-element>a</a></code>.  <!-- Cannot be serialized as text/html.
+  -->
+
+  <li><var title="">child</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>
+  "address", "article", "aside", "blockquote", "center", "details", "dir",
+  "div", "dl", "fieldset", "figcaption", "figure", "footer", "h1", "h2", "h3",
+  "h4", "h5", "h6", "header", "hgroup", "hr", "listing", "menu", "nav", "ol",
+  "p", "plaintext", "pre", "section", "summary", "table", "ul", or "xmp"; and
+  <var title="">parent</var> or some <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>ancestor</a> of <var title="">parent</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> "h1", "h2", "h3", "h4", "h5", "h6", or "p".
+  <!-- This cannot be serialized as text/html if the parent is a p, or if the
+  parent and child are both h*.  Something like <h1>foo<p>bar</p></h1> will
+  actually work, but while we're here, we may as well disallow it. -->
+</ul>
+
 <p>The <dfn id=css-styling-flag>CSS styling flag</dfn> is a boolean flag, which must initially be
 false.
 
@@ -341,13 +378,14 @@
   <li>Return <var title="">replacement element</var>.
 </ol>
 
-<p>To remove 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> while <dfn id=preserving-its-descendants>preserving its
-descendants</dfn>, <a href=#split-the-parent>split the parent</a> of <var title="">node</var>'s
-<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>.
-
 <p>To <dfn id=split-the-parent>split the parent</dfn> of a list <var title="">node list</var> of consecutive
 <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-sibling title=concept-tree-sibling>sibling</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>nodes</a>:
 
+<p class=XXX>Pretty much any time we call this algorithm, it can cause trouble
+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.
+
 <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
   <var title="">node list</var>.
@@ -360,18 +398,12 @@
   parent</var>.
 
   <li>If the last <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 in <var title="">node
-  list</var>:
-
-  <ol>
-    <li><a href=#remove-extraneous-line-breaks-after>Remove extraneous line breaks after</a> <var title="">original
-    parent</var>.
-
-    <li>If <var title="">original parent</var>'s last <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> and <code class=external data-anolis-spec=domcore title=dom-Node-nextSibling><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-nextsibling>nextSibling</a></code> are
-    both <a href=#inline-node title="inline node">inline nodes</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
-    <code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument>ownerDocument</a></code> of <var title="">original parent</var>, 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 after
-    <var title="">original parent</var>.
-  </ol>
+  list</var>, and <var title="">original parent</var>'s last <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> and
+  <code class=external data-anolis-spec=domcore title=dom-Node-nextSibling><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-nextsibling>nextSibling</a></code> are both <a href=#inline-node title="inline node">inline nodes</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
+  <code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument>ownerDocument</a></code> of <var title="">original parent</var>, 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 after <var title="">original
+  parent</var>.
 
   <li>If 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 not in <var title="">node
   list</var>, but its last <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> is:
@@ -402,6 +434,9 @@
     parent</var> immediately after <var title="">original parent</var>, <a href=#preserving-ranges>preserving
     ranges</a>.
 
+    <li><a href=#remove-extraneous-line-breaks-at-the-end-of>Remove extraneous line breaks at the end of</a> <var title="">original
+    parent</var>.
+
     <li>Abort these steps.
   </ol>
 
@@ -439,10 +474,19 @@
   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>, <a href=#preserving-ranges>preserving ranges</a>.
 
+  <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>.
+
   <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>.
 </ol>
 
+<p>To remove 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> while <dfn id=preserving-its-descendants>preserving its
+descendants</dfn>, <a href=#split-the-parent>split the parent</a> of <var title="">node</var>'s
+<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>.
+
 <p>To <dfn id=wrap>wrap</dfn> a list <var title="">node list</var> of consecutive <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-sibling title=concept-tree-sibling>sibling</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>nodes</a>, given <dfn id=sibling-criteria>sibling criteria</dfn> and <dfn id=new-parent-instructions>new parent
 instructions</dfn>:
@@ -467,11 +511,9 @@
   <li>If <var title="">new parent</var>'s <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 null:
 
   <ol>
-    <li>If <var title="">new parent</var> cannot be the <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 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 <var title="">node list</var>, <a href=#split-the-parent>split the parent</a> of
-    <var title="">node list</var>.
-
-    <p class=XXX>"Cannot be the child" needs to be defined.
+    <li>While <var title="">new parent</var> is not an <a href=#allowed-child>allowed child</a> of 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 <var title="">node list</var>, <a href=#split-the-parent>split the
+    parent</a> of <var title="">node list</var>.
 
     <li>Insert <var title="">new parent</var> 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 the first member of
     <var title="">node list</var> immediately before the first member of <var title="">node
@@ -555,7 +597,7 @@
   of <var title="">node</var> 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>.
 </ol>
 
-<p>To <dfn id=remove-extraneous-line-breaks-after>remove extraneous line breaks after</dfn> 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>
+<p>To <dfn id=remove-extraneous-line-breaks-at-the-end-of>remove extraneous line breaks at the end of</dfn> 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>:
 
 <ol>
@@ -569,8 +611,8 @@
 </ol>
 
 <p>To <dfn id=remove-extraneous-line-breaks-from>remove extraneous line breaks from</dfn> 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>, first
-<a href=#remove-extraneous-line-breaks-before>remove extraneous line breaks before</a> it, then <a href=#remove-extraneous-line-breaks-after>remove
-extraneous line breaks after</a> it.
+<a href=#remove-extraneous-line-breaks-before>remove extraneous line breaks before</a> it, then <a href=#remove-extraneous-line-breaks-at-the-end-of>remove
+extraneous line breaks at the end of</a> it.
 
 <p>To move 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> to a new location, <dfn id=preserving-ranges>preserving ranges</dfn>, remove
 the <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> from its original <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> (if any), then insert it in the new
@@ -3549,6 +3591,26 @@
   "header", "hgroup", "li", "nav", "ol", "section", "table", "tbody", "td",
   "th", "thead", "tr", or "ul".
 
+  <li>For each <var title="">node</var> in <var title="">node list</var>, while <var title="">node</var>
+  is a <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>descendant</a> of an <a href=#html-element>HTML element</a> <a href=#in-the-same-editing-host>in the same editing
+  host</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> "address", "h1", "h2", "h3", "h4", "h5", "h6",
+  "p", or "pre", <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
+  consistin of <var title="">node</var>.
+
+  <div class=XXX>
+  <p>This is needed so we don't get things like p nested inside address, and
+  instead convert part of the multi-line address into a p.  But div can contain
+  any of these things, so we don't break that apart ever, which is bad.  It
+  means if you have
+
+  </p><xmp><div>foo<br>bar</div></xmp>
+
+  <p>then formatBlocking "foo" then "bar" as p has different results from doing
+  both at once.  Maybe we should split divs as well, but only if they're the
+  parent of the node we're dealing with?  Or just split divs always, and hope
+  the "in the same editing host" thing handles it?
+  </div>
+
   <!--
   We have two different behaviors, one for div and p and one for everything
   else.  The basic difference is that for div and p, we assume that it should
--- a/implementation.js	Wed May 25 08:59:52 2011 -0600
+++ b/implementation.js	Wed May 25 10:18:04 2011 -0600
@@ -443,12 +443,6 @@
 	return replacementElement;
 }
 
-// "To remove a node node while preserving its descendants, split the parent of
-// node's children."
-function removePreservingDescendants(node) {
-	splitParent([].slice.call(node.childNodes));
-}
-
 function splitParent(nodeList) {
 	// "Let original parent be the parent of the first member of node list."
 	var originalParent = nodeList[0].parentNode;
@@ -466,19 +460,15 @@
 		removeExtraneousLineBreaksBefore(originalParent);
 	}
 
-	// "If the last child of original parent is in node list:"
-	if (nodeList.indexOf(originalParent.lastChild) != -1) {
-		// "Remove extraneous line breaks after original parent."
-		removeExtraneousLineBreaksAfter(originalParent);
-
-		// "If original parent's last child and nextSibling are both inline
-		// nodes, call createElement("br") on the ownerDocument of original
-		// parent, then insert the result into the parent of original parent
-		// immediately after original parent."
-		if (isInlineNode(originalParent.lastChild)
-		&& isInlineNode(originalParent.nextSibling)) {
-			originalParent.parentNode.insertBefore(originalParent.ownerDocument.createElement("br"), originalParent.nextSibling);
-		}
+	// "If the last child of original parent is in node list, and original
+	// parent's last child and nextSibling are both inline nodes, call
+	// createElement("br") on the ownerDocument of original parent, then insert
+	// the result into the parent of original parent immediately after original
+	// parent."
+	if (nodeList.indexOf(originalParent.lastChild) != -1
+	&& isInlineNode(originalParent.lastChild)
+	&& isInlineNode(originalParent.nextSibling)) {
+		originalParent.parentNode.insertBefore(originalParent.ownerDocument.createElement("br"), originalParent.nextSibling);
 	}
 
 	// "If the first child of original parent is not in node list, but its last
@@ -492,6 +482,9 @@
 			movePreservingRanges(nodeList[i], originalParent.parentNode, 1 + getNodeIndex(originalParent));
 		}
 
+		// "Remove extraneous line breaks at the end of original parent."
+		removeExtraneousLineBreaksAtTheEndOf(originalParent);
+
 		// "Abort these steps."
 		return;
 	}
@@ -529,12 +522,27 @@
 		movePreservingRanges(nodeList[i], originalParent.parentNode, getNodeIndex(originalParent));
 	}
 
+	// "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."
+	if (isInlineNode(nodeList[nodeList.length - 1])
+	&& !isHtmlElement(nodeList[nodeList.length - 1])
+	&& isHtmlElement(originalParent.firstChild, "BR")) {
+		originalParent.removeChild(originalParent.firstChild);
+	}
+
 	// "If original parent has no children, remove it from its parent."
 	if (!originalParent.hasChildNodes()) {
 		originalParent.parentNode.removeChild(originalParent);
 	}
 }
 
+// "To remove a node node while preserving its descendants, split the parent of
+// node's children."
+function removePreservingDescendants(node) {
+	splitParent([].slice.call(node.childNodes));
+}
+
 function wrap(nodeList, siblingCriteria, newParentInstructions) {
 	// "If node list is empty, or the first member of node list is not
 	// editable, return null and abort these steps."
@@ -566,11 +574,9 @@
 
 	// "If new parent's parent is null:"
 	if (!newParent.parentNode) {
-		// "If new parent cannot be the child of the parent of the first member
-		// of node list, split the parent of node list."
-		//
-		// Hack for now, as usual.  Don't use this for inline elements!
-		if (isHtmlElement(nodeList[0].parentNode, "P")) {
+		// "While new parent is not an allowed child of the parent of the first
+		// member of node list, split the parent of node list."
+		while (!isAllowedChild(newParent, nodeList[0].parentNode)) {
 			splitParent(nodeList);
 		}
 
@@ -675,7 +681,7 @@
 	}
 }
 
-function removeExtraneousLineBreaksAfter(node) {
+function removeExtraneousLineBreaksAtTheEndOf(node) {
 	// "If node is not an Element, or it is an inline node, do nothing and
 	// abort these steps."
 	if (!node
@@ -696,10 +702,10 @@
 }
 
 // "To remove extraneous line breaks from a node, first remove extraneous line
-// breaks before it, then remove extraneous line breaks after it."
+// breaks before it, then remove extraneous line breaks at the end of it."
 function removeExtraneousLineBreaksFrom(node) {
 	removeExtraneousLineBreaksBefore(node);
-	removeExtraneousLineBreaksAfter(node);
+	removeExtraneousLineBreaksAtTheEndOf(node);
 }
 
 // "An editing host is a node that is either an Element with a contenteditable
@@ -726,6 +732,70 @@
 		&& (isEditingHost(node.parentNode) || isEditable(node.parentNode));
 }
 
+// "The editing host of node is null if node is neither editable nor an editing
+// host; node itself, if node is an editing host; or the nearest ancestor of
+// node that is an editing host, if node is editable."
+function getEditingHostOf(node) {
+	if (isEditingHost(node)) {
+		return node;
+	} else if (isEditable(node)) {
+		var ancestor = node.parentNode;
+		while (!isEditingHost(ancestor)) {
+			ancestor = ancestor.parentNode;
+		}
+		return ancestor;
+	} else {
+		return null;
+	}
+}
+
+// "Two nodes are in the same editing host if both are editable and the editing
+// host of both is the same."
+function inSameEditingHost(node1, node2) {
+	return isEditable(node1)
+		&& isEditable(node2)
+		&& getEditingHostOf(node1) == getEditingHostOf(node2);
+}
+
+// "A node child is an allowed child of a node parent unless one of the
+// following conditions is met:
+//
+//   * "child is an a, and parent or some ancestor of parent is an a.
+//   * "child is an HTML element with local name "address", "article", "aside",
+//     "blockquote", "center", "details", "dir", "div", "dl", "fieldset",
+//     "figcaption", "figure", "footer", "h1", "h2", "h3", "h4", "h5", "h6",
+//     "header", "hgroup", "hr", "listing", "menu", "nav", "ol", "p",
+//     "plaintext", "pre", "section", "summary", "table", "ul", or "xmp"; and
+//     parent or some ancestor of parent is an HTML element with local name
+//     "h1", "h2", "h3", "h4", "h5", "h6", or "p"."
+function isAllowedChild(child, parent_) {
+	if (isHtmlElement(child, "A")) {
+		var ancestor = parent_;
+		while (ancestor && !isHtmlElement(ancestor, "A")) {
+			ancestor = ancestor.parentNode;
+		}
+		if (isHtmlElement(ancestor, "A")) {
+			return false;
+		}
+	}
+
+	if (isHtmlElement(child, ["ADDRESS", "ARTICLE", "ASIDE", "BLOCKQUOTE",
+	"CENTER", "DETAILS", "DIR", "DIV", "DL", "FIELDSET", "FIGCAPTION",
+	"FIGURE", "FOOTER", "H1", "H2", "H3", "H4", "H5", "H6", "HEADER", "HGROUP",
+	"HR", "LISTING", "MENU", "NAV", "OL", "P", "PLAINTEXT", "PRE", "SECTION",
+	"SUMMARY", "TABLE", "UL", "XMP"])) {
+		var ancestor = parent_;
+		while (ancestor && !isHtmlElement(ancestor, ["H1", "H2", "H3", "H4", "H5", "H6", "P"])) {
+			ancestor = ancestor.parentNode;
+		}
+		if (isHtmlElement(ancestor, ["H1", "H2", "H3", "H4", "H5", "H6", "P"])) {
+			return false;
+		}
+	}
+
+	return true;
+}
+
 function hasEditableDescendants(node) {
 	for (var i = 0; i < node.childNodes.length; i++) {
 		if (isEditable(node.childNodes[i])
@@ -2523,6 +2593,28 @@
 				"TBODY", "TD", "TH", "THEAD", "TR", "UL"]);
 		});
 
+		// "For each node in node list, while node is a descendant of an HTML
+		// element in the same editing host with local name "address", "h1",
+		// "h2", "h3", "h4", "h5", "h6", "p", or "pre", split the parent of the
+		// one-node list consisting of node."
+		for (var i = 0; i < nodeList.length; i++) {
+			node = nodeList[i];
+
+			do {
+				var ancestor = node.parentNode;
+				while (ancestor
+				&& !isHtmlElement(ancestor, ["ADDRESS", "H1", "H2", "H3", "H4", "H5", "H6", "P", "PRE"])) {
+					ancestor = ancestor.parentNode;
+				}
+				if (ancestor
+				&& inSameEditingHost(node, ancestor)) {
+					splitParent([node]);
+				} else {
+					break;
+				}
+			} while (true);
+		}
+
 		// "If value is "div" or "p", then while node list is not empty:"
 		if (value == "div" || value == "p") {
 			while (nodeList.length) {
--- a/source.html	Wed May 25 08:59:52 2011 -0600
+++ b/source.html	Wed May 25 10:18:04 2011 -0600
@@ -247,6 +247,43 @@
 editable, so authors are assured that editing commands will only modify the
 editing host's contents and not the editing host itself.
 
+<p>The <dfn>editing host of</dfn> <var>node</var> is null if <var>node</var> is
+neither <span>editable</span> nor an <span>editing host</span>; <var>node</var>
+itself, if <var>node</var> is an <span>editing host</span>; or the nearest
+[[ancestor]] of <var>node</var> that is an <span>editing host</span>, if
+<var>node</var> is <span>editable</span>.
+
+<p>Two [[nodes]] are <dfn>in the same editing host</dfn> if both are
+<span>editable</span> and the <span>editing host of</span> both is the same.
+
+<p>A [[node]] <var>child</var> is an <dfn>allowed child</dfn> of a [[node]]
+<var>parent</var> unless one of the following conditions is met:
+
+<p class=XXX>This is very ad hoc and might need to be rethought.  We can't use
+the HTML spec's definitions because those are too complicated, they don't take
+obsolete elements into account, and they're sometimes too restrictive.  (We
+don't like having to contort the DOM to ensure that it's valid, because it can
+have unwanted side effects, so we want to minimize the number of cases we
+disallow.)  Mostly this list covers only things that don't serialize as
+text/html.
+
+<ul>
+  <li><var>child</var> is an [[a]], and <var>parent</var> or some [[ancestor]]
+  of <var>parent</var> is an [[a]].  <!-- Cannot be serialized as text/html.
+  -->
+
+  <li><var>child</var> is an <span>HTML element</span> with [[localname]]
+  "address", "article", "aside", "blockquote", "center", "details", "dir",
+  "div", "dl", "fieldset", "figcaption", "figure", "footer", "h1", "h2", "h3",
+  "h4", "h5", "h6", "header", "hgroup", "hr", "listing", "menu", "nav", "ol",
+  "p", "plaintext", "pre", "section", "summary", "table", "ul", or "xmp"; and
+  <var>parent</var> or some [[ancestor]] of <var>parent</var> is an <span>HTML
+  element</span> with [[localname]] "h1", "h2", "h3", "h4", "h5", "h6", or "p".
+  <!-- This cannot be serialized as text/html if the parent is a p, or if the
+  parent and child are both h*.  Something like <h1>foo<p>bar</p></h1> will
+  actually work, but while we're here, we may as well disallow it. -->
+</ul>
+
 <p>The <dfn>CSS styling flag</dfn> is a boolean flag, which must initially be
 false.
 
@@ -297,13 +334,14 @@
   <li>Return <var>replacement element</var>.
 </ol>
 
-<p>To remove a [[node]] <var>node</var> while <dfn>preserving its
-descendants</dfn>, <span>split the parent</span> of <var>node</var>'s
-[[children]].
-
 <p>To <dfn>split the parent</dfn> of a list <var>node list</var> of consecutive
 [[sibling]] [[nodes]]:
 
+<p class=XXX>Pretty much any time we call this algorithm, it can cause trouble
+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.
+
 <ol>
   <li>Let <var>original parent</var> be the [[parent]] of the first member of
   <var>node list</var>.
@@ -316,20 +354,13 @@
   parent</var>.
 
   <li>If the last [[child]] of <var>original parent</var> is in <var>node
-  list</var>:
-
-  <ol>
-    <li><span>Remove extraneous line breaks after</span> <var>original
-    parent</var>.
-
-    <li>If <var>original parent</var>'s last [[child]] and [[nextsibling]] are
-    both <span title="inline node">inline nodes</span>, call <code
-    data-anolis-spec=domcore
-    title=dom-Document-createElement>createElement("br")</code> on the
-    [[ownerdocument]] of <var>original parent</var>, then insert the result
-    into the [[parent]] of <var>original parent</var> immediately after
-    <var>original parent</var>.
-  </ol>
+  list</var>, and <var>original parent</var>'s last [[child]] and
+  [[nextsibling]] are both <span title="inline node">inline nodes</span>, call
+  <code data-anolis-spec=domcore
+  title=dom-Document-createElement>createElement("br")</code> on the
+  [[ownerdocument]] of <var>original parent</var>, then insert the result into
+  the [[parent]] of <var>original parent</var> immediately after <var>original
+  parent</var>.
 
   <li>If the first [[child]] of <var>original parent</var> is not in <var>node
   list</var>, but its last [[child]] is:
@@ -360,6 +391,9 @@
     parent</var> immediately after <var>original parent</var>, <span>preserving
     ranges</span>.
 
+    <li><span>Remove extraneous line breaks at the end of</span> <var>original
+    parent</var>.
+
     <li>Abort these steps.
   </ol>
 
@@ -399,10 +433,19 @@
   into the [[parent]] of <var>original parent</var> immediately before
   <var>original parent</var>, <span>preserving ranges</span>.
 
+  <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>.
+
   <li>If <var>original parent</var> has no [[children]], remove it from its
   [[parent]].
 </ol>
 
+<p>To remove a [[node]] <var>node</var> while <dfn>preserving its
+descendants</dfn>, <span>split the parent</span> of <var>node</var>'s
+[[children]].
+
 <p>To <dfn>wrap</dfn> a list <var>node list</var> of consecutive [[sibling]]
 [[nodes]], given <dfn>sibling criteria</dfn> and <dfn>new parent
 instructions</dfn>:
@@ -427,11 +470,9 @@
   <li>If <var>new parent</var>'s [[parent]] is null:
 
   <ol>
-    <li>If <var>new parent</var> cannot be the [[child]] of the [[parent]] of
-    the first member of <var>node list</var>, <span>split the parent</span> of
-    <var>node list</var>.
-
-    <p class=XXX>"Cannot be the child" needs to be defined.
+    <li>While <var>new parent</var> is not an <span>allowed child</span> of the
+    [[parent]] of the first member of <var>node list</var>, <span>split the
+    parent</span> of <var>node list</var>.
 
     <li>Insert <var>new parent</var> into the [[parent]] of the first member of
     <var>node list</var> immediately before the first member of <var>node
@@ -518,7 +559,7 @@
   of <var>node</var> from its [[parent]].
 </ol>
 
-<p>To <dfn>remove extraneous line breaks after</dfn> a [[node]]
+<p>To <dfn>remove extraneous line breaks at the end of</dfn> a [[node]]
 <var>node</var>:
 
 <ol>
@@ -533,7 +574,7 @@
 
 <p>To <dfn>remove extraneous line breaks from</dfn> a [[node]], first
 <span>remove extraneous line breaks before</span> it, then <span>remove
-extraneous line breaks after</span> it.
+extraneous line breaks at the end of</span> it.
 
 <p>To move a [[node]] to a new location, <dfn>preserving ranges</dfn>, remove
 the [[node]] from its original [[parent]] (if any), then insert it in the new
@@ -3579,6 +3620,26 @@
   "header", "hgroup", "li", "nav", "ol", "section", "table", "tbody", "td",
   "th", "thead", "tr", or "ul".
 
+  <li>For each <var>node</var> in <var>node list</var>, while <var>node</var>
+  is a [[descendant]] of an <span>HTML element</span> <span>in the same editing
+  host</span> with [[localname]] "address", "h1", "h2", "h3", "h4", "h5", "h6",
+  "p", or "pre", <span>split the parent</span> of the one-[[node]] list
+  consistin of <var>node</var>.
+
+  <div class=XXX>
+  <p>This is needed so we don't get things like p nested inside address, and
+  instead convert part of the multi-line address into a p.  But div can contain
+  any of these things, so we don't break that apart ever, which is bad.  It
+  means if you have
+
+  <xmp><div>foo<br>bar</div></xmp>
+
+  <p>then formatBlocking "foo" then "bar" as p has different results from doing
+  both at once.  Maybe we should split divs as well, but only if they're the
+  parent of the node we're dealing with?  Or just split divs always, and hope
+  the "in the same editing host" thing handles it?
+  </div>
+
   <!--
   We have two different behaviors, one for div and p and one for everything
   else.  The basic difference is that for div and p, we assume that it should