Another daily commit, lots more changes
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Wed, 23 Feb 2011 15:15:21 -0700
changeset 8 641c267313c3
parent 7 c07184a3d893
child 9 9223dc704133
Another daily commit, lots more changes

More fiddling with the styling algorithm; added foreColor and backColor.
Still all very preliminary. Something seems to have broken
implementation.html in Firefox and Opera, and I'm not sure why.
editcommands.html
implementation.html
preprocess
source.html
xrefs.json
--- a/editcommands.html	Tue Feb 22 15:15:44 2011 -0700
+++ b/editcommands.html	Wed Feb 23 15:15:21 2011 -0700
@@ -19,7 +19,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-22-february-2011>Work in Progress &mdash; Last Update 22 February 2011</h2>
+<h2 class="no-num no-toc" id=work-in-progress-&mdash;-last-update-23-february-2011>Work in Progress &mdash; Last Update 23 February 2011</h2>
 <dl>
  <dt>Editor
  <dd>Aryeh Gregor &lt;ayg+spec@aryeh.name&gt;
@@ -62,9 +62,27 @@
 
 <h2 id=issues><span class=secno>2 </span>Issues</h2>
 
+<p>I'm not sure if my priorities in writing the algorithms here are correct.
+My goals were 1) make the algorithms as simple as possible, and 2) minimize
+surprising user-visible behavior (e.g., "I clicked B but it didn't turn
+bold!").  I didn't try to optimize the niceness of the resulting DOM at all, so
+for instance, when bolding <code title="">abc &lt;i&gt;def&lt;/i&gt; &lt;br&gt; ghi</code>
+you get <code title="">&lt;b&gt;abc &lt;/b&gt;&lt;i style="font-weight:
+bold"&gt;def&lt;/i&gt;&lt;b&gt; &lt;/b&gt;&lt;br style="font-weight: bold"&gt;&lt;b&gt;
+ghi&lt;/b&gt;</code> instead of wrapping the whole thing in a single <code title="">&lt;b&gt;</code>.  This is to avoid making the algorithm understand content
+models, but maybe it's worth revisiting later.  Likewise, unbolding the middle
+word of <code title="">&lt;b&gt;Foo bar baz&lt;/b&gt;</code> produces <code title="">&lt;b&gt;Foo &lt;span style="font-weight: normal"&gt;bar&lt;/span&gt;
+baz&lt;/b&gt;</code> instead of the simpler <code title="">&lt;b&gt;Foo &lt;/b&gt;bar&lt;b&gt;
+baz&lt;/b&gt;</code>.  For now, the algorithm works, even if it produces messy
+DOMs.
+
+<p>Other issues:
+
 <ul>
   <li><p>Need to make CSS terminology more precise, about setting/unsetting CSS
   properties.  The intent is to modify the style attribute, CSSOM-style.
+  Likewise, CSS value comparisons need to be done after serializing both
+  values, so "bold" == "700" and "red" == "#f00" and so on.
 
   <li><p>Also not sure about computed style.  There are differences between
   "computed" and "used" and things like that, what do we actually want here?
@@ -253,8 +271,6 @@
   parent has, so the list of children to unstyle needs to be computed
   beforehand.
 
-  <li><p>Let <var title="">children</var> be an empty list of <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a>s.
-
   <li><p>If either
 
   <ul>
@@ -271,6 +287,8 @@
   <p>then:
 
   <ol>
+    <li><p>Let <var title="">children</var> be an empty list of <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a>s.
+
     <li><p>While <var title="">element</var> has children:
 
     <ol>
@@ -283,65 +301,35 @@
 
     <li><p>Remove <var title="">element</var>.
 
-    <li><p>Return <var title="">children</var> and abort this algorithm.
+    <li><p>Return <var title="">children</var>.
   </ol>
 
   <li><p>Unset the CSS property <var title="">property name</var> of <var title="">element</var>.
 
-  <li><p>If <var title="">element</var> is an <a href=#html-element>HTML element</a> with
-  <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> in <var title="">tag list</var>:
-
-  <ol>
-    <li><p>Let <var title="">new element</var> be a new <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> "span", with the same attributes and <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument><code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code></a>
-    as <var title="">element</var>.
-
-    <li><p>Append <var title="">new element</var> to <var title="">element</var>'s
-    parent as the previous sibling of <var title="">element</var>.
-
-    <li><p>While <var title="">element</var> has children:
+  <li><p>If <var title="">element</var> is not an <a href=#html-element>HTML element</a> or its
+  <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> is not in <var title="">tag list</var>, return the empty list.
 
-    <ol>
-      <li><p>Let <var title="">child</var> be the first child of <var title="">element</var>.
-
-      <li><p>Append <var title="">child</var> to <var title="">children</var>.
+  <li><p>Let <var title="">new element</var> be a new <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> "span", with the same attributes and <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument><code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code></a>
+  as <var title="">element</var>.
 
-      <li><p>Append <var title="">child</var> as the last child of <var title="">new
-      element</var>.
-    </ol>
+  <li><p>Append <var title="">new element</var> to <var title="">element</var>'s
+  parent as the previous sibling of <var title="">element</var>.
 
-    <li><p>Remove <var title="">element</var>.
-  </ol>
+  <li><p>While <var title="">element</var> has children, append its first child
+  as the last child of <var title="">new element</var>.
 
-  <li><p>Return <var title="">children</var>.
+  <li><p>Remove <var title="">element</var>.
+
+  <li><p>Return the one-<a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> list consisting of <var title="">new element</var>.
 </ol>
 
 
 <h2 id=styling-a-range><span class=secno>6 </span>Styling a Range</h2>
-<p>When a user agent is to <dfn id=style-a-range>style a <code class=external data-anolis-spec=domrange>Range</code></dfn> <var title="">range</var>, it must
-run the following steps.  There are four inputs: a CSS property name <var title="">property name</var>, a new value <var title="">property value</var>, a
-nonempty list of strings <var title="">tag list</var>, and a string
-<var title="">commandId</var>.
-
-<div class=XXX>
-<p>This description tries to keep the algorithm as simple as possible at the
-expense of producing much more complicated markup in some cases than a slightly
-more sophisticated algorithm.  It's similar to what Gecko does in spirit,
-although the details differ.  The basic cause of most of the ugly markup is
-that we don't bake in any element nesting restrictions, so <code title="">&lt;em&gt;Abc&lt;/em&gt; &lt;em&gt;def&lt;/em&gt;</code> becomes <code title="">&lt;em
-style=font-weight:bold&gt;Abc&lt;/em&gt;&lt;b&gt; &lt;/b&gt;&lt;em
-style=font-weight:bold&gt;def&lt;/em&gt;</code> instead of just <code title="">&lt;b&gt;&lt;em&gt;Abc&lt;/em&gt; &lt;em&gt;def&lt;/em&gt;&lt;/b&gt;</code>.  Otherwise
-we'd have to add content model checks.  Maybe this is worth it?  IE and WebKit
-seem to do it, Gecko and Opera seem not to (they only wrap text nodes AFAICT).
-
-<p>I did add some complication by saying conflicting inline style needs to be
-removed.  This is what WebKit does.  Gecko and IE9 leave it alone, which leads
-to stuff like <code title="">&lt;b&gt;&lt;span
-style=font-weight:100&gt;Foo&lt;/span&gt;&lt;/b&gt;</code> that doesn't actually work.
-Opera avoids the problem by producing code like <code title="">&lt;span
-style=font-weight:100&gt;&lt;b&gt;Foo&lt;/b&gt;&lt;/span&gt;</code>, but that doesn't work
-with the CSS-based strategy.
-</div>
+<p>When a user agent is to <dfn id=style-a-range>style a <code class=external data-anolis-spec=domrange>Range</code></dfn> <var title="">range</var>, it
+must run the following steps.  There are three inputs: a CSS property name
+<var title="">property name</var>, a new value <var title="">property value</var>, and a possibly
+empty list of strings <var title="">tag list</var>.
 
 <ol>
   <li><p>Let <var title="">node list</var> be the result of <a href=#decompose-a-range title="decompose
@@ -353,19 +341,16 @@
     <li><p>If <var title="">node</var> is an <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a>:
 
     <ol>
-      <li><p>If <var title="">node</var> is an <a href=#html-element>HTML element</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a>
-      in <var title="">tag list</var>, unset the CSS property <var title="">property
-      name</var> of <var title="">node</var>.  Otherwise, set the CSS property
-      <var title="">property name</var> of <var title="">node</var> to <var title="">property value</var>.
+      <li><p>Unset the CSS property <var title="">property name</var> of <var title="">node</var>.
 
-      <li><p>If the <a href=#state>state</a> of <var title="">commandId</var> on
-      <var title="">node</var> is false, set the CSS property <var title="">property name</var>
-      of <var title="">node</var> to <var title="">property value</var>.
-      <!-- This fixes cases where the element won't actually create the desired
-      style.  E.g., <span style=font-weight:lighter><b>Foo</b></span> will not
-      actually bold "Foo" (because the default style for <b> is font-weight:
-      bolder), so we do <b style=font-weight:bold> instead.  Likewise when we
-      do similarly a bit later on in this algorithm. -->
+      <li><p>If the computed value of <var title="">property name</var> for
+      <var title="">node</var> is not <var title="">property value</var>, set the CSS property
+      <var title="">property name</var> of <var title="">node</var> to <var title="">property value</var>.
+      <!-- This means we don't bother applying the property if the style is
+      already present, e.g., from an ancestor.  But we do apply it if the
+      element is the expected sort of element but the style is wrong anyway,
+      e.g., <span style=font-weight:100><b>Foo</b></span> where b's style is
+      font-weight: bold. -->
 
       <li><p>Let <var title="">element children</var> be the <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a> children
       of <var title="">node</var>.
@@ -381,28 +366,22 @@
     <li><p>Otherwise, if <var title="">node</var> is a <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text><code class=external data-anolis-spec=domcore>Text</code></a> node:
 
     <ol>
-      <!-- This next step is not strictly necessary from a user point of view,
-      since there's no visible difference.  We could just always insert a new
-      element.  But that can drastically complicate the DOM if there are lots
-      of text nodes, which happens easily when the algorithms here are invoked
-      a lot: you get splitText() called when decomposing ranges, and unstyling
-      elements can also dump their text node children into the DOM next to
-      other text nodes. -->
-      <li><p>If the previous sibling of <var title="">node</var> is an <a href=#html-element>HTML
-      element</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-element-local-name title=concept-element-local-name>local name</a> in <var title="">tag list</var> with no
-      attributes, and the <a href=#state>state</a> of <var title="">commandId</var> on
-      <var title="">node</var> is true, let <var title="">new parent</var> equal the previous
-      sibling of <var title="">node</var>.
+      <li><p>Let <var title="">new parent</var> be a new <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 first string in <var title="">tag list</var> (or equal
+      to "span" if <var title="">tag list</var> is empty), with no attributes, and
+      <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument><code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code></a> the same as <var title="">node</var>.
 
-      <li><p>Otherwise, let <var title="">new parent</var> be a new <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 first string in <var title="">tag
-      list</var>, with no attributes, and <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument><code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code></a> the same as
-      <var title="">node</var>.  Append <var title="">new parent</var> to <var title="">node</var>'s
-      parent as the previous sibling of <var title="">node</var>.
+      <li><p>Append <var title="">new parent</var> to <var title="">node</var>'s parent as the
+      previous sibling of <var title="">node</var>.
 
-      <li><p>If the <a href=#state>state</a> of <var title="">commandId</var> on <var title="">new
-      parent</var> is false, set the CSS property <var title="">property name</var> of
-      <var title="">new parent</var> to <var title="">property value</var>.
+      <li><p>If the computed value of <var title="">property name</var> on <var title="">new
+      parent</var> is not equal to <var title="">property value</var>, set the CSS
+      property <var title="">property name</var> of <var title="">new parent</var> to
+      <var title="">property value</var>.
+      <!-- This is needed if tag list is empty, but also if the correct style
+      is being suppressed for some reason, like <span
+      style=font-weight:100><b>Foo</b></span> where b is font-weight: bolder.
+      -->
 
       <li><p>Append <var title="">node</var> to <var title="">new parent</var> as
       its last child.
@@ -411,7 +390,6 @@
     <li><p>Otherwise, do nothing.
   </ol>
 </ol>
-
 <!-- Out of IE9, Gecko, WebKit, and Opera, when asked to (e.g.) bold an
 element, IE9 and WebKit and Opera wrap various descendants in <b> or <strong>;
 Gecko just adds a style attribute.  The latter is simpler, particularly because
@@ -422,10 +400,10 @@
 
 
 <h2 id=unstyling-a-range><span class=secno>7 </span>Unstyling a Range</h2>
-<p>When a user agent is to <dfn id=unstyle-a-range>unstyle a <code class=external data-anolis-spec=domrange>Range</code></dfn> <var title="">range</var>, it must
-run the following steps.  There are four inputs: a CSS property name <var title="">property name</var>, a new value <var title="">property value</var>, a
-possibly empty list of strings <var title="">tag list</var>, and a string
-<var title="">commandId</var>.
+<p>When a user agent is to <dfn id=unstyle-a-range>unstyle a <code class=external data-anolis-spec=domrange>Range</code></dfn> <var title="">range</var>, it
+must run the following steps.  There are three inputs: a CSS property name
+<var title="">property name</var>, a new value <var title="">property value</var>, and a possibly
+empty list of strings <var title="">tag list</var>.
 
 <ol>
   <li><p>Let <var title="">node list</var> be the result of <a href=#decompose-a-range title="decompose
@@ -438,18 +416,18 @@
     <li><p>If <var title="">node</var> is an <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a>:
 
     <ol>
-      <li><p>Let <var title="">children</var> be the result of <a href=#unstyle-an-element title="unstyle an element">unstyling</a> <var title="">node</var>.
+      <li><p>Let <var title="">extra nodes</var> be the result of <a href=#unstyle-an-element title="unstyle an element">unstyling</a> <var title="">node</var>.
 
       <li><p>If <var title="">node</var> no longer has a parent:
 
       <ol>
-        <li><p>Insert all the <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a>s in <var title="">children</var> into <var title="">node list</var> immediately after <var title="">node</var>, in
-        order.
+        <li><p>Insert all the <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a>s in <var title="">extra nodes</var> into
+        <var title="">node list</var> immediately after <var title="">node</var>, in order.
 
-        <li><p>Continue with the next <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> in <var title="">node list</var>,
-        if any.
+        <li><p>Continue with the next <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> in <var title="">node list</var>, if any.
 
-        <p class=note>The next <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> will be the first former child of <var title="">node</var>, if <var title="">node</var> had children.
+        <p class=note>The next <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> will be the first in <var title="">extra
+        nodes</var>, unless <var title="">extra nodes</var> is empty.
       </ol>
 
       <li><p>If the computed value of <var title="">property name</var> for <var title="">node</var> is not <var title="">property value</var>, set the CSS
@@ -505,22 +483,78 @@
 
 <p>The <dfn id=querycommandstate() title=queryCommandState()><code>queryCommandState(<var title="">commandId</var>)</code></dfn>
 method on the <a href=http://www.whatwg.org/html/#htmldocument><code class=external data-anolis-spec=html>HTMLDocument</code></a> interface must
-return the <a href=#state>state</a> of <var title="">commandId</var> on the
-<a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>'s <a href=#active-range>active range</a>.  If there is no <a href=#active-range>active
-range</a>, return false.
+return the state of <var title="">commandId</var> on the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>'s
+<a href=#active-range>active range</a>, as given by the list below.  If there is no
+<a href=#active-range>active range</a>, or if <var title="">commandId</var> is not on the list,
+return false.
 <!-- Gecko throws an exception if there are no ranges in the selection, but
 other engines seem to just return false, which seems like nicer behavior
-anyway. -->
+anyway.
 
-<p>The <dfn id=state>state</dfn> of a command <var title="">commandId</var> on a <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> is
-given by the list below.  If <var title="">commandId</var> is not on the list, its
-<a href=#state>state</a> is false on any <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>.  The <a href=#state>state</a> of a
-command on a <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> <var title="">node</var> is the <a href=#state>state</a> of that command
-on the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> with <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> (<var title="">node</var>, 0), <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a>
-(<var title="">node</var>, <var title="">node</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a>), and <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-root title=concept-range-root>root</a> equal to
-<var title="">node</var>'s <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-ownerdocument><code class=external data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code></a>.
+Requesting the state of an unknown command throws an exception in IE 9 RC
+and Firefox 4b11, and returns boolean false in Chrome 10 and Opera 11. -->
+
+<p>The <dfn id=querycommandvalue() title=queryCommandValue()><code>queryCommandValue(<var title="">commandId</var>)</code></dfn>
+method on the <a href=http://www.whatwg.org/html/#htmldocument><code class=external data-anolis-spec=html>HTMLDocument</code></a> interface must
+return the value of <var title="">commandId</var> on the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#context-object>context object</a>'s
+<a href=#active-range>active range</a>, as given by the list below.  If there is no
+<a href=#active-range>active range</a>, or if <var title="">commandId</var> is not on the list,
+return the empty string.
+<!-- Requesting the value of an unknown command throws an exception in
+IE 9 RC and in Firefox 4b11.  It returns boolean false in Chrome 10, and the
+empty string in Opera 11. -->
+
+<p class=XXX>Querying the value or state of an unrecognized command throws an
+exception in IE and Firefox.  Need to consider changing to that behavior.
+
+<p>The possible values for <var title="">commandId</var>, and their corresponding
+meanings, are as follows.  These values must be compared to the argument in an
+<a class=external data-anolis-spec=html href=http://www.whatwg.org/html/#ascii-case-insensitive>ASCII case-insensitive</a> manner.
 
 <dl>
+<dt><code title=""><dfn id=command-backcolor title=command-backColor>backColor</dfn></code>
+
+<dd><p><strong>Action</strong>: If <var title="">value</var> is not a valid CSS color,
+the user agent must do nothing and abort these steps.  Otherwise, it must <a href=#style-a-range title="style a range">style the <code class=external data-anolis-spec=domrange>Range</code></a> with <var title="">property name</var>
+equal to "background-color", <var title="">property value</var> equal to
+<var title="">value</var>, and <var title="">tag list</var> equal to the empty list.
+<!-- Firefox documentation says it normally sets the background color of the
+document, but I can't get it to work at all in brief testing in 4b11.  (It says
+it behaves differently in styleWithCss mode.)  Opera 11 appears to set the
+background color of the nearest block container of the cursor, or something.
+IE 9 RC and Chrome 10 behave as I'd expect, namely, they set the background of
+the selection, just like all the other styling features.  I go with IE/WebKit.
+
+Invalid colors are probably the same craziness as with foreColor.
+
+Chrome 10 dev actually sets background, not background-color, so it resets all
+the other background stuff.  I go with IE 9 RC and Opera 11, which only set
+background-color. -->
+
+<dd><p><strong>State</strong>: Always false.
+
+<dd><p><strong>Value</strong>: The value is given by the following algorithm:
+
+<ol>
+  <li><p>Let <var title="">element</var> be the <a href=#beginning-element>beginning element</a> of the
+  <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>.
+
+  <li><p>While the computed style of "background-color" on <var title="">element</var>
+  is any fully transparent value, set <var title="">element</var> to its parent.
+
+  <p class=XXX>It's intended that for these purposes, the root element will
+  have a white background.  Should that be specified somewhere?  I don't think
+  all UAs actually do it.
+
+  <li><p>Return the computed style of "background-color" for
+  <var title="">element</var>.
+</ol>
+<!-- Chrome 10 returns rgba(0, 0, 0, 0) if there's no background defined
+anywhere.  Opera 11 returns rgb(255, 255, 255) as I'd like.  Firefox 4b11 just
+throws an exception for some reason.  IE 9 RC seems to return the number 0
+across the board, as with foreColor. -->
+
+
 <dt><code title=""><dfn id=command-bold title=command-bold>bold</dfn></code>
 
 <dd><p><strong>Action</strong>: If the state of the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> for this command
@@ -534,13 +568,57 @@
 <dd><p><strong>State</strong>: True if the <a href=#beginning-element>beginning element</a> of the
 <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> has font-weight with computed value less than 700, otherwise false.
 
+<dd><p><strong>Value</strong>: Always the empty string.
+<!-- We have lots of options here (and presumably for all the others where
+value is meaningless).  IE 9 RC returns the boolean false, Firefox 4b11 and
+Opera 11 both return the empty string, Chrome 10 returns the string "false".
+The HTML5 spec as of February 2011 mandates WebKit's behavior.  It makes sense
+to always return a string, a majority of string-returners return the empty
+string, and three out of the four return something that evaluates to false as a
+boolean, so I'll go with Firefox and Opera. -->
+
+
+<dt><code title=""><dfn id=command-forecolor title=command-forecolor>foreColor</dfn></code>
+
+<dd><p><strong>Action</strong>: If <var title="">value</var> is not a valid CSS color,
+the user agent must do nothing and abort these steps.
+<!-- Browsers are all over the place here.  IE 9 RC seems to treat unrecognized
+colors as black, but everyone else ignores them.  There are also special rules
+for certain things that aren't valid CSS colors, in some browsers, like:
+
+IE 9 RC: "ffe" -> "#ffe", "123" -> "#123"
+Firefox 4b11: "ffe" -> no style, "123" -> no style
+Chrome 10 dev: "ffe" -> "#FFFFEE", "123" -> "#000123"
+Opera 11: "ffe" -> "#0f0f0e", "123" -> "#010203"
+
+Firefox seems to stick to just CSS colors, so with any luck that works.
+"limegreen" works as expected in all browsers.  rgb(255, 255, 255) does too,
+except in Opera, which tries to parse it in some crazy way and winds up with
+"#00b025".  rgba() colors don't work as uniformly, but I don't see any reason
+to prohibit them.  Best to just match CSS. -->
+Otherwise, it must <a href=#style-a-range title="style a range">style the <code class=external data-anolis-spec=domrange>Range</code></a> with
+<var title="">property name</var> equal to "color", <var title="">property value</var> equal to
+<var title="">value</var>, and <var title="">tag list</var> equal to the empty list.
+
+<dd><p><strong>State</strong>: Always false.
+<!-- This matches IE 9 RC and Chrome 10.  Opera 11 seems to return true if
+there's some color style applied, false otherwise, which seems fairly useless;
+authors want to use value here, not state.  Firefox 4b11 throws an exception,
+which is an interesting approach, but I'll go with IE/WebKit, which makes at
+least as much sense. -->
+
+<dd><p><strong>Value</strong>: The computed value of the CSS property "color"
+for the <a href=#beginning-element>beginning element</a> of the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>.
+<!-- IE 9 RC returns the number 0 always, which makes no sense at all.  This
+matches the other browsers. -->
+
 
 <dt><code title=""><dfn id=command-italic title=command-italic>italic</dfn></code>
 
 <dd><p><strong>Action</strong>: If the of the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> for this command is
 false, the user agent must <a href=#style-a-range title="style a range">style the
 <code class=external data-anolis-spec=domrange>Range</code></a> with <var title="">property name</var> "font-style", <var title="">property
-value</var> "italic", and <var title="">tag list</var> ["i", "em"].  Otherwise, it must
+value</var> "italic", <var title="">tag list</var> ["i", "em"].  Otherwise, it must
 <a href=#unstyle-a-range title="unstyle a range">unstyle it</a> with <var title="">property name</var>
 "font-style", <var title="">property value</var> "normal", and <var title="">tag list</var> ["i",
 "em"].
@@ -549,6 +627,9 @@
 <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> has font-style with computed value "italic" or "oblique", otherwise
 false.
 
+<dd><p><strong>Value</strong>: Always the empty string.
+<!-- See comment for bold -->
+
 
 <dt><code title=""><dfn id=command-underline title=command-underline>underline</dfn></code>
 
@@ -558,8 +639,8 @@
 
 <dd class=XXX><p><strong>State</strong>: ...
 
-<p class=XXX>This is wrong, it will clear strikethrough and overline.  Also,
-computed style isn't useful, because text-decoration doesn't inherit.
+<dd><p><strong>Value</strong>: Always the empty string.
+<!-- See comment for bold -->
 </dl>
 
 
--- a/implementation.html	Tue Feb 22 15:15:44 2011 -0700
+++ b/implementation.html	Wed Feb 23 15:15:21 2011 -0700
@@ -1,18 +1,20 @@
 <!doctype html>
 <title>execCommand() experimental implementation</title>
+<p><label>Command: <input oninput="command = this.value"></label>
+   <label>Value: <input oninput="val = this.value"></label>
+<script>
+// Silly Firefox, prefilling inputs when you reload
+var command = document.querySelector("input").value;
+var val = document.querySelectorAll("input")[1].value;
+</script>
 <p>Spec:
-<button onclick="myExecCommand('bold')"><b>B</b></button>
-<button onclick="myExecCommand('italic')"><i>I</i></button>
-<button onclick="alert(myQueryCommandState('bold'))">B?</button>
-<button onclick="alert(myQueryCommandState('italic'))">I?</button>
-
-<p>Actual execCommand():
-<button onclick="execCommand('bold', false, null)"><b>B</b></button>
-<button onclick="execCommand('italic', false, null)"><i>I</i></button>
-<button onclick="execCommand('underline', false, null)"><u>U</u></button>
-<button onclick="alert(queryCommandState('bold'))">B?</button>
-<button onclick="alert(queryCommandState('italic'))">I?</button>
-<button onclick="alert(queryCommandState('underline'))">U?</button>
+<button onclick='myExecCommand(command, null, val)'>exec</button>
+<button onclick='var ret = myQueryCommandState(command); alert(typeof ret + " \"" + ret + "\"")'>state</button>
+<button onclick='var ret = myQueryCommandValue(command); alert(typeof ret + " \"" + ret + "\"")'>value</button>
+<p>Actual:
+<button onclick='execCommand(command, null, val)'>exec</button>
+<button onclick='var ret = queryCommandState(command); alert(typeof ret + " \"" + ret + "\"")'>state</button>
+<button onclick='var ret = queryCommandValue(command); alert(typeof ret + " \"" + ret + "\"")'>value</button>
 <div contenteditable=true id=test>
 	<br> <br>
 	Some simple text<br>
@@ -20,28 +22,13 @@
 	<b>Some more text</b><br>
 	<strong>Some more text</strong><br>
 	<span style=font-weight:bold>Some more text</span><br>
-	<span style=font-weight:bolder>Some more text</span><br>
-	<span style=font-weight:lighter>Some more text</span><br>
-	<span style=font-weight:900>Some more text</span><br>
-	<span style=font-weight:100>Some more text</span><br>
 	<i>Some more text</i><br>
-	<span style=font-style:italic>Some more text</span><br>
 	<span style=font-style:italic;font-weight:bold>Some more text</span><br>
-	<em style=font-weight:bold>Some more text</em><br>
 	<b>Some <span style=font-weight:200>more <b>te<span style=font-weight:bold>xt<strong>!</strong></span></b></span></b><br>
 	<p>Some simple text
 	<p><span>Some more text</span>
 	<p><b>Some more text</b>
-	<p><strong>Some more text</strong>
-	<p><span style=font-weight:bold>Some more text</span>
-	<p><span style=font-weight:bolder>Some more text</span>
-	<p><span style=font-weight:lighter>Some more text</span>
-	<p><span style=font-weight:900>Some more text</span>
-	<p><span style=font-weight:100>Some more text</span>
 	<p><i>Some more text</i>
-	<p><span style=font-style:italic>Some more text</span>
-	<p><span style=font-style:italic;font-weight:bold>Some more text</span>
-	<p><em style=font-weight:bold>Some more text</em>
 	<p><b>Some <span style=font-weight:200>more <b>te<span style=font-weight:bold>xt<strong>!</strong></span></b></span></b>
 </div>
 <script>
@@ -106,7 +93,11 @@
 		"fontWeight": "font-weight",
 		"textDecoration": "text-decoration",
 	};
-	return map[propertyName];
+	if (typeof map[propertyName] != "undefined") {
+		return map[propertyName];
+	}
+
+	return propertyName;
 }
 
 
@@ -333,9 +324,6 @@
 		unstyleElement(elementChildren[j], propertyName, tagList);
 	}
 
-	// "Let children be an empty list of Nodes."
-	var children = [];
-
 	// "If either
 	//
 	// * element is an HTML element with name either "span" or in tag list, and
@@ -359,6 +347,9 @@
 		&& tagList.indexOf(element.nodeName.toLowerCase()) != -1
 		&& element.attributes.length == 0)
 	) {
+		// "Let children be an empty list of Nodes."
+		var children = [];
+
 		// "While element has children:"
 		while (element.hasChildNodes()) {
 			// "Let child be the first child of element."
@@ -374,7 +365,7 @@
 		// "Remove element."
 		element.parentNode.removeChild(element);
 
-		// "Return children and abort this algorithm."
+		// "Return children."
 		return children;
 	}
 
@@ -384,42 +375,45 @@
 		element.removeAttribute("style");
 	}
 
-	// "If element is an HTML element with name in tag list:"
-	if (element.namespaceURI == htmlNamespace
-	&& tagList.indexOf(element.tagName.toLowerCase()) != -1) {
-		// "Let new element be a new HTML element with name "span", with the
-		// same attributes and ownerDocument as element."
-		var newElement = element.ownerDocument.createElement("span");
-		for (var j = 0; j < element.attributes.length; j++) {
-			// FIXME: Namespaces?
-			newElement.setAttribute(element.attributes[j].localName, element.attributes[j].value);
-		}
-
-		// "Append new element to element's parent as the previous sibling of
-		// element."
-		element.parentNode.insertBefore(newElement, element);
-
-		// "While element has children:"
-		while (element.hasChildNodes()) {
-			// "Let child be the first child of element."
-			var child = element.firstChild;
-
-			// "Append child to children."
-			children.push(child);
-
-			// "Append child as the last child of new element."
-			newElement.appendChild(child);
-		}
-
-		// "Remove element."
-		element.parentNode.removeChild(element);
+	// "If element is not an HTML element or its local name is not in tag list,
+	// return the empty list."
+	if (element.namespaceURI != htmlNamespace
+	|| tagList.indexOf(element.tagName.toLowerCase()) == -1) {
+		return [];
 	}
 
-	// "Return children."
-	return children;
+	// "Let new element be a new HTML element with name "span", with the
+	// same attributes and ownerDocument as element."
+	var newElement = element.ownerDocument.createElement("span");
+	for (var j = 0; j < element.attributes.length; j++) {
+		// FIXME: Namespaces?
+		newElement.setAttribute(element.attributes[j].localName, element.attributes[j].value);
+	}
+
+	// "Append new element to element's parent as the previous sibling of
+	// element."
+	element.parentNode.insertBefore(newElement, element);
+
+	// "While element has children, append its first child as the last
+	// child of new element."
+	while (element.hasChildNodes()) {
+		newElement.appendChild(element.firstChild);
+	}
+
+	// "Remove element."
+	element.parentNode.removeChild(element);
+
+	// "Return the one-Node list consisting of new element."
+	return [newElement];
 }
 
-function styleRange(range, propertyName, propertyValue, tagList, commandId) {
+function styleRange(range, propertyName, propertyValue, tagList) {
+	// Extra step to implement CSS value equality checking; this needs to be
+	// specced somehow.
+	var testEl = document.createElement("span");
+	testEl.style[propertyName] = propertyValue;
+	var expectedPropertyValue = testEl.style[propertyName];
+
 	// "Let node list be the result of decomposing range."
 	var nodeList = decomposeRange(range);
 
@@ -428,22 +422,16 @@
 		var node = nodeList[i];
 		// "If node is an Element:"
 		if (node.nodeType == Node.ELEMENT_NODE) {
-			// "If node is an HTML element with name in tag list, unset the CSS
-			// property property name of node. Otherwise, set the CSS property
-			// property name of node to property value."
-			if (node.namespaceURI == htmlNamespace
-			&& tagList.indexOf(node.tagName.toLowerCase()) != -1) {
-				node.style[propertyName] = '';
-				if (node.getAttribute("style") == "") {
-					node.removeAttribute("style");
-				}
-			} else {
-				node.style[propertyName] = propertyValue;
+			// "Unset the CSS property property name of node."
+			node.style[propertyName] = '';
+			if (node.getAttribute("style") == "") {
+				node.removeAttribute("style");
 			}
 
-			// "If the state of commandId on node is false, set the CSS
-			// property property name of node to property value."
-			if (!getState(commandId, node)) {
+			// "If the computed value of property name for node is not property
+			// value, set the CSS property property name of node to property
+			// value."
+			if (getComputedStyle(node)[propertyName] != expectedPropertyValue) {
 				node.style[propertyName] = propertyValue;
 			}
 
@@ -461,30 +449,19 @@
 			}
 		// "Otherwise, if node is a Text node:"
 		} else if (node.nodeType == Node.TEXT_NODE) {
-			var newParent;
-			// "If the previous sibling of node is an HTML element with local
-			// name in tag list with no attributes, and the state of commandId
-			// on node is true, let new parent equal the previous sibling of
+			// "Let new parent be a new HTML element with local name equal to
+			// the first string in tag list (or equal to "span" if tag list is
+			// empty), with no attributes, and ownerDocument the same as node."
+			newParent = node.ownerDocument.createElement(tagList.length ? tagList[0] : "span");
+
+			// "Append new parent to node's parent as the previous sibling of
 			// node."
-			if (node.previousSibling
-			&& node.previousSibling.nodeType == Node.ELEMENT_NODE
-			&& node.previousSibling.namespaceURI == htmlNamespace
-			&& tagList.indexOf(node.previousSibling.tagName.toLowerCase()) != -1
-			&& node.previousSibling.attributes.length == 0
-			&& getState(commandId, node.previousSibling)) {
-				newParent = node.previousSibling;
-			} else {
-				// "Otherwise, let new parent be a new HTML element with local
-				// name equal to the first string in tag list, with no
-				// attributes, and ownerDocument the same as node. Append new
-				// parent to node's parent as the previous sibling of node."
-				newParent = node.ownerDocument.createElement(tagList[0]);
-				node.parentNode.insertBefore(newParent, node);
-			}
+			node.parentNode.insertBefore(newParent, node);
 
-			// "If the state of commandId on new parent is false, set the CSS
-			// property property name of new parent to property value."
-			if (!getState(commandId, newParent)) {
+			// "If the computed value of property name on new parent is not
+			// equal to property value, set the CSS property property name of
+			// new parent to property value."
+			if (getComputedStyle(newParent)[propertyName] != expectedPropertyValue) {
 				newParent.style[propertyName] = propertyValue;
 			}
 
@@ -495,9 +472,13 @@
 	}
 }
 
-// Note: because browsers are inconsistent about what to return for computed
-// styles for bold, I'm making propertyValue an array in the implementation.
 function unstyleRange(range, propertyName, propertyValue, tagList) {
+	// Extra step to implement CSS value equality checking; this needs to be
+	// specced somehow.
+	var testEl = document.createElement("span");
+	testEl.style[propertyName] = propertyValue;
+	var expectedPropertyValue = testEl.style[propertyName];
+
 	// "Let node list be the result of decomposing range."
 	var nodeList = decomposeRange(range);
 
@@ -507,17 +488,17 @@
 
 		// "If node is an Element:"
 		if (node.nodeType == Node.ELEMENT_NODE) {
-			// "Let children be the result of unstyling node."
-			var children = unstyleElement(node, propertyName, tagList);
+			// "Let extra nodes be the result of unstyling node."
+			var extraNodes = unstyleElement(node, propertyName, tagList);
 
 			// "If node no longer has a parent:"
 			if (!node.parentNode) {
-				// "Insert all the Nodes in children into node list immediately
-				// after node, in order."
+				// "Insert all the Nodes in extra nodes into node list
+				// immediately after node, in order."
 				//
 				// splice() would be perfect, but it requires varargs.  :(
 				nodeList = nodeList.slice(0, i + 1)
-					.concat(children)
+					.concat(extraNodes)
 					.concat(nodeList.slice(i + 1));
 
 				// "Continue with the next Node in node list, if any."
@@ -527,8 +508,8 @@
 			// "If the computed value of property name for node is not property
 			// value, set the CSS property property name of node to property
 			// value."
-			if (propertyValue.indexOf(getComputedStyle(node)[propertyName]) == -1) {
-				node.style[propertyName] = propertyValue[0];
+			if (getComputedStyle(node)[propertyName] != expectedPropertyValue) {
+				node.style[propertyName] = propertyValue;
 			}
 
 			// "Let element children be the Element children of node."
@@ -546,14 +527,14 @@
 		// "Otherwise, if node is a Text node and the computed value of
 		// property name for node's parent is not property value:"
 		} else if (node.nodeType == Node.TEXT_NODE
-		&& propertyValue.indexOf(getComputedStyle(node.parentNode)[propertyName]) == -1) {
+		&& getComputedStyle(node.parentNode)[propertyName] != expectedPropertyValue) {
 			// "Let new parent be a new HTML element with name "span", with no
 			// attributes, and with ownerDocument equal to node's."
 			var newParent = node.ownerDocument.createElement("span");
 
 			// "Set the CSS property property name of new parent to property
 			// value."
-			newParent.style[propertyName] = propertyValue[0];
+			newParent.style[propertyName] = propertyValue;
 
 			// "Insert new parent as node's previous sibling."
 			node.parentNode.insertBefore(newParent, node);
@@ -566,6 +547,7 @@
 }
 
 function myExecCommand(commandId, showUI, value) {
+	commandId = commandId.toLowerCase();
 	var range = activeRange(document);
 
 	if (!range) {
@@ -575,17 +557,27 @@
 	switch (commandId) {
 		case "bold":
 		if (getState("bold", range)) {
-			unstyleRange(range, "fontWeight", ["normal", "400"], ["b", "strong"]);
+			unstyleRange(range, "fontWeight", "normal", ["b", "strong"]);
 		} else {
-			styleRange(range, "fontWeight", "bold", ["b", "strong"], "bold");
+			styleRange(range, "fontWeight", "bold", ["b", "strong"]);
 		}
 		break;
 
+		case "foreColor":
+		// Hacky test to see if the color is valid
+		var testEl = document.createElement("span");
+		testEl.style.color = value;
+		if (testEl.style.color === "") {
+			return;
+		}
+		styleRange(range, "color", value, []);
+		break;
+
 		case "italic":
 		if (getState("italic", range)) {
-			unstyleRange(range, "fontStyle", ["normal"], ["i", "em"]);
+			unstyleRange(range, "fontStyle", "normal", ["i", "em"]);
 		} else {
-			styleRange(range, "fontStyle", "italic", ["i", "em"], "italic");
+			styleRange(range, "fontStyle", "italic", ["i", "em"]);
 		}
 		break;
 
@@ -595,6 +587,7 @@
 }
 
 function myQueryCommandState(commandId) {
+	commandId = commandId.toLowerCase();
 	var range = activeRange(document);
 
 	if (!range) {
@@ -605,12 +598,6 @@
 }
 
 function getState(commandId, range) {
-	if (range instanceof Node) {
-		var node = range;
-		range = node.ownerDocument.createRange();
-		range.selectNodeContents(node);
-	}
-
 	var style = getComputedStyle(beginningElement(range));
 
 	switch (commandId) {
@@ -625,4 +612,33 @@
 		return false;
 	}
 }
+
+function myQueryCommandValue(commandId) {
+	commandId = commandId.toLowerCase();
+	var range = activeRange(document);
+
+	if (!range) {
+		return "";
+	}
+
+	switch (commandId) {
+		case "backcolor":
+		var element = beginningElement(range);
+		while (element.nodeType == Node.ELEMENT_NODE
+		&& (getComputedStyle(element).backgroundColor == "rgba(0, 0, 0, 0)"
+		|| getComputedStyle(element).backgroundColor === "")) {
+			element = element.parentNode;
+		}
+		if (element.nodeType != Node.ELEMENT_NODE) {
+			return 'rgb(255, 255, 255)';
+		}
+		return getComputedStyle(element).backgroundColor;
+
+		case "forecolor":
+		return getComputedStyle(beginningElement(range)).color;
+
+		default:
+		return "";
+	}
+}
 </script>
--- a/preprocess	Tue Feb 22 15:15:44 2011 -0700
+++ b/preprocess	Wed Feb 23 15:15:21 2011 -0700
@@ -7,6 +7,8 @@
 
 replace = {
     "ancestor": "<span data-anolis-spec=domcore title=concept-ancestor-node>ancestor</span>",
+    "attrlocalname": "<span data-anolis-spec=domcore title=concept-attr-local-name>local name</span>",
+    "attrvalue": "<span data-anolis-spec=domcore title=concept-attr-value>value</span>",
     "boundarypoint": "<span data-anolis-spec=domrange title=concept-boundary-point>boundary point</span>",
     "bpnode": "<span data-anolis-spec=domrange title=concept-boundary-point-node>node</span>",
     "bpoffset": "<span data-anolis-spec=domrange title=concept-boundary-point-offset>offset</span>",
--- a/source.html	Tue Feb 22 15:15:44 2011 -0700
+++ b/source.html	Wed Feb 23 15:15:21 2011 -0700
@@ -50,9 +50,29 @@
 
 <h2>Issues</h2>
 
+<p>I'm not sure if my priorities in writing the algorithms here are correct.
+My goals were 1) make the algorithms as simple as possible, and 2) minimize
+surprising user-visible behavior (e.g., "I clicked B but it didn't turn
+bold!").  I didn't try to optimize the niceness of the resulting DOM at all, so
+for instance, when bolding <code title>abc &lt;i>def&lt;/i> &lt;br> ghi</code>
+you get <code title>&lt;b>abc &lt;/b>&lt;i style="font-weight:
+bold">def&lt;/i>&lt;b> &lt;/b>&lt;br style="font-weight: bold">&lt;b>
+ghi&lt;/b></code> instead of wrapping the whole thing in a single <code
+title>&lt;b></code>.  This is to avoid making the algorithm understand content
+models, but maybe it's worth revisiting later.  Likewise, unbolding the middle
+word of <code title>&lt;b>Foo bar baz&lt;/b></code> produces <code
+title>&lt;b>Foo &lt;span style="font-weight: normal">bar&lt;/span>
+baz&lt;/b></code> instead of the simpler <code title>&lt;b>Foo &lt;/b>bar&lt;b>
+baz&lt;/b></code>.  For now, the algorithm works, even if it produces messy
+DOMs.
+
+<p>Other issues:
+
 <ul>
   <li><p>Need to make CSS terminology more precise, about setting/unsetting CSS
   properties.  The intent is to modify the style attribute, CSSOM-style.
+  Likewise, CSS value comparisons need to be done after serializing both
+  values, so "bold" == "700" and "red" == "#f00" and so on.
 
   <li><p>Also not sure about computed style.  There are differences between
   "computed" and "used" and things like that, what do we actually want here?
@@ -73,21 +93,21 @@
 following algorithm:
 
 <ol>
-  <li><p>Let <var title>range</var> be the [[range]] under discussion.
+  <li><p>Let <var>range</var> be the [[range]] under discussion.
 
-  <li><p>If <var title>range</var>'s [[rangestart]] [[bpoffset]] is equal to
+  <li><p>If <var>range</var>'s [[rangestart]] [[bpoffset]] is equal to
   the [[nodelength]] of its [[rangestart]] [[bpnode]], return the first
   [[node]] that is after the [[rangestart]] [[bpnode]] and all its
   [[descendants]] (if any) in [[treeorder]].  If there is no such [[node]],
   return the last [[node]] in the document.
 
-  <li><p>If <var title>range</var>'s [[rangestart]] [[bpnode]] is a [[text]],
+  <li><p>If <var>range</var>'s [[rangestart]] [[bpnode]] is a [[text]],
   [[comment]], or [[processinginstruction]] node, return that.
 
-  <li><p>If <var title>range</var>'s [[rangestart]] [[bpnode]] has children,
+  <li><p>If <var>range</var>'s [[rangestart]] [[bpnode]] has children,
   return the child with [[index]] equal to the [[rangestart]] [[bpoffset]].
 
-  <li><p>Return <var title>range</var>'s [[rangestart]] [[bpnode]].
+  <li><p>Return <var>range</var>'s [[rangestart]] [[bpnode]].
 </ol>
 
 <p>The <dfn>beginning element</dfn> of a [[range]] is its <span>first
@@ -132,103 +152,103 @@
 [[processinginstruction]]s.
 
 <ol>
-  <li><p>Let <var title>start node</var>, <var title>start offset</var>, <var
-  title>end node</var>, and <var title>end offset</var> be the [[rangestart]]
-  and [[rangeend]] [[bpnodes]] and [[bpoffsets]] of <var title>range</var>,
+  <li><p>Let <var>start node</var>, <var>start offset</var>, <var
+  title>end node</var>, and <var>end offset</var> be the [[rangestart]]
+  and [[rangeend]] [[bpnodes]] and [[bpoffsets]] of <var>range</var>,
   respectively.
 
-  <li><p>If <var title>start node</var> or <var title>end node</var> is not an
+  <li><p>If <var>start node</var> or <var>end node</var> is not an
   [[element]], [[text]], [[processinginstruction]], or [[comment]] node, or is
   not an [[element]] and has no parent, abort these steps.
 
   <p class=XXX>Figure out something sensible here.
 
-  <li><p>If <var title>start node</var> and <var title>end node</var> are both
-  [[text]] nodes, and <var title>start node</var> is the same as <var title>end
-  node</var>, and neither <var title>start offset</var> nor <var title>end
-  offset</var> is equal to 0 or the [[nodelength]] of <var title>start
+  <li><p>If <var>start node</var> and <var>end node</var> are both
+  [[text]] nodes, and <var>start node</var> is the same as <var>end
+  node</var>, and neither <var>start offset</var> nor <var>end
+  offset</var> is equal to 0 or the [[nodelength]] of <var>start
   node</var>:
 
   <ol>
     <li><p>Run <code data-anolis-spec=domcore
-    title=dom-Text-splitText>splitText(<var title>start offset</var>)</code> on
-    <var title>start node</var> and set <var title>start node</var> to the
+    title=dom-Text-splitText>splitText(<var>start offset</var>)</code> on
+    <var>start node</var> and set <var>start node</var> to the
     result.
 
     <li><p>Run <code data-anolis-spec=domcore
-    title=dom-Text-splitText>splitText(<var title>end offset</var> &minus; <var
-    title>start offset</var>)</code> on <var title>start node</var> and set
-    <var title>start node</var> to the previous sibling of the result.
+    title=dom-Text-splitText>splitText(<var>end offset</var> &minus; <var
+    title>start offset</var>)</code> on <var>start node</var> and set
+    <var>start node</var> to the previous sibling of the result.
 
-    <li><p>Return the list consisting of the single [[node]] <var title>start
+    <li><p>Return the list consisting of the single [[node]] <var>start
     node</var>, and abort these steps.
   </ol>
 
-  <li><p>If <var title>start node</var> is a [[text]] node and <var title>start
-  offset</var> is neither 0 nor the [[nodelength]] of <var title>start
+  <li><p>If <var>start node</var> is a [[text]] node and <var>start
+  offset</var> is neither 0 nor the [[nodelength]] of <var>start
   node</var>, run <code data-anolis-spec=domcore
-  title=dom-Text-splitText>splitText(<var title>start offset</var>)</code> on
-  <var title>start node</var> and set <var title>start node</var> to the
-  returned node.  Set <var title>start offset</var> to 0.
-
-  <li><p>If <var title>end node</var> is a [[text]] node and <var title>end
-  offset</var> is neither 0 nor the [[nodelength]] of <var title>end
-  node</var>, run <code data-anolis-spec=domcore
-  title=dom-Text-splitText>splitText(<var title>end offset</var>)</code> on
-  <var title>end node</var> and set <var title>end node</var> to the previous
-  sibling of the returned node.  Set <var title>end offset</var> to the
-  [[nodelength]] of the new <var title>end node</var>.
+  title=dom-Text-splitText>splitText(<var>start offset</var>)</code> on
+  <var>start node</var> and set <var>start node</var> to the
+  returned node.  Set <var>start offset</var> to 0.
 
-  <li><p>If <var title>start node</var> is an [[element]] with at least one
-  child, let <var title>node</var> be the child of <var title>start node</var>
-  with [[index]] <var title>start offset</var>.
-
-  <li><p>Otherwise, if <var title>start node</var> is a [[text]] node and <var
-  title>start offset</var> is its [[nodelength]], let <var title>node</var> be
-  the first [[node]] after <var title>start node</var> in [[treeorder]].
+  <li><p>If <var>end node</var> is a [[text]] node and <var>end
+  offset</var> is neither 0 nor the [[nodelength]] of <var>end
+  node</var>, run <code data-anolis-spec=domcore
+  title=dom-Text-splitText>splitText(<var>end offset</var>)</code> on
+  <var>end node</var> and set <var>end node</var> to the previous
+  sibling of the returned node.  Set <var>end offset</var> to the
+  [[nodelength]] of the new <var>end node</var>.
 
-  <li><p>Otherwise, let <var title>node</var> be <var title>start node</var>.
+  <li><p>If <var>start node</var> is an [[element]] with at least one
+  child, let <var>node</var> be the child of <var>start node</var>
+  with [[index]] <var>start offset</var>.
 
-  <li><p>If <var title>end node</var> is an [[element]] and <var title>end
-  offset</var> is not 0, let <var title>end</var> be the child of <var
-  title>end node</var> with [[index]] <var title>end offset</var> &minus; 1.
+  <li><p>Otherwise, if <var>start node</var> is a [[text]] node and <var
+  title>start offset</var> is its [[nodelength]], let <var>node</var> be
+  the first [[node]] after <var>start node</var> in [[treeorder]].
 
-  <li><p>Otherwise, if <var title>end offset</var> is 0, let <var
-  title>end</var> be the first [[node]] before <var title>end node</var> in
+  <li><p>Otherwise, let <var>node</var> be <var>start node</var>.
+
+  <li><p>If <var>end node</var> is an [[element]] and <var>end
+  offset</var> is not 0, let <var>end</var> be the child of <var
+  title>end node</var> with [[index]] <var>end offset</var> &minus; 1.
+
+  <li><p>Otherwise, if <var>end offset</var> is 0, let <var
+  title>end</var> be the first [[node]] before <var>end node</var> in
   [[treeorder]].
 
-  <li><p>Otherwise, let <var title>end</var> be <var title>end node</var>.
+  <li><p>Otherwise, let <var>end</var> be <var>end node</var>.
 
   <!-- We try to include a node's parent instead of that node if possible,
   because this generally reduces the number of nodes we're handling.  So if the
   string "oo bar" was selected in <b>Foo <i>bar</i></b>, we'd add the <i> to
   the selection, even if the browser registered the end as the text node "bar".
   -->
-  <li><p>While <var title>node</var> is the first child of its parent and <var
-  title>end</var> is not a [[descendant]] of <var title>node</var>'s parent,
-  set <var title>node</var> to its parent.
+  <li><p>While <var>node</var> is the first child of its parent and <var
+  title>end</var> is not a [[descendant]] of <var>node</var>'s parent,
+  set <var>node</var> to its parent.
 
-  <li><p>While <var title>end</var> is the last child of its parent and <var
-  title>node</var> is not a [[descendant]] of <var title>end</var>'s parent,
-  set <var title>end</var> to its parent.
+  <li><p>While <var>end</var> is the last child of its parent and <var
+  title>node</var> is not a [[descendant]] of <var>end</var>'s parent,
+  set <var>end</var> to its parent.
 
-  <li><p>Let <var title>node list</var> be an empty list of [[node]]s.
+  <li><p>Let <var>node list</var> be an empty list of [[node]]s.
 
-  <li><p>While <var title>node</var> is not after <var title>end</var> in
+  <li><p>While <var>node</var> is not after <var>end</var> in
   [[treeorder]]:
 
   <ol>
-    <li><p>Append <var title>node</var> to <var title>node list</var>.
+    <li><p>Append <var>node</var> to <var>node list</var>.
 
-    <li><p>Set <var title>node</var> to the first [[node]] in [[treeorder]]
-    that is after <var title>node</var> and (if applicable) all its
+    <li><p>Set <var>node</var> to the first [[node]] in [[treeorder]]
+    that is after <var>node</var> and (if applicable) all its
     [[descendants]].  If no such [[node]] exists, break out of these substeps.
 
-    <li><p>While <var title>node</var> is an [[ancestor]] of <var
-    title>end</var>, set <var title>node</var> to its first child.
+    <li><p>While <var>node</var> is an [[ancestor]] of <var
+    title>end</var>, set <var>node</var> to its first child.
   </ol>
 
-  <li><p>Return <var title>node list</var>.
+  <li><p>Return <var>node list</var>.
 </ol>
 
 
@@ -239,194 +259,149 @@
 list of the element's former children.
 
 <ol>
-  <li><p>Let <var title>element</var> be the <code
+  <li><p>Let <var>element</var> be the <code
   data-anolis-spec=domcore>Element</code> to be unstyled.
 
-  <li><p>Let <var title>property name</var> and <var title>tag list</var> be as
+  <li><p>Let <var>property name</var> and <var>tag list</var> be as
   in the invoking algorithm.
 
-  <li><p>Let <var title>element children</var> be the [[element]] children of
-  <var title>element</var>.
+  <li><p>Let <var>element children</var> be the [[element]] children of
+  <var>element</var>.
 
   <li><p><span title="unstyle an element">Unstyle</span> each [[element]] in
-  <var title>element children</var>, in order.
+  <var>element children</var>, in order.
 
   <p class=note>Unstyling an element can change the number of children its
   parent has, so the list of children to unstyle needs to be computed
   beforehand.
 
-  <li><p>Let <var title>children</var> be an empty list of [[node]]s.
-
   <li><p>If either
 
   <ul>
-    <li><p><var title>element</var> is an <span>HTML element</span> with
-    [[localname]] either "span" or in <var title>tag list</var>, and it has
+    <li><p><var>element</var> is an <span>HTML element</span> with
+    [[localname]] either "span" or in <var>tag list</var>, and it has
     only a single attribute, and that attribute is named "style", and
-    that style attribute sets only the CSS property <var title>property
+    that style attribute sets only the CSS property <var>property
     name</var>; or
 
-    <li><p><var title>element</var> is an <span>HTML element</span> with
-    [[localname]] in <var title>tag list</var> and it has no attributes,
+    <li><p><var>element</var> is an <span>HTML element</span> with
+    [[localname]] in <var>tag list</var> and it has no attributes,
   </ul>
 
   <p>then:
 
   <ol>
-    <li><p>While <var title>element</var> has children:
+    <li><p>Let <var>children</var> be an empty list of [[node]]s.
+
+    <li><p>While <var>element</var> has children:
 
     <ol>
-      <li><p>Let <var title>child</var> be the first child of <var
+      <li><p>Let <var>child</var> be the first child of <var
       title>element</var>.
 
-      <li><p>Append <var title>child</var> to <var title>children</var>.
+      <li><p>Append <var>child</var> to <var>children</var>.
 
-      <li><p>Insert <var title>child</var> as the previous sibling of <var
+      <li><p>Insert <var>child</var> as the previous sibling of <var
       title>element</var>.
     </ol>
 
-    <li><p>Remove <var title>element</var>.
+    <li><p>Remove <var>element</var>.
 
-    <li><p>Return <var title>children</var> and abort this algorithm.
+    <li><p>Return <var>children</var>.
   </ol>
 
-  <li><p>Unset the CSS property <var title>property name</var> of <var
+  <li><p>Unset the CSS property <var>property name</var> of <var
   title>element</var>.
 
-  <li><p>If <var title>element</var> is an <span>HTML element</span> with
-  [[localname]] in <var title>tag list</var>:
-
-  <ol>
-    <li><p>Let <var title>new element</var> be a new <span>HTML element</span> with
-    [[localname]] "span", with the same attributes and <code
-    data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code>
-    as <var title>element</var>.
-
-    <li><p>Append <var title>new element</var> to <var title>element</var>'s
-    parent as the previous sibling of <var title>element</var>.
-
-    <li><p>While <var title>element</var> has children:
+  <li><p>If <var>element</var> is not an <span>HTML element</span> or its
+  [[localname]] is not in <var>tag list</var>, return the empty list.
 
-    <ol>
-      <li><p>Let <var title>child</var> be the first child of <var
-      title>element</var>.
-
-      <li><p>Append <var title>child</var> to <var title>children</var>.
+  <li><p>Let <var>new element</var> be a new <span>HTML element</span> with
+  [[localname]] "span", with the same attributes and <code
+  data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code>
+  as <var>element</var>.
 
-      <li><p>Append <var title>child</var> as the last child of <var title>new
-      element</var>.
-    </ol>
+  <li><p>Append <var>new element</var> to <var>element</var>'s
+  parent as the previous sibling of <var>element</var>.
 
-    <li><p>Remove <var title>element</var>.
-  </ol>
+  <li><p>While <var>element</var> has children, append its first child
+  as the last child of <var>new element</var>.
 
-  <li><p>Return <var title>children</var>.
+  <li><p>Remove <var>element</var>.
+
+  <li><p>Return the one-[[node]] list consisting of <var>new element</var>.
 </ol>
 
 
 <h2>Styling a Range</h2>
-<p>When a user agent is to <dfn>style a <code
-data-anolis-spec=domrange>Range</code></dfn> <var title>range</var>, it must
-run the following steps.  There are four inputs: a CSS property name <var
-title>property name</var>, a new value <var title>property value</var>, a
-nonempty list of strings <var title>tag list</var>, and a string
-<var>commandId</var>.
-
-<div class=XXX>
-<p>This description tries to keep the algorithm as simple as possible at the
-expense of producing much more complicated markup in some cases than a slightly
-more sophisticated algorithm.  It's similar to what Gecko does in spirit,
-although the details differ.  The basic cause of most of the ugly markup is
-that we don't bake in any element nesting restrictions, so <code
-title>&lt;em>Abc&lt;/em> &lt;em>def&lt;/em></code> becomes <code title>&lt;em
-style=font-weight:bold>Abc&lt;/em>&lt;b> &lt;/b>&lt;em
-style=font-weight:bold>def&lt;/em></code> instead of just <code
-title>&lt;b>&lt;em>Abc&lt;/em> &lt;em>def&lt;/em>&lt;/b></code>.  Otherwise
-we'd have to add content model checks.  Maybe this is worth it?  IE and WebKit
-seem to do it, Gecko and Opera seem not to (they only wrap text nodes AFAICT).
-
-<p>I did add some complication by saying conflicting inline style needs to be
-removed.  This is what WebKit does.  Gecko and IE9 leave it alone, which leads
-to stuff like <code title>&lt;b>&lt;span
-style=font-weight:100>Foo&lt;/span>&lt;/b></code> that doesn't actually work.
-Opera avoids the problem by producing code like <code title>&lt;span
-style=font-weight:100>&lt;b>Foo&lt;/b>&lt;/span></code>, but that doesn't work
-with the CSS-based strategy.
-</div>
+<p>When a user agent is to <dfn>style a [[range]]</dfn> <var>range</var>, it
+must run the following steps.  There are three inputs: a CSS property name
+<var>property name</var>, a new value <var>property value</var>, and a possibly
+empty list of strings <var>tag list</var>.
 
 <ol>
-  <li><p>Let <var title>node list</var> be the result of <span title="decompose
-  a range">decomposing</span> <var title>range</var>.
+  <li><p>Let <var>node list</var> be the result of <span title="decompose
+  a range">decomposing</span> <var>range</var>.
 
-  <li><p>For each <var title>node</var> in <var title>node list</var>, in <span
+  <li><p>For each <var>node</var> in <var>node list</var>, in <span
   data-anolis-spec=domcore>tree order</span>:
 
   <ol>
-    <li><p>If <var title>node</var> is an <code
+    <li><p>If <var>node</var> is an <code
     data-anolis-spec=domcore>Element</code>:
 
     <ol>
-      <li><p>If <var title>node</var> is an <span>HTML element</span> with [[localname]]
-      in <var title>tag list</var>, unset the CSS property <var title>property
-      name</var> of <var title>node</var>.  Otherwise, set the CSS property
-      <var title>property name</var> of <var title>node</var> to <var
-      title>property value</var>.
+      <li><p>Unset the CSS property <var>property name</var> of <var
+      title>node</var>.
 
-      <li><p>If the <span>state</span> of <var>commandId</var> on
-      <var>node</var> is false, set the CSS property <var>property name</var>
-      of <var>node</var> to <var>property value</var>.
-      <!-- This fixes cases where the element won't actually create the desired
-      style.  E.g., <span style=font-weight:lighter><b>Foo</b></span> will not
-      actually bold "Foo" (because the default style for <b> is font-weight:
-      bolder), so we do <b style=font-weight:bold> instead.  Likewise when we
-      do similarly a bit later on in this algorithm. -->
+      <li><p>If the computed value of <var>property name</var> for
+      <var>node</var> is not <var>property value</var>, set the CSS property
+      <var>property name</var> of <var>node</var> to <var>property value</var>.
+      <!-- This means we don't bother applying the property if the style is
+      already present, e.g., from an ancestor.  But we do apply it if the
+      element is the expected sort of element but the style is wrong anyway,
+      e.g., <span style=font-weight:100><b>Foo</b></span> where b's style is
+      font-weight: bold. -->
 
-      <li><p>Let <var title>element children</var> be the [[element]] children
-      of <var title>node</var>.
+      <li><p>Let <var>element children</var> be the [[element]] children
+      of <var>node</var>.
 
       <li><p><span title="unstyle an element">Unstyle</span> each [[element]]
-      in <var title>element children</var>, in order.
+      in <var>element children</var>, in order.
 
       <p class=note>Unstyling an element can change the number of children its
       parent has, so the list of children to unstyle needs to be computed
       beforehand.
     </ol>
 
-    <li><p>Otherwise, if <var title>node</var> is a <code
+    <li><p>Otherwise, if <var>node</var> is a <code
     data-anolis-spec=domcore>Text</code> node:
 
     <ol>
-      <!-- This next step is not strictly necessary from a user point of view,
-      since there's no visible difference.  We could just always insert a new
-      element.  But that can drastically complicate the DOM if there are lots
-      of text nodes, which happens easily when the algorithms here are invoked
-      a lot: you get splitText() called when decomposing ranges, and unstyling
-      elements can also dump their text node children into the DOM next to
-      other text nodes. -->
-      <li><p>If the previous sibling of <var>node</var> is an <span>HTML
-      element</span> with [[localname]] in <var>tag list</var> with no
-      attributes, and the <span>state</span> of <var>commandId</var> on
-      <var>node</var> is true, let <var>new parent</var> equal the previous
-      sibling of <var>node</var>.
+      <li><p>Let <var>new parent</var> be a new <span>HTML element</span> with
+      [[localname]] equal to the first string in <var>tag list</var> (or equal
+      to "span" if <var>tag list</var> is empty), with no attributes, and
+      [[ownerdocument]] the same as <var>node</var>.
 
-      <li><p>Otherwise, let <var>new parent</var> be a new <span>HTML
-      element</span> with [[localname]] equal to the first string in <var>tag
-      list</var>, with no attributes, and [[ownerdocument]] the same as
-      <var>node</var>.  Append <var>new parent</var> to <var>node</var>'s
-      parent as the previous sibling of <var>node</var>.
+      <li><p>Append <var>new parent</var> to <var>node</var>'s parent as the
+      previous sibling of <var>node</var>.
 
-      <li><p>If the <span>state</span> of <var>commandId</var> on <var>new
-      parent</var> is false, set the CSS property <var>property name</var> of
-      <var>new parent</var> to <var>property value</var>.
+      <li><p>If the computed value of <var>property name</var> on <var>new
+      parent</var> is not equal to <var>property value</var>, set the CSS
+      property <var>property name</var> of <var>new parent</var> to
+      <var>property value</var>.
+      <!-- This is needed if tag list is empty, but also if the correct style
+      is being suppressed for some reason, like <span
+      style=font-weight:100><b>Foo</b></span> where b is font-weight: bolder.
+      -->
 
-      <li><p>Append <var title>node</var> to <var title>new parent</var> as
+      <li><p>Append <var>node</var> to <var>new parent</var> as
       its last child.
     </ol>
 
     <li><p>Otherwise, do nothing.
   </ol>
 </ol>
-
 <!-- Out of IE9, Gecko, WebKit, and Opera, when asked to (e.g.) bold an
 element, IE9 and WebKit and Opera wrap various descendants in <b> or <strong>;
 Gecko just adds a style attribute.  The latter is simpler, particularly because
@@ -437,77 +412,73 @@
 
 
 <h2>Unstyling a Range</h2>
-<p>When a user agent is to <dfn>unstyle a <code
-data-anolis-spec=domrange>Range</code></dfn> <var title>range</var>, it must
-run the following steps.  There are four inputs: a CSS property name <var
-title>property name</var>, a new value <var title>property value</var>, a
-possibly empty list of strings <var title>tag list</var>, and a string
-<var>commandId</var>.
+<p>When a user agent is to <dfn>unstyle a [[range]]</dfn> <var>range</var>, it
+must run the following steps.  There are three inputs: a CSS property name
+<var>property name</var>, a new value <var>property value</var>, and a possibly
+empty list of strings <var>tag list</var>.
 
 <ol>
-  <li><p>Let <var title>node list</var> be the result of <span title="decompose
-  a range">decomposing</span> <var title>range</var>.
+  <li><p>Let <var>node list</var> be the result of <span title="decompose
+  a range">decomposing</span> <var>range</var>.
 
-  <li><p>For each <var title>node</var> in <var title>node list</var>, in
+  <li><p>For each <var>node</var> in <var>node list</var>, in
   order:
 
   <ol>
-    <li><p>If <var title>node</var> is an <code
+    <li><p>If <var>node</var> is an <code
     data-anolis-spec=domcore>Element</code>:
 
     <ol>
-      <li><p>Let <var title>children</var> be the result of <span
-      title="unstyle an element">unstyling</span> <var title>node</var>.
+      <li><p>Let <var>extra nodes</var> be the result of <span
+      title="unstyle an element">unstyling</span> <var>node</var>.
 
-      <li><p>If <var title>node</var> no longer has a parent:
+      <li><p>If <var>node</var> no longer has a parent:
 
       <ol>
-        <li><p>Insert all the [[node]]s in <var title>children</var> into <var
-        title>node list</var> immediately after <var title>node</var>, in
-        order.
+        <li><p>Insert all the [[node]]s in <var>extra nodes</var> into
+        <var>node list</var> immediately after <var>node</var>, in order.
 
-        <li><p>Continue with the next [[node]] in <var title>node list</var>,
-        if any.
+        <li><p>Continue with the next [[node]] in <var>node list</var>, if any.
 
-        <p class=note>The next [[node]] will be the first former child of <var
-        title>node</var>, if <var title>node</var> had children.
+        <p class=note>The next [[node]] will be the first in <var>extra
+        nodes</var>, unless <var>extra nodes</var> is empty.
       </ol>
 
-      <li><p>If the computed value of <var title>property name</var> for <var
-      title>node</var> is not <var title>property value</var>, set the CSS
-      property <var title>property name</var> of <var title>node</var> to <var
+      <li><p>If the computed value of <var>property name</var> for <var
+      title>node</var> is not <var>property value</var>, set the CSS
+      property <var>property name</var> of <var>node</var> to <var
       title>property value</var>.
 
-      <li><p>Let <var title>element children</var> be the [[element]] children
-      of <var title>node</var>.
+      <li><p>Let <var>element children</var> be the [[element]] children
+      of <var>node</var>.
 
       <li><p><span title="unstyle an element">Unstyle</span> each [[element]]
-      in <var title>element children</var>, in order.
+      in <var>element children</var>, in order.
 
       <p class=note>Unstyling an element can change the number of children its
       parent has, so the list of children to unstyle needs to be computed
       beforehand.
     </ol>
 
-    <li><p>Otherwise, if <var title>node</var> is a <code
+    <li><p>Otherwise, if <var>node</var> is a <code
     data-anolis-spec=domcore>Text</code> node and the computed value of <var
-    title>property name</var> for <var title>node</var>'s parent is not <var
+    title>property name</var> for <var>node</var>'s parent is not <var
     title>property value</var>:
 
     <ol>
-      <li><p>Let <var title>new parent</var> be a new <span>HTML element</span> with
+      <li><p>Let <var>new parent</var> be a new <span>HTML element</span> with
       [[localname]] "span", with no attributes, and with <code
       data-anolis-spec=domcore
       title=dom-Node-ownerDocument>ownerDocument</code> equal to <var
       title>node</var>'s.
 
-      <li><p>Set the CSS property <var title>property name</var> of <var
-      title>new parent</var> to <var title>property value</var>.
+      <li><p>Set the CSS property <var>property name</var> of <var
+      title>new parent</var> to <var>property value</var>.
 
-      <li><p>Insert <var title>new parent</var> as <var title>node</var>'s
+      <li><p>Insert <var>new parent</var> as <var>node</var>'s
       previous sibling.
 
-      <li><p>Append <var title>node</var> to <var title>new parent</var> as its
+      <li><p>Append <var>node</var> to <var>new parent</var> as its
       child.
     </ol>
 
@@ -517,15 +488,15 @@
 
 
 <h2>Commands</h2>
-<p>The <dfn title=execCommand()><code>execCommand(<var title>commandId</var>,
-<var title>showUI</var>, <var title>value</var>)</code></dfn> method on the
+<p>The <dfn title=execCommand()><code>execCommand(<var>commandId</var>,
+<var>showUI</var>, <var>value</var>)</code></dfn> method on the
 <code data-anolis-spec=html>HTMLDocument</code> interface allows scripts to
 perform actions on the current selection or at the current caret position.
 Generally, these commands would be used to implement editor UI, for example
 having a "delete" button on a toolbar.
 
 <p>There are three variants to this method, with one, two, and three arguments
-respectively. The <var title>showUI</var> and <var title>value</var>
+respectively. The <var>showUI</var> and <var>value</var>
 parameters, even if specified, are ignored except where otherwise stated.
 
 <p>When <code>execCommand()</code> is invoked, the user agent must take the
@@ -536,22 +507,80 @@
 <p>The <dfn
 title=queryCommandState()><code>queryCommandState(<var>commandId</var>)</code></dfn>
 method on the <code data-anolis-spec=html>HTMLDocument</code> interface must
-return the <span>state</span> of <var>commandId</var> on the
-[[contextobject]]'s <span>active range</span>.  If there is no <span>active
-range</span>, return false.
+return the state of <var>commandId</var> on the [[contextobject]]'s
+<span>active range</span>, as given by the list below.  If there is no
+<span>active range</span>, or if <var>commandId</var> is not on the list,
+return false.
 <!-- Gecko throws an exception if there are no ranges in the selection, but
 other engines seem to just return false, which seems like nicer behavior
-anyway. -->
+anyway.
 
-<p>The <dfn>state</dfn> of a command <var>commandId</var> on a [[range]] is
-given by the list below.  If <var>commandId</var> is not on the list, its
-<span>state</span> is false on any [[range]].  The <span>state</span> of a
-command on a [[node]] <var>node</var> is the <span>state</span> of that command
-on the [[range]] with [[rangestart]] (<var>node</var>, 0), [[rangeend]]
-(<var>node</var>, <var>node</var>'s [[nodelength]]), and [[rangeroot]] equal to
-<var>node</var>'s [[ownerdocument]].
+Requesting the state of an unknown command throws an exception in IE 9 RC
+and Firefox 4b11, and returns boolean false in Chrome 10 and Opera 11. -->
+
+<p>The <dfn
+title=queryCommandValue()><code>queryCommandValue(<var>commandId</var>)</code></dfn>
+method on the <code data-anolis-spec=html>HTMLDocument</code> interface must
+return the value of <var>commandId</var> on the [[contextobject]]'s
+<span>active range</span>, as given by the list below.  If there is no
+<span>active range</span>, or if <var>commandId</var> is not on the list,
+return the empty string.
+<!-- Requesting the value of an unknown command throws an exception in
+IE 9 RC and in Firefox 4b11.  It returns boolean false in Chrome 10, and the
+empty string in Opera 11. -->
+
+<p class=XXX>Querying the value or state of an unrecognized command throws an
+exception in IE and Firefox.  Need to consider changing to that behavior.
+
+<p>The possible values for <var>commandId</var>, and their corresponding
+meanings, are as follows.  These values must be compared to the argument in an
+<span data-anolis-spec=html>ASCII case-insensitive</span> manner.
 
 <dl>
+<dt><code title><dfn title=command-backColor>backColor</dfn></code>
+
+<dd><p><strong>Action</strong>: If <var>value</var> is not a valid CSS color,
+the user agent must do nothing and abort these steps.  Otherwise, it must <span
+title="style a range">style the [[range]]</span> with <var>property name</var>
+equal to "background-color", <var>property value</var> equal to
+<var>value</var>, and <var>tag list</var> equal to the empty list.
+<!-- Firefox documentation says it normally sets the background color of the
+document, but I can't get it to work at all in brief testing in 4b11.  (It says
+it behaves differently in styleWithCss mode.)  Opera 11 appears to set the
+background color of the nearest block container of the cursor, or something.
+IE 9 RC and Chrome 10 behave as I'd expect, namely, they set the background of
+the selection, just like all the other styling features.  I go with IE/WebKit.
+
+Invalid colors are probably the same craziness as with foreColor.
+
+Chrome 10 dev actually sets background, not background-color, so it resets all
+the other background stuff.  I go with IE 9 RC and Opera 11, which only set
+background-color. -->
+
+<dd><p><strong>State</strong>: Always false.
+
+<dd><p><strong>Value</strong>: The value is given by the following algorithm:
+
+<ol>
+  <li><p>Let <var>element</var> be the <span>beginning element</span> of the
+  [[range]].
+
+  <li><p>While the computed style of "background-color" on <var>element</var>
+  is any fully transparent value, set <var>element</var> to its parent.
+
+  <p class=XXX>It's intended that for these purposes, the root element will
+  have a white background.  Should that be specified somewhere?  I don't think
+  all UAs actually do it.
+
+  <li><p>Return the computed style of "background-color" for
+  <var>element</var>.
+</ol>
+<!-- Chrome 10 returns rgba(0, 0, 0, 0) if there's no background defined
+anywhere.  Opera 11 returns rgb(255, 255, 255) as I'd like.  Firefox 4b11 just
+throws an exception for some reason.  IE 9 RC seems to return the number 0
+across the board, as with foreColor. -->
+
+
 <dt><code title><dfn title=command-bold>bold</dfn></code>
 
 <dd><p><strong>Action</strong>: If the state of the [[range]] for this command
@@ -565,13 +594,57 @@
 <dd><p><strong>State</strong>: True if the <span>beginning element</span> of the
 [[range]] has font-weight with computed value less than 700, otherwise false.
 
+<dd><p><strong>Value</strong>: Always the empty string.
+<!-- We have lots of options here (and presumably for all the others where
+value is meaningless).  IE 9 RC returns the boolean false, Firefox 4b11 and
+Opera 11 both return the empty string, Chrome 10 returns the string "false".
+The HTML5 spec as of February 2011 mandates WebKit's behavior.  It makes sense
+to always return a string, a majority of string-returners return the empty
+string, and three out of the four return something that evaluates to false as a
+boolean, so I'll go with Firefox and Opera. -->
+
+
+<dt><code title><dfn title=command-forecolor>foreColor</dfn></code>
+
+<dd><p><strong>Action</strong>: If <var>value</var> is not a valid CSS color,
+the user agent must do nothing and abort these steps.
+<!-- Browsers are all over the place here.  IE 9 RC seems to treat unrecognized
+colors as black, but everyone else ignores them.  There are also special rules
+for certain things that aren't valid CSS colors, in some browsers, like:
+
+IE 9 RC: "ffe" -> "#ffe", "123" -> "#123"
+Firefox 4b11: "ffe" -> no style, "123" -> no style
+Chrome 10 dev: "ffe" -> "#FFFFEE", "123" -> "#000123"
+Opera 11: "ffe" -> "#0f0f0e", "123" -> "#010203"
+
+Firefox seems to stick to just CSS colors, so with any luck that works.
+"limegreen" works as expected in all browsers.  rgb(255, 255, 255) does too,
+except in Opera, which tries to parse it in some crazy way and winds up with
+"#00b025".  rgba() colors don't work as uniformly, but I don't see any reason
+to prohibit them.  Best to just match CSS. -->
+Otherwise, it must <span title="style a range">style the [[range]]</span> with
+<var>property name</var> equal to "color", <var>property value</var> equal to
+<var>value</var>, and <var>tag list</var> equal to the empty list.
+
+<dd><p><strong>State</strong>: Always false.
+<!-- This matches IE 9 RC and Chrome 10.  Opera 11 seems to return true if
+there's some color style applied, false otherwise, which seems fairly useless;
+authors want to use value here, not state.  Firefox 4b11 throws an exception,
+which is an interesting approach, but I'll go with IE/WebKit, which makes at
+least as much sense. -->
+
+<dd><p><strong>Value</strong>: The computed value of the CSS property "color"
+for the <span>beginning element</span> of the [[range]].
+<!-- IE 9 RC returns the number 0 always, which makes no sense at all.  This
+matches the other browsers. -->
+
 
 <dt><code title><dfn title=command-italic>italic</dfn></code>
 
 <dd><p><strong>Action</strong>: If the of the [[range]] for this command is
 false, the user agent must <span title="style a range">style the
 [[range]]</span> with <var>property name</var> "font-style", <var>property
-value</var> "italic", and <var>tag list</var> ["i", "em"].  Otherwise, it must
+value</var> "italic", <var>tag list</var> ["i", "em"].  Otherwise, it must
 <span title="unstyle a range">unstyle it</span> with <var>property name</var>
 "font-style", <var>property value</var> "normal", and <var>tag list</var> ["i",
 "em"].
@@ -580,6 +653,9 @@
 [[range]] has font-style with computed value "italic" or "oblique", otherwise
 false.
 
+<dd><p><strong>Value</strong>: Always the empty string.
+<!-- See comment for bold -->
+
 
 <dt><code title><dfn title=command-underline>underline</dfn></code>
 
@@ -589,8 +665,8 @@
 
 <dd class=XXX><p><strong>State</strong>: ...
 
-<p class=XXX>This is wrong, it will clear strikethrough and overline.  Also,
-computed style isn't useful, because text-decoration doesn't inherit.
+<dd><p><strong>Value</strong>: Always the empty string.
+<!-- See comment for bold -->
 </dl>
 
 
--- a/xrefs.json	Tue Feb 22 15:15:44 2011 -0700
+++ b/xrefs.json	Wed Feb 23 15:15:21 2011 -0700
@@ -1,7 +1,9 @@
 {
   "active range": "active-range",
   "beginning element": "beginning-element",
+  "command-backcolor": "command-backcolor",
   "command-bold": "command-bold",
+  "command-forecolor": "command-forecolor",
   "command-italic": "command-italic",
   "command-underline": "command-underline",
   "decompose a range": "decompose-a-range",
@@ -9,7 +11,7 @@
   "first node": "first-node",
   "html element": "html-element",
   "querycommandstate()": "querycommandstate()",
-  "state": "state",
+  "querycommandvalue()": "querycommandvalue()",
   "style a range": "style-a-range",
   "unstyle a range": "unstyle-a-range",
   "unstyle an element": "unstyle-an-element"