<!doctype html>
<html>
  <head>
    <title>Testing Selection Events</title>
    <script src="/tests/SimpleTest/EventUtils.js"></script>
    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
  </head>

  <body>
    <div id="normal">
      <span id="inner">A bunch of text in a span inside of a div which should be selected</span>
    </div>

    <div id="ce">
      This is a random block of text
    </div>

    <input type="text" id="input" value="XXXXXXXXXXXXXXXXXXX" width="200"> <br>

    <textarea id="textarea" width="200">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</textarea>

    <script>
      // Call the testing methods from the parent window
      var is = parent.is;
      var ok = parent.ok;

      // spin() spins the event loop for two cycles, giving time for
      // selectionchange events to be fired, and handled by our listeners.
      function spin() {
        return new Promise(function(a) {
          parent.SimpleTest.executeSoon(function() {
            parent.SimpleTest.executeSoon(a)
          });
        });
      }

      /**
       * @param {Node} node
       */
      function isProperSelectionChangeTarget(node) {
        return node === document || node === input || node === textarea;
      }

      // The main test
      parent.add_task(async function() {
        await spin();

        var selectstart = 0;
        var selectionchange = 0;
        var inputSelectionchange = 0;
        var textareaSelectionchange = 0;

        var cancel = false;
        var selectstartTarget = null;

        async function UpdateSelectEventsOnTextControlsPref({ selectstart, selectionchange }) {
          await SpecialPowers.pushPrefEnv({
            'set': [
              ['dom.select_events.textcontrols.selectstart.enabled', !!selectstart],
              ['dom.select_events.textcontrols.selectionchange.enabled', !!selectionchange],
            ]
          });
        }
        await UpdateSelectEventsOnTextControlsPref({
          selectstart: false,
          selectionchange: false,
        });

        document.addEventListener('selectstart', function(aEvent) {
          console.log("originaltarget", aEvent.originalTarget, "new", selectstartTarget);
          is(aEvent.originalTarget, selectstartTarget,
             "The original target of selectstart");
          selectstartTarget = null;

          console.log(selectstart);
          selectstart++;

          if (cancel) {
            aEvent.preventDefault();
          }
        });
        document.addEventListener('selectionchange', function(aEvent) {
          ok(isProperSelectionChangeTarget(aEvent.target),
             "The target of selectionchange should be one of document, input, or textarea");
          console.log(selectionchange);
          selectionchange++;
        });

        function elt(aId) { return document.getElementById(aId); }
        function reset() {
          selectstart = 0;
          selectionchange = 0;
          inputSelectionchange = 0;
          textareaSelectionchange = 0;
          cancel = false;
        }

        elt("input").addEventListener('selectionchange', function(aEvent) {
          is (aEvent.originalTarget, elt("input"),
              "The original target of selectionchange should be the input");
          console.log(inputSelectionchange);
          inputSelectionchange++;
        });
        elt("textarea").addEventListener('selectionchange', function(aEvent) {
          is (aEvent.originalTarget, elt("textarea"),
              "The original target of selectionchange should be the textarea");
          console.log(textareaSelectionchange);
          textareaSelectionchange++;
        });

        function checkEventCounts(
          aTestDescription,
          aSituationDescription,
          aExpectedEventCounts
        ) {
          let {
            selectstartOnDocument = 0,
            selectionchangeOnDocument = 0,
            selectionchangeOnInput = 0,
            selectionchangeOnTextarea = 0,
          } = aExpectedEventCounts;

          is(
            selectstart,
            selectstartOnDocument,
            `${
              aTestDescription
            }: "selectstart" event on the document node should be fired ${
              selectstartOnDocument
            } times ${aSituationDescription}`
          );
          is(
            selectionchange,
            selectionchangeOnDocument,
            `${
              aTestDescription
            }: "selectionchange" event on the document node should be fired ${
              selectionchangeOnDocument
            } times ${aSituationDescription}`
          );
          is(
            inputSelectionchange,
            selectionchangeOnInput,
            `${
              aTestDescription
            }: "selectionchange" event on the <input> should be fired ${
              selectionchangeOnInput
            } times ${aSituationDescription}`
          );
          is(
            textareaSelectionchange,
            selectionchangeOnTextarea,
            `${
              aTestDescription
            }: "selectionchange" event on the <textarea> should be fired ${
              selectionchangeOnTextarea
            } times ${aSituationDescription}`
          );
        }

        async function testWithSynthesizingMouse(
          aDescription,
          aElement,
          aOffset,
          aType,
          aExpectedEventCounts
        ) {
          const eventObject = aType == "click" ? {} : { type: aType };
          if (aOffset.y === undefined || aOffset.y === null) {
            aOffset.y = 10;
          }
          synthesizeMouse(aElement, aOffset.x, aOffset.y, eventObject);
          await spin();

          checkEventCounts(
            aDescription,
            `after synthesizing ${aType} at ${aOffset.x}, ${aOffset.y}`,
            aExpectedEventCounts
          );
          reset();
        }

        async function testWithSynthesizingKey(
          aDescription,
          aKey,
          aEventObject,
          aExpectedEventCounts
        ) {
          synthesizeKey(aKey, aEventObject);
          await spin();

          checkEventCounts(
            aDescription,
            `after synthesizing a key press of "${aKey}"`,
            aExpectedEventCounts
          );
          reset();
        }

        async function testWithSettingContentEditableAttribute(
          aDescription,
          aElement,
          aContentEditableValue,
          aExpectedEventCounts
        ) {
          aElement.setAttribute("contenteditable",
                                aContentEditableValue ? "true" : "false");
          await spin();

          checkEventCounts(
            aDescription,
            `after setting contenteditable attribute to ${
              aElement.getAttribute("contenteditable")
            }`,
            aExpectedEventCounts
          );
          reset();
        }

        var selection = document.getSelection();
        function isCollapsed() {
          is(selection.isCollapsed, true, "Selection is collapsed");
        }
        function isNotCollapsed() {
          is(selection.isCollapsed, false, "Selection is not collapsed");
        }

        // Make sure setting the element to contentEditable doesn't cause any selectionchange events
        await testWithSettingContentEditableAttribute(
          "Setting contenteditable attribute to true of <div> should not change selection",
          elt("ce"),
          true,
          {}
        );

        // Make sure setting the element to not be contentEditable doesn't cause any selectionchange events
        await testWithSettingContentEditableAttribute(
          'Setting contenteditable attribute to false of <div contenteditable="true"> should not change selection',
          elt("ce"),
          false,
          {}
        );

        // Now make the div contentEditable and proceed with the test
        await testWithSettingContentEditableAttribute(
          'Setting contenteditable attribute to true of <div contenteditable="false"> should not change selection',
          elt("ce"),
          true,
          {}
        );

        // Focus the contenteditable text
        await testWithSynthesizingMouse(
          'Clicking in <div contenteditable="true"> should change selection',
          elt("ce"),
          { x: 100 },
          "click",
          { selectionchangeOnDocument: 1 }
        );
        isCollapsed();

        // Move the selection to the right, this should only fire selectstart once
        selectstartTarget = elt("ce").firstChild;
        await testWithSynthesizingKey(
          'Synthesizing Shift-ArrowRight to select a character in the text node of <div contenteditable="true"> should start to select again and change selection',
          "KEY_ArrowRight",
          { shiftKey: true },
          { selectstartOnDocument: 1, selectionchangeOnDocument: 1 }
        );
        isNotCollapsed();
        await testWithSynthesizingKey(
          'Synthesizing Shift-ArrowRight again to select 2 characters in the text node of <div contenteditable="true"> should change selection',
          "KEY_ArrowRight",
          { shiftKey: true },
          { selectionchangeOnDocument: 1 }
        );
        isNotCollapsed();

        // Move it back so that the selection is empty again
        await testWithSynthesizingKey(
          'Synthesizing Shift-ArrowLeft to shrink selection in the text node of <div contenteditable="true"> should change selection',
          "KEY_ArrowLeft",
          { shiftKey: true },
          { selectionchangeOnDocument: 1 }
        );
        isNotCollapsed();
        await testWithSynthesizingKey(
          'Synthesizing Shift-ArrowLeft again to collapse selection in the text node of <div contenteditable="true"> should change selection',
          "KEY_ArrowLeft",
          { shiftKey: true },
          { selectionchangeOnDocument: 1 }
        );
        isCollapsed();

        // Going from empty to non-empty should fire selectstart again
        selectstartTarget = elt("ce").firstChild;
        await testWithSynthesizingKey(
          'Synthesizing Shift-ArrowLeft again to select a character on the other side in the text node of <div contenteditable="true"> should start to select and change selection',
          "KEY_ArrowLeft",
          { shiftKey: true },
          { selectstartOnDocument: 1, selectionchangeOnDocument: 1 }
        );
        isNotCollapsed();

        async function testWithSynthesizingMouseDrag(
          aDescription,
          aElement,
          aSelectstartTarget
        ) {
          // Select a region
          await testWithSynthesizingMouse(
            `Pressing left mouse button ${
              aDescription
            } should not start to select but should change selection`,
            aElement,
            { x: 50 },
            "mousedown",
            { selectionchangeOnDocument: 1 }
          );
          isCollapsed();

          selectstartTarget = aSelectstartTarget;
          await testWithSynthesizingMouse(
            `Dragging mouse to right to extend selection ${
              aDescription
            } should start to select and change selection`,
            aElement,
            { x: 100 },
            "mousemove",
            { selectstartOnDocument: 1, selectionchangeOnDocument: 1 }
          );
          isNotCollapsed();

          // Moving it more shouldn't trigger a start (move back to empty)
          await testWithSynthesizingMouse(
            `Dragging mouse to left to shrink selection ${
              aDescription
            } should change selection`,
            aElement,
            { x: 75 },
            "mousemove",
            { selectionchangeOnDocument: 1 }
          );
          isNotCollapsed();
          await testWithSynthesizingMouse(
            `Dragging mouse to left to collapse selection ${
              aDescription
            } should change selection`,
            aElement,
            { x: 50 },
            "mousemove",
            { selectionchangeOnDocument: 1 }
          );
          isCollapsed();

          // Wiggling the mouse a little such that it doesn't select any
          // characters shouldn't trigger a selection
          await testWithSynthesizingMouse(
            `Dragging mouse to bottom a bit ${
              aDescription
            } should not cause selection change`,
            aElement,
            { x: 50, y: 11 },
            "mousemove",
            {}
          );
          isCollapsed();

          // Moving the mouse again from an empty selection should trigger a
          // selectstart
          selectstartTarget = aSelectstartTarget;
          await testWithSynthesizingMouse(
            `Dragging mouse to left to extend selection ${
              aDescription
            } should start to select and change selection`,
            aElement,
            { x: 25 },
            "mousemove",
            { selectstartOnDocument: 1, selectionchangeOnDocument: 1 }
          );
          isNotCollapsed();

          // Releasing the mouse shouldn't do anything
          await testWithSynthesizingMouse(
            `Releasing left mouse button to stop dragging ${
              aDescription
            } should not change selection`,
            aElement,
            { x: 25 },
            "mouseup",
            {}
          );
          isNotCollapsed();

          // And neither should moving your mouse around when the mouse
          // button isn't pressed
          await testWithSynthesizingMouse(
            `Just moving mouse to right ${
              aDescription
            } should not start to select nor change selection`,
            aElement,
            { x: 50 },
            "mousemove",
            {}
          );
          isNotCollapsed();

          // Clicking in an random location should move the selection, but not perform a
          // selectstart
          await testWithSynthesizingMouse(
            `Clicking to collapse selection ${
              aDescription
            } should cause only selection change`,
            aElement,
            { x: 50 },
            "click",
            { selectionchangeOnDocument: 1 }
          );
          isCollapsed();

          // Clicking there again should do nothing
          await testWithSynthesizingMouse(
            `Clicking same position again ${
              aDescription
            } should not change selection`,
            aElement,
            { x: 50 },
            "click",
            {}
          );
          isCollapsed();

          // Selecting a region, and canceling the selectstart should mean that the
          // selection remains collapsed
          await testWithSynthesizingMouse(
            `Pressing left mouse button on different character to move caret ${
              aDescription
            } should cause only selection change`,
            aElement,
            { x: 75 },
            "mousedown",
            { selectionchangeOnDocument: 1 }
          );
          isCollapsed();
          cancel = true;
          selectstartTarget = aSelectstartTarget;
          await testWithSynthesizingMouse(
            `Moving mouse to right to extend selection but selectstart event will be prevented default ${
              aDescription
            } should start to select and change selection`,
            aElement,
            { x: 100 },
            "mousemove",
            { selectstartOnDocument: 1, selectionchangeOnDocument: 1 }
          );
          isCollapsed();
          await testWithSynthesizingMouse(
            `Releasing the left mouse button after dragging but selectstart was prevented the default ${
              aDescription
            } should not change selection`,
            aElement,
            { x: 100 },
            "mouseup",
            {}
          );
          isCollapsed();
        }

        // Should work both on normal
        await testWithSynthesizingMouseDrag(
          "on the text node in the non-editable <div>",
          elt("inner"),
          elt("inner").firstChild
        );
        // and contenteditable fields
        await testWithSynthesizingMouseDrag(
          'on the text node in the editable <div contenteditable="true">',
          elt("ce"),
          elt("ce").firstChild
        );
        // and fields with elements in them
        await testWithSynthesizingMouseDrag(
          "on the text node in the non-editable <div>'s child",
          elt("normal"),
          elt("inner").firstChild
        );

        await testWithSynthesizingMouse(
          "Clicking in the text node in the `<div>` should change selection",
          elt("inner"),
          { x: 50 },
          "click",
          { selectionchangeOnDocument: 1 }
        );
        isCollapsed();

        reset();
        // Select all should fire both selectstart and change
        selectstartTarget = document.body;
        await testWithSynthesizingKey(
          "Select All when no editor has focus should start to select and select all content",
          "a", { accelKey: true },
          { selectstartOnDocument: 1, selectionchangeOnDocument: 1 }
        );
        isNotCollapsed();

        // Clear the selection
        await testWithSynthesizingMouse(
          "Clicking in the non-editable <div> should clear selection",
          elt("inner"),
          { x: 50 },
          "click",
          { selectionchangeOnDocument: 1 }
        );
        isCollapsed();

        // Even if we already have a selection
        await testWithSynthesizingMouse(
          "Pressing the left mouse button in non-editable <div> should change selection",
          elt("inner"),
          { x: 75 },
          "mousedown",
          { selectionchangeOnDocument: 1 }
        );
        isCollapsed();
        selectstartTarget = elt("inner").firstChild;
        await testWithSynthesizingMouse(
          "Dragging mouse to right to extend selection should start and change selection",
          elt("inner"),
          { x: 100 },
          "mousemove",
          { selectstartOnDocument: 1, selectionchangeOnDocument: 1 }
        );
        isNotCollapsed();
        await testWithSynthesizingMouse(
          "Releasing the left mouse button should not change selection",
          elt("inner"),
          { x: 100 },
          "mouseup",
          {}
        );
        isNotCollapsed();

        selectstartTarget = document.body;
        await testWithSynthesizingKey(
          "Select All when no editor has focus should start to select and select all content (again)",
          "a",
          { accelKey: true },
          { selectstartOnDocument: 1, selectionchangeOnDocument: 1 }
        );
        isNotCollapsed();

        // Clear the selection
        await testWithSynthesizingMouse(
          "Clicking in the non-editable <div> should clear selection (again)",
          elt("inner"),
          { x: 50 },
          "click",
          {selectionchangeOnDocument: 1 }
        );
        isCollapsed();

        // Make sure that a synthesized selection change doesn't fire selectstart
        getSelection().removeAllRanges();
        await spin();
        is(
          selectstart,
          0,
          "Selection.removeAllRanges() should not cause selectstart event"
        );
        is(
          selectionchange,
          1,
          "Selection.removeAllRanges() should cause selectionchange event"
        );
        reset();
        isCollapsed();

        await (async function test_Selection_selectNode() {
          const range = document.createRange();
          range.selectNode(elt("inner"));
          getSelection().addRange(range);
          await spin();
          is(
            selectstart,
            0,
            "Selection.addRange() should not cause selectstart event"
          );
          is(
            selectionchange,
            1,
            "Selection.addRange() should cause selectionchange event"
          );
          reset();
          isNotCollapsed();
        })();

        // Change the range, without replacing
        await (async function test_Selection_getRangeAt_selectNode() {
          getSelection().getRangeAt(0).selectNode(elt("ce"));
          await spin();
          is(
            selectstart,
            0,
            "Selection.getRangeAt(0).selectNode() should not cause selectstart event"
          );
          is(
            selectionchange,
            1,
            "Selection.getRangeAt(0).selectNode() should cause selectionchange event"
          );
          reset();
          isNotCollapsed();
        })();

        // Remove the range
        getSelection().removeAllRanges();
        await spin();
        is(
          selectstart,
          0,
          "Selection.removeAllRanges() should not cause selectstart event (again)"
        );
        is(
          selectionchange,
          1,
          "Selection.removeAllRanges() should cause selectionchange event (again)"
        );
        reset();
        isCollapsed();

        for (const textControl of [elt("input"), elt("textarea")]) {
          await UpdateSelectEventsOnTextControlsPref({
            selectstart: false,
            selectionchange: false,
          });

          // Without the dom.select_events.textcontrols.enabled pref,
          // pressing the mouse shouldn't do anything.
          await testWithSynthesizingMouse(
            `Pressing the left mouse button in <${
                textControl.tagName.toLocaleLowerCase()
            }> should change selection of the document`,
            textControl,
            { x: 50 },
            "mousedown",
            {
              // XXX Bug 1721287: For some reason we fire 2 selectchange events
              // on the body when switching from the <input> to the <textarea>.
              selectionchangeOnDocument:
                document.activeElement != textControl &&
                  (document.activeElement.tagName.toLocaleLowerCase() == "input" ||
                   document.activeElement.tagName.toLocaleLowerCase() == "textarea")
                  ? 2
                  : 1,
            }
          );

          // Releasing the mouse shouldn't do anything
          await testWithSynthesizingMouse(
            `Releasing the left mouse button in <${
                textControl.tagName.toLocaleLowerCase()
            }> should not change any selection`,
            textControl,
            { x: 50 },
            "mouseup",
            {}
          );

          for (const selectstart of [1, 0]) {
            await UpdateSelectEventsOnTextControlsPref({
              selectstart,
              selectionchange: true,
            });

            const selectstartEventSetting = `selectstart in text controls is ${
              selectstart ? "enabled" : "disabled"
            }`;

            const isInput = textControl.tagName.toLocaleLowerCase() == "input";

            await testWithSynthesizingMouse(
              `Pressing the left mouse button in <${
                textControl.tagName.toLocaleLowerCase()
              }> should change selection (${selectstartEventSetting})`,
              textControl,
              { x: 40 },
              "mousedown",
              {
                selectionchangeOnDocument: 1,
                selectionchangeOnInput: isInput ? 1 : 0,
                selectionchangeOnTextarea: isInput ? 0 : 1,
              }
            );

            selectstartTarget = textControl;
            await testWithSynthesizingMouse(
              `Dragging mouse to right to extend selection in <${
                textControl.tagName.toLocaleLowerCase()
              }> should start to select and change selection (${
                selectstartEventSetting
              })`,
              textControl,
              { x: 100 },
              "mousemove",
              {
                selectstartOnDocument: selectstart,
                selectionchangeOnDocument: 1,
                selectionchangeOnInput: isInput ? 1 : 0,
                selectionchangeOnTextarea: isInput ? 0 : 1,
              }
            );

            // Moving it more shouldn't trigger a start (move back to empty)
            await testWithSynthesizingMouse(
              `Dragging mouse to left to shrink selection in <${
                textControl.tagName.toLocaleLowerCase()
              }> should change selection (${selectstartEventSetting})`,
              textControl,
              { x: 75 },
              "mousemove",
              {
                selectionchangeOnDocument: 1,
                selectionchangeOnInput: isInput ? 1 : 0,
                selectionchangeOnTextarea: isInput ? 0 : 1,
              }
            );
            await testWithSynthesizingMouse(
              `Dragging mouse to left to collapse selection in <${
                textControl.tagName.toLocaleLowerCase()
              }> should change selection (${selectstartEventSetting})`,
              textControl,
              { x: 40 },
              "mousemove",
              {
                selectionchangeOnDocument: 1,
                selectionchangeOnInput: isInput ? 1 : 0,
                selectionchangeOnTextarea: isInput ? 0 : 1,
              }
            );

            // Wiggling the mouse a little such that it doesn't select any
            // characters shouldn't trigger a selection
            await testWithSynthesizingMouse(
              `Pressing the left mouse button at caret in <${
                textControl.tagName.toLocaleLowerCase()
              }> should not change selection (${selectstartEventSetting})`,
              textControl,
              {
                x: 40,
                y: 11,
              },
              "mousemove",
              {}
            );

            // Moving the mouse again from an empty selection should trigger a
            // selectstart
            selectstartTarget = textControl;
            await testWithSynthesizingMouse(
              `Dragging mouse to left to extend selection in <${
                textControl.tagName.toLocaleLowerCase()
              }> should start to select and change selection (${
                selectstartEventSetting
              })`,
              textControl,
              { x: 25 },
              "mousemove",
              {
                selectstartOnDocument: selectstart,
                selectionchangeOnDocument: 1,
                selectionchangeOnInput: isInput ? 1 : 0,
                selectionchangeOnTextarea: isInput ? 0 : 1,
              }
            );

            // Releasing the mouse shouldn't do anything
            await testWithSynthesizingMouse(
              `Releasing the left mouse button in <${
                textControl.tagName.toLocaleLowerCase()
              }> should not change selection (${selectstartEventSetting})`,
              textControl,
              { x: 25 },
              "mouseup",
              {}
            );

            // And neither should moving your mouse around when the mouse
            // button isn't pressed
            await testWithSynthesizingMouse(
              `Just moving mouse to right in <${
                textControl.tagName.toLocaleLowerCase()
              }> should not start to select nor change selection (${
                selectstartEventSetting
              })`,
              textControl,
              { x: 50 },
              "mousemove",
              {}
            );

            // Clicking in an random location should move the selection, but
            // not perform a selectstart
            await testWithSynthesizingMouse(
              `Clicking in <${
                textControl.tagName.toLocaleLowerCase()
              }> should change selection, but should not start selection (${
                selectstartEventSetting
              })`,
              textControl,
              { x: 50 },
              "click",
              {
                selectionchangeOnDocument: 1,
                selectionchangeOnInput: isInput ? 1 : 0,
                selectionchangeOnTextarea: isInput ? 0 : 1,
              }
            );

            // Clicking there again should do nothing
            await testWithSynthesizingMouse(
              `Clicking at caret in <${
                textControl.tagName.toLocaleLowerCase()
              }> should not change selection (${selectstartEventSetting})`,
              textControl,
              { x: 50 },
              "click",
              {}
            );

            // Selecting a region, and canceling the selectstart should mean that the
            // selection remains collapsed
            await testWithSynthesizingMouse(
              `Pressing the left mouse button at different character in <${
                textControl.tagName.toLocaleLowerCase()
              }> should change selection (${selectstartEventSetting})`,
              textControl,
              { x: 75 },
              "mousedown",
              {
                selectionchangeOnDocument: 1,
                selectionchangeOnInput: isInput ? 1 : 0,
                selectionchangeOnTextarea: isInput ? 0 : 1,
              }
            );
            cancel = true;
            selectstartTarget = textControl;
            await testWithSynthesizingMouse(
              `Dragging mouse to right to extend selection in <${
                textControl.tagName.toLocaleLowerCase()
              }> but the default of selectstart is prevented should cause selectstart and selectionchange events (${
                selectstartEventSetting
              })`,
              textControl,
              { x: 100 },
              "mousemove",
              {
                selectstartOnDocument: selectstart,
                selectionchangeOnDocument: 1,
                selectionchangeOnInput: isInput ? 1 : 0,
                selectionchangeOnTextarea: isInput ? 0 : 1,
              }
            );
            await testWithSynthesizingMouse(
              `Releasing the left mouse button in <${
                textControl.tagName.toLocaleLowerCase()
              }> should not cause changing selection (${selectstartEventSetting})`,
              textControl,
              { x: 100 },
              "mouseup",
              {}
            );
          }
        }

        // Marking the input and textarea as display: none and then as visible again
        // shouldn't trigger any changes, although the nodes will be re-framed
        for (const textControl of [elt("input"), elt("textarea")]) {
          await (async function test_set_display_of_text_control_to_none() {
            textControl.setAttribute("style", "display: none;");
            await spin();
            checkEventCounts(
              `Setting display of <${
                textControl.tagName.toLocaleLowerCase()
              }> to none`,
              "",
              {}
            );
            reset();
          })();

          await (async function test_remove_display_none_of_text_control() {
            textControl.setAttribute("style", "");
            await spin();
            checkEventCounts(
              `Removing display:none of <${
                textControl.tagName.toLocaleLowerCase()
              }>`,
              "",
              {}
            );
            reset();
          })();
        }

        // When selection is at the end of contentEditable's content,
        // clearing the content should trigger selection events.
        await (async function test_removing_contenteditable() {
          const savedContent = elt("ce").innerHTML;
          document.getSelection().setBaseAndExtent(elt("ce"), 1, elt("ce"), 1);
          await spin();
          reset();

          elt("ce").firstChild.remove();
          await spin();
          checkEventCounts(
            'Removing <div contenteditable="true"> from the DOM tree',
            "",
            { selectionchangeOnDocument: 1 }
          );

          elt("ce").innerHTML = savedContent;
          await spin();
          reset();
        })();
      });
    </script>
  </body>
</html>
