summaryrefslogtreecommitdiffstats
path: root/accessible/tests/mochitest/text
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--accessible/tests/mochitest/text.js814
-rw-r--r--accessible/tests/mochitest/text/a11y.ini19
-rw-r--r--accessible/tests/mochitest/text/doc.html9
-rw-r--r--accessible/tests/mochitest/text/test_atcaretoffset.html425
-rw-r--r--accessible/tests/mochitest/text/test_charboundary.html138
-rw-r--r--accessible/tests/mochitest/text/test_doc.html40
-rw-r--r--accessible/tests/mochitest/text/test_dynamic.html80
-rw-r--r--accessible/tests/mochitest/text/test_general.xhtml79
-rw-r--r--accessible/tests/mochitest/text/test_gettext.html135
-rw-r--r--accessible/tests/mochitest/text/test_hypertext.html150
-rw-r--r--accessible/tests/mochitest/text/test_lineboundary.html422
-rw-r--r--accessible/tests/mochitest/text/test_paragraphboundary.html148
-rw-r--r--accessible/tests/mochitest/text/test_passwords.html72
-rw-r--r--accessible/tests/mochitest/text/test_selection.html119
-rw-r--r--accessible/tests/mochitest/text/test_settext_input_event.html38
-rw-r--r--accessible/tests/mochitest/text/test_textBounds.html36
-rw-r--r--accessible/tests/mochitest/text/test_wordboundary.html361
-rw-r--r--accessible/tests/mochitest/text/test_words.html131
-rw-r--r--accessible/tests/mochitest/textattrs/a11y.ini11
-rw-r--r--accessible/tests/mochitest/textattrs/test_general.html823
-rw-r--r--accessible/tests/mochitest/textattrs/test_general.xhtml51
-rw-r--r--accessible/tests/mochitest/textattrs/test_invalid.html59
-rw-r--r--accessible/tests/mochitest/textattrs/test_mathml.html47
-rw-r--r--accessible/tests/mochitest/textattrs/test_spelling.html52
-rw-r--r--accessible/tests/mochitest/textattrs/test_svg.html52
-rw-r--r--accessible/tests/mochitest/textcaret/a11y.ini5
-rw-r--r--accessible/tests/mochitest/textcaret/test_general.html174
-rw-r--r--accessible/tests/mochitest/textrange/a11y.ini7
-rw-r--r--accessible/tests/mochitest/textrange/test_general.html106
-rw-r--r--accessible/tests/mochitest/textrange/test_selection.html144
-rw-r--r--accessible/tests/mochitest/textselection/a11y.ini6
-rw-r--r--accessible/tests/mochitest/textselection/test_general.html221
-rw-r--r--accessible/tests/mochitest/textselection/test_userinput.html76
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>