Greatly improve "delete the selection" algorithm
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Fri, 03 Jun 2011 13:08:06 -0600
changeset 226 9309a882b6ac
parent 225 bb59ae1bbffd
child 227 6c96ab178ca6
Greatly improve "delete the selection" algorithm

Currently I'm really just using insertImage for testing, but now I'm
going to start working on defining delete.
autoimplementation.html
editcommands.html
implementation.js
source.html
--- a/autoimplementation.html	Thu Jun 02 15:37:20 2011 -0600
+++ b/autoimplementation.html	Fri Jun 03 13:08:06 2011 -0600
@@ -943,6 +943,22 @@
 		'<p>foo[bar<p><b>baz]quz</b>',
 
 		'<div><p>foo<p>[bar<p>baz]</div>',
+
+		'foo[<br>]bar',
+		'<p>foo[</p><p>]bar</p>',
+		'<p>foo[</p><p>]bar<br>baz</p>',
+		'foo[<p>]bar</p>',
+		'foo[<p>]bar<br>baz</p>',
+		'foo[<p>]bar</p>baz',
+		'<p>foo[</p>]bar',
+		'<p>foo[</p>]bar<br>baz',
+		'<p>foo[</p>]bar<p>baz</p>',
+		'foo[<div><p>]bar</div>',
+		'<div><p>foo[</p></div>]bar',
+		'foo[<div><p>]bar</p>baz</div>',
+		'foo[<div>]bar<p>baz</p></div>',
+		'<div><p>foo</p>bar[</div>]baz',
+		'<div>foo<p>bar[</p></div>]baz',
 	],
 	insertorderedlist: [
 		'foo[]bar',
--- a/editcommands.html	Thu Jun 02 15:37:20 2011 -0600
+++ b/editcommands.html	Fri Jun 03 13:08:06 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-2-june-2011>Work in Progress &mdash; Last Update 2 June 2011</h2>
+<h2 class="no-num no-toc" id=work-in-progress-&mdash;-last-update-3-june-2011>Work in Progress &mdash; Last Update 3 June 2011</h2>
 <dl>
  <dt>Editor
  <dd>Aryeh Gregor &lt;ayg+spec@aryeh.name&gt;
@@ -758,6 +758,9 @@
   <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>.
 
+  <li>Otherwise, <a href=#remove-extraneous-line-breaks-before>remove extraneous line breaks before</a>
+  <var title="">original parent</var>.
+
   <li>If <var title="">node list</var>'s last member's <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> is null,
   <a href=#remove-extraneous-line-breaks-at-the-end-of>remove extraneous line breaks at the end of</a> <var title="">node
   list</var>'s last member'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>.
@@ -933,26 +936,22 @@
 block element empty, when we really need to add 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> <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> no matter
 what so it doesn't collapse.
 
+<p class=XXX>I'm uncertain about the use of prohibited paragraph children here.
+I'm using it mostly because it's convenient and seems relatively sensible.  If
+we really want to use it, we probably want to change its name.
+
 <ol>
   <li>Let <var title="">range</var> be the <a href=#active-range>active range</a>.
 
   <li>If <var title="">range</var> is null, abort these steps and do nothing.
 
-  <li>If no <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> is <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> in <var title="">range</var>, and
-  either <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> is not a <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code> or <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment>Comment</a></code> node
-  or <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a> is its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a>,
-  and either <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> is not a <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text>Text</a></code> or <code class=external data-anolis-spec=domcore><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment>Comment</a></code>
-  node or <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a> is 0, call <code class=external data-anolis-spec=domrange title=dom-Range-deleteContents><a href=http://html5.org/specs/dom-range.html#dom-range-deletecontents>deleteContents()</a></code> on <var title="">range</var>
-  and abort these steps.
-  <!-- This is precisely the case where deleteContents() will not alter the
-  DOM, just collapse the selection.  I think. -->
-
-  <p class=XXX>Is it possible for subsequent steps to make the range's start go
-  past its end or vice versa, or does this step successfully prevent that case?
+  <li>Let <var title="">start node</var>, <var title="">start offset</var>, <var title="">end node</var>,
+  and <var title="">end offset</var> be <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> and
+  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>nodes</a> and <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offsets</a>.
 
   <!-- Drill the range down to the lowest possible level, so we don't delete
   more elements than necessary. -->
-  <li>While the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> of <var title="">range</var> has at least one <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>:
+  <li>While <var title="">start node</var> has at least one <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>:
   <!--
   We don't want to keep going when we hit an element with no children, because
   then we'd do something like
@@ -966,93 +965,181 @@
   -->
 
   <ol>
-    <li>If <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a> is its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>'s
-    <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a>, and its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>'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 not null, set
-    <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> to (<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 class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>, 1 +
-    <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>) and continue this loop from the beginning.
+    <li>If <var title="">start offset</var> is <var title="">start node</var>'s
+    <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a>, and <var title="">start node</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 <a href=#in-the-same-editing-host>in the same
+    editing host</a>, and <var title="">start node</var> is not a <a href=#prohibited-paragraph-child>prohibited
+    paragraph child</a>, set <var title="">start offset</var> to one plus the
+    <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <var title="">start node</var>, then set <var title="">start node</var> to 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> and continue this loop from the beginning.
     <!--
     For instance:
 
       <b>foo[</b><i>bar]</i>
+      -> <b>foo{</b><i>bar]</i>
       -> <b>foo</b>{<i>bar]</i>
 
     Then the next step will make it <b>foo</b><i>[bar]</i>.
+
+    We don't want to do this for prohibited paragraph children, because that
+    would lead to something like
+
+      <p>foo[</p><p>]bar<p>
+
+    ultimately collapsing, which is wrong.  Once we do the deletion, it needs
+    to wind up <p>foo[]bar</p>, whereas an actually collapsed selection should
+    do nothing.
     -->
 
-    <li>If <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a> is its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>'s
-    <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a>, break from this loop.
+    <li>If <var title="">start offset</var> is <var title="">start node</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a>,
+    break from this loop.
     <!-- This happens if the first step brought us all the way up to the root.
     The step immediately after this loop will bring us back down again.  -->
 
-    <li>Let <var title="">reference node</var> 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 <var title="">range</var>'s
-    <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> equal to its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a>.
+    <li>Let <var title="">reference node</var> 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 <var title="">start node</var>
+    with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> equal to <var title="">start offset</var>.
 
     <li>If <var title="">reference node</var> is 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> with 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>,
     break from this loop.
     <!-- Don't descend into it, since then it won't get deleted even if it's
     selected. -->
 
-    <li>Set <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> to (<var title="">reference node</var>,
-    0).
+    <li>Set <var title="">start node</var> to <var title="">reference node</var> and <var title="">start
+    offset</var> to 0.
   </ol>
 
-  <!-- Corner case: maybe we went all the way up to the end of the root. -->
-  <li>If <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a> is the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of its
-  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>, then while <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> has <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>
-  and 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 not 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> with 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>, set
-  <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> to (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 <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>,
-  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of 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 <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>).
-
-  <li>While the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> of <var title="">range</var> has at least one <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>:
+  <li>While <var title="">end node</var> has at least one <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>:
 
   <ol>
-    <li>If <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a> is 0, and its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>'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 not null, set <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> to (<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 class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>, <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>) and continue this loop from the
-    beginning.
-
-    <li>If <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a> is 0, break from this loop.
-
-    <li>Let <var title="">reference node</var> 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 <var title="">range</var>'s
-    <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> equal to its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a> minus one.
+    <li>If <var title="">end offset</var> is 0, and <var title="">end node</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 <a href=#in-the-same-editing-host>in the same editing host</a>, and <var title="">end
+    node</var> is not a <a href=#prohibited-paragraph-child>prohibited paragraph child</a>, set <var title="">end
+    offset</var> to the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <var title="">end node</var>, then set <var title="">end
+    node</var> to 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> and continue this loop from the beginning.
+
+    <li>If <var title="">end offset</var> is 0, break from this loop.
+
+    <li>Let <var title="">reference node</var> 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 <var title="">end node</var>
+    with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> equal to <var title="">end offset</var> minus one.
 
     <li>If <var title="">reference node</var> is 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> with 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>,
     break from this loop.
 
-    <li>Set <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> to (<var title="">reference node</var>,
-    <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of <var title="">reference node</var>).
+    <li>Set <var title="">end node</var> to <var title="">reference node</var> and <var title="">end
+    offset</var> to the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of <var title="">reference node</var>.
   </ol>
 
-  <li>If <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a> is 0, then while <var title="">range</var>'s
-  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> has <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> and its 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> is not 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>
-  with 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>, set <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> to (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 <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>, 0).
+  <li>If (<var title="">end node</var>, <var title="">end offset</var>) is <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-bp-before title=concept-bp-before>before</a> (<var title="">start
+  node</var>, <var title="">start offset</var>), call <code class=external data-anolis-spec=domrange title=dom-Range-deleteContents><a href=http://html5.org/specs/dom-range.html#dom-range-deletecontents>deleteContents()</a></code> on <var title="">range</var>
+  and abort these steps.
+
+  <li>Set <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> to (<var title="">start node</var>,
+  <var title="">start offset</var>) and its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> to (<var title="">end node</var>,
+  <var title="">end offset</var>).
 
   <li>Let <var title="">start block</var> be the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> of
   <var title="">range</var>.
 
   <li>While <var title="">start block</var> is not a <a href=#prohibited-paragraph-child>prohibited paragraph
-  child</a>, set <var title="">start block</var> to 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>.
-
-  <p class=XXX>I'm uncertain about the use of prohibited paragraph children
-  here.  I'm using it mostly because it's convenient and seems relatively
-  sensible.  If we really want to use it, we probably want to change its name.
+  child</a> 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 <a href=#in-the-same-editing-host>in the same editing host</a>, set
+  <var title="">start block</var> to 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>.
 
   <li>Let <var title="">end block</var> be the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> of
   <var title="">range</var>.
 
   <li>While <var title="">end block</var> is not a <a href=#prohibited-paragraph-child>prohibited paragraph
-  child</a>, set <var title="">end block</var> to 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>.
+  child</a> 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 <a href=#in-the-same-editing-host>in the same editing host</a>, set
+  <var title="">end block</var> to 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>.
 
   <li>Call <code class=external data-anolis-spec=domrange title=dom-Range-deleteContents><a href=http://html5.org/specs/dom-range.html#dom-range-deletecontents>deleteContents()</a></code> on <var title="">range</var>.
-
-  <li>If <var title="">start block</var> or 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 null, or <var title="">end
-  block</var> or 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 null, or <var title="">start block</var> is an
-  <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> or <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 <var title="">end block</var>, abort these steps.
+  <!--
+  Now we need to merge blocks.  The simplest case is something like
+
+    <p>fo[o</p><p>bar</p><p>b]az</p>
+    -> <p>fo</p>{}<p>az</p>
+    -> <p>fo{}az</p>
+
+  where neither block descends from the other.  More complicated is something
+  like
+
+    foo[<p>]bar</p>
+    -> foo[]bar
+
+  or
+
+    <p>foo[</p>]bar
+    -> <p>foo[]bar</p>
+
+  where one descends from the other.
+  -->
+
+  <li>If <var title="">start block</var> is not <a href=#in-the-same-editing-host>in the same editing host</a> as
+  <var title="">end block</var>, or <var title="">start block</var> is neither an <a href=#editing-host>editing
+  host</a> nor a <a href=#prohibited-paragraph-child>prohibited paragraph child</a>, or <var title="">end
+  block</var> is neither an <a href=#editing-host>editing host</a> nor a <a href=#prohibited-paragraph-child>prohibited
+  paragraph child</a>, or <var title="">start block</var> and <var title="">end block</var>
+  are the same, abort these steps.
+
+  <li>If <var title="">start block</var> is an <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="">end block</var>:
+  <!-- Just repeatedly blow up the end block. -->
+
+  <ol>
+    <li>Let <var title="">reference node</var> be <var title="">end block</var>.
+
+    <li>While <var title="">reference node</var> is not a <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="">start
+    block</var>, set <var title="">reference node</var> to 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>.
+
+    <li>Set the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> and <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> of <var title="">range</var> to
+    (<var title="">start block</var>, <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <var title="">reference node</var>).
+
+    <li>If <var title="">end block</var>'s <code class=external data-anolis-spec=domcore title=dom-Node-firstChild><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-firstchild>firstChild</a></code> is not an <a href=#inline-node>inline
+    node</a>, abort these steps.
+
+    <li>Let <var title="">children</var> be an array of <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>, initially empty.
+
+    <li>Append 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="">end block</var> to
+    <var title="">children</var>.
+
+    <li>While <var title="">children</var>'s last member is not 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="">children</var>'s last member's <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> is an <a href=#inline-node>inline
+    node</a>, append <var title="">children</var>'s last member's <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> to
+    <var title="">children</var>.
+
+    <li>While <var title="">children</var>'s first member'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 not <var title="">start
+    block</var>, <a href=#split-the-parent>split the parent</a> of <var title="">children</var>.
+
+    <li>If 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 <var title="">range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a>
+    equal to its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a> 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> and is not the first member of
+    <var title="">children</var>, remove that <code class=external data-anolis-spec=html title="the br element"><a href=http://www.whatwg.org/html/#the-br-element>br</a></code> 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>.
+
+    <li>Abort these steps.
+  </ol>
 
   <li>Set the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> and <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> of <var title="">range</var> to
   (<var title="">start block</var>, <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of <var title="">start block</var>).
 
+  <li>If <var title="">start block</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 <var title="">end block</var>:
+  <!-- Pull in everything that comes after <var title>start block</var>, until we hit
+  a br or prohibited paragraph child. -->
+
+  <ol>
+    <li>Let <var title="">reference node</var> be <var title="">start block</var>.
+
+    <li>While <var title="">reference node</var> is not a <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="">end
+    block</var>, set <var title="">reference node</var> to 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>.
+
+    <li>While the <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> of <var title="">reference node</var> is neither null
+    nor 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> nor a <a href=#prohibited-paragraph-child>prohibited paragraph child</a>, append the
+    <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> of <var title="">reference node</var> as 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="">start block</var>, <a href=#preserving-ranges>preserving ranges</a>.
+
+    <li>If the <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> of <var title="">reference node</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
+    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>.
+
+    <li>Abort these steps.
+  </ol>
+
+  <!-- In the last case, just move all the children of the end block to the
+  start block, and then get rid of any elements we emptied that way. -->
   <li>While <var title="">end block</var> has <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>, append 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="">end block</var> to <var title="">start block</var>, <a href=#preserving-ranges>preserving
   ranges</a>.
--- a/implementation.js	Thu Jun 02 15:37:20 2011 -0600
+++ b/implementation.js	Fri Jun 03 13:08:06 2011 -0600
@@ -593,6 +593,10 @@
 	// "If original parent has no children, remove it from its parent."
 	if (!originalParent.hasChildNodes()) {
 		originalParent.parentNode.removeChild(originalParent);
+
+	// "Otherwise, remove extraneous line breaks before original parent."
+	} else {
+		removeExtraneousLineBreaksBefore(originalParent);
 	}
 
 	// "If node list's last member's nextSibling is null, remove extraneous
@@ -772,43 +776,36 @@
 		return;
 	}
 
-	// "If no node is contained in range, and either range's start node is not
-	// a Text or Comment node or range's start offset is its start node's
-	// length, and either range's end node is not a Text or Comment node or
-	// range's end offset is 0, call deleteContents() on range and abort these
-	// steps."
-	if (!collectContainedNodes(range).length
-	&& ((range.startContainer.nodeType != Node.TEXT_NODE
-	&& range.startContainer.nodeType != Node.COMMENT_NODE)
-	|| range.startOffset == getNodeLength(range.startContainer))
-	&& ((range.endContainer.nodeType != Node.TEXT_NODE
-	&& range.endContainer.nodeType != Node.COMMENT_NODE)
-	|| range.endOffset == 0)) {
-		range.deleteContents();
-		return;
-	}
-
-	// "While the start node of range has at least one child:"
-	while (range.startContainer.hasChildNodes()) {
-		// "If range's start offset is its start node's length, and its start
-		// node's parent is not null, set range's start to (parent of start
-		// node, 1 + index of start node) and continue this loop from the
-		// beginning."
-		if (range.startOffset == getNodeLength(range.startContainer)
-		&& range.startContainer.parentNode) {
-			range.setStart(range.startContainer.parentNode, 1 + getNodeIndex(range.startContainer));
+	// "Let start node, start offset, end node, and end offset be range's start
+	// and end nodes and offsets."
+	var startNode = range.startContainer;
+	var startOffset = range.startOffset;
+	var endNode = range.endContainer;
+	var endOffset = range.endOffset;
+
+	// "While start node has at least one child:"
+	while (startNode.hasChildNodes()) {
+		// "If start offset is start node's length, and start node's parent is
+		// in the same editing host, and start node is not a prohibited
+		// paragraph child, set start offset to one plus the index of start
+		// node, then set start node to its parent and continue this loop from
+		// the beginning."
+		if (startOffset == getNodeLength(startNode)
+		&& inSameEditingHost(startNode, startNode.parentNode)
+		&& !isProhibitedParagraphChild(startNode)) {
+			startOffset = 1 + getNodeIndex(startNode);
+			startNode = startNode.parentNode;
 			continue;
 		}
 
-		// "If range's start offset is its start node's length, break from this
-		// loop."
-		if (range.startOffset == getNodeLength(range.startContainer)) {
+		// "If start offset is start node's length, break from this loop."
+		if (startOffset == getNodeLength(startNode)) {
 			break;
 		}
 
-		// "Let reference node be the child of range's start node with index
-		// equal to its start offset."
-		var referenceNode = range.startContainer.childNodes[range.startOffset];
+		// "Let reference node be the child of start node with index equal to
+		// start offset."
+		var referenceNode = startNode.childNodes[startOffset];
 
 		// "If reference node is an Element with no children, break from this
 		// loop."
@@ -817,41 +814,33 @@
 			break;
 		}
 
-		// "Set range's start to (reference node, 0)."
-		range.setStart(referenceNode, 0);
-	}
-
-	// "If range's start offset is the length of its start node, then while
-	// range's start node has children and its last child is not an Element
-	// with no children, set range's start to (last child of start node, length
-	// of last child of start node)."
-	if (range.startOffset == getNodeLength(range.startContainer)) {
-		while (range.startContainer.hasChildNodes()
-		&& (range.lastChild.nodeType != Node.ELEMENT_NODE
-		|| range.lastChild.hasChildNodes())) {
-			range.setStart(range.startContainer.lastChild, getNodeLength(range.startContainer.lastChild));
-		}
-	}
-
-	// "While the end node of range has at least one child:"
-	while (range.endContainer.hasChildNodes()) {
-		// "If range's end offset is 0, and its end node's parent is not null,
-		// set range's end to (parent of end node, index of end node) and
+		// "Set start node to reference node and start offset to 0."
+		startNode = referenceNode;
+		startOffset = 0;
+	}
+
+	// "While end node has at least one child:"
+	while (endNode.hasChildNodes()) {
+		// "If end offset is 0, and end node's parent is in the same editing
+		// host, and end node is not a prohibited paragraph child, set end
+		// offset to the index of end node, then set end node to its parent and
 		// continue this loop from the beginning."
-		if (range.endOffset == 0
-		&& range.endContainer.parentNode) {
-			range.setEnd(range.endContainer.parentNode, getNodeIndex(range.endContainer));
+		if (endOffset == 0
+		&& inSameEditingHost(endNode, endNode.parentNode)
+		&& !isProhibitedParagraphChild(endNode)) {
+			endOffset = getNodeIndex(endNode);
+			endNode = endNode.parentNode;
 			continue;
 		}
 
-		// "If range's end offset is 0, break from this loop."
-		if (range.endOffset == 0) {
+		// "If end offset is 0, break from this loop."
+		if (endOffset == 0) {
 			break;
 		}
 
-		// "Let reference node be the child of range's end node with index
-		// equal to its end offset minus one."
-		var referenceNode = range.endContainer.childNodes[range.endOffset - 1];
+		// "Let reference node be the child of end node with index equal to end
+		// offset minus one."
+		var referenceNode = endNode.childNodes[endOffset - 1];
 
 		// "If reference node is an Element with no children, break from this
 		// loop."
@@ -860,51 +849,113 @@
 			break;
 		}
 
-		// "Set range's end to (reference node, length of reference node)."
-		range.setEnd(referenceNode, getNodeLength(referenceNode));
-	}
-
-	// "If range's end offset is 0, then while range's end node has children
-	// and its first child is not an Element with no children, set range's end
-	// to (first child of end node, 0)."
-	if (range.endOffset == 0) {
-		while (range.endContainer.hasChildNodes()
-		&& (range.lastChild.nodeType != Node.ELEMENT_NODE
-		|| range.lastChild.hasChildNodes())) {
-			range.setEnd(range.endContainer.firstChild, 0);
-		}
-	}
+		// "Set end node to reference node and end offset to the length of
+		// reference node."
+		endNode = referenceNode;
+		endOffset = getNodeLength(referenceNode);
+	}
+
+	// "If (end node, end offset) is before (start node, start offset), call
+	// deleteContents() on range and abort these steps."
+	var startPoint = document.createRange();
+	startPoint.setStart(startNode, startOffset);
+	var endPoint = document.createRange();
+	endPoint.setStart(endNode, endOffset);
+	if (startPoint.compareBoundaryPoints(Range.START_TO_START, endPoint) == 1) {
+		range.deleteContents();
+		return;
+	}
+
+	// "Set range's start to (start node, start offset) and its end to (end
+	// node, end offset)."
+	range.setStart(startNode, startOffset);
+	range.setEnd(endNode, endOffset);
 
 	// "Let start block be the start node of range."
 	var startBlock = range.startContainer;
 
-	// "While start block is not a prohibited paragraph child, set start block
-	// to its parent."
-	while (!isProhibitedParagraphChild(startBlock)) {
+	// "While start block is not a prohibited paragraph child and its parent is
+	// in the same editing host, set start block to its parent."
+	while (!isProhibitedParagraphChild(startBlock)
+	&& inSameEditingHost(startBlock, startBlock.parentNode)) {
 		startBlock = startBlock.parentNode;
 	}
 
 	// "Let end block be the end node of range."
 	var endBlock = range.endContainer;
 
-	// "While end block is not a prohibited paragraph child, set end block to
-	// its parent."
-	while (!isProhibitedParagraphChild(endBlock)) {
+	// "While end block is not a prohibited paragraph child and its parent is
+	// in the same editing host, set end block to its parent."
+	while (!isProhibitedParagraphChild(endBlock)
+	&& inSameEditingHost(endBlock, endBlock.parentNode)) {
 		endBlock = endBlock.parentNode;
 	}
 
 	// "Call deleteContents() on range."
 	range.deleteContents();
 
-	// "If start block or its parent is null, or end block or its parent is
-	// null, or start block is an ancestor or descendant of end block, abort
-	// these steps."
-	if (!startBlock
-	|| !startBlock.parentNode
-	|| !endBlock
-	|| !endBlock.parentNode
-	|| isAncestor(startBlock, endBlock)
-	|| isDescendant(startBlock, endBlock)) {
+	// "If start block is not in the same editing host as end block, or start
+	// block is neither an editing host nor a prohibited paragraph child, or
+	// end block is neither an editing host nor a prohibited paragraph child,
+	// or start block and end block are the same, abort these steps."
+	if (!inSameEditingHost(startBlock, endBlock)
+	|| (!isEditingHost(startBlock) && !isProhibitedParagraphChild(startBlock))
+	|| (!isEditingHost(endBlock) && !isProhibitedParagraphChild(endBlock))
+	|| startBlock == endBlock) {
+		return;
+	}
+
+	// "If start block is an ancestor of end block:"
+	if (isAncestor(startBlock, endBlock)) {
+		// "Let reference node be end block."
+		var referenceNode = endBlock;
+
+		// "While reference node is not a child of start block, set reference
+		// node to its parent."
+		while (referenceNode.parentNode != startBlock) {
+			referenceNode = referenceNode.parentNode;
+		}
+
+		// "Set the start and end of range to (start block, index of reference
+		// node)."
+		range.setStart(startBlock, getNodeIndex(referenceNode));
+		range.setEnd(startBlock, getNodeIndex(referenceNode));
+
+		// "If end block's firstChild is not an inline node, abort these
+		// steps."
+		if (!isInlineNode(endBlock.firstChild)) {
+			return;
+		}
+
+		// "Let children be an array of nodes, initially empty."
+		var children = [];
+
+		// "Append the first child of end block to children."
+		children.push(endBlock.firstChild);
+
+		// "While children's last member is not a br, and children's last
+		// member's nextSibling is an inline node, append children's last
+		// member's nextSibling to children."
+		while (!isHtmlElement(children[children.length - 1], "br")
+		&& isInlineNode(children[children.length - 1].nextSibling)) {
+			children.push(children[children.length - 1].nextSibling);
+		}
+
+		// "While children's first member's parent is not start block, split
+		// the parent of children."
+		while (children[0].parentNode != startBlock) {
+			splitParent(children);
+		}
+
+		// "If the child of range's start node with index equal to its start
+		// offset is a br and is not the first member of children, remove that
+		// br from its parent."
+		if (isHtmlElement(range.startContainer.childNodes[range.startOffset], "br")
+		&& range.startContainer.childNodes[range.startOffset] != children[0]) {
+			range.startContainer.removeChild(range.startContainer.childNodes[range.startOffset]);
+		}
+
+		// "Abort these steps."
 		return;
 	}
 
@@ -913,6 +964,36 @@
 	range.setStart(startBlock, getNodeLength(startBlock));
 	range.setEnd(startBlock, getNodeLength(startBlock));
 
+	// "If start block is a descendant of end block:"
+	if (isDescendant(startBlock, endBlock)) {
+		// "Let reference node be start block."
+		var referenceNode = startBlock;
+
+		// "While reference node is not a child of end block, set reference
+		// node to its parent."
+		while (referenceNode.parentNode != endBlock) {
+			referenceNode = referenceNode.parentNode;
+		}
+
+		// "While the nextSibling of reference node is neither null nor a br
+		// nor a prohibited paragraph child, append the nextSibling of
+		// reference node as the last child of start block, preserving ranges."
+		while (referenceNode.nextSibling
+		&& !isHtmlElement(referenceNode.nextSibling, "br")
+		&& !isProhibitedParagraphChild(referenceNode.nextSibling)) {
+			movePreservingRanges(referenceNode.nextSibling, startBlock, -1);
+		}
+
+		// "If the nextSibling of reference node is a br, remove it from its
+		// parent."
+		if (isHtmlElement(referenceNode.nextSibling, "br")) {
+			referenceNode.parentNode.removeChild(referenceNode.nextSibling);
+		}
+
+		// "Abort these steps."
+		return;
+	}
+
 	// "While end block has children, append the first child of end block to
 	// start block, preserving ranges."
 	while (endBlock.hasChildNodes()) {
--- a/source.html	Thu Jun 02 15:37:20 2011 -0600
+++ b/source.html	Fri Jun 03 13:08:06 2011 -0600
@@ -714,6 +714,9 @@
   <li>If <var>original parent</var> has no [[children]], remove it from its
   [[parent]].
 
+  <li>Otherwise, <span>remove extraneous line breaks before</span>
+  <var>original parent</var>.
+
   <li>If <var>node list</var>'s last member's [[nextsibling]] is null,
   <span>remove extraneous line breaks at the end of</span> <var>node
   list</var>'s last member's [[parent]].
@@ -894,28 +897,22 @@
 block element empty, when we really need to add a [[br]] [[child]] no matter
 what so it doesn't collapse.
 
+<p class=XXX>I'm uncertain about the use of prohibited paragraph children here.
+I'm using it mostly because it's convenient and seems relatively sensible.  If
+we really want to use it, we probably want to change its name.
+
 <ol>
   <li>Let <var>range</var> be the <span>active range</span>.
 
   <li>If <var>range</var> is null, abort these steps and do nothing.
 
-  <li>If no [[node]] is [[contained]] in <var>range</var>, and
-  either <var>range</var>'s [[startnode]] is not a [[text]] or [[comment]] node
-  or <var>range</var>'s [[startoffset]] is its [[startnode]]'s [[nodelength]],
-  and either <var>range</var>'s [[endnode]] is not a [[text]] or [[comment]]
-  node or <var>range</var>'s [[endoffset]] is 0, call <code
-  data-anolis-spec=domrange
-  title=dom-Range-deleteContents>deleteContents()</code> on <var>range</var>
-  and abort these steps.
-  <!-- This is precisely the case where deleteContents() will not alter the
-  DOM, just collapse the selection.  I think. -->
-
-  <p class=XXX>Is it possible for subsequent steps to make the range's start go
-  past its end or vice versa, or does this step successfully prevent that case?
+  <li>Let <var>start node</var>, <var>start offset</var>, <var>end node</var>,
+  and <var>end offset</var> be <var>range</var>'s [[rangestart]] and
+  [[rangeend]] [[bpnodes]] and [[bpoffsets]].
 
   <!-- Drill the range down to the lowest possible level, so we don't delete
   more elements than necessary. -->
-  <li>While the [[startnode]] of <var>range</var> has at least one [[child]]:
+  <li>While <var>start node</var> has at least one [[child]]:
   <!--
   We don't want to keep going when we hit an element with no children, because
   then we'd do something like
@@ -929,94 +926,184 @@
   -->
 
   <ol>
-    <li>If <var>range</var>'s [[startoffset]] is its [[startnode]]'s
-    [[nodelength]], and its [[startnode]]'s [[parent]] is not null, set
-    <var>range</var>'s [[rangestart]] to ([[parent]] of [[startnode]], 1 +
-    [[index]] of [[startnode]]) and continue this loop from the beginning.
+    <li>If <var>start offset</var> is <var>start node</var>'s
+    [[nodelength]], and <var>start node</var>'s [[parent]] is <span>in the same
+    editing host</span>, and <var>start node</var> is not a <span>prohibited
+    paragraph child</span>, set <var>start offset</var> to one plus the
+    [[index]] of <var>start node</var>, then set <var>start node</var> to its
+    [[parent]] and continue this loop from the beginning.
     <!--
     For instance:
 
       <b>foo[</b><i>bar]</i>
+      -> <b>foo{</b><i>bar]</i>
       -> <b>foo</b>{<i>bar]</i>
 
     Then the next step will make it <b>foo</b><i>[bar]</i>.
+
+    We don't want to do this for prohibited paragraph children, because that
+    would lead to something like
+
+      <p>foo[</p><p>]bar<p>
+
+    ultimately collapsing, which is wrong.  Once we do the deletion, it needs
+    to wind up <p>foo[]bar</p>, whereas an actually collapsed selection should
+    do nothing.
     -->
 
-    <li>If <var>range</var>'s [[startoffset]] is its [[startnode]]'s
-    [[nodelength]], break from this loop.
+    <li>If <var>start offset</var> is <var>start node</var>'s [[nodelength]],
+    break from this loop.
     <!-- This happens if the first step brought us all the way up to the root.
     The step immediately after this loop will bring us back down again.  -->
 
-    <li>Let <var>reference node</var> be the [[child]] of <var>range</var>'s
-    [[startnode]] with [[index]] equal to its [[startoffset]].
+    <li>Let <var>reference node</var> be the [[child]] of <var>start node</var>
+    with [[index]] equal to <var>start offset</var>.
 
     <li>If <var>reference node</var> is an [[element]] with no [[children]],
     break from this loop.
     <!-- Don't descend into it, since then it won't get deleted even if it's
     selected. -->
 
-    <li>Set <var>range</var>'s [[rangestart]] to (<var>reference node</var>,
-    0).
+    <li>Set <var>start node</var> to <var>reference node</var> and <var>start
+    offset</var> to 0.
   </ol>
 
-  <!-- Corner case: maybe we went all the way up to the end of the root. -->
-  <li>If <var>range</var>'s [[startoffset]] is the [[nodelength]] of its
-  [[startnode]], then while <var>range</var>'s [[startnode]] has [[children]]
-  and its last [[child]] is not an [[element]] with no [[children]], set
-  <var>range</var>'s [[rangestart]] to (last [[child]] of [[startnode]],
-  [[nodelength]] of last [[child]] of [[startnode]]).
-
-  <li>While the [[endnode]] of <var>range</var> has at least one [[child]]:
+  <li>While <var>end node</var> has at least one [[child]]:
 
   <ol>
-    <li>If <var>range</var>'s [[endoffset]] is 0, and its [[endnode]]'s
-    [[parent]] is not null, set <var>range</var>'s [[rangeend]] to ([[parent]]
-    of [[endnode]], [[index]] of [[endnode]]) and continue this loop from the
-    beginning.
-
-    <li>If <var>range</var>'s [[endoffset]] is 0, break from this loop.
-
-    <li>Let <var>reference node</var> be the [[child]] of <var>range</var>'s
-    [[endnode]] with [[index]] equal to its [[endoffset]] minus one.
+    <li>If <var>end offset</var> is 0, and <var>end node</var>'s
+    [[parent]] is <span>in the same editing host</span>, and <var>end
+    node</var> is not a <span>prohibited paragraph child</span>, set <var>end
+    offset</var> to the [[index]] of <var>end node</var>, then set <var>end
+    node</var> to its [[parent]] and continue this loop from the beginning.
+
+    <li>If <var>end offset</var> is 0, break from this loop.
+
+    <li>Let <var>reference node</var> be the [[child]] of <var>end node</var>
+    with [[index]] equal to <var>end offset</var> minus one.
 
     <li>If <var>reference node</var> is an [[element]] with no [[children]],
     break from this loop.
 
-    <li>Set <var>range</var>'s [[rangeend]] to (<var>reference node</var>,
-    [[nodelength]] of <var>reference node</var>).
+    <li>Set <var>end node</var> to <var>reference node</var> and <var>end
+    offset</var> to the [[nodelength]] of <var>reference node</var>.
   </ol>
 
-  <li>If <var>range</var>'s [[endoffset]] is 0, then while <var>range</var>'s
-  [[endnode]] has [[children]] and its first [[child]] is not an [[element]]
-  with no [[children]], set <var>range</var>'s [[rangeend]] to (first [[child]]
-  of [[endnode]], 0).
+  <li>If (<var>end node</var>, <var>end offset</var>) is <span
+  data-anolis-spec=domrange title=concept-bp-before>before</span> (<var>start
+  node</var>, <var>start offset</var>), call <code data-anolis-spec=domrange
+  title=dom-Range-deleteContents>deleteContents()</code> on <var>range</var>
+  and abort these steps.
+
+  <li>Set <var>range</var>'s [[rangestart]] to (<var>start node</var>,
+  <var>start offset</var>) and its [[rangeend]] to (<var>end node</var>,
+  <var>end offset</var>).
 
   <li>Let <var>start block</var> be the [[rangestart]] [[bpnode]] of
   <var>range</var>.
 
   <li>While <var>start block</var> is not a <span>prohibited paragraph
-  child</span>, set <var>start block</var> to its [[parent]].
-
-  <p class=XXX>I'm uncertain about the use of prohibited paragraph children
-  here.  I'm using it mostly because it's convenient and seems relatively
-  sensible.  If we really want to use it, we probably want to change its name.
+  child</span> and its [[parent]] is <span>in the same editing host</span>, set
+  <var>start block</var> to its [[parent]].
 
   <li>Let <var>end block</var> be the [[rangeend]] [[bpnode]] of
   <var>range</var>.
 
   <li>While <var>end block</var> is not a <span>prohibited paragraph
-  child</span>, set <var>end block</var> to its [[parent]].
+  child</span> and its [[parent]] is <span>in the same editing host</span>, set
+  <var>end block</var> to its [[parent]].
 
   <li>Call <code data-anolis-spec=domrange
   title=dom-Range-deleteContents>deleteContents()</code> on <var>range</var>.
-
-  <li>If <var>start block</var> or its [[parent]] is null, or <var>end
-  block</var> or its [[parent]] is null, or <var>start block</var> is an
-  [[ancestor]] or [[descendant]] of <var>end block</var>, abort these steps.
+  <!--
+  Now we need to merge blocks.  The simplest case is something like
+
+    <p>fo[o</p><p>bar</p><p>b]az</p>
+    -> <p>fo</p>{}<p>az</p>
+    -> <p>fo{}az</p>
+
+  where neither block descends from the other.  More complicated is something
+  like
+
+    foo[<p>]bar</p>
+    -> foo[]bar
+
+  or
+
+    <p>foo[</p>]bar
+    -> <p>foo[]bar</p>
+
+  where one descends from the other.
+  -->
+
+  <li>If <var>start block</var> is not <span>in the same editing host</span> as
+  <var>end block</var>, or <var>start block</var> is neither an <span>editing
+  host</span> nor a <span>prohibited paragraph child</span>, or <var>end
+  block</var> is neither an <span>editing host</span> nor a <span>prohibited
+  paragraph child</span>, or <var>start block</var> and <var>end block</var>
+  are the same, abort these steps.
+
+  <li>If <var>start block</var> is an [[ancestor]] of <var>end block</var>:
+  <!-- Just repeatedly blow up the end block. -->
+
+  <ol>
+    <li>Let <var>reference node</var> be <var>end block</var>.
+
+    <li>While <var>reference node</var> is not a [[child]] of <var>start
+    block</var>, set <var>reference node</var> to its [[parent]].
+
+    <li>Set the [[rangestart]] and [[rangeend]] of <var>range</var> to
+    (<var>start block</var>, [[index]] of <var>reference node</var>).
+
+    <li>If <var>end block</var>'s [[firstchild]] is not an <span>inline
+    node</span>, abort these steps.
+
+    <li>Let <var>children</var> be an array of [[nodes]], initially empty.
+
+    <li>Append the first [[child]] of <var>end block</var> to
+    <var>children</var>.
+
+    <li>While <var>children</var>'s last member is not a [[br]], and
+    <var>children</var>'s last member's [[nextsibling]] is an <span>inline
+    node</span>, append <var>children</var>'s last member's [[nextsibling]] to
+    <var>children</var>.
+
+    <li>While <var>children</var>'s first member's [[parent]] is not <var>start
+    block</var>, <span>split the parent</span> of <var>children</var>.
+
+    <li>If the [[child]] of <var>range</var>'s [[startnode]] with [[index]]
+    equal to its [[startoffset]] is a [[br]] and is not the first member of
+    <var>children</var>, remove that [[br]] from its [[parent]].
+
+    <li>Abort these steps.
+  </ol>
 
   <li>Set the [[rangestart]] and [[rangeend]] of <var>range</var> to
   (<var>start block</var>, [[nodelength]] of <var>start block</var>).
 
+  <li>If <var>start block</var> is a [[descendant]] of <var>end block</var>:
+  <!-- Pull in everything that comes after <var>start block</var>, until we hit
+  a br or prohibited paragraph child. -->
+
+  <ol>
+    <li>Let <var>reference node</var> be <var>start block</var>.
+
+    <li>While <var>reference node</var> is not a [[child]] of <var>end
+    block</var>, set <var>reference node</var> to its [[parent]].
+
+    <li>While the [[nextsibling]] of <var>reference node</var> is neither null
+    nor a [[br]] nor a <span>prohibited paragraph child</span>, append the
+    [[nextsibling]] of <var>reference node</var> as the last [[child]] of
+    <var>start block</var>, <span>preserving ranges</span>.
+
+    <li>If the [[nextsibling]] of <var>reference node</var> is a [[br]], remove
+    it from its [[parent]].
+
+    <li>Abort these steps.
+  </ol>
+
+  <!-- In the last case, just move all the children of the end block to the
+  start block, and then get rid of any elements we emptied that way. -->
   <li>While <var>end block</var> has [[children]], append the first [[child]]
   of <var>end block</var> to <var>start block</var>, <span>preserving
   ranges</span>.