diff options
Diffstat (limited to 'dom/tests/mochitest/general/frameSelectEvents.html')
-rw-r--r-- | dom/tests/mochitest/general/frameSelectEvents.html | 929 |
1 files changed, 929 insertions, 0 deletions
diff --git a/dom/tests/mochitest/general/frameSelectEvents.html b/dom/tests/mochitest/general/frameSelectEvents.html new file mode 100644 index 0000000000..21995c86ef --- /dev/null +++ b/dom/tests/mochitest/general/frameSelectEvents.html @@ -0,0 +1,929 @@ +<!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> |