diff options
Diffstat (limited to '')
33 files changed, 5050 insertions, 0 deletions
diff --git a/accessible/tests/mochitest/text.js b/accessible/tests/mochitest/text.js new file mode 100644 index 0000000000..21392336a1 --- /dev/null +++ b/accessible/tests/mochitest/text.js @@ -0,0 +1,814 @@ +/* import-globals-from common.js */ + +// ////////////////////////////////////////////////////////////////////////////// +// Public + +const BOUNDARY_CHAR = nsIAccessibleText.BOUNDARY_CHAR; +const BOUNDARY_WORD_START = nsIAccessibleText.BOUNDARY_WORD_START; +const BOUNDARY_WORD_END = nsIAccessibleText.BOUNDARY_WORD_END; +const BOUNDARY_LINE_START = nsIAccessibleText.BOUNDARY_LINE_START; +const BOUNDARY_LINE_END = nsIAccessibleText.BOUNDARY_LINE_END; +const BOUNDARY_PARAGRAPH = nsIAccessibleText.BOUNDARY_PARAGRAPH; + +const kTextEndOffset = nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT; +const kCaretOffset = nsIAccessibleText.TEXT_OFFSET_CARET; + +const EndPoint_Start = nsIAccessibleTextRange.EndPoint_Start; +const EndPoint_End = nsIAccessibleTextRange.EndPoint_End; + +const kTodo = 1; // a test is expected to fail +const kOk = 2; // a test doesn't fail + +/** + * Test characterCount for the given array of accessibles. + * + * @param aCount [in] the expected character count + * @param aIDs [in] array of accessible identifiers to test + * @param aTodoFlag [in, optional] either kOk or kTodo + */ +function testCharacterCount(aIDs, aCount, aTodoFlag) { + var ids = aIDs instanceof Array ? aIDs : [aIDs]; + var isFunc = aTodoFlag == kTodo ? todo_is : is; + for (var i = 0; i < ids.length; i++) { + var textacc = getAccessible(ids[i], [nsIAccessibleText]); + isFunc( + textacc.characterCount, + aCount, + "Wrong character count for " + prettyName(ids[i]) + ); + } +} + +/** + * Test text between two given offsets. + * + * @param aIDs [in] an array of accessible IDs to test + * @param aStartOffset [in] the start offset within the text to test + * @param aEndOffset [in] the end offset up to which the text is tested + * @param aText [in] the expected result from the test + * @param aTodoFlag [in, optional] either kOk or kTodo + */ +function testText(aIDs, aStartOffset, aEndOffset, aText, aTodoFlag) { + var ids = aIDs instanceof Array ? aIDs : [aIDs]; + var isFunc = aTodoFlag == kTodo ? todo_is : is; + for (var i = 0; i < ids.length; i++) { + var acc = getAccessible(ids[i], nsIAccessibleText); + try { + isFunc( + acc.getText(aStartOffset, aEndOffset), + aText, + "getText: wrong text between start and end offsets '" + + aStartOffset + + "', '" + + aEndOffset + + " for '" + + prettyName(ids[i]) + + "'" + ); + } catch (e) { + ok( + false, + "getText fails between start and end offsets '" + + aStartOffset + + "', '" + + aEndOffset + + " for '" + + prettyName(ids[i]) + + "'" + ); + } + } +} + +/** + * Test getTextAtOffset for BOUNDARY_CHAR over different elements. + * + * @param aIDs [in] the accessible identifier or array of accessible + * identifiers + * @param aOffset [in] the offset to get a character at it + * @param aChar [in] the expected character + * @param aStartOffset [in] expected start offset of the character + * @param aEndOffset [in] expected end offset of the character + */ +function testCharAtOffset(aIDs, aOffset, aChar, aStartOffset, aEndOffset) { + var IDs = aIDs instanceof Array ? aIDs : [aIDs]; + for (var i = 0; i < IDs.length; i++) { + var acc = getAccessible(IDs[i], nsIAccessibleText); + testTextHelper( + IDs[i], + aOffset, + BOUNDARY_CHAR, + aChar, + aStartOffset, + aEndOffset, + kOk, + kOk, + kOk, + acc.getTextAtOffset, + "getTextAtOffset " + ); + } +} + +/** + * Test getTextAtOffset function over different elements. + * + * @param aIDs [in] ID or array of IDs + * @param aBoundaryType [in] boundary type for text to be retrieved + * @param aTestList [in] array of sets: + * offset1 and offset2 defining the offset range + * the text in the range + * start offset of the text in the range + * end offset of the text in the range + * + * or + * + * @param aOffset [in] the offset to get the text at + * @param aBoundaryType [in] Boundary type for text to be retrieved + * @param aText [in] expected return text for getTextAtOffset + * @param aStartOffset [in] expected return start offset for getTextAtOffset + * @param aEndOffset [in] expected return end offset for getTextAtOffset + * @param ... [in] list of ids or list of tuples made of: + * element identifier + * kTodo or kOk for returned text + * kTodo or kOk for returned start offset + * kTodo or kOk for returned offset result + */ +function testTextAtOffset() { + testTextSuperHelper("getTextAtOffset", arguments); +} + +/** + * Test getTextAfterOffset for BOUNDARY_CHAR over different elements. + * + * @param aIDs [in] the accessible identifier or array of accessible + * identifiers + * @param aOffset [in] the offset to get a character after it + * @param aChar [in] the expected character + * @param aStartOffset [in] expected start offset of the character + * @param aEndOffset [in] expected end offset of the character + */ +function testCharAfterOffset(aIDs, aOffset, aChar, aStartOffset, aEndOffset) { + var IDs = aIDs instanceof Array ? aIDs : [aIDs]; + for (var i = 0; i < IDs.length; i++) { + var acc = getAccessible(IDs[i], nsIAccessibleText); + testTextHelper( + IDs[i], + aOffset, + BOUNDARY_CHAR, + aChar, + aStartOffset, + aEndOffset, + kOk, + kOk, + kOk, + acc.getTextAfterOffset, + "getTextAfterOffset " + ); + } +} + +/** + * Test getTextAfterOffset function over different elements + * + * @param aIDs [in] ID or array of IDs + * @param aBoundaryType [in] boundary type for text to be retrieved + * @param aTestList [in] array of sets: + * offset1 and offset2 defining the offset range + * the text in the range + * start offset of the text in the range + * end offset of the text in the range + * + * or + * + * @param aOffset [in] the offset to get the text after + * @param aBoundaryType [in] Boundary type for text to be retrieved + * @param aText [in] expected return text for getTextAfterOffset + * @param aStartOffset [in] expected return start offset for getTextAfterOffset + * @param aEndOffset [in] expected return end offset for getTextAfterOffset + * @param ... [in] list of ids or list of tuples made of: + * element identifier + * kTodo or kOk for returned text + * kTodo or kOk for returned start offset + * kTodo or kOk for returned offset result + */ +function testTextAfterOffset( + aOffset, + aBoundaryType, + aText, + aStartOffset, + aEndOffset +) { + testTextSuperHelper("getTextAfterOffset", arguments); +} + +/** + * Test getTextBeforeOffset for BOUNDARY_CHAR over different elements. + * + * @param aIDs [in] the accessible identifier or array of accessible + * identifiers + * @param aOffset [in] the offset to get a character before it + * @param aChar [in] the expected character + * @param aStartOffset [in] expected start offset of the character + * @param aEndOffset [in] expected end offset of the character + */ +function testCharBeforeOffset(aIDs, aOffset, aChar, aStartOffset, aEndOffset) { + var IDs = aIDs instanceof Array ? aIDs : [aIDs]; + for (var i = 0; i < IDs.length; i++) { + var acc = getAccessible(IDs[i], nsIAccessibleText); + testTextHelper( + IDs[i], + aOffset, + BOUNDARY_CHAR, + aChar, + aStartOffset, + aEndOffset, + kOk, + kOk, + kOk, + acc.getTextBeforeOffset, + "getTextBeforeOffset " + ); + } +} + +/** + * Test getTextBeforeOffset function over different elements + * + * @param aIDs [in] ID or array of IDs + * @param aBoundaryType [in] boundary type for text to be retrieved + * @param aTestList [in] array of sets: + * offset1 and offset2 defining the offset range + * the text in the range + * start offset of the text in the range + * end offset of the text in the range + * + * or + * + * @param aOffset [in] the offset to get the text before + * @param aBoundaryType [in] Boundary type for text to be retrieved + * @param aText [in] expected return text for getTextBeforeOffset + * @param aStartOffset [in] expected return start offset for getTextBeforeOffset + * @param aEndOffset [in] expected return end offset for getTextBeforeOffset + * @param ... [in] list of ids or list of tuples made of: + * element identifier + * kTodo or kOk for returned text + * kTodo or kOk for returned start offset + * kTodo or kOk for returned offset result + */ +function testTextBeforeOffset( + aOffset, + aBoundaryType, + aText, + aStartOffset, + aEndOffset +) { + testTextSuperHelper("getTextBeforeOffset", arguments); +} + +/** + * Test word count for an element. + * + * @param aElement [in] element identifier + * @param aCount [in] Expected word count + * @param aToDoFlag [in] kTodo or kOk for returned text + */ +function testWordCount(aElement, aCount, aToDoFlag) { + var isFunc = aToDoFlag == kTodo ? todo_is : is; + var acc = getAccessible(aElement, nsIAccessibleText); + var startOffsetObj = {}, + endOffsetObj = {}; + var length = acc.characterCount; + var offset = 0; + var wordCount = 0; + while (true) { + acc.getTextAtOffset( + offset, + BOUNDARY_WORD_START, + startOffsetObj, + endOffsetObj + ); + if (offset >= length) { + break; + } + + wordCount++; + offset = endOffsetObj.value; + } + isFunc( + wordCount, + aCount, + "wrong words count for '" + + acc.getText(0, -1) + + "': " + + wordCount + + " in " + + prettyName(aElement) + ); +} + +/** + * Test word at a position for an element. + * + * @param aElement [in] element identifier + * @param aWordIndex [in] index of the word to test + * @param aText [in] expected text for that word + * @param aToDoFlag [in] kTodo or kOk for returned text + */ +function testWordAt(aElement, aWordIndex, aText, aToDoFlag) { + var isFunc = aToDoFlag == kTodo ? todo_is : is; + var acc = getAccessible(aElement, nsIAccessibleText); + + var textLength = acc.characterCount; + var wordIdx = aWordIndex; + var startOffsetObj = { value: 0 }, + endOffsetObj = { value: 0 }; + for (let offset = 0; offset < textLength; offset = endOffsetObj.value) { + acc.getTextAtOffset( + offset, + BOUNDARY_WORD_START, + startOffsetObj, + endOffsetObj + ); + + wordIdx--; + if (wordIdx < 0) { + break; + } + } + + if (wordIdx >= 0) { + ok( + false, + "the given word index '" + + aWordIndex + + "' exceeds words amount in " + + prettyName(aElement) + ); + + return; + } + + var startWordOffset = startOffsetObj.value; + var endWordOffset = endOffsetObj.value; + + // Calculate the end word offset. + acc.getTextAtOffset( + endOffsetObj.value, + BOUNDARY_WORD_END, + startOffsetObj, + endOffsetObj + ); + if (startOffsetObj.value != textLength) { + endWordOffset = startOffsetObj.value; + } + + if (endWordOffset <= startWordOffset) { + todo( + false, + "wrong start and end offset for word at index '" + + aWordIndex + + "': " + + " of text '" + + acc.getText(0, -1) + + "' in " + + prettyName(aElement) + ); + + return; + } + + let text = acc.getText(startWordOffset, endWordOffset); + isFunc( + text, + aText, + "wrong text for word at index '" + + aWordIndex + + "': " + + " of text '" + + acc.getText(0, -1) + + "' in " + + prettyName(aElement) + ); +} + +/** + * Test words in a element. + * + * @param aElement [in] element identifier + * @param aWords [in] array of expected words + * @param aToDoFlag [in, optional] kTodo or kOk for returned text + */ +function testWords(aElement, aWords, aToDoFlag) { + if (aToDoFlag == null) { + aToDoFlag = kOk; + } + + testWordCount(aElement, aWords.length, aToDoFlag); + + for (var i = 0; i < aWords.length; i++) { + testWordAt(aElement, i, aWords[i], aToDoFlag); + } +} + +/** + * Remove all selections. + * + * @param aID [in] Id, DOM node, or acc obj + */ +function cleanTextSelections(aID) { + var acc = getAccessible(aID, [nsIAccessibleText]); + + while (acc.selectionCount > 0) { + acc.removeSelection(0); + } +} + +/** + * Test addSelection method. + * + * @param aID [in] Id, DOM node, or acc obj + * @param aStartOffset [in] start offset for the new selection + * @param aEndOffset [in] end offset for the new selection + * @param aSelectionsCount [in] expected number of selections after addSelection + */ +function testTextAddSelection(aID, aStartOffset, aEndOffset, aSelectionsCount) { + var acc = getAccessible(aID, [nsIAccessibleText]); + var text = acc.getText(0, -1); + + acc.addSelection(aStartOffset, aEndOffset); + + is( + acc.selectionCount, + aSelectionsCount, + text + + ": failed to add selection from offset '" + + aStartOffset + + "' to offset '" + + aEndOffset + + "': selectionCount after" + ); +} + +/** + * Test removeSelection method. + * + * @param aID [in] Id, DOM node, or acc obj + * @param aSelectionIndex [in] index of the selection to be removed + * @param aSelectionsCount [in] expected number of selections after + * removeSelection + */ +function testTextRemoveSelection(aID, aSelectionIndex, aSelectionsCount) { + var acc = getAccessible(aID, [nsIAccessibleText]); + var text = acc.getText(0, -1); + + acc.removeSelection(aSelectionIndex); + + is( + acc.selectionCount, + aSelectionsCount, + text + + ": failed to remove selection at index '" + + aSelectionIndex + + "': selectionCount after" + ); +} + +/** + * Test setSelectionBounds method. + * + * @param aID [in] Id, DOM node, or acc obj + * @param aStartOffset [in] new start offset for the selection + * @param aEndOffset [in] new end offset for the selection + * @param aSelectionIndex [in] index of the selection to set + * @param aSelectionsCount [in] expected number of selections after + * setSelectionBounds + */ +function testTextSetSelection( + aID, + aStartOffset, + aEndOffset, + aSelectionIndex, + aSelectionsCount +) { + var acc = getAccessible(aID, [nsIAccessibleText]); + var text = acc.getText(0, -1); + + acc.setSelectionBounds(aSelectionIndex, aStartOffset, aEndOffset); + + is( + acc.selectionCount, + aSelectionsCount, + text + + ": failed to set selection at index '" + + aSelectionIndex + + "': selectionCount after" + ); +} + +/** + * Test selectionCount method. + * + * @param aID [in] Id, DOM node, or acc obj + * @param aCount [in] expected selection count + */ +function testTextSelectionCount(aID, aCount) { + var acc = getAccessible(aID, [nsIAccessibleText]); + var text = acc.getText(0, -1); + + is(acc.selectionCount, aCount, text + ": wrong selectionCount: "); +} + +/** + * Test getSelectionBounds method. + * + * @param aID [in] Id, DOM node, or acc obj + * @param aStartOffset [in] expected start offset for the selection + * @param aEndOffset [in] expected end offset for the selection + * @param aSelectionIndex [in] index of the selection to get + */ +function testTextGetSelection(aID, aStartOffset, aEndOffset, aSelectionIndex) { + var acc = getAccessible(aID, [nsIAccessibleText]); + var text = acc.getText(0, -1); + + var startObj = {}, + endObj = {}; + acc.getSelectionBounds(aSelectionIndex, startObj, endObj); + + is( + startObj.value, + aStartOffset, + text + ": wrong start offset for index '" + aSelectionIndex + "'" + ); + is( + endObj.value, + aEndOffset, + text + ": wrong end offset for index '" + aSelectionIndex + "'" + ); +} + +function testTextRange( + aRange, + aRangeDescr, + aStartContainer, + aStartOffset, + aEndContainer, + aEndOffset, + aText, + aCommonContainer, + aChildren +) { + isObject( + aRange.startContainer, + getAccessible(aStartContainer), + "Wrong start container of " + aRangeDescr + ); + is(aRange.startOffset, aStartOffset, "Wrong start offset of " + aRangeDescr); + isObject( + aRange.endContainer, + getAccessible(aEndContainer), + "Wrong end container of " + aRangeDescr + ); + is(aRange.endOffset, aEndOffset, "Wrong end offset of " + aRangeDescr); + + if (aText === undefined) { + return; + } + + is(aRange.text, aText, "Wrong text of " + aRangeDescr); + + var children = aRange.embeddedChildren; + is( + children ? children.length : 0, + aChildren ? aChildren.length : 0, + "Wrong embedded children count of " + aRangeDescr + ); + + isObject( + aRange.container, + getAccessible(aCommonContainer), + "Wrong container of " + aRangeDescr + ); + + if (aChildren) { + for (var i = 0; i < aChildren.length; i++) { + var expectedChild = getAccessible(aChildren[i]); + var actualChild = children.queryElementAt(i, nsIAccessible); + isObject( + actualChild, + expectedChild, + "Wrong child at index '" + i + "' of " + aRangeDescr + ); + } + } +} + +// ////////////////////////////////////////////////////////////////////////////// +// Private + +function testTextSuperHelper(aFuncName, aArgs) { + // List of tests. + if (aArgs[2] instanceof Array) { + let ids = aArgs[0] instanceof Array ? aArgs[0] : [aArgs[0]]; + let boundaryType = aArgs[1]; + let list = aArgs[2]; + for (let i = 0; i < list.length; i++) { + let offset1 = list[i][0], + offset2 = list[i][1]; + let text = list[i][2], + startOffset = list[i][3], + endOffset = list[i][4]; + let failureList = list[i][5]; + for (let offset = offset1; offset <= offset2; offset++) { + for (let idIdx = 0; idIdx < ids.length; idIdx++) { + let id = ids[idIdx]; + + let flagOk1 = kOk, + flagOk2 = kOk, + flagOk3 = kOk; + if (failureList) { + for (let fIdx = 0; fIdx < failureList.length; fIdx++) { + if ( + offset == failureList[fIdx][0] && + id == failureList[fIdx][1] + ) { + flagOk1 = failureList[fIdx][2]; + flagOk2 = failureList[fIdx][3]; + flagOk3 = failureList[fIdx][4]; + break; + } + } + } + + let acc = getAccessible(id, nsIAccessibleText); + testTextHelper( + id, + offset, + boundaryType, + text, + startOffset, + endOffset, + flagOk1, + flagOk2, + flagOk3, + acc[aFuncName], + aFuncName + " " + ); + } + } + } + return; + } + + // Test at single offset. List of IDs. + var offset = aArgs[0]; + var boundaryType = aArgs[1]; + var text = aArgs[2]; + var startOffset = aArgs[3]; + var endOffset = aArgs[4]; + if (aArgs[5] instanceof Array) { + let ids = aArgs[5]; + for (let i = 0; i < ids.length; i++) { + let acc = getAccessible(ids[i], nsIAccessibleText); + testTextHelper( + ids[i], + offset, + boundaryType, + text, + startOffset, + endOffset, + kOk, + kOk, + kOk, + acc[aFuncName], + aFuncName + " " + ); + } + + return; + } + + // Each ID is tested separately. + for (let i = 5; i < aArgs.length; i = i + 4) { + let ID = aArgs[i]; + let acc = getAccessible(ID, nsIAccessibleText); + let toDoFlag1 = aArgs[i + 1]; + let toDoFlag2 = aArgs[i + 2]; + let toDoFlag3 = aArgs[i + 3]; + + testTextHelper( + ID, + offset, + boundaryType, + text, + startOffset, + endOffset, + toDoFlag1, + toDoFlag2, + toDoFlag3, + acc[aFuncName], + aFuncName + " " + ); + } +} + +function testTextHelper( + aID, + aOffset, + aBoundaryType, + aText, + aStartOffset, + aEndOffset, + aToDoFlag1, + aToDoFlag2, + aToDoFlag3, + aTextFunc, + aTextFuncName +) { + var exceptionFlag = + aToDoFlag1 == undefined || + aToDoFlag2 == undefined || + aToDoFlag3 == undefined; + + var startMsg = aTextFuncName + "(" + boundaryToString(aBoundaryType) + "): "; + var endMsg = ", id: " + prettyName(aID) + ";"; + + try { + var startOffsetObj = {}, + endOffsetObj = {}; + var text = aTextFunc(aOffset, aBoundaryType, startOffsetObj, endOffsetObj); + + if (exceptionFlag) { + ok(false, startMsg + "no expected failure at offset " + aOffset + endMsg); + return; + } + + var isFunc1 = aToDoFlag1 == kTodo ? todo : ok; + var isFunc2 = aToDoFlag2 == kTodo ? todo : ok; + var isFunc3 = aToDoFlag3 == kTodo ? todo : ok; + + isFunc1( + text == aText, + startMsg + + "wrong text " + + "(got '" + + text + + "', expected: '" + + aText + + "')" + + ", offset: " + + aOffset + + endMsg + ); + isFunc2( + startOffsetObj.value == aStartOffset, + startMsg + + "wrong start offset" + + "(got '" + + startOffsetObj.value + + "', expected: '" + + aStartOffset + + "')" + + ", offset: " + + aOffset + + endMsg + ); + isFunc3( + endOffsetObj.value == aEndOffset, + startMsg + + "wrong end offset" + + "(got '" + + endOffsetObj.value + + "', expected: '" + + aEndOffset + + "')" + + ", offset: " + + aOffset + + endMsg + ); + } catch (e) { + var okFunc = exceptionFlag ? todo : ok; + okFunc( + false, + startMsg + "failed at offset " + aOffset + endMsg + ", exception: " + e + ); + } +} + +function boundaryToString(aBoundaryType) { + switch (aBoundaryType) { + case BOUNDARY_CHAR: + return "char"; + case BOUNDARY_WORD_START: + return "word start"; + case BOUNDARY_WORD_END: + return "word end"; + case BOUNDARY_LINE_START: + return "line start"; + case BOUNDARY_LINE_END: + return "line end"; + case BOUNDARY_PARAGRAPH: + return "paragraph"; + } + return "unknown"; +} diff --git a/accessible/tests/mochitest/text/a11y.ini b/accessible/tests/mochitest/text/a11y.ini new file mode 100644 index 0000000000..18c4982aa6 --- /dev/null +++ b/accessible/tests/mochitest/text/a11y.ini @@ -0,0 +1,19 @@ +[DEFAULT] +support-files = doc.html + !/accessible/tests/mochitest/*.js + +[test_atcaretoffset.html] +[test_charboundary.html] +[test_doc.html] +[test_dynamic.html] +[test_general.xhtml] +[test_gettext.html] +[test_hypertext.html] +[test_lineboundary.html] +[test_paragraphboundary.html] +[test_passwords.html] +[test_selection.html] +[test_settext_input_event.html] +[test_textBounds.html] +[test_wordboundary.html] +[test_words.html] diff --git a/accessible/tests/mochitest/text/doc.html b/accessible/tests/mochitest/text/doc.html new file mode 100644 index 0000000000..d57406c226 --- /dev/null +++ b/accessible/tests/mochitest/text/doc.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<html> +<head> + <script type="application/javascript"> + document.documentElement.appendChild(document.createTextNode("outbody")); + </script> +</head> +<body>inbody</body> +</html> 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> diff --git a/accessible/tests/mochitest/text/test_charboundary.html b/accessible/tests/mochitest/text/test_charboundary.html new file mode 100644 index 0000000000..5ca4120a47 --- /dev/null +++ b/accessible/tests/mochitest/text/test_charboundary.html @@ -0,0 +1,138 @@ +<!DOCTYPE html> +<html> +<head> + <title>Char boundary text tests</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 type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../text.js"></script> + + <script type="application/javascript"> + function doTest() { + // //////////////////////////////////////////////////////////////////////// + // + // __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__ + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + + var IDs = [ "i1", "d1", "e1", "t1" ]; + + testCharBeforeOffset(IDs, 0, "", 0, 0); + testCharBeforeOffset(IDs, 1, "h", 0, 1); + testCharBeforeOffset(IDs, 14, "n", 13, 14); + testCharBeforeOffset(IDs, 15, "d", 14, 15); + + testCharAtOffset(IDs, 0, "h", 0, 1); + testCharAtOffset(IDs, 1, "e", 1, 2); + testCharAtOffset(IDs, 14, "d", 14, 15); + testCharAtOffset(IDs, 15, "", 15, 15); + + testCharAfterOffset(IDs, 0, "e", 1, 2); + testCharAfterOffset(IDs, 1, "l", 2, 3); + testCharAfterOffset(IDs, 14, "", 15, 15); + testCharAfterOffset(IDs, 15, "", 15, 15); + + // //////////////////////////////////////////////////////////////////////// + // + // __B__r__a__v__e__ __S__i__r__ __ __R__o__b__i__n__ __ __ __r__a__n + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 + + IDs = [ "i2", "d2", "e2", "t2" ]; + + testCharBeforeOffset(IDs, 0, "", 0, 0); + testCharBeforeOffset(IDs, 1, "B", 0, 1); + testCharBeforeOffset(IDs, 6, " ", 5, 6); + testCharBeforeOffset(IDs, 10, " ", 9, 10); + testCharBeforeOffset(IDs, 11, " ", 10, 11); + testCharBeforeOffset(IDs, 17, " ", 16, 17); + testCharBeforeOffset(IDs, 19, " ", 18, 19); + + testCharAtOffset(IDs, 0, "B", 0, 1); + testCharAtOffset(IDs, 1, "r", 1, 2); + testCharAtOffset(IDs, 5, " ", 5, 6); + testCharAtOffset(IDs, 9, " ", 9, 10); + testCharAtOffset(IDs, 10, " ", 10, 11); + testCharAtOffset(IDs, 17, " ", 17, 18); + + testCharAfterOffset(IDs, 0, "r", 1, 2); + testCharAfterOffset(IDs, 1, "a", 2, 3); + testCharAfterOffset(IDs, 4, " ", 5, 6); + testCharAfterOffset(IDs, 5, "S", 6, 7); + testCharAfterOffset(IDs, 8, " ", 9, 10); + testCharAfterOffset(IDs, 9, " ", 10, 11); + testCharAfterOffset(IDs, 10, "R", 11, 12); + testCharAfterOffset(IDs, 15, " ", 16, 17); + testCharAfterOffset(IDs, 16, " ", 17, 18); + testCharAfterOffset(IDs, 17, " ", 18, 19); + testCharAfterOffset(IDs, 18, "r", 19, 20); + + // //////////////////////////////////////////////////////////////////////// + // + // __o__n__e__w__o__r__d__\n + // 0 1 2 3 4 5 6 7 + // __\n + // 8 + // __t__w__o__ __w__o__r__d__s__\n + // 9 10 11 12 13 14 15 16 17 18 + + IDs = ["d3", "dbr3", "e3", "ebr3", "t3"]; + + testCharBeforeOffset(IDs, 8, "\n", 7, 8); + testCharBeforeOffset(IDs, 9, "\n", 8, 9); + testCharBeforeOffset(IDs, 10, "t", 9, 10); + + testCharAtOffset(IDs, 7, "\n", 7, 8); + testCharAtOffset(IDs, 8, "\n", 8, 9); + testCharAtOffset(IDs, 9, "t", 9, 10); + + testCharAfterOffset(IDs, 6, "\n", 7, 8); + testCharAfterOffset(IDs, 7, "\n", 8, 9); + testCharAfterOffset(IDs, 8, "t", 9, 10); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <input id="i1" value="hello my friend"/> + <div id="d1">hello my friend</div> + <div id="e1" contenteditable="true">hello my friend</div> + <textarea id="t1" contenteditable="true">hello my friend</textarea> + + <input id="i2" value="Brave Sir Robin ran"/> + <pre> + <div id="d2">Brave Sir Robin ran</div> + <div id="e2" contenteditable="true">Brave Sir Robin ran</div> + </pre> + <textarea id="t2" cols="300">Brave Sir Robin ran</textarea> + + <pre> + <div id="d3">oneword + +two words +</div> + <div id="dbr3">oneword<br/><br/>two words<br/></div> + <div id="e3" contenteditable="true">oneword + +two words +</div> + <div id="ebr3" contenteditable="true">oneword<br/><br/>two words<br/></div> + <textarea id="t3" cols="300">oneword + +two words</textarea> + </pre> + +</body> +</html> diff --git a/accessible/tests/mochitest/text/test_doc.html b/accessible/tests/mochitest/text/test_doc.html new file mode 100644 index 0000000000..88b75b98c4 --- /dev/null +++ b/accessible/tests/mochitest/text/test_doc.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<html> +<head> + <title>nsIAccessibleText getText related function tests for document accessible</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 type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../text.js"></script> + <script type="application/javascript"> + + function doTest() { + var iframeDoc = [ getNode("iframe").contentDocument ]; + testCharacterCount(iframeDoc, 15); + testText(iframeDoc, 0, 15, "outbody inbody "); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Elements appended outside the body aren't accessible" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=608887">Mozilla Bug 608887</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <iframe id="iframe" src="doc.html"></iframe> + +</body> +</html> diff --git a/accessible/tests/mochitest/text/test_dynamic.html b/accessible/tests/mochitest/text/test_dynamic.html new file mode 100644 index 0000000000..63889fc664 --- /dev/null +++ b/accessible/tests/mochitest/text/test_dynamic.html @@ -0,0 +1,80 @@ +<!DOCTYPE html> +<html> +<head> + <title>nsIAccessibleText getText related function tests for tree mutations</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 type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../text.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + function insertBefore(aId, aEl, aTextBefore, aTextAfter, aStartIdx, aEndIdx) { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, aId), + ]; + + this.invoke = function insertBefore_invoke() { + testText(aId, 0, -1, aTextBefore); + getNode(aId).insertBefore(aEl, getNode(aId).firstChild); + }; + + this.finalCheck = function insertBefore_finalCheck() { + testText(aId, aStartIdx, aEndIdx, aTextAfter); + }; + + this.getID = function insertTextBefore_getID() { + return "insert " + prettyName(aEl) + " before"; + }; + } + + function insertTextBefore(aId, aTextBefore, aText) { + var el = document.createTextNode(aText); + this.__proto__ = new insertBefore(aId, el, aTextBefore, + aText + aTextBefore, 0, -1); + } + + function insertImgBefore(aId, aTextBefore) { + var el = document.createElement("img"); + el.setAttribute("src", "../moz.png"); + el.setAttribute("alt", "mozilla"); + + this.__proto__ = new insertBefore(aId, el, aTextBefore, + kEmbedChar + aTextBefore, 0, -1); + } + + function insertTextBefore2(aId) { + var el = document.createTextNode("hehe"); + this.__proto__ = new insertBefore(aId, el, "ho", "ho", 4, -1); + } + + var gQueue = null; + function doTest() { + gQueue = new eventQueue(); + gQueue.push(new insertTextBefore("c1", "ho", "ha")); + gQueue.push(new insertImgBefore("c1", "haho")); + gQueue.push(new insertImgBefore("c2", kEmbedChar)); + gQueue.push(new insertTextBefore2("c3")); + gQueue.invoke(); // will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="c1">ho</div> + <div id="c2"><img src="../moz.png" alt="mozilla"></div> + <div id="c3">ho</div> +</body> +</html> diff --git a/accessible/tests/mochitest/text/test_general.xhtml b/accessible/tests/mochitest/text/test_general.xhtml new file mode 100644 index 0000000000..df0ffcc0c6 --- /dev/null +++ b/accessible/tests/mochitest/text/test_general.xhtml @@ -0,0 +1,79 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml" + title="Tests: XUL label text interface"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + <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="../text.js"></script> + + <script type="application/javascript"> + <![CDATA[ + //////////////////////////////////////////////////////////////////////////// + // Testing + + var gQueue = null; + function doTests() + { + ////////////////////////////////////////////////////////////////////////// + // XUL label + + var ids = ["label1", "label2"]; + + testCharacterCount(ids, 5); + + testText(ids, 0, -1, "Hello"); + testText(ids, 0, 1, "H"); + + testCharAfterOffset(ids, 0, "e", 1, 2); + testCharBeforeOffset(ids, 1, "H", 0, 1); + testCharAtOffset(ids, 1, "e", 1, 2); + + ////////////////////////////////////////////////////////////////////////// + // HTML input + + testTextAtOffset([ getNode("tbox1") ], BOUNDARY_LINE_START, + [ [ 0, 4, "test", 0, 4 ] ]); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + ]]> + </script> + + <vbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=396166" + title="xul:label@value accessible should implement nsIAccessibleText"> + Bug 396166 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=899433" + title="Accessibility returns empty line for last line in certain cases"> + Bug 899433 + </a> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + <label id="label1" value="Hello"/> + <label id="label2">Hello</label> + + <html:input id="tbox1" value="test"/> + </vbox> +</window> diff --git a/accessible/tests/mochitest/text/test_gettext.html b/accessible/tests/mochitest/text/test_gettext.html new file mode 100644 index 0000000000..2f221a416b --- /dev/null +++ b/accessible/tests/mochitest/text/test_gettext.html @@ -0,0 +1,135 @@ +<!DOCTYPE html> +<html> +<head> + <title>Get text between offsets tests</title> + <meta charset="utf-8"> + <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 type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../text.js"></script> + + <script type="application/javascript"> + function doTest() { + // //////////////////////////////////////////////////////////////////////// + // + // __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__ + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + + var IDs = [ "i1", "d1", "d1wrap", "e1", "t1" ]; + + testCharacterCount(IDs, 15); + + testText(IDs, 0, 1, "h"); + testText(IDs, 1, 3, "el"); + testText(IDs, 14, 15, "d"); + testText(IDs, 0, 15, "hello my friend"); + testText(IDs, 0, -1, "hello my friend"); + + // //////////////////////////////////////////////////////////////////////// + // + // __B__r__a__v__e__ __S__i__r__ __ __R__o__b__i__n__ __ __ __r__a__n + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 + + IDs = [ "i2", "dpre2", "epre2", "t2" ]; + + testCharacterCount(IDs, 22); + + testText(IDs, 0, 1, "B"); + testText(IDs, 5, 6, " "); + testText(IDs, 9, 11, " "); + testText(IDs, 16, 19, " "); + testText(IDs, 0, 22, "Brave Sir Robin ran"); + testText(IDs, 0, -1, "Brave Sir Robin ran"); + + testCharacterCount(["d2", "e2"], 19); + testText(["d2", "e2"], 0, 19, "Brave Sir Robin ran"); + + // //////////////////////////////////////////////////////////////////////// + // + // __o__n__e__w__o__r__d__\n + // 0 1 2 3 4 5 6 7 + // __\n + // 8 + // __t__w__o__ __w__o__r__d__s__\n + // 9 10 11 12 13 14 15 16 17 18 + + IDs = ["d3", "dbr3", "e3", "ebr3", "t3"]; + + testCharacterCount(IDs, 19); + + testText(IDs, 0, 19, "oneword\n\ntwo words\n"); + testText(IDs, 0, -1, "oneword\n\ntwo words\n"); + + // //////////////////////////////////////////////////////////////////////// + // + // CSS text-transform + // + // Content with `text-transform:uppercase | lowercase | capitalize` returns + // the transformed content. + // + testText(["d4a"], 0, -1, "HELLO MY FRIEND"); + testText(["d4b"], 0, -1, "hello my friend"); + testText(["d4c"], 0, -1, "Hello My Friend"); + + // `text-transform: full-width | full-size-kana` should not be reflected in + // a11y. + testText(["d5a"], 0, -1, "hello my friend"); + testText(["d5b"], 0, -1, "ゕゖヵヶ"); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <input id="i1" value="hello my friend"/> + <div id="d1">hello my friend</div> + <div id="d1wrap" style="word-wrap:break-word; width:1px">hello my friend</div> + <div id="e1" contenteditable="true">hello my friend</div> + <textarea id="t1">hello my friend</textarea> + + <input id="i2" value="Brave Sir Robin ran"/> + <pre><div id="dpre2">Brave Sir Robin ran</div></pre> + <pre><div id="epre2" contenteditable="true">Brave Sir Robin ran</div></pre> + <textarea id="t2" cols="300">Brave Sir Robin ran</textarea> + <div id="d2">Brave Sir Robin ran</div> + <div id="e2" contenteditable="true">Brave Sir Robin ran</div> + + <pre> + <div id="d3">oneword + +two words +</div> + <div id="dbr3">oneword<br/><br/>two words<br/></div> + <div id="e3" contenteditable="true">oneword + +two words +</div> + <div id="ebr3" contenteditable="true">oneword<br/><br/>two words<br/></div> + <textarea id="t3" cols="300">oneword + +two words +</textarea> + </pre> + + <div id="d4a" style="text-transform:uppercase">Hello My Friend</div> + <div id="d4b" style="text-transform:lowercase">Hello My Friend</div> + <div id="d4c" style="text-transform:capitalize">hello my friend</div> + + <div id="d5a" style="text-transform:full-width">hello my friend</div> + <div id="d5b" style="text-transform:full-size-kana">ゕゖヵヶ</div> + +</body> +</html> diff --git a/accessible/tests/mochitest/text/test_hypertext.html b/accessible/tests/mochitest/text/test_hypertext.html new file mode 100644 index 0000000000..b8a289ea52 --- /dev/null +++ b/accessible/tests/mochitest/text/test_hypertext.html @@ -0,0 +1,150 @@ +<!DOCTYPE html> +<html> +<head> + <title>nsIAccessibleText getText related function tests for rich text</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <style> + #listitemnone { + list-style-type: none; + } + h6.gencontent:before { + content: "aga" + } + </style> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../text.js"></script> + + <script type="application/javascript"> + function doTest() { + // //////////////////////////////////////////////////////////////////////// + // null getText + // //////////////////////////////////////////////////////////////////////// + + var emptyTextAcc = getAccessible("nulltext", [nsIAccessibleText]); + is(emptyTextAcc.getText(0, -1), "", "getText() END_OF_TEXT with null string"); + is(emptyTextAcc.getText(0, 0), "", "getText() Len==0 with null string"); + + // //////////////////////////////////////////////////////////////////////// + // hypertext + // //////////////////////////////////////////////////////////////////////// + + // ! - embedded object char + // __h__e__l__l__o__ __!__ __s__e__e__ __!__ + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + + var IDs = [ "hypertext", "hypertext2", "ht_displaycontents" ]; + + // ////////////////////////////////////////////////////////////////////// + // characterCount + + testCharacterCount(IDs, 13); + + // ////////////////////////////////////////////////////////////////////// + // getText + + testText(IDs, 0, 1, "h"); + testText(IDs, 5, 7, " " + kEmbedChar); + testText(IDs, 10, 13, "e " + kEmbedChar); + testText(IDs, 0, 13, "hello " + kEmbedChar + " see " + kEmbedChar); + + // ////////////////////////////////////////////////////////////////////// + // getTextAtOffset line boundary + + testTextAtOffset(0, BOUNDARY_LINE_START, "line ", 0, 5, + "hypertext3", kOk, kOk, kOk); + + // XXX: see bug 634202. + testTextAtOffset(0, BOUNDARY_LINE_START, "line ", 0, 5, + "hypertext4", kOk, kOk, kOk); + + // //////////////////////////////////////////////////////////////////////// + // list + // //////////////////////////////////////////////////////////////////////// + + IDs = [ "list" ]; + testCharacterCount(IDs, 2); + testText(IDs, 0, 2, kEmbedChar + kEmbedChar); + + IDs = [ "listitem" ]; + testCharacterCount(IDs, 6); + testText(IDs, 0, 6, "1. foo"); + + IDs = [ "listitemnone" ]; + testCharacterCount(IDs, 3); + testText(IDs, 0, 3, "bar"); + + testText(["testbr"], 0, 3, "foo"); + + testTextAtOffset(2, nsIAccessibleText.BOUNDARY_CHAR, "o", 2, 3, "testbr", + kOk, kOk, kOk); + testTextAtOffset(2, nsIAccessibleText.BOUNDARY_WORD_START, "foo\n", 0, 4, + "testbr", kOk, kOk, kOk); + testTextBeforeOffset(2, nsIAccessibleText.BOUNDARY_LINE_START, "foo\n", + 0, 4, "testbr", kTodo, kOk, kTodo); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Fix getText" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=630001"> + Bug 630001, part3 + </a> + <a target="_blank" + title="getTextAtOffset line boundary may return more than one line" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=638326"> + Bug 638326 + </a> + <a target="_blank" + title="getText(0, -1) fails with empty text" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=749810"> + Bug 749810 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="nulltext"></div> + + <div id="hypertext">hello <a>friend</a> see <img src="about:blank"></div> + <div id="hypertext2">hello <a>friend</a> see <input></div> + <div id="ht_displaycontents">hello <a>friend</a> see <ul id="ul" style="display: contents;"> + <li>Supermarket 1</li> + <li>Supermarket 2</li> + </ul></div> + <ol id="list"> + <li id="listitem">foo</li> + <li id="listitemnone">bar</li> + </ol> + + <div id="hypertext3">line +<!-- haha --> +<!-- hahaha --> +<h6>heading</h6> + </div> + + <div id="hypertext4">line +<!-- haha --> +<!-- hahaha --> +<h6 role="presentation" class="gencontent">heading</h6> + </div> + + <div id="testbr">foo<br/></div> + + <div></div> +</body> +</html> diff --git a/accessible/tests/mochitest/text/test_lineboundary.html b/accessible/tests/mochitest/text/test_lineboundary.html new file mode 100644 index 0000000000..77b35ece5d --- /dev/null +++ b/accessible/tests/mochitest/text/test_lineboundary.html @@ -0,0 +1,422 @@ +<!DOCTYPE html> +<html> +<head> + <title>Line boundary getText* functions tests</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 type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../text.js"></script> + <script type="application/javascript"> + function doTest() { + testTextAtOffset("line_test_1", BOUNDARY_LINE_START, + [[0, 6, "Line 1 ", 0, 7], + // See the kOk test below. + // [7, 7, kEmbedChar, 7, 8], + [8, 15, "Line 3 ", 8, 15]]); + testTextAtOffset(/* aOffset */ 7, BOUNDARY_LINE_START, + kEmbedChar, /* aStartOffset */ 7, /* aEndOffset */ 8, + "line_test_1", + /* returned text */ kOk, + /* returned start offset */ kOk, /* returned end offset */ kOk); + + // //////////////////////////////////////////////////////////////////////// + // __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__ + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + + var IDs = [ "input", "div", "editable", "textarea", + getNode("ta", getNode("ta_cntr").contentDocument) ]; + + testTextBeforeOffset(IDs, BOUNDARY_LINE_START, + [ [ 0, 15, "", 0, 0 ] ]); + testTextBeforeOffset(IDs, BOUNDARY_LINE_END, + [ [ 0, 15, "", 0, 0 ] ]); + + testTextAtOffset(IDs, BOUNDARY_LINE_START, + [ [ 0, 15, "hello my friend", 0, 15 ] ]); + testTextAtOffset(IDs, BOUNDARY_LINE_END, + [ [ 0, 15, "hello my friend", 0, 15 ] ]); + + testTextAfterOffset(IDs, BOUNDARY_LINE_START, + [ [ 0, 15, "", 15, 15 ] ]); + testTextAfterOffset(IDs, BOUNDARY_LINE_END, + [ [ 0, 15, "", 15, 15 ] ]); + + // //////////////////////////////////////////////////////////////////////// + // __o__n__e__w__o__r__d__\n + // 0 1 2 3 4 5 6 7 + // __\n + // 8 + // __t__w__o__ __w__o__r__d__s__\n + // 9 10 11 12 13 14 15 16 17 18 + + IDs = [ "ml_div", "ml_divbr", "ml_editable", "ml_editablebr", "ml_textarea"]; + + testTextBeforeOffset(IDs, BOUNDARY_LINE_START, + [ [ 0, 7, "", 0, 0 ], + [ 8, 8, "oneword\n", 0, 8 ], + [ 9, 18, "\n", 8, 9 ], + [ 19, 19, "two words\n", 9, 19 ]]); + + testTextBeforeOffset(IDs, BOUNDARY_LINE_END, + [ [ 0, 7, "", 0, 0 ], + [ 8, 8, "oneword", 0, 7 ], + [ 9, 18, "\n", 7, 8 ], + [ 19, 19, "\ntwo words", 8, 18 ]]); + + testTextAtOffset(IDs, BOUNDARY_LINE_START, + [ [ 0, 7, "oneword\n", 0, 8 ], + [ 8, 8, "\n", 8, 9 ], + [ 9, 18, "two words\n", 9, 19 ], + [ 19, 19, "", 19, 19 ]]); + testTextAtOffset(IDs, BOUNDARY_LINE_END, + [ [ 0, 7, "oneword", 0, 7 ], + [ 8, 8, "\n", 7, 8 ], + [ 9, 18, "\ntwo words", 8, 18 ], + [ 19, 19, "\n", 18, 19 ]]); + + testTextAfterOffset(IDs, BOUNDARY_LINE_START, + [ [ 0, 7, "\n", 8, 9 ], + [ 8, 8, "two words\n", 9, 19 ], + [ 9, 19, "", 19, 19 ]]); + testTextAfterOffset(IDs, BOUNDARY_LINE_END, + [ [ 0, 7, "\n", 7, 8 ], + [ 8, 8, "\ntwo words", 8, 18 ], + [ 9, 18, "\n", 18, 19 ], + [ 19, 19, "", 19, 19 ]]); + + // //////////////////////////////////////////////////////////////////////// + // a * b (* is embedded char for link) + testTextBeforeOffset([ getAccessible("ht_1").firstChild ], BOUNDARY_LINE_START, + [ [ 0, 5, "", 0, 0 ] ]); + + testTextBeforeOffset([ getAccessible("ht_1").firstChild ], BOUNDARY_LINE_END, + [ [ 0, 5, "", 0, 0 ] ]); + + testTextAtOffset([ getAccessible("ht_1").firstChild ], BOUNDARY_LINE_START, + [ [ 0, 5, "a " + kEmbedChar + " c", 0, 5 ] ]); + + testTextAtOffset([ getAccessible("ht_1").firstChild ], BOUNDARY_LINE_END, + [ [ 0, 5, "a " + kEmbedChar + " c", 0, 5 ] ]); + + testTextAfterOffset([ getAccessible("ht_1").firstChild ], BOUNDARY_LINE_START, + [ [ 0, 5, "", 5, 5 ] ]); + + testTextAfterOffset([ getAccessible("ht_1").firstChild ], BOUNDARY_LINE_END, + [ [ 0, 5, "", 5, 5 ] ]); + + // //////////////////////////////////////////////////////////////////////// + // foo<br> and foo<br><br> + + testTextAtOffset([ getAccessible("ht_2").firstChild.firstChild ], + BOUNDARY_LINE_START, + [ [ 0, 3, "foo\n", 0, 4 ] ]); + testTextAtOffset([ getAccessible("ht_3").firstChild.firstChild ], + BOUNDARY_LINE_START, + [ [ 0, 3, "foo\n", 0, 4 ], [ 4, 4, "\n", 4, 5 ] ]); + + // //////////////////////////////////////////////////////////////////////// + // 'Hello world ' (\n is rendered as space) + + testTextAtOffset([ "ht_4" ], BOUNDARY_LINE_START, + [ [ 0, 12, "Hello world ", 0, 12 ] ]); + + // //////////////////////////////////////////////////////////////////////// + // list items + + testTextAtOffset([ "li1" ], BOUNDARY_LINE_START, + [ [ 0, 6, kDiscBulletText + "Item", 0, 6 ] ]); + testTextAtOffset([ "li2" ], BOUNDARY_LINE_START, + [ [ 0, 2, kDiscBulletText, 0, 2 ] ]); + testTextAtOffset([ "li3" ], BOUNDARY_LINE_START, + [ [ 0, 8, kDiscBulletText + "a long ", 0, 9 ], + [ 9, 12, "and ", 9, 13 ] ]); + testTextAtOffset([ "li4" ], BOUNDARY_LINE_START, + [ [ 0, 7, kDiscBulletText + "a " + kEmbedChar + " c", 0, 7 ] ]); + testTextAtOffset([ "li5" ], BOUNDARY_LINE_START, + [ [ 0, 2, kDiscBulletText + "\n", 0, 3 ], + [ 3, 7, "hello", 3, 8 ] ]); + testTextAtOffset([ "ul1" ], BOUNDARY_LINE_START, + [ [ 0, 0, kEmbedChar, 0, 1 ], + [ 1, 1, kEmbedChar, 1, 2 ], + [ 2, 2, kEmbedChar, 2, 3 ], + [ 3, 3, kEmbedChar, 3, 4 ], + [ 4, 5, kEmbedChar, 4, 5 ] ]); + + testTextAtOffset([ "li6" ], BOUNDARY_LINE_START, + [ [ 0, 7, "1. Item", 0, 7 ] ]); + testTextAtOffset([ "li7" ], BOUNDARY_LINE_START, + [ [ 0, 3, "2. ", 0, 3 ] ]); + testTextAtOffset([ "li8" ], BOUNDARY_LINE_START, + [ [ 0, 9, "3. a long ", 0, 10 ], + [ 10, 13, "and ", 10, 14 ] ]); + testTextAtOffset([ "li9" ], BOUNDARY_LINE_START, + [ [ 0, 8, "4. a " + kEmbedChar + " c", 0, 8 ] ]); + testTextAtOffset([ "li10" ], BOUNDARY_LINE_START, + [ [ 0, 3, "5. \n", 0, 4 ], + [ 4, 8, "hello", 4, 9 ] ]); + testTextAtOffset([ "ol1" ], BOUNDARY_LINE_START, + [ [ 0, 0, kEmbedChar, 0, 1 ], + [ 1, 1, kEmbedChar, 1, 2 ], + [ 2, 2, kEmbedChar, 2, 3 ], + [ 3, 3, kEmbedChar, 3, 4 ], + [ 4, 5, kEmbedChar, 4, 5 ] ]); + + // //////////////////////////////////////////////////////////////////////// + // Nested hypertexts + + testTextAtOffset(["ht_5" ], BOUNDARY_LINE_START, + [ [ 0, 0, kEmbedChar, 0, 1 ] ]); + + // //////////////////////////////////////////////////////////////////////// + // Block followed by list + + testTextAtOffset([ "block_then_ul" ], BOUNDARY_LINE_START, + [ [ 0, 0, kEmbedChar, 0, 1 ], + [ 1, 1, kEmbedChar, 1, 2 ] ]); + + // Embedded char containing a line break breaks line offsets in parent. + testTextAtOffset([ "brInEmbed" ], BOUNDARY_LINE_START, + [ [0, 1, "a " + kEmbedChar, 0, 3], + [2, 2, "a " + kEmbedChar + " d", 0, 5], + [3, 5, kEmbedChar + " d", 2, 5] ]); + testTextAtOffset([ "brInEmbedAndBefore" ], BOUNDARY_LINE_START, + [ [0, 1, "a\n", 0, 2], + [2, 3, "b " + kEmbedChar, 2, 5], + [4, 4, "b " + kEmbedChar + " e", 2, 7], + [5, 7, kEmbedChar + " e", 4, 7] ]); + testTextAtOffset([ "brInEmbedAndAfter" ], BOUNDARY_LINE_START, + [ [0, 1, "a " + kEmbedChar, 0, 3], + [2, 2, "a " + kEmbedChar + " d\n", 0, 6], + [3, 5, kEmbedChar + " d\n", 2, 6], + [6, 7, "e", 6, 7] ]); + testTextAtOffset([ "brInEmbedAndBlockElementAfter" ], BOUNDARY_LINE_START, + [ [0, 2, "a " + kEmbedChar, 0, 3], + [3, 4, kEmbedChar, 3, 4] ]); + testTextAtOffset([ "brInEmbedThenTextThenBlockElement" ], BOUNDARY_LINE_START, + [ [0, 1, "a " + kEmbedChar, 0, 3], + [2, 2, "a " + kEmbedChar + " d", 0, 5], + [3, 4, kEmbedChar + " d", 2, 5], + [5, 6, kEmbedChar, 5, 6] ]); + testTextAtOffset([ "noBrInEmbedButOneBefore" ], BOUNDARY_LINE_START, + [ [0, 1, "a\n", 0, 2], + [2, 7, "b " + kEmbedChar + " d", 2, 7] ]); + testTextAtOffset([ "noBrInEmbedButOneAfter" ], BOUNDARY_LINE_START, + [ [0, 3, "a " + kEmbedChar + "\n", 0, 4], + [4, 5, "c", 4, 5] ]); + testTextAtOffset([ "twoEmbedsWithBRs" ], BOUNDARY_LINE_START, + [ [0, 1, "a " + kEmbedChar, 0, 3], + [2, 2, "a " + kEmbedChar + kEmbedChar, 0, 4], + [3, 3, kEmbedChar + kEmbedChar + " f", 2, 6], + [4, 6, kEmbedChar + " f", 3, 6] ]); + + // Inline block span with nested spans and BRs + testTextAtOffset([ "inlineBlockWithSpansAndBrs" ], BOUNDARY_LINE_START, + [ [0, 1, "a\n", 0, 2], + [2, 3, "b\n", 2, 4], + [4, 5, "c", 4, 5] ]); + + // Spans with BRs and whitespaces. + testTextAtOffset([ "spansWithWhitespaces" ], BOUNDARY_LINE_START, + [ [0, 6, "Line 1\n", 0, 7], + [7, 13, "Line 2\n", 7, 14], + [14, 20, "Line 3\n", 14, 21], + [21, 27, "Line 4\n", 21, 28], + [28, 28, "", 28, 28] ]); + + // A line with an empty display: contents leaf in the middle. + testTextAtOffset([ "displayContents" ], BOUNDARY_LINE_START, + // See the kOk test below. + // [ [0, 3, `a${kEmbedChar}b`, 0, 3] ]); + [ [0, 0, `a${kEmbedChar}b`, 0, 3], + [2, 3, `a${kEmbedChar}b`, 0, 3] ]); + testTextAtOffset(/* aOffset */ 1, BOUNDARY_LINE_START, + `a${kEmbedChar}b`, /* aStartOffset */ 0, /* aEndOffset */ 3, + "displayContents", + /* returned text */ kOk, + /* returned start offset */ kOk, + /* returned end offset */ kOk); + + // A line which wraps, followed by a br, followed by another line. + testTextAtOffset([ "brAfterWrapped" ], BOUNDARY_LINE_START, + [ [0, 1, "a ", 0, 2], + [2, 3, "b\n", 2, 4], + [4, 5, "c", 4, 5] ]); + + testTextAtOffset([ "inlineInput" ], BOUNDARY_LINE_END, + [ [0, 1, "a", 0, 1], + [2, 7, `\nb ${kEmbedChar} d`, 1, 7, + [ [ 4, "inlineInput", kOk, kOk, kOk] ] ] ]); + + testTextAtOffset([ "inlineInput2" ], BOUNDARY_LINE_END, + [ [0, 1, "a", 0, 1], + [2, 7, `\n${kEmbedChar} c d`, 1, 7, + [ [ 2, "inlineInput2", kOk, kOk, kOk] ] ] ]); + + testTextAtOffset([ "inlineInput3" ], BOUNDARY_LINE_END, + [ [0, 1, "a", 0, 1], + [2, 8, `\nb${kEmbedChar} c d`, 1, 8, + [ [ 3, "inlineInput3", kOk, kOk, kOk] ] ] ]); + + testTextAtOffset([ "inlineInput4" ], BOUNDARY_LINE_END, + [ [0, 1, "a", 0, 1], + [2, 7, `\n${kEmbedChar}b c d`, 1, 8, + [ [ 2, "inlineInput4", kOk, kOk, kOk ] ] ] ]); + + testTextAtOffset(/* aOffset */ 0, BOUNDARY_LINE_START, + kEmbedChar, 0, 1, "contentEditableTable", + /* returned text */ kOk, + /* returned start offset */ kOk, + /* returned end offset */ kOk); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="getTextAtOffset for word boundaries: beginning of a new life" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=853340"> + Bug 853340 + </a> + <a target="_blank" + title="getTextBeforeOffset for word boundaries: evolving" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=855732"> + Bug 855732 + </a> + <a target="_blank" + title=" getTextAfterOffset for line boundary on new rails" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=882292"> + Bug 882292 + </a> + <a target="_blank" + title="getTextAtOffset broken for last object when closing tag is preceded by newline char" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=947170"> + Bug 947170 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <input id="input" value="hello my friend"/> + <div id="div">hello my friend</div> + <div id="editable" contenteditable="true">hello my friend</div> + <textarea id="textarea">hello my friend</textarea> + <iframe id="ta_cntr" + src="data:text/html,<html><body><textarea id='ta'>hello my friend</textarea></body></html>"></iframe> + + <pre> + <div id="ml_div" style="border-style:outset;">oneword + +two words +</div> + <div id="ml_divbr" style="border-style:outset;">oneword<br/><br/>two words<br/></div> + <div id="ml_editable" style="border-style:outset;" contenteditable="true">oneword + +two words +</div> + <div id="ml_editablebr" contenteditable="true" style="border-style:outset;">oneword<br/><br/>two words<br/></div> + <textarea id="ml_textarea" cols="300">oneword + +two words +</textarea> + </pre> + + <iframe id="ht_1" src="data:text/html,<html><body>a <a href=''>b</a> c</body></html>"></iframe> + + <iframe id="ht_2" src="data:text/html,<div contentEditable='true'>foo<br/></div>"></iframe> + <iframe id="ht_3" src="data:text/html,<div contentEditable='true'>foo<br/><br/></div>"></iframe> + + <p id="ht_4">Hello world +</p> + + <ul id="ul1"> + <li id="li1">Item</li> + <li id="li2"></li> + <li id="li3" style="font-family:monospace; font-size:10pt; width:8ch;">a long and winding road that lead me to your door</li> + <li id="li4">a <a href=''>b</a> c</li> + <li id="li5"><br>hello</li> + </ul> + + <ol id="ol1"> + <li id="li6">Item</li> + <li id="li7"></li> + <li id="li8" style="font-family:monospace; font-size:10pt; width:8ch;">a long and winding road that lead me to your door</li> + <li id="li9">a <a href=''>b</a> c</li> + <li id="li10"><br>hello</li> + </ol> + + <div id="ht_5"> + <div> + <p>sectiounus</p> + <p>seciofarus</p> + </div> + </div> + <div id="line_test_1"> + Line 1 + <center><input type="TEXT"><input value="Button" type="SUBMIT"></center> + Line 3 + </div> + + <div id="block_then_ul"> + <p>Block</p> + <ul><li>Li</li></ul> + </div> + <div id="brInEmbed" contenteditable>a <a href="https://mozilla.org/">b<br>c</a> d</div> + <div id="brInEmbedAndBefore">a<br>b <a href="https://mozilla.org/">c<br>d</a> e</div> + <div id="brInEmbedAndAfter">a <a href="https://mozilla.org/">b<br>c</a> d<br>e</div> + <div id="brInEmbedAndBlockElementAfter">a <a href="https://mozilla.org/">b<br>c</a><p>d</p></div> + <div id="brInEmbedThenTextThenBlockElement">a <a href="https://mozilla.org/">b<br>c</a> d<p>e</p></div> + <div id="noBrInEmbedButOneBefore">a<br>b <a href="https://mozilla.org/">c</a> d</div> + <div id="noBrInEmbedButOneAfter">a <a href="https://mozilla.org/">b</a><br>c</div> + <div id="twoEmbedsWithBRs">a <a href="https://mozilla.org">b<br>c</a><a href="https://mozilla.org">d<br>e</a> f</div> + <span id="inlineBlockWithSpansAndBrs" style="display: inline-block;"><span>a<br>b<br><span></span></span>c</span> + <div id="spansWithWhitespaces"> <!-- Don't indent the following block --> +<span>Line 1<br/> +</span> +<span>Line 2<br/> +</span> +<span>Line 3<br/> +</span> +<span>Line 4<br/> +</span></div><!-- OK to indent again --> + <div id="displayContents">a<ul style="display: contents;"><li style="display: contents;"></li></ul>b</div> + <div id="brAfterWrapped" style="width: 10px;">a b<br>c</div> + <div id="inlineInput">a<br>b <input value="c"> d</div> + <div id="inlineInput2">a<br><input value="b"> c d</div> + <div id="inlineInput3">a<br> b<input value=""> c d</div> + <div id="inlineInput4">a<br><input value="">b c d</div> + <div id="contentEditableTable" contenteditable> + <table style="display: inline-table"> + <thead> + <th>Foo</th> + </thead> + <tbody> + <tr> + <td>Bar</td> + </tr> + </tbody> + </table> + <br> + <table style="display: inline-table"> + <thead> + <th>Foo</th> + </thead> + <tbody> + <tr> + <td>Bar</td> + </tr> + </tbody> + </table> + <br> + </div> +</body> +</html> diff --git a/accessible/tests/mochitest/text/test_paragraphboundary.html b/accessible/tests/mochitest/text/test_paragraphboundary.html new file mode 100644 index 0000000000..8b270b661c --- /dev/null +++ b/accessible/tests/mochitest/text/test_paragraphboundary.html @@ -0,0 +1,148 @@ +<!DOCTYPE html> +<html> +<head> + <title>Paragraph boundary getText* functions tests</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 type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../text.js"></script> + <script type="application/javascript"> + function doTest() { + // First, test the contentEditable. + testTextAtOffset("ce", BOUNDARY_PARAGRAPH, + [[0, 0, kEmbedChar, 0, 1], + [1, 2, kEmbedChar, 1, 2]]); + + // Now, test each paragraph. + var ID = getNode("ce").firstElementChild; + testTextAtOffset(ID, BOUNDARY_PARAGRAPH, + [[0, 15, "hello my friend", 0, 15]]); + ID = getNode("ce").lastElementChild; + testTextAtOffset(ID, BOUNDARY_PARAGRAPH, + [[0, 11, "hello again", 0, 11]]); + + // Test a paragraph whose line forcefully wraps. + testTextAtOffset("forced_wrap", BOUNDARY_PARAGRAPH, + [[0, 2, "ab", 0, 2]]); + + // Test paragraphs with a few line breaks. + testTextAtOffset("forced_br", BOUNDARY_PARAGRAPH, + [[0, 1, "a\n", 0, 2], // a and br treated as a paragraph + [2, 3, "b\n", 2, 4], // b treated as a paragraph, excl 2nd line break + [4, 4, "\n", 4, 5], // second br treated as a separate paragraph + [5, 6, "c", 5, 6]]); // Last paragraph treated as usual + testTextAtOffset("br_at_beginning", BOUNDARY_PARAGRAPH, + [[0, 0, "\n", 0, 1], // br treated as a separate paragraph + [1, 2, "a\n", 1, 3], // a and br treated as a paragraph + [3, 4, "b", 3, 4]]); // b treated as last paragraph + + // Test a paragraph with an embedded link. + testTextAtOffset("pWithLink", BOUNDARY_PARAGRAPH, + [[0, 3, "a" + kEmbedChar + "d", 0, 3]]); + testTextAtOffset("link", BOUNDARY_PARAGRAPH, + [[0, 2, "bc", 0, 2]]); + + // Paragraph with link that contains a line break. + testTextAtOffset("pWithLinkWithBr", BOUNDARY_PARAGRAPH, + [[0, 0, "a" + kEmbedChar, 0, 2], + [1, 1, "a" + kEmbedChar + "d", 0, 3], + [2, 3, kEmbedChar + "d", 1, 3]]); + + // Test a list and list item + testTextAtOffset("ul", BOUNDARY_PARAGRAPH, + [[0, 0, kEmbedChar, 0, 1], + [1, 2, kEmbedChar, 1, 2]]); + testTextAtOffset("li1", BOUNDARY_PARAGRAPH, + [[0, 3, "• a", 0, 3]]); + testTextAtOffset("li2", BOUNDARY_PARAGRAPH, + [[0, 3, "• a", 0, 3]]); + // Test a list item containing multiple text leaf nodes. + testTextAtOffset("liMultiLeaf", BOUNDARY_PARAGRAPH, + [[0, 4, "• ab", 0, 4]]); + + // Test line breaks in a textarea. + testTextAtOffset("textarea", BOUNDARY_PARAGRAPH, + [[0, 1, "a\n", 0, 2], + [2, 3, "b\n", 2, 4], + [4, 4, "\n", 4, 5], + [5, 6, "c", 5, 6]]); + + // Test that a textarea has a blank paragraph at the end if it contains + // a line break as its last character. + testTextAtOffset("textarea_with_trailing_br", BOUNDARY_PARAGRAPH, + [[0, 15, "This is a test.\n", 0, 16], + [16, 16, "", 16, 16]]); + + // Paragraph with a presentational line break. + testTextAtOffset("presentational_br", BOUNDARY_PARAGRAPH, + [[0, 3, "a b", 0, 3]]); + + // Two paragraphs in a div, non-editable case. + testTextAtOffset("two_paragraphs", BOUNDARY_PARAGRAPH, + [[0, 0, kEmbedChar, 0, 1], + [1, 2, kEmbedChar, 1, 2]]); + + // Div containing a paragraph containing a link + testTextAtOffset("divWithParaWithLink", BOUNDARY_PARAGRAPH, + [[0, 0, kEmbedChar, 0, 1], + [1, 2, "b", 1, 2]]); + + // Two text nodes and a br + testTextAtOffset("twoTextNodesAndBr", BOUNDARY_PARAGRAPH, + [[0, 2, "ab\n", 0, 3], + [3, 3, "", 3, 3]]); + + // Link followed by a paragraph. + testTextAtOffset("linkThenPara", BOUNDARY_PARAGRAPH, + [[0, 0, kEmbedChar, 0, 1], + [1, 2, kEmbedChar, 1, 2]]); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="getTextAtOffset for paragraph boundaries" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1520779"> + Bug 1520779 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="ce" contenteditable="true"> + <p>hello my friend</p> + <p>hello again</p> + </div> + <p id="forced_wrap" style="width: 1px; word-break: break-all;">ab</p> + <p id="forced_br">a<br>b<br><br>c</p> + <p id="br_at_beginning"><br>a<br>b</p> + <p id="pWithLink">a<a id="link" href="https://example.com/">bc</a>d</p> + <p id="pWithLinkWithBr">a<a href="#">b<br>c</a>d</p> + <ul id="ul"><li id="li1">a</li><li>b</li></ul> + <style>#li2::marker { content:'\2022\0020'; }</style> + <ul id="ul"><li id="li2">a</li><li>b</li></ul> + <ul><li id="liMultiLeaf">a<span>b</span></li></ul> + <textarea id="textarea">a +b + +c</textarea> <!-- This must be outdented for a correct test case --> + <textarea id="textarea_with_trailing_br">This is a test. +</textarea> <!-- This must be outdented for a correct test case --> + <p id="presentational_br" style="white-space: pre-wrap;">a<span> <br role="presentation"></span>b</p> + <div id="two_paragraphs"><p>a</p><p>b</p></div> + <div id ="divWithParaWithLink"><p><a href="#">a</a></p>b</div> + <p id="twoTextNodesAndBr">a<span>b</span><br></p> + <div id="linkThenPara"><a href="#">a</a><p>b</p></div> +</body> +</html> diff --git a/accessible/tests/mochitest/text/test_passwords.html b/accessible/tests/mochitest/text/test_passwords.html new file mode 100644 index 0000000000..fc184f2d45 --- /dev/null +++ b/accessible/tests/mochitest/text/test_passwords.html @@ -0,0 +1,72 @@ +<!DOCTYPE html> +<html> +<head> + <title>nsIAccessibleText getText related function tests for text and password inputs</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 type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../text.js"></script> + + <script type="application/javascript"> + function doTest() { + // //////////////////////////////////////////////////////////////////////// + // regular text and password inputs + // //////////////////////////////////////////////////////////////////////// + + // ////////////////////////////////////////////////////////////////////// + // characterCount and getText for regular text field + + var IDs = [ "username" ]; + testCharacterCount(IDs, 4); + testText(IDs, 0, 4, "test"); + + // ////////////////////////////////////////////////////////////////////// + // characterCount and getText for password field + + IDs = [ "password" ]; + testCharacterCount(IDs, 4); + let password = document.getElementById("password"); + let editor = SpecialPowers.wrap(password).editor; + let passwordMask = editor.passwordMask; + testText(IDs, 0, 4, `${passwordMask}${passwordMask}${passwordMask}${passwordMask}`); + // a11y data is updated at next tick so that we need to refresh here. + editor.unmask(0, 2); + SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(0); + testText(IDs, 0, 4, `te${passwordMask}${passwordMask}`); + editor.unmask(2, 4); + SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(0); + testText(IDs, 0, 4, `${passwordMask}${passwordMask}st`); + editor.unmask(0, 4); + SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(0); + testText(IDs, 0, 4, `test`); + SpecialPowers.DOMWindowUtils.restoreNormalRefresh(); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="mochitest for getText for password fields" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=415943">Mozilla Bug 415943</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <form action="post.php" method="post"> + <label for="username">User name:</label> + <input id="username" value="test"><br /> + <label for="password">Password:</label> + <input type="password" id="password" value="test"/> + </form> +</body> +</html> diff --git a/accessible/tests/mochitest/text/test_selection.html b/accessible/tests/mochitest/text/test_selection.html new file mode 100644 index 0000000000..ce83e57086 --- /dev/null +++ b/accessible/tests/mochitest/text/test_selection.html @@ -0,0 +1,119 @@ +<!DOCTYPE html> +<html> +<head> + <title>Test text selection functions</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="../text.js"></script> + + <script type="application/javascript"> + + function doTest() { + // Test selection count: clean selection / check count. + testTextAddSelection("div0", 0, 2, 1); // |Test selection... + cleanTextSelections("div0"); + testTextSelectionCount("div0", 0); + + // Test addition: adding two equal selections, the second one should + // not be added. + testTextAddSelection("div1", 7, 9, 1); // Test ad|di|ng two... + testTextAddSelection("div1", 7, 9, 1); // Test ad|di|ng two... + testTextGetSelection("div1", 7, 9, 0); + + // Test overlapping selections: adding three selections, one adjacent. + testTextAddSelection("div2", 0, 3, 1); // |Tes|t adding 3... + testTextAddSelection("div2", 7, 9, 2); // |Tes|t ad|di|ng 3... + testTextAddSelection("div2", 3, 4, 3); // |Tes||t| ad|di|ng 3... + testTextGetSelection("div2", 0, 3, 0); + testTextGetSelection("div2", 3, 4, 1); + testTextGetSelection("div2", 7, 9, 2); + + // Test selection re-ordering: adding two selections. + // NOTE: removeSelections aSelectionIndex is from start of document. + testTextAddSelection("div3", 0, 3, 1); // |Tes|t adding 2... + testTextAddSelection("div3", 7, 9, 2); // |Tes|t ad|di|ng 2... + testTextRemoveSelection("div3", 4, 1); // Test ad|di|ng 2... + + // Test extending existing selection. + // NOTE: setSelectionBounds aSelectionIndex is from start of document. + testTextAddSelection("div4", 4, 5, 1); // Test| |extending... + testTextSetSelection("div4", 4, 9, 6, 1); // Test| exte|nding... + + // Test moving an existing selection. + // NOTE: setSelectionBounds aSelectionIndex is from start of document. + testTextAddSelection("div5", 1, 3, 1); // T|es|t moving... + testTextSetSelection("div5", 5, 9, 6, 1); // Test |movi|ng... + + // Test adding selections to multiple inner elements. + testTextAddSelection("div71", 0, 3, 1); // |Tes|t adding... + testTextAddSelection("div71", 7, 8, 2); // |Tes|t ad|d|ing... + testTextAddSelection("div72", 4, 6, 1); // Test| a|dding... + testTextAddSelection("div72", 7, 8, 2); // Test| a|d|d|ing... + + // Test adding selection to parent element. + // NOTE: If inner elements are represented as embedded chars + // we count their internal selections. + testTextAddSelection("div7", 7, 8, 5); // Test ad|d|ing... + + // Test attempt to selected generated content. + // range's start is clipped to end of generated content. + testTextAddSelection("div8", 1, 8, 1); + testTextGetSelection("div8", 6, 8, 0); + // range's end is expanded to end of container hypertext. + testTextAddSelection("div8", 10, 15, 2); + testTextGetSelection("div8", 10, 23, 1); + + testTextAddSelection("li", 0, 8, 1); + testTextGetSelection("li", 3, 8, 0); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + +</script> +</head> + +<body> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="div0">Test selection count</div> + </br> + <div id="div1">Test adding two equal selections </div> + <div id="div2">Test adding 3 selections one adjacent </div> + <div id="div3">Test adding 2 selections, remove first one </div> + <div id="div4">Test extending a selection </div> + <div id="div5">Test moving a selection </div> + </br> + <div id="div7">Test adding selections to parent element + <div id="div71">Test adding selections to inner element1 </div> + <div id="div72">Test adding selections to inner element2 </div> + </div> + <style> + #div8:before { + content: 'hello '; + } + #div8:after { + content: ', i love you'; + } + </style> + <div id="div8">world</div> + <ol> + <li id="li">Number one</li> + </ol> + +</body> + +</html> diff --git a/accessible/tests/mochitest/text/test_settext_input_event.html b/accessible/tests/mochitest/text/test_settext_input_event.html new file mode 100644 index 0000000000..2f0ecacf30 --- /dev/null +++ b/accessible/tests/mochitest/text/test_settext_input_event.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<html> +<head> + <title>Test that setTextContents only sends one DOM input event</title> + <meta charset="utf-8" /> + <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 type="application/javascript" + src="../common.js"></script> + <script type="application/javascript"> + async function doTest() { + let input = getAccessible("input", [nsIAccessibleEditableText]); + let eventPromise = new Promise(resolve => + document.getElementById("input").addEventListener( + "input", resolve, { once: true })); + + input.setTextContents("goodbye"); + let inputEvent = await eventPromise; + is(inputEvent.target.value, "goodbye", "input set to new value."); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="HyperTextAccessible::ReplaceText causes two distinct DOM 'input' events" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1490840">Mozilla Bug 1490840</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <input id="input" value="hello"> +</body> +</html> diff --git a/accessible/tests/mochitest/text/test_textBounds.html b/accessible/tests/mochitest/text/test_textBounds.html new file mode 100644 index 0000000000..66e7f1a93f --- /dev/null +++ b/accessible/tests/mochitest/text/test_textBounds.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html> +<head> + <title>TextBounds tests</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 type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../layout.js"></script> + + <script type="application/javascript"> + function doTest() { + // Returned rect should be all 0 if no frame; e.g. display: contents. + testTextBounds( + "displayContents", 0, 0, [0, 0, 0, 0], COORDTYPE_SCREEN_RELATIVE + ); + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <section id="displayContents" style="display: contents;"></section> +</body> +</html> diff --git a/accessible/tests/mochitest/text/test_wordboundary.html b/accessible/tests/mochitest/text/test_wordboundary.html new file mode 100644 index 0000000000..49d4d95561 --- /dev/null +++ b/accessible/tests/mochitest/text/test_wordboundary.html @@ -0,0 +1,361 @@ +<!DOCTYPE html> +<html> +<head> + <title>Word boundary text tests</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 type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../text.js"></script> + + <script type="application/javascript"> + function doTest() { + // "hello" + // __h__e__l__l__o__ + // 0 1 2 3 4 5 + var ids = [ "i1", "d1", "e1", "t1" ]; + testTextBeforeOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 5, "", 0, 0 ] ]); + testTextBeforeOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 5, "", 0, 0 ] ]); + + testTextAtOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 5, "hello", 0, 5 ] ]); + testTextAtOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 5, "hello", 0, 5 ] ]); + + testTextAfterOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 5, "", 5, 5 ] ]); + testTextAfterOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 5, "", 5, 5 ] ]); + + // "hello " + // __h__e__l__l__o__ __ + // 0 1 2 3 4 5 6 + ids = [ "i2", "d2", "p2", "e2", "t2" ]; + testTextBeforeOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 6, "", 0, 0 ] ]); + testTextBeforeOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 5, "", 0, 0 ], + [ 6, 6, "hello", 0, 5 ], + ]); + + testTextAtOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 6, "hello ", 0, 6 ] ]); + testTextAtOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 4, "hello", 0, 5 ], + [ 5, 6, " ", 5, 6 ], + ]); + + testTextAfterOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 6, "", 6, 6 ] ]); + testTextAfterOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 5, " ", 5, 6 ], + [ 6, 6, "", 6, 6 ], + ]); + + // "hello all" + // __h__e__l__l__o__ __a__l__l__ + // 0 1 2 3 4 5 6 7 8 9 + ids = [ "i6", "d6", "e6", "t6" ]; + testTextBeforeOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 5, "", 0, 0 ], + [ 6, 9, "hello ", 0, 6 ]]); + testTextBeforeOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 5, "", 0, 0 ], + [ 6, 9, "hello", 0, 5 ] ]); + + testTextAtOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 5, "hello ", 0, 6 ], + [ 6, 9, "all", 6, 9 ] ]); + testTextAtOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 4, "hello", 0, 5 ], + [ 5, 9, " all", 5, 9 ] ]); + + testTextAfterOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 5, "all", 6, 9 ], + [ 6, 9, "", 9, 9 ] ]); + testTextAfterOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 5, " all", 5, 9 ], + [ 6, 9, "", 9, 9 ] ]); + + // " hello all " (with whitespace collapsing) + // __h__e__l__l__o__ __a__l__l__ __ + // 0 1 2 3 4 5 6 7 8 9 10 + ids = [ "d6a", "e6a" ]; + testTextBeforeOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 5, "", 0, 0 ], + [ 6, 10, "hello ", 0, 6 ] ]); + testTextBeforeOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 5, "", 0, 0 ], + [ 6, 9, "hello", 0, 5 ], + [ 10, 10, " all", 5, 9 ] ]); + + testTextAtOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 5, "hello ", 0, 6 ], + [ 6, 10, "all ", 6, 10 ] ]); + testTextAtOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 4, "hello", 0, 5 ], + [ 5, 8, " all", 5, 9 ], + [ 9, 10, " ", 9, 10 ] ]); + + testTextAfterOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 5, "all ", 6, 10 ], + [ 6, 10, "", 10, 10 ] ]); + testTextAfterOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 5, " all", 5, 9 ], + [ 6, 9, " ", 9, 10 ], + [ 10, 10, "", 10, 10 ] ]); + + // "hello my friend" + // __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__ + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + ids = [ "i7", "d7", "e7", "t7", "w7" ]; + testTextBeforeOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 5, "", 0, 0 ], + [ 6, 8, "hello ", 0, 6 ], + [ 9, 15, "my ", 6, 9 ] ]); + testTextBeforeOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 5, "", 0, 0 ], + [ 6, 8, "hello", 0, 5 ], + [ 9, 15, " my", 5, 8 ] ]); + + testTextAtOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 5, "hello ", 0, 6 ], + [ 6, 8, "my ", 6, 9 ], + [ 9, 15, "friend", 9, 15] ]); + testTextAtOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 4, "hello", 0, 5 ], + [ 5, 7, " my", 5, 8 ], + [ 8, 15, " friend", 8, 15] ]); + + testTextAfterOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 5, "my ", 6, 9 ], + [ 6, 8, "friend", 9, 15 ], + [ 9, 15, "", 15, 15 ] ]); + testTextAfterOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 5, " my", 5, 8 ], + [ 6, 8, " friend", 8, 15 ], + [ 9, 15, "", 15, 15 ] ]); + + // "Brave Sir Robin ran" + // __B__r__a__v__e__ __S__i__r__ __ __R__o__b__i__n__ __ __ __r__a__n__ + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 + ids = [ "i8", "d8", "e8", "t8" ]; + testTextBeforeOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 5, "", 0, 0 ], + [ 6, 10, "Brave ", 0, 6 ], + [ 11, 18, "Sir ", 6, 11 ], + [ 19, 22, "Robin ", 11, 19 ] ]); + testTextBeforeOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 5, "", 0, 0 ], + [ 6, 9, "Brave", 0, 5 ], + [ 10, 16, " Sir", 5, 9 ], + [ 17, 22, " Robin", 9, 16 ] ]); + + testTextAtOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 5, "Brave ", 0, 6 ], + [ 6, 10, "Sir ", 6, 11 ], + [ 11, 18, "Robin ", 11, 19 ], + [ 19, 22, "ran", 19, 22 ] ]); + testTextAtOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 4, "Brave", 0, 5 ], + [ 5, 8, " Sir", 5, 9 ], + [ 9, 15, " Robin", 9, 16 ], + [ 16, 22, " ran", 16, 22 ] ]); + + testTextAfterOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 5, "Sir ", 6, 11 ], + [ 6, 10, "Robin ", 11, 19 ], + [ 11, 18, "ran", 19, 22 ], + [ 19, 22, "", 22, 22 ] ]); + testTextAfterOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 5, " Sir", 5, 9 ], + [ 6, 9, " Robin", 9, 16 ], + [ 10, 16, " ran", 16, 22 ], + [ 17, 22, "", 22, 22 ] ]); + + // 'oneword + // ' + // 'two words + // ' + // __o__n__e__w__o__r__d__\n + // 0 1 2 3 4 5 6 7 + // __\n + // 8 + // __t__w__o__ __w__o__r__d__s__\n__ + // 9 10 11 12 13 14 15 16 17 18 19 + + ids = ["ml_div1", "ml_divbr1", "ml_ediv1", "ml_edivbr1", "ml_t1"]; + testTextBeforeOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 8, "", 0, 0 ], + [ 9, 12, "oneword\n\n", 0, 9 ], + [ 13, 19, "two ", 9, 13 ] ]); + testTextBeforeOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 7, "", 0, 0 ], + [ 8, 12, "oneword", 0, 7, + [ [ 8, "ml_divbr1", kOk, kOk, kOk ], + [ 8, "ml_edivbr1", kOk, kOk, kOk ], + [ 9, "ml_divbr1", kOk, kOk, kOk ], + [ 9, "ml_edivbr1", kOk, kOk, kOk ] ] ], + [ 13, 18, "\n\ntwo", 7, 12 ], + [ 19, 19, " words", 12, 18, + [ [ 19, "ml_divbr1", kOk, kOk, kOk ], + [ 19, "ml_edivbr1", kOk, kOk, kOk ] ] ], + ] ); + + testTextAtOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 8, "oneword\n\n", 0, 9 ], + [ 9, 12, "two ", 9, 13 ], + [ 13, 19, "words\n", 13, 19 ] ]); + testTextAtOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 6, "oneword", 0, 7 ], + [ 7, 11, "\n\ntwo", 7, 12 ], + [ 12, 17, " words", 12, 18 ], + [ 18, 19, "\n", 18, 19, + [ [ 18, "ml_divbr1", kOk, kOk, kOk ], + [ 18, "ml_edivbr1", kOk, kOk, kOk ], + [ 19, "ml_divbr1", kOk, kOk, kOk ], + [ 19, "ml_edivbr1", kOk, kOk, kOk ] ] ] ]); + + testTextAfterOffset(ids, BOUNDARY_WORD_START, + [ [ 0, 8, "two ", 9, 13 ], + [ 9, 12, "words\n", 13, 19 ], + [ 13, 19, "", 19, 19 ] ]); + testTextAfterOffset(ids, BOUNDARY_WORD_END, + [ [ 0, 7, "\n\ntwo", 7, 12 ], + [ 8, 12, " words", 12, 18 ], + [ 13, 18, "\n", 18, 19, + [ [ 18, "ml_divbr1", kOk, kOk, kOk ], + [ 18, "ml_edivbr1", kOk, kOk, kOk ] ] ], + [ 19, 19, "", 19, 19 ] ]); + + // a <a href="#">b</a> + // a * + testTextBeforeOffset("cntr_1", BOUNDARY_WORD_START, + [ [ 0, 1, "", 0, 0 ], + [ 2, 3, "a ", 0, 2 ] ]); + + testTextAtOffset("cntr_1", BOUNDARY_WORD_START, + [ [ 0, 1, "a ", 0, 2 ], + [ 2, 3, kEmbedChar, 2, 3 ] ]); + testTextAfterOffset("cntr_1", BOUNDARY_WORD_START, + [ [ 0, 1, kEmbedChar, 2, 3 ], + [ 2, 3, "", 3, 3 ] ]); + + // Punctuation tests. + testTextAtOffset("punc_alone", BOUNDARY_WORD_START, [ + [ 0, 1, "a ", 0, 2 ], + [ 2, 4, "@@ ", 2, 5 ], + [ 5, 6, "b", 5, 6 ] + ]); + testTextAtOffset("punc_begin", BOUNDARY_WORD_START, [ + [ 0, 1, "a ", 0, 2 ], + [ 2, 5, "@@b ", 2, 6 ], + [ 6, 7, "c", 6, 7 ] + ]); + testTextAtOffset("punc_end", BOUNDARY_WORD_START, [ + [ 0, 1, "a ", 0, 2 ], + [ 2, 5, "b@@ ", 2, 6 ], + [ 6, 7, "c", 6, 7 ] + ]); + testTextAtOffset("punc_middle", BOUNDARY_WORD_START, [ + [ 0, 1, "a ", 0, 2 ], + [ 2, 4, "b@@", 2, 5 ], + [ 5, 6, "c ", 5, 7 ], + [ 7, 8, "d", 7, 8 ] + ]); + testTextAtOffset("punc_everywhere", BOUNDARY_WORD_START, [ + [ 0, 1, "a ", 0, 2 ], + [ 2, 6, "@@b@@", 2, 7 ], + [ 7, 10, "c@@ ", 7, 11 ], + [ 11, 12, "d", 11, 12 ] + ]); + + // Multi-word embedded object test. + testTextAtOffset("multiword_embed", BOUNDARY_WORD_START, [ + [ 0, 1, "a ", 0, 2 ], + [ 2, 3, `${kEmbedChar} `, 2, 4, [ + // Word at offset 2 returns end offset 3, should be 4. + [ 2, "multiword_embed", kOk, kOk, kOk ] + ] ], + [ 4, 5, "b", 4, 5 ] + ]); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <input id="i1" value="hello"/> + <div id="d1">hello</div> + <div id="e1" contenteditable="true">hello</div> + <textarea id="t1">hello</textarea> + + <input id="i2" value="hello "/> + <div id="d2"> hello </div> + <pre><div id="p2">hello </div></pre> + <div id="e2" contenteditable="true" style='white-space:pre'>hello </div> + <textarea id="t2">hello </textarea> + + <input id="i6" value="hello all"/> + <div id="d6"> hello all</div> + <div id="e6" contenteditable="true">hello all</div> + <textarea id="t6">hello all</textarea> + + <div id="d6a"> hello all </div> + <div id="e6a" contenteditable="true"> hello all </div> + + <input id="i7" value="hello my friend"/> + <div id="d7"> hello my friend</div> + <div id="e7" contenteditable="true">hello my friend</div> + <textarea id="t7">hello my friend</textarea> + <div id="w7" style="width:1em"> hello my friend</div> + + <input id="i8" value="Brave Sir Robin ran"/> + <pre> + <div id="d8">Brave Sir Robin ran</div> + <div id="e8" contenteditable="true">Brave Sir Robin ran</div> + </pre> + <textarea id="t8" cols="300">Brave Sir Robin ran</textarea> + + <pre> +<div id="ml_div1">oneword + +two words +</div> +<div id="ml_divbr1">oneword<br/><br/>two words<br/></div> +<div id="ml_ediv1" contenteditable="true">oneword + +two words +</div> +<div id="ml_edivbr1" contenteditable="true">oneword<br/><br/>two words<br/></div> +<textarea id="ml_t1" cols="300">oneword + +two words +</textarea> + </pre> + + <div id="cntr_1">a <a href="#">b</a></div> + + <p id="punc_alone">a @@ b</p> + <p id="punc_begin">a @@b c</p> + <p id="punc_end">a b@@ c</p> + <p id="punc_middle">a b@@c d</p> + <p id="punc_everywhere">a @@b@@c@@ d</p> + + <p id="multiword_embed">a <a href="#">x y</a> b</p> +</body> +</html> diff --git a/accessible/tests/mochitest/text/test_words.html b/accessible/tests/mochitest/text/test_words.html new file mode 100644 index 0000000000..7e4fba9154 --- /dev/null +++ b/accessible/tests/mochitest/text/test_words.html @@ -0,0 +1,131 @@ +<!DOCTYPE html> +<html> +<head> + <title>nsIAccessibleText getText related function tests for html:input,html:div and html:textarea</title> + <meta charset="utf-8" /> + <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 type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../text.js"></script> + <script type="application/javascript"> + if (navigator.platform.startsWith("Mac")) { + SimpleTest.expectAssertions(0, 1); + } else { + SimpleTest.expectAssertions(0, 1); + } + + function doTest() { + // "one two" + testWords("div1", ["one", "two"]); + + // "one two" + testWords("div2", ["one", "two"]); + + // "one,two" + testWordCount("div3", 2, kOk); + testWordAt("div3", 0, "one", kTodo); + testWordAt("div3", 1, "two", kOk); + + // "one, two" + testWordCount("div4", 2, kOk); + testWordAt("div4", 0, "one", kTodo); + testWordAt("div4", 1, "two", kOk); + + // "one+two" + testWordCount("div5", 2, kOk); + testWordAt("div5", 0, "one", kTodo); + testWordAt("div5", 1, "two", kOk); + + // "one+two " + testWordCount("div6", 2, kOk); + testWordAt("div6", 0, "one", kTodo); + testWordAt("div6", 1, "two", kOk); + + // "one\ntwo" + testWordCount("div7", 2, kOk); + testWordAt("div7", 0, "one", kOk); + testWordAt("div7", 1, "two", kTodo); + + // "one.two" + testWordCount("div8", 2, kOk); + testWordAt("div8", 0, "one", kTodo); + testWordAt("div8", 1, "two", kOk); + + // "345" + testWords("div9", ["345"]); + + // "3a A4" + testWords("div10", ["3a", "A4"]); + + // "3.1416" + testWords("div11", ["3.1416"], kTodo); + + // "4,261.01" + testWords("div12", ["4,261.01"], kTodo); + + // "カタカナ" + testWords("div13", ["カタカナ"], kOk); + + // "Peter's car" + testWords("div14", ["Peter's", "car"], kTodo); + + // "N.A.T.O." + testWords("div15", ["N.A.T.O."], kTodo); + + // "3+4*5=23" + testWordCount("div16", 4, kOk); + testWordAt("div15", 0, "3", kTodo); + testWordAt("div15", 1, "4", kTodo); + testWordAt("div15", 2, "5", kTodo); + testWordAt("div15", 3, "23", kTodo); + + // "Hello. Friend, are you here?!" + testWordCount("div17", 5, kOk); + testWordAt("div17", 0, "Hello", kTodo); + testWordAt("div17", 1, "Friend", kTodo); + testWordAt("div17", 2, "are", kOk); + testWordAt("div17", 3, "you", kOk); + testWordAt("div17", 4, "here", kTodo); + + testWords("input_1", ["foo", "bar"]); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="nsIAccessibleText test word boundaries" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=452769">Mozilla Bug 452769</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + <div id="div1">one two</div> + <div id="div2">one two</div> + <div id="div3">one,two</div> + <div id="div4">one, two</div> + <div id="div5">one+two</div> + <div id="div6">one+two </div> + <div id="div7">one<br/>two</div> + <div id="div8">one.two</div> + <div id="div9">345</div> + <div id="div10">3a A4</div> + <div id="div11">3.1416</div> + <div id="div12">4,261.01</div> + <div id="div13">カタカナ</div> + <div id="div14">Peter's car</div> + <div id="div15">N.A.T.O.</div> + <div id="div16">3+4*5=23</div> + <div id="div17">Hello. Friend, are you here?!</div> + </pre> + <input id="input_1" type="text" value="foo bar" placeholder="something or other"> + +</body> +</html> diff --git a/accessible/tests/mochitest/textattrs/a11y.ini b/accessible/tests/mochitest/textattrs/a11y.ini new file mode 100644 index 0000000000..6250e663b4 --- /dev/null +++ b/accessible/tests/mochitest/textattrs/a11y.ini @@ -0,0 +1,11 @@ +[DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/moz.png + +[test_general.html] +[test_general.xhtml] +[test_invalid.html] +[test_mathml.html] +[test_spelling.html] +[test_svg.html] diff --git a/accessible/tests/mochitest/textattrs/test_general.html b/accessible/tests/mochitest/textattrs/test_general.html new file mode 100644 index 0000000000..02adcf675a --- /dev/null +++ b/accessible/tests/mochitest/textattrs/test_general.html @@ -0,0 +1,823 @@ +<html> + +<head> + <title>Text attributes tests</title> + <meta charset="utf-8"> + <link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <style> + .gencontent:before { content: "*"; } + .gencontent:after { content: "*"; } + </style> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script src="../common.js"></script> + <script src="../attributes.js"></script> + <script src="../events.js"></script> + + <script> + var gComputedStyle = null; + + function doTest() { + // //////////////////////////////////////////////////////////////////////// + // area1 + var ID = "area1"; + var defAttrs = buildDefaultTextAttrs(ID, "10pt"); + testDefaultTextAttrs(ID, defAttrs); + + var attrs = {}; + testTextAttrs(ID, 0, attrs, defAttrs, 0, 7); + + attrs = { "font-weight": kBoldFontWeight }; + testTextAttrs(ID, 7, attrs, defAttrs, 7, 11); + + attrs = {}; + testTextAttrs(ID, 12, attrs, defAttrs, 11, 18); + + // //////////////////////////////////////////////////////////////////////// + // area2 + ID = "area2"; + defAttrs = buildDefaultTextAttrs(ID, "14pt"); + testDefaultTextAttrs(ID, defAttrs); + + attrs = {}; + testTextAttrs(ID, 0, attrs, defAttrs, 0, 7); + + attrs = { "font-weight": kBoldFontWeight }; + testTextAttrs(ID, 7, attrs, defAttrs, 7, 12); + + var tempElem = getNode(ID).firstChild.nextSibling.firstChild.nextSibling; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = {"font-style": gComputedStyle.fontStyle, + "font-weight": kBoldFontWeight }; + testTextAttrs(ID, 13, attrs, defAttrs, 12, 19); + + attrs = { "font-weight": kBoldFontWeight }; + testTextAttrs(ID, 20, attrs, defAttrs, 19, 23); + + attrs = {}; + testTextAttrs(ID, 24, attrs, defAttrs, 23, 30); + + // //////////////////////////////////////////////////////////////////////// + // area3 + ID = "area3"; + defAttrs = buildDefaultTextAttrs(ID, "12pt"); + testDefaultTextAttrs(ID, defAttrs); + + tempElem = getNode(ID).firstChild.nextSibling; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = {"color": gComputedStyle.color}; + testTextAttrs(ID, 0, attrs, defAttrs, 0, 6); + + tempElem = tempElem.firstChild.nextSibling; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = {"color": gComputedStyle.color}; + testTextAttrs(ID, 6, attrs, defAttrs, 6, 26); + + tempElem = tempElem.parentNode; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = {"color": gComputedStyle.color}; + testTextAttrs(ID, 26, attrs, defAttrs, 26, 27); + + tempElem = tempElem.nextSibling; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = {"color": gComputedStyle.color, + "background-color": gComputedStyle.backgroundColor}; + testTextAttrs(ID, 27, attrs, defAttrs, 27, 50); + + // //////////////////////////////////////////////////////////////////////// + // area4 + ID = "area4"; + defAttrs = buildDefaultTextAttrs(ID, "12pt"); + testDefaultTextAttrs(ID, defAttrs); + + tempElem = getNode(ID).firstChild.nextSibling; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = {"color": gComputedStyle.color}; + testTextAttrs(ID, 0, attrs, defAttrs, 0, 16); + + tempElem = tempElem.nextSibling.firstChild.nextSibling; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = {"color": gComputedStyle.color}; + testTextAttrs(ID, 16, attrs, defAttrs, 16, 33); + + tempElem = tempElem.parentNode; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = {"color": gComputedStyle.color}; + testTextAttrs(ID, 34, attrs, defAttrs, 33, 46); + + // //////////////////////////////////////////////////////////////////////// + // area5: "Green!*!RedNormal" + ID = "area5"; + defAttrs = buildDefaultTextAttrs(ID, "12pt"); + testDefaultTextAttrs(ID, defAttrs); + + // Green + tempElem = getNode(ID).firstChild.nextSibling; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = {"color": gComputedStyle.color}; + testTextAttrs(ID, 0, attrs, defAttrs, 0, 5); + + // br + attrs = {}; + testTextAttrs(ID, 5, attrs, defAttrs, 5, 6); + + // img, embedded accessible, no attributes + attrs = {}; + testTextAttrs(ID, 6, attrs, {}, 6, 7); + + // br + attrs = {}; + testTextAttrs(ID, 7, attrs, defAttrs, 7, 8); + + // Red + tempElem = tempElem.nextSibling.nextSibling.nextSibling.nextSibling; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = {"color": gComputedStyle.color}; + testTextAttrs(ID, 9, attrs, defAttrs, 8, 11); + + // Normal + attrs = {}; + testTextAttrs(ID, 11, attrs, defAttrs, 11, 18); + + // //////////////////////////////////////////////////////////////////////// + // area6 (CSS vertical-align property, refer to bug 445938 for details + // and sup and sub elements, refer to bug 735645 for details) + ID = "area6"; + defAttrs = buildDefaultTextAttrs(ID, "12pt"); + testDefaultTextAttrs(ID, defAttrs); + + attrs = {}; + testTextAttrs(ID, 0, attrs, defAttrs, 0, 5); + + // Embedded object (sup) has no attributes but takes up one character. + testTextAttrs(ID, 5, {}, {}, 5, 6); + + attrs = {}; + testTextAttrs(ID, 6, attrs, defAttrs, 6, 20); + + attrs = { "text-position": "super" }; + testTextAttrs(ID, 20, attrs, defAttrs, 20, 28); + + attrs = {}; + testTextAttrs(ID, 28, attrs, defAttrs, 28, 32); + + // Embedded object (sub) has no attributes but takes up one character. + testTextAttrs(ID, 32, {}, {}, 32, 33); + + attrs = {}; + testTextAttrs(ID, 33, attrs, defAttrs, 33, 38); + + attrs = { "text-position": "sub" }; + testTextAttrs(ID, 38, attrs, defAttrs, 38, 47); + + attrs = {}; + testTextAttrs(ID, 47, attrs, defAttrs, 47, 52); + + attrs = { "text-position": "super" }; + testTextAttrs(ID, 52, attrs, defAttrs, 52, 67); + + attrs = {}; + testTextAttrs(ID, 67, attrs, defAttrs, 67, 72); + + attrs = { "text-position": "sub" }; + testTextAttrs(ID, 72, attrs, defAttrs, 72, 85); + + attrs = {}; + testTextAttrs(ID, 85, attrs, defAttrs, 85, 90); + + attrs = { "text-position": "super" }; + testTextAttrs(ID, 90, attrs, defAttrs, 90, 106); + + attrs = {}; + testTextAttrs(ID, 106, attrs, defAttrs, 106, 111); + + attrs = { "text-position": "sub" }; + testTextAttrs(ID, 111, attrs, defAttrs, 111, 125); + + // //////////////////////////////////////////////////////////////////////// + // area7 + ID = "area7"; + defAttrs = buildDefaultTextAttrs(ID, "12pt"); + defAttrs.language = "en"; + testDefaultTextAttrs(ID, defAttrs); + + attrs = {"language": "ru"}; + testTextAttrs(ID, 0, attrs, defAttrs, 0, 6); + + attrs = {}; + testTextAttrs(ID, 6, attrs, defAttrs, 6, 7); + + tempElem = getNode(ID).firstChild.nextSibling.nextSibling.nextSibling; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = { "background-color": gComputedStyle.backgroundColor}; + testTextAttrs(ID, 13, attrs, defAttrs, 7, 20); + + attrs = {}; + testTextAttrs(ID, 20, attrs, defAttrs, 20, 21); + + attrs = {"language": "de"}; + testTextAttrs(ID, 21, attrs, defAttrs, 21, 36); + + attrs = {}; + testTextAttrs(ID, 36, attrs, defAttrs, 36, 44); + + attrs = {}; + testTextAttrs(ID, 37, attrs, defAttrs, 36, 44); + + tempElem = tempElem.nextSibling.nextSibling.nextSibling.nextSibling.firstChild.nextSibling; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = {"color": gComputedStyle.color}; + testTextAttrs(ID, 44, attrs, defAttrs, 44, 51); + + tempElem = tempElem.firstChild.nextSibling; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = {"font-weight": kBoldFontWeight, + "color": gComputedStyle.color}; + testTextAttrs(ID, 51, attrs, defAttrs, 51, 55); + + tempElem = tempElem.parentNode; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = {"color": gComputedStyle.color}; + testTextAttrs(ID, 55, attrs, defAttrs, 55, 62); + + // //////////////////////////////////////////////////////////////////////// + // area9, different single style spans in styled paragraph + ID = "area9"; + defAttrs = buildDefaultTextAttrs(ID, "10pt"); + testDefaultTextAttrs(ID, defAttrs); + + attrs = {}; + testTextAttrs(ID, 0, attrs, defAttrs, 0, 6); + + attrs = { "font-size": "12pt" }; + testTextAttrs(ID, 7, attrs, defAttrs, 6, 12); + + attrs = {}; + testTextAttrs(ID, 13, attrs, defAttrs, 12, 21); + + // Walk to the span with the different background color + tempElem = getNode(ID).firstChild.nextSibling.nextSibling.nextSibling; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = { "background-color": gComputedStyle.backgroundColor }; + testTextAttrs(ID, 22, attrs, defAttrs, 21, 36); + + attrs = {}; + testTextAttrs(ID, 37, attrs, defAttrs, 36, 44); + + // Walk from the background color span to the one with font-style + tempElem = tempElem.nextSibling.nextSibling; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = { "font-style": gComputedStyle.fontStyle }; + testTextAttrs(ID, 45, attrs, defAttrs, 44, 61); + + attrs = {}; + testTextAttrs(ID, 62, attrs, defAttrs, 61, 69); + + // Walk from span with font-style to the one with font-family. + tempElem = tempElem.nextSibling.nextSibling; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = { "font-family": kMonospaceFontFamily }; + testTextAttrs(ID, 70, attrs, defAttrs, 69, 83); + + attrs = {}; + testTextAttrs(ID, 84, attrs, defAttrs, 83, 91); + + attrs = { + "text-underline-style": "solid", + "text-underline-color": gComputedStyle.color, + }; + testTextAttrs(ID, 92, attrs, defAttrs, 91, 101); + + attrs = {}; + testTextAttrs(ID, 102, attrs, defAttrs, 101, 109); + + attrs = { + "text-line-through-style": "solid", + "text-line-through-color": gComputedStyle.color, + }; + testTextAttrs(ID, 110, attrs, defAttrs, 109, 122); + + attrs = {}; + testTextAttrs(ID, 123, attrs, defAttrs, 122, 130); + + attrs = { + "text-line-through-style": "solid", + "text-line-through-color": gComputedStyle.color, + }; + testTextAttrs(ID, 131, attrs, defAttrs, 130, 143); + + attrs = {}; + testTextAttrs(ID, 144, attrs, defAttrs, 143, 151); + + attrs = { + "text-line-through-style": "solid", + "text-line-through-color": gComputedStyle.color, + }; + testTextAttrs(ID, 152, attrs, defAttrs, 151, 164); + + attrs = {}; + testTextAttrs(ID, 165, attrs, defAttrs, 164, 172); + + // //////////////////////////////////////////////////////////////////////// + // area10, different single style spans in non-styled paragraph + ID = "area10"; + defAttrs = buildDefaultTextAttrs(ID, "12pt"); + testDefaultTextAttrs(ID, defAttrs); + + attrs = {}; + testTextAttrs(ID, 0, attrs, defAttrs, 0, 7); + + attrs = { "font-size": "14pt" }; + testTextAttrs(ID, 7, attrs, defAttrs, 7, 13); + + attrs = {}; + testTextAttrs(ID, 13, attrs, defAttrs, 13, 22); + + // Walk to the span with the different background color + tempElem = getNode(ID).firstChild.nextSibling.nextSibling.nextSibling; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = { "background-color": gComputedStyle.backgroundColor }; + testTextAttrs(ID, 23, attrs, defAttrs, 22, 37); + + attrs = {}; + testTextAttrs(ID, 38, attrs, defAttrs, 37, 45); + + // Walk from the background color span to the one with font-style + tempElem = tempElem.nextSibling.nextSibling; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = {"font-style": gComputedStyle.fontStyle}; + testTextAttrs(ID, 46, attrs, defAttrs, 45, 62); + + attrs = {}; + testTextAttrs(ID, 63, attrs, defAttrs, 62, 70); + + // Walk from span with font-style to the one with font-family. + tempElem = tempElem.nextSibling.nextSibling; + gComputedStyle = document.defaultView.getComputedStyle(tempElem); + attrs = { "font-family": kMonospaceFontFamily }; + testTextAttrs(ID, 71, attrs, defAttrs, 70, 84); + + attrs = {}; + testTextAttrs(ID, 85, attrs, defAttrs, 84, 92); + + attrs = { + "text-underline-style": "solid", + "text-underline-color": gComputedStyle.color, + }; + testTextAttrs(ID, 93, attrs, defAttrs, 92, 102); + + attrs = {}; + testTextAttrs(ID, 103, attrs, defAttrs, 102, 110); + + attrs = { + "text-line-through-style": "solid", + "text-line-through-color": gComputedStyle.color, + }; + testTextAttrs(ID, 111, attrs, defAttrs, 110, 123); + + attrs = {}; + testTextAttrs(ID, 124, attrs, defAttrs, 123, 131); + + // //////////////////////////////////////////////////////////////////////// + // area11, "font-weight" tests + ID = "area11"; + defAttrs = buildDefaultTextAttrs(ID, "12pt", kBoldFontWeight); + testDefaultTextAttrs(ID, defAttrs); + + attrs = { }; + testTextAttrs(ID, 0, attrs, defAttrs, 0, 13); + + attrs = { "font-weight": kNormalFontWeight }; + testTextAttrs(ID, 13, attrs, defAttrs, 13, 20); + + attrs = { }; + testTextAttrs(ID, 20, attrs, defAttrs, 20, 27); + + attrs = { "font-weight": kNormalFontWeight }; + testTextAttrs(ID, 27, attrs, defAttrs, 27, 33); + + attrs = { }; + testTextAttrs(ID, 33, attrs, defAttrs, 33, 51); + + attrs = { "font-weight": kNormalFontWeight }; + testTextAttrs(ID, 51, attrs, defAttrs, 51, 57); + + attrs = { }; + testTextAttrs(ID, 57, attrs, defAttrs, 57, 97); + + // //////////////////////////////////////////////////////////////////////// + // test out of range offset + testTextAttrsWrongOffset("area12", -1); + testTextAttrsWrongOffset("area12", 500); + + // //////////////////////////////////////////////////////////////////////// + // test zero offset on empty hypertext accessibles + ID = "area13"; + defAttrs = buildDefaultTextAttrs(ID, "12pt"); + attrs = { }; + testTextAttrs(ID, 0, attrs, defAttrs, 0, 0); + + ID = "area14"; + defAttrs = buildDefaultTextAttrs(ID, kInputFontSize, + kNormalFontWeight, kInputFontFamily); + + attrs = { }; + testTextAttrs(ID, 0, attrs, defAttrs, 0, 0); + + // //////////////////////////////////////////////////////////////////////// + // area15, embed char tests, "*plain*plain**bold*bold*" + ID = "area15"; + defAttrs = buildDefaultTextAttrs(ID, "12pt"); + + // p + testTextAttrs(ID, 0, { }, { }, 0, 1); + // plain + testTextAttrs(ID, 1, { }, defAttrs, 1, 6); + // p + testTextAttrs(ID, 6, { }, { }, 6, 7); + // plain + testTextAttrs(ID, 7, { }, defAttrs, 7, 12); + // p and img + testTextAttrs(ID, 12, { }, { }, 12, 14); + // bold + attrs = { "font-weight": kBoldFontWeight }; + testTextAttrs(ID, 14, attrs, defAttrs, 14, 18); + // p + testTextAttrs(ID, 18, { }, { }, 18, 19); + // bold + attrs = { "font-weight": kBoldFontWeight }; + testTextAttrs(ID, 19, attrs, defAttrs, 19, 23); + // p + testTextAttrs(ID, 23, { }, { }, 23, 24); + + // //////////////////////////////////////////////////////////////////////// + // area16, "font-family" tests + ID = "area16"; + defAttrs = buildDefaultTextAttrs(ID, "12pt"); + testDefaultTextAttrs(ID, defAttrs); + + attrs = { "font-family": kMonospaceFontFamily }; + testTextAttrs(ID, 0, attrs, defAttrs, 0, 4); + + attrs = { }; + testTextAttrs(ID, 4, attrs, defAttrs, 4, 9); + + attrs = { "font-family": kSerifFontFamily }; + testTextAttrs(ID, 9, attrs, defAttrs, 9, 13); + + attrs = { }; + testTextAttrs(ID, 13, attrs, defAttrs, 13, 18); + + attrs = { "font-family": kAbsentFontFamily }; + testTextAttrs(ID, 18, attrs, defAttrs, 18, 22); + + // bug 1224498 - this fails with 'cursive' fontconfig lookup + if (!LINUX) { + attrs = { }; + testTextAttrs(ID, 22, attrs, defAttrs, 22, 27); + + attrs = { "font-family": kCursiveFontFamily }; + testTextAttrs(ID, 27, attrs, defAttrs, 27, 31); + + attrs = { }; + testTextAttrs(ID, 31, attrs, defAttrs, 31, 45); + } + + // //////////////////////////////////////////////////////////////////////// + // area17, "text-decoration" tests + ID = "area17"; + defAttrs = buildDefaultTextAttrs(ID, "12pt"); + testDefaultTextAttrs(ID, defAttrs); + + attrs = { + "text-underline-style": "solid", + "text-underline-color": getSystemColor("CanvasText"), + }; + testTextAttrs(ID, 0, attrs, defAttrs, 0, 10); + + attrs = { + "text-underline-style": "solid", + "text-underline-color": "rgb(0, 0, 255)", + }; + testTextAttrs(ID, 10, attrs, defAttrs, 10, 15); + + attrs = { + "text-underline-style": "dotted", + "text-underline-color": getSystemColor("CanvasText"), + }; + testTextAttrs(ID, 15, attrs, defAttrs, 15, 22); + + attrs = { + "text-line-through-style": "solid", + "text-line-through-color": getSystemColor("CanvasText"), + }; + testTextAttrs(ID, 22, attrs, defAttrs, 22, 34); + + attrs = { + "text-line-through-style": "solid", + "text-line-through-color": "rgb(0, 0, 255)", + }; + testTextAttrs(ID, 34, attrs, defAttrs, 34, 39); + + attrs = { + "text-line-through-style": "wavy", + "text-line-through-color": getSystemColor("CanvasText"), + }; + testTextAttrs(ID, 39, attrs, defAttrs, 39, 44); + + // //////////////////////////////////////////////////////////////////////// + // area18, "auto-generation text" tests + ID = "area18"; + defAttrs = buildDefaultTextAttrs(ID, "12pt"); + testDefaultTextAttrs(ID, defAttrs); + + attrs = { + "auto-generated": "true", + "font-family": "-moz-bullet-font", + }; + testTextAttrs(ID, 0, attrs, defAttrs, 0, 2); + testTextAttrs(ID, 3, { }, defAttrs, 3, 7); + attrs = { + "auto-generated": "true", + }; + testTextAttrs(ID, 7, attrs, defAttrs, 7, 8); + + // //////////////////////////////////////////////////////////////////////// + // area19, "HTML5 mark tag" test + // text enclosed in mark tag will have a different background color + // However, since bug 982125, it is its own accessible. + // Therefore, anything other than the default background color is + // unexpected. + ID = "area19"; + defAttrs = buildDefaultTextAttrs(ID, "12pt"); + + attrs = {}; + testTextAttrs(ID, 0, attrs, defAttrs, 0, 10); + + ID = "area19mark"; + let defMarkAttrs = buildDefaultTextAttrs(ID, "12pt"); + attrs = {}; + testTextAttrs(ID, 0, attrs, defMarkAttrs, 0, 7); + + ID = "area19"; + attrs = {}; + testTextAttrs(ID, 11, attrs, defAttrs, 11, 22); + + // //////////////////////////////////////////////////////////////////////// + // area20, "aOffset as -1 (Mozilla Bug 789621)" test + + ID = "area20"; + defAttrs = buildDefaultTextAttrs(ID, "15pt"); + testDefaultTextAttrs(ID, defAttrs); + + testTextAttrs(ID, -1, {}, defAttrs, 0, 11); + + // //////////////////////////////////////////////////////////////////////// + // HTML sub tag offset test - verify attributes + ID = "sub_tag"; + defAttrs = buildDefaultTextAttrs(ID, "10pt"); + defAttrs["text-position"] = "sub"; + testDefaultTextAttrs(ID, defAttrs); + testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true); + + // //////////////////////////////////////////////////////////////////////// + // HTML sup tag offset test - verify attributes + ID = "sup_tag"; + defAttrs = buildDefaultTextAttrs(ID, "10pt"); + defAttrs["text-position"] = "super"; + testDefaultTextAttrs(ID, defAttrs); + testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true); + + // //////////////////////////////////////////////////////////////////////// + // ARIA subscript role - verify text-position attribute + ID = "subscript_role"; + defAttrs = { "text-position": "sub" }; + testDefaultTextAttrs(ID, defAttrs, true); + testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true); + + // //////////////////////////////////////////////////////////////////////// + // ARIA superscript role - verify text-position attribute + ID = "superscript_role"; + defAttrs = { "text-position": "super" }; + testDefaultTextAttrs(ID, defAttrs, true); + testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true); + + // //////////////////////////////////////////////////////////////////////// + // test text-position attributes in various situations + ID = "superscript_role_in_div"; + defAttrs = { "text-position": "super" }; + testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true); + + ID = "sub_within_superscript_role"; + defAttrs = { "text-position": "sub" }; + testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true); + + ID = "sup_within_subscript_role"; + defAttrs = { "text-position": "super" }; + testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true); + + ID = "sub_within_sup"; + defAttrs = { "text-position": "sub" }; + testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true); + + ID = "sup_within_sub"; + defAttrs = { "text-position": "super" }; + testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true); + + ID = "css_sub_within_superscript_role"; + attrs = { "text-position": "sub" }; + testTextAttrs(ID, 0, attrs, {}, 0, 11, true); + + ID = "css_super_within_subscript_role"; + attrs = { "text-position": "super" }; + testTextAttrs(ID, 0, attrs, {}, 0, 11, true); + + ID = "sub_with_superscript_role"; + defAttrs = { "text-position": "super" }; + testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true); + + ID = "sup_with_subscript_role"; + defAttrs = { "text-position": "sub" }; + testTextAttrs(ID, 0, {}, defAttrs, 0, 11, true); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body style="font-size: 12pt"> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=345759" + title="Implement text attributes"> + Mozilla Bug 345759 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=473569" + title="Restrict text-position to allowed values"> + Mozilla Bug 473569 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=473576" + title="font-family text attribute should expose actual font used"> + Mozilla Bug 473576 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=523304" + title="expose text-underline-color and text-line-through-color text attributes"> + Mozilla Bug 523304 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=735645" + title="expose sub and sup elements in text attributes"> + Mozilla Bug 735645 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=445516" + title="Support auto-generated text attribute on bullet lists"> + Mozilla Bug 445516 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=789621" + title="getTextAttributes doesn't work with magic offsets"> + Mozilla Bug 789621 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <p id="area1" style="font-size: smaller">Normal <b>Bold</b> Normal</p> + <p id="area2" style="font-size: 120%">Normal <b>Bold <i>Italic </i>Bold</b> Normal</p> + <p id="area3" style="background-color: blue;"> + <span style="color: green; background-color: rgb(0, 0, 255)"> + Green + <span style="color: red">but children are red</span> + </span><span style="color: green; background-color: rgb(255, 255, 0);"> + Another green section. + </span> + </p> + <p id="area4"> + <span style="color: green"> + Green + </span><span style="color: green"> + Green too + <span style="color: red">with red children</span> + Green again + </span> + </p> + <!-- Green!*!RedNormal--> + <p id="area5"> + <span style="color: green">Green</span> + <img src="../moz.png" alt="image"/> + <span style="color: red">Red</span>Normal + </p> + <p id="area6"> + This <sup>sentence</sup> has the word + <span style="vertical-align:super;">sentence</span> in + <sub>superscript</sub> and + <span style="vertical-align:sub;">subscript</span> and + <span style="vertical-align:20%;">superscript 20%</span> and + <span style="vertical-align:-20%;">subscript 20%</span> and + <span style="vertical-align:20px;">superscript 20px</span> and + <span style="vertical-align:-20px;">subscript 20px</span> + </p> + + <p lang="en" id="area7"> + <span lang="ru">Привет</span> + <span style="background-color: blue">Blue BG color</span> + <span lang="de">Ich bin/Du bist</span> + <span lang="en"> + Normal + <span style="color: magenta">Magenta<b>Bold</b>Magenta</span> + </span> + </p> + + <p id="area9" style="font-size: smaller">Small + <span style="font-size: 120%">bigger</span> smaller + <span style="background-color: blue;">background blue</span> normal + <span style="font-style: italic;">Different styling</span> normal + <span style="font-family: monospace;">Different font</span> normal + <span style="text-decoration: underline;">underlined</span> normal + <span style="text-decoration: line-through;">strikethrough</span> normal + <s>strikethrough</s> normal + <strike>strikethrough</strike> normal + </p> + + <p id="area10">Normal + <span style="font-size: 120%">bigger</span> smaller + <span style="background-color: blue;">background blue</span> normal + <span style="font-style: italic;">Different styling</span> normal + <span style="font-family: monospace;">Different font</span> normal + <span style="text-decoration: underline;">underlined</span> normal + <span style="text-decoration: line-through;">strikethrough</span> normal + </p> + + <p id="area11" style="font-weight: bolder;"> + <span style="font-weight: bolder;">bolder</span>bolder + <span style="font-weight: lighter;">lighter</span>bolder + <span style="font-weight: normal;">normal</span>bolder + <b>bold</b>bolder + <span style="font-weight: 400;">normal</span>bolder + <span style="font-weight: 700;">bold</span>bolder + <span style="font-weight: bold;">bold</span>bolder + <span style="font-weight: 900;">bold</span>bolder + </p> + + <p id="area12">hello</p> + <p id="area13"></p> + <input id="area14"> + + <!-- *plain*plain**bold*bold*--> + <div id="area15"><p>embed</p>plain<p>embed</p>plain<p>embed</p><img src="../moz.png" alt="image"/><b>bold</b><p>embed</p><b>bold</b><p>embed</p></div> + + <p id="area16" style="font-family: sans-serif;"> + <span style="font-family: monospace;">text</span>text + <span style="font-family: serif;">text</span>text + <span style="font-family: BodoniThatDoesntExist;">text</span>text + <span style="font-family: Comic Sans MS, cursive;">text</span>text + <span style="font-family: sans-serif, fantasy;">text</span>text + </p> + + <p id="area17"> + <span style="text-decoration-line: underline;">underline + </span><span style="text-decoration: underline; text-decoration-color: blue;">blue + </span><span style="text-decoration: underline; text-decoration-style: dotted;">dotted + </span><span style="text-decoration-line: line-through;">linethrough + </span><span style="text-decoration: line-through; text-decoration-color: blue;">blue + </span><span style="text-decoration: line-through; text-decoration-style: wavy;">wavy + </span> + </p> + + <ul> + <li id="area18" class="gencontent">item</li> + </ul> + + <p id="area19">uncolored + <mark id="area19mark">colored</mark> uncolored + </p> + + <p id="area20" style="font-size: 15pt;">offset test</p> + + <!-- subscript, superscript tests --> + <sub id="sub_tag">offset test</sub> + <sup id="sup_tag">offset test</sup> + <p id="subscript_role" role="subscript">offset test</p> + <p id="superscript_role" role="superscript">offset test</p> + + <div><span id="superscript_role_in_div" role="superscript">offset test</span></div> + <p role="superscript"><sub id="sub_within_superscript_role">offset test</sub></p> + <p role="subscript"><sup id="sup_within_subscript_role">offset test</sup></p> + <sup><sub id="sub_within_sup">offset test</sub></sup>
+ <sub><sup id="sup_within_sub">offset test</sup></sub>
+ <p id="css_sub_within_superscript_role" role="superscript"><span style="vertical-align: sub">offset test</span></p> + <p id="css_super_within_subscript_role" role="subscript"><span style="vertical-align: super">offset test</span></p> + <sub id="sub_with_superscript_role" role="superscript">offset test</sub> + <sup id="sup_with_subscript_role" role="subscript">offset test</sup> + +</body> +</html> diff --git a/accessible/tests/mochitest/textattrs/test_general.xhtml b/accessible/tests/mochitest/textattrs/test_general.xhtml new file mode 100644 index 0000000000..de0556f10c --- /dev/null +++ b/accessible/tests/mochitest/textattrs/test_general.xhtml @@ -0,0 +1,51 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml" + title="Tests: XUL label text interface"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + <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="../attributes.js"></script> + + <script type="application/javascript"> + <![CDATA[ + //////////////////////////////////////////////////////////////////////////// + // Testing + + function doTests() + { + ////////////////////////////////////////////////////////////////////////// + // XUL label + + testTextAttrs("label1", 0, {}, {}, 0, 5, true); + testTextAttrs("label2", 0, {}, {}, 0, 5, true); + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + ]]> + </script> + + <vbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + <label id="label1" value="Hello"/> + <label id="label2">Hello</label> + </vbox> +</window> diff --git a/accessible/tests/mochitest/textattrs/test_invalid.html b/accessible/tests/mochitest/textattrs/test_invalid.html new file mode 100644 index 0000000000..7028b32622 --- /dev/null +++ b/accessible/tests/mochitest/textattrs/test_invalid.html @@ -0,0 +1,59 @@ +<html> + +<head> + <title>Invalid text attribute</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="../attributes.js"></script> + + <script type="application/javascript"> + function doTests() { + testDefaultTextAttrs("aria_invalid_empty", {}, true); + testDefaultTextAttrs("aria_invalid_true", { "invalid": "true" }, true); + testDefaultTextAttrs("aria_invalid_false", { "invalid": "false" }, true); + testDefaultTextAttrs("aria_invalid_grammar", { "invalid": "grammar" }, true); + testDefaultTextAttrs("aria_invalid_spelling", { "invalid": "spelling" }, true); + testDefaultTextAttrs("aria_invalid_erroneous", { "invalid": "true" }, true); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + </script> +</head> + +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=445510" + title="Support ARIA-based text attributes"> + Mozilla Bug 445510 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="aria_invalid_empty" aria-invalid="">no invalid</div> + <div id="aria_invalid_true" aria-invalid="true">invalid:true</div> + <div id="aria_invalid_false" aria-invalid="false">invalid:false</div> + <div id="aria_invalid_grammar" aria-invalid="grammar">invalid:grammar</div> + <div id="aria_invalid_spelling" aria-invalid="spelling">invalid:spelling</div> + <div id="aria_invalid_erroneous" aria-invalid="erroneous">invalid:true</div> +</body> +</html> diff --git a/accessible/tests/mochitest/textattrs/test_mathml.html b/accessible/tests/mochitest/textattrs/test_mathml.html new file mode 100644 index 0000000000..e6936ebc7f --- /dev/null +++ b/accessible/tests/mochitest/textattrs/test_mathml.html @@ -0,0 +1,47 @@ +<html> + +<head> + <title>MathML Text attributes tests</title> + <meta charset="utf-8"> + <link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script src="../common.js"></script> + <script src="../attributes.js"></script> + <script src="../events.js"></script> + + <script> + function doTest() { + const math = getNode("math"); + const defAttrs = buildDefaultTextAttrs(math, "10pt"); + testDefaultTextAttrs(math, defAttrs); + + for (const id of ["mn", "mi", "annotation", "annotationXml"]) { + testTextAttrs(id, 0, {}, defAttrs, 0, 1); + } + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body style="font-size: 12pt"> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <math id="math" style="font-size: smaller"> + <mn id="mn">1</mn> + <mi id="mi">x</mi> + <!-- tabindex forces creation of an Accessible --> + <annotation id="annotation" tabindex="0">a</annotation> + <annotation-xml id="annotationXml" tabindex="0">a</annotation-xml> + </math> + +</body> +</html> diff --git a/accessible/tests/mochitest/textattrs/test_spelling.html b/accessible/tests/mochitest/textattrs/test_spelling.html new file mode 100644 index 0000000000..b8f3858353 --- /dev/null +++ b/accessible/tests/mochitest/textattrs/test_spelling.html @@ -0,0 +1,52 @@ +<html> + +<head> + <title>Spell check text attribute tests</title> + <meta charset="utf-8" /> + <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 type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../attributes.js"></script> + <script type="application/javascript" + src="../promisified-events.js"></script> + + <script type="application/javascript"> + async function doTest() { + const misspelledAttrs = {"invalid": "spelling"}; + + let editable = document.getElementById("div_after_misspelling"); + // The attr change event gets fired on the last accessible containing a + // spelling error. + let spellDone = waitForEvent(EVENT_TEXT_ATTRIBUTE_CHANGED, "div_after_misspelling_div2"); + editable.focus(); + await spellDone; + testTextAttrs("div_after_misspelling_div1", 0, {}, {}, 0, 5, true); + testTextAttrs("div_after_misspelling_div1", 5, misspelledAttrs, {}, 5, 9, true); + testTextAttrs("div_after_misspelling_div2", 0, {}, {}, 0, 5, true); + testTextAttrs("div_after_misspelling_div2", 5, misspelledAttrs, {}, 5, 9, true); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <!-- Text attribute offsets for accessibles after first spelling error (bug 1479678) --> + <div id="div_after_misspelling" contenteditable="true" spellcheck="true" lang="en-US"> + <div id="div_after_misspelling_div1">Test tset</div> + <div id="div_after_misspelling_div2">test tset</div> + </div> + +</body> +</html> diff --git a/accessible/tests/mochitest/textattrs/test_svg.html b/accessible/tests/mochitest/textattrs/test_svg.html new file mode 100644 index 0000000000..c8d5d5f893 --- /dev/null +++ b/accessible/tests/mochitest/textattrs/test_svg.html @@ -0,0 +1,52 @@ +<html> + +<head> + <title>SVG Text attributes tests</title> + <meta charset="utf-8"> + <link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script src="../common.js"></script> + <script src="../attributes.js"></script> + <script src="../events.js"></script> + + <script> + function doTest() { + const svg = getNode("svg"); + const defAttrs = buildDefaultTextAttrs(svg, "10pt"); + testDefaultTextAttrs(svg, defAttrs); + testTextAttrs(svg, 0, {}, defAttrs, 0, 2); + + const g = getNode("g"); + testTextAttrs(g, 0, {}, defAttrs, 0, 2); + + const a = getNode("a"); + const aDefAttrs = buildDefaultTextAttrs(a, "10pt"); + testTextAttrs(a, 0, {}, aDefAttrs, 0, 1); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body style="font-size: 12pt"> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <svg id="svg" style="font-size: smaller"> + <foreignobject>f1</foreignobject> + <g id="g"> + <title>g</title> + <foreignobject>f2</foreignobject> + </g> + <text><a href="#" id="a">a</a></text> + </svg> + +</body> +</html> diff --git a/accessible/tests/mochitest/textcaret/a11y.ini b/accessible/tests/mochitest/textcaret/a11y.ini new file mode 100644 index 0000000000..6e2e4ef8d7 --- /dev/null +++ b/accessible/tests/mochitest/textcaret/a11y.ini @@ -0,0 +1,5 @@ +[DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js + +[test_general.html] diff --git a/accessible/tests/mochitest/textcaret/test_general.html b/accessible/tests/mochitest/textcaret/test_general.html new file mode 100644 index 0000000000..bd949116fd --- /dev/null +++ b/accessible/tests/mochitest/textcaret/test_general.html @@ -0,0 +1,174 @@ +<html> + +<head> + <title>Text accessible caret testing</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 type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + /** + * Turn on/off the caret browsing mode. + */ + function turnCaretBrowsing(aIsOn) { + Services.prefs.setBoolPref("accessibility.browsewithcaret", aIsOn); + } + + /** + * Test caret offset for the given accessible. + */ + function testCaretOffset(aID, aCaretOffset) { + var acc = getAccessible(aID, [nsIAccessibleText]); + is(acc.caretOffset, aCaretOffset, + "Wrong caret offset for " + aID); + } + + function testCaretOffsets(aList) { + for (var i = 0; i < aList.length; i++) + testCaretOffset(aList[0][0], aList[0][1]); + } + + function queueTraversalList(aList, aFocusNode) { + for (var i = 0 ; i < aList.length; i++) { + var node = aList[i].DOMPoint[0]; + var nodeOffset = aList[i].DOMPoint[1]; + + var textAcc = aList[i].point[0]; + var textOffset = aList[i].point[1]; + var textList = aList[i].pointList; + var invoker = + new moveCaretToDOMPoint(textAcc, node, nodeOffset, textOffset, + ((i == 0) ? aFocusNode : null), + testCaretOffsets.bind(null, textList)); + gQueue.push(invoker); + } + } + + /** + * Do tests. + */ + var gQueue = null; + + // gA11yEventDumpID = "eventdump"; // debug stuff + // gA11yEventDumpToConsole = true; + + function doTests() { + turnCaretBrowsing(true); + + // test caret offsets + testCaretOffset(document, 0); // because of no selection ranges + testCaretOffset("textbox", -1); + testCaretOffset("textarea", -1); + testCaretOffset("p", -1); + + // test caret move events and caret offsets + gQueue = new eventQueue(); + + gQueue.push(new setCaretOffset("textbox", 1, "textbox")); + gQueue.push(new setCaretOffset("link", 1, "link")); + gQueue.push(new setCaretOffset("heading", 1, document)); + + // a*b*c + var p2Doc = getNode("p2_container").contentDocument; + var traversalList = [ + { // before 'a' + DOMPoint: [ getNode("p2", p2Doc).firstChild, 0 ], + point: [ getNode("p2", p2Doc), 0 ], + pointList: [ [ p2Doc, 0 ] ], + }, + { // after 'a' (before anchor) + DOMPoint: [ getNode("p2", p2Doc).firstChild, 1 ], + point: [ getNode("p2", p2Doc), 1 ], + pointList: [ [ p2Doc, 0 ] ], + }, + { // before 'b' (inside anchor) + DOMPoint: [ getNode("p2_a", p2Doc).firstChild, 0 ], + point: [ getNode("p2_a", p2Doc), 0 ], + pointList: [ + [ getNode("p2", p2Doc), 1 ], + [ p2Doc, 0 ], + ], + }, + { // after 'b' (inside anchor) + DOMPoint: [ getNode("p2_a", p2Doc).firstChild, 1 ], + point: [ getNode("p2_a", p2Doc), 1 ], + pointList: [ + [ getNode("p2", p2Doc), 1 ], + [ p2Doc, 0 ], + ], + }, + { // before 'c' (after anchor) + DOMPoint: [ getNode("p2", p2Doc).lastChild, 0 ], + point: [ getNode("p2", p2Doc), 2 ], + pointList: [ [ p2Doc, 0 ] ], + }, + { // after 'c' + DOMPoint: [ getNode("p2", p2Doc).lastChild, 1 ], + point: [ getNode("p2", p2Doc), 3 ], + pointList: [ [ p2Doc, 0 ] ], + }, + ]; + queueTraversalList(traversalList, getNode("p2", p2Doc)); + + gQueue.onFinish = function() { + turnCaretBrowsing(false); + }; + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + </script> +</head> + +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=448744" + title="caretOffset should return -1 if the system caret is not currently with in that particular object"> + Bug 448744 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=524115" + title="HyperText accessible should get focus when the caret is positioned inside of it, text is changed or copied into clipboard by ATs"> + Bug 524115 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=546068" + title="Position is not being updated when atk_text_set_caret_offset is used"> + Bug 546068 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=672717" + title="Broken caret when moving into/out of embedded objects with right arrow"> + Bug 672717 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=725581" + title="caretOffset for textarea should be -1 when textarea doesn't have a focus"> + Bug 725581 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <input id="textbox" value="hello"/> + <textarea id="textarea">text<br>text</textarea> + <p id="p" contentEditable="true"><span>text</span><br/>text</p> + <a id="link" href="about:mozilla">about mozilla</a> + <h5 id="heading">heading</h5> + <iframe id="p2_container" + src="data:text/html,<p id='p2' contentEditable='true'>a<a id='p2_a' href='mozilla.org'>b</a>c</p>"></iframe> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/textrange/a11y.ini b/accessible/tests/mochitest/textrange/a11y.ini new file mode 100644 index 0000000000..4e610c61f3 --- /dev/null +++ b/accessible/tests/mochitest/textrange/a11y.ini @@ -0,0 +1,7 @@ +[DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/moz.png + +[test_general.html] +[test_selection.html] diff --git a/accessible/tests/mochitest/textrange/test_general.html b/accessible/tests/mochitest/textrange/test_general.html new file mode 100644 index 0000000000..b7da4ee8aa --- /dev/null +++ b/accessible/tests/mochitest/textrange/test_general.html @@ -0,0 +1,106 @@ +<!DOCTYPE html> +<html> +<head> + <title>Text Range tests</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 type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../text.js"></script> + <script type="application/javascript" + src="../layout.js"></script> + <script type="application/javascript"> + + function doTest() { + // enclosingRange + var input = getAccessible("input", [ nsIAccessibleText ]); + testTextRange(input.enclosingRange, "enclosing range for 'input'", + input, 0, input, 5, "hello", input); + + var ta = getAccessible("textarea", [ nsIAccessibleText ]); + testTextRange(ta.enclosingRange, "enclosing range for 'textarea'", + ta, 0, ta, 5, "hello", ta); + + var iframeDocNode = getNode("iframe").contentDocument; + var iframeDoc = getAccessible(iframeDocNode, [ nsIAccessibleText ]); + testTextRange(iframeDoc.enclosingRange, "enclosing range for iframe doc", + iframeDoc, 0, iframeDoc, 1, "hello", + iframeDoc, [ getNode("p", iframeDocNode) ]); + + // getRangeByChild + var docacc = getAccessible(document, [ nsIAccessibleText ]); + var p1 = getAccessible("p1"); + var p1Range = docacc.getRangeByChild(p1); + testTextRange(p1Range, "range by 'p1' child", + p1, 0, "p1", 11, "text text", + p1, ["p1_img"]); + + testTextRange(docacc.getRangeByChild(getAccessible("p1_img")), + "range by 'p1_img' child", + "p1", 5, "p1", 5, "", + "p1", ["p1_img"]); + + var p2 = getAccessible("p2"); + var p2Range = docacc.getRangeByChild(p2); + testTextRange(p2Range, "range by 'p2' child", + p2, 0, "p2", 11, "text link text", + p2, ["p2_a"]); + + testTextRange(docacc.getRangeByChild(getAccessible("p2_a")), + "range by 'p2_a' child", + "p2_a", 0, "p2_a", 5, "link", + "p2_a", ["p2_img"]); + + // getRangeAtPoint + getNode("p2_a").scrollIntoView(true); + var [x, y] = getPos("p2_a"); + testTextRange(docacc.getRangeAtPoint(x + 1, y + 1), + "range at 'p2_a' top-left edge", + "p2_a", 0, "p2_a", 0, "", + "p2_a"); + + // TextRange::compare + ok(input.enclosingRange.compare(input.enclosingRange), + "input enclosing ranges should be equal"); + + ok(!input.enclosingRange.compare(ta.enclosingRange), + "input and textarea enclosing ranges can't be equal"); + + // TextRange::compareEndPoints + var res = p1Range.compareEndPoints(EndPoint_End, p2Range, EndPoint_Start); + is(res, -1, "p1 range must be lesser with p2 range"); + + res = p2Range.compareEndPoints(EndPoint_Start, p1Range, EndPoint_End); + is(res, 1, "p2 range must be greater with p1 range"); + + res = p1Range.compareEndPoints(EndPoint_Start, p1Range, EndPoint_Start); + is(res, 0, "p1 range must be equal with p1 range"); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Implement Text accessible text range methods" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=975065">Bug 975065</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <input id="input" value="hello"> + <textarea id="textarea">hello</textarea> + <iframe id="iframe" src="data:text/html,<html><body><p id='p'>hello</p></body></html>"></iframe> + <p id="p1">text <img id="p1_img", src="../moz.png"> text</p> + <p id="p2">text <a id="p2_a" href="www">link<img id="p2_img", src="../moz.png"></a> text</p> + +</body> +</html> diff --git a/accessible/tests/mochitest/textrange/test_selection.html b/accessible/tests/mochitest/textrange/test_selection.html new file mode 100644 index 0000000000..2a5d4da5c2 --- /dev/null +++ b/accessible/tests/mochitest/textrange/test_selection.html @@ -0,0 +1,144 @@ +<!DOCTYPE html> +<html> +<head> + <title>Text Range selection tests</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 type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../text.js"></script> + <script type="application/javascript" + src="../layout.js"></script> + <script type="application/javascript"> + + function doTest() { + var sel = window.getSelection(); + var p = getNode("p1"); + var a = getNode("p2_a"); + + var range = document.createRange(); + sel.addRange(range); + + // the accessible is contained by the range + range.selectNode(p); + + var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges; + var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange); + + testTextRange(a11yrange, "selection range #1", document, 3, document, 4); + + ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #1."); + testTextRange(a11yrange, "cropped range #1", a, 0, a, 5); + + // the range is contained by the accessible + range.selectNode(a); + a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges; + a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange); + + testTextRange(a11yrange, "selection range #2", p, 5, p, 6); + + ok(a11yrange.crop(getAccessible(p)), "Range failed to crop #2."); + testTextRange(a11yrange, "cropped range #2", p, 5, p, 6); + + // the range starts before the accessible and ends inside it + range.setStart(p, 0); + range.setEndAfter(a.firstChild, 4); + a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges; + a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange); + + testTextRange(a11yrange, "selection range #3", p, 0, a, 4); + + ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #3."); + testTextRange(a11yrange, "cropped range #3", a, 0, a, 4); + + // the range starts inside the accessible and ends after it + range.setStart(a.firstChild, 1); + range.setEndAfter(p); + a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges; + a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange); + + testTextRange(a11yrange, "selection range #4", a, 1, document, 4); + + ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #4."); + testTextRange(a11yrange, "cropped range #4", a, 1, a, 5); + + // the range ends before the accessible + range.setStart(p.firstChild, 0); + range.setEnd(p.firstChild, 4); + a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges; + a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange); + + testTextRange(a11yrange, "selection range #5", p, 0, p, 4); + ok(!a11yrange.crop(getAccessible(a)), "Crop #5 succeeded while it shouldn't"); + + // the range starts after the accessible + range.setStart(p.lastChild, 0); + range.setEnd(p.lastChild, 4); + a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges; + a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange); + + testTextRange(a11yrange, "selection range #6", p, 6, p, 10); + + ok(!a11yrange.crop(getAccessible(a)), "Crop #6 succeeded while it shouldn't"); + + // crop a range by a table + range.selectNode(getNode("c2")); + a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges; + a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange); + + testTextRange(a11yrange, "selection range #7", document, 4, document, 5); + + ok(a11yrange.crop(getAccessible("table")), "Range failed to crop #7."); + testTextRange(a11yrange, "cropped range #7", "table", 0, "table", 1); + + // test compare points for selection with start in nested node + range.setStart(a.firstChild, 2); + range.setEnd(p.lastChild, 3); + a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges; + a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange); + var res = a11yrange.compareEndPoints(EndPoint_Start, a11yrange, EndPoint_End); + is(res, -1, "start must be lesser than end"); + + res = a11yrange.compareEndPoints(EndPoint_End, a11yrange, EndPoint_Start); + is(res, 1, "end must be greater than start"); + + // Crop a range to its next sibling. + range.selectNode(getNode("c3p1").firstChild); + a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges; + a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange); + testTextRange(a11yrange, "selection range #8", "c3p1", 0, "c3p1", 1); + ok(!a11yrange.crop(getAccessible("c3p2")), "Crop #8 succeeded but shouldn't have."); + // Crop a range to its previous sibling. + range.selectNode(getNode("c3p2").firstChild); + a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges; + a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange); + testTextRange(a11yrange, "selection range #9", "c3p2", 0, "c3p2", 1); + ok(!a11yrange.crop(getAccessible("c3p1")), "Crop #9 succeeded but shouldn't have."); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Implement IAccessible2_3::selectionRanges" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1233118">Bug 1233118</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <p id="p1">text <a id="p2_a" href="www">link<img id="p2_img", src="../moz.png"></a> text</p> + + <div id="c2">start<table id="table"><tr><td>cell</td></tr></table>end</div> + + <div id="c3"><p id="c3p1">a</p><p id="c3p2">b</p></div> +</body> +</html> diff --git a/accessible/tests/mochitest/textselection/a11y.ini b/accessible/tests/mochitest/textselection/a11y.ini new file mode 100644 index 0000000000..6581af56db --- /dev/null +++ b/accessible/tests/mochitest/textselection/a11y.ini @@ -0,0 +1,6 @@ +[DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js + +[test_general.html] +[test_userinput.html] diff --git a/accessible/tests/mochitest/textselection/test_general.html b/accessible/tests/mochitest/textselection/test_general.html new file mode 100644 index 0000000000..92e7988a87 --- /dev/null +++ b/accessible/tests/mochitest/textselection/test_general.html @@ -0,0 +1,221 @@ +<html> + +<head> + <title>Text selection testing</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 type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../promisified-events.js"></script> + + <script type="application/javascript"> + /** + * Helper function to test selection bounds. + * @param {string} aID The ID to test. + * @param {nsIAccessibleText} acc The accessible to test. + * @param {int} index The selection's index to test. + * @param {array} offsets The start and end offset to test against. + * @param {string} msgStart The start of the message to return in test + * messages. + */ + function testSelectionBounds(aID, acc, index, offsets, msgStart) { + const [expectedStart, expectedEnd] = offsets; + const startOffset = {}, endOffset = {}; + acc.getSelectionBounds(index, startOffset, endOffset); + + is(startOffset.value, Math.min(expectedStart, expectedEnd), + msgStart + ": Wrong start offset for " + aID); + is(endOffset.value, Math.max(expectedStart, expectedEnd), + msgStart + ": Wrong end offset for " + aID); + } + + /** + * Test adding selections to accessibles. + * @param {string} aID The ID of the element to test. + * @param {array} aSelections Array of selection start and end indices. + */ + async function addSelections(aID, aSelections) { + info("Test adding selections to " + aID); + const hyperText = getAccessible(aID, [ nsIAccessibleText ]); + const initialSelectionCount = hyperText.selectionCount; + + // Multiple selection changes will be coalesced, so just listen for one. + const selectionChange = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, aID); + for (let [startOffset, endOffset] of aSelections) { + hyperText.addSelection(startOffset, endOffset); + } + await selectionChange; + + is(hyperText.selectionCount, + aSelections.length + initialSelectionCount, + "addSelection: Wrong selection count for " + aID); + + for (let i in aSelections) { + testSelectionBounds(aID, hyperText, initialSelectionCount + i, + aSelections[i], "addSelection"); + } + + is(hyperText.caretOffset, aSelections[hyperText.selectionCount -1][1], + "addSelection: caretOffset not at selection end for " + aID); + } + + /** + * Test changing selections in accessibles. + * @param {string} aID The ID of the element to test. + * @param {int} aIndex The index of the selection to change. + * @param {array} aSelection Array of the selection's new start and end + * indices. + */ + async function changeSelection(aID, aIndex, aSelection) { + info("Test changing the selection of " + aID + " at index " + aIndex); + const [startOffset, endOffset] = aSelection; + const hyperText = getAccessible(aID, [ nsIAccessibleText ]); + + const selectionChanged = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, aID); + hyperText.setSelectionBounds(aIndex, startOffset, endOffset); + await selectionChanged; + + testSelectionBounds(aID, hyperText, aIndex, + aSelection, "setSelectionBounds"); + + is(hyperText.caretOffset, endOffset, + "setSelectionBounds: caretOffset not at selection end for " + aID); + } + + /** + * Test removing all selections from accessibles. + * @param {string} aID The ID of the element to test. + */ + async function removeSelections(aID) { + info("Testing removal of all selections from " + aID); + const hyperText = getAccessible(aID, [ nsIAccessibleText ]); + + let selectionsRemoved = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, document); + const selectionCount = hyperText.selectionCount; + for (let i = 0; i < selectionCount; i++) { + hyperText.removeSelection(0); + } + await selectionsRemoved; + + is(hyperText.selectionCount, 0, + "removeSelection: Wrong selection count for " + aID); + } + + /** + * Test that changing the DOM selection is reflected in the accessibles. + * @param {string} aID The container ID to test in + * @param {string} aNodeID1 The start node of the selection + * @param {int} aNodeOffset1 The offset where the selection should start + * @param {string} aNodeID2 The node in which the selection should end + * @param {int} aNodeOffset2 The index at which the selection should end + * @param {array} aTests An array of accessibles and their start and end + * offsets to test. + */ + async function changeDOMSelection(aID, aNodeID1, aNodeOffset1, + aNodeID2, aNodeOffset2, + aTests) { + info("Test that DOM selection changes are reflected in the accessibles"); + + let selectionChanged = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, aID); + // HyperTextAccessible::GetSelectionDOMRanges ignores hidden selections. + // Here we may be focusing an editable element (and thus hiding the + // main document selection), so blur it so that we test what we want to + // test. + document.activeElement.blur(); + + const sel = window.getSelection(); + const range = document.createRange(); + range.setStart(getNode(aNodeID1), aNodeOffset1); + range.setEnd(getNode(aNodeID2), aNodeOffset2); + sel.addRange(range); + await selectionChanged; + + for (let i = 0; i < aTests.length; i++) { + const text = getAccessible(aTests[i][0], nsIAccessibleText); + is(text.selectionCount, 1, + "setSelectionBounds: Wrong selection count for " + aID); + testSelectionBounds(aID, text, 0, [aTests[i][1], aTests[i][2]], + "setSelectionBounds"); + } + } + + /** + * Test expected and unexpected events for selecting + * all text and focusing both an input and text area. We expect a caret + * move, but not a text selection change. + * @param {string} aID The ID of the element to test. + */ + async function eventsForSelectingAllTextAndFocus(aID) { + info("Test expected caretMove and unexpected textSelection events for " +aID); + let events = waitForEvents({ + expected: [[EVENT_TEXT_CARET_MOVED, aID]], + unexpected: [[EVENT_TEXT_SELECTION_CHANGED, aID]]}, aID); + selectAllTextAndFocus(aID); + await events; + } + + /** + * Do tests + */ + + async function doTests() { + await addSelections("paragraph", [[1, 3], [6, 10]]); + await changeSelection("paragraph", 0, [2, 4]); + await removeSelections("paragraph"); + + // reverse selection + await addSelections("paragraph", [[1, 3], [10, 6]]); + await removeSelections("paragraph"); + + await eventsForSelectingAllTextAndFocus("textbox"); + await changeSelection("textbox", 0, [1, 3]); + + // reverse selection + await changeSelection("textbox", 0, [3, 1]); + + await eventsForSelectingAllTextAndFocus("textarea"); + await changeSelection("textarea", 0, [1, 3]); + + await changeDOMSelection("c1", "c1_span1", 0, "c1_span2", 0, + [["c1", 2, 2]]); + await changeDOMSelection("c2", "c2", 0, "c2_div2", 1, + [["c2", 0, 3], ["c2_div2", 0, 2]]); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + </script> +</head> + +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=688126" + title="nsIAccessibleText::setSelectionBounds doesn't fire text selection changed events in some cases"> + Bug 688126 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=688124" + title="no text selection changed event when selection is removed"> + Bug 688124 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <p id="paragraph">hello world</p> + <input id="textbox" value="hello"/> + <textarea id="textarea">hello</textarea> + <div id="c1">hi<span id="c1_span1"></span><span id="c1_span2"></span>hi</div> + <div id="c2">hi<div id="c2_div2">hi</div></div> + +</body> +</html> diff --git a/accessible/tests/mochitest/textselection/test_userinput.html b/accessible/tests/mochitest/textselection/test_userinput.html new file mode 100644 index 0000000000..1aa0b2abb6 --- /dev/null +++ b/accessible/tests/mochitest/textselection/test_userinput.html @@ -0,0 +1,76 @@ +<html> + +<head> + <title>Text selection by user input</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="../promisified-events.js"></script> + + <script type="application/javascript"> + async function doTests() { + // Tab to 't2' and then tab out of it: it must not have any selection. + info("Select all text in t1 and focus it"); + let focused = waitForEvent(EVENT_FOCUS, "t1"); + // Simulate tabbing to t1 by selecting all text before focusing it. + selectAllTextAndFocus("t1"); + await focused; + + info("Tab to t2"); + const t2 = getNode("t2"); + focused = waitForEvent(EVENT_FOCUS, t2); + synthesizeKey("VK_TAB"); + await focused; + + info("Tab to t3 and make sure there is no selection in t2 afterwards"); + const t3 = getNode("t3"); + focused = waitForEvent(EVENT_FOCUS, t3); + synthesizeKey("VK_TAB"); + await focused; + const prevFocus = getAccessible(t2, [ nsIAccessibleText ]); + is(prevFocus.selectionCount, 0, + "Wrong selection count for t2"); + + let exceptionCaught = false; + try { + const startOffsetObj = {}, endOffsetObj = {}; + prevFocus.getSelectionBounds(0, startOffsetObj, endOffsetObj); + } catch (e) { + exceptionCaught = true; + } + + ok(exceptionCaught, "No selection was expected for t2"); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + </script> +</head> + +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=440590" + title="Text selection information is not updated when HTML and XUL entries lose focus"> + Bug 440590 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <input type="text" id="t1" maxlength="3" size="3" value="1"> + <input type="text" id="t2" maxlength="3" size="3" value="1"> + <input type="text" id="t3" maxlength="3" size="3" value="1"> + +</body> +</html> |