Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/editor/libeditor/tests/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 184 kB image not shown  

Quelle  test_dragdrop.html   Sprache: HTML

 
 products/Sources/formale Sprachen/C/Firefox/editor/libeditor/tests/test_dragdrop.html


<!doctype html>
<html>

<head>
  <link rel="stylesheet" href="/tests/SimpleTest/test.css">

  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <script src="/tests/SimpleTest/EventUtils.js"></script>
</head>

<body>
  <div id="dropZone"
       ondragenter="event.dataTransfer.dropEffect = 'copy'; event.preventDefault();"
       ondragover="event.dataTransfer.dropEffect = 'copy'; event.preventDefault();"
       ondrop="event.preventDefault();"
       style="height: 4px; background-color: lemonchiffon;"></div>
  <div id="container"></div>

<script type="application/javascript">

const { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
  "resource://gre/modules/AppConstants.sys.mjs"
);

SimpleTest.waitForExplicitFinish();

function checkInputEvent(aEvent, aExpectedTarget, aInputType, aData, aDataTransfer, aTargetRanges, aDescription) {
  ok(aEvent instanceof InputEvent, `${aDescription}: "${aEvent.type}" event should be dispatched with InputEvent interface`);
  is(aEvent.cancelable, aEvent.type === "beforeinput", `${aDescription}: "${aEvent.type}" event should be ${aEvent.type === "beforeinput" ? "" : "never "}cancelable`);
  is(aEvent.bubbles, true, `${aDescription}: "${aEvent.type}" event should always bubble`);
  is(aEvent.target, aExpectedTarget, `${aDescription}: "${aEvent.type}" event should be fired on the <${aExpectedTarget.tagName.toLowerCase()}> element`);
  is(aEvent.inputType, aInputType, `${aDescription}: inputType of "${aEvent.type}" event should be "${aInputType}" on the <${aExpectedTarget.tagName.toLowerCase()}> element`);
  is(aEvent.data, aData, `${aDescription}: data of "${aEvent.type}" event should be ${aData} on the <${aExpectedTarget.tagName.toLowerCase()}> element`);
  if (aDataTransfer === null) {
    is(aEvent.dataTransfer, null, `${aDescription}: dataTransfer should be null on the <${aExpectedTarget.tagName.toLowerCase()}> element`);
  } else {
    for (let dataTransfer of aDataTransfer) {
      let description = `${aDescription}: on the <${aExpectedTarget.tagName.toLowerCase()}> element`;
      if (dataTransfer.todo) {
        // XXX It seems that synthesizeDrop() don't emulate perfectly if caller specifies the data directly.
        todo_is(aEvent.dataTransfer.getData(dataTransfer.type), dataTransfer.data,
                `${description}: dataTransfer of "${aEvent.type}" event should have "${dataTransfer.data}" whose type is "${dataTransfer.type}"`);
      } else {
        is(aEvent.dataTransfer.getData(dataTransfer.type), dataTransfer.data,
           `${description}: dataTransfer of "${aEvent.type}" event should have "${dataTransfer.data}" whose type is "${dataTransfer.type}"`);
      }
    }
  }
  let targetRanges = aEvent.getTargetRanges();
  if (aTargetRanges.length === 0) {
    is(targetRanges.length, 0,
       `${aDescription}: getTargetRange() of "${aEvent.type}" event should return empty array`);
  } else {
    is(targetRanges.length, aTargetRanges.length,
       `${aDescription}: getTargetRange() of "${aEvent.type}" event should return static range array`);
    if (targetRanges.length == aTargetRanges.length) {
      for (let i = 0; i < targetRanges.length; i++) {
        is(targetRanges[i].startContainer, aTargetRanges[i].startContainer,
           `${aDescription}: startContainer of getTargetRanges()[${i}] of "${aEvent.type}" event does not match`);
        is(targetRanges[i].startOffset, aTargetRanges[i].startOffset,
           `${aDescription}: startOffset of getTargetRanges()[${i}] of "${aEvent.type}" event does not match`);
        is(targetRanges[i].endContainer, aTargetRanges[i].endContainer,
           `${aDescription}: endContainer of getTargetRanges()[${i}] of "${aEvent.type}" event does not match`);
        is(targetRanges[i].endOffset, aTargetRanges[i].endOffset,
           `${aDescription}: endOffset of getTargetRanges()[${i}] of "${aEvent.type}" event does not match`);
      }
    }
  }
}

// eslint-disable-next-line complexity
async function doTest() {
  await SpecialPowers.pushPrefEnv({
    set: [
      ["dom.element.contenteditable.plaintext-only.enabled", true],
    ],
  });

  const container = document.getElementById("container");
  const dropZone = document.getElementById("dropZone");

  let beforeinputEvents = [];
  let inputEvents = [];
  let dragEvents = [];
  function onBeforeinput(event) {
    beforeinputEvents.push(event);
  }
  function onInput(event) {
    inputEvents.push(event);
  }
  document.addEventListener("beforeinput", onBeforeinput);
  document.addEventListener("input", onInput);

  function preventDefaultDeleteByDrag(aEvent) {
    if (aEvent.inputType === "deleteByDrag") {
      aEvent.preventDefault();
    }
  }
  function preventDefaultInsertFromDrop(aEvent) {
    if (aEvent.inputType === "insertFromDrop") {
      aEvent.preventDefault();
    }
  }

  const selection = window.getSelection();

  const kIsMac = navigator.platform.includes("Mac");
  const kIsWin = navigator.platform.includes("Win");

  const kNativeLF = kIsWin ? "\r\n" : "\n";

  const kModifiersToCopy = {
    ctrlKey: !kIsMac,
    altKey: kIsMac,
  }

  function comparePlainText(aGot, aExpected, aDescription) {
    is(aGot.replace(/\r\n?/g, "\n"), aExpected, aDescription);
  }
  function compareHTML(aGot, aExpected, aDescription) {
    is(aGot.replace(/\r\n?/g, "\n"), aExpected, aDescription);
  }

  async function trySynthesizePlainDragAndDrop(aDescription, aOptions) {
    try {
      await synthesizePlainDragAndDrop(aOptions);
      return true;
    } catch (e) {
      ok(false, `${aDescription}: Failed to emulate drag and drop (${e.message})`);
      return false;
    }
  }

  // -------- Test dragging regular text
  await (async function test_dragging_regular_text() {
    const description = "dragging part of non-editable element";
    container.innerHTML = 'Some Text';
    const span = document.querySelector("div#container > span");
    selection.setBaseAndExtent(span.firstChild, 4, span.firstChild, 6);
    beforeinputEvents = [];
    inputEvents = [];
    dragEvents = [];
    const onDrop = aEvent => {
      dragEvents.push(aEvent);
      comparePlainText(aEvent.dataTransfer.getData("text/plain"),
                      span.textContent.substring(4, 6),
                      `${description}: dataTransfer should have selected text as "text/plain"`);
      compareHTML(aEvent.dataTransfer.getData("text/html"),
                  span.outerHTML.replace(/>.+</, `>${span.textContent.substring(4, 6)}<`),
                  `${description}: dataTransfer should have the parent inline element and only selected text a"text/html"`);
    };
    document.addEventListener("drop", onDrop);
    if (
      await trySynthesizePlainDragAndDrop(
        description,
        {
          srcSelection: selection,
          destElement: dropZone,
        }
      )
    ) {
      is(beforeinputEvents.length, 0,
        `${description}: No "beforeinput" event should be fired when dragging non-editable selection to non-editable drop zone`);
      is(inputEvents.length, 0,
        `${description}: No "input" event should be fired when dragging non-editable selection to non-editable drop zone`);
      is(dragEvents.length, 1,
        `${description}: only one "drop" event should be fired`);
    }
    document.removeEventListener("drop", onDrop);
  })();

  // -------- Test dragging text from an <input>
  await (async function test_dragging_text_from_input_element() {
    const description = "dragging part of text in element";
    container.innerHTML = '';
    const input = document.querySelector("div#container > input");
    document.documentElement.scrollTop; // Need reflow to create TextControlState and its colleagues.
    input.setSelectionRange(1, 4);
    beforeinputEvents = [];
    inputEvents = [];
    dragEvents = [];
    const onDrop = aEvent => {
      dragEvents.push(aEvent);
      comparePlainText(aEvent.dataTransfer.getData("text/plain"),
                      input.value.substring(1, 4),
                      `${description}: dataTransfer should have selected text as "text/plain"`);
      is(aEvent.dataTransfer.getData("text/html"), "",
        `${description}: dataTransfer should not have data as "text/html"`);
    };
    document.addEventListener("drop", onDrop);
    if (
      await trySynthesizePlainDragAndDrop(
        description,
        {
          srcSelection: SpecialPowers.wrap(input).editor.selection,
          destElement: dropZone,
        }
      )
    ) {
      is(beforeinputEvents.length, 0,
        `${description}: No "beforeinput" event should be fired when dragging <input> value to non-editable drop zone`);
      is(inputEvents.length, 0,
        `${description}: No "input" event should be fired when dragging <input> value to non-editable drop zone`);
      is(dragEvents.length, 1,
        `${description}: only one "drop" event should be fired`);
    }
    document.removeEventListener("drop", onDrop);
  })();

  // -------- Test dragging text from an <textarea>
  await (async function test_dragging_text_from_textarea_element() {
    const description = "dragging part of text in ";
    const textarea = document.querySelector("div#container > textarea");
    document.documentElement.scrollTop; // Need reflow to create TextControlState and its colleagues.
    textarea.setSelectionRange(1, 7);
    beforeinputEvents = [];
    inputEvents = [];
    dragEvents = [];
    const onDrop = aEvent => {
      dragEvents.push(aEvent);
      comparePlainText(aEvent.dataTransfer.getData("text/plain"),
                      textarea.value.substring(1, 7),
                      `${description}: dataTransfer should have selected text as "text/plain"`);
      is(aEvent.dataTransfer.getData("text/html"), "",
        `${description}: dataTransfer should not have data as "text/html"`);
    };
    document.addEventListener("drop", onDrop);
    if (
      await trySynthesizePlainDragAndDrop(
        description,
        {
          srcSelection: SpecialPowers.wrap(textarea).editor.selection,
          destElement: dropZone,
        }
      )
    ) {
      is(beforeinputEvents.length, 0,
        `${description}: No "beforeinput" event should be fired when dragging <textarea> value to non-editable drop zone`);
      is(inputEvents.length, 0,
        `${description}: No "input" event should be fired when dragging <textarea> value to non-editable drop zone`);
      is(dragEvents.length, 1,
        `${description}: only one "drop" event should be fired`);
    }
    document.removeEventListener("drop", onDrop);
  })();

  // -------- Test dragging text from a contenteditable
  await (async function test_dragging_text_from_contenteditable() {
    const description = "dragging part of text in contenteditable element";
    container.innerHTML = "

This is some editable text.

"
;
    const b = document.querySelector("div#container > p > b");
    selection.setBaseAndExtent(b.firstChild, 2, b.firstChild, 6);
    beforeinputEvents = [];
    inputEvents = [];
    dragEvents = [];
    const onDrop = aEvent => {
      dragEvents.push(aEvent);
      comparePlainText(aEvent.dataTransfer.getData("text/plain"),
                      b.textContent.substring(2, 6),
                      `${description}: dataTransfer should have selected text as "text/plain"`);
      compareHTML(aEvent.dataTransfer.getData("text/html"),
                  b.outerHTML.replace(/>.+</, `>${b.textContent.substring(2, 6)}<`),
                  `${description}: dataTransfer should have selected nodes as "text/html"`);
    };
    document.addEventListener("drop", onDrop);
    if (
      await trySynthesizePlainDragAndDrop(
        description,
        {
          srcSelection: selection,
          destElement: dropZone,
        }
      )
    ) {
      is(beforeinputEvents.length, 0,
        `${description}: No "beforeinput" event should be fired when dragging <textarea> value to non-editable drop zone`);
      is(inputEvents.length, 0,
        `${description}: No "input" event should be fired when dragging <textarea> value to non-editable drop zone`);
      is(dragEvents.length, 1,
        `${description}: only one "drop" event should be fired`);
    }
    document.removeEventListener("drop", onDrop);
  })();


  for (const inputType of ["text""search"]) {
    // -------- Test dragging regular text of text/html to <input>
    await (async function test_dragging_text_from_span_element_to_input_element() {
      const description = `dragging text in non-editable <span> to <input type=${inputType}>`;
      container.innerHTML = `<span>Static</span><input type="${inputType}">`;
      const span = document.querySelector("div#container > span");
      const input = document.querySelector("div#container > input");
      selection.setBaseAndExtent(span.firstChild, 2, span.firstChild, 5);
      beforeinputEvents = [];
      inputEvents = [];
      dragEvents = [];
      const onDrop = aEvent => {
        dragEvents.push(aEvent);
        comparePlainText(aEvent.dataTransfer.getData("text/plain"),
                        span.textContent.substring(2, 5),
                        `${description}: dataTransfer should have selected text as "text/plain"`);
        compareHTML(aEvent.dataTransfer.getData("text/html"),
                    span.outerHTML.replace(/>.+</, `>${span.textContent.substring(2, 5)}<`),
                    `${description}: dataTransfer should have selected nodes as "text/html"`);
      };
      document.addEventListener("drop", onDrop);
      if (
        await trySynthesizePlainDragAndDrop(
          description,
          {
            srcSelection: selection,
            destElement: input,
          }
        )
      ) {
        is(input.value, span.textContent.substring(2, 5),
          `${description}: <input>.value should be modified`);
        is(beforeinputEvents.length, 1,
          `${description}: one "beforeinput" event should be fired on <input>`);
        checkInputEvent(beforeinputEvents[0], input"insertFromDrop"span.textContent.substring(2, 5), null, [], description);
        is(inputEvents.length, 1,
          `${description}: one "input" event should be fired on <input>`);
        checkInputEvent(inputEvents[0], input"insertFromDrop"span.textContent.substring(2, 5), null, [], description);
        is(dragEvents.length, 1,
          `${description}: only one "drop" event should be fired on <input>`);
      }
      document.removeEventListener("drop", onDrop);
    })();

    // -------- Test dragging regular text of text/html to disabled <input>
    await (async function test_dragging_text_from_span_element_to_disabled_input_element() {
      const description = `dragging text in non-editable <span> to <input disabled type="${inputType}">`;
      container.innerHTML = `<span>Static</span><input disabled type="${inputType}">`;
      const span = document.querySelector("div#container > span");
      const input = document.querySelector("div#container > input");
      selection.setBaseAndExtent(span.firstChild, 2, span.firstChild, 5);
      beforeinputEvents = [];
      inputEvents = [];
      dragEvents = [];
      const onDrop = aEvent => {
        dragEvents.push(aEvent);
      };
      document.addEventListener("drop", onDrop);
      if (
        await trySynthesizePlainDragAndDrop(
          description,
          {
            srcSelection: selection,
            destElement: input,
          }
        )
      ) {
        is(input.value, "",
          `${description}: <input disable>.value should not be modified`);
        is(beforeinputEvents.length, 0,
          `${description}: no "beforeinput" event should be fired on <input disabled>`);
        is(inputEvents.length, 0,
          `${description}: no "input" event should be fired on <input disabled>`);
        is(dragEvents.length, 0,
          `${description}: no "drop" event should be fired on <input disabled>`);
      }
      document.removeEventListener("drop", onDrop);
    })();

    // -------- Test dragging regular text of text/html to readonly <input>
    await (async function test_dragging_text_from_span_element_to_readonly_input_element() {
      const description = `dragging text in non-editable <span> to <input readonly type="${inputType}">`;
      container.innerHTML = `<span>Static</span><input readonly type="${inputType}">`;
      const span = document.querySelector("div#container > span");
      const input = document.querySelector("div#container > input");
      selection.setBaseAndExtent(span.firstChild, 2, span.firstChild, 5);
      beforeinputEvents = [];
      inputEvents = [];
      dragEvents = [];
      const onDrop = aEvent => {
        dragEvents.push(aEvent);
        comparePlainText(aEvent.dataTransfer.getData("text/plain"),
                        span.textContent.substring(2, 5),
                        `${description}: dataTransfer should have selected text as "text/plain"`);
        compareHTML(aEvent.dataTransfer.getData("text/html"),
                    span.outerHTML.replace(/>.+</, `>${span.textContent.substring(2, 5)}<`),
                    `${description}: dataTransfer should have selected nodes as "text/html"`);
      };
      document.addEventListener("drop", onDrop);
      if (
        await trySynthesizePlainDragAndDrop(
          description,
          {
            srcSelection: selection,
            destElement: input,
          }
        )
      ) {
        is(input.value, "",
          `${description}: <input readonly>.value should not be modified`);
        is(beforeinputEvents.length, 0,
          `${description}: no "beforeinput" event should be fired on <input readonly>`);
        is(inputEvents.length, 0,
          `${description}: no "input" event should be fired on <input readonly>`);
        is(dragEvents.length, 0,
          `${description}: no "drop" event should be fired on <input readonly>`);
      }
      document.removeEventListener("drop", onDrop);
    })();

    // -------- Test dragging only text/html data (like from another app) to <input>.
    await (async function test_dragging_only_html_text_to_input_element() {
      const description = `dragging only text/html data to <input type="${inputType}>`;
      container.innerHTML = `<span>Static</span><input type="${inputType}">`;
      const span = document.querySelector("div#container > span");
      const input = document.querySelector("div#container > input");
      selection.selectAllChildren(span);
      beforeinputEvents = [];
      inputEvents = [];
      const onDragStart = aEvent => {
        // Clear all dataTransfer data first.  Then, it'll be filled only with
        // the text/html data passed to synthesizeDrop().
        aEvent.dataTransfer.clearData();
      };
      window.addEventListener("dragstart", onDragStart, {capture: true});
      synthesizeDrop(spaninput, [[{type: "text/html", data: "Some Bold Text"}]], "copy");
      is(beforeinputEvents.length, 0,
        `${description}: no "beforeinput" event should be fired on <input>`);
      is(inputEvents.length, 0,
        `${description}: no "input" event should be fired on <input>`);
      window.removeEventListener("dragstart", onDragStart, {capture: true});
    })();

    // -------- Test dragging both text/plain and text/html data (like from another app) to <input>.
    await (async function test_dragging_both_html_text_and_plain_text_to_input_element() {
      const description = `dragging both text/plain and text/html data to <input type=${inputType}>`;
      container.innerHTML = `<span>Static</span><input type="${inputType}">`;
      const span = document.querySelector("div#container > span");
      const input = document.querySelector("div#container > input");
      selection.selectAllChildren(span);
      beforeinputEvents = [];
      inputEvents = [];
      const onDragStart = aEvent => {
        // Clear all dataTransfer data first.  Then, it'll be filled only with
        // the text/plain data and text/html data passed to synthesizeDrop().
        aEvent.dataTransfer.clearData();
      };
      window.addEventListener("dragstart", onDragStart, {capture: true});
      synthesizeDrop(spaninput, [[{type: "text/html", data: "Some Bold Text"},
                                    {type: "text/plain", data: "Some Plain Text"}]], "copy");
      is(input.value, "Some Plain Text",
        `${description}: The text/plain data should be inserted`);
      is(beforeinputEvents.length, 1,
        `${description}: only one "beforeinput" events should be fired on <input> element`);
      checkInputEvent(beforeinputEvents[0], input"insertFromDrop""Some Plain Text", null, [],
                      description);
      is(inputEvents.length, 1,
        `${description}: only one "input" events should be fired on <input> element`);
      checkInputEvent(inputEvents[0], input"insertFromDrop""Some Plain Text", null, [],
                      description);
      window.removeEventListener("dragstart", onDragStart, {capture: true});
    })();

    // -------- Test dragging special text type from another app to <input>
    await (async function test_dragging_only_moz_text_internal_to_input_element() {
      const description = `dragging both text/x-moz-text-internal data to <input type="${inputType}">`;
      container.innerHTML = `<span>Static</span><input type="${inputType}">`;
      const span = document.querySelector("div#container > span");
      const input = document.querySelector("div#container > input");
      selection.selectAllChildren(span);
      beforeinputEvents = [];
      inputEvents = [];
      const onDragStart = aEvent => {
        // Clear all dataTransfer data first.  Then, it'll be filled only with
        // the text/x-moz-text-internal data passed to synthesizeDrop().
        aEvent.dataTransfer.clearData();
      };
      window.addEventListener("dragstart", onDragStart, {capture: true});
      synthesizeDrop(spaninput, [[{type: "text/x-moz-text-internal", data: "Some Special Text"}]], "copy");
      is(input.value, "",
        `${description}: <input>.value should not be modified with "text/x-moz-text-internal" data`);
      // Note that even if editor does not handle given dataTransfer, web apps
      // may handle it by itself.  Therefore, editor should dispatch "beforeinput"
      // event.
      is(beforeinputEvents.length, 1,
        `${description}: one "beforeinput" event should be fired when dropping "text/x-moz-text-internal" data into <input> element`);
      // But unfortunately, on <input> and <textarea>, dataTransfer won't be set...
      checkInputEvent(beforeinputEvents[0], input"insertFromDrop""", null, [], description);
      is(inputEvents.length, 0,
        `${description}: no "input" event should be fired when dropping "text/x-moz-text-internal" data into <input> element`);
      window.removeEventListener("dragstart", onDragStart, {capture: true});
    })();

    // -------- Test dragging contenteditable to <input>
    await (async function test_dragging_from_contenteditable_to_input_element() {
      const description = `dragging text in contenteditable to <input type="${inputType}">`;
      container.innerHTML = `<div contenteditable>Some <b>bold</b> text</div><input type="${inputType}">`;
      const contenteditable = document.querySelector("div#container > div");
      const input = document.querySelector("div#container > input");
      const selectionContainers = [contenteditable.firstChild, contenteditable.firstChild.nextSibling.nextSibling];
      selection.setBaseAndExtent(selectionContainers[0], 2, selectionContainers[1], 2);
      beforeinputEvents = [];
      inputEvents = [];
      dragEvents = [];
      const onDrop = aEvent => {
        dragEvents.push(aEvent);
        is(aEvent.dataTransfer.getData("text/plain"), "me bold t",
          `${description}: dataTransfer should have selected text as "text/plain"`);
        is(aEvent.dataTransfer.getData("text/html"), "me bold t",
          `${description}: dataTransfer should have selected nodes as "text/html"`);
      };
      document.addEventListener("drop", onDrop);
      if (
        await trySynthesizePlainDragAndDrop(
          description,
          {
            srcSelection: selection,
            destElement: input,
          }
        )
      ) {
        is(contenteditable.innerHTML, "Soext",
          `${description}: Dragged range should be removed from contenteditable`);
        is(input.value, "me bold t",
          `${description}: <input>.value should be modified`);
        is(beforeinputEvents.length, 2,
                `${description}: 2 "beforeinput" events should be fired on contenteditable and <input>`);
        checkInputEvent(beforeinputEvents[0], contenteditable, "deleteByDrag", null, null,
                        [{startContainer: selectionContainers[0], startOffset: 2,
                          endContainer: selectionContainers[1], endOffset: 2}],
                        description);
        checkInputEvent(beforeinputEvents[1], input"insertFromDrop""me bold t", null, [], description);
        is(inputEvents.length, 2,
                `${description}: 2 "input" events should be fired on contenteditable and <input>`);
        checkInputEvent(inputEvents[0], contenteditable, "deleteByDrag", null, null, [], description);
        checkInputEvent(inputEvents[1], input"insertFromDrop""me bold t", null, [], description);
        is(dragEvents.length, 1,
          `${description}: only one "drop" event should be fired on <textarea>`);
      }
      document.removeEventListener("drop", onDrop);
    })();

    // -------- Test dragging contenteditable to <input> (canceling "deleteByDrag")
    await (async function test_dragging_from_contenteditable_to_input_element_and_canceling_delete_by_drag() {
      const description = `dragging text in contenteditable to <input type="${inputType}"> (canceling "deleteByDrag")`;
      container.innerHTML = `<div contenteditable>Some <b>bold</b> text</div><input type="${inputType}">`;
      const contenteditable = document.querySelector("div#container > div");
      const input = document.querySelector("div#container > input");
      const selectionContainers = [contenteditable.firstChild, contenteditable.firstChild.nextSibling.nextSibling];
      selection.setBaseAndExtent(selectionContainers[0], 2, selectionContainers[1], 2);
      beforeinputEvents = [];
      inputEvents = [];
      dragEvents = [];
      const onDrop = aEvent => {
        dragEvents.push(aEvent);
        is(aEvent.dataTransfer.getData("text/plain"), "me bold t",
          `${description}: dataTransfer should have selected text as "text/plain"`);
        is(aEvent.dataTransfer.getData("text/html"), "me bold t",
          `${description}: dataTransfer should have selected nodes as "text/html"`);
      };
      document.addEventListener("drop", onDrop);
      document.addEventListener("beforeinput", preventDefaultDeleteByDrag);
      if (
        await trySynthesizePlainDragAndDrop(
          description,
          {
            srcSelection: selection,
            destElement: input,
          }
        )
      ) {
        is(contenteditable.innerHTML, "Some bold text",
          `${description}: Dragged range shouldn't be removed from contenteditable`);
        is(input.value, "me bold t",
          `${description}: <input>.value should be modified`);
        is(beforeinputEvents.length, 2,
                `${description}: 2 "beforeinput" events should be fired on contenteditable and <input>`);
        checkInputEvent(beforeinputEvents[0], contenteditable, "deleteByDrag", null, null,
                        [{startContainer: selectionContainers[0], startOffset: 2,
                          endContainer: selectionContainers[1], endOffset: 2}],
                        description);
        checkInputEvent(beforeinputEvents[1], input"insertFromDrop""me bold t", null, [], description);
        is(inputEvents.length, 1,
                `${description}: only one "input" event should be fired on <input>`);
        checkInputEvent(inputEvents[0], input"insertFromDrop""me bold t", null, [], description);
        is(dragEvents.length, 1,
          `${description}: only one "drop" event should be fired on <input>`);
      }
      document.removeEventListener("drop", onDrop);
      document.removeEventListener("beforeinput", preventDefaultDeleteByDrag);
    })();

    // -------- Test dragging contenteditable to <input> (canceling "insertFromDrop")
    await (async function test_dragging_from_contenteditable_to_input_element_and_canceling_insert_from_drop() {
      const description = `dragging text in contenteditable to <input type="${inputType}"> (canceling "insertFromDrop")`;
      container.innerHTML = "
Some bold text
"
;
      const contenteditable = document.querySelector("div#container > div");
      const input = document.querySelector("div#container > input");
      const selectionContainers = [contenteditable.firstChild, contenteditable.firstChild.nextSibling.nextSibling];
      selection.setBaseAndExtent(selectionContainers[0], 2, selectionContainers[1], 2);
      beforeinputEvents = [];
      inputEvents = [];
      dragEvents = [];
      const onDrop = aEvent => {
        dragEvents.push(aEvent);
        is(aEvent.dataTransfer.getData("text/plain"), "me bold t",
          `${description}: dataTransfer should have selected text as "text/plain"`);
        is(aEvent.dataTransfer.getData("text/html"), "me bold t",
          `${description}: dataTransfer should have selected nodes as "text/html"`);
      };
      document.addEventListener("drop", onDrop);
      document.addEventListener("beforeinput", preventDefaultInsertFromDrop);
      if (
        await trySynthesizePlainDragAndDrop(
          description,
          {
            srcSelection: selection,
            destElement: input,
          }
        )
      ) {
        is(contenteditable.innerHTML, "Soext",
          `${description}: Dragged range should be removed from contenteditable`);
        is(input.value, "",
          `${description}: <input>.value shouldn't be modified`);
        is(beforeinputEvents.length, 2,
                `${description}: 2 "beforeinput" events should be fired on contenteditable and <input>`);
        checkInputEvent(beforeinputEvents[0], contenteditable, "deleteByDrag", null, null,
                        [{startContainer: selectionContainers[0], startOffset: 2,
                          endContainer: selectionContainers[1], endOffset: 2}],
                        description);
        checkInputEvent(beforeinputEvents[1], input"insertFromDrop""me bold t", null, [], description);
        is(inputEvents.length, 1,
                `${description}: only one "input" event should be fired on contenteditable`);
        checkInputEvent(inputEvents[0], contenteditable, "deleteByDrag", null, null, [], description);
        is(dragEvents.length, 1,
          `${description}: only one "drop" event should be fired on <input>`);
      }
      document.removeEventListener("drop", onDrop);
      document.removeEventListener("beforeinput", preventDefaultInsertFromDrop);
    })();
  }

  // -------- Test dragging regular text of text/html to <input type="number">
  //
  // FIXME(emilio): The -moz-appearance bit is just a hack to
  // work around bug 1611720.
  await (async function test_dragging_from_span_element_to_input_element_whose_type_number() {
    const description = `dragging text in non-editable <span> to <input type="number">`;
    container.innerHTML = `<span>123456</span><input type="number" style="-moz-appearance: textfield">`;
    const span = document.querySelector("div#container > span");
    const input = document.querySelector("div#container > input");
    selection.setBaseAndExtent(span.firstChild, 2, span.firstChild, 5);
    beforeinputEvents = [];
    inputEvents = [];
    dragEvents = [];
    const onDrop = aEvent => {
      dragEvents.push(aEvent);
      comparePlainText(aEvent.dataTransfer.getData("text/plain"),
                       span.textContent.substring(2, 5),
                       `${description}: dataTransfer should have selected text as "text/plain"`);
      compareHTML(aEvent.dataTransfer.getData("text/html"),
                  span.outerHTML.replace(/>.+</, `>${span.textContent.substring(2, 5)}<`),
                  `${description}: dataTransfer should have selected nodes as "text/html"`);
    };
    document.addEventListener("drop", onDrop);
    if (
      await trySynthesizePlainDragAndDrop(
        description,
        {
          srcSelection: selection,
          destElement: input,
        }
      )
    ) {
      is(input.value, span.textContent.substring(2, 5),
        `${description}: <input>.value should be modified`);
      is(beforeinputEvents.length, 1,
        `${description}: one "beforeinput" event should be fired on <input>`);
      checkInputEvent(beforeinputEvents[0], input"insertFromDrop"span.textContent.substring(2, 5), null, [], description);
      is(inputEvents.length, 1,
        `${description}: one "input" event should be fired on <input>`);
      checkInputEvent(inputEvents[0], input"insertFromDrop"span.textContent.substring(2, 5), null, [], description);
      is(dragEvents.length, 1,
        `${description}: only one "drop" event should be fired on <input>`);
    }
    document.removeEventListener("drop", onDrop);
  })();

  // -------- Test dragging only text/plain data (like from another app) to contenteditable.
  await (async function test_dragging_only_plain_text_to_contenteditable() {
    const description = "dragging both text/plain and text/html data to contenteditable";
    container.innerHTML = 'Static
'
;
    const span = document.querySelector("div#container > span");
    const contenteditable = document.querySelector("div#container > div");
    selection.selectAllChildren(span);
    beforeinputEvents = [];
    inputEvents = [];
    const onDragStart = aEvent => {
      // Clear all dataTransfer data first.  Then, it'll be filled only with
      // the text/plain data and text/html data passed to synthesizeDrop().
      aEvent.dataTransfer.clearData();
    };
    window.addEventListener("dragstart", onDragStart, {capture: true});
    synthesizeDrop(span, contenteditable, [[{type: "text/plain", data: "Sample Text"}]], "copy");
    is(contenteditable.innerHTML, "Sample Text",
      `${description}: The text/plain data should be inserted`);
    is(beforeinputEvents.length, 1,
      `${description}: only one "beforeinput" events should be fired on contenteditable element`);
    checkInputEvent(beforeinputEvents[0], contenteditable, "insertFromDrop", null,
                    [{todo: true, type: "text/plain", data: "Sample Text"}],
                    [{startContainer: contenteditable, startOffset: 0,
                      endContainer: contenteditable, endOffset: 0}],
                    description);
    is(inputEvents.length, 1,
      `${description}: only one "input" events should be fired on contenteditable element`);
    checkInputEvent(inputEvents[0], contenteditable, "insertFromDrop", null,
                    [{todo: true, type: "text/plain", data: "Sample Text"}],
                    [],
                    description);
    window.removeEventListener("dragstart", onDragStart, {capture: true});
  })();

  // -------- Test dragging only text/html data (like from another app) to contenteditable.
  await (async function test_dragging_only_html_text_to_contenteditable() {
    const description = "dragging only text/html data to contenteditable";
    container.innerHTML = 'Static
'
;
    const span = document.querySelector("div#container > span");
    const contenteditable = document.querySelector("div#container > div");
    selection.selectAllChildren(span);
    beforeinputEvents = [];
    inputEvents = [];
    const onDragStart = aEvent => {
      // Clear all dataTransfer data first.  Then, it'll be filled only with
      // the text/plain data and text/html data passed to synthesizeDrop().
      aEvent.dataTransfer.clearData();
    };
    window.addEventListener("dragstart", onDragStart, {capture: true});
    synthesizeDrop(span, contenteditable, [[{type: "text/html", data: "Sample Italic Text"}]], "copy");
    is(contenteditable.innerHTML, "Sample Italic Text",
      `${description}: The text/plain data should be inserted`);
    is(beforeinputEvents.length, 1,
      `${description}: only one "beforeinput" events should be fired on contenteditable element`);
    checkInputEvent(beforeinputEvents[0], contenteditable, "insertFromDrop", null,
                    [{todo: true, type: "text/html", data: "Sample Italic Text"}],
                    [{startContainer: contenteditable, startOffset: 0,
                      endContainer: contenteditable, endOffset: 0}],
                    description);
    is(inputEvents.length, 1,
      `${description}: only one "input" events should be fired on contenteditable element`);
    checkInputEvent(inputEvents[0], contenteditable, "insertFromDrop", null,
                    [{todo: true, type: "text/html", data: "Sample Italic Text"}],
                    [],
                    description);
    window.removeEventListener("dragstart", onDragStart, {capture: true});
  })();

  // -------- Test dragging regular text of text/plain to <textarea>
  await (async function test_dragging_from_span_element_to_textarea_element() {
    const description = "dragging text in non-editable to ";
    const span = document.querySelector("div#container > span");
    const textarea = document.querySelector("div#container > textarea");
    selection.setBaseAndExtent(span.firstChild, 2, span.firstChild, 5);
    beforeinputEvents = [];
    inputEvents = [];
    dragEvents = [];
    const onDrop = aEvent => {
      dragEvents.push(aEvent);
      comparePlainText(aEvent.dataTransfer.getData("text/plain"),
                      span.textContent.substring(2, 5),
                      `${description}: dataTransfer should have selected text as "text/plain"`);
      compareHTML(aEvent.dataTransfer.getData("text/html"),
                  span.outerHTML.replace(/>.+</, `>${span.textContent.substring(2, 5)}<`),
                  `${description}: dataTransfer should have selected nodes as "text/html"`);
    };
    document.addEventListener("drop", onDrop);
    if (
      await trySynthesizePlainDragAndDrop(
        description,
        {
          srcSelection: selection,
          destElement: textarea,
        }
      )
    ) {
      is(textarea.value, span.textContent.substring(2, 5),
        `${description}: <textarea>.value should be modified`);
      is(beforeinputEvents.length, 1,
        `${description}: one "beforeinput" event should be fired on <textarea>`);
      checkInputEvent(beforeinputEvents[0], textarea"insertFromDrop"span.textContent.substring(2, 5), null, [], description);
      is(inputEvents.length, 1,
        `${description}: one "input" event should be fired on <textarea>`);
      checkInputEvent(inputEvents[0], textarea"insertFromDrop"span.textContent.substring(2, 5), null, [], description);
      is(dragEvents.length, 1,
        `${description}: only one "drop" event should be fired on <textarea>`);
    }
    document.removeEventListener("drop", onDrop);
  })();


  // -------- Test dragging contenteditable to <textarea>
  await (async function test_dragging_contenteditable_to_textarea_element() {
    const description = "dragging text in contenteditable to ";
    const contenteditable = document.querySelector("div#container > div");
    const textarea = document.querySelector("div#container > textarea");
    const selectionContainers = [contenteditable.firstChild, contenteditable.firstChild.nextSibling.nextSibling];
    selection.setBaseAndExtent(selectionContainers[0], 2, selectionContainers[1], 2);
    beforeinputEvents = [];
    inputEvents = [];
    dragEvents = [];
    const onDrop = aEvent => {
      dragEvents.push(aEvent);
      is(aEvent.dataTransfer.getData("text/plain"), "me bold t",
        `${description}: dataTransfer should have selected text as "text/plain"`);
      is(aEvent.dataTransfer.getData("text/html"), "me bold t",
        `${description}: dataTransfer should have selected nodes as "text/html"`);
    };
    document.addEventListener("drop", onDrop);
    if (
      await trySynthesizePlainDragAndDrop(
        description,
        {
          srcSelection: selection,
          destElement: textarea,
        }
      )
    ) {
      is(contenteditable.innerHTML, "Soext",
        `${description}: Dragged range should be removed from contenteditable`);
      is(textarea.value, "me bold t",
        `${description}: <textarea>.value should be modified`);
      is(beforeinputEvents.length, 2,
        `${description}: 2 "beforeinput" events should be fired on contenteditable and <textarea>`);
      checkInputEvent(beforeinputEvents[0], contenteditable, "deleteByDrag", null, null,
                      [{startContainer: selectionContainers[0], startOffset: 2,
                        endContainer: selectionContainers[1], endOffset: 2}],
                      description);
      checkInputEvent(beforeinputEvents[1], textarea"insertFromDrop""me bold t", null, [], description);
      is(inputEvents.length, 2,
        `${description}: 2 "input" events should be fired on contenteditable and <textarea>`);
      checkInputEvent(inputEvents[0], contenteditable, "deleteByDrag", null, null, [], description);
      checkInputEvent(inputEvents[1], textarea"insertFromDrop""me bold t", null, [], description);
      is(dragEvents.length, 1,
        `${description}: only one "drop" event should be fired on <textarea>`);
    }
    document.removeEventListener("drop", onDrop);
  })();

  // -------- Test dragging contenteditable to <textarea> (canceling "deleteByDrag")
  await (async function test_dragging_from_contenteditable_to_textarea_and_canceling_delete_by_drag() {
    const description = 'dragging text in contenteditable to ";
    const contenteditable = document.querySelector("div#container > div");
    const textarea = document.querySelector("div#container > textarea");
    const selectionContainers = [contenteditable.firstChild, contenteditable.firstChild.nextSibling.nextSibling];
    selection.setBaseAndExtent(selectionContainers[0], 2, selectionContainers[1], 2);
    beforeinputEvents = [];
    inputEvents = [];
    dragEvents = [];
    const onDrop = aEvent => {
      dragEvents.push(aEvent);
      is(aEvent.dataTransfer.getData("text/plain"), "me bold t",
        `${description}: dataTransfer should have selected text as "text/plain"`);
      is(aEvent.dataTransfer.getData("text/html"), "me bold t",
        `${description}: dataTransfer should have selected nodes as "text/html"`);
    };
    document.addEventListener("drop", onDrop);
    document.addEventListener("beforeinput", preventDefaultDeleteByDrag);
    if (
      await trySynthesizePlainDragAndDrop(
        description,
        {
          srcSelection: selection,
          destElement: textarea,
        }
      )
    ) {
      is(contenteditable.innerHTML, "Some bold text",
        `${description}: Dragged range shouldn't be removed from contenteditable`);
      is(textarea.value, "me bold t",
        `${description}: <textarea>.value should be modified`);
      is(beforeinputEvents.length, 2,
        `${description}: 2 "beforeinput" events should be fired on contenteditable and <textarea>`);
      checkInputEvent(beforeinputEvents[0], contenteditable, "deleteByDrag", null, null,
                      [{startContainer: selectionContainers[0], startOffset: 2,
                        endContainer: selectionContainers[1], endOffset: 2}],
                      description);
      checkInputEvent(beforeinputEvents[1], textarea"insertFromDrop""me bold t", null, [], description);
      is(inputEvents.length, 1,
        `${description}: only one "input" event should be fired on <textarea>`);
      checkInputEvent(inputEvents[0], textarea"insertFromDrop""me bold t", null, [], description);
      is(dragEvents.length, 1,
        `${description}: only one "drop" event should be fired on <textarea>`);
    }
    document.removeEventListener("drop", onDrop);
    document.removeEventListener("beforeinput", preventDefaultDeleteByDrag);
  })();

  // -------- Test dragging contenteditable to <textarea> (canceling "insertFromDrop")
  await (async function test_dragging_from_contenteditable_to_textarea_and_canceling_insert_from_drop() {
    const description = 'dragging text in contenteditable to ";
    const contenteditable = document.querySelector("div#container > div");
    const textarea = document.querySelector("div#container > textarea");
    const selectionContainers = [contenteditable.firstChild, contenteditable.firstChild.nextSibling.nextSibling];
    selection.setBaseAndExtent(selectionContainers[0], 2, selectionContainers[1], 2);
    beforeinputEvents = [];
    inputEvents = [];
    dragEvents = [];
    const onDrop = aEvent => {
      dragEvents.push(aEvent);
      is(aEvent.dataTransfer.getData("text/plain"), "me bold t",
        `${description}: dataTransfer should have selected text as "text/plain"`);
      is(aEvent.dataTransfer.getData("text/html"), "me bold t",
        `${description}: dataTransfer should have selected nodes as "text/html"`);
    };
    document.addEventListener("drop", onDrop);
    document.addEventListener("beforeinput", preventDefaultInsertFromDrop);
    if (
      await trySynthesizePlainDragAndDrop(
        description,
        {
          srcSelection: selection,
          destElement: textarea,
        }
      )
    ) {
      is(contenteditable.innerHTML, "Soext",
        `${description}: Dragged range should be removed from contenteditable`);
      is(textarea.value, "",
        `${description}: <textarea>.value shouldn't be modified`);
      is(beforeinputEvents.length, 2,
        `${description}: 2 "beforeinput" events should be fired on contenteditable and <textarea>`);
      checkInputEvent(beforeinputEvents[0], contenteditable, "deleteByDrag", null, null,
                      [{startContainer: selectionContainers[0], startOffset: 2,
                        endContainer: selectionContainers[1], endOffset: 2}],
                      description);
      checkInputEvent(beforeinputEvents[1], textarea"insertFromDrop""me bold t", null, [], description);
      is(inputEvents.length, 1,
        `${description}: only one "input" event should be fired on contenteditable`);
      checkInputEvent(inputEvents[0], contenteditable, "deleteByDrag", null, null, [], description);
      is(dragEvents.length, 1,
        `${description}: only one "drop" event should be fired on <textarea>`);
    }
    document.removeEventListener("drop", onDrop);
    document.removeEventListener("beforeinput", preventDefaultInsertFromDrop);
  })();

  // -------- Test dragging contenteditable to same contenteditable
  // Bug 1904272: Android Non-XOrigin incorrectly inserts after the 3rd M
  // instead of after the 2nd M in some of the following tests.
  const isAndroidException = AppConstants.platform === "android" && !isXOrigin;

  await (async function test_dragging_from_contenteditable_to_itself() {
    const description = "dragging text in contenteditable to same contenteditable";
    container.innerHTML = "
bold MMMM
"
;
    const contenteditable = document.querySelector("div#container > div");
    const b = document.querySelector("div#container > div > b");
    const span = document.querySelector("div#container > div > span");
    const lastTextNode = span.firstChild;
    const selectionContainers = [b.firstChild, b.firstChild];
    selection.setBaseAndExtent(selectionContainers[0], 1, selectionContainers[1], 3);
    beforeinputEvents = [];
    inputEvents = [];
    dragEvents = [];
    const onDrop = aEvent => {
      dragEvents.push(aEvent);
      is(aEvent.dataTransfer.getData("text/plain"), "ol",
        `${description}: dataTransfer should have selected text as "text/plain"`);
      is(aEvent.dataTransfer.getData("text/html"), "ol",
        `${description}: dataTransfer should have selected nodes as "text/html"`);
    };
    document.addEventListener("drop", onDrop);
    if (
      await trySynthesizePlainDragAndDrop(
        description,
        {
          srcSelection: selection,
          destElement: span,
        }
      )
    ) {
      const kExpectedOffsets = isAndroidException ? [3,3] : [2,2];
      if (isAndroidException) {
        todo_is(contenteditable.innerHTML, "bd MMolMM",
          `${description}: dragged range should be removed from contenteditable`);
        isnot(contenteditable.innerHTML, "bd MMMMol",
          `${description}: dragged range should be removed from contenteditable`);
      } else {
        is(contenteditable.innerHTML, "bd MMolMM",
          `${description}: dragged range should be removed from contenteditable`);
      }
      is(beforeinputEvents.length, 2,
        `${description}: 2 "beforeinput" events should be fired on contenteditable`);
      checkInputEvent(beforeinputEvents[0], contenteditable, "deleteByDrag", null, null,
                      [{startContainer: selectionContainers[0], startOffset: 1,
                        endContainer: selectionContainers[1], endOffset: 3}],
                      description);
      checkInputEvent(beforeinputEvents[1], contenteditable, "insertFromDrop", null,
                      [{type: "text/html", data: "ol"},
                      {type: "text/plain", data: "ol"}],
                      [{startContainer: lastTextNode, startOffset: kExpectedOffsets[0],
                        endContainer: lastTextNode, endOffset: kExpectedOffsets[1]}],
                      description);
      is(inputEvents.length, 2,
        `${description}: 2 "input" events should be fired on contenteditable`);
      checkInputEvent(inputEvents[0], contenteditable, "deleteByDrag", null, null, [], description);
      checkInputEvent(inputEvents[1], contenteditable, "insertFromDrop", null,
                      [{type: "text/html", data: "ol"},
                      {type: "text/plain", data: "ol"}],
                      [],
                      description);
      is(dragEvents.length, 1,
        `${description}: only one "drop" event should be fired on contenteditable`);
    }
    document.removeEventListener("drop", onDrop);
  })();

  // -------- Test dragging contenteditable to same contenteditable (canceling "deleteByDrag")
  await (async function test_dragging_from_contenteditable_to_itself_and_canceling_delete_by_drag() {
    const description = 'dragging text in contenteditable to same contenteditable (canceling "deleteByDrag")';
    container.innerHTML = "
bold MMMM
"
;
    const contenteditable = document.querySelector("div#container > div");
    const b = document.querySelector("div#container > div > b");
    const span = document.querySelector("div#container > div > span");
    const lastTextNode = span.firstChild;
    const selectionContainers = [b.firstChild, b.firstChild];
    selection.setBaseAndExtent(selectionContainers[0], 1, selectionContainers[1], 3);
    beforeinputEvents = [];
    inputEvents = [];
    dragEvents = [];
    const onDrop = aEvent => {
      dragEvents.push(aEvent);
      is(aEvent.dataTransfer.getData("text/plain"), "ol",
        `${description}: dataTransfer should have selected text as "text/plain"`);
      is(aEvent.dataTransfer.getData("text/html"), "ol",
        `${description}: dataTransfer should have selected nodes as "text/html"`);
    };
    document.addEventListener("drop", onDrop);
    document.addEventListener("beforeinput", preventDefaultDeleteByDrag);
    if (
      await trySynthesizePlainDragAndDrop(
        description,
        {
          srcSelection: selection,
          destElement: span,
        }
      )
    ) {
      const kExpectedOffsets = isAndroidException ? [3,3] : [2,2];
      if (isAndroidException) {
        todo_is(contenteditable.innerHTML, "bold MMolMM",
          `${description}: dragged range shouldn't be removed from contenteditable`);
        isnot(contenteditable.innerHTML, "bold MMMMol",
          `${description}: dragged range shouldn't be removed from contenteditable`);
      } else {
        is(contenteditable.innerHTML, "bold MMolMM",
          `${description}: dragged range shouldn't be removed from contenteditable`);
      }
      is(beforeinputEvents.length, 2,
        `${description}: 2 "beforeinput" events should be fired on contenteditable`);
      checkInputEvent(beforeinputEvents[0], contenteditable, "deleteByDrag", null, null,
                      [{startContainer: selectionContainers[0], startOffset: 1,
                        endContainer: selectionContainers[1], endOffset: 3}],
                      description);
      checkInputEvent(beforeinputEvents[1], contenteditable, "insertFromDrop", null,
                      [{type: "text/html", data: "ol"},
                      {type: "text/plain", data: "ol"}],
                      [{startContainer: lastTextNode, startOffset: kExpectedOffsets[0],
                        endContainer: lastTextNode, endOffset: kExpectedOffsets[1]}],
                      description);
      is(inputEvents.length, 1,
        `${description}: only one "input" event should be fired on contenteditable`);
      checkInputEvent(inputEvents[0], contenteditable, "insertFromDrop", null,
                      [{type: "text/html", data: "ol"},
                      {type: "text/plain", data: "ol"}],
                      [],
                      description);
      is(dragEvents.length, 1,
        `${description}: only one "drop" event should be fired on contenteditable`);
    }
    document.removeEventListener("drop", onDrop);
    document.removeEventListener("beforeinput", preventDefaultDeleteByDrag);
  })();

  // -------- Test dragging contenteditable to same contenteditable (canceling "insertFromDrop")
  await (async function test_dragging_from_contenteditable_to_itself_and_canceling_insert_from_drop() {
    const description = 'dragging text in contenteditable to same contenteditable (canceling "insertFromDrop")';
    container.innerHTML = "
bold MMMM
"
;
    const contenteditable = document.querySelector("div#container > div");
    const b = document.querySelector("div#container > div > b");
    const span = document.querySelector("div#container > div > span");
    const lastTextNode = span.firstChild;
    const selectionContainers = [b.firstChild, b.firstChild];
    selection.setBaseAndExtent(selectionContainers[0], 1, selectionContainers[1], 3);
    beforeinputEvents = [];
    inputEvents = [];
    dragEvents = [];
    const onDrop = aEvent => {
      dragEvents.push(aEvent);
      is(aEvent.dataTransfer.getData("text/plain"), "ol",
        `${description}: dataTransfer should have selected text as "text/plain"`);
      is(aEvent.dataTransfer.getData("text/html"), "ol",
        `${description}: dataTransfer should have selected nodes as "text/html"`);
    };
    document.addEventListener("drop", onDrop);
    document.addEventListener("beforeinput", preventDefaultInsertFromDrop);
    if (
      await trySynthesizePlainDragAndDrop(
        description,
        {
          srcSelection: selection,
          destElement: span,
        }
      )
    ) {
      is(contenteditable.innerHTML, "bd MMMM",
        `${description}: dragged range should be removed from contenteditable`);
      is(beforeinputEvents.length, 2,
        `${description}: 2 "beforeinput" events should be fired on contenteditable`);
      checkInputEvent(beforeinputEvents[0], contenteditable, "deleteByDrag", null, null,
                      [{startContainer: selectionContainers[0], startOffset: 1,
                        endContainer: selectionContainers[1], endOffset: 3}],
                      description);
      const kExpectedOffsets = isAndroidException ? [3,3] : [2,2];
      checkInputEvent(beforeinputEvents[1], contenteditable, "insertFromDrop", null,
                      [{type: "text/html", data: "ol"},
                      {type: "text/plain", data: "ol"}],
                      [{startContainer: lastTextNode, startOffset: kExpectedOffsets[0],
                        endContainer: lastTextNode, endOffset: kExpectedOffsets[1]}],
                      description);
      is(inputEvents.length, 1,
        `${description}: only one "input" event should be fired on contenteditable`);
      checkInputEvent(inputEvents[0], contenteditable, "deleteByDrag", null, null, [], description);
      is(dragEvents.length, 1,
        `${description}: only one "drop" event should be fired on contenteditable`);
    }
    document.removeEventListener("drop", onDrop);
    document.removeEventListener("beforeinput", preventDefaultInsertFromDrop);
  })();

  // -------- Test copy-dragging contenteditable to same contenteditable
  await (async function test_copy_dragging_from_contenteditable_to_itself() {
    const description = "copy-dragging text in contenteditable to same contenteditable";
    container.innerHTML = "
bold MMMM
"
;
    document.documentElement.scrollTop;
    const contenteditable = document.querySelector("div#container > div");
    const b = document.querySelector("div#container > div > b");
    const span = document.querySelector("div#container > div > span");
    const lastTextNode = span.firstChild;
    selection.setBaseAndExtent(b.firstChild, 1, b.firstChild, 3);
    beforeinputEvents = [];
    inputEvents = [];
    dragEvents = [];
    const onDrop = aEvent => {
      dragEvents.push(aEvent);
      is(aEvent.dataTransfer.getData("text/plain"), "ol",
        `${description}: dataTransfer should have selected text as "text/plain"`);
      is(aEvent.dataTransfer.getData("text/html"), "ol",
        `${description}: dataTransfer should have selected nodes as "text/html"`);
    };
    document.addEventListener("drop", onDrop);
    if (
      await trySynthesizePlainDragAndDrop(
        description,
        {
          srcSelection: selection,
          destElement: span,
          dragEvent: kModifiersToCopy,
        }
      )
    ) {
      const kExpectedOffsets = isAndroidException ? [3,3] : [2,2];
      if (isAndroidException) {
        todo_is(contenteditable.innerHTML, "bold MMolMM",
          `${description}: dragged range shouldn't be removed from contenteditable`);
        isnot(contenteditable.innerHTML, "bold MMMMol",
          `${description}: dragged range shouldn't be removed from contenteditable`);
      } else {
        is(contenteditable.innerHTML, "bold MMolMM",
          `${description}: dragged range shouldn't be removed from contenteditable`);
      }
      is(beforeinputEvents.length, 1,
        `${description}: only 1 "beforeinput" events should be fired on contenteditable`);
      checkInputEvent(beforeinputEvents[0], contenteditable, "insertFromDrop", null,
                      [{type: "text/html", data: "ol"},
                      {type: "text/plain", data: "ol"}],
                      [{startContainer: lastTextNode, startOffset: kExpectedOffsets[0],
                        endContainer: lastTextNode, endOffset: kExpectedOffsets[1]}],
                      description);
      is(inputEvents.length, 1,
        `${description}: only 1 "input" events should be fired on contenteditable`);
      checkInputEvent(inputEvents[0], contenteditable, "insertFromDrop", null,
                      [{type: "text/html", data: "ol"},
                      {type: "text/plain", data: "ol"}],
                      [],
                      description);
      is(dragEvents.length, 1,
        `${description}: only one "drop" event should be fired on contenteditable`);
    }
    document.removeEventListener("drop", onDrop);
  })();

  // -------- Test dragging contenteditable to other contenteditable
  await (async function test_dragging_from_contenteditable_to_other_contenteditable() {
    const description = "dragging text in contenteditable to other contenteditable";
    container.innerHTML = '
bold

'
;
    const contenteditable = document.querySelector("div#container > div");
    const b = document.querySelector("div#container > div > b");
    const otherContenteditable = document.querySelector("div#container > div ~ div");
    const selectionContainers = [b.firstChild, b.firstChild];
    selection.setBaseAndExtent(selectionContainers[0], 1, selectionContainers[1], 3);
    beforeinputEvents = [];
    inputEvents = [];
    dragEvents = [];
    const onDrop = aEvent => {
      dragEvents.push(aEvent);
      is(aEvent.dataTransfer.getData("text/plain"), "ol",
        `${description}: dataTransfer should have selected text as "text/plain"`);
      is(aEvent.dataTransfer.getData("text/html"), "ol",
        `${description}: dataTransfer should have selected nodes as "text/html"`);
    };
    document.addEventListener("drop", onDrop);
    if (
      await trySynthesizePlainDragAndDrop(
        description,
        {
          srcSelection: selection,
          destElement: otherContenteditable,
        }
      )
    ) {
      is(contenteditable.innerHTML, "bd",
        `${description}: dragged range should be removed from contenteditable`);
      is(otherContenteditable.innerHTML, "ol",
        `${description}: dragged content should be inserted into other contenteditable`);
      is(beforeinputEvents.length, 2,
        `${description}: 2 "beforeinput" events should be fired on contenteditable`);
      checkInputEvent(beforeinputEvents[0], contenteditable, "deleteByDrag", null, null,
                      [{startContainer: selectionContainers[0], startOffset: 1,
                        endContainer: selectionContainers[1], endOffset: 3}],
                      description);
      checkInputEvent(beforeinputEvents[1], otherContenteditable, "insertFromDrop", null,
                      [{type: "text/html", data: "ol"},
                      {type: "text/plain", data: "ol"}],
                      [{startContainer: otherContenteditable, startOffset: 0,
                        endContainer: otherContenteditable, endOffset: 0}],
                      description);
      is(inputEvents.length, 2,
        `${description}: 2 "input" events should be fired on contenteditable`);
      checkInputEvent(inputEvents[0], contenteditable, "deleteByDrag", null, null, [], description);
      checkInputEvent(inputEvents[1], otherContenteditable, "insertFromDrop", null,
                      [{type: "text/html", data: "ol"},
                      {type: "text/plain", data: "ol"}],
                      [],
                      description);
      is(dragEvents.length, 1,
        `${description}: only one "drop" event should be fired on other contenteditable`);
    }
    document.removeEventListener("drop", onDrop);
  })();

  // -------- Test dragging contenteditable to other contenteditable (canceling "deleteByDrag")
  await (async function test_dragging_from_contenteditable_to_other_contenteditable_and_canceling_delete_by_drag() {
    const description = 'dragging text in contenteditable to other contenteditable (canceling "deleteByDrag")';
    container.innerHTML = '
bold

'
;
    const contenteditable = document.querySelector("div#container > div");
    const b = document.querySelector("div#container > div > b");
    const otherContenteditable = document.querySelector("div#container > div ~ div");
    const selectionContainers = [b.firstChild, b.firstChild];
    selection.setBaseAndExtent(selectionContainers[0], 1, selectionContainers[1], 3);
    beforeinputEvents = [];
    inputEvents = [];
    dragEvents = [];
    const onDrop = aEvent => {
      dragEvents.push(aEvent);
      is(aEvent.dataTransfer.getData("text/plain"), "ol",
--> --------------------

--> maximum size reached

--> --------------------

100%


¤ Dauer der Verarbeitung: 0.25 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.