Improve "delete the selection" even more
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Fri, 03 Jun 2011 15:20:13 -0600
changeset 227 6c96ab178ca6
parent 226 9309a882b6ac
child 228 7bd750edd8a1
Improve "delete the selection" even more

Now gives good results on every test I have, more or less. Also defined
a stub delete command that just deletes the selection and doesn't yet
delete the previous character.
autoimplementation.html
editcommands.html
implementation.js
source.html
--- a/autoimplementation.html	Fri Jun 03 13:08:06 2011 -0600
+++ b/autoimplementation.html	Fri Jun 03 15:20:13 2011 -0600
@@ -298,6 +298,56 @@
 		'<a name=abc>foo[bar]baz</a>',
 		'<a name=abc><b>foo[bar]baz</b></a>',
 	],
+	// Opera requires this to be quoted, contrary to ES5 11.1.5 which allows
+	// PropertyName to be any IdentifierName, and see 7.6 which defines
+	// IdentifierName to include ReservedWord; Identifier excludes it.
+	"delete": [
+		'foo[]bar',
+		'<span>foo</span>{}<span>bar</span>',
+		'<span>foo[</span><span>]bar</span>',
+		'foo[bar]baz',
+
+		'foo<b>[bar]</b>baz',
+		'foo<b>{bar}</b>baz',
+		'foo{<b>bar</b>}baz',
+		'foo<span>[bar]</span>baz',
+		'foo<span>{bar}</span>baz',
+		'foo{<span>bar</span>}baz',
+		'<b>foo[bar</b><i>baz]quz</i>',
+		'<p>foo</p><p>[bar]</p><p>baz</p>',
+		'<p>foo</p><p>{bar}</p><p>baz</p>',
+		'<p>foo</p>{<p>bar</p>}<p>baz</p>',
+
+		'<p>foo[bar<p>baz]quz',
+		'<p>foo[bar<div>baz]quz</div>',
+		'<p>foo[bar<h1>baz]quz</h1>',
+		'<div>foo[bar</div><p>baz]quz',
+		'<blockquote>foo[bar</blockquote><pre>baz]quz</pre>',
+
+		'<p><b>foo[bar</b><p>baz]quz',
+		'<div><p>foo[bar</div><p>baz]quz',
+		'<p>foo[bar<blockquote><p>baz]quz<p>qoz</blockquote',
+		'<p>foo[bar<p style=color:red>baz]quz',
+		'<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',
+	],
 	fontname: [
 		'foo[]bar',
 		'<span>foo</span>{}<span>bar</span>',
--- a/editcommands.html	Fri Jun 03 13:08:06 2011 -0600
+++ b/editcommands.html	Fri Jun 03 15:20:13 2011 -0600
@@ -83,19 +83,20 @@
    <li><a href=#the-backcolor-command><span class=secno>6.8 </span>The <code title="">backColor</code> command</a></li>
    <li><a href=#the-bold-command><span class=secno>6.9 </span>The <code title="">bold</code> command</a></li>
    <li><a href=#the-createlink-command><span class=secno>6.10 </span>The <code title="">createLink</code> command</a></li>
-   <li><a href=#the-fontname-command><span class=secno>6.11 </span>The <code title="">fontName</code> command</a></li>
-   <li><a href=#the-fontsize-command><span class=secno>6.12 </span>The <code title="">fontSize</code> command</a></li>
-   <li><a href=#the-forecolor-command><span class=secno>6.13 </span>The <code title="">foreColor</code> command</a></li>
-   <li><a href=#the-hilitecolor-command><span class=secno>6.14 </span>The <code title="">hiliteColor</code> command</a></li>
-   <li><a href=#the-inserthtml-command><span class=secno>6.15 </span>The <code title="">insertHTML</code> command</a></li>
-   <li><a href=#the-insertimage-command><span class=secno>6.16 </span>The <code title="">insertImage</code> command</a></li>
-   <li><a href=#the-italic-command><span class=secno>6.17 </span>The <code title="">italic</code> command</a></li>
-   <li><a href=#the-removeformat-command><span class=secno>6.18 </span>The <code title="">removeFormat</code> command</a></li>
-   <li><a href=#the-strikethrough-command><span class=secno>6.19 </span>The <code title="">strikethrough</code> command</a></li>
-   <li><a href=#the-subscript-command><span class=secno>6.20 </span>The <code title="">subscript</code> command</a></li>
-   <li><a href=#the-superscript-command><span class=secno>6.21 </span>The <code title="">superscript</code> command</a></li>
-   <li><a href=#the-underline-command><span class=secno>6.22 </span>The <code title="">underline</code> command</a></li>
-   <li><a href=#the-unlink-command><span class=secno>6.23 </span>The <code title="">unlink</code> command</a></ol></li>
+   <li><a href=#the-delete-command><span class=secno>6.11 </span>The <code title="">delete</code> command</a></li>
+   <li><a href=#the-fontname-command><span class=secno>6.12 </span>The <code title="">fontName</code> command</a></li>
+   <li><a href=#the-fontsize-command><span class=secno>6.13 </span>The <code title="">fontSize</code> command</a></li>
+   <li><a href=#the-forecolor-command><span class=secno>6.14 </span>The <code title="">foreColor</code> command</a></li>
+   <li><a href=#the-hilitecolor-command><span class=secno>6.15 </span>The <code title="">hiliteColor</code> command</a></li>
+   <li><a href=#the-inserthtml-command><span class=secno>6.16 </span>The <code title="">insertHTML</code> command</a></li>
+   <li><a href=#the-insertimage-command><span class=secno>6.17 </span>The <code title="">insertImage</code> command</a></li>
+   <li><a href=#the-italic-command><span class=secno>6.18 </span>The <code title="">italic</code> command</a></li>
+   <li><a href=#the-removeformat-command><span class=secno>6.19 </span>The <code title="">removeFormat</code> command</a></li>
+   <li><a href=#the-strikethrough-command><span class=secno>6.20 </span>The <code title="">strikethrough</code> command</a></li>
+   <li><a href=#the-subscript-command><span class=secno>6.21 </span>The <code title="">subscript</code> command</a></li>
+   <li><a href=#the-superscript-command><span class=secno>6.22 </span>The <code title="">superscript</code> command</a></li>
+   <li><a href=#the-underline-command><span class=secno>6.23 </span>The <code title="">underline</code> command</a></li>
+   <li><a href=#the-unlink-command><span class=secno>6.24 </span>The <code title="">unlink</code> command</a></ol></li>
  <li><a href=#block-formatting-commands><span class=secno>7 </span>Block formatting commands</a>
   <ol>
    <li><a href=#block-formatting-command-definitions><span class=secno>7.1 </span>Block formatting command definitions</a></li>
@@ -1031,6 +1032,9 @@
   <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.
+  <!-- This actually will have no effect other than collapsing the selection to
+  whatever point deleteContents() likes to collapse it to.  It might make more
+  sense to collapse to the start or end, though. -->
 
   <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>,
@@ -1051,6 +1055,7 @@
   <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>.
+
   <!--
   Now we need to merge blocks.  The simplest case is something like
 
@@ -1072,12 +1077,43 @@
   where one descends from the other.
   -->
 
+  <!-- The deletions might have left some empty elements.  We want to remove
+  empty inline nodes, and add a <br> child to empty blocks.  We remove the
+  empty inline nodes first, and only add the <br>s later, because we don't want
+  to add the brs to blocks that we're about to merge. -->
+  <li>While <var title="">start node</var> is an <a href=#editable>editable</a> <a href=#inline-node>inline
+  node</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> 0, let <var title="">parent</var> be 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>,
+  then remove <var title="">start node</var> from <var title="">parent</var>, then set <var title="">start
+  node</var> to <var title="">parent</var>.
+
+  <li>While <var title="">end node</var> is an <a href=#editable>editable</a> <a href=#inline-node>inline
+  node</a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> 0, let <var title="">parent</var> be 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>,
+  then remove <var title="">end node</var> from <var title="">parent</var>, then set <var title="">end
+  node</var> to <var title="">parent</var>.
+
   <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.
+  are the same:
+  <!-- Then we don't try to merge anything. -->
+
+  <ol>
+    <li>If <var title="">start 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>, is
+    either <a href=#editable>editable</a> or an <a href=#editing-host>editing host</a>, and is not an
+    <a href=#inline-node>inline node</a>, call <code class=external data-anolis-spec=domcore title=dom-Document-createElement><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement>createElement("br")</a></code> on the
+    <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a> and append the result 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
+    node</var>.
+
+    <li>If <var title="">end 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>, is
+    either <a href=#editable>editable</a> or an <a href=#editing-host>editing host</a>, and is not an
+    <a href=#inline-node>inline node</a>, call <code class=external data-anolis-spec=domcore title=dom-Document-createElement><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement>createElement("br")</a></code> on the
+    <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a> and append the result 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="">end
+    node</var>.
+
+    <li>Abort these steps.
+  </ol>
 
   <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. -->
@@ -1110,18 +1146,17 @@
     <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>:
+  <li>Otherwise, 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>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>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
@@ -1134,19 +1169,28 @@
 
     <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>
 
+  <li>Otherwise:
   <!-- 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>.
-
-  <li>While <var title="">end block</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>, let <var title="">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 <var title="">end block</var>, then remove <var title="">end block</var> from
-  <var title="">parent</var>, then set <var title="">end block</var> to <var title="">parent</var>.
+
+  <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>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>.
+
+    <li>While <var title="">end block</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>, let <var title="">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 <var title="">end block</var>, then remove <var title="">end block</var> from
+    <var title="">parent</var>, then set <var title="">end block</var> to <var title="">parent</var>.
+  </ol>
+
+  <li>If <var title="">start block</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>, call
+  <code class=external data-anolis-spec=domcore title=dom-Document-createElement><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement>createElement("br")</a></code> on the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a> and append the result 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>.
 </ol>
 
 <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
@@ -2298,7 +2342,25 @@
 <!-- I'd have expected the value to be the URL, but guess not. -->
 
 
-<h3 id=the-fontname-command><span class=secno>6.11 </span><dfn>The <code title="">fontName</code> command</dfn></h3>
+<h3 id=the-delete-command><span class=secno>6.11 </span><dfn>The <code title="">delete</code> command</dfn></h3>
+<!-- Not really specifically "inline", but I didn't want to create a new
+section just for it. -->
+
+<p><a href=#action>Action</a>:
+
+<ol>
+  <li>If the <a href=#active-range>active range</a> is not <code class=external data-anolis-spec=domrange title=dom-Range-collapsed><a href=http://html5.org/specs/dom-range.html#dom-range-collapsed>collapsed</a></code>, <a href=#delete-the-selection>delete the selection</a>
+  and abort these steps.
+
+  <p class=XXX>Maybe we sometimes want to do the following even if it isn't
+  collapsed?  WebKit seems to do some normalization on the range before
+  deciding whether it's collapsed, and that sounds like a good idea.
+
+  <li class=XXX>The stuff where we delete the previous character goes here.
+</ol>
+
+
+<h3 id=the-fontname-command><span class=secno>6.12 </span><dfn>The <code title="">fontName</code> command</dfn></h3>
 
 <p><a href=#action>Action</a>: <a href=#decompose>Decompose</a> the <a href=#active-range>active range</a>,
 then <a href=#set-the-value>set the value</a> of each returned <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 <var title="">value</var>.
@@ -2347,7 +2409,7 @@
 <p><a href=#relevant-css-property>Relevant CSS property</a>: "font-family"
 
 
-<h3 id=the-fontsize-command><span class=secno>6.12 </span><dfn>The <code title="">fontSize</code> command</dfn></h3>
+<h3 id=the-fontsize-command><span class=secno>6.13 </span><dfn>The <code title="">fontSize</code> command</dfn></h3>
 
 <p><a href=#action>Action</a>:
 
@@ -2454,7 +2516,7 @@
 <p><a href=#relevant-css-property>Relevant CSS property</a>: "font-size"
 
 
-<h3 id=the-forecolor-command><span class=secno>6.13 </span><dfn>The <code title="">foreColor</code> command</dfn></h3>
+<h3 id=the-forecolor-command><span class=secno>6.14 </span><dfn>The <code title="">foreColor</code> command</dfn></h3>
 
 <p><a href=#action>Action</a>:
 
@@ -2539,7 +2601,7 @@
 <p><a href=#relevant-css-property>Relevant CSS property</a>: "color"
 
 
-<h3 id=the-hilitecolor-command><span class=secno>6.14 </span><dfn>The <code title="">hiliteColor</code> command</dfn></h3>
+<h3 id=the-hilitecolor-command><span class=secno>6.15 </span><dfn>The <code title="">hiliteColor</code> command</dfn></h3>
 <!-- IE 9 RC doesn't support this.  It uses backColor instead, but Gecko and
 Opera treat that differently, while all non-IE browsers treat hiliteColor the
 same, so I'm standardizing hiliteColor as the way to highlight text.
@@ -2578,7 +2640,7 @@
 <p><a href=#relevant-css-property>Relevant CSS property</a>: "background-color"
 
 
-<h3 id=the-inserthtml-command><span class=secno>6.15 </span><dfn>The <code title="">insertHTML</code> command</dfn></h3>
+<h3 id=the-inserthtml-command><span class=secno>6.16 </span><dfn>The <code title="">insertHTML</code> command</dfn></h3>
 <!-- Not supported by IE9. -->
 
 <p><a href=#action>Action</a>:
@@ -2608,7 +2670,7 @@
 </ol>
 
 
-<h3 id=the-insertimage-command><span class=secno>6.16 </span><dfn>The <code title="">insertImage</code> command</dfn></h3>
+<h3 id=the-insertimage-command><span class=secno>6.17 </span><dfn>The <code title="">insertImage</code> command</dfn></h3>
 
 <p><a href=#action>Action</a>:
 
@@ -2644,7 +2706,7 @@
 </ol>
 
 
-<h3 id=the-italic-command><span class=secno>6.17 </span><dfn>The <code title="">italic</code> command</dfn></h3>
+<h3 id=the-italic-command><span class=secno>6.18 </span><dfn>The <code title="">italic</code> command</dfn></h3>
 
 <p><a href=#action>Action</a>: <a href=#decompose>Decompose</a> the <a href=#active-range>active range</a>.
 If the <a href=#state>state</a> is then false, <a href=#set-the-value>set the value</a> of each
@@ -2660,7 +2722,7 @@
 <p><a href=#relevant-css-property>Relevant CSS property</a>: "font-style"
 
 
-<h3 id=the-removeformat-command><span class=secno>6.18 </span><dfn>The <code title="">removeFormat</code> command</dfn></h3>
+<h3 id=the-removeformat-command><span class=secno>6.19 </span><dfn>The <code title="">removeFormat</code> command</dfn></h3>
 <!--
 Tested in IE 9, Firefox 4.0, Chrome 12 dev, Opera 11.00.
 
@@ -2781,7 +2843,7 @@
 </ol>
 
 
-<h3 id=the-strikethrough-command><span class=secno>6.19 </span><dfn>The <code title="">strikethrough</code> command</dfn></h3>
+<h3 id=the-strikethrough-command><span class=secno>6.20 </span><dfn>The <code title="">strikethrough</code> command</dfn></h3>
 
 <p><a href=#action>Action</a>: <a href=#decompose>Decompose</a> the <a href=#active-range>active range</a>.
 If the <a href=#state>state</a> is then false, <a href=#set-the-value>set the value</a> of each
@@ -2797,7 +2859,7 @@
 <p><a href=#value>Value</a>: Always the empty string.
 
 
-<h3 id=the-subscript-command><span class=secno>6.20 </span><dfn>The <code title="">subscript</code> command</dfn></h3>
+<h3 id=the-subscript-command><span class=secno>6.21 </span><dfn>The <code title="">subscript</code> command</dfn></h3>
 
 <p><a href=#action>Action</a>:
 
@@ -2822,7 +2884,7 @@
 <p><a href=#relevant-css-property>Relevant CSS property</a>: "vertical-align"
 
 
-<h3 id=the-superscript-command><span class=secno>6.21 </span><dfn>The <code title="">superscript</code> command</dfn></h3>
+<h3 id=the-superscript-command><span class=secno>6.22 </span><dfn>The <code title="">superscript</code> command</dfn></h3>
 
 <p><a href=#action>Action</a>:
 
@@ -2847,7 +2909,7 @@
 <p><a href=#relevant-css-property>Relevant CSS property</a>: "vertical-align"
 
 
-<h3 id=the-underline-command><span class=secno>6.22 </span><dfn>The <code title="">underline</code> command</dfn></h3>
+<h3 id=the-underline-command><span class=secno>6.23 </span><dfn>The <code title="">underline</code> command</dfn></h3>
 
 <p><a href=#action>Action</a>: <a href=#decompose>Decompose</a> the <a href=#active-range>active range</a>.
 If the <a href=#state>state</a> is then false, <a href=#set-the-value>set the value</a> of each
@@ -2909,7 +2971,7 @@
 <p><a href=#value>Value</a>: Always the empty string.
 
 
-<h3 id=the-unlink-command><span class=secno>6.23 </span><dfn>The <code title="">unlink</code> command</dfn></h3>
+<h3 id=the-unlink-command><span class=secno>6.24 </span><dfn>The <code title="">unlink</code> command</dfn></h3>
 
 <p><a href=#action>Action</a>:
 
@@ -4943,12 +5005,24 @@
 
 <h2 id=additional-requirements><span class=secno>9 </span>Additional requirements</h2>
 
+<p class=XXX>It has been <a href=http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2009-December/024628.html>suggested</a>
+that some things here need to be platform-dependent, not fully standardized.
+For now I'm standardizing them anyway, because the large majority of behavior
+should be platform-agnostic.  If anyone has suggestions as to particular things
+that should be left up to platform behavior, please say so.
+
 <p>When the user instructs the user agent to insert a line break inside an
 <a href=#editing-host>editing host</a>, such as by pressing the Return key while the cursor
 is in an <a href=#editable>editable</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>, the user agent must take the
 <a href=#action>action</a> for <a href=#the-insertparagraph-command>the <code title="">insertParagraph</code>
 command</a>.
 
+<p>When the user instructs the user agent to delete the previous character
+inside an <a href=#editing-host>editing host</a>, such as by pressing the Backspace key
+while the cursor is in an <a href=#editable>editable</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>, the user agent must
+take the <a href=#action>action</a> for <a href=#the-delete-command>the <code title="">delete</code>
+command</a>.
+
 
 <h2 class=no-num id=acknowledgements>Acknowledgements</h2>
 <p>Thanks to:
--- a/implementation.js	Fri Jun 03 13:08:06 2011 -0600
+++ b/implementation.js	Fri Jun 03 15:20:13 2011 -0600
@@ -894,14 +894,59 @@
 	// "Call deleteContents() on range."
 	range.deleteContents();
 
+	// "While start node is an editable inline node with length 0, let parent
+	// be its parent, then remove start node from parent, then set start node
+	// to parent."
+	while (isEditable(startNode)
+	&& isInlineNode(startNode)
+	&& getNodeLength(startNode) == 0) {
+		var parent_ = startNode.parentNode;
+		parent_.removeChild(startNode);
+		startNode = parent_;
+	}
+
+	// "While end node is an editable inline node with length 0, let parent be
+	// its parent, then remove end node from parent, then set end node to
+	// parent."
+	while (isEditable(endNode)
+	&& isInlineNode(endNode)
+	&& getNodeLength(endNode) == 0) {
+		var parent_ = endNode.parentNode;
+		parent_.removeChild(endNode);
+		endNode = parent_;
+	}
+
 	// "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."
+	// or start block and end block are the same:"
 	if (!inSameEditingHost(startBlock, endBlock)
 	|| (!isEditingHost(startBlock) && !isProhibitedParagraphChild(startBlock))
 	|| (!isEditingHost(endBlock) && !isProhibitedParagraphChild(endBlock))
 	|| startBlock == endBlock) {
+		// "If start node is an Element with no children, is either editable or
+		// an editing host, and is not an inline node, call createElement("br")
+		// on the context object and append the result as the last child of
+		// start node."
+		if ((isEditable(startNode) || isEditingHost(startNode))
+		&& startNode.nodeType == Node.ELEMENT_NODE
+		&& !startNode.hasChildNodes()
+		&& !isInlineNode(startNode)) {
+			startNode.appendChild(document.createElement("br"));
+		}
+
+		// "If end node is an Element with no children, is either editable or
+		// an editing host, and is not an inline node, call createElement("br")
+		// on the context object and append the result as the last child of end
+		// node."
+		if ((isEditable(endNode) || isEditingHost(endNode))
+		&& endNode.nodeType == Node.ELEMENT_NODE
+		&& !endNode.hasChildNodes()
+		&& !isInlineNode(endNode)) {
+			endNode.appendChild(document.createElement("br"));
+		}
+
+		// "Abort these steps."
 		return;
 	}
 
@@ -955,17 +1000,13 @@
 			range.startContainer.removeChild(range.startContainer.childNodes[range.startOffset]);
 		}
 
-		// "Abort these steps."
-		return;
-	}
-
-	// "Set the start and end of range to (start block, length of start
-	// block)."
-	range.setStart(startBlock, getNodeLength(startBlock));
-	range.setEnd(startBlock, getNodeLength(startBlock));
-
-	// "If start block is a descendant of end block:"
-	if (isDescendant(startBlock, endBlock)) {
+	// "Otherwise, if start block is a descendant of end block:"
+	} else if (isDescendant(startBlock, endBlock)) {
+		// "Set the start and end of range to (start block, length of start
+		// block)."
+		range.setStart(startBlock, getNodeLength(startBlock));
+		range.setEnd(startBlock, getNodeLength(startBlock));
+
 		// "Let reference node be start block."
 		var referenceNode = startBlock;
 
@@ -990,22 +1031,33 @@
 			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()) {
-		movePreservingRanges(endBlock.firstChild, startBlock, -1);
-	}
-
-	// "While end block has no children, let parent be the parent of end block,
-	// then remove end block from parent, then set end block to parent."
-	while (!endBlock.hasChildNodes()) {
-		var parent_ = endBlock.parentNode;
-		parent_.removeChild(endBlock);
-		endBlock = parent_;
+	// "Otherwise:"
+	} else {
+		// "Set the start and end of range to (start block, length of start
+		// block)."
+		range.setStart(startBlock, getNodeLength(startBlock));
+		range.setEnd(startBlock, getNodeLength(startBlock));
+
+		// "While end block has children, append the first child of end block
+		// to start block, preserving ranges."
+		while (endBlock.hasChildNodes()) {
+			movePreservingRanges(endBlock.firstChild, startBlock, -1);
+		}
+
+		// "While end block has no children, let parent be the parent of end
+		// block, then remove end block from parent, then set end block to
+		// parent."
+		while (!endBlock.hasChildNodes()) {
+			var parent_ = endBlock.parentNode;
+			parent_.removeChild(endBlock);
+			endBlock = parent_;
+		}
+	}
+
+	// "If start block has no children, call createElement("br") on the context
+	// object and append the result as the last child of start block."
+	if (!startBlock.hasChildNodes()) {
+		startBlock.appendChild(document.createElement("br"));
 	}
 }
 
@@ -2984,6 +3036,15 @@
 		}
 		break;
 
+		case "delete":
+		// "If the active range is not collapsed, delete the selection and
+		// abort these steps."
+		if (!range.collapsed) {
+			deleteSelection();
+			return;
+		}
+		break;
+
 		case "fontname":
 		// "Decompose the range, then set the value of each returned node with
 		// new value equal to value."
--- a/source.html	Fri Jun 03 13:08:06 2011 -0600
+++ b/source.html	Fri Jun 03 15:20:13 2011 -0600
@@ -994,6 +994,9 @@
   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.
+  <!-- This actually will have no effect other than collapsing the selection to
+  whatever point deleteContents() likes to collapse it to.  It might make more
+  sense to collapse to the start or end, though. -->
 
   <li>Set <var>range</var>'s [[rangestart]] to (<var>start node</var>,
   <var>start offset</var>) and its [[rangeend]] to (<var>end node</var>,
@@ -1015,6 +1018,7 @@
 
   <li>Call <code data-anolis-spec=domrange
   title=dom-Range-deleteContents>deleteContents()</code> on <var>range</var>.
+
   <!--
   Now we need to merge blocks.  The simplest case is something like
 
@@ -1036,12 +1040,43 @@
   where one descends from the other.
   -->
 
+  <!-- The deletions might have left some empty elements.  We want to remove
+  empty inline nodes, and add a <br> child to empty blocks.  We remove the
+  empty inline nodes first, and only add the <br>s later, because we don't want
+  to add the brs to blocks that we're about to merge. -->
+  <li>While <var>start node</var> is an <span>editable</span> <span>inline
+  node</span> with [[nodelength]] 0, let <var>parent</var> be its [[parent]],
+  then remove <var>start node</var> from <var>parent</var>, then set <var>start
+  node</var> to <var>parent</var>.
+
+  <li>While <var>end node</var> is an <span>editable</span> <span>inline
+  node</span> with [[nodelength]] 0, let <var>parent</var> be its [[parent]],
+  then remove <var>end node</var> from <var>parent</var>, then set <var>end
+  node</var> to <var>parent</var>.
+
   <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.
+  are the same:
+  <!-- Then we don't try to merge anything. -->
+
+  <ol>
+    <li>If <var>start node</var> is an [[element]] with no [[children]], is
+    either <span>editable</span> or an <span>editing host</span>, and is not an
+    <span>inline node</span>, call [[createelement|"br"]] on the
+    [[contextobject]] and append the result as the last [[child]] of <var>start
+    node</var>.
+
+    <li>If <var>end node</var> is an [[element]] with no [[children]], is
+    either <span>editable</span> or an <span>editing host</span>, and is not an
+    <span>inline node</span>, call [[createelement|"br"]] on the
+    [[contextobject]] and append the result as the last [[child]] of <var>end
+    node</var>.
+
+    <li>Abort these steps.
+  </ol>
 
   <li>If <var>start block</var> is an [[ancestor]] of <var>end block</var>:
   <!-- Just repeatedly blow up the end block. -->
@@ -1074,18 +1109,17 @@
     <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>:
+  <li>Otherwise, 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>Set the [[rangestart]] and [[rangeend]] of <var>range</var> to
+    (<var>start block</var>, [[nodelength]] of <var>start block</var>).
+
     <li>Let <var>reference node</var> be <var>start block</var>.
 
     <li>While <var>reference node</var> is not a [[child]] of <var>end
@@ -1098,19 +1132,28 @@
 
     <li>If the [[nextsibling]] of <var>reference node</var> is a [[br]], remove
     it from its [[parent]].
-
-    <li>Abort these steps.
   </ol>
 
+  <li>Otherwise:
   <!-- 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>.
-
-  <li>While <var>end block</var> has no [[children]], let <var>parent</var> be
-  the [[parent]] of <var>end block</var>, then remove <var>end block</var> from
-  <var>parent</var>, then set <var>end block</var> to <var>parent</var>.
+
+  <ol>
+    <li>Set the [[rangestart]] and [[rangeend]] of <var>range</var> to
+    (<var>start block</var>, [[nodelength]] of <var>start block</var>).
+
+    <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>.
+
+    <li>While <var>end block</var> has no [[children]], let <var>parent</var> be
+    the [[parent]] of <var>end block</var>, then remove <var>end block</var> from
+    <var>parent</var>, then set <var>end block</var> to <var>parent</var>.
+  </ol>
+
+  <li>If <var>start block</var> has no [[children]], call
+  [[createelement|"br"]] on the [[contextobject]] and append the result as the
+  last [[child]] of <var>start block</var>.
 </ol>
 
 <p>To move a [[node]] to a new location, <dfn>preserving ranges</dfn>, remove
@@ -2285,6 +2328,25 @@
 <!-- I'd have expected the value to be the URL, but guess not. -->
 
 
+<h3><dfn>The <code title>delete</code> command</dfn></h3>
+<!-- Not really specifically "inline", but I didn't want to create a new
+section just for it. -->
+
+<p><span>Action</span>:
+
+<ol>
+  <li>If the <span>active range</span> is not <code data-anolis-spec=domrange
+  title=dom-Range-collapsed>collapsed</code>, <span>delete the selection</span>
+  and abort these steps.
+
+  <p class=XXX>Maybe we sometimes want to do the following even if it isn't
+  collapsed?  WebKit seems to do some normalization on the range before
+  deciding whether it's collapsed, and that sounds like a good idea.
+
+  <li class=XXX>The stuff where we delete the previous character goes here.
+</ol>
+
+
 <h3><dfn>The <code title>fontName</code> command</dfn></h3>
 
 <p><span>Action</span>: <span>Decompose</span> the <span>active range</span>,
@@ -4980,12 +5042,25 @@
 
 <h2>Additional requirements</h2>
 
+<p class=XXX>It has been <a
+href=http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2009-December/024628.html>suggested</a>
+that some things here need to be platform-dependent, not fully standardized.
+For now I'm standardizing them anyway, because the large majority of behavior
+should be platform-agnostic.  If anyone has suggestions as to particular things
+that should be left up to platform behavior, please say so.
+
 <p>When the user instructs the user agent to insert a line break inside an
 <span>editing host</span>, such as by pressing the Return key while the cursor
 is in an <span>editable</span> [[node]], the user agent must take the
 <span>action</span> for <span>the <code title>insertParagraph</code>
 command</span>.
 
+<p>When the user instructs the user agent to delete the previous character
+inside an <span>editing host</span>, such as by pressing the Backspace key
+while the cursor is in an <span>editable</span> [[node]], the user agent must
+take the <span>action</span> for <span>the <code title>delete</code>
+command</span>.
+
 
 <h2 class=no-num>Acknowledgements</h2>
 <p>Thanks to: