Bug 597331 - Reframing a textarea sets the caret position to the end of its contents; r=bzbarsky a=blocking-final+
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 13 Oct 2010 01:34:05 -0400
changeset 55424 178f26e21cfc6ce5a2b8bee8fdb61b95b1cff696
parent 55423 18d0545c5f0ef31d98c787c10c55dd53076e2939
child 55425 d925cd5764b5b1e6d922cd3163434315513c7daa
child 57205 6435ea1c2fbaca0806f06b6ee6957ce2519108c3
push id16253
push usereakhgari@mozilla.com
push dateWed, 13 Oct 2010 14:34:48 +0000
treeherdermozilla-central@178f26e21cfc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky, blocking-final
bugs597331
milestone2.0b8pre
first release with
nightly mac
178f26e21cfc / 4.0b8pre / 20101013074759 / files
nightly linux32
nightly linux64
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly mac
Bug 597331 - Reframing a textarea sets the caret position to the end of its contents; r=bzbarsky a=blocking-final+
content/html/content/src/nsTextEditorState.cpp
content/html/content/src/nsTextEditorState.h
editor/libeditor/text/tests/Makefile.in
editor/libeditor/text/tests/test_bug597331.html
--- a/content/html/content/src/nsTextEditorState.cpp
+++ b/content/html/content/src/nsTextEditorState.cpp
@@ -68,16 +68,21 @@
 #include "nsTextEditorState.h"
 
 static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID);
 static NS_DEFINE_CID(kFrameSelectionCID, NS_FRAMESELECTION_CID);
 
 static nsINativeKeyBindings *sNativeInputBindings = nsnull;
 static nsINativeKeyBindings *sNativeTextAreaBindings = nsnull;
 
+struct SelectionState {
+  PRInt32 mStart;
+  PRInt32 mEnd;
+};
+
 /*static*/
 PRBool
 nsITextControlElement::GetWrapPropertyEnum(nsIContent* aContent,
   nsITextControlElement::nsHTMLTextWrap& aWrapProp)
 {
   // soft is the default; "physical" defaults to soft as well because all other
   // browsers treat it that way and there is no real reason to maintain physical
   // and virtual as separate entities if no one else does.  Only hard and off
@@ -1316,16 +1321,22 @@ nsTextEditorState::PrepareEditor(const n
   if (!mEditorInitialized) {
     newEditor->PostCreate();
     mEditorInitialized = PR_TRUE;
   }
 
   if (mTextListener)
     newEditor->AddEditorObserver(mTextListener);
 
+  // Restore our selection after being bound to a new frame
+  if (mSelState) {
+    mBoundFrame->SetSelectionRange(mSelState->mStart, mSelState->mEnd);
+    mSelState = nsnull;
+  }
+
   return rv;
 }
 
 void
 nsTextEditorState::DestroyEditor()
 {
   // notify the editor that we are going away
   if (mEditorInitialized) {
@@ -1346,16 +1357,30 @@ nsTextEditorState::UnbindFromFrame(nsTex
   NS_ASSERTION(!aFrame || aFrame == mBoundFrame, "Unbinding from the wrong frame");
   NS_ENSURE_TRUE(!aFrame || aFrame == mBoundFrame, );
 
   // We need to start storing the value outside of the editor if we're not
   // going to use it anymore, so retrieve it for now.
   nsAutoString value;
   GetValue(value, PR_TRUE);
 
+  // Save our selection state if needed.
+  // Note that nsTextControlFrame::GetSelectionRange attempts to initialize the
+  // editor before grabbing the range, and because this is not an acceptable
+  // side effect for unbinding from a text control frame, we need to call
+  // GetSelectionRange before calling DestroyEditor, and only if
+  // mEditorInitialized indicates that we actually have an editor available.
+  if (mEditorInitialized) {
+    mSelState = new SelectionState();
+    nsresult rv = mBoundFrame->GetSelectionRange(&mSelState->mStart, &mSelState->mEnd);
+    if (NS_FAILED(rv)) {
+      mSelState = nsnull;
+    }
+  }
+
   // Destroy our editor
   DestroyEditor();
 
   // Clean up the controller
   if (!SuppressEventHandlers(mBoundFrame->PresContext()))
   {
     nsCOMPtr<nsIControllers> controllers;
     nsCOMPtr<nsIDOMHTMLInputElement> inputElement =
--- a/content/html/content/src/nsTextEditorState.h
+++ b/content/html/content/src/nsTextEditorState.h
@@ -46,16 +46,17 @@
 class nsTextInputListener;
 class nsTextControlFrame;
 class nsTextInputSelectionImpl;
 class nsAnonDivObserver;
 class nsISelectionController;
 class nsFrameSelection;
 class nsIEditor;
 class nsITextControlElement;
+struct SelectionState;
 
 /**
  * nsTextEditorState is a class which is responsible for managing the state of
  * plaintext controls.  This currently includes the following HTML elements:
  *   <input type=text>
  *   <input type=password>
  *   <textarea>
  * and also XUL controls such as <textbox> which use one of these elements behind
@@ -245,16 +246,17 @@ private:
   private:
     nsTextEditorState& mState;
     PRBool mGuardSet;
   };
   friend class InitializationGuard;
 
   nsITextControlElement* const mTextCtrlElement;
   nsRefPtr<nsTextInputSelectionImpl> mSelCon;
+  nsAutoPtr<SelectionState> mSelState;
   nsCOMPtr<nsIEditor> mEditor;
   nsCOMPtr<nsIContent> mRootNode;
   nsCOMPtr<nsIContent> mPlaceholderDiv;
   nsTextControlFrame* mBoundFrame;
   nsTextInputListener* mTextListener;
   nsAutoPtr<nsCString> mValue;
   nsRefPtr<nsAnonDivObserver> mMutationObserver;
   mutable nsString mCachedValue; // Caches non-hard-wrapped value on a multiline control.
--- a/editor/libeditor/text/tests/Makefile.in
+++ b/editor/libeditor/text/tests/Makefile.in
@@ -46,16 +46,17 @@ include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
 		test_bug471722.html \
 		test_bug569988.html \
 		test_bug590554.html \
 		test_bug596001.html \
 		test_bug596333.html \
 		test_bug596506.html \
+		test_bug597331.html \
 		test_bug600570.html \
 		$(NULL)
 
 # disables the key handling test on gtk2 because gtk2 overrides some key events
 # on our editor, and the combinations depend on the system.
 ifneq ($(MOZ_WIDGET_TOOLKIT),gtk2)
 _TEST_FILES += \
 		test_texteditor_keyevent_handling.html \
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/text/tests/test_bug597331.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=597331
+-->
+<head>
+  <title>Test for Bug 597331</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=597331">Mozilla Bug 597331</a>
+<p id="display"></p>
+<div id="content">
+<textarea>line1
+line2
+line3
+</textarea>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 597331 **/
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+  var t = document.querySelector("textarea");
+  t.focus();
+  t.selectionStart = 4;
+  t.selectionEnd = 4;
+  SimpleTest.executeSoon(function() {
+    var before = snapshotWindow(window, true);
+    t.selectionStart = 5;
+    t.selectionEnd = 5;
+    t.addEventListener("keydown", function() {
+      t.removeEventListener("keydown", arguments.callee, false);
+
+      SimpleTest.executeSoon(function() {
+        t.style.display = 'block';
+        document.body.offsetWidth;
+        t.style.display = '';
+        document.body.offsetWidth;
+
+        is(t.selectionStart, 4, "Cursor should be moved correctly");
+        is(t.selectionEnd, 4, "Cursor should be moved correctly");
+
+        var after = snapshotWindow(window, true);
+
+        var result = compareSnapshots(before, after, true);
+        ok(result[0], "The caret should be displayed correctly after reframing");
+
+        SimpleTest.finish();
+      });
+    }, false);
+    synthesizeKey("VK_LEFT", {});
+  });
+});
+
+</script>
+</pre>
+</body>
+</html>