Bug 371680 - expose nsIAccessibleText::scrollSubstringTo, r=aaronlev, roc, sr=roc, a=roc
authorsurkov.alexander@gmail.com
Thu, 23 Aug 2007 21:54:45 -0700
changeset 5245 733c92e47762de6db00ffb082c56a2c093339867
parent 5244 4fde6b5e7dca6ce7e8cc3eb65b12f439af12f089
child 5246 cb2fa164ecd485c8780519fca0b5c992d2d37045
push idunknown
push userunknown
push dateunknown
reviewersaaronlev, roc, roc, roc
bugs371680
milestone1.9a8pre
Bug 371680 - expose nsIAccessibleText::scrollSubstringTo, r=aaronlev, roc, sr=roc, a=roc
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/nsHyperTextAccessible.cpp
accessible/src/html/nsHyperTextAccessible.h
accessible/src/msaa/CAccessibleText.cpp
accessible/src/msaa/nsTextAccessibleWrap.cpp
content/base/public/nsISelection2.idl
layout/generic/nsSelection.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(17389a66-5cc5-4550-80e0-49e7b63990a4)]
+[scriptable, uuid(948419b2-53f6-4a74-bb69-1345faf3e8e8)]
 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;
@@ -162,16 +162,30 @@ interface nsIAccessibleText : nsISupport
 
   void setSelectionBounds (in long selectionNum,
                               in long startOffset,
                               in long endOffset);
 
   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).
+   */
+  void scrollSubstringTo(in long aStartIndex, in long aEndIndex,
+                         in unsigned long aScrollType);
 };
 
 /*
  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
@@ -75,16 +75,22 @@ interface nsIAccessibleScrollType : nsIS
    */
   const unsigned long SCROLL_TYPE_LEFT_EDGE =0x04;
 
   /**
    * Scroll the right edge of the object or substring to the right edge of the
    * window (or as close as possible).
    */
   const unsigned long SCROLL_TYPE_RIGHT_EDGE = 0x05;
+
+  /**
+   * 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.
  */
 [scriptable, uuid(c9fbdf10-619e-436f-bf4b-8566686f1577)]
@@ -101,8 +107,9 @@ interface nsIAccessibleCoordinateType : 
   const unsigned long COORDTYPE_WINDOW_RELATIVE = 0x01;
 
   /**
    * The coordinates are relative to the upper left corner of the bounding box
    * of the immediate parent.
    */
   const unsigned long COORDTYPE_PARENT_RELATIVE = 0x02;
 };
+
--- a/accessible/src/base/nsAccessNode.cpp
+++ b/accessible/src/base/nsAccessNode.cpp
@@ -421,45 +421,18 @@ nsAccessNode::ScrollTo(PRUint32 aScrollT
   NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
 
   nsIFrame *frame = GetFrame();
   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIContent> content = frame->GetContent();
   NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
 
-  PRInt32 vPercent, hPercent;
-  switch (aScrollType)
-  {
-    case nsIAccessibleScrollType::SCROLL_TYPE_TOP_LEFT:
-      vPercent = NS_PRESSHELL_SCROLL_TOP;
-      hPercent = NS_PRESSHELL_SCROLL_LEFT;
-      break;
-    case nsIAccessibleScrollType::SCROLL_TYPE_BOTTOM_RIGHT:
-      vPercent = NS_PRESSHELL_SCROLL_BOTTOM;
-      hPercent = NS_PRESSHELL_SCROLL_RIGHT;
-      break;
-    case nsIAccessibleScrollType::SCROLL_TYPE_TOP_EDGE:
-      vPercent = NS_PRESSHELL_SCROLL_TOP;
-      hPercent = NS_PRESSHELL_SCROLL_ANYWHERE;
-      break;
-    case nsIAccessibleScrollType::SCROLL_TYPE_BOTTOM_EDGE:
-      vPercent = NS_PRESSHELL_SCROLL_BOTTOM;
-      hPercent = NS_PRESSHELL_SCROLL_ANYWHERE;
-      break;
-    case nsIAccessibleScrollType::SCROLL_TYPE_LEFT_EDGE:
-      vPercent = NS_PRESSHELL_SCROLL_ANYWHERE;
-      hPercent = NS_PRESSHELL_SCROLL_LEFT;
-      break;
-    case nsIAccessibleScrollType::SCROLL_TYPE_RIGHT_EDGE:
-      vPercent = NS_PRESSHELL_SCROLL_ANYWHERE;
-      hPercent = NS_PRESSHELL_SCROLL_RIGHT;
-      break;
-  }
-
+  PRInt16 vPercent, hPercent;
+  nsAccUtils::ConvertScrollTypeToPercents(aScrollType, &vPercent, &hPercent);
   return shell->ScrollContentIntoView(content, vPercent, hPercent);
 }
 
 NS_IMETHODIMP
 nsAccessNode::ScrollToPoint(PRUint32 aCoordinateType, PRInt32 aX, PRInt32 aY)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
--- a/accessible/src/base/nsAccessibilityUtils.cpp
+++ b/accessible/src/base/nsAccessibilityUtils.cpp
@@ -33,22 +33,31 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * 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 "nsAccessibleEventData.h"
 
+#include "nsIDOMRange.h"
 #include "nsIDOMXULSelectCntrlEl.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIEventListenerManager.h"
+#include "nsISelection2.h"
+#include "nsISelectionController.h"
+
+#include "nsContentCID.h"
+#include "nsComponentManagerUtils.h"
+
+static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
 
 void
 nsAccUtils::GetAccAttr(nsIPersistentProperties *aAttributes, nsIAtom *aAttrName,
                        nsAString& aAttrValue)
 {
   nsCAutoString attrName;
   aAttrName->ToUTF8String(attrName);
   aAttributes->GetStringProperty(attrName, aAttrValue);
@@ -218,8 +227,87 @@ nsAccUtils::GetAncestorWithRole(nsIAcces
     nsCOMPtr<nsIAccessibleDocument> docAccessible = do_QueryInterface(testRoleAccessible);
     if (docAccessible) {
       break;
     }
     parentAccessible.swap(testRoleAccessible);
   }
   return nsnull;
 }
+
+nsresult
+nsAccUtils::ScrollSubstringTo(nsIFrame *aFrame,
+                              nsIDOMNode *aStartNode, PRInt32 aStartIndex,
+                              nsIDOMNode *aEndNode, PRInt32 aEndIndex,
+                              PRUint32 aScrollType)
+{
+  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);
+
+  nsCOMPtr<nsISelectionController> selCon;
+  aFrame->GetSelectionController(presContext, getter_AddRefs(selCon));
+  NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
+
+  scrollToRange->SetStart(aStartNode, aStartIndex);
+  scrollToRange->SetEnd(aEndNode, aEndIndex);
+
+  nsCOMPtr<nsISelection> selection1;
+  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);
+
+    selection->CollapseToStart();
+  }
+
+  return NS_OK;
+}
+
+void
+nsAccUtils::ConvertScrollTypeToPercents(PRUint32 aScrollType,
+                                        PRInt16 *aVPercent,
+                                        PRInt16 *aHPercent)
+{
+  switch (aScrollType)
+  {
+    case nsIAccessibleScrollType::SCROLL_TYPE_TOP_LEFT:
+      *aVPercent = NS_PRESSHELL_SCROLL_TOP;
+      *aHPercent = NS_PRESSHELL_SCROLL_LEFT;
+      break;
+    case nsIAccessibleScrollType::SCROLL_TYPE_BOTTOM_RIGHT:
+      *aVPercent = NS_PRESSHELL_SCROLL_BOTTOM;
+      *aHPercent = NS_PRESSHELL_SCROLL_RIGHT;
+      break;
+    case nsIAccessibleScrollType::SCROLL_TYPE_TOP_EDGE:
+      *aVPercent = NS_PRESSHELL_SCROLL_TOP;
+      *aHPercent = NS_PRESSHELL_SCROLL_ANYWHERE;
+      break;
+    case nsIAccessibleScrollType::SCROLL_TYPE_BOTTOM_EDGE:
+      *aVPercent = NS_PRESSHELL_SCROLL_BOTTOM;
+      *aHPercent = NS_PRESSHELL_SCROLL_ANYWHERE;
+      break;
+    case nsIAccessibleScrollType::SCROLL_TYPE_LEFT_EDGE:
+      *aVPercent = NS_PRESSHELL_SCROLL_ANYWHERE;
+      *aHPercent = NS_PRESSHELL_SCROLL_LEFT;
+      break;
+    case nsIAccessibleScrollType::SCROLL_TYPE_RIGHT_EDGE:
+      *aVPercent = NS_PRESSHELL_SCROLL_ANYWHERE;
+      *aHPercent = NS_PRESSHELL_SCROLL_RIGHT;
+      break;
+    default:
+      *aVPercent = NS_PRESSHELL_SCROLL_ANYWHERE;
+      *aHPercent = NS_PRESSHELL_SCROLL_ANYWHERE;
+  }
+}
+
--- a/accessible/src/base/nsAccessibilityUtils.h
+++ b/accessible/src/base/nsAccessibilityUtils.h
@@ -40,16 +40,17 @@
 #define nsAccessibilityUtils_h_
 
 #include "nsAccessibilityAtoms.h"
 #include "nsIAccessible.h"
 
 #include "nsIDOMNode.h"
 #include "nsIPersistentProperties2.h"
 #include "nsIContent.h"
+#include "nsIFrame.h"
 
 class nsAccUtils
 {
 public:
   /**
    * Returns value of attribute from the given attributes container.
    *
    * @param aAttributes - attributes container
@@ -119,19 +120,44 @@ public:
    * Note: A node is not considered to be the ancestor of itself.
    * @param aPossibleAncestorNode -- node to test for ancestor-ness of aPossibleDescendantNode
    * @param aPossibleDescendantNode -- node to test for descendant-ness of aPossibleAncestorNode
    * @return PR_TRUE if aPossibleAncestorNode is an ancestor of aPossibleDescendantNode
    */
    static PRBool IsAncestorOf(nsIDOMNode *aPossibleAncestorNode,
                               nsIDOMNode *aPossibleDescendantNode);
 
-   /**
+  /**
     * If an ancestor in this document exists with the given role, return it
     * @param aDescendant Descendant to start search with
     * @param aRole Role to find matching ancestor for
     * @return The ancestor accessible with the given role, or nsnull if no match is found
     */
    static already_AddRefed<nsIAccessible>
      GetAncestorWithRole(nsIAccessible *aDescendant, PRUint32 aRole);
+
+  /**
+   * Helper method to scroll range into view, used for implementation of
+   * nsIAccessibleText::scrollSubstringTo().
+   *
+   * @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 aScrollType   the place a range should be scrolled to
+   */
+  static nsresult ScrollSubstringTo(nsIFrame *aFrame,
+                                    nsIDOMNode *aStartNode, PRInt32 aStartIndex,
+                                    nsIDOMNode *aEndNode, PRInt32 aEndIndex,
+                                    PRUint32 aScrollType);
+
+  /**
+   * Converts scroll type constant defined in nsIAccessibleScrollType to
+   * vertical and horizontal percents.
+   */
+  static void ConvertScrollTypeToPercents(PRUint32 aScrollType,
+                                          PRInt16 *aVPercent,
+                                          PRInt16 *aHPercent);
 };
 
 #endif
+
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -310,17 +310,19 @@ nsIntRect nsHyperTextAccessible::GetBoun
 }
 
 /*
  * Gets the specified text.
  */
 nsIFrame*
 nsHyperTextAccessible::GetPosAndText(PRInt32& aStartOffset, PRInt32& aEndOffset,
                                      nsAString *aText, nsIFrame **aEndFrame,
-                                     nsIntRect *aBoundsRect)
+                                     nsIntRect *aBoundsRect,
+                                     nsIAccessible **aStartAcc,
+                                     nsIAccessible **aEndAcc)
 {
   PRInt32 startOffset = aStartOffset;
   PRInt32 endOffset = aEndOffset;
 
   // Clear out parameters and set up loop
   if (aText) {
     aText->Truncate();
   }
@@ -334,16 +336,20 @@ nsHyperTextAccessible::GetPosAndText(PRI
 
   nsIFrame *startFrame = nsnull;
   if (aEndFrame) {
     *aEndFrame = nsnull;
   }
   if (aBoundsRect) {
     aBoundsRect->Empty();
   }
+  if (aStartAcc)
+    *aStartAcc = nsnull;
+  if (aEndAcc)
+    *aEndAcc = nsnull;
 
   nsIntRect unionRect;
   nsCOMPtr<nsIAccessible> accessible;
 
   gfxSkipChars skipChars;
   gfxSkipCharsIterator iter;
 
   // Loop through children and collect valid offsets, text and bounds
@@ -380,16 +386,18 @@ nsHyperTextAccessible::GetPosAndText(PRI
           // Get out the continuing text frame with this offset
           PRInt32 outStartLineUnused;
           PRInt32 contentOffset = iter.ConvertSkippedToOriginal(startOffset) +
                                   ourRenderedStart - ourContentStart;
           frame->GetChildFrameContainingOffset(contentOffset, PR_TRUE,
                                                &outStartLineUnused, &frame);
           if (aEndFrame) {
             *aEndFrame = frame; // We ended in the current frame
+            if (aEndAcc)
+              NS_ADDREF(*aEndAcc = accessible);
           }
           if (substringEndOffset > endOffset) {
             // Need to stop before the end of the available text
             substringEndOffset = endOffset;
           }
           aEndOffset = endOffset;
         }
         if (aText) {
@@ -400,16 +408,18 @@ nsHyperTextAccessible::GetPosAndText(PRI
         if (aBoundsRect) {    // Caller wants the bounds of the text
           aBoundsRect->UnionRect(*aBoundsRect,
                                  GetBoundsForString(primaryFrame, startOffset,
                                                     substringEndOffset));
         }
         if (!startFrame) {
           startFrame = frame;
           aStartOffset = startOffset;
+          if (aStartAcc)
+            NS_ADDREF(*aStartAcc = accessible);
         }
         // We already started copying in this accessible's string,
         // for the next accessible we'll start at offset 0
         startOffset = 0;
       }
       else {
         // We have not found the start position yet, get the new startOffset
         // that is relative to next accessible
@@ -433,27 +443,31 @@ nsHyperTextAccessible::GetPosAndText(PRI
           if (aBoundsRect) {
             aBoundsRect->UnionRect(*aBoundsRect,
                                    frame->GetScreenRectExternal());
           }
         }
         if (!startFrame) {
           startFrame = frame;
           aStartOffset = 0;
+          if (aStartAcc)
+            NS_ADDREF(*aStartAcc = accessible);
         }
       }
       -- endOffset;
     }
     if (endOffset <= 0 && startFrame) {
       break; // If we don't have startFrame yet, get that in next loop iteration
     }
   }
 
   if (aEndFrame && !*aEndFrame) {
     *aEndFrame = startFrame;
+    if (aStartAcc && aEndAcc)
+      NS_ADDREF(*aEndAcc = *aStartAcc);
   }
 
   return startFrame;
 }
 
 NS_IMETHODIMP nsHyperTextAccessible::GetText(PRInt32 aStartOffset, PRInt32 aEndOffset, nsAString &aText)
 {
   if (!mDOMNode) {
@@ -1504,16 +1518,68 @@ 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);
 }
 
+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());
+
+  PRBool isStartAccText = IsText(startAcc);
+  if (isStartAccText) {
+    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());
+
+  PRBool isEndAccText = IsText(endAcc);
+  if (isEndAccText) {
+    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);
+
+  return nsAccUtils::ScrollSubstringTo(GetFrame(), startNode, startOffset,
+                                       endNode, endOffset, aScrollType);
+}
+
 nsresult nsHyperTextAccessible::ContentToRenderedOffset(nsIFrame *aFrame, PRInt32 aContentOffset,
                                                         PRUint32 *aRenderedOffset)
 {
   gfxSkipChars skipChars;
   gfxSkipCharsIterator iter;
   // Only get info up to original ofset, we know that will be larger than skipped offset
   nsresult rv = aFrame->GetRenderedText(nsnull, &skipChars, &iter, 0, aContentOffset);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/accessible/src/html/nsHyperTextAccessible.h
+++ b/accessible/src/html/nsHyperTextAccessible.h
@@ -134,30 +134,48 @@ protected:
     * @param aFromOffset, the starting offset we're moving from
     * @param aAmount, how much are we moving (word/line/etc.) ?
     * @param aDirection, forward or backward?
     * @param aNeedsStart, for word and line cases, are we basing this on the start or end?
     * @return, the resulting offset into this hypertext
     */
   PRInt32 GetRelativeOffset(nsIPresShell *aPresShell, nsIFrame *aFromFrame, PRInt32 aFromOffset,
                             nsSelectionAmount aAmount, nsDirection aDirection, PRBool aNeedsStart);
+
   /**
-    * Given a start offset and end offset, get substring information. Different info is returned depending
-    * on what optional paramters are provided.
-    * @param aStartOffset, the start offset into the hyper text. This is also an out parameter used to return
-    *                      the offset into the start frame's rendered text content (start frame is the @return)
-    * @param aEndHyperOffset, the endoffset into the hyper text. This is also an out parameter used to return
-    *                    the offset into the end frame's rendered text content
-    * @param aText (optional), return the substring's text
-    * @param aEndFrame (optional), return the end frame for this substring
-    * @param aBoundsRect (optional), return the bounds rectangle for this substring
-    * @return the start frame for this substring
+    * Provides information for substring that is defined by the given start
+    * and end offsets for this hyper text.
+    *
+    * @param  aStartOffset  [inout] the start offset into the hyper text. This
+    *                       is also an out parameter used to return the offset
+    *                       into the start frame's rendered text content
+    *                       (start frame is the @return)
+    *
+    * @param  aEndOffset    [inout] the end offset into the hyper text. This is
+    *                       also an out parameter used to return
+    *                       the offset into the end frame's rendered
+    *                       text content.
+    *
+    * @param  aText         [out, optional] return the substring's text
+    * @param  aEndFrame     [out, optional] return the end frame for this
+    *                       substring
+    * @param  aBoundsRect   [out, optional] return the bounds rectangle for this
+    *                       substring
+    * @param  aStartAcc     [out, optional] return the start accessible for this
+    *                       substring
+    * @param  aEndAcc       [out, optional] return the end accessible for this
+    *                       substring
+    * @return               the start frame for this substring
     */
-  nsIFrame* GetPosAndText(PRInt32& aStartOffset, PRInt32& aEndOffset, nsAString *aText = nsnull,
-                          nsIFrame **aEndFrame = nsnull, nsIntRect *aBoundsRect = nsnull);
+  nsIFrame* GetPosAndText(PRInt32& aStartOffset, PRInt32& aEndOffset,
+                          nsAString *aText = nsnull,
+                          nsIFrame **aEndFrame = nsnull,
+                          nsIntRect *aBoundsRect = nsnull,
+                          nsIAccessible **aStartAcc = nsnull,
+                          nsIAccessible **aEndAcc = nsnull);
 
   nsIntRect GetBoundsForString(nsIFrame *aFrame, PRUint32 aStartRenderedOffset, PRUint32 aEndRenderedOffset);
 
   // Selection helpers
   nsresult GetSelections(nsISelectionController **aSelCon, nsISelection **aDomSel);
   nsresult SetSelectionRange(PRInt32 aStartPos, PRInt32 aEndPos);
 };
 
--- a/accessible/src/msaa/CAccessibleText.cpp
+++ b/accessible/src/msaa/CAccessibleText.cpp
@@ -355,39 +355,18 @@ CAccessibleText::get_nCharacters(long *a
 }
 
 STDMETHODIMP
 CAccessibleText::scrollSubstringTo(long aStartIndex, long aEndIndex,
                                    enum IA2ScrollType aScrollType)
 {
   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;
-
-  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->scrollTo(aScrollType);
-  pAccessible2->Release();
-
-  return hr;
+  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,
                                         long aX, long aY)
 {
   GET_NSIACCESSIBLETEXT
--- a/accessible/src/msaa/nsTextAccessibleWrap.cpp
+++ b/accessible/src/msaa/nsTextAccessibleWrap.cpp
@@ -34,31 +34,25 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 // NOTE: alphabetically ordered
 #include "nsTextAccessibleWrap.h"
 #include "ISimpleDOMText_i.c"
-#include "nsContentCID.h"
 #include "nsIAccessibleDocument.h"
-#include "nsIDOMRange.h"
 #include "nsIFontMetrics.h"
 #include "nsIFrame.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsIRenderingContext.h"
-#include "nsISelection.h"
-#include "nsISelectionController.h"
 #include "nsIWidget.h"
 #include "nsIComponentManager.h"
 
-static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
-
 // --------------------------------------------------------
 // nsTextAccessibleWrap Accessible
 // --------------------------------------------------------
 
 nsTextAccessibleWrap::nsTextAccessibleWrap(nsIDOMNode* aDOMNode, nsIWeakReference* aShell):
 nsTextAccessible(aDOMNode, aShell)
 { 
 }
@@ -153,51 +147,24 @@ STDMETHODIMP nsTextAccessibleWrap::get_u
                                     aX, aY, aWidth, aHeight))) {
     return NS_ERROR_FAILURE;
   }
 
   return S_OK;
 }
 
 
-STDMETHODIMP nsTextAccessibleWrap::scrollToSubstring( 
+STDMETHODIMP nsTextAccessibleWrap::scrollToSubstring(
     /* [in] */ unsigned int aStartIndex,
     /* [in] */ unsigned int aEndIndex)
 {
-  nsCOMPtr<nsIPresShell> presShell(GetPresShell());
-  nsIFrame *frame = GetFrame();
-
-  if (!frame || !presShell) {
-    return E_FAIL;  // This accessible has been shut down
-  }
-
-  nsPresContext *presContext = presShell->GetPresContext();
-  nsCOMPtr<nsIDOMRange> scrollToRange = do_CreateInstance(kRangeCID);
-  nsCOMPtr<nsISelectionController> selCon;
-  frame->GetSelectionController(presContext, getter_AddRefs(selCon));
-  if (!presContext || !scrollToRange || !selCon) {
-    return E_FAIL;
-  }
-
-  scrollToRange->SetStart(mDOMNode, aStartIndex);
-  scrollToRange->SetEnd(mDOMNode, aEndIndex);
-  nsCOMPtr<nsISelection> domSel;
-  selCon->GetSelection(nsISelectionController::SELECTION_ACCESSIBILITY, 
-                       getter_AddRefs(domSel));
-  if (domSel) {
-    domSel->RemoveAllRanges();
-    domSel->AddRange(scrollToRange);
-
-    selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_ACCESSIBILITY, 
-      nsISelectionController::SELECTION_ANCHOR_REGION, PR_TRUE);
-
-    domSel->CollapseToStart();
-  }
-
-  return S_OK;
+  nsresult rv = nsAccUtils::ScrollSubstringTo(GetFrame(), mDOMNode, aStartIndex,
+                                              mDOMNode, aEndIndex,
+                                              nsIAccessibleScrollType::SCROLL_TYPE_ANYWHERE);
+  return NS_FAILED(rv) ? E_FAIL : S_OK;
 }
 
 nsIFrame* nsTextAccessibleWrap::GetPointFromOffset(nsIFrame *aContainingFrame, 
                                                    PRInt32 aOffset, 
                                                    PRBool aPreferNext, 
                                                    nsPoint& aOutPoint)
 {
   nsIFrame *textFrame = nsnull;
--- a/content/base/public/nsISelection2.idl
+++ b/content/base/public/nsISelection2.idl
@@ -42,24 +42,60 @@ interface nsIDOMNode;
 interface nsIDOMRange;
 
 %{C++
 #include "nsCOMArray.h"
 %}
 
 [ptr] native RangeArray(nsCOMArray<nsIDOMRange>);
 
-[scriptable, uuid(eab4ae76-efdc-4e09-828c-bd921e9a662f)]
+[scriptable, uuid(b515878d-3b06-433b-bc9e-5c53d2fa3eff)]
 interface nsISelection2 : nsISelection
 {
   void GetRangesForInterval(
       in nsIDOMNode beginNode, in PRInt32 beginOffset,
       in nsIDOMNode endNode, in PRInt32 endOffset,
       in PRBool allowAdjacent,
       out PRUint32 resultCount,
       [retval, array, size_is(resultCount)] out nsIDOMRange results);
 
   [noscript] void GetRangesForIntervalCOMArray(
       in nsIDOMNode beginNode, in PRInt32 beginOffset,
       in nsIDOMNode endNode, in PRInt32 endOffset,
       in PRBool allowAdjacent,
       in RangeArray results);
+
+  /**
+   * Scrolls a region of the selection, so that it is visible in
+   * the scrolled view.
+   *
+   * @param aRegion - the region inside the selection to scroll into view
+   *                  (see selection region constants defined in
+   *                   nsISelectionController).
+   * @param aIsSynchronous - when true, scrolls the selection into view
+   *                         before returning. If false, posts a request which
+   *                         is processed at some point after the method returns.
+   * @param aVPercent - how to align the frame vertically. A value of 0
+   *                   means the frame's upper edge is aligned with the top edge
+   *                   of the visible area. A value of 100 means the frame's
+   *                   bottom edge is aligned with the bottom edge of
+   *                   the visible area. For values in between, the point
+   *                   "aVPercent" down the frame is placed at the point
+   *                   "aVPercent" down the visible area. A value of 50 centers
+   *                   the frame vertically. A value of -1 means move
+   *                   the frame the minimum amount necessary in order for
+   *                   the entire frame to be visible vertically (if possible).
+   * @param aHPercent - how to align the frame horizontally. A value of 0
+   *                    means the frame's left edge is aligned with the left
+   *                    edge of the visible area. A value of 100 means the
+   *                    frame's right edge is aligned with the right edge of
+   *                    the visible area. For values in between, the point
+   *                    "aHPercent" across the frame is placed at the point
+   *                    "aHPercent" across the visible area. A value of 50
+   *                    centers the frame horizontally . A value of -1 means
+   *                    move the frame the minimum amount necessary in order
+   *                    for the entire frame to be visible horizontally
+   *                    (if possible).
+   */
+  void scrollIntoView(in short aRegion, in boolean aIsSynchronous,
+                      in short aVPercent, in short aHPercent);
 };
+
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -210,17 +210,19 @@ public:
   nsresult      GetPointFromOffset(nsIFrame *aFrame, PRInt32 aContentOffset, nsPoint *aPoint);
   nsresult      GetSelectionRegionRectAndScrollableView(SelectionRegion aRegion, nsRect *aRect, nsIScrollableView **aScrollableView);
   nsresult      ScrollRectIntoView(nsIScrollableView *aScrollableView, nsRect& aRect, PRIntn  aVPercent, PRIntn  aHPercent, PRBool aScrollParentViews);
 
   nsresult      PostScrollSelectionIntoViewEvent(SelectionRegion aRegion);
   // aDoFlush only matters if aIsSynchronous is true.  If not, we'll just flush
   // when the scroll event fires so we make sure to scroll to the right place.
   nsresult      ScrollIntoView(SelectionRegion aRegion, PRBool aIsSynchronous,
-                               PRBool aDoFlush);
+                               PRBool aDoFlush,
+                               PRInt16 aVPercent = NS_PRESSHELL_SCROLL_ANYWHERE,
+                               PRInt16 aHPercent = NS_PRESSHELL_SCROLL_ANYWHERE);
   nsresult      AddItem(nsIDOMRange *aRange);
   nsresult      RemoveItem(nsIDOMRange *aRange);
   nsresult      Clear(nsPresContext* aPresContext);
 
   // methods for convenience. Note, these don't addref
   nsIDOMNode*  FetchAnchorNode();  //where did the selection begin
   PRInt32      FetchAnchorOffset();
 
@@ -7293,19 +7295,28 @@ nsTypedSelection::PostScrollSelectionInt
       new ScrollSelectionIntoViewEvent(this, aRegion);
   nsresult rv = NS_DispatchToCurrentThread(ev);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mScrollEvent = ev;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsTypedSelection::ScrollIntoView(SelectionRegion aRegion, PRBool aIsSynchronous,
+                                 PRInt16 aVPercent, PRInt16 aHPercent)
+{
+  return ScrollIntoView(aRegion, aIsSynchronous, PR_FALSE,
+                        aVPercent, aHPercent);
+}
+
 nsresult
 nsTypedSelection::ScrollIntoView(SelectionRegion aRegion,
-                                 PRBool aIsSynchronous, PRBool aDoFlush)
+                                 PRBool aIsSynchronous, PRBool aDoFlush,
+                                 PRInt16 aVPercent, PRInt16 aHPercent)
 {
   nsresult result;
   if (!mFrameSelection)
     return NS_OK;//nothing to do
 
   if (mFrameSelection->GetBatching())
     return NS_OK;
 
@@ -7353,17 +7364,18 @@ nsTypedSelection::ScrollIntoView(Selecti
       return result;
 
     //
     // It's ok if we don't have a scrollable view, just return early.
     //
     if (!scrollableView)
       return NS_OK;
 
-    result = ScrollRectIntoView(scrollableView, rect, NS_PRESSHELL_SCROLL_ANYWHERE, NS_PRESSHELL_SCROLL_ANYWHERE, PR_TRUE);
+    result = ScrollRectIntoView(scrollableView, rect, aVPercent, aHPercent,
+                                PR_TRUE);
   }
   return result;
 }
 
 
 
 NS_IMETHODIMP
 nsTypedSelection::AddSelectionListener(nsISelectionListener* aNewListener)