tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

edit-context-input.tentative.html (12714B)


      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4 <title>EditContext: The HTMLElement.editContext property</title>
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 <script src="/resources/testdriver.js"></script>
      8 <script src="/resources/testdriver-actions.js"></script>
      9 <script src="/resources/testdriver-vendor.js"></script>
     10 </head>
     11 <body>
     12  <script>
     13      const kBackspaceKey = "\uE003";
     14      const kDeleteKey = "\uE017";
     15 
     16    async function testBasicTestInput(element) {
     17      const editContext = new EditContext();
     18      let textForView = "";
     19      document.body.appendChild(element);
     20      let beforeInputType = null;
     21      let beforeInputTargetRanges = null;
     22      element.addEventListener("beforeinput", e => {
     23        beforeInputType = e.inputType;
     24        beforeInputTargetRanges = e.getTargetRanges().map(
     25          staticRange => [staticRange.startOffset, staticRange.endOffset]);
     26      });
     27      editContext.addEventListener("textupdate", e => {
     28        textForView = `${textForView.substring(0, e.updateRangeStart)}${e.text}${textForView.substring(e.updateRangeEnd)}`;
     29      });
     30      element.editContext = editContext;
     31      element.focus();
     32      await test_driver.send_keys(element, 'a');
     33      assert_equals(editContext.text, "a");
     34      assert_equals(textForView, "a");
     35      assert_equals(beforeInputType, "insertText");
     36      if (element instanceof HTMLCanvasElement) {
     37        // DOM selection doesn't work inside <canvas>, so events
     38        // in <canvas> can't have target ranges.
     39        assert_equals(beforeInputTargetRanges.length, 0);
     40      } else {
     41        assert_equals(beforeInputTargetRanges.length, 1);
     42        assert_array_equals(beforeInputTargetRanges[0], [0, 0]);
     43      }
     44 
     45      element.remove();
     46    }
     47 
     48    promise_test(testBasicTestInput.bind(null, document.createElement("div")), "Basic text input with div");
     49    promise_test(testBasicTestInput.bind(null, document.createElement("canvas")), "Basic text input with canvas");
     50 
     51    async function testBasicTestInputWithExistingSelection(element) {
     52      const editContext = new EditContext();
     53      let textForView = "";
     54      document.body.appendChild(element);
     55      editContext.addEventListener("textupdate", e => {
     56        textForView = `${textForView.substring(0, e.updateRangeStart)}${e.text}${textForView.substring(e.updateRangeEnd)}`;
     57      });
     58      element.editContext = editContext;
     59      element.focus();
     60 
     61      editContext.updateText(0, 0, "abcd");
     62      textForView = "abcd";
     63      assert_equals(editContext.text, "abcd");
     64      editContext.updateSelection(2, 3);
     65      await test_driver.send_keys(element, 'Z');
     66      assert_equals(editContext.text, "abZd");
     67      assert_equals(textForView, "abZd");
     68 
     69      editContext.updateSelection(2, 1);
     70      await test_driver.send_keys(element, 'Y');
     71      assert_equals(editContext.text, "aYZd");
     72      assert_equals(textForView, "aYZd");
     73 
     74      element.remove();
     75    }
     76 
     77    promise_test(testBasicTestInputWithExistingSelection.bind(null, document.createElement("div")), "Text insertion with non-collapsed selection with div");
     78    promise_test(testBasicTestInputWithExistingSelection.bind(null, document.createElement("canvas")), "Text insertion with non-collapsed selection with canvas");
     79 
     80    promise_test(async function() {
     81      const editContext = new EditContext();
     82      assert_not_equals(editContext, null);
     83      const test = document.createElement("div");
     84      document.body.appendChild(test);
     85      test.editContext = editContext;
     86      test.focus();
     87      await test_driver.send_keys(test, 'a');
     88      assert_equals(test.innerHTML, "");
     89      test.remove();
     90    }, 'EditContext should disable DOM mutation');
     91 
     92    promise_test(async function() {
     93      const editContext = new EditContext();
     94      assert_not_equals(editContext, null);
     95      const test = document.createElement("div");
     96      document.body.appendChild(test);
     97      test.focus();
     98      test.editContext = editContext;
     99      test.addEventListener("beforeinput", e => {
    100        if (e.inputType === "insertText") {
    101          e.preventDefault();
    102        }
    103      });
    104      await test_driver.send_keys(test, 'a');
    105      assert_equals(editContext.text, "");
    106      test.remove();
    107    }, 'beforeInput(insertText) should be cancelable');
    108 
    109  promise_test(async () => {
    110    let div = document.createElement("div");
    111    document.body.appendChild(div);
    112    let divText = "Hello World";
    113    div.innerText = divText;
    114    div.editContext = new EditContext();
    115    div.focus();
    116    let got_before_input_event = false;
    117    div.addEventListener("beforeinput", e => {
    118      got_before_input_event = true;
    119    });
    120    let got_textupdate_event = false;
    121    div.editContext.addEventListener("textupdate", e => {
    122      got_textupdate_event = true;
    123    });
    124 
    125    div.editContext = null;
    126    await test_driver.send_keys(div, "a");
    127 
    128    assert_false(got_textupdate_event, "Shouldn't have received textupdate event after editContext was detached");
    129    assert_false(got_before_input_event, "Shouldn't have received beforeinput event after editContext was detached");
    130 
    131    div.remove();
    132  }, "EditContext should not receive events after being detached from element");
    133 
    134  async function testBackspaceAndDelete(element) {
    135      const editContext = new EditContext();
    136      let textForView = "hello there";
    137      document.body.appendChild(element);
    138      let beforeInputType = null;
    139      let beforeInputTargetRanges = null;
    140      element.addEventListener("beforeinput", e => {
    141        beforeInputType = e.inputType;
    142        beforeInputTargetRanges = e.getTargetRanges().map(
    143          staticRange => [staticRange.startOffset, staticRange.endOffset]);
    144      });
    145      let textUpdateSelection = null;
    146      editContext.addEventListener("textupdate", e => {
    147        textUpdateSelection = [e.selectionStart, e.selectionEnd];
    148        textForView = `${textForView.substring(0, e.updateRangeStart)}${e.text}${textForView.substring(e.updateRangeEnd)}`;
    149      });
    150      element.editContext = editContext;
    151      editContext.updateText(0, 11, "hello there");
    152      editContext.updateSelection(10, 10);
    153      const selection = window.getSelection();
    154 
    155      await test_driver.send_keys(element, kBackspaceKey);
    156      assert_equals(textForView, "hello thee");
    157      assert_array_equals(textUpdateSelection, [9, 9]);
    158      assert_equals(beforeInputType, "deleteContentBackward");
    159      assert_equals(beforeInputTargetRanges.length, 0, "Backspace should not have a target range in EditContext");
    160 
    161      await test_driver.send_keys(element, kDeleteKey);
    162      assert_equals(textForView, "hello the");
    163      assert_array_equals(textUpdateSelection, [9, 9]);
    164      assert_equals(beforeInputType, "deleteContentForward");
    165      assert_equals(beforeInputTargetRanges.length, 0, "Delete should not have a target range in EditContext");
    166      element.remove();
    167    }
    168 
    169    promise_test(testBackspaceAndDelete.bind(null, document.createElement("div")), "Backspace and delete in EditContext with div");
    170    promise_test(testBackspaceAndDelete.bind(null, document.createElement("canvas")) , "Backspace and delete in EditContext with canvas");
    171 
    172    async function testBackspaceAndDeleteWithExistingSelection(element) {
    173      const editContext = new EditContext();
    174      let textForView = "hello there";
    175      document.body.appendChild(element);
    176      let beforeInputType = null;
    177      let beforeInputTargetRanges = null;
    178      element.addEventListener("beforeinput", e => {
    179        beforeInputType = e.inputType;
    180        beforeInputTargetRanges = e.getTargetRanges().map(
    181          staticRange => [staticRange.startOffset, staticRange.endOffset]);
    182      });
    183      let textUpdateSelection = null;
    184      editContext.addEventListener("textupdate", e => {
    185        textUpdateSelection = [e.selectionStart, e.selectionEnd];
    186        textForView = `${textForView.substring(0, e.updateRangeStart)}${e.text}${textForView.substring(e.updateRangeEnd)}`;
    187      });
    188      element.editContext = editContext;
    189      const initialText = "abcdefghijklmnopqrstuvwxyz";
    190      editContext.updateText(0, initialText.length, initialText);
    191      textForView = initialText;
    192      element.focus();
    193 
    194      editContext.updateSelection(3, 6);
    195      await test_driver.send_keys(element, kBackspaceKey);
    196      assert_equals(editContext.text, "abcghijklmnopqrstuvwxyz");
    197      assert_equals(textForView, "abcghijklmnopqrstuvwxyz");
    198      assert_array_equals(textUpdateSelection, [3, 3]);
    199      assert_equals(beforeInputType, "deleteContentBackward");
    200      assert_equals(beforeInputTargetRanges.length, 0, "Backspace should not have a target range in EditContext");
    201 
    202      editContext.updateSelection(3, 6);
    203      await test_driver.send_keys(element, kDeleteKey);
    204      assert_equals(editContext.text, "abcjklmnopqrstuvwxyz");
    205      assert_equals(textForView, "abcjklmnopqrstuvwxyz");
    206      assert_array_equals(textUpdateSelection, [3, 3]);
    207      assert_equals(beforeInputType, "deleteContentForward");
    208      assert_equals(beforeInputTargetRanges.length, 0, "Delete should not have a target range in EditContext");
    209 
    210      editContext.updateSelection(6, 3);
    211      await test_driver.send_keys(element, kBackspaceKey);
    212      assert_equals(editContext.text, "abcmnopqrstuvwxyz");
    213      assert_equals(textForView, "abcmnopqrstuvwxyz");
    214      assert_array_equals(textUpdateSelection, [3, 3]);
    215      assert_equals(beforeInputType, "deleteContentBackward");
    216      assert_equals(beforeInputTargetRanges.length, 0, "Backspace should not have a target range in EditContext");
    217 
    218      editContext.updateSelection(6, 3);
    219      await test_driver.send_keys(element, kDeleteKey);
    220      assert_equals(editContext.text, "abcpqrstuvwxyz");
    221      assert_equals(textForView, "abcpqrstuvwxyz");
    222      assert_array_equals(textUpdateSelection, [3, 3]);
    223      assert_equals(beforeInputType, "deleteContentForward");
    224      assert_equals(beforeInputTargetRanges.length, 0, "Delete should not have a target range in EditContext");
    225 
    226      element.remove();
    227    }
    228 
    229    promise_test(testBackspaceAndDeleteWithExistingSelection.bind(null, document.createElement("div")), "Backspace and delete with existing selection with div");
    230    promise_test(testBackspaceAndDeleteWithExistingSelection.bind(null, document.createElement("canvas")) , "Backspace and delete with existing selection with canvas");
    231 
    232    promise_test(async function() {
    233      const iframe = document.createElement("iframe");
    234      document.body.appendChild(iframe);
    235      const editContext = new EditContext();
    236      iframe.contentDocument.body.editContext = editContext;
    237      iframe.contentDocument.body.focus();
    238      let got_textupdate_event = false;
    239      editContext.addEventListener("textupdate", e => {
    240        got_textupdate_event = true;
    241      });
    242      await test_driver.send_keys(iframe.contentDocument.body, "a");
    243      assert_equals(iframe.contentDocument.body.innerHTML, "", "EditContext should disable DOM modification in iframe.");
    244      assert_true(got_textupdate_event, "Input in iframe EditContext should trigger textupdate event");
    245      iframe.remove();
    246    }, 'EditContext constructed outside iframe can be used in iframe');
    247 
    248    promise_test(async function () {
    249      const div = document.createElement("div");
    250      document.body.appendChild(div);
    251      const editContext = new EditContext();
    252      div.editContext = editContext;
    253      let textupdateEventCount = 0;
    254      editContext.addEventListener("textupdate", e => {
    255        textupdateEventCount++;
    256      });
    257 
    258      div.focus();
    259      await test_driver.send_keys(div, 'a');
    260      assert_equals(textupdateEventCount, 1);
    261      assert_equals(div.innerHTML, '');
    262 
    263      const iframe = document.createElement('iframe');
    264      document.body.appendChild(iframe);
    265      iframe.contentDocument.body.appendChild(div);
    266 
    267      div.focus();
    268      await test_driver.send_keys(div, 'b');
    269      assert_equals(textupdateEventCount, 2);
    270      assert_equals(div.innerHTML, '');
    271 
    272      iframe.remove();
    273    }, 'Textupdate event should be fired on edit context when the editor element is moved to an iframe');
    274 
    275    promise_test(async function() {
    276      const div = document.createElement("div");
    277      const input = document.createElement("input");
    278      document.body.appendChild(div);
    279      document.body.appendChild(input);
    280      const editContext = new EditContext();
    281      div.editContext = editContext;
    282      div.focus();
    283      div.remove();
    284      input.focus();
    285      await test_driver.send_keys(input, "a");
    286      assert_equals(input.value, "a", "input should have received text input");
    287 
    288      input.remove();
    289    }, 'Removing EditContext-associated element with focus doesn\'t prevent further text input on the page');
    290  </script>
    291 </body>
    292 </html>