browser_input.js (7235B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 function selectedTextEventPromises(stateChangeType, id) { 8 return [ 9 waitForMacEventWithInfo("AXSelectedTextChanged", (elem, info) => { 10 return ( 11 info.AXTextStateChangeType == stateChangeType && 12 elem.getAttributeValue("AXDOMIdentifier") == "body" 13 ); 14 }), 15 waitForMacEventWithInfo("AXSelectedTextChanged", (elem, info) => { 16 return ( 17 info.AXTextStateChangeType == stateChangeType && 18 elem.getAttributeValue("AXDOMIdentifier") == id 19 ); 20 }), 21 ]; 22 } 23 24 async function testInput(browser, accDoc, id = "input") { 25 let input = getNativeInterface(accDoc, id); 26 27 is(input.getAttributeValue("AXDescription"), "Name", "Correct input label"); 28 is(input.getAttributeValue("AXTitle"), "", "Correct input title"); 29 is(input.getAttributeValue("AXValue"), "Elmer Fudd", "Correct input value"); 30 is( 31 input.getAttributeValue("AXNumberOfCharacters"), 32 10, 33 "Correct length of value" 34 ); 35 36 ok(input.attributeNames.includes("AXSelectedText"), "Has AXSelectedText"); 37 ok( 38 input.attributeNames.includes("AXSelectedTextRange"), 39 "Has AXSelectedTextRange" 40 ); 41 42 let evt = Promise.all([ 43 waitForMacEvent("AXFocusedUIElementChanged", id), 44 ...selectedTextEventPromises(AXTextStateChangeTypeSelectionMove, id), 45 ]); 46 await SpecialPowers.spawn(browser, [id], domId => { 47 content.document.getElementById(domId).focus(); 48 }); 49 await evt; 50 51 evt = Promise.all( 52 selectedTextEventPromises(AXTextStateChangeTypeSelectionExtend, id) 53 ); 54 await SpecialPowers.spawn(browser, [id], domId => { 55 let elm = content.document.getElementById(domId); 56 if (elm.setSelectionRange) { 57 elm.setSelectionRange(6, 9); 58 } else { 59 let r = new content.Range(); 60 let textNode = elm.firstElementChild.firstChild; 61 r.setStart(textNode, 6); 62 r.setEnd(textNode, 9); 63 64 let s = content.getSelection(); 65 s.removeAllRanges(); 66 s.addRange(r); 67 } 68 }); 69 await evt; 70 71 is( 72 input.getAttributeValue("AXSelectedText"), 73 "Fud", 74 "Correct text is selected" 75 ); 76 77 Assert.deepEqual( 78 input.getAttributeValue("AXSelectedTextRange"), 79 [6, 3], 80 "correct range selected" 81 ); 82 83 ok( 84 input.isAttributeSettable("AXSelectedTextRange"), 85 "AXSelectedTextRange is settable" 86 ); 87 88 evt = Promise.all( 89 selectedTextEventPromises(AXTextStateChangeTypeSelectionExtend, id) 90 ); 91 input.setAttributeValue("AXSelectedTextRange", NSRange(1, 7)); 92 await evt; 93 94 Assert.deepEqual( 95 input.getAttributeValue("AXSelectedTextRange"), 96 [1, 7], 97 "correct range selected" 98 ); 99 100 is( 101 input.getAttributeValue("AXSelectedText"), 102 "lmer Fu", 103 "Correct text is selected" 104 ); 105 106 let domSelection = await SpecialPowers.spawn(browser, [], () => { 107 let elm = content.document.querySelector("input#input"); 108 if (elm) { 109 return elm.value.substring(elm.selectionStart, elm.selectionEnd); 110 } 111 112 return content.getSelection().toString(); 113 }); 114 115 is(domSelection, "lmer Fu", "correct DOM selection"); 116 117 is( 118 input.getParameterizedAttributeValue("AXStringForRange", NSRange(3, 5)), 119 "er Fu", 120 "AXStringForRange works" 121 ); 122 } 123 124 /** 125 * Input selection test 126 */ 127 addAccessibleTask( 128 `<input aria-label="Name" id="input" value="Elmer Fudd">`, 129 testInput 130 ); 131 132 /** 133 * contenteditable selection test with no role 134 */ 135 addAccessibleTask( 136 `<div aria-label="Name" id="no-role-editable" contenteditable> 137 <p>Elmer Fudd</p> 138 </div> 139 <div aria-label="Name" id="no-role-editable-single-line" aria-multiline="false" contenteditable> 140 <p>Elmer Fudd</p> 141 </div>`, 142 async (browser, accDoc) => { 143 await testInput(browser, accDoc, "no-role-editable"); 144 const noRoleEditable = getNativeInterface(accDoc, "no-role-editable"); 145 is( 146 noRoleEditable.getAttributeValue("AXRole"), 147 "AXTextArea", 148 "Correct role for multi-line contenteditable with no role" 149 ); 150 151 await testInput(browser, accDoc, "no-role-editable-single-line"); 152 const noRoleEditableSingleLine = getNativeInterface( 153 accDoc, 154 "no-role-editable-single-line" 155 ); 156 is( 157 noRoleEditableSingleLine.getAttributeValue("AXRole"), 158 "AXTextField", 159 "Correct role for single-line contenteditable with no role" 160 ); 161 } 162 ); 163 164 /** 165 * contenteditable selection test 166 */ 167 addAccessibleTask( 168 `<div aria-label="Name" tabindex="0" role="textbox" aria-multiline="true" id="input" contenteditable> 169 <p>Elmer Fudd</p> 170 </div>`, 171 testInput 172 ); 173 174 /** 175 * test contenteditable with selection that extends past editable part 176 */ 177 addAccessibleTask( 178 `<span aria-label="Name" 179 tabindex="0" 180 role="textbox" 181 id="input" 182 contenteditable>Elmer Fudd</span> <span id="notinput">is the name</span>`, 183 async (browser, accDoc) => { 184 let evt = Promise.all([ 185 waitForMacEvent("AXFocusedUIElementChanged", "input"), 186 waitForMacEvent("AXSelectedTextChanged", "body"), 187 waitForMacEvent("AXSelectedTextChanged", "input"), 188 ]); 189 await SpecialPowers.spawn(browser, [], () => { 190 content.document.getElementById("input").focus(); 191 }); 192 await evt; 193 194 evt = waitForEvent(EVENT_TEXT_CARET_MOVED); 195 await SpecialPowers.spawn(browser, [], () => { 196 let input = content.document.getElementById("input"); 197 let notinput = content.document.getElementById("notinput"); 198 199 let r = new content.Range(); 200 r.setStart(input.firstChild, 4); 201 r.setEnd(notinput.firstChild, 6); 202 203 let s = content.getSelection(); 204 s.removeAllRanges(); 205 s.addRange(r); 206 }); 207 await evt; 208 209 let input = getNativeInterface(accDoc, "input"); 210 211 is( 212 input.getAttributeValue("AXSelectedText"), 213 "r Fudd", 214 "Correct text is selected in #input" 215 ); 216 217 is( 218 stringForRange( 219 input, 220 input.getAttributeValue("AXSelectedTextMarkerRange") 221 ), 222 "r Fudd is the", 223 "Correct text is selected in document" 224 ); 225 } 226 ); 227 228 /** 229 * test nested content editables and their ancestor getters. 230 */ 231 addAccessibleTask( 232 `<div id="outer" role="textbox" contenteditable="true"> 233 <p id="p">Bob <a href="#" id="link">Loblaw's</a></p> 234 <div id="inner" role="textbox" contenteditable="true"> 235 Law <a href="#" id="inner_link">Blog</a> 236 </div> 237 </div>`, 238 (browser, accDoc) => { 239 let link = getNativeInterface(accDoc, "link"); 240 let innerLink = getNativeInterface(accDoc, "inner_link"); 241 242 let idmatches = (elem, id) => { 243 is(elem.getAttributeValue("AXDOMIdentifier"), id, "Matches ID"); 244 }; 245 246 idmatches(link.getAttributeValue("AXEditableAncestor"), "outer"); 247 idmatches(link.getAttributeValue("AXFocusableAncestor"), "outer"); 248 idmatches(link.getAttributeValue("AXHighestEditableAncestor"), "outer"); 249 250 idmatches(innerLink.getAttributeValue("AXEditableAncestor"), "inner"); 251 idmatches(innerLink.getAttributeValue("AXFocusableAncestor"), "inner"); 252 idmatches( 253 innerLink.getAttributeValue("AXHighestEditableAncestor"), 254 "outer" 255 ); 256 } 257 );