Rewrite everything again
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Thu, 17 Mar 2011 16:14:25 -0600
changeset 16 b3393741ce02
parent 15 9b6a8f5e5622
child 17 7d1fa07e4e60
Rewrite everything again

The algorithm is now much better: it's a lot shorter (if you discount
the newly-added documentation) and produces much better results.
Currently we're back to only handling bold and italic, however. I'll
have to redo other properties, which will be tricky in some cases (like
underline and hiliteColor).
autoimplementation.html
editcommands.html
implementation.js
preprocess
source.html
--- a/autoimplementation.html	Tue Mar 15 22:39:50 2011 -0600
+++ b/autoimplementation.html	Thu Mar 17 16:14:25 2011 -0600
@@ -1,7 +1,31 @@
 <!doctype html>
-<table border=1>
-	<tr><th>Input <th>Spec <th>Browser
-</table>
+<title>Auto-running execCommand() bold tests</title>
+<style>
+.yes { color: green }
+.no { color: red }
+b, strong { font-weight: bold }
+.bold { font-weight: bold }
+.notbold { font-weight: normal }
+td > div:first-child {
+	padding-bottom: 0.2em;
+	border-bottom: 1px solid black;
+}
+td > div:last-child {
+	padding-top: 0.2em;
+}
+</style>
+<p>Legend: {[ are the selection anchor, }] are the selection focus, {}
+represent an element boundary point, [] represent a text node boundary point.
+Syntax and some of the tests taken from <a
+href=http://www.browserscope.org/richtext2/test>Browserscope</a>.
+
+<p><label>Enter new test here: <input></label>
+<button onclick="addTest(document.querySelector('input').value)">Add test</button>
+
+<div>
+<table border=1><tr><th>Input <th>Spec <th>Browser <th>Same?</table>
+</div>
+
 <script src=implementation.js></script>
 <script>
 // Note: this data format can only yield selections whose start and end are
@@ -26,37 +50,139 @@
 	'<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'
+	'foo bar <b>baz [qoz</b> quz] sic',
+	'<b id=foo>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',
+	'foo<span style="font-weight: 900">[bar]</span>baz',
+	'foo<span style="font-weight: normal">[bar]</span>baz',
+	'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',
+	'foo<span style="font-weight: 900">[bar</span>]baz',
+	'foo<span style="font-weight: normal">[bar</span>]baz',
+	'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',
+	'foo[<span style="font-weight: 900">bar]</span>baz',
+	'foo[<span style="font-weight: normal">bar]</span>baz',
+	'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',
+	'foo[<span style="font-weight: 900">bar</span>]baz',
+	'foo[<span style="font-weight: normal">bar</span>]baz',
+	'<span style="font-weight: 100">foo[bar]baz</span>',
+	'<span style="font-weight: 400">foo[bar]baz</span>',
+	'<span style="font-weight: 700">foo[bar]baz</span>',
+	'<span style="font-weight: 900">foo[bar]baz</span>',
+	'<span style="font-weight: normal">foo[bar]baz</span>',
+	'{<span style="font-weight: 100">foobar]baz</span>',
+	'{<span style="font-weight: 400">foobar]baz</span>',
+	'{<span style="font-weight: 700">foobar]baz</span>',
+	'{<span style="font-weight: 900">foobar]baz</span>',
+	'{<span style="font-weight: normal">foobar]baz</span>',
+	'<span style="font-weight: 100">foo[barbaz</span>}',
+	'<span style="font-weight: 400">foo[barbaz</span>}',
+	'<span style="font-weight: 700">foo[barbaz</span>}',
+	'<span style="font-weight: 900">foo[barbaz</span>}',
+	'<span style="font-weight: normal">foo[barbaz</span>}',
+	'<h3>Foo[bar]baz</h3>',
+	'{<h3>Foobar]baz</h3>',
+	'<h3>Foo[barbaz</h3>}',
+	'<h3>[Foobarbaz]</h3>',
+	'{<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>',
+	'{<p><p> <p>Foo</p>}',
+	'[Foo<span class=notbold>bar</span>baz]',
 ];
 
-var table = document.querySelector("table");
-table.contentEditable = "true";
 for (var i = 0; i < tests.length; i++) {
+	addTest(tests[i]);
+}
+
+function addTest(test) {
+	document.querySelector("div").contentEditable = "true";
+	var table = document.querySelector("table");
+
 	var tr = document.createElement("tr");
-	table.appendChild(tr);
+	// Insert at the top, because Chrome debugger doesn't let you scroll down
+	// while you're stopped at a breakpoint . . .
+	//if (table.childNodes.length == 1) {
+		table.appendChild(tr);
+	//} else {
+	//	table.insertBefore(tr, table.childNodes[1]);
+	//}
 
 	var inputCell = document.createElement("td");
-	inputCell.textContent = tests[i];
+	inputCell.innerHTML = test;
+	inputCell.innerHTML = "<div>" + inputCell.innerHTML + "</div><div>" + inputCell.innerHTML.replace(/\&/g, "&amp;").replace(/</g, "&lt;") + "</div>";
 	tr.appendChild(inputCell);
 
 	var specCell = document.createElement("td");
-	specCell.innerHTML = tests[i];
+	specCell.innerHTML = test;
 	tr.appendChild(specCell);
-	selectBrackets(specCell);
-	myExecCommand("bold");
-	specCell.textContent = specCell.innerHTML;
+	try {
+		selectBrackets(specCell);
+		myExecCommand("bold");
+		specCell.innerHTML = "<div>" + specCell.innerHTML + "</div><div>" + specCell.innerHTML.replace(/\&/g, "&amp;").replace(/</g, "&lt;") + "</div>";
+	} catch (e) {
+		specCell.textContent = "Exception: " + e;
+	}
 
 	var browserCell = document.createElement("td");
-	browserCell.innerHTML = tests[i];
+	browserCell.innerHTML = test;
 	tr.appendChild(browserCell);
-	selectBrackets(browserCell);
-	document.execCommand("bold", null, false);
-	browserCell.textContent = browserCell.innerHTML;
+	try {
+		selectBrackets(browserCell);
+		try {
+			document.execCommand("styleWithCSS", null, false);
+		} catch (e) {
+			// IE, we don't care
+		}
+		document.execCommand("bold", null, false);
+		browserCell.innerHTML = "<div>" + browserCell.innerHTML + "</div><div>" + browserCell.innerHTML.replace(/\&/g, "&amp;").replace(/</g, "&lt;") + "</div>";
+	} catch (e) {
+		browserCell.textContent = "Exception: " + e;
+	}
+
+	var sameCell = document.createElement("td");
+	// Ad hoc normalization to avoid basically spurious mismatches
+	var normalizedSpecCell = specCell.lastChild.textContent
+		.replace(/;? ?"/g, '"');
+	var normalizedBrowserCell = browserCell.lastChild.textContent
+		.replace(/;? ?"/g, '"')
+		.replace(/ class="Apple-style-span"/g, "");
+	if (normalizedSpecCell == normalizedBrowserCell) {
+		sameCell.className = "yes";
+		sameCell.innerHTML = "&#x2713;";
+	} else {
+		sameCell.className = "no";
+		sameCell.innerHTML = "&#x2717;";
+	}
+	tr.appendChild(sameCell);
+
+	getSelection().removeAllRanges();
+	document.querySelector("div").contentEditable = "inherit";
 }
-getSelection().removeAllRanges();
-table.contentEditable = false;
 
 function selectBrackets(node) {
+	if (node.textContent.replace(/[^{[]/g, "").length != 1) {
+		throw "Need one [ or {, found " + node.textContent.replace(/[^{[]/g, "").length;
+	}
+
+	if (node.textContent.replace(/[^}\]]/g, "").length != 1) {
+		throw "Need one ] or }, found " + node.textContent.replace(/[^}\]]/g, "").length;
+	}
+
 	var startNode, startOffset, endNode, endOffset;
 
 	var cur = node;
@@ -123,7 +249,18 @@
 		}
 	}
 
-	if ("extend" in getSelection()) {
+	if (navigator.userAgent.indexOf("Opera") != -1) {
+		// Yes, browser sniffing is evil, but I can't be bothered to debug
+		// Opera.
+		var range = document.createRange();
+		range.setStart(startNode, startOffset);
+		range.setEnd(endNode, endOffset);
+		if (range.collapsed) {
+			range.setEnd(startNode, startOffset);
+		}
+		getSelection().removeAllRanges();
+		getSelection().addRange(range);
+	} else if ("extend" in getSelection()) {
 		// WebKit behaves unreasonably for collapse(), so do that manually.
 		var range = document.createRange();
 		range.setStart(startNode, startOffset);
@@ -132,16 +269,22 @@
 		getSelection().extend(endNode, endOffset);
 	} else {
 		// IE9.  Selections have no direction, so we just make the selection
-		// always forwards;
-		var range = document.createRange();
+		// always forwards.
+		var range;
+		if (getSelection().rangeCount) {
+			range = getSelection().getRangeAt(0);
+		} else {
+			range = document.createRange();
+		}
 		range.setStart(startNode, startOffset);
 		range.setEnd(endNode, endOffset);
 		if (range.collapsed) {
 			// Phooey, we got them backwards.
 			range.setEnd(startNode, startOffset);
 		}
-		getSelection().removeAllRanges();
-		getSelection().addRange(range);
+		if (!getSelection().rangeCount) {
+			getSelection().addRange(range);
+		}
 	}
 }
 </script>
--- a/editcommands.html	Tue Mar 15 22:39:50 2011 -0600
+++ b/editcommands.html	Thu Mar 17 16:14:25 2011 -0600
@@ -22,11 +22,12 @@
    white-space: pre-wrap;
  }
  div.note > p:first-child::before { content: 'Note: '; }
+ div + * > li { margin: 1em 0 }
 </style>
 <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-15-march-2011>Work in Progress &mdash; Last Update 15 March 2011</h2>
+<h2 class="no-num no-toc" id=work-in-progress-&mdash;-last-update-17-march-2011>Work in Progress &mdash; Last Update 17 March 2011</h2>
 <dl>
  <dt>Editor
  <dd>Aryeh Gregor &lt;ayg+spec@aryeh.name&gt;
@@ -56,15 +57,12 @@
  <li><a href=#introduction><span class=secno>1 </span>Introduction</a></li>
  <li><a href=#issues><span class=secno>2 </span>Issues</a></li>
  <li><a href=#definitions><span class=secno>3 </span>Definitions</a></li>
- <li><a href=#decomposing-a-range-into-nodes><span class=secno>4 </span>Decomposing a Range into Nodes</a></li>
+ <li><a href=#decomposing-a-range-into-nodes><span class=secno>4 </span>Decomposing a range into nodes</a></li>
  <li><a href="#clearing-an-element's-styles"><span class=secno>5 </span>Clearing an element's styles</a></li>
- <li><a href="#recursively-clearing-an-element's-styles"><span class=secno>6 </span>Recursively clearing an element's styles</a></li>
- <li><a href=#styling-a-node><span class=secno>7 </span>Styling a Node</a></li>
- <li><a href=#recursively-styling-a-node><span class=secno>8 </span>Recursively styling a Node</a></li>
- <li><a href=#styling-a-range><span class=secno>9 </span>Styling a Range</a></li>
- <li><a href=#unstyling-a-node><span class=secno>10 </span>Unstyling a Node</a></li>
- <li><a href=#unstyling-a-range><span class=secno>11 </span>Unstyling a Range</a></li>
- <li><a href=#commands><span class=secno>12 </span>Commands</a></li>
+ <li><a href=#pushing-down-styles><span class=secno>6 </span>Pushing down styles</a></li>
+ <li><a href=#forcing-the-style-of-a-node><span class=secno>7 </span>Forcing the style of a node</a></li>
+ <li><a href=#styling-a-node><span class=secno>8 </span>Styling a node</a></li>
+ <li><a href=#commands><span class=secno>9 </span>Commands</a></li>
  <li><a class=no-num href=#references>References</a></li>
  <li><a class=no-num href=#acknowledgements>Acknowledgements</a></ol>
 <!--end-toc-->
@@ -82,6 +80,57 @@
 major preexisting rendering engines are known not to match it, the reasoning is
 included in HTML comments so as not to distract the reader.
 
+<p>The principles I've used for writing this specification so far are:
+
+<ul>
+  <li>If all browsers that implement a particular feature agree on some detail
+  of how it works, match them unless there's very good reason not to.  When
+  it's not clear what behavior is best, try to follow the implementations with
+  the most market share.  But if one browser's behavior is clearly better than
+  the others', go with the better behavior.
+
+  <li>If a command is issued to format some text in a particular way, we will
+  format the text that way no matter what.  If the user clicks the "bold"
+  button, they don't care that the text didn't become bold because of an
+  external CSS rule or for any other reason, they only care that it didn't
+  work.  The only exception (beyond where it's simply impossible, like
+  propagated text-decorations we can't remove) is that we don't try to override
+  !important rules from external stylesheets, although we also don't go out of
+  our way to respect them.
+
+  <li>When we're given a presentational command like "bold", don't modify
+  anything other than presentational markup related to that command.  If an
+  element has non-presentational attributes like id or class, don't split it up
+  or remove it or anything.  At most convert it to a span, if it's some type of
+  presentational element (where "presentational" here really means "browsers
+  produce it in response to execCommand() so we need to treat it as
+  presentational", so it includes things like <a href=http://www.whatwg.org/html/#the-strong-element><code class=external data-anolis-spec=html title="the strong element">strong</code></a> and <a href=http://www.whatwg.org/html/#the-em-element><code class=external data-anolis-spec=html title="the em element">em</code></a>).
+
+  <li>Don't interfere with more markup than necessary.  If the user modifies
+  only a small run of text, don't go around simplifying ancestors or siblings
+  or whatever unless it's necessary to produce simpler markup in the place that
+  was actually modified.
+
+  <li>But if we are already changing around something's style, convert existing
+  styles to the preferred format.  For instance, we use <a href=http://www.whatwg.org/html/#the-b-element><code class=external data-anolis-spec=html title="the b element">b</code></a> for bold, and
+  convert <a href=http://www.whatwg.org/html/#the-strong-element><code class=external data-anolis-spec=html title="the strong element">strong</code></a> and &lt;<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-style-attribute><code class=external data-anolis-spec=html title="the style attribute">style</code></a>="font-weight: bold"&gt; if we
+  happen to be modifying that node anyway.
+
+  <li>Don't make the document less conforming than it originally was.  If we
+  happen to make it more conforming, good, although we don't have to go out of
+  our way to do that.  (In the future, I'll add a mode that supports using
+  obsolete presentational elements instead of CSS for the sake of e-mail
+  clients and such, and this rule will be bent in that mode.)
+
+  <li>Keep the markup as concise as possible.  (I've received feedback that
+  this is very important to authors.)  Ideally, the markup should look as
+  simple and neat as what a human would have produced by hand-editing.  We do
+  complicated manipulation to pull styles down from ancestors rather than
+  having to use inline CSS, and make sure to tidy up any styles on elements
+  that we happen to be modifying anyway.  Previous principles take precedence
+  over this one, however.
+</ul>
+
 
 <h2 id=issues><span class=secno>2 </span>Issues</h2>
 
@@ -127,27 +176,15 @@
 <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-attr-namespace title=concept-attr-namespace>namespace</a> is
 the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#html-namespace>HTML namespace</a>.
 
-<p>The <dfn id=beginning-element>beginning element</dfn> of a <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> is returned by the following
-algorithm:
-
-<ol>
-  <li>If the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> of the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> 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>,
-  <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 the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a>
-  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a> of the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> is not equal to the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of its
-  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>, let <var title="">first node</var> be the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>'s
-  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>.
+<p>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> is <dfn id=effectively-contained>effectively contained</dfn> in a <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> if either it
+is <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> in the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>; or it is the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a>
+<a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>, it 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, and its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> is different from the
+<a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a>; or it is the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a>
+<a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>, it 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, and the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a>
+<a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a> is not 0.
 
-  <li>Otherwise, let <var title="">first node</var> be the first <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
-  <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#tree-order>tree order</a> that is <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> in the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>, if there is any.
-
-  <li>If <var title="">first node</var> is defined and 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>, return
-  <var title="">first node</var>.
-
-  <li>Otherwise, if <var title="">first node</var> is defined and its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> is 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>, return <var title="">first 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>.
-
-  <li>Return null.
-</ol>
+<p class=note>A node is <a href=#effectively-contained>effectively contained</a> in a range if and
+only if it would be <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> after the range is <a href=#decompose title=decompose>decomposed</a>.
 
 <p>The <dfn id=active-range>active range</dfn> of a <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#document><code class=external data-anolis-spec=domcore>Document</code></a> is the value returned by the
 following algorithm:
@@ -171,90 +208,28 @@
   <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.
 </ol>
 
-<p>Given a CSS property name <var title="">property name</var>, an (optional) value
-<var title="">property value</var> for that property, and a possibly empty list of
-strings <var title="">tag list</var>, 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> is a <dfn id=potentially-relevant-styling-element>potentially relevant styling
-element</dfn> if it is an <a href=#html-element>HTML element</a> and one of the following
-holds:
-
-<ul>
-  <li>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 in <var title="">tag list</var> and it has no attributes.
-
-  <li>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 in <var title="">tag list</var> or is "span" or is "font",
-  and it has exactly one attribute, and that attribute is an <a href=#html-attribute>HTML
-  attribute</a> with <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-attr-local-name title=concept-attr-local-name>local name</a> "style", and that attribute sets
-  exactly one CSS property, and that property is <var title="">property name</var>, and
-  either <var title="">property value</var> is undefined or the value the attribute sets
-  the property to is <var title="">property value</var>.
-
-  <li>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 "font", and it has exactly one attribute, and that
-  attribute is 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>
-  attribute, and either <var title="">property value</var> is undefined or the effect of
-  the attribute is to hint that the CSS color attribute be set to <var title="">property
-  value</var>, and <var title="">property name</var> is "color".
-
-  <li>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 "font", and it has exactly one attribute, and that
-  attribute is 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>
-  attribute, and either <var title="">property value</var> is undefined or the effect of
-  the attribute is to hint that the CSS font-family attribute be set to
-  <var title="">property value</var>, and <var title="">property name</var> is "font-family".
+<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).
 
-  <li>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 "font", and it has exactly one attribute, and that
-  attribute is a <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, and either <var title="">property value</var> is undefined or the effect of
-  the attribute is to hint that the CSS font-size attribute be set to
-  <var title="">property value</var>, and <var title="">property name</var> is "font-size".
-
-  <!-- Should we bother handling <font color=red style=color:red>?  Let's not.
-  -->
-</ul>
-
-<p>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> is a <dfn id=relevant-styling-element>relevant styling element</dfn> if it is a
-<a href=#potentially-relevant-styling-element>potentially relevant styling element</a>, and its CSS property
-<var title="">property name</var> computes to <var title="">property value</var> (which cannot be
-undefined).
-
-<div class=note>
-<p>If <var title="">property name</var> is "font-weight", <var title="">property value</var> is
-"bold", and <var title="">tag list</var> contains "b", an example of a <a href=#potentially-relevant-styling-element>potentially
-relevant styling element</a> that is not actually <a href=#relevant-styling-element title="relevant
-styling element">relevant</a> is
-
-</p><xmp><p style="font-weight: 100"><b>Foo</b></p></xmp>
-
-<p>Since <a href=http://www.whatwg.org/html/#the-b-element><code class=external data-anolis-spec=html title="the b element">b</code></a>'s default
-font-weight is "bolder", the computed font-weight will most likely end up being
-"normal" or lighter.
-</div>
-
-<p>A <dfn id=phrasing-element>phrasing element</dfn> is either an <a href=#html-element>HTML element</a> that is
-categorized as <a class=external data-anolis-spec=html href=http://www.whatwg.org/html/#phrasing-content>phrasing content</a>, or a <a class=external data-anolis-spec=html href=http://www.whatwg.org/html/#non-conforming-element title="non-conforming element">non-conforming</a> <a href=#html-element>HTML element</a>
-(which thus has no categories), or 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> that is not an <a href=#html-element>HTML
-element</a>.
-
-<p class=XXX>We should allow unrecognized HTML elements too.
-
-<p>The <dfn id=specified-style>specified style</dfn> of 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> for a given <var title="">property
-name</var> is returned by the following algorithm, which will return either a
-CSS value or null:
+<p>The <dfn id=specified-style>specified style</dfn> of 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> for a given
+<var title="">property</var> is returned by the following algorithm, which will return
+either a CSS value or null:
 
 <ol>
   <li>If 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> has a <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 set, and that attribute has
-  the effect of setting <var title="">property name</var>, return the value that it sets
-  <var title="">property name</var> to.
+  the effect of setting <var title="">property</var>, return the value that it sets
+  <var title="">property</var> to.
 
   <li>If 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> is a <a href=http://www.whatwg.org/html/#font><code class=external data-anolis-spec=html>font</code></a> element
   that has an attribute whose effect is to create a <a class=external data-anolis-spec=html href=http://www.whatwg.org/html/#presentational-hints title="presentational hints">presentational hint</a> for
-  <var title="">property name</var>, return the value that the hint sets <var title="">property
-  name</var> to.
+  <var title="">property</var>, return the value that the hint sets <var title="">property</var>
+  to.
 
-  <li>If 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> is in the following list, and <var title="">property name</var>
-  is equal to the CSS property name listed for it, return the string listed for
+  <li>If 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> is in the following list, and <var title="">property</var> is
+  equal to the CSS property name listed for it, return the string listed for
   it.
 
-  <p class=XXX>Add any other elements that can be output by the style/unstyle
-  algorithms, or existing browser implementations of execCommand().
-
   <dl class=switch>
     <dt><a href=http://www.whatwg.org/html/#the-b-element><code class=external data-anolis-spec=html title="the b element">b</code></a>
     <dt><a href=http://www.whatwg.org/html/#the-strong-element><code class=external data-anolis-spec=html title="the strong element">strong</code></a>
@@ -271,6 +246,38 @@
   <li>Return null.
 </ol>
 
+<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:
+
+<ol>
+  <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>, <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/#font><code class=external data-anolis-spec=html title=font>font</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.
+
+  <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>, <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/#font><code class=external data-anolis-spec=html title=font>font</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 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
+  <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 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 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/#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>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
 though the method had actually been called from JavaScript.  In particular,
@@ -284,8 +291,8 @@
 sequentially in the list's order.
 
 
-<h2 id=decomposing-a-range-into-nodes><span class=secno>4 </span>Decomposing a Range into Nodes</h2>
-<p>When a user agent is to <dfn id=decompose-a-range>decompose a <code class=external data-anolis-spec=domrange>Range</code></dfn> <var title="">range</var>,
+<h2 id=decomposing-a-range-into-nodes><span class=secno>4 </span>Decomposing a range into nodes</h2>
+<p>When a user agent is to <dfn id=decompose>decompose</dfn> a <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> <var title="">range</var>,
 it must run the following steps.
 
 <ol>
@@ -340,9 +347,25 @@
   <var title="">start offset</var>) and its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> to (<var title="">end node</var>,
   <var title="">end offset</var>).
 
-  <li>Return a list consisting of every <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> in
-  <var title="">range</var> in <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#tree-order>tree order</a>, omitting any whose <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> is also
-  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> in <var title="">range</var>.
+  <!-- Now we want to make sure our range contains as many nodes as possible,
+  such as by changing <tag>[foo]</tag> to {<tag>foo</tag>}. -->
+  <li>Let <var title="">cloned range</var> be the result of calling <a href=http://html5.org/specs/dom-range.html#dom-range-clonerange><code class=external data-anolis-spec=domrange title=dom-Range-cloneRange>cloneRange()</code></a> on
+  <var title="">range</var>.
+
+  <li>While the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a> of <var title="">cloned range</var> is 0,
+  and 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="">cloned range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> is
+  not null, set the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> of <var title="">cloned range</var> to (<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> of
+  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>, <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-start title=concept-range-start>start</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>).
+
+  <li>While the <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-offset title=concept-boundary-point-offset>offset</a> of <var title="">cloned range</var> equals the
+  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-node-length title=concept-node-length>length</a> of its <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>, and 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="">clone range</var>'s <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a> is not null, set the
+  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> of <var title="">cloned range</var> to (<a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> of <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a>
+  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>, 1 + <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-indexof title=concept-indexof>index</a> of <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-range-end title=concept-range-end>end</a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#concept-boundary-point-node title=concept-boundary-point-node>node</a>).
+
+  <li>Return a list consisting of every <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> in <var title="">cloned
+  range</var> in <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#tree-order>tree order</a>, omitting any whose <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> is also
+  <a class=external data-anolis-spec=domrange href=http://html5.org/specs/dom-range.html#contained>contained</a> in <var title="">cloned range</var>.
 </ol>
 
 
@@ -350,27 +373,22 @@
 <p>When a user agent is to <dfn id=clear-styles>clear styles</dfn> on an element, it must run
 the following steps:
 
-<div class=note>
-<p>Clearing styles (<a href=#recursively-clear-styles title="recursively clear styles">recursively</a>
-or not) can remove it from its parent and put other nodes in its place.  When
-implementations do something like clear style on all children of an element,
-they should take care not to assume that the set of children won't change as
-they're unstyled.  If the element is removed, the algorithm will return the
-list of nodes inserted in its place.
+<p class=note>Clearing styles can remove it from its parent and put other nodes
+in its place.  When implementations do something like clear style on all
+children of an element, they should take care not to assume that the set of
+children won't change as they're unstyled.  If the element is removed, the
+algorithm will return the list of nodes inserted in its place.
 
-<p>Clearing styles only removes inline styles from the element.  It doesn't
-ensure that the element isn't inheriting styles from an ancestor (or a style
-rule).  For that, one must <a href=#unstyle-a-node>unstyle a node</a>.
-</div>
+<p class=XXX>This should probably convert, e.g., &lt;font color=red id=foo&gt;
+into &lt;span id=foo&gt; instead of &lt;font id=foo&gt;.
 
 <ol>
   <li>Let <var title="">element</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> to be unstyled.
 
-  <li>Let <var title="">property name</var> and <var title="">tag list</var> be as
-  in the invoking algorithm.
+  <li>Let <var title="">property</var> be as in the invoking algorithm.
 
-  <li>If <var title="">element</var> is a <a href=#potentially-relevant-styling-element>potentially relevant styling
-  element</a>:
+  <li>If <var title="">element</var> is a <a href=#simple-styling-element>simple styling element</a> and its
+  <a href=#specified-style>specified style</a> for <var title="">property</var> is not null:
 
   <ol>
     <li>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.
@@ -386,31 +404,31 @@
       <var title="">element</var>.
     </ol>
 
-    <li>Remove <var title="">element</var>.
+    <li>Remove <var title="">element</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>Return <var title="">children</var>.
   </ol>
 
-  <li>Unset the CSS property <var title="">property name</var> of <var title="">element</var>.
+  <li>Unset the CSS property <var title="">property</var> of <var title="">element</var>.
 
   <li>If <var title="">element</var> is a <a href=http://www.whatwg.org/html/#font><code class=external data-anolis-spec=html>font</code></a>
   element:
 
   <ol>
-    <li>If <var title="">property name</var> is "color", unset <var title="">element</var>'s
+    <li>If <var title="">property</var> is "color", unset <var title="">element</var>'s
     <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, if
     set.
 
-    <li>If <var title="">property name</var> is "font-family", unset
+    <li>If <var title="">property</var> is "font-family", unset
     <var title="">element</var>'s <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, if set.
 
-    <li>If <var title="">property name</var> is "font-size", unset <var title="">element</var>'s
+    <li>If <var title="">property</var> is "font-size", unset <var title="">element</var>'s
     <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, if
     set.
   </ol>
 
-  <li>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.
+  <li>If <var title="">element</var>'s <var title="">specified style</var> 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. -->
 
@@ -424,190 +442,49 @@
   <li>While <var title="">element</var> has children, append its first child
   as the last child of <var title="">new element</var>.
 
-  <li>Remove <var title="">element</var>.
+  <li>Remove <var title="">element</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>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="recursively-clearing-an-element's-styles"><span class=secno>6 </span>Recursively clearing an element's styles</h2>
-<p>When a user agent is to <dfn id=recursively-clear-styles>recursively clear styles</dfn> on an element,
-it must run the following steps:
-
-<ol>
-  <li>Let <var title="">element</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> to be unstyled.
-
-  <li>Let <var title="">property name</var> and <var title="">tag list</var> be as
-  in the invoking algorithm.
-
-  <li>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="">element</var>.
-
-  <li><a href=#recursively-clear-styles>Recursively clear styles</a> on each <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a> in <var title="">element
-  children</var>.
-
-  <li><a href=#clear-styles>Clear styles</a> on <var title="">element</var>, and return the resulting
-  list.
-</ol>
-
-
-<h2 id=styling-a-node><span class=secno>7 </span>Styling a Node</h2>
-<p>When a user agent is to <dfn id=style-a-node>style a <code class=external data-anolis-spec=domcore>Node</code></dfn> <var title="">node</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>.
-
-<p class=note>This algorithm applies the given style to the node itself, but
-doesn't interfere with conflicting styles on its descendants.  <a href=#recursively-style-a-node title="recursively style a node">Recursive styling</a> removes conflicting
-styles from descendants first.
+<h2 id=pushing-down-styles><span class=secno>6 </span>Pushing down styles</h2>
+<p>When a user agent is to <dfn id=push-down-styles>push down styles</dfn> to 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>, given a CSS property name <var title="">property</var> and a new value
+<var title="">new value</var>, it must run the following steps:
 
 <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 null, or if <var title="">node</var> 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>, <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, abort
-  this algorithm. <!-- XXX: What to do here? -->
-
-  <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>:
-
-  <ol>
-    <li><a href=#clear-styles>Clear styles</a> on <var title="">node</var>, and let <var title="">new
-    nodes</var> be the result.
-
-    <li>For each <var title="">new node</var> in <var title="">new nodes</var>, <a href=#style-a-node title="style a node">style <var title="">new node</var></a>, with the same inputs
-    as this invocation of the algorithm.
-
-    <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.
-  </ol>
-
-  <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> but not a <a href=#phrasing-element>phrasing
-  element</a>:
+  <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. -->
 
-  <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 name</var> is neither null nor equal to <var title="">property
-    value</var>.
-
-    <li><a href=#style-a-node title="style a node">Style</a> 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>.
-
-    <li>Abort this algorithm.
-  </ol>
+  <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> and <var title="">property</var> computes to
+  <var title="">new value</var> on <var title="">node</var>, abort this algorithm.
 
-  <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=#relevant-styling-element>relevant styling
-  element</a>, 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>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=#relevant-styling-element>relevant styling
-  element</a>, 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>If <var title="">node</var> is 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>, abort
-  this algorithm.  <!-- There's no point in making a new element in this case.
-  -->
-
-  <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> and the computed style of
-  <var title="">property name</var> for it is <var title="">property value</var>, abort this
+  <li>If <var title="">node</var> 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> and <var title="">property</var> computes
+  to <var title="">new value</var> on <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>, abort this
   algorithm.
 
-  <li>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 and the computed style of
-  <var title="">property name</var> for its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> is <var title="">property value</var>,
-  abort this algorithm.
-
-  <li>Let <var title="">tag</var> be the first string in <var title="">tag list</var>, if that is
-  not empty, or "span" if it is empty.
-
-  <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
-  <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
-  <var title="">node</var>.
-
-  <li>If the computed value of <var title="">property name</var> for <var title="">new
-  parent</var> is not <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>.
-
-  <li>Append <var title="">node</var> to <var title="">new parent</var> as its last <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a>.
-</ol>
-
-
-<h2 id=recursively-styling-a-node><span class=secno>8 </span>Recursively styling a Node</h2>
-<p>When a user agent is to <dfn id=recursively-style-a-node>recursively style a <code class=external data-anolis-spec=domcore>Node</code></dfn>
-<var title="">node</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>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, or if <var title="">node</var> 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>, <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, abort
-  this algorithm. <!-- XXX: What to do here? -->
-
-  <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>:
-
-  <ol>
-    <li><a href=#recursively-clear-styles>Recursively clear styles</a> on <var title="">node</var>, and let
-    <var title="">new nodes</var> be the result.
-
-    <li>For each <var title="">new node</var> in <var title="">new nodes</var>, <a href=#recursively-style-a-node title="recursively style a node">recursively style <var title="">new
-    node</var></a>, with the same inputs as this invocation of the
-    algorithm.
-
-    <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.
-  </ol>
-
-  <li><a href=#style-a-node title="style a node">Style</a> <var title="">node</var>.
-</ol>
-
-
-<h2 id=styling-a-range><span class=secno>9 </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>, it must <a href=#decompose-a-range title="decompose a range">decompose</a> the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>, then <a href=#recursively-style-a-node title="recursively style a node">recursively style</a> 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 the
-returned list.
-
-
-<h2 id=unstyling-a-node><span class=secno>10 </span>Unstyling a Node</h2>
-<p>When a user agent is to <dfn id=unstyle-a-node>unstyle a <code class=external data-anolis-spec=domcore>Node</code></dfn> <var title="">node</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="">new value</var>, and a possibly
-empty list of strings <var title="">tag list</var>.
-
-<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 null, or if <var title="">node</var> 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> or <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, abort this algorithm. <!-- XXX: What to do
-  here?  We want to ignore comments and PIs, but we might want to support
-  detached elements, documents, document fragments, . . . -->
-
-  <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>:
-
-  <ol>
-    <li><a href=#recursively-clear-styles>Recursively clear styles</a> on <var title="">node</var>, and let
-    <var title="">new nodes</var> be the result.
-
-    <li>For each <var title="">new node</var> in <var title="">new nodes</var>, <a href=#unstyle-a-node title="unstyle a node">unstyle <var title="">new node</var></a>, with the same
-    inputs as this invocation of the algorithm.
-
-    <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.
-  </ol>
-
-  <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>, let <var title="">current value</var> equal
-  the computed value of <var title="">property name</var> on <var title="">node</var>.
-  Otherwise, let <var title="">current value</var> equal the computed value of
-  <var title="">property name</var> on <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>.
-
-  <li>If <var title="">current value</var> equals <var title="">new value</var>, abort this
-  algorithm.
+  <li>Let <var title="">current ancestor</var> be <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>.
 
   <li>Let <var title="">ancestor list</var> be a 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, initially empty.
 
-  <li>Let <var title="">current ancestor</var> equal <var title="">node</var>.
+  <li>While <var title="">current ancestor</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> and
+  <var title="">property</var> does not compute to <var title="">new value</var> on it, append
+  <var title="">current ancestor</var> to <var title="">ancestor list</var>, then set
+  <var title="">current ancestor</var> to its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>.
 
-  <li>While <var title="">current ancestor</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 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>, set
-  <var title="">current ancestor</var> to its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a>, then append it to
-  <var title="">ancestor list</var>.
-  
-  <li>While <var title="">ancestor list</var> is not empty, and the last member of
-  <var title="">ancestor list</var> has <a href=#specified-style>specified style</a> for <var title="">property
-  name</var> equal to <var title="">new value</var> or null, remove the last member from
-  <var title="">ancestor list</var>.
+  <!-- We can only remove specified styles, so if the style isn't specified,
+  give up. -->
+  <li>If <var title="">ancestor list</var> is not empty, and the <a href=#specified-style>specified
+  style</a> of <var title="">property</var> on the last member of <var title="">ancestor
+  list</var> is null, abort this algorithm.
+
+  <!-- If we go all the way up to the root and still don't have the desired
+  style, pushing down styles is pointless.  It will create extra markup for no
+  purpose. -->
+  <li>If <var title="">ancestor list</var> is not empty, and the <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-parent title=concept-tree-parent>parent</a> of the last
+  member of <var title="">ancestor list</var> 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.
 
   <li>While <var title="">ancestor list</var> is not empty:
 
@@ -618,7 +495,7 @@
     <li>Remove the last member from <var title="">ancestor list</var>.
 
     <li>Let <var title="">propagated value</var> be the <a href=#specified-style>specified style</a> of
-    <var title="">current ancestor</var> for <var title="">property name</var>.
+    <var title="">current ancestor</var> for <var title="">property</var>.
 
     <li>If <var title="">propagated value</var> is null, continue this loop from the
     beginning.
@@ -635,53 +512,222 @@
       <var title="">child</var>.
 
       <li>If <var title="">child</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> whose <a href=#specified-style>specified
-      style</a> for <var title="">property name</var> is neither null nor equal to
+      style</a> for <var title="">property</var> is neither null nor equal to
       <var title="">propagated value</var>, continue with the next <var title="">child</var>.
 
       <li>If <var title="">child</var> is the last member of <var title="">ancestor list</var>,
-      set <var title="">child</var>'s CSS property <var title="">property name</var> to
+      set <var title="">child</var>'s CSS property <var title="">property</var> to
       <var title="">propagated value</var> and continue with the next <var title="">child</var>.
 
       <p class=note>This style will be removed on the next loop iteration and
       distributed to its children.
 
-      <li><a href=#style-a-node title="style a node">Style</a> <var title="">child</var>, with
-      <var title="">property name</var> and <var title="">tag list</var> as in this algorithm,
-      and <var title="">property value</var> equal to <var title="">propagated value</var>.
+      <li><a href=#force-the-style>Force the style</a> of <var title="">child</var>, with
+      <var title="">property</var> as in this algorithm and <var title="">new value</var> equal
+      to <var title="">propagated value</var>.
     </ol>
   </ol>
-
-  <!-- We might have a rule inherited from someplace where we can't remove it,
-  or maybe even a rule in a stylesheet (although that case is pathological and
-  we generally ignore it) -->
-  <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> and <var title="">property name</var> does
-  not compute to <var title="">new value</var> on it, set <var title="">property name</var> to
-  <var title="">new value</var> on it.
-
-  <li>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 and <var title="">property name</var> does
-  not compute to <var title="">new value</var> on 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>
-    <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("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>Set <var title="">property name</var> to <var title="">new value</var> on <var title="">new
-    parent</var>.
-
-    <li>Insert <var title="">new parent</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>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="">new parent</var>.
-  </ol>
 </ol>
 
 
-<h2 id=unstyling-a-range><span class=secno>11 </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 <a href=#decompose-a-range title="decompose a range">decompose</a> the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>, then <a href=#unstyle-a-node title="unstyle a node">unstyle</a> 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 the returned list.
+<h2 id=forcing-the-style-of-a-node><span class=secno>7 </span>Forcing the style of a node</h2>
+<p>When a user agent is to <dfn id=force-the-style>force the 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>, given a CSS property name <var title="">property</var> and a new value
+<var title="">new value</var>, it must run the following steps.
+
+<p class=note>This algorithm checks if the node has the desired style, and if
+not, it wraps the node (or, if that's not possible, its descendants) in a
+<a href=#simple-styling-element>simple styling element</a>.  This is only used as a last resort after
+<a href=#clear-styles title="clear styles">clearing styles</a> and <a href=#push-down-styles title="push down
+styles">pushing down styles</a> don't work to achieve the desired style.
+After forcing the style, descendants might still have different style.
+
+<ol>
+  <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> and <var title="">property</var> computes to
+  <var title="">new value</var> on <var title="">node</var>, abort this algorithm.
+
+  <li>If <var title="">node</var> 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>, <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
+  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>, and <var title="">property</var> computes to <var title="">new value</var> on
+  <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>, abort this algorithm.
+
+  <li>If <var title="">node</var> is an <a href=#unwrappable-element>unwrappable element</a>:
+
+  <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.
+
+    <li>Abort this algorithm.
+  </ol>
+
+  <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 computed style 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>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 computed style 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.
+
+  <!-- At this point we have to make a new element as a wrapper.  This isn't
+  worth the effort for comments or PIs, so abort in that case. -->
+  <li>If <var title="">node</var> is 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>, 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> and <var title="">property</var> computes to
+  <var title="">new value</var> on <var title="">node</var>, abort this algorithm.
+
+  <li>If <var title="">node</var> 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>, <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
+  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>, and <var title="">property</var> computes to <var title="">new value</var> on
+  <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>, 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>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 <var title="">tag</var> is not set, let <var title="">tag</var> be "span".
+
+  <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
+  <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
+  <var title="">node</var>.
+
+  <li>If the computed value of <var title="">property</var> for <var title="">new parent</var> is
+  not <var title="">new value</var>, set the CSS property <var title="">property</var> of
+  <var title="">new parent</var> to <var title="">new value</var>.
+
+  <li>Append <var title="">node</var> to <var title="">new parent</var> as its last <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a>.
+
+  <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> and the computed value of
+  <var title="">property</var> for <var title="">node</var> is not <var title="">new value</var>, set the
+  CSS property <var title="">property</var> of <var title="">node</var> to <var title="">new value</var>.
+</ol>
 
 
-<h2 id=commands><span class=secno>12 </span>Commands</h2>
+<h2 id=styling-a-node><span class=secno>8 </span>Styling a node</h2>
+<p>When a user agent is to <dfn id=style>style</dfn> 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>, it must
+run the following steps.  There are two inputs: a CSS property name
+<var title="">property</var> and a new value <var title="">new value</var>.
+
+<div class=note>
+<p>The effect of this algorithm is to ensure that the node and all its
+descendants have the style requested, no matter what, producing the simplest
+markup possible to achieve that effect.  It's inspired by the approach WebKit
+takes.  The only places where the algorithm should fail are when there's an
+!important CSS rule that conflicts with the requested style (which we don't try
+to override because we assume it's !important for a reason), or when it's
+literally impossible to succeed (such as when a text-decoration is propagated
+from an ancestor we can't reach).  Any other failures are bugs.
+
+<p>First, if the node is an element with an inline style rule for this
+property, we unset it ("clearing styles").  This step also removes <a href=#simple-styling-element title="simple styling element">simple styling elements</a> entirely, and
+replaces elements like <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/#font><code class=external data-anolis-spec=html title=font>font</code></a> with <a href=http://www.whatwg.org/html/#the-span-element><code class=external data-anolis-spec=html title="the span element">span</code></a>s if they aren't simple
+styling elements.  This will be sufficient if the desired style is inherited
+from an ancestor, or if it's the default (like font-style: normal) and no
+conflicting style is inherited from an ancestor.  Even if clearing styles
+doesn't actually fix the style of the node we're dealing with, we do it anyway
+to simplify the generated markup.
+
+<p>If clearing styles didn't work, and it looks like an ancestor has inline
+style that we're inheriting, we push the style down from that ancestor.  Thus
+if we're unbolding the letter "r" in
+
+</p><xmp><b>foo <i>bar</i> baz</b>,</xmp>
+
+<p>we get
+
+</p><xmp><b>foo </b><i><b>ba</b>r</i><b> baz</b>.</xmp>
+
+<p>If we didn't push down styles, the final step (forcing styles) would instead
+give us
+
+</p><xmp><b>foo <i>ba<span style="font-weight: normal">r</span></i> baz</b>,</xmp>
+
+<p>which is much longer and uglier.  We take care not to disturb the style or
+semantics of anything but the node we're dealing with.
+
+<p>We'll only push down styles if some ancestor actually has the style we want,
+so we can inherit it.  Otherwise, it will just create useless markup.
+
+<p>Finally, if neither of the above strategies worked, we have to add new
+markup to get the desired style ("forcing styles").  First we try just sticking
+it into its previous or next sibling, if that's a <a href=#simple-styling-element>simple styling
+element</a> (so it won't add any styles or semantics we don't want).
+Otherwise, we create a new simple styling element and wrap it in that.  It's
+common that a previous sibling is the simple styling element we want, because
+often we'll style several consecutive siblings in succession.  In that case,
+the element created for the first can be reused for the later ones.
+
+<p>This last step works a bit differently if the node is an <a href=#unwrappable-element>unwrappable
+element</a>.  In that case, wrapping it in a simple styling element would
+make the document less conforming than it already was.  Instead, we recursively
+force style on its children.  The recursion will terminate when we hit a node
+that's wrappable, or when there are no further descendants.
+
+<p>After all this, the node is guaranteed to have the style we want, barring
+bugs in the algorithm or the two exceptions noted earlier (!important style
+rules, and impossible cases).  We then re-run the algorithm on each child
+recursively.  Typically this means just clearing the style of each descendant,
+because it should then inherit the style we just set on its ancestor.  In the
+unusual case that a descendant's style is wrong even after we clear style on
+it, such as because of a non-inline style rule (like trying to unbold a
+heading), we'll repeat the above steps to ensure that the style really gets set
+as desired.
+</div>
+
+<ol>
+  <li>If <var title="">node</var> is a <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#document><code class=external data-anolis-spec=domcore>Document</code></a>, <a href=#style>style</a> its <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 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> (if it has one) and abort this algorithm.
+
+  <li>If <var title="">node</var> is a <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#documentfragment><code class=external data-anolis-spec=domcore>DocumentFragment</code></a>, let <var title="">children</var> be
+  a list of 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>children</a>.  <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>,
+  then abort this algorithm.
+
+  <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, or if <var title="">node</var> is a <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#documenttype><code class=external data-anolis-spec=domcore>DocumentType</code></a>, abort this algorithm.
+
+  <p class=XXX>We could style detached elements, but maybe it's not worth the
+  effort.  Is execCommand() even supposed to work on things that don't descend
+  from a document?  Needs investigation.
+
+  <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>:
+
+  <ol>
+    <li><a href=#clear-styles>Clear styles</a> on <var title="">node</var>, and let <var title="">new
+    nodes</var> be the result.
+
+    <li>For each <var title="">new node</var> in <var title="">new nodes</var>,
+    <a href=#style>style</a> <var title="">new node</var>, with the same inputs as this
+    invocation of the algorithm.
+
+    <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.
+  </ol>
+
+  <li><a href=#push-down-styles>Push down styles</a> on <var title="">node</var>.
+
+  <li><a href=#force-the-style>Force the style</a> of <var title="">node</var>.
+
+  <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>.
+
+  <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
+  children in full before they begin styling.
+</ol>
+
+
+<h2 id=commands><span class=secno>9 </span>Commands</h2>
 <p>The <dfn id=execcommand() title=execCommand()><code>execCommand(<var title="">commandId</var>,
 <var title="">showUI</var>, <var title="">value</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 allows scripts to
@@ -732,9 +778,10 @@
 <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.
+the user agent must do nothing and abort these steps.  Otherwise, it must
+<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>, then <a href=#style>style</a> each returned
+<a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> with <var title="">property</var> equal to "background-color" and <var title="">new
+value</var> equal to <var title="">value</var>.
 <!-- 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
@@ -761,8 +808,7 @@
 <dd><p><strong>Value</strong>: The value is given by the following algorithm:
 
 <ol>
-  <li>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 class=XXX>...
 
   <li>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.
@@ -782,16 +828,17 @@
 
 <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
-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-weight", <var title="">property
-value</var> "bold", and <var title="">tag list</var> ["b", "strong"].  Otherwise, it
-must <a href=#unstyle-a-range title="unstyle a range">unstyle it</a> with <var title="">property
-name</var> "font-weight", <var title="">property value</var> "normal", and <var title="">tag
-list</var> ["b", "strong"].
+<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
+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 is then true, <a href=#style>style</a> each
+returned <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> with <var title="">property</var> "font-weight" and <var title="">new
+value</var> "bold".  Otherwise, <a href=#style>style</a> them with <var title="">new
+value</var> "normal".
 
-<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>State</strong>: True if every <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a> that is
+<a href=#effectively-contained>effectively contained</a> in the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> has computed font-weight at
+least 700, and 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 every <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 that is <a href=#effectively-contained>effectively
+contained</a> in the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> has computed font-weight at least 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
@@ -817,8 +864,7 @@
   specify "#" for the value, or the author can rewrite it, so it's not like
   this makes the API less useful. -->
 
-  <li>Let <var title="">node list</var> be the result of <a href=#decompose-a-range title="decompose a
-  range">decomposing</a> the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>.
+  <li>Let <var title="">node list</var> be the result of <a href=#decompose title=decompose>decomposing</a> the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a>.
 
   <li>For each <var title="">node</var> in <var title="">node list</var>, in order:
 
@@ -893,10 +939,9 @@
 
 <dt><code title=""><dfn id=command-fontname title=command-fontname>fontName</dfn></code>
 
-<dd><p><strong>Action</strong>: 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> equal to
-"font-family", <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>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>, then
+<a href=#style>style</a> each returned <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> with <var title="">property</var> equal to
+"font-family" and <var title="">new value</var> equal to <var title="">value</var>.
 <!-- UAs differ a bit in the details here:
 
 IE 9 RC: Empty string sets <font face="">
@@ -921,7 +966,7 @@
 <dd><p><strong>State</strong>: Always false.
 
 <dd><p><strong>Value</strong>: The computed value of the CSS property
-"font-family" 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>.
+"font-family" for . . .
 <!-- Complicated.
 
 IE 9 RC: Always the empty string.  Not very useful.
@@ -958,9 +1003,9 @@
 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.
+Otherwise, it must <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>, then
+<a href=#style>style</a> each returned <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> with <var title="">property</var> equal to
+"color" and <var title="">new value</var> equal to <var title="">value</var>.
 
 <dd><p><strong>State</strong>: Always false.
 <!-- This matches IE 9 RC and Chrome 10.  Opera 11 seems to return true if
@@ -970,7 +1015,7 @@
 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>.
+for . . .
 <!-- IE 9 RC returns the number 0 always, which makes no sense at all.  This
 matches the other browsers. -->
 
@@ -1019,17 +1064,17 @@
 
 <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", <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"].
+<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
+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 is then true, <a href=#style>style</a> each
+returned <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#node><code class=external data-anolis-spec=domcore>Node</code></a> with <var title="">property</var> "font-style" and <var title="">new
+value</var> "italic".  Otherwise, <a href=#style>style</a> them with <var title="">new
+value</var> "normal".
 
-<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-style with computed value "italic" or "oblique", otherwise
-false.
+<dd><p><strong>State</strong>: True if every <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#element><code class=external data-anolis-spec=domcore>Element</code></a> that is
+<a href=#effectively-contained>effectively contained</a> in the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> has computed font-style
+"italic" or "oblique", and 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 every <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 that is
+<a href=#effectively-contained>effectively contained</a> in the <a href=http://html5.org/specs/dom-range.html#range><code class=external data-anolis-spec=domrange>Range</code></a> has computed font-style
+"italic" or "oblique".  Otherwise false.
 
 <dd><p><strong>Value</strong>: Always the empty string.
 
--- a/implementation.js	Tue Mar 15 22:39:50 2011 -0600
+++ b/implementation.js	Thu Mar 17 16:14:25 2011 -0600
@@ -2,24 +2,7 @@
 
 var htmlNamespace = "http://www.w3.org/1999/xhtml";
 
-function getNodeIndex(node) {
-	var ret = 0;
-	while (node != node.parentNode.childNodes[ret]) {
-		ret++;
-	}
-	return ret;
-}
-
-function getNodeLength(node) {
-	if (node.nodeType == Node.TEXT_NODE
-	|| node.nodeType == Node.COMMENT_NODE
-	|| node.nodeType == Node.PROCESSING_INSTRUCTION_NODE) {
-		return node.data.length;
-	}
-
-	return node.childNodes.length;
-}
-
+// Utility functions
 function nextNode(node) {
 	if (node.hasChildNodes()) {
 		return node.firstChild;
@@ -52,35 +35,71 @@
 	return node.nextSibling;
 }
 
-function convertProperty(propertyName) {
+function convertProperty(property) {
 	// Special-case for now
 	var map = {
 		"fontStyle": "font-style",
 		"fontWeight": "font-weight",
 		"textDecoration": "text-decoration",
 	};
-	if (typeof map[propertyName] != "undefined") {
-		return map[propertyName];
+	if (typeof map[property] != "undefined") {
+		return map[property];
 	}
 
-	return propertyName;
+	return property;
 }
 
-function cssValuesEqual(propertyName, val1, val2) {
+function cssValuesEqual(property, val1, val2) {
 	// This is a bad hack to work around browser incompatibility.  It wouldn't
 	// work in real life, but it's good enough for a test implementation.
+	if (val1 === null || val2 === null) {
+		return val1 === val2;
+	}
+
+	if (property == "fontWeight") {
+		return val1 == val2
+			|| (val1.toLowerCase() == "bold" && val2 == "700")
+			|| (val2.toLowerCase() == "bold" && val1 == "700")
+			|| (val1.toLowerCase() == "normal" && val2 == "400")
+			|| (val2.toLowerCase() == "normal" && val1 == "400");
+	}
 	var test1 = document.createElement("span");
-	test1.style[propertyName] = val1;
+	test1.style[property] = val1;
 	var test2 = document.createElement("span");
-	test2.style[propertyName] = val2;
+	test2.style[property] = val2;
 
-	return test1.style[propertyName] == test2.style[propertyName];
+	return test1.style[property] == test2.style[property];
+}
+
+// Opera 11 puts HTML elements in the null namespace, it seems.
+function isHtmlNamespace(ns) {
+	return ns === null
+		|| ns === htmlNamespace;
 }
 
 
+// Functions for stuff in DOM Range
+function getNodeIndex(node) {
+	var ret = 0;
+	while (node != node.parentNode.childNodes[ret]) {
+		ret++;
+	}
+	return ret;
+}
+
+function getNodeLength(node) {
+	if (node.nodeType == Node.TEXT_NODE
+	|| node.nodeType == Node.COMMENT_NODE
+	|| node.nodeType == Node.PROCESSING_INSTRUCTION_NODE) {
+		return node.data.length;
+	}
+
+	return node.childNodes.length;
+}
+
 /**
  * The position of two boundary points relative to one another, as defined by
- * the spec.
+ * DOM Range.
  */
 function getPosition(nodeA, offsetA, nodeB, offsetB) {
 	// "If node A is the same as node B, return equal if offset A equals offset
@@ -156,54 +175,43 @@
 		&& pos2 == "before";
 }
 
+
+// Things defined in the edit command spec (i.e., the interesting stuff)
+
+
+// "A Node is an HTML element if it is an Element whose namespace is the HTML
+// namespace."
 function isHtmlElement(node) {
 	return node
 		&& node.nodeType == Node.ELEMENT_NODE
-		&& node.namespaceURI == htmlNamespace;
+		&& isHtmlNamespace(node.namespaceURI);
 }
 
-function beginningElement(range) {
-	// "If the start node of the Range is a Text, Comment, or
-	// ProcessingInstruction node, and the start offset of the Range is not
-	// equal to the length of its start node, let first node be the Range's
-	// start node."
-	var firstNode = null;
-	if (range.startOffset != getNodeLength(range.startContainer)
-	&& (range.startContainer.nodeType == Node.TEXT_NODE
-	|| range.startContainer.nodeType == Node.COMMENT_NODE
-	|| range.startContainer.nodeType == Node.PROCESSING_INSTRUCTION_NODE)) {
-		firstNode = range.startContainer;
-	// "Otherwise, let first node be the first Node in tree order that is
-	// contained in the Range, if there is any."
-	} else {
-		var firstContained = range.startContainer;
-		while (firstContained != range.endContainer
-		&& !isContained(firstContained, range)) {
-			firstContained = nextNode(firstContained);
-		}
-		if (firstContained != range.endContainer) {
-			firstNode = firstContained;
-		}
+
+/**
+ * "A Node is effectively contained in a Range if either it is contained in the
+ * Range; or it is the Range's start node, it is a Text node, and its length is
+ * different from the Range's start offset; or it is the Range's end node, it
+ * is a Text node, and the Range's end offset is not 0."
+ */
+function isEffectivelyContained(node, range) {
+	if (isContained(node, range)) {
+		return true;
 	}
-
-	// "If first node is defined and is an Element, return first node."
-	if (firstNode && firstNode.nodeType == Node.ELEMENT_NODE) {
-		return firstNode;
+	if (node == range.startContainer
+	&& node.nodeType == Node.TEXT_NODE
+	&& getNodeLength(node) != range.startOffset) {
+		return true;
 	}
-
-	// "Otherwise, if first node is defined and its parent is an Element,
-	// return first node's parent."
-	if (firstNode
-	&& firstNode.parentNode
-	&& firstNode.parentNode.nodeType == Node.ELEMENT_NODE) {
-		return firstNode.parentNode;
+	if (node == range.endContainer
+	&& node.nodeType == Node.TEXT_NODE
+	&& range.endOffset != 0) {
+		return true;
 	}
-
-	// "Return null."
-	return null;
+	return false;
 }
 
-function activeRange(doc) {
+function getActiveRange(doc) {
 	// "Let selection be the result of calling getSelection() on the Document."
 	//
 	// We call getSelection() on defaultView instead, because Firefox and Opera
@@ -243,161 +251,65 @@
 	}
 }
 
-/**
- * "Given a CSS property name property name, an (optional) value property value
- * for that property, and a possibly empty list of strings tag list, a Node is
- * a potentially relevant styling element if it is an HTML element and one of
- * the following holds:
- *
- *  * Its local name is in tag list and it has no attributes.
- *  * Its local name is in tag list or is "span" or is "font", and it has
- *    exactly one attribute, and that attribute is an HTML attribute with local
- *    name "style", and that attribute sets exactly one CSS property, and that
- *    property is property name, and either property value is undefined or the
- *    value the attribute sets the property to is property value.
- *  * Its local name is "font", and it has exactly one attribute, and that
- *    attribute is a color attribute, and either property value is undefined or
- *    the effect of the attribute is to hint that the CSS color attribute be
- *    set to property value, and property name is "color".
- *  * Its local name is "font", and it has exactly one attribute, and that
- *    attribute is a face attribute, and either property value is undefined or
- *    the effect of the attribute is to hint that the CSS font-family attribute
- *    be set to property value, and property name is "font-family".
- *  * Its local name is "font", and it has exactly one attribute, and that
- *    attribute is a size attribute, and either property value is undefined or
- *    the effect of the attribute is to hint that the CSS font-size attribute
- *    be set to property value, and property name is "font-size"."
- */
-function isPotentiallyRelevantStylingElement(element, propertyName, propertyValue, tagList) {
-	if (!isHtmlElement(element)) {
+// "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)."
+//
+// I don't bother implementing this exactly, just well enough for testing.
+function isUnwrappableElement(node) {
+	if (!isHtmlElement(node)) {
 		return false;
 	}
 
-	var localName = element.tagName.toLowerCase();
-
-	if (tagList.indexOf(localName) != -1 && element.attributes.length == 0) {
-		return true;
-	}
-
-	if ((tagList.indexOf(localName) != -1 || localName == "span" || localName == "font")
-	&& element.attributes.length == 1
-	// Not checking namespace because it seems buggy, maybe?
-	//&& element.attributes[0].namespaceURI == htmlNamespace
-	&& element.attributes[0].localName == "style"
-	&& element.style.length == 1
-	&& element.style.item(0) == convertProperty(propertyName)
-	&& (propertyValue === null
-	|| cssValuesEqual(propertyName, element.style[propertyName], propertyValue))) {
-		return true;
-	}
-
-	var fontAttr = null;
-	if (propertyName == "color") {
-		fontAttr = "color";
-	} else if (propertyName == "fontFamily") {
-		fontAttr = "face";
-	} else if (propertyName == "fontSize") {
-		fontAttr = "size";
-	}
-
-	// TODO: cssValuesEqual() is total nonsense for font-size.
-	if (fontAttr
-	&& localName == "font"
-	&& element.attributes.length == 1
-	//&& element.attributes[0].namespaceURI == htmlNamespace
-	&& element.attributes[0].localName == fontAttr
-	&& (propertyValue === null
-	|| cssValuesEqual(propertyName, element[fontAttr], propertyValue))) {
-		return true;
-	}
-}
-
-/**
- * "A Node is a relevant styling element if it is a potentially relevant
- * styling element, and its CSS property property name computes to property
- * value."
- */
-function isRelevantStylingElement(node, propertyName, propertyValue, tagList) {
-	return isPotentiallyRelevantStylingElement(node, propertyName, propertyValue, tagList)
-		&& cssValuesEqual(propertyName, getComputedStyle(node)[propertyName], propertyValue);
+	return [
+		"h1", "h2", "h3", "h4", "h5", "h6", "p", "hr", "pre", "blockquote",
+		"ol", "ul", "li", "dl", "dt", "dd", "div", "table", "caption",
+		"colgroup", "col", "tbody", "thead", "tfoot", "tr", "th", "td",
+	].indexOf(node.tagName.toLowerCase()) != -1;
 }
 
 /**
- * "A phrasing element is either an HTML element that is categorized as
- * phrasing content, or a non-conforming HTML element (which thus has no
- * categories), or an Element that is not an HTML element."
+ * "specified style" per edit command spec
  */
-function isPhrasingElement(element) {
-	if (!element || element.nodeType != Node.ELEMENT_NODE) {
-		return false;
-	}
-
-	if (!isHtmlElement(element)) {
-		return true;
-	}
-
-	// As of March 2011.
-	var nonConforming = ["applet", "acronym", "bgsound", "dir", "frame",
-	"frameset", "noframes", "isindex", "listing", "xmp", "nextid", "noembed",
-	"plaintext", "rb", "strike", "basefont", "big", "blink", "center", "font",
-	"marquee", "multicol", "nobr", "spacer", "tt", "u"];
-
-	// I'm skipping checks for elements that are only sometimes phrasing
-	// content.  I just assume they always are.
-	var phrasingElements = ["a", "abbr", "area", "audio", "b", "bdi", "bdo",
-	"br", "button", "canvas", "cite", "code", "command", "datalist", "del",
-	"dfn", "em", "embed", "i", "iframe", "img", "input", "ins", "kbd",
-	"keygen", "label", "link", "map", "mark", "math", "meta", "meter",
-	"noscript", "object", "output", "progress", "q", "ruby", "s", "samp",
-	"script", "select", "small", "span", "strong", "sub", "sup", "svg",
-	"textarea", "time", "var", "video", "wbr"];
-
-	return nonConforming.indexOf(element.tagName.toLowerCase()) != -1
-		|| phrasingElements.indexOf(element.tagName.toLowerCase()) != -1;
-}
-
-/**
- * "specified style" per spec
- */
-function getSpecifiedStyle(element, propertyName) {
+function getSpecifiedStyle(element, property) {
 	// "If the Element has a style attribute set, and that attribute has the
-	// effect of setting property name, return the value that it sets property
-	// name to."
-	if (element.style[propertyName] != "") {
-		return element.style[propertyName];
+	// effect of setting property, return the value that it sets property to."
+	if (element.style[property] != "") {
+		return element.style[property];
 	}
 
 	// "If the Element is a font element that has an attribute whose effect is
-	// to create a presentational hint for property name, return the value that
-	// the hint sets property name to."
+	// to create a presentational hint for property, return the value that the
+	// hint sets property to."
 	//
-	// I'm cheating on this one for simplicity.  Font-size is especially wrong.
-	if (element.namespaceURI == htmlNamespace
+	// I'm cheating on this one for simplicity.  Font-size is especially wrong,
+	// and will have to be fixed when I implement execCommand() for that.
+	if (isHtmlNamespace(element.namespaceURI)
 	&& element.tagName == "FONT") {
-		if (propertyName == "color" && element.hasAttribute("color")) {
+		if (property == "color" && element.hasAttribute("color")) {
 			return element.color;
 		}
-		if (propertyName == "fontFamily" && element.hasAttribute("face")) {
+		if (property == "fontFamily" && element.hasAttribute("face")) {
 			return element.face;
 		}
-		if (propertyName == "fontSize" && element.hasAttribute("size")) {
+		if (property == "fontSize" && element.hasAttribute("size")) {
 			return element.size;
 		}
 	}
 
-	// "If the Element is in the following list, and property name is equal to
-	// the CSS property name listed for it, return the string listed for it."
+	// "If the Element is in the following list, and property is equal to the
+	// CSS property name listed for it, return the string listed for it."
 	//
 	// A list follows, whose meaning is copied here.
-	if (propertyName == "fontWeight"
+	if (property == "fontWeight"
 	&& (element.tagName == "B" || element.tagName == "STRONG")) {
 		return "bold";
 	}
-	if (propertyName == "fontStyle"
+	if (property == "fontStyle"
 	&& (element.tagName == "I" || element.tagName == "EM")) {
 		return "italic";
 	}
-	if (propertyName == "textDecoration"
+	if (property == "textDecoration"
 	&& element.tagName == "U") {
 		return "underline";
 	}
@@ -406,6 +318,87 @@
 	return null;
 }
 
+function isSimpleStylingElement(node) {
+	// "A simple styling element is an HTML element for which at least one of
+	// the following holds:"
+	if (!isHtmlElement(node)) {
+		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) {
+		return true;
+	}
+
+	// If it's got more than one attribute, everything after this fails.
+	if (node.attributes.length > 1) {
+		return false;
+	}
+
+	// "It is a b, em, font, i, span, strong, or u element with exactly one
+	// attribute, which is style, which sets no CSS properties (including
+	// invalid or unrecognized properties)."
+	//
+	// Not gonna try for invalid or unrecognized.
+	if (node.hasAttribute("style")
+	&& node.style.length == 0) {
+		return true;
+	}
+
+	// "It is a font element with one attribute, which is either color, face,
+	// or size."
+	if (node.tagName == "FONT"
+	&& (node.hasAttribute("color")
+		|| 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"."
+	if ((node.tagName == "B" || node.tagName == "STRONG")
+	&& node.hasAttribute("style")
+	&& node.style.length == 1
+	&& node.style.fontWeight != "") {
+		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"."
+	if ((node.tagName == "I" || node.tagName == "EM")
+	&& node.hasAttribute("style")
+	&& node.style.length == 1
+	&& node.style.fontStyle != "") {
+		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"."
+	if (node.tagName == "U"
+	&& node.hasAttribute("style")
+	&& node.style.length == 1
+	&& node.style.textDecoration != "") {
+		return true;
+	}
+
+	// "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)."
+	if ((node.tagName == "FONT" || node.tagName == "SPAN")
+	&& node.hasAttribute("style")
+	&& node.style.length == 1) {
+		return true;
+	}
+
+	return false;
+}
+
 function decomposeRange(range) {
 	// "If range's start and end are the same, return an empty list."
 	if (range.startContainer == range.endContainer
@@ -481,21 +474,42 @@
 	range.setStart(startNode, startOffset);
 	range.setEnd(endNode, endOffset);
 
-	// "Return a list consisting of every Node contained in range in tree
-	// order, omitting any whose parent is also contained in range."
+	// "Let cloned range be the result of calling cloneRange() on range."
+	var clonedRange = range.cloneRange();
+
+	// "While the start offset of cloned range is 0, and the parent of cloned
+	// range's start node is not null, set the start of cloned range to (parent
+	// of start node, index of start node)."
+	while (clonedRange.startOffset == 0
+	&& clonedRange.startContainer.parentNode) {
+		clonedRange.setStart(clonedRange.startContainer.parentNode, getNodeIndex(clonedRange.startContainer));
+	}
+
+	// "While the end offset of cloned range equals the length of its end node,
+	// and the parent of clone range's end node is not null, set the end of
+	// cloned range to (parent of end node, 1 + index of end node)."
+	while (clonedRange.endOffset == getNodeLength(clonedRange.endContainer)
+	&& clonedRange.endContainer.parentNode) {
+		clonedRange.setEnd(clonedRange.endContainer.parentNode, 1 + getNodeIndex(clonedRange.endContainer));
+	}
+
+	// "Return a list consisting of every Node contained in cloned range in
+	// tree order, omitting any whose parent is also contained in cloned
+	// range."
 	var ret = [];
-	for (var node = startNode; node != nextNodeDescendants(endNode); node = nextNode(node)) {
-		if (isContained(node, range)
-		&& !isContained(node.parentNode, range)) {
+	for (var node = clonedRange.startContainer; node != nextNodeDescendants(clonedRange.endContainer); node = nextNode(node)) {
+		if (isContained(node, clonedRange)
+		&& !isContained(node.parentNode, clonedRange)) {
 			ret.push(node);
 		}
 	}
 	return ret;
 }
 
-function clearStyles(element, propertyName, tagList) {
-	// "If element is a potentially relevant styling element:"
-	if (isPotentiallyRelevantStylingElement(element, propertyName, null, tagList)) {
+function clearStyles(element, property) {
+	// "If element is a simple styling element and its specified style for
+	// property is not null:"
+	if (isSimpleStylingElement(element) && getSpecifiedStyle(element, property) !== null) {
 		// "Let children be an empty list of Nodes."
 		var children = [];
 
@@ -511,44 +525,42 @@
 			element.parentNode.insertBefore(child, element);
 		}
 
-		// "Remove element."
+		// "Remove element from its parent."
 		element.parentNode.removeChild(element);
 
 		// "Return children."
 		return children;
 	}
 
-	// "Unset the CSS property property name of element."
-	element.style[propertyName] = '';
+	// "Unset the CSS property property of element."
+	element.style[property] = '';
 	if (element.getAttribute("style") == "") {
 		element.removeAttribute("style");
 	}
 
 	// "If element is a font element:"
-	if (element.namespaceURI == htmlNamespace && element.tagName == "FONT") {
-		// "If property name is "color", unset element's color attribute, if
-		// set."
-		if (propertyName == "color") {
+	if (isHtmlNamespace(element.namespaceURI) && element.tagName == "FONT") {
+		// "If property is "color", unset element's color attribute, if set."
+		if (property == "color") {
 			element.removeAttribute("color");
 		}
 
-		// "If property name is "font-family", unset element's face attribute,
-		// if set."
-		if (propertyName == "fontFamily") {
+		// "If property is "font-family", unset element's face attribute, if
+		// set."
+		if (property == "fontFamily") {
 			element.removeAttribute("face");
 		}
 
-		// "If property name is "font-size", unset element's size attribute, if
+		// "If property is "font-size", unset element's size attribute, if
 		// set."
-		if (propertyName == "fontSize") {
+		if (property == "fontSize") {
 			element.removeAttribute("size");
 		}
 	}
 
-	// "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) {
+	// "If element's specified style for property is null, return the empty
+	// list."
+	if (getSpecifiedStyle(element, property) === null) {
 		return [];
 	}
 
@@ -570,243 +582,63 @@
 		newElement.appendChild(element.firstChild);
 	}
 
-	// "Remove element."
+	// "Remove element from its parent."
 	element.parentNode.removeChild(element);
 
 	// "Return the one-Node list consisting of new element."
 	return [newElement];
 }
 
-function recursivelyClearStyles(element, propertyName, tagList) {
-	// "Let element children be the Element children of element."
-	var elementChildren = [];
-	for (var j = 0; j < element.childNodes.length; j++) {
-		if (element.childNodes[j].nodeType == Node.ELEMENT_NODE) {
-			elementChildren.push(element.childNodes[j]);
-		}
-	}
-
-	// "Recursively clear styles on each Element in element children."
-	for (var j = 0; j < elementChildren.length; j++) {
-		recursivelyClearStyles(elementChildren[j], propertyName, tagList);
-	}
-
-	// "Clear styles on element, and return the resulting list."
-	return clearStyles(element, propertyName, tagList);
-}
-
-function styleNode(node, propertyName, propertyValue, tagList) {
-	// "If node's parent is null, or if node is not an Element, Text, Comment,
-	// or ProcessingInstruction node, abort this algorithm."
+function pushDownStyles(node, property, newValue) {
+	// "If node's parent is not an Element, abort this algorithm."
 	if (!node.parentNode
-	|| [Node.ELEMENT_NODE, Node.TEXT_NODE, Node.COMMENT_NODE,
-	Node.PROCESSING_INSTRUCTION_NODE].indexOf(node.nodeType) == -1) {
-		return;
-	}
-
-	// "If node is an Element:"
-	if (node.nodeType == Node.ELEMENT_NODE) {
-		// "Clear styles on node, and let new nodes be the result."
-		var newNodes = clearStyles(node, propertyName, tagList);
-
-		// "For each new node in new nodes, style new node, with the same
-		// inputs as this invocation of the algorithm."
-		for (var i = 0; i < newNodes.length; i++) {
-			styleNode(newNodes[i], propertyName, propertyValue, tagList);
-		}
-
-		// "If node's parent is null, abort this algorithm."
-		if (!node.parentNode) {
-			return;
-		}
-	}
-
-	// "If node is an Element but not a phrasing element:"
-	if (node.nodeType == Node.ELEMENT_NODE
-	&& !isPhrasingElement(node)) {
-		// "Let children be all children of node, omitting any that are
-		// Elements whose specified style for property name is neither null nor
-		// equal to property 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], propertyName);
-
-				if (specifiedStyle !== null
-				&& !cssValuesEqual(propertyName, propertyValue, specifiedStyle)) {
-					continue;
-				}
-			}
-			children.push(node.childNodes[i]);
-		}
-
-		// "Style each Node in children."
-		for (var i = 0; i < children.length; i++) {
-			styleNode(children[i], propertyName, propertyValue, tagList);
-		}
-
-		// "Abort this algorithm."
-		return;
-	}
-
-	// "If node's previousSibling is a relevant styling element, append node as
-	// the last child of its previousSibling and abort this algorithm."
-	if (isRelevantStylingElement(node.previousSibling, propertyName, propertyValue, tagList)) {
-		node.previousSibling.appendChild(node);
-		return;
-	}
-
-	// "If node's nextSibling is a relevant styling element, insert node as the
-	// first child of its nextSibling and abort this algorithm."
-	if (isRelevantStylingElement(node.nextSibling, propertyName, propertyValue, tagList)) {
-		node.nextSibling.insertBefore(node, node.nextSibling.childNodes.length
-			? node.nextSibling.childNodes[0]
-			: null);
-		return;
-	}
-
-	// "If node is a Comment or ProcessingInstruction, abort this algorithm."
-	if (node.nodeType == Node.COMMENT_NODE
-	|| node.nodeType == Node.PROCESSING_INSTRUCTION_NODE) {
-		return;
-	}
-
-	// "If node is an Element and the computed style of property name for it is
-	// property value, abort this algorithm."
-	if (node.nodeType == Node.ELEMENT_NODE
-	&& cssValuesEqual(propertyName, getComputedStyle(node)[propertyName], propertyValue)) {
+	|| node.parentNode.nodeType != Node.ELEMENT_NODE) {
 		return;
 	}
 
-	// "If node is a Text node and the computed style of property name for its
-	// parent is property value, abort this algorithm."
-	if (node.nodeType == Node.TEXT_NODE
-	&& cssValuesEqual(propertyName, getComputedStyle(node.parentNode)[propertyName], propertyValue)) {
-		return;
-	}
-
-	// "Let tag be the first string in tag list, if that is not empty, or
-	// "span" if it is empty."
-	var tag = tagList.length ? tagList[0] : "span";
-
-	// "Let new parent be the result of calling createElement(tag) on the
-	// ownerDocument of node."
-	var newParent = node.ownerDocument.createElement(tag);
-
-	// "Insert new parent in node's parent before node."
-	node.parentNode.insertBefore(newParent, node);
-
-	// "If the computed value of property name for new parent is not property
-	// value, set the CSS property property name of new parent to property
-	// value."
-	if (!cssValuesEqual(propertyName, getComputedStyle(newParent)[propertyName], propertyValue)) {
-		newParent.style[propertyName] = propertyValue;
-	}
-
-	// "Append node to new parent as its last child."
-	newParent.appendChild(node);
-}
-
-function recursivelyStyleNode(node, propertyName, propertyValue, tagList) {
-	// "If node's parent is null, or if node is not an Element, Text, Comment,
-	// or ProcessingInstruction node, abort this algorithm."
-	if (!node.parentNode
-	|| [Node.ELEMENT_NODE, Node.TEXT_NODE, Node.COMMENT_NODE,
-	Node.PROCESSING_INSTRUCTION_NODE].indexOf(node.nodeType) == -1) {
+	// "If node is an Element and property computes to new value on node, abort
+	// this algorithm."
+	if (node.nodeType == Node.ELEMENT_NODE
+	&& cssValuesEqual(property, getComputedStyle(node)[property], newValue)) {
 		return;
 	}
 
-	// "If node is an Element:"
-	if (node.nodeType == Node.ELEMENT_NODE) {
-		// "Recursively clear styles on node, and let new nodes be the result."
-		var newNodes = recursivelyClearStyles(node, propertyName, tagList);
-
-		// "For each new node in new nodes, recursively style new node, with
-		// the same inputs as this invocation of the algorithm."
-		for (var i = 0; i < newNodes.length; i++) {
-			recursivelyStyleNode(newNodes[i], propertyName, propertyValue, tagList);
-		}
-
-		// "If node's parent is null, abort this algorithm."
-		if (!node.parentNode) {
-			return;
-		}
-	}
-
-	// "Style node."
-	styleNode(node, propertyName, propertyValue, tagList);
-}
-
-// "When a user agent is to style a Range, it must decompose the Range, then
-// recursively style each Node in the returned list."
-function styleRange(range, propertyName, propertyValue, tagList) {
-	var nodeList = decomposeRange(range);
-	for (var i = 0; i < nodeList.length; i++) {
-		recursivelyStyleNode(nodeList[i], propertyName, propertyValue, tagList);
-	}
-}
-
-function unstyleNode(node, propertyName, newValue, tagList) {
-	// "If node's parent is null, or if node is not an Element or Text node,
-	// abort this algorithm."
-	if (!node.parentNode
-	|| (node.nodeType != Node.ELEMENT_NODE && node.nodeType != Node.TEXT_NODE)) {
+	// "If node is not an Element and property computes to new value on node's
+	// parent, abort this algorithm."
+	if (node.nodeType != Node.ELEMENT_NODE
+	&& cssValuesEqual(property, getComputedStyle(node.parentNode)[property], newValue)) {
 		return;
 	}
 
-	// "If node is an Element:"
-	if (node.nodeType == Node.ELEMENT_NODE) {
-		// "Recursively clear styles on node, and let new nodes be the result."
-		var newNodes = recursivelyClearStyles(node, propertyName, tagList);
-
-		// "For each new node in new nodes, unstyle new node, with the same
-		// inputs as this invocation of the algorithm."
-		for (var i = 0; i < newNodes.length; i++) {
-			unstyleNode(newNodes[i], propertyName, newValue, tagList);
-		}
-
-		// "If node's parent is null, abort this algorithm."
-		if (!node.parentNode) {
-			return;
-		}
-	}
-
-	// "If node is an Element, let current value equal the computed value of
-	// property name on node. Otherwise, let current value equal the computed
-	// value of property name on node's parent."
-	var currentValue;
-	if (node.nodeType == Node.ELEMENT_NODE) {
-		currentValue = getComputedStyle(node)[propertyName];
-	} else {
-		currentValue = getComputedStyle(node.parentNode)[propertyName];
-	}
-
-	// "If current value equals new value, abort this algorithm."
-	if (cssValuesEqual(propertyName, currentValue, newValue)) {
-		return;
-	}
+	// "Let current ancestor be node's parent."
+	var currentAncestor = node.parentNode;
 
 	// "Let ancestor list be a list of Nodes, initially empty."
 	var ancestorList = [];
 
-	// "Let current ancestor equal node."
-	var currentAncestor = node;
-
-	// "While current ancestor's parent is an Element, set current ancestor to
-	// its parent, then append it to ancestor list."
-	while (currentAncestor.parentNode
-	&& currentAncestor.parentNode.nodeType == Node.ELEMENT_NODE) {
+	// "While current ancestor is an Element and property does not compute to
+	// new value on it, append current ancestor to ancestor list, then set
+	// current ancestor to its parent."
+	while (currentAncestor
+	&& currentAncestor.nodeType == Node.ELEMENT_NODE
+	&& !cssValuesEqual(property, getComputedStyle(currentAncestor)[property], newValue)) {
+		ancestorList.push(currentAncestor);
 		currentAncestor = currentAncestor.parentNode;
-		ancestorList.push(currentAncestor);
 	}
 
-	// "While ancestor list is not empty, and the last member of ancestor list
-	// has specified style for property name equal to new value or null, remove
-	// the last member from ancestor list."
-	while (ancestorList.length
-	&& (getSpecifiedStyle(ancestorList[ancestorList.length - 1], propertyName) === null
-	|| cssValuesEqual(propertyName, newValue, getSpecifiedStyle(ancestorList[ancestorList.length - 1], propertyName)))) {
-		ancestorList.pop();
+	// "If ancestor list is not empty, and the specified style of property on
+	// the last member of ancestor list is null, abort this algorithm."
+	if (ancestorList.length != 0
+	&& getSpecifiedStyle(ancestorList[ancestorList.length - 1], property) === null) {
+		return;
+	}
+
+	// "If ancestor list is not empty, and the parent of the last member of
+	// ancestor list is not an Element, abort this algorithm."
+	if (ancestorList.length != 0
+	&& (!ancestorList.slice(-1)[0]
+	|| ancestorList.slice(-1)[0].nodeType != Node.ELEMENT_NODE)) {
+		return;
 	}
 
 	// "While ancestor list is not empty:"
@@ -816,8 +648,8 @@
 		var currentAncestor = ancestorList.pop();
 
 		// "Let propagated value be the specified style of current ancestor for
-		// property name."
-		var propagatedValue = getSpecifiedStyle(currentAncestor, propertyName);
+		// property."
+		var propagatedValue = getSpecifiedStyle(currentAncestor, property);
 
 		// "If propagated value is null, continue this loop from the
 		// beginning."
@@ -832,7 +664,7 @@
 		}
 
 		// "Clear styles on current ancestor."
-		clearStyles(currentAncestor, propertyName, tagList);
+		clearStyles(currentAncestor, property);
 
 		// "For every child in children:"
 		for (var i = 0; i < children.length; i++) {
@@ -847,64 +679,223 @@
 			// is neither null nor equal to propagated value, continue with the
 			// next child."
 			if (child.nodeType == Node.ELEMENT_NODE
-			&& getSpecifiedStyle(child, propertyName) !== null
-			&& !cssValuesEqual(propertyName, propagatedValue, getSpecifiedStyle(child, propertyName))) {
+			&& getSpecifiedStyle(child, property) !== null
+			&& !cssValuesEqual(property, propagatedValue, getSpecifiedStyle(child, property))) {
 				continue;
 			}
 
 			// "If child is the last member of ancestor list, set child's CSS
-			// property property name to propagated value and continue with the
-			// next child."
+			// property property to propagated value and continue with the next
+			// child."
 			if (child == ancestorList[ancestorList.length - 1]) {
-				child.style[propertyName] = propagatedValue;
+				child.style[property] = propagatedValue;
 				continue;
 			}
 
-			// "Style child, with property name and tag list as in this
-			// algorithm, and property value equal to propagated value."
-			styleNode(child, propertyName, propagatedValue, tagList);
+			// "Force the style of child, with property as in this algorithm
+			// and new value equal to propagated value."
+			forceStyle(child, property, propagatedValue);
+		}
+	}
+}
+
+function forceStyle(node, property, newValue) {
+	// "If node is an Element and property computes to new value on node, abort
+	// this algorithm."
+	if (node.nodeType == Node.ELEMENT_NODE
+	&& cssValuesEqual(property, getComputedStyle(node)[property], newValue)) {
+		return;
+	}
+
+	// "If node is not an Element, node's parent is an Element, and property
+	// computes to new value on node's parent, abort this algorithm."
+	if (node.nodeType != Node.ELEMENT_NODE
+	&& node.parentNode.nodeType == Node.ELEMENT_NODE
+	&& cssValuesEqual(property, getComputedStyle(node.parentNode)[property], newValue)) {
+		return;
+	}
+
+	// "If node is an unwrappable element:"
+	if (isUnwrappableElement(node)) {
+		// "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);
+		}
+
+		// "Abort this algorithm."
+		return;
+	}
+
+	// "If node's previousSibling is a simple styling element whose specified
+	// style and computed 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, getComputedStyle(node.previousSibling)[property], newValue)) {
+		node.previousSibling.appendChild(node);
+		return;
+	}
+
+	// "If node's nextSibling is a simple styling element whose specified style
+	// and computed 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, getComputedStyle(node.nextSibling)[property], newValue)) {
+		node.nextSibling.insertBefore(node, node.nextSibling.childNodes.length
+			? node.nextSibling.childNodes[0]
+			: null);
+		return;
+	}
+
+	// "If node is a Comment or ProcessingInstruction, abort this algorithm."
+	if (node.nodeType == Node.COMMENT_NODE
+	|| node.nodeType == Node.PROCESSING_INSTRUCTION_NODE) {
+		return;
+	}
+
+	// "If node is an Element and property computes to new value on node, abort
+	// this algorithm."
+	if (node.nodeType == Node.ELEMENT_NODE
+	&& cssValuesEqual(property, getComputedStyle(node)[property], newValue)) {
+		return;
+	}
+
+	// "If node is not an Element, node's parent is an Element, and property
+	// computes to new value on node's parent, abort this algorithm."
+	if (node.nodeType != Node.ELEMENT_NODE
+	&& node.parentNode.nodeType == Node.ELEMENT_NODE
+	&& cssValuesEqual(property, getComputedStyle(node.parentNode)[property], newValue)) {
+		return;
+	}
+
+	// "If property is "font-weight" and new value is "bold", let tag be "b"."
+	var tag;
+	if (property == "fontWeight" && newValue == "bold") {
+		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 the result of calling createElement(tag) on the
+	// ownerDocument of node."
+	var newParent = node.ownerDocument.createElement(tag);
+
+	// "Insert new parent in node's parent before node."
+	node.parentNode.insertBefore(newParent, node);
+
+	// "If the computed value of property for new parent is not new value, set
+	// the CSS property property of new parent to new value."
+	if (!cssValuesEqual(property, getComputedStyle(newParent)[property], newValue)) {
+		newParent.style[property] = newValue;
+	}
+
+	// "Append node to new parent as its last child."
+	newParent.appendChild(node);
+
+	// "If node is an Element and the computed value of property for node is
+	// not new value, set the CSS property property of node to new value."
+	if (node.nodeType == Node.ELEMENT_NODE
+	&& !cssValuesEqual(property, getComputedStyle(node)[property], newValue)) {
+		node.style[property] = newValue;
+	}
+}
+
+function styleNode(node, property, newValue) {
+	// "If node is a Document, style its Element child (if it has one) and
+	// abort this algorithm."
+	if (node.nodeType == Node.DOCUMENT_NODE) {
+		for (var i = 0; i < node.childNodes.length; i++) {
+			if (node.childNodes[i].nodeType == Node.ELEMENT_NODE) {
+				styleNode(node.childNodes[i], property, newValue);
+				break;
+			}
+		}
+		return;
+	}
+
+	// "If node is a DocumentFragment, let children be a list of its children.
+	// Style each member of children, then abort this algorithm."
+	if (node.nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
+		var children = [];
+		for (var i = 0; i < node.childNodes.length; i++) {
+			children.push(node.childNodes[i]);
+		}
+		for (var i = 0; i < children.length; i++) {
+			styleNode(children[i], property, newValue);
+		}
+		return;
+	}
+
+	// "If node's parent is null, or if node is a DocumentType, abort this
+	// algorithm."
+	if (!node.parentNode || node.nodeType == Node.DOCUMENT_TYPE_NODE) {
+		return;
+	}
+
+	// "If node is an Element:"
+	if (node.nodeType == Node.ELEMENT_NODE) {
+		// "Clear styles on node, and let new nodes be the result."
+		var newNodes = clearStyles(node, property);
+
+		// "For each new node in new nodes, style new node, with the same
+		// inputs as this invocation of the algorithm."
+		for (var i = 0; i < newNodes.length; i++) {
+			styleNode(newNodes[i], property, newValue);
+		}
+
+		// "If node's parent is null, abort this algorithm."
+		if (!node.parentNode) {
+			return;
 		}
 	}
 
-	// "If node is an Element and property name does not compute to new value
-	// on it, set property name to new value on it."
-	if (node.nodeType == Node.ELEMENT_NODE
-	&& !cssValuesEqual(propertyName, newValue, getComputedStyle(node)[propertyName])) {
-		node.style[propertyName] = newValue;
+	// "Push down styles on node."
+	pushDownStyles(node, property, newValue);
+
+	// "Force the style of node."
+	forceStyle(node, property, newValue);
+
+	// "Let children be the children of node."
+	var children = [];
+	for (var i = 0; i < node.childNodes.length; i++) {
+		children.push(node.childNodes[i]);
 	}
 
-	// "If node is a Text node and property name does not compute to new value
-	// on its parent:"
-	if (node.nodeType == Node.TEXT_NODE
-	&& !cssValuesEqual(propertyName, newValue, getComputedStyle(node.parentNode)[propertyName])) {
-		// "Let new parent be the result of calling createElement("span") on
-		// the ownerDocument of node."
-		var newParent = node.ownerDocument.createElement("span");
-
-		// "Set property name to new value on new parent."
-		newParent.style[propertyName] = newValue;
-
-		// "Insert new parent into node's parent before node."
-		node.parentNode.insertBefore(newParent, node);
-
-		// "Append node as the last child of new parent."
-		newParent.appendChild(node);
-	}
-}
-
-// "When a user agent is to unstyle a Range range, it must decompose the Range,
-// then unstyle each Node in the returned list."
-function unstyleRange(range, propertyName, propertyValue, tagList) {
-	var nodeList = decomposeRange(range);
-
-	for (var i = 0; i < nodeList.length; i++) {
-		unstyleNode(nodeList[i], propertyName, propertyValue, tagList);
+	// "Style each member of children."
+	for (var i = 0; i < children.length; i++) {
+		styleNode(children[i], property, newValue);
 	}
 }
 
 function myExecCommand(commandId, showUI, value) {
 	commandId = commandId.toLowerCase();
-	var range = activeRange(document);
+	var range = getActiveRange(document);
 
 	if (!range) {
 		return;
@@ -912,13 +903,17 @@
 
 	switch (commandId) {
 		case "bold":
-		if (getState("bold", range)) {
-			unstyleRange(range, "fontWeight", "normal", ["b", "strong"]);
-		} else {
-			styleRange(range, "fontWeight", "bold", ["b", "strong"]);
+		// "Decompose the Range. If the state of the Range for this command is
+		// then true, style each returned Node with property "font-weight" and
+		// new value "bold". Otherwise, style them with new value "normal"."
+		var nodeList = decomposeRange(range);
+		var newValue = getState("bold", range) ? "normal" : "bold";
+		for (var i = 0; i < nodeList.length; i++) {
+			styleNode(nodeList[i], "fontWeight", newValue);
 		}
 		break;
 
+		/*
 		case "createlink":
 		// "If value is the empty string, do nothing."
 		if (value === "") {
@@ -957,7 +952,7 @@
 				// "While ancestor link is not an HTML element, or its local
 				// name is not "a", or it has no HTML attribute with local name
 				// "href":"
-				while (ancestorLink.namespaceURI != htmlNamespace
+				while (!isHtmlNamespace(ancestorLink.namespaceURI)
 				|| ancestorLink.nodeType != Node.ELEMENT_NODE
 				|| ancestorLink.tagName != "A"
 				|| !ancestorLink.hasAttribute("href")) {
@@ -994,22 +989,16 @@
 				newParent.appendChild(textNode);
 			}
 		}
-
-		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"]);
-		} else {
-			styleRange(range, "fontStyle", "italic", ["i", "em"]);
+		// "Decompose the Range. If the state of the Range for this command is
+		// then true, style each returned Node with property "font-style" and
+		// new value "italic". Otherwise, style them with new value "normal"."
+		var nodeList = decomposeRange(range);
+		var newValue = getState("italic", range) ? "normal" : "italic";
+		for (var i = 0; i < nodeList.length; i++) {
+			styleNode(nodeList[i], "fontStyle", newValue);
 		}
 		break;
 
@@ -1020,7 +1009,7 @@
 
 function myQueryCommandState(commandId) {
 	commandId = commandId.toLowerCase();
-	var range = activeRange(document);
+	var range = getActiveRange(document);
 
 	if (!range) {
 		return false;
@@ -1030,29 +1019,69 @@
 }
 
 function getState(commandId, range) {
-	var style = getComputedStyle(beginningElement(range));
-
-	switch (commandId) {
-		case "bold":
-		return style.fontWeight == "bold"
-			|| (/^[0-9]+$/.test(style.fontWeight) && style.fontWeight >= 700);
-
-		case "italic":
-		return style.fontStyle == "italic" || style.fontStyle == "oblique";
-
-		default:
+	if (commandId != "bold"
+	&& commandId != "italic") {
 		return false;
 	}
+
+	var node = range.startContainer;
+	var stop = nextNode(range.endContainer);
+
+	for (node = range.startContainer; node && node != nextNodeDescendants(range.endContainer); node = nextNode(node)) {
+		if (!isEffectivelyContained(node, range)) {
+			continue;
+		}
+
+		var element;
+		if (node.nodeType == Node.TEXT_NODE) {
+			element = node.parentNode;
+		} else {
+			element = node;
+		}
+
+		if (element.nodeType != Node.ELEMENT_NODE) {
+			continue;
+		}
+
+		var style = getComputedStyle(element);
+
+		if (commandId == "bold") {
+			// "True if every Element that is effectively contained in the
+			// Range has computed font-weight at least 700, and the parent of
+			// every Text node that is effectively contained in the Range has
+			// computed font-weight at least 700. Otherwise false."
+			if (style.fontWeight != "bold"
+			&& style.fontWeight != "700"
+			&& style.fontWeight != "800"
+			&& style.fontWeight != "900") {
+				return false;
+			}
+		} else if (commandId == "italic") {
+			// "True if every Element that is effectively contained in the
+			// Range has computed font-style "italic" or "oblique", and the
+			// parent of every Text node that is effectively contained in the
+			// Range has computed font-style "italic" or "oblique". Otherwise
+			// false."
+			if (style.fontStyle != "italic"
+			&& style.fontStyle != "oblique") {
+				return false;
+			}
+		}
+	}
+
+	return true;
 }
 
 function myQueryCommandValue(commandId) {
 	commandId = commandId.toLowerCase();
-	var range = activeRange(document);
+	var range = getActiveRange(document);
 
 	if (!range) {
 		return "";
 	}
 
+	return "";
+	/*
 	var style = getComputedStyle(beginningElement(range));
 
 	switch (commandId) {
@@ -1081,5 +1110,5 @@
 
 		default:
 		return "";
-	}
+	}*/
 }
--- a/preprocess	Tue Mar 15 22:39:50 2011 -0600
+++ b/preprocess	Thu Mar 17 16:14:25 2011 -0600
@@ -6,44 +6,56 @@
 # in Anolis proper.
 
 replace = {
-    "ancestor": "<span data-anolis-spec=domcore title=concept-tree-ancestor>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>",
-    "bpposition": "<span data-anolis-spec=domrange title=concept-bp-position>position</span>",
-    "child": "<span data-anolis-spec=domcore title=concept-tree-child>child</span>",
-    "children": "<span data-anolis-spec=domcore title=concept-tree-child>children</span>",
-    "collection": "<span data-anolis-spec=domcore title=concept-collection>collection</span>",
-    "contained": "<span data-anolis-spec=domrange>contained</span>",
-    "comment": "<code data-anolis-spec=domcore>Comment</code>",
-    "contextobject": "<span data-anolis-spec=domrange>context object</span>",
-    "descendant": "<span data-anolis-spec=domcore title=concept-tree-descendant>descendant</span>",
-    "document": "<code data-anolis-spec=domcore>Document</code>",
-    "element": "<code data-anolis-spec=domcore>Element</code>",
-    "getselection": "<code data-anolis-spec=domrange title=dom-Document-getSelection>getSelection()</code>",
-    "htmlnamespace": "<span data-anolis-spec=domcore>HTML namespace</span>",
-    "index": "<span data-anolis-spec=domrange title=concept-indexof>index</span>",
-    "localname": "<span data-anolis-spec=domcore title=concept-element-local-name>local name</span>",
-    "namespace": "<span data-anolis-spec=domcore title=concept-element-namespace>namespace</span>",
-    "nextsibling": "<code data-anolis-spec=domcore title=dom-Node-nextSibling>nextSibling</code>",
-    "node": "<code data-anolis-spec=domcore>Node</code>",
-    "nodelength": "<span data-anolis-spec=domrange title=concept-node-length>length</span>",
-    "ownerdocument": "<code data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code>",
-    "parent": "<span data-anolis-spec=domcore title=concept-tree-parent>parent</span>",
-    "partiallycontained": "<span data-anolis-spec=domrange>partially contained</span>",
-    "phrasingcontent": "<span data-anolis-spec=html>phrasing content</span>",
-    "presentationalhint": "<span data-anolis-spec=html title='presentational hints'>presentational hint</span>",
-    "previoussibling": "<code data-anolis-spec=domcore title=dom-Node-previousSibling>previousSibling</code>",
-    "processinginstruction": "<code data-anolis-spec=domcore>ProcessingInstruction</code>",
-    "range": "<code data-anolis-spec=domrange>Range</code>",
-    "rangeend": "<span data-anolis-spec=domrange title=concept-range-end>end</span>",
-    "rangeroot": "<span data-anolis-spec=domrange title=concept-range-root>root</span>",
-    "rangestart": "<span data-anolis-spec=domrange title=concept-range-start>start</span>",
-    "selection": "<code data-anolis-spec=domrange>Selection</code>",
-    "text": "<code data-anolis-spec=domcore>Text</code>",
-    "treeorder": "<span data-anolis-spec=domcore>tree order</span>",
+    'ancestor': '<span data-anolis-spec=domcore title=concept-tree-ancestor>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>',
+    'b': '<code data-anolis-spec=html title="the b element">b</code>',
+    '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>',
+    'bpposition': '<span data-anolis-spec=domrange title=concept-bp-position>position</span>',
+    'child': '<span data-anolis-spec=domcore title=concept-tree-child>child</span>',
+    'children': '<span data-anolis-spec=domcore title=concept-tree-child>children</span>',
+    'collection': '<span data-anolis-spec=domcore title=concept-collection>collection</span>',
+    'contained': '<span data-anolis-spec=domrange>contained</span>',
+    'comment': '<code data-anolis-spec=domcore>Comment</code>',
+    'contextobject': '<span data-anolis-spec=domrange>context object</span>',
+    'descendant': '<span data-anolis-spec=domcore title=concept-tree-descendant>descendant</span>',
+    'document': '<code data-anolis-spec=domcore>Document</code>',
+    'documentfragment': '<code data-anolis-spec=domcore>DocumentFragment</code>',
+    'element': '<code data-anolis-spec=domcore>Element</code>',
+    'em': '<code data-anolis-spec=html title="the em element">em</code>',
+    'font': '<code data-anolis-spec=html title=font>font</code>',
+    'fontcolor': '<code data-anolis-spec=html title=dom-font-color>color</code>',
+    'fontface': '<code data-anolis-spec=html title=dom-font-face>face</code>',
+    'fontsize': '<code data-anolis-spec=html title=dom-font-size>size</code>',
+    'getselection': '<code data-anolis-spec=domrange title=dom-Document-getSelection>getSelection()</code>',
+    'htmlnamespace': '<span data-anolis-spec=domcore>HTML namespace</span>',
+    'i': '<code data-anolis-spec=html title="the i element">i</code>',
+    'index': '<span data-anolis-spec=domrange title=concept-indexof>index</span>',
+    'localname': '<span data-anolis-spec=domcore title=concept-element-local-name>local name</span>',
+    'namespace': '<span data-anolis-spec=domcore title=concept-element-namespace>namespace</span>',
+    'nextsibling': '<code data-anolis-spec=domcore title=dom-Node-nextSibling>nextSibling</code>',
+    'node': '<code data-anolis-spec=domcore>Node</code>',
+    'nodelength': '<span data-anolis-spec=domrange title=concept-node-length>length</span>',
+    'ownerdocument': '<code data-anolis-spec=domcore title=dom-Node-ownerDocument>ownerDocument</code>',
+    'parent': '<span data-anolis-spec=domcore title=concept-tree-parent>parent</span>',
+    'partiallycontained': '<span data-anolis-spec=domrange>partially contained</span>',
+    'phrasingcontent': '<span data-anolis-spec=html>phrasing content</span>',
+    'presentationalhint': '<span data-anolis-spec=html title="presentational hints">presentational hint</span>',
+    'previoussibling': '<code data-anolis-spec=domcore title=dom-Node-previousSibling>previousSibling</code>',
+    'processinginstruction': '<code data-anolis-spec=domcore>ProcessingInstruction</code>',
+    'range': '<code data-anolis-spec=domrange>Range</code>',
+    'rangeend': '<span data-anolis-spec=domrange title=concept-range-end>end</span>',
+    'rangeroot': '<span data-anolis-spec=domrange title=concept-range-root>root</span>',
+    'rangestart': '<span data-anolis-spec=domrange title=concept-range-start>start</span>',
+    'selection': '<code data-anolis-spec=domrange>Selection</code>',
+    'span': '<code data-anolis-spec=html title="the span element">span</code>',
+    'strong': '<code data-anolis-spec=html title="the strong element">strong</code>',
+    'style': '<code data-anolis-spec=html title="the style attribute">style</code>',
+    'text': '<code data-anolis-spec=domcore>Text</code>',
+    'treeorder': '<span data-anolis-spec=domcore>tree order</span>',
+    'u': '<code data-anolis-spec=html title="the u element">u</code>',
 }
 
 s = open("source.html", "r").read()
@@ -60,3 +72,4 @@
 f = open("intermediate.html", "w")
 f.write(s)
 f.close()
+# vim: set textwidth=0:
--- a/source.html	Tue Mar 15 22:39:50 2011 -0600
+++ b/source.html	Thu Mar 17 16:14:25 2011 -0600
@@ -23,6 +23,7 @@
    white-space: pre-wrap;
  }
  div.note > p:first-child::before { content: 'Note: '; }
+ div + * > li { margin: 1em 0 }
 </style>
 <body class=draft>
 <div class=head id=head>
@@ -66,6 +67,57 @@
 major preexisting rendering engines are known not to match it, the reasoning is
 included in HTML comments so as not to distract the reader.
 
+<p>The principles I've used for writing this specification so far are:
+
+<ul>
+  <li>If all browsers that implement a particular feature agree on some detail
+  of how it works, match them unless there's very good reason not to.  When
+  it's not clear what behavior is best, try to follow the implementations with
+  the most market share.  But if one browser's behavior is clearly better than
+  the others', go with the better behavior.
+
+  <li>If a command is issued to format some text in a particular way, we will
+  format the text that way no matter what.  If the user clicks the "bold"
+  button, they don't care that the text didn't become bold because of an
+  external CSS rule or for any other reason, they only care that it didn't
+  work.  The only exception (beyond where it's simply impossible, like
+  propagated text-decorations we can't remove) is that we don't try to override
+  !important rules from external stylesheets, although we also don't go out of
+  our way to respect them.
+
+  <li>When we're given a presentational command like "bold", don't modify
+  anything other than presentational markup related to that command.  If an
+  element has non-presentational attributes like id or class, don't split it up
+  or remove it or anything.  At most convert it to a span, if it's some type of
+  presentational element (where "presentational" here really means "browsers
+  produce it in response to execCommand() so we need to treat it as
+  presentational", so it includes things like [[strong]] and [[em]]).
+
+  <li>Don't interfere with more markup than necessary.  If the user modifies
+  only a small run of text, don't go around simplifying ancestors or siblings
+  or whatever unless it's necessary to produce simpler markup in the place that
+  was actually modified.
+
+  <li>But if we are already changing around something's style, convert existing
+  styles to the preferred format.  For instance, we use [[b]] for bold, and
+  convert [[strong]] and &lt;[[span]] [[style]]="font-weight: bold"> if we
+  happen to be modifying that node anyway.
+
+  <li>Don't make the document less conforming than it originally was.  If we
+  happen to make it more conforming, good, although we don't have to go out of
+  our way to do that.  (In the future, I'll add a mode that supports using
+  obsolete presentational elements instead of CSS for the sake of e-mail
+  clients and such, and this rule will be bent in that mode.)
+
+  <li>Keep the markup as concise as possible.  (I've received feedback that
+  this is very important to authors.)  Ideally, the markup should look as
+  simple and neat as what a human would have produced by hand-editing.  We do
+  complicated manipulation to pull styles down from ancestors rather than
+  having to use inline CSS, and make sure to tidy up any styles on elements
+  that we happen to be modifying anyway.  Previous principles take precedence
+  over this one, however.
+</ul>
+
 
 <h2>Issues</h2>
 
@@ -113,27 +165,16 @@
 <span data-anolis-spec=domcore title=concept-attr-namespace>namespace</span> is
 the [[htmlnamespace]].
 
-<p>The <dfn>beginning element</dfn> of a [[range]] is returned by the following
-algorithm:
-
-<ol>
-  <li>If the [[rangestart]] [[bpnode]] of the [[range]] is a [[text]],
-  [[comment]], or [[processinginstruction]] node, and the [[rangestart]]
-  [[bpoffset]] of the [[range]] is not equal to the [[nodelength]] of its
-  [[rangestart]] [[bpnode]], let <var>first node</var> be the [[range]]'s
-  [[rangestart]] [[bpnode]].
+<p>A [[node]] is <dfn>effectively contained</dfn> in a [[range]] if either it
+is [[contained]] in the [[range]]; or it is the [[range]]'s [[rangestart]]
+[[bpnode]], it is a [[text]] node, and its [[nodelength]] is different from the
+[[range]]'s [[rangestart]] [[bpoffset]]; or it is the [[range]]'s [[rangeend]]
+[[bpnode]], it is a [[text]] node, and the [[range]]'s [[rangeend]]
+[[bpoffset]] is not 0.
 
-  <li>Otherwise, let <var>first node</var> be the first [[node]] in
-  [[treeorder]] that is [[contained]] in the [[range]], if there is any.
-
-  <li>If <var>first node</var> is defined and is an [[element]], return
-  <var>first node</var>.
-
-  <li>Otherwise, if <var>first node</var> is defined and its [[parent]] is an
-  [[element]], return <var>first node</var>'s [[parent]].
-
-  <li>Return null.
-</ol>
+<p class=note>A node is <span>effectively contained</span> in a range if and
+only if it would be [[contained]] after the range is <span
+title=decompose>decomposed</span>.
 
 <p>The <dfn>active range</dfn> of a [[document]] is the value returned by the
 following algorithm:
@@ -157,108 +198,77 @@
   [[selection]], the active range is simply the only one in the selection.
 </ol>
 
-<p>Given a CSS property name <var>property name</var>, an (optional) value
-<var>property value</var> for that property, and a possibly empty list of
-strings <var>tag list</var>, a [[node]] is a <dfn>potentially relevant styling
-element</dfn> if it is an <span>HTML element</span> and one of the following
-holds:
-
-<ul>
-  <li>Its [[localname]] is in <var>tag list</var> and it has no attributes.
-
-  <li>Its [[localname]] is in <var>tag list</var> or is "span" or is "font",
-  and it has exactly one attribute, and that attribute is an <span>HTML
-  attribute</span> with [[attrlocalname]] "style", and that attribute sets
-  exactly one CSS property, and that property is <var>property name</var>, and
-  either <var>property value</var> is undefined or the value the attribute sets
-  the property to is <var>property value</var>.
-
-  <li>Its [[localname]] is "font", and it has exactly one attribute, and that
-  attribute is a <code data-anolis-spec=html title=dom-font-color>color</code>
-  attribute, and either <var>property value</var> is undefined or the effect of
-  the attribute is to hint that the CSS color attribute be set to <var>property
-  value</var>, and <var>property name</var> is "color".
-
-  <li>Its [[localname]] is "font", and it has exactly one attribute, and that
-  attribute is a <code data-anolis-spec=html title=dom-font-face>face</code>
-  attribute, and either <var>property value</var> is undefined or the effect of
-  the attribute is to hint that the CSS font-family attribute be set to
-  <var>property value</var>, and <var>property name</var> is "font-family".
+<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).
 
-  <li>Its [[localname]] is "font", and it has exactly one attribute, and that
-  attribute is a <code data-anolis-spec=html title=dom-font-size>size</code>
-  attribute, and either <var>property value</var> is undefined or the effect of
-  the attribute is to hint that the CSS font-size attribute be set to
-  <var>property value</var>, and <var>property name</var> is "font-size".
-
-  <!-- Should we bother handling <font color=red style=color:red>?  Let's not.
-  -->
-</ul>
-
-<p>A [[node]] is a <dfn>relevant styling element</dfn> if it is a
-<span>potentially relevant styling element</span>, and its CSS property
-<var>property name</var> computes to <var>property value</var> (which cannot be
-undefined).
-
-<div class=note>
-<p>If <var>property name</var> is "font-weight", <var>property value</var> is
-"bold", and <var>tag list</var> contains "b", an example of a <span>potentially
-relevant styling element</span> that is not actually <span title="relevant
-styling element">relevant</span> is
-
-<xmp><p style="font-weight: 100"><b>Foo</b></p></xmp>
-
-<p>Since <code data-anolis-spec=html title="the b element">b</code>'s default
-font-weight is "bolder", the computed font-weight will most likely end up being
-"normal" or lighter.
-</div>
-
-<p>A <dfn>phrasing element</dfn> is either an <span>HTML element</span> that is
-categorized as [[phrasingcontent]], or a <span data-anolis-spec=html
-title="non-conforming element">non-conforming</span> <span>HTML element</span>
-(which thus has no categories), or an [[element]] that is not an <span>HTML
-element</span>.
-
-<p class=XXX>We should allow unrecognized HTML elements too.
-
-<p>The <dfn>specified style</dfn> of an [[element]] for a given <var>property
-name</var> is returned by the following algorithm, which will return either a
-CSS value or null:
+<p>The <dfn>specified style</dfn> of an [[element]] for a given
+<var>property</var> is returned by the following algorithm, which will return
+either a CSS value or null:
 
 <ol>
   <li>If the [[element]] has a <code data-anolis-spec=html
   title="the style attribute">style</code> attribute set, and that attribute has
-  the effect of setting <var>property name</var>, return the value that it sets
-  <var>property name</var> to.
+  the effect of setting <var>property</var>, return the value that it sets
+  <var>property</var> to.
 
   <li>If the [[element]] is a <code data-anolis-spec=html>font</code> element
   that has an attribute whose effect is to create a [[presentationalhint]] for
-  <var>property name</var>, return the value that the hint sets <var>property
-  name</var> to.
+  <var>property</var>, return the value that the hint sets <var>property</var>
+  to.
 
-  <li>If the [[element]] is in the following list, and <var>property name</var>
-  is equal to the CSS property name listed for it, return the string listed for
+  <li>If the [[element]] is in the following list, and <var>property</var> is
+  equal to the CSS property name listed for it, return the string listed for
   it.
 
-  <p class=XXX>Add any other elements that can be output by the style/unstyle
-  algorithms, or existing browser implementations of execCommand().
-
   <dl class=switch>
-    <dt><code data-anolis-spec=html title="the b element">b</code>
-    <dt><code data-anolis-spec=html title="the strong element">strong</code>
+    <dt>[[b]]
+    <dt>[[strong]]
     <dd>font-weight: "bold"
 
-    <dt><code data-anolis-spec=html title="the i element">i</code>
-    <dt><code data-anolis-spec=html title="the em element">em</code>
+    <dt>[[i]]
+    <dt>[[em]]
     <dd>font-style: "italic"
 
-    <dt><code data-anolis-spec=html title="the u element">u</code>
+    <dt>[[u]]
     <dd>text-decoration: "underline"
   </dl>
 
   <li>Return null.
 </ol>
 
+<p>A <dfn>simple styling element</dfn> is an <span>HTML element</span> for
+which at least one of the following holds:
+
+<ol>
+  <li>It is a [[b]], [[em]], [[font]], [[i]], [[span]], [[strong]], or [[u]]
+  element with no attributes.
+
+  <li>It is a [[b]], [[em]], [[font]], [[i]], [[span]], [[strong]], or [[u]]
+  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
+  [[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 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 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 [[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>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
 though the method had actually been called from JavaScript.  In particular,
@@ -272,8 +282,8 @@
 sequentially in the list's order.
 
 
-<h2>Decomposing a Range into Nodes</h2>
-<p>When a user agent is to <dfn>decompose a [[range]]</dfn> <var>range</var>,
+<h2>Decomposing a range into nodes</h2>
+<p>When a user agent is to <dfn>decompose</dfn> a [[range]] <var>range</var>,
 it must run the following steps.
 
 <ol>
@@ -331,9 +341,26 @@
   <var>start offset</var>) and its [[rangeend]] to (<var>end node</var>,
   <var>end offset</var>).
 
-  <li>Return a list consisting of every [[node]] [[contained]] in
-  <var>range</var> in [[treeorder]], omitting any whose [[parent]] is also
-  [[contained]] in <var>range</var>.
+  <!-- Now we want to make sure our range contains as many nodes as possible,
+  such as by changing <tag>[foo]</tag> to {<tag>foo</tag>}. -->
+  <li>Let <var>cloned range</var> be the result of calling <code
+  data-anolis-spec=domrange title=dom-Range-cloneRange>cloneRange()</code> on
+  <var>range</var>.
+
+  <li>While the [[rangestart]] [[bpoffset]] of <var>cloned range</var> is 0,
+  and the [[parent]] of <var>cloned range</var>'s [[rangestart]] [[bpnode]] is
+  not null, set the [[rangestart]] of <var>cloned range</var> to ([[parent]] of
+  [[rangestart]] [[bpnode]], [[index]] of [[rangestart]] [[bpnode]]).
+
+  <li>While the [[rangeend]] [[bpoffset]] of <var>cloned range</var> equals the
+  [[nodelength]] of its [[rangeend]] [[bpnode]], and the [[parent]] of
+  <var>clone range</var>'s [[rangeend]] [[bpnode]] is not null, set the
+  [[rangeend]] of <var>cloned range</var> to ([[parent]] of [[rangeend]]
+  [[bpnode]], 1 + [[index]] of [[rangeend]] [[bpnode]]).
+
+  <li>Return a list consisting of every [[node]] [[contained]] in <var>cloned
+  range</var> in [[treeorder]], omitting any whose [[parent]] is also
+  [[contained]] in <var>cloned range</var>.
 </ol>
 
 
@@ -341,27 +368,22 @@
 <p>When a user agent is to <dfn>clear styles</dfn> on an element, it must run
 the following steps:
 
-<div class=note>
-<p>Clearing styles (<span title="recursively clear styles">recursively</span>
-or not) can remove it from its parent and put other nodes in its place.  When
-implementations do something like clear style on all children of an element,
-they should take care not to assume that the set of children won't change as
-they're unstyled.  If the element is removed, the algorithm will return the
-list of nodes inserted in its place.
+<p class=note>Clearing styles can remove it from its parent and put other nodes
+in its place.  When implementations do something like clear style on all
+children of an element, they should take care not to assume that the set of
+children won't change as they're unstyled.  If the element is removed, the
+algorithm will return the list of nodes inserted in its place.
 
-<p>Clearing styles only removes inline styles from the element.  It doesn't
-ensure that the element isn't inheriting styles from an ancestor (or a style
-rule).  For that, one must <span>unstyle a node</span>.
-</div>
+<p class=XXX>This should probably convert, e.g., &lt;font color=red id=foo>
+into &lt;span id=foo> instead of &lt;font id=foo>.
 
 <ol>
   <li>Let <var>element</var> be the [[element]] to be unstyled.
 
-  <li>Let <var>property name</var> and <var>tag list</var> be as
-  in the invoking algorithm.
+  <li>Let <var>property</var> be as in the invoking algorithm.
 
-  <li>If <var>element</var> is a <span>potentially relevant styling
-  element</span>:
+  <li>If <var>element</var> is a <span>simple styling element</span> and its
+  <span>specified style</span> for <var>property</var> is not null:
 
   <ol>
     <li>Let <var>children</var> be an empty list of [[node]]s.
@@ -377,32 +399,32 @@
       <var>element</var>.
     </ol>
 
-    <li>Remove <var>element</var>.
+    <li>Remove <var>element</var> from its [[parent]].
 
     <li>Return <var>children</var>.
   </ol>
 
-  <li>Unset the CSS property <var>property name</var> of <var>element</var>.
+  <li>Unset the CSS property <var>property</var> of <var>element</var>.
 
   <li>If <var>element</var> is a <code data-anolis-spec=html>font</code>
   element:
 
   <ol>
-    <li>If <var>property name</var> is "color", unset <var>element</var>'s
+    <li>If <var>property</var> is "color", unset <var>element</var>'s
     <code data-anolis-spec=html title=dom-font-color>color</code> attribute, if
     set.
 
-    <li>If <var>property name</var> is "font-family", unset
+    <li>If <var>property</var> is "font-family", unset
     <var>element</var>'s <code data-anolis-spec=html
     title=dom-font-face>face</code> attribute, if set.
 
-    <li>If <var>property name</var> is "font-size", unset <var>element</var>'s
+    <li>If <var>property</var> is "font-size", unset <var>element</var>'s
     <code data-anolis-spec=html title=dom-font-size>size</code> attribute, if
     set.
   </ol>
 
-  <li>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.
+  <li>If <var>element</var>'s <var>specified style</var> 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. -->
 
@@ -416,198 +438,49 @@
   <li>While <var>element</var> has children, append its first child
   as the last child of <var>new element</var>.
 
-  <li>Remove <var>element</var>.
+  <li>Remove <var>element</var> from its [[parent]].
 
   <li>Return the one-[[node]] list consisting of <var>new element</var>.
 </ol>
 
 
-<h2>Recursively clearing an element's styles</h2>
-<p>When a user agent is to <dfn>recursively clear styles</dfn> on an element,
-it must run the following steps:
-
-<ol>
-  <li>Let <var>element</var> be the [[element]] to be unstyled.
-
-  <li>Let <var>property name</var> and <var>tag list</var> be as
-  in the invoking algorithm.
-
-  <li>Let <var>element children</var> be the [[element]] children of
-  <var>element</var>.
-
-  <li><span>Recursively clear styles</span> on each [[element]] in <var>element
-  children</var>.
-
-  <li><span>Clear styles</span> on <var>element</var>, and return the resulting
-  list.
-</ol>
-
-
-<h2>Styling a Node</h2>
-<p>When a user agent is to <dfn>style a [[node]]</dfn> <var>node</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>.
-
-<p class=note>This algorithm applies the given style to the node itself, but
-doesn't interfere with conflicting styles on its descendants.  <span
-title="recursively style a node">Recursive styling</span> removes conflicting
-styles from descendants first.
+<h2>Pushing down styles</h2>
+<p>When a user agent is to <dfn>push down styles</dfn> to a [[node]]
+<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:
 
 <ol>
-  <li>If <var>node</var>'s [[parent]] is null, or if <var>node</var> is not an
-  [[element]], [[text]], [[comment]], or [[processinginstruction]] node, abort
-  this algorithm. <!-- XXX: What to do here? -->
-
-  <li>If <var>node</var> is an [[element]]:
-
-  <ol>
-    <li><span>Clear styles</span> on <var>node</var>, and let <var>new
-    nodes</var> be the result.
-
-    <li>For each <var>new node</var> in <var>new nodes</var>, <span
-    title="style a node">style <var>new node</var></span>, with the same inputs
-    as this invocation of the algorithm.
-
-    <li>If <var>node</var>'s [[parent]] is null, abort this algorithm.
-  </ol>
-
-  <li>If <var>node</var> is an [[element]] but not a <span>phrasing
-  element</span>:
+  <li>If <var>node</var>'s [[parent]] is not an [[element]], abort this
+  algorithm. <!-- E.g., a text node child of a document fragment. -->
 
-  <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 name</var> is neither null nor equal to <var>property
-    value</var>.
-
-    <li><span title="style a node">Style</span> each [[node]] in
-    <var>children</var>.
-
-    <li>Abort this algorithm.
-  </ol>
+  <li>If <var>node</var> is an [[element]] and <var>property</var> computes to
+  <var>new value</var> on <var>node</var>, abort this algorithm.
 
-  <li>If <var>node</var>'s [[previoussibling]] is a <span>relevant styling
-  element</span>, append <var>node</var> as the last [[child]] of its
-  [[previoussibling]] and abort this algorithm.
-
-  <li>If <var>node</var>'s [[nextsibling]] is a <span>relevant styling
-  element</span>, insert <var>node</var> as the first [[child]] of its
-  [[nextsibling]] and abort this algorithm.
-
-  <li>If <var>node</var> is a [[comment]] or [[processinginstruction]], abort
-  this algorithm.  <!-- There's no point in making a new element in this case.
-  -->
-
-  <li>If <var>node</var> is an [[element]] and the computed style of
-  <var>property name</var> for it is <var>property value</var>, abort this
+  <li>If <var>node</var> is not an [[element]] and <var>property</var> computes
+  to <var>new value</var> on <var>node</var>'s [[parent]], abort this
   algorithm.
 
-  <li>If <var>node</var> is a [[text]] node and the computed style of
-  <var>property name</var> for its [[parent]] is <var>property value</var>,
-  abort this algorithm.
-
-  <li>Let <var>tag</var> be the first string in <var>tag list</var>, if that is
-  not empty, or "span" if it is empty.
-
-  <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
-  [[ownerdocument]] of <var>node</var>.
-
-  <li>Insert <var>new parent</var> in <var>node</var>'s [[parent]] before
-  <var>node</var>.
-
-  <li>If the computed value of <var>property name</var> for <var>new
-  parent</var> is not <var>property value</var>, set the CSS property
-  <var>property name</var> of <var>new parent</var> to <var>property
-  value</var>.
-
-  <li>Append <var>node</var> to <var>new parent</var> as its last [[child]].
-</ol>
-
-
-<h2>Recursively styling a Node</h2>
-<p>When a user agent is to <dfn>recursively style a [[node]]</dfn>
-<var>node</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>If <var>node</var>'s [[parent]] is null, or if <var>node</var> is not an
-  [[element]], [[text]], [[comment]], or [[processinginstruction]] node, abort
-  this algorithm. <!-- XXX: What to do here? -->
-
-  <li>If <var>node</var> is an [[element]]:
-
-  <ol>
-    <li><span>Recursively clear styles</span> on <var>node</var>, and let
-    <var>new nodes</var> be the result.
-
-    <li>For each <var>new node</var> in <var>new nodes</var>, <span
-    title="recursively style a node">recursively style <var>new
-    node</var></span>, with the same inputs as this invocation of the
-    algorithm.
-
-    <li>If <var>node</var>'s [[parent]] is null, abort this algorithm.
-  </ol>
-
-  <li><span title="style a node">Style</span> <var>node</var>.
-</ol>
-
-
-<h2>Styling a Range</h2>
-<p>When a user agent is to <dfn>style a [[range]]</dfn>, it must <span
-title="decompose a range">decompose</span> the [[range]], then <span
-title="recursively style a node">recursively style</span> each [[node]] in the
-returned list.
-
-
-<h2>Unstyling a Node</h2>
-<p>When a user agent is to <dfn>unstyle a [[node]]</dfn> <var>node</var>, it
-must run the following steps.  There are three inputs: a CSS property name
-<var>property name</var>, a new value <var>new value</var>, and a possibly
-empty list of strings <var>tag list</var>.
-
-<ol>
-  <li>If <var>node</var>'s [[parent]] is null, or if <var>node</var> is not an
-  [[element]] or [[text]] node, abort this algorithm. <!-- XXX: What to do
-  here?  We want to ignore comments and PIs, but we might want to support
-  detached elements, documents, document fragments, . . . -->
-
-  <li>If <var>node</var> is an [[element]]:
-
-  <ol>
-    <li><span>Recursively clear styles</span> on <var>node</var>, and let
-    <var>new nodes</var> be the result.
-
-    <li>For each <var>new node</var> in <var>new nodes</var>, <span
-    title="unstyle a node">unstyle <var>new node</var></span>, with the same
-    inputs as this invocation of the algorithm.
-
-    <li>If <var>node</var>'s [[parent]] is null, abort this algorithm.
-  </ol>
-
-  <li>If <var>node</var> is an [[element]], let <var>current value</var> equal
-  the computed value of <var>property name</var> on <var>node</var>.
-  Otherwise, let <var>current value</var> equal the computed value of
-  <var>property name</var> on <var>node</var>'s [[parent]].
-
-  <li>If <var>current value</var> equals <var>new value</var>, abort this
-  algorithm.
+  <li>Let <var>current ancestor</var> be <var>node</var>'s [[parent]].
 
   <li>Let <var>ancestor list</var> be a list of [[node]]s, initially empty.
 
-  <li>Let <var>current ancestor</var> equal <var>node</var>.
+  <li>While <var>current ancestor</var> is an [[element]] and
+  <var>property</var> does not compute to <var>new value</var> on it, append
+  <var>current ancestor</var> to <var>ancestor list</var>, then set
+  <var>current ancestor</var> to its [[parent]].
 
-  <li>While <var>current ancestor</var>'s [[parent]] is an [[element]], set
-  <var>current ancestor</var> to its [[parent]], then append it to
-  <var>ancestor list</var>.
-  
-  <li>While <var>ancestor list</var> is not empty, and the last member of
-  <var>ancestor list</var> has <span>specified style</span> for <var>property
-  name</var> equal to <var>new value</var> or null, remove the last member from
-  <var>ancestor list</var>.
+  <!-- We can only remove specified styles, so if the style isn't specified,
+  give up. -->
+  <li>If <var>ancestor list</var> is not empty, and the <span>specified
+  style</span> of <var>property</var> on the last member of <var>ancestor
+  list</var> is null, abort this algorithm.
+
+  <!-- If we go all the way up to the root and still don't have the desired
+  style, pushing down styles is pointless.  It will create extra markup for no
+  purpose. -->
+  <li>If <var>ancestor list</var> is not empty, and the [[parent]] of the last
+  member of <var>ancestor list</var> is not an [[element]], abort this
+  algorithm.
 
   <li>While <var>ancestor list</var> is not empty:
 
@@ -618,7 +491,7 @@
     <li>Remove the last member from <var>ancestor list</var>.
 
     <li>Let <var>propagated value</var> be the <span>specified style</span> of
-    <var>current ancestor</var> for <var>property name</var>.
+    <var>current ancestor</var> for <var>property</var>.
 
     <li>If <var>propagated value</var> is null, continue this loop from the
     beginning.
@@ -635,53 +508,223 @@
       <var>child</var>.
 
       <li>If <var>child</var> is an [[element]] whose <span>specified
-      style</span> for <var>property name</var> is neither null nor equal to
+      style</span> for <var>property</var> is neither null nor equal to
       <var>propagated value</var>, continue with the next <var>child</var>.
 
       <li>If <var>child</var> is the last member of <var>ancestor list</var>,
-      set <var>child</var>'s CSS property <var>property name</var> to
+      set <var>child</var>'s CSS property <var>property</var> to
       <var>propagated value</var> and continue with the next <var>child</var>.
 
       <p class=note>This style will be removed on the next loop iteration and
       distributed to its children.
 
-      <li><span title="style a node">Style</span> <var>child</var>, with
-      <var>property name</var> and <var>tag list</var> as in this algorithm,
-      and <var>property value</var> equal to <var>propagated value</var>.
+      <li><span>Force the style</span> of <var>child</var>, with
+      <var>property</var> as in this algorithm and <var>new value</var> equal
+      to <var>propagated value</var>.
     </ol>
   </ol>
-
-  <!-- We might have a rule inherited from someplace where we can't remove it,
-  or maybe even a rule in a stylesheet (although that case is pathological and
-  we generally ignore it) -->
-  <li>If <var>node</var> is an [[element]] and <var>property name</var> does
-  not compute to <var>new value</var> on it, set <var>property name</var> to
-  <var>new value</var> on it.
-
-  <li>If <var>node</var> is a [[text]] node and <var>property name</var> does
-  not compute to <var>new value</var> on its [[parent]]:
-
-  <ol>
-    <li>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>Set <var>property name</var> to <var>new value</var> on <var>new
-    parent</var>.
-
-    <li>Insert <var>new parent</var> into <var>node</var>'s [[parent]] before
-    <var>node</var>.
-
-    <li>Append <var>node</var> as the last [[child]] of <var>new parent</var>.
-  </ol>
 </ol>
 
 
-<h2>Unstyling a Range</h2>
-<p>When a user agent is to <dfn>unstyle a [[range]]</dfn> <var>range</var>, it
-must <span title="decompose a range">decompose</span> the [[range]], then <span
-title="unstyle a node">unstyle</span> each [[node]] in the returned list.
+<h2>Forcing the style of a node</h2>
+<p>When a user agent is to <dfn>force the style</dfn> of a [[node]]
+<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.
+
+<p class=note>This algorithm checks if the node has the desired style, and if
+not, it wraps the node (or, if that's not possible, its descendants) in a
+<span>simple styling element</span>.  This is only used as a last resort after
+<span title="clear styles">clearing styles</span> and <span title="push down
+styles">pushing down styles</span> don't work to achieve the desired style.
+After forcing the style, descendants might still have different style.
+
+<ol>
+  <li>If <var>node</var> is an [[element]] and <var>property</var> computes to
+  <var>new value</var> on <var>node</var>, abort this algorithm.
+
+  <li>If <var>node</var> is not an [[element]], <var>node</var>'s [[parent]] is
+  an [[element]], and <var>property</var> computes to <var>new value</var> on
+  <var>node</var>'s [[parent]], abort this algorithm.
+
+  <li>If <var>node</var> is an <span>unwrappable element</span>:
+
+  <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.
+
+    <li>Abort this algorithm.
+  </ol>
+
+  <li>If <var>node</var>'s [[previoussibling]] 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>, append <var>node</var> as
+  the last [[child]] of its [[previoussibling]] and abort this algorithm.
+
+  <li>If <var>node</var>'s [[nextsibling]] 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>, insert <var>node</var> as
+  the first [[child]] of its [[nextsibling]] and abort this algorithm.
+
+  <!-- At this point we have to make a new element as a wrapper.  This isn't
+  worth the effort for comments or PIs, so abort in that case. -->
+  <li>If <var>node</var> is a [[comment]] or [[processinginstruction]], abort
+  this algorithm.
+
+  <li>If <var>node</var> is an [[element]] and <var>property</var> computes to
+  <var>new value</var> on <var>node</var>, abort this algorithm.
+
+  <li>If <var>node</var> is not an [[element]], <var>node</var>'s [[parent]] is
+  an [[element]], and <var>property</var> computes to <var>new value</var> on
+  <var>node</var>'s [[parent]], 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>If <var>property</var> is "text-decoration" and <var>new value</var> is
+  "underline", let <var>tag</var> be "u".
+
+  <li>If <var>tag</var> is not set, let <var>tag</var> be "span".
+
+  <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
+  [[ownerdocument]] of <var>node</var>.
+
+  <li>Insert <var>new parent</var> in <var>node</var>'s [[parent]] before
+  <var>node</var>.
+
+  <li>If the computed value of <var>property</var> for <var>new parent</var> is
+  not <var>new value</var>, set the CSS property <var>property</var> of
+  <var>new parent</var> to <var>new value</var>.
+
+  <li>Append <var>node</var> to <var>new parent</var> as its last [[child]].
+
+  <li>If <var>node</var> is an [[element]] and the computed value of
+  <var>property</var> for <var>node</var> is not <var>new value</var>, set the
+  CSS property <var>property</var> of <var>node</var> to <var>new value</var>.
+</ol>
+
+
+<h2>Styling a node</h2>
+<p>When a user agent is to <dfn>style</dfn> a [[node]] <var>node</var>, it must
+run the following steps.  There are two inputs: a CSS property name
+<var>property</var> and a new value <var>new value</var>.
+
+<div class=note>
+<p>The effect of this algorithm is to ensure that the node and all its
+descendants have the style requested, no matter what, producing the simplest
+markup possible to achieve that effect.  It's inspired by the approach WebKit
+takes.  The only places where the algorithm should fail are when there's an
+!important CSS rule that conflicts with the requested style (which we don't try
+to override because we assume it's !important for a reason), or when it's
+literally impossible to succeed (such as when a text-decoration is propagated
+from an ancestor we can't reach).  Any other failures are bugs.
+
+<p>First, if the node is an element with an inline style rule for this
+property, we unset it ("clearing styles").  This step also removes <span
+title="simple styling element">simple styling elements</span> entirely, and
+replaces elements like [[b]] or [[font]] with [[span]]s if they aren't simple
+styling elements.  This will be sufficient if the desired style is inherited
+from an ancestor, or if it's the default (like font-style: normal) and no
+conflicting style is inherited from an ancestor.  Even if clearing styles
+doesn't actually fix the style of the node we're dealing with, we do it anyway
+to simplify the generated markup.
+
+<p>If clearing styles didn't work, and it looks like an ancestor has inline
+style that we're inheriting, we push the style down from that ancestor.  Thus
+if we're unbolding the letter "r" in
+
+<xmp><b>foo <i>bar</i> baz</b>,</xmp>
+
+<p>we get
+
+<xmp><b>foo </b><i><b>ba</b>r</i><b> baz</b>.</xmp>
+
+<p>If we didn't push down styles, the final step (forcing styles) would instead
+give us
+
+<xmp><b>foo <i>ba<span style="font-weight: normal">r</span></i> baz</b>,</xmp>
+
+<p>which is much longer and uglier.  We take care not to disturb the style or
+semantics of anything but the node we're dealing with.
+
+<p>We'll only push down styles if some ancestor actually has the style we want,
+so we can inherit it.  Otherwise, it will just create useless markup.
+
+<p>Finally, if neither of the above strategies worked, we have to add new
+markup to get the desired style ("forcing styles").  First we try just sticking
+it into its previous or next sibling, if that's a <span>simple styling
+element</span> (so it won't add any styles or semantics we don't want).
+Otherwise, we create a new simple styling element and wrap it in that.  It's
+common that a previous sibling is the simple styling element we want, because
+often we'll style several consecutive siblings in succession.  In that case,
+the element created for the first can be reused for the later ones.
+
+<p>This last step works a bit differently if the node is an <span>unwrappable
+element</span>.  In that case, wrapping it in a simple styling element would
+make the document less conforming than it already was.  Instead, we recursively
+force style on its children.  The recursion will terminate when we hit a node
+that's wrappable, or when there are no further descendants.
+
+<p>After all this, the node is guaranteed to have the style we want, barring
+bugs in the algorithm or the two exceptions noted earlier (!important style
+rules, and impossible cases).  We then re-run the algorithm on each child
+recursively.  Typically this means just clearing the style of each descendant,
+because it should then inherit the style we just set on its ancestor.  In the
+unusual case that a descendant's style is wrong even after we clear style on
+it, such as because of a non-inline style rule (like trying to unbold a
+heading), we'll repeat the above steps to ensure that the style really gets set
+as desired.
+</div>
+
+<ol>
+  <li>If <var>node</var> is a [[document]], <span>style</span> its [[element]]
+  [[child]] (if it has one) and abort this algorithm.
+
+  <li>If <var>node</var> is a [[documentfragment]], let <var>children</var> be
+  a list of its [[children]].  <span>Style</span> each member of [[children]],
+  then abort this algorithm.
+
+  <li>If <var>node</var>'s [[parent]] is null, or if <var>node</var> is a <code
+  data-anolis-spec=domcore>DocumentType</code>, abort this algorithm.
+
+  <p class=XXX>We could style detached elements, but maybe it's not worth the
+  effort.  Is execCommand() even supposed to work on things that don't descend
+  from a document?  Needs investigation.
+
+  <li>If <var>node</var> is an [[element]]:
+
+  <ol>
+    <li><span>Clear styles</span> on <var>node</var>, and let <var>new
+    nodes</var> be the result.
+
+    <li>For each <var>new node</var> in <var>new nodes</var>,
+    <span>style</span> <var>new node</var>, with the same inputs as this
+    invocation of the algorithm.
+
+    <li>If <var>node</var>'s [[parent]] is null, abort this algorithm.
+  </ol>
+
+  <li><span>Push down styles</span> on <var>node</var>.
+
+  <li><span>Force the style</span> of <var>node</var>.
+
+  <li>Let <var>children</var> be the [[children]] of <var>node</var>.
+
+  <li><span>Style</span> each member of [[children]].
+
+  <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
+  children in full before they begin styling.
+</ol>
 
 
 <h2>Commands</h2>
@@ -737,10 +780,10 @@
 <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.
+the user agent must do nothing and abort these steps.  Otherwise, it must
+<span>decompose</span> the [[range]], then <span>style</span> each returned
+[[node]] with <var>property</var> equal to "background-color" and <var>new
+value</var> equal to <var>value</var>.
 <!-- 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
@@ -767,8 +810,7 @@
 <dd><p><strong>Value</strong>: The value is given by the following algorithm:
 
 <ol>
-  <li>Let <var>element</var> be the <span>beginning element</span> of the
-  [[range]].
+  <li class=XXX>...
 
   <li>While the computed style of "background-color" on <var>element</var>
   is any fully transparent value, set <var>element</var> to its parent.
@@ -788,16 +830,17 @@
 
 <dt><code title><dfn title=command-bold>bold</dfn></code>
 
-<dd><p><strong>Action</strong>: If the state 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-weight", <var>property
-value</var> "bold", and <var>tag list</var> ["b", "strong"].  Otherwise, it
-must <span title="unstyle a range">unstyle it</span> with <var>property
-name</var> "font-weight", <var>property value</var> "normal", and <var>tag
-list</var> ["b", "strong"].
+<dd><p><strong>Action</strong>: <span>Decompose</span> the [[range]].  If the
+state of the [[range]] for this command is then true, <span>style</span> each
+returned [[node]] with <var>property</var> "font-weight" and <var>new
+value</var> "bold".  Otherwise, <span>style</span> them with <var>new
+value</var> "normal".
 
-<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>State</strong>: True if every [[element]] that is
+<span>effectively contained</span> in the [[range]] has computed font-weight at
+least 700, and the [[parent]] of every [[text]] node that is <span>effectively
+contained</span> in the [[range]] has computed font-weight at least 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
@@ -823,8 +866,8 @@
   specify "#" for the value, or the author can rewrite it, so it's not like
   this makes the API less useful. -->
 
-  <li>Let <var>node list</var> be the result of <span title="decompose a
-  range">decomposing</span> the [[range]].
+  <li>Let <var>node list</var> be the result of <span
+  title=decompose>decomposing</span> the [[range]].
 
   <li>For each <var>node</var> in <var>node list</var>, in order:
 
@@ -902,10 +945,9 @@
 
 <dt><code title><dfn title=command-fontname>fontName</dfn></code>
 
-<dd><p><strong>Action</strong>: The user agent must <span title="style a
-range">style the [[range]]</span> with <var>property name</var> equal to
-"font-family", <var>property value</var> equal to <var>value</var>, and
-<var>tag list</var> equal to the empty list.
+<dd><p><strong>Action</strong>: <span>Decompose</span> the [[range]], then
+<span>style</span> each returned [[node]] with <var>property</var> equal to
+"font-family" and <var>new value</var> equal to <var>value</var>.
 <!-- UAs differ a bit in the details here:
 
 IE 9 RC: Empty string sets <font face="">
@@ -930,7 +972,7 @@
 <dd><p><strong>State</strong>: Always false.
 
 <dd><p><strong>Value</strong>: The computed value of the CSS property
-"font-family" for the <span>beginning element</span> of the [[range]].
+"font-family" for . . .
 <!-- Complicated.
 
 IE 9 RC: Always the empty string.  Not very useful.
@@ -967,9 +1009,9 @@
 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.
+Otherwise, it must <span>decompose</span> the [[range]], then
+<span>style</span> each returned [[node]] with <var>property</var> equal to
+"color" and <var>new value</var> equal to <var>value</var>.
 
 <dd><p><strong>State</strong>: Always false.
 <!-- This matches IE 9 RC and Chrome 10.  Opera 11 seems to return true if
@@ -979,7 +1021,7 @@
 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]].
+for . . .
 <!-- IE 9 RC returns the number 0 always, which makes no sense at all.  This
 matches the other browsers. -->
 
@@ -1034,17 +1076,17 @@
 
 <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", <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"].
+<dd><p><strong>Action</strong>: <span>Decompose</span> the [[range]].  If the
+state of the [[range]] for this command is then true, <span>style</span> each
+returned [[node]] with <var>property</var> "font-style" and <var>new
+value</var> "italic".  Otherwise, <span>style</span> them with <var>new
+value</var> "normal".
 
-<dd><p><strong>State</strong>: True if the <span>beginning element</span> of the
-[[range]] has font-style with computed value "italic" or "oblique", otherwise
-false.
+<dd><p><strong>State</strong>: True if every [[element]] that is
+<span>effectively contained</span> in the [[range]] has computed font-style
+"italic" or "oblique", and the [[parent]] of every [[text]] node that is
+<span>effectively contained</span> in the [[range]] has computed font-style
+"italic" or "oblique".  Otherwise false.
 
 <dd><p><strong>Value</strong>: Always the empty string.