<!DOCTYPE HTML>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=348681
-->

<head>
  <title>Test for Bug 348681</title>
  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>

<body onload="loaded()">
  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=348681">Mozilla Bug 348681</a>
  <p id="display"></p>
  <div id="content" style="display: none">
  </div>

  <pre id="test">
    <script type="application/javascript">

      /** Test for Bug 348681 **/

      SimpleTest.waitForExplicitFinish();

      function loaded() {
        SpecialPowers.pushPrefEnv({"set": [["dom.testing.selection.GetRangesForInterval", true]]}, doTest);
      }

      var rangeChecker = {
        ranges: [],

        reset: function() {
          this.ranges = [];
        },

        check: function(testID, selection) {
          is(selection.rangeCount, this.ranges.length,
             "Test "+testID+": correct number of ranges in selection");
          var rangesMatch = true;
          var foundMsg = "Found ";
          var expectedMsg = "Expected ";
          var maxIndex = Math.max(selection.rangeCount, this.ranges.length);
          for (var x = 0; x < maxIndex; x++) {
            var expect = null;
            if (x < this.ranges.length)
              expect = this.ranges[x];

            var found = null;
            if (x < selection.rangeCount)
              found = selection.getRangeAt(x);

            if (found) {
              foundMsg +="["+found.startOffset+","+found.endOffset+"] ";
            }
            if (expect) {
              expectedMsg +="["+expect.start+","+expect.end+"] ";
            }

            if (found && expect) {
              if (found.startContainer != expect.node ||
                  found.endContainer != expect.node ||
                  found.startOffset != expect.start ||
                  found.endOffset != expect.end)
                rangesMatch = false;
            } else {
              rangesMatch = false;
            }
          }
          var okMsg = "Test "+testID+": correct ranges in selection.";
          okMsg = okMsg + foundMsg + expectedMsg;
          ok(rangesMatch, okMsg);
        },

        addExpected: function(node, start, end) {
          var expected = {};
          expected.node = node;
          expected.start = start;
          expected.end = end;
          this.ranges[this.ranges.length] = expected;
          this.ranges.sort(function(a,b) {return a.start - b.start;});
        }
      }

      var intervalChecker = {
        ranges: [],

        reset: function() {
          this.ranges = [];
        },

        check: function(testID, testArr) {
          is(testArr.length, this.ranges.length,
             "Test "+testID+": correct number of ranges for interval");
          var rangesMatch = true;
          var foundMsg = "Found ";
          var expectedMsg = "Expected ";
          var maxIndex = Math.max(testArr.length, this.ranges.length);
          for (var x = 0; x < maxIndex; x++) {
            var expect = null;
            if (x < this.ranges.length)
              expect = this.ranges[x];

            var found = null;
            if (x < testArr.length) {
              // testArr may contain the results coming from
              // nsISelectionPrivate.GetRangesForInterval(), which are
              // wrappers for the underlying objects, therefore we may
              // need to unwrap them before comparing them with the
              // expected objects.
              found = SpecialPowers.unwrap(testArr[x]);
            }

            if (found) {
              foundMsg +="["+found.startOffset+","+found.endOffset+"] ";
            }
            if (expect) {
              expectedMsg +="["+expect.start+","+expect.end+"] ";
            }

            if (found && expect) {
              if (found.startContainer != expect.node ||
                  found.endContainer != expect.node ||
                  found.startOffset != expect.start ||
                  found.endOffset != expect.end)
                rangesMatch = false;
            } else {
              rangesMatch = false;
            }
          }
          var okMsg = "Test "+testID+": correct ranges for interval.";
          okMsg = okMsg + foundMsg + expectedMsg;
          ok(rangesMatch, okMsg);
        },

        addExpected: function(node, start, end) {
          var expected = {};
          expected.node = node;
          expected.start = start;
          expected.end = end;
          this.ranges[this.ranges.length] = expected;
          this.ranges.sort(function(a,b) {return a.start - b.start;});
        }
      }

      function doTest() {
       var testNode = document.getElementById("testparagraph").firstChild;

       var selection = window.getSelection();
       selection.removeAllRanges();
       ok(selection.rangeCount == 0, "Test 0 - No selections so far");
       
       // Test 1. Add a single range, to ensure we've not broken anything.
       var range1 = document.createRange();
       range1.setStart(testNode, 0);
       range1.setEnd(testNode, 5);
       selection.addRange(range1);
       rangeChecker.addExpected(testNode, 0, 5);
       rangeChecker.check(1, selection);

       // Test 2. Add a non-overlapping range, to ensure it gets added too.
       var range2 = document.createRange();
       range2.setStart(testNode, 8);
       range2.setEnd(testNode, 10);
       selection.addRange(range2);
       rangeChecker.addExpected(testNode, 8, 10);
       rangeChecker.check(2, selection);

       // Test 3. Add the same range again. This should silently succeed.
       selection.addRange(range2);
       rangeChecker.check(3, selection);

       // Test 4. Add a range that is left-adjacent to an existing range.
       var range3 = document.createRange();
       range3.setStart(testNode, 6);
       range3.setEnd(testNode, 8);
       selection.addRange(range3);
       rangeChecker.addExpected(testNode, 6, 8);
       rangeChecker.check(4, selection);

       // Test 5. Add a range that is right-adjacent to an existing range.
       selection.removeRange(range2);
       selection.addRange(range2);
       rangeChecker.check(5, selection);

       // Test 6. Add a range, add a second range that overlaps it.
       rangeChecker.reset();
       selection.removeAllRanges();
       selection.addRange(range2);
       var range4 = document.createRange();
       range4.setStart(testNode, 9);
       range4.setEnd(testNode, 11);
       selection.addRange(range4);
       rangeChecker.addExpected(testNode, 8, 9);
       rangeChecker.addExpected(testNode, 9, 11);
       rangeChecker.check(6, selection);

       // Test 7. Add a range, and this time a left-hand overlap.
       rangeChecker.reset();
       selection.removeAllRanges();
       var range5 = document.createRange();
       range5.setStart(testNode, 5);
       range5.setEnd(testNode, 7);
       selection.addRange(range3);
       selection.addRange(range5);
       rangeChecker.addExpected(testNode, 5, 7);
       rangeChecker.addExpected(testNode, 7, 8);
       rangeChecker.check(7, selection);

       // Test 8. Add a range, and add a second range wholly contained
       // within it.
       rangeChecker.reset();
       selection.removeAllRanges();
       var range6 = document.createRange();
       range6.setStart(testNode, 0);
       range6.setEnd(testNode, 10);
       selection.addRange(range6);
       selection.addRange(range5);
       rangeChecker.addExpected(testNode, 0, 5);
       rangeChecker.addExpected(testNode, 5, 7);
       rangeChecker.addExpected(testNode, 7, 10);
       rangeChecker.check(8, selection);

       // Test 9. Add a range that will wholly contain some existing ranges.
       rangeChecker.reset();
       selection.addRange(range6);
       rangeChecker.addExpected(testNode, 0, 10);
       rangeChecker.check(9, selection);

       // Test 10. This time with the last range being a partial overlap.
       selection.removeAllRanges();
       selection.addRange(range1);
       selection.addRange(range3);
       selection.addRange(range4);
       selection.addRange(range6);
       rangeChecker.addExpected(testNode, 10, 11);
       rangeChecker.check(10, selection);

       // Test 11. Check we can add a collapsed range without problem.
       selection.removeAllRanges();
       rangeChecker.reset();
       range1.collapse(true);
       selection.addRange(range1);
       rangeChecker.addExpected(testNode, 0, 0);
       rangeChecker.check(11, selection);

       // Test 12. Check we can add a collapsed range twice without a problem.
       // Part 1 - No other ranges present.
       selection.addRange(range1);
       rangeChecker.check(12, selection);

       // Test 13. Check we can add a collapsed range twice without problem.
       // Part 2 - Collapsed range is before all existing ranges.
       selection.removeAllRanges();
       rangeChecker.reset();
       selection.addRange(range2);
       selection.addRange(range1);
       selection.addRange(range1);
       rangeChecker.addExpected(testNode, 0, 0);
       rangeChecker.addExpected(testNode, 8, 10);
       rangeChecker.check(13, selection);

       // Test 14. Check we can add a collapsed range twice without problem.
       // Part 3 - Collapsed range is after all existing ranges.
       selection.removeAllRanges();
       rangeChecker.reset();
       selection.addRange(range3);
       range4.collapse(false);
       selection.addRange(range3);
       selection.addRange(range4);
       rangeChecker.addExpected(testNode, 6, 8);
       rangeChecker.addExpected(testNode, 11, 11);
       rangeChecker.check(14, selection);

       // Test 15. Check that when adding a range where after overlap
       // adjustment an exisiting range would collapse that the collapsed range
       // is removed - part 1 (LHS).
       selection.removeAllRanges();
       rangeChecker.reset();
       selection.addRange(range2);
       var range7 = document.createRange();
       range7.setStart(testNode, 6);
       range7.setEnd(testNode, 10);
       selection.addRange(range7);
       rangeChecker.addExpected(testNode, 6, 10);
       rangeChecker.check(15, selection);

       // Test 16. Check that when adding a range where after overlap
       // adjustment an exisiting range would collapse that the collapsed range
       // is removed - part 2 (RHS).
       selection.removeAllRanges();
       selection.addRange(range3);
       selection.addRange(range7);
       rangeChecker.check(16, selection);

       // Test 17. Check GetRangesForInterval returns correct results.
       // Part 1 - Test interval matches a range, adjacency not allowed.
       selection.removeAllRanges();
       selection.addRange(range2);
       selection.addRange(range3);
       var range8 = document.createRange();
       range8.setStart(testNode, 10);
       range8.setEnd(testNode, 12);
       selection.addRange(range8);
       intervalChecker.reset();
       intervalChecker.addExpected(testNode, 8, 10);
       var privSel = SpecialPowers.wrap(selection)
                                  .QueryInterface(SpecialPowers.Ci.nsISelectionPrivate);
       ok(privSel, "Test 17 - QIed to instance of nsISelection2 interface");
       var results = privSel.GetRangesForInterval(testNode, 8, testNode, 10,
                                                  false);
       intervalChecker.check(17, results);

       // Test 18. Check GetRangesForInterval returns correct results.
       // Part 2 - Test interval matches a range, adjacent ranges allowed.
       intervalChecker.addExpected(testNode, 6, 8);
       intervalChecker.addExpected(testNode, 10, 12);
       results = privSel.GetRangesForInterval(testNode, 8, testNode, 10,
                                              true);
       intervalChecker.check(18, results);

       // Test 19. Check GetRangesForInterval returns correct results.
       // Part 3 - Test interval not selected.
       intervalChecker.reset();
       results = privSel.GetRangesForInterval(testNode, 14, testNode, 16,
                                              true);
       intervalChecker.check(19, results);

       // Test 20. Check GetRangesForInterval returns correct results.
       // Part 4 - Test interval is not equal to, and entirely contained in
       //          an existing range.
       selection.removeAllRanges();
       selection.addRange(range6);
       intervalChecker.reset();
       intervalChecker.addExpected(testNode, 0, 10);
       results = privSel.GetRangesForInterval(testNode, 5, testNode, 7,
                                              true);
       intervalChecker.check(20, results);

       // Test 21. Check GetRangesForInterval returns correct results.
       // Part 5 - Test interval is not equal to, and entirely contains an
       //          existing range.
       selection.removeAllRanges();
       selection.addRange(range3);
       intervalChecker.reset();
       intervalChecker.addExpected(testNode, 6, 8);
       results = privSel.GetRangesForInterval(testNode, 5, testNode, 9,
                                              true);
       intervalChecker.check(21, results);

       // Test 22. Check GetRangesForInterval returns correct results.
       // Part 6 - Test interval is end-adjacent to range at position 0 in
       //          the range array, and adjacencies permitted.
       selection.removeAllRanges();
       selection.addRange(range2);
       intervalChecker.reset();
       intervalChecker.addExpected(testNode, 8, 10);
       results = privSel.GetRangesForInterval(testNode, 6, testNode, 8,
                                              true);
       intervalChecker.check(22, results);

       // Test 23. Check GetRangesForInterval returns correct results.
       // Part 7 - Test interval is end-adjacent to range at position 0 in
       //          the range array, and adjacencies not permitted.
       intervalChecker.reset();
       results = privSel.GetRangesForInterval(testNode, 6, testNode, 8,
                                              false);
       intervalChecker.check(23, results);

       // Test 24. Check GetRangesForInterval returns correct results.
       // Part 8 - Test interval is start-adjacent to last range in array,
       //          and adjacencies permitted.
       intervalChecker.addExpected(testNode, 8, 10);
       results = privSel.GetRangesForInterval(testNode, 10, testNode, 12,
                                              true);
       intervalChecker.check(24, results);

       // Test 25. Check GetRangesForInterval returns correct results.
       // Part 9 - Test interval is start-adjacent to last range in array,
       //          and adjacencies not permitted.
       intervalChecker.reset();
       results = privSel.GetRangesForInterval(testNode, 10, testNode, 12,
                                              false);
       intervalChecker.check(25, results);

       // Test 26.  Check GetRangesForInterval returns correct results.
       // Part 10 - Test interval is equal to a collapsed range at position 0
       //           in the range array, and adjacencies permitted.
       selection.removeAllRanges();
       selection.addRange(range4);
       intervalChecker.addExpected(testNode, 11, 11);
       results = privSel.GetRangesForInterval(testNode, 11, testNode, 11,
                                              true);
       intervalChecker.check(26, results);

       // Test 27.  Check GetRangesForInterval returns correct results.
       // Part 11 - Test interval is equal to a collapsed range at position 0
       //           in the range array, and adjacencies not permitted.
       results = privSel.GetRangesForInterval(testNode, 11, testNode, 11,
                                              false);
       intervalChecker.check(27, results);

       // Test 28.  Check GetRangesForInterval returns correct results.
       // Part 12 - Test interval is equal to a collapsed range at end of the
       //           range array, and adjacencies permitted.
       selection.removeAllRanges();
       selection.addRange(range2);
       selection.addRange(range4);
       results = privSel.GetRangesForInterval(testNode, 11, testNode, 11,
                                              true);
       intervalChecker.check(28, results);

       // Test 29.  Check GetRangesForInterval returns correct results.
       // Part 13 - Test interval is equal to a collapsed range at end of the
       //           range array, and adjacencies not permitted.
       results = privSel.GetRangesForInterval(testNode, 11, testNode, 11,
                                              false);
       intervalChecker.check(29, results);

       // Test 30.  Check GetRangesForInterval returns correct results.
       // Part 14 - Test interval is a collapsed range contained in an
       //           existing range.
       selection.removeAllRanges();
       selection.addRange(range3);
       intervalChecker.reset();
       intervalChecker.addExpected(testNode, 6, 8);
       results = privSel.GetRangesForInterval(testNode, 7, testNode, 7,
                                              true);
       intervalChecker.check(30, results);

       // Test 31.  Check GetRangesForInterval returns correct results.
       // Part 15 - Test interval equals a collapsed range which is not stored
       //           at either endpoint of the selection's range array,
       //           adjacencies not allowed.
       selection.removeAllRanges();
       range3.collapse(false);
       selection.addRange(range5);
       selection.addRange(range3);
       selection.addRange(range8);
       intervalChecker.reset();
       intervalChecker.addExpected(testNode, 8, 8);
       results = privSel.GetRangesForInterval(testNode, 8, testNode, 8,
                                              false);
       intervalChecker.check(31, results);

       // Test 32.  Check GetRangesForInterval returns correct results.
       // Part 16 - Test interval equals a collapsed range which is not stored
       //           at either endpoint of the selection's range array,
       //           adjacencies allowed.
       results = privSel.GetRangesForInterval(testNode, 8, testNode, 8,
                                              true);
       intervalChecker.check(32, results);

       // Test 33.  Check GetRangesForInterval returns correct results.
       // Part 17 - Test interval contains a collapsed range which is not
       //           stored at either endpoint of the selection's range array.
       results = privSel.GetRangesForInterval(testNode, 7, testNode, 9,
                                              false);
       intervalChecker.check(33, results);

       // Test 34.  Check GetRangesForInterval returns correct results.
       // Part 18 - Test interval left-ovelaps an existing range.
       intervalChecker.reset();
       intervalChecker.addExpected(testNode, 5, 7);
       results = privSel.GetRangesForInterval(testNode, 2, testNode, 6,
                                              true);
       intervalChecker.check(34, results);

       // Test 35.  Check GetRangesForInterval returns correct results.
       // Part 19 - Test interval right-ovelaps an existing range.
       selection.removeAllRanges();
       selection.addRange(range8);
       intervalChecker.reset();
       intervalChecker.addExpected(testNode, 10, 12);
       results = privSel.GetRangesForInterval(testNode, 11, testNode, 13,
                                              true);
       intervalChecker.check(35, results);

       SimpleTest.finish();
     }
   </script>
  </pre>

  <p id="testparagraph">
This will be the first child of the p node above. The intention is that it is a suitably long text node to which we can add multiple ranges in order to test the various aspects of the bug.
</p>
</body>
</html>