Commit another day's work
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Wed, 23 Mar 2011 16:03:54 -0600
changeset 26 4dead548b9d6
parent 25 c9c544b49fcd
child 27 61e55187231f
Commit another day's work

The spec now supports styleWithCSS/useCSS. Also a number of refinements
that improve the quality of the resulting markup in particular cases, at
the expense of additional algorithmic complexity.
autoimplementation.html
editcommands.html
implementation.js
source.html
--- a/autoimplementation.html	Wed Mar 23 11:59:33 2011 -0600
+++ b/autoimplementation.html	Wed Mar 23 16:03:54 2011 -0600
@@ -39,6 +39,7 @@
 <h1>bold</h1>
 
 <table border=1><tr><th>Input <th>Spec <th>Browser <th>Same?</table>
+<table border=1><tr><th>Input <th>Spec <th>Browser <th>Same?</table>
 
 <p><label>Enter new test here: <input></label>
 <button onclick="addTest('bold', document.querySelector('#bold input').value)">Add test</button>
@@ -47,9 +48,10 @@
 <div id=fontname>
 <h1>fontname</h1>
 
-<p>Tests set the font-family to "serif".
+<p>Tests set the font-family to "sans-serif".  Note that the body's font-family is "serif".
 
 <table border=1><tr><th>Input <th>Spec <th>Browser <th>Same?</table>
+<table border=1><tr><th>Input <th>Spec <th>Browser <th>Same?</table>
 
 <p><label>Enter new test here: <input></label>
 <button onclick="addTest('bold', document.querySelector('#fontname input').value)">Add test</button>
@@ -61,6 +63,7 @@
 <p>Tests set the color to "#FF0000".
 
 <table border=1><tr><th>Input <th>Spec <th>Browser <th>Same?</table>
+<table border=1><tr><th>Input <th>Spec <th>Browser <th>Same?</table>
 
 <p><label>Enter new test here: <input></label>
 <button onclick="addTest('forecolor', document.querySelector('#forecolor input').value)">Add test</button>
@@ -81,6 +84,7 @@
 <h1>italic</h1>
 
 <table border=1><tr><th>Input <th>Spec <th>Browser <th>Same?</table>
+<table border=1><tr><th>Input <th>Spec <th>Browser <th>Same?</table>
 
 <p><label>Enter new test here: <input></label>
 <button onclick="addTest('italic', document.querySelector('#italic input').value)">Add test</button>
@@ -90,6 +94,7 @@
 <h1>underline</h1>
 
 <table border=1><tr><th>Input <th>Spec <th>Browser <th>Same?</table>
+<table border=1><tr><th>Input <th>Spec <th>Browser <th>Same?</table>
 
 <p><label>Enter new test here: <input></label>
 <button onclick="addTest('underline', document.querySelector('#underline input').value)">Add test</button>
@@ -99,7 +104,7 @@
 <script>
 var values = {
 	bold: null,
-	fontname: "serif",
+	fontname: "sans-serif",
 	forecolor: "#FF0000",
 	hilitecolor: "#FF8888",
 	italic: null,
@@ -112,17 +117,26 @@
 		'foo]bar[baz',
 		'{<p><p> <p>Foo</p>}',
 		'foo[bar<i>baz]qoz</i>quz',
+
 		'foo<span style="font-weight: bold">[bar]</span>baz',
 		'foo<b>[bar]</b>baz',
 		'foo<b>bar</b>[baz]',
 		'[foo]<b>bar</b>baz',
+		'<b>foo</b>[bar]<b>baz</b>',
 		'foo<strong>bar</strong>[baz]',
 		'[foo]<strong>bar</strong>baz',
+		'<strong>foo</strong>[bar]<strong>baz</strong>',
+		'<b>foo</b>[bar]<strong>baz</strong>',
+		'<strong>foo</strong>[bar]<b>baz</b>',
 		'foo[<b>bar</b>]baz',
 		'foo[<b>bar]</b>baz',
 		'foo<b>[bar</b>]baz',
-		'foo<span style="font-weight: normal"><b>{bar}</b></span>baz',
+
 		'foo{<b></b>}baz',
+		'foo{<i></i>}baz',
+		'foo{<b><i></i></b>}baz',
+		'foo{<i><b></b></i>}baz',
+
 		'foo<strong>[bar]</strong>baz',
 		'foo[<strong>bar</strong>]baz',
 		'foo[<strong>bar]</strong>baz',
@@ -131,11 +145,15 @@
 		'foo[<span style="font-weight: bold">bar</span>]baz',
 		'foo[<span style="font-weight: bold">bar]</span>baz',
 		'foo<span style="font-weight: bold">[bar</span>]baz',
+
 		'<b>{<p>foo</p><p>bar</p>}<p>baz</p></b>',
 		'<b><p>foo[<i>bar</i>}</p><p>baz</p></b>',
+
 		'foo [bar <b>baz] qoz</b> quz sic',
 		'foo bar <b>baz [qoz</b> quz] sic',
+
 		'<b id=purple>bar [baz] qoz</b>',
+
 		'foo<span style="font-weight: 100">[bar]</span>baz',
 		'foo<span style="font-weight: 400">[bar]</span>baz',
 		'foo<span style="font-weight: 700">[bar]</span>baz',
@@ -158,6 +176,7 @@
 		'<span style="font-weight: 400">foo[barbaz</span>}',
 		'<span style="font-weight: 700">foo[barbaz</span>}',
 		'<span style="font-weight: 900">foo[barbaz</span>}',
+
 		'<h3>Foo[bar]baz</h3>',
 		'{<h3>Foobar]baz</h3>',
 		'<h3>Foo[barbaz</h3>}',
@@ -165,13 +184,19 @@
 		'{<h3>Foobarbaz]</h3>',
 		'<h3>[Foobarbaz</h3>}',
 		'{<h3>Foobarbaz</h3>}',
+
 		'<b>Foo<span style="font-weight: normal">bar<b>[baz]</b>quz</span>qoz</b>',
 		'<b>Foo<span style="font-weight: normal">[bar]</span>baz</b>',
+
 		'{<b>Foo</b> <b>bar</b>}',
 		'{<h3>Foo</h3><b>bar</b>}',
+
 		'<i><b>Foo</b></i>[bar]<i><b>baz</b></i>',
 		'<i><b>Foo</b></i>[bar]<b>baz</b>',
 		'<b>Foo</b>[bar]<i><b>baz</b></i>',
+		'<font color=red face=monospace><b>Foo</b></font>[bar]',
+
+		'foo<span style="font-weight: normal"><b>{bar}</b></span>baz',
 		'[Foo<span class=notbold>bar</span>baz]',
 	],
 	fontname: [
@@ -214,8 +239,8 @@
 		'foo<samp>b[ar</samp>baz]',
 		'foo<tt>b[ar</tt>baz]',
 
-		'foo<span style="font-family: serif">[bar]</span>baz',
-		'foo<span style="font-family: serif">b[a]r</span>baz',
+		'foo<span style="font-family: sans-serif">[bar]</span>baz',
+		'foo<span style="font-family: sans-serif">b[a]r</span>baz',
 		'foo<span style="font-family: monospace">[bar]</span>baz',
 		'foo<span style="font-family: monospace">b[a]r</span>baz',
 	],
@@ -306,6 +331,10 @@
 		'foo<span style="text-decoration: underline">[bar]</span>baz',
 		'<u>foo[bar]baz</u>',
 		'<u>foo[b<span style="color:red">ar]ba</span>z</u>',
+		'<u>foo[b<span style="color:red" id=foo>ar]ba</span>z</u>',
+		'<u>foo[b<span style="font-size:3em">ar]ba</span>z</u>',
+		'<u>foo[b<i>ar]ba</i>z</u>',
+		'<p style="text-decoration: underline">foo[bar]baz</p>',
 	],
 };
 
@@ -316,21 +345,33 @@
 }
 
 function addTest(command, test) {
-	var tr = doSetup(command);
+	var doubleTesting = ["bold", "italic", "underline", "forecolor",
+	"fontname", "fontsize"].indexOf(command) != -1;
+
+	var tr = doSetup(command, 0);
 
 	doInputCell(tr, test);
-	doSpecCell(tr, test, command);
-	doBrowserCell(tr, test, command);
+	doSpecCell(tr, test, command, false);
+	doBrowserCell(tr, test, command, false);
 	doSameCell(tr);
 
+	if (doubleTesting) {
+		var tr = doSetup(command, 1);
+
+		doInputCell(tr, test);
+		doSpecCell(tr, test, command, true);
+		doBrowserCell(tr, test, command, true);
+		doSameCell(tr);
+	}
+
 	doTearDown(command);
 }
 
-function doSetup(command) {
+function doSetup(command, idx) {
 	var div = document.getElementById(command);
 	div.contentEditable = "true";
 	div.spellcheck = false;
-	var table = div.querySelector("table");
+	var table = div.querySelectorAll("table")[idx];
 
 	var tr = document.createElement("tr");
 	// Insert at the top, because Chrome debugger doesn't let you scroll down
@@ -352,7 +393,7 @@
 	tr.appendChild(inputCell);
 }
 
-function doSpecCell(tr, test, command) {
+function doSpecCell(tr, test, command, styleWithCss) {
 	var value = values[command];
 
 	var specCell = document.createElement("td");
@@ -368,16 +409,32 @@
 		if (range.collapsed) {
 			range.setEnd(points[0], points[1]);
 		}
+		myExecCommand("styleWithCSS", false, styleWithCss);
 		myExecCommand(command, false, value, range);
 		specCell.lastChild.textContent = specCell.firstChild.innerHTML;
 	} catch (e) {
-		specCell.textContent = "Exception: " + e;
+		specCell.lastChild.style.color = "red";
+		specCell.lastChild.style.fontWeight = "bold";
+		specCell.lastChild.textContent = "Note, exception: " + e;
+	}
+
+	var key = "execcommand-" + command
+		+ "-" + Number(myQueryCommandState("styleWithCSS"))
+		+ "-" + test;
+
+	var oldValue = localStorage[key];
+	localStorage[key] = specCell.lastChild.textContent;
+
+	if (oldValue !== null && oldValue !== undefined && oldValue != specCell.lastChild.textContent) {
+		specCell.appendChild(document.createElement("div"));
+		specCell.lastChild.style.color = "red";
+		specCell.lastChild.style.fontWeight = "bold";
+		specCell.lastChild.textContent = "Note, last run produced different markup: " + oldValue;
 	}
 }
 
-function doBrowserCell(tr, test, command) {
+function doBrowserCell(tr, test, command, styleWithCss) {
 	var value = values[command];
-	var styleWithCss = command != "bold" && command != "italic" && command != "underline";
 
 	var browserCell = document.createElement("td");
 	browserCell.innerHTML = "<div></div><div></div>";
@@ -387,10 +444,8 @@
 		var points = parseBrackets(browserCell);
 		setSelection(points[0], points[1], points[2], points[3]);
 		try {
-			document.execCommand("styleWithCss", false, styleWithCss);
-		} catch (e) {
-			// IE, we don't care
-		}
+			document.execCommand("styleWithCSS", false, styleWithCss);
+		} catch (e) {}
 		document.execCommand(command, false, value);
 		browserCell.lastChild.textContent = browserCell.firstChild.innerHTML;
 	} catch (e) {
@@ -401,9 +456,9 @@
 function doSameCell(tr) {
 	var sameCell = document.createElement("td");
 	// Ad hoc normalization to avoid basically spurious mismatches
-	var normalizedSpecCell = tr.childNodes[1].lastChild.textContent
+	var normalizedSpecCell = tr.childNodes[1].childNodes[1].textContent
 		.replace(/;? ?"/g, '"');
-	var normalizedBrowserCell = tr.childNodes[2].lastChild.textContent
+	var normalizedBrowserCell = tr.childNodes[2].childNodes[1].textContent
 		.replace(/;? ?"/g, '"')
 		.replace(/<(\/?)strong/g, '<$1b')
 		.replace(/<(\/?)em/g, '<$1i')
--- a/editcommands.html	Wed Mar 23 11:59:33 2011 -0600
+++ b/editcommands.html	Wed Mar 23 16:03:54 2011 -0600
@@ -207,7 +207,9 @@
 
   <li>Return the last <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> in <var title="">selection</var> whose <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a>
   is <var title="">start</var>.
-  <!-- This is what Firefox seems to do, no reason to change it . . . -->
+
+  <p class=XXX>Double-check that this is what Firefox actually does.  It seems
+  pretty baroque.
 
   <p class=note>In user agents that support only one <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> per
   <a href=http://html5.org/specs/dom-range.html#selection><code class=external data-anolis-spec=domrange>Selection</code></a>, the active range is simply the only one in the selection.
@@ -215,7 +217,16 @@
 
 <p>An <dfn id=unwrappable-element>unwrappable element</dfn> is an <a href=#html-element>HTML element</a> which may
 not be used where only <a class=external data-anolis-spec=html href=http://www.whatwg.org/html/#phrasing-content>phrasing content</a> is expected (not counting unknown or
-obsolete elements, which cannot be used at all).
+obsolete elements, which cannot be used at all); or any <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a> whose
+display property computes to something other than "inline", "inline-block", or
+"inline-table".
+
+<p class=XXX>Currently when we hit an unwrappable element, we ignore it and
+style its children.  Alternatively, if we would otherwise create a span with a
+style element on it, maybe we could put the style element directly on the
+unwrappable element.  This would produce shorter markup in many cases, but
+would also cause problems for things like background-color that do something
+different on block elements.
 
 <p>The <dfn id=effective-style>effective style</dfn> of 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> for a given
 <var title="">property</var> is returned by the following algorithm, which will return
@@ -290,6 +301,19 @@
   <li>Return null.
 </ol>
 
+<p>A <dfn id=styling-element>styling element</dfn> is a <a href=http://www.whatwg.org/html/#the-b-element><code class=external data-anolis-spec=html title="the b element">b</code></a>, <a href=http://www.whatwg.org/html/#the-em-element><code class=external data-anolis-spec=html title="the em element">em</code></a>, <a href=http://www.whatwg.org/html/#the-i-element><code class=external data-anolis-spec=html title="the i element">i</code></a>, <a href=http://www.whatwg.org/html/#the-span-element><code class=external data-anolis-spec=html title="the span element">span</code></a>,
+<a href=http://www.whatwg.org/html/#the-strong-element><code class=external data-anolis-spec=html title="the strong element">strong</code></a>, or <a href=http://www.whatwg.org/html/#the-u-element><code class=external data-anolis-spec=html title="the u element">u</code></a> element with no attributes except possibly <a href=http://www.whatwg.org/html/#the-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a>, or
+a <a href=http://www.whatwg.org/html/#font><code class=external data-anolis-spec=html title=font>font</code></a> element with no attributes except possibly <a href=http://www.whatwg.org/html/#the-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a>, <a href=http://www.whatwg.org/html/#dom-font-color><code class=external data-anolis-spec=html title=dom-font-color>color</code></a>,
+<a href=http://www.whatwg.org/html/#dom-font-face><code class=external data-anolis-spec=html title=dom-font-face>face</code></a>, and/or <a href=http://www.whatwg.org/html/#dom-font-size><code class=external data-anolis-spec=html title=dom-font-size>size</code></a>.
+
+<p class=note>Conceptually, a <a href=#styling-element>styling element</a> is a phrasing
+element whose only purpose is to style text.  Thus changing around styling
+elements (such as moving them, breaking them up, deleting them, or switching
+them with other styling elements) is fine as long as the style is preserved.
+In fact, some styling elements are supposed to have semantics other than
+styling, but the algorithms here treat them as styling elements anyway, for
+compatibility with legacy user-agents that treat them that way.
+
 <p>A <dfn id=simple-styling-element>simple styling element</dfn> is an <a href=#html-element>HTML element</a> for
 which at least one of the following holds:
 
@@ -301,30 +325,36 @@
   element with exactly one attribute, which is <a href=http://www.whatwg.org/html/#the-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a>, which sets no CSS
   properties (including invalid or unrecognized properties).
 
-  <li>It is a <a href=http://www.whatwg.org/html/#font><code class=external data-anolis-spec=html title=font>font</code></a> element with one attribute, which is either
+  <li>It is a <a href=http://www.whatwg.org/html/#font><code class=external data-anolis-spec=html title=font>font</code></a> element with exactly one attribute, which is either
   <a href=http://www.whatwg.org/html/#dom-font-color><code class=external data-anolis-spec=html title=dom-font-color>color</code></a>, <a href=http://www.whatwg.org/html/#dom-font-face><code class=external data-anolis-spec=html title=dom-font-face>face</code></a>, or <a href=http://www.whatwg.org/html/#dom-font-size><code class=external data-anolis-spec=html title=dom-font-size>size</code></a>.
 
-  <li>It is a <a href=http://www.whatwg.org/html/#the-b-element><code class=external data-anolis-spec=html title="the b element">b</code></a> or <a href=http://www.whatwg.org/html/#the-strong-element><code class=external data-anolis-spec=html title="the strong element">strong</code></a> element with one attribute, which is
-  <a href=http://www.whatwg.org/html/#the-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a>, and the only CSS property set by the <a href=http://www.whatwg.org/html/#the-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a> attribute
-  (including invalid or unrecognized properties) is "font-weight".
+  <li>It is a <a href=http://www.whatwg.org/html/#the-b-element><code class=external data-anolis-spec=html title="the b element">b</code></a> or <a href=http://www.whatwg.org/html/#the-strong-element><code class=external data-anolis-spec=html title="the strong element">strong</code></a> element with exactly one attribute, which is
+  <a href=http://www.whatwg.org/html/#the-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a>, and the <a href=http://www.whatwg.org/html/#the-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a> attribute sets exactly one CSS property
+  (including invalid or unrecognized properties), which is "font-weight".
 
-  <li>It is an <a href=http://www.whatwg.org/html/#the-i-element><code class=external data-anolis-spec=html title="the i element">i</code></a> or <a href=http://www.whatwg.org/html/#the-em-element><code class=external data-anolis-spec=html title="the em element">em</code></a> element with one attribute, which is <a href=http://www.whatwg.org/html/#the-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a>,
-  and the only CSS property set by the <a href=http://www.whatwg.org/html/#the-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a> attribute (including invalid
-  or unrecognized properties) is "font-style".
+  <li>It is an <a href=http://www.whatwg.org/html/#the-i-element><code class=external data-anolis-spec=html title="the i element">i</code></a> or <a href=http://www.whatwg.org/html/#the-em-element><code class=external data-anolis-spec=html title="the em element">em</code></a> element with exactly one attribute, which is
+  <a href=http://www.whatwg.org/html/#the-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a>, and the <a href=http://www.whatwg.org/html/#the-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a> attribute sets exactly one CSS property
+  (including invalid or unrecognized properties), which is "font-style".
 
-  <li>It is a <a href=http://www.whatwg.org/html/#the-u-element><code class=external data-anolis-spec=html title="the u element">u</code></a> element with one attribute, which is <a href=http://www.whatwg.org/html/#the-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a>, and the
-  only CSS property set by the <a href=http://www.whatwg.org/html/#the-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a> attribute (including invalid or
-  unrecognized properties) is "text-decoration", which is set to "underline" or
-  "none".
+  <li>It is a <a href=http://www.whatwg.org/html/#the-u-element><code class=external data-anolis-spec=html title="the u element">u</code></a> element with exactly one attribute, which is <a href=http://www.whatwg.org/html/#the-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a>, and
+  the <a href=http://www.whatwg.org/html/#the-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a> attribute sets exactly one CSS property (including invalid or
+  unrecognized properties), which is "text-decoration", which is set to
+  "underline" or "none".
 
   <li>It is a <a href=http://www.whatwg.org/html/#font><code class=external data-anolis-spec=html title=font>font</code></a> or <a href=http://www.whatwg.org/html/#the-span-element><code class=external data-anolis-spec=html title="the span element">span</code></a> element with exactly one attribute, which is
   <a href=http://www.whatwg.org/html/#the-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a>, and the <a href=http://www.whatwg.org/html/#the-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a> attribute sets exactly one CSS property
   (including invalid or unrecognized properties).
 </ol>
 
-<p class=note>Conceptually, a <a href=#simple-styling-element>simple styling element</a> is an element
-which has a <a href=#specified-style>specified style</a> for exactly one CSS property, but does
-nothing else.
+<p class=note>Conceptually, a <a href=#simple-styling-element>simple styling element</a> is a
+<a href=#styling-element>styling element</a> which <a href=#specified-style title="specified
+style">specifies</a> at most one CSS property.
+
+<p>The <dfn id=css-styling-flag>CSS styling flag</dfn> is a boolean flag, which must initially be
+false.
+
+<p class=XXX>Is the styling flag associated with the document, with the editing
+host, what?  Needs reverse-engineering.
 
 <p>When the user agent is instructed to run a particular method, it must follow
 the steps defined for that method in the appropriate specification, not act as
@@ -476,7 +506,7 @@
     set.
   </ol>
 
-  <li>If <var title="">element</var>'s <var title="">specified style</var> for
+  <li>If <var title="">element</var>'s <a href=#specified-style>specified style</a> for
   <var title="">property</var> is null, return the empty list.
   <!-- If we get past this step, we're something like <b class=foo> where we
   want to keep the extra attributes, so we stick them on a span. -->
@@ -502,6 +532,32 @@
 <var title="">node</var>, given a CSS property name <var title="">property</var> and a new value
 <var title="">new value</var>, it must run the following steps:
 
+<!-- This algorithm goes up to just below the nearest ancestor with the right
+style, then re-applies the bad styles repeatedly going down, omitting the
+things we want to have the new style.  This is basically what WebKit does,
+although WebKit sometimes starts higher up and therefore makes more intrusive
+changes, often creating more markup.
+
+Gecko instead seems to start breaking up elements from the bottom, so that the
+range consists of a few consecutive siblings, and it can then break up the
+problematic element into a maximum of two pieces.  The spec's approach seems to
+create fewer elements and simpler markup (or at least markup that's no more
+complex) in every case I throw at it.
+
+Gecko's approach does have the major advantage that it gets underlines right in
+many cases for free.  E.g.,
+
+  <u>foo<font color=red>[bar]baz</font></u>
+  -> <u>foo</u><font color=red>bar<u>baz</u></font> (spec)
+  -> <u>foo</u><font color=red>bar</font><u><font color=red>baz</font></u> (Gecko)
+
+The spec's markup here is much shorter and contains fewer elements, but is
+wrong: the underline under "baz" has changed color from black to red.  It might
+be worth trying to copy Gecko's results in such cases, but that won't solve all
+underline problems, so perhaps it's not worth it.
+
+Opera also seems to break up the markup surrounding the range, but even more
+aggressively: even if it doesn't need to pull down styles. -->
 <ol>
   <li>If <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-parent title=concept-tree-parent>parent</a> is not 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>, abort this
   algorithm. <!-- E.g., a text node child of a document fragment. -->
@@ -588,84 +644,96 @@
 After forcing the style, descendants might still have different style.
 
 <ol>
-  <!-- Even if the style matches, we stick it in a preceding sibling if
-  possible.  This ensures "a<cite>b</cite>c" -> "<i>a<cite>b</cite>c</i>"
-  instead of "<i>a</i><cite>b</cite><i>c</i>". -->
+  <li>If <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-parent title=concept-tree-parent>parent</a> is null, abort this algorithm.
+
   <li>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>, <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#text><code class=external data-anolis-spec=domcore>Text</code></a>, <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#comment><code class=external data-anolis-spec=domcore>Comment</code></a>, or
   <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#processinginstruction><code class=external data-anolis-spec=domcore>ProcessingInstruction</code></a> node, and is not an <a href=#unwrappable-element>unwrappable
   element</a>:
 
   <ol>
-    <!-- Handle cases like bolding "bar" in "<i><b>Foo</b></i>bar".
+    <!-- Even if the style matches, we stick it in a preceding sibling if
+    possible.  This ensures "a<cite>b</cite>c" -> "<i>a<cite>b</cite>c</i>"
+    instead of "<i>a</i><cite>b</cite><i>c</i>".  While we're at it, we also
+    handle more elaborate cases like <b>foo</b>[bar]<b>baz</b> and even
+    <i><b>foo</b></i>[bar]<i><b>baz</b></i> (the latter becomes
+    <b><i>foo</i>bar<i>baz</i></b>).
+
     Theoretically this algorithm could pointlessly reorganize the DOM in the
     event of unreasonable style rules, but it's not a big enough deal for us to
-    care, probably. -->
-    <!-- This is probably more complicated than we want, given how likely this
-    is to come up, so I've commented it out for now.
-    <li>Let <var title>candidate</var> be <var title>node</var>'s <code data-anolis-spec=domcore title=dom-Node-previousSibling>previousSibling</code>.
+    care, since the resulting style will still be right. -->
+    <li>Let <var title="">candidate</var> be <var title="">node</var>'s <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-previoussibling><code class=external data-anolis-spec=domcore title=dom-Node-previousSibling>previousSibling</code></a>.
 
-    <li>While <var title>candidate</var> is a <span>simple styling element</span>,
-    and <var title>candidate</var> has exactly one <span data-anolis-spec=domcore title=concept-tree-child>child</span>, and that <span data-anolis-spec=domcore title=concept-tree-child>child</span> is
-    also a <span>simple styling element</span>, and <var title>candidate</var>'s
-    <span>specified style</span> for <var title>property</var> is not <var title>new
-    value</var>, set <var title>candidate</var> to its <span data-anolis-spec=domcore title=concept-tree-child>child</span>.
+    <li>While <var title="">candidate</var> is a <a href=#styling-element>styling element</a>, and
+    <var title="">candidate</var> has exactly one <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a>, and that <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> is also
+    a <a href=#styling-element>styling element</a>, and <var title="">candidate</var> is not a
+    <a href=#simple-styling-element>simple styling element</a> or <var title="">candidate</var>'s
+    <a href=#specified-style>specified style</a> for <var title="">property</var> is not <var title="">new
+    value</var>, set <var title="">candidate</var> to its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a>.
 
-    <li>If <var title>candidate</var> is a <span>simple styling element</span> whose
-    <span>specified style</span> and computed style for <var title>property</var> are
-    both <var title>new value</var>, and <var title>candidate</var> is not the
-    <code data-anolis-spec=domcore title=dom-Node-previousSibling>previousSibling</code> of <var title>node</var>:
+    <li>If <var title="">candidate</var> is a <a href=#simple-styling-element>simple styling element</a> whose
+    <a href=#specified-style>specified style</a> and <a href=#effective-style>effective style</a> for
+    <var title="">property</var> are both <var title="">new value</var>, and <var title="">candidate</var>
+    is not the <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-previoussibling><code class=external data-anolis-spec=domcore title=dom-Node-previousSibling>previousSibling</code></a> of <var title="">node</var>:
 
     <ol>
-      <li>While <var title>candidate</var> has <span data-anolis-spec=domcore title=concept-tree-child>children</span>, append the first
-      <span data-anolis-spec=domcore title=concept-tree-child>child</span> of <var title>candidate</var> as the last <span data-anolis-spec=domcore title=concept-tree-child>child</span> of
-      <var title>candidate</var>'s <span data-anolis-spec=domcore title=concept-tree-parent>parent</span>.
-
-      <li>Insert <var title>candidate</var> into <var title>node</var>'s <span data-anolis-spec=domcore title=concept-tree-parent>parent</span> before
-      <var title>node</var>.
-
-      <li>Append the <code data-anolis-spec=domcore title=dom-Node-previousSibling>previousSibling</code> of <var title>candidate</var> as the last
-      <span data-anolis-spec=domcore title=concept-tree-child>child</span> of <var title>candidate</var>.
-    </ol>
-    -->
+      <li>While <var title="">candidate</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="">candidate</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="">candidate</var>'s <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
 
-    <li>If <var title="">node</var>'s <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-previoussibling><code class=external data-anolis-spec=domcore title=dom-Node-previousSibling>previousSibling</code></a> is a <a href=#simple-styling-element>simple styling
-    element</a> whose <a href=#specified-style>specified style</a> and <a href=#effective-style>effective
-    style</a> for <var title="">property</var> are both <var title="">new value</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 its <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-previoussibling><code class=external data-anolis-spec=domcore title=dom-Node-previousSibling>previousSibling</code></a> and abort
-    this algorithm.
-
-    <!--
-    <li>Let <var title>candidate</var> be <var title>node</var>'s <code data-anolis-spec=domcore title=dom-Node-nextSibling>nextSibling</code>.
+      <li>Insert <var title="">candidate</var> into <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-parent title=concept-tree-parent>parent</a> before
+      <var title="">node</var>.
 
-    <li>While <var title>candidate</var> is a <span>simple styling element</span>,
-    and <var title>candidate</var> has exactly one <span data-anolis-spec=domcore title=concept-tree-child>child</span>, and that <span data-anolis-spec=domcore title=concept-tree-child>child</span> is
-    also a <span>simple styling element</span>, and <var title>candidate</var>'s
-    <span>specified style</span> for <var title>property</var> is not <var title>new
-    value</var>, set <var title>candidate</var> to its <span data-anolis-spec=domcore title=concept-tree-child>child</span>.
+      <li>Append the <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-previoussibling><code class=external data-anolis-spec=domcore title=dom-Node-previousSibling>previousSibling</code></a> of <var title="">candidate</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="">candidate</var>.
+    </ol>
 
-    <li>If <var title>candidate</var> is a <span>simple styling element</span> whose
-    <span>specified style</span> and computed style for <var title>property</var> are
-    both <var title>new value</var>, and <var title>candidate</var> is not the
-    <code data-anolis-spec=domcore title=dom-Node-nextSibling>nextSibling</code> of <var title>node</var>:
+    <li>Let <var title="">candidate</var> be <var title="">node</var>'s <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-nextsibling><code class=external data-anolis-spec=domcore title=dom-Node-nextSibling>nextSibling</code></a>.
+
+    <li>While <var title="">candidate</var> is a <a href=#styling-element>styling element</a>, and
+    <var title="">candidate</var> has exactly one <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a>, and that <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> is also
+    a <a href=#styling-element>styling element</a>, and <var title="">candidate</var> is not a
+    <a href=#simple-styling-element>simple styling element</a> or <var title="">candidate</var>'s
+    <a href=#specified-style>specified style</a> for <var title="">property</var> is not <var title="">new
+    value</var>, set <var title="">candidate</var> to its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a>.
+
+    <li>If <var title="">candidate</var> is a <a href=#simple-styling-element>simple styling element</a> whose
+    <a href=#specified-style>specified style</a> and <a href=#effective-style>effective style</a> for
+    <var title="">property</var> are both <var title="">new value</var>, and <var title="">candidate</var>
+    is not the <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-nextsibling><code class=external data-anolis-spec=domcore title=dom-Node-nextSibling>nextSibling</code></a> of <var title="">node</var>:
 
     <ol>
-      <li>While <var title>candidate</var> has <span data-anolis-spec=domcore title=concept-tree-child>children</span>, append the first
-      <span data-anolis-spec=domcore title=concept-tree-child>child</span> of <var title>candidate</var> as the last <span data-anolis-spec=domcore title=concept-tree-child>child</span> of
-      <var title>candidate</var>'s <span data-anolis-spec=domcore title=concept-tree-parent>parent</span>.
-
-      <li>Insert <var title>candidate</var> into <var title>node</var>'s <span data-anolis-spec=domcore title=concept-tree-parent>parent</span> after
-      <var title>node</var>.
+      <li>While <var title="">candidate</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="">candidate</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="">candidate</var>'s <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
 
-      <li>Append the <code data-anolis-spec=domcore title=dom-Node-nextSibling>nextSibling</code> of <var title>candidate</var> as the last
-      <span data-anolis-spec=domcore title=concept-tree-child>child</span> of <var title>candidate</var>.
-    </ol>
-    -->
+      <li>Insert <var title="">candidate</var> into <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-parent title=concept-tree-parent>parent</a> after
+      <var title="">node</var>.
 
-    <li>If <var title="">node</var>'s <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-nextsibling><code class=external data-anolis-spec=domcore title=dom-Node-nextSibling>nextSibling</code></a> is a <a href=#simple-styling-element>simple styling
-    element</a> whose <a href=#specified-style>specified style</a> and <a href=#effective-style>effective
-    style</a> for <var title="">property</var> are both <var title="">new value</var>, insert
-    <var title="">node</var> as 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 its <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-nextsibling><code class=external data-anolis-spec=domcore title=dom-Node-nextSibling>nextSibling</code></a> and abort
-    this algorithm.
+      <li>Append the <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-nextsibling><code class=external data-anolis-spec=domcore title=dom-Node-nextSibling>nextSibling</code></a> of <var title="">candidate</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="">candidate</var>.
+    </ol>
+
+    <li>Let <var title="">previous sibling</var> and <var title="">next sibling</var> be
+    <var title="">node</var>'s <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-previoussibling><code class=external data-anolis-spec=domcore title=dom-Node-previousSibling>previousSibling</code></a> and <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-nextsibling><code class=external data-anolis-spec=domcore title=dom-Node-nextSibling>nextSibling</code></a>.
+
+    <li>If <var title="">previous sibling</var> is a <a href=#simple-styling-element>simple styling element</a>
+    whose <a href=#specified-style>specified style</a> and <a href=#effective-style>effective style</a> for
+    <var title="">property</var> are both <var title="">new value</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="">previous sibling</var>.
+
+    <li>If <var title="">next sibling</var> is a <a href=#simple-styling-element>simple styling element</a>
+    whose <a href=#specified-style>specified style</a> and <a href=#effective-style>effective style</a> for
+    <var title="">property</var> are both <var title="">new value</var>:
+
+    <ol>
+      <li>If <var title="">node</var> is not a <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> of <var title="">previous sibling</var>,
+      insert <var title="">node</var> as 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="">next sibling</var>.
+
+      <li>Otherwise, while <var title="">next sibling</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="">next sibling</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="">previous sibling</var>.  Then remove <var title="">next sibling</var> from
+      its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
+    </ol>
   </ol>
 
   <li>If the <a href=#effective-style>effective style</a> of <var title="">property</var> is <var title="">new
@@ -693,18 +761,45 @@
   <li>If the <a href=#effective-style>effective style</a> of <var title="">property</var> is <var title="">new
   value</var> on <var title="">node</var>, abort this algorithm.
 
-  <li>If <var title="">property</var> is "font-weight" and <var title="">new value</var> is
-  "bold", let <var title="">tag</var> be "b".
-
-  <li>If <var title="">property</var> is "font-style" and <var title="">new value</var> is
-  "italic", let <var title="">tag</var> be "i".
+  <li>Let <var title="">new parent</var> be null.
 
-  <li>If <var title="">property</var> is "text-decoration" and <var title="">new value</var> is
-  "underline", let <var title="">tag</var> be "u".
+  <li>If the <a href=#css-styling-flag>CSS styling flag</a> is false:
 
-  <li>If <var title="">tag</var> is not set, let <var title="">tag</var> be "span".
+  <ol>
+    <li>If <var title="">property</var> is "font-weight" and <var title="">new value</var> is
+    "bold", let <var title="">new parent</var> be the result of calling <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement><code class=external data-anolis-spec=domcore title=dom-Document-createElement>createElement("b")</code></a> on the
+    <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> of <var title="">node</var>.
 
-  <li>Let <var title="">new parent</var> be the result of calling <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement><code class=external data-anolis-spec=domcore title=dom-Document-createElement>createElement(<var title="">tag</var>)</code></a> on the
+    <li>If <var title="">property</var> is "font-style" and <var title="">new value</var> is
+    "italic", let <var title="">new parent</var> be the result of calling <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement><code class=external data-anolis-spec=domcore title=dom-Document-createElement>createElement("i")</code></a> on the
+    <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> of <var title="">node</var>.
+
+    <li>If <var title="">property</var> is "text-decoration" and <var title="">new value</var> is
+    "underline", let <var title="">new parent</var> be the result of calling <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement><code class=external data-anolis-spec=domcore title=dom-Document-createElement>createElement("u")</code></a> on the
+    <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> of <var title="">node</var>.
+
+    <li>If <var title="">property</var> is "color", let <var title="">new parent</var> be the
+    result of calling <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement><code class=external data-anolis-spec=domcore title=dom-Document-createElement>createElement("font")</code></a> on the
+    <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> of <var title="">node</var>, then set the <a href=http://www.whatwg.org/html/#dom-font-color><code class=external data-anolis-spec=html title=dom-font-color>color</code></a> attribute
+    of <var title="">new parent</var> to <var title="">new value</var>.
+
+    <p class=XXX>This can be wrong if, e.g., the color has an alpha channel.
+    We also need to specify exactly how it's serialized, since CSSOM doesn't
+    cover this.  It might be best to always output simple colors.
+
+    <li>If <var title="">property</var> is "font-family", let <var title="">new parent</var> be
+    the result of calling <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement><code class=external data-anolis-spec=domcore title=dom-Document-createElement>createElement("font")</code></a> on the
+    <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> of <var title="">node</var>, then set the <a href=http://www.whatwg.org/html/#dom-font-face><code class=external data-anolis-spec=html title=dom-font-face>face</code></a> attribute
+    of <var title="">new parent</var> to <var title="">new value</var>.
+
+    <li>If <var title="">property</var> is "font-size", let <var title="">new parent</var> be the
+    result of calling <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement><code class=external data-anolis-spec=domcore title=dom-Document-createElement>createElement("font")</code></a> on the
+    <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> of <var title="">node</var>, then set the <a href=http://www.whatwg.org/html/#dom-font-size><code class=external data-anolis-spec=html title=dom-font-size>size</code></a> attribute
+    of <var title="">new parent</var> to <var title="">new value</var>.
+  </ol>
+
+  <li>If <var title="">new parent</var> is null, let <var title="">new parent</var> be the result
+  of calling <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-createelement><code class=external data-anolis-spec=domcore title=dom-Document-createElement>createElement("span")</code></a> on the
   <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> of <var title="">node</var>.
 
   <li>Insert <var title="">new parent</var> in <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-parent title=concept-tree-parent>parent</a> before
@@ -720,13 +815,25 @@
   of <var title="">property</var> for <var title="">node</var> is not <var title="">new value</var>:
 
   <ol>
-    <li>Set the CSS property <var title="">property</var> of <var title="">node</var> to <var title="">new
-    value</var>.
-
     <li>Insert <var title="">node</var> into the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> of <var title="">new parent</var>
     before <var title="">new parent</var>.
 
     <li>Remove <var title="">new parent</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>.
+
+    <li>If <var title="">new parent</var> is a <a href=http://www.whatwg.org/html/#the-span-element><code class=external data-anolis-spec=html title="the span element">span</code></a>, set the CSS property
+    <var title="">property</var> of <var title="">node</var> to <var title="">new value</var>.
+
+    <li>Otherwise:
+
+    <ol>
+      <li>Let <var title="">children</var> be all <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>,
+      omitting any that are <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a>s whose <a href=#specified-style>specified style</a> for
+      <var title="">property</var> is neither null nor equal to <var title="">new value</var>.
+
+      <li><a href=#force-the-style>Force the style</a> of each <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="">children</var>,
+      with <var title="">property</var> and <var title="">new value</var> as in this invocation
+      of the algorithm.
+    </ol>
   </ol>
 </ol>
 
@@ -835,7 +942,7 @@
 
   <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=#style>Style</a> each member of <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>.
+  <li><a href=#style>Style</a> each member of <var title="">children</var>.
 
   <p class=note>Styling a node involves clearing its styles, which can remove
   it from the tree.  Implementers should be careful to compute the list of
@@ -1155,6 +1262,20 @@
 <dd><p><strong>Value</strong>: Always the empty string.
 
 
+<dt><code title=""><dfn id=command-stylewithcss title=command-stylewithcss>styleWithCSS</dfn></code>
+
+<dd><p><strong>Action</strong>: Convert <var title="">value</var> to a boolean according
+to the algorithm in WebIDL, and set the <a href=#css-styling-flag>CSS styling flag</a> to the
+result.
+
+<p class=XXX>Properly cross-reference.
+
+<dd><p><strong>State</strong>: True if the <a href=#css-styling-flag>CSS styling flag</a> is
+true, otherwise false.
+
+<dd><p><strong>Value</strong>:
+
+
 <dt><code title=""><dfn id=command-underline title=command-underline>underline</dfn></code>
 
 <dd><p><strong>Action</strong>: <a href=#decompose>Decompose</a> the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>.  If the
@@ -1204,7 +1325,6 @@
 style</a> either null or "underline" for text-decoration.  Otherwise false.
 
 <dd><p><strong>Value</strong>: Always the empty string.
-</dl>
 
 
 <dt><code title=""><dfn id=command-unlink title=command-unlink>unlink</dfn></code>
@@ -1254,6 +1374,22 @@
 <dd><p><strong>Value</strong>: Always the empty string.
 
 
+<dt><code title=""><dfn id=command-usecss title=command-usecss>useCSS</dfn></code>
+
+<dd><p><strong>Action</strong>: Convert <var title="">value</var> to a boolean according
+to the algorithm in WebIDL, and set the <a href=#css-styling-flag>CSS styling flag</a> to the
+negation of the result.  Since the effect of this command is the opposite of
+what one would expect, user agents are encouraged to point authors to <a href=#command-stylewithcss><code title=command-stylewithcss>styleWithCSS</code></a> when <a href=#command-usecss><code title=command-usecss>useCSS</code></a> is used, such as by logging a warning to an
+error console.
+
+<p class=XXX>Properly cross-reference.
+
+<dd><p><strong>State</strong>:
+
+<dd><p><strong>Value</strong>:
+</dl>
+
+
 <h2 class=no-num id=references>References</h2><!--REFS-->
 <p>All references are normative unless marked "Non-normative".</p>
 <div id=anolis-references><dl></dl></div>
--- a/implementation.js	Wed Mar 23 11:59:33 2011 -0600
+++ b/implementation.js	Wed Mar 23 16:03:54 2011 -0600
@@ -2,6 +2,8 @@
 
 var htmlNamespace = "http://www.w3.org/1999/xhtml";
 
+var cssStylingFlag = false;
+
 // Utility functions
 function nextNode(node) {
 	if (node.hasChildNodes()) {
@@ -262,10 +264,22 @@
 
 // "An unwrappable element is an HTML element which may not be used where only
 // phrasing content is expected (not counting unknown or obsolete elements,
-// which cannot be used at all)."
+// which cannot be used at all); or any Element whose display property computes
+// to something other than "inline", "inline-block", or "inline-table"."
 //
 // I don't bother implementing this exactly, just well enough for testing.
 function isUnwrappableElement(node) {
+	if (!node || node.nodeType != Node.ELEMENT_NODE) {
+		return false;
+	}
+
+	var display = getComputedStyle(node).display;
+	if (display != "inline"
+	&& display != "inline-block"
+	&& display != "inline-table") {
+		return true;
+	}
+
 	if (!isHtmlElement(node)) {
 		return false;
 	}
@@ -395,6 +409,52 @@
 	return null;
 }
 
+// "A styling element is a b, em, i, span, strong, or u element with no
+// attributes except possibly style, or a font element with no attributes
+// except possibly style, color, face, and/or size."
+function isStylingElement(node) {
+	if (!isHtmlElement(node)) {
+		return false;
+	}
+
+	if (["B", "EM", "I", "SPAN", "STRONG", "U"].indexOf(node.tagName) != -1) {
+		if (node.attributes.length == 0) {
+			return true;
+		}
+
+		if (node.attributes.length == 1
+		&& node.hasAttribute("style")) {
+			return true;
+		}
+	}
+
+	if (node.tagName == "FONT") {
+		var numAttrs = node.attributes.length;
+
+		if (node.hasAttribute("style")) {
+			numAttrs--;
+		}
+
+		if (node.hasAttribute("color")) {
+			numAttrs--;
+		}
+
+		if (node.hasAttribute("face")) {
+			numAttrs--;
+		}
+
+		if (node.hasAttribute("size")) {
+			numAttrs--;
+		}
+
+		if (numAttrs == 0) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
 function isSimpleStylingElement(node) {
 	// "A simple styling element is an HTML element for which at least one of
 	// the following holds:"
@@ -402,9 +462,13 @@
 		return false;
 	}
 
+	// Only these elements can possibly be a simple styling element.
+	if (["B", "EM", "FONT", "I", "SPAN", "STRONG", "U"].indexOf(node.tagName) == -1) {
+		return false;
+	}
+
 	// "It is a b, em, font, i, span, strong, or u element with no attributes."
-	if (node.attributes.length == 0
-	&& ["B", "EM", "FONT", "I", "SPAN", "STRONG", "U"].indexOf(node.tagName) != -1) {
+	if (node.attributes.length == 0) {
 		return true;
 	}
 
@@ -423,19 +487,19 @@
 		return true;
 	}
 
-	// "It is a font element with one attribute, which is either color, face,
-	// or size."
+	// "It is a font element with exactly one attribute, which is either color,
+	// face, or size."
 	if (node.tagName == "FONT"
 	&& (node.hasAttribute("color")
-		|| node.hasAttribute("face")
-		|| node.hasAttribute("size")
+	|| node.hasAttribute("face")
+	|| node.hasAttribute("size")
 	)) {
 		return true;
 	}
 
-	// "It is a b or strong element with one attribute, which is style, and the
-	// only CSS property set by the style attribute (including invalid or
-	// unrecognized properties) is "font-weight"."
+	// "It is a b or strong element with exactly one attribute, which is style,
+	// and the style attribute sets exactly one CSS property (including invalid
+	// or unrecognized properties), which is "font-weight"."
 	if ((node.tagName == "B" || node.tagName == "STRONG")
 	&& node.hasAttribute("style")
 	&& node.style.length == 1
@@ -443,9 +507,9 @@
 		return true;
 	}
 
-	// "It is an i or em element with one attribute, which is style, and the
-	// only CSS property set by the style attribute (including invalid or
-	// unrecognized properties) is "font-style"."
+	// "It is an i or em element with exactly one attribute, which is style,
+	// and the style attribute sets exactly one CSS property (including invalid
+	// or unrecognized properties), which is "font-style"."
 	if ((node.tagName == "I" || node.tagName == "EM")
 	&& node.hasAttribute("style")
 	&& node.style.length == 1
@@ -453,10 +517,10 @@
 		return true;
 	}
 
-	// "It is a u element with one attribute, which is style, and the only CSS
-	// property set by the style attribute (including invalid or unrecognized
-	// properties) is "text-decoration", which is set to "underline" or
-	// "none"."
+	// "It is a u element with exactly one attribute, which is style, and the
+	// style attribute sets exactly one CSS property (including invalid or
+	// unrecognized properties), which is "text-decoration", which is set to
+	// "underline" or "none"."
 	if (node.tagName == "U"
 	&& node.hasAttribute("style")
 	&& node.style.length == 1
@@ -769,6 +833,11 @@
 }
 
 function forceStyle(node, property, newValue) {
+	// "If node's parent is null, abort this algorithm."
+	if (!node.parentNode) {
+		return;
+	}
+
 	// "If node is an Element, Text, Comment, or ProcessingInstruction node,
 	// and is not an unwrappable element:"
 	if ((node.nodeType == Node.ELEMENT_NODE
@@ -776,28 +845,110 @@
 	|| node.nodeType == Node.COMMENT_NODE
 	|| node.nodeType == Node.PROCESSING_INSTRUCTION_NODE)
 	&& !isUnwrappableElement(node)) {
-		// "If node's previousSibling is a simple styling element whose
-		// specified style and effective style for property are both new value,
-		// append node as the last child of its previousSibling and abort this
-		// algorithm."
-		if (isSimpleStylingElement(node.previousSibling)
-		&& cssValuesEqual(property, getSpecifiedStyle(node.previousSibling, property), newValue)
-		&& cssValuesEqual(property, getEffectiveStyle(node.previousSibling, property), newValue)) {
-			node.previousSibling.appendChild(node);
-			return;
+		// "Let candidate be node's previousSibling."
+		var candidate = node.previousSibling;
+
+		// "While candidate is a styling element, and candidate has exactly one
+		// child, and that child is also a styling element, and candidate is
+		// not a simple styling element or candidate's specified style for
+		// property is not new value, set candidate to its child."
+		while (isStylingElement(candidate)
+		&& candidate.childNodes.length == 1
+		&& isStylingElement(candidate.firstChild)
+		&& (!isSimpleStylingElement(candidate)
+		|| !cssValuesEqual(property, getSpecifiedStyle(candidate, property), newValue))) {
+			candidate = candidate.firstChild;
 		}
 
-		// "If node's nextSibling is a simple styling element whose specified
-		// style and effective style for property are both new value, insert
-		// node as the first child of its nextSibling and abort this
-		// algorithm."
-		if (isSimpleStylingElement(node.nextSibling)
-		&& cssValuesEqual(property, getSpecifiedStyle(node.nextSibling, property), newValue)
-		&& cssValuesEqual(property, getEffectiveStyle(node.nextSibling, property), newValue)) {
-			node.nextSibling.insertBefore(node, node.nextSibling.childNodes.length
-				? node.nextSibling.childNodes[0]
-				: null);
-			return;
+		// "If candidate is a simple styling element whose specified style and
+		// effective style for property are both new value, and candidate is
+		// not the previousSibling of node:"
+		if (isSimpleStylingElement(candidate)
+		&& cssValuesEqual(property, getSpecifiedStyle(candidate, property), newValue)
+		&& cssValuesEqual(property, getEffectiveStyle(candidate, property), newValue)
+		&& candidate != node.previousSibling) {
+			// "While candidate has children, append the first child of
+			// candidate as the last child of candidate's parent."
+			while (candidate.childNodes.length > 0) {
+				candidate.parentNode.appendChild(candidate.firstChild);
+			}
+
+			// "Insert candidate into node's parent before node."
+			node.parentNode.insertBefore(candidate, node);
+
+			// "Append the previousSibling of candidate as the last child of
+			// candidate."
+			candidate.appendChild(candidate.previousSibling);
+		}
+
+		// "Let candidate be node's nextSibling."
+		var candidate = node.nextSibling;
+
+		// "While candidate is a styling element, and candidate has exactly one
+		// child, and that child is also a styling element, and candidate is
+		// not a simple styling element or candidate's specified style for
+		// property is not new value, set candidate to its child."
+		while (isStylingElement(candidate)
+		&& candidate.childNodes.length == 1
+		&& isStylingElement(candidate.firstChild)
+		&& (!isSimpleStylingElement(candidate)
+		|| !cssValuesEqual(property, getSpecifiedStyle(candidate, property), newValue))) {
+			candidate = candidate.firstChild;
+		}
+
+		// "If candidate is a simple styling element whose specified style and
+		// effective style for property are both new value, and candidate is
+		// not the nextSibling of node:"
+		if (isSimpleStylingElement(candidate)
+		&& cssValuesEqual(property, getSpecifiedStyle(candidate, property), newValue)
+		&& cssValuesEqual(property, getEffectiveStyle(candidate, property), newValue)
+		&& candidate != node.nextSibling) {
+			// "While candidate has children, append the first child of
+			// candidate as the last child of candidate's parent."
+			while (candidate.childNodes.length > 0) {
+				candidate.parentNode.appendChild(candidate.firstChild);
+			}
+
+			// "Insert candidate into node's parent after node."
+			node.parentNode.insertBefore(candidate, node.nextSibling);
+
+			// "Append the nextSibling of candidate as the last child of
+			// candidate."
+			candidate.appendChild(candidate.nextSibling);
+		}
+
+		// "Let previous sibling and next sibling be node's previousSibling and
+		// nextSibling."
+		var previousSibling = node.previousSibling;
+		var nextSibling = node.nextSibling;
+
+		// "If previous sibling is a simple styling element whose specified
+		// style and effective style for property are both new value, append
+		// node as the last child of previous sibling."
+		if (isSimpleStylingElement(previousSibling)
+		&& cssValuesEqual(property, getSpecifiedStyle(previousSibling, property), newValue)
+		&& cssValuesEqual(property, getEffectiveStyle(previousSibling, property), newValue)) {
+			previousSibling.appendChild(node);
+		}
+
+		// "If next sibling is a simple styling element whose specified style
+		// and effective style for property are both new value:"
+		if (isSimpleStylingElement(nextSibling)
+		&& cssValuesEqual(property, getSpecifiedStyle(nextSibling, property), newValue)
+		&& cssValuesEqual(property, getEffectiveStyle(nextSibling, property), newValue)) {
+			// "If node is not a child of previous sibling, insert node as the
+			// first child of next sibling."
+			if (node.parentNode != previousSibling) {
+				nextSibling.insertBefore(node, nextSibling.firstChild);
+			// "Otherwise, while next sibling has children, append the first
+			// child of next sibling as the last child of previous sibling.
+			// Then remove next sibling from its parent."
+			} else {
+				while (nextSibling.childNodes.length) {
+					previousSibling.appendChild(nextSibling.firstChild);
+				}
+				nextSibling.parentNode.removeChild(nextSibling);
+			}
 		}
 	}
 
@@ -847,25 +998,62 @@
 		return;
 	}
 
-	// "If property is "font-weight" and new value is "bold", let tag be "b"."
-	var tag;
-	if (property == "fontWeight" && (newValue == "bold" || newValue == "700")) {
-		tag = "b";
-	// "If property is "font-style" and new value is "italic", let tag be "i"."
-	} else if (property == "fontStyle" && newValue == "italic") {
-		tag = "i";
-	// "If property is "text-decoration" and new value is "underline", let tag
-	// be "u"."
-	} else if (property == "textDecoration" && newValue == "underline") {
-		tag = "u";
-	// "If tag is not set, let tag be "span"."
-	} else {
-		tag = "span";
+	// "Let new parent be null."
+	var newParent = null;
+
+	// "If the CSS styling flag is false:"
+	if (!cssStylingFlag) {
+		// "If property is "font-weight" and new value is "bold", let new
+		// parent be the result of calling createElement("b") on the
+		// ownerDocument of node."
+		if (property == "fontWeight" && (newValue == "bold" || newValue == "700")) {
+			newParent = node.ownerDocument.createElement("b");
+		}
+
+		// "If property is "font-style" and new value is "italic", let new
+		// parent be the result of calling createElement("i") on the
+		// ownerDocument of node."
+		if (property == "fontStyle" && newValue == "italic") {
+			newParent = node.ownerDocument.createElement("i");
+		}
+
+		// "If property is "text-decoration" and new value is "underline", let
+		// new parent be the result of calling createElement("u") on the
+		// ownerDocument of node."
+		if (property == "textDecoration" && newValue == "underline") {
+			newParent = node.ownerDocument.createElement("u");
+		}
+
+		// "If property is "color", let new parent be the result of calling
+		// createElement("font") on the ownerDocument of node, then set the
+		// color attribute of new parent to new value."
+		if (property == "color") {
+			newParent = node.ownerDocument.createElement("font");
+			newParent.color = newValue;
+		}
+
+		// "If property is "font-family", let new parent be the result of
+		// calling createElement("font") on the ownerDocument of node, then set
+		// the face attribute of new parent to new value."
+		if (property == "fontFamily") {
+			newParent = node.ownerDocument.createElement("font");
+			newParent.face = newValue;
+		}
+
+		// "If property is "font-size", let new parent be the result of calling
+		// createElement("font") on the ownerDocument of node, then set the
+		// size attribute of new parent to new value."
+		if (property == "fontSize") {
+			newParent = node.ownerDocument.createElement("font");
+			newParent.size = newValue;
+		}
 	}
 
-	// "Let new parent be the result of calling createElement(tag) on the
-	// ownerDocument of node."
-	var newParent = node.ownerDocument.createElement(tag);
+	// "If new parent is null, let new parent be the result of calling
+	// createElement("span") on the ownerDocument of node."
+	if (!newParent) {
+		newParent = node.ownerDocument.createElement("span");
+	}
 
 	// "Insert new parent in node's parent before node."
 	node.parentNode.insertBefore(newParent, node);
@@ -883,14 +1071,41 @@
 	// not new value:"
 	if (node.nodeType == Node.ELEMENT_NODE
 	&& !cssValuesEqual(property, getEffectiveStyle(node, property), newValue)) {
-		// "Set the CSS property property of node to new value."
-		node.style[property] = newValue;
-
 		// "Insert node into the parent of new parent before new parent."
 		newParent.parentNode.insertBefore(node, newParent);
 
 		// "Remove new parent from its parent."
 		newParent.parentNode.removeChild(newParent);
+
+		// "If new parent is a span, set the CSS property property of node to
+		// new value."
+		if (newParent.tagName == "SPAN") {
+			node.style[property] = newValue;
+
+			// "Otherwise:"
+		} else {
+			// "Let children be all children of node, omitting any that are
+			// Elements whose specified style for property is neither null nor
+			// equal to new value."
+			var children = [];
+			for (var i = 0; i < node.childNodes.length; i++) {
+				if (node.childNodes[i].nodeType == Node.ELEMENT_NODE) {
+					var specifiedStyle = getSpecifiedStyle(node.childNodes[i], property);
+
+					if (specifiedStyle !== null
+					&& !cssValuesEqual(property, newValue, specifiedStyle)) {
+						continue;
+					}
+				}
+				children.push(node.childNodes[i]);
+			}
+
+			// "Force the style of each Node in children, with property and new
+			// value as in this invocation of the algorithm."
+			for (var i = 0; i < children.length; i++) {
+				forceStyle(children[i], property, newValue);
+			}
+		}
 	}
 }
 
@@ -964,12 +1179,14 @@
 function myExecCommand(commandId, showUI, value, range) {
 	commandId = commandId.toLowerCase();
 
-	if (typeof range == "undefined") {
-		range = getActiveRange(document);
-	}
+	if (commandId != "stylewithcss" && commandId != "usecss") {
+		if (typeof range == "undefined") {
+			range = getActiveRange(document);
+		}
 
-	if (!range) {
-		return;
+		if (!range) {
+			return;
+		}
 	}
 
 	switch (commandId) {
@@ -1108,6 +1325,12 @@
 		}
 		break;
 
+		case "stylewithcss":
+		// "Convert value to a boolean according to the algorithm in WebIDL,
+		// and set the CSS styling flag to the result."
+		cssStylingFlag = Boolean(value);
+		break;
+
 		case "underline":
 		// "Decompose the Range. If the state of the Range for this command is
 		// then true, style each returned Node with property "text-decoration"
@@ -1120,6 +1343,12 @@
 		}
 		break;
 
+		case "usecss":
+		// "Convert value to a boolean according to the algorithm in WebIDL,
+		// and set the CSS styling flag to the negation of the result."
+		cssStylingFlag = !value;
+		break;
+
 		default:
 		break;
 	}
@@ -1137,6 +1366,10 @@
 }
 
 function getState(commandId, range) {
+	if (commandId == "stylewithcss") {
+		return cssStylingFlag;
+	}
+
 	if (commandId != "bold"
 	&& commandId != "italic"
 	&& commandId != "underline") {
--- a/source.html	Wed Mar 23 11:59:33 2011 -0600
+++ b/source.html	Wed Mar 23 16:03:54 2011 -0600
@@ -197,7 +197,9 @@
 
   <li>Return the last [[range]] in <var>selection</var> whose [[rangestart]]
   is <var>start</var>.
-  <!-- This is what Firefox seems to do, no reason to change it . . . -->
+
+  <p class=XXX>Double-check that this is what Firefox actually does.  It seems
+  pretty baroque.
 
   <p class=note>In user agents that support only one [[range]] per
   [[selection]], the active range is simply the only one in the selection.
@@ -205,7 +207,16 @@
 
 <p>An <dfn>unwrappable element</dfn> is an <span>HTML element</span> which may
 not be used where only [[phrasingcontent]] is expected (not counting unknown or
-obsolete elements, which cannot be used at all).
+obsolete elements, which cannot be used at all); or any [[element]] whose
+display property computes to something other than "inline", "inline-block", or
+"inline-table".
+
+<p class=XXX>Currently when we hit an unwrappable element, we ignore it and
+style its children.  Alternatively, if we would otherwise create a span with a
+style element on it, maybe we could put the style element directly on the
+unwrappable element.  This would produce shorter markup in many cases, but
+would also cause problems for things like background-color that do something
+different on block elements.
 
 <p>The <dfn>effective style</dfn> of a [[node]] <var>node</var> for a given
 <var>property</var> is returned by the following algorithm, which will return
@@ -281,6 +292,19 @@
   <li>Return null.
 </ol>
 
+<p>A <dfn>styling element</dfn> is a [[b]], [[em]], [[i]], [[span]],
+[[strong]], or [[u]] element with no attributes except possibly [[style]], or
+a [[font]] element with no attributes except possibly [[style]], [[fontcolor]],
+[[fontface]], and/or [[fontsize]].
+
+<p class=note>Conceptually, a <span>styling element</span> is a phrasing
+element whose only purpose is to style text.  Thus changing around styling
+elements (such as moving them, breaking them up, deleting them, or switching
+them with other styling elements) is fine as long as the style is preserved.
+In fact, some styling elements are supposed to have semantics other than
+styling, but the algorithms here treat them as styling elements anyway, for
+compatibility with legacy user-agents that treat them that way.
+
 <p>A <dfn>simple styling element</dfn> is an <span>HTML element</span> for
 which at least one of the following holds:
 
@@ -292,30 +316,36 @@
   element with exactly one attribute, which is [[style]], which sets no CSS
   properties (including invalid or unrecognized properties).
 
-  <li>It is a [[font]] element with one attribute, which is either
+  <li>It is a [[font]] element with exactly one attribute, which is either
   [[fontcolor]], [[fontface]], or [[fontsize]].
 
-  <li>It is a [[b]] or [[strong]] element with one attribute, which is
-  [[style]], and the only CSS property set by the [[style]] attribute
-  (including invalid or unrecognized properties) is "font-weight".
+  <li>It is a [[b]] or [[strong]] element with exactly one attribute, which is
+  [[style]], and the [[style]] attribute sets exactly one CSS property
+  (including invalid or unrecognized properties), which is "font-weight".
 
-  <li>It is an [[i]] or [[em]] element with one attribute, which is [[style]],
-  and the only CSS property set by the [[style]] attribute (including invalid
-  or unrecognized properties) is "font-style".
+  <li>It is an [[i]] or [[em]] element with exactly one attribute, which is
+  [[style]], and the [[style]] attribute sets exactly one CSS property
+  (including invalid or unrecognized properties), which is "font-style".
 
-  <li>It is a [[u]] element with one attribute, which is [[style]], and the
-  only CSS property set by the [[style]] attribute (including invalid or
-  unrecognized properties) is "text-decoration", which is set to "underline" or
-  "none".
+  <li>It is a [[u]] element with exactly one attribute, which is [[style]], and
+  the [[style]] attribute sets exactly one CSS property (including invalid or
+  unrecognized properties), which is "text-decoration", which is set to
+  "underline" or "none".
 
   <li>It is a [[font]] or [[span]] element with exactly one attribute, which is
   [[style]], and the [[style]] attribute sets exactly one CSS property
   (including invalid or unrecognized properties).
 </ol>
 
-<p class=note>Conceptually, a <span>simple styling element</span> is an element
-which has a <span>specified style</span> for exactly one CSS property, but does
-nothing else.
+<p class=note>Conceptually, a <span>simple styling element</span> is a
+<span>styling element</span> which <span title="specified
+style">specifies</span> at most one CSS property.
+
+<p>The <dfn>CSS styling flag</dfn> is a boolean flag, which must initially be
+false.
+
+<p class=XXX>Is the styling flag associated with the document, with the editing
+host, what?  Needs reverse-engineering.
 
 <p>When the user agent is instructed to run a particular method, it must follow
 the steps defined for that method in the appropriate specification, not act as
@@ -472,7 +502,7 @@
     set.
   </ol>
 
-  <li>If <var>element</var>'s <var>specified style</var> for
+  <li>If <var>element</var>'s <span>specified style</span> for
   <var>property</var> is null, return the empty list.
   <!-- If we get past this step, we're something like <b class=foo> where we
   want to keep the extra attributes, so we stick them on a span. -->
@@ -498,6 +528,32 @@
 <var>node</var>, given a CSS property name <var>property</var> and a new value
 <var>new value</var>, it must run the following steps:
 
+<!-- This algorithm goes up to just below the nearest ancestor with the right
+style, then re-applies the bad styles repeatedly going down, omitting the
+things we want to have the new style.  This is basically what WebKit does,
+although WebKit sometimes starts higher up and therefore makes more intrusive
+changes, often creating more markup.
+
+Gecko instead seems to start breaking up elements from the bottom, so that the
+range consists of a few consecutive siblings, and it can then break up the
+problematic element into a maximum of two pieces.  The spec's approach seems to
+create fewer elements and simpler markup (or at least markup that's no more
+complex) in every case I throw at it.
+
+Gecko's approach does have the major advantage that it gets underlines right in
+many cases for free.  E.g.,
+
+  <u>foo<font color=red>[bar]baz</font></u>
+  -> <u>foo</u><font color=red>bar<u>baz</u></font> (spec)
+  -> <u>foo</u><font color=red>bar</font><u><font color=red>baz</font></u> (Gecko)
+
+The spec's markup here is much shorter and contains fewer elements, but is
+wrong: the underline under "baz" has changed color from black to red.  It might
+be worth trying to copy Gecko's results in such cases, but that won't solve all
+underline problems, so perhaps it's not worth it.
+
+Opera also seems to break up the markup surrounding the range, but even more
+aggressively: even if it doesn't need to pull down styles. -->
 <ol>
   <li>If <var>node</var>'s [[parent]] is not an [[element]], abort this
   algorithm. <!-- E.g., a text node child of a document fragment. -->
@@ -584,32 +640,36 @@
 After forcing the style, descendants might still have different style.
 
 <ol>
-  <!-- Even if the style matches, we stick it in a preceding sibling if
-  possible.  This ensures "a<cite>b</cite>c" -> "<i>a<cite>b</cite>c</i>"
-  instead of "<i>a</i><cite>b</cite><i>c</i>". -->
+  <li>If <var>node</var>'s [[parent]] is null, abort this algorithm.
+
   <li>If <var>node</var> is an [[element]], [[text]], [[comment]], or
   [[processinginstruction]] node, and is not an <span>unwrappable
   element</span>:
 
   <ol>
-    <!-- Handle cases like bolding "bar" in "<i><b>Foo</b></i>bar".
+    <!-- Even if the style matches, we stick it in a preceding sibling if
+    possible.  This ensures "a<cite>b</cite>c" -> "<i>a<cite>b</cite>c</i>"
+    instead of "<i>a</i><cite>b</cite><i>c</i>".  While we're at it, we also
+    handle more elaborate cases like <b>foo</b>[bar]<b>baz</b> and even
+    <i><b>foo</b></i>[bar]<i><b>baz</b></i> (the latter becomes
+    <b><i>foo</i>bar<i>baz</i></b>).
+
     Theoretically this algorithm could pointlessly reorganize the DOM in the
     event of unreasonable style rules, but it's not a big enough deal for us to
-    care, probably. -->
-    <!-- This is probably more complicated than we want, given how likely this
-    is to come up, so I've commented it out for now.
+    care, since the resulting style will still be right. -->
     <li>Let <var>candidate</var> be <var>node</var>'s [[previoussibling]].
 
-    <li>While <var>candidate</var> is a <span>simple styling element</span>,
-    and <var>candidate</var> has exactly one [[child]], and that [[child]] is
-    also a <span>simple styling element</span>, and <var>candidate</var>'s
+    <li>While <var>candidate</var> is a <span>styling element</span>, and
+    <var>candidate</var> has exactly one [[child]], and that [[child]] is also
+    a <span>styling element</span>, and <var>candidate</var> is not a
+    <span>simple styling element</span> or <var>candidate</var>'s
     <span>specified style</span> for <var>property</var> is not <var>new
     value</var>, set <var>candidate</var> to its [[child]].
 
     <li>If <var>candidate</var> is a <span>simple styling element</span> whose
-    <span>specified style</span> and computed style for <var>property</var> are
-    both <var>new value</var>, and <var>candidate</var> is not the
-    [[previoussibling]] of <var>node</var>:
+    <span>specified style</span> and <span>effective style</span> for
+    <var>property</var> are both <var>new value</var>, and <var>candidate</var>
+    is not the [[previoussibling]] of <var>node</var>:
 
     <ol>
       <li>While <var>candidate</var> has [[children]], append the first
@@ -622,27 +682,20 @@
       <li>Append the [[previoussibling]] of <var>candidate</var> as the last
       [[child]] of <var>candidate</var>.
     </ol>
-    -->
 
-    <li>If <var>node</var>'s [[previoussibling]] is a <span>simple styling
-    element</span> whose <span>specified style</span> and <span>effective
-    style</span> for <var>property</var> are both <var>new value</var>, append
-    <var>node</var> as the last [[child]] of its [[previoussibling]] and abort
-    this algorithm.
-
-    <!--
     <li>Let <var>candidate</var> be <var>node</var>'s [[nextsibling]].
 
-    <li>While <var>candidate</var> is a <span>simple styling element</span>,
-    and <var>candidate</var> has exactly one [[child]], and that [[child]] is
-    also a <span>simple styling element</span>, and <var>candidate</var>'s
+    <li>While <var>candidate</var> is a <span>styling element</span>, and
+    <var>candidate</var> has exactly one [[child]], and that [[child]] is also
+    a <span>styling element</span>, and <var>candidate</var> is not a
+    <span>simple styling element</span> or <var>candidate</var>'s
     <span>specified style</span> for <var>property</var> is not <var>new
     value</var>, set <var>candidate</var> to its [[child]].
 
     <li>If <var>candidate</var> is a <span>simple styling element</span> whose
-    <span>specified style</span> and computed style for <var>property</var> are
-    both <var>new value</var>, and <var>candidate</var> is not the
-    [[nextsibling]] of <var>node</var>:
+    <span>specified style</span> and <span>effective style</span> for
+    <var>property</var> are both <var>new value</var>, and <var>candidate</var>
+    is not the [[nextsibling]] of <var>node</var>:
 
     <ol>
       <li>While <var>candidate</var> has [[children]], append the first
@@ -655,13 +708,28 @@
       <li>Append the [[nextsibling]] of <var>candidate</var> as the last
       [[child]] of <var>candidate</var>.
     </ol>
-    -->
 
-    <li>If <var>node</var>'s [[nextsibling]] is a <span>simple styling
-    element</span> whose <span>specified style</span> and <span>effective
-    style</span> for <var>property</var> are both <var>new value</var>, insert
-    <var>node</var> as the first [[child]] of its [[nextsibling]] and abort
-    this algorithm.
+    <li>Let <var>previous sibling</var> and <var>next sibling</var> be
+    <var>node</var>'s [[previoussibling]] and [[nextsibling]].
+
+    <li>If <var>previous sibling</var> is a <span>simple styling element</span>
+    whose <span>specified style</span> and <span>effective style</span> for
+    <var>property</var> are both <var>new value</var>, append <var>node</var>
+    as the last [[child]] of <var>previous sibling</var>.
+
+    <li>If <var>next sibling</var> is a <span>simple styling element</span>
+    whose <span>specified style</span> and <span>effective style</span> for
+    <var>property</var> are both <var>new value</var>:
+
+    <ol>
+      <li>If <var>node</var> is not a [[child]] of <var>previous sibling</var>,
+      insert <var>node</var> as the first [[child]] of <var>next sibling</var>.
+
+      <li>Otherwise, while <var>next sibling</var> has [[children]], append the
+      first [[child]] of <var>next sibling</var> as the last [[child]] of
+      <var>previous sibling</var>.  Then remove <var>next sibling</var> from
+      its [[parent]].
+    </ol>
   </ol>
 
   <li>If the <span>effective style</span> of <var>property</var> is <var>new
@@ -689,20 +757,55 @@
   <li>If the <span>effective style</span> of <var>property</var> is <var>new
   value</var> on <var>node</var>, abort this algorithm.
 
-  <li>If <var>property</var> is "font-weight" and <var>new value</var> is
-  "bold", let <var>tag</var> be "b".
-
-  <li>If <var>property</var> is "font-style" and <var>new value</var> is
-  "italic", let <var>tag</var> be "i".
+  <li>Let <var>new parent</var> be null.
 
-  <li>If <var>property</var> is "text-decoration" and <var>new value</var> is
-  "underline", let <var>tag</var> be "u".
+  <li>If the <span>CSS styling flag</span> is false:
 
-  <li>If <var>tag</var> is not set, let <var>tag</var> be "span".
+  <ol>
+    <li>If <var>property</var> is "font-weight" and <var>new value</var> is
+    "bold", let <var>new parent</var> be the result of calling <code
+    data-anolis-spec=domcore
+    title=dom-Document-createElement>createElement("b")</code> on the
+    [[ownerdocument]] of <var>node</var>.
 
-  <li>Let <var>new parent</var> be the result of calling <code
-  data-anolis-spec=domcore
-  title=dom-Document-createElement>createElement(<var>tag</var>)</code> on the
+    <li>If <var>property</var> is "font-style" and <var>new value</var> is
+    "italic", let <var>new parent</var> be the result of calling <code
+    data-anolis-spec=domcore
+    title=dom-Document-createElement>createElement("i")</code> on the
+    [[ownerdocument]] of <var>node</var>.
+
+    <li>If <var>property</var> is "text-decoration" and <var>new value</var> is
+    "underline", let <var>new parent</var> be the result of calling <code
+    data-anolis-spec=domcore
+    title=dom-Document-createElement>createElement("u")</code> on the
+    [[ownerdocument]] of <var>node</var>.
+
+    <li>If <var>property</var> is "color", let <var>new parent</var> be the
+    result of calling <code data-anolis-spec=domcore
+    title=dom-Document-createElement>createElement("font")</code> on the
+    [[ownerdocument]] of <var>node</var>, then set the [[fontcolor]] attribute
+    of <var>new parent</var> to <var>new value</var>.
+
+    <p class=XXX>This can be wrong if, e.g., the color has an alpha channel.
+    We also need to specify exactly how it's serialized, since CSSOM doesn't
+    cover this.  It might be best to always output simple colors.
+
+    <li>If <var>property</var> is "font-family", let <var>new parent</var> be
+    the result of calling <code data-anolis-spec=domcore
+    title=dom-Document-createElement>createElement("font")</code> on the
+    [[ownerdocument]] of <var>node</var>, then set the [[fontface]] attribute
+    of <var>new parent</var> to <var>new value</var>.
+
+    <li>If <var>property</var> is "font-size", let <var>new parent</var> be the
+    result of calling <code data-anolis-spec=domcore
+    title=dom-Document-createElement>createElement("font")</code> on the
+    [[ownerdocument]] of <var>node</var>, then set the [[fontsize]] attribute
+    of <var>new parent</var> to <var>new value</var>.
+  </ol>
+
+  <li>If <var>new parent</var> is null, let <var>new parent</var> be the result
+  of calling <code data-anolis-spec=domcore
+  title=dom-Document-createElement>createElement("span")</code> on the
   [[ownerdocument]] of <var>node</var>.
 
   <li>Insert <var>new parent</var> in <var>node</var>'s [[parent]] before
@@ -718,13 +821,25 @@
   of <var>property</var> for <var>node</var> is not <var>new value</var>:
 
   <ol>
-    <li>Set the CSS property <var>property</var> of <var>node</var> to <var>new
-    value</var>.
-
     <li>Insert <var>node</var> into the [[parent]] of <var>new parent</var>
     before <var>new parent</var>.
 
     <li>Remove <var>new parent</var> from its [[parent]].
+
+    <li>If <var>new parent</var> is a [[span]], set the CSS property
+    <var>property</var> of <var>node</var> to <var>new value</var>.
+
+    <li>Otherwise:
+
+    <ol>
+      <li>Let <var>children</var> be all [[children]] of <var>node</var>,
+      omitting any that are [[element]]s whose <span>specified style</span> for
+      <var>property</var> is neither null nor equal to <var>new value</var>.
+
+      <li><span>Force the style</span> of each [[node]] in <var>children</var>,
+      with <var>property</var> and <var>new value</var> as in this invocation
+      of the algorithm.
+    </ol>
   </ol>
 </ol>
 
@@ -835,7 +950,7 @@
 
   <li>Let <var>children</var> be the [[children]] of <var>node</var>.
 
-  <li><span>Style</span> each member of [[children]].
+  <li><span>Style</span> each member of <var>children</var>.
 
   <p class=note>Styling a node involves clearing its styles, which can remove
   it from the tree.  Implementers should be careful to compute the list of
@@ -1167,6 +1282,20 @@
 <dd><p><strong>Value</strong>: Always the empty string.
 
 
+<dt><code title><dfn title=command-stylewithcss>styleWithCSS</dfn></code>
+
+<dd><p><strong>Action</strong>: Convert <var>value</var> to a boolean according
+to the algorithm in WebIDL, and set the <span>CSS styling flag</span> to the
+result.
+
+<p class=XXX>Properly cross-reference.
+
+<dd><p><strong>State</strong>: True if the <span>CSS styling flag</span> is
+true, otherwise false.
+
+<dd><p><strong>Value</strong>:
+
+
 <dt><code title><dfn title=command-underline>underline</dfn></code>
 
 <dd><p><strong>Action</strong>: <span>Decompose</span> the [[range]].  If the
@@ -1216,7 +1345,6 @@
 style</span> either null or "underline" for text-decoration.  Otherwise false.
 
 <dd><p><strong>Value</strong>: Always the empty string.
-</dl>
 
 
 <dt><code title><dfn title=command-unlink>unlink</dfn></code>
@@ -1266,6 +1394,24 @@
 <dd><p><strong>Value</strong>: Always the empty string.
 
 
+<dt><code title><dfn title=command-usecss>useCSS</dfn></code>
+
+<dd><p><strong>Action</strong>: Convert <var>value</var> to a boolean according
+to the algorithm in WebIDL, and set the <span>CSS styling flag</span> to the
+negation of the result.  Since the effect of this command is the opposite of
+what one would expect, user agents are encouraged to point authors to <code
+title=command-stylewithcss>styleWithCSS</code> when <code
+title=command-usecss>useCSS</code> is used, such as by logging a warning to an
+error console.
+
+<p class=XXX>Properly cross-reference.
+
+<dd><p><strong>State</strong>:
+
+<dd><p><strong>Value</strong>:
+</dl>
+
+
 <h2 class=no-num id=references>References</h2><!--REFS-->
 <p>All references are normative unless marked "Non-normative".</p>
 <div id=anolis-references></div>