Bug 406069 - nsIAccessibleText::GetTextAtOffset() not practical for getting the line at the caret patch by aaronleventhal r=ginn.chen a=dsicore
authorEvan.Yan@Sun.COM
Tue, 04 Dec 2007 23:34:47 -0800
changeset 8755 2d4fff75907c2922bed08cf99a4c1318b2d14e1b
parent 8754 c95d0e21fc47674d0ae0f0a43018ce33ef08ff3c
child 8756 516938132524413ad7218f270d86f59f4ab3d86d
push idunknown
push userunknown
push dateunknown
reviewersginn.chen, dsicore
bugs406069
milestone1.9b2pre
Bug 406069 - nsIAccessibleText::GetTextAtOffset() not practical for getting the line at the caret patch by aaronleventhal r=ginn.chen a=dsicore
accessible/public/nsIAccessibleText.idl
accessible/src/html/nsHyperTextAccessible.cpp
--- a/accessible/public/nsIAccessibleText.idl
+++ b/accessible/public/nsIAccessibleText.idl
@@ -43,16 +43,22 @@
 
 typedef long nsAccessibleTextBoundary;
 
 interface nsIAccessible;
 
 [scriptable, uuid(caa4f543-070e-4705-8428-2e53575c41bb)]
 interface nsIAccessibleText : nsISupports
 {
+  // In parameters for character offsets:
+  //  -1 will be treated as the equal to the end of the text
+  //  -2 will be treated as the caret position
+  const PRInt32 TEXT_OFFSET_END_OF_TEXT = -1;
+  const PRInt32 TEXT_OFFSET_CARET       = -2;
+
   const nsAccessibleTextBoundary BOUNDARY_CHAR = 0;
   const nsAccessibleTextBoundary BOUNDARY_WORD_START = 1;
   const nsAccessibleTextBoundary BOUNDARY_WORD_END = 2;
   const nsAccessibleTextBoundary BOUNDARY_SENTENCE_START = 3; // don't use, deprecated
   const nsAccessibleTextBoundary BOUNDARY_SENTENCE_END = 4;  // don't use, deprecated
   const nsAccessibleTextBoundary BOUNDARY_LINE_START = 5;
   const nsAccessibleTextBoundary BOUNDARY_LINE_END = 6;
   const nsAccessibleTextBoundary BOUNDARY_ATTRIBUTE_RANGE = 7;
@@ -161,17 +167,16 @@ interface nsIAccessibleText : nsISupport
                          in unsigned long coordType);
 
   void getSelectionBounds (in long selectionNum,
                            out long startOffset,
                            out long endOffset);
 
   /**
    * Set the bounds for the given selection range
-   * Offsets < 0 will be treated as the equal to the end of the text
    */
   void setSelectionBounds (in long selectionNum,
                            in long startOffset,
                            in long endOffset);
 
   void addSelection (in long startOffset, in long endOffset);
 
   void removeSelection (in long selectionNum);
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -51,20 +51,22 @@
 #include "nsIDOMDocumentView.h"
 #include "nsIDOMRange.h"
 #include "nsIDOMWindowInternal.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIEditingSession.h"
 #include "nsIEditor.h"
 #include "nsIFontMetrics.h"
 #include "nsIFrame.h"
+#include "nsFrameSelection.h"
 #include "nsIScrollableFrame.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIPlaintextEditor.h"
 #include "nsISelection2.h"
+#include "nsISelectionPrivate.h"
 #include "nsIServiceManager.h"
 #include "nsTextFragment.h"
 #include "gfxSkipChars.h"
 
 static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
 
 // ------------
 // nsHyperTextAccessible
@@ -317,22 +319,28 @@ nsIntRect nsHyperTextAccessible::GetBoun
  */
 nsIFrame*
 nsHyperTextAccessible::GetPosAndText(PRInt32& aStartOffset, PRInt32& aEndOffset,
                                      nsAString *aText, nsIFrame **aEndFrame,
                                      nsIntRect *aBoundsRect,
                                      nsIAccessible **aStartAcc,
                                      nsIAccessible **aEndAcc)
 {
-  if (aStartOffset < 0) {
+  if (aStartOffset == nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT) {
     GetCharacterCount(&aStartOffset);
   }
-  if (aEndOffset < 0) {
+  if (aStartOffset == nsIAccessibleText::TEXT_OFFSET_CARET) {
+    GetCaretOffset(&aStartOffset);
+  }
+  if (aEndOffset == nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT) {
     GetCharacterCount(&aEndOffset);
   }
+  if (aEndOffset == nsIAccessibleText::TEXT_OFFSET_CARET) {
+    GetCaretOffset(&aEndOffset);
+  }
 
   PRInt32 startOffset = aStartOffset;
   PRInt32 endOffset = aEndOffset;
 
   // Clear out parameters and set up loop
   if (aText) {
     aText->Truncate();
   }
@@ -837,41 +845,62 @@ nsresult nsHyperTextAccessible::GetTextH
   aText.Truncate();
   *aStartOffset = *aEndOffset = 0;
 
   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
   if (!presShell) {
     return NS_ERROR_FAILURE;
   }
 
-  PRInt32 startOffset = aOffset;
-  PRInt32 endOffset = aOffset;
+  if (aOffset == nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT) {
+    GetCharacterCount(&aOffset);
+  }
+  if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET) {
+    GetCaretOffset(&aOffset);
+    if (aOffset > 0 && (aBoundaryType == BOUNDARY_LINE_START ||
+                        aBoundaryType == BOUNDARY_LINE_END)) {
+      // It is the same character offset when the caret is visually at the very end of a line
+      // or the start of a new line. Getting text at the line should provide the line with the visual caret,
+      // otherwise screen readers will announce the wrong line as the user presses up or down arrow and land
+      // at the end of a line.
+      nsCOMPtr<nsISelection> domSel;
+      nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel));
+      nsCOMPtr<nsISelectionPrivate> privateSelection(do_QueryInterface(domSel));
+      nsCOMPtr<nsFrameSelection> frameSelection;
+      rv = privateSelection->GetFrameSelection(getter_AddRefs(frameSelection));
+      NS_ENSURE_SUCCESS(rv, rv);
+      if (frameSelection->GetHint() == nsFrameSelection::HINTLEFT) {
+        -- aOffset;  // We are at the start of a line
+      }
+    }
+  }
+  else if (aOffset < 0) {
+    return NS_ERROR_FAILURE;
+  }
 
-  if (aBoundaryType == BOUNDARY_LINE_END) {
-    // Avoid getting the previous line
-    ++ startOffset;
-    ++ endOffset;
-  }
+  PRInt32 startOffset = aOffset + (aBoundaryType == BOUNDARY_LINE_END);  // Avoid getting the previous line
+  PRInt32 endOffset = startOffset;
+
   // Convert offsets to frame-relative
   nsCOMPtr<nsIAccessible> startAcc;
   nsIFrame *startFrame = GetPosAndText(startOffset, endOffset, nsnull, nsnull,
                                        nsnull, getter_AddRefs(startAcc));
 
   if (!startFrame) {
     PRInt32 textLength;
     GetCharacterCount(&textLength);
     if (aBoundaryType == BOUNDARY_LINE_START && aOffset > 0 && aOffset == textLength) {
       // Asking for start of line, while on last character
       nsCOMPtr<nsPIAccessNode> startAccessNode = do_QueryInterface(startAcc);
       if (startAccessNode) {
         startFrame = startAccessNode->GetFrame();
       }
     }
     if (!startFrame) {
-      return (aOffset < 0 || aOffset > textLength) ? NS_ERROR_FAILURE : NS_OK;
+      return aOffset > textLength ? NS_ERROR_FAILURE : NS_OK;
     }
     else {
       // We're on the last continuation since we're on the last character
       startFrame = startFrame->GetLastContinuation();
     }
   }
 
   nsSelectionAmount amount;
@@ -944,17 +973,18 @@ nsresult nsHyperTextAccessible::GetTextH
 
   if (aType == eGetBefore) {
     endOffset = aOffset;
   }
   else {
     // Start moving forward from the start so that we don't get 
     // 2 words/lines if the offset occured on whitespace boundary
     // Careful, startOffset and endOffset are passed by reference to GetPosAndText() and changed
-    startOffset = endOffset = finalStartOffset;
+    // For BOUNDARY_LINE_END, make sure we start of this line
+    startOffset = endOffset = finalStartOffset + (aBoundaryType == BOUNDARY_LINE_END);
     nsCOMPtr<nsIAccessible> endAcc;
     nsIFrame *endFrame = GetPosAndText(startOffset, endOffset, nsnull, nsnull,
                                        nsnull, getter_AddRefs(endAcc));
     if (!endFrame) {
       return NS_ERROR_FAILURE;
     }
     finalEndOffset = GetRelativeOffset(presShell, endFrame, endOffset, endAcc,
                                        amount, eDirNext, needsStart);