<!doctype html> <html> <head> <title>Testing Selection Events</title> <script type="text/javascript" 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) }); }); } // The main test parent.add_task(function *() { yield spin(); var selectstart = 0; var selectionchange = 0; var inputSelectionchange = 0; var textareaSelectionchange = 0; var cancel = false; var selectstartTarget = null; function* UpdateSelectEventsOntextControlsPref(aEnabled) { yield SpecialPowers.pushPrefEnv({'set': [['dom.select_events.textcontrols.enabled', aEnabled]]}); } yield* UpdateSelectEventsOntextControlsPref(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) { is(aEvent.originalTarget, document, "The original target of selectionchange should be the document"); 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* mouseAction(aElement, aOffset, aType, aSelStart, aSelChng, aISelChng, aTSelChng, aYOffset) { if (aType == "click") { // You can simulate a click event by sending undefined aType = undefined; } if (!aYOffset) { aYOffset = 10; } synthesizeMouse(aElement, aOffset, aYOffset, { type: aType }); yield spin(); is(selectstart, aSelStart, "SelStart Mouse Action (" + aOffset + " - " + aType + ")"); is(selectionchange, aSelChng, "SelChng Mouse Action (" + aOffset + " - " + aType + ")"); is(inputSelectionchange, aISelChng || 0, "ISelChng Mouse Action (" + aOffset + " - " + aType + ")"); is(textareaSelectionchange, aTSelChng || 0, "TSelChng Mouse Action (" + aOffset + " - " + aType + ")"); reset(); } function* keyAction(aKey, aShift, aAccel, aSelStart, aSelChng, aISelChng, aTSelChng) { synthesizeKey(aKey, { shiftKey: aShift, accelKey: aAccel }); yield spin(); is(selectstart, aSelStart, "SelStart Key Action (" + aKey + " - " + aShift + " - " + aAccel + ")"); is(selectionchange, aSelChng, "SelChng Key Action (" + aKey + " - " + aShift + " - " + aAccel + ")"); is(inputSelectionchange, aISelChng || 0, "ISelChng Key Action (" + aKey + " - " + aShift + " - " + aAccel + ")"); is(textareaSelectionchange, aTSelChng || 0, "TSelChng Key Action (" + aKey + " - " + aShift + " - " + aAccel + ")"); reset(); } function* contentEditableAction(aElement, aContentEditable, aSelStart, aSelChng, aISelChng, aTSelChng) { aElement.setAttribute("contenteditable", aContentEditable ? "true" : "false"); yield spin(); is(selectstart, aSelStart, "SelStart ContentEditable Action"); is(selectionchange, aSelChng, "SelStart ContentEditable Action"); is(inputSelectionchange, aISelChng || 0, "SelStart ContentEditable Action"); is(textareaSelectionchange, aTSelChng || 0, "SelStart ContentEditable Action"); 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 yield* contentEditableAction(elt("ce"), true, 0, 0, 0, 0); // Make sure setting the element to not be contentEditable doesn't cause any selectionchange events yield* contentEditableAction(elt("ce"), false, 0, 0, 0, 0); // Now make the div contentEditable and proceed with the test yield* contentEditableAction(elt("ce"), true, 0, 0, 0, 0); // Focus the contenteditable text yield* mouseAction(elt("ce"), 100, "click", 0, 1); isCollapsed(); // Move the selection to the right, this should only fire selectstart once selectstartTarget = elt("ce").firstChild; yield* keyAction("VK_RIGHT", true, false, 1, 1); isNotCollapsed(); yield* keyAction("VK_RIGHT", true, false, 0, 1); isNotCollapsed(); // Move it back so that the selection is empty again yield* keyAction("VK_LEFT", true, false, 0, 1); isNotCollapsed(); yield* keyAction("VK_LEFT", true, false, 0, 1); isCollapsed(); // Going from empty to non-empty should fire selectstart again selectstartTarget = elt("ce").firstChild; yield* keyAction("VK_LEFT", true, false, 1, 1); isNotCollapsed(); function* mouseMoves(aElement, aTarget) { // Select a region yield* mouseAction(aElement, 50, "mousedown", 0, 1); isCollapsed(); selectstartTarget = aTarget; yield* mouseAction(aElement, 100, "mousemove", 1, 1); isNotCollapsed(); // Moving it more shouldn't trigger a start (move back to empty) yield* mouseAction(aElement, 75, "mousemove", 0, 1); isNotCollapsed(); yield* mouseAction(aElement, 50, "mousemove", 0, 1); isCollapsed(); // Wiggling the mouse a little such that it doesn't select any // characters shouldn't trigger a selection yield* mouseAction(aElement, 50, "mousemove", 0, 0, 0, 0, 11); isCollapsed(); // Moving the mouse again from an empty selection should trigger a // selectstart selectstartTarget = aTarget; yield* mouseAction(aElement, 25, "mousemove", 1, 1); isNotCollapsed(); // Releasing the mouse shouldn't do anything yield* mouseAction(aElement, 25, "mouseup", 0, 0); isNotCollapsed(); // And neither should moving your mouse around when the mouse // button isn't pressed yield* mouseAction(aElement, 50, "mousemove", 0, 0); isNotCollapsed(); // Clicking in an random location should move the selection, but not perform a // selectstart yield* mouseAction(aElement, 50, "click", 0, 1); isCollapsed(); // Clicking there again should do nothing yield* mouseAction(aElement, 50, "click", 0, 0); isCollapsed(); // Selecting a region, and canceling the selectstart should mean that the // selection remains collapsed yield* mouseAction(aElement, 75, "mousedown", 0, 1); isCollapsed(); cancel = true; selectstartTarget = aTarget; yield* mouseAction(aElement, 100, "mousemove", 1, 1); isCollapsed(); yield* mouseAction(aElement, 100, "mouseup", 0, 0); isCollapsed(); } // Should work both on normal yield* mouseMoves(elt("inner"), elt("inner").firstChild); // and contenteditable fields yield* mouseMoves(elt("ce"), elt("ce").firstChild); // and fields with elements in them yield* mouseMoves(elt("normal"), elt("inner").firstChild); yield* mouseAction(elt("inner"), 50, "click", 0, 1); isCollapsed(); reset(); // Select all should fire both selectstart and change selectstartTarget = document.body; yield* keyAction("A", false, true, 1, 1); isNotCollapsed(); // Clear the selection yield* mouseAction(elt("inner"), 50, "click", 0, 1); isCollapsed(); // Even if we already have a selection yield* mouseAction(elt("inner"), 75, "mousedown", 0, 1); isCollapsed(); selectstartTarget = elt("inner").firstChild; yield* mouseAction(elt("inner"), 100, "mousemove", 1, 1); isNotCollapsed(); yield* mouseAction(elt("inner"), 100, "mouseup", 0, 0); isNotCollapsed(); selectstartTarget = document.body; yield* keyAction("A", false, true, 1, 1); isNotCollapsed(); // Clear the selection yield* mouseAction(elt("inner"), 50, "click", 0, 1); isCollapsed(); // Make sure that a synthesized selection change doesn't fire selectstart var s = document.getSelection(); s.removeAllRanges(); yield spin(); is(selectstart, 0, "Synthesized range removals shouldn't fire selectstart"); is(selectionchange, 1, "Synthesized range removals should change selectionchange"); reset(); isCollapsed(); var range = document.createRange(); range.selectNode(elt("inner")); s.addRange(range); yield spin(); is(selectstart, 0, "Synthesized range additions shouldn't fire selectstart"); is(selectionchange, 1, "Synthesized range additions should change selectionchange"); reset(); isNotCollapsed(); // Change the range, without replacing range.selectNode(elt("ce")); yield spin(); is(selectstart, 0, "Synthesized range mutations shouldn't fire selectstart"); is(selectionchange, 1, "Synthesized range mutations should change selectionchange"); reset(); isNotCollapsed(); // Remove the range s.removeAllRanges(); yield spin(); is(selectstart, 0, "Synthesized range removal"); is(selectionchange, 1, "Synthesized range removal"); reset(); isCollapsed(); /* Selection events mouse move on input type=text */ // Select a region // Without the dom.select_events.textcontrols.enabled pref, // pressing the mouse shouldn't do anything. yield* mouseAction(elt("input"), 50, "mousedown", 0, 1, 0, 0); // Releasing the mouse shouldn't do anything yield* mouseAction(elt("input"), 50, "mouseup", 0, 0, 0, 0); yield* UpdateSelectEventsOntextControlsPref(true); yield* mouseAction(elt("input"), 50, "mousedown", 0, 0, 0, 0); selectstartTarget = elt("input"); yield* mouseAction(elt("input"), 100, "mousemove", 1, 0, 1, 0); // Moving it more shouldn't trigger a start (move back to empty) yield* mouseAction(elt("input"), 75, "mousemove", 0, 0, 1, 0); yield* mouseAction(elt("input"), 50, "mousemove", 0, 0, 1, 0); // Wiggling the mouse a little such that it doesn't select any // characters shouldn't trigger a selection yield* mouseAction(elt("input"), 50, "mousemove", 0, 0, 0, 0, 11); // Moving the mouse again from an empty selection should trigger a // selectstart selectstartTarget = elt("input"); yield* mouseAction(elt("input"), 25, "mousemove", 1, 0, 1, 0); // Releasing the mouse shouldn't do anything yield* mouseAction(elt("input"), 25, "mouseup", 0, 0, 0, 0); // And neither should moving your mouse around when the mouse // button isn't pressed yield* mouseAction(elt("input"), 50, "mousemove", 0, 0, 0, 0); // Clicking in an random location should move the selection, but // not perform a selectstart yield* mouseAction(elt("input"), 50, "click", 0, 0, 1, 0); // Clicking there again should do nothing yield* mouseAction(elt("input"), 50, "click", 0, 0, 0, 0); // Selecting a region, and canceling the selectstart should mean that the // selection remains collapsed yield* mouseAction(elt("input"), 75, "mousedown", 0, 0, 1, 0); cancel = true; selectstartTarget = elt("input"); yield* mouseAction(elt("input"), 100, "mousemove", 1, 0, 1, 0); yield* mouseAction(elt("input"), 100, "mouseup", 0, 0, 0, 0); yield* UpdateSelectEventsOntextControlsPref(false); // Without the dom.select_events.textcontrols.enabled pref, // pressing the mouse shouldn't do anything. // XXX For some reason we fire 2 selectchange events on the body // when switching from the input to the text area. yield* mouseAction(elt("textarea"), 50, "mousedown", 0, 2, 0, 0); // Releasing the mouse shouldn't do anything yield* mouseAction(elt("textarea"), 50, "mouseup", 0, 0, 0, 0); yield* UpdateSelectEventsOntextControlsPref(true); // Select a region yield* mouseAction(elt("textarea"), 50, "mousedown", 0, 0, 0, 0); selectstartTarget = elt("textarea"); yield* mouseAction(elt("textarea"), 100, "mousemove", 1, 0, 0, 1); // Moving it more shouldn't trigger a start (move back to empty) yield* mouseAction(elt("textarea"), 75, "mousemove", 0, 0, 0, 1); yield* mouseAction(elt("textarea"), 50, "mousemove", 0, 0, 0, 1); // Wiggling the mouse a little such that it doesn't select any // characters shouldn't trigger a selection yield* mouseAction(elt("textarea"), 50, "mousemove", 0, 0, 0, 0, 11); // Moving the mouse again from an empty selection should trigger a // selectstart selectstartTarget = elt("textarea"); yield* mouseAction(elt("textarea"), 25, "mousemove", 1, 0, 0, 1); // Releasing the mouse shouldn't do anything yield* mouseAction(elt("textarea"), 25, "mouseup", 0, 0, 0, 0); // And neither should moving your mouse around when the mouse // button isn't pressed yield* mouseAction(elt("textarea"), 50, "mousemove", 0, 0, 0, 0); // Clicking in an random location should move the selection, but not perform a // selectstart yield* mouseAction(elt("textarea"), 50, "click", 0, 0, 0, 1); // Clicking there again should do nothing yield* mouseAction(elt("textarea"), 50, "click", 0, 0, 0, 0); // Selecting a region, and canceling the selectstart should mean that the // selection remains collapsed yield* mouseAction(elt("textarea"), 75, "mousedown", 0, 0, 0, 1); cancel = true; selectstartTarget = elt("textarea"); yield* mouseAction(elt("textarea"), 100, "mousemove", 1, 0, 0, 1); yield* mouseAction(elt("textarea"), 100, "mouseup", 0, 0, 0, 0); // 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 elt("input").setAttribute("style", "display: none;"); yield spin(); is(selectstart, 0, "idn - ss 1"); is(selectionchange, 0, "idn - sc 1"); is(inputSelectionchange, 0, "idn - isc 1"); is(textareaSelectionchange, 0, "idn - tsc 1"); reset(); elt("input").setAttribute("style", ""); yield spin(); is(selectstart, 0, "idn - ss 2"); is(selectionchange, 0, "idn - sc 2"); is(inputSelectionchange, 0, "idn - isc 2"); is(textareaSelectionchange, 0, "idn - tsc 2"); reset(); elt("textarea").setAttribute("style", "display: none;"); yield spin(); is(selectstart, 0, "tdn - ss 1"); is(selectionchange, 0, "tdn - sc 1"); is(inputSelectionchange, 0, "tdn - isc 1"); is(textareaSelectionchange, 0, "tdn - tsc 1"); reset(); elt("textarea").setAttribute("style", ""); yield spin(); is(selectstart, 0, "tdn - ss 2"); is(selectionchange, 0, "tdn - sc 2"); is(inputSelectionchange, 0, "tdn - isc 2"); is(textareaSelectionchange, 0, "tdn - tsc 2"); reset(); }); </script> </body> </html>