diff options
Diffstat (limited to 'accessible/tests/mochitest/text/test_atcaretoffset.html')
-rw-r--r-- | accessible/tests/mochitest/text/test_atcaretoffset.html | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/accessible/tests/mochitest/text/test_atcaretoffset.html b/accessible/tests/mochitest/text/test_atcaretoffset.html new file mode 100644 index 0000000000..33fe7cd793 --- /dev/null +++ b/accessible/tests/mochitest/text/test_atcaretoffset.html @@ -0,0 +1,425 @@ +<!DOCTYPE html> +<html> +<head> + <title>Test: nsIAccessibleText getText* functions at caret offset</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../states.js"></script> + <script type="application/javascript" + src="../events.js"></script> + <script type="application/javascript" + src="../text.js"></script> + + <script type="application/javascript"> + // gA11yEventDumpToConsole = true; // debugging + + function traverseTextByLines(aQueue, aID, aLines) { + var wholeText = ""; + for (var i = 0; i < aLines.length ; i++) + wholeText += aLines[i][0] + aLines[i][1]; + + var baseInvokerFunc = synthClick; + var charIter = new charIterator(wholeText, aLines); + // charIter.debugOffset = 10; // enable to run tests at given offset only + + while (charIter.next()) { + aQueue.push(new tmpl_moveTo(aID, baseInvokerFunc, wholeText, charIter)); + baseInvokerFunc = synthRightKey; + } + } + + /** + * Used to get test list for each traversed character. + */ + function charIterator(aWholeText, aLines) { + this.next = function charIterator_next() { + // Don't increment offset if we are at end of the wrapped line + // (offset is shared between end of this line and start of next line). + if (this.mAtWrappedLineEnd) { + this.mAtWrappedLineEnd = false; + this.mLine = this.mLine.nextLine; + return true; + } + + this.mOffset++; + if (this.mOffset > aWholeText.length) + return false; + + var nextLine = this.mLine.nextLine; + if (!nextLine.isFakeLine() && this.mOffset == nextLine.start) { + if (nextLine.start == this.mLine.end) + this.mAtWrappedLineEnd = true; + else + this.mLine = nextLine; + } + + return true; + }; + + Object.defineProperty(this, "offset", { get() { return this.mOffset; }, + }); + + Object.defineProperty(this, "offsetDescr", { get() { + return this.mOffset + " offset (" + this.mLine.number + " line, " + + (this.mOffset - this.mLine.start) + " offset on the line)"; + }, + }); + + Object.defineProperty(this, "tests", { get() { + // Line boundary tests. + var cLine = this.mLine; + var pLine = cLine.prevLine; + var ppLine = pLine.prevLine; + var nLine = cLine.nextLine; + var nnLine = nLine.nextLine; + + var lineTests = [ + [ testTextBeforeOffset, BOUNDARY_LINE_START, pLine.start, cLine.start], + [ testTextBeforeOffset, BOUNDARY_LINE_END, ppLine.end, pLine.end], + [ testTextAtOffset, BOUNDARY_LINE_START, cLine.start, nLine.start], + [ testTextAtOffset, BOUNDARY_LINE_END, pLine.end, cLine.end], + [ testTextAfterOffset, BOUNDARY_LINE_START, nLine.start, nnLine.start], + [ testTextAfterOffset, BOUNDARY_LINE_END, cLine.end, nLine.end], + ]; + + // Word boundary tests. + var cWord = this.mLine.firstWord; + var nWord = cWord.nextWord, pWord = cWord.prevWord; + + // The current word is a farthest word starting at or after the offset. + if (this.mOffset >= nWord.start) { + while (this.mOffset >= nWord.start && !this.mLine.isLastWord(cWord)) { + cWord = nWord; + nWord = nWord.nextWord; + } + pWord = cWord.prevWord; + } else if (this.mOffset < cWord.start) { + while (this.mOffset < cWord.start) { + cWord = pWord; + pWord = pWord.prevWord; + } + nWord = cWord.nextWord; + } + + var nnWord = nWord.nextWord, ppWord = pWord.prevWord; + + var isAfterWordEnd = + this.mOffset > cWord.end || cWord.line != this.mLine; + var isAtOrAfterWordEnd = (this.mOffset >= cWord.end); + var useNextWordForAtWordEnd = + isAtOrAfterWordEnd && this.mOffset != aWholeText.length; + + var wordTests = [ + [ testTextBeforeOffset, BOUNDARY_WORD_START, + pWord.start, cWord.start ], + [ testTextBeforeOffset, BOUNDARY_WORD_END, + (isAfterWordEnd ? pWord : ppWord).end, + (isAfterWordEnd ? cWord : pWord).end ], + [ testTextAtOffset, BOUNDARY_WORD_START, + cWord.start, nWord.start ], + [ testTextAtOffset, BOUNDARY_WORD_END, + (useNextWordForAtWordEnd ? cWord : pWord).end, + (useNextWordForAtWordEnd ? nWord : cWord).end ], + [ testTextAfterOffset, BOUNDARY_WORD_START, + nWord.start, nnWord.start ], + [ testTextAfterOffset, BOUNDARY_WORD_END, + (isAfterWordEnd ? nWord : cWord).end, + (isAfterWordEnd ? nnWord : nWord).end ], + ]; + + // Character boundary tests. + var prevOffset = this.offset > 1 ? this.offset - 1 : 0; + var nextOffset = this.offset >= aWholeText.length ? + this.offset : this.offset + 1; + var nextAfterNextOffset = nextOffset >= aWholeText.length ? + nextOffset : nextOffset + 1; + + var charTests = [ + [ testTextBeforeOffset, BOUNDARY_CHAR, + prevOffset, this.offset ], + [ testTextAtOffset, BOUNDARY_CHAR, + this.offset, + this.mAtWrappedLineEnd ? this.offset : nextOffset ], + [ testTextAfterOffset, BOUNDARY_CHAR, + this.mAtWrappedLineEnd ? this.offset : nextOffset, + this.mAtWrappedLineEnd ? nextOffset : nextAfterNextOffset ], + ]; + + return lineTests.concat(wordTests.concat(charTests)); + }, + }); + + Object.defineProperty(this, "failures", { get() { + if (this.mOffset == this.mLine.start) + return this.mLine.lineStartFailures; + if (this.mOffset == this.mLine.end) + return this.mLine.lineEndFailures; + return []; + }, + }); + + this.mOffset = -1; + this.mLine = new line(aWholeText, aLines, 0); + this.mAtWrappedLineEnd = false; + this.mWord = this.mLine.firstWord; + } + + /** + * A line object. Allows to navigate by lines and by words. + */ + function line(aWholeText, aLines, aIndex) { + Object.defineProperty(this, "prevLine", { get() { + return new line(aWholeText, aLines, aIndex - 1); + }, + }); + Object.defineProperty(this, "nextLine", { get() { + return new line(aWholeText, aLines, aIndex + 1); + }, + }); + + Object.defineProperty(this, "start", { get() { + if (aIndex < 0) + return 0; + + if (aIndex >= aLines.length) + return aWholeText.length; + + return aLines[aIndex][2]; + }, + }); + Object.defineProperty(this, "end", { get() { + if (aIndex < 0) + return 0; + + if (aIndex >= aLines.length) + return aWholeText.length; + + return aLines[aIndex][3]; + }, + }); + + Object.defineProperty(this, "number", { get() { return aIndex; }, + }); + Object.defineProperty(this, "wholeText", { get() { return aWholeText; }, + }); + this.isFakeLine = function line_isFakeLine() { + return aIndex < 0 || aIndex >= aLines.length; + }; + + Object.defineProperty(this, "lastWord", { get() { + if (aIndex < 0) + return new word(this, [], -1); + if (aIndex >= aLines.length) + return new word(this, [], 0); + + var words = aLines[aIndex][4].words; + return new word(this, words, words.length - 2); + }, + }); + Object.defineProperty(this, "firstWord", { get() { + if (aIndex < 0) + return new word(this, [], -1); + if (aIndex >= aLines.length) + return new word(this, [], 0); + + var words = aLines[aIndex][4].words; + return new word(this, words, 0); + }, + }); + + this.isLastWord = function line_isLastWord(aWord) { + var lastWord = this.lastWord; + return lastWord.start == aWord.start && lastWord.end == aWord.end; + }; + + Object.defineProperty(this, "lineStartFailures", { get() { + if (aIndex < 0 || aIndex >= aLines.length) + return []; + + return aLines[aIndex][4].lsf || []; + }, + }); + Object.defineProperty(this, "lineEndFailures", { get() { + if (aIndex < 0 || aIndex >= aLines.length) + return []; + + return aLines[aIndex][4].lef || []; + }, + }); + } + + /** + * A word object. Allows to navigate by words. + */ + function word(aLine, aWords, aIndex) { + Object.defineProperty(this, "prevWord", { get() { + if (aIndex >= 2) + return new word(aLine, aWords, aIndex - 2); + + var prevLineLastWord = aLine.prevLine.lastWord; + if (this.start == prevLineLastWord.start && !this.isFakeStartWord()) + return prevLineLastWord.prevWord; + return prevLineLastWord; + }, + }); + Object.defineProperty(this, "nextWord", { get() { + if (aIndex + 2 < aWords.length) + return new word(aLine, aWords, aIndex + 2); + + var nextLineFirstWord = aLine.nextLine.firstWord; + if (this.end == nextLineFirstWord.end && !this.isFakeEndWord()) + return nextLineFirstWord.nextWord; + return nextLineFirstWord; + }, + }); + + Object.defineProperty(this, "line", { get() { return aLine; } }); + + Object.defineProperty(this, "start", { get() { + if (this.isFakeStartWord()) + return 0; + + if (this.isFakeEndWord()) + return aLine.end; + return aWords[aIndex]; + }, + }); + Object.defineProperty(this, "end", { get() { + if (this.isFakeStartWord()) + return 0; + + return this.isFakeEndWord() ? aLine.end : aWords[aIndex + 1]; + }, + }); + + this.toString = function word_toString() { + var start = this.start, end = this.end; + return "'" + aLine.wholeText.substring(start, end) + + "' at [" + start + ", " + end + "]"; + }; + + this.isFakeStartWord = function() { return aIndex < 0; }; + this.isFakeEndWord = function() { return aIndex >= aWords.length; }; + } + + /** + * A template invoker to move through the text. + */ + function tmpl_moveTo(aID, aInvokerFunc, aWholeText, aCharIter) { + this.offset = aCharIter.offset; + + var checker = new caretMoveChecker(this.offset, true, aID); + this.__proto__ = new (aInvokerFunc)(aID, checker); + + this.finalCheck = function genericMoveTo_finalCheck() { + if (this.noTests()) + return; + + for (var i = 0; i < this.tests.length; i++) { + var func = this.tests[i][0]; + var boundary = this.tests[i][1]; + var startOffset = this.tests[i][2]; + var endOffset = this.tests[i][3]; + var text = aWholeText.substring(startOffset, endOffset); + + var isOk1 = kOk, isOk2 = kOk, isOk3 = kOk; + for (var fIdx = 0; fIdx < this.failures.length; fIdx++) { + var failure = this.failures[fIdx]; + if (func.name.includes(failure[0]) && boundary == failure[1]) { + isOk1 = failure[2]; + isOk2 = failure[3]; + isOk3 = failure[4]; + } + } + + func(kCaretOffset, boundary, text, startOffset, endOffset, + aID, isOk1, isOk2, isOk3); + } + }; + + this.getID = function genericMoveTo_getID() { + return "move to " + this.offsetDescr; + }; + + this.noTests = function tmpl_moveTo_noTests() { + return ("debugOffset" in aCharIter) && + (aCharIter.debugOffset != this.offset); + }; + + this.offsetDescr = aCharIter.offsetDescr; + this.tests = this.noTests() ? null : aCharIter.tests; + this.failures = aCharIter.failures; + } + + var gQueue = null; + function doTest() { + gQueue = new eventQueue(); + + // __a__w__o__r__d__\n + // 0 1 2 3 4 5 + // __t__w__o__ (soft line break) + // 6 7 8 9 + // __w__o__r__d__s + // 10 11 12 13 14 15 + + traverseTextByLines(gQueue, "textarea", + [ [ "aword", "\n", 0, 5, { words: [ 0, 5 ] } ], + [ "two ", "", 6, 10, { words: [ 6, 9 ] } ], + [ "words", "", 10, 15, { words: [ 10, 15 ] } ], + ] ); + + var line4 = [ // "riend " + [ "TextBeforeOffset", BOUNDARY_WORD_END, + kOk, kOk, kOk], + [ "TextAfterOffset", BOUNDARY_WORD_END, + kOk, kOk, kOk ], + ]; + traverseTextByLines(gQueue, "ta_wrapped", + [ [ "hi ", "", 0, 3, { words: [ 0, 2 ] } ], + [ "hello ", "", 3, 9, { words: [ 3, 8 ] } ], + [ "my ", "", 9, 12, { words: [ 9, 11 ] } ], + [ "longf", "", 12, 17, { words: [ 12, 17 ] } ], + [ "riend ", "", 17, 23, { words: [ 17, 22 ], lsf: line4 } ], + [ "t sq ", "", 23, 28, { words: [ 23, 24, 25, 27 ] } ], + [ "t", "", 28, 29, { words: [ 28, 29 ] } ], + ] ); + + gQueue.invoke(); // will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="nsIAccessibleText getText related functions tests at caret offset" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=852021"> + Bug 852021 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + + <textarea id="textarea" cols="5">aword +two words</textarea> + + <!-- scrollbar-width: none is needed so that the width of the scrollbar + doesn't incorrectly affect the width of the textarea on some systems. + See bug 1600170 and bug 33654. + --> + <textarea id="ta_wrapped" cols="5" style="scrollbar-width: none;">hi hello my longfriend t sq t</textarea> + </pre> +</body> +</html> |