Introduce "effective style" definition
authorAryeh Gregor <AryehGregor+gitcommit@gmail.com>
Mon, 21 Mar 2011 14:08:35 -0600
changeset 21 7b28dc5917ca
parent 20 43eb55543101
child 22 a88df2f75682
Introduce "effective style" definition

This helps to pave the way for supporting things like text-decoration.
autoimplementation.html
editcommands.html
implementation.js
source.html
--- a/autoimplementation.html	Mon Mar 21 12:08:09 2011 -0600
+++ b/autoimplementation.html	Mon Mar 21 14:08:35 2011 -0600
@@ -1,6 +1,7 @@
 <!doctype html>
 <title>Auto-running execCommand() tests</title>
 <style>
+body { font-family: serif }
 .yes { color: green }
 .no { color: red }
 b, strong { font-weight: bold }
@@ -271,6 +272,7 @@
 function doSetup(command) {
 	var div = document.getElementById(command);
 	div.contentEditable = "true";
+	div.spellcheck = false;
 	var table = div.querySelector("table");
 
 	var tr = document.createElement("tr");
@@ -287,8 +289,9 @@
 
 function doInputCell(tr, test) {
 	var inputCell = document.createElement("td");
-	inputCell.innerHTML = test;
-	inputCell.innerHTML = "<div>" + inputCell.innerHTML + "</div><div>" + inputCell.innerHTML.replace(/\&/g, "&amp;").replace(/</g, "&lt;") + "</div>";
+	inputCell.innerHTML = "<div></div><div></div>";
+	inputCell.firstChild.innerHTML = test;
+	inputCell.lastChild.textContent = inputCell.firstChild.innerHTML;
 	tr.appendChild(inputCell);
 }
 
@@ -301,8 +304,7 @@
 	}[command];
 
 	var specCell = document.createElement("td");
-	specCell.appendChild(document.createElement("div"));
-	specCell.appendChild(document.createElement("div"));
+	specCell.innerHTML = "<div></div><div></div>";
 	specCell.firstChild.innerHTML = test;
 	tr.appendChild(specCell);
 	try {
@@ -336,8 +338,7 @@
 	}[command];
 
 	var browserCell = document.createElement("td");
-	browserCell.appendChild(document.createElement("div"));
-	browserCell.appendChild(document.createElement("div"));
+	browserCell.innerHTML = "<div></div><div></div>";
 	browserCell.firstChild.innerHTML = test;
 	tr.appendChild(browserCell);
 	try {
@@ -472,10 +473,13 @@
 		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);
 		getSelection().removeAllRanges();
 		getSelection().addRange(range);
+		*/
+		getSelection().collapse(startNode, startOffset);
 		getSelection().extend(endNode, endOffset);
 	} else {
 		// IE9.  Selections have no direction, so we just make the selection
--- a/editcommands.html	Mon Mar 21 12:08:09 2011 -0600
+++ b/editcommands.html	Mon Mar 21 14:08:35 2011 -0600
@@ -212,6 +212,36 @@
 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).
 
+<p>The <dfn id=effective-style>effective 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> <var title="">element</var> for a
+given <var title="">property</var> is returned by the following algorithm, which will
+return a CSS value:
+
+<ol>
+  <li>If <var title="">property</var> is "text-decoration", and the "text-decoration"
+  property of <var title="">element</var> or any of its <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-ancestor title=concept-tree-ancestor>ancestors</a> computes to
+  "underline", return "underline".  Otherwise, return "none".
+
+  <p class=XXX>We ignore the possibility of text-decorations other than
+  underline for now, so we pretend that there are only two possible values:
+  "underline" and "none".
+
+  <li>If <var title="">property</var> is "background-color":
+
+  <ol>
+    <li>While the computed style of "background-color" on <var title="">element</var> is
+    any fully transparent value, and <var title="">element</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="">element</var> to its parent.
+
+    <li>If the computed style of "background-color" on <var title="">element</var> is
+    a fully transparent value, return "rgb(255, 255, 255)".
+
+    <li>Otherwise, return the computed style of "background-color" for
+    <var title="">element</var>.
+  </ol>
+
+  <li>Return the computed style of <var title="">property</var> for <var title="">element</var>.
+</ol>
+
 <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:
@@ -278,6 +308,10 @@
   (including invalid or unrecognized properties).
 </ol>
 
+<p class=note>Conceptually, a <a href=#simple-styling-element>simple styling element</a> is an element
+which has a <a href=#specified-style>specified style</a> for exactly one CSS property, but does
+nothing else.
+
 <p>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,
@@ -458,21 +492,22 @@
   <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. -->
 
-  <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 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 <a href=#effective-style>effective style</a>
+  of <var title="">property</var> is <var title="">new value</var> on <var title="">node</var>, abort this
+  algorithm.
 
-  <li>If <var title="">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 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 the <a href=#effective-style>effective
+  style</a> of <var title="">property</var> is <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>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>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> 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
+  <a href=#effective-style>effective style</a> of <var title="">property</var> is not <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>.
 
   <!-- We can only remove specified styles, so if the style isn't specified,
   give up. -->
@@ -585,9 +620,10 @@
     -->
 
     <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.
+    element</a> whose <a href=#specified-style>specified style</a> and <a href=#effective-style>effective
+    style</a> for <var title="">property</var> are both <var title="">new value</var>, append
+    <var title="">node</var> as the last <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> of its <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-previoussibling><code class=external data-anolis-spec=domcore title=dom-Node-previousSibling>previousSibling</code></a> and abort
+    this algorithm.
 
     <!--
     <li>Let <var title>candidate</var> be <var title>node</var>'s <code data-anolis-spec=domcore title=dom-Node-nextSibling>nextSibling</code>.
@@ -617,17 +653,20 @@
     -->
 
     <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.
+    element</a> whose <a href=#specified-style>specified style</a> and <a href=#effective-style>effective
+    style</a> for <var title="">property</var> are both <var title="">new value</var>, insert
+    <var title="">node</var> as the first <a class=external data-anolis-spec=domcore href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>child</a> of its <a href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-nextsibling><code class=external data-anolis-spec=domcore title=dom-Node-nextSibling>nextSibling</code></a> and abort
+    this algorithm.
   </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 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 <a href=#effective-style>effective style</a>
+  of <var title="">property</var> is <var title="">new value</var> on <var title="">node</var>, abort this
+  algorithm.
 
   <li>If <var title="">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.
+  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 <a href=#effective-style>effective style</a> of <var title="">property</var>
+  is <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>:
 
@@ -648,12 +687,14 @@
   <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 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 <a href=#effective-style>effective style</a>
+  of <var title="">property</var> is <var title="">new value</var> on <var title="">node</var>, abort this
+  algorithm.
 
   <li>If <var title="">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.
+  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 <a href=#effective-style>effective style</a> of <var title="">property</var>
+  is <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".
@@ -672,14 +713,14 @@
   <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>If the <a href=#effective-style>effective style</a> 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>:
+  <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 <a href=#effective-style>effective style</a>
+  of <var title="">property</var> for <var title="">node</var> is not <var title="">new value</var>:
 
   <ol>
     <li>Set the CSS property <var title="">property</var> of <var title="">node</var> to <var title="">new
@@ -883,21 +924,8 @@
 
 <dd><p><strong>State</strong>: Always false.
 
-<dd><p><strong>Value</strong>: The value is given by the following algorithm:
-
-<ol>
-  <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.
-
-  <p class=XXX>It's intended that for these purposes, the root element will
-  have a white background.  Should that be specified somewhere?  I don't think
-  all UAs actually do it.
-
-  <li>Return the computed style of "background-color" for
-  <var title="">element</var>.
-</ol>
+<dd><p><strong>Value</strong>: The <a href=#effective-style>effective style</a> for
+"background-color" on . . .
 <!-- Chrome 10 returns rgba(0, 0, 0, 0) if there's no background defined
 anywhere.  Opera 11 returns rgb(255, 255, 255) as I'd like.  Firefox 4b11 just
 throws an exception for some reason.  IE 9 RC seems to return the number 0
--- a/implementation.js	Mon Mar 21 12:08:09 2011 -0600
+++ b/implementation.js	Mon Mar 21 14:08:35 2011 -0600
@@ -270,6 +270,55 @@
 }
 
 /**
+ * "effective style" per edit command spec
+ */
+function getEffectiveStyle(element, property) {
+	// "If property is "text-decoration", and the "text-decoration" property of
+	// element or any of its ancestors computes to "underline", return
+	// "underline". Otherwise, return "none"."
+	if (property == "textDecoration") {
+		do {
+			if (getComputedStyle(element).textDecoration == "underline") {
+				return "underline";
+			}
+			element = element.parentNode;
+		} while (element && element.nodeType == Node.ELEMENT_NODE);
+		return "none";
+	}
+
+	// "If property is "background-color":"
+	if (property == "backgroundColor") {
+		// "While the computed style of "background-color" on element is any
+		// fully transparent value, and element's parent is an Element, set
+		// element to its parent."
+		//
+		// Another lame hack to avoid flawed APIs.
+		while ((getComputedStyle(element).backgroundColor == "rgba(0, 0, 0, 0)"
+		|| getComputedStyle(element).backgroundColor === ""
+		|| getComputedStyle(element).backgroundColor == "transparent")
+		&& element.parentNode
+		&& element.parentNode.nodeType == Node.ELEMENT_NODE) {
+			element = element.parentNode;
+		}
+
+		// "If the computed style of "background-color" on element is a fully
+		// transparent value, return "rgb(255, 255, 255)"."
+		if (getComputedStyle(element).backgroundColor == "rgba(0, 0, 0, 0)"
+        || getComputedStyle(element).backgroundColor === ""
+        || getComputedStyle(element).backgroundColor == "transparent") {
+			return "rgb(255, 255, 255)";
+		}
+
+		// "Otherwise, return the computed style of "background-color" for
+		// element."
+		return getComputedStyle(element).backgroundColor;
+	}
+
+	// "Return the computed style of property for element."
+	return getComputedStyle(element)[property];
+}
+
+/**
  * "specified style" per edit command spec
  */
 function getSpecifiedStyle(element, property) {
@@ -597,17 +646,17 @@
 		return;
 	}
 
-	// "If node is an Element and property computes to new value on node, abort
-	// this algorithm."
+	// "If node is an Element and the effective style of property is new value
+	// on node, abort this algorithm."
 	if (node.nodeType == Node.ELEMENT_NODE
-	&& cssValuesEqual(property, getComputedStyle(node)[property], newValue)) {
+	&& cssValuesEqual(property, getEffectiveStyle(node, property), newValue)) {
 		return;
 	}
 
-	// "If node is not an Element and property computes to new value on node's
-	// parent, abort this algorithm."
+	// "If node is not an Element and the effective style of property is new
+	// value on node's parent, abort this algorithm."
 	if (node.nodeType != Node.ELEMENT_NODE
-	&& cssValuesEqual(property, getComputedStyle(node.parentNode)[property], newValue)) {
+	&& cssValuesEqual(property, getEffectiveStyle(node.parentNode, property), newValue)) {
 		return;
 	}
 
@@ -617,12 +666,12 @@
 	// "Let ancestor list be a list of Nodes, initially empty."
 	var ancestorList = [];
 
-	// "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 current ancestor is an Element and the effective style of
+	// property is not 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)) {
+	&& !cssValuesEqual(property, getEffectiveStyle(currentAncestor, property), newValue)) {
 		ancestorList.push(currentAncestor);
 		currentAncestor = currentAncestor.parentNode;
 	}
@@ -709,23 +758,23 @@
 	|| node.nodeType == Node.PROCESSING_INSTRUCTION_NODE)
 	&& !isUnwrappableElement(node)) {
 		// "If node's previousSibling is a simple styling element whose
-		// specified style and computed style for property are both new value,
+		// specified style and effective style for property are both new value,
 		// append node as the last child of its previousSibling and abort this
 		// algorithm."
 		if (isSimpleStylingElement(node.previousSibling)
 		&& cssValuesEqual(property, getSpecifiedStyle(node.previousSibling, property), newValue)
-		&& cssValuesEqual(property, getComputedStyle(node.previousSibling)[property], newValue)) {
+		&& cssValuesEqual(property, getEffectiveStyle(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
+		// style and effective style for property are both new value, insert
 		// node as the first child of its nextSibling and abort this
 		// algorithm."
 		if (isSimpleStylingElement(node.nextSibling)
 		&& cssValuesEqual(property, getSpecifiedStyle(node.nextSibling, property), newValue)
-		&& cssValuesEqual(property, getComputedStyle(node.nextSibling)[property], newValue)) {
+		&& cssValuesEqual(property, getEffectiveStyle(node.nextSibling, property), newValue)) {
 			node.nextSibling.insertBefore(node, node.nextSibling.childNodes.length
 				? node.nextSibling.childNodes[0]
 				: null);
@@ -733,18 +782,19 @@
 		}
 	}
 
-	// "If node is an Element and property computes to new value on node, abort
-	// this algorithm."
+	// "If node is an Element and the effective style of property is new value
+	// on node, abort this algorithm."
 	if (node.nodeType == Node.ELEMENT_NODE
-	&& cssValuesEqual(property, getComputedStyle(node)[property], newValue)) {
+	&& cssValuesEqual(property, getEffectiveStyle(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 is not an Element, node's parent is an Element, and the
+	// effective style of property is 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)) {
+	&& cssValuesEqual(property, getEffectiveStyle(node.parentNode, property), newValue)) {
 		return;
 	}
 
@@ -782,18 +832,19 @@
 		return;
 	}
 
-	// "If node is an Element and property computes to new value on node, abort
-	// this algorithm."
+	// "If node is an Element and the effective style of property is new value
+	// on node, abort this algorithm."
 	if (node.nodeType == Node.ELEMENT_NODE
-	&& cssValuesEqual(property, getComputedStyle(node)[property], newValue)) {
+	&& cssValuesEqual(property, getEffectiveStyle(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 is not an Element, node's parent is an Element, and the
+	// effective style of property is 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)) {
+	&& cssValuesEqual(property, getEffectiveStyle(node.parentNode, property), newValue)) {
 		return;
 	}
 
@@ -820,19 +871,19 @@
 	// "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
+	// "If the effective style 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)) {
+	if (!cssValuesEqual(property, getEffectiveStyle(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
+	// "If node is an Element and the effective style of property for node is
 	// not new value:"
 	if (node.nodeType == Node.ELEMENT_NODE
-	&& !cssValuesEqual(property, getComputedStyle(node)[property], newValue)) {
+	&& !cssValuesEqual(property, getEffectiveStyle(node, property), newValue)) {
 		// "Set the CSS property property of node to new value."
 		node.style[property] = newValue;
 
--- a/source.html	Mon Mar 21 12:08:09 2011 -0600
+++ b/source.html	Mon Mar 21 14:08:35 2011 -0600
@@ -202,6 +202,36 @@
 not be used where only [[phrasingcontent]] is expected (not counting unknown or
 obsolete elements, which cannot be used at all).
 
+<p>The <dfn>effective style</dfn> of an [[element]] <var>element</var> for a
+given <var>property</var> is returned by the following algorithm, which will
+return a CSS value:
+
+<ol>
+  <li>If <var>property</var> is "text-decoration", and the "text-decoration"
+  property of <var>element</var> or any of its [[ancestors]] computes to
+  "underline", return "underline".  Otherwise, return "none".
+
+  <p class=XXX>We ignore the possibility of text-decorations other than
+  underline for now, so we pretend that there are only two possible values:
+  "underline" and "none".
+
+  <li>If <var>property</var> is "background-color":
+
+  <ol>
+    <li>While the computed style of "background-color" on <var>element</var> is
+    any fully transparent value, and <var>element</var>'s [[parent]] is an
+    [[element]], set <var>element</var> to its parent.
+
+    <li>If the computed style of "background-color" on <var>element</var> is
+    a fully transparent value, return "rgb(255, 255, 255)".
+
+    <li>Otherwise, return the computed style of "background-color" for
+    <var>element</var>.
+  </ol>
+
+  <li>Return the computed style of <var>property</var> for <var>element</var>.
+</ol>
+
 <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:
@@ -269,6 +299,10 @@
   (including invalid or unrecognized properties).
 </ol>
 
+<p class=note>Conceptually, a <span>simple styling element</span> is an element
+which has a <span>specified style</span> for exactly one CSS property, but does
+nothing else.
+
 <p>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,
@@ -454,21 +488,22 @@
   <li>If <var>node</var>'s [[parent]] is not an [[element]], abort this
   algorithm. <!-- E.g., a text node child of a document fragment. -->
 
-  <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 an [[element]] and the <span>effective style</span>
+  of <var>property</var> is <var>new value</var> on <var>node</var>, abort this
+  algorithm.
 
-  <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 not an [[element]] and the <span>effective
+  style</span> of <var>property</var> is <var>new value</var> on
+  <var>node</var>'s [[parent]], 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>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> is an [[element]] and the
+  <span>effective style</span> of <var>property</var> is not <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]].
 
   <!-- We can only remove specified styles, so if the style isn't specified,
   give up. -->
@@ -581,9 +616,10 @@
     -->
 
     <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.
+    element</span> whose <span>specified style</span> and <span>effective
+    style</span> for <var>property</var> are both <var>new value</var>, append
+    <var>node</var> as the last [[child]] of its [[previoussibling]] and abort
+    this algorithm.
 
     <!--
     <li>Let <var>candidate</var> be <var>node</var>'s [[nextsibling]].
@@ -613,17 +649,20 @@
     -->
 
     <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.
+    element</span> whose <span>specified style</span> and <span>effective
+    style</span> for <var>property</var> are both <var>new value</var>, insert
+    <var>node</var> as the first [[child]] of its [[nextsibling]] and abort
+    this algorithm.
   </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 an [[element]] and the <span>effective style</span>
+  of <var>property</var> is <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.
+  an [[element]], and the <span>effective style</span> of <var>property</var>
+  is <var>new value</var> on <var>node</var>'s [[parent]], abort this
+  algorithm.
 
   <li>If <var>node</var> is an <span>unwrappable element</span>:
 
@@ -644,12 +683,14 @@
   <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 an [[element]] and the <span>effective style</span>
+  of <var>property</var> is <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.
+  an [[element]], and the <span>effective style</span> of <var>property</var>
+  is <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".
@@ -670,14 +711,14 @@
   <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>If the <span>effective style</span> 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>:
+  <li>If <var>node</var> is an [[element]] and the <span>effective style</span>
+  of <var>property</var> for <var>node</var> is not <var>new value</var>:
 
   <ol>
     <li>Set the CSS property <var>property</var> of <var>node</var> to <var>new
@@ -885,21 +926,8 @@
 
 <dd><p><strong>State</strong>: Always false.
 
-<dd><p><strong>Value</strong>: The value is given by the following algorithm:
-
-<ol>
-  <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.
-
-  <p class=XXX>It's intended that for these purposes, the root element will
-  have a white background.  Should that be specified somewhere?  I don't think
-  all UAs actually do it.
-
-  <li>Return the computed style of "background-color" for
-  <var>element</var>.
-</ol>
+<dd><p><strong>Value</strong>: The <span>effective style</span> for
+"background-color" on . . .
 <!-- Chrome 10 returns rgba(0, 0, 0, 0) if there's no background defined
 anywhere.  Opera 11 returns rgb(255, 255, 255) as I'd like.  Firefox 4b11 just
 throws an exception for some reason.  IE 9 RC seems to return the number 0