Preserve styles when breaking up nodes
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Tue, 12 Jul 2011 11:28:30 -0600
changeset 396 e4328ccf371a
parent 395 5ab04f33ebba
child 397 cf1e0d048f96
Preserve styles when breaking up nodes

This is a pretty big change. It mostly only does anything when there
are inline style attributes. So now when you have
<p style=color:red>foo<p>[]bar
and hit backspace, you get
<p><font color=red>foo</font>[]bar
or
<p><span style=color:red>foo</span>[]bar
instead of
<p style=color:red>foo[]bar
which makes "bar" red too. In principle it might have effects in other
scenarios too. If it doesn't, it might be best to just aggressively
kill inline style attributes on blocks . . .
editcommands.html
implementation.js
source.html
tests.js
--- a/editcommands.html	Mon Jul 11 13:58:51 2011 -0600
+++ b/editcommands.html	Tue Jul 12 11:28:30 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-11-july-2011>Work in Progress &mdash; Last Update 11 July 2011</h2>
+<h2 class="no-num no-toc" id=work-in-progress-&mdash;-last-update-12-july-2011>Work in Progress &mdash; Last Update 12 July 2011</h2>
 <dl>
  <dt>Editor
  <dd>Aryeh Gregor &lt;[email protected]&gt;
@@ -328,6 +328,15 @@
   especially since in many cases, the command's effect isn't currently
   interoperable anyway.  I'll switch them back to doing nothing if implementers
   think this is a problem.
+
+  <li>The CSS styling flag is an issue.  Currently authors are forced to turn
+  it entirely on or entirely off.  If it's on, it produces stuff like <code title="">&lt;span style=font-weight:bold&gt;</code> instead of <code title="">&lt;b&gt;</code>, while if it's off, it produces stuff like <code title="">&lt;font color=red&gt;</code> instead of <code title="">&lt;span
+  style=color:red&gt;</code>.  The issue is that authors might want a mix, like
+  making the markup as concise as possible while still conforming, and they
+  can't do that.  Changing the flag on a per-command basis doesn't help because
+  of things like the "restore the values" algorithm, which might create several
+  different types of style at once and has to use the same styling flag for all
+  of them.
 </ul>
 
 <p class=XXX>A variety of other issues are also noted in the text, formatted
@@ -1521,6 +1530,65 @@
   <a href=#preserving-ranges>preserving ranges</a>.
 </ol>
 
+<p>To <dfn id=record-the-values>record the values</dfn> of a list 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> <var title="">node list</var>:
+
+<ol>
+  <li>Let <var title="">values</var> be a list 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>node</a>, <a href=#command>command</a>,
+  <a href=#specified-value>specified value</a>) triples, initially empty.
+
+  <li>For each <var title="">node</var> in <var title="">node list</var>, for each
+  <var title="">command</var> in the list "subscript", "bold", "fontName", "fontSize",
+  "foreColor", "hiliteColor", "italic", "strikethrough", and "underline" in
+  that order:
+  <!-- As with removeFormat, we put subscript first so it doesn't interfere
+  with fontSize, and omit superscript because it's redundant with subscript.
+  -->
+
+  <ol>
+    <li>Let <var title="">ancestor</var> equal <var title="">node</var>.
+
+    <li>If <var title="">ancestor</var> 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>, set it 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 <var title="">ancestor</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> and its <a href=#specified-value>specified
+    value</a> for <var title="">command</var> is null, set it 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>If <var title="">ancestor</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>, add (<var title="">node</var>,
+    <var title="">command</var>, <var title="">ancestor</var>'s <a href=#specified-value>specified value</a> for
+    <var title="">command</var>) to <var title="">values</var>.  Otherwise add (<var title="">node</var>,
+    <var title="">command</var>, null) to <var title="">values</var>.
+  </ol>
+
+  <li>Return <var title="">values</var>.
+</ol>
+
+<p>To <dfn id=restore-the-values>restore the values</dfn> specified by a list <var title="">values</var>
+returned by the <a href=#record-the-values>record the values</a> algorithm:
+
+<ol>
+  <li>For each (<var title="">node</var>, <var title="">command</var>, <var title="">value</var>) triple
+  in <var title="">values</var>:
+
+  <ol>
+    <li>Let <var title="">ancestor</var> equal <var title="">node</var>.
+
+    <li>If <var title="">ancestor</var> 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>, set it 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 <var title="">ancestor</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> and its <a href=#specified-value>specified
+    value</a> for <var title="">command</var> is null, set it 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>If <var title="">value</var> is null and <var title="">ancestor</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>,
+    <a href=#push-down-values>push down values</a> on <var title="">node</var> for <var title="">command</var>,
+    with <var title="">new value</var> null.
+
+    <li>Otherwise, if <var title="">ancestor</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> and its
+    <a href=#specified-value>specified value</a> for <var title="">command</var> is different from
+    <var title="">value</var>, or if <var title="">ancestor</var> 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> and
+    <var title="">value</var> is not null, <a href=#force-the-value>force the value</a> of
+    <var title="">command</var> to <var title="">value</var> on <var title="">node</var>.
+  </ol>
+</ol>
+
+
 
 <h3 id="clearing-an-element's-value"><span class=secno>7.3 </span>Clearing an element's value</h3>
 
@@ -3063,18 +3131,26 @@
 "dt", or "dd".
 
 <p>The <dfn id=default-single-line-container-name>default single-line container name</dfn> is "p".
-<!-- Possibly to be made configurable later. -->
+<!-- TODO V2: Make this configurable. -->
 
 
 <h3 id=assorted-block-formatting-command-algorithms><span class=secno>8.2 </span>Assorted block formatting command algorithms</h3>
 
 <p>To <dfn id=fix-disallowed-ancestors>fix disallowed ancestors</dfn> of <var title="">node</var>:
+<!-- TODO V2: When breaking a non-inline element out of an inline element, like
+p in b or whatever, it would make sense to re-wrap the contents in the inline
+tag. -->
 
 <ol>
   <li>If <var title="">node</var> is not <a href=#editable>editable</a>, abort these steps.
 
   <li>If <var title="">node</var> is not an <a href=#allowed-child>allowed child</a> of any of its
-  <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-ancestor title=concept-tree-ancestor>ancestors</a> <a href=#in-the-same-editing-host>in the same editing host</a>:
+  <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-ancestor title=concept-tree-ancestor>ancestors</a> <a href=#in-the-same-editing-host>in the same editing host</a>, and is not an <a href=#html-element>HTML
+  element</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> equal to the <a href=#default-single-line-container-name>default single-line
+  container name</a>:
+  <!-- The requirement about default containers is to prevent an infinite loop.
+  This case is really intended to handle stuff like list items or table cells
+  that wander outside their proper place. -->
 
   <ol>
     <li>If <var title="">node</var> is a <code class=external data-anolis-spec=html title="the dd element"><a href=http://www.whatwg.org/html/#the-dd-element>dd</a></code> or <code class=external data-anolis-spec=html title="the dt element"><a href=http://www.whatwg.org/html/#the-dt-element>dt</a></code>, <a href=#wrap>wrap</a> the
@@ -3101,9 +3177,14 @@
     <li>Abort these steps.
   </ol>
 
+  <li><a href=#record-the-values>Record the values</a> of the one-<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> list consisting of
+  <var title="">node</var>, and let <var title="">values</var> be the result.
+
   <li>While <var title="">node</var> is not an <a href=#allowed-child>allowed child</a> of its
   <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>, <a href=#split-the-parent>split the parent</a> of the one-<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> list consisting
   of <var title="">node</var>.
+
+  <li><a href=#restore-the-values>Restore the values</a> from <var title="">values</var>.
 </ol>
 
 <p>To <dfn id=normalize-sublists>normalize sublists</dfn> in a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> <var title="">item</var>:
@@ -3730,7 +3811,7 @@
     <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>Let <var title="">children</var> be a list 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>.
@@ -3740,6 +3821,9 @@
     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><a href=#record-the-values>Record the values</a> of <var title="">children</var>, and let
+    <var title="">values</var> be the result.
+
     <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>.
 
@@ -3765,9 +3849,21 @@
     node</a> and <var title="">start block</var>'s <code class=external data-anolis-spec=domcore title=dom-Node-lastChild><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-lastchild>lastChild</a></code> 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
     <var title="">start block</var>'s <code class=external data-anolis-spec=domcore title=dom-Node-lastChild><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-lastchild>lastChild</a></code> from it.
 
-    <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=#block-node>block node</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>,
+    <li>Let <var title="">nodes to move</var> be a list 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>If <var title="">reference node</var>'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 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=#block-node>block node</a>, append it to <var title="">nodes to
+    move</var>.
+
+    <li>While <var title="">nodes to move</var> is nonempty and its 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 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=#block-node>block node</a>,
+    append it to <var title="">nodes to move</var>.
+
+    <li><a href=#record-the-values>Record the values</a> of <var title="">nodes to move</var>, and let
+    <var title="">values</var> be the result.
+
+    <li>For each <var title="">node</var> in <var title="">nodes to move</var>, append
+    <var title="">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
@@ -3786,6 +3882,9 @@
     and <var title="">start block</var>'s <code class=external data-anolis-spec=domcore title=dom-Node-lastChild><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-lastchild>lastChild</a></code> 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 <var title="">start
     block</var>'s <code class=external data-anolis-spec=domcore title=dom-Node-lastChild><a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-lastchild>lastChild</a></code> from it.
 
+    <li><a href=#record-the-values>Record the values</a> of <var title="">end block</var>'s <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>,
+    and let <var title="">values</var> be the result.
+
     <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>.
@@ -3795,6 +3894,8 @@
     <var title="">parent</var>, then set <var title="">end block</var> to <var title="">parent</var>.
   </ol>
 
+  <li><a href=#restore-the-values>Restore the values</a> from <var title="">values</var>.
+
   <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>.
@@ -3806,13 +3907,6 @@
 <p>To <dfn id=split-the-parent>split the parent</dfn> of a list <var title="">node list</var> of consecutive
 <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-sibling title=concept-tree-sibling>sibling</a> <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a>:
 
-<p class=XXX>Pretty much any time we call this algorithm, it can cause trouble
-if the parent had styles, classes, etc.  There's not going to be any general
-way to handle this, but we should at least try to handle the special case of
-inline styles, because Firefox does actually add them to arbitrary elements.
-Also, when splitting out of an inline parent, it might be good to wrap all the
-inline descendants in a clone of the former parent.
-
 <ol>
   <li>Let <var title="">original parent</var> be the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> of the first member of
   <var title="">node list</var>.
@@ -4342,26 +4436,6 @@
   indentation element, as does the spec.
   -->
 
-  <div class=XXX>
-  <p>We don't handle a case like
-
-  </p><xmp><ol><ol style="color: red"><li>foo<li>bar</ol><li>baz</ol></xmp>
-
-  <p>If the inner <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> is selected to be outdented, "foo" and "bar" will stop
-  being red.  It seems nontrivial to handle this case in general, since we
-  can't group <code class=external data-anolis-spec=html title="the li element"><a href=http://www.whatwg.org/html/#the-li-element>li</a></code>s.  If the list we're outdenting is a child of a non-list,
-  then we can just change it to a div.  Hopefully
-  <a href="http://www.w3.org/Bugs/Public/show_bug.cgi?id=13128">HTML bug
-  13128</a> will get fixed, in which case I can just use a div here too.
-  </div>
-  <!--
-  Chrome 12 dev seems to special-case style attributes by converting them to
-  the corresponding inline markup elements (at least in easy cases like color).
-  For other attributes and non-WebKit browsers (IE9/FF4/O11.10), it looks like
-  all the attributes are just removed.  Maybe we should try to copy WebKit
-  here.
-  -->
-
   <ol>
     <li>Unset the <code class=external data-anolis-spec=html title=attr-ol-reversed><a href=http://www.whatwg.org/html/#attr-ol-reversed>reversed</a></code>, <code class=external data-anolis-spec=html title=attr-ol-start><a href=http://www.whatwg.org/html/#attr-ol-start>start</a></code>, and <code class=external data-anolis-spec=html title=attr-ol-type><a href=http://www.whatwg.org/html/#attr-ol-type>type</a></code> attributes of <var title="">node</var>, if any are
     set.
@@ -4370,8 +4444,23 @@
 
     <li>If <var title="">node</var> has attributes, and its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> is not an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code>
     or <code class=external data-anolis-spec=html title="the ul element"><a href=http://www.whatwg.org/html/#the-ul-element>ul</a></code>, <a href=#set-the-tag-name>set the tag name</a> of <var title="">node</var> to "div".
-
-    <li>Otherwise remove <var title="">node</var>, <a href=#preserving-its-descendants>preserving its descendants</a>.
+    <!--
+    We can't turn it into a div if it's the child of an ol or ul, because
+    that's not currently allowed.  TODO V2: change this if
+    http://www.w3.org/Bugs/Public/show_bug.cgi?id=13128 is fixed and we can
+    make it a div.
+    -->
+
+    <li>Otherwise:
+
+    <ol>
+      <li><a href=#record-the-values>Record the values</a> of <var title="">node</var>'s <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, and
+      let <var title="">values</var> be the result.
+
+      <li>Remove <var title="">node</var>, <a href=#preserving-its-descendants>preserving its descendants</a>.
+
+      <li><a href=#restore-the-values>Restore the values</a> from <var title="">values</var>.
+    </ol>
 
     <li><a href=#fix-disallowed-ancestors>Fix disallowed ancestors</a> of each member of
     <var title="">children</var>.
@@ -4798,10 +4887,15 @@
     <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> <var title="">tag name</var>, remove the first member from <var title="">node
     list</var> and append it to <var title="">sublist</var>.
 
+    <li><a href=#record-the-values>Record the values</a> of <var title="">sublist</var>, and let
+    <var title="">values</var> be the result.
+
     <li><a href=#split-the-parent>Split the parent</a> of <var title="">sublist</var>.
 
     <li><a href=#fix-disallowed-ancestors>Fix disallowed ancestors</a> of each member of
     <var title="">sublist</var>.
+
+    <li><a href=#restore-the-values>Restore the values</a> from <var title="">values</var>.
   </ol>
 
   <li>Otherwise, while <var title="">node list</var> is not empty:
@@ -4822,8 +4916,9 @@
 
     <li>If <var title="">sublist</var> contains more than one member, <a href=#wrap>wrap</a>
     <var title="">sublist</var>, with <a href=#sibling-criteria>sibling criteria</a> matching nothing and
-    <a href=#new-parent-instructions>new parent instructions</a> returning the result of calling <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("li")</a></code> on the
-    <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>.  Let <var title="">node</var> be the result.
+    <a href=#new-parent-instructions>new parent instructions</a> returning the result of calling
+    <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("li")</a></code> on the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>.  Let <var title="">node</var> be
+    the result.
 
     <li>Otherwise, let <var title="">node</var> be the sole member of
     <var title="">sublist</var>.
@@ -4834,14 +4929,18 @@
     <ol>
       <li>Let <var title="">children</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>children</a> of <var title="">node</var>.
 
+      <li><a href=#record-the-values>Record the values</a> of <var title="">children</var>, and let
+      <var title="">values</var> be the result.
+
       <li>Remove <var title="">node</var>, <a href=#preserving-its-descendants>preserving its descendants</a>.
 
       <li><a href=#wrap>Wrap</a> <var title="">children</var>, with <a href=#sibling-criteria>sibling
       criteria</a> matching any <a href=#html-element>HTML element</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a>
-      <var title="">tag name</var> and <a href=#new-parent-instructions>new parent
-      instructions</a> returning the result of calling <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(<var title="">tag
-      name</var>)</a></code> on the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>.  Let <var title="">node</var> be the
-      result.
+      <var title="">tag name</var> and <a href=#new-parent-instructions>new parent instructions</a> returning
+      the result of calling <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(<var title="">tag name</var>)</a></code> on the
+      <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>.  Let <var title="">node</var> be the result.
+
+      <li><a href=#restore-the-values>Restore the values</a> from <var title="">values</var>.
 
       <li>Prepend the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-descendant title=concept-tree-descendant>descendants</a> of <var title="">node</var> that are <a href=#html-element title="HTML element">HTML elements</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> <var title="">other
       tag name</var> (if any) to <var title="">node list</var>.
@@ -4883,6 +4982,9 @@
     with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> <var title="">other tag name</var>:
 
     <ol>
+      <li><a href=#record-the-values>Record the values</a> of the one-<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> list consisting of
+      <var title="">node</var>, and let <var title="">values</var> be the result.
+
       <li><a href=#split-the-parent>Split the parent</a> of the one-<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> list consisting of
       <var title="">node</var>.
 
@@ -4890,8 +4992,9 @@
       <var title="">node</var>, with <a href=#sibling-criteria>sibling criteria</a> matching any
       <a href=#html-element>HTML element</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> <var title="">tag name</var>,
       and with <a href=#new-parent-instructions>new parent instructions</a> returning the result of
-      calling <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(<var title="">tag
-      name</var>)</a></code> on the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>.
+      calling <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(<var title="">tag name</var>)</a></code> on the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>.
+
+      <li><a href=#restore-the-values>Restore the values</a> from <var title="">values</var>.
 
       <li>Prepend the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-descendant title=concept-tree-descendant>descendants</a> of <var title="">node</var> that are <a href=#html-element title="HTML element">HTML elements</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> <var title="">other
       tag name</var> (if any) to <var title="">node list</var>.
@@ -5160,9 +5263,14 @@
     <li><a href=#normalize-sublists>Normalize sublists</a> of each <var title="">item</var> in
     <var title="">items</var>.
 
+    <li><a href=#record-the-values>Record the values</a> of the one-<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> list consisting of
+    <var title="">node</var>, and let <var title="">values</var> be the result.
+
     <li><a href=#split-the-parent>Split the parent</a> of the one-<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> list consisting of
     <var title="">node</var>.
 
+    <li><a href=#restore-the-values>Restore the values</a> from <var title="">values</var>.
+
     <li>If <var title="">node</var> is a <code class=external data-anolis-spec=html title="the dd element"><a href=http://www.whatwg.org/html/#the-dd-element>dd</a></code> or <code class=external data-anolis-spec=html title="the dt element"><a href=http://www.whatwg.org/html/#the-dt-element>dt</a></code>, and it is not an
     <a href=#allowed-child>allowed child</a> of any of its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-ancestor title=concept-tree-ancestor>ancestors</a> <a href=#in-the-same-editing-host>in the same
     editing host</a>, <a href=#set-the-tag-name>set the tag name</a> of <var title="">node</var> to
@@ -5438,6 +5546,9 @@
   of "p" or a <code class=external data-anolis-spec=html title="the dd element"><a href=http://www.whatwg.org/html/#the-dd-element>dd</a></code> or <code class=external data-anolis-spec=html title="the dt element"><a href=http://www.whatwg.org/html/#the-dt-element>dt</a></code>, and <var title="">node</var> is not the <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
   a <a href=#prohibited-paragraph-child>prohibited paragraph child</a>.
 
+  <li><a href=#record-the-values>Record the values</a> of <var title="">node list</var>, and let
+  <var title="">values</var> be the result.
+
   <li>For each <var title="">node</var> in <var title="">node list</var>, while <var title="">node</var>
   is the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-descendant title=concept-tree-descendant>descendant</a> of an <a href=#editable>editable</a> <a href=#html-element>HTML element</a>
   <a href=#in-the-same-editing-host>in the same editing host</a>, which has <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> "address",
@@ -5458,6 +5569,8 @@
   out of lists or tables or such if they happen to be nested in a <div>.
   -->
 
+  <li><a href=#restore-the-values>Restore the values</a> from <var title="">values</var>.
+
   <!--
   We have two different behaviors, one for div and p and one for everything
   else.  The basic difference is that for div and p, we assume that it should
@@ -5518,9 +5631,14 @@
       <li>Let <var title="">sublist</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>children</a> of the first member of
       <var title="">node list</var>.
 
+      <li><a href=#record-the-values>Record the values</a> of <var title="">sublist</var>, and let
+      <var title="">values</var> be the result.
+
       <li>Remove the first member of <var title="">node list</var> from its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>,
       <a href=#preserving-its-descendants>preserving its descendants</a>.
 
+      <li><a href=#restore-the-values>Restore the values</a> from <var title="">values</var>.
+
       <li>Remove the first member from <var title="">node list</var>.
     </ol>
 
@@ -6777,37 +6895,33 @@
 
   <li>Let <var title="">node list</var> be a list 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>For each <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> <var title="">node</var> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> in <var title="">new range</var>:
-
-  <ol>
-    <li>If the last member of <var title="">node list</var> (if any) 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="">node</var>, or if <var title="">node</var> is not <a href=#editable>editable</a>,
-    continue with the next <var title="">node</var>.
-
-    <!--
-    This step is kind of weird.  For regular outdenting, we start at the inside
-    and outdent going out, so that we remove the innermost indentation, on the
-    theory that that will produce the cleanest markup (remove the most nodes).
-    For lists, we remove the outermost indentation, because it makes a
-    difference whether we remove inner or outer indentation, and logically we
-    want to remove outer.  E.g.,
-
-      <ol><li>foo</li><ul><li>bar</li></ul></ol>
-
-    should become
-
-      foo<ul><li>bar</li></ul>
-
-    not
-
-      foo<ol><li>bar</li></ol>.
-
-    But this is a bit weird and I'm wondering if it's really correct.
-    -->
-    <li>If <var title="">node</var> has no <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-tree-descendant title=concept-tree-descendant>descendants</a>, or is
-    an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> or <code class=external data-anolis-spec=html title="the ul element"><a href=http://www.whatwg.org/html/#the-ul-element>ul</a></code>, or is an <code class=external data-anolis-spec=html title="the li element"><a href=http://www.whatwg.org/html/#the-li-element>li</a></code> whose <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 an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> or
-    <code class=external data-anolis-spec=html title="the ul element"><a href=http://www.whatwg.org/html/#the-ul-element>ul</a></code>, append it to <var title="">node list</var>.
-  </ol>
+  <li>For each <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> <var title="">node</var> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> in <var title="">new range</var>,
+  append <var title="">node</var> to <var title="">node list</var> if the last member of
+  <var title="">node list</var> (if any) is not 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="">node</var>;
+  <var title="">node</var> is <a href=#editable>editable</a>; and either <var title="">node</var> has no
+  <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-tree-descendant title=concept-tree-descendant>descendants</a>, or is an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> or <code class=external data-anolis-spec=html title="the ul element"><a href=http://www.whatwg.org/html/#the-ul-element>ul</a></code>, or is an
+  <code class=external data-anolis-spec=html title="the li element"><a href=http://www.whatwg.org/html/#the-li-element>li</a></code> whose <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 an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> or <code class=external data-anolis-spec=html title="the ul element"><a href=http://www.whatwg.org/html/#the-ul-element>ul</a></code>.
+  <!--
+  This step is kind of weird.  For regular outdenting, we start at the inside
+  and outdent going out, so that we remove the innermost indentation, on the
+  theory that that will produce the cleanest markup (remove the most nodes).
+  For lists, we remove the outermost indentation, because it makes a difference
+  whether we remove inner or outer indentation, and logically we want to remove
+  outer.  E.g.,
+
+    <ol><li>foo</li><ul><li>bar</li></ul></ol>
+
+  should become
+
+    foo<ul><li>bar</li></ul>
+
+  not
+
+    foo<ol><li>bar</li></ol>.
+
+  But this is a bit weird and I'm wondering if it's really correct.  TODO V2:
+  Reexamine this.
+  -->
 
   <li>While <var title="">node list</var> is not empty:
 
@@ -6828,10 +6942,15 @@
     list</var> is not an <code class=external data-anolis-spec=html title="the ol element"><a href=http://www.whatwg.org/html/#the-ol-element>ol</a></code> or <code class=external data-anolis-spec=html title="the ul element"><a href=http://www.whatwg.org/html/#the-ul-element>ul</a></code>, remove the first member of <var title="">node
     list</var> and append it to <var title="">sublist</var>.
 
+    <li><a href=#record-the-values>Record the values</a> of <var title="">sublist</var>, and let
+    <var title="">values</var> be the result.
+
     <li><a href=#split-the-parent>Split the parent</a> of <var title="">sublist</var>.
 
     <li><a href=#fix-disallowed-ancestors>Fix disallowed ancestors</a> of each member of
     <var title="">sublist</var>.
+
+    <li><a href=#restore-the-values>Restore the values</a> from <var title="">values</var>.
   </ol>
 </ol>
 
--- a/implementation.js	Mon Jul 11 13:58:51 2011 -0600
+++ b/implementation.js	Tue Jul 12 11:28:30 2011 -0600
@@ -2054,6 +2054,91 @@
 	movePreservingRanges(node, candidate, -1);
 }
 
+function recordValues(nodeList) {
+	// "Let values be a list of (node, command, specified value) triples,
+	// initially empty."
+	var values = [];
+
+	// "For each node in node list, for each command in the list "subscript",
+	// "bold", "fontName", "fontSize", "foreColor", "hiliteColor", "italic",
+	// "strikethrough", and "underline" in that order:"
+	nodeList.forEach(function(node) {
+		["subscript", "bold", "fontname", "fontsize", "forecolor",
+		"hilitecolor", "italic", "strikethrough", "underline"].forEach(function(command) {
+			// "Let ancestor equal node."
+			var ancestor = node;
+
+			// "If ancestor is not an Element, set it to its parent."
+			if (ancestor.nodeType != Node.ELEMENT_NODE) {
+				ancestor = ancestor.parentNode;
+			}
+
+			// "While ancestor is an Element and its specified value for
+			// command is null, set it to its parent."
+			while (ancestor
+			&& ancestor.nodeType == Node.ELEMENT_NODE
+			&& getSpecifiedValue(ancestor, command) === null) {
+				ancestor = ancestor.parentNode;
+			}
+
+			// "If ancestor is an Element, add (node, command, ancestor's
+			// specified value for command) to values. Otherwise add (node,
+			// command, null) to values."
+			if (ancestor && ancestor.nodeType == Node.ELEMENT_NODE) {
+				values.push([node, command, getSpecifiedValue(ancestor, command)]);
+			} else {
+				values.push([node, command, null]);
+			}
+		});
+	});
+
+	// "Return values."
+	return values;
+}
+
+function restoreValues(values) {
+	// "For each (node, command, value) triple in values:"
+	values.forEach(function(triple) {
+		var node = triple[0];
+		var command = triple[1];
+		var value = triple[2];
+
+		// "Let ancestor equal node."
+		var ancestor = node;
+
+		// "If ancestor is not an Element, set it to its parent."
+		if (!ancestor || ancestor.nodeType != Node.ELEMENT_NODE) {
+			ancestor = ancestor.parentNode;
+		}
+
+		// "While ancestor is an Element and its specified value for command is
+		// null, set it to its parent."
+		while (ancestor
+		&& ancestor.nodeType == Node.ELEMENT_NODE
+		&& getSpecifiedValue(ancestor, command) === null) {
+			ancestor = ancestor.parentNode;
+		}
+
+		// "If value is null and ancestor is an Element, push down values on
+		// node for command, with new value null."
+		if (value === null
+		&& ancestor
+		&& ancestor.nodeType == Node.ELEMENT_NODE) {
+			pushDownValues(node, command, null);
+
+		// "Otherwise, if ancestor is an Element and its specified value for
+		// command is different from value, or if ancestor is not an Element
+		// and value is not null, force the value of command to value on node."
+		} else if ((ancestor
+		&& ancestor.nodeType == Node.ELEMENT_NODE
+		&& !valuesEqual(command, getSpecifiedValue(ancestor, command), value))
+		|| ((!ancestor || ancestor.nodeType != Node.ELEMENT_NODE)
+		&& value !== null)) {
+			forceValue(node, command, value);
+		}
+	});
+}
+
 //@}
 
 ///// Clearing an element's value /////
@@ -3443,11 +3528,13 @@
 	}
 
 	// "If node is not an allowed child of any of its ancestors in the same
-	// editing host:"
+	// editing host, and is not an HTML element with local name equal to the
+	// default single-line container name:"
 	if (getAncestors(node).every(function(ancestor) {
 		return !inSameEditingHost(node, ancestor)
 			|| !isAllowedChild(node, ancestor)
-	})) {
+	})
+	&& !isHtmlElement(node, defaultSingleLineContainerName)) {
 		// "If node is a dd or dt, wrap the one-node list consisting of node,
 		// with sibling criteria matching any dl with no attributes, and new
 		// parent instructions returning the result of calling
@@ -3483,11 +3570,18 @@
 		return;
 	}
 
+	// "Record the values of the one-node list consisting of node, and let
+	// values be the result."
+	var values = recordValues([node]);
+
 	// "While node is not an allowed child of its parent, split the parent of
 	// the one-node list consisting of node."
 	while (!isAllowedChild(node, node.parentNode)) {
 		splitParent([node]);
 	}
+
+	// "Restore the values from values."
+	restoreValues(values);
 }
 
 function normalizeSublists(item) {
@@ -4189,7 +4283,7 @@
 			return;
 		}
 
-		// "Let children be an array of nodes, initially empty."
+		// "Let children be a list of nodes, initially empty."
 		var children = [];
 
 		// "Append the first child of end block to children."
@@ -4203,6 +4297,9 @@
 			children.push(children[children.length - 1].nextSibling);
 		}
 
+		// "Record the values of children, and let values be the result."
+		var values = recordValues(children);
+
 		// "While children's first member's parent is not start block, split
 		// the parent of children."
 		while (children[0].parentNode != startBlock) {
@@ -4239,14 +4336,35 @@
 			startBlock.removeChild(startBlock.lastChild);
 		}
 
-		// "While the nextSibling of reference node is neither null nor a br
-		// nor a block node, append the nextSibling of reference node as the
-		// last child of start block, preserving ranges."
-		while (referenceNode.nextSibling
+		// "Let nodes to move be a list of nodes, initially empty."
+		var nodesToMove = [];
+
+		// "If reference node's nextSibling is neither null nor a br nor a
+		// block node, append it to nodes to move."
+		if (referenceNode.nextSibling
 		&& !isHtmlElement(referenceNode.nextSibling, "br")
 		&& !isBlockNode(referenceNode.nextSibling)) {
-			movePreservingRanges(referenceNode.nextSibling, startBlock, -1);
-		}
+			nodesToMove.push(referenceNode.nextSibling);
+		}
+
+		// "While nodes to move is nonempty and its last member's nextSibling
+		// is neither null nor a br nor a block node, append it to nodes to
+		// move."
+		if (nodesToMove.length
+		&& nodesToMove[nodesToMove.length - 1].nextSibling
+		&& !isHtmlElement(nodesToMove[nodesToMove.length - 1].nextSibling, "br")
+		&& !isBlockNode(nodesToMove[nodesToMove.length - 1].nextSibling)) {
+			nodesToMove.push(nodesToMove[nodesToMove.length - 1].nextSibling);
+		}
+
+		// "Record the values of nodes to move, and let values be the result."
+		var values = recordValues(nodesToMove);
+
+		// "For each node in nodes to move, append node as the last child of
+		// start block, preserving ranges."
+		nodesToMove.forEach(function(node) {
+			movePreservingRanges(node, startBlock, -1);
+		});
 
 		// "If the nextSibling of reference node is a br, remove it from its
 		// parent."
@@ -4268,6 +4386,10 @@
 			startBlock.removeChild(startBlock.lastChild);
 		}
 
+		// "Record the values of end block's children, and let values be the
+		// result."
+		var values = recordValues([].slice.call(endBlock.childNodes));
+
 		// "While end block has children, append the first child of end block
 		// to start block, preserving ranges."
 		while (endBlock.hasChildNodes()) {
@@ -4284,6 +4406,9 @@
 		}
 	}
 
+	// "Restore the values from values."
+	restoreValues(values);
+
 	// "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()) {
@@ -4794,9 +4919,17 @@
 		&& !isHtmlElement(node.parentNode, ["OL", "UL"])) {
 			setTagName(node, "div");
 
-		// "Otherwise remove node, preserving its descendants."
+		// "Otherwise:"
 		} else {
+			// "Record the values of node's children, and let values be the
+			// result."
+			var values = recordValues([].slice.call(node.childNodes));
+
+			// "Remove node, preserving its descendants."
 			removePreservingDescendants(node);
+
+			// "Restore the values from values."
+			restoreValues(values);
 		}
 
 		// "Fix disallowed ancestors of each member of children."
@@ -4877,24 +5010,13 @@
 
 	// "Let items be a list of all lis that are ancestor containers of the
 	// range's start and/or end node."
-	//
-	// Has to be in tree order, remember!
-	var items = [];
-	for (var node = range.endContainer; node != range.commonAncestorContainer; node = node.parentNode) {
-		if (isHtmlElement(node, "LI")) {
-			items.unshift(node);
-		}
-	}
-	for (var node = range.startContainer; node != range.commonAncestorContainer; node = node.parentNode) {
-		if (isHtmlElement(node, "LI")) {
-			items.unshift(node);
-		}
-	}
-	for (var node = range.commonAncestorContainer; node; node = node.parentNode) {
-		if (isHtmlElement(node, "LI")) {
-			items.unshift(node);
-		}
-	}
+	var items = getDescendants(document).filter(function(node) {
+		return isHtmlElement(node, "li")
+			&& (isAncestor(node, range.startContainer)
+			|| node == range.startContainer
+			|| isAncestor(node, range.endContainer)
+			|| node == range.endContainer);
+	});
 
 	// "For each item in items, normalize sublists of item."
 	for (var i = 0; i < items.length; i++) {
@@ -4905,28 +5027,19 @@
 	var newRange = blockExtend(range);
 
 	// "Let node list be a list of nodes, initially empty."
-	var nodeList = [];
-
+	//
 	// "For each node node contained in new range, if node is editable; the
 	// last member of node list (if any) is not an ancestor of node; node
 	// is not an indentation element; and either node is an ol or ul, or its
 	// parent is an ol or ul, or it is an allowed child of "li"; then append
 	// node to node list."
-	for (
-		var node = newRange.startContainer;
-		node != nextNodeDescendants(newRange.endContainer);
-		node = nextNode(node)
-	) {
-		if (isEditable(node)
-		&& isContained(node, newRange)
-		&& (!nodeList.length || !isAncestor(nodeList[nodeList.length - 1], node))
+	var nodeList = getContainedNodes(newRange, function(node) {
+		return isEditable(node)
 		&& !isIndentationElement(node)
 		&& (isHtmlElement(node, ["OL", "UL"])
 		|| isHtmlElement(node.parentNode, ["OL", "UL"])
-		|| isAllowedChild(node, "li"))) {
-			nodeList.push(node);
-		}
-	}
+		|| isAllowedChild(node, "li"));
+	});
 
 	// "If mode is "disable", then while node list is not empty:"
 	if (mode == "disable") {
@@ -4956,6 +5069,9 @@
 				sublist.push(nodeList.shift());
 			}
 
+			// "Record the values of sublist, and let values be the result."
+			var values = recordValues(sublist);
+
 			// "Split the parent of sublist."
 			splitParent(sublist);
 
@@ -4963,6 +5079,9 @@
 			for (var i = 0; i < sublist.length; i++) {
 				fixDisallowedAncestors(sublist[i]);
 			}
+
+			// "Restore the values from values."
+			restoreValues(values);
 		}
 
 	// "Otherwise, while node list is not empty:"
@@ -5009,6 +5128,10 @@
 				// "Let children be the children of node."
 				var children = [].slice.call(node.childNodes);
 
+				// "Record the values of children, and let values be the
+				// result."
+				var values = recordValues(children);
+
 				// "Remove node, preserving its descendants."
 				removePreservingDescendants(node);
 
@@ -5020,6 +5143,9 @@
 					function(node) { return isHtmlElement(node, tagName) },
 					function() { return document.createElement(tagName) });
 
+				// "Restore the values from values."
+				restoreValues(values);
+
 				// "Prepend the descendants of node that are HTML elements with
 				// local name other tag name (if any) to node list."
 				nodeList = [].slice.call(node.querySelectorAll(otherTagName)).concat(nodeList);
@@ -5037,6 +5163,10 @@
 			// "If node is the child of an HTML element with local name other
 			// tag name:"
 			if (isHtmlElement(node.parentNode, otherTagName)) {
+				// "Record the values of the one-node list consisting of node,
+				// and let values be the result."
+				var values = recordValues([node]);
+
 				// "Split the parent of the one-node list consisting of
 				// node."
 				splitParent([node]);
@@ -5049,6 +5179,9 @@
 					function(node) { return isHtmlElement(node, tagName) },
 					function() { return document.createElement(tagName) });
 
+				// "Restore the values from values."
+				restoreValues(values);
+
 				// "Prepend the descendants of node that are HTML elements with
 				// local name other tag name (if any) to node list."
 				nodeList = [].slice.call(node.querySelectorAll(otherTagName)).concat(nodeList);
@@ -5350,9 +5483,16 @@
 				normalizeSublists(items[i]);
 			}
 
+			// "Record the values of the one-node list consisting of node, and
+			// let values be the result."
+			var values = recordValues([node]);
+
 			// "Split the parent of the one-node list consisting of node."
 			splitParent([node]);
 
+			// "Restore the values from values."
+			restoreValues(values);
+
 			// "If node is a dd or dt, and it is not an allowed child of any of
 			// its ancestors in the same editing host, set the tag name of node
 			// to the default single-line container name and let node be the
@@ -5595,6 +5735,9 @@
 				&& !getDescendants(node).some(isProhibitedParagraphChild);
 		});
 
+		// "Record the values of node list, and let values be the result."
+		var values = recordValues(nodeList);
+
 		// "For each node in node list, while node is the descendant of an
 		// editable HTML element in the same editing host, which has local name
 		// "address", "dd", "div", "dt", "h1", "h2", "h3", "h4", "h5", "h6",
@@ -5613,6 +5756,9 @@
 			}
 		}
 
+		// "Restore the values from values."
+		restoreValues(values);
+
 		// "While node list is not empty:"
 		while (nodeList.length) {
 			var sublist;
@@ -5624,10 +5770,17 @@
 				// list."
 				sublist = [].slice.call(nodeList[0].childNodes);
 
+				// "Record the values of sublist, and let values be the
+				// result."
+				var values = recordValues(sublist);
+
 				// "Remove the first member of node list from its parent,
 				// preserving its descendants."
 				removePreservingDescendants(nodeList[0]);
 
+				// "Restore the values from values."
+				restoreValues(values);
+
 				// "Remove the first member from node list."
 				nodeList.shift();
 
@@ -6891,24 +7044,13 @@
 	action: function() {
 		// "Let items be a list of all lis that are ancestor containers of the
 		// active range's start and/or end node."
-		//
-		// Has to be in tree order, remember!
-		var items = [];
-		for (var node = getActiveRange().endContainer; node != getActiveRange().commonAncestorContainer; node = node.parentNode) {
-			if (isHtmlElement(node, "LI")) {
-				items.unshift(node);
-			}
-		}
-		for (var node = getActiveRange().startContainer; node != getActiveRange().commonAncestorContainer; node = node.parentNode) {
-			if (isHtmlElement(node, "LI")) {
-				items.unshift(node);
-			}
-		}
-		for (var node = getActiveRange().commonAncestorContainer; node; node = node.parentNode) {
-			if (isHtmlElement(node, "LI")) {
-				items.unshift(node);
-			}
-		}
+		var items = getDescendants(document).filter(function(node) {
+			return isHtmlElement(node, "li")
+				&& (isAncestor(node, getActiveRange().startContainer)
+				|| node == getActiveRange().startContainer
+				|| isAncestor(node, getActiveRange().endContainer)
+				|| node == getActiveRange().endContainer);
+		});
 
 		// "For each item in items, normalize sublists of item."
 		for (var i = 0; i < items.length; i++) {
@@ -6919,34 +7061,17 @@
 		var newRange = blockExtend(getActiveRange());
 
 		// "Let node list be a list of nodes, initially empty."
-		var nodeList = [];
-
-		// "For each node node contained in new range:"
-		for (
-			var node = newRange.startContainer;
-			node != nextNodeDescendants(newRange.endContainer);
-			node = nextNode(node)
-		) {
-			if (!isContained(node, newRange)) {
-				continue;
-			}
-
-			// "If the last member of node list (if any) is an ancestor of
-			// node, or if node is not editable, continue with the next node."
-			if ((nodeList.length && isAncestor(nodeList[nodeList.length - 1], node))
-			|| !isEditable(node)) {
-				continue;
-			}
-
-			// "If node has no editable descendants, or is an ol or ul, or is
-			// an li whose parent is an ol or ul, append it to node list."
-			if (!hasEditableDescendants(node)
-			|| isHtmlElement(node, ["OL", "UL"])
-			|| (isHtmlElement(node, "LI")
-			&& isHtmlElement(node.parentNode, ["OL", "UL"]))) {
-				nodeList.push(node);
-			}
-		}
+		//
+		// "For each node node contained in new range, append node to node list
+		// if the last member of node list (if any) is not an ancestor of node;
+		// node is editable; and either node has no editable descendants, or is
+		// an ol or ul, or is an li whose parent is an ol or ul."
+		var nodeList = getContainedNodes(newRange, function(node) {
+			return isEditable(node)
+				&& (!getDescendants(node).some(isEditable)
+				|| isHtmlElement(node, ["ol", "ul"])
+				|| (isHtmlElement(node, "li") && isHtmlElement(node.parentNode, ["ol", "ul"])));
+		});
 
 		// "While node list is not empty:"
 		while (nodeList.length) {
@@ -6980,13 +7105,17 @@
 				sublist.push(nodeList.shift());
 			}
 
+			// "Record the values of sublist, and let values be the result."
+			var values = recordValues(sublist);
+
 			// "Split the parent of sublist, with new parent null."
 			splitParent(sublist);
 
 			// "Fix disallowed ancestors of each member of sublist."
-			for (var i = 0; i < sublist.length; i++) {
-				fixDisallowedAncestors(sublist[i]);
-			}
+			sublist.forEach(fixDisallowedAncestors);
+
+			// "Restore the values from values."
+			restoreValues(values);
 		}
 	}
 };
--- a/source.html	Mon Jul 11 13:58:51 2011 -0600
+++ b/source.html	Tue Jul 12 11:28:30 2011 -0600
@@ -256,6 +256,18 @@
   especially since in many cases, the command's effect isn't currently
   interoperable anyway.  I'll switch them back to doing nothing if implementers
   think this is a problem.
+
+  <li>The CSS styling flag is an issue.  Currently authors are forced to turn
+  it entirely on or entirely off.  If it's on, it produces stuff like <code
+  title>&lt;span style=font-weight:bold></code> instead of <code
+  title>&lt;b></code>, while if it's off, it produces stuff like <code
+  title>&lt;font color=red></code> instead of <code title>&lt;span
+  style=color:red></code>.  The issue is that authors might want a mix, like
+  making the markup as concise as possible while still conforming, and they
+  can't do that.  Changing the flag on a per-command basis doesn't help because
+  of things like the "restore the values" algorithm, which might create several
+  different types of style at once and has to use the same styling flag for all
+  of them.
 </ul>
 
 <p class=XXX>A variety of other issues are also noted in the text, formatted
@@ -1474,6 +1486,65 @@
   <li>Append the <var>node</var> as the last [[child]] of <var>candidate</var>,
   <span>preserving ranges</span>.
 </ol>
+
+<p>To <dfn>record the values</dfn> of a list of [[nodes]] <var>node list</var>:
+
+<ol>
+  <li>Let <var>values</var> be a list of ([[node]], <span>command</span>,
+  <span>specified value</span>) triples, initially empty.
+
+  <li>For each <var>node</var> in <var>node list</var>, for each
+  <var>command</var> in the list "subscript", "bold", "fontName", "fontSize",
+  "foreColor", "hiliteColor", "italic", "strikethrough", and "underline" in
+  that order:
+  <!-- As with removeFormat, we put subscript first so it doesn't interfere
+  with fontSize, and omit superscript because it's redundant with subscript.
+  -->
+
+  <ol>
+    <li>Let <var>ancestor</var> equal <var>node</var>.
+
+    <li>If <var>ancestor</var> is not an [[element]], set it to its [[parent]].
+
+    <li>While <var>ancestor</var> is an [[element]] and its <span>specified
+    value</span> for <var>command</var> is null, set it to its [[parent]].
+
+    <li>If <var>ancestor</var> is an [[element]], add (<var>node</var>,
+    <var>command</var>, <var>ancestor</var>'s <span>specified value</span> for
+    <var>command</var>) to <var>values</var>.  Otherwise add (<var>node</var>,
+    <var>command</var>, null) to <var>values</var>.
+  </ol>
+
+  <li>Return <var>values</var>.
+</ol>
+
+<p>To <dfn>restore the values</dfn> specified by a list <var>values</var>
+returned by the <span>record the values</span> algorithm:
+
+<ol>
+  <li>For each (<var>node</var>, <var>command</var>, <var>value</var>) triple
+  in <var>values</var>:
+
+  <ol>
+    <li>Let <var>ancestor</var> equal <var>node</var>.
+
+    <li>If <var>ancestor</var> is not an [[element]], set it to its [[parent]].
+
+    <li>While <var>ancestor</var> is an [[element]] and its <span>specified
+    value</span> for <var>command</var> is null, set it to its [[parent]].
+
+    <li>If <var>value</var> is null and <var>ancestor</var> is an [[element]],
+    <span>push down values</span> on <var>node</var> for <var>command</var>,
+    with <var>new value</var> null.
+
+    <li>Otherwise, if <var>ancestor</var> is an [[element]] and its
+    <span>specified value</span> for <var>command</var> is different from
+    <var>value</var>, or if <var>ancestor</var> is not an [[element]] and
+    <var>value</var> is not null, <span>force the value</span> of
+    <var>command</var> to <var>value</var> on <var>node</var>.
+  </ol>
+</ol>
+
 <!-- @} -->
 
 <h3>Clearing an element's value</h3>
@@ -3039,18 +3110,26 @@
 "dt", or "dd".
 
 <p>The <dfn>default single-line container name</dfn> is "p".
-<!-- Possibly to be made configurable later. -->
+<!-- TODO V2: Make this configurable. -->
 <!-- @} -->
 
 <h3>Assorted block formatting command algorithms</h3>
 <!-- @{ -->
 <p>To <dfn>fix disallowed ancestors</dfn> of <var>node</var>:
+<!-- TODO V2: When breaking a non-inline element out of an inline element, like
+p in b or whatever, it would make sense to re-wrap the contents in the inline
+tag. -->
 
 <ol>
   <li>If <var>node</var> is not <span>editable</span>, abort these steps.
 
   <li>If <var>node</var> is not an <span>allowed child</span> of any of its
-  [[ancestors]] <span>in the same editing host</span>:
+  [[ancestors]] <span>in the same editing host</span>, and is not an <span>HTML
+  element</span> with [[localname]] equal to the <span>default single-line
+  container name</span>:
+  <!-- The requirement about default containers is to prevent an infinite loop.
+  This case is really intended to handle stuff like list items or table cells
+  that wander outside their proper place. -->
 
   <ol>
     <li>If <var>node</var> is a [[dd]] or [[dt]], <span>wrap</span> the
@@ -3077,9 +3156,14 @@
     <li>Abort these steps.
   </ol>
 
+  <li><span>Record the values</span> of the one-[[node]] list consisting of
+  <var>node</var>, and let <var>values</var> be the result.
+
   <li>While <var>node</var> is not an <span>allowed child</span> of its
   [[parent]], <span>split the parent</span> of the one-[[node]] list consisting
   of <var>node</var>.
+
+  <li><span>Restore the values</span> from <var>values</var>.
 </ol>
 
 <p>To <dfn>normalize sublists</dfn> in a [[node]] <var>item</var>:
@@ -3709,7 +3793,7 @@
     <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>Let <var>children</var> be a list of [[nodes]], initially empty.
 
     <li>Append the first [[child]] of <var>end block</var> to
     <var>children</var>.
@@ -3719,6 +3803,9 @@
     node</span>, append <var>children</var>'s last member's [[nextsibling]] to
     <var>children</var>.
 
+    <li><span>Record the values</span> of <var>children</var>, and let
+    <var>values</var> be the result.
+
     <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>.
 
@@ -3744,9 +3831,21 @@
     node</span> and <var>start block</var>'s [[lastchild]] is a [[br]], remove
     <var>start block</var>'s [[lastchild]] from it.
 
-    <li>While the [[nextsibling]] of <var>reference node</var> is neither null
-    nor a [[br]] nor a <span>block node</span>, append the [[nextsibling]] of
-    <var>reference node</var> as the last [[child]] of <var>start block</var>,
+    <li>Let <var>nodes to move</var> be a list of [[nodes]], initially empty.
+
+    <li>If <var>reference node</var>'s [[nextsibling]] is neither null nor a
+    [[br]] nor a <span>block node</span>, append it to <var>nodes to
+    move</var>.
+
+    <li>While <var>nodes to move</var> is nonempty and its last member's
+    [[nextsibling]] is neither null nor a [[br]] nor a <span>block node</span>,
+    append it to <var>nodes to move</var>.
+
+    <li><span>Record the values</span> of <var>nodes to move</var>, and let
+    <var>values</var> be the result.
+
+    <li>For each <var>node</var> in <var>nodes to move</var>, append
+    <var>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
@@ -3765,6 +3864,9 @@
     and <var>start block</var>'s [[lastchild]] is a [[br]], remove <var>start
     block</var>'s [[lastchild]] from it.
 
+    <li><span>Record the values</span> of <var>end block</var>'s [[children]],
+    and let <var>values</var> be the result.
+
     <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>.
@@ -3774,6 +3876,8 @@
     <var>parent</var>, then set <var>end block</var> to <var>parent</var>.
   </ol>
 
+  <li><span>Restore the values</span> from <var>values</var>.
+
   <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>.
@@ -3785,13 +3889,6 @@
 <p>To <dfn>split the parent</dfn> of a list <var>node list</var> of consecutive
 [[sibling]] [[nodes]]:
 
-<p class=XXX>Pretty much any time we call this algorithm, it can cause trouble
-if the parent had styles, classes, etc.  There's not going to be any general
-way to handle this, but we should at least try to handle the special case of
-inline styles, because Firefox does actually add them to arbitrary elements.
-Also, when splitting out of an inline parent, it might be good to wrap all the
-inline descendants in a clone of the former parent.
-
 <ol>
   <li>Let <var>original parent</var> be the [[parent]] of the first member of
   <var>node list</var>.
@@ -4326,26 +4423,6 @@
   indentation element, as does the spec.
   -->
 
-  <div class=XXX>
-  <p>We don't handle a case like
-
-  <xmp><ol><ol style="color: red"><li>foo<li>bar</ol><li>baz</ol></xmp>
-
-  <p>If the inner [[ol]] is selected to be outdented, "foo" and "bar" will stop
-  being red.  It seems nontrivial to handle this case in general, since we
-  can't group [[li]]s.  If the list we're outdenting is a child of a non-list,
-  then we can just change it to a div.  Hopefully
-  <a href=http://www.w3.org/Bugs/Public/show_bug.cgi?id=13128>HTML bug
-  13128</a> will get fixed, in which case I can just use a div here too.
-  </div>
-  <!--
-  Chrome 12 dev seems to special-case style attributes by converting them to
-  the corresponding inline markup elements (at least in easy cases like color).
-  For other attributes and non-WebKit browsers (IE9/FF4/O11.10), it looks like
-  all the attributes are just removed.  Maybe we should try to copy WebKit
-  here.
-  -->
-
   <ol>
     <li>Unset the <code data-anolis-spec=html
     title=attr-ol-reversed>reversed</code>, <code data-anolis-spec=html
@@ -4357,8 +4434,23 @@
 
     <li>If <var>node</var> has attributes, and its [[parent]] is not an [[ol]]
     or [[ul]], <span>set the tag name</span> of <var>node</var> to "div".
-
-    <li>Otherwise remove <var>node</var>, <span>preserving its descendants</span>.
+    <!--
+    We can't turn it into a div if it's the child of an ol or ul, because
+    that's not currently allowed.  TODO V2: change this if
+    http://www.w3.org/Bugs/Public/show_bug.cgi?id=13128 is fixed and we can
+    make it a div.
+    -->
+
+    <li>Otherwise:
+
+    <ol>
+      <li><span>Record the values</span> of <var>node</var>'s [[children]], and
+      let <var>values</var> be the result.
+
+      <li>Remove <var>node</var>, <span>preserving its descendants</span>.
+
+      <li><span>Restore the values</span> from <var>values</var>.
+    </ol>
 
     <li><span>Fix disallowed ancestors</span> of each member of
     <var>children</var>.
@@ -4785,10 +4877,15 @@
     [[localname]] <var>tag name</var>, remove the first member from <var>node
     list</var> and append it to <var>sublist</var>.
 
+    <li><span>Record the values</span> of <var>sublist</var>, and let
+    <var>values</var> be the result.
+
     <li><span>Split the parent</span> of <var>sublist</var>.
 
     <li><span>Fix disallowed ancestors</span> of each member of
     <var>sublist</var>.
+
+    <li><span>Restore the values</span> from <var>values</var>.
   </ol>
 
   <li>Otherwise, while <var>node list</var> is not empty:
@@ -4809,10 +4906,9 @@
 
     <li>If <var>sublist</var> contains more than one member, <span>wrap</span>
     <var>sublist</var>, with <span>sibling criteria</span> matching nothing and
-    <span>new parent instructions</span> returning the result of calling <code
-    data-anolis-spec=domcore
-    title=dom-Document-createElement>createElement("li")</code> on the
-    [[contextobject]].  Let <var>node</var> be the result.
+    <span>new parent instructions</span> returning the result of calling
+    [[createelement|"li"]] on the [[contextobject]].  Let <var>node</var> be
+    the result.
 
     <li>Otherwise, let <var>node</var> be the sole member of
     <var>sublist</var>.
@@ -4823,16 +4919,18 @@
     <ol>
       <li>Let <var>children</var> be the [[children]] of <var>node</var>.
 
+      <li><span>Record the values</span> of <var>children</var>, and let
+      <var>values</var> be the result.
+
       <li>Remove <var>node</var>, <span>preserving its descendants</span>.
 
       <li><span>Wrap</span> <var>children</var>, with <span>sibling
       criteria</span> matching any <span>HTML element</span> with [[localname]]
-      <var>tag name</var> and <span>new parent
-      instructions</span> returning the result of calling <code
-      data-anolis-spec=domcore
-      title=dom-Document-createElement>createElement(<var>tag
-      name</var>)</code> on the [[contextobject]].  Let <var>node</var> be the
-      result.
+      <var>tag name</var> and <span>new parent instructions</span> returning
+      the result of calling [[createelement|<var>tag name</var>]] on the
+      [[contextobject]].  Let <var>node</var> be the result.
+
+      <li><span>Restore the values</span> from <var>values</var>.
 
       <li>Prepend the [[descendants]] of <var>node</var> that are <span
       title="HTML element">HTML elements</span> with [[localname]] <var>other
@@ -4875,6 +4973,9 @@
     with [[localname]] <var>other tag name</var>:
 
     <ol>
+      <li><span>Record the values</span> of the one-[[node]] list consisting of
+      <var>node</var>, and let <var>values</var> be the result.
+
       <li><span>Split the parent</span> of the one-[[node]] list consisting of
       <var>node</var>.
 
@@ -4882,9 +4983,9 @@
       <var>node</var>, with <span>sibling criteria</span> matching any
       <span>HTML element</span> with [[localname]] <var>tag name</var>,
       and with <span>new parent instructions</span> returning the result of
-      calling <code data-anolis-spec=domcore
-      title=dom-Document-createElement>createElement(<var>tag
-      name</var>)</code> on the [[contextobject]].
+      calling [[createelement|<var>tag name</var>]] on the [[contextobject]].
+
+      <li><span>Restore the values</span> from <var>values</var>.
 
       <li>Prepend the [[descendants]] of <var>node</var> that are <span
       title="HTML element">HTML elements</span> with [[localname]] <var>other
@@ -5159,9 +5260,14 @@
     <li><span>Normalize sublists</span> of each <var>item</var> in
     <var>items</var>.
 
+    <li><span>Record the values</span> of the one-[[node]] list consisting of
+    <var>node</var>, and let <var>values</var> be the result.
+
     <li><span>Split the parent</span> of the one-[[node]] list consisting of
     <var>node</var>.
 
+    <li><span>Restore the values</span> from <var>values</var>.
+
     <li>If <var>node</var> is a [[dd]] or [[dt]], and it is not an
     <span>allowed child</span> of any of its [[ancestors]] <span>in the same
     editing host</span>, <span>set the tag name</span> of <var>node</var> to
@@ -5437,6 +5543,9 @@
   of "p" or a [[dd]] or [[dt]], and <var>node</var> is not the [[ancestor]] of
   a <span>prohibited paragraph child</span>.
 
+  <li><span>Record the values</span> of <var>node list</var>, and let
+  <var>values</var> be the result.
+
   <li>For each <var>node</var> in <var>node list</var>, while <var>node</var>
   is the [[descendant]] of an <span>editable</span> <span>HTML element</span>
   <span>in the same editing host</span>, which has [[localname]] "address",
@@ -5457,6 +5566,8 @@
   out of lists or tables or such if they happen to be nested in a <div>.
   -->
 
+  <li><span>Restore the values</span> from <var>values</var>.
+
   <!--
   We have two different behaviors, one for div and p and one for everything
   else.  The basic difference is that for div and p, we assume that it should
@@ -5517,9 +5628,14 @@
       <li>Let <var>sublist</var> be the [[children]] of the first member of
       <var>node list</var>.
 
+      <li><span>Record the values</span> of <var>sublist</var>, and let
+      <var>values</var> be the result.
+
       <li>Remove the first member of <var>node list</var> from its [[parent]],
       <span>preserving its descendants</span>.
 
+      <li><span>Restore the values</span> from <var>values</var>.
+
       <li>Remove the first member from <var>node list</var>.
     </ol>
 
@@ -6791,37 +6907,33 @@
 
   <li>Let <var>node list</var> be a list of [[nodes]], initially empty.
 
-  <li>For each [[node]] <var>node</var> [[contained]] in <var>new range</var>:
-
-  <ol>
-    <li>If the last member of <var>node list</var> (if any) is an [[ancestor]]
-    of <var>node</var>, or if <var>node</var> is not <span>editable</span>,
-    continue with the next <var>node</var>.
-
-    <!--
-    This step is kind of weird.  For regular outdenting, we start at the inside
-    and outdent going out, so that we remove the innermost indentation, on the
-    theory that that will produce the cleanest markup (remove the most nodes).
-    For lists, we remove the outermost indentation, because it makes a
-    difference whether we remove inner or outer indentation, and logically we
-    want to remove outer.  E.g.,
-
-      <ol><li>foo</li><ul><li>bar</li></ul></ol>
-
-    should become
-
-      foo<ul><li>bar</li></ul>
-
-    not
-
-      foo<ol><li>bar</li></ol>.
-
-    But this is a bit weird and I'm wondering if it's really correct.
-    -->
-    <li>If <var>node</var> has no <span>editable</span> [[descendants]], or is
-    an [[ol]] or [[ul]], or is an [[li]] whose [[parent]] is an [[ol]] or
-    [[ul]], append it to <var>node list</var>.
-  </ol>
+  <li>For each [[node]] <var>node</var> [[contained]] in <var>new range</var>,
+  append <var>node</var> to <var>node list</var> if the last member of
+  <var>node list</var> (if any) is not an [[ancestor]] of <var>node</var>;
+  <var>node</var> is <span>editable</span>; and either <var>node</var> has no
+  <span>editable</span> [[descendants]], or is an [[ol]] or [[ul]], or is an
+  [[li]] whose [[parent]] is an [[ol]] or [[ul]].
+  <!--
+  This step is kind of weird.  For regular outdenting, we start at the inside
+  and outdent going out, so that we remove the innermost indentation, on the
+  theory that that will produce the cleanest markup (remove the most nodes).
+  For lists, we remove the outermost indentation, because it makes a difference
+  whether we remove inner or outer indentation, and logically we want to remove
+  outer.  E.g.,
+
+    <ol><li>foo</li><ul><li>bar</li></ul></ol>
+
+  should become
+
+    foo<ul><li>bar</li></ul>
+
+  not
+
+    foo<ol><li>bar</li></ol>.
+
+  But this is a bit weird and I'm wondering if it's really correct.  TODO V2:
+  Reexamine this.
+  -->
 
   <li>While <var>node list</var> is not empty:
 
@@ -6842,10 +6954,15 @@
     list</var> is not an [[ol]] or [[ul]], remove the first member of <var>node
     list</var> and append it to <var>sublist</var>.
 
+    <li><span>Record the values</span> of <var>sublist</var>, and let
+    <var>values</var> be the result.
+
     <li><span>Split the parent</span> of <var>sublist</var>.
 
     <li><span>Fix disallowed ancestors</span> of each member of
     <var>sublist</var>.
+
+    <li><span>Restore the values</span> from <var>values</var>.
   </ol>
 </ol>
 <!-- @} -->
--- a/tests.js	Mon Jul 11 13:58:51 2011 -0600
+++ b/tests.js	Tue Jul 12 11:28:30 2011 -0600
@@ -433,6 +433,37 @@
 		'<div><div><p>foo</p></div></div><div><div><!--abc--><div>[]bar</div></div></div>',
 		'<div><div><p>foo</p></div></div><div><div><div><!--abc-->[]bar</div></div></div>',
 
+		// Styled stuff with collapsed selection
+		'<p style=color:red>foo<p>[]bar',
+		'<p style=color:red>foo<p style=color:blue>[]bar',
+		'<p>foo<p style=color:blue>[]bar',
+		'<p><font color=red>foo</font><p>[]bar',
+		'<p><font color=red>foo</font><p><font color=blue>[]bar</font>',
+		'<p>foo<p><font color=blue>[]bar</font>',
+		'<p><span style=color:red>foo</font><p>[]bar',
+		'<p><span style=color:red>foo</font><p><span style=color:blue>[]bar</font>',
+		'<p>foo<p><span style=color:blue>[]bar</font>',
+
+		'<p style=background-color:salmon>foo<p>[]bar',
+		'<p style=background-color:salmon>foo<p style=background-color:aqua>[]bar',
+		'<p>foo<p style=background-color:aqua>[]bar',
+		'<p><span style=background-color:salmon>foo</font><p>[]bar',
+		'<p><span style=background-color:salmon>foo</font><p><span style=background-color:aqua>[]bar</font>',
+		'<p>foo<p><span style=background-color:aqua>[]bar</font>',
+
+		'<p style=text-decoration:underline>foo<p>[]bar',
+		'<p style=text-decoration:underline>foo<p style=text-decoration:line-through>[]bar',
+		'<p>foo<p style=text-decoration:line-through>[]bar',
+		'<p><u>foo</u><p>[]bar',
+		'<p><u>foo</u><p><s>[]bar</s>',
+		'<p>foo<p><s>[]bar</s>',
+
+		'<p style=color:red>foo</p>[]bar',
+		'foo<p style=color:blue>[]bar',
+		'<div style=color:red><p style=color:green>foo</div>[]bar',
+		'<div style=color:red><p style=color:green>foo</div><p style=color:blue>[]bar',
+		'<p style=color:red>foo<div style=color:blue><p style=color:green>[]bar',
+
 		// Uncollapsed selection
 		'foo[bar]baz',
 
@@ -1043,6 +1074,8 @@
 		'<p>[foo<h1>bar]</h1>',
 		'<h1>[foo</h1><h2>bar]</h2>',
 		'<div>[foo</div>bar]',
+
+		['<p>', '<div style=color:red>[foo]</div>'],
 	],
 	//@}
 	forwarddelete: [
@@ -1198,6 +1231,37 @@
 		'<div><div><p>foo[]</p></div></div><div><div><!--abc--><div>bar</div></div></div>',
 		'<div><div><p>foo[]</p></div></div><div><div><div><!--abc-->bar</div></div></div>',
 
+		// Styled stuff with collapsed selection
+		'<p style=color:red>foo[]<p>bar',
+		'<p style=color:red>foo[]<p style=color:blue>bar',
+		'<p>foo[]<p style=color:blue>bar',
+		'<p><font color=red>foo[]</font><p>bar',
+		'<p><font color=red>foo[]</font><p><font color=blue>bar</font>',
+		'<p>foo[]<p><font color=blue>bar</font>',
+		'<p><span style=color:red>foo[]</font><p>bar',
+		'<p><span style=color:red>foo[]</font><p><span style=color:blue>bar</font>',
+		'<p>foo[]<p><span style=color:blue>bar</font>',
+
+		'<p style=background-color:salmon>foo[]<p>bar',
+		'<p style=background-color:salmon>foo[]<p style=background-color:aqua>bar',
+		'<p>foo[]<p style=background-color:aqua>bar',
+		'<p><span style=background-color:salmon>foo[]</font><p>bar',
+		'<p><span style=background-color:salmon>foo[]</font><p><span style=background-color:aqua>bar</font>',
+		'<p>foo[]<p><span style=background-color:aqua>bar</font>',
+
+		'<p style=text-decoration:underline>foo[]<p>bar',
+		'<p style=text-decoration:underline>foo[]<p style=text-decoration:line-through>bar',
+		'<p>foo[]<p style=text-decoration:line-through>bar',
+		'<p><u>foo[]</u><p>bar',
+		'<p><u>foo[]</u><p><s>bar</s>',
+		'<p>foo[]<p><s>bar</s>',
+
+		'<p style=color:red>foo[]</p>bar',
+		'foo[]<p style=color:blue>bar',
+		'<div style=color:red><p style=color:green>foo[]</div>bar',
+		'<div style=color:red><p style=color:green>foo[]</div><p style=color:blue>bar',
+		'<p style=color:red>foo[]<div style=color:blue><p style=color:green>bar',
+
 		// Uncollapsed selection (should be same as delete command)
 		'foo[bar]baz',
 
@@ -1639,6 +1703,10 @@
 
 		['<nobr>abc</nobr>', '<nobr>f[o]o</nobr>'],
 		['<nobr>abc</nobr>', 'f[o]o'],
+
+		['<p>abc', '<font color=red>foo[]bar</font>'],
+		['<p>abc', '<span style=color:red>foo[]bar</span>'],
+		['<p>abc', '<span style=font-variant:small-caps>foo[]bar</span>'],
 	],
 	//@}
 	insertimage: [