<!doctype html>
<meta charset="utf-8">
<title>Selection indices after content change</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<input id="i1" type="text" value="hello">
<textarea id="t1">hello</textarea>

<script>
"use strict";

// This helper ensures that when the selection direction is reset, it always is reset to the same value consistently
// (which must be one of either "none" or "forward"). This helps catch bugs like one observed in Chrome, where textareas
// reset to "none" but inputs reset to "forward".
let observedResetSelectionDirection;
function assertSelectionDirectionIsReset(element) {
  if (!observedResetSelectionDirection) {
    assert_in_array(element.selectionDirection, ["none", "forward"],
      "selectionDirection must be set to either none or forward");
    observedResetSelectionDirection = element.selectionDirection;
  } else {
    assert_equals(element.selectionDirection, observedResetSelectionDirection,
      `selectionDirection must be reset to ${observedResetSelectionDirection} (which was previously observed to be ` +
      `the value after resetting the selection direction)`);
  }
}

runInputTest("input out of document", () => {
  const input = document.createElement("input");
  input.value = "hello";
  return input;
});

runInputTest("input in document", () => {
  const input = document.querySelector("#i1");
  input.value = "hello";
  return input;
});

runInputTest("input in document, with focus", () => {
  const input = document.querySelector("#i1");
  input.value = "hello";
  input.focus();
  return input;
});

runTextareaTest("textarea out of document", () => {
  const textarea = document.createElement("textarea");
  textarea.value = "hello";
  return textarea;
});

runTextareaTest("textarea in document", () => {
  const textarea = document.querySelector("#t1");
  textarea.value = "hello";
  return textarea;
});

runTextareaTest("textarea in document, with focus", () => {
  const textarea = document.querySelector("#t1");
  textarea.value = "hello";
  textarea.focus();
  return textarea;
});

function runTest(descriptor, elementFactory) {
  test(() => {
    const element = elementFactory();
    element.setSelectionRange(1, 3, "backward");

    assert_equals(element.selectionStart, 1, "Sanity check: selectionStart was set correctly");
    assert_equals(element.selectionEnd, 3, "Sanity check: selectionEnd was set correctly");
    assert_equals(element.selectionDirection, "backward", "Sanity check: selectionDirection was set correctly");

    element.value = "hello";

    assert_equals(element.selectionStart, 1, "selectionStart must not change");
    assert_equals(element.selectionEnd, 3, "selectionEnd must not change");
    assert_equals(element.selectionDirection, "backward", "selectionDirection must not change");
  }, `${descriptor}: selection must not change when setting the same value`);

  test(() => {
    const element = elementFactory();
    element.setSelectionRange(1, 3, "backward");

    assert_equals(element.selectionStart, 1, "Sanity check: selectionStart was set correctly");
    assert_equals(element.selectionEnd, 3, "Sanity check: selectionEnd was set correctly");
    assert_equals(element.selectionDirection, "backward", "Sanity check: selectionDirection was set correctly");

    element.value = "hello2";

    assert_equals(element.selectionStart, element.value.length, "selectionStart must be reset to the end");
    assert_equals(element.selectionEnd, element.value.length, "selectionEnd must be reset to the end");
    assertSelectionDirectionIsReset(element);
  }, `${descriptor}: selection must change when setting a different value`);
}

function runInputTest(descriptor, elementFactory) {
  runTest(descriptor, elementFactory);

  test(() => {
    const input = elementFactory();
    input.setSelectionRange(1, 3, "backward");

    assert_equals(input.selectionStart, 1, "Sanity check: selectionStart was set correctly");
    assert_equals(input.selectionEnd, 3, "Sanity check: selectionEnd was set correctly");
    assert_equals(input.selectionDirection, "backward", "Sanity check: selectionDirection was set correctly");

    input.value = "he\nllo";

    assert_equals(input.selectionStart, 1, "selectionStart must not change");
    assert_equals(input.selectionEnd, 3, "selectionEnd must not change");
    assert_equals(input.selectionDirection, "backward", "selectionDirection must not change");
  }, `${descriptor}: selection must not change when setting a value that becomes the same after the value ` +
     `sanitization algorithm`);
}

function runTextareaTest(descriptor, elementFactory) {
  runTest(descriptor, elementFactory);

  test(() => {
    const textarea = elementFactory();
    textarea.value = "hell\no";
    textarea.setSelectionRange(1, 3, "backward");

    assert_equals(textarea.selectionStart, 1, "Sanity check: selectionStart was set correctly");
    assert_equals(textarea.selectionEnd, 3, "Sanity check: selectionEnd was set correctly");
    assert_equals(textarea.selectionDirection, "backward", "Sanity check: selectionDirection was set correctly");

    textarea.value = "hell\r\no";

    assert_equals(textarea.selectionStart, 1, "selectionStart must not change when setting to CRLF");
    assert_equals(textarea.selectionEnd, 3, "selectionEnd must not change when setting to CRLF");
    assert_equals(textarea.selectionDirection, "backward", "selectionDirection must not change when setting to CRLF");

    textarea.value = "hell\ro";

    assert_equals(textarea.selectionStart, 1, "selectionStart must not change when setting to CR");
    assert_equals(textarea.selectionEnd, 3, "selectionEnd must not change when setting to CR");
    assert_equals(textarea.selectionDirection, "backward", "selectionDirection must not change when setting to CR");
  }, `${descriptor}: selection must not change when setting the same normalized value`);
}
</script>
