Results unsurprising: Gecko is perfect, WebKit is almost perfect except
that it doesn't support addRange() correctly, Opera is totally broken,
IE is basically okay and is correct per previous vague specs but has a
bug or two in light of the precise wording of the current spec.

--- /dev/null Thu Jan 01 00:00:00 1970 +0000+++ b/selecttest/Selection-collapseToStartEnd.html Tue Oct 11 15:30:30 2011 -0600@@ -0,0 +1,123 @@+<!doctype html>+<title>Selection.collapseTo(Start|End)() tests</title>+<div id=log></div>+<script src=http://w3c-test.org/resources/testharness.js></script>+<script src=common.js></script>+<script>+"use strict";++function testCollapseToStartEnd(range) {+}++// Also test a selection with no ranges+testRanges.unshift("[]");++for (var i = 0; i < testRanges.length; i++) {+ test(function() {+ selection.removeAllRanges();+ var endpoints = eval(testRanges[i]);+ if (!endpoints.length) {+ assert_throws("INVALID_STATE_ERR", function() {+ selection.collapseToStart();+ }, "Must throw InvalidStateErr if the selection's range is null");+ return;+ }++ var addedRange = ownerDocument(endpoints[0]).createRange();+ addedRange.setStart(endpoints[0], endpoints[1]);+ addedRange.setEnd(endpoints[2], endpoints[3]);+ selection.addRange(addedRange);++ // We don't penalize browsers here for mishandling addRange() and+ // adding a different range than we specified. They fail addRange()+ // tests for that, and don't have to fail collapseToStart/End() tests+ // too. They do fail if they throw unexpectedly, though. I also fail+ // them if there's no range at all, because otherwise they could pass+ // all tests if addRange() always does nothing and collapseToStart()+ // always throws.+ assert_equals(selection.rangeCount, 1,+ "Sanity check: rangeCount must equal 1 after addRange()");++ var expectedEndpoint = [+ selection.getRangeAt(0).startContainer,+ selection.getRangeAt(0).startOffset+ ];++ selection.collapseToStart();++ assert_equals(selection.rangeCount, 1,+ "selection.rangeCount must equal 1");+ assert_equals(selection.focusNode, expectedEndpoint[0],+ "focusNode must equal the original start node");+ assert_equals(selection.focusOffset, expectedEndpoint[1],+ "focusOffset must equal the original start offset");+ assert_equals(selection.anchorNode, expectedEndpoint[0],+ "anchorNode must equal the original start node");+ assert_equals(selection.anchorOffset, expectedEndpoint[1],+ "anchorOffset must equal the original start offset");+ assert_equals(addedRange.startContainer, endpoints[0],+ "collapseToStart() must not change the startContainer of the selection's original range");+ assert_equals(addedRange.startOffset, endpoints[1],+ "collapseToStart() must not change the startOffset of the selection's original range");+ assert_equals(addedRange.endContainer, endpoints[2],+ "collapseToStart() must not change the endContainer of the selection's original range");+ assert_equals(addedRange.endOffset, endpoints[3],+ "collapseToStart() must not change the endOffset of the selection's original range");+ }, "Range " + i + " " + testRanges[i] + " collapseToStart()");++ // Copy-paste of above+ test(function() {+ selection.removeAllRanges();+ var endpoints = eval(testRanges[i]);+ if (!endpoints.length) {+ assert_throws("INVALID_STATE_ERR", function() {+ selection.collapseToEnd();+ }, "Must throw InvalidStateErr if the selection's range is null");+ return;+ }++ var addedRange = ownerDocument(endpoints[0]).createRange();+ addedRange.setStart(endpoints[0], endpoints[1]);+ addedRange.setEnd(endpoints[2], endpoints[3]);+ selection.addRange(addedRange);++ // We don't penalize browsers here for mishandling addRange() and+ // adding a different range than we specified. They fail addRange()+ // tests for that, and don't have to fail collapseToStart/End() tests+ // too. They do fail if they throw unexpectedly, though. I also fail+ // them if there's no range at all, because otherwise they could pass+ // all tests if addRange() always does nothing and collapseToStart()+ // always throws.+ assert_equals(selection.rangeCount, 1,+ "Sanity check: rangeCount must equal 1 after addRange()");++ var expectedEndpoint = [+ selection.getRangeAt(0).endContainer,+ selection.getRangeAt(0).endOffset+ ];++ selection.collapseToEnd();++ assert_equals(selection.rangeCount, 1,+ "selection.rangeCount must equal 1");+ assert_equals(selection.focusNode, expectedEndpoint[0],+ "focusNode must equal the original end node");+ assert_equals(selection.focusOffset, expectedEndpoint[1],+ "focusOffset must equal the original end offset");+ assert_equals(selection.anchorNode, expectedEndpoint[0],+ "anchorNode must equal the original end node");+ assert_equals(selection.anchorOffset, expectedEndpoint[1],+ "anchorOffset must equal the original end offset");+ assert_equals(addedRange.startContainer, endpoints[0],+ "collapseToEnd() must not change the startContainer of the selection's original range");+ assert_equals(addedRange.startOffset, endpoints[1],+ "collapseToEnd() must not change the startOffset of the selection's original range");+ assert_equals(addedRange.endContainer, endpoints[2],+ "collapseToEnd() must not change the endContainer of the selection's original range");+ assert_equals(addedRange.endOffset, endpoints[3],+ "collapseToEnd() must not change the endOffset of the selection's original range");+ }, "Range " + i + " " + testRanges[i] + " collapseToEnd()");+}++testDiv.style.display = "none";+</script>

--- a/source.html Tue Oct 11 13:20:52 2011 -0600+++ b/source.html Tue Oct 11 15:30:30 2011 -0600@@ -868,23 +868,24 @@ <var>offset</var>). </ol>+<p class=comments>For collapseToStart/End, IE9 mutates the existing range,+while Firefox 9.0a2 and Chrome 15 dev replace it with a new one. The spec+follows the majority and replaces it with a new one, leaving the old Range+object unchanged.+ <p>The <dfn title=dom-Selection-collapseToStart><code>collapseToStart()</code></dfn> method must [[throw]] an [[InvalidStateError]] exception if the [[contextobject]]'s-[[range]] is null. Otherwise, it must invoke the [[selcollapse|]] method with-the [[startnode]] and [[startoffset]] of the [[contextobject]]'s [[range]] as-the arguments.+[[range]] is null. Otherwise, it must set the [[contextobject]]'s [[range]] to+a new [[range]] object with [[rangestart]] and [[rangeend]] both equal to the+[[contextobject]]'s old [[range]]'s [[rangestart]]. <p>The <dfn title=dom-Selection-collapseToEnd><code>collapseToEnd()</code></dfn> method must [[throw]] an [[InvalidStateError]] exception if the [[contextobject]]'s-[[range]] is null. Otherwise, it must invoke the [[selcollapse|]] method with-the [[endnode]] and [[endoffset]] values of the last [[range]] object in the-list as the arguments.--<p class=XXX>This implies that they'll replace the range object instead of-changing it. Will they? Needs testing. It seems like they shouldn't. Same-for collapse() itself.+[[range]] is null. Otherwise, it must set the [[contextobject]]'s [[range]] to+a new [[range]] object with [[rangestart]] and [[rangeend]] both equal to the+[[contextobject]]'s old [[range]]'s [[rangeend]]. <p class=comments>Reverse-engineered circa January 2011. IE doesn't support it, so I'm relying on Firefox (implemented extend() sometime before 2000) and WebKit