Bug 629878 - Part 2: Do not scroll the selection into view when restoring the selection offsets for a text control after a reframe; r=roc a=blocking-final+
authorEhsan Akhgari <ehsan@mozilla.com>
Fri, 04 Feb 2011 19:29:29 -0500
changeset 62234 c7327b618b343a9ba53be0976c2928652541118f
parent 62233 757171acb2a2068d32cfb57a1971d70dea73ffae
child 62235 855ac974c3c163645e874c9a264cdf2c5e110146
push id1
push userroot
push dateTue, 10 Dec 2013 15:46:25 +0000
reviewersroc, blocking-final
bugs629878
milestone2.0b12pre
Bug 629878 - Part 2: Do not scroll the selection into view when restoring the selection offsets for a text control after a reframe; r=roc a=blocking-final+ Previously, we used to scroll the text control's selection into view after a reframe. This had two problems: it was not precise (in case the selection was modified by the mouse, for example), and it lead to problems such as bug 629878 if the control's frame was reconstructed because of the control being moved inside the DOM. This patch disables that behavior by wrapping the selection scroll function into nsITextControlFrame::ScrollSelectionIntoView, so that APIs such as setSelectionRange on the text control's content node can still call it explicitly (since they actually need this behavior), but other callers of nsITextControlFrame::SetSelectionRange (such as the nsTextEditorState object's selection offset restoring mechanism) don't get this behavior as an undesired side-effect.
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/src/nsHTMLTextAreaElement.cpp
layout/forms/nsITextControlFrame.h
layout/forms/nsTextControlFrame.cpp
layout/forms/nsTextControlFrame.h
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -2852,18 +2852,22 @@ NS_IMETHODIMP
 nsHTMLInputElement::SetSelectionRange(PRInt32 aSelectionStart,
                                       PRInt32 aSelectionEnd)
 {
   nsresult rv = NS_ERROR_FAILURE;
   nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
 
   if (formControlFrame) {
     nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
-    if (textControlFrame)
+    if (textControlFrame) {
       rv = textControlFrame->SetSelectionRange(aSelectionStart, aSelectionEnd);
+      if (NS_SUCCEEDED(rv)) {
+        rv = textControlFrame->ScrollSelectionIntoView();
+      }
+    }
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsHTMLInputElement::GetSelectionStart(PRInt32* aSelectionStart)
 {
@@ -2876,18 +2880,22 @@ nsHTMLInputElement::GetSelectionStart(PR
 NS_IMETHODIMP
 nsHTMLInputElement::SetSelectionStart(PRInt32 aSelectionStart)
 {
   nsresult rv = NS_ERROR_FAILURE;
   nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
 
   if (formControlFrame) {
     nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
-    if (textControlFrame)
+    if (textControlFrame) {
       rv = textControlFrame->SetSelectionStart(aSelectionStart);
+      if (NS_SUCCEEDED(rv)) {
+        rv = textControlFrame->ScrollSelectionIntoView();
+      }
+    }
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsHTMLInputElement::GetSelectionEnd(PRInt32* aSelectionEnd)
 {
@@ -2901,18 +2909,22 @@ nsHTMLInputElement::GetSelectionEnd(PRIn
 NS_IMETHODIMP
 nsHTMLInputElement::SetSelectionEnd(PRInt32 aSelectionEnd)
 {
   nsresult rv = NS_ERROR_FAILURE;
   nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
 
   if (formControlFrame) {
     nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
-    if (textControlFrame)
+    if (textControlFrame) {
       rv = textControlFrame->SetSelectionEnd(aSelectionEnd);
+      if (NS_SUCCEEDED(rv)) {
+        rv = textControlFrame->ScrollSelectionIntoView();
+      }
+    }
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsHTMLInputElement::GetFiles(nsIDOMFileList** aFileList)
 {
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp
+++ b/content/html/content/src/nsHTMLTextAreaElement.cpp
@@ -874,18 +874,22 @@ nsHTMLTextAreaElement::GetSelectionStart
 NS_IMETHODIMP
 nsHTMLTextAreaElement::SetSelectionStart(PRInt32 aSelectionStart)
 {
   nsresult rv = NS_ERROR_FAILURE;
   nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
 
   if (formControlFrame){
     nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
-    if (textControlFrame)
+    if (textControlFrame) {
       rv = textControlFrame->SetSelectionStart(aSelectionStart);
+      if (NS_SUCCEEDED(rv)) {
+        rv = textControlFrame->ScrollSelectionIntoView();
+      }
+    }
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsHTMLTextAreaElement::GetSelectionEnd(PRInt32 *aSelectionEnd)
 {
@@ -898,18 +902,22 @@ nsHTMLTextAreaElement::GetSelectionEnd(P
 NS_IMETHODIMP
 nsHTMLTextAreaElement::SetSelectionEnd(PRInt32 aSelectionEnd)
 {
   nsresult rv = NS_ERROR_FAILURE;
   nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
 
   if (formControlFrame) {
     nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
-    if (textControlFrame)
+    if (textControlFrame) {
       rv = textControlFrame->SetSelectionEnd(aSelectionEnd);
+      if (NS_SUCCEEDED(rv)) {
+        rv = textControlFrame->ScrollSelectionIntoView();
+      }
+    }
   }
 
   return rv;
 }
 
 nsresult
 nsHTMLTextAreaElement::GetSelectionRange(PRInt32* aSelectionStart,
                                       PRInt32* aSelectionEnd)
@@ -929,18 +937,22 @@ nsHTMLTextAreaElement::GetSelectionRange
 NS_IMETHODIMP
 nsHTMLTextAreaElement::SetSelectionRange(PRInt32 aSelectionStart, PRInt32 aSelectionEnd)
 { 
   nsresult rv = NS_ERROR_FAILURE;
   nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
 
   if (formControlFrame) {
     nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
-    if (textControlFrame)
+    if (textControlFrame) {
       rv = textControlFrame->SetSelectionRange(aSelectionStart, aSelectionEnd);
+      if (NS_SUCCEEDED(rv)) {
+        rv = textControlFrame->ScrollSelectionIntoView();
+      }
+    }
   }
 
   return rv;
 } 
 
 nsresult
 nsHTMLTextAreaElement::Reset()
 {
--- a/layout/forms/nsITextControlFrame.h
+++ b/layout/forms/nsITextControlFrame.h
@@ -71,11 +71,13 @@ public:
   virtual nsresult GetPhonetic(nsAString& aPhonetic) = 0;
 
   /**
    * Ensure editor is initialized with the proper flags and the default value.
    * @throws NS_ERROR_NOT_INITIALIZED if mEditor has not been created
    * @throws various and sundry other things
    */
   virtual nsresult EnsureEditorInitialized() = 0;
+
+  virtual nsresult ScrollSelectionIntoView() = 0;
 };
 
 #endif
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -850,28 +850,33 @@ nsTextControlFrame::SetSelectionInternal
   nsCOMPtr<nsISelection> selection;
   selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));  
   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
 
   rv = selection->RemoveAllRanges();
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = selection->AddRange(range);  // NOTE: can destroy the world
-  NS_ENSURE_SUCCESS(rv, rv);
+  return rv;
+}
 
-  // Fetch it again since it might have been destroyed (bug 626014).
-  selCon = txtCtrl->GetSelectionController();
-  if (!selCon) {
-    return NS_OK;  // nothing to scroll, we're done
+nsresult
+nsTextControlFrame::ScrollSelectionIntoView()
+{
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
+  nsISelectionController* selCon = txtCtrl->GetSelectionController();
+  if (selCon) {
+    // Scroll the selection into view (see bug 231389).
+    return selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
+                                           nsISelectionController::SELECTION_FOCUS_REGION,
+                                           nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY);
   }
 
-  // Scroll the selection into view (see bug 231389).
-  return selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
-                                         nsISelectionController::SELECTION_FOCUS_REGION,
-                                         nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY);
+  return NS_ERROR_FAILURE;
 }
 
 nsresult
 nsTextControlFrame::GetRootNodeAndInitializeEditor(nsIDOMElement **aRootElement)
 {
   NS_ENSURE_ARG_POINTER(aRootElement);
 
   nsCOMPtr<nsIEditor> editor;
@@ -909,18 +914,21 @@ nsTextControlFrame::SelectAllOrCollapseT
       if (child && child->IsNodeOfType(nsINode::eTEXT)) {
         rootNode = do_QueryInterface(child);
         const nsTextFragment* fragment = child->GetText();
         numChildren = fragment ? fragment->GetLength() : 0;
       }
     }
   }
 
-  return SetSelectionInternal(rootNode, aSelect ? 0 : numChildren,
-                              rootNode, numChildren);
+  rv = SetSelectionInternal(rootNode, aSelect ? 0 : numChildren,
+                            rootNode, numChildren);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return ScrollSelectionIntoView();
 }
 
 nsresult
 nsTextControlFrame::SetSelectionEndPoints(PRInt32 aSelStart, PRInt32 aSelEnd)
 {
   NS_ASSERTION(aSelStart <= aSelEnd, "Invalid selection offsets!");
 
   if (aSelStart > aSelEnd)
--- a/layout/forms/nsTextControlFrame.h
+++ b/layout/forms/nsTextControlFrame.h
@@ -365,16 +365,18 @@ protected:
   void PreDestroy();
 
   // Compute our intrinsic size.  This does not include any borders, paddings,
   // etc.  Just the size of our actual area for the text (and the scrollbars,
   // for <textarea>).
   nsresult CalcIntrinsicSize(nsIRenderingContext* aRenderingContext,
                              nsSize&              aIntrinsicSize);
 
+  nsresult ScrollSelectionIntoView();
+
 private:
   //helper methods
   nsresult SetSelectionInternal(nsIDOMNode *aStartNode, PRInt32 aStartOffset,
                                 nsIDOMNode *aEndNode, PRInt32 aEndOffset);
   nsresult SelectAllOrCollapseToEndOfText(PRBool aSelect);
   nsresult SetSelectionEndPoints(PRInt32 aSelStart, PRInt32 aSelEnd);
 
   // accessors for the notify on input flag