<!DOCTYPE html>
<html>
<head>
<title>EditContext: The HTMLElement.editContext property</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src='../../html/resources/common.js'></script>
</head>
<body>
  <div id="test"></div>
  <div id="contenteditableDiv" contenteditable></div>
  <script>
    test(function() {
      const editContextDict = {
        text: "Hello world",
        selectionStart: 11,
        selectionEnd: 11,
        inputMode: "text",
        inputPanelPolicy: "auto",
        enterKeyHint: "enter"
      };
      const editContext = new EditContext(editContextDict);
      assert_not_equals(editContext, null);
      // Verify all the members of the EditContext
      assert_equals(editContext.text, "Hello world");
      assert_equals(editContext.selectionStart, 11);
      assert_equals(editContext.selectionEnd, 11);
      assert_equals(editContext.inputMode, "text");
      assert_equals(editContext.inputPanelPolicy, "auto");
      assert_equals(editContext.enterKeyHint, "enter");
    }, 'Testing EditContext Dictionary Init');

    test(function() {
      contenteditableDiv.editContext = new EditContext();
      contenteditableDiv.editContext = null;
      contenteditableDiv.focus();
      assert_equals(document.activeElement, contenteditableDiv);
    }, 'A contenteditable element should remain editable after attaching and detaching EditContext.');

    test(function() {
      const editContext = new EditContext();
      assert_not_equals(editContext, null);

      const disconnected_div = document.createElement("DIV");
      assert_equals(disconnected_div.editContext, null);

      disconnected_div.editContext = editContext;
      assert_equals(disconnected_div.editContext, editContext);
      assert_equals(editContext.attachedElements().length, 1);
      assert_equals(editContext.attachedElements()[0], disconnected_div);
    }, 'EditContext can be associated with an element that is not in the tree.');

    test(function() {
      const editContext = new EditContext();
      assert_not_equals(editContext, null);

      const div = document.createElement("DIV");
      assert_equals(div.editContext, null);

      document.body.appendChild(div);
      div.editContext = editContext;
      assert_equals(div.editContext, editContext);
      assert_equals(editContext.attachedElements().length, 1);
      assert_equals(editContext.attachedElements()[0], div);

      document.body.removeChild(div);
      assert_equals(div.editContext, editContext);
      assert_equals(editContext.attachedElements().length, 1);
      assert_equals(editContext.attachedElements()[0], div);
    }, 'If an element is removed from the tree, the associated EditContext remains connected to the element.');

    test(function() {
      const editContext = new EditContext();

      const div_parent = document.createElement("DIV");
      const div_child = document.createElement("DIV");
      document.body.appendChild(div_parent);
      div_parent.appendChild(div_child);

      div_child.editContext = editContext;
      assert_equals(div_child.editContext, editContext);
      assert_equals(div_parent.editContext, null);
      assert_equals(editContext.attachedElements().length, 1);
      assert_equals(editContext.attachedElements()[0], div_child);

      document.body.removeChild(div_parent);
      assert_equals(div_child.editContext, editContext);
      assert_equals(editContext.attachedElements().length, 1);
      assert_equals(editContext.attachedElements()[0], div_child);
    }, 'If an element\'s ancestor is removed from tree, the associated EditContext remains connected to the element.');

    test(function() {
      const editContext = new EditContext();
      const test = document.getElementById("test");

      test.editContext = editContext;

      assert_equals(test.editContext, editContext);
      assert_equals(editContext.attachedElements().length, 1);
      assert_equals(editContext.attachedElements()[0], test);

      test.editContext = null;

      assert_equals(editContext.attachedElements().length, 0);
    }, '.attachedElements() should return associated element');

    test(function() {
      const editContext = new EditContext();
      assert_not_equals(editContext, null);
      editContext.text = "foo";
      assert_equals(editContext.text, "foo");
      const test = document.getElementById('test');
      // Update the layout of the |EditContext|
      var viewRect = test.getBoundingClientRect();
      viewRect.x = viewRect.left;
      viewRect.y = viewRect.top;
      var caretRect = test.getBoundingClientRect();
      caretRect.x = caretRect.left;
      caretRect.y = 2.2 * caretRect.top;
      caretRect.width = 1;
      editContext.updateSelection(0, 0);
      assert_equals(editContext.selectionStart, 0);
      assert_equals(editContext.selectionEnd, 0);
      editContext.selectionStart = 1;
      assert_equals(editContext.selectionStart, 0); // selectionEnd is still 0
      editContext.selectionEnd = 1;
      assert_equals(editContext.selectionEnd, 1);
      editContext.selectionStart = 1;
      assert_equals(editContext.selectionStart, 1);
      editContext.updateControlBounds(viewRect);
      editContext.updateSelectionBounds(caretRect);
    }, 'Testing EditContext update text, selection and layout');

    test(function() {
      const editContext = new EditContext();
      const test = document.getElementById('test');
      var rect1 = DOMRect.fromRect({x:0, y:1, width:100, height:200});
      var rect2 = DOMRect.fromRect({x:2, y:3, width:300, height:400});
      var rectArray = [rect1, rect2];
      var rangeStart = 2;
      editContext.updateCharacterBounds(rangeStart, rectArray);
      assert_equals(editContext.characterBoundsRangeStart, 2);

      var actualRectArray = editContext.characterBounds();
      assert_equals(actualRectArray.length, 2);
      assert_equals(actualRectArray[0].x, 0);
      assert_equals(actualRectArray[0].y, 1);
      assert_equals(actualRectArray[0].width, 100);
      assert_equals(actualRectArray[0].height, 200);
      rect2.x=100;
      assert_equals(actualRectArray[1].x, 2);  // the cached value shouldn't change.
      assert_equals(actualRectArray[1].y, 3);
      assert_equals(actualRectArray[1].width, 300);
      assert_equals(actualRectArray[1].height, 400);
    }, 'updateCharacterBounds(), characterBounds(), and characterBoundsRangeStart should work properly');

    test(function() {
      const editContext = new EditContext();
      assert_not_equals(editContext, null);
      editContext.text = "foo";
      assert_equals(editContext.text, "foo");
      assert_throws_dom("IndexSizeError", function() { editContext.updateSelection(10, 0); });
      assert_equals(editContext.selectionStart, 0);
      assert_equals(editContext.selectionEnd, 0);
      assert_throws_dom("IndexSizeError", function() { editContext.updateText(10, 1, "h"); });
      assert_equals(editContext.text, "foo");
    }, 'Testing EditContext update text and selection with invalid values');
  </script>
</body>
</html>
