browser_text_basics.js (12004B)
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 testRangeAtMarker(macDoc, marker, attribute, expected, msg) { 8 let range = macDoc.getParameterizedAttributeValue(attribute, marker); 9 is(stringForRange(macDoc, range), expected, msg); 10 } 11 12 function testUIElement( 13 macDoc, 14 marker, 15 msg, 16 expectedRole, 17 expectedValue, 18 expectedRange 19 ) { 20 let elem = macDoc.getParameterizedAttributeValue( 21 "AXUIElementForTextMarker", 22 marker 23 ); 24 is( 25 elem.getAttributeValue("AXRole"), 26 expectedRole, 27 `${msg}: element role matches` 28 ); 29 is(elem.getAttributeValue("AXValue"), expectedValue, `${msg}: element value`); 30 let elemRange = macDoc.getParameterizedAttributeValue( 31 "AXTextMarkerRangeForUIElement", 32 elem 33 ); 34 is( 35 stringForRange(macDoc, elemRange), 36 expectedRange, 37 `${msg}: element range matches element value` 38 ); 39 } 40 41 function testStyleRun(macDoc, marker, msg, expectedStyleRun) { 42 testRangeAtMarker( 43 macDoc, 44 marker, 45 "AXStyleTextMarkerRangeForTextMarker", 46 expectedStyleRun, 47 `${msg}: style run matches` 48 ); 49 } 50 51 function testParagraph(macDoc, marker, msg, expectedParagraph) { 52 testRangeAtMarker( 53 macDoc, 54 marker, 55 "AXParagraphTextMarkerRangeForTextMarker", 56 expectedParagraph, 57 `${msg}: paragraph matches` 58 ); 59 } 60 61 function testWords(macDoc, marker, msg, expectedLeft, expectedRight) { 62 testRangeAtMarker( 63 macDoc, 64 marker, 65 "AXLeftWordTextMarkerRangeForTextMarker", 66 expectedLeft, 67 `${msg}: left word matches` 68 ); 69 70 testRangeAtMarker( 71 macDoc, 72 marker, 73 "AXRightWordTextMarkerRangeForTextMarker", 74 expectedRight, 75 `${msg}: right word matches` 76 ); 77 } 78 79 function testLines( 80 macDoc, 81 marker, 82 msg, 83 expectedLine, 84 expectedLeft, 85 expectedRight 86 ) { 87 testRangeAtMarker( 88 macDoc, 89 marker, 90 "AXLineTextMarkerRangeForTextMarker", 91 expectedLine, 92 `${msg}: line matches` 93 ); 94 95 testRangeAtMarker( 96 macDoc, 97 marker, 98 "AXLeftLineTextMarkerRangeForTextMarker", 99 expectedLeft, 100 `${msg}: left line matches` 101 ); 102 103 testRangeAtMarker( 104 macDoc, 105 marker, 106 "AXRightLineTextMarkerRangeForTextMarker", 107 expectedRight, 108 `${msg}: right line matches` 109 ); 110 } 111 112 function* markerIterator(macDoc, reverse = false) { 113 let m = macDoc.getAttributeValue( 114 reverse ? "AXEndTextMarker" : "AXStartTextMarker" 115 ); 116 let c = 0; 117 while (m) { 118 yield [m, c++]; 119 m = macDoc.getParameterizedAttributeValue( 120 reverse 121 ? "AXPreviousTextMarkerForTextMarker" 122 : "AXNextTextMarkerForTextMarker", 123 m 124 ); 125 } 126 } 127 128 // Tests consistency in text markers between: 129 // 1. "Linked list" forward navagation 130 // 2. Getting markers by index 131 // 3. "Linked list" reverse navagation 132 // For each iteration method check that the returned index is consistent 133 function testMarkerIntegrity(accDoc, expectedMarkerValues) { 134 let macDoc = accDoc.nativeInterface.QueryInterface( 135 Ci.nsIAccessibleMacInterface 136 ); 137 138 // Iterate forward with "AXNextTextMarkerForTextMarker" 139 let prevMarker; 140 let count = 0; 141 for (let [marker, index] of markerIterator(macDoc)) { 142 count++; 143 let markerIndex = macDoc.getParameterizedAttributeValue( 144 "AXIndexForTextMarker", 145 marker 146 ); 147 is( 148 markerIndex, 149 index, 150 `Correct index in "AXNextTextMarkerForTextMarker": ${index}` 151 ); 152 if (prevMarker) { 153 let range = macDoc.getParameterizedAttributeValue( 154 "AXTextMarkerRangeForUnorderedTextMarkers", 155 [prevMarker, marker] 156 ); 157 is( 158 macDoc.getParameterizedAttributeValue( 159 "AXLengthForTextMarkerRange", 160 range 161 ), 162 1, 163 `[${index}] marker moved one character` 164 ); 165 } 166 prevMarker = marker; 167 168 testWords( 169 macDoc, 170 marker, 171 `At index ${index}`, 172 ...expectedMarkerValues[index].words 173 ); 174 testLines( 175 macDoc, 176 marker, 177 `At index ${index}`, 178 ...expectedMarkerValues[index].lines 179 ); 180 testUIElement( 181 macDoc, 182 marker, 183 `At index ${index}`, 184 ...expectedMarkerValues[index].element 185 ); 186 testParagraph( 187 macDoc, 188 marker, 189 `At index ${index}`, 190 expectedMarkerValues[index].paragraph 191 ); 192 testStyleRun( 193 macDoc, 194 marker, 195 `At index ${index}`, 196 expectedMarkerValues[index].style 197 ); 198 } 199 200 is(expectedMarkerValues.length, count, `Correct marker count: ${count}`); 201 202 // Use "AXTextMarkerForIndex" to retrieve all text markers 203 for (let i = 0; i < count; i++) { 204 let marker = macDoc.getParameterizedAttributeValue( 205 "AXTextMarkerForIndex", 206 i 207 ); 208 let index = macDoc.getParameterizedAttributeValue( 209 "AXIndexForTextMarker", 210 marker 211 ); 212 is(index, i, `Correct index in "AXTextMarkerForIndex": ${i}`); 213 214 if (i == count - 1) { 215 ok( 216 !macDoc.getParameterizedAttributeValue( 217 "AXNextTextMarkerForTextMarker", 218 marker 219 ), 220 "Iterated through all markers" 221 ); 222 } 223 } 224 225 count = expectedMarkerValues.length; 226 227 // Iterate backward with "AXPreviousTextMarkerForTextMarker" 228 for (let [marker] of markerIterator(macDoc, true)) { 229 if (count <= 0) { 230 ok(false, "Exceeding marker count"); 231 break; 232 } 233 count--; 234 let index = macDoc.getParameterizedAttributeValue( 235 "AXIndexForTextMarker", 236 marker 237 ); 238 is( 239 index, 240 count, 241 `Correct index in "AXPreviousTextMarkerForTextMarker": ${count}` 242 ); 243 } 244 245 is(count, 0, "Iterated backward through all text markers"); 246 } 247 248 // Run tests with old word segmenter 249 addAccessibleTask("mac/doc_textmarker_test.html", async (browser, accDoc) => { 250 await SpecialPowers.pushPrefEnv({ 251 set: [ 252 ["intl.icu4x.segmenter.enabled", false], 253 ["layout.word_select.stop_at_punctuation", true], // This is default 254 ], 255 }); 256 257 const expectedValues = await SpecialPowers.spawn(browser, [], async () => { 258 return content.wrappedJSObject.getExpected(false, true); 259 }); 260 261 testMarkerIntegrity(accDoc, expectedValues); 262 263 await SpecialPowers.popPrefEnv(); 264 }); 265 266 // new UAX#14 segmenter without stop_at_punctuation. 267 addAccessibleTask("mac/doc_textmarker_test.html", async (browser, accDoc) => { 268 await SpecialPowers.pushPrefEnv({ 269 set: [ 270 ["intl.icu4x.segmenter.enabled", true], 271 ["layout.word_select.stop_at_punctuation", false], 272 ], 273 }); 274 275 const expectedValues = await SpecialPowers.spawn(browser, [], async () => { 276 return content.wrappedJSObject.getExpected(true, false); 277 }); 278 279 testMarkerIntegrity(accDoc, expectedValues); 280 281 await SpecialPowers.popPrefEnv(); 282 }); 283 284 // new UAX#14 segmenter with stop_at_punctuation 285 addAccessibleTask("mac/doc_textmarker_test.html", async (browser, accDoc) => { 286 await SpecialPowers.pushPrefEnv({ 287 set: [ 288 ["intl.icu4x.segmenter.enabled", true], 289 ["layout.word_select.stop_at_punctuation", true], // this is default 290 ], 291 }); 292 293 const expectedValues = await SpecialPowers.spawn(browser, [], async () => { 294 return content.wrappedJSObject.getExpected(true, true); 295 }); 296 297 testMarkerIntegrity(accDoc, expectedValues); 298 299 await SpecialPowers.popPrefEnv(); 300 }); 301 302 // Test text marker lesser-than operator 303 addAccessibleTask( 304 `<p id="p">hello <a id="a" href="#">goodbye</a> world</p>`, 305 async (browser, accDoc) => { 306 let macDoc = accDoc.nativeInterface.QueryInterface( 307 Ci.nsIAccessibleMacInterface 308 ); 309 310 let start = macDoc.getParameterizedAttributeValue( 311 "AXTextMarkerForIndex", 312 1 313 ); 314 let end = macDoc.getParameterizedAttributeValue("AXTextMarkerForIndex", 10); 315 316 let range = macDoc.getParameterizedAttributeValue( 317 "AXTextMarkerRangeForUnorderedTextMarkers", 318 [end, start] 319 ); 320 is(stringForRange(macDoc, range), "ello good"); 321 } 322 ); 323 324 addAccessibleTask( 325 `<input id="input" value=""><a href="#">goodbye</a>`, 326 async (browser, accDoc) => { 327 let macDoc = accDoc.nativeInterface.QueryInterface( 328 Ci.nsIAccessibleMacInterface 329 ); 330 331 let input = getNativeInterface(accDoc, "input"); 332 333 let range = macDoc.getParameterizedAttributeValue( 334 "AXTextMarkerRangeForUIElement", 335 input 336 ); 337 338 is(stringForRange(macDoc, range), "", "string value is correct"); 339 } 340 ); 341 342 addAccessibleTask( 343 `<div role="listbox" id="box"> 344 <input type="radio" name="test" role="option" title="First item"/> 345 <input type="radio" name="test" role="option" title="Second item"/> 346 </div>`, 347 async (browser, accDoc) => { 348 let box = getNativeInterface(accDoc, "box"); 349 const children = box.getAttributeValue("AXChildren"); 350 is(children.length, 2, "Listbox contains two items"); 351 is(children[0].getAttributeValue("AXValue"), "First item"); 352 is(children[1].getAttributeValue("AXValue"), "Second item"); 353 } 354 ); 355 356 addAccessibleTask( 357 `<div id="t"> 358 A link <b>should</b> explain <u>clearly</u> what information the <i>reader</i> will get by clicking on that link. 359 </div>`, 360 async (browser, accDoc) => { 361 let t = getNativeInterface(accDoc, "t"); 362 const children = t.getAttributeValue("AXChildren"); 363 const expectedTitles = [ 364 "A link ", 365 "should", 366 " explain ", 367 "clearly", 368 " what information the ", 369 "reader", 370 " will get by clicking on that link. ", 371 ]; 372 is(children.length, 7, "container has seven children"); 373 children.forEach((child, index) => { 374 is(child.getAttributeValue("AXValue"), expectedTitles[index]); 375 }); 376 } 377 ); 378 379 addAccessibleTask( 380 `<a href="#">link</a> <input id="input" value="hello">`, 381 async (browser, accDoc) => { 382 let macDoc = accDoc.nativeInterface.QueryInterface( 383 Ci.nsIAccessibleMacInterface 384 ); 385 386 let input = getNativeInterface(accDoc, "input"); 387 let range = macDoc.getParameterizedAttributeValue( 388 "AXTextMarkerRangeForUIElement", 389 input 390 ); 391 392 let firstMarkerInInput = macDoc.getParameterizedAttributeValue( 393 "AXStartTextMarkerForTextMarkerRange", 394 range 395 ); 396 397 let leftWordRange = macDoc.getParameterizedAttributeValue( 398 "AXLeftWordTextMarkerRangeForTextMarker", 399 firstMarkerInInput 400 ); 401 let str = macDoc.getParameterizedAttributeValue( 402 "AXStringForTextMarkerRange", 403 leftWordRange 404 ); 405 is(str, "hello", "Left word at start of input should be right word"); 406 } 407 ); 408 409 addAccessibleTask(`<p id="p">hello world</p>`, async (browser, accDoc) => { 410 let macDoc = accDoc.nativeInterface.QueryInterface( 411 Ci.nsIAccessibleMacInterface 412 ); 413 414 let p = getNativeInterface(accDoc, "p"); 415 let range = macDoc.getParameterizedAttributeValue( 416 "AXTextMarkerRangeForUIElement", 417 p 418 ); 419 420 let bounds = macDoc.getParameterizedAttributeValue( 421 "AXBoundsForTextMarkerRange", 422 range 423 ); 424 425 ok(bounds.origin && bounds.size, "Returned valid bounds"); 426 427 // Test bounds of collapsed range. 428 429 let marker = macDoc.getParameterizedAttributeValue( 430 "AXStartTextMarkerForTextMarkerRange", 431 range 432 ); 433 434 let collapsedRange = macDoc.getParameterizedAttributeValue( 435 "AXTextMarkerRangeForUnorderedTextMarkers", 436 [marker, marker] 437 ); 438 439 let collapsedBounds = macDoc.getParameterizedAttributeValue( 440 "AXBoundsForTextMarkerRange", 441 collapsedRange 442 ); 443 444 ok( 445 collapsedBounds.origin && collapsedBounds.size, 446 "Returned valid collapsed bounds" 447 ); 448 449 let nextMarker = macDoc.getParameterizedAttributeValue( 450 "AXNextTextMarkerForTextMarker", 451 marker 452 ); 453 454 range = macDoc.getParameterizedAttributeValue( 455 "AXTextMarkerRangeForUnorderedTextMarkers", 456 [marker, nextMarker] 457 ); 458 459 is(stringForRange(macDoc, range), "h", "range is first letter"); 460 461 bounds = macDoc.getParameterizedAttributeValue( 462 "AXBoundsForTextMarkerRange", 463 range 464 ); 465 466 Assert.less( 467 collapsedBounds.size[0], 468 bounds.size[0], 469 "Collapsed range is smaller than character range" 470 ); 471 });