Bug 391617 - CAccessibleText::scroll* methods are incorrect, r=aaronlev, a=dsicore
authorsurkov.alexander@gmail.com
Mon, 24 Sep 2007 22:48:51 -0700
changeset 6286 98faaa5aff69818eecc8b9b55784529c7f1f9111
parent 6285 466835a64712962736326e881e5c5e93005bcc5c
child 6287 a84baa4b3012aec68c68171c48a63df71e4b2814
push idunknown
push userunknown
push dateunknown
reviewersaaronlev, dsicore
bugs391617
milestone1.9a9pre
Bug 391617 - CAccessibleText::scroll* methods are incorrect, r=aaronlev, a=dsicore
accessible/public/nsIAccessibleText.idl
accessible/public/nsIAccessibleTypes.idl
accessible/src/base/nsAccessNode.cpp
accessible/src/base/nsAccessibilityUtils.cpp
accessible/src/base/nsAccessibilityUtils.h
accessible/src/html/Makefile.in
accessible/src/html/nsHyperTextAccessible.cpp
accessible/src/html/nsHyperTextAccessible.h
accessible/src/msaa/CAccessibleText.cpp
accessible/src/msaa/nsAccessibleWrap.cpp
--- a/accessible/public/nsIAccessibleText.idl
+++ b/accessible/public/nsIAccessibleText.idl
@@ -40,17 +40,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 typedef long nsAccessibleTextBoundary;
 
 interface nsIAccessible;
 
-[scriptable, uuid(948419b2-53f6-4a74-bb69-1345faf3e8e8)]
+[scriptable, uuid(caa4f543-070e-4705-8428-2e53575c41bb)]
 interface nsIAccessibleText : nsISupports
 {
   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;
@@ -175,25 +175,41 @@ interface nsIAccessibleText : nsISupport
   void addSelection (in long startOffset, in long endOffset);
 
   void removeSelection (in long selectionNum);
 
 
   /**
    * Makes a specific part of string visible on screen.
    *
-   * @param aStartIndex - 0-based character offset.
-   * @param aEndIndex - 0-based character offset - the offset of the
-   *                        character just past the last character of the
-   *                        string.
-   * @param aScrollType - defines how to scroll (see nsIAccessibleScrollType for
-   *                      available constants).
+   * @param startIndex  0-based character offset
+   * @param endIndex    0-based character offset - the offset of the
+   *                    character just past the last character of the
+   *                    string
+   * @param scrollType  defines how to scroll (see nsIAccessibleScrollType for
+   *                    available constants)
    */
-  void scrollSubstringTo(in long aStartIndex, in long aEndIndex,
-                         in unsigned long aScrollType);
+  void scrollSubstringTo(in long startIndex, in long endIndex,
+                         in unsigned long scrollType);
+
+  /**
+   * Moves the top left of a substring to a specified location.
+   *
+   * @param startIndex      0-based character offset
+   * @param endIndex        0-based character offset - the offset of the
+   *                        character just past the last character of
+   *                        the string
+   * @param coordinateType  specifies the coordinates origin (for available
+   *                        constants refer to nsIAccessibleCoordinateType)
+   * @param x               defines the x coordinate
+   * @param y               defines the y coordinate
+   */
+  void scrollSubstringToPoint(in long startIndex, in long endIndex,
+                              in unsigned long coordinateType,
+                              in long x, in long y);
 };
 
 /*
  Assumptions:
 
  Using wstring (UCS2) instead of string encoded in UTF-8.
  Multibyte encodings (or at least potentially multi-byte
  encodings) would be preferred for the reasons cited above.
--- a/accessible/public/nsIAccessibleTypes.idl
+++ b/accessible/public/nsIAccessibleTypes.idl
@@ -85,18 +85,17 @@ interface nsIAccessibleScrollType : nsIS
    * Scroll an object the minimum amount necessary in order for the entire
    * frame to be visible (if possible).
    */
   const unsigned long SCROLL_TYPE_ANYWHERE = 0x06;
 };
 
 
 /**
- * These constants define which coordinate system a point is located in. Note,
- * keep them synchronized with IA2CoordinateType.
+ * These constants define which coordinate system a point is located in.
  */
 [scriptable, uuid(c9fbdf10-619e-436f-bf4b-8566686f1577)]
 interface nsIAccessibleCoordinateType : nsISupports
 {
   /**
    * The coordinates are relative to the screen.
    */
   const unsigned long COORDTYPE_SCREEN_RELATIVE = 0x00;
--- a/accessible/src/base/nsAccessNode.cpp
+++ b/accessible/src/base/nsAccessNode.cpp
@@ -56,17 +56,16 @@
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMNSDocument.h"
 #include "nsIDOMNSHTMLElement.h"
 #include "nsIDOMViewCSS.h"
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIFrame.h"
-#include "nsIScrollableFrame.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsIServiceManager.h"
 #include "nsIStringBundle.h"
 #include "nsITimer.h"
 #include "nsRootAccessible.h"
@@ -436,81 +435,24 @@ nsAccessNode::ScrollTo(PRUint32 aScrollT
 
 NS_IMETHODIMP
 nsAccessNode::ScrollToPoint(PRUint32 aCoordinateType, PRInt32 aX, PRInt32 aY)
 {
   nsIFrame *frame = GetFrame();
   if (!frame)
     return NS_ERROR_FAILURE;
 
-  nsPresContext *presContext = frame->PresContext();
-
-  switch (aCoordinateType) {
-    case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE:
-      break;
-
-    case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE:
-    {
-      nsIntPoint wndCoords = nsAccUtils::GetScreenCoordsForWindow(mDOMNode);
-      aX += wndCoords.x;
-      aY += wndCoords.y;
-      break;
-    }
-
-    case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE:
-    {
-      nsCOMPtr<nsPIAccessNode> parent;
-
-      nsCOMPtr<nsIAccessible> accessible;
-      nsresult rv = QueryInterface(NS_GET_IID(nsIAccessible),
-                                   getter_AddRefs(accessible));
-      if (NS_SUCCEEDED(rv) && accessible) {
-        nsCOMPtr<nsIAccessible> parentAccessible;
-        accessible->GetParent(getter_AddRefs(parentAccessible));
-        parent = do_QueryInterface(parentAccessible);
-      } else {
-        nsCOMPtr<nsIAccessNode> parentAccessNode;
-        GetParentNode(getter_AddRefs(parentAccessNode));
-        parent = do_QueryInterface(parentAccessNode);
-      }
-
-      NS_ENSURE_STATE(parent);
-      nsIFrame *parentFrame = parent->GetFrame();
-      NS_ENSURE_STATE(parentFrame);
-
-      nsIntRect parentRect = parentFrame->GetScreenRectExternal();
-      aX += parentRect.x;
-      aY += parentRect.y;
-      break;
-    }
-
-    default:
-      return NS_ERROR_INVALID_ARG;
-  }
+  nsIntPoint coords;
+  nsresult rv = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordinateType,
+                                                  this, &coords);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   nsIFrame *parentFrame = frame;
-  while (parentFrame = parentFrame->GetParent()) {
-    nsIScrollableFrame *scrollableFrame = nsnull;
-    CallQueryInterface(parentFrame, &scrollableFrame);
-    if (scrollableFrame) {
-      nsIntRect frameRect = frame->GetScreenRectExternal();
-      PRInt32 devDeltaX = aX - frameRect.x;
-      PRInt32 devDeltaY = aY - frameRect.y;
-
-      nsPoint deltaPoint;
-      deltaPoint.x = presContext->DevPixelsToAppUnits(devDeltaX);
-      deltaPoint.y = presContext->DevPixelsToAppUnits(devDeltaY);
-
-      nsPoint scrollPoint = scrollableFrame->GetScrollPosition();
-
-      scrollPoint -= deltaPoint;
-
-      scrollableFrame->ScrollTo(scrollPoint);
-    }
-  }
+  while (parentFrame = parentFrame->GetParent())
+    nsAccUtils::ScrollFrameToPoint(parentFrame, frame, coords);
 
   return NS_OK;
 }
 
 nsresult
 nsAccessNode::MakeAccessNode(nsIDOMNode *aNode, nsIAccessNode **aAccessNode)
 {
   *aAccessNode = nsnull;
--- a/accessible/src/base/nsAccessibilityUtils.cpp
+++ b/accessible/src/base/nsAccessibilityUtils.cpp
@@ -35,16 +35,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsAccessibilityUtils.h"
 
 #include "nsIAccessibleTypes.h"
 #include "nsPIAccessible.h"
+#include "nsPIAccessNode.h"
 #include "nsAccessibleEventData.h"
 
 #include "nsAccessNode.h"
 #include "nsARIAMap.h"
 #include "nsIDocument.h"
 #include "nsIDOMAbstractView.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMDocumentView.h"
@@ -52,16 +53,17 @@
 #include "nsIDOMNodeList.h"
 #include "nsIDOMRange.h"
 #include "nsIDOMXULSelectCntrlEl.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDOMWindowInternal.h"
 #include "nsIEventListenerManager.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
+#include "nsIScrollableFrame.h"
 #include "nsIEventStateManager.h"
 #include "nsISelection2.h"
 #include "nsISelectionController.h"
 
 #include "nsContentCID.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIInterfaceRequestorUtils.h"
 
@@ -282,16 +284,29 @@ nsAccUtils::GetAncestorWithRole(nsIAcces
 }
 
 nsresult
 nsAccUtils::ScrollSubstringTo(nsIFrame *aFrame,
                               nsIDOMNode *aStartNode, PRInt32 aStartIndex,
                               nsIDOMNode *aEndNode, PRInt32 aEndIndex,
                               PRUint32 aScrollType)
 {
+  PRInt16 vPercent, hPercent;
+  ConvertScrollTypeToPercents(aScrollType, &vPercent, &hPercent);
+
+  return ScrollSubstringTo(aFrame, aStartNode, aStartIndex, aEndNode, aEndIndex,
+                           vPercent, hPercent);
+}
+
+nsresult
+nsAccUtils::ScrollSubstringTo(nsIFrame *aFrame,
+                              nsIDOMNode *aStartNode, PRInt32 aStartIndex,
+                              nsIDOMNode *aEndNode, PRInt32 aEndIndex,
+                              PRInt16 aVPercent, PRInt16 aHPercent)
+{
   if (!aFrame || !aStartNode || !aEndNode)
     return NS_ERROR_FAILURE;
 
   nsPresContext *presContext = aFrame->PresContext();
 
   nsCOMPtr<nsIDOMRange> scrollToRange = do_CreateInstance(kRangeCID);
   NS_ENSURE_TRUE(scrollToRange, NS_ERROR_FAILURE);
 
@@ -306,28 +321,52 @@ nsAccUtils::ScrollSubstringTo(nsIFrame *
   selCon->GetSelection(nsISelectionController::SELECTION_ACCESSIBILITY,
                        getter_AddRefs(selection1));
 
   nsCOMPtr<nsISelection2> selection(do_QueryInterface(selection1));
   if (selection) {
     selection->RemoveAllRanges();
     selection->AddRange(scrollToRange);
 
-    PRInt16 vPercent, hPercent;
-    ConvertScrollTypeToPercents(aScrollType, &vPercent, &hPercent);
     selection->ScrollIntoView(nsISelectionController::SELECTION_ANCHOR_REGION,
-                              PR_TRUE, vPercent, hPercent);
+                              PR_TRUE, aVPercent, aHPercent);
 
     selection->CollapseToStart();
   }
 
   return NS_OK;
 }
 
 void
+nsAccUtils::ScrollFrameToPoint(nsIFrame *aScrollableFrame,
+                               nsIFrame *aFrame,
+                               const nsIntPoint& aPoint)
+{
+  nsIScrollableFrame *scrollableFrame = nsnull;
+  CallQueryInterface(aScrollableFrame, &scrollableFrame);
+  if (!scrollableFrame)
+    return;
+
+  nsPresContext *presContext = aFrame->PresContext();
+
+  nsIntRect frameRect = aFrame->GetScreenRectExternal();
+  PRInt32 devDeltaX = aPoint.x - frameRect.x;
+  PRInt32 devDeltaY = aPoint.y - frameRect.y;
+
+  nsPoint deltaPoint;
+  deltaPoint.x = presContext->DevPixelsToAppUnits(devDeltaX);
+  deltaPoint.y = presContext->DevPixelsToAppUnits(devDeltaY);
+
+  nsPoint scrollPoint = scrollableFrame->GetScrollPosition();
+  scrollPoint -= deltaPoint;
+
+  scrollableFrame->ScrollTo(scrollPoint);
+}
+
+void
 nsAccUtils::ConvertScrollTypeToPercents(PRUint32 aScrollType,
                                         PRInt16 *aVPercent,
                                         PRInt16 *aHPercent)
 {
   switch (aScrollType)
   {
     case nsIAccessibleScrollType::SCROLL_TYPE_TOP_LEFT:
       *aVPercent = NS_PRESSHELL_SCROLL_TOP;
@@ -354,16 +393,77 @@ nsAccUtils::ConvertScrollTypeToPercents(
       *aHPercent = NS_PRESSHELL_SCROLL_RIGHT;
       break;
     default:
       *aVPercent = NS_PRESSHELL_SCROLL_ANYWHERE;
       *aHPercent = NS_PRESSHELL_SCROLL_ANYWHERE;
   }
 }
 
+nsresult
+nsAccUtils::ConvertToScreenCoords(PRInt32 aX, PRInt32 aY,
+                                  PRUint32 aCoordinateType,
+                                  nsIAccessNode *aAccessNode,
+                                  nsIntPoint *aCoords)
+{
+  NS_ENSURE_ARG_POINTER(aCoords);
+
+  aCoords->MoveTo(aX, aY);
+
+  switch (aCoordinateType) {
+    case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE:
+      break;
+
+    case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE:
+    {
+      NS_ENSURE_ARG(aAccessNode);
+
+      nsCOMPtr<nsIDOMNode> DOMNode;
+      aAccessNode->GetDOMNode(getter_AddRefs(DOMNode));
+      NS_ENSURE_STATE(DOMNode);
+
+      nsIntPoint wndCoords = nsAccUtils::GetScreenCoordsForWindow(DOMNode);
+      *aCoords += wndCoords;
+      break;
+    }
+
+    case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE:
+    {
+      NS_ENSURE_ARG(aAccessNode);
+
+      nsCOMPtr<nsPIAccessNode> parent;
+      nsCOMPtr<nsIAccessible> accessible(do_QueryInterface(aAccessNode));
+      if (accessible) {
+        nsCOMPtr<nsIAccessible> parentAccessible;
+        accessible->GetParent(getter_AddRefs(parentAccessible));
+        parent = do_QueryInterface(parentAccessible);
+      } else {
+        nsCOMPtr<nsIAccessNode> parentAccessNode;
+        aAccessNode->GetParentNode(getter_AddRefs(parentAccessNode));
+        parent = do_QueryInterface(parentAccessNode);
+      }
+
+      NS_ENSURE_STATE(parent);
+
+      nsIFrame *parentFrame = parent->GetFrame();
+      NS_ENSURE_STATE(parentFrame);
+
+      nsIntRect parentRect = parentFrame->GetScreenRectExternal();
+      aCoords->x += parentRect.x;
+      aCoords->y += parentRect.y;
+      break;
+    }
+
+    default:
+      return NS_ERROR_INVALID_ARG;
+  }
+
+  return NS_OK;
+}
+
 nsIntPoint
 nsAccUtils::GetScreenCoordsForWindow(nsIDOMNode *aNode)
 {
   nsIntPoint coords(0, 0);
   nsCOMPtr<nsIDocShellTreeItem> treeItem(GetDocShellTreeItemFor(aNode));
   if (!treeItem)
     return coords;
 
--- a/accessible/src/base/nsAccessibilityUtils.h
+++ b/accessible/src/base/nsAccessibilityUtils.h
@@ -36,16 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsAccessibilityUtils_h_
 #define nsAccessibilityUtils_h_
 
 #include "nsAccessibilityAtoms.h"
 #include "nsIAccessible.h"
+#include "nsIAccessNode.h"
 #include "nsARIAMap.h"
 
 #include "nsIDOMNode.h"
 #include "nsIPersistentProperties2.h"
 #include "nsIContent.h"
 #include "nsIFrame.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsPoint.h"
@@ -157,25 +158,68 @@ public:
    * @param aEndOffset    an offset inside the end node
    * @param aScrollType   the place a range should be scrolled to
    */
   static nsresult ScrollSubstringTo(nsIFrame *aFrame,
                                     nsIDOMNode *aStartNode, PRInt32 aStartIndex,
                                     nsIDOMNode *aEndNode, PRInt32 aEndIndex,
                                     PRUint32 aScrollType);
 
+  /** Helper method to scroll range into view, used for implementation of
+   * nsIAccessibleText::scrollSubstringTo[Point]().
+   *
+   * @param aFrame        the frame for accessible the range belongs to.
+   * @param aStartNode    start node of a range
+   * @param aStartOffset  an offset inside the start node
+   * @param aEndNode      end node of a range
+   * @param aEndOffset    an offset inside the end node
+   * @param aVPercent     how to align vertically, specified in percents
+   * @param aHPercent     how to align horizontally, specified in percents
+   */
+  static nsresult ScrollSubstringTo(nsIFrame *aFrame,
+                                    nsIDOMNode *aStartNode, PRInt32 aStartIndex,
+                                    nsIDOMNode *aEndNode, PRInt32 aEndIndex,
+                                    PRInt16 aVPercent, PRInt16 aHPercent);
+
+  /**
+   * Scrolls the given frame to the point, used for implememntation of
+   * nsIAccessNode::scrollToPoint and nsIAccessibleText::scrollSubstringToPoint.
+   *
+   * @param aScrollableFrame  the scrollable frame
+   * @param aFrame            the frame to scroll
+   * @param aPoint            the point scroll to
+   */
+  static void ScrollFrameToPoint(nsIFrame *aScrollableFrame,
+                                 nsIFrame *aFrame, const nsIntPoint& aPoint);
+
   /**
    * Converts scroll type constant defined in nsIAccessibleScrollType to
    * vertical and horizontal percents.
    */
   static void ConvertScrollTypeToPercents(PRUint32 aScrollType,
                                           PRInt16 *aVPercent,
                                           PRInt16 *aHPercent);
 
   /**
+   * Converts the given coordinates to coordinates relative screen.
+   *
+   * @param aX               [in] the given x coord
+   * @param aY               [in] the given y coord
+   * @param aCoordinateType  [in] specifies coordinates origin (refer to
+   *                         nsIAccessibleCoordinateType)
+   * @param aAccessNode      [in] the accessible if coordinates are given
+   *                         relative it.
+   * @param aCoords          [out] converted coordinates
+   */
+  static nsresult ConvertToScreenCoords(PRInt32 aX, PRInt32 aY,
+                                        PRUint32 aCoordinateType,
+                                        nsIAccessNode *aAccessNode,
+                                        nsIntPoint *aCoords);
+
+  /**
    * Returns coordinates relative screen for the top level window.
    *
    * @param - aNode - the DOM node hosted in the window.
    */
   static nsIntPoint GetScreenCoordsForWindow(nsIDOMNode *aNode);
 
   /**
    * Return document shell tree item for the given DOM node.
--- a/accessible/src/html/Makefile.in
+++ b/accessible/src/html/Makefile.in
@@ -56,16 +56,17 @@ REQUIRES	= composer \
 		  imglib2 \
 		  intl \
 		  js \
 		  layout \
 		  locale \
 		  necko \
 		  string \
 		  thebes \
+		  view \
 		  webshell \
 		  widget \
 		  xpcom \
 		  xpconnect \
 		  $(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2)
 REQUIRES += editor
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -51,16 +51,17 @@
 #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 "nsIScrollableFrame.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIPlaintextEditor.h"
 #include "nsISelection2.h"
 #include "nsIServiceManager.h"
 #include "nsTextFragment.h"
 #include "gfxSkipChars.h"
 
 static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
@@ -660,16 +661,69 @@ nsresult nsHyperTextAccessible::DOMPoint
       // If not at end of last text node, we will return the accessible we were in
       NS_ADDREF(*aFinalAccessible = childAccessible);
     }
   }
 
   return NS_OK;
 }
 
+nsresult
+nsHyperTextAccessible::HypertextOffsetsToDOMRange(PRInt32 aStartHTOffset,
+                                                  PRInt32 aEndHTOffset,
+                                                  nsIDOMNode **aStartNode,
+                                                  PRInt32 *aStartOffset,
+                                                  nsIDOMNode **aEndNode,
+                                                  PRInt32 *aEndOffset)
+{
+  NS_ENSURE_ARG_POINTER(aStartNode);
+  *aStartNode = nsnull;
+
+  NS_ENSURE_ARG_POINTER(aStartOffset);
+  *aStartOffset = -1;
+
+  NS_ENSURE_ARG_POINTER(aEndNode);
+  *aEndNode = nsnull;
+
+  NS_ENSURE_ARG_POINTER(aEndOffset);
+  *aEndOffset = -1;
+
+  nsCOMPtr<nsIAccessible> startAcc, endAcc;
+  PRInt32 startOffset = aStartHTOffset, endOffset = aEndHTOffset;
+  nsIFrame *startFrame = nsnull, *endFrame = nsnull;
+
+  startFrame = GetPosAndText(startOffset, endOffset, nsnull, &endFrame, nsnull,
+                             getter_AddRefs(startAcc), getter_AddRefs(endAcc));
+  if (!startAcc || !endAcc)
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIDOMNode> startNode, endNode;
+  nsresult rv = GetDOMPointByFrameOffset(startFrame, startOffset, startAcc,
+                                         getter_AddRefs(startNode),
+                                         &startOffset);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (aStartHTOffset != aEndHTOffset) {
+    rv = GetDOMPointByFrameOffset(endFrame, endOffset, endAcc,
+                                  getter_AddRefs(endNode), &endOffset);
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else {
+    endNode = startNode;
+    endOffset = startOffset;
+  }
+
+  NS_ADDREF(*aStartNode = startNode);
+  *aStartOffset = startOffset;
+
+  NS_ADDREF(*aEndNode = endNode);
+  *aEndOffset = endOffset;
+
+  return NS_OK;
+}
+
 PRInt32
 nsHyperTextAccessible::GetRelativeOffset(nsIPresShell *aPresShell,
                                          nsIFrame *aFromFrame,
                                          PRInt32 aFromOffset,
                                          nsIAccessible *aFromAccessible,
                                          nsSelectionAmount aAmount,
                                          nsDirection aDirection,
                                          PRBool aNeedsStart)
@@ -1567,111 +1621,57 @@ NS_IMETHODIMP nsHyperTextAccessible::Get
   }
 
   return DOMPointToHypertextOffset(endNode, endOffset, aEndOffset, nsnull, PR_TRUE);
 }
 
 /*
  * Changes the start and end offset of the specified selection.
  */
-NS_IMETHODIMP nsHyperTextAccessible::SetSelectionBounds(PRInt32 aSelectionNum, PRInt32 aStartOffset, PRInt32 aEndOffset)
+NS_IMETHODIMP
+nsHyperTextAccessible::SetSelectionBounds(PRInt32 aSelectionNum,
+                                          PRInt32 aStartOffset,
+                                          PRInt32 aEndOffset)
 {
   nsCOMPtr<nsISelection> domSel;
   nsresult rv = GetSelections(nsnull, getter_AddRefs(domSel));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  PRInt32 isOnlyCaret = (aStartOffset == aEndOffset); // Caret is a collapsed selection
+  // Caret is a collapsed selection
+  PRBool isOnlyCaret = (aStartOffset == aEndOffset);
 
   PRInt32 rangeCount;
   domSel->GetRangeCount(&rangeCount);
   nsCOMPtr<nsIDOMRange> range;
   if (aSelectionNum == rangeCount) { // Add a range
     range = do_CreateInstance(kRangeCID);
     NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY);
   }
   else if (aSelectionNum < 0 || aSelectionNum > rangeCount) {
     return NS_ERROR_INVALID_ARG;
   }
   else {
     domSel->GetRangeAt(aSelectionNum, getter_AddRefs(range));
     NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
   }
 
-  nsIFrame *endFrame;
-  nsCOMPtr<nsIAccessible> startAcc, endAcc;
-  nsIFrame *startFrame = GetPosAndText(aStartOffset, aEndOffset, nsnull, &endFrame, nsnull,
-                                       getter_AddRefs(startAcc), getter_AddRefs(endAcc));
-
-  nsCOMPtr<nsIPresShell> shell = GetPresShell();
+  PRInt32 startOffset, endOffset;
+  nsCOMPtr<nsIDOMNode> startNode, endNode;
 
-  if (!startFrame) { // past the end of the hyper text
-    nsCOMPtr<nsIAccessNode> startAccessNode = do_QueryInterface(startAcc);
-    NS_ENSURE_TRUE(startAccessNode, NS_ERROR_FAILURE);
-    nsCOMPtr<nsIDOMNode> node;
-    startAccessNode->GetDOMNode(getter_AddRefs(node));
-    NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
-    rv = range->SetStartAfter(node);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  else {
-    nsIContent *startParentContent = startFrame->GetContent();
-    PRInt32 startOffset;
-    if (startFrame->GetType() != nsAccessibilityAtoms::textFrame) {
-      nsIContent *newParent = startParentContent->GetParent();
-      startOffset = newParent->IndexOf(startParentContent);
-      startParentContent = newParent;
-    }
-    else {
-      // We have a rendered offset into the text frame, and it needs to be
-      // a content offset for us to set the caret
-      nsIFrame *startPrimaryFrame =
-        shell->GetPrimaryFrameFor(startFrame->GetContent());
-      rv = RenderedToContentOffset(startPrimaryFrame, aStartOffset, &startOffset);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-    nsCOMPtr<nsIDOMNode> startParentNode(do_QueryInterface(startParentContent));
-    NS_ENSURE_TRUE(startParentNode, NS_ERROR_FAILURE);
-    rv = range->SetStart(startParentNode, startOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
+  rv = HypertextOffsetsToDOMRange(aStartOffset, aEndOffset,
+                                  getter_AddRefs(startNode), &startOffset,
+                                  getter_AddRefs(endNode), &endOffset);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  if (isOnlyCaret) { 
-    rv = range->Collapse(PR_TRUE);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  else if (!endFrame) {  // past the end of the hyper text
-    nsCOMPtr<nsIAccessNode> endAccessNode = do_QueryInterface(endAcc);
-    NS_ENSURE_TRUE(endAccessNode, NS_ERROR_FAILURE);
-    nsCOMPtr<nsIDOMNode> node;
-    endAccessNode->GetDOMNode(getter_AddRefs(node));
-    NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
-    rv = range->SetEndAfter(node);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  else {
-    nsIContent *endParentContent = endFrame->GetContent();
-    PRInt32 endOffset;
-    if (endFrame->GetType() != nsAccessibilityAtoms::textFrame) {
-      nsIContent *newParent = endParentContent->GetParent();
-      endOffset = newParent->IndexOf(endParentContent);
-      endParentContent = newParent;
-    }
-    else {
-      // We have a rendered offset into the text frame, and it needs to be
-      // a content offset for us to set the caret
-      nsIFrame *endPrimaryFrame =
-        shell->GetPrimaryFrameFor(endFrame->GetContent());
-      rv = RenderedToContentOffset(endPrimaryFrame, aEndOffset, &endOffset);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-    nsCOMPtr<nsIDOMNode> endParentNode(do_QueryInterface(endParentContent));
-    NS_ENSURE_TRUE(endParentNode, NS_ERROR_FAILURE);
-    rv = range->SetEnd(endParentNode, endOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
+  rv = range->SetStart(startNode, startOffset);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = isOnlyCaret ? range->Collapse(PR_TRUE) :
+                     range->SetEnd(endNode, endOffset);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   if (aSelectionNum == rangeCount) { // Add successfully created new range
     return domSel->AddRange(range);
   }
   return NS_OK;
 }
 
 /*
@@ -1703,66 +1703,106 @@ NS_IMETHODIMP nsHyperTextAccessible::Rem
   if (aSelectionNum < 0 || aSelectionNum >= rangeCount)
     return NS_ERROR_INVALID_ARG;
 
   nsCOMPtr<nsIDOMRange> range;
   domSel->GetRangeAt(aSelectionNum, getter_AddRefs(range));
   return domSel->RemoveRange(range);
 }
 
+// void nsIAccessibleText::
+//   scrollSubstringTo(in long startIndex, in long endIndex,
+//                     in unsigned long scrollType);
 NS_IMETHODIMP
 nsHyperTextAccessible::ScrollSubstringTo(PRInt32 aStartIndex, PRInt32 aEndIndex,
                                          PRUint32 aScrollType)
 {
-  PRInt32 startOffset = aStartIndex, endOffset = aEndIndex;
-  nsIFrame *startFrame = nsnull, *endFrame = nsnull;
-  nsCOMPtr<nsIAccessible> startAcc, endAcc;
-
-  startFrame = GetPosAndText(startOffset, endOffset,
-                             nsnull, &endFrame, nsnull,
-                             getter_AddRefs(startAcc), getter_AddRefs(endAcc));
-  if (!startFrame || !endFrame)
-    return NS_ERROR_FAILURE;
-
-  nsCOMPtr<nsIDOMNode> startNode;
-  nsCOMPtr<nsIContent> startContent(startFrame->GetContent());
+  PRInt32 startOffset, endOffset;
+  nsCOMPtr<nsIDOMNode> startNode, endNode;
 
-  if (startFrame->GetType() == nsAccessibilityAtoms::textFrame) {
-    nsresult rv = RenderedToContentOffset(startFrame, startOffset,
-                                          &startOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
-    startNode = do_QueryInterface(startContent);
-  } else {
-    nsCOMPtr<nsIContent> startParent(startContent->GetParent());
-    NS_ENSURE_STATE(startParent);
-    startOffset = startParent->IndexOf(startContent);
-    startNode = do_QueryInterface(startParent);
-  }
-  NS_ENSURE_STATE(startNode);
-
-  nsCOMPtr<nsIDOMNode> endNode;
-  nsCOMPtr<nsIContent> endContent(endFrame->GetContent());
-
-  if (endFrame->GetType() == nsAccessibilityAtoms::textFrame) {
-    nsresult rv = RenderedToContentOffset(endFrame, endOffset,
-                                          &endOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
-    endNode = do_QueryInterface(endContent);
-  } else {
-    nsCOMPtr<nsIContent> endParent(endContent->GetParent());
-    NS_ENSURE_STATE(endParent);
-    endOffset = endParent->IndexOf(endContent);
-    endNode = do_QueryInterface(endParent);
-  }
-  NS_ENSURE_STATE(endNode);
+  nsresult rv = HypertextOffsetsToDOMRange(aStartIndex, aEndIndex,
+                                           getter_AddRefs(startNode),
+                                           &startOffset,
+                                           getter_AddRefs(endNode),
+                                           &endOffset);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   return nsAccUtils::ScrollSubstringTo(GetFrame(), startNode, startOffset,
                                        endNode, endOffset, aScrollType);
 }
 
+// void nsIAccessibleText::
+//   scrollSubstringToPoint(in long startIndex, in long endIndex,
+//                          in unsigned long coordinateType,
+//                          in long x, in long y);
+NS_IMETHODIMP
+nsHyperTextAccessible::ScrollSubstringToPoint(PRInt32 aStartIndex,
+                                              PRInt32 aEndIndex,
+                                              PRUint32 aCoordinateType,
+                                              PRInt32 aX, PRInt32 aY)
+{
+  nsIFrame *frame = GetFrame();
+  if (!frame)
+    return NS_ERROR_FAILURE;
+
+  nsIntPoint coords;
+  nsresult rv = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordinateType,
+                                                  this, &coords);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRInt32 startOffset, endOffset;
+  nsCOMPtr<nsIDOMNode> startNode, endNode;
+
+  rv = HypertextOffsetsToDOMRange(aStartIndex, aEndIndex,
+                                  getter_AddRefs(startNode), &startOffset,
+                                  getter_AddRefs(endNode), &endOffset);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsPresContext *presContext = frame->PresContext();
+
+  PRBool initialScrolled = PR_FALSE;
+  nsIFrame *parentFrame = frame;
+  while (parentFrame = parentFrame->GetParent()) {
+    nsIScrollableFrame *scrollableFrame = nsnull;
+    CallQueryInterface(parentFrame, &scrollableFrame);
+    if (scrollableFrame) {
+      if (!initialScrolled) {
+        // Scroll substring to the given point. Turn the point into percents
+        // relative scrollable area to use nsAccUtils::ScrollSubstringTo.
+        nsIntRect frameRect = parentFrame->GetScreenRectExternal();
+        PRInt32 devOffsetX = coords.x - frameRect.x;
+        PRInt32 devOffsetY = coords.y - frameRect.y;
+
+        nsPoint offsetPoint(presContext->DevPixelsToAppUnits(devOffsetX),
+                            presContext->DevPixelsToAppUnits(devOffsetY));
+
+        nsSize size(parentFrame->GetSize());
+        PRInt16 hPercent = offsetPoint.x * 100 / size.width;
+        PRInt16 vPercent = offsetPoint.y * 100 / size.height;
+
+        rv = nsAccUtils::ScrollSubstringTo(GetFrame(), startNode, startOffset,
+                                           endNode, endOffset,
+                                           vPercent, hPercent);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        initialScrolled = PR_TRUE;
+      } else {
+        // Substring was scrolled to the given point already inside its closest
+        // scrollable area. If there are nested scrollable areas then make
+        // sure we scroll lower areas to the given point inside currently
+        // traversed scrollable area.
+        nsAccUtils::ScrollFrameToPoint(parentFrame, frame, coords);
+      }
+    }
+    frame = parentFrame;
+  }
+
+  return NS_OK;
+}
+
 nsresult nsHyperTextAccessible::ContentToRenderedOffset(nsIFrame *aFrame, PRInt32 aContentOffset,
                                                         PRUint32 *aRenderedOffset)
 {
   NS_ASSERTION(aFrame->GetType() == nsAccessibilityAtoms::textFrame,
                "Need text frame for offset conversion");
   NS_ASSERTION(aFrame->GetPrevContinuation() == nsnull,
                "Call on primary frame only");
 
@@ -1798,9 +1838,63 @@ nsresult nsHyperTextAccessible::Rendered
   PRUint32 ourRenderedStart = iter.GetSkippedOffset();
   PRInt32 ourContentStart = iter.GetOriginalOffset();
 
   *aContentOffset = iter.ConvertSkippedToOriginal(aRenderedOffset + ourRenderedStart) - ourContentStart;
 
   return NS_OK;
 }
 
+nsresult
+nsHyperTextAccessible::GetDOMPointByFrameOffset(nsIFrame *aFrame,
+                                                PRInt32 aOffset,
+                                                nsIAccessible *aAccessible,
+                                                nsIDOMNode **aNode,
+                                                PRInt32 *aNodeOffset)
+{
+  NS_ENSURE_ARG(aAccessible);
 
+  nsCOMPtr<nsIDOMNode> node;
+
+  if (!aFrame) {
+    // If the given frame is null then set offset after the DOM node of the
+    // given accessible.
+    nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(aAccessible));
+
+    nsCOMPtr<nsIDOMNode> DOMNode;
+    accessNode->GetDOMNode(getter_AddRefs(DOMNode));
+    nsCOMPtr<nsIContent> content(do_QueryInterface(DOMNode));
+    NS_ENSURE_STATE(content);
+
+    nsCOMPtr<nsIContent> parent(content->GetParent());
+    NS_ENSURE_STATE(parent);
+
+    *aNodeOffset = parent->IndexOf(content) + 1;
+    node = do_QueryInterface(parent);
+
+  } else if (aFrame->GetType() == nsAccessibilityAtoms::textFrame) {
+    nsCOMPtr<nsIContent> content(aFrame->GetContent());
+    NS_ENSURE_STATE(content);
+
+    nsCOMPtr<nsIPresShell> shell(GetPresShell());
+    NS_ENSURE_STATE(shell);
+
+    nsIFrame *primaryFrame = shell->GetPrimaryFrameFor(content);
+    nsresult rv = RenderedToContentOffset(primaryFrame, aOffset, aNodeOffset);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    node = do_QueryInterface(content);
+
+  } else {
+    nsCOMPtr<nsIContent> content(aFrame->GetContent());
+    NS_ENSURE_STATE(content);
+
+    nsCOMPtr<nsIContent> parent(content->GetParent());
+    NS_ENSURE_STATE(parent);
+
+    *aNodeOffset = parent->IndexOf(content);
+    node = do_QueryInterface(parent);
+  }
+
+  NS_IF_ADDREF(*aNode = node);
+  return NS_OK;
+}
+
--- a/accessible/src/html/nsHyperTextAccessible.h
+++ b/accessible/src/html/nsHyperTextAccessible.h
@@ -118,16 +118,33 @@ public:
     *                       by the offset returned is at [offset]. If the passed-in offset in inside a
     *                       descendant, then the returned offset will be on the relevant embedded object char.
     */
   nsresult DOMPointToHypertextOffset(nsIDOMNode* aNode, PRInt32 aNodeOffset,
                                      PRInt32 *aHypertextOffset,
                                      nsIAccessible **aFinalAccessible = nsnull,
                                      PRBool aIsEndOffset = PR_FALSE);
 
+  /**
+   * Turn a start and end hypertext offsets into DOM range.
+   *
+   * @param  aStartHTOffset  [in] the given start hypertext offset
+   * @param  aEndHTOffset    [in] the given end hypertext offset
+   * @param  aStartNode      [out] start node of the range
+   * @param  aStartOffset    [out] start offset of the range
+   * @param  aEndNode        [out] end node of the range
+   * @param  aEndOffset      [out] end offset of the range
+   */
+  nsresult HypertextOffsetsToDOMRange(PRInt32 aStartHTOffset,
+                                      PRInt32 aEndHTOffset,
+                                      nsIDOMNode **aStartNode,
+                                      PRInt32 *aStartOffset,
+                                      nsIDOMNode **aEndNode,
+                                      PRInt32 *aEndOffset);
+
 protected:
   /*
    * 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
@@ -198,15 +215,20 @@ protected:
    * @param aSelCon      The selection controller for the current hyper text, or nsnull if not needed
    * @param aDomSel      The selection interface for the current hyper text, or nsnull if not needed
    * @param aRanges      The selected ranges within the current subtree, or nsnull if not needed
    */
   nsresult GetSelections(nsISelectionController **aSelCon,
                          nsISelection **aDomSel = nsnull,
                          nsCOMArray<nsIDOMRange>* aRanges = nsnull);
   nsresult SetSelectionRange(PRInt32 aStartPos, PRInt32 aEndPos);
+
+  // Helpers
+  nsresult GetDOMPointByFrameOffset(nsIFrame *aFrame, PRInt32 aOffset,
+                                    nsIAccessible *aAccessible,
+                                    nsIDOMNode **aNode, PRInt32 *aNodeOffset);
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsHyperTextAccessible,
                               NS_HYPERTEXTACCESSIBLE_IMPL_CID)
 
 #endif  // _nsHyperTextAccessible_H_
 
--- a/accessible/src/msaa/CAccessibleText.cpp
+++ b/accessible/src/msaa/CAccessibleText.cpp
@@ -365,44 +365,28 @@ CAccessibleText::scrollSubstringTo(long 
   GET_NSIACCESSIBLETEXT
 
   nsresult rv = textAcc->ScrollSubstringTo(aStartIndex, aEndIndex, aScrollType);
   return NS_FAILED(rv) ? E_FAIL : S_OK;
 }
 
 STDMETHODIMP
 CAccessibleText::scrollSubstringToPoint(long aStartIndex, long aEndIndex,
-                                        enum IA2CoordinateType aCoordinateType,
+                                        enum IA2CoordinateType aCoordType,
                                         long aX, long aY)
 {
   GET_NSIACCESSIBLETEXT
 
-  nsCOMPtr<nsIAccessible> accessible;
-  PRInt32 startOffset = 0, endOffset = 0;
-
-  // XXX: aEndIndex isn't used.
-  textAcc->GetAttributeRange(aStartIndex, &startOffset, &endOffset,
-                             getter_AddRefs(accessible));
-  if (!accessible)
-    return E_FAIL;
+  PRUint32 geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ?
+    nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE :
+    nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE;
 
-  nsCOMPtr<nsIWinAccessNode> winAccessNode(do_QueryInterface(accessible));
-  if (!winAccessNode)
-    return E_FAIL;
-
-  void **instancePtr = 0;
-  winAccessNode->QueryNativeInterface(IID_IAccessible2, instancePtr);
-  if (!instancePtr)
-    return E_FAIL;
-
-  IAccessible2 *pAccessible2 = static_cast<IAccessible2*>(*instancePtr);
-  HRESULT hr = pAccessible2->scrollToPoint(aCoordinateType, aX, aY);
-  pAccessible2->Release();
-
-  return hr;
+  nsresult rv = textAcc->ScrollSubstringToPoint(aStartIndex, aEndIndex,
+                                                geckoCoordType, aX, aY);
+  return NS_FAILED(rv) ? E_FAIL : S_OK;
 }
 
 STDMETHODIMP
 CAccessibleText::get_newText(IA2TextSegment *aNewText)
 {
   return GetModifiedText(PR_TRUE, aNewText);
 }
 
--- a/accessible/src/msaa/nsAccessibleWrap.cpp
+++ b/accessible/src/msaa/nsAccessibleWrap.cpp
@@ -1184,20 +1184,25 @@ STDMETHODIMP
 nsAccessibleWrap::scrollTo(enum IA2ScrollType aScrollType)
 {
   if (NS_SUCCEEDED(ScrollTo(aScrollType)))
     return S_OK;
   return E_FAIL;
 }
 
 STDMETHODIMP
-nsAccessibleWrap::scrollToPoint(enum IA2CoordinateType coordinateType,
-                                long x, long y)
+nsAccessibleWrap::scrollToPoint(enum IA2CoordinateType aCoordType,
+                                long aX, long aY)
 {
-  return E_NOTIMPL;
+  PRUint32 geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ?
+    nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE :
+    nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE;
+
+  return NS_SUCCEEDED(ScrollToPoint(geckoCoordType, aX, aY)) ?
+    S_OK : E_FAIL;
 }
 
 STDMETHODIMP
 nsAccessibleWrap::get_groupPosition(long *aGroupLevel,
                                     long *aSimilarItemsInGroup,
                                     long *aPositionInGroup)
 {
   PRInt32 groupLevel = 0;