<!DOCTYPE html>
<meta charset="utf-8">
<title>Selection.modify() inside contenteditable</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div contenteditable id="host">Editable</div>
<div>Non-editable</div>
<div id="inlinehosts">
  Prefix: <span contenteditable title="inline">Editable</span>: Suffix<br>
  Prefix: <span contenteditable style="display:inline-block;" title="inline-block">Editable</span>: Suffix<br>
  <span contenteditable title="suffix only">Editable</span>: Suffix<br>
  Prefix: <span contenteditable title="prefix only">Editable</span><br>
  <span contenteditable title="standalone">Editable</span><br>
  Prefix: <span contenteditable title="inline linebreak">Edit<br>able</span>: Suffix<br>
  Prefix: <span contenteditable style="display:inline-block;" title="inline-block linebreak">Edit<br>able</span>:
  Suffix<br>
</div>
<script>
  /** @param {Node} parent */
  function* textNodeEntries(parent) {
    for (const [i, node] of parent.childNodes.entries()) {
      if (node.nodeType === Node.TEXT_NODE) {
        yield [i, node];
      }
    }
  }

  const selection = getSelection();
  test(() => {
    selection.collapse(host);
    selection.modify('extend', 'forward', 'word');
    selection.modify('extend', 'forward', 'word');
    assert_equals(selection.focusNode.parentElement, host);
  }, "Selection.modify() must not select outside of the host");

  /** @type {NodeListOf<HTMLElement>} */
  const hosts = inlinehosts.querySelectorAll("span[contenteditable]");
  for (const host of hosts) {
    test(() => {
      for (const [i, text] of textNodeEntries(host)) {
        selection.collapse(text, 1);
        selection.modify("move", "forward", "lineboundary");
        if (selection.focusNode === host) {
          assert_equals(selection.focusOffset, i + 1, "focusOffset should be after the text node");
        } else {
          assert_equals(selection.focusNode, text, "focusNode should be the text node");
          assert_equals(selection.focusOffset, text.textContent.length, "focusOffset should be the length of the text node");
        }
      }
    }, `Selection.modify('move', 'forward', 'lineboundary') must be within the inline editing host: ${host.title}`);
    test(() => {
      for (const [i, text] of textNodeEntries(host)) {
        selection.collapse(text, 1);
        selection.modify("move", "backward", "lineboundary");
        assert_equals(selection.focusNode, text, "focusNode should be the text node");
        assert_equals(selection.focusOffset, 0, "focusOffset should be 0");
      }
    }, `Selection.modify('move', 'backward', 'lineboundary') must be within the inline editing host: ${host.title}`);
  }
</script>
