1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/accessible/tests/mochitest/text/test_atcaretoffset.html Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,463 @@ 1.4 +<!DOCTYPE html> 1.5 +<html> 1.6 +<head> 1.7 + <title>Test: nsIAccessibleText getText* functions at caret offset</title> 1.8 + 1.9 + <link rel="stylesheet" type="text/css" 1.10 + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> 1.11 + 1.12 + <script type="application/javascript" 1.13 + src="chrome://mochikit/content/MochiKit/packed.js"></script> 1.14 + <script type="application/javascript" 1.15 + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> 1.16 + <script type="application/javascript" 1.17 + src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> 1.18 + 1.19 + <script type="application/javascript" 1.20 + src="../common.js"></script> 1.21 + <script type="application/javascript" 1.22 + src="../role.js"></script> 1.23 + <script type="application/javascript" 1.24 + src="../states.js"></script> 1.25 + <script type="application/javascript" 1.26 + src="../events.js"></script> 1.27 + <script type="application/javascript" 1.28 + src="../text.js"></script> 1.29 + 1.30 + <script type="application/javascript"> 1.31 + //gA11yEventDumpToConsole = true; // debugging 1.32 + 1.33 + function traverseTextByLines(aQueue, aID, aLines) 1.34 + { 1.35 + var wholeText = ""; 1.36 + for (var i = 0; i < aLines.length ; i++) 1.37 + wholeText += aLines[i][0] + aLines[i][1]; 1.38 + 1.39 + var baseInvokerFunc = synthClick; 1.40 + var charIter = new charIterator(wholeText, aLines); 1.41 + //charIter.debugOffset = 10; // enable to run tests at given offset only 1.42 + 1.43 + while (charIter.next()) { 1.44 + aQueue.push(new tmpl_moveTo(aID, baseInvokerFunc, wholeText, charIter)); 1.45 + baseInvokerFunc = synthRightKey; 1.46 + } 1.47 + } 1.48 + 1.49 + /** 1.50 + * Used to get test list for each traversed character. 1.51 + */ 1.52 + function charIterator(aWholeText, aLines) 1.53 + { 1.54 + this.next = function charIterator_next() 1.55 + { 1.56 + // Don't increment offset if we are at end of the wrapped line 1.57 + // (offset is shared between end of this line and start of next line). 1.58 + if (this.mAtWrappedLineEnd) { 1.59 + this.mAtWrappedLineEnd = false; 1.60 + this.mLine = this.mLine.nextLine; 1.61 + return true; 1.62 + } 1.63 + 1.64 + this.mOffset++; 1.65 + if (this.mOffset > aWholeText.length) 1.66 + return false; 1.67 + 1.68 + var nextLine = this.mLine.nextLine; 1.69 + if (!nextLine.isFakeLine() && this.mOffset == nextLine.start) { 1.70 + if (nextLine.start == this.mLine.end) 1.71 + this.mAtWrappedLineEnd = true; 1.72 + else 1.73 + this.mLine = nextLine; 1.74 + } 1.75 + 1.76 + return true; 1.77 + } 1.78 + 1.79 + Object.defineProperty(this, "offset", { get: function() 1.80 + { return this.mOffset; } 1.81 + }); 1.82 + 1.83 + Object.defineProperty(this, "offsetDescr", { get: function() 1.84 + { 1.85 + return this.mOffset + " offset (" + this.mLine.number + " line, " + 1.86 + (this.mOffset - this.mLine.start) + " offset on the line)"; 1.87 + } 1.88 + }); 1.89 + 1.90 + Object.defineProperty(this, "tests", { get: function() 1.91 + { 1.92 + // Line boundary tests. 1.93 + var cLine = this.mLine; 1.94 + var pLine = cLine.prevLine; 1.95 + var ppLine = pLine.prevLine; 1.96 + var nLine = cLine.nextLine; 1.97 + var nnLine = nLine.nextLine; 1.98 + 1.99 + var lineTests = [ 1.100 + [ testTextBeforeOffset, BOUNDARY_LINE_START, pLine.start, cLine.start], 1.101 + [ testTextBeforeOffset, BOUNDARY_LINE_END, ppLine.end, pLine.end], 1.102 + [ testTextAtOffset, BOUNDARY_LINE_START, cLine.start, nLine.start], 1.103 + [ testTextAtOffset, BOUNDARY_LINE_END, pLine.end, cLine.end], 1.104 + [ testTextAfterOffset, BOUNDARY_LINE_START, nLine.start, nnLine.start], 1.105 + [ testTextAfterOffset, BOUNDARY_LINE_END, cLine.end, nLine.end] 1.106 + ]; 1.107 + 1.108 + // Word boundary tests. 1.109 + var cWord = this.mLine.firstWord; 1.110 + var nWord = cWord.nextWord, pWord = cWord.prevWord; 1.111 + 1.112 + // The current word is a farthest word starting at or after the offset. 1.113 + if (this.mOffset >= nWord.start) { 1.114 + while (this.mOffset >= nWord.start && !this.mLine.isLastWord(cWord)) { 1.115 + cWord = nWord; 1.116 + nWord = nWord.nextWord; 1.117 + } 1.118 + pWord = cWord.prevWord; 1.119 + 1.120 + } else if (this.mOffset < cWord.start) { 1.121 + while (this.mOffset < cWord.start) { 1.122 + cWord = pWord; 1.123 + pWord = pWord.prevWord; 1.124 + } 1.125 + nWord = cWord.nextWord; 1.126 + } 1.127 + 1.128 + var nnWord = nWord.nextWord, ppWord = pWord.prevWord; 1.129 + 1.130 + var isAfterWordEnd = 1.131 + this.mOffset > cWord.end || cWord.line != this.mLine; 1.132 + var isAtOrAfterWordEnd = (this.mOffset >= cWord.end); 1.133 + var useNextWordForAtWordEnd = 1.134 + isAtOrAfterWordEnd && this.mOffset != aWholeText.length; 1.135 + 1.136 + var wordTests = [ 1.137 + [ testTextBeforeOffset, BOUNDARY_WORD_START, 1.138 + pWord.start, cWord.start ], 1.139 + [ testTextBeforeOffset, BOUNDARY_WORD_END, 1.140 + (isAfterWordEnd ? pWord : ppWord).end, 1.141 + (isAfterWordEnd ? cWord : pWord).end ], 1.142 + [ testTextAtOffset, BOUNDARY_WORD_START, 1.143 + cWord.start, nWord.start ], 1.144 + [ testTextAtOffset, BOUNDARY_WORD_END, 1.145 + (useNextWordForAtWordEnd ? cWord : pWord).end, 1.146 + (useNextWordForAtWordEnd ? nWord : cWord).end ], 1.147 + [ testTextAfterOffset, BOUNDARY_WORD_START, 1.148 + nWord.start, nnWord.start ], 1.149 + [ testTextAfterOffset, BOUNDARY_WORD_END, 1.150 + (isAfterWordEnd ? nWord : cWord).end, 1.151 + (isAfterWordEnd ? nnWord : nWord).end ] 1.152 + ]; 1.153 + 1.154 + // Character boundary tests. 1.155 + var prevOffset = this.offset > 1 ? this.offset - 1 : 0; 1.156 + var nextOffset = this.offset >= aWholeText.length ? 1.157 + this.offset : this.offset + 1; 1.158 + var nextAfterNextOffset = nextOffset >= aWholeText.length ? 1.159 + nextOffset : nextOffset + 1; 1.160 + 1.161 + var charTests = [ 1.162 + [ testTextBeforeOffset, BOUNDARY_CHAR, 1.163 + prevOffset, this.offset ], 1.164 + [ testTextAtOffset, BOUNDARY_CHAR, 1.165 + this.offset, 1.166 + this.mAtWrappedLineEnd ? this.offset : nextOffset ], 1.167 + [ testTextAfterOffset, BOUNDARY_CHAR, 1.168 + this.mAtWrappedLineEnd ? this.offset : nextOffset, 1.169 + this.mAtWrappedLineEnd ? nextOffset : nextAfterNextOffset ] 1.170 + ]; 1.171 + 1.172 + return lineTests.concat(wordTests.concat(charTests)); 1.173 + } 1.174 + }); 1.175 + 1.176 + Object.defineProperty(this, "failures", { get: function() 1.177 + { 1.178 + if (this.mOffset == this.mLine.start) 1.179 + return this.mLine.lineStartFailures; 1.180 + if (this.mOffset == this.mLine.end) 1.181 + return this.mLine.lineEndFailures; 1.182 + return []; 1.183 + } 1.184 + }); 1.185 + 1.186 + this.mOffset = -1; 1.187 + this.mLine = new line(aWholeText, aLines, 0); 1.188 + this.mAtWrappedLineEnd = false; 1.189 + this.mWord = this.mLine.firstWord; 1.190 + } 1.191 + 1.192 + /** 1.193 + * A line object. Allows to navigate by lines and by words. 1.194 + */ 1.195 + function line(aWholeText, aLines, aIndex) 1.196 + { 1.197 + Object.defineProperty(this, "prevLine", { get: function() 1.198 + { 1.199 + return new line(aWholeText, aLines, aIndex - 1); 1.200 + } 1.201 + }); 1.202 + Object.defineProperty(this, "nextLine", { get: function() 1.203 + { 1.204 + return new line(aWholeText, aLines, aIndex + 1); 1.205 + } 1.206 + }); 1.207 + 1.208 + Object.defineProperty(this, "start", { get: function() 1.209 + { 1.210 + if (aIndex < 0) 1.211 + return 0; 1.212 + 1.213 + if (aIndex >= aLines.length) 1.214 + return aWholeText.length; 1.215 + 1.216 + return aLines[aIndex][2]; 1.217 + } 1.218 + }); 1.219 + Object.defineProperty(this, "end", { get: function() 1.220 + { 1.221 + if (aIndex < 0) 1.222 + return 0; 1.223 + 1.224 + if (aIndex >= aLines.length) 1.225 + return aWholeText.length; 1.226 + 1.227 + return aLines[aIndex][3]; 1.228 + } 1.229 + }); 1.230 + 1.231 + Object.defineProperty(this, "number", { get: function() 1.232 + { return aIndex; } 1.233 + }); 1.234 + Object.defineProperty(this, "wholeText", { get: function() 1.235 + { return aWholeText; } 1.236 + }); 1.237 + this.isFakeLine = function line_isFakeLine() 1.238 + { 1.239 + return aIndex < 0 || aIndex >= aLines.length; 1.240 + } 1.241 + 1.242 + Object.defineProperty(this, "lastWord", { get: function() 1.243 + { 1.244 + if (aIndex < 0) 1.245 + return new word(this, [], -1); 1.246 + if (aIndex >= aLines.length) 1.247 + return new word(this, [], 0); 1.248 + 1.249 + var words = aLines[aIndex][4].words; 1.250 + return new word(this, words, words.length - 2); 1.251 + } 1.252 + }); 1.253 + Object.defineProperty(this, "firstWord", { get: function() 1.254 + { 1.255 + if (aIndex < 0) 1.256 + return new word(this, [], -1); 1.257 + if (aIndex >= aLines.length) 1.258 + return new word(this, [], 0); 1.259 + 1.260 + var words = aLines[aIndex][4].words; 1.261 + return new word(this, words, 0); 1.262 + } 1.263 + }); 1.264 + 1.265 + this.isLastWord = function line_isLastWord(aWord) 1.266 + { 1.267 + var lastWord = this.lastWord; 1.268 + return lastWord.start == aWord.start && lastWord.end == aWord.end; 1.269 + } 1.270 + 1.271 + Object.defineProperty(this, "lineStartFailures", { get: function() 1.272 + { 1.273 + if (aIndex < 0 || aIndex >= aLines.length) 1.274 + return []; 1.275 + 1.276 + return aLines[aIndex][4].lsf || []; 1.277 + } 1.278 + }); 1.279 + Object.defineProperty(this, "lineEndFailures", { get: function() 1.280 + { 1.281 + if (aIndex < 0 || aIndex >= aLines.length) 1.282 + return []; 1.283 + 1.284 + return aLines[aIndex][4].lef || []; 1.285 + } 1.286 + }); 1.287 + } 1.288 + 1.289 + /** 1.290 + * A word object. Allows to navigate by words. 1.291 + */ 1.292 + function word(aLine, aWords, aIndex) 1.293 + { 1.294 + Object.defineProperty(this, "prevWord", { get: function() 1.295 + { 1.296 + if (aIndex >= 2) 1.297 + return new word(aLine, aWords, aIndex - 2); 1.298 + 1.299 + var prevLineLastWord = aLine.prevLine.lastWord; 1.300 + if (this.start == prevLineLastWord.start && !this.isFakeStartWord()) 1.301 + return prevLineLastWord.prevWord; 1.302 + return prevLineLastWord; 1.303 + } 1.304 + }); 1.305 + Object.defineProperty(this, "nextWord", { get: function() 1.306 + { 1.307 + if (aIndex + 2 < aWords.length) 1.308 + return new word(aLine, aWords, aIndex + 2); 1.309 + 1.310 + var nextLineFirstWord = aLine.nextLine.firstWord; 1.311 + if (this.end == nextLineFirstWord.end && !this.isFakeEndWord()) 1.312 + return nextLineFirstWord.nextWord; 1.313 + return nextLineFirstWord; 1.314 + } 1.315 + }); 1.316 + 1.317 + Object.defineProperty(this, "line", { get: function() { return aLine; } }); 1.318 + 1.319 + Object.defineProperty(this, "start", { get: function() 1.320 + { 1.321 + if (this.isFakeStartWord()) 1.322 + return 0; 1.323 + 1.324 + if (this.isFakeEndWord()) 1.325 + return aLine.end; 1.326 + return aWords[aIndex]; 1.327 + } 1.328 + }); 1.329 + Object.defineProperty(this, "end", { get: function() 1.330 + { 1.331 + if (this.isFakeStartWord()) 1.332 + return 0; 1.333 + 1.334 + return this.isFakeEndWord() ? aLine.end : aWords[aIndex + 1]; 1.335 + } 1.336 + }); 1.337 + 1.338 + this.toString = function word_toString() 1.339 + { 1.340 + var start = this.start, end = this.end; 1.341 + return "'" + aLine.wholeText.substring(start, end) + 1.342 + "' at [" + start + ", " + end + "]"; 1.343 + } 1.344 + 1.345 + this.isFakeStartWord = function() { return aIndex < 0; } 1.346 + this.isFakeEndWord = function() { return aIndex >= aWords.length; } 1.347 + } 1.348 + 1.349 + /** 1.350 + * A template invoker to move through the text. 1.351 + */ 1.352 + function tmpl_moveTo(aID, aInvokerFunc, aWholeText, aCharIter) 1.353 + { 1.354 + this.offset = aCharIter.offset; 1.355 + 1.356 + var checker = new caretMoveChecker(this.offset, aID); 1.357 + this.__proto__ = new (aInvokerFunc)(aID, checker); 1.358 + 1.359 + this.finalCheck = function genericMoveTo_finalCheck() 1.360 + { 1.361 + if (this.noTests()) 1.362 + return; 1.363 + 1.364 + for (var i = 0; i < this.tests.length; i++) { 1.365 + var func = this.tests[i][0]; 1.366 + var boundary = this.tests[i][1]; 1.367 + var startOffset = this.tests[i][2]; 1.368 + var endOffset = this.tests[i][3]; 1.369 + var text = aWholeText.substring(startOffset, endOffset); 1.370 + 1.371 + var isOk1 = kOk, isOk2 = kOk, isOk3 = kOk; 1.372 + for (var fIdx = 0; fIdx < this.failures.length; fIdx++) { 1.373 + var failure = this.failures[fIdx]; 1.374 + if (func.name.indexOf(failure[0]) != -1 && boundary == failure[1]) { 1.375 + isOk1 = failure[2]; 1.376 + isOk2 = failure[3]; 1.377 + isOk3 = failure[4]; 1.378 + } 1.379 + } 1.380 + 1.381 + func.call(null, kCaretOffset, boundary, text, startOffset, endOffset, 1.382 + aID, isOk1, isOk2, isOk3); 1.383 + } 1.384 + } 1.385 + 1.386 + this.getID = function genericMoveTo_getID() 1.387 + { 1.388 + return "move to " + this.offsetDescr; 1.389 + } 1.390 + 1.391 + this.noTests = function tmpl_moveTo_noTests() 1.392 + { 1.393 + return ("debugOffset" in aCharIter) && 1.394 + (aCharIter.debugOffset != this.offset); 1.395 + } 1.396 + 1.397 + this.offsetDescr = aCharIter.offsetDescr; 1.398 + this.tests = this.noTests() ? null : aCharIter.tests; 1.399 + this.failures = aCharIter.failures; 1.400 + } 1.401 + 1.402 + var gQueue = null; 1.403 + function doTest() 1.404 + { 1.405 + gQueue = new eventQueue(); 1.406 + 1.407 + // __a__w__o__r__d__\n 1.408 + // 0 1 2 3 4 5 1.409 + // __t__w__o__ (soft line break) 1.410 + // 6 7 8 9 1.411 + // __w__o__r__d__s 1.412 + // 10 11 12 13 14 15 1.413 + 1.414 + traverseTextByLines(gQueue, "textarea", 1.415 + [ [ "aword", "\n", 0, 5, { words: [ 0, 5 ] } ], 1.416 + [ "two ", "", 6, 10, { words: [ 6, 9 ] } ], 1.417 + [ "words", "", 10, 15, { words: [ 10, 15 ] } ] 1.418 + ] ); 1.419 + 1.420 + var line2 = [ // " my " 1.421 + [ "TextBeforeOffset", BOUNDARY_WORD_END, kTodo, kTodo, kTodo ], 1.422 + [ "TextAfterOffset", BOUNDARY_WORD_END, kTodo, kTodo, kTodo ] 1.423 + ]; 1.424 + var line4 = [ // "riend" 1.425 + [ "TextBeforeOffset", BOUNDARY_WORD_END, kTodo, kTodo, kTodo ], 1.426 + [ "TextAfterOffset", BOUNDARY_WORD_END, kTodo, kTodo, kTodo ] 1.427 + ]; 1.428 + var line5 = [ // " t " 1.429 + [ "TextBeforeOffset", BOUNDARY_WORD_END, kTodo, kTodo, kTodo ], 1.430 + [ "TextAfterOffset", BOUNDARY_WORD_END, kTodo, kTodo, kTodo ] 1.431 + ]; 1.432 + traverseTextByLines(gQueue, "ta_wrapped", 1.433 + [ [ "hi ", "", 0, 3, { words: [ 0, 2 ] } ], 1.434 + [ "hello", "", 3, 8, { words: [ 3, 8 ] } ], 1.435 + [ " my ", "", 8, 12, { words: [ 9, 11 ], lsf: line2 } ], 1.436 + [ "longf", "", 12, 17, { words: [ 12, 17 ] } ], 1.437 + [ "riend", "", 17, 22, { words: [ 17, 22 ], lsf: line4 } ], 1.438 + [ " t ", "", 22, 25, { words: [ 23, 24 ], lsf: line5 } ], 1.439 + [ "sq t", "", 25, 29, { words: [ 25, 27, 28, 29 ] } ] 1.440 + ] ); 1.441 + 1.442 + gQueue.invoke(); // will call SimpleTest.finish(); 1.443 + } 1.444 + 1.445 + SimpleTest.waitForExplicitFinish(); 1.446 + addA11yLoadEvent(doTest); 1.447 + </script> 1.448 +</head> 1.449 +<body> 1.450 + 1.451 + <a target="_blank" 1.452 + title="nsIAccessibleText getText related functions tests at caret offset" 1.453 + href="https://bugzilla.mozilla.org/show_bug.cgi?id=852021"> 1.454 + Bug 852021 1.455 + </a> 1.456 + <p id="display"></p> 1.457 + <div id="content" style="display: none"></div> 1.458 + <pre id="test"> 1.459 + 1.460 + <textarea id="textarea" cols="5">aword 1.461 +two words</textarea> 1.462 + 1.463 + <textarea id="ta_wrapped" cols="5">hi hello my longfriend t sq t</textarea> 1.464 + </pre> 1.465 +</body> 1.466 +</html>