Restrict the definition of "enabled" more
authorAryeh Gregor <ayg@aryeh.name>
Fri, 24 Feb 2012 08:46:37 -0700
changeset 702 33d0413c3be8
parent 701 1def04410100
child 703 a089a5315642
Restrict the definition of "enabled" more

Now most commands are not enabled if the selection isn't fully contained
in an editing host, or if it has one of its endpoints outside an editing
host. This matches Gecko/WebKit better, and is handy for the event
changes I'm about to do. Could use more tests.

Fixes: https://www.w3.org/Bugs/Public/show_bug.cgi?id=16094
Reported-By: Ryosuke Niwa
conformancetest/data.js
editing.html
implementation.js
source.html
--- a/conformancetest/data.js	Fri Feb 24 08:52:30 2012 -0700
+++ b/conformancetest/data.js	Fri Feb 24 08:46:37 2012 -0700
@@ -442,11 +442,11 @@
 ["foo<span contenteditable=false>[bar]</span>baz",
 	[["stylewithcss","false"],["bold",""]],
 	"foo<span contenteditable=\"false\">[bar]</span>baz",
-	{"stylewithcss":[false,true,"",false,false,""],"bold":[false,false,"",false,true,""]}],
+	{"stylewithcss":[false,true,"",false,false,""],"bold":[false,false,"",false,false,""]}],
 ["foo<span contenteditable=false>[bar]</span>baz",
 	[["stylewithcss","true"],["bold",""]],
 	"foo<span contenteditable=\"false\">[bar]</span>baz",
-	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,false,"",false,true,""]}],
+	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,false,"",false,false,""]}],
 ["fo[o<span contenteditable=false>bar</span>b]az",
 	[["stylewithcss","false"],["bold",""]],
 	"fo<b>[o</b><span contenteditable=\"false\">bar</span><b>b]</b>az",
@@ -457,20 +457,20 @@
 	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,false,"",false,true,""]}],
 ["foo<span contenteditable=false>ba[r</span>b]az",
 	[["stylewithcss","false"],["bold",""]],
-	"foo<span contenteditable=\"false\">ba[r</span><b>b]</b>az",
-	{"stylewithcss":[false,true,"",false,false,""],"bold":[false,false,"",false,true,""]}],
+	"foo<span contenteditable=\"false\">ba[r</span>b]az",
+	{"stylewithcss":[false,true,"",false,false,""],"bold":[false,false,"",false,false,""]}],
 ["foo<span contenteditable=false>ba[r</span>b]az",
 	[["stylewithcss","true"],["bold",""]],
-	"foo<span contenteditable=\"false\">ba[r</span><span style=\"font-weight:bold\">b]</span>az",
-	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,false,"",false,true,""]}],
+	"foo<span contenteditable=\"false\">ba[r</span>b]az",
+	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,false,"",false,false,""]}],
 ["fo[o<span contenteditable=false>b]ar</span>baz",
 	[["stylewithcss","false"],["bold",""]],
-	"fo<b>[o</b><span contenteditable=\"false\">b]ar</span>baz",
-	{"stylewithcss":[false,true,"",false,false,""],"bold":[false,false,"",false,true,""]}],
+	"fo[o<span contenteditable=\"false\">b]ar</span>baz",
+	{"stylewithcss":[false,true,"",false,false,""],"bold":[false,false,"",false,false,""]}],
 ["fo[o<span contenteditable=false>b]ar</span>baz",
 	[["stylewithcss","true"],["bold",""]],
-	"fo<span style=\"font-weight:bold\">[o</span><span contenteditable=\"false\">b]ar</span>baz",
-	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,false,"",false,true,""]}],
+	"fo[o<span contenteditable=\"false\">b]ar</span>baz",
+	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,false,"",false,false,""]}],
 ["fo[<b>o</b><span contenteditable=false>bar</span><b>b</b>]az",
 	[["stylewithcss","false"],["bold",""]],
 	"fo[o<span contenteditable=\"false\">bar</span>b]az",
@@ -489,36 +489,36 @@
 	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,false,"",false,true,""]}],
 ["<span contenteditable=false>fo[o<span contenteditable=true>bar</span>b]az</span>",
 	[["stylewithcss","false"],["bold",""]],
-	"<span contenteditable=\"false\">fo[o<span contenteditable=\"true\"><b>bar</b></span>b]az</span>",
-	{"stylewithcss":[false,true,"",false,false,""],"bold":[false,false,"",false,true,""]}],
+	"<span contenteditable=\"false\">fo[o<span contenteditable=\"true\">bar</span>b]az</span>",
+	{"stylewithcss":[false,true,"",false,false,""],"bold":[false,false,"",false,false,""]}],
 ["<span contenteditable=false>fo[o<span contenteditable=true>bar</span>b]az</span>",
 	[["stylewithcss","true"],["bold",""]],
-	"<span contenteditable=\"false\">fo[o<span contenteditable=\"true\"><span style=\"font-weight:bold\">bar</span></span>b]az</span>",
-	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,false,"",false,true,""]}],
+	"<span contenteditable=\"false\">fo[o<span contenteditable=\"true\">bar</span>b]az</span>",
+	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,false,"",false,false,""]}],
 ["<span contenteditable=false>foo<span contenteditable=true>ba[r</span>b]az</span>",
 	[["stylewithcss","false"],["bold",""]],
-	"<span contenteditable=\"false\">foo<span contenteditable=\"true\">ba<b>[r</b></span>b]az</span>",
-	{"stylewithcss":[false,true,"",false,false,""],"bold":[false,false,"",false,true,""]}],
+	"<span contenteditable=\"false\">foo<span contenteditable=\"true\">ba[r</span>b]az</span>",
+	{"stylewithcss":[false,true,"",false,false,""],"bold":[false,false,"",false,false,""]}],
 ["<span contenteditable=false>foo<span contenteditable=true>ba[r</span>b]az</span>",
 	[["stylewithcss","true"],["bold",""]],
-	"<span contenteditable=\"false\">foo<span contenteditable=\"true\">ba<span style=\"font-weight:bold\">[r</span></span>b]az</span>",
-	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,false,"",false,true,""]}],
+	"<span contenteditable=\"false\">foo<span contenteditable=\"true\">ba[r</span>b]az</span>",
+	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,false,"",false,false,""]}],
 ["<span contenteditable=false>fo[o<span contenteditable=true>b]ar</span>baz</span>",
 	[["stylewithcss","false"],["bold",""]],
-	"<span contenteditable=\"false\">fo[o<span contenteditable=\"true\"><b>b]</b>ar</span>baz</span>",
-	{"stylewithcss":[false,true,"",false,false,""],"bold":[false,false,"",false,true,""]}],
+	"<span contenteditable=\"false\">fo[o<span contenteditable=\"true\">b]ar</span>baz</span>",
+	{"stylewithcss":[false,true,"",false,false,""],"bold":[false,false,"",false,false,""]}],
 ["<span contenteditable=false>fo[o<span contenteditable=true>b]ar</span>baz</span>",
 	[["stylewithcss","true"],["bold",""]],
-	"<span contenteditable=\"false\">fo[o<span contenteditable=\"true\"><span style=\"font-weight:bold\">b]</span>ar</span>baz</span>",
-	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,false,"",false,true,""]}],
+	"<span contenteditable=\"false\">fo[o<span contenteditable=\"true\">b]ar</span>baz</span>",
+	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,false,"",false,false,""]}],
 ["<span contenteditable=false>fo[<b>o<span contenteditable=true>bar</span>b</b>]az</span>",
 	[["stylewithcss","false"],["bold",""]],
-	"<span contenteditable=\"false\">fo[<b>o<span contenteditable=\"true\"><span style=\"font-weight:normal\">bar</span></span>b</b>]az</span>",
-	{"stylewithcss":[false,true,"",false,false,""],"bold":[false,true,"",false,false,""]}],
+	"<span contenteditable=\"false\">fo[<b>o<span contenteditable=\"true\">bar</span>b</b>]az</span>",
+	{"stylewithcss":[false,true,"",false,false,""],"bold":[false,true,"",false,true,""]}],
 ["<span contenteditable=false>fo[<b>o<span contenteditable=true>bar</span>b</b>]az</span>",
 	[["stylewithcss","true"],["bold",""]],
-	"<span contenteditable=\"false\">fo[<b>o<span contenteditable=\"true\"><span style=\"font-weight:normal\">bar</span></span>b</b>]az</span>",
-	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,true,"",false,false,""]}],
+	"<span contenteditable=\"false\">fo[<b>o<span contenteditable=\"true\">bar</span>b</b>]az</span>",
+	{"stylewithcss":[false,false,"",false,true,""],"bold":[false,true,"",false,true,""]}],
 ["<table><tbody><tr><td>foo<td>b[a]r<td>baz</table>",
 	[["stylewithcss","false"],["bold",""]],
 	"<table><tbody><tr><td>foo</td><td>b<b>[a]</b>r</td><td>baz</td></tr></tbody></table>",
@@ -4881,20 +4881,20 @@
 	{"stylewithcss":[false,false,"",false,true,""],"fontname":[false,false,"monospace",false,false,"sans-serif"]}],
 ["foo<tt contenteditable=false>ba[r</tt>b]az",
 	[["stylewithcss","false"],["fontname","sans-serif"]],
-	"foo<tt contenteditable=\"false\">ba[r</tt><font face=\"sans-serif\">b]</font>az",
-	{"stylewithcss":[false,true,"",false,false,""],"fontname":[false,false,"serif",false,false,"sans-serif"]}],
+	"foo<tt contenteditable=\"false\">ba[r</tt>b]az",
+	{"stylewithcss":[false,true,"",false,false,""],"fontname":[false,false,"serif",false,false,"serif"]}],
 ["foo<tt contenteditable=false>ba[r</tt>b]az",
 	[["stylewithcss","true"],["fontname","sans-serif"]],
-	"foo<tt contenteditable=\"false\">ba[r</tt><span style=\"font-family:sans-serif\">b]</span>az",
-	{"stylewithcss":[false,false,"",false,true,""],"fontname":[false,false,"serif",false,false,"sans-serif"]}],
+	"foo<tt contenteditable=\"false\">ba[r</tt>b]az",
+	{"stylewithcss":[false,false,"",false,true,""],"fontname":[false,false,"serif",false,false,"serif"]}],
 ["fo[o<tt contenteditable=false>b]ar</tt>baz",
 	[["stylewithcss","false"],["fontname","sans-serif"]],
-	"fo<font face=\"sans-serif\">[o</font><tt contenteditable=\"false\">b]ar</tt>baz",
-	{"stylewithcss":[false,true,"",false,false,""],"fontname":[false,false,"serif",false,false,"sans-serif"]}],
+	"fo[o<tt contenteditable=\"false\">b]ar</tt>baz",
+	{"stylewithcss":[false,true,"",false,false,""],"fontname":[false,false,"serif",false,false,"serif"]}],
 ["fo[o<tt contenteditable=false>b]ar</tt>baz",
 	[["stylewithcss","true"],["fontname","sans-serif"]],
-	"fo<span style=\"font-family:sans-serif\">[o</span><tt contenteditable=\"false\">b]ar</tt>baz",
-	{"stylewithcss":[false,false,"",false,true,""],"fontname":[false,false,"serif",false,false,"sans-serif"]}],
+	"fo[o<tt contenteditable=\"false\">b]ar</tt>baz",
+	{"stylewithcss":[false,false,"",false,true,""],"fontname":[false,false,"serif",false,false,"serif"]}],
 ["foo<tt>{}<br></tt>bar",
 	[["stylewithcss","false"],["fontname","sans-serif"]],
 	"foo<tt>{}<br></tt>bar",
--- a/editing.html	Fri Feb 24 08:52:30 2012 -0700
+++ b/editing.html	Fri Feb 24 08:46:37 2012 -0700
@@ -67,7 +67,7 @@
 <body class=draft>
 <div class=head id=head>
 <h1>HTML Editing APIs</h1>
-<h2 class="no-num no-toc" id=work-in-progress-&mdash;-last-update-19-january-2012>Work in Progress &mdash; Last Update 19 January 2012</h2>
+<h2 class="no-num no-toc" id=work-in-progress-&mdash;-last-update-24-february-2012>Work in Progress &mdash; Last Update 24 February 2012</h2>
 <dl>
  <dt>Editor
  <dd>Aryeh Gregor &lt;<a href=mailto:ayg@aryeh.name>ayg@aryeh.name</a>&gt;
@@ -1372,18 +1372,28 @@
 
 <p>It's not clear to me what the point of this method is.  There's no way we're
 going to always return true if the command will do something and false if it
-won't.  I just stuck with a really conservative definition that happens to be
+won't.  I originally just stuck with a really conservative definition that happens to be
 convenient: if there's nothing selected, obviously nothing will work, and we
 want to bail out early in that case anyway because all the algorithms will talk
 about the active range.  If there are use-cases for it to be more precise, I
 could make it so.
+
+<p><a href="https://www.w3.org/Bugs/Public/show_bug.cgi?id=16094">Bug 16094</a>
+illustrated that we don't really want to be able to modify multiple editing
+hosts at once, nor do we want to do anything if the start and end aren't both
+editable, so I co-opted this definition to fit my ends.
 </div>
 
 <p>Among <a href=#command title=command>commands</a> defined in this specification,
 those listed in <a href=#miscellaneous-commands>Miscellaneous commands</a> are
-always <a href=#enabled>enabled</a>.  The other <a href=#command title=command>commands</a>
-defined here are <a href=#enabled>enabled</a> if the <a href=#active-range>active range</a> is not
-null, and disabled otherwise.
+always <a href=#enabled>enabled</a>, except for <a href=#the-cut-command>the <code title="">cut</code>
+command</a> and <a href=#the-paste-command>the <code title="">paste</code> command</a>.  The
+other <a href=#command title=command>commands</a> defined here are <a href=#enabled>enabled</a>
+if the <a href=#active-range>active range</a> is not null, its <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-range-start-node title=concept-range-start-node>start node</a> is either
+<a href=#editable>editable</a> or an <a href=#editing-host>editing host</a>, its <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-range-end-node title=concept-range-end-node>end node</a> is
+either <a href=#editable>editable</a> or an <a href=#editing-host>editing host</a>, and there is some
+<a href=#editing-host>editing host</a> that is an <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-inclusive-ancestor title=concept-tree-inclusive-ancestor>inclusive ancestor</a> of both its
+<a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-range-start-node title=concept-range-start-node>start node</a> and its <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-range-end-node title=concept-range-end-node>end node</a>.
 
 
 
@@ -1482,8 +1492,7 @@
   behavior but made slightly more sane.
   </div>
 
-  <p>If <var title="">command</var> has no <a href=#indeterminate title=indeterminate>indeterminacy</a> or is not <a href=#enabled>enabled</a>,
-  return false.
+  <p>If <var title="">command</var> has no <a href=#indeterminate title=indeterminate>indeterminacy</a>, return false.
 
   <li>Return true if <var title="">command</var> is <a href=#indeterminate>indeterminate</a>,
   otherwise false.
@@ -1505,8 +1514,7 @@
   <p class=comments>See comment on the comparable line for
   <a href=#querycommandindeterm()>queryCommandIndeterm()</a>.
 
-  <p>If <var title="">command</var> has no <a href=#state>state</a> or is not
-  <a href=#enabled>enabled</a>, return false.
+  <p>If <var title="">command</var> has no <a href=#state>state</a>, return false.
 
   <li>If the <a href=#state-override>state override</a> for <var title="">command</var> is set, return
   it.
@@ -1548,8 +1556,7 @@
   see the comment on the comparable line for
   <a href=#querycommandindeterm()>queryCommandIndeterm()</a>.
 
-  <p>If <var title="">command</var> has no <a href=#value>value</a> or is not
-  <a href=#enabled>enabled</a>, return the empty string.
+  <p>If <var title="">command</var> has no <a href=#value>value</a>, return the empty string.
 
   <li>
   <p class=comments>Yuck.  This is incredibly messy, as are lots of other
@@ -3982,6 +3989,8 @@
 <p><a href=#value>Value</a>:
 
 <ol>
+  <li>If the <a href=#active-range>active range</a> is null, return the empty string.
+
   <li>
   <p class=comments>See comment for standard inline value commands on how I
   decided on this choice of node.
@@ -4728,6 +4737,8 @@
 </div>
 
 <ol>
+  <li>If the <a href=#active-range>active range</a> is null, return "none".
+
   <li><a href=#block-extend>Block-extend</a> the <a href=#active-range>active range</a>, and let <var title="">new
   range</var> be the result.
 
@@ -8266,6 +8277,8 @@
 <p><a href=#indeterminate>Indeterminate</a>:
 
 <ol>
+  <li>If the <a href=#active-range>active range</a> is null, return the empty string.
+
   <li><a href=#block-extend>Block-extend</a> the <a href=#active-range>active range</a>, and let <var title="">new
   range</var> be the result.
 
@@ -8321,6 +8334,8 @@
 
 <p><a href=#value>Value</a>:
 <ol>
+  <li>If the <a href=#active-range>active range</a> is null, return the empty string.
+
   <li><a href=#block-extend>Block-extend</a> the <a href=#active-range>active range</a>, and let <var title="">new
   range</var> be the result.
 
@@ -9712,11 +9727,12 @@
 attention even to some nodes that do have children.
 </div>
 
-<p><a href=#indeterminate>Indeterminate</a>: <a href=#block-extend>Block-extend</a> the <a href=#active-range>active
-range</a>.  Return true if among <a href=#visible>visible</a> <a href=#editable>editable</a>
-<a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a> that are <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#contained>contained</a> in the result and have no <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, at
-least one has <a href=#alignment-value>alignment value</a> "center" and at least one does not.
-Otherwise return false.
+<p><a href=#indeterminate>Indeterminate</a>: Return false if the <a href=#active-range>active range</a> is
+null.  Otherwise, <a href=#block-extend>block-extend</a> the <a href=#active-range>active range</a>.
+Return true if among <a href=#visible>visible</a> <a href=#editable>editable</a> <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a> that
+are <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#contained>contained</a> in the result and have no <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, at least one has
+<a href=#alignment-value>alignment value</a> "center" and at least one does not.  Otherwise
+return false.
 
 <div class=comments>
 <p>IE9 throws exceptions in almost every case when querying the state of
@@ -9740,8 +9756,9 @@
 I'll support it anyway, since Gecko/WebKit do.
 </div>
 
-<p><a href=#state>State</a>: <a href=#block-extend>Block-extend</a> the <a href=#active-range>active range</a>.
-Return true if there is at least one <a href=#visible>visible</a> <a href=#editable>editable</a>
+<p><a href=#state>State</a>: Return false if the <a href=#active-range>active range</a> is null.
+Otherwise, <a href=#block-extend>block-extend</a> the <a href=#active-range>active range</a>.  Return
+true if there is at least one <a href=#visible>visible</a> <a href=#editable>editable</a>
 <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> that is <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#contained>contained</a> in the result and has no <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, and all
 such <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a> have <a href=#alignment-value>alignment value</a> "center".  Otherwise return
 false.
@@ -9754,7 +9771,8 @@
 case.  Chrome 14 dev returns the state cast to a string, as usual.  Opera 11.11
 always returns the empty string.
 
-<p><a href=#value>Value</a>: <a href=#block-extend>Block-extend</a> the <a href=#active-range>active range</a>,
+<p><a href=#value>Value</a>: Return the empty string if the <a href=#active-range>active range</a>
+is null.  Otherwise, <a href=#block-extend>block-extend</a> the <a href=#active-range>active range</a>,
 and return the <a href=#alignment-value>alignment value</a> of the first <a href=#visible>visible</a>
 <a href=#editable>editable</a> <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> that is <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#contained>contained</a> in the result and has no
 <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>.  If there is no such <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a>, return "left".
@@ -9765,19 +9783,22 @@
 <p><a href=#action>Action</a>: <a href=#justify-the-selection>Justify the selection</a> with
 <var title="">alignment</var> "justify".
 
-<p><a href=#indeterminate>Indeterminate</a>: <a href=#block-extend>Block-extend</a> the <a href=#active-range>active
-range</a>.  Return true if among <a href=#visible>visible</a> <a href=#editable>editable</a>
-<a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a> that are <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#contained>contained</a> in the result and have no <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, at
-least one has <a href=#alignment-value>alignment value</a> "justify" and at least one does not.
-Otherwise return false.
-
-<p><a href=#state>State</a>: <a href=#block-extend>Block-extend</a> the <a href=#active-range>active range</a>.
-Return true if there is at least one <a href=#visible>visible</a> <a href=#editable>editable</a>
+<p><a href=#indeterminate>Indeterminate</a>: Return false if the <a href=#active-range>active range</a> is
+null.  Otherwise, <a href=#block-extend>block-extend</a> the <a href=#active-range>active range</a>.
+Return true if among <a href=#visible>visible</a> <a href=#editable>editable</a> <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a> that
+are <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#contained>contained</a> in the result and have no <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, at least one has
+<a href=#alignment-value>alignment value</a> "justify" and at least one does not.  Otherwise
+return false.
+
+<p><a href=#state>State</a>: Return false if the <a href=#active-range>active range</a> is null.
+Otherwise, <a href=#block-extend>block-extend</a> the <a href=#active-range>active range</a>.  Return
+true if there is at least one <a href=#visible>visible</a> <a href=#editable>editable</a>
 <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> that is <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#contained>contained</a> in the result and has no <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, and all
 such <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a> have <a href=#alignment-value>alignment value</a> "justify".  Otherwise return
 false.
 
-<p><a href=#value>Value</a>: <a href=#block-extend>Block-extend</a> the <a href=#active-range>active range</a>,
+<p><a href=#value>Value</a>: Return the empty string if the <a href=#active-range>active range</a>
+is null.  Otherwise, <a href=#block-extend>block-extend</a> the <a href=#active-range>active range</a>,
 and return the <a href=#alignment-value>alignment value</a> of the first <a href=#visible>visible</a>
 <a href=#editable>editable</a> <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> that is <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#contained>contained</a> in the result and has no
 <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>.  If there is no such <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a>, return "left".
@@ -9788,19 +9809,22 @@
 <p><a href=#action>Action</a>: <a href=#justify-the-selection>Justify the selection</a> with
 <var title="">alignment</var> "left".
 
-<p><a href=#indeterminate>Indeterminate</a>: <a href=#block-extend>Block-extend</a> the <a href=#active-range>active
-range</a>.  Return true if among <a href=#visible>visible</a> <a href=#editable>editable</a>
-<a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a> that are <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#contained>contained</a> in the result and have no <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, at
-least one has <a href=#alignment-value>alignment value</a> "left" and at least one does not.
-Otherwise return false.
-
-<p><a href=#state>State</a>: <a href=#block-extend>Block-extend</a> the <a href=#active-range>active range</a>.
-Return true if there is at least one <a href=#visible>visible</a> <a href=#editable>editable</a>
+<p><a href=#indeterminate>Indeterminate</a>: Return false if the <a href=#active-range>active range</a> is
+null.  Otherwise, <a href=#block-extend>block-extend</a> the <a href=#active-range>active range</a>.
+Return true if among <a href=#visible>visible</a> <a href=#editable>editable</a> <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a> that
+are <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#contained>contained</a> in the result and have no <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, at least one has
+<a href=#alignment-value>alignment value</a> "left" and at least one does not.  Otherwise
+return false.
+
+<p><a href=#state>State</a>: Return false if the <a href=#active-range>active range</a> is null.
+Otherwise, <a href=#block-extend>block-extend</a> the <a href=#active-range>active range</a>.  Return
+true if there is at least one <a href=#visible>visible</a> <a href=#editable>editable</a>
 <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> that is <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#contained>contained</a> in the result and has no <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, and all
 such <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a> have <a href=#alignment-value>alignment value</a> "left".  Otherwise return
 false.
 
-<p><a href=#value>Value</a>: <a href=#block-extend>Block-extend</a> the <a href=#active-range>active range</a>,
+<p><a href=#value>Value</a>: Return the empty string if the <a href=#active-range>active range</a>
+is null.  Otherwise, <a href=#block-extend>block-extend</a> the <a href=#active-range>active range</a>,
 and return the <a href=#alignment-value>alignment value</a> of the first <a href=#visible>visible</a>
 <a href=#editable>editable</a> <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> that is <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#contained>contained</a> in the result and has no
 <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>.  If there is no such <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a>, return "left".
@@ -9811,19 +9835,22 @@
 <p><a href=#action>Action</a>: <a href=#justify-the-selection>Justify the selection</a> with
 <var title="">alignment</var> "right".
 
-<p><a href=#indeterminate>Indeterminate</a>: <a href=#block-extend>Block-extend</a> the <a href=#active-range>active
-range</a>.  Return true if among <a href=#visible>visible</a> <a href=#editable>editable</a>
-<a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a> that are <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#contained>contained</a> in the result and have no <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, at
-least one has <a href=#alignment-value>alignment value</a> "right" and at least one does not.
-Otherwise return false.
-
-<p><a href=#state>State</a>: <a href=#block-extend>Block-extend</a> the <a href=#active-range>active range</a>.
-Return true if there is at least one <a href=#visible>visible</a> <a href=#editable>editable</a>
+<p><a href=#indeterminate>Indeterminate</a>: Return false if the <a href=#active-range>active range</a> is
+null.  Otherwise, <a href=#block-extend>block-extend</a> the <a href=#active-range>active range</a>.
+Return true if among <a href=#visible>visible</a> <a href=#editable>editable</a> <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a> that
+are <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#contained>contained</a> in the result and have no <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, at least one has
+<a href=#alignment-value>alignment value</a> "right" and at least one does not.  Otherwise
+return false.
+
+<p><a href=#state>State</a>: Return false if the <a href=#active-range>active range</a> is null.
+Otherwise, <a href=#block-extend>block-extend</a> the <a href=#active-range>active range</a>.  Return
+true if there is at least one <a href=#visible>visible</a> <a href=#editable>editable</a>
 <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> that is <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#contained>contained</a> in the result and has no <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>, and all
 such <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>nodes</a> have <a href=#alignment-value>alignment value</a> "right".  Otherwise return
 false.
 
-<p><a href=#value>Value</a>: <a href=#block-extend>Block-extend</a> the <a href=#active-range>active range</a>,
+<p><a href=#value>Value</a>: Return the empty string if the <a href=#active-range>active range</a>
+is null.  Otherwise, <a href=#block-extend>block-extend</a> the <a href=#active-range>active range</a>,
 and return the <a href=#alignment-value>alignment value</a> of the first <a href=#visible>visible</a>
 <a href=#editable>editable</a> <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a> that is <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#contained>contained</a> in the result and has no
 <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-tree-child title=concept-tree-child>children</a>.  If there is no such <a class=external data-anolis-spec=dom href=http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#concept-node title=concept-node>node</a>, return "left".
--- a/implementation.js	Fri Feb 24 08:52:30 2012 -0700
+++ b/implementation.js	Fri Feb 24 08:46:37 2012 -0700
@@ -564,11 +564,19 @@
 	// "If command is not supported, raise a NOT_SUPPORTED_ERR exception."
 	return editCommandMethod(command, range, (function(command) { return function() {
 		// "Among commands defined in this specification, those listed in
-		// Miscellaneous commands are always enabled. The other commands defined
-		// here are enabled if the active range is not null, and disabled
-		// otherwise."
-		return ["copy", "cut", "paste", "selectall", "stylewithcss", "usecss"].indexOf(command) != -1
-			|| getActiveRange() !== null;
+		// Miscellaneous commands are always enabled, except for the cut
+		// command and the paste command. The other commands defined here are
+		// enabled if the active range is not null, its start node is either
+		// editable or an editing host, its end node is either editable or an
+		// editing host, and there is some editing host that is an inclusive
+		// ancestor of both its start node and its end node."
+		return ["copy", "selectall", "stylewithcss", "usecss"].indexOf(command) != -1
+			|| (
+				getActiveRange() !== null
+				&& (isEditable(getActiveRange().startContainer) || isEditingHost(getActiveRange().startContainer))
+				&& (isEditable(getActiveRange().endContainer) || isEditingHost(getActiveRange().endContainer))
+				&& (getInclusiveAncestors(getActiveRange().commonAncestorContainer).some(isEditingHost))
+			);
 	}})(command));
 }
 
@@ -579,9 +587,8 @@
 
 	// "If command is not supported, raise a NOT_SUPPORTED_ERR exception."
 	return editCommandMethod(command, range, (function(command) { return function() {
-		// "If command has no indeterminacy or is not enabled, return false."
-		if (!("indeterm" in commands[command])
-		|| !myQueryCommandEnabled(command)) {
+		// "If command has no indeterminacy, return false."
+		if (!("indeterm" in commands[command])) {
 			return false;
 		}
 
@@ -597,9 +604,8 @@
 
 	// "If command is not supported, raise a NOT_SUPPORTED_ERR exception."
 	return editCommandMethod(command, range, (function(command) { return function() {
-		// "If command has no state or is not enabled, return false."
-		if (!("state" in commands[command])
-		|| !myQueryCommandEnabled(command)) {
+		// "If command has no state, return false."
+		if (!("state" in commands[command])) {
 			return false;
 		}
 
@@ -631,10 +637,8 @@
 
 	// "If command is not supported, raise a NOT_SUPPORTED_ERR exception."
 	return editCommandMethod(command, range, function() {
-		// "If command has no value or is not enabled, return the empty
-		// string."
-		if (!("value" in commands[command])
-		|| !myQueryCommandEnabled(command)) {
+		// "If command has no value, return the empty string."
+		if (!("value" in commands[command])) {
 			return "";
 		}
 
@@ -3257,6 +3261,11 @@
 			return arr.slice(0, i).indexOf(value) == -1;
 		}).length >= 2;
 	}, value: function() {
+		// "If the active range is null, return the empty string."
+		if (!getActiveRange()) {
+			return "";
+		}
+
 		// "Let pixel size be the effective command value of the first
 		// formattable node that is effectively contained in the active range,
 		// or if there is no such node, the effective command value of the
@@ -3884,6 +3893,11 @@
 }
 
 function getSelectionListState() {
+	// "If the active range is null, return "none"."
+	if (!getActiveRange()) {
+		return "none";
+	}
+
 	// "Block-extend the active range, and let new range be the result."
 	var newRange = blockExtend(getActiveRange());
 
@@ -6643,6 +6657,11 @@
 		// "Restore states and values from overrides."
 		restoreStatesAndValues(overrides);
 	}, indeterm: function() {
+		// "If the active range is null, return false."
+		if (!getActiveRange()) {
+			return false;
+		}
+
 		// "Block-extend the active range, and let new range be the result."
 		var newRange = blockExtend(getActiveRange());
 
@@ -6701,6 +6720,11 @@
 		// "Return false."
 		return false;
 	}, value: function() {
+		// "If the active range is null, return the empty string."
+		if (!getActiveRange()) {
+			return "";
+		}
+
 		// "Block-extend the active range, and let new range be the result."
 		var newRange = blockExtend(getActiveRange());
 
@@ -7901,29 +7925,41 @@
 	// "Justify the selection with alignment "center"."
 	action: function() { justifySelection("center") },
 	indeterm: function() {
-		// "Block-extend the active range. Return true if among visible
-		// editable nodes that are contained in the result and have no
-		// children, at least one has alignment value "center" and at least one
-		// does not. Otherwise return false."
+		// "Return false if the active range is null.  Otherwise, block-extend
+		// the active range. Return true if among visible editable nodes that
+		// are contained in the result and have no children, at least one has
+		// alignment value "center" and at least one does not. Otherwise return
+		// false."
+		if (!getActiveRange()) {
+			return false;
+		}
 		var nodes = getAllContainedNodes(blockExtend(getActiveRange()), function(node) {
 			return isEditable(node) && isVisible(node) && !node.hasChildNodes();
 		});
 		return nodes.some(function(node) { return getAlignmentValue(node) == "center" })
 			&& nodes.some(function(node) { return getAlignmentValue(node) != "center" });
 	}, state: function() {
-		// "Block-extend the active range. Return true if there is at least one
-		// visible editable node that is contained in the result and has no
-		// children, and all such nodes have alignment value "center".
-		// Otherwise return false."
+		// "Return false if the active range is null.  Otherwise, block-extend
+		// the active range. Return true if there is at least one visible
+		// editable node that is contained in the result and has no children,
+		// and all such nodes have alignment value "center".  Otherwise return
+		// false."
+		if (!getActiveRange()) {
+			return false;
+		}
 		var nodes = getAllContainedNodes(blockExtend(getActiveRange()), function(node) {
 			return isEditable(node) && isVisible(node) && !node.hasChildNodes();
 		});
 		return nodes.length
 			&& nodes.every(function(node) { return getAlignmentValue(node) == "center" });
 	}, value: function() {
-		// "Block-extend the active range, and return the alignment value of
-		// the first visible editable node that is contained in the result and
-		// has no children. If there is no such node, return "left"."
+		// "Return the empty string if the active range is null.  Otherwise,
+		// block-extend the active range, and return the alignment value of the
+		// first visible editable node that is contained in the result and has
+		// no children. If there is no such node, return "left"."
+		if (!getActiveRange()) {
+			return "";
+		}
 		var nodes = getAllContainedNodes(blockExtend(getActiveRange()), function(node) {
 			return isEditable(node) && isVisible(node) && !node.hasChildNodes();
 		});
@@ -7942,29 +7978,41 @@
 	// "Justify the selection with alignment "justify"."
 	action: function() { justifySelection("justify") },
 	indeterm: function() {
-		// "Block-extend the active range. Return true if among visible
-		// editable nodes that are contained in the result and have no
-		// children, at least one has alignment value "justify" and at least
-		// one does not. Otherwise return false."
+		// "Return false if the active range is null.  Otherwise, block-extend
+		// the active range. Return true if among visible editable nodes that
+		// are contained in the result and have no children, at least one has
+		// alignment value "justify" and at least one does not. Otherwise
+		// return false."
+		if (!getActiveRange()) {
+			return false;
+		}
 		var nodes = getAllContainedNodes(blockExtend(getActiveRange()), function(node) {
 			return isEditable(node) && isVisible(node) && !node.hasChildNodes();
 		});
 		return nodes.some(function(node) { return getAlignmentValue(node) == "justify" })
 			&& nodes.some(function(node) { return getAlignmentValue(node) != "justify" });
 	}, state: function() {
-		// "Block-extend the active range. Return true if there is at least one
-		// visible editable node that is contained in the result and has no
-		// children, and all such nodes have alignment value "justify".
-		// Otherwise return false."
+		// "Return false if the active range is null.  Otherwise, block-extend
+		// the active range. Return true if there is at least one visible
+		// editable node that is contained in the result and has no children,
+		// and all such nodes have alignment value "justify".  Otherwise return
+		// false."
+		if (!getActiveRange()) {
+			return false;
+		}
 		var nodes = getAllContainedNodes(blockExtend(getActiveRange()), function(node) {
 			return isEditable(node) && isVisible(node) && !node.hasChildNodes();
 		});
 		return nodes.length
 			&& nodes.every(function(node) { return getAlignmentValue(node) == "justify" });
 	}, value: function() {
-		// "Block-extend the active range, and return the alignment value of
-		// the first visible editable node that is contained in the result and
-		// has no children. If there is no such node, return "left"."
+		// "Return the empty string if the active range is null.  Otherwise,
+		// block-extend the active range, and return the alignment value of the
+		// first visible editable node that is contained in the result and has
+		// no children. If there is no such node, return "left"."
+		if (!getActiveRange()) {
+			return "";
+		}
 		var nodes = getAllContainedNodes(blockExtend(getActiveRange()), function(node) {
 			return isEditable(node) && isVisible(node) && !node.hasChildNodes();
 		});
@@ -7983,29 +8031,41 @@
 	// "Justify the selection with alignment "left"."
 	action: function() { justifySelection("left") },
 	indeterm: function() {
-		// "Block-extend the active range. Return true if among visible
-		// editable nodes that are contained in the result and have no
-		// children, at least one has alignment value "left" and at least one
-		// does not. Otherwise return false."
+		// "Return false if the active range is null.  Otherwise, block-extend
+		// the active range. Return true if among visible editable nodes that
+		// are contained in the result and have no children, at least one has
+		// alignment value "left" and at least one does not. Otherwise return
+		// false."
+		if (!getActiveRange()) {
+			return false;
+		}
 		var nodes = getAllContainedNodes(blockExtend(getActiveRange()), function(node) {
 			return isEditable(node) && isVisible(node) && !node.hasChildNodes();
 		});
 		return nodes.some(function(node) { return getAlignmentValue(node) == "left" })
 			&& nodes.some(function(node) { return getAlignmentValue(node) != "left" });
 	}, state: function() {
-		// "Block-extend the active range. Return true if there is at least one
-		// visible editable node that is contained in the result and has no
-		// children, and all such nodes have alignment value "left".  Otherwise
-		// return false."
+		// "Return false if the active range is null.  Otherwise, block-extend
+		// the active range. Return true if there is at least one visible
+		// editable node that is contained in the result and has no children,
+		// and all such nodes have alignment value "left".  Otherwise return
+		// false."
+		if (!getActiveRange()) {
+			return false;
+		}
 		var nodes = getAllContainedNodes(blockExtend(getActiveRange()), function(node) {
 			return isEditable(node) && isVisible(node) && !node.hasChildNodes();
 		});
 		return nodes.length
 			&& nodes.every(function(node) { return getAlignmentValue(node) == "left" });
 	}, value: function() {
-		// "Block-extend the active range, and return the alignment value of
-		// the first visible editable node that is contained in the result and
-		// has no children. If there is no such node, return "left"."
+		// "Return the empty string if the active range is null.  Otherwise,
+		// block-extend the active range, and return the alignment value of the
+		// first visible editable node that is contained in the result and has
+		// no children. If there is no such node, return "left"."
+		if (!getActiveRange()) {
+			return "";
+		}
 		var nodes = getAllContainedNodes(blockExtend(getActiveRange()), function(node) {
 			return isEditable(node) && isVisible(node) && !node.hasChildNodes();
 		});
@@ -8024,29 +8084,41 @@
 	// "Justify the selection with alignment "right"."
 	action: function() { justifySelection("right") },
 	indeterm: function() {
-		// "Block-extend the active range. Return true if among visible
-		// editable nodes that are contained in the result and have no
-		// children, at least one has alignment value "right" and at least one
-		// does not. Otherwise return false."
+		// "Return false if the active range is null.  Otherwise, block-extend
+		// the active range. Return true if among visible editable nodes that
+		// are contained in the result and have no children, at least one has
+		// alignment value "right" and at least one does not. Otherwise return
+		// false."
+		if (!getActiveRange()) {
+			return false;
+		}
 		var nodes = getAllContainedNodes(blockExtend(getActiveRange()), function(node) {
 			return isEditable(node) && isVisible(node) && !node.hasChildNodes();
 		});
 		return nodes.some(function(node) { return getAlignmentValue(node) == "right" })
 			&& nodes.some(function(node) { return getAlignmentValue(node) != "right" });
 	}, state: function() {
-		// "Block-extend the active range. Return true if there is at least one
-		// visible editable node that is contained in the result and has no
-		// children, and all such nodes have alignment value "right".
-		// Otherwise return false."
+		// "Return false if the active range is null.  Otherwise, block-extend
+		// the active range. Return true if there is at least one visible
+		// editable node that is contained in the result and has no children,
+		// and all such nodes have alignment value "right".  Otherwise return
+		// false."
+		if (!getActiveRange()) {
+			return false;
+		}
 		var nodes = getAllContainedNodes(blockExtend(getActiveRange()), function(node) {
 			return isEditable(node) && isVisible(node) && !node.hasChildNodes();
 		});
 		return nodes.length
 			&& nodes.every(function(node) { return getAlignmentValue(node) == "right" });
 	}, value: function() {
-		// "Block-extend the active range, and return the alignment value of
-		// the first visible editable node that is contained in the result and
-		// has no children. If there is no such node, return "left"."
+		// "Return the empty string if the active range is null.  Otherwise,
+		// block-extend the active range, and return the alignment value of the
+		// first visible editable node that is contained in the result and has
+		// no children. If there is no such node, return "left"."
+		if (!getActiveRange()) {
+			return "";
+		}
 		var nodes = getAllContainedNodes(blockExtend(getActiveRange()), function(node) {
 			return isEditable(node) && isVisible(node) && !node.hasChildNodes();
 		});
@@ -8248,6 +8320,10 @@
 	if ("inlineCommandActivatedValues" in commands[command]
 	&& !("indeterm" in commands[command])) {
 		commands[command].indeterm = function() {
+			if (!getActiveRange()) {
+				return false;
+			}
+
 			var values = getAllEffectivelyContainedNodes(getActiveRange(), isFormattableNode)
 				.map(function(node) { return getEffectiveCommandValue(node, command) });
 
@@ -8268,6 +8344,10 @@
 	// them have an effective command value equal to one of the given values."
 	if ("inlineCommandActivatedValues" in commands[command]) {
 		commands[command].state = function() {
+			if (!getActiveRange()) {
+				return false;
+			}
+
 			var nodes = getAllEffectivelyContainedNodes(getActiveRange(), isFormattableNode);
 
 			if (nodes.length == 0) {
@@ -8291,6 +8371,10 @@
 	// if that is null, the empty string."
 	if ("standardInlineValueCommand" in commands[command]) {
 		commands[command].indeterm = function() {
+			if (!getActiveRange()) {
+				return false;
+			}
+
 			var values = getAllEffectivelyContainedNodes(getActiveRange())
 				.filter(isFormattableNode)
 				.map(function(node) { return getEffectiveCommandValue(node, command) });
@@ -8303,6 +8387,10 @@
 		};
 
 		commands[command].value = function() {
+			if (!getActiveRange()) {
+				return "";
+			}
+
 			var refNode = getAllEffectivelyContainedNodes(getActiveRange(), isFormattableNode)[0];
 
 			if (typeof refNode == "undefined") {
--- a/source.html	Fri Feb 24 08:52:30 2012 -0700
+++ b/source.html	Fri Feb 24 08:46:37 2012 -0700
@@ -1338,18 +1338,28 @@
 
 <p>It's not clear to me what the point of this method is.  There's no way we're
 going to always return true if the command will do something and false if it
-won't.  I just stuck with a really conservative definition that happens to be
+won't.  I originally just stuck with a really conservative definition that happens to be
 convenient: if there's nothing selected, obviously nothing will work, and we
 want to bail out early in that case anyway because all the algorithms will talk
 about the active range.  If there are use-cases for it to be more precise, I
 could make it so.
+
+<p><a href=https://www.w3.org/Bugs/Public/show_bug.cgi?id=16094>Bug 16094</a>
+illustrated that we don't really want to be able to modify multiple editing
+hosts at once, nor do we want to do anything if the start and end aren't both
+editable, so I co-opted this definition to fit my ends.
 </div>
 
 <p>Among <span title=command>commands</span> defined in this specification,
 those listed in <a href=#miscellaneous-commands>Miscellaneous commands</a> are
-always <span>enabled</span>.  The other <span title=command>commands</span>
-defined here are <span>enabled</span> if the <span>active range</span> is not
-null, and disabled otherwise.
+always <span>enabled</span>, except for <span>the <code title>cut</code>
+command</span> and <span>the <code title>paste</code> command</span>.  The
+other <span title=command>commands</span> defined here are <span>enabled</span>
+if the <span>active range</span> is not null, its [[startnode]] is either
+<span>editable</span> or an <span>editing host</span>, its [[endnode]] is
+either <span>editable</span> or an <span>editing host</span>, and there is some
+<span>editing host</span> that is an [[inclusiveancestor]] of both its
+[[startnode]] and its [[endnode]].
 
 <!-- @} -->
 
@@ -1453,8 +1463,7 @@
   </div>
 
   <p>If <var>command</var> has no <span
-  title=indeterminate>indeterminacy</span> or is not <span>enabled</span>,
-  return false.
+  title=indeterminate>indeterminacy</span>, return false.
 
   <li>Return true if <var>command</var> is <span>indeterminate</span>,
   otherwise false.
@@ -1477,8 +1486,7 @@
   <p class=comments>See comment on the comparable line for
   <span>queryCommandIndeterm()</span>.
 
-  <p>If <var>command</var> has no <span>state</span> or is not
-  <span>enabled</span>, return false.
+  <p>If <var>command</var> has no <span>state</span>, return false.
 
   <li>If the <span>state override</span> for <var>command</var> is set, return
   it.
@@ -1522,8 +1530,7 @@
   see the comment on the comparable line for
   <span>queryCommandIndeterm()</span>.
 
-  <p>If <var>command</var> has no <span>value</span> or is not
-  <span>enabled</span>, return the empty string.
+  <p>If <var>command</var> has no <span>value</span>, return the empty string.
 
   <li>
   <p class=comments>Yuck.  This is incredibly messy, as are lots of other
@@ -4016,6 +4023,8 @@
 <p><span>Value</span>:
 
 <ol>
+  <li>If the <span>active range</span> is null, return the empty string.
+
   <li>
   <p class=comments>See comment for standard inline value commands on how I
   decided on this choice of node.
@@ -4772,6 +4781,8 @@
 </div>
 
 <ol>
+  <li>If the <span>active range</span> is null, return "none".
+
   <li><span>Block-extend</span> the <span>active range</span>, and let <var>new
   range</var> be the result.
 
@@ -8358,6 +8369,8 @@
 <p><span>Indeterminate</span>:
 
 <ol>
+  <li>If the <span>active range</span> is null, return the empty string.
+
   <li><span>Block-extend</span> the <span>active range</span>, and let <var>new
   range</var> be the result.
 
@@ -8413,6 +8426,8 @@
 
 <p><span>Value</span>:
 <ol>
+  <li>If the <span>active range</span> is null, return the empty string.
+
   <li><span>Block-extend</span> the <span>active range</span>, and let <var>new
   range</var> be the result.
 
@@ -9816,11 +9831,12 @@
 attention even to some nodes that do have children.
 </div>
 
-<p><span>Indeterminate</span>: <span>Block-extend</span> the <span>active
-range</span>.  Return true if among <span>visible</span> <span>editable</span>
-[[nodes]] that are [[contained]] in the result and have no [[children]], at
-least one has <span>alignment value</span> "center" and at least one does not.
-Otherwise return false.
+<p><span>Indeterminate</span>: Return false if the <span>active range</span> is
+null.  Otherwise, <span>block-extend</span> the <span>active range</span>.
+Return true if among <span>visible</span> <span>editable</span> [[nodes]] that
+are [[contained]] in the result and have no [[children]], at least one has
+<span>alignment value</span> "center" and at least one does not.  Otherwise
+return false.
 
 <div class=comments>
 <p>IE9 throws exceptions in almost every case when querying the state of
@@ -9844,8 +9860,9 @@
 I'll support it anyway, since Gecko/WebKit do.
 </div>
 
-<p><span>State</span>: <span>Block-extend</span> the <span>active range</span>.
-Return true if there is at least one <span>visible</span> <span>editable</span>
+<p><span>State</span>: Return false if the <span>active range</span> is null.
+Otherwise, <span>block-extend</span> the <span>active range</span>.  Return
+true if there is at least one <span>visible</span> <span>editable</span>
 [[node]] that is [[contained]] in the result and has no [[children]], and all
 such [[nodes]] have <span>alignment value</span> "center".  Otherwise return
 false.
@@ -9858,7 +9875,8 @@
 case.  Chrome 14 dev returns the state cast to a string, as usual.  Opera 11.11
 always returns the empty string.
 
-<p><span>Value</span>: <span>Block-extend</span> the <span>active range</span>,
+<p><span>Value</span>: Return the empty string if the <span>active range</span>
+is null.  Otherwise, <span>block-extend</span> the <span>active range</span>,
 and return the <span>alignment value</span> of the first <span>visible</span>
 <span>editable</span> [[node]] that is [[contained]] in the result and has no
 [[children]].  If there is no such [[node]], return "left".
@@ -9869,19 +9887,22 @@
 <p><span>Action</span>: <span>Justify the selection</span> with
 <var>alignment</var> "justify".
 
-<p><span>Indeterminate</span>: <span>Block-extend</span> the <span>active
-range</span>.  Return true if among <span>visible</span> <span>editable</span>
-[[nodes]] that are [[contained]] in the result and have no [[children]], at
-least one has <span>alignment value</span> "justify" and at least one does not.
-Otherwise return false.
-
-<p><span>State</span>: <span>Block-extend</span> the <span>active range</span>.
-Return true if there is at least one <span>visible</span> <span>editable</span>
+<p><span>Indeterminate</span>: Return false if the <span>active range</span> is
+null.  Otherwise, <span>block-extend</span> the <span>active range</span>.
+Return true if among <span>visible</span> <span>editable</span> [[nodes]] that
+are [[contained]] in the result and have no [[children]], at least one has
+<span>alignment value</span> "justify" and at least one does not.  Otherwise
+return false.
+
+<p><span>State</span>: Return false if the <span>active range</span> is null.
+Otherwise, <span>block-extend</span> the <span>active range</span>.  Return
+true if there is at least one <span>visible</span> <span>editable</span>
 [[node]] that is [[contained]] in the result and has no [[children]], and all
 such [[nodes]] have <span>alignment value</span> "justify".  Otherwise return
 false.
 
-<p><span>Value</span>: <span>Block-extend</span> the <span>active range</span>,
+<p><span>Value</span>: Return the empty string if the <span>active range</span>
+is null.  Otherwise, <span>block-extend</span> the <span>active range</span>,
 and return the <span>alignment value</span> of the first <span>visible</span>
 <span>editable</span> [[node]] that is [[contained]] in the result and has no
 [[children]].  If there is no such [[node]], return "left".
@@ -9892,19 +9913,22 @@
 <p><span>Action</span>: <span>Justify the selection</span> with
 <var>alignment</var> "left".
 
-<p><span>Indeterminate</span>: <span>Block-extend</span> the <span>active
-range</span>.  Return true if among <span>visible</span> <span>editable</span>
-[[nodes]] that are [[contained]] in the result and have no [[children]], at
-least one has <span>alignment value</span> "left" and at least one does not.
-Otherwise return false.
-
-<p><span>State</span>: <span>Block-extend</span> the <span>active range</span>.
-Return true if there is at least one <span>visible</span> <span>editable</span>
+<p><span>Indeterminate</span>: Return false if the <span>active range</span> is
+null.  Otherwise, <span>block-extend</span> the <span>active range</span>.
+Return true if among <span>visible</span> <span>editable</span> [[nodes]] that
+are [[contained]] in the result and have no [[children]], at least one has
+<span>alignment value</span> "left" and at least one does not.  Otherwise
+return false.
+
+<p><span>State</span>: Return false if the <span>active range</span> is null.
+Otherwise, <span>block-extend</span> the <span>active range</span>.  Return
+true if there is at least one <span>visible</span> <span>editable</span>
 [[node]] that is [[contained]] in the result and has no [[children]], and all
 such [[nodes]] have <span>alignment value</span> "left".  Otherwise return
 false.
 
-<p><span>Value</span>: <span>Block-extend</span> the <span>active range</span>,
+<p><span>Value</span>: Return the empty string if the <span>active range</span>
+is null.  Otherwise, <span>block-extend</span> the <span>active range</span>,
 and return the <span>alignment value</span> of the first <span>visible</span>
 <span>editable</span> [[node]] that is [[contained]] in the result and has no
 [[children]].  If there is no such [[node]], return "left".
@@ -9915,19 +9939,22 @@
 <p><span>Action</span>: <span>Justify the selection</span> with
 <var>alignment</var> "right".
 
-<p><span>Indeterminate</span>: <span>Block-extend</span> the <span>active
-range</span>.  Return true if among <span>visible</span> <span>editable</span>
-[[nodes]] that are [[contained]] in the result and have no [[children]], at
-least one has <span>alignment value</span> "right" and at least one does not.
-Otherwise return false.
-
-<p><span>State</span>: <span>Block-extend</span> the <span>active range</span>.
-Return true if there is at least one <span>visible</span> <span>editable</span>
+<p><span>Indeterminate</span>: Return false if the <span>active range</span> is
+null.  Otherwise, <span>block-extend</span> the <span>active range</span>.
+Return true if among <span>visible</span> <span>editable</span> [[nodes]] that
+are [[contained]] in the result and have no [[children]], at least one has
+<span>alignment value</span> "right" and at least one does not.  Otherwise
+return false.
+
+<p><span>State</span>: Return false if the <span>active range</span> is null.
+Otherwise, <span>block-extend</span> the <span>active range</span>.  Return
+true if there is at least one <span>visible</span> <span>editable</span>
 [[node]] that is [[contained]] in the result and has no [[children]], and all
 such [[nodes]] have <span>alignment value</span> "right".  Otherwise return
 false.
 
-<p><span>Value</span>: <span>Block-extend</span> the <span>active range</span>,
+<p><span>Value</span>: Return the empty string if the <span>active range</span>
+is null.  Otherwise, <span>block-extend</span> the <span>active range</span>,
 and return the <span>alignment value</span> of the first <span>visible</span>
 <span>editable</span> [[node]] that is [[contained]] in the result and has no
 [[children]].  If there is no such [[node]], return "left".