Bug 1325850 - part 2: Add automated tests for nsIContentIterator concrete classes r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 03 Jan 2019 22:27:47 +0000
changeset 452689 1541218476834334f1e5af8b22671ff75b4c54b0
parent 452688 7cf2438bcd343bcd530e8e9f8e8e9c4cc92942e4
child 452690 39ab423f06ef236d72d25629bb462200ed8483ef
push id75532
push usermasayuki@d-toybox.com
push dateMon, 07 Jan 2019 07:14:57 +0000
treeherderautoland@154121847683 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1325850
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1325850 - part 2: Add automated tests for nsIContentIterator concrete classes r=smaug nsContentIterator in post-order collects nodes starting from deepest first child to next siblings and parents. I.e., collecting end points (i.e., "close tag" if element node). This is tested by test_content_iterator_post_order.html. nsContentIterator in pre-order collects nodes starting from most ancestor to last deepest child node. I.e., collecting start points (i.e., "open tag" if element node). This is tested by test_content_iterator_pre_order.html. nsContentSubtreeIterator collects most ancestor nodes which are selected entirely by specified range or positions. This is tested by test_content_iterator_subtree.html I.e., all of them start with "test_content_iterator_" for making find easier. Differential Revision: https://phabricator.services.mozilla.com/D15286
dom/base/test/mochitest.ini
dom/base/test/test_content_iterator_post_order.html
dom/base/test/test_content_iterator_pre_order.html
dom/base/test/test_content_iterator_subtree.html
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -617,16 +617,19 @@ skip-if = os == "mac"
 [test_bug1472427.html]
 [test_bug1499169.html]
 skip-if = toolkit == 'android' # Timeouts on android due to page closing issues with embedded pdf
 [test_caretPositionFromPoint.html]
 [test_change_policy.html]
 [test_clearTimeoutIntervalNoArg.html]
 [test_constructor-assignment.html]
 [test_constructor.html]
+[test_content_iterator_post_order.html]
+[test_content_iterator_pre_order.html]
+[test_content_iterator_subtree.html]
 [test_copyimage.html]
 subsuite = clipboard
 skip-if = toolkit == 'android' #bug 904183
 [test_copypaste.html]
 subsuite = clipboard
 skip-if = toolkit == 'android' #bug 904183
 [test_copypaste.xhtml]
 subsuite = clipboard
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_content_iterator_post_order.html
@@ -0,0 +1,875 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for post-order content iterator</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<script>
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+function finish() {
+  // The SimpleTest may require usual elements in the template, but they shouldn't be during test.
+  // So, let's create them at end of the test.
+  document.body.innerHTML = '<div id="display"></div><div id="content"></div><pre id="test"></pre>';
+  SimpleTest.finish();
+}
+
+function createContentIterator() {
+  return Cc["@mozilla.org/scriptable-content-iterator;1"]
+      .createInstance(Ci.nsIScriptableContentIterator);
+}
+
+function getNodeDescription(aNode) {
+  if (aNode === undefined) {
+    return "undefine";
+  }
+  if (aNode === null) {
+    return "null";
+  }
+  function getElementDescription(aElement) {
+    if (aElement.tagName === "BR") {
+      if (aElement.previousSibling) {
+        return `<br> element after ${getNodeDescription(aElement.previousSibling)}`;
+      }
+      return `<br> element in ${getElementDescription(aElement.parentElement)}`;
+    }
+    let hasHint = aElement == document.body;
+    let tag = `<${aElement.tagName.toLowerCase()}`;
+    if (aElement.getAttribute("id")) {
+      tag += ` id="${aElement.getAttribute("id")}"`;
+      hasHint = true;
+    }
+    if (aElement.getAttribute("class")) {
+      tag += ` class="${aElement.getAttribute("class")}"`;
+      hasHint = true;
+    }
+    if (aElement.getAttribute("type")) {
+      tag += ` type="${aElement.getAttribute("type")}"`;
+    }
+    if (aElement.getAttribute("name")) {
+      tag += ` name="${aElement.getAttribute("name")}"`;
+    }
+    if (aElement.getAttribute("value")) {
+      tag += ` value="${aElement.getAttribute("value")}"`;
+      hasHint = true;
+    }
+    if (aElement.getAttribute("style")) {
+      tag += ` style="${aElement.getAttribute("style")}"`;
+      hasHint = true;
+    }
+    if (hasHint) {
+      return tag + ">";
+    }
+    return `${tag}> in ${getElementDescription(aElement.parentElement)}`;
+  }
+  switch (aNode.nodeType) {
+    case aNode.TEXT_NODE:
+      return `text node, "${aNode.wholeText.replace(/\n/g, '\\n')}"`;
+    case aNode.COMMENT_NODE:
+      return `comment node, "${aNode.data.replace(/\n/g, '\\n')}"`;
+    case aNode.ELEMENT_NODE:
+      return getElementDescription(SpecialPowers.unwrap(aNode));
+    default:
+      return "unknown node";
+  }
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function () {
+  let iter = createContentIterator();
+
+  /**
+   * Basic behavior tests of first(), last(), prev() and next() after initialized with an empty element.
+   */
+  document.body.innerHTML = "<div></div>";
+  let description = "Initialized with empty <div> as root node:";
+  iter.initWithRootNode(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, document.body.firstChild);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first()`);
+
+  iter.last();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling last() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling last()`);
+
+  iter.prev();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling prev() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling prev()`); // XXX Is this expected?
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling first() even after once done (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first() even after once done`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Basic behavior tests of first(), last(), prev() and next() after initialized with a range which selects empty element.
+   */
+  let range = document.createRange();
+  range.selectNode(document.body.firstChild);
+  description = "Initialized with range including only empty <div>:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first()`);
+
+  iter.last();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling last() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling last()`);
+
+  iter.prev();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling prev() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling prev()`); // XXX Is this expected?
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling first() even after once done (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first() even after once done`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Basic behavior tests of first(), last(), prev() and next() after initialized with positions which select empty element.
+   */
+  range.selectNode(document.body.firstChild);
+  description = "Initialized with positions including only empty <div>:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first()`);
+
+  iter.last();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling last() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling last()`);
+
+  iter.prev();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling prev() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling prev()`); // XXX Is this expected?
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling first() even after once done (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first() even after once done`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Tests to initializing with collapsed range in an empty element.
+   */
+  range = document.createRange();
+  range.collapse(document.body.firstChild, 0);
+  description = "Initialized with range collapsed in empty <div>:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Tests to initializing with collapsed range in an empty element.
+   */
+  description = "Initialized with a position in empty <div>:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         document.body.firstChild, 0, document.body.firstChild, 0);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Basic behavior tests of first(), last(), prev() and next() after initialized with the text element.
+   */
+  document.body.innerHTML = "<div>some text.</div>";
+  description = "Initialized with a text node as root node:";
+  iter.initWithRootNode(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, document.body.firstChild.firstChild);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be the text node after calling first()`);
+
+  iter.last();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node after calling last() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling last()`);
+
+  iter.prev();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling prev() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling prev()`); // XXX Is this expected?
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node after calling first() even after once done (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first() even after once done`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Basic behavior tests of first(), last(), prev() and next() after initialized with a range which selects the text node.
+   */
+  range = document.createRange();
+  range.selectNode(document.body.firstChild.firstChild);
+  description = "Initialized with range including only text node:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first()`);
+
+  iter.last();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node after calling last() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling last()`);
+
+  iter.prev();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling prev() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling prev()`); // XXX Is this expected?
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node after calling first() even after once done (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first() even after once done`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Basic behavior tests of first() and next() after initialized with positions which select the text node.
+   * XXX In this case, content iterator lists up the parent <div> element.  Not sure if this is intentional difference
+   *     from initWithRange().
+   */
+  range.selectNode(document.body.firstChild);
+  description = "Initialized with positions including only text node:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first()`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> element after calling next() from first position (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling next() from first position`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() from second position (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next() from second position`);
+
+  /**
+   * Tests to initializing with collapsed range at start of a text node.
+   */
+  range = document.createRange();
+  range.collapse(document.body.firstChild.firstChild, 0);
+  description = "Initialized with range collapsed at start of text node:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Tests to initializing with collapsed range at start of a text node.
+   * XXX In this case, content iterator lists up the text node.  Not sure if this is intentional difference
+   *     from initWithRange().
+   */
+  description = "Initialized with a position at start of text node:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         document.body.firstChild.firstChild, 0, document.body.firstChild.firstChild, 0);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Tests to initializing with collapsed range at end of a text node.
+   */
+  range = document.createRange();
+  range.collapse(document.body.firstChild.firstChild, document.body.firstChild.firstChild.length);
+  description = "Initialized with range collapsed at end of text node:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Tests to initializing with collapsed range at end of a text node.
+   * XXX In this case, content iterator lists up the text node.  Not sure if this is intentional difference
+   *     from initWithRange().
+   */
+  description = "Initialized with a position at end of text node:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         document.body.firstChild.firstChild, document.body.firstChild.firstChild.length,
+                         document.body.firstChild.firstChild, document.body.firstChild.firstChild.length);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Tests to initializing with collapsed range at middle of a text node.
+   */
+  range = document.createRange();
+  range.collapse(document.body.firstChild.firstChild, document.body.firstChild.firstChild.length / 2);
+  description = "Initialized with range collapsed at end of text node:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Tests to initializing with collapsed range at middle of a text node.
+   * XXX In this case, content iterator lists up the text node.  Not sure if this is intentional difference
+   *     from initWithRange().
+   */
+  description = "Initialized with a position at end of text node:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         document.body.firstChild.firstChild, document.body.firstChild.firstChild.length / 2,
+                         document.body.firstChild.firstChild, document.body.firstChild.firstChild.length / 2);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Tests to initializing with a range selecting all text in a text node.
+   */
+  range = document.createRange();
+  range.setStart(document.body.firstChild.firstChild, 0);
+  range.setEnd(document.body.firstChild.firstChild, document.body.firstChild.firstChild.length);
+  description = "Initialized with range selecting all text in text node:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Tests to initializing with positions selecting all text in a text node.
+   */
+  description = "Initialized with positions selecting all text in text node:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         document.body.firstChild.firstChild, 0,
+                         document.body.firstChild.firstChild, document.body.firstChild.firstChild.length);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Basic tests with complicated tree.
+   */
+  function check(aIter, aExpectedResult, aDescription) {
+    if (aExpectedResult.length > 0) {
+      is(SpecialPowers.unwrap(aIter.currentNode), aExpectedResult[0],
+        `${aDescription}: currentNode should be the text node immediately after initialization (got: ${getNodeDescription(aIter.currentNode)}, expected: ${getNodeDescription(aExpectedResult[0])})`);
+      ok(!aIter.isDone, `${aDescription}: isDone shouldn't be true immediately after initialization`);
+
+      aIter.first();
+      is(SpecialPowers.unwrap(aIter.currentNode), aExpectedResult[0],
+        `${aDescription}: currentNode should be the text node after calling first() (got: ${getNodeDescription(aIter.currentNode)}, expected: ${getNodeDescription(aExpectedResult[0])})`);
+      ok(!aIter.isDone, `${aDescription}: isDone shouldn't be true after calling first()`);
+
+      for (let expected of aExpectedResult) {
+        is(SpecialPowers.unwrap(aIter.currentNode), expected,
+          `${aDescription}: currentNode should be the node (got: ${getNodeDescription(aIter.currentNode)}, expected: ${getNodeDescription(expected)})`);
+        ok(!aIter.isDone, `${aDescription}: isDone shouldn't be true when ${getNodeDescription(expected)} is expected`);
+        aIter.next();
+      }
+
+      is(SpecialPowers.unwrap(aIter.currentNode), null,
+        `${aDescription}: currentNode should be null after calling next() finally (got: ${getNodeDescription(aIter.currentNode)}`);
+      ok(aIter.isDone, `${aDescription}: isDone should be true after calling next() finally`);
+    } else {
+      is(SpecialPowers.unwrap(aIter.currentNode), null,
+        `${aDescription}: currentNode should be null immediately after initialization (got: ${getNodeDescription(aIter.currentNode)})`);
+      ok(aIter.isDone, `${aDescription}: isDone should be true immediately after initialization`);
+
+      aIter.first();
+      is(SpecialPowers.unwrap(aIter.currentNode), null,
+        `${aDescription}: currentNode should be null after calling first() (got: ${getNodeDescription(aIter.currentNode)})`);
+      ok(aIter.isDone, `${aDescription}: isDone should be true after calling first()`);
+    }
+  }
+
+  document.body.innerHTML = "<p>" +
+                              "Here is <b>bold</b> and <i><u>underlined and </u>italic </i><span>or no style text.</span><br>" +
+                            "</p>" +
+                            "<p>" +
+                              "Here is an &lt;input&gt; element: <input type=\"text\" value=\"default value\"><br>\n" +
+                              "and a &lt;textarea&gt; element: <textarea>text area's text node</textarea><br><br>\n" +
+                              "<!-- and here is comment node -->" +
+                            "</p>";
+
+  let expectedResult =
+    [document.body.firstChild.firstChild, // the first text node
+     document.body.firstChild.firstChild.nextSibling.firstChild, // text in <b>
+     document.body.firstChild.firstChild.nextSibling, // <b>
+     document.body.firstChild.firstChild.nextSibling.nextSibling, // text next to <b>
+     document.body.firstChild.firstChild.nextSibling.nextSibling.nextSibling.firstChild.firstChild, // text in <u>
+     document.body.firstChild.firstChild.nextSibling.nextSibling.nextSibling.firstChild, // <u>
+     document.body.firstChild.firstChild.nextSibling.nextSibling.nextSibling.firstChild.nextSibling, // text next to <u>
+     document.body.firstChild.firstChild.nextSibling.nextSibling.nextSibling, // <i>
+     document.body.firstChild.firstChild.nextSibling.nextSibling.nextSibling.nextSibling.firstChild, // text in <span>
+     document.body.firstChild.firstChild.nextSibling.nextSibling.nextSibling.nextSibling, // <span>
+     document.body.firstChild.firstChild.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling, // <br> next to <span>
+     document.body.firstChild, // first <p>
+     document.body.firstChild.nextSibling.firstChild, // the first text node in second <p>
+     document.body.firstChild.nextSibling.firstChild.nextSibling, // <input>
+     document.body.firstChild.nextSibling.firstChild.nextSibling.nextSibling, // <br> next to <input>
+     document.body.firstChild.nextSibling.firstChild.nextSibling.nextSibling.nextSibling, // text next to <input>
+     document.body.firstChild.nextSibling.firstChild.nextSibling.nextSibling.nextSibling.nextSibling.firstChild, // text in <textarea>
+     document.body.firstChild.nextSibling.firstChild.nextSibling.nextSibling.nextSibling.nextSibling, // <textarea>
+     document.body.firstChild.nextSibling.firstChild.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling, // <br> next to <textarea>
+     document.body.firstChild.nextSibling.firstChild.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling, // <br> next to <br>
+     document.body.firstChild.nextSibling.firstChild.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling, // text next to <br>
+     document.body.firstChild.nextSibling.firstChild.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling, // comment
+     document.body.firstChild.nextSibling, // second <p>
+     document.body]; // <body>
+
+  iter.initWithRootNode(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, document.body);
+  check(iter, expectedResult, "Initialized with the <body> as root element:");
+
+  /**
+   * Selects the <body> with a range.
+   */
+  range = document.createRange();
+  range.selectNode(document.body);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter, expectedResult, "Initialized with range selecting the <body>");
+
+  /**
+   * Selects the <body> with positions.
+   */
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset, range.endContainer, range.endOffset);
+  check(iter, expectedResult, "Initialized with positions selecting the <body>");
+
+  /**
+   * Selects all children in the <body> with a range.
+   */
+  expectedResult.pop(); // <body> shouldn't be listed up.
+  range = document.createRange();
+  range.selectNodeContents(document.body);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter, expectedResult, "Initialized with range selecting all children in the <body>");
+
+  /**
+   * Selects all children in the <body> with positions.
+   */
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset, range.endContainer, range.endOffset);
+  check(iter, expectedResult, "Initialized with positions selecting all children in the <body>");
+
+  /**
+   * range/positions around elements.
+   */
+  document.body.innerHTML = "abc<b>def</b><i>ghi</i>jkl";
+  range = document.createRange();
+
+  range.setStart(document.body.firstChild, 0);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild, // text before <b>
+         document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with range selecting '[abc<b>de]f'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild, // text before <b>
+         document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting '[abc<b>de]f'");
+
+  range.setStart(document.body.firstChild, 2);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild, // text before <b>
+         document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with range selecting 'ab[c<b>de]f'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild, // text before <b>
+         document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting 'ab[c<b>de]f'");
+
+  range.setStart(document.body.firstChild, 3);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild, // text before <b>
+         document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with range selecting 'abc[<b>de]f'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild, // text before <b>
+         document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting 'abc[<b>de]f'");
+
+  range.setStart(document.body, 1);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with range selecting 'abc{<b>de]f'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting 'abc{<b>de]f'");
+
+  range.setStart(document.body.firstChild.nextSibling, 0);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with range selecting '<b>{de]f'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting '<b>{de]f'");
+
+  range.setStart(document.body.firstChild.nextSibling, 0);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 3);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with range selecting '<b>{def]</b>'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting '<b>{def]</b>'");
+
+  range.setStart(document.body.firstChild.nextSibling, 0);
+  range.setEnd(document.body.firstChild.nextSibling, 1);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with range selecting '<b>{def}</b>'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting '<b>{def}</b>'");
+
+  range.setStart(document.body.firstChild.nextSibling, 0);
+  range.setEnd(document.body, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild, // text in <b>
+         document.body.firstChild.nextSibling], // <b>
+        "Initialized with range selecting '<b>{def</b>}<i>ghi'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild, // text in <b>
+         document.body.firstChild.nextSibling], // <b>
+        "Initialized with positions selecting '<b>{def</b>}<i>ghi'");
+
+  range.setStart(document.body.firstChild.nextSibling.firstChild, 3);
+  range.setEnd(document.body, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild, // text in <b>
+         document.body.firstChild.nextSibling], // <b>
+        "Initialized with range selecting '<b>def[</b>}<i>ghi'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild, // text in <b>
+         document.body.firstChild.nextSibling], // <b>
+        "Initialized with positions selecting '<b>def[</b>}<i>ghi'");
+
+  range.setStart(document.body.firstChild.nextSibling, 1);
+  range.setEnd(document.body, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling], // <b>
+        "Initialized with range selecting '<b>def{</b>}<i>ghi'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling], // <b>
+        "Initialized with positions selecting '<b>def{</b>}<i>ghi'");
+
+  range.setStart(document.body.firstChild.nextSibling, 1);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling], // <b>
+        "Initialized with range selecting '<b>def{</b><i>}ghi'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling], // <b>
+        "Initialized with positions selecting '<b>def{</b><i>}ghi'");
+
+  range.setStart(document.body.firstChild.nextSibling, 1);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling.firstChild, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling, // <b>
+         document.body.firstChild.nextSibling.nextSibling.firstChild], // text in <i>
+        "Initialized with range selecting '<b>def{</b><i>]ghi'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling, // <b>
+         document.body.firstChild.nextSibling.nextSibling.firstChild], // text in <i>
+        "Initialized with positions selecting '<b>def{</b><i>]ghi'");
+
+  range.setStart(document.body.firstChild.nextSibling.nextSibling, 0);
+  range.setEnd(document.body, 3);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling.firstChild, // text in <i>
+         document.body.firstChild.nextSibling.nextSibling], // <i>
+        "Initialized with range selecting '<i>{ghi</i>}jkl'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling.firstChild, // text in <i>
+         document.body.firstChild.nextSibling.nextSibling], // <i>
+        "Initialized with positions selecting '<i>{ghi</i>}jkl'");
+
+  range.setStart(document.body.firstChild.nextSibling.nextSibling.firstChild, 3);
+  range.setEnd(document.body, 3);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling.firstChild, // text in <i>
+         document.body.firstChild.nextSibling.nextSibling], // <i>
+        "Initialized with range selecting '<i>ghi[</i>}jkl'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling.firstChild, // text in <i>
+         document.body.firstChild.nextSibling.nextSibling], // <i>
+        "Initialized with positions selecting '<i>ghi[</i>}jkl'");
+
+  range.setStart(document.body.firstChild.nextSibling.nextSibling, 1);
+  range.setEnd(document.body, 3);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling], // <i>
+        "Initialized with range selecting '<i>ghi{</i>}jkl'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling], // <i>
+        "Initialized with positions selecting '<i>ghi{</i>}jkl'");
+
+  range.setStart(document.body.firstChild.nextSibling.nextSibling, 1);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling, // <i>
+         document.body.firstChild.nextSibling.nextSibling.nextSibling], // text after <i>
+        "Initialized with range selecting '<i>ghi{</i>]jkl'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling, // <i>
+         document.body.firstChild.nextSibling.nextSibling.nextSibling], // text after <i>
+        "Initialized with positions selecting '<i>ghi{</i>]jkl'");
+
+  /**
+   * range/positions around <br> elements.
+   */
+  document.body.innerHTML = "abc<br>def";
+  range = document.createRange();
+  range.setStart(document.body.firstChild, 3);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild, // text before <br>
+         document.body.firstChild.nextSibling, // <br>
+         document.body.firstChild.nextSibling.nextSibling], // text after <br>
+        "Initialized with range selecting 'abc[<br>]def'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild, // text before <br>
+         document.body.firstChild.nextSibling, // <br>
+         document.body.firstChild.nextSibling.nextSibling], // text after <br>
+        "Initialized with positions selecting 'abc[<br>]def'");
+
+  range.setStart(document.body, 1);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling, // <br>
+         document.body.firstChild.nextSibling.nextSibling], // text after <br>
+        "Initialized with range selecting 'abc{<br>]def'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling, // <br>
+         document.body.firstChild.nextSibling.nextSibling], // text after <br>
+        "Initialized with positions selecting 'abc{<br>]def'");
+
+  range.setStart(document.body.firstChild.nextSibling, 0);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling, // <br>
+         document.body.firstChild.nextSibling.nextSibling], // text after <br>
+        "Initialized with range selecting 'abc{<br>]def' (starting in <br>)");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling, // <br>
+         document.body.firstChild.nextSibling.nextSibling], // text after <br>
+        "Initialized with positions selecting 'abc{<br>]def' (starting in <br>)");
+
+  range.setStart(document.body, 1);
+  range.setEnd(document.body, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling], // <br>
+        "Initialized with range selecting 'abc{<br>}def'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling], // <br>
+        "Initialized with positions selecting 'abc{<br>}def'");
+
+  range.setStart(document.body.firstChild, 3);
+  range.setEnd(document.body.firstChild.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild], // text before <br>
+        "Initialized with range selecting 'abc[}<br>def' (ending in <br>)");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.POST_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild], // text before <br>
+        "Initialized with positions selecting 'abc[}<br>def' (ending in <br>)");
+
+  finish();
+});
+</script>
+</head>
+<body></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_content_iterator_pre_order.html
@@ -0,0 +1,869 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for pre-order content iterator</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<script>
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+function finish() {
+  // The SimpleTest may require usual elements in the template, but they shouldn't be during test.
+  // So, let's create them at end of the test.
+  document.body.innerHTML = '<div id="display"></div><div id="content"></div><pre id="test"></pre>';
+  SimpleTest.finish();
+}
+
+function createContentIterator() {
+  return Cc["@mozilla.org/scriptable-content-iterator;1"]
+      .createInstance(Ci.nsIScriptableContentIterator);
+}
+
+function getNodeDescription(aNode) {
+  if (aNode === undefined) {
+    return "undefine";
+  }
+  if (aNode === null) {
+    return "null";
+  }
+  function getElementDescription(aElement) {
+    if (aElement.tagName === "BR") {
+      if (aElement.previousSibling) {
+        return `<br> element after ${getNodeDescription(aElement.previousSibling)}`;
+      }
+      return `<br> element in ${getElementDescription(aElement.parentElement)}`;
+    }
+    let hasHint = aElement == document.body;
+    let tag = `<${aElement.tagName.toLowerCase()}`;
+    if (aElement.getAttribute("id")) {
+      tag += ` id="${aElement.getAttribute("id")}"`;
+      hasHint = true;
+    }
+    if (aElement.getAttribute("class")) {
+      tag += ` class="${aElement.getAttribute("class")}"`;
+      hasHint = true;
+    }
+    if (aElement.getAttribute("type")) {
+      tag += ` type="${aElement.getAttribute("type")}"`;
+    }
+    if (aElement.getAttribute("name")) {
+      tag += ` name="${aElement.getAttribute("name")}"`;
+    }
+    if (aElement.getAttribute("value")) {
+      tag += ` value="${aElement.getAttribute("value")}"`;
+      hasHint = true;
+    }
+    if (aElement.getAttribute("style")) {
+      tag += ` style="${aElement.getAttribute("style")}"`;
+      hasHint = true;
+    }
+    if (hasHint) {
+      return tag + ">";
+    }
+    return `${tag}> in ${getElementDescription(aElement.parentElement)}`;
+  }
+  switch (aNode.nodeType) {
+    case aNode.TEXT_NODE:
+      return `text node, "${aNode.wholeText.replace(/\n/g, '\\n')}"`;
+    case aNode.COMMENT_NODE:
+      return `comment node, "${aNode.data.replace(/\n/g, '\\n')}"`;
+    case aNode.ELEMENT_NODE:
+      return getElementDescription(SpecialPowers.unwrap(aNode));
+    default:
+      return "unknown node";
+  }
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function () {
+  let iter = createContentIterator();
+
+  /**
+   * Basic behavior tests of first(), last(), prev() and next() after initialized with an empty element.
+   */
+  document.body.innerHTML = "<div></div>";
+  let description = "Initialized with empty <div> as root node:";
+  iter.initWithRootNode(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, document.body.firstChild);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first()`);
+
+  iter.last();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling last() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling last()`);
+
+  iter.prev();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling prev() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling prev()`); // XXX Is this expected?
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling first() even after once done (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first() even after once done`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Basic behavior tests of first(), last(), prev() and next() after initialized with a range which selects empty element.
+   */
+  let range = document.createRange();
+  range.selectNode(document.body.firstChild);
+  description = "Initialized with range including only empty <div>:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first()`);
+
+  iter.last();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling last() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling last()`);
+
+  iter.prev();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling prev() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling prev()`); // XXX Is this expected?
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling first() even after once done (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first() even after once done`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Basic behavior tests of first(), last(), prev() and next() after initialized with positions which select empty element.
+   */
+  range.selectNode(document.body.firstChild);
+  description = "Initialized with positions including only empty <div>:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first()`);
+
+  iter.last();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling last() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling last()`);
+
+  iter.prev();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling prev() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling prev()`); // XXX Is this expected?
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling first() even after once done (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first() even after once done`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Tests to initializing with collapsed range in an empty element.
+   */
+  range = document.createRange();
+  range.collapse(document.body.firstChild, 0);
+  description = "Initialized with range collapsed in empty <div>:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Tests to initializing with collapsed range in an empty element.
+   */
+  description = "Initialized with a position in empty <div>:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         document.body.firstChild, 0, document.body.firstChild, 0);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Basic behavior tests of first(), last(), prev() and next() after initialized with the text element.
+   */
+  document.body.innerHTML = "<div>some text.</div>";
+  description = "Initialized with a text node as root node:";
+  iter.initWithRootNode(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, document.body.firstChild.firstChild);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be the text node after calling first()`);
+
+  iter.last();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node after calling last() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling last()`);
+
+  iter.prev();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling prev() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling prev()`); // XXX Is this expected?
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node after calling first() even after once done (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first() even after once done`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Basic behavior tests of first(), last(), prev() and next() after initialized with a range which selects the text node.
+   */
+  range = document.createRange();
+  range.selectNode(document.body.firstChild.firstChild);
+  description = "Initialized with range including only text node:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first()`);
+
+  iter.last();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node after calling last() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling last()`);
+
+  iter.prev();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling prev() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling prev()`); // XXX Is this expected?
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node after calling first() even after once done (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first() even after once done`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Basic behavior tests of first() and next() after initialized with positions which select the text node.
+   * XXX In this case, content iterator lists up the parent <div> element.  Not sure if this is intentional difference
+   *     from initWithRange().
+   */
+  range.selectNode(document.body.firstChild);
+  description = "Initialized with positions including only text node:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> element immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> element after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first()`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node after calling next() from first position (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling next() from first position`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() from second position (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next() from second position`);
+
+  /**
+   * Tests to initializing with collapsed range at start of a text node.
+   */
+  range = document.createRange();
+  range.collapse(document.body.firstChild.firstChild, 0);
+  description = "Initialized with range collapsed at start of text node:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Tests to initializing with collapsed range at start of a text node.
+   * XXX In this case, content iterator lists up the text node.  Not sure if this is intentional difference
+   *     from initWithRange().
+   */
+  description = "Initialized with a position at start of text node:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         document.body.firstChild.firstChild, 0, document.body.firstChild.firstChild, 0);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Tests to initializing with collapsed range at end of a text node.
+   */
+  range = document.createRange();
+  range.collapse(document.body.firstChild.firstChild, document.body.firstChild.firstChild.length);
+  description = "Initialized with range collapsed at end of text node:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Tests to initializing with collapsed range at end of a text node.
+   * XXX In this case, content iterator lists up the text node.  Not sure if this is intentional difference
+   *     from initWithRange().
+   */
+  description = "Initialized with a position at end of text node:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         document.body.firstChild.firstChild, document.body.firstChild.firstChild.length,
+                         document.body.firstChild.firstChild, document.body.firstChild.firstChild.length);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Tests to initializing with collapsed range at middle of a text node.
+   */
+  range = document.createRange();
+  range.collapse(document.body.firstChild.firstChild, document.body.firstChild.firstChild.length / 2);
+  description = "Initialized with range collapsed at end of text node:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Tests to initializing with collapsed range at middle of a text node.
+   * XXX In this case, content iterator lists up the text node.  Not sure if this is intentional difference
+   *     from initWithRange().
+   */
+  description = "Initialized with a position at end of text node:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         document.body.firstChild.firstChild, document.body.firstChild.firstChild.length / 2,
+                         document.body.firstChild.firstChild, document.body.firstChild.firstChild.length / 2);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Tests to initializing with a range selecting all text in a text node.
+   */
+  range = document.createRange();
+  range.setStart(document.body.firstChild.firstChild, 0);
+  range.setEnd(document.body.firstChild.firstChild, document.body.firstChild.firstChild.length);
+  description = "Initialized with range selecting all text in text node:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Tests to initializing with positions selecting all text in a text node.
+   */
+  description = "Initialized with positions selecting all text in text node:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         document.body.firstChild.firstChild, 0,
+                         document.body.firstChild.firstChild, document.body.firstChild.firstChild.length);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Basic tests with complicated tree.
+   */
+  function check(aIter, aExpectedResult, aDescription) {
+    if (aExpectedResult.length > 0) {
+      is(SpecialPowers.unwrap(aIter.currentNode), aExpectedResult[0],
+        `${aDescription}: currentNode should be the text node immediately after initialization (got: ${getNodeDescription(aIter.currentNode)}, expected: ${getNodeDescription(aExpectedResult[0])})`);
+      ok(!aIter.isDone, `${aDescription}: isDone shouldn't be true immediately after initialization`);
+
+      aIter.first();
+      is(SpecialPowers.unwrap(aIter.currentNode), aExpectedResult[0],
+        `${aDescription}: currentNode should be the text node after calling first() (got: ${getNodeDescription(aIter.currentNode)}, expected: ${getNodeDescription(aExpectedResult[0])})`);
+      ok(!aIter.isDone, `${aDescription}: isDone shouldn't be true after calling first()`);
+
+      for (let expected of aExpectedResult) {
+        is(SpecialPowers.unwrap(aIter.currentNode), expected,
+          `${aDescription}: currentNode should be the node (got: ${getNodeDescription(aIter.currentNode)}, expected: ${getNodeDescription(expected)})`);
+        ok(!aIter.isDone, `${aDescription}: isDone shouldn't be true when ${getNodeDescription(expected)} is expected`);
+        aIter.next();
+      }
+
+      is(SpecialPowers.unwrap(aIter.currentNode), null,
+        `${aDescription}: currentNode should be null after calling next() finally (got: ${getNodeDescription(aIter.currentNode)}`);
+      ok(aIter.isDone, `${aDescription}: isDone should be true after calling next() finally`);
+    } else {
+      is(SpecialPowers.unwrap(aIter.currentNode), null,
+        `${aDescription}: currentNode should be null immediately after initialization (got: ${getNodeDescription(aIter.currentNode)})`);
+      ok(aIter.isDone, `${aDescription}: isDone should be true immediately after initialization`);
+
+      aIter.first();
+      is(SpecialPowers.unwrap(aIter.currentNode), null,
+        `${aDescription}: currentNode should be null after calling first() (got: ${getNodeDescription(aIter.currentNode)})`);
+      ok(aIter.isDone, `${aDescription}: isDone should be true after calling first()`);
+    }
+  }
+
+  document.body.innerHTML = "<p>" +
+                              "Here is <b>bold</b> and <i><u>underlined and </u>italic </i><span>or no style text.</span><br>" +
+                            "</p>" +
+                            "<p>" +
+                              "Here is an &lt;input&gt; element: <input type=\"text\" value=\"default value\"><br>\n" +
+                              "and a &lt;textarea&gt; element: <textarea>text area's text node</textarea><br><br>\n" +
+                              "<!-- and here is comment node -->" +
+                            "</p>";
+
+  let expectedResult =
+    [document.body, // <body>
+     document.body.firstChild, // first <p>
+     document.body.firstChild.firstChild, // the first text node
+     document.body.firstChild.firstChild.nextSibling, // <b>
+     document.body.firstChild.firstChild.nextSibling.firstChild, // text in <b>
+     document.body.firstChild.firstChild.nextSibling.nextSibling, // text next to <b>
+     document.body.firstChild.firstChild.nextSibling.nextSibling.nextSibling, // <i>
+     document.body.firstChild.firstChild.nextSibling.nextSibling.nextSibling.firstChild, // <u>
+     document.body.firstChild.firstChild.nextSibling.nextSibling.nextSibling.firstChild.firstChild, // text in <u>
+     document.body.firstChild.firstChild.nextSibling.nextSibling.nextSibling.firstChild.nextSibling, // text next to <u>
+     document.body.firstChild.firstChild.nextSibling.nextSibling.nextSibling.nextSibling, // <span>
+     document.body.firstChild.firstChild.nextSibling.nextSibling.nextSibling.nextSibling.firstChild, // text in <span>
+     document.body.firstChild.firstChild.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling, // <br> next to <span>
+     document.body.firstChild.nextSibling, // second <p>
+     document.body.firstChild.nextSibling.firstChild, // the first text node in second <p>
+     document.body.firstChild.nextSibling.firstChild.nextSibling, // <input>
+     document.body.firstChild.nextSibling.firstChild.nextSibling.nextSibling, // <br> next to <input>
+     document.body.firstChild.nextSibling.firstChild.nextSibling.nextSibling.nextSibling, // text next to <input>
+     document.body.firstChild.nextSibling.firstChild.nextSibling.nextSibling.nextSibling.nextSibling, // <textarea>
+     document.body.firstChild.nextSibling.firstChild.nextSibling.nextSibling.nextSibling.nextSibling.firstChild, // text in <textarea>
+     document.body.firstChild.nextSibling.firstChild.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling, // <br> next to <textarea>
+     document.body.firstChild.nextSibling.firstChild.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling, // <br> next to <br>
+     document.body.firstChild.nextSibling.firstChild.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling, // text next to <br>
+     document.body.firstChild.nextSibling.firstChild.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling.nextSibling] // comment
+
+  iter.initWithRootNode(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, document.body);
+  check(iter, expectedResult, "Initialized with the <body> as root element");
+
+  /**
+   * Selects the <body> with a range.
+   */
+  range = document.createRange();
+  range.selectNode(document.body);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter, expectedResult, "Initialized with range selecting the <body>");
+
+  /**
+   * Selects the <body> with positions.
+   */
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset, range.endContainer, range.endOffset);
+  check(iter, expectedResult, "Initialized with positions selecting the <body>");
+
+  /**
+   * Selects all children in the <body> with a range.
+   */
+  expectedResult.shift(); // <body> shouldn't be listed up.
+  range = document.createRange();
+  range.selectNodeContents(document.body);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter, expectedResult, "Initialized with range selecting all children in the <body>");
+
+  /**
+   * Selects all children in the <body> with positions.
+   */
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset, range.endContainer, range.endOffset);
+  check(iter, expectedResult, "Initialized with positions selecting all children in the <body>");
+
+  /**
+   * range/positions around elements.
+   */
+  document.body.innerHTML = "abc<b>def</b><i>ghi</i>jkl";
+  range = document.createRange();
+
+  range.setStart(document.body.firstChild, 0);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild, // text before <b>
+         document.body.firstChild.nextSibling, // <b>
+         document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with range selecting '[abc<b>de]f'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild, // text before <b>
+         document.body.firstChild.nextSibling, // <b>
+         document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting '[abc<b>de]f'");
+
+  range.setStart(document.body.firstChild, 2);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild, // text before <b>
+         document.body.firstChild.nextSibling, // <b>
+         document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with range selecting 'ab[c<b>de]f'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild, // text before <b>
+         document.body.firstChild.nextSibling, // <b>
+         document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting 'ab[c<b>de]f'");
+
+  range.setStart(document.body.firstChild, 3);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild, // text before <b>
+         document.body.firstChild.nextSibling, // <b>
+         document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with range selecting 'abc[<b>de]f'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild, // text before <b>
+         document.body.firstChild.nextSibling, // <b>
+         document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting 'abc[<b>de]f'");
+
+  range.setStart(document.body, 1);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling, // <b>
+         document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with range selecting 'abc{<b>de]f'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling, // <b>
+         document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting 'abc{<b>de]f'");
+
+  range.setStart(document.body.firstChild.nextSibling, 0);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with range selecting '<b>{de]f'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting '<b>{de]f'");
+
+  range.setStart(document.body.firstChild.nextSibling, 0);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 3);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with range selecting '<b>{def]</b>'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting '<b>{def]</b>'");
+
+  range.setStart(document.body.firstChild.nextSibling, 0);
+  range.setEnd(document.body.firstChild.nextSibling, 1);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with range selecting '<b>{def}</b>'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting '<b>{def}</b>'");
+
+  range.setStart(document.body.firstChild.nextSibling, 0);
+  range.setEnd(document.body, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with range selecting '<b>{def</b>}<i>ghi'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting '<b>{def</b>}<i>ghi'");
+
+  range.setStart(document.body.firstChild.nextSibling.firstChild, 3);
+  range.setEnd(document.body, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with range selecting '<b>def[</b>}<i>ghi'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting '<b>def[</b>}<i>ghi'");
+
+  range.setStart(document.body.firstChild.nextSibling, 1);
+  range.setEnd(document.body, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter, [],
+        "Initialized with range selecting '<b>def{</b>}<i>ghi'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter, [],
+        "Initialized with positions selecting '<b>def{</b>}<i>ghi'");
+
+  range.setStart(document.body.firstChild.nextSibling, 1);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling], // <i>
+        "Initialized with range selecting '<b>def{</b><i>}ghi'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling], // <i>
+        "Initialized with positions selecting '<b>def{</b><i>}ghi'");
+
+  range.setStart(document.body.firstChild.nextSibling, 1);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling.firstChild, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling, // <i>
+         document.body.firstChild.nextSibling.nextSibling.firstChild], // text in <i>
+        "Initialized with range selecting '<b>def{</b><i>]ghi'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling, // <i>
+         document.body.firstChild.nextSibling.nextSibling.firstChild], // text in <i>
+        "Initialized with positions selecting '<b>def{</b><i>]ghi'");
+
+  range.setStart(document.body.firstChild.nextSibling.nextSibling, 0);
+  range.setEnd(document.body, 3);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling.firstChild], // text in <i>
+        "Initialized with range selecting '<i>{ghi</i>}jkl'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling.firstChild], // text in <i>
+        "Initialized with positions selecting '<i>{ghi</i>}jkl'");
+
+  range.setStart(document.body.firstChild.nextSibling.nextSibling.firstChild, 3);
+  range.setEnd(document.body, 3);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling.firstChild], // text in <i>
+        "Initialized with range selecting '<i>ghi[</i>}jkl'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling.firstChild], // text in <i>
+        "Initialized with positions selecting '<i>ghi[</i>}jkl'");
+
+  range.setStart(document.body.firstChild.nextSibling.nextSibling, 1);
+  range.setEnd(document.body, 3);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter, [],
+        "Initialized with range selecting '<i>ghi{</i>}jkl'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter, [],
+        "Initialized with positions selecting '<i>ghi{</i>}jkl'");
+
+  range.setStart(document.body.firstChild.nextSibling.nextSibling, 1);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling.nextSibling], // text after <i>
+        "Initialized with range selecting '<i>ghi{</i>]jkl'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling.nextSibling], // text after <i>
+        "Initialized with positions selecting '<i>ghi{</i>]jkl'");
+
+  /**
+   * range/positions around <br> elements.
+   */
+  document.body.innerHTML = "abc<br>def";
+  range = document.createRange();
+  range.setStart(document.body.firstChild, 3);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild, // text before <br>
+         document.body.firstChild.nextSibling, // <br>
+         document.body.firstChild.nextSibling.nextSibling], // text after <br>
+        "Initialized with range selecting 'abc[<br>]def'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild, // text before <br>
+         document.body.firstChild.nextSibling, // <br>
+         document.body.firstChild.nextSibling.nextSibling], // text after <br>
+        "Initialized with positions selecting 'abc[<br>]def'");
+
+  range.setStart(document.body, 1);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling, // <br>
+         document.body.firstChild.nextSibling.nextSibling], // text after <br>
+        "Initialized with range selecting 'abc{<br>]def'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling, // <br>
+         document.body.firstChild.nextSibling.nextSibling], // text after <br>
+        "Initialized with positions selecting 'abc{<br>]def'");
+
+  range.setStart(document.body.firstChild.nextSibling, 0);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling, // <br>
+         document.body.firstChild.nextSibling.nextSibling], // text after <br>
+        "Initialized with range selecting 'abc{<br>]def' (starting in <br>)");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling, // <br>
+         document.body.firstChild.nextSibling.nextSibling], // text after <br>
+        "Initialized with positions selecting 'abc{<br>]def' (starting in <br>)");
+
+  range.setStart(document.body, 1);
+  range.setEnd(document.body, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling], // <br>
+        "Initialized with range selecting 'abc{<br>}def'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling], // <br>
+        "Initialized with positions selecting 'abc{<br>}def'");
+
+  range.setStart(document.body.firstChild, 3);
+  range.setEnd(document.body.firstChild.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild], // text before <br>
+        "Initialized with range selecting 'abc[}<br>def' (ending in <br>)");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.PRE_ORDER_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild], // text before <br>
+        "Initialized with positions selecting 'abc[}<br>def' (ending in <br>)");
+
+  finish();
+});
+</script>
+</head>
+<body></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_content_iterator_subtree.html
@@ -0,0 +1,690 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for content subtree iterator</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<script>
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+function finish() {
+  // The SimpleTest may require usual elements in the template, but they shouldn't be during test.
+  // So, let's create them at end of the test.
+  document.body.innerHTML = '<div id="display"></div><div id="content"></div><pre id="test"></pre>';
+  SimpleTest.finish();
+}
+
+function createContentIterator() {
+  return Cc["@mozilla.org/scriptable-content-iterator;1"]
+      .createInstance(Ci.nsIScriptableContentIterator);
+}
+
+function getNodeDescription(aNode) {
+  if (aNode === undefined) {
+    return "undefine";
+  }
+  if (aNode === null) {
+    return "null";
+  }
+  function getElementDescription(aElement) {
+    if (aElement.tagName === "BR") {
+      if (aElement.previousSibling) {
+        return `<br> element after ${getNodeDescription(aElement.previousSibling)}`;
+      }
+      return `<br> element in ${getElementDescription(aElement.parentElement)}`;
+    }
+    let hasHint = aElement == document.body;
+    let tag = `<${aElement.tagName.toLowerCase()}`;
+    if (aElement.getAttribute("id")) {
+      tag += ` id="${aElement.getAttribute("id")}"`;
+      hasHint = true;
+    }
+    if (aElement.getAttribute("class")) {
+      tag += ` class="${aElement.getAttribute("class")}"`;
+      hasHint = true;
+    }
+    if (aElement.getAttribute("type")) {
+      tag += ` type="${aElement.getAttribute("type")}"`;
+    }
+    if (aElement.getAttribute("name")) {
+      tag += ` name="${aElement.getAttribute("name")}"`;
+    }
+    if (aElement.getAttribute("value")) {
+      tag += ` value="${aElement.getAttribute("value")}"`;
+      hasHint = true;
+    }
+    if (aElement.getAttribute("style")) {
+      tag += ` style="${aElement.getAttribute("style")}"`;
+      hasHint = true;
+    }
+    if (hasHint) {
+      return tag + ">";
+    }
+    return `${tag}> in ${getElementDescription(aElement.parentElement)}`;
+  }
+  switch (aNode.nodeType) {
+    case aNode.TEXT_NODE:
+      return `text node, "${aNode.wholeText.replace(/\n/g, '\\n')}"`;
+    case aNode.COMMENT_NODE:
+      return `comment node, "${aNode.data.replace(/\n/g, '\\n')}"`;
+    case aNode.ELEMENT_NODE:
+      return getElementDescription(SpecialPowers.unwrap(aNode));
+    default:
+      return "unknown node";
+  }
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function () {
+  let iter = createContentIterator();
+
+  /**
+   * FYI: nsContentSubtreeIterator does not support initWithRootNode() nor positionAt().
+   */
+
+  /**
+   * Basic behavior tests of first(), last(), prev() and next() after initialized with a range which selects empty element.
+   */
+  document.body.innerHTML = "<div></div>";
+  let range = document.createRange();
+  range.selectNode(document.body.firstChild);
+  let description = "Initialized with range including only empty <div>:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first()`);
+
+  iter.last();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling last() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling last()`);
+
+  iter.prev();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling prev() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling prev()`); // XXX Is this expected?
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling first() even after once done (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first() even after once done`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Basic behavior tests of first(), last(), prev() and next() after initialized with positions which select empty element.
+   */
+  range.selectNode(document.body.firstChild);
+  description = "Initialized with positions including only empty <div>:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first()`);
+
+  iter.last();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling last() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling last()`);
+
+  iter.prev();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling prev() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling prev()`); // XXX Is this expected?
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> after calling first() even after once done (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first() even after once done`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Tests to initializing with collapsed range in an empty element.
+   */
+  range = document.createRange();
+  range.collapse(document.body.firstChild, 0);
+  description = "Initialized with range collapsed in empty <div>:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Tests to initializing with collapsed range in an empty element.
+   */
+  description = "Initialized with a position in empty <div>:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         document.body.firstChild, 0, document.body.firstChild, 0);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Basic behavior tests of first(), last(), prev() and next() after initialized with a range which selects the text node.
+   */
+  document.body.innerHTML = "<div>some text.</div>";
+  range = document.createRange();
+  range.selectNode(document.body.firstChild.firstChild);
+  description = "Initialized with range including only text node:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first()`);
+
+  iter.last();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node after calling last() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling last()`);
+
+  iter.prev();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling prev() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling prev()`); // XXX Is this expected?
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild.firstChild,
+    `${description} currentNode should be the text node after calling first() even after once done (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first() even after once done`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next()`);
+
+  /**
+   * Basic behavior tests of first() and next() after initialized with positions which select the text node.
+   * XXX In this case, content iterator lists up the parent <div> element.  Not sure if this is intentional difference
+   *     from initWithRange().
+   */
+  range.selectNode(document.body.firstChild);
+  description = "Initialized with positions including only text node:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> element immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), document.body.firstChild,
+    `${description} currentNode should be the <div> element after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(!iter.isDone, `${description} isDone shouldn't be true after calling first()`);
+
+  iter.next();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null after calling next() from first position (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true after calling next() from first position`);
+
+  /**
+   * Tests to initializing with collapsed range at start of a text node.
+   */
+  range = document.createRange();
+  range.collapse(document.body.firstChild.firstChild, 0);
+  description = "Initialized with range collapsed at start of text node:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Tests to initializing with collapsed range at start of a text node.
+   */
+  description = "Initialized with a position at start of text node:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         document.body.firstChild.firstChild, 0, document.body.firstChild.firstChild, 0);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Tests to initializing with collapsed range at end of a text node.
+   */
+  range = document.createRange();
+  range.collapse(document.body.firstChild.firstChild, document.body.firstChild.firstChild.length);
+  description = "Initialized with range collapsed at end of text node:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Tests to initializing with collapsed range at end of a text node.
+   */
+  description = "Initialized with a position at end of text node:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         document.body.firstChild.firstChild, document.body.firstChild.firstChild.length,
+                         document.body.firstChild.firstChild, document.body.firstChild.firstChild.length);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Tests to initializing with collapsed range at middle of a text node.
+   */
+  range = document.createRange();
+  range.collapse(document.body.firstChild.firstChild, document.body.firstChild.firstChild.length / 2);
+  description = "Initialized with range collapsed at end of text node:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Tests to initializing with collapsed range at middle of a text node.
+   */
+  description = "Initialized with a position at end of text node:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         document.body.firstChild.firstChild, document.body.firstChild.firstChild.length / 2,
+                         document.body.firstChild.firstChild, document.body.firstChild.firstChild.length / 2);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Tests to initializing with a range selecting all text in a text node.
+   */
+  range = document.createRange();
+  range.setStart(document.body.firstChild.firstChild, 0);
+  range.setEnd(document.body.firstChild.firstChild, document.body.firstChild.firstChild.length);
+  description = "Initialized with range selecting all text in text node:";
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Tests to initializing with positions selecting all text in a text node.
+   */
+  description = "Initialized with positions selecting all text in text node:";
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         document.body.firstChild.firstChild, 0,
+                         document.body.firstChild.firstChild, document.body.firstChild.firstChild.length);
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null immediately after initialization (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true immediately after initialization`);
+
+  iter.first();
+  is(SpecialPowers.unwrap(iter.currentNode), null,
+    `${description} currentNode should be null even after calling first() (got: ${getNodeDescription(iter.currentNode)})`);
+  ok(iter.isDone, `${description} isDone should be true even after calling first()`);
+
+  /**
+   * Basic tests with complicated tree.
+   */
+  function check(aIter, aExpectedResult, aDescription) {
+    if (aExpectedResult.length > 0) {
+      is(SpecialPowers.unwrap(aIter.currentNode), aExpectedResult[0],
+        `${aDescription}: currentNode should be the text node immediately after initialization (got: ${getNodeDescription(aIter.currentNode)}, expected: ${getNodeDescription(aExpectedResult[0])})`);
+      ok(!aIter.isDone, `${aDescription}: isDone shouldn't be true immediately after initialization`);
+
+      aIter.first();
+      is(SpecialPowers.unwrap(aIter.currentNode), aExpectedResult[0],
+        `${aDescription}: currentNode should be the text node after calling first() (got: ${getNodeDescription(aIter.currentNode)}, expected: ${getNodeDescription(aExpectedResult[0])})`);
+      ok(!aIter.isDone, `${aDescription}: isDone shouldn't be true after calling first()`);
+
+      for (let expected of aExpectedResult) {
+        is(SpecialPowers.unwrap(aIter.currentNode), expected,
+          `${aDescription}: currentNode should be the node (got: ${getNodeDescription(aIter.currentNode)}, expected: ${getNodeDescription(expected)})`);
+        ok(!aIter.isDone, `${aDescription}: isDone shouldn't be true when ${getNodeDescription(expected)} is expected`);
+        aIter.next();
+      }
+
+      is(SpecialPowers.unwrap(aIter.currentNode), null,
+        `${aDescription}: currentNode should be null after calling next() finally (got: ${getNodeDescription(aIter.currentNode)}`);
+      ok(aIter.isDone, `${aDescription}: isDone should be true after calling next() finally`);
+    } else {
+      is(SpecialPowers.unwrap(aIter.currentNode), null,
+        `${aDescription}: currentNode should be null immediately after initialization (got: ${getNodeDescription(aIter.currentNode)})`);
+      ok(aIter.isDone, `${aDescription}: isDone should be true immediately after initialization`);
+
+      aIter.first();
+      is(SpecialPowers.unwrap(aIter.currentNode), null,
+        `${aDescription}: currentNode should be null after calling first() (got: ${getNodeDescription(aIter.currentNode)})`);
+      ok(aIter.isDone, `${aDescription}: isDone should be true after calling first()`);
+    }
+  }
+
+  document.body.innerHTML = "<p>" +
+                              "Here is <b>bold</b> and <i><u>underlined and </u>italic </i><span>or no style text.</span><br>" +
+                            "</p>" +
+                            "<p>" +
+                              "Here is an &lt;input&gt; element: <input type=\"text\" value=\"default value\"><br>\n" +
+                              "and a &lt;textarea&gt; element: <textarea>text area's text node</textarea><br><br>\n" +
+                              "<!-- and here is comment node -->" +
+                            "</p>";
+
+  /**
+   * Selects the <body> with a range.
+   */
+  range = document.createRange();
+  range.selectNode(document.body);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter, [document.body], "Initialized with range selecting the <body>");
+
+  /**
+   * Selects the <body> with positions.
+   */
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset, range.endContainer, range.endOffset);
+  check(iter, [document.body], "Initialized with positions selecting the <body>");
+
+  /**
+   * Selects all children in the <body> with a range.
+   */
+  range = document.createRange();
+  range.selectNodeContents(document.body);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild, // first <p>
+         document.body.firstChild.nextSibling], // second <p>
+        "Initialized with range selecting all children in the <body>");
+
+  /**
+   * Selects all children in the <body> with positions.
+   */
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset, range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild, // first <p>
+         document.body.firstChild.nextSibling], // second <p>
+        "Initialized with positions selecting all children in the <body>");
+
+  /**
+   * range/positions around elements.
+   */
+  document.body.innerHTML = "abc<b>def</b><i>ghi</i>jkl";
+  range = document.createRange();
+
+  range.setStart(document.body.firstChild, 0);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter, [], "Initialized with range selecting '[abc<b>de]f'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter, [], "Initialized with positions selecting '[abc<b>de]f'");
+
+  range.setStart(document.body.firstChild, 2);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter,[], "Initialized with range selecting 'ab[c<b>de]f'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter, [], "Initialized with positions selecting 'ab[c<b>de]f'");
+
+  range.setStart(document.body.firstChild, 3);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter, [], "Initialized with range selecting 'abc[<b>de]f'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter, [], "Initialized with positions selecting 'abc[<b>de]f'");
+
+  range.setStart(document.body, 1);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter, [], "Initialized with range selecting 'abc{<b>de]f'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter, [], "Initialized with positions selecting 'abc{<b>de]f'");
+
+  range.setStart(document.body.firstChild.nextSibling, 0);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter, [], "Initialized with range selecting '<b>{de]f'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter, [], "Initialized with positions selecting '<b>{de]f'");
+
+  range.setStart(document.body.firstChild.nextSibling, 0);
+  range.setEnd(document.body.firstChild.nextSibling.firstChild, 3);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter, [], "Initialized with range selecting '<b>{def]</b>'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter, [], "Initialized with positions selecting '<b>{def]</b>'");
+
+  range.setStart(document.body.firstChild.nextSibling, 0);
+  range.setEnd(document.body.firstChild.nextSibling, 1);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with range selecting '<b>{def}</b>'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting '<b>{def}</b>'");
+
+  range.setStart(document.body.firstChild.nextSibling, 0);
+  range.setEnd(document.body, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+       "Initialized with range selecting '<b>{def</b>}<i>ghi'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.firstChild], // text in <b>
+        "Initialized with positions selecting '<b>{def</b>}<i>ghi'");
+
+  range.setStart(document.body.firstChild.nextSibling.firstChild, 3);
+  range.setEnd(document.body, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter, [], "Initialized with range selecting '<b>def[</b>}<i>ghi'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter, [], "Initialized with positions selecting '<b>def[</b>}<i>ghi'");
+
+  range.setStart(document.body.firstChild.nextSibling, 1);
+  range.setEnd(document.body, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter, [], "Initialized with range selecting '<b>def{</b>}<i>ghi'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter, [], "Initialized with positions selecting '<b>def{</b>}<i>ghi'");
+
+  range.setStart(document.body.firstChild.nextSibling, 1);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter, [], "Initialized with range selecting '<b>def{</b><i>}ghi'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter, [], "Initialized with positions selecting '<b>def{</b><i>}ghi'");
+
+  range.setStart(document.body.firstChild.nextSibling, 1);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling.firstChild, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter, [], "Initialized with range selecting '<b>def{</b><i>]ghi'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter, [], "Initialized with positions selecting '<b>def{</b><i>]ghi'");
+
+  range.setStart(document.body.firstChild.nextSibling.nextSibling, 0);
+  range.setEnd(document.body, 3);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling.firstChild], // text in <i>
+        "Initialized with range selecting '<i>{ghi</i>}jkl'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling.nextSibling.firstChild], // text in <i>
+        "Initialized with positions selecting '<i>{ghi</i>}jkl'");
+
+  range.setStart(document.body.firstChild.nextSibling.nextSibling.firstChild, 3);
+  range.setEnd(document.body, 3);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter, [], "Initialized with range selecting '<i>ghi[</i>}jkl'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter, [], "Initialized with positions selecting '<i>ghi[</i>}jkl'");
+
+  range.setStart(document.body.firstChild.nextSibling.nextSibling, 1);
+  range.setEnd(document.body, 3);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter, [], "Initialized with range selecting '<i>ghi{</i>}jkl'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter, [], "Initialized with positions selecting '<i>ghi{</i>}jkl'");
+
+  range.setStart(document.body.firstChild.nextSibling.nextSibling, 1);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter, [], "Initialized with range selecting '<i>ghi{</i>]jkl'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter, [], "Initialized with positions selecting '<i>ghi{</i>]jkl'");
+
+  /**
+   * range/positions around <br> elements.
+   */
+  document.body.innerHTML = "abc<br>def";
+  range = document.createRange();
+  range.setStart(document.body.firstChild, 3);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling], // <br>
+        "Initialized with range selecting 'abc[<br>]def'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling], // <br>
+        "Initialized with positions selecting 'abc[<br>]def'");
+
+  range.setStart(document.body, 1);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling], // <br>
+        "Initialized with range selecting 'abc{<br>]def'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling], // <br>
+        "Initialized with positions selecting 'abc{<br>]def'");
+
+  range.setStart(document.body.firstChild.nextSibling, 0);
+  range.setEnd(document.body.firstChild.nextSibling.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter, [], "Initialized with range selecting 'abc{<br>]def' (starting in <br>)");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter, [], "Initialized with positions selecting 'abc{<br>]def' (starting in <br>)");
+
+  range.setStart(document.body, 1);
+  range.setEnd(document.body, 2);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter,
+        [document.body.firstChild.nextSibling], // <br>
+        "Initialized with range selecting 'abc{<br>}def'");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter,
+        [document.body.firstChild.nextSibling], // <br>
+        "Initialized with positions selecting 'abc{<br>}def'");
+
+  range.setStart(document.body.firstChild, 3);
+  range.setEnd(document.body.firstChild.nextSibling, 0);
+  iter.initWithRange(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR, range);
+  check(iter, [], "Initialized with range selecting 'abc[}<br>def' (ending in <br>)");
+  iter.initWithPositions(Ci.nsIScriptableContentIterator.SUBTREE_ITERATOR,
+                         range.startContainer, range.startOffset,
+                         range.endContainer, range.endOffset);
+  check(iter, [], "Initialized with positions selecting 'abc[}<br>def' (ending in <br>)");
+
+  finish();
+});
+</script>
+</head>
+<body></body>
+</html>