Bug 630001 - Stop usage of nsIFrame::GetRenderedText in GetTextAt/Before/After for boundary char, r=fer, a=betaN
authorAlexander Surkov <surkov.alexander@gmail.com>
Thu, 03 Feb 2011 22:29:12 +0800
changeset 61843 e9fe7402dc536c0ba9386381da934b718bf19795
parent 61842 f6cbc806e87cdf852218c232bd261666a4844f88
child 61844 dfe5ab77a706b6d795d406b9a13f56c666e8cc0c
push idunknown
push userunknown
push dateunknown
reviewersfer, betaN
bugs630001
milestone2.0b12pre
Bug 630001 - Stop usage of nsIFrame::GetRenderedText in GetTextAt/Before/After for boundary char, r=fer, a=betaN
accessible/src/html/nsHyperTextAccessible.cpp
accessible/src/html/nsHyperTextAccessible.h
accessible/tests/mochitest/text.js
accessible/tests/mochitest/text/test_singleline.html
accessible/tests/mochitest/text/test_whitespaces.html
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -478,61 +478,53 @@ NS_IMETHODIMP
 nsHyperTextAccessible::GetText(PRInt32 aStartOffset, PRInt32 aEndOffset,
                                nsAString &aText)
 {
   aText.Truncate();
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  if (aStartOffset == nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT)
-    aStartOffset = CharacterCount();
-  else if (aStartOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
-    GetCaretOffset(&aStartOffset);
-
-  if (aEndOffset == nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT)
-    aEndOffset = CharacterCount();
-  else if (aEndOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
-    GetCaretOffset(&aEndOffset);
-
-  PRInt32 startChildIdx = GetChildIndexAtOffset(aStartOffset);
+  PRInt32 startOffset = ConvertMagicOffset(aStartOffset);
+  PRInt32 startChildIdx = GetChildIndexAtOffset(startOffset);
   if (startChildIdx == -1)
     return NS_ERROR_INVALID_ARG;
 
-  PRInt32 endChildIdx = GetChildIndexAtOffset(aEndOffset);
+  PRInt32 endOffset = ConvertMagicOffset(aEndOffset);
+  PRInt32 endChildIdx = GetChildIndexAtOffset(endOffset);
   if (endChildIdx == -1)
     return NS_ERROR_INVALID_ARG;
 
   if (startChildIdx == endChildIdx) {
     PRInt32 childOffset =  GetChildOffset(startChildIdx);
     NS_ENSURE_STATE(childOffset != -1);
 
     nsAccessible* child = GetChildAt(startChildIdx);
-    child->AppendTextTo(aText, aStartOffset - childOffset,
-                        aEndOffset - aStartOffset);
+    child->AppendTextTo(aText, startOffset - childOffset,
+                        endOffset - startOffset);
 
     return NS_OK;
   }
 
   PRInt32 startChildOffset =  GetChildOffset(startChildIdx);
   NS_ENSURE_STATE(startChildOffset != -1);
 
   nsAccessible* startChild = GetChildAt(startChildIdx);
-  startChild->AppendTextTo(aText, aStartOffset - startChildOffset);
+  startChild->AppendTextTo(aText, startOffset - startChildOffset);
 
   for (PRInt32 childIdx = startChildIdx + 1; childIdx < endChildIdx; childIdx++) {
     nsAccessible* child = GetChildAt(childIdx);
     child->AppendTextTo(aText);
   }
 
   PRInt32 endChildOffset =  GetChildOffset(endChildIdx);
   NS_ENSURE_STATE(endChildOffset != -1);
 
   nsAccessible* endChild = GetChildAt(endChildIdx);
-  endChild->AppendTextTo(aText, 0, aEndOffset - endChildOffset);
+  endChild->AppendTextTo(aText, 0, endOffset - endChildOffset);
 
   return NS_OK;
 }
 
 /*
  * Gets the character count.
  */
 NS_IMETHODIMP nsHyperTextAccessible::GetCharacterCount(PRInt32 *aCharacterCount)
@@ -547,30 +539,29 @@ NS_IMETHODIMP nsHyperTextAccessible::Get
   return NS_OK;
 }
 
 /*
  * Gets the specified character.
  */
 NS_IMETHODIMP nsHyperTextAccessible::GetCharacterAtOffset(PRInt32 aOffset, PRUnichar *aCharacter)
 {
+  NS_ENSURE_ARG_POINTER(aCharacter);
+  *aCharacter = nsnull;
+
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  nsAutoString text;
-  nsresult rv = GetText(aOffset, aOffset + 1, text);
-  if (NS_FAILED(rv)) {
-    return rv;
+  nsAutoString character;
+  if (GetCharAt(aOffset, eGetAt, character)) {
+    *aCharacter = character.First();
+    return NS_OK;
   }
 
-  if (text.IsEmpty()) {
-    return NS_ERROR_FAILURE;
-  }
-  *aCharacter = text.First();
-  return NS_OK;
+  return NS_ERROR_INVALID_ARG;
 }
 
 nsAccessible*
 nsHyperTextAccessible::DOMPointToHypertextOffset(nsINode *aNode,
                                                  PRInt32 aNodeOffset,
                                                  PRInt32 *aHyperTextOffset,
                                                  PRBool aIsEndOffset)
 {
@@ -1087,28 +1078,43 @@ nsresult nsHyperTextAccessible::GetTextH
 }
 
 /**
   * nsIAccessibleText impl.
   */
 NS_IMETHODIMP nsHyperTextAccessible::GetTextBeforeOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType,
                                                          PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText)
 {
+  if (aBoundaryType == BOUNDARY_CHAR) {
+    GetCharAt(aOffset, eGetBefore, aText, aStartOffset, aEndOffset);
+    return NS_OK;
+  }
+
   return GetTextHelper(eGetBefore, aBoundaryType, aOffset, aStartOffset, aEndOffset, aText);
 }
 
 NS_IMETHODIMP nsHyperTextAccessible::GetTextAtOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType,
                                                      PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText)
 {
+  if (aBoundaryType == BOUNDARY_CHAR) {
+    GetCharAt(aOffset, eGetAt, aText, aStartOffset, aEndOffset);
+    return NS_OK;
+  }
+
   return GetTextHelper(eGetAt, aBoundaryType, aOffset, aStartOffset, aEndOffset, aText);
 }
 
 NS_IMETHODIMP nsHyperTextAccessible::GetTextAfterOffset(PRInt32 aOffset, nsAccessibleTextBoundary aBoundaryType,
                                                         PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsAString & aText)
 {
+  if (aBoundaryType == BOUNDARY_CHAR) {
+    GetCharAt(aOffset, eGetAfter, aText, aStartOffset, aEndOffset);
+    return NS_OK;
+  }
+
   return GetTextHelper(eGetAfter, aBoundaryType, aOffset, aStartOffset, aEndOffset, aText);
 }
 
 // nsIPersistentProperties
 // nsIAccessibleText::getTextAttributes(in boolean includeDefAttrs,
 //                                      in long offset,
 //                                      out long rangeStartOffset,
 //                                      out long rangeEndOffset);
@@ -2159,16 +2165,39 @@ nsresult nsHyperTextAccessible::Rendered
   *aContentOffset = iter.ConvertSkippedToOriginal(aRenderedOffset + ourRenderedStart) - ourContentStart;
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHyperTextAccessible public
 
+bool
+nsHyperTextAccessible::GetCharAt(PRInt32 aOffset, EGetTextType aShift,
+                                 nsAString& aChar, PRInt32* aStartOffset,
+                                 PRInt32* aEndOffset)
+{
+  aChar.Truncate();
+
+  PRInt32 offset = ConvertMagicOffset(aOffset) + static_cast<PRInt32>(aShift);
+  PRInt32 childIdx = GetChildIndexAtOffset(offset);
+  if (childIdx == -1)
+    return false;
+
+  nsAccessible* child = GetChildAt(childIdx);
+  child->AppendTextTo(aChar, offset - GetChildOffset(childIdx), 1);
+
+  if (aStartOffset)
+    *aStartOffset = offset;
+  if (aEndOffset)
+    *aEndOffset = aChar.IsEmpty() ? offset : offset + 1;
+
+  return true;
+}
+
 PRInt32
 nsHyperTextAccessible::GetChildOffset(PRUint32 aChildIndex,
                                       PRBool aInvalidateAfter)
 {
   if (aChildIndex == 0) {
     if (aInvalidateAfter)
       mOffsets.Clear();
 
--- a/accessible/src/html/nsHyperTextAccessible.h
+++ b/accessible/src/html/nsHyperTextAccessible.h
@@ -206,16 +206,30 @@ public:
    * Return character count within the hypertext accessible.
    */
   inline PRUint32 CharacterCount()
   {
     return GetChildOffset(GetChildCount());
   }
 
   /**
+   * Get a character before/at/after the given offset.
+   *
+   * @param aOffset       [in] the given offset
+   * @param aShift        [in] specifies whether to get a char before/at/after
+   *                        offset
+   * @param aChar         [out] the character
+   * @param aStartOffset  [out, optional] the start offset of the character
+   * @param aEndOffset    [out, optional] the end offset of the character
+   * @return               false if offset at the given shift is out of range
+   */
+  bool GetCharAt(PRInt32 aOffset, EGetTextType aShift, nsAString& aChar,
+                 PRInt32* aStartOffset = nsnull, PRInt32* aEndOffset = nsnull);
+
+  /**
    * Return text offset of the given child accessible within hypertext
    * accessible.
    *
    * @param  aChild           [in] accessible child to get text offset for
    * @param  aInvalidateAfter [in, optional] indicates whether invalidate
    *                           cached offsets for next siblings of the child
    */
   PRInt32 GetChildOffset(nsAccessible* aChild,
@@ -246,16 +260,33 @@ public:
   nsAccessible* GetChildAtOffset(PRUint32 aOffset)
   {
     return GetChildAt(GetChildIndexAtOffset(aOffset));
   }
 
 protected:
   // nsHyperTextAccessible
 
+  /**
+   * Transform magic offset into text offset.
+   */
+  inline PRInt32 ConvertMagicOffset(PRInt32 aOffset)
+  {
+    if (aOffset == nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT)
+      return CharacterCount();
+
+    if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET) {
+      PRInt32 caretOffset = -1;
+      GetCaretOffset(&caretOffset);
+      return caretOffset;
+    }
+
+    return aOffset;
+  }
+
   /*
    * This does the work for nsIAccessibleText::GetText[At|Before|After]Offset
    * @param aType, eGetBefore, eGetAt, eGetAfter
    * @param aBoundaryType, char/word-start/word-end/line-start/line-end/paragraph/attribute
    * @param aOffset, offset into the hypertext to start from
    * @param *aStartOffset, the resulting start offset for the returned substring
    * @param *aEndOffset, the resulting end offset for the returned substring
    * @param aText, the resulting substring
--- a/accessible/tests/mochitest/text.js
+++ b/accessible/tests/mochitest/text.js
@@ -39,16 +39,38 @@ function testText(aIDs, aStartOffset, aE
       ok(false,
          "getText fails between start and end offsets '" + aStartOffset +
          "', '" + aEndOffset + " for '" + prettyName(aIDs[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 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 tuples made of:
@@ -71,29 +93,50 @@ function testTextAtOffset(aOffset, aBoun
     testTextHelper(ID, aOffset, aBoundaryType,
                    aText, aStartOffset, aEndOffset,
                    toDoFlag1, toDoFlag2, toDoFlag3,
                    acc.getTextAtOffset, "getTextAtOffset ");
   }
 }
 
 /**
+ * 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 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 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)
 {
   for (var i = 5; i < arguments.length; i = i + 4) {
     var ID = arguments[i];
     var acc = getAccessible(ID, nsIAccessibleText);
     var toDoFlag1 = arguments[i + 1];
@@ -103,16 +146,38 @@ function testTextAfterOffset(aOffset, aB
     testTextHelper(ID, aOffset, aBoundaryType,
                    aText, aStartOffset, aEndOffset,
                    toDoFlag1, toDoFlag2, toDoFlag3, 
                    acc.getTextAfterOffset, "getTextAfterOffset ");
   }
 }
 
 /**
+ * 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 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 tuples made of:
--- a/accessible/tests/mochitest/text/test_singleline.html
+++ b/accessible/tests/mochitest/text/test_singleline.html
@@ -32,37 +32,33 @@
       testText(IDs, 0, 1, "h");
       testText(IDs, 1, 3, "el");
       testText(IDs, 14, 15, "d");
       testText(IDs, 0, 15, "hello my friend");
 
       ////////////////////////////////////////////////////////////////////////
       // getTextAfterOffset
 
+      var IDs = [ "input", "div", "editable", "textarea" ];
+      var regularIDs = [ "input", "div", "editable" ];
+
       // BOUNDARY_CHAR
-      testTextAfterOffset(0, BOUNDARY_CHAR, "e", 1, 2,
-                          "input", kTodo, kTodo, kTodo,
-                          "div", kTodo, kTodo, kTodo,
-                          "editable", kTodo, kTodo, kTodo,
-                          "textarea", kTodo, kTodo, kTodo);
-      testTextAfterOffset(1, BOUNDARY_CHAR, "l", 2, 3,
-                          "input", kTodo, kTodo, kTodo,
-                          "div", kTodo, kTodo, kTodo,
-                          "editable", kTodo, kTodo, kTodo,
-                          "textarea", kTodo, kTodo, kTodo);
-      testTextAfterOffset(14, BOUNDARY_CHAR, "", 15, 15,
-                          "input", kTodo, kTodo, kOk,
-                          "div", kTodo, kTodo, kOk,
-                          "editable", kTodo, kTodo, kOk,
-                          "textarea", kTodo, kTodo, kOk);
+
+      testCharAfterOffset(IDs, 0, "e", 1, 2);
+      testCharAfterOffset(IDs, 1, "l", 2, 3);
+      testCharAfterOffset(regularIDs, 14, "", 15, 15);
+      testCharAfterOffset("textarea", 14, "\n", 15, 16);
+
+      // XXX: why are 15/15 expected? there's no 16 offset we are trying to
+      // get an offset for?
       testTextAfterOffset(15, BOUNDARY_CHAR, "", 15, 15,
 			  "input", kOk, kTodo, kTodo,
 			  "div", kOk, kTodo, kTodo,
-			  "editable", kOk, kTodo, kTodo,
-			  "textarea", kTodo, kOk, kTodo);
+			  "editable", kOk, kTodo, kTodo);
+      testCharAfterOffset("textarea", 15, "", 16, 16);
 
       // BOUNDARY_WORD_START
       testTextAfterOffset(0, BOUNDARY_WORD_START, "my ", 6, 9,
                           "input", kTodo, kTodo, kTodo,
                           "div", kTodo, kTodo, kTodo,
                           "editable", kTodo, kTodo, kTodo,
                           "textarea", kTodo, kTodo, kTodo);
       testTextAfterOffset(1, BOUNDARY_WORD_START, "my ", 6, 9,
@@ -205,37 +201,23 @@
                           "input", kOk, kTodo, kTodo,
                           "div", kOk, kTodo, kTodo,
                           "editable", kOk, kTodo, kTodo,
                           "textarea", kOk, kTodo, kTodo);
 
       ////////////////////////////////////////////////////////////////////////
       // getTextBeforeOffset
 
+      var IDs = [ "input", "div", "editable", "textarea" ];
+
       // BOUNDARY_CHAR
-      testTextBeforeOffset(0, BOUNDARY_CHAR, "", 0, 0, 
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(1, BOUNDARY_CHAR, "h", 0, 1,
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(14, BOUNDARY_CHAR, "n", 13, 14,
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(15, BOUNDARY_CHAR, "d", 14, 15,
-                           "input", kTodo, kTodo, kTodo,
-                           "div", kTodo, kTodo, kTodo,
-                           "editable", kTodo, kTodo, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
+      testCharBeforeOffset(IDs, 0, "", 0, 0);
+      testCharBeforeOffset(IDs, 1, "h", 0, 1);
+      testCharBeforeOffset(IDs, 14, "n", 13, 14);
+      testCharBeforeOffset(IDs, 15, "d", 14, 15);
 
       // BOUNDARY_WORD_START
       testTextBeforeOffset(0, BOUNDARY_WORD_START, "", 0, 0,
                            "input", kTodo, kOk, kTodo,
                            "div", kTodo, kOk, kTodo,
                            "editable", kTodo, kOk, kTodo,
                            "textarea", kTodo, kOk, kTodo);
       testTextBeforeOffset(1, BOUNDARY_WORD_START, "", 0, 0,
@@ -378,37 +360,27 @@
                            "input", kOk, kOk, kOk,
                            "div", kOk, kOk, kOk,
                            "editable", kOk, kOk, kOk,
                            "textarea", kOk, kOk, kOk);
 
       ////////////////////////////////////////////////////////////////////////
       // getTextAtOffset
 
+      IDs = [ "input", "div", "editable", "textarea" ];
+      regularIDs = [ "input", "div", "editable" ];
+
       // BOUNDARY_CHAR
-      testTextAtOffset(0, BOUNDARY_CHAR, "h", 0, 1,
-                       "input", kOk, kOk, kOk,
-                       "div", kOk, kOk, kOk,
-                       "editable", kOk, kOk, kOk,
-                       "textarea", kOk, kOk, kOk);
-      testTextAtOffset(1, BOUNDARY_CHAR, "e", 1, 2,
-                       "input", kOk, kOk, kOk,
-                       "div", kOk, kOk, kOk,
-                       "editable", kOk, kOk, kOk,
-                       "textarea", kOk, kOk, kOk);
-      testTextAtOffset(14, BOUNDARY_CHAR, "d", 14, 15,
-                       "input", kOk, kOk, kOk,
-                       "div", kOk, kOk, kOk,
-                       "editable", kOk, kOk, kOk,
-                       "textarea", kOk, kOk, kOk);
-      testTextAtOffset(15, BOUNDARY_CHAR, "", 15, 15,
-                        "input", kOk, kTodo, kTodo,
-                        "div", kOk, kTodo, kTodo,
-                        "editable", kOk, kTodo, kTodo,
-                        "textarea", kTodo, kOk, kTodo);
+
+      testCharAtOffset(IDs, 0, "h", 0, 1);
+      testCharAtOffset(IDs, 1, "e", 1, 2);
+      testCharAtOffset(IDs, 14, "d", 14, 15);
+      testCharAtOffset(regularIDs, 15, "", 15, 15);
+      testCharAtOffset("textarea", 15, "\n", 15, 16);
+      testCharAtOffset("textarea", 16, "", 16, 16);
 
       // BOUNDARY_WORD_START
       testTextAtOffset(0, BOUNDARY_WORD_START, "hello ", 0, 6,
                        "input", kOk, kOk, kOk,
                        "div", kOk, kOk, kOk,
                        "editable", kOk, kOk, kOk,
                        "textarea", kOk, kOk, kOk);
       testTextAtOffset(1, BOUNDARY_WORD_START, "hello ", 0, 6,
--- a/accessible/tests/mochitest/text/test_whitespaces.html
+++ b/accessible/tests/mochitest/text/test_whitespaces.html
@@ -36,72 +36,30 @@
       testText(IDs, 5, 6, " ");
       testText(IDs, 9, 11, "  ");
       testText(IDs, 16, 19, "   ");
       testText(IDs, 0, 22, "Brave Sir  Robin   ran");
 
       ////////////////////////////////////////////////////////////////////////
       // getTextAfterOffset
 
+      var IDs = [ "input", "div", "editable", "textarea" ];
+
       // BOUNDARY_CHAR
-      testTextAfterOffset(0, BOUNDARY_CHAR, "r", 1, 2,
-                          "input", kTodo, kTodo, kTodo,
-                          "div", kTodo, kTodo, kTodo,
-                          "editable", kTodo, kTodo, kTodo,
-                          "textarea", kTodo, kTodo, kTodo);
-      testTextAfterOffset(1, BOUNDARY_CHAR, "a", 2, 3,
-                          "input", kTodo, kTodo, kTodo,
-                          "div", kTodo, kTodo, kTodo,
-                          "editable", kTodo, kTodo, kTodo,
-                          "textarea", kTodo, kTodo, kTodo);
-      testTextAfterOffset(4, BOUNDARY_CHAR, " ", 5, 6,
-                          "input", kTodo, kTodo, kTodo,
-                          "div", kTodo, kTodo, kTodo,
-                          "editable", kTodo, kTodo, kTodo,
-                          "textarea", kTodo, kTodo, kTodo);
-      testTextAfterOffset(5, BOUNDARY_CHAR, "S", 6, 7,
-                          "input", kTodo, kTodo, kTodo,
-                          "div", kTodo, kTodo, kTodo,
-                          "editable", kTodo, kTodo, kTodo,
-                          "textarea", kTodo, kTodo, kTodo);
-      testTextAfterOffset(8, BOUNDARY_CHAR, " ", 9, 10,
-                          "input", kTodo, kTodo, kTodo,
-                          "div", kTodo, kTodo, kTodo,
-                          "editable", kTodo, kTodo, kTodo,
-                          "textarea", kTodo, kTodo, kTodo);
-      testTextAfterOffset(9, BOUNDARY_CHAR, " ", 10, 11,
-                          "input", kOk, kTodo, kTodo,
-                          "div", kOk, kTodo, kTodo,
-                          "editable", kOk, kTodo, kTodo,
-                          "textarea", kOk, kTodo, kTodo);
-      testTextAfterOffset(10, BOUNDARY_CHAR, "R", 11, 12,
-			  "input", kTodo, kTodo, kTodo,
-			  "div", kTodo, kTodo, kTodo,
-			  "editable", kTodo, kTodo, kTodo,
-			  "textarea", kTodo, kTodo, kTodo);
-      testTextAfterOffset(15, BOUNDARY_CHAR, " ", 16, 17,
-			  "input", kTodo, kTodo, kTodo,
-			  "div", kTodo, kTodo, kTodo,
-			  "editable", kTodo, kTodo, kTodo,
-			  "textarea", kTodo, kTodo, kTodo);
-      testTextAfterOffset(16, BOUNDARY_CHAR, " ", 17, 18,
-			  "input", kOk, kTodo, kTodo,
-			  "div", kOk, kTodo, kTodo,
-			  "editable", kOk, kTodo, kTodo,
-			  "textarea", kOk, kTodo, kTodo);
-      testTextAfterOffset(17, BOUNDARY_CHAR, " ", 18, 19,
-			  "input", kOk, kTodo, kTodo,
-			  "div", kOk, kTodo, kTodo,
-			  "editable", kOk, kTodo, kTodo,
-			  "textarea", kOk, kTodo, kTodo);
-      testTextAfterOffset(18, BOUNDARY_CHAR, "r", 19, 20,
-			  "input", kTodo, kTodo, kTodo,
-			  "div", kTodo, kTodo, kTodo,
-			  "editable", kTodo, kTodo, kTodo,
-			  "textarea", kTodo, kTodo, kTodo);
+      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);
 
       // BOUNDARY_WORD_START
       testTextAfterOffset(0, BOUNDARY_WORD_START, "Sir  ", 6, 11,
                           "input", kTodo, kTodo, kTodo,
                           "div", kTodo, kTodo, kTodo,
                           "editable", kTodo, kTodo, kTodo,
                           "textarea", kTodo, kTodo, kTodo);
       testTextAfterOffset(5, BOUNDARY_WORD_START, "Sir  ", 6, 11,
@@ -220,52 +178,26 @@
                           "input", kOk, kTodo, kTodo,
                           "div", kOk, kTodo, kTodo,
                           "editable", kOk, kTodo, kTodo,
                           "textarea", kTodo, kOk, kTodo);
 
       ////////////////////////////////////////////////////////////////////////
       // getTextBeforeOffset
 
+      var IDs = [ "input", "div", "editable", "textarea" ];
+
       // BOUNDARY_CHAR
-      testTextBeforeOffset(0, BOUNDARY_CHAR, "", 0, 0, 
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(1, BOUNDARY_CHAR, "B", 0, 1,
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(6, BOUNDARY_CHAR, " ", 5, 6,
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(10, BOUNDARY_CHAR, " ", 9, 10,
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(11, BOUNDARY_CHAR, " ", 10, 11,
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(17, BOUNDARY_CHAR, " ", 16, 17,
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
-      testTextBeforeOffset(19, BOUNDARY_CHAR, " ", 18, 19,
-                           "input", kTodo, kOk, kTodo,
-                           "div", kTodo, kOk, kTodo,
-                           "editable", kTodo, kOk, kTodo,
-                           "textarea", kTodo, kOk, kTodo);
+      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);
 
       // BOUNDARY_WORD_START
       testTextBeforeOffset(0, BOUNDARY_WORD_START, "", 0, 0,
                            "input", kTodo, kOk, kTodo,
                            "div", kTodo, kOk, kTodo,
                            "editable", kTodo, kOk, kTodo,
                            "textarea", kTodo, kOk, kTodo);
       testTextBeforeOffset(1, BOUNDARY_WORD_START, "", 0, 0,