test_general.html (8815B)
1 <html> 2 3 <head> 4 <title>Text selection testing</title> 5 6 <link rel="stylesheet" type="text/css" 7 href="chrome://mochikit/content/tests/SimpleTest/test.css" /> 8 9 <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> 10 11 <script type="application/javascript" 12 src="../common.js"></script> 13 <script type="application/javascript" 14 src="../promisified-events.js"></script> 15 16 <script type="application/javascript"> 17 /** 18 * Helper function to test selection bounds. 19 * 20 * @param {string} aID The ID to test. 21 * @param {nsIAccessibleText} acc The accessible to test. 22 * @param {int} index The selection's index to test. 23 * @param {Array} offsets The start and end offset to test against. 24 * @param {string} msgStart The start of the message to return in test 25 * messages. 26 */ 27 function testSelectionBounds(aID, acc, index, offsets, msgStart) { 28 const [expectedStart, expectedEnd] = offsets; 29 const startOffset = {}, endOffset = {}; 30 acc.getSelectionBounds(index, startOffset, endOffset); 31 32 is(startOffset.value, Math.min(expectedStart, expectedEnd), 33 msgStart + ": Wrong start offset for " + aID); 34 is(endOffset.value, Math.max(expectedStart, expectedEnd), 35 msgStart + ": Wrong end offset for " + aID); 36 } 37 38 /** 39 * Test adding selections to accessibles. 40 * 41 * @param {string} aID The ID of the element to test. 42 * @param {Array} aSelections Array of selection start and end indices. 43 */ 44 async function addSelections(aID, aSelections) { 45 info("Test adding selections to " + aID); 46 const hyperText = getAccessible(aID, [ nsIAccessibleText ]); 47 const initialSelectionCount = hyperText.selectionCount; 48 49 // Multiple selection changes will be coalesced, so just listen for one. 50 const selectionChange = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, aID); 51 for (let [startOffset, endOffset] of aSelections) { 52 hyperText.addSelection(startOffset, endOffset); 53 } 54 await selectionChange; 55 56 is(hyperText.selectionCount, 57 aSelections.length + initialSelectionCount, 58 "addSelection: Wrong selection count for " + aID); 59 60 for (let i in aSelections) { 61 testSelectionBounds(aID, hyperText, initialSelectionCount + i, 62 aSelections[i], "addSelection"); 63 } 64 65 is(hyperText.caretOffset, aSelections[hyperText.selectionCount -1][1], 66 "addSelection: caretOffset not at selection end for " + aID); 67 } 68 69 /** 70 * Test changing selections in accessibles. 71 * 72 * @param {string} aID The ID of the element to test. 73 * @param {int} aIndex The index of the selection to change. 74 * @param {Array} aSelection Array of the selection's new start and end 75 * indices. 76 */ 77 async function changeSelection(aID, aIndex, aSelection) { 78 info("Test changing the selection of " + aID + " at index " + aIndex); 79 const [startOffset, endOffset] = aSelection; 80 const hyperText = getAccessible(aID, [ nsIAccessibleText ]); 81 82 const selectionChanged = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, aID); 83 hyperText.setSelectionBounds(aIndex, startOffset, endOffset); 84 await selectionChanged; 85 86 testSelectionBounds(aID, hyperText, aIndex, 87 aSelection, "setSelectionBounds"); 88 89 is(hyperText.caretOffset, endOffset, 90 "setSelectionBounds: caretOffset not at selection end for " + aID); 91 } 92 93 /** 94 * Test removing all selections from accessibles. 95 * 96 * @param {string} aID The ID of the element to test. 97 */ 98 async function removeSelections(aID) { 99 info("Testing removal of all selections from " + aID); 100 const hyperText = getAccessible(aID, [ nsIAccessibleText ]); 101 102 let selectionsRemoved = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, document); 103 const selectionCount = hyperText.selectionCount; 104 for (let i = 0; i < selectionCount; i++) { 105 hyperText.removeSelection(0); 106 } 107 await selectionsRemoved; 108 109 is(hyperText.selectionCount, 0, 110 "removeSelection: Wrong selection count for " + aID); 111 } 112 113 /** 114 * Test that changing the DOM selection is reflected in the accessibles. 115 * 116 * @param {string} aID The container ID to test in 117 * @param {string} aNodeID1 The start node of the selection 118 * @param {int} aNodeOffset1 The offset where the selection should start 119 * @param {string} aNodeID2 The node in which the selection should end 120 * @param {int} aNodeOffset2 The index at which the selection should end 121 * @param {Array} aTests An array of accessibles and their start and end 122 * offsets to test. 123 */ 124 async function changeDOMSelection(aID, aNodeID1, aNodeOffset1, 125 aNodeID2, aNodeOffset2, 126 aTests) { 127 info("Test that DOM selection changes are reflected in the accessibles"); 128 129 let selectionChanged = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, aID); 130 // HyperTextAccessible::GetSelectionDOMRanges ignores hidden selections. 131 // Here we may be focusing an editable element (and thus hiding the 132 // main document selection), so blur it so that we test what we want to 133 // test. 134 document.activeElement.blur(); 135 136 const sel = window.getSelection(); 137 const range = document.createRange(); 138 range.setStart(getNode(aNodeID1), aNodeOffset1); 139 range.setEnd(getNode(aNodeID2), aNodeOffset2); 140 sel.addRange(range); 141 await selectionChanged; 142 143 for (let i = 0; i < aTests.length; i++) { 144 const text = getAccessible(aTests[i][0], nsIAccessibleText); 145 is(text.selectionCount, 1, 146 "setSelectionBounds: Wrong selection count for " + aID); 147 testSelectionBounds(aID, text, 0, [aTests[i][1], aTests[i][2]], 148 "setSelectionBounds"); 149 } 150 } 151 152 /** 153 * Test expected and unexpected events for selecting 154 * all text and focusing both an input and text area. We expect a caret 155 * move, but not a text selection change. 156 * 157 * @param {string} aID The ID of the element to test. 158 */ 159 async function eventsForSelectingAllTextAndFocus(aID) { 160 info("Test expected caretMove and unexpected textSelection events for " +aID); 161 let events = waitForEvents({ 162 expected: [[EVENT_TEXT_CARET_MOVED, aID]], 163 unexpected: [[EVENT_TEXT_SELECTION_CHANGED, aID]]}, aID); 164 selectAllTextAndFocus(aID); 165 await events; 166 } 167 168 /** 169 * Do tests 170 */ 171 172 async function doTests() { 173 await addSelections("paragraph", [[1, 3], [6, 10]]); 174 await changeSelection("paragraph", 0, [2, 4]); 175 await removeSelections("paragraph"); 176 177 // reverse selection 178 await addSelections("paragraph", [[1, 3], [10, 6]]); 179 await removeSelections("paragraph"); 180 181 await eventsForSelectingAllTextAndFocus("textbox"); 182 await changeSelection("textbox", 0, [1, 3]); 183 184 // reverse selection 185 await changeSelection("textbox", 0, [3, 1]); 186 187 await eventsForSelectingAllTextAndFocus("textarea"); 188 await changeSelection("textarea", 0, [1, 3]); 189 190 // XXX Bug 1973154: Because the two spans don't occupy hyperText 191 // characters, the only way to represent this selection is (c2, 2, 2). 192 // Because that is a collapsed range, it gets removed by 193 // HyperTextAccessibleBase::CroppedSelectionRanges, resulting in 194 // accessibility incorrectly reporting no selection. 195 await changeDOMSelection("c1", "c1_span1", 0, "c1_span2", 0, 196 []); 197 await changeDOMSelection("c2", "c2", 0, "c2_div2", 1, 198 [["c2", 0, 3], ["c2_div2", 0, 2]]); 199 200 SimpleTest.finish(); 201 } 202 203 SimpleTest.waitForExplicitFinish(); 204 addA11yLoadEvent(doTests); 205 </script> 206 </head> 207 208 <body> 209 210 <a target="_blank" 211 href="https://bugzilla.mozilla.org/show_bug.cgi?id=688126" 212 title="nsIAccessibleText::setSelectionBounds doesn't fire text selection changed events in some cases"> 213 Bug 688126 214 </a> 215 <a target="_blank" 216 href="https://bugzilla.mozilla.org/show_bug.cgi?id=688124" 217 title="no text selection changed event when selection is removed"> 218 Bug 688124 219 </a> 220 <p id="display"></p> 221 <div id="content" style="display: none"></div> 222 <pre id="test"> 223 </pre> 224 225 <p id="paragraph">hello world</p> 226 <input id="textbox" value="hello"/> 227 <textarea id="textarea">hello</textarea> 228 <div id="c1">hi<span id="c1_span1"></span><span id="c1_span2"></span>hi</div> 229 <div id="c2">hi<div id="c2_div2">hi</div></div> 230 231 </body> 232 </html>