Bug 534785 - Move the ownership of editor and selection controller from the text frame to the content node; r=roc,jst sr=roc
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 21 Apr 2010 16:17:41 -0400
changeset 43129 2437636244f39bbdc7d20b062e2c978b9d85c239
parent 43128 21f0727c27a6ac4c49cb7c93fce8299491c60ec8
child 43130 44439c29d5b22d2b5da8b8ee86c270b18dc922fc
push idunknown
push userunknown
push dateunknown
reviewersroc, jst, roc
bugs534785
milestone1.9.3a5pre
Bug 534785 - Move the ownership of editor and selection controller from the text frame to the content node; r=roc,jst sr=roc
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
content/html/content/public/nsITextControlElement.h
content/html/content/src/Makefile.in
content/html/content/src/nsGenericHTMLElement.cpp
content/html/content/src/nsGenericHTMLElement.h
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/src/nsHTMLTextAreaElement.cpp
content/html/content/src/nsTextEditorState.cpp
content/html/content/src/nsTextEditorState.h
editor/libeditor/base/nsEditor.cpp
editor/libeditor/base/nsEditor.h
editor/libeditor/base/nsEditorEventListener.cpp
layout/build/nsLayoutStatics.cpp
layout/forms/nsFileControlFrame.cpp
layout/forms/nsFileControlFrame.h
layout/forms/nsITextControlFrame.h
layout/forms/nsIsIndexFrame.cpp
layout/forms/nsTextControlFrame.cpp
layout/forms/nsTextControlFrame.h
layout/forms/test/Makefile.in
layout/forms/test/test_bug534785.html
layout/generic/nsFrame.cpp
layout/generic/test/test_bug527306.html
layout/reftests/editor/dynamic-type-1.html
layout/reftests/editor/dynamic-type-2.html
layout/reftests/editor/dynamic-type-3.html
layout/reftests/editor/dynamic-type-4.html
layout/reftests/editor/reftest.list
layout/reftests/forms/placeholder/placeholder-18.html
layout/reftests/forms/placeholder/reftest.list
layout/reftests/forms/reftest.list
layout/reftests/forms/textarea-setvalue-framereconstruction-1.html
layout/reftests/forms/textarea-setvalue-framereconstruction-ref.html
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1610,16 +1610,24 @@ public:
    *
    * If one can't be found, a BasicLayerManager is created and returned.
    *
    * @param aDoc the document for which to return a layer manager.
    */
   static already_AddRefed<mozilla::layers::LayerManager>
   LayerManagerForDocument(nsIDocument *aDoc);
 
+  /**
+   * Determine whether a content node is focused or not,
+   *
+   * @param aContent the content node to check
+   * @return true if the content node is focused, false otherwise.
+   */
+  static PRBool IsFocusedContent(nsIContent *aContent);
+
 private:
 
   static PRBool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static nsIDOMScriptObjectFactory *GetDOMScriptObjectFactory();
 
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -173,16 +173,18 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_
 #include "nsIDOMDragEvent.h"
 #include "nsDOMDataTransfer.h"
 #include "nsHtml5Module.h"
 #include "nsPresContext.h"
 #include "nsLayoutStatics.h"
 #include "nsLayoutUtils.h"
 #include "nsFrameManager.h"
 #include "BasicLayers.h"
+#include "nsFocusManager.h"
+#include "nsTextEditorState.h"
 
 #ifdef IBMBIDI
 #include "nsIBidiKeyboard.h"
 #endif
 #include "nsCycleCollectionParticipant.h"
 
 // for ReportToConsole
 #include "nsIStringBundle.h"
@@ -1042,16 +1044,18 @@ nsContentUtils::Shutdown()
                sBlockedScriptRunners->Count() == 0,
                "How'd this happen?");
   delete sBlockedScriptRunners;
   sBlockedScriptRunners = nsnull;
 
   NS_IF_RELEASE(sSameOriginChecker);
   
   nsAutoGCRoot::Shutdown();
+
+  nsTextEditorState::ShutDown();
 }
 
 // static
 PRBool
 nsContentUtils::IsCallerTrustedForCapability(const char* aCapability)
 {
   // The secman really should handle UniversalXPConnect case, since that
   // should include UniversalBrowserRead... doesn't right now, though.
@@ -5948,16 +5952,25 @@ mozAutoRemovableBlockerRemover::~mozAuto
   for (PRUint32 i = 0; i < mNestingLevel; ++i) {
     nsContentUtils::AddRemovableScriptBlocker();
     if (mObserver) {
       mObserver->BeginUpdate(mDocument, UPDATE_CONTENT_MODEL);
     }
   }
 }
 
+// static
+PRBool
+nsContentUtils::IsFocusedContent(nsIContent* aContent)
+{
+  nsFocusManager* fm = nsFocusManager::GetFocusManager();
+
+  return fm && fm->GetFocusedContent() == aContent;
+}
+
 void nsContentUtils::RemoveNewlines(nsString &aString)
 {
   // strip CR/LF and null
   static const char badChars[] = {'\r', '\n', 0};
   aString.StripChars(badChars);
 }
 
 void
--- a/content/html/content/public/nsITextControlElement.h
+++ b/content/html/content/public/nsITextControlElement.h
@@ -35,41 +35,183 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsITextControlElement_h___
 #define nsITextControlElement_h___
 
 #include "nsISupports.h"
+class nsIContent;
 class nsAString;
-class nsITextControlFrame;
+class nsIEditor;
+class nsISelectionController;
+class nsFrameSelection;
+class nsTextControlFrame;
 
 // IID for the nsITextControl interface
 #define NS_ITEXTCONTROLELEMENT_IID    \
-{ 0x8c22af1e, 0x1dd2, 0x11b2,    \
-  { 0x9d, 0x72, 0xb4, 0xc1, 0x53, 0x68, 0xdc, 0xa1 } }
+{ 0x66545dde, 0x3f4a, 0x49fd,    \
+  { 0x82, 0x73, 0x69, 0x7e, 0xab, 0x54, 0x06, 0x0a } }
 
 /**
- * This interface is used for the text control frame to store its value away
- * into the content.
+ * This interface is used for the text control frame to get the editor and
+ * selection controller objects, and some helper properties.
  */
 class nsITextControlElement : public nsISupports {
 public:
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITEXTCONTROLELEMENT_IID)
 
   /**
-   * Set the control's value without security checks
-   */
-  NS_IMETHOD TakeTextFrameValue(const nsAString& aValue) = 0;
-
-  /**
    * Tell the control that value has been deliberately changed (or not).
    */
   NS_IMETHOD SetValueChanged(PRBool changed) = 0;
+
+  /**
+   * Find out whether this is a single line text control.  (text or password)
+   * @return whether this is a single line text control
+   */
+  NS_IMETHOD_(PRBool) IsSingleLineTextControl() const = 0;
+
+  /**
+   * Find out whether this control is a textarea.
+   * @return whether this is a textarea text control
+   */
+  NS_IMETHOD_(PRBool) IsTextArea() const = 0;
+
+  /**
+   * Find out whether this control edits plain text.  (Currently always true.)
+   * @return whether this is a plain text control
+   */
+  NS_IMETHOD_(PRBool) IsPlainTextControl() const = 0;
+
+  /**
+   * Find out whether this is a password control (input type=password)
+   * @return whether this is a password ontrol
+   */
+  NS_IMETHOD_(PRBool) IsPasswordTextControl() const = 0;
+
+  /**
+   * Get the cols attribute (if textarea) or a default
+   * @return the number of columns to use
+   */
+  NS_IMETHOD_(PRInt32) GetCols() = 0;
+
+  /**
+   * Get the column index to wrap at, or -1 if we shouldn't wrap
+   */
+  NS_IMETHOD_(PRInt32) GetWrapCols() = 0;
+
+  /**
+   * Get the rows attribute (if textarea) or a default
+   * @return the number of rows to use
+   */
+  NS_IMETHOD_(PRInt32) GetRows() = 0;
+
+  /**
+   * Get the default value of the text control
+   */
+  NS_IMETHOD_(void) GetDefaultValueFromContent(nsAString& aValue) = 0;
+
+  /**
+   * Return true if the value of the control has been changed.
+   */
+  NS_IMETHOD_(PRBool) ValueChanged() const = 0;
+
+  /**
+   * Get the current value of the text editor.
+   *
+   * @param aValue the buffer to retrieve the value in
+   * @param aIgnoreWrap whether to ignore the text wrapping behavior specified
+   * for the element.
+   */
+  NS_IMETHOD_(void) GetTextEditorValue(nsAString& aValue, PRBool aIgnoreWrap) const = 0;
+
+  /**
+   * Set the current value of the text editor.
+   *
+   * @param aValue the new value for the text control.
+   * @param aUserInput whether this value is coming from user input.
+   */
+  NS_IMETHOD_(void) SetTextEditorValue(const nsAString& aValue, PRBool aUserInput) = 0;
+
+  /**
+   * Get the editor object associated with the text editor.
+   * The return value is null if the control does not support an editor
+   * (for example, if it is a checkbox.)
+   */
+  NS_IMETHOD_(nsIEditor*) GetTextEditor() = 0;
+
+  /**
+   * Get the selection controller object associated with the text editor.
+   * The return value is null if the control does not support an editor
+   * (for example, if it is a checkbox.)
+   */
+  NS_IMETHOD_(nsISelectionController*) GetSelectionController() = 0;
+
+  NS_IMETHOD_(nsFrameSelection*) GetConstFrameSelection() = 0;
+
+  /**
+   * Binds a frame to the text control.  This is performed when a frame
+   * is created for the content node.
+   */
+  NS_IMETHOD BindToFrame(nsTextControlFrame* aFrame) = 0;
+
+  /**
+   * Unbinds a frame from the text control.  This is performed when a frame
+   * belonging to a content node is destroyed.
+   */
+  NS_IMETHOD_(void) UnbindFromFrame(nsTextControlFrame* aFrame) = 0;
+
+  /**
+   * Creates an editor for the text control.  This should happen when
+   * a frame has been created for the text control element, but the created
+   * editor may outlive the frame itself.
+   */
+  NS_IMETHOD CreateEditor() = 0;
+
+  /**
+   * Get the anonymous root node for the text control.
+   */
+  NS_IMETHOD_(nsIContent*) GetRootEditorNode() = 0;
+
+  /**
+   * Get the placeholder anonymous node for the text control.
+   */
+  NS_IMETHOD_(nsIContent*) GetPlaceholderNode() = 0;
+
+  /**
+   * Initialize the keyboard event listeners.
+   */
+  NS_IMETHOD_(void) InitializeKeyboardEventListeners() = 0;
+
+  /**
+   * Notify the text control that the placeholder text needs to be updated.
+   */
+  NS_IMETHOD_(void) UpdatePlaceholderText(PRBool aNotify) = 0;
+
+  /**
+   * Show/hide the placeholder for the control.
+   */
+  NS_IMETHOD_(void) SetPlaceholderClass(PRBool aVisible, PRBool aNotify) = 0;
+
+  static const PRInt32 DEFAULT_COLS = 20;
+  static const PRInt32 DEFAULT_ROWS = 1;
+  static const PRInt32 DEFAULT_ROWS_TEXTAREA = 2;
+  static const PRInt32 DEFAULT_UNDO_CAP = 1000;
+
+  // wrap can be one of these three values.  
+  typedef enum {
+    eHTMLTextWrap_Off     = 1,    // "off"
+    eHTMLTextWrap_Hard    = 2,    // "hard"
+    eHTMLTextWrap_Soft    = 3     // the default
+  } nsHTMLTextWrap;
+
+  static PRBool
+  GetWrapPropertyEnum(nsIContent* aContent, nsHTMLTextWrap& aWrapProp);
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsITextControlElement,
                               NS_ITEXTCONTROLELEMENT_IID)
 
 #endif // nsITextControlElement_h___
 
--- a/content/html/content/src/Makefile.in
+++ b/content/html/content/src/Makefile.in
@@ -53,16 +53,17 @@ EXPORTS		= \
 		$(NULL)
 
 CPPSRCS		= \
 		nsClientRect.cpp \
 		nsHTMLDNSPrefetch.cpp \
 		nsGenericHTMLElement.cpp \
 		nsFormSubmission.cpp \
 		nsImageMapUtils.cpp \
+		nsTextEditorState.cpp \
 		nsHTMLAnchorElement.cpp \
 		nsHTMLAreaElement.cpp \
 		nsHTMLBRElement.cpp \
 		nsHTMLBodyElement.cpp \
 		nsHTMLButtonElement.cpp \
 		nsHTMLCanvasElement.cpp \
 		nsHTMLDelElement.cpp \
 		nsHTMLDivElement.cpp \
@@ -119,16 +120,17 @@ endif
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
 INCLUDES	+= \
 		-I$(srcdir)/../../../base/src \
 		-I$(srcdir)/../../../events/src \
 		-I$(srcdir)/../../../xbl/src \
+		-I$(srcdir)/../../../../layout/forms \
 		-I$(srcdir)/../../../../layout/style \
 		-I$(srcdir)/../../../../layout/tables \
 		-I$(srcdir)/../../../../layout/xul/base/src \
 		-I$(srcdir)/../../../../layout/generic \
 		-I$(srcdir)/../../../../dom/base \
 		-I$(srcdir)/../../../../editor/libeditor/base \
 		-I$(srcdir)/../../../../editor/libeditor/text \
 		-I$(srcdir) \
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -105,16 +105,17 @@
 
 #include "nsIEditor.h"
 #include "nsIEditorIMESupport.h"
 #include "nsEventDispatcher.h"
 #include "nsLayoutUtils.h"
 #include "nsContentCreatorFunctions.h"
 #include "mozAutoDocUpdate.h"
 #include "nsHtml5Module.h"
+#include "nsITextControlElement.h"
 
 #include "nsThreadUtils.h"
 
 class nsINodeInfo;
 class nsIDOMNodeList;
 class nsRuleWalker;
 
 // XXX todo: add in missing out-of-memory checks
@@ -2703,23 +2704,29 @@ PRBool
 nsGenericHTMLFormElement::IsTextControl(PRBool aExcludePassword) const
 {
   PRInt32 type = GetType();
   return nsGenericHTMLFormElement::IsSingleLineTextControl(aExcludePassword) ||
          type == NS_FORM_TEXTAREA;
 }
 
 PRBool
+nsGenericHTMLFormElement::IsSingleLineTextControlInternal(PRBool aExcludePassword,
+                                                          PRInt32 aType) const
+{
+  return aType == NS_FORM_INPUT_TEXT ||
+         aType == NS_FORM_INPUT_SEARCH ||
+         aType == NS_FORM_INPUT_TEL ||
+         (!aExcludePassword && aType == NS_FORM_INPUT_PASSWORD);
+}
+
+PRBool
 nsGenericHTMLFormElement::IsSingleLineTextControl(PRBool aExcludePassword) const
 {
-  PRInt32 type = GetType();
-  return type == NS_FORM_INPUT_TEXT ||
-         type == NS_FORM_INPUT_SEARCH ||
-         type == NS_FORM_INPUT_TEL ||
-         (!aExcludePassword && type == NS_FORM_INPUT_PASSWORD);
+  return IsSingleLineTextControlInternal(aExcludePassword, GetType());
 }
 
 PRBool
 nsGenericHTMLFormElement::IsLabelableControl() const
 {
   // Check for non-labelable form controls as they are not numerous.
   // TODO: datalist should be added to this list.
   PRInt32 type = GetType();
@@ -3161,22 +3168,20 @@ nsGenericHTMLElement::GetEditor(nsIEdito
   return GetEditorInternal(aEditor);
 }
 
 nsresult
 nsGenericHTMLElement::GetEditorInternal(nsIEditor** aEditor)
 {
   *aEditor = nsnull;
 
-  nsIFormControlFrame *fcFrame = GetFormControlFrame(PR_FALSE);
-  if (fcFrame) {
-    nsITextControlFrame *textFrame = do_QueryFrame(fcFrame);
-    if (textFrame) {
-      return textFrame->GetEditor(aEditor);
-    }
+  nsCOMPtr<nsITextControlElement> textCtrl = do_QueryInterface(this);
+  if (textCtrl) {
+    *aEditor = textCtrl->GetTextEditor();
+    NS_IF_ADDREF(*aEditor);
   }
 
   return NS_OK;
 }
 
 already_AddRefed<nsIEditor>
 nsGenericHTMLElement::GetAssociatedEditor()
 {
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -858,16 +858,18 @@ protected:
 
   /**
    * Returns true if the control can be disabled
    */
   PRBool CanBeDisabled() const;
 
   void UpdateEditableFormControlState();
 
+  PRBool IsSingleLineTextControlInternal(PRBool aExcludePassword, PRInt32 mType) const;
+
   // The focusability state of this form control.  eUnfocusable means that it
   // shouldn't be focused at all, eInactiveWindow means it's in an inactive
   // window, eActiveWindow means it's in an active window.
   enum FocusTristate {
     eUnfocusable,
     eInactiveWindow,
     eActiveWindow
   };
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -107,16 +107,18 @@
 #include "nsImageLoadingContent.h"
 #include "nsIDOMWindowInternal.h"
 
 #include "mozAutoDocUpdate.h"
 #include "nsHTMLFormElement.h"
 
 #include "nsTextEditRules.h"
 
+#include "nsTextEditorState.h"
+
 // XXX align=left, hspace, vspace, border? other nav4 attrs
 
 static NS_DEFINE_CID(kXULControllersCID,  NS_XULCONTROLLERS_CID);
 static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
 
 //
 // Accessors for mBitField
 //
@@ -303,19 +305,40 @@ public:
   virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
                               PRBool aNullParent = PR_TRUE);
 
   virtual void DoneCreatingElement();
 
   virtual PRInt32 IntrinsicState() const;
 
   // nsITextControlElement
-  NS_IMETHOD TakeTextFrameValue(const nsAString& aValue);
   NS_IMETHOD SetValueChanged(PRBool aValueChanged);
-  
+  NS_IMETHOD_(PRBool) IsSingleLineTextControl() const;
+  NS_IMETHOD_(PRBool) IsTextArea() const;
+  NS_IMETHOD_(PRBool) IsPlainTextControl() const;
+  NS_IMETHOD_(PRBool) IsPasswordTextControl() const;
+  NS_IMETHOD_(PRInt32) GetCols();
+  NS_IMETHOD_(PRInt32) GetWrapCols();
+  NS_IMETHOD_(PRInt32) GetRows();
+  NS_IMETHOD_(void) GetDefaultValueFromContent(nsAString& aValue);
+  NS_IMETHOD_(PRBool) ValueChanged() const;
+  NS_IMETHOD_(void) GetTextEditorValue(nsAString& aValue, PRBool aIgnoreWrap) const;
+  NS_IMETHOD_(void) SetTextEditorValue(const nsAString& aValue, PRBool aUserInput);
+  NS_IMETHOD_(nsIEditor*) GetTextEditor();
+  NS_IMETHOD_(nsISelectionController*) GetSelectionController();
+  NS_IMETHOD_(nsFrameSelection*) GetConstFrameSelection();
+  NS_IMETHOD BindToFrame(nsTextControlFrame* aFrame);
+  NS_IMETHOD_(void) UnbindFromFrame(nsTextControlFrame* aFrame);
+  NS_IMETHOD CreateEditor();
+  NS_IMETHOD_(nsIContent*) GetRootEditorNode();
+  NS_IMETHOD_(nsIContent*) GetPlaceholderNode();
+  NS_IMETHOD_(void) UpdatePlaceholderText(PRBool aNotify);
+  NS_IMETHOD_(void) SetPlaceholderClass(PRBool aVisible, PRBool aNotify);
+  NS_IMETHOD_(void) InitializeKeyboardEventListeners();
+
   // nsIFileControlElement
   virtual void GetDisplayFileName(nsAString& aFileName);
   virtual void GetFileArray(nsCOMArray<nsIFile> &aFile);
   virtual void SetFileNames(const nsTArray<nsString>& aFileNames);
 
   // nsIRadioControlElement
   NS_IMETHOD RadioSetChecked(PRBool aNotify);
   NS_IMETHOD SetCheckedChanged(PRBool aCheckedChanged);
@@ -336,19 +359,22 @@ public:
     return UpdateEditableFormControlState();
   }
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsHTMLInputElement,
                                                      nsGenericHTMLFormElement)
 
   void MaybeLoadImage();
 protected:
+  // Pull IsSingleLineTextControl into our scope, otherwise it'd be hidden
+  // by the nsITextControlElement version.
+  using nsGenericHTMLFormElement::IsSingleLineTextControl;
+
   // Helper method
   nsresult SetValueInternal(const nsAString& aValue,
-                            nsITextControlFrame* aFrame,
                             PRBool aUserInput);
 
   void ClearFileNames() {
     nsTArray<nsString> fileNames;
     SetFileNames(fileNames);
   }
 
   void SetSingleFileName(const nsAString& aFileName) {
@@ -450,32 +476,49 @@ protected:
   nsresult UpdateFileList();
 
   /**
    * Determine whether the editor needs to be initialized explicitly for
    * a particular event.
    */
   PRBool NeedToInitializeEditorForEvent(nsEventChainPreVisitor& aVisitor) const;
 
+  void FreeData();
+  nsTextEditorState *GetEditorState() const;
+
   nsCOMPtr<nsIControllers> mControllers;
 
   /**
    * The type of this input (<input type=...>) as an integer.
    * @see nsIFormControl.h (specifically NS_FORM_INPUT_*)
    */
   PRUint8                  mType;
   /**
    * A bitfield containing our booleans
    * @see GET_BOOLBIT / SET_BOOLBIT macros and BF_* field identifiers
    */
   PRInt16                  mBitField;
-  /**
-   * The current value of the input if it has been changed from the default
+  /*
+   * In mInputData, the mState field is used if IsSingleLineTextControl returns
+   * true and mValue is used otherwise.  We have to be careful when handling it
+   * on a type change.
+   *
+   * Accessing the mState member should be done using the GetEditorState function,
+   * which returns null if the state is not present.
    */
-  char*                    mValue;
+  union InputData {
+    /**
+     * The current value of the input if it has been changed from the default
+     */
+    char*                    mValue;
+    /**
+     * The state of the text editor associated with the text/password input
+     */
+    nsTextEditorState*       mState;
+  } mInputData;
   /**
    * The value of the input if it is a file input. This is the list of filenames
    * used when uploading a file. It is vital that this is kept separate from
    * mValue so that it won't be possible to 'leak' the value from a text-input
    * to a file-input. Additionally, the logic for this value is kept as simple
    * as possible to avoid accidental errors where the wrong filename is used.
    * Therefor the list of filenames is always owned by this member, never by
    * the frame. Whenever the frame wants to change the filename it has to call
@@ -498,37 +541,63 @@ static nsresult FireEventForAccessibilit
 //
 
 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Input)
 
 nsHTMLInputElement::nsHTMLInputElement(nsINodeInfo *aNodeInfo,
                                        PRBool aFromParser)
   : nsGenericHTMLFormElement(aNodeInfo),
     mType(kInputDefaultType->value),
-    mBitField(0),
-    mValue(nsnull)
+    mBitField(0)
 {
   SET_BOOLBIT(mBitField, BF_PARSER_CREATING, aFromParser);
+  mInputData.mState = new nsTextEditorState(this);
+  NS_ADDREF(mInputData.mState);
 }
 
 nsHTMLInputElement::~nsHTMLInputElement()
 {
   DestroyImageLoadingContent();
-  if (mValue) {
-    nsMemory::Free(mValue);
+  FreeData();
+}
+
+void
+nsHTMLInputElement::FreeData()
+{
+  if (!IsSingleLineTextControl(PR_FALSE)) {
+    nsMemory::Free(mInputData.mValue);
+    mInputData.mValue = nsnull;
+  } else {
+    NS_IF_RELEASE(mInputData.mState);
   }
 }
 
+nsTextEditorState*
+nsHTMLInputElement::GetEditorState() const
+{
+  if (!IsSingleLineTextControl(PR_FALSE)) {
+    return nsnull;
+  }
+
+  NS_ASSERTION(mInputData.mState,
+    "Single line text controls need to have a state associated with them");
+
+  return mInputData.mState;
+}
+
 
 // nsISupports
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLInputElement)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLInputElement,
                                                   nsGenericHTMLFormElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mControllers)
+  if (tmp->IsSingleLineTextControl(PR_FALSE)) {
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mInputData.mState, nsTextEditorState)
+  }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ADDREF_INHERITED(nsHTMLInputElement, nsGenericElement) 
 NS_IMPL_RELEASE_INHERITED(nsHTMLInputElement, nsGenericElement) 
 
 
 DOMCI_DATA(HTMLInputElement, nsHTMLInputElement)
 
@@ -573,17 +642,17 @@ nsHTMLInputElement::Clone(nsINodeInfo *a
     case NS_FORM_INPUT_TEL:
       if (GET_BOOLBIT(mBitField, BF_VALUE_CHANGED)) {
         // We don't have our default value anymore.  Set our value on
         // the clone.
         // XXX GetValue should be const
         nsAutoString value;
         const_cast<nsHTMLInputElement*>(this)->GetValue(value);
         // SetValueInternal handles setting the VALUE_CHANGED bit for us
-        it->SetValueInternal(value, nsnull, PR_FALSE);
+        it->SetValueInternal(value, PR_FALSE);
       }
       break;
     case NS_FORM_INPUT_FILE:
       it->mFileNames = mFileNames;
       break;
     case NS_FORM_INPUT_RADIO:
     case NS_FORM_INPUT_CHECKBOX:
       if (GET_BOOLBIT(mBitField, BF_CHECKED_CHANGED)) {
@@ -702,30 +771,27 @@ nsHTMLInputElement::AfterSetAttr(PRInt32
 
       if (!aValue) {
         // We're now a text input.  Note that we have to handle this manually,
         // since removing an attribute (which is what happened, since aValue is
         // null) doesn't call ParseAttribute.
         mType = kInputDefaultType->value;
       }
     
-      // If we are changing type from File/Text/Tel/Passwd
-      // to other input types we need save the mValue into value attribute
-      if (mValue &&
+      // If we are changing type from File/Text/Tel/Passwd to other input types
+      // we need save the mValue into value attribute
+      if (mInputData.mValue &&
           mType != NS_FORM_INPUT_TEXT &&
           mType != NS_FORM_INPUT_SEARCH &&
           mType != NS_FORM_INPUT_PASSWORD &&
           mType != NS_FORM_INPUT_TEL &&
           mType != NS_FORM_INPUT_FILE) {
         SetAttr(kNameSpaceID_None, nsGkAtoms::value,
-                NS_ConvertUTF8toUTF16(mValue), PR_FALSE);
-        if (mValue) {
-          nsMemory::Free(mValue);
-          mValue = nsnull;
-        }
+                NS_ConvertUTF8toUTF16(mInputData.mValue), PR_FALSE);
+        FreeData();
       }
 
       if (mType != NS_FORM_INPUT_IMAGE) {
         // We're no longer an image input.  Cancel our image requests, if we have
         // any.  Note that doing this when we already weren't an image is ok --
         // just does nothing.
         CancelImageRequests(aNotify);
       } else if (aNotify) {
@@ -884,50 +950,19 @@ nsHTMLInputElement::SetSize(PRUint32 aVa
   val.AppendInt(aValue);
 
   return SetAttr(kNameSpaceID_None, nsGkAtoms::size, val, PR_TRUE);
 }
 
 NS_IMETHODIMP 
 nsHTMLInputElement::GetValue(nsAString& aValue)
 {
-  if (IsSingleLineTextControl(PR_FALSE)) {
-    // No need to flush here, if there's no frame created for this
-    // input yet, there won't be a value in it (that we don't already
-    // have) even if we force it to be created
-    nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_FALSE);
-
-    PRBool frameOwnsValue = PR_FALSE;
-    if (formControlFrame) {
-      nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
-      if (textControlFrame) {
-        textControlFrame->OwnsValue(&frameOwnsValue);
-      } else {
-        // We assume if it's not a text control frame that it owns the value
-        frameOwnsValue = PR_TRUE;
-      }
-    }
-
-    if (frameOwnsValue) {
-      formControlFrame->GetFormProperty(nsGkAtoms::value, aValue);
-    } else {
-      if (!GET_BOOLBIT(mBitField, BF_VALUE_CHANGED) || !mValue) {
-        GetDefaultValue(aValue);
-      } else {
-        CopyUTF8toUTF16(mValue, aValue);
-      }
-
-      // If the value is not owned by the frame, then we should handle any
-      // exiting newline characters inside it, instead of relying on the
-      // editor to do it for us.
-      nsString value(aValue);
-      nsTextEditRules::HandleNewLines(value, -1);
-      aValue.Assign(value);
-    }
-
+  nsTextEditorState* state = GetEditorState();
+  if (state) {
+    state->GetValue(aValue, PR_TRUE);
     return NS_OK;
   }
 
   if (mType == NS_FORM_INPUT_FILE) {
     if (nsContentUtils::IsCallerTrustedForCapability("UniversalFileRead")) {
       if (!mFileNames.IsEmpty()) {
         aValue = mFileNames[0];
       }
@@ -974,17 +1009,17 @@ nsHTMLInputElement::SetValue(const nsASt
       }
       SetSingleFileName(aValue);
     }
     else {
       ClearFileNames();
     }
   }
   else {
-    SetValueInternal(aValue, nsnull, PR_FALSE);
+    SetValueInternal(aValue, PR_FALSE);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 nsHTMLInputElement::MozGetFileNameArray(PRUint32 *aLength, PRUnichar ***aFileNames)
 {
@@ -1040,31 +1075,109 @@ nsHTMLInputElement::SetUserInput(const n
   if (!nsContentUtils::IsCallerTrustedForWrite()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   if (mType == NS_FORM_INPUT_FILE)
   {
     SetSingleFileName(aValue);
   } else {
-    SetValueInternal(aValue, nsnull, PR_TRUE);
+    SetValueInternal(aValue, PR_TRUE);
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP_(nsIEditor*)
+nsHTMLInputElement::GetTextEditor()
+{
+  nsTextEditorState *state = GetEditorState();
+  if (state) {
+    return state->GetEditor();
+  return nsnull;
+}
+
+NS_IMETHODIMP_(nsISelectionController*)
+nsHTMLInputElement::GetSelectionController()
+{
+  nsTextEditorState *state = GetEditorState();
+  if (state) {
+    return state->GetSelectionController();
+  return nsnull;
+}
+
+nsFrameSelection*
+nsHTMLInputElement::GetConstFrameSelection()
+{
+  nsTextEditorState *state = GetEditorState();
+  if (state) {
+    return state->GetConstFrameSelection();
+  return nsnull;
+}
+
 NS_IMETHODIMP
-nsHTMLInputElement::TakeTextFrameValue(const nsAString& aValue)
+nsHTMLInputElement::BindToFrame(nsTextControlFrame* aFrame)
 {
-  if (mValue) {
-    nsMemory::Free(mValue);
+  nsTextEditorState *state = GetEditorState();
+  if (state) {
+    return state->BindToFrame(aFrame);
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP_(void)
+nsHTMLInputElement::UnbindFromFrame(nsTextControlFrame* aFrame)
+{
+  nsTextEditorState *state = GetEditorState();
+  if (state) {
+    state->UnbindFromFrame(aFrame);
   }
-  nsString value(aValue);
-  nsContentUtils::PlatformToDOMLineBreaks(value);
-  mValue = ToNewUTF8String(value);
-  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHTMLInputElement::CreateEditor()
+{
+  nsTextEditorState *state = GetEditorState();
+  if (state) {
+    return state->PrepareEditor();
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP_(nsIContent*)
+nsHTMLInputElement::GetRootEditorNode()
+{
+  nsTextEditorState *state = GetEditorState();
+  if (state) {
+    return state->GetRootNode();
+  return nsnull;
+}
+
+NS_IMETHODIMP_(nsIContent*)
+nsHTMLInputElement::GetPlaceholderNode()
+{
+  nsTextEditorState *state = GetEditorState();
+  if (state) {
+    return state->GetPlaceholderNode();
+  return nsnull;
+}
+
+NS_IMETHODIMP_(void)
+nsHTMLInputElement::UpdatePlaceholderText(PRBool aNotify)
+{
+  nsTextEditorState *state = GetEditorState();
+  if (state) {
+    state->UpdatePlaceholderText(aNotify);
+  }
+}
+
+NS_IMETHODIMP_(void)
+nsHTMLInputElement::SetPlaceholderClass(PRBool aVisible, PRBool aNotify)
+{
+  nsTextEditorState *state = GetEditorState();
+  if (state) {
+    state->SetPlaceholderClass(aVisible, aNotify);
+  }
 }
 
 void
 nsHTMLInputElement::GetDisplayFileName(nsAString& aValue)
 {
   aValue.Truncate();
   for (PRUint32 i = 0; i < mFileNames.Length(); ++i) {
     if (i == 0) {
@@ -1153,42 +1266,29 @@ nsHTMLInputElement::UpdateFileList()
     }
   }
 
   return NS_OK;
 }
 
 nsresult
 nsHTMLInputElement::SetValueInternal(const nsAString& aValue,
-                                     nsITextControlFrame* aFrame,
                                      PRBool aUserInput)
 {
   NS_PRECONDITION(mType != NS_FORM_INPUT_FILE,
                   "Don't call SetValueInternal for file inputs");
 
   if (IsSingleLineTextControl(PR_FALSE)) {
-    nsIFormControlFrame* formControlFrame = aFrame;
-    if (!formControlFrame) {
-      // No need to flush here, if there's no frame at this point we
-      // don't need to force creation of one just to tell it about this
-      // new value.
-      formControlFrame = GetFormControlFrame(PR_FALSE);
-    }
-
-    if (formControlFrame) {
-      // Always set the value in the frame.  If the frame does not own the
-      // value yet (per OwnsValue()), it will turn around and call
-      // TakeTextFrameValue() on us, but will update its display with the new
-      // value if needed.
-      return formControlFrame->SetFormProperty(
-        aUserInput ? nsGkAtoms::userInput : nsGkAtoms::value, aValue);
-    }
-
+    // Need to set the value changed flag here, so that
+    // nsTextControlFrame::UpdateValueDisplay retrieves the correct value
+    // if needed.
     SetValueChanged(PR_TRUE);
-    return TakeTextFrameValue(aValue);
+    mInputData.mState->SetValue(aValue, aUserInput);
+
+    return NS_OK;
   }
 
   if (mType == NS_FORM_INPUT_FILE) {
     return NS_ERROR_UNEXPECTED;
   }
 
   // If the value of a hidden input was changed, we mark it changed so that we
   // will know we need to save / restore the value.  Yes, we are overloading
@@ -1205,19 +1305,18 @@ nsHTMLInputElement::SetValueInternal(con
                                            PR_TRUE);
 }
 
 NS_IMETHODIMP
 nsHTMLInputElement::SetValueChanged(PRBool aValueChanged)
 {
   SET_BOOLBIT(mBitField, BF_VALUE_CHANGED, aValueChanged);
   if (!aValueChanged) {
-    if (mValue) {
-      nsMemory::Free(mValue);
-      mValue = nsnull;
+    if (!IsSingleLineTextControl(PR_FALSE)) {
+      FreeData();
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 nsHTMLInputElement::GetChecked(PRBool* aChecked)
 {
@@ -2276,16 +2375,29 @@ nsHTMLInputElement::ParseAttribute(PRInt
         // SetAttr() process).
         if (newType == NS_FORM_INPUT_FILE || mType == NS_FORM_INPUT_FILE) {
           // This call isn't strictly needed any more since we'll never
           // confuse values and filenames. However it's there for backwards
           // compat.
           ClearFileNames();
         }
 
+        // Only single line text inputs have a text editor state.
+        PRBool isNewTypeSingleLine =
+          IsSingleLineTextControlInternal(PR_FALSE, newType);
+        PRBool isCurrentTypeSingleLine =
+          IsSingleLineTextControl(PR_FALSE);
+        if (isNewTypeSingleLine && !isCurrentTypeSingleLine) {
+          FreeData();
+          mInputData.mState = new nsTextEditorState(this);
+          NS_ADDREF(mInputData.mState);
+        } else if (isCurrentTypeSingleLine && !isNewTypeSingleLine) {
+          FreeData();
+        }
+
         mType = newType;
       }
 
       return success;
     }
     if (aAttribute == nsGkAtoms::width) {
       return aResult.ParseSpecialIntValue(aValue, PR_TRUE);
     }
@@ -2904,17 +3016,17 @@ nsHTMLInputElement::RestoreState(nsPresS
           break;
         }
 
       case NS_FORM_INPUT_SEARCH:
       case NS_FORM_INPUT_TEXT:
       case NS_FORM_INPUT_TEL:
       case NS_FORM_INPUT_HIDDEN:
         {
-          SetValueInternal(inputState->GetValue(), nsnull, PR_FALSE);
+          SetValueInternal(inputState->GetValue(), PR_FALSE);
           break;
         }
       case NS_FORM_INPUT_FILE:
         {
           SetFileNames(inputState->GetFilenames());
           break;
         }
     }
@@ -3281,8 +3393,103 @@ NS_GetRadioGetCheckedChangedVisitor(PRBo
   if (!*aVisitor) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   NS_ADDREF(*aVisitor);
 
   return NS_OK;
 }
 
+NS_IMETHODIMP_(PRBool)
+nsHTMLInputElement::IsSingleLineTextControl() const
+{
+  return IsSingleLineTextControl(PR_FALSE);
+}
+
+NS_IMETHODIMP_(PRBool)
+nsHTMLInputElement::IsTextArea() const
+{
+  return PR_FALSE;
+}
+
+NS_IMETHODIMP_(PRBool)
+nsHTMLInputElement::IsPlainTextControl() const
+{
+  // need to check our HTML attribute and/or CSS.
+  return PR_TRUE;
+}
+
+NS_IMETHODIMP_(PRBool)
+nsHTMLInputElement::IsPasswordTextControl() const
+{
+  return mType == NS_FORM_INPUT_PASSWORD;
+}
+
+NS_IMETHODIMP_(PRInt32)
+nsHTMLInputElement::GetCols()
+{
+  // Else we know (assume) it is an input with size attr
+  const nsAttrValue* attr = GetParsedAttr(nsGkAtoms::size);
+  if (attr && attr->Type() == nsAttrValue::eInteger) {
+    PRInt32 cols = attr->GetIntegerValue();
+    if (cols > 0) {
+      return cols;
+    }
+  }
+
+  return DEFAULT_COLS;
+}
+
+NS_IMETHODIMP_(PRInt32)
+nsHTMLInputElement::GetWrapCols()
+{
+  return -1; // only textarea's can have wrap cols
+}
+
+NS_IMETHODIMP_(PRInt32)
+nsHTMLInputElement::GetRows()
+{
+  return DEFAULT_ROWS;
+}
+
+NS_IMETHODIMP_(void)
+nsHTMLInputElement::GetDefaultValueFromContent(nsAString& aValue)
+{
+  nsTextEditorState *state = GetEditorState();
+  if (state) {
+    GetDefaultValue(aValue);
+  }
+}
+
+NS_IMETHODIMP_(PRBool)
+nsHTMLInputElement::ValueChanged() const
+{
+  return GET_BOOLBIT(mBitField, BF_VALUE_CHANGED);
+}
+
+NS_IMETHODIMP_(void)
+nsHTMLInputElement::GetTextEditorValue(nsAString& aValue,
+                                       PRBool aIgnoreWrap) const
+{
+  nsTextEditorState *state = GetEditorState();
+  if (state) {
+    state->GetValue(aValue, aIgnoreWrap);
+  }
+}
+
+NS_IMETHODIMP_(void)
+nsHTMLInputElement::SetTextEditorValue(const nsAString& aValue,
+                                       PRBool aUserInput)
+{
+  nsTextEditorState *state = GetEditorState();
+  if (state) {
+    state->SetValue(aValue, aUserInput);
+  }
+}
+
+NS_IMETHODIMP_(void)
+nsHTMLInputElement::InitializeKeyboardEventListeners()
+{
+  nsTextEditorState *state = GetEditorState();
+  if (state) {
+    state->InitializeKeyboardEventListeners();
+  }
+}
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp
+++ b/content/html/content/src/nsHTMLTextAreaElement.cpp
@@ -72,30 +72,31 @@
 #include "nsEventDispatcher.h"
 #include "nsLayoutUtils.h"
 #include "nsLayoutErrors.h"
 #include "nsStubMutationObserver.h"
 #include "nsDOMError.h"
 #include "mozAutoDocUpdate.h"
 #include "nsISupportsPrimitives.h"
 
+#include "nsTextEditorState.h"
+
 static NS_DEFINE_CID(kXULControllersCID,  NS_XULCONTROLLERS_CID);
 
 #define NS_NO_CONTENT_DISPATCH (1 << 0)
 
 class nsHTMLTextAreaElement : public nsGenericHTMLFormElement,
                               public nsIDOMHTMLTextAreaElement,
                               public nsIDOMNSHTMLTextAreaElement,
                               public nsITextControlElement,
                               public nsIDOMNSEditableElement,
                               public nsStubMutationObserver
 {
 public:
   nsHTMLTextAreaElement(nsINodeInfo *aNodeInfo, PRBool aFromParser = PR_FALSE);
-  virtual ~nsHTMLTextAreaElement();
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIDOMNode
   NS_FORWARD_NSIDOMNODE(nsGenericHTMLFormElement::)
 
   // nsIDOMElement
@@ -121,18 +122,39 @@ public:
   NS_IMETHOD_(PRUint32) GetType() const { return NS_FORM_TEXTAREA; }
   NS_IMETHOD Reset();
   NS_IMETHOD SubmitNamesValues(nsFormSubmission* aFormSubmission,
                                nsIContent* aSubmitElement);
   NS_IMETHOD SaveState();
   virtual PRBool RestoreState(nsPresState* aState);
 
   // nsITextControlElemet
-  NS_IMETHOD TakeTextFrameValue(const nsAString& aValue);
   NS_IMETHOD SetValueChanged(PRBool aValueChanged);
+  NS_IMETHOD_(PRBool) IsSingleLineTextControl() const;
+  NS_IMETHOD_(PRBool) IsTextArea() const;
+  NS_IMETHOD_(PRBool) IsPlainTextControl() const;
+  NS_IMETHOD_(PRBool) IsPasswordTextControl() const;
+  NS_IMETHOD_(PRInt32) GetCols();
+  NS_IMETHOD_(PRInt32) GetWrapCols();
+  NS_IMETHOD_(PRInt32) GetRows();
+  NS_IMETHOD_(void) GetDefaultValueFromContent(nsAString& aValue);
+  NS_IMETHOD_(PRBool) ValueChanged() const;
+  NS_IMETHOD_(void) GetTextEditorValue(nsAString& aValue, PRBool aIgnoreWrap) const;
+  NS_IMETHOD_(void) SetTextEditorValue(const nsAString& aValue, PRBool aUserInput);
+  NS_IMETHOD_(nsIEditor*) GetTextEditor();
+  NS_IMETHOD_(nsISelectionController*) GetSelectionController();
+  NS_IMETHOD_(nsFrameSelection*) GetConstFrameSelection();
+  NS_IMETHOD BindToFrame(nsTextControlFrame* aFrame);
+  NS_IMETHOD_(void) UnbindFromFrame(nsTextControlFrame* aFrame);
+  NS_IMETHOD CreateEditor();
+  NS_IMETHOD_(nsIContent*) GetRootEditorNode();
+  NS_IMETHOD_(nsIContent*) GetPlaceholderNode();
+  NS_IMETHOD_(void) UpdatePlaceholderText(PRBool aNotify);
+  NS_IMETHOD_(void) SetPlaceholderClass(PRBool aVisible, PRBool aNotify);
+  NS_IMETHOD_(void) InitializeKeyboardEventListeners();
 
   // nsIContent
   virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult);
   virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const;
   virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
@@ -167,41 +189,42 @@ public:
   {
     return UpdateEditableFormControlState();
   }
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsHTMLTextAreaElement,
                                            nsGenericHTMLFormElement)
 
 protected:
+  using nsGenericHTMLFormElement::IsSingleLineTextControl; // get rid of the compiler warning
+
   nsCOMPtr<nsIControllers> mControllers;
-  /** The current value.  This is null if the frame owns the value. */
-  char*                    mValue;
   /** Whether or not the value has changed since its default value was given. */
   PRPackedBool             mValueChanged;
   /** Whether or not we are already handling select event. */
   PRPackedBool             mHandlingSelect;
   /** Whether or not we are done adding children (always PR_TRUE if not
       created by a parser */
   PRPackedBool             mDoneAddingChildren;
   /** Whether our disabled state has changed from the default **/
   PRPackedBool             mDisabledChanged;
+  /** The state of the text editor (selection controller and the editor) **/
+  nsRefPtr<nsTextEditorState> mState;
   
   NS_IMETHOD SelectAll(nsPresContext* aPresContext);
   /**
    * Get the value, whether it is from the content or the frame.
    * @param aValue the value [out]
    * @param aIgnoreWrap whether to ignore the wrap attribute when getting the
    *        value.  If this is true, linebreaks will not be inserted even if
    *        wrap=hard.
    */
   void GetValueInternal(nsAString& aValue, PRBool aIgnoreWrap);
 
   nsresult SetValueInternal(const nsAString& aValue,
-                            nsITextControlFrame* aFrame,
                             PRBool aUserInput);
   nsresult GetSelectionRange(PRInt32* aSelectionStart, PRInt32* aSelectionEnd);
 
   virtual PRBool AcceptAutofocus() const
   {
     return PR_TRUE;
   }
 
@@ -218,41 +241,35 @@ protected:
 
 
 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(TextArea)
 
 
 nsHTMLTextAreaElement::nsHTMLTextAreaElement(nsINodeInfo *aNodeInfo,
                                              PRBool aFromParser)
   : nsGenericHTMLFormElement(aNodeInfo),
-    mValue(nsnull),
     mValueChanged(PR_FALSE),
     mHandlingSelect(PR_FALSE),
     mDoneAddingChildren(!aFromParser),
-    mDisabledChanged(PR_FALSE)
+    mDisabledChanged(PR_FALSE),
+    mState(new nsTextEditorState(this))
 {
   AddMutationObserver(this);
 }
 
-nsHTMLTextAreaElement::~nsHTMLTextAreaElement()
-{
-  if (mValue) {
-    nsMemory::Free(mValue);
-  }
-}
-
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLTextAreaElement)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHTMLTextAreaElement,
                                                 nsGenericHTMLFormElement)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mControllers)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLTextAreaElement,
                                                   nsGenericHTMLFormElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mControllers)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mState, nsTextEditorState)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ADDREF_INHERITED(nsHTMLTextAreaElement, nsGenericElement) 
 NS_IMPL_RELEASE_INHERITED(nsHTMLTextAreaElement, nsGenericElement) 
 
 
 DOMCI_DATA(HTMLTextAreaElement, nsHTMLTextAreaElement)
 
@@ -392,116 +409,114 @@ nsHTMLTextAreaElement::GetValue(nsAStrin
 {
   GetValueInternal(aValue, PR_TRUE);
   return NS_OK;
 }
 
 void
 nsHTMLTextAreaElement::GetValueInternal(nsAString& aValue, PRBool aIgnoreWrap)
 {
-  // Get the frame.
-  // No need to flush here, if there is no frame yet for this textarea
-  // there won't be a value in it we don't already have even if we
-  // force the frame to be created.
-  nsIFrame* primaryFrame = GetPrimaryFrame();
-  nsITextControlFrame* textControlFrame = nsnull;
-  if (primaryFrame) {
-    textControlFrame = do_QueryFrame(primaryFrame);
-  }
+  mState->GetValue(aValue, aIgnoreWrap);
+}
+
+NS_IMETHODIMP_(nsIEditor*)
+nsHTMLTextAreaElement::GetTextEditor()
+{
+  return mState->GetEditor();
+}
 
-  // If the frame exists and owns the value, get it from the frame.  Otherwise
-  // get it from content.
-  PRBool frameOwnsValue = PR_FALSE;
-  if (textControlFrame) {
-    textControlFrame->OwnsValue(&frameOwnsValue);
-  }
-  if (frameOwnsValue) {
-    textControlFrame->GetValue(aValue, aIgnoreWrap);
-  } else {
-    if (!mValueChanged || !mValue) {
-      GetDefaultValue(aValue);
-    } else {
-      CopyUTF8toUTF16(mValue, aValue);
-    }
-  }
+NS_IMETHODIMP_(nsISelectionController*)
+nsHTMLTextAreaElement::GetSelectionController()
+{
+  return mState->GetSelectionController();
+}
+
+NS_IMETHODIMP_(nsFrameSelection*)
+nsHTMLTextAreaElement::GetConstFrameSelection()
+{
+  return mState->GetConstFrameSelection();
 }
 
 NS_IMETHODIMP
-nsHTMLTextAreaElement::TakeTextFrameValue(const nsAString& aValue)
+nsHTMLTextAreaElement::BindToFrame(nsTextControlFrame* aFrame)
+{
+  return mState->BindToFrame(aFrame);
+}
+
+NS_IMETHODIMP_(void)
+nsHTMLTextAreaElement::UnbindFromFrame(nsTextControlFrame* aFrame)
+{
+  mState->UnbindFromFrame(aFrame);
+}
+
+NS_IMETHODIMP
+nsHTMLTextAreaElement::CreateEditor()
+{
+  return mState->PrepareEditor();
+}
+
+NS_IMETHODIMP_(nsIContent*)
+nsHTMLTextAreaElement::GetRootEditorNode()
 {
-  if (mValue) {
-    nsMemory::Free(mValue);
-  }
-  nsString value(aValue);
-  nsContentUtils::PlatformToDOMLineBreaks(value);
-  mValue = ToNewUTF8String(value);
-  return NS_OK;
+  return mState->GetRootNode();
+}
+
+NS_IMETHODIMP_(nsIContent*)
+nsHTMLTextAreaElement::GetPlaceholderNode()
+{
+  return mState->GetPlaceholderNode();
+}
+
+NS_IMETHODIMP_(void)
+nsHTMLTextAreaElement::UpdatePlaceholderText(PRBool aNotify)
+{
+  mState->UpdatePlaceholderText(aNotify);
+}
+
+NS_IMETHODIMP_(void)
+nsHTMLTextAreaElement::SetPlaceholderClass(PRBool aVisible, PRBool aNotify)
+{
+  mState->SetPlaceholderClass(aVisible, aNotify);
 }
 
 nsresult
 nsHTMLTextAreaElement::SetValueInternal(const nsAString& aValue,
-                                        nsITextControlFrame* aFrame,
                                         PRBool aUserInput)
 {
-  nsITextControlFrame* textControlFrame = aFrame;
-  nsIFormControlFrame* formControlFrame = textControlFrame;
-  if (!textControlFrame) {
-    // No need to flush here, if there is no frame for this yet forcing
-    // creation of one will not do us any good
-    formControlFrame = GetFormControlFrame(PR_FALSE);
-
-    if (formControlFrame) {
-      textControlFrame = do_QueryFrame(formControlFrame);
-    }
-  }
-
-  PRBool frameOwnsValue = PR_FALSE;
-  if (textControlFrame) {
-    textControlFrame->OwnsValue(&frameOwnsValue);
-  }
-  if (frameOwnsValue) {
-    formControlFrame->SetFormProperty(
-      aUserInput ? nsGkAtoms::userInput : nsGkAtoms::value, aValue);
-  }
-  else {
-    if (mValue) {
-      nsMemory::Free(mValue);
-    }
-    mValue = ToNewUTF8String(aValue);
-    NS_ENSURE_TRUE(mValue, NS_ERROR_OUT_OF_MEMORY);
-
-    SetValueChanged(PR_TRUE);
-  }
+  // Need to set the value changed flag here, so that
+  // nsTextControlFrame::UpdateValueDisplay retrieves the correct value
+  // if needed.
+  SetValueChanged(PR_TRUE);
+  mState->SetValue(aValue, aUserInput);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 nsHTMLTextAreaElement::SetValue(const nsAString& aValue)
 {
-  return SetValueInternal(aValue, nsnull, PR_FALSE);
+  return SetValueInternal(aValue, PR_FALSE);
 }
 
 NS_IMETHODIMP 
 nsHTMLTextAreaElement::SetUserInput(const nsAString& aValue)
 {
   if (!nsContentUtils::IsCallerTrustedForWrite()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
-  SetValueInternal(aValue, nsnull, PR_TRUE);
+  SetValueInternal(aValue, PR_TRUE);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLTextAreaElement::SetValueChanged(PRBool aValueChanged)
 {
   mValueChanged = aValueChanged;
-  if (!aValueChanged && mValue) {
-    nsMemory::Free(mValue);
-    mValue = nsnull;
+  if (!aValueChanged && !mState->IsEmpty()) {
+    mState->EmptyValue();
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLTextAreaElement::GetDefaultValue(nsAString& aDefaultValue)
 {
   nsContentUtils::GetNodeTextContent(this, PR_FALSE, aDefaultValue);
@@ -998,8 +1013,106 @@ nsHTMLTextAreaElement::CopyInnerTo(nsGen
   if (aDest->GetOwnerDoc()->IsStaticDocument()) {
     nsAutoString value;
     const_cast<nsHTMLTextAreaElement*>(this)->GetValue(value);
     static_cast<nsHTMLTextAreaElement*>(aDest)->SetValue(value);
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP_(PRBool)
+nsHTMLTextAreaElement::IsSingleLineTextControl() const
+{
+  return PR_FALSE;
+}
+
+NS_IMETHODIMP_(PRBool)
+nsHTMLTextAreaElement::IsTextArea() const
+{
+  return PR_TRUE;
+}
+
+NS_IMETHODIMP_(PRBool)
+nsHTMLTextAreaElement::IsPlainTextControl() const
+{
+  // need to check our HTML attribute and/or CSS.
+  return PR_TRUE;
+}
+
+NS_IMETHODIMP_(PRBool)
+nsHTMLTextAreaElement::IsPasswordTextControl() const
+{
+  return PR_FALSE;
+}
+
+NS_IMETHODIMP_(PRInt32)
+nsHTMLTextAreaElement::GetCols()
+{
+  const nsAttrValue* attr = GetParsedAttr(nsGkAtoms::cols);
+  if (attr) {
+    PRInt32 cols = attr->Type() == nsAttrValue::eInteger ?
+                   attr->GetIntegerValue() : 0;
+    // XXX why a default of 1 char, why hide it
+    return (cols <= 0) ? 1 : cols;
+  }
+
+  return DEFAULT_COLS;
+}
+
+NS_IMETHODIMP_(PRInt32)
+nsHTMLTextAreaElement::GetWrapCols()
+{
+  // wrap=off means -1 for wrap width no matter what cols is
+  nsHTMLTextWrap wrapProp;
+  nsITextControlElement::GetWrapPropertyEnum(this, wrapProp);
+  if (wrapProp == nsITextControlElement::eHTMLTextWrap_Off) {
+    // do not wrap when wrap=off
+    return -1;
+  }
+
+  // Otherwise we just wrap at the given number of columns
+  return GetCols();
+}
+
+
+NS_IMETHODIMP_(PRInt32)
+nsHTMLTextAreaElement::GetRows()
+{
+  const nsAttrValue* attr = GetParsedAttr(nsGkAtoms::rows);
+  if (attr && attr->Type() == nsAttrValue::eInteger) {
+    PRInt32 rows = attr->GetIntegerValue();
+    return (rows <= 0) ? DEFAULT_ROWS_TEXTAREA : rows;
+  }
+
+  return DEFAULT_ROWS_TEXTAREA;
+}
+
+NS_IMETHODIMP_(void)
+nsHTMLTextAreaElement::GetDefaultValueFromContent(nsAString& aValue)
+{
+  GetDefaultValue(aValue);
+}
+
+NS_IMETHODIMP_(PRBool)
+nsHTMLTextAreaElement::ValueChanged() const
+{
+  return mValueChanged;
+}
+
+NS_IMETHODIMP_(void)
+nsHTMLTextAreaElement::GetTextEditorValue(nsAString& aValue,
+                                          PRBool aIgnoreWrap) const
+{
+  mState->GetValue(aValue, aIgnoreWrap);
+}
+
+NS_IMETHODIMP_(void)
+nsHTMLTextAreaElement::SetTextEditorValue(const nsAString& aValue,
+                                          PRBool aUserInput)
+{
+  mState->SetValue(aValue, aUserInput);
+}
+
+NS_IMETHODIMP_(void)
+nsHTMLTextAreaElement::InitializeKeyboardEventListeners()
+{
+  mState->InitializeKeyboardEventListeners();
+}
new file mode 100644
--- /dev/null
+++ b/content/html/content/src/nsTextEditorState.cpp
@@ -0,0 +1,1890 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 et tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla.org client code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ehsan Akhgari <ehsan@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 "nsCOMPtr.h"
+#include "nsIPresShell.h"
+#include "nsIView.h"
+#include "nsCaret.h"
+#include "nsEditorCID.h"
+#include "nsLayoutCID.h"
+#include "nsITextControlFrame.h" 
+#include "nsIPlaintextEditor.h"
+#include "nsIDOMDocument.h"
+#include "nsContentCreatorFunctions.h"
+#include "nsTextControlFrame.h"
+#include "nsIControllers.h"
+#include "nsIDOMNSHTMLInputElement.h"
+#include "nsIDOMNSHTMLTextAreaElement.h"
+#include "nsITransactionManager.h"
+#include "nsIControllerContext.h"
+#include "nsAttrValue.h"
+#include "nsGenericHTMLElement.h"
+#include "nsIDOMKeyListener.h"
+#include "nsIEditorObserver.h"
+#include "nsINativeKeyBindings.h"
+#include "nsIDocumentEncoder.h"
+#include "nsISelectionPrivate.h"
+#include "nsPIDOMWindow.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIDOMEventGroup.h"
+#include "nsIEditor.h"
+#include "nsTextEditRules.h"
+
+#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;
+
+/*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
+  // do anything different.
+  aWrapProp = eHTMLTextWrap_Soft; // the default
+
+  nsAutoString wrap;
+  if (aContent->IsHTML()) {
+    static nsIContent::AttrValuesArray strings[] =
+      {&nsGkAtoms::HARD, &nsGkAtoms::OFF, nsnull};
+
+    switch (aContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::wrap,
+                                      strings, eIgnoreCase)) {
+      case 0: aWrapProp = eHTMLTextWrap_Hard; break;
+      case 1: aWrapProp = eHTMLTextWrap_Off; break;
+    }
+
+    return PR_TRUE;
+  }
+
+  return PR_FALSE;
+}
+
+static PRBool
+SuppressEventHandlers(nsPresContext* aPresContext)
+{
+  PRBool suppressHandlers = PR_FALSE;
+
+  if (aPresContext)
+  {
+    // Right now we only suppress event handlers and controller manipulation
+    // when in a print preview or print context!
+
+    // In the current implementation, we only paginate when
+    // printing or in print preview.
+
+    suppressHandlers = aPresContext->IsPaginated();
+  }
+
+  return suppressHandlers;
+}
+
+class nsAnonDivObserver : public nsStubMutationObserver
+{
+public:
+  nsAnonDivObserver(nsTextEditorState* aTextEditorState)
+  : mTextEditorState(aTextEditorState) {}
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
+  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
+  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
+  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
+
+private:
+  nsTextEditorState* mTextEditorState;
+};
+
+class nsTextInputSelectionImpl : public nsSupportsWeakReference
+                               , public nsISelectionController
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsTextInputSelectionImpl, nsISelectionController)
+
+  nsTextInputSelectionImpl(nsFrameSelection *aSel, nsIPresShell *aShell, nsIContent *aLimiter);
+  ~nsTextInputSelectionImpl(){}
+
+  void SetScrollableFrame(nsIScrollableFrame *aScrollableFrame);
+  nsFrameSelection* GetConstFrameSelection()
+    { return mFrameSelection; }
+
+  //NSISELECTIONCONTROLLER INTERFACES
+  NS_IMETHOD SetDisplaySelection(PRInt16 toggle);
+  NS_IMETHOD GetDisplaySelection(PRInt16 *_retval);
+  NS_IMETHOD SetSelectionFlags(PRInt16 aInEnable);
+  NS_IMETHOD GetSelectionFlags(PRInt16 *aOutEnable);
+  NS_IMETHOD GetSelection(PRInt16 type, nsISelection **_retval);
+  NS_IMETHOD ScrollSelectionIntoView(PRInt16 aType, PRInt16 aRegion, PRBool aIsSynchronous);
+  NS_IMETHOD RepaintSelection(PRInt16 type);
+  NS_IMETHOD RepaintSelection(nsPresContext* aPresContext, SelectionType aSelectionType);
+  NS_IMETHOD SetCaretEnabled(PRBool enabled);
+  NS_IMETHOD SetCaretReadOnly(PRBool aReadOnly);
+  NS_IMETHOD GetCaretEnabled(PRBool *_retval);
+  NS_IMETHOD GetCaretVisible(PRBool *_retval);
+  NS_IMETHOD SetCaretVisibilityDuringSelection(PRBool aVisibility);
+  NS_IMETHOD CharacterMove(PRBool aForward, PRBool aExtend);
+  NS_IMETHOD CharacterExtendForDelete();
+  NS_IMETHOD WordMove(PRBool aForward, PRBool aExtend);
+  NS_IMETHOD WordExtendForDelete(PRBool aForward);
+  NS_IMETHOD LineMove(PRBool aForward, PRBool aExtend);
+  NS_IMETHOD IntraLineMove(PRBool aForward, PRBool aExtend);
+  NS_IMETHOD PageMove(PRBool aForward, PRBool aExtend);
+  NS_IMETHOD CompleteScroll(PRBool aForward);
+  NS_IMETHOD CompleteMove(PRBool aForward, PRBool aExtend);
+  NS_IMETHOD ScrollPage(PRBool aForward);
+  NS_IMETHOD ScrollLine(PRBool aForward);
+  NS_IMETHOD ScrollHorizontal(PRBool aLeft);
+  NS_IMETHOD SelectAll(void);
+  NS_IMETHOD CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, PRBool *_retval);
+
+private:
+  nsCOMPtr<nsFrameSelection> mFrameSelection;
+  nsCOMPtr<nsIContent>       mLimiter;
+  nsIScrollableFrame        *mScrollFrame;
+  nsWeakPtr mPresShellWeak;
+};
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsTextInputSelectionImpl, nsISelectionController)
+NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsTextInputSelectionImpl, nsISelectionController)
+NS_INTERFACE_TABLE_HEAD(nsTextInputSelectionImpl)
+  NS_INTERFACE_TABLE3(nsTextInputSelectionImpl,
+                      nsISelectionController,
+                      nsISelectionDisplay,
+                      nsISupportsWeakReference)
+  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsTextInputSelectionImpl)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_2(nsTextInputSelectionImpl, mFrameSelection, mLimiter)
+
+
+// BEGIN nsTextInputSelectionImpl
+
+nsTextInputSelectionImpl::nsTextInputSelectionImpl(nsFrameSelection *aSel,
+                                                   nsIPresShell *aShell,
+                                                   nsIContent *aLimiter)
+  : mScrollFrame(nsnull)
+{
+  if (aSel && aShell)
+  {
+    mFrameSelection = aSel;//we are the owner now!
+    mLimiter = aLimiter;
+    mFrameSelection->Init(aShell, mLimiter);
+    mPresShellWeak = do_GetWeakReference(aShell);
+  }
+}
+
+void
+nsTextInputSelectionImpl::SetScrollableFrame(nsIScrollableFrame *aScrollableFrame)
+{
+  mScrollFrame = aScrollableFrame;
+  if (!mScrollFrame && mFrameSelection) {
+    mFrameSelection->DisconnectFromPresShell();
+    mFrameSelection = nsnull;
+  }
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::SetDisplaySelection(PRInt16 aToggle)
+{
+  if (!mFrameSelection)
+    return NS_ERROR_NULL_POINTER;
+  
+  mFrameSelection->SetDisplaySelection(aToggle);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::GetDisplaySelection(PRInt16 *aToggle)
+{
+  if (!mFrameSelection)
+    return NS_ERROR_NULL_POINTER;
+
+  *aToggle = mFrameSelection->GetDisplaySelection();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::SetSelectionFlags(PRInt16 aToggle)
+{
+  return NS_OK;//stub this out. not used in input
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::GetSelectionFlags(PRInt16 *aOutEnable)
+{
+  *aOutEnable = nsISelectionDisplay::DISPLAY_TEXT;
+  return NS_OK; 
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::GetSelection(PRInt16 type, nsISelection **_retval)
+{
+  if (!mFrameSelection)
+    return NS_ERROR_NULL_POINTER;
+    
+  *_retval = mFrameSelection->GetSelection(type);
+  
+  if (!(*_retval))
+    return NS_ERROR_FAILURE;
+
+  NS_ADDREF(*_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::ScrollSelectionIntoView(PRInt16 aType, PRInt16 aRegion, PRBool aIsSynchronous)
+{
+  if (!mFrameSelection) 
+    return NS_ERROR_FAILURE; 
+
+  return mFrameSelection->ScrollSelectionIntoView(aType, aRegion, aIsSynchronous); 
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::RepaintSelection(PRInt16 type)
+{
+  if (!mFrameSelection)
+    return NS_ERROR_FAILURE;
+
+  return mFrameSelection->RepaintSelection(type);
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::RepaintSelection(nsPresContext* aPresContext, SelectionType aSelectionType)
+{
+  if (!mFrameSelection)
+    return NS_ERROR_FAILURE;
+
+  return mFrameSelection->RepaintSelection(aSelectionType);
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::SetCaretEnabled(PRBool enabled)
+{
+  if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
+
+  nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak);
+  if (!shell) return NS_ERROR_FAILURE;
+
+  // tell the pres shell to enable the caret, rather than settings its visibility directly.
+  // this way the presShell's idea of caret visibility is maintained.
+  nsCOMPtr<nsISelectionController> selCon = do_QueryInterface(shell);
+  if (!selCon) return NS_ERROR_NO_INTERFACE;
+  selCon->SetCaretEnabled(enabled);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::SetCaretReadOnly(PRBool aReadOnly)
+{
+  if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
+  nsresult result;
+  nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
+  if (shell)
+  {
+    nsRefPtr<nsCaret> caret = shell->GetCaret();
+    if (caret) {
+      nsISelection* domSel = mFrameSelection->
+        GetSelection(nsISelectionController::SELECTION_NORMAL);
+      if (domSel)
+        caret->SetCaretReadOnly(aReadOnly);
+      return NS_OK;
+    }
+  }
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::GetCaretEnabled(PRBool *_retval)
+{
+  return GetCaretVisible(_retval);
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::GetCaretVisible(PRBool *_retval)
+{
+  if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
+  nsresult result;
+  nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
+  if (shell)
+  {
+    nsRefPtr<nsCaret> caret = shell->GetCaret();
+    if (caret) {
+      nsISelection* domSel = mFrameSelection->
+        GetSelection(nsISelectionController::SELECTION_NORMAL);
+      if (domSel)
+        return caret->GetCaretVisible(_retval);
+    }
+  }
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::SetCaretVisibilityDuringSelection(PRBool aVisibility)
+{
+  if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
+  nsresult result;
+  nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
+  if (shell)
+  {
+    nsRefPtr<nsCaret> caret = shell->GetCaret();
+    if (caret) {
+      nsISelection* domSel = mFrameSelection->
+        GetSelection(nsISelectionController::SELECTION_NORMAL);
+      if (domSel)
+        caret->SetVisibilityDuringSelection(aVisibility);
+      return NS_OK;
+    }
+  }
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::CharacterMove(PRBool aForward, PRBool aExtend)
+{
+  if (mFrameSelection)
+    return mFrameSelection->CharacterMove(aForward, aExtend);
+  return NS_ERROR_NULL_POINTER;
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::CharacterExtendForDelete()
+{
+  if (mFrameSelection)
+    return mFrameSelection->CharacterExtendForDelete();
+  return NS_ERROR_NULL_POINTER;
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::WordMove(PRBool aForward, PRBool aExtend)
+{
+  if (mFrameSelection)
+    return mFrameSelection->WordMove(aForward, aExtend);
+  return NS_ERROR_NULL_POINTER;
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::WordExtendForDelete(PRBool aForward)
+{
+  if (mFrameSelection)
+    return mFrameSelection->WordExtendForDelete(aForward);
+  return NS_ERROR_NULL_POINTER;
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::LineMove(PRBool aForward, PRBool aExtend)
+{
+  if (mFrameSelection)
+  {
+    nsresult result = mFrameSelection->LineMove(aForward, aExtend);
+    if (NS_FAILED(result))
+      result = CompleteMove(aForward,aExtend);
+    return result;
+  }
+  return NS_ERROR_NULL_POINTER;
+}
+
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::IntraLineMove(PRBool aForward, PRBool aExtend)
+{
+  if (mFrameSelection)
+    return mFrameSelection->IntraLineMove(aForward, aExtend);
+  return NS_ERROR_NULL_POINTER;
+}
+
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::PageMove(PRBool aForward, PRBool aExtend)
+{
+  // expected behavior for PageMove is to scroll AND move the caret
+  // and to remain relative position of the caret in view. see Bug 4302.
+  if (mScrollFrame)
+  {
+    mFrameSelection->CommonPageMove(aForward, aExtend, mScrollFrame);
+  }
+  // After ScrollSelectionIntoView(), the pending notifications might be
+  // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
+  return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::CompleteScroll(PRBool aForward)
+{
+  if (!mScrollFrame)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  mScrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
+                         nsIScrollableFrame::WHOLE,
+                         nsIScrollableFrame::INSTANT);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::CompleteMove(PRBool aForward, PRBool aExtend)
+{
+  // grab the parent / root DIV for this text widget
+  nsIContent* parentDIV = mFrameSelection->GetLimiter();
+  if (!parentDIV)
+    return NS_ERROR_UNEXPECTED;
+
+  // make the caret be either at the very beginning (0) or the very end
+  PRInt32 offset = 0;
+  nsFrameSelection::HINT hint = nsFrameSelection::HINTLEFT;
+  if (aForward)
+  {
+    offset = parentDIV->GetChildCount();
+
+    // Prevent the caret from being placed after the last
+    // BR node in the content tree!
+
+    if (offset > 0)
+    {
+      nsIContent *child = parentDIV->GetChildAt(offset - 1);
+
+      if (child->Tag() == nsGkAtoms::br)
+      {
+        --offset;
+        hint = nsFrameSelection::HINTRIGHT; // for Bug 106855
+      }
+    }
+  }
+
+  mFrameSelection->HandleClick(parentDIV, offset, offset, aExtend,
+                               PR_FALSE, hint);
+
+  // if we got this far, attempt to scroll no matter what the above result is
+  return CompleteScroll(aForward);
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::ScrollPage(PRBool aForward)
+{
+  if (!mScrollFrame)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  mScrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
+                         nsIScrollableFrame::PAGES,
+                         nsIScrollableFrame::SMOOTH);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::ScrollLine(PRBool aForward)
+{
+  if (!mScrollFrame)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  mScrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
+                         nsIScrollableFrame::LINES,
+                         nsIScrollableFrame::SMOOTH);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::ScrollHorizontal(PRBool aLeft)
+{
+  if (!mScrollFrame)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  mScrollFrame->ScrollBy(nsIntPoint(aLeft ? -1 : 1, 0),
+                         nsIScrollableFrame::LINES,
+                         nsIScrollableFrame::SMOOTH);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::SelectAll()
+{
+  if (mFrameSelection)
+    return mFrameSelection->SelectAll();
+  return NS_ERROR_NULL_POINTER;
+}
+
+NS_IMETHODIMP
+nsTextInputSelectionImpl::CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, PRBool *_retval)
+{
+  if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
+  nsresult result;
+  nsCOMPtr<nsISelectionController> shell = do_QueryReferent(mPresShellWeak, &result);
+  if (shell)
+  {
+    return shell->CheckVisibility(node,startOffset,EndOffset, _retval);
+  }
+  return NS_ERROR_FAILURE;
+
+}
+
+class nsTextInputListener : public nsISelectionListener,
+                            public nsIDOMKeyListener,
+                            public nsIEditorObserver,
+                            public nsSupportsWeakReference
+{
+public:
+  /** the default constructor
+   */ 
+  explicit nsTextInputListener(nsITextControlElement* aTxtCtrlElement);
+  /** the default destructor. virtual due to the possibility of derivation.
+   */
+  virtual ~nsTextInputListener();
+
+  /** SetEditor gives an address to the editor that will be accessed
+   *  @param aEditor the editor this listener calls for editing operations
+   */
+  void SetFrame(nsTextControlFrame *aFrame){mFrame = aFrame;}
+
+  NS_DECL_ISUPPORTS
+
+  NS_DECL_NSISELECTIONLISTENER
+
+  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
+
+  // nsIDOMKeyListener
+  NS_IMETHOD KeyDown(nsIDOMEvent *aKeyEvent);
+  NS_IMETHOD KeyPress(nsIDOMEvent *aKeyEvent);
+  NS_IMETHOD KeyUp(nsIDOMEvent *aKeyEvent);
+
+  NS_DECL_NSIEDITOROBSERVER
+
+protected:
+
+  nsresult  UpdateTextInputCommands(const nsAString& commandsToUpdate);
+
+  NS_HIDDEN_(nsINativeKeyBindings*) GetKeyBindings();
+
+protected:
+
+  nsTextControlFrame* mFrame;  // weak reference
+
+  nsITextControlElement* const mTxtCtrlElement;
+
+  PRPackedBool    mSelectionWasCollapsed;
+  /**
+   * Whether we had undo items or not the last time we got EditAction()
+   * notification (when this state changes we update undo and redo menus)
+   */
+  PRPackedBool    mHadUndoItems;
+  /**
+   * Whether we had redo items or not the last time we got EditAction()
+   * notification (when this state changes we update undo and redo menus)
+   */
+  PRPackedBool    mHadRedoItems;
+};
+
+
+/*
+ * nsTextInputListener implementation
+ */
+
+nsTextInputListener::nsTextInputListener(nsITextControlElement* aTxtCtrlElement)
+: mFrame(nsnull)
+, mTxtCtrlElement(aTxtCtrlElement)
+, mSelectionWasCollapsed(PR_TRUE)
+, mHadUndoItems(PR_FALSE)
+, mHadRedoItems(PR_FALSE)
+{
+}
+
+nsTextInputListener::~nsTextInputListener() 
+{
+}
+
+NS_IMPL_ADDREF(nsTextInputListener)
+NS_IMPL_RELEASE(nsTextInputListener)
+
+NS_INTERFACE_MAP_BEGIN(nsTextInputListener)
+  NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
+  NS_INTERFACE_MAP_ENTRY(nsIEditorObserver)
+  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMKeyListener)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMKeyListener)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMKeyListener)
+NS_INTERFACE_MAP_END
+
+// BEGIN nsIDOMSelectionListener
+
+NS_IMETHODIMP
+nsTextInputListener::NotifySelectionChanged(nsIDOMDocument* aDoc, nsISelection* aSel, PRInt16 aReason)
+{
+  PRBool collapsed;
+  if (!mFrame || !aDoc || !aSel || NS_FAILED(aSel->GetIsCollapsed(&collapsed)))
+    return NS_OK;
+
+  // Fire the select event
+  // The specs don't exactly say when we should fire the select event.
+  // IE: Whenever you add/remove a character to/from the selection. Also
+  //     each time for select all. Also if you get to the end of the text 
+  //     field you will get new event for each keypress or a continuous 
+  //     stream of events if you use the mouse. IE will fire select event 
+  //     when the selection collapses to nothing if you are holding down
+  //     the shift or mouse button.
+  // Mozilla: If we have non-empty selection we will fire a new event for each
+  //          keypress (or mouseup) if the selection changed. Mozilla will also
+  //          create the event each time select all is called, even if everything
+  //          was previously selected, becase technically select all will first collapse
+  //          and then extend. Mozilla will never create an event if the selection 
+  //          collapses to nothing.
+  if (!collapsed && (aReason & (nsISelectionListener::MOUSEUP_REASON | 
+                                nsISelectionListener::KEYPRESS_REASON |
+                                nsISelectionListener::SELECTALL_REASON)))
+  {
+    nsIContent* content = mFrame->GetContent();
+    if (content) 
+    {
+      nsCOMPtr<nsIDocument> doc = content->GetDocument();
+      if (doc) 
+      {
+        nsCOMPtr<nsIPresShell> presShell = doc->GetPrimaryShell();
+        if (presShell) 
+        {
+          nsEventStatus status = nsEventStatus_eIgnore;
+          nsEvent event(PR_TRUE, NS_FORM_SELECTED);
+
+          presShell->HandleEventWithTarget(&event, mFrame, content, &status);
+        }
+      }
+    }
+  }
+
+  // if the collapsed state did not change, don't fire notifications
+  if (collapsed == mSelectionWasCollapsed)
+    return NS_OK;
+  
+  mSelectionWasCollapsed = collapsed;
+
+  if (!mFrame || !nsContentUtils::IsFocusedContent(mFrame->GetContent()))
+    return NS_OK;
+
+  return UpdateTextInputCommands(NS_LITERAL_STRING("select"));
+}
+
+// END nsIDOMSelectionListener
+
+// BEGIN nsIDOMKeyListener
+
+NS_IMETHODIMP
+nsTextInputListener::HandleEvent(nsIDOMEvent* aEvent)
+{
+  return NS_OK;
+}
+
+static void
+DoCommandCallback(const char *aCommand, void *aData)
+{
+  nsTextControlFrame *frame = static_cast<nsTextControlFrame*>(aData);
+  nsIContent *content = frame->GetContent();
+
+  nsCOMPtr<nsIControllers> controllers;
+  nsCOMPtr<nsIDOMNSHTMLInputElement> input = do_QueryInterface(content);
+  if (input) {
+    input->GetControllers(getter_AddRefs(controllers));
+  } else {
+    nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textArea =
+      do_QueryInterface(content);
+
+    if (textArea) {
+      textArea->GetControllers(getter_AddRefs(controllers));
+    }
+  }
+
+  if (!controllers) {
+    NS_WARNING("Could not get controllers");
+    return;
+  }
+
+  nsCOMPtr<nsIController> controller;
+  controllers->GetControllerForCommand(aCommand, getter_AddRefs(controller));
+  if (controller) {
+    controller->DoCommand(aCommand);
+  }
+}
+
+
+NS_IMETHODIMP
+nsTextInputListener::KeyDown(nsIDOMEvent *aDOMEvent)
+{
+  nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aDOMEvent));
+  NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
+
+  nsNativeKeyEvent nativeEvent;
+  nsINativeKeyBindings *bindings = GetKeyBindings();
+  if (bindings &&
+      nsContentUtils::DOMEventToNativeKeyEvent(keyEvent, &nativeEvent, PR_FALSE)) {
+    if (bindings->KeyDown(nativeEvent, DoCommandCallback, mFrame)) {
+      aDOMEvent->PreventDefault();
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTextInputListener::KeyPress(nsIDOMEvent *aDOMEvent)
+{
+  nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aDOMEvent));
+  NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
+
+  nsNativeKeyEvent nativeEvent;
+  nsINativeKeyBindings *bindings = GetKeyBindings();
+  if (bindings &&
+      nsContentUtils::DOMEventToNativeKeyEvent(keyEvent, &nativeEvent, PR_TRUE)) {
+    if (bindings->KeyPress(nativeEvent, DoCommandCallback, mFrame)) {
+      aDOMEvent->PreventDefault();
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsTextInputListener::KeyUp(nsIDOMEvent *aDOMEvent)
+{
+  nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aDOMEvent));
+  NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
+
+  nsNativeKeyEvent nativeEvent;
+  nsINativeKeyBindings *bindings = GetKeyBindings();
+  if (bindings &&
+      nsContentUtils::DOMEventToNativeKeyEvent(keyEvent, &nativeEvent, PR_FALSE)) {
+    if (bindings->KeyUp(nativeEvent, DoCommandCallback, mFrame)) {
+      aDOMEvent->PreventDefault();
+    }
+  }
+
+  return NS_OK;
+}
+// END nsIDOMKeyListener
+
+// BEGIN nsIEditorObserver
+
+NS_IMETHODIMP
+nsTextInputListener::EditAction()
+{
+  //
+  // Update the undo / redo menus
+  //
+  nsCOMPtr<nsIEditor> editor;
+  mFrame->GetEditor(getter_AddRefs(editor));
+
+  nsCOMPtr<nsITransactionManager> manager;
+  editor->GetTransactionManager(getter_AddRefs(manager));
+  NS_ENSURE_TRUE(manager, NS_ERROR_FAILURE);
+
+  // Get the number of undo / redo items
+  PRInt32 numUndoItems = 0;
+  PRInt32 numRedoItems = 0;
+  manager->GetNumberOfUndoItems(&numUndoItems);
+  manager->GetNumberOfRedoItems(&numRedoItems);
+  if ((numUndoItems && !mHadUndoItems) || (!numUndoItems && mHadUndoItems) ||
+      (numRedoItems && !mHadRedoItems) || (!numRedoItems && mHadRedoItems)) {
+    // Modify the menu if undo or redo items are different
+    UpdateTextInputCommands(NS_LITERAL_STRING("undo"));
+
+    mHadUndoItems = numUndoItems != 0;
+    mHadRedoItems = numRedoItems != 0;
+  }
+
+  // Make sure we know we were changed (do NOT set this to false if there are
+  // no undo items; JS could change the value and we'd still need to save it)
+  mFrame->SetValueChanged(PR_TRUE);
+
+  // Fire input event
+  mFrame->FireOnInput();
+
+  return NS_OK;
+}
+
+// END nsIEditorObserver
+
+
+nsresult
+nsTextInputListener::UpdateTextInputCommands(const nsAString& commandsToUpdate)
+{
+  NS_ENSURE_STATE(mFrame);
+
+  nsIContent* content = mFrame->GetContent();
+  NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
+  
+  nsCOMPtr<nsIDocument> doc = content->GetDocument();
+  NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+
+  nsPIDOMWindow *domWindow = doc->GetWindow();
+  NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
+
+  return domWindow->UpdateCommands(commandsToUpdate);
+}
+
+nsINativeKeyBindings*
+nsTextInputListener::GetKeyBindings()
+{
+  if (mTxtCtrlElement->IsTextArea()) {
+    static PRBool sNoTextAreaBindings = PR_FALSE;
+
+    if (!sNativeTextAreaBindings && !sNoTextAreaBindings) {
+      CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "textarea",
+                     &sNativeTextAreaBindings);
+
+      if (!sNativeTextAreaBindings) {
+        sNoTextAreaBindings = PR_TRUE;
+      }
+    }
+
+    return sNativeTextAreaBindings;
+  }
+
+  static PRBool sNoInputBindings = PR_FALSE;
+  if (!sNativeInputBindings && !sNoInputBindings) {
+    CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "input",
+                   &sNativeInputBindings);
+
+    if (!sNativeInputBindings) {
+      sNoInputBindings = PR_TRUE;
+    }
+  }
+
+  return sNativeInputBindings;
+}
+
+// END nsTextInputListener
+
+// nsTextEditorState
+
+nsTextEditorState::nsTextEditorState(nsITextControlElement* aOwningElement)
+  : mTextCtrlElement(aOwningElement),
+    mBoundFrame(nsnull),
+    mTextListener(nsnull),
+    mEditorInitialized(PR_FALSE)
+{
+  MOZ_COUNT_CTOR(nsTextEditorState);
+}
+
+nsTextEditorState::~nsTextEditorState()
+{
+  MOZ_COUNT_DTOR(nsTextEditorState);
+  Clear();
+}
+
+void
+nsTextEditorState::Clear()
+{
+  if (mBoundFrame) {
+    // Oops, we still have a frame!
+    // This should happen when the type of a text input control is being changed
+    // to something which is not a text control.  In this case, we should pretend
+    // that a frame is being destroyed, and clean up after ourselves properly.
+    UnbindFromFrame(mBoundFrame);
+    mEditor = nsnull;
+  } else {
+    // If we have a bound frame around, UnbindFromFrame will call DestroyEditor
+    // for us.
+    DestroyEditor();
+  }
+  NS_IF_RELEASE(mTextListener);
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsTextEditorState)
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsTextEditorState, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsTextEditorState, Release)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(nsTextEditorState)
+  tmp->Clear();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsTextEditorState)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mSelCon, nsISelectionController)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEditor)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRootNode)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPlaceholderDiv)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+nsFrameSelection*
+nsTextEditorState::GetConstFrameSelection() {
+  if (mSelCon)
+    return mSelCon->GetConstFrameSelection();
+  return nsnull;
+}
+
+nsIEditor*
+nsTextEditorState::GetEditor()
+{
+  if (!mEditor) {
+    nsresult rv = PrepareEditor();
+    NS_ENSURE_SUCCESS(rv, nsnull);
+  }
+  return mEditor;
+}
+
+nsISelectionController*
+nsTextEditorState::GetSelectionController() const
+{
+  return mSelCon;
+}
+
+nsresult
+nsTextEditorState::BindToFrame(nsTextControlFrame* aFrame)
+{
+  NS_ASSERTION(aFrame, "The frame to bind to should be valid");
+  NS_ENSURE_ARG_POINTER(aFrame);
+
+  NS_ASSERTION(!mBoundFrame, "Cannot bind twice, need to unbind first");
+  NS_ENSURE_TRUE(!mBoundFrame, NS_ERROR_FAILURE);
+
+  // If we'll need to transfer our current value to the editor, save it before
+  // binding to the frame.
+  nsAutoString currentValue;
+  if (mEditor) {
+    GetValue(currentValue, PR_TRUE);
+  }
+
+  mBoundFrame = aFrame;
+
+  nsIContent *rootNode = GetRootNode();
+
+  nsIPresShell *shell = mBoundFrame->PresContext()->GetPresShell();
+  NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
+
+  // Create selection
+  nsresult rv;
+  nsCOMPtr<nsFrameSelection> frameSel;
+  frameSel = do_CreateInstance(kFrameSelectionCID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Create a SelectionController
+  mSelCon = new nsTextInputSelectionImpl(frameSel, shell, rootNode);
+  NS_ENSURE_TRUE(mSelCon, NS_ERROR_OUT_OF_MEMORY);
+  mTextListener = new nsTextInputListener(mTextCtrlElement);
+  NS_ENSURE_TRUE(mTextListener, NS_ERROR_OUT_OF_MEMORY);
+  NS_ADDREF(mTextListener);
+
+  mTextListener->SetFrame(mBoundFrame);
+  mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
+
+  // Get the caret and make it a selection listener.
+  nsRefPtr<nsISelection> domSelection;
+  if (NS_SUCCEEDED(mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
+                                         getter_AddRefs(domSelection))) &&
+      domSelection) {
+    nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(domSelection));
+    nsRefPtr<nsCaret> caret = shell->GetCaret();
+    nsCOMPtr<nsISelectionListener> listener;
+    if (caret) {
+      listener = do_QueryInterface(caret);
+      if (listener) {
+        selPriv->AddSelectionListener(listener);
+      }
+    }
+
+    selPriv->AddSelectionListener(static_cast<nsISelectionListener*>
+                                             (mTextListener));
+  }
+
+  // If an editor exists from before, prepare it for usage
+  if (mEditor) {
+    class PrepareEditorEvent : public nsRunnable {
+    public:
+      PrepareEditorEvent(nsTextEditorState &aState,
+                         nsIContent *aOwnerContent,
+                         const nsAString &aCurrentValue)
+        : mState(aState)
+        , mOwnerContent(aOwnerContent)
+        , mCurrentValue(aCurrentValue)
+      {
+      }
+
+      NS_IMETHOD Run() {
+        // Transfer the saved value to the editor if we have one
+        const nsAString *value = nsnull;
+        if (!mCurrentValue.IsEmpty()) {
+          value = &mCurrentValue;
+        }
+
+        mState.PrepareEditor(value);
+
+        return NS_OK;
+      }
+
+    private:
+      nsTextEditorState &mState;
+      nsCOMPtr<nsIContent> mOwnerContent; // strong reference
+      nsAutoString mCurrentValue;
+    };
+
+    nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
+    NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
+
+    if (!nsContentUtils::AddScriptRunner(
+          new PrepareEditorEvent(*this, content, currentValue)))
+      return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsTextEditorState::PrepareEditor(const nsAString *aValue)
+{
+  if (!mBoundFrame) {
+    // Cannot create an editor without a bound frame.
+    // Don't return a failure code, because js callers can't handle that.
+    return NS_OK;
+  }
+
+  if (mEditorInitialized) {
+    // Do not initialize the editor multiple times.
+    return NS_OK;
+  }
+
+  // Note that we don't check mEditor here, because we might already have one
+  // around, in which case we don't create a new one, and we'll just tie the
+  // required machinery to it.
+
+  nsPresContext *presContext = mBoundFrame->PresContext();
+  nsIPresShell *shell = presContext->GetPresShell();
+
+  // Setup the editor flags
+  PRUint32 editorFlags = 0;
+  if (IsPlainTextControl())
+    editorFlags |= nsIPlaintextEditor::eEditorPlaintextMask;
+  if (IsSingleLineTextControl())
+    editorFlags |= nsIPlaintextEditor::eEditorSingleLineMask;
+  if (IsPasswordTextControl())
+    editorFlags |= nsIPlaintextEditor::eEditorPasswordMask;
+
+  // All nsTextControlFrames are widgets
+  editorFlags |= nsIPlaintextEditor::eEditorWidgetMask;
+
+  // Use async reflow and painting for text widgets to improve
+  // performance.
+
+  // XXX: Using editor async updates exposes bugs 158782, 151882,
+  //      and 165130, so we're disabling it for now, until they
+  //      can be addressed.
+  // editorFlags |= nsIPlaintextEditor::eEditorUseAsyncUpdatesMask;
+
+  PRBool shouldInitializeEditor = PR_FALSE;
+  nsCOMPtr<nsIEditor> newEditor; // the editor that we might create
+  nsresult rv;
+  if (!mEditor) {
+    shouldInitializeEditor = PR_TRUE;
+
+    // Create an editor
+    newEditor = do_CreateInstance(kTextEditorCID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Make sure we clear out the non-breaking space before we initialize the editor
+    rv = mBoundFrame->UpdateValueDisplay(PR_FALSE, PR_TRUE);
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else {
+    if (aValue) {
+      // Set the correct value in the root node
+      rv = mBoundFrame->UpdateValueDisplay(PR_FALSE, PR_FALSE, aValue);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    newEditor = mEditor; // just pretend that we have a new editor!
+  }
+
+  if (!mEditorInitialized) {
+    // Now initialize the editor.
+    //
+    // NOTE: Conversion of '\n' to <BR> happens inside the
+    //       editor's Init() call.
+
+    // Get the DOM document
+    nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(shell->GetDocument());
+    if (!domdoc)
+      return NS_ERROR_FAILURE;
+
+    // What follows is a bit of a hack.  The editor uses the public DOM APIs
+    // for its content manipulations, and it causes it to fail some security
+    // checks deep inside when initializing.  So we push a null JSContext
+    // on the JS stack here to make it clear that we're native code.
+    // Note that any script that's directly trying to access our value
+    // has to be going through some scriptable object to do that and that
+    // already does the relevant security checks.
+    nsCxPusher pusher;
+    pusher.PushNull();
+
+    rv = newEditor->Init(domdoc, shell, GetRootNode(), mSelCon, editorFlags);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // Initialize the controller for the editor
+
+  if (!SuppressEventHandlers(presContext)) {
+    nsCOMPtr<nsIControllers> controllers;
+    nsCOMPtr<nsIDOMNSHTMLInputElement> inputElement =
+      do_QueryInterface(mTextCtrlElement);
+    if (inputElement) {
+      rv = inputElement->GetControllers(getter_AddRefs(controllers));
+    } else {
+      nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textAreaElement =
+        do_QueryInterface(mTextCtrlElement);
+
+      if (!textAreaElement)
+        return NS_ERROR_FAILURE;
+
+      rv = textAreaElement->GetControllers(getter_AddRefs(controllers));
+    }
+
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (controllers) {
+      PRUint32 numControllers;
+      PRBool found = PR_FALSE;
+      rv = controllers->GetControllerCount(&numControllers);
+      for (PRUint32 i = 0; i < numControllers; i ++) {
+        nsCOMPtr<nsIController> controller;
+        rv = controllers->GetControllerAt(i, getter_AddRefs(controller));
+        if (NS_SUCCEEDED(rv) && controller) {
+          nsCOMPtr<nsIControllerContext> editController =
+            do_QueryInterface(controller);
+          if (editController) {
+            editController->SetCommandContext(newEditor);
+            found = PR_TRUE;
+          }
+        }
+      }
+      if (!found)
+        rv = NS_ERROR_FAILURE;
+    }
+  }
+
+  if (shouldInitializeEditor) {
+    // Initialize the plaintext editor
+    nsCOMPtr<nsIPlaintextEditor> textEditor(do_QueryInterface(newEditor));
+    if (textEditor) {
+      // Set up wrapping
+      textEditor->SetWrapColumn(GetWrapCols());
+
+      // Set max text field length
+      PRInt32 maxLength;
+      if (GetMaxLength(&maxLength)) { 
+        textEditor->SetMaxTextLength(maxLength);
+      }
+    }
+  }
+
+  nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
+  if (content) {
+    rv = newEditor->GetFlags(&editorFlags);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Check if the readonly attribute is set.
+    if (content->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly))
+      editorFlags |= nsIPlaintextEditor::eEditorReadonlyMask;
+
+    // Check if the disabled attribute is set.
+    if (content->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) 
+      editorFlags |= nsIPlaintextEditor::eEditorDisabledMask;
+
+    // Disable the selection if necessary.
+    if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
+      mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
+
+    newEditor->SetFlags(editorFlags);
+  }
+
+  // Get the current value of the textfield from the content.
+  // Note that if we've created a new editor, mEditor is null at this stage,
+  // so we will get the real value from the content.
+  nsAutoString defaultValue;
+  if (aValue) {
+    defaultValue = *aValue;
+  } else {
+    GetValue(defaultValue, PR_TRUE);
+  }
+
+  if (shouldInitializeEditor) {
+    // Hold on to the newly created editor
+    mEditor = newEditor;
+  }
+
+  // If we have a default value, insert it under the div we created
+  // above, but be sure to use the editor so that '*' characters get
+  // displayed for password fields, etc. SetValue() will call the
+  // editor for us.
+
+  if (!defaultValue.IsEmpty()) {
+    // Avoid causing reentrant painting and reflowing by telling the editor
+    // that we don't want it to force immediate view refreshes or force
+    // immediate reflows during any editor calls.
+
+    rv = newEditor->SetFlags(editorFlags |
+                             nsIPlaintextEditor::eEditorUseAsyncUpdatesMask);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Now call SetValue() which will make the necessary editor calls to set
+    // the default value.  Make sure to turn off undo before setting the default
+    // value, and turn it back on afterwards. This will make sure we can't undo
+    // past the default value.
+
+    rv = newEditor->EnableUndo(PR_FALSE);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    SetValue(defaultValue, PR_FALSE);
+
+    rv = newEditor->EnableUndo(PR_TRUE);
+    NS_ASSERTION(NS_SUCCEEDED(rv),"Transaction Manager must have failed");
+
+    // Now restore the original editor flags.
+    rv = newEditor->SetFlags(editorFlags);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  nsCOMPtr<nsITransactionManager> transMgr;
+  newEditor->GetTransactionManager(getter_AddRefs(transMgr));
+  NS_ENSURE_TRUE(transMgr, NS_ERROR_FAILURE);
+
+  transMgr->SetMaxTransactionCount(nsITextControlElement::DEFAULT_UNDO_CAP);
+
+  if (IsPasswordTextControl()) {
+    // Disable undo for password textfields.  Note that we want to do this at
+    // the very end of InitEditor, so the calls to EnableUndo when setting the
+    // default value don't screw us up.
+    // Since changing the control type does a reframe, we don't have to worry
+    // about dynamic type changes here.
+    newEditor->EnableUndo(PR_FALSE);
+  }
+
+  if (!mEditorInitialized) {
+    newEditor->PostCreate();
+    mEditorInitialized = PR_TRUE;
+  }
+
+  if (mTextListener)
+    newEditor->AddEditorObserver(mTextListener);
+
+  return rv;
+}
+
+void
+nsTextEditorState::DestroyEditor()
+{
+  // notify the editor that we are going away
+  if (mEditorInitialized) {
+    if (mTextListener)
+      mEditor->RemoveEditorObserver(mTextListener);
+
+    mEditor->PreDestroy(PR_TRUE);
+    mEditorInitialized = PR_FALSE;
+  }
+}
+
+void
+nsTextEditorState::UnbindFromFrame(nsTextControlFrame* aFrame)
+{
+  NS_ASSERTION(mBoundFrame, "Can't be unbound without being bound originally");
+  NS_ENSURE_TRUE(mBoundFrame, );
+
+  // If it was, however, it should be unbounded from the same frame.
+  NS_ASSERTION(aFrame == mBoundFrame, "Unbinding from the wrong frame");
+  NS_ENSURE_TRUE(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);
+
+  // Destroy our editor
+  DestroyEditor();
+
+  // Clean up the controller
+  if (!SuppressEventHandlers(mBoundFrame->PresContext()))
+  {
+    nsCOMPtr<nsIControllers> controllers;
+    nsCOMPtr<nsIDOMNSHTMLInputElement> inputElement =
+      do_QueryInterface(mTextCtrlElement);
+    if (inputElement)
+      inputElement->GetControllers(getter_AddRefs(controllers));
+    else
+    {
+      nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textAreaElement =
+        do_QueryInterface(mTextCtrlElement);
+      if (textAreaElement) {
+        textAreaElement->GetControllers(getter_AddRefs(controllers));
+      }
+    }
+
+    if (controllers)
+    {
+      PRUint32 numControllers;
+      nsresult rv = controllers->GetControllerCount(&numControllers);
+      NS_ASSERTION((NS_SUCCEEDED(rv)), "bad result in gfx text control destructor");
+      for (PRUint32 i = 0; i < numControllers; i ++)
+      {
+        nsCOMPtr<nsIController> controller;
+        rv = controllers->GetControllerAt(i, getter_AddRefs(controller));
+        if (NS_SUCCEEDED(rv) && controller)
+        {
+          nsCOMPtr<nsIControllerContext> editController = do_QueryInterface(controller);
+          if (editController)
+          {
+            editController->SetCommandContext(nsnull);
+          }
+        }
+      }
+    }
+  }
+
+  if (mSelCon) {
+    if (mTextListener) {
+      nsRefPtr<nsISelection> domSelection;
+      if (NS_SUCCEEDED(mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
+                                             getter_AddRefs(domSelection))) &&
+          domSelection) {
+        nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(domSelection));
+
+        selPriv->RemoveSelectionListener(static_cast<nsISelectionListener*>
+                                         (mTextListener));
+      }
+    }
+
+    mSelCon->SetScrollableFrame(nsnull);
+    mSelCon = nsnull;
+  }
+
+  if (mTextListener)
+  {
+    mTextListener->SetFrame(nsnull);
+
+    nsCOMPtr<nsIDOMEventGroup> systemGroup;
+    nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
+    content->GetSystemEventGroup(getter_AddRefs(systemGroup));
+    nsCOMPtr<nsIDOM3EventTarget> dom3Targ = do_QueryInterface(mTextCtrlElement);
+    if (dom3Targ) {
+      // cast because of ambiguous base
+      nsIDOMEventListener *listener = static_cast<nsIDOMKeyListener*>
+                                                 (mTextListener);
+
+      dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keydown"),
+                                           listener, PR_FALSE, systemGroup);
+      dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keypress"),
+                                           listener, PR_FALSE, systemGroup);
+      dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keyup"),
+                                           listener, PR_FALSE, systemGroup);
+    }
+
+    NS_RELEASE(mTextListener);
+    mTextListener = nsnull;
+  }
+
+  mBoundFrame = nsnull;
+
+  // Now that we don't have a frame any more, store the value in the text buffer.
+  SetValue(value, PR_FALSE);
+
+  if (mRootNode && mMutationObserver) {
+    mRootNode->RemoveMutationObserver(mMutationObserver);
+    mMutationObserver = nsnull;
+  }
+
+  // Unbind the anonymous content from the tree.
+  // We actually hold a reference to the content nodes so that
+  // they're not actually destroyed.
+  nsContentUtils::DestroyAnonymousContent(&mRootNode);
+  nsContentUtils::DestroyAnonymousContent(&mPlaceholderDiv);
+}
+
+nsresult
+nsTextEditorState::CreateRootNode()
+{
+  NS_ENSURE_TRUE(!mRootNode, NS_ERROR_UNEXPECTED);
+  NS_ENSURE_ARG_POINTER(mBoundFrame);
+
+  nsIPresShell *shell = mBoundFrame->PresContext()->GetPresShell();
+  NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
+
+  nsIDocument *doc = shell->GetDocument();
+  NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+
+  // Now create a DIV and add it to the anonymous content child list.
+  nsCOMPtr<nsINodeInfo> nodeInfo;
+  nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::div, nsnull,
+                                                 kNameSpaceID_XHTML);
+  NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
+
+  nsresult rv = NS_NewHTMLElement(getter_AddRefs(mRootNode), nodeInfo, PR_FALSE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Set the necessary classes on the text control. We use class values
+  // instead of a 'style' attribute so that the style comes from a user-agent
+  // style sheet and is still applied even if author styles are disabled.
+  nsAutoString classValue;
+  classValue.AppendLiteral("anonymous-div");
+  PRInt32 wrapCols = GetWrapCols();
+  if (wrapCols >= 0) {
+    classValue.AppendLiteral(" wrap");
+  }
+  if (!IsSingleLineTextControl()) {
+    // We can't just inherit the overflow because setting visible overflow will
+    // crash when the number of lines exceeds the height of the textarea and
+    // setting -moz-hidden-unscrollable overflow (NS_STYLE_OVERFLOW_CLIP)
+    // doesn't paint the caret for some reason.
+    const nsStyleDisplay* disp = mBoundFrame->GetStyleDisplay();
+    if (disp->mOverflowX != NS_STYLE_OVERFLOW_VISIBLE &&
+        disp->mOverflowX != NS_STYLE_OVERFLOW_CLIP) {
+      classValue.AppendLiteral(" inherit-overflow");
+    }
+
+    mMutationObserver = new nsAnonDivObserver(this);
+    NS_ENSURE_TRUE(mMutationObserver, NS_ERROR_OUT_OF_MEMORY);
+    mRootNode->AddMutationObserver(mMutationObserver);
+  }
+  rv = mRootNode->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
+                          classValue, PR_FALSE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mBoundFrame->UpdateValueDisplay(PR_FALSE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return rv;
+}
+
+nsresult
+nsTextEditorState::CreatePlaceholderNode()
+{
+  NS_ENSURE_TRUE(!mPlaceholderDiv, NS_ERROR_UNEXPECTED);
+  NS_ENSURE_ARG_POINTER(mBoundFrame);
+
+  nsIPresShell *shell = mBoundFrame->PresContext()->GetPresShell();
+  NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
+
+  nsIDocument *doc = shell->GetDocument();
+  NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+
+  nsNodeInfoManager* pNodeInfoManager = doc->NodeInfoManager();
+  NS_ENSURE_TRUE(pNodeInfoManager, NS_ERROR_OUT_OF_MEMORY);
+
+  nsresult rv;
+  nsCOMPtr<nsIContent> placeholderText;
+
+  // Create a DIV for the placeholder
+  // and add it to the anonymous content child list
+  nsCOMPtr<nsINodeInfo> nodeInfo;
+  nodeInfo = pNodeInfoManager->GetNodeInfo(nsGkAtoms::div, nsnull,
+                                           kNameSpaceID_XHTML);
+  NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
+
+  rv = NS_NewHTMLElement(getter_AddRefs(mPlaceholderDiv), nodeInfo, PR_FALSE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Create the text node for the placeholder text before doing anything else
+  rv = NS_NewTextNode(getter_AddRefs(placeholderText), pNodeInfoManager);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mPlaceholderDiv->AppendChildTo(placeholderText, PR_FALSE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // initialize the text
+  UpdatePlaceholderText(PR_FALSE);
+
+  return NS_OK;
+}
+
+PRBool
+nsTextEditorState::GetMaxLength(PRInt32* aMaxLength)
+{
+  nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
+  NS_ENSURE_TRUE(content, PR_FALSE);
+  nsGenericHTMLElement* element = nsGenericHTMLElement::FromContent(content);
+  NS_ENSURE_TRUE(element, PR_FALSE);
+
+  const nsAttrValue* attr = element->GetParsedAttr(nsGkAtoms::maxlength);
+  if (attr && attr->Type() == nsAttrValue::eInteger) {
+    *aMaxLength = attr->GetIntegerValue();
+
+    return PR_TRUE;
+  }
+
+  return PR_FALSE;
+}
+
+void
+nsTextEditorState::GetValue(nsAString& aValue, PRBool aIgnoreWrap) const
+{
+  if (mEditor && mBoundFrame) {
+    PRBool canCache = aIgnoreWrap && !IsSingleLineTextControl();
+    if (canCache && !mCachedValue.IsEmpty()) {
+      aValue = mCachedValue;
+      return;
+    }
+
+    aValue.Truncate(); // initialize out param
+
+    PRUint32 flags = (nsIDocumentEncoder::OutputLFLineBreak |
+                      nsIDocumentEncoder::OutputPreformatted |
+                      nsIDocumentEncoder::OutputPersistNBSP);
+
+    if (IsPlainTextControl())
+    {
+      flags |= nsIDocumentEncoder::OutputBodyOnly;
+    }
+
+    if (!aIgnoreWrap) {
+      nsITextControlElement::nsHTMLTextWrap wrapProp;
+      nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
+      if (content &&
+          nsITextControlElement::GetWrapPropertyEnum(content, wrapProp) &&
+          wrapProp == nsITextControlElement::eHTMLTextWrap_Hard) {
+        flags |= nsIDocumentEncoder::OutputWrap;
+      }
+    }
+
+    // What follows is a bit of a hack.  The problem is that we could be in
+    // this method because we're being destroyed for whatever reason while
+    // script is executing.  If that happens, editor will run with the
+    // privileges of the executing script, which means it may not be able to
+    // access its own DOM nodes!  Let's try to deal with that by pushing a null
+    // JSContext on the JSContext stack to make it clear that we're native
+    // code.  Note that any script that's directly trying to access our value
+    // has to be going through some scriptable object to do that and that
+    // already does the relevant security checks.
+    // XXXbz if we could just get the textContent of our anonymous content (eg
+    // if plaintext editor didn't create <br> nodes all over), we wouldn't need
+    // this.
+    { /* Scope for context pusher */
+      nsCxPusher pusher;
+      pusher.PushNull();
+
+      mEditor->OutputToString(NS_LITERAL_STRING("text/plain"), flags,
+                              aValue);
+    }
+    if (canCache) {
+      mCachedValue = aValue;
+    } else {
+      mCachedValue.Truncate();
+    }
+  } else {
+    if (!mTextCtrlElement->ValueChanged() || !mValue) {
+      mTextCtrlElement->GetDefaultValueFromContent(aValue);
+    } else {
+      aValue = NS_ConvertUTF8toUTF16(*mValue);
+    }
+
+    if (IsSingleLineTextControl()) {
+      // If the value is not owned by the frame, then we should handle any
+      // existing newline characters inside it, instead of relying on the
+      // editor to do it for us.
+      nsString value(aValue);
+      nsTextEditRules::HandleNewLines(value, -1);
+      aValue.Assign(value);
+    }
+  }
+}
+
+void
+nsTextEditorState::SetValue(const nsAString& aValue, PRBool aUserInput)
+{
+  if (mEditor && mBoundFrame) {
+    // The InsertText call below might flush pending notifications, which
+    // could lead into a scheduled PrepareEditor to be called.  That will
+    // lead to crashes (or worse) because we'd be initializing the editor
+    // before InsertText returns.  This script blocker makes sure that
+    // PrepareEditor cannot be called prematurely.
+    nsAutoScriptBlocker scriptBlocker;
+
+    PRBool fireChangeEvent = mBoundFrame->GetFireChangeEventState();
+    if (aUserInput) {
+      mBoundFrame->SetFireChangeEventState(PR_TRUE);
+    }
+
+    nsAutoString currentValue;
+    mBoundFrame->GetText(currentValue);
+
+    nsWeakFrame weakFrame(mBoundFrame);
+
+    // this is necessary to avoid infinite recursion
+    if (!currentValue.Equals(aValue))
+    {
+      nsTextControlFrame::ValueSetter valueSetter(mBoundFrame,
+                                                  mBoundFrame->mFocusedValue.Equals(currentValue));
+
+      // \r is an illegal character in the dom, but people use them,
+      // so convert windows and mac platform linebreaks to \n:
+      // Unfortunately aValue is declared const, so we have to copy
+      // in order to do this substitution.
+      nsString newValue(aValue);
+      if (aValue.FindChar(PRUnichar('\r')) != -1) {
+        nsContentUtils::PlatformToDOMLineBreaks(newValue);
+      }
+
+      nsCOMPtr<nsIDOMDocument> domDoc;
+      mEditor->GetDocument(getter_AddRefs(domDoc));
+      if (!domDoc) {
+        NS_WARNING("Why don't we have a document?");
+        return;
+      }
+
+      // Time to mess with our security context... See comments in GetValue()
+      // for why this is needed.  Note that we have to do this up here, because
+      // otherwise SelectAll() will fail.
+      { /* Scope for context pusher */
+        nsCxPusher pusher;
+        pusher.PushNull();
+
+        nsCOMPtr<nsISelection> domSel;
+        nsCOMPtr<nsISelectionPrivate> selPriv;
+        mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
+                              getter_AddRefs(domSel));
+        if (domSel)
+        {
+          selPriv = do_QueryInterface(domSel);
+          if (selPriv)
+            selPriv->StartBatchChanges();
+        }
+
+        nsCOMPtr<nsISelectionController> kungFuDeathGrip = mSelCon.get();
+        PRUint32 currentLength = currentValue.Length();
+        PRUint32 newlength = newValue.Length();
+        if (!currentLength ||
+            !StringBeginsWith(newValue, currentValue)) {
+          // Replace the whole text.
+          currentLength = 0;
+          mSelCon->SelectAll();
+        } else {
+          // Collapse selection to the end so that we can append data.
+          mBoundFrame->SelectAllOrCollapseToEndOfText(PR_FALSE);
+        }
+        const nsAString& insertValue =
+          StringTail(newValue, newlength - currentLength);
+        nsCOMPtr<nsIPlaintextEditor> plaintextEditor = do_QueryInterface(mEditor);
+        if (!plaintextEditor || !weakFrame.IsAlive()) {
+          NS_WARNING("Somehow not a plaintext editor?");
+          return;
+        }
+
+        valueSetter.Init();
+
+        // get the flags, remove readonly and disabled, set the value,
+        // restore flags
+        PRUint32 flags, savedFlags;
+        mEditor->GetFlags(&savedFlags);
+        flags = savedFlags;
+        flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
+        flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
+        flags |= nsIPlaintextEditor::eEditorUseAsyncUpdatesMask;
+        flags |= nsIPlaintextEditor::eEditorDontEchoPassword;
+        mEditor->SetFlags(flags);
+
+        // Also don't enforce max-length here
+        PRInt32 savedMaxLength;
+        plaintextEditor->GetMaxTextLength(&savedMaxLength);
+        plaintextEditor->SetMaxTextLength(-1);
+
+        if (insertValue.IsEmpty()) {
+          mEditor->DeleteSelection(nsIEditor::eNone);
+        } else {
+          plaintextEditor->InsertText(insertValue);
+        }
+        if (!weakFrame.IsAlive()) {
+          NS_ASSERTION(!mBoundFrame, "The frame should have been unbounded");
+          SetValue(newValue, PR_FALSE);
+          valueSetter.Cancel();
+          return;
+        }
+
+        if (!IsSingleLineTextControl()) {
+          mCachedValue = newValue;
+        }
+
+        plaintextEditor->SetMaxTextLength(savedMaxLength);
+        mEditor->SetFlags(savedFlags);
+        if (selPriv)
+          selPriv->EndBatchChanges();
+      }
+    }
+
+    // This second check _shouldn't_ be necessary, but let's be safe.
+    if (!weakFrame.IsAlive()) {
+      return;
+    }
+    nsIScrollableFrame* scrollableFrame = do_QueryFrame(mBoundFrame->GetFirstChild(nsnull));
+    if (scrollableFrame)
+    {
+      // Scroll the upper left corner of the text control's
+      // content area back into view.
+      scrollableFrame->ScrollTo(nsPoint(0, 0), nsIScrollableFrame::INSTANT);
+    }
+
+    if (aUserInput) {
+      mBoundFrame->SetFireChangeEventState(fireChangeEvent);
+    }
+  } else {
+    if (!mValue) {
+      mValue = new nsCString;
+    }
+    nsString value(aValue);
+    nsContentUtils::PlatformToDOMLineBreaks(value);
+    *mValue = ToNewUTF8String(value);
+
+    // Update the frame display if needed
+    if (mBoundFrame) {
+      mBoundFrame->UpdateValueDisplay(PR_TRUE);
+    }
+  }
+
+  // If we've reached the point where the root node has been created, we
+  // can assume that it's safe to notify.
+  ValueWasChanged(!!mRootNode);
+}
+
+void
+nsTextEditorState::InitializeKeyboardEventListeners()
+{
+  nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
+
+  //register key listeners
+  nsCOMPtr<nsIDOMEventGroup> systemGroup;
+  content->GetSystemEventGroup(getter_AddRefs(systemGroup));
+  nsCOMPtr<nsIDOM3EventTarget> dom3Targ = do_QueryInterface(content);
+  if (dom3Targ) {
+    // cast because of ambiguous base
+    nsIDOMEventListener *listener = static_cast<nsIDOMKeyListener*>
+                                               (mTextListener);
+
+    dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keydown"),
+                                      listener, PR_FALSE, systemGroup);
+    dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keypress"),
+                                      listener, PR_FALSE, systemGroup);
+    dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keyup"),
+                                      listener, PR_FALSE, systemGroup);
+  }
+
+  mSelCon->SetScrollableFrame(do_QueryFrame(mBoundFrame->GetFirstChild(nsnull)));
+}
+
+/* static */ void
+nsTextEditorState::ShutDown()
+{
+  NS_IF_RELEASE(sNativeTextAreaBindings);
+  NS_IF_RELEASE(sNativeInputBindings);
+}
+
+void
+nsTextEditorState::ValueWasChanged(PRBool aNotify)
+{
+  // placeholder management
+  PRBool showPlaceholder = PR_FALSE;
+  nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
+  if (!nsContentUtils::IsFocusedContent(content)) {
+    // If the content is focused, we don't care about the changes because
+    // the placeholder is going to be hidden/shown on blur.
+    nsAutoString valueString;
+    GetValue(valueString, PR_TRUE);
+    showPlaceholder = valueString.IsEmpty();
+  }
+  SetPlaceholderClass(showPlaceholder, aNotify);
+}
+
+void
+nsTextEditorState::UpdatePlaceholderText(PRBool aNotify)
+{
+  nsAutoString placeholderValue;
+
+  nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
+  content->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, placeholderValue);
+  nsContentUtils::RemoveNewlines(placeholderValue);
+  NS_ASSERTION(mPlaceholderDiv->GetChildAt(0), "placeholder div has no child");
+  mPlaceholderDiv->GetChildAt(0)->SetText(placeholderValue, aNotify);
+  ValueWasChanged(aNotify);
+}
+
+void
+nsTextEditorState::SetPlaceholderClass(PRBool aVisible,
+                                       PRBool aNotify)
+{
+  // No need to do anything if we don't have a frame yet
+  if (!mBoundFrame)
+    return;
+
+  nsAutoString classValue;
+
+  classValue.Assign(NS_LITERAL_STRING("anonymous-div placeholder"));
+
+  if (!aVisible)
+    classValue.AppendLiteral(" hidden");
+
+  nsIContent* placeholderDiv = GetPlaceholderNode();
+  NS_ENSURE_TRUE(placeholderDiv, );
+
+  placeholderDiv->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
+                          classValue, aNotify);
+}
+
+NS_IMPL_ISUPPORTS1(nsAnonDivObserver, nsIMutationObserver)
+
+void
+nsAnonDivObserver::CharacterDataChanged(nsIDocument*             aDocument,
+                                        nsIContent*              aContent,
+                                        CharacterDataChangeInfo* aInfo)
+{
+  mTextEditorState->ClearValueCache();
+}
+
+void
+nsAnonDivObserver::ContentAppended(nsIDocument* aDocument,
+                                   nsIContent*  aContainer,
+                                   nsIContent*  aFirstNewContent,
+                                   PRInt32      /* unused */)
+{
+  mTextEditorState->ClearValueCache();
+}
+
+void
+nsAnonDivObserver::ContentInserted(nsIDocument* aDocument,
+                                   nsIContent*  aContainer,
+                                   nsIContent*  aChild,
+                                   PRInt32      /* unused */)
+{
+  mTextEditorState->ClearValueCache();
+}
+
+void
+nsAnonDivObserver::ContentRemoved(nsIDocument* aDocument,
+                                  nsIContent*  aContainer,
+                                  nsIContent*  aChild,
+                                  PRInt32      aIndexInContainer)
+{
+  mTextEditorState->ClearValueCache();
+}
new file mode 100644
--- /dev/null
+++ b/content/html/content/src/nsTextEditorState.h
@@ -0,0 +1,239 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 et tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla.org client code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ehsan Akhgari <ehsan@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#ifndef nsTextEditorState_h__
+#define nsTextEditorState_h__
+
+#include "nsAutoPtr.h"
+#include "nsITextControlElement.h"
+#include "nsCycleCollectionParticipant.h"
+
+class nsTextInputListener;
+class nsTextControlFrame;
+class nsTextInputSelectionImpl;
+class nsAnonDivObserver;
+class nsISelectionController;
+class nsFrameSelection;
+class nsIEditor;
+class nsITextControlElement;
+
+/**
+ * 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
+ * the scenes.
+ *
+ * This class is held as a member of nsHTMLInputElement and nsHTMLTextAreaElement.
+ * The public functions in this class include the public APIs which content/ uses.
+ * Layout code uses the nsITextControlElement interface to invoke functions on this
+ * class.
+ *
+ * The design motivation behind this class is maintaining all of the things which
+ * collectively are considered the "state" of the text control in a single location.
+ * This state includes several things:
+ *
+ *  * The control's value.  This value is stored in the mValue member, and is only
+ *    used when there is no frame for the control, or when the editor object has
+ *    not been initialized yet.
+ *
+ *  * The control's associated frame.  This value is stored in the mBoundFrame member.
+ *    A text control might never have an associated frame during its life cycle,
+ *    or might have several different ones, but at any given moment in time there is
+ *    a maximum of 1 bound frame to each text control.
+ *
+ *  * The control's associated editor.  This value is stored in the mEditor member.
+ *    An editor is initilized for the control only when necessary (that is, when either
+ *    the user is about to interact with the text control, or when some other code
+ *    needs to access the editor object.  Without a frame bound to the control, an
+ *    editor is never initialzied.  Once initialized, the editor might outlive the frame,
+ *    in which case the same editor will be used if a new frame gets bound to the
+ *    text control.
+ *
+ *  * The anonymous content associated with the text control's frame, including the
+ *    value div (the DIV element responsible for holding the value of the text control)
+ *    and the placeholder div (the DIV element responsible for holding the placeholder
+ *    value of the text control.)  These values are stored in the mRootNode and
+ *    mPlaceholderDiv members, respectively.  They will be created when a
+ *    frame is bound to the text control.  They will be destroyed when the frame is
+ *    unbound from the object.  We could try and hold on to the anonymous content
+ *    between different frames, but unfortunately that is not currently possible
+ *    because they are not unbound from the document in time.
+ *
+ *  * The frame selection controller.  This value is stored in the mSelCon member.
+ *    The frame selection controller is responsible for maintaining the selection state
+ *    on a frame.  It is created when a frame is bound to the text control element,
+ *    and will be destroy when the frame is being unbound from the text control element.
+ *    It is created alongside with the frame selection object which is stored in the
+ *    mFrameSel member.
+ *
+ *  * The editor text listener.  This value is stored in the mTextListener member.
+ *    Its job is to listen to selection and keyboard events, and act accordingly.
+ *    It is created when an a frame is first bound to the control, and will be destroyed
+ *    when the frame is unbound from the text control element.
+ *
+ *  * The editor's cached value.  This value is stored in the mCachedValue member.
+ *    It is used to improve the performance of append operations to the text
+ *    control.  A mutation observer stored in the mAnonDivObserver has the job of
+ *    invalidating this cache when the anonymous contect containing the value is
+ *    changed.
+ *
+ *
+ * As a general rule, nsTextEditorState objects own the value of the text control, and any
+ * attempt to retrieve or set the value must be made through those objects.  Internally,
+ * the value can be represented in several different ways, based on the state the control is
+ * in.
+ *
+ *   * When the control is first initialized, its value is equal to the default value of
+ *     the DOM node.  For <input> text controls, this default value is the value of the
+ *     value attribute.  For <textarea> elements, this default value is the value of the
+ *     text node children of the element.
+ *
+ *   * If the value has been changed through the DOM node (before the editor for the object
+ *     is initialized), the value is stored as a simple string inside the mValue member of
+ *     the nsTextEditorState object.
+ *
+ *   * If an editor has been initialized for the control, the value is set and retrievd via
+ *     the nsIPlaintextEditor interface, and is internally managed by the editor as the
+ *     native anonymous content tree attached to the control's frame.
+ *
+ *   * If the text editor state object is unbound from the control's frame, the value is
+ *     transferred to the mValue member variable, and will be managed there until a new
+ *     frame is bound to the text editor state object.
+ */
+
+class nsTextEditorState {
+public:
+  explicit nsTextEditorState(nsITextControlElement* aOwningElement);
+  ~nsTextEditorState();
+
+  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsTextEditorState)
+  NS_INLINE_DECL_REFCOUNTING(nsTextEditorState)
+
+  nsIEditor* GetEditor();
+  nsISelectionController* GetSelectionController() const;
+  nsFrameSelection* GetConstFrameSelection();
+  nsresult BindToFrame(nsTextControlFrame* aFrame);
+  void UnbindFromFrame(nsTextControlFrame* aFrame);
+  nsresult PrepareEditor(const nsAString *aValue = nsnull);
+  void InitializeKeyboardEventListeners();
+
+  void SetValue(const nsAString& aValue, PRBool aUserInput);
+  void GetValue(nsAString& aValue, PRBool aIgnoreWrap) const;
+  void EmptyValue() { if (mValue) mValue->Truncate(); }
+  PRBool IsEmpty() const { return mValue ? mValue->IsEmpty() : PR_TRUE; }
+
+  nsIContent* GetRootNode() {
+    if (!mRootNode)
+      CreateRootNode();
+    return mRootNode;
+  }
+  nsIContent* GetPlaceholderNode() {
+    if (!mPlaceholderDiv)
+      CreatePlaceholderNode();
+    return mPlaceholderDiv;
+  }
+
+  PRBool IsSingleLineTextControl() const {
+    return mTextCtrlElement->IsSingleLineTextControl();
+  }
+  PRBool IsTextArea() const {
+    return mTextCtrlElement->IsTextArea();
+  }
+  PRBool IsPlainTextControl() const {
+    return mTextCtrlElement->IsPlainTextControl();
+  }
+  PRBool IsPasswordTextControl() const {
+    return mTextCtrlElement->IsPasswordTextControl();
+  }
+  PRInt32 GetCols() {
+    return mTextCtrlElement->GetCols();
+  }
+  PRInt32 GetWrapCols() {
+    return mTextCtrlElement->GetWrapCols();
+  }
+  PRInt32 GetRows() {
+    return mTextCtrlElement->GetRows();
+  }
+
+  // placeholder methods
+  void SetPlaceholderClass(PRBool aVisible, PRBool aNotify);
+  void UpdatePlaceholderText(PRBool aNotify); 
+
+  /**
+   * Get the maxlength attribute
+   * @param aMaxLength the value of the max length attr
+   * @returns PR_FALSE if attr not defined
+   */
+  PRBool GetMaxLength(PRInt32* aMaxLength);
+
+  /* called to free up native keybinding services */
+  static NS_HIDDEN_(void) ShutDown();
+
+  void ClearValueCache() { mCachedValue.Truncate(); }
+
+private:
+  // not copy constructible
+  nsTextEditorState(const nsTextEditorState&);
+  // not assignable
+  void operator= (const nsTextEditorState&);
+
+  nsresult CreateRootNode();
+  nsresult CreatePlaceholderNode();
+
+  void ValueWasChanged(PRBool aNotify);
+
+  void DestroyEditor();
+  void Clear();
+
+  nsITextControlElement* const mTextCtrlElement;
+  nsRefPtr<nsTextInputSelectionImpl> mSelCon;
+  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.
+  PRPackedBool mEditorInitialized;
+};
+
+#endif
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -129,17 +129,16 @@ extern nsIParserService *sParserService;
 //
 // nsEditor: base editor class implementation
 //
 //---------------------------------------------------------------------------
 
 nsEditor::nsEditor()
 :  mModCount(0)
 ,  mPresShellWeak(nsnull)
-,  mViewManager(nsnull)
 ,  mUpdateCount(0)
 ,  mSpellcheckCheckboxState(eTriUnset)
 ,  mPlaceHolderTxn(nsnull)
 ,  mPlaceHolderName(nsnull)
 ,  mPlaceHolderBatch(0)
 ,  mSelState(nsnull)
 ,  mSavedSel()
 ,  mRangeUpdater()
@@ -161,18 +160,16 @@ nsEditor::nsEditor()
 
 nsEditor::~nsEditor()
 {
   NS_ASSERTION(!mDocWeak || mDidPreDestroy, "Why PreDestroy hasn't been called?");
 
   mTxnMgr = nsnull;
 
   delete mPhonetic;
- 
-  NS_IF_RELEASE(mViewManager);
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsEditor)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsEditor)
  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRootElement)
  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mInlineSpellChecker)
  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTxnMgr)
@@ -247,17 +244,16 @@ nsEditor::Init(nsIDOMDocument *aDoc, nsI
 
   // Set up the DTD
   // XXX - in the long run we want to get this from the document, but there
   // is no way to do that right now.  So we leave it null here and set
   // up a nav html dtd in nsHTMLEditor::Init
 
   mViewManager = ps->GetViewManager();
   if (!mViewManager) {return NS_ERROR_NULL_POINTER;}
-  NS_ADDREF(mViewManager);
 
   mUpdateCount=0;
 
   /* initialize IME stuff */
   mIMETextNode = nsnull;
   mIMETextOffset = 0;
   mIMEBufferLength = 0;
   
@@ -276,16 +272,19 @@ nsEditor::Init(nsIDOMDocument *aDoc, nsI
 //hack to get around this for now.
   nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mSelConWeak);
   if (shell)
     BeginningOfDocument();
 #endif
 
   NS_POSTCONDITION(mDocWeak && mPresShellWeak, "bad state");
 
+  // Make sure that the editor will be destroyed properly
+  mDidPreDestroy = PR_FALSE;
+
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsEditor::PostCreate()
 {
   // Synchronize some stuff for the flags
@@ -313,17 +312,19 @@ nsEditor::PostCreate()
   NotifyDocumentListeners(eDocumentStateChanged);
   
   return NS_OK;
 }
 
 nsresult
 nsEditor::CreateEventListeners()
 {
-  NS_ENSURE_TRUE(!mEventListener, NS_ERROR_ALREADY_INITIALIZED);
+  // Don't create the handler twice
+  if (mEventListener)
+    return NS_OK;
   mEventListener = do_QueryInterface(
     static_cast<nsIDOMKeyListener*>(new nsEditorEventListener()));
   NS_ENSURE_TRUE(mEventListener, NS_ERROR_OUT_OF_MEMORY);
   return NS_OK;
 }
 
 nsresult
 nsEditor::InstallEventListeners()
--- a/editor/libeditor/base/nsEditor.h
+++ b/editor/libeditor/base/nsEditor.h
@@ -652,17 +652,17 @@ public:
 
 protected:
 
   PRUint32        mModCount;		// number of modifications (for undo/redo stack)
   PRUint32        mFlags;		// behavior flags. See nsIPlaintextEditor.idl for the flags we use.
   
   nsWeakPtr       mPresShellWeak;   // weak reference to the nsIPresShell
   nsWeakPtr       mSelConWeak;   // weak reference to the nsISelectionController
-  nsIViewManager *mViewManager;
+  nsCOMPtr<nsIViewManager> mViewManager;
   PRInt32         mUpdateCount;
   nsIViewManager::UpdateViewBatch mBatch;
 
   // Spellchecking
   enum Tristate {
     eTriUnset,
     eTriFalse,
     eTriTrue
--- a/editor/libeditor/base/nsEditorEventListener.cpp
+++ b/editor/libeditor/base/nsEditorEventListener.cpp
@@ -92,17 +92,16 @@ nsEditorEventListener::~nsEditorEventLis
     Disconnect();
   }
 }
 
 nsresult
 nsEditorEventListener::Connect(nsEditor* aEditor)
 {
   NS_ENSURE_ARG(aEditor);
-  NS_ENSURE_TRUE(!mEditor, NS_ERROR_NOT_AVAILABLE);
 
   mEditor = aEditor;
 
   nsresult rv = InstallToEditor();
   if (NS_FAILED(rv)) {
     Disconnect();
   }
   return rv;
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -355,17 +355,16 @@ nsLayoutStatics::Shutdown()
   nsContentUtils::Shutdown();
   nsNodeInfo::ClearCache();
   nsLayoutStylesheetCache::Shutdown();
   NS_NameSpaceManagerShutdown();
 
   nsJSRuntime::Shutdown();
   nsGlobalWindow::ShutDown();
   nsDOMClassInfo::ShutDown();
-  nsTextControlFrame::ShutDown();
   nsListControlFrame::Shutdown();
   nsXBLWindowKeyHandler::ShutDown();
   nsAutoCopyListener::Shutdown();
 
 #ifndef MOZILLA_PLAINTEXT_EDITOR_ONLY
   nsHTMLEditor::Shutdown();
   nsTextServicesDocument::Shutdown();
 #endif
--- a/layout/forms/nsFileControlFrame.cpp
+++ b/layout/forms/nsFileControlFrame.cpp
@@ -137,29 +137,21 @@ NS_NewFileControlFrame(nsIPresShell* aPr
 {
   return new (aPresShell) nsFileControlFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsFileControlFrame)
 
 nsFileControlFrame::nsFileControlFrame(nsStyleContext* aContext):
   nsBlockFrame(aContext),
-  mTextFrame(nsnull), 
-  mCachedState(nsnull)
+  mTextFrame(nsnull)
 {
   AddStateBits(NS_BLOCK_FLOAT_MGR);
 }
 
-nsFileControlFrame::~nsFileControlFrame()
-{
-  if (mCachedState) {
-    delete mCachedState;
-    mCachedState = nsnull;
-  }
-}
 
 NS_IMETHODIMP
 nsFileControlFrame::Init(nsIContent* aContent,
                          nsIFrame*   aParent,
                          nsIFrame*   aPrevInFlow)
 {
   nsresult rv = nsBlockFrame::Init(aContent, aParent, aPrevInFlow);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -670,21 +662,16 @@ NS_IMETHODIMP nsFileControlFrame::Reflow
   DO_GLOBAL_REFLOW_COUNT("nsFileControlFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   aStatus = NS_FRAME_COMPLETE;
 
   if (mState & NS_FRAME_FIRST_REFLOW) {
     mTextFrame = GetTextControlFrame(aPresContext, this);
     NS_ENSURE_TRUE(mTextFrame, NS_ERROR_UNEXPECTED);
-    if (mCachedState) {
-      mTextFrame->SetFormProperty(nsGkAtoms::value, *mCachedState);
-      delete mCachedState;
-      mCachedState = nsnull;
-    }
   }
 
   // nsBlockFrame takes care of all our reflow
   return nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState,
                              aStatus);
 }
 
 nsNewFrame*
@@ -780,23 +767,21 @@ nsFileControlFrame::GetFrameName(nsAStri
 }
 #endif
 
 nsresult
 nsFileControlFrame::SetFormProperty(nsIAtom* aName,
                                     const nsAString& aValue)
 {
   if (nsGkAtoms::value == aName) {
-    if (mTextFrame) {
-      mTextFrame->SetValue(aValue);
-    } else {
-      if (mCachedState) delete mCachedState;
-      mCachedState = new nsString(aValue);
-      NS_ENSURE_TRUE(mCachedState, NS_ERROR_OUT_OF_MEMORY);
-    }
+    nsCOMPtr<nsIDOMHTMLInputElement> textControl =
+      do_QueryInterface(mTextContent);
+    NS_ASSERTION(textControl,
+                 "The text control should exist and be an input element");
+    textControl->SetValue(aValue);
   }
   return NS_OK;
 }      
 
 nsresult
 nsFileControlFrame::GetFormProperty(nsIAtom* aName, nsAString& aValue) const
 {
   aValue.Truncate();  // initialize out param
--- a/layout/forms/nsFileControlFrame.h
+++ b/layout/forms/nsFileControlFrame.h
@@ -48,17 +48,16 @@
 typedef   nsTextControlFrame nsNewFrame;
 
 class nsFileControlFrame : public nsBlockFrame,
                            public nsIFormControlFrame,
                            public nsIAnonymousContentCreator
 {
 public:
   nsFileControlFrame(nsStyleContext* aContext);
-  virtual ~nsFileControlFrame();
 
   NS_IMETHOD Init(nsIContent* aContent,
                   nsIFrame*   aParent,
                   nsIFrame*   aPrevInFlow);
 
   NS_IMETHOD BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                               const nsRect&           aDirtyRect,
                               const nsDisplayListSet& aLists);
@@ -163,21 +162,16 @@ protected:
    * @see nsFileControlFrame::CreateAnonymousContent
    */
   nsCOMPtr<nsIContent> mTextContent;
   /**
    * The browse button input.
    * @see nsFileControlFrame::CreateAnonymousContent
    */
   nsCOMPtr<nsIContent> mBrowse;
-  /**
-   * The current value, stored during those rare in-between periods where the
-   * file frame is there but the input frame is not.
-   */
-  nsString*           mCachedState;
 
   /**
    * Our mouse listener.  This makes sure we don't get used after destruction.
    */
   nsRefPtr<MouseListener> mMouseListener;
 
 private:
   /**
--- a/layout/forms/nsITextControlFrame.h
+++ b/layout/forms/nsITextControlFrame.h
@@ -47,48 +47,30 @@ class nsFrameSelection;
 
 class nsITextControlFrame : public nsIFormControlFrame
 {
 public:
   NS_DECL_QUERYFRAME_TARGET(nsITextControlFrame)
 
   NS_IMETHOD    GetEditor(nsIEditor **aEditor) = 0;
 
-  /**
-   * Tell whether the frame currently owns the value or the content does (for
-   * edge cases where the frame has just been created or is just going away).
-   *
-   * @param aOwnsValue whether the frame owns the value [out]
-   */
-  NS_IMETHOD    OwnsValue(PRBool* aOwnsValue) = 0;
-
-  /**
-   * Get the current value, either from the editor or from the textarea.
-   *
-   * @param aValue the value [out]
-   * @param aIgnoreWrap whether to ignore the wrap attribute when getting the
-   *        value.  If this is true, linebreaks will not be inserted even if
-   *        wrap=hard.
-   */
-  NS_IMETHOD    GetValue(nsAString& aValue, PRBool aIgnoreWrap) const = 0;
-  
   NS_IMETHOD    GetTextLength(PRInt32* aTextLength) = 0;
   
   /**
    * Fire onChange if the value has changed since it was focused or since it
    * was last fired.
    */
   NS_IMETHOD    CheckFireOnChange() = 0;
   NS_IMETHOD    SetSelectionStart(PRInt32 aSelectionStart) = 0;
   NS_IMETHOD    SetSelectionEnd(PRInt32 aSelectionEnd) = 0;
   
   NS_IMETHOD    SetSelectionRange(PRInt32 aSelectionStart, PRInt32 aSelectionEnd) = 0;
   NS_IMETHOD    GetSelectionRange(PRInt32* aSelectionStart, PRInt32* aSelectionEnd) = 0;
 
-  virtual nsISelectionController* GetOwnedSelectionController() = 0;
+  NS_IMETHOD    GetOwnedSelectionController(nsISelectionController** aSelCon) = 0;
   virtual nsFrameSelection* GetOwnedFrameSelection() = 0;
 
   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
--- a/layout/forms/nsIsIndexFrame.cpp
+++ b/layout/forms/nsIsIndexFrame.cpp
@@ -155,30 +155,28 @@ nsIsIndexFrame::GetInputFrame(nsIFormCon
     }
   }
   return NS_OK;
 }
 
 void
 nsIsIndexFrame::GetInputValue(nsString& oString)
 {
-  nsIFormControlFrame* frame = nsnull;
-  GetInputFrame(&frame);
-  if (frame) {
-    ((nsNewFrame*)frame)->GetValue(oString, PR_FALSE);
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(mInputContent);
+  if (txtCtrl) {
+    txtCtrl->GetTextEditorValue(oString, PR_FALSE);
   }
 }
 
 void
 nsIsIndexFrame::SetInputValue(const nsString& aString)
 {
-  nsIFormControlFrame* frame = nsnull;
-  GetInputFrame(&frame);
-  if (frame) {
-    ((nsNewFrame*)frame)->SetValue(aString);
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(mInputContent);
+  if (txtCtrl) {
+    txtCtrl->SetTextEditorValue(aString, PR_FALSE);
   }
 }
 
 void 
 nsIsIndexFrame::SetFocus(PRBool aOn, PRBool aRepaint)
 {
   nsIFormControlFrame* frame = nsnull;
   GetInputFrame(&frame);
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -58,17 +58,16 @@
 #include "nsGenericHTMLElement.h"
 #include "nsIEditorIMESupport.h"
 #include "nsIPhonetic.h"
 #include "nsIEditorObserver.h"
 #include "nsEditProperty.h"
 #include "nsIDOMHTMLTextAreaElement.h"
 #include "nsINameSpaceManager.h"
 #include "nsINodeInfo.h"
-#include "nsIScrollableFrame.h" //to turn off scroll bars
 #include "nsFormControlFrame.h" //for registering accesskeys
 #include "nsIDeviceContext.h" // to measure fonts
 
 #include "nsIContent.h"
 #include "nsIAtom.h"
 #include "nsPresContext.h"
 #include "nsGkAtoms.h"
 #include "nsLayoutUtils.h"
@@ -101,825 +100,38 @@
 #include "nsIDOMNodeList.h" //for selection setting helper func
 #include "nsIDOMRange.h" //for selection setting helper func
 #include "nsPIDOMWindow.h" //needed for notify selection changed to update the menus ect.
 #ifdef ACCESSIBILITY
 #include "nsIAccessibilityService.h"
 #endif
 #include "nsIServiceManager.h"
 #include "nsIDOMNode.h"
-#include "nsITextControlElement.h"
 
 #include "nsIEditorObserver.h"
 #include "nsITransactionManager.h"
 #include "nsIDOMText.h" //for multiline getselection
 #include "nsNodeInfoManager.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsIDOMKeyListener.h"
 #include "nsIDOMEventGroup.h"
 #include "nsIDOM3EventTarget.h"
 #include "nsINativeKeyBindings.h"
 #include "nsIJSContextStack.h"
 #include "nsFocusManager.h"
 #include "nsTextEditRules.h"
+#include "nsIFontMetrics.h"
 
 #include "mozilla/FunctionTimer.h"
 
 #define DEFAULT_COLUMN_WIDTH 20
 
 #include "nsContentCID.h"
 static NS_DEFINE_IID(kRangeCID,     NS_RANGE_CID);
 
-static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID);
-static NS_DEFINE_CID(kFrameSelectionCID, NS_FRAMESELECTION_CID);
-
-static const PRInt32 DEFAULT_COLS = 20;
-static const PRInt32 DEFAULT_ROWS = 1;
-static const PRInt32 DEFAULT_ROWS_TEXTAREA = 2;
-static const PRInt32 DEFAULT_UNDO_CAP = 1000;
-
-static nsINativeKeyBindings *sNativeInputBindings = nsnull;
-static nsINativeKeyBindings *sNativeTextAreaBindings = nsnull;
-
-// wrap can be one of these three values.  
-typedef enum {
-  eHTMLTextWrap_Off     = 1,    // "off"
-  eHTMLTextWrap_Hard    = 2,    // "hard"
-  eHTMLTextWrap_Soft    = 3     // the default
-} nsHTMLTextWrap;
-
-static PRBool 
-GetWrapPropertyEnum(nsIContent* aContent, 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
-  // do anything different.
-  aWrapProp = eHTMLTextWrap_Soft; // the default
-  
-  nsAutoString wrap;
-  if (aContent->IsHTML()) {
-    static nsIContent::AttrValuesArray strings[] =
-      {&nsGkAtoms::HARD, &nsGkAtoms::OFF, nsnull};
-
-    switch (aContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::wrap,
-                                      strings, eIgnoreCase)) {
-      case 0: aWrapProp = eHTMLTextWrap_Hard; break;
-      case 1: aWrapProp = eHTMLTextWrap_Off; break;
-    }
-
-    return PR_TRUE;
-  }
- 
-  return PR_FALSE;
-}
-
-class nsTextInputListener : public nsISelectionListener,
-                            public nsIDOMKeyListener,
-                            public nsIEditorObserver,
-                            public nsSupportsWeakReference
-{
-public:
-  /** the default constructor
-   */ 
-  nsTextInputListener();
-  /** the default destructor. virtual due to the possibility of derivation.
-   */
-  virtual ~nsTextInputListener();
-
-  /** SetEditor gives an address to the editor that will be accessed
-   *  @param aEditor the editor this listener calls for editing operations
-   */
-  void SetFrame(nsTextControlFrame *aFrame){mFrame = aFrame;}
-
-  NS_DECL_ISUPPORTS
-
-  NS_DECL_NSISELECTIONLISTENER
-
-  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
-
-  // nsIDOMKeyListener
-  NS_IMETHOD KeyDown(nsIDOMEvent *aKeyEvent);
-  NS_IMETHOD KeyPress(nsIDOMEvent *aKeyEvent);
-  NS_IMETHOD KeyUp(nsIDOMEvent *aKeyEvent);
-
-  NS_DECL_NSIEDITOROBSERVER
-
-protected:
-
-  nsresult  UpdateTextInputCommands(const nsAString& commandsToUpdate);
-
-  NS_HIDDEN_(nsINativeKeyBindings*) GetKeyBindings();
-
-protected:
-
-  nsTextControlFrame* mFrame;  // weak reference
-  
-  PRPackedBool    mSelectionWasCollapsed;
-  /**
-   * Whether we had undo items or not the last time we got EditAction()
-   * notification (when this state changes we update undo and redo menus)
-   */
-  PRPackedBool    mHadUndoItems;
-  /**
-   * Whether we had redo items or not the last time we got EditAction()
-   * notification (when this state changes we update undo and redo menus)
-   */
-  PRPackedBool    mHadRedoItems;
-};
-
-
-/*
- * nsTextEditorListener implementation
- */
-
-nsTextInputListener::nsTextInputListener()
-: mFrame(nsnull)
-, mSelectionWasCollapsed(PR_TRUE)
-, mHadUndoItems(PR_FALSE)
-, mHadRedoItems(PR_FALSE)
-{
-}
-
-nsTextInputListener::~nsTextInputListener() 
-{
-}
-
-NS_IMPL_ADDREF(nsTextInputListener)
-NS_IMPL_RELEASE(nsTextInputListener)
-
-NS_INTERFACE_MAP_BEGIN(nsTextInputListener)
-  NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
-  NS_INTERFACE_MAP_ENTRY(nsIEditorObserver)
-  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMKeyListener)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMKeyListener)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMKeyListener)
-NS_INTERFACE_MAP_END
-
-// BEGIN nsIDOMSelectionListener
-
-static PRBool
-IsFocusedContent(nsIContent* aContent)
-{
-  nsFocusManager* fm = nsFocusManager::GetFocusManager();
-
-  return fm && fm->GetFocusedContent() == aContent;
-}
-
-NS_IMETHODIMP
-nsTextInputListener::NotifySelectionChanged(nsIDOMDocument* aDoc, nsISelection* aSel, PRInt16 aReason)
-{
-  PRBool collapsed;
-  if (!mFrame || !aDoc || !aSel || NS_FAILED(aSel->GetIsCollapsed(&collapsed)))
-    return NS_OK;
-
-  // Fire the select event
-  // The specs don't exactly say when we should fire the select event.
-  // IE: Whenever you add/remove a character to/from the selection. Also
-  //     each time for select all. Also if you get to the end of the text 
-  //     field you will get new event for each keypress or a continuous 
-  //     stream of events if you use the mouse. IE will fire select event 
-  //     when the selection collapses to nothing if you are holding down
-  //     the shift or mouse button.
-  // Mozilla: If we have non-empty selection we will fire a new event for each
-  //          keypress (or mouseup) if the selection changed. Mozilla will also
-  //          create the event each time select all is called, even if everything
-  //          was previously selected, becase technically select all will first collapse
-  //          and then extend. Mozilla will never create an event if the selection 
-  //          collapses to nothing.
-  if (!collapsed && (aReason & (nsISelectionListener::MOUSEUP_REASON | 
-                                nsISelectionListener::KEYPRESS_REASON |
-                                nsISelectionListener::SELECTALL_REASON)))
-  {
-    nsIContent* content = mFrame->GetContent();
-    if (content) 
-    {
-      nsCOMPtr<nsIDocument> doc = content->GetDocument();
-      if (doc) 
-      {
-        nsCOMPtr<nsIPresShell> presShell = doc->GetPrimaryShell();
-        if (presShell) 
-        {
-          nsEventStatus status = nsEventStatus_eIgnore;
-          nsEvent event(PR_TRUE, NS_FORM_SELECTED);
-
-          presShell->HandleEventWithTarget(&event, mFrame, content, &status);
-        }
-      }
-    }
-  }
-
-  // if the collapsed state did not change, don't fire notifications
-  if (collapsed == mSelectionWasCollapsed)
-    return NS_OK;
-  
-  mSelectionWasCollapsed = collapsed;
-
-  if (!mFrame || !IsFocusedContent(mFrame->GetContent()))
-    return NS_OK;
-
-  return UpdateTextInputCommands(NS_LITERAL_STRING("select"));
-}
-
-// END nsIDOMSelectionListener
-
-// BEGIN nsIDOMKeyListener
-
-NS_IMETHODIMP
-nsTextInputListener::HandleEvent(nsIDOMEvent* aEvent)
-{
-  return NS_OK;
-}
-
-static void
-DoCommandCallback(const char *aCommand, void *aData)
-{
-  nsTextControlFrame *frame = static_cast<nsTextControlFrame*>(aData);
-  nsIContent *content = frame->GetContent();
-
-  nsCOMPtr<nsIControllers> controllers;
-  nsCOMPtr<nsIDOMNSHTMLInputElement> input = do_QueryInterface(content);
-  if (input) {
-    input->GetControllers(getter_AddRefs(controllers));
-  } else {
-    nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textArea =
-      do_QueryInterface(content);
-
-    if (textArea) {
-      textArea->GetControllers(getter_AddRefs(controllers));
-    }
-  }
-
-  if (!controllers) {
-    NS_WARNING("Could not get controllers");
-    return;
-  }
-
-  nsCOMPtr<nsIController> controller;
-  controllers->GetControllerForCommand(aCommand, getter_AddRefs(controller));
-  if (controller) {
-    controller->DoCommand(aCommand);
-  }
-}
-
-
-NS_IMETHODIMP
-nsTextInputListener::KeyDown(nsIDOMEvent *aDOMEvent)
-{
-  nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aDOMEvent));
-  NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
-
-  nsNativeKeyEvent nativeEvent;
-  nsINativeKeyBindings *bindings = GetKeyBindings();
-  if (bindings &&
-      nsContentUtils::DOMEventToNativeKeyEvent(keyEvent, &nativeEvent, PR_FALSE)) {
-    if (bindings->KeyDown(nativeEvent, DoCommandCallback, mFrame)) {
-      aDOMEvent->PreventDefault();
-    }
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsTextInputListener::KeyPress(nsIDOMEvent *aDOMEvent)
-{
-  nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aDOMEvent));
-  NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
-
-  nsNativeKeyEvent nativeEvent;
-  nsINativeKeyBindings *bindings = GetKeyBindings();
-  if (bindings &&
-      nsContentUtils::DOMEventToNativeKeyEvent(keyEvent, &nativeEvent, PR_TRUE)) {
-    if (bindings->KeyPress(nativeEvent, DoCommandCallback, mFrame)) {
-      aDOMEvent->PreventDefault();
-    }
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsTextInputListener::KeyUp(nsIDOMEvent *aDOMEvent)
-{
-  nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aDOMEvent));
-  NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
-
-  nsNativeKeyEvent nativeEvent;
-  nsINativeKeyBindings *bindings = GetKeyBindings();
-  if (bindings &&
-      nsContentUtils::DOMEventToNativeKeyEvent(keyEvent, &nativeEvent, PR_FALSE)) {
-    if (bindings->KeyUp(nativeEvent, DoCommandCallback, mFrame)) {
-      aDOMEvent->PreventDefault();
-    }
-  }
-
-  return NS_OK;
-}
-// END nsIDOMKeyListener
-
-// BEGIN nsIEditorObserver
-
-NS_IMETHODIMP
-nsTextInputListener::EditAction()
-{
-  //
-  // Update the undo / redo menus
-  //
-  nsCOMPtr<nsIEditor> editor;
-  mFrame->GetEditor(getter_AddRefs(editor));
-
-  nsCOMPtr<nsITransactionManager> manager;
-  editor->GetTransactionManager(getter_AddRefs(manager));
-  NS_ENSURE_TRUE(manager, NS_ERROR_FAILURE);
-
-  // Get the number of undo / redo items
-  PRInt32 numUndoItems = 0;
-  PRInt32 numRedoItems = 0;
-  manager->GetNumberOfUndoItems(&numUndoItems);
-  manager->GetNumberOfRedoItems(&numRedoItems);
-  if ((numUndoItems && !mHadUndoItems) || (!numUndoItems && mHadUndoItems) ||
-      (numRedoItems && !mHadRedoItems) || (!numRedoItems && mHadRedoItems)) {
-    // Modify the menu if undo or redo items are different
-    UpdateTextInputCommands(NS_LITERAL_STRING("undo"));
-
-    mHadUndoItems = numUndoItems != 0;
-    mHadRedoItems = numRedoItems != 0;
-  }
-
-  // Make sure we know we were changed (do NOT set this to false if there are
-  // no undo items; JS could change the value and we'd still need to save it)
-  mFrame->SetValueChanged(PR_TRUE);
-
-  // Fire input event
-  mFrame->FireOnInput();
-
-  return NS_OK;
-}
-
-// END nsIEditorObserver
-
-
-nsresult
-nsTextInputListener::UpdateTextInputCommands(const nsAString& commandsToUpdate)
-{
-  NS_ENSURE_STATE(mFrame);
-
-  nsIContent* content = mFrame->GetContent();
-  NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
-  
-  nsCOMPtr<nsIDocument> doc = content->GetDocument();
-  NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
-
-  nsPIDOMWindow *domWindow = doc->GetWindow();
-  NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
-
-  return domWindow->UpdateCommands(commandsToUpdate);
-}
-
-nsINativeKeyBindings*
-nsTextInputListener::GetKeyBindings()
-{
-  if (mFrame->IsTextArea()) {
-    static PRBool sNoTextAreaBindings = PR_FALSE;
-
-    if (!sNativeTextAreaBindings && !sNoTextAreaBindings) {
-      CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "textarea",
-                     &sNativeTextAreaBindings);
-
-      if (!sNativeTextAreaBindings) {
-        sNoTextAreaBindings = PR_TRUE;
-      }
-    }
-
-    return sNativeTextAreaBindings;
-  }
-
-  static PRBool sNoInputBindings = PR_FALSE;
-  if (!sNativeInputBindings && !sNoInputBindings) {
-    CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "input",
-                   &sNativeInputBindings);
-
-    if (!sNativeInputBindings) {
-      sNoInputBindings = PR_TRUE;
-    }
-  }
-
-  return sNativeInputBindings;
-}
-
-// END nsTextInputListener
-
-class nsTextInputSelectionImpl : public nsSupportsWeakReference
-                               , public nsISelectionController
-{
-public:
-  NS_DECL_ISUPPORTS
-
-  nsTextInputSelectionImpl(nsFrameSelection *aSel, nsIPresShell *aShell,
-                           nsIContent *aLimiter);
-  ~nsTextInputSelectionImpl(){}
-
-  void SetScrollableFrame(nsIScrollableFrame *aScrollableFrame)
-  { mScrollFrame = aScrollableFrame; }
-
-  //NSISELECTIONCONTROLLER INTERFACES
-  NS_IMETHOD SetDisplaySelection(PRInt16 toggle);
-  NS_IMETHOD GetDisplaySelection(PRInt16 *_retval);
-  NS_IMETHOD SetSelectionFlags(PRInt16 aInEnable);
-  NS_IMETHOD GetSelectionFlags(PRInt16 *aOutEnable);
-  NS_IMETHOD GetSelection(PRInt16 type, nsISelection **_retval);
-  NS_IMETHOD ScrollSelectionIntoView(PRInt16 aType, PRInt16 aRegion, PRBool aIsSynchronous);
-  NS_IMETHOD RepaintSelection(PRInt16 type);
-  NS_IMETHOD RepaintSelection(nsPresContext* aPresContext, SelectionType aSelectionType);
-  NS_IMETHOD SetCaretEnabled(PRBool enabled);
-  NS_IMETHOD SetCaretReadOnly(PRBool aReadOnly);
-  NS_IMETHOD GetCaretEnabled(PRBool *_retval);
-  NS_IMETHOD GetCaretVisible(PRBool *_retval);
-  NS_IMETHOD SetCaretVisibilityDuringSelection(PRBool aVisibility);
-  NS_IMETHOD CharacterMove(PRBool aForward, PRBool aExtend);
-  NS_IMETHOD CharacterExtendForDelete();
-  NS_IMETHOD WordMove(PRBool aForward, PRBool aExtend);
-  NS_IMETHOD WordExtendForDelete(PRBool aForward);
-  NS_IMETHOD LineMove(PRBool aForward, PRBool aExtend);
-  NS_IMETHOD IntraLineMove(PRBool aForward, PRBool aExtend);
-  NS_IMETHOD PageMove(PRBool aForward, PRBool aExtend);
-  NS_IMETHOD CompleteScroll(PRBool aForward);
-  NS_IMETHOD CompleteMove(PRBool aForward, PRBool aExtend);
-  NS_IMETHOD ScrollPage(PRBool aForward);
-  NS_IMETHOD ScrollLine(PRBool aForward);
-  NS_IMETHOD ScrollHorizontal(PRBool aLeft);
-  NS_IMETHOD SelectAll(void);
-  NS_IMETHOD CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, PRBool *_retval);
-
-private:
-  nsCOMPtr<nsFrameSelection> mFrameSelection;
-  nsCOMPtr<nsIContent>       mLimiter;
-  nsIScrollableFrame        *mScrollFrame;
-  nsWeakPtr mPresShellWeak;
-};
-
-// Implement our nsISupports methods
-NS_IMPL_ISUPPORTS3(nsTextInputSelectionImpl,
-                   nsISelectionController,
-                   nsISelectionDisplay,
-                   nsISupportsWeakReference)
-
-
-// BEGIN nsTextInputSelectionImpl
-
-nsTextInputSelectionImpl::nsTextInputSelectionImpl(nsFrameSelection *aSel,
-                                                   nsIPresShell *aShell,
-                                                   nsIContent *aLimiter)
-  : mScrollFrame(nsnull)
-{
-  if (aSel && aShell)
-  {
-    mFrameSelection = aSel;//we are the owner now!
-    mLimiter = aLimiter;
-    mFrameSelection->Init(aShell, mLimiter);
-    mPresShellWeak = do_GetWeakReference(aShell);
-  }
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::SetDisplaySelection(PRInt16 aToggle)
-{
-  if (!mFrameSelection)
-    return NS_ERROR_NULL_POINTER;
-  
-  mFrameSelection->SetDisplaySelection(aToggle);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::GetDisplaySelection(PRInt16 *aToggle)
-{
-  if (!mFrameSelection)
-    return NS_ERROR_NULL_POINTER;
-
-  *aToggle = mFrameSelection->GetDisplaySelection();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::SetSelectionFlags(PRInt16 aToggle)
-{
-  return NS_OK;//stub this out. not used in input
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::GetSelectionFlags(PRInt16 *aOutEnable)
-{
-  *aOutEnable = nsISelectionDisplay::DISPLAY_TEXT;
-  return NS_OK; 
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::GetSelection(PRInt16 type, nsISelection **_retval)
-{
-  if (!mFrameSelection)
-    return NS_ERROR_NULL_POINTER;
-    
-  *_retval = mFrameSelection->GetSelection(type);
-  
-  if (!(*_retval))
-    return NS_ERROR_FAILURE;
-
-  NS_ADDREF(*_retval);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::ScrollSelectionIntoView(PRInt16 aType, PRInt16 aRegion, PRBool aIsSynchronous)
-{
-  if (!mFrameSelection)
-    return NS_ERROR_FAILURE;
-
-  return mFrameSelection->ScrollSelectionIntoView(aType, aRegion, aIsSynchronous);
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::RepaintSelection(PRInt16 type)
-{
-  if (!mFrameSelection)
-    return NS_ERROR_FAILURE;
-
-  return mFrameSelection->RepaintSelection(type);
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::RepaintSelection(nsPresContext* aPresContext, SelectionType aSelectionType)
-{
-  if (!mFrameSelection)
-    return NS_ERROR_FAILURE;
-
-  return mFrameSelection->RepaintSelection(aSelectionType);
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::SetCaretEnabled(PRBool enabled)
-{
-  if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
-
-  nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak);
-  if (!shell) return NS_ERROR_FAILURE;
-
-  // tell the pres shell to enable the caret, rather than settings its visibility directly.
-  // this way the presShell's idea of caret visibility is maintained.
-  nsCOMPtr<nsISelectionController> selCon = do_QueryInterface(shell);
-  if (!selCon) return NS_ERROR_NO_INTERFACE;
-  selCon->SetCaretEnabled(enabled);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::SetCaretReadOnly(PRBool aReadOnly)
-{
-  if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
-  nsresult result;
-  nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
-  if (shell)
-  {
-    nsRefPtr<nsCaret> caret = shell->GetCaret();
-    if (caret) {
-      nsISelection* domSel = mFrameSelection->
-        GetSelection(nsISelectionController::SELECTION_NORMAL);
-      if (domSel)
-        caret->SetCaretReadOnly(aReadOnly);
-      return NS_OK;
-    }
-  }
-  return NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::GetCaretEnabled(PRBool *_retval)
-{
-  return GetCaretVisible(_retval);
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::GetCaretVisible(PRBool *_retval)
-{
-  if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
-  nsresult result;
-  nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
-  if (shell)
-  {
-    nsRefPtr<nsCaret> caret = shell->GetCaret();
-    if (caret) {
-      nsISelection* domSel = mFrameSelection->
-        GetSelection(nsISelectionController::SELECTION_NORMAL);
-      if (domSel)
-        return caret->GetCaretVisible(_retval);
-    }
-  }
-  return NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::SetCaretVisibilityDuringSelection(PRBool aVisibility)
-{
-  if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
-  nsresult result;
-  nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
-  if (shell)
-  {
-    nsRefPtr<nsCaret> caret = shell->GetCaret();
-    if (caret) {
-      nsISelection* domSel = mFrameSelection->
-        GetSelection(nsISelectionController::SELECTION_NORMAL);
-      if (domSel)
-        caret->SetVisibilityDuringSelection(aVisibility);
-      return NS_OK;
-    }
-  }
-  return NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::CharacterMove(PRBool aForward, PRBool aExtend)
-{
-  if (mFrameSelection)
-    return mFrameSelection->CharacterMove(aForward, aExtend);
-  return NS_ERROR_NULL_POINTER;
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::CharacterExtendForDelete()
-{
-  if (mFrameSelection)
-    return mFrameSelection->CharacterExtendForDelete();
-  return NS_ERROR_NULL_POINTER;
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::WordMove(PRBool aForward, PRBool aExtend)
-{
-  if (mFrameSelection)
-    return mFrameSelection->WordMove(aForward, aExtend);
-  return NS_ERROR_NULL_POINTER;
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::WordExtendForDelete(PRBool aForward)
-{
-  if (mFrameSelection)
-    return mFrameSelection->WordExtendForDelete(aForward);
-  return NS_ERROR_NULL_POINTER;
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::LineMove(PRBool aForward, PRBool aExtend)
-{
-  if (mFrameSelection)
-  {
-    nsresult result = mFrameSelection->LineMove(aForward, aExtend);
-    if (NS_FAILED(result))
-      result = CompleteMove(aForward,aExtend);
-    return result;
-  }
-  return NS_ERROR_NULL_POINTER;
-}
-
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::IntraLineMove(PRBool aForward, PRBool aExtend)
-{
-  if (mFrameSelection)
-    return mFrameSelection->IntraLineMove(aForward, aExtend);
-  return NS_ERROR_NULL_POINTER;
-}
-
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::PageMove(PRBool aForward, PRBool aExtend)
-{
-  // expected behavior for PageMove is to scroll AND move the caret
-  // and to remain relative position of the caret in view. see Bug 4302.
-  if (mScrollFrame)
-  {
-    mFrameSelection->CommonPageMove(aForward, aExtend, mScrollFrame);
-  }
-  // After ScrollSelectionIntoView(), the pending notifications might be
-  // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
-  return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::CompleteScroll(PRBool aForward)
-{
-  if (!mScrollFrame)
-    return NS_ERROR_NOT_INITIALIZED;
-
-  mScrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
-                         nsIScrollableFrame::WHOLE,
-                         nsIScrollableFrame::INSTANT);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::CompleteMove(PRBool aForward, PRBool aExtend)
-{
-  // grab the parent / root DIV for this text widget
-  nsIContent* parentDIV = mFrameSelection->GetLimiter();
-  if (!parentDIV)
-    return NS_ERROR_UNEXPECTED;
-
-  // make the caret be either at the very beginning (0) or the very end
-  PRInt32 offset = 0;
-  nsFrameSelection::HINT hint = nsFrameSelection::HINTLEFT;
-  if (aForward)
-  {
-    offset = parentDIV->GetChildCount();
-
-    // Prevent the caret from being placed after the last
-    // BR node in the content tree!
-
-    if (offset > 0)
-    {
-      nsIContent *child = parentDIV->GetChildAt(offset - 1);
-
-      if (child->Tag() == nsGkAtoms::br)
-      {
-        --offset;
-        hint = nsFrameSelection::HINTRIGHT; // for Bug 106855
-      }
-    }
-  }
-
-  mFrameSelection->HandleClick(parentDIV, offset, offset, aExtend,
-                               PR_FALSE, hint);
-
-  // if we got this far, attempt to scroll no matter what the above result is
-  return CompleteScroll(aForward);
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::ScrollPage(PRBool aForward)
-{
-  if (!mScrollFrame)
-    return NS_ERROR_NOT_INITIALIZED;
-
-  mScrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
-                         nsIScrollableFrame::PAGES,
-                         nsIScrollableFrame::SMOOTH);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::ScrollLine(PRBool aForward)
-{
-  if (!mScrollFrame)
-    return NS_ERROR_NOT_INITIALIZED;
-
-  mScrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
-                         nsIScrollableFrame::LINES,
-                         nsIScrollableFrame::SMOOTH);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::ScrollHorizontal(PRBool aLeft)
-{
-  if (!mScrollFrame)
-    return NS_ERROR_NOT_INITIALIZED;
-
-  // will we have bug #7354 because we aren't forcing an update here?
-  mScrollFrame->ScrollBy(nsIntPoint(aLeft ? -1 : 1, 0),
-                         nsIScrollableFrame::LINES,
-                         nsIScrollableFrame::SMOOTH);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::SelectAll()
-{
-  if (mFrameSelection)
-    return mFrameSelection->SelectAll();
-  return NS_ERROR_NULL_POINTER;
-}
-
-NS_IMETHODIMP
-nsTextInputSelectionImpl::CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, PRBool *_retval)
-{
-  if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
-  nsresult result;
-  nsCOMPtr<nsISelectionController> shell = do_QueryReferent(mPresShellWeak, &result);
-  if (shell)
-  {
-    return shell->CheckVisibility(node,startOffset,EndOffset, _retval);
-  }
-  return NS_ERROR_FAILURE;
-
-}
-
 nsIFrame*
 NS_NewTextControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsTextControlFrame(aPresShell, aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsTextControlFrame)
 
@@ -967,199 +179,54 @@ private:
 };
 #endif
 
 nsTextControlFrame::nsTextControlFrame(nsIPresShell* aShell, nsStyleContext* aContext)
   : nsStackFrame(aShell, aContext)
   , mUseEditor(PR_FALSE)
   , mIsProcessing(PR_FALSE)
   , mNotifyOnInput(PR_TRUE)
-  , mDidPreDestroy(PR_FALSE)
   , mFireChangeEventState(PR_FALSE)
   , mInSecureKeyboardInputMode(PR_FALSE)
 #ifdef DEBUG
   , mInEditorInitialization(PR_FALSE)
 #endif
-  , mTextListener(nsnull)
 {
 }
 
 nsTextControlFrame::~nsTextControlFrame()
 {
-  NS_IF_RELEASE(mTextListener);
-}
-
-static PRBool
-SuppressEventHandlers(nsPresContext* aPresContext)
-{
-  PRBool suppressHandlers = PR_FALSE;
-
-  if (aPresContext)
-  {
-    // Right now we only suppress event handlers and controller manipulation
-    // when in a print preview or print context!
-
-    // In the current implementation, we only paginate when
-    // printing or in print preview.
-
-    suppressHandlers = aPresContext->IsPaginated();
-  }
-
-  return suppressHandlers;
-}
-
-void
-nsTextControlFrame::PreDestroy()
-{
-  // notify the editor that we are going away
-  if (mEditor)
-  {
-    // If we were in charge of state before, relinquish it back
-    // to the control.
-    if (mUseEditor)
-    {
-      // First get the frame state from the editor
-      nsAutoString value;
-      GetValue(value, PR_TRUE);
-
-      mUseEditor = PR_FALSE;
-
-      // Next store the frame state in the control
-      // (now that mUseEditor is false values get stored
-      // in content).
-      SetValue(value);
-
-      // Reset mUseEditor for now, so that if any of the rest of the operation
-      // leads to an attempt at getting the editor, lazy initialization doesn't
-      // kick in.  See bug 557689 for an example of the types of problems this
-      // prevents.
-      mUseEditor = PR_TRUE;
-    }
-    mEditor->PreDestroy(PR_TRUE);
-  }
-  
-  // Clean up the controller
-
-  if (!SuppressEventHandlers(PresContext()))
-  {
-    nsCOMPtr<nsIControllers> controllers;
-    nsCOMPtr<nsIDOMNSHTMLInputElement> inputElement = do_QueryInterface(mContent);
-    if (inputElement)
-      inputElement->GetControllers(getter_AddRefs(controllers));
-    else
-    {
-      nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textAreaElement = do_QueryInterface(mContent);
-      if (textAreaElement) {
-        textAreaElement->GetControllers(getter_AddRefs(controllers));
-      }
-    }
-
-    if (controllers)
-    {
-      PRUint32 numControllers;
-      nsresult rv = controllers->GetControllerCount(&numControllers);
-      NS_ASSERTION((NS_SUCCEEDED(rv)), "bad result in gfx text control destructor");
-      for (PRUint32 i = 0; i < numControllers; i ++)
-      {
-        nsCOMPtr<nsIController> controller;
-        rv = controllers->GetControllerAt(i, getter_AddRefs(controller));
-        if (NS_SUCCEEDED(rv) && controller)
-        {
-          nsCOMPtr<nsIControllerContext> editController = do_QueryInterface(controller);
-          if (editController)
-          {
-            editController->SetCommandContext(nsnull);
-          }
-        }
-      }
-    }
-  }
-
-  mUseEditor = PR_FALSE;
-  mEditor = nsnull;
-  mScrollEvent.Revoke();
-  if (mSelCon) {
-    mSelCon->SetScrollableFrame(nsnull);
-    mSelCon = nsnull;
-  }
-  if (mFrameSel) {
-    mFrameSel->DisconnectFromPresShell();
-    mFrameSel = nsnull;
-  }
-
-  nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), PR_FALSE);
-  if (mTextListener)
-  {
-    mTextListener->SetFrame(nsnull);
-
-    nsCOMPtr<nsIDOMEventGroup> systemGroup;
-    mContent->GetSystemEventGroup(getter_AddRefs(systemGroup));
-    nsCOMPtr<nsIDOM3EventTarget> dom3Targ = do_QueryInterface(mContent);
-    if (dom3Targ) {
-      // cast because of ambiguous base
-      nsIDOMEventListener *listener = static_cast<nsIDOMKeyListener*>
-                                                 (mTextListener);
-
-      dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keydown"),
-                                           listener, PR_FALSE, systemGroup);
-      dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keypress"),
-                                           listener, PR_FALSE, systemGroup);
-      dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keyup"),
-                                           listener, PR_FALSE, systemGroup);
-    }
-  }
-
-  mDidPreDestroy = PR_TRUE; 
 }
 
 void
 nsTextControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
   if (mInSecureKeyboardInputMode) {
     MaybeEndSecureKeyboardInput();
   }
-  if (!mDidPreDestroy) {
-    PreDestroy();
-  }
-  if (mValueDiv && mMutationObserver) {
-    mValueDiv->RemoveMutationObserver(mMutationObserver);
-  }
-  nsContentUtils::DestroyAnonymousContent(&mValueDiv);
-  nsContentUtils::DestroyAnonymousContent(&mPlaceholderDiv);
+
+  mScrollEvent.Revoke();
+
+  // Unbind the text editor state object from the frame.  The editor will live
+  // on, but things like controllers will be released.
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
+  txtCtrl->UnbindFromFrame(this);
+
+  nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), PR_FALSE);
+
   nsBoxFrame::DestroyFrom(aDestructRoot);
 }
 
 nsIAtom*
 nsTextControlFrame::GetType() const 
 { 
   return nsGkAtoms::textInputFrame;
 } 
 
-// XXX: wouldn't it be nice to get this from the style context!
-PRBool nsTextControlFrame::IsSingleLineTextControl() const
-{
-  nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(mContent);
-  if (formControl) {
-    return formControl->IsSingleLineTextControl(PR_FALSE);
-  }
-  return PR_FALSE;
-}
-
-PRBool nsTextControlFrame::IsTextArea() const
-{
-  return mContent && mContent->Tag() == nsGkAtoms::textarea;
-}
-
-// XXX: wouldn't it be nice to get this from the style context!
-PRBool nsTextControlFrame::IsPlainTextControl() const
-{
-  // need to check HTML attribute of mContent and/or CSS.
-  return PR_TRUE;
-}
-
 nsresult nsTextControlFrame::MaybeBeginSecureKeyboardInput()
 {
   nsresult rv = NS_OK;
   if (IsPasswordTextControl() && !mInSecureKeyboardInputMode) {
     nsIWidget* window = GetWindow();
     NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
     rv = window->BeginSecureKeyboardInput();
     mInSecureKeyboardInputMode = NS_SUCCEEDED(rv);
@@ -1173,72 +240,16 @@ void nsTextControlFrame::MaybeEndSecureK
     nsIWidget* window = GetWindow();
     if (!window)
       return;
     window->EndSecureKeyboardInput();
     mInSecureKeyboardInputMode = PR_FALSE;
   }
 }
 
-PRBool nsTextControlFrame::IsPasswordTextControl() const
-{
-  nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(mContent);
-  return formControl && formControl->GetType() == NS_FORM_INPUT_PASSWORD;
-}
-
-
-PRInt32
-nsTextControlFrame::GetCols()
-{
-  nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
-  NS_ASSERTION(content, "Content is not HTML content!");
-
-  if (IsTextArea()) {
-    const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::cols);
-    if (attr) {
-      PRInt32 cols = attr->Type() == nsAttrValue::eInteger ?
-                     attr->GetIntegerValue() : 0;
-      // XXX why a default of 1 char, why hide it
-      return (cols <= 0) ? 1 : cols;
-    }
-  } else {
-    // Else we know (assume) it is an input with size attr
-    const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::size);
-    if (attr && attr->Type() == nsAttrValue::eInteger) {
-      PRInt32 cols = attr->GetIntegerValue();
-      if (cols > 0) {
-        return cols;
-      }
-    }
-  }
-
-  return DEFAULT_COLS;
-}
-
-
-PRInt32
-nsTextControlFrame::GetRows()
-{
-  if (IsTextArea()) {
-    nsGenericHTMLElement *content =
-      nsGenericHTMLElement::FromContent(mContent);
-    NS_ASSERTION(content, "Content is not HTML content!");
-
-    const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::rows);
-    if (attr && attr->Type() == nsAttrValue::eInteger) {
-      PRInt32 rows = attr->GetIntegerValue();
-      return (rows <= 0) ? DEFAULT_ROWS_TEXTAREA : rows;
-    }
-    return DEFAULT_ROWS_TEXTAREA;
-  }
-
-  return DEFAULT_ROWS;
-}
-
-
 nsresult
 nsTextControlFrame::CalcIntrinsicSize(nsIRenderingContext* aRenderingContext,
                                       nsSize&              aIntrinsicSize)
 {
   // Get leading and the Average/MaxAdvance char width 
   nscoord lineHeight  = 0;
   nscoord charWidth   = 0;
   nscoord charMaxAdvance  = 0;
@@ -1321,48 +332,19 @@ nsTextControlFrame::CalcIntrinsicSize(ns
     aIntrinsicSize.width  += scrollbarSizes.LeftRight();
     
     aIntrinsicSize.height += scrollbarSizes.TopBottom();;
   }
 
   return NS_OK;
 }
 
-PRInt32
-nsTextControlFrame::GetWrapCols()
-{
-  if (IsTextArea()) {
-    // wrap=off means -1 for wrap width no matter what cols is
-    nsHTMLTextWrap wrapProp;
-    ::GetWrapPropertyEnum(mContent, wrapProp);
-    if (wrapProp == eHTMLTextWrap_Off) {
-      // do not wrap when wrap=off
-      return -1;
-    }
-   
-    // Otherwise we just wrap at the given number of columns
-    return GetCols();
-  }
-
-  // Never wrap non-textareas
-  return -1;
-}
-
 nsresult
 nsTextControlFrame::EnsureEditorInitialized()
 {
-  nsWeakFrame weakFrame(this);
-  nsresult rv = EnsureEditorInitializedInternal();
-  NS_ENSURE_STATE(weakFrame.IsAlive());
-  return rv;
-}
-
-nsresult
-nsTextControlFrame::EnsureEditorInitializedInternal()
-{
   // This method initializes our editor, if needed.
 
   // This code used to be called from CreateAnonymousContent(), but
   // when the editor set the initial string, it would trigger a
   // PresShell listener which called FlushPendingNotifications()
   // during frame construction. This was causing other form controls
   // to display wrong values.  Additionally, calling this every time
   // a text frame control is instantiated means that we're effectively
@@ -1398,350 +380,93 @@ nsTextControlFrame::EnsureEditorInitiali
   pusher.PushNull();
 
   // Make sure that we try to focus the content even if the method fails
   class EnsureSetFocus {
   public:
     explicit EnsureSetFocus(nsTextControlFrame* aFrame)
       : mFrame(aFrame) {}
     ~EnsureSetFocus() {
-      if (IsFocusedContent(mFrame->GetContent()))
+      if (nsContentUtils::IsFocusedContent(mFrame->GetContent()))
         mFrame->SetFocus(PR_TRUE, PR_FALSE);
     }
   private:
     nsTextControlFrame *mFrame;
   };
   EnsureSetFocus makeSureSetFocusHappens(this);
 
-  // Create an editor
-
-  nsresult rv;
-  mEditor = do_CreateInstance(kTextEditorCID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  
-  // Setup the editor flags
-
-  PRUint32 editorFlags = 0;
-  if (IsPlainTextControl())
-    editorFlags |= nsIPlaintextEditor::eEditorPlaintextMask;
-  if (IsSingleLineTextControl())
-    editorFlags |= nsIPlaintextEditor::eEditorSingleLineMask;
-  if (IsPasswordTextControl())
-    editorFlags |= nsIPlaintextEditor::eEditorPasswordMask;
-
-  // All nsTextControlFrames are widgets
-  editorFlags |= nsIPlaintextEditor::eEditorWidgetMask;
-
-  // Use async reflow and painting for text widgets to improve
-  // performance.
-
-  // XXX: Using editor async updates exposes bugs 158782, 151882,
-  //      and 165130, so we're disabling it for now, until they
-  //      can be addressed.
-  // editorFlags |= nsIPlaintextEditor::eEditorUseAsyncUpdatesMask;
-
-  // Now initialize the editor.
-  //
-  // NOTE: Conversion of '\n' to <BR> happens inside the
-  //       editor's Init() call.
-
-  nsPresContext *presContext = PresContext();
-  nsIPresShell *shell = presContext->GetPresShell();
-
-  // Get the DOM document
-  nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(shell->GetDocument());
-  if (!domdoc)
-    return NS_ERROR_FAILURE;
-
-  // Make sure we clear out the non-breaking space before we initialize the editor
-  UpdateValueDisplay(PR_FALSE, PR_TRUE);
-
-  rv = mEditor->Init(domdoc, shell, mValueDiv, mSelCon, editorFlags);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Initialize the controller for the editor
-
-  if (!SuppressEventHandlers(presContext)) {
-    nsCOMPtr<nsIControllers> controllers;
-    nsCOMPtr<nsIDOMNSHTMLInputElement> inputElement =
-      do_QueryInterface(mContent);
-    if (inputElement) {
-      rv = inputElement->GetControllers(getter_AddRefs(controllers));
-    } else {
-      nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textAreaElement =
-        do_QueryInterface(mContent);
-
-      if (!textAreaElement)
-        return NS_ERROR_FAILURE;
-
-      rv = textAreaElement->GetControllers(getter_AddRefs(controllers));
-    }
-
-    if (NS_FAILED(rv))
-      return rv;
-
-    if (controllers) {
-      PRUint32 numControllers;
-      PRBool found = PR_FALSE;
-      rv = controllers->GetControllerCount(&numControllers);
-      for (PRUint32 i = 0; i < numControllers; i ++) {
-        nsCOMPtr<nsIController> controller;
-        rv = controllers->GetControllerAt(i, getter_AddRefs(controller));
-        if (NS_SUCCEEDED(rv) && controller) {
-          nsCOMPtr<nsIControllerContext> editController =
-            do_QueryInterface(controller);
-          if (editController) {
-            editController->SetCommandContext(mEditor);
-            found = PR_TRUE;
-          }
-        }
-      }
-      if (!found)
-        rv = NS_ERROR_FAILURE;
-    }
-  }
-
-  // Initialize the plaintext editor
-  nsCOMPtr<nsIPlaintextEditor> textEditor(do_QueryInterface(mEditor));
-  if (textEditor) {
-    // Set up wrapping
-    textEditor->SetWrapColumn(GetWrapCols());
-
-    // Set max text field length
-    PRInt32 maxLength;
-    if (GetMaxLength(&maxLength)) { 
-      textEditor->SetMaxTextLength(maxLength);
-    }
-  }
-  
-  if (mContent) {
-    rv = mEditor->GetFlags(&editorFlags);
-
-    if (NS_FAILED(rv))
-      return nsnull;
-
-    // Check if the readonly attribute is set.
-
-    if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly))
-      editorFlags |= nsIPlaintextEditor::eEditorReadonlyMask;
-
-    // Check if the disabled attribute is set.
-
-    if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) 
-      editorFlags |= nsIPlaintextEditor::eEditorDisabledMask;
-
-    // Disable the selection if necessary.
-
-    if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
-      mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
-
-    mEditor->SetFlags(editorFlags);
-  }
-
-  // Get the current value of the textfield from the content.
-  nsAutoString defaultValue;
-  GetValue(defaultValue, PR_TRUE);
-
-  // Turn on mUseEditor so that subsequent calls will use the
-  // editor.
-  mUseEditor = PR_TRUE;
-
 #ifdef DEBUG
   // Make sure we are not being called again until we're finished.
   // If reentrancy happens, just pretend that we don't have an editor.
   const EditorInitializerEntryTracker tracker(*this);
   NS_ASSERTION(!tracker.EnteredMoreThanOnce(),
                "EnsureEditorInitialized has been called while a previous call was in progress");
 #endif
 
-  // If we have a default value, insert it under the div we created
-  // above, but be sure to use the editor so that '*' characters get
-  // displayed for password fields, etc. SetValue() will call the
-  // editor for us.
-
-  if (!defaultValue.IsEmpty()) {
-    // Avoid causing reentrant painting and reflowing by telling the editor
-    // that we don't want it to force immediate view refreshes or force
-    // immediate reflows during any editor calls.
-
-    rv = mEditor->SetFlags(editorFlags |
-                           nsIPlaintextEditor::eEditorUseAsyncUpdatesMask);
-
-    if (NS_FAILED(rv))
-      return rv;
-
-    // Now call SetValue() which will make the necessary editor calls to set
-    // the default value.  Make sure to turn off undo before setting the default
-    // value, and turn it back on afterwards. This will make sure we can't undo
-    // past the default value.
-
-    rv = mEditor->EnableUndo(PR_FALSE);
-
-    if (NS_FAILED(rv))
-      return rv;
-
-    SetValue(defaultValue);
-
-    rv = mEditor->EnableUndo(PR_TRUE);
-    NS_ASSERTION(NS_SUCCEEDED(rv),"Transaction Manager must have failed");
+  // Create an editor for the frame, if one doesn't already exist
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
+  nsresult rv = txtCtrl->CreateEditor();
+  NS_ENSURE_SUCCESS(rv, rv);
 
-    // Now restore the original editor flags.
-    rv = mEditor->SetFlags(editorFlags);
-
-    if (NS_FAILED(rv))
-      return rv;
-
-    // By default the placeholder is shown,
-    // we should hide it if the default value is not empty.
-    nsWeakFrame weakFrame(this);
-    HidePlaceholder();
-    NS_ENSURE_STATE(weakFrame.IsAlive());
-  }
-
-  nsCOMPtr<nsITransactionManager> transMgr;
-  mEditor->GetTransactionManager(getter_AddRefs(transMgr));
-  NS_ENSURE_TRUE(transMgr, NS_ERROR_FAILURE);
-
-  transMgr->SetMaxTransactionCount(DEFAULT_UNDO_CAP);
-
-  if (IsPasswordTextControl()) {
-    // Disable undo for password textfields.  Note that we want to do this at
-    // the very end of InitEditor, so the calls to EnableUndo when setting the
-    // default value don't screw us up.
-    // Since changing the control type does a reframe, we don't have to worry
-    // about dynamic type changes here.
-    mEditor->EnableUndo(PR_FALSE);
-  }
-
-  mEditor->PostCreate();
-
-  if (mTextListener)
-    mEditor->AddEditorObserver(mTextListener);
+  // Turn on mUseEditor so that subsequent calls will use the
+  // editor.
+  mUseEditor = PR_TRUE;
 
   return NS_OK;
 }
 
 nsresult
 nsTextControlFrame::CreateAnonymousContent(nsTArray<nsIContent*>& aElements)
 {
   mState |= NS_FRAME_INDEPENDENT_SELECTION;
 
-  nsIPresShell *shell = PresContext()->GetPresShell();
-  if (!shell)
-    return NS_ERROR_FAILURE;
-
-  nsIDocument *doc = shell->GetDocument();
-  if (!doc)
-    return NS_ERROR_FAILURE;
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
 
-  // Now create a DIV and add it to the anonymous content child list.
-  nsCOMPtr<nsINodeInfo> nodeInfo;
-  nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::div, nsnull,
-                                                 kNameSpaceID_XHTML);
-  NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
-
-  nsresult rv = NS_NewHTMLElement(getter_AddRefs(mValueDiv), nodeInfo, PR_FALSE);
+  // Bind the frame to its text control
+  nsresult rv = txtCtrl->BindToFrame(this);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Set the necessary classes on the text control. We use class values
-  // instead of a 'style' attribute so that the style comes from a user-agent
-  // style sheet and is still applied even if author styles are disabled.
-  nsAutoString classValue;
-  classValue.AppendLiteral("anonymous-div");
-  PRInt32 wrapCols = GetWrapCols();
-  if (wrapCols >= 0) {
-    classValue.AppendLiteral(" wrap");
-  }
-  if (!IsSingleLineTextControl()) {
-    // We can't just inherit the overflow because setting visible overflow will
-    // crash when the number of lines exceeds the height of the textarea and
-    // setting -moz-hidden-unscrollable overflow (NS_STYLE_OVERFLOW_CLIP)
-    // doesn't paint the caret for some reason.
-    const nsStyleDisplay* disp = GetStyleDisplay();
-    if (disp->mOverflowX != NS_STYLE_OVERFLOW_VISIBLE &&
-        disp->mOverflowX != NS_STYLE_OVERFLOW_CLIP) {
-      classValue.AppendLiteral(" inherit-overflow");
-    }
+  nsIContent* rootNode = txtCtrl->GetRootEditorNode();
+  NS_ENSURE_TRUE(rootNode, NS_ERROR_OUT_OF_MEMORY);
 
-    mMutationObserver = new nsAnonDivObserver(this);
-    NS_ENSURE_TRUE(mMutationObserver, NS_ERROR_OUT_OF_MEMORY);
-    mValueDiv->AddMutationObserver(mMutationObserver);
-  }
-  rv = mValueDiv->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
-                          classValue, PR_FALSE);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!aElements.AppendElement(mValueDiv))
+  if (!aElements.AppendElement(rootNode))
     return NS_ERROR_OUT_OF_MEMORY;
 
-  // Now create the placeholder anonymous content
-  rv = CreatePlaceholderDiv(aElements, doc->NodeInfoManager());
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsIContent* placeholderNode = txtCtrl->GetPlaceholderNode();
+  NS_ENSURE_TRUE(placeholderNode, NS_ERROR_OUT_OF_MEMORY);
+
+  if (!aElements.AppendElement(placeholderNode))
+    return NS_ERROR_OUT_OF_MEMORY;
 
   rv = UpdateValueDisplay(PR_FALSE);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Create selection
-
-  mFrameSel = do_CreateInstance(kFrameSelectionCID, &rv);
-  if (NS_FAILED(rv))
-    return rv;
-
-  // Create a SelectionController
-
-  mSelCon = new nsTextInputSelectionImpl(mFrameSel, shell,
-                                         mValueDiv);
-  if (!mSelCon)
-    return NS_ERROR_OUT_OF_MEMORY;
-  mTextListener = new nsTextInputListener();
-  if (!mTextListener)
-    return NS_ERROR_OUT_OF_MEMORY;
-  NS_ADDREF(mTextListener);
-
-  mTextListener->SetFrame(this);
-  mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
-
-  // Get the caret and make it a selection listener.
-
-  nsRefPtr<nsISelection> domSelection;
-  if (NS_SUCCEEDED(mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
-                                         getter_AddRefs(domSelection))) &&
-      domSelection) {
-    nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(domSelection));
-    nsRefPtr<nsCaret> caret = shell->GetCaret();
-    nsCOMPtr<nsISelectionListener> listener;
-    if (caret) {
-      listener = do_QueryInterface(caret);
-      if (listener) {
-        selPriv->AddSelectionListener(listener);
-      }
-    }
-
-    selPriv->AddSelectionListener(static_cast<nsISelectionListener*>
-                                             (mTextListener));
-  }
-
   if (!IsSingleLineTextControl()) {
     // textareas are eagerly initialized
     NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
                  "Someone forgot a script blocker?");
 
     if (!nsContentUtils::AddScriptRunner(new EditorInitializer(this))) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
   return NS_OK;
 }
 
 void
 nsTextControlFrame::AppendAnonymousContentTo(nsBaseContentList& aElements)
 {
-  aElements.MaybeAppendElement(mValueDiv);
-  aElements.MaybeAppendElement(mPlaceholderDiv);
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
+
+  aElements.MaybeAppendElement(txtCtrl->GetRootEditorNode());
+  aElements.MaybeAppendElement(txtCtrl->GetPlaceholderNode());
 }
 
 nscoord
 nsTextControlFrame::GetMinWidth(nsIRenderingContext* aRenderingContext)
 {
   // Our min width is just our preferred width if we have auto width.
   nscoord result;
   DISPLAY_MIN_WIDTH(this, result);
@@ -1889,65 +614,75 @@ PRBool
 nsTextControlFrame::IsLeaf() const
 {
   return PR_TRUE;
 }
 
 NS_IMETHODIMP
 nsTextControlFrame::ScrollOnFocusEvent::Run()
 {
-  if (mFrame && mFrame->mSelCon) {
-    mFrame->mScrollEvent.Forget();
-    mFrame->mSelCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
-                                             nsISelectionController::SELECTION_FOCUS_REGION,
-                                             PR_TRUE);
+  if (mFrame) {
+    nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(mFrame->GetContent());
+    NS_ASSERTION(txtCtrl, "Content not a text control element");
+    nsISelectionController* selCon = txtCtrl->GetSelectionController();
+    if (selCon) {
+      mFrame->mScrollEvent.Forget();
+      selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
+                                      nsISelectionController::SELECTION_FOCUS_REGION,
+                                      PR_TRUE);
+    }
   }
   return NS_OK;
 }
 
 //IMPLEMENTING NS_IFORMCONTROLFRAME
 void nsTextControlFrame::SetFocus(PRBool aOn, PRBool aRepaint)
 {
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
+
   // Revoke the previous scroll event if one exists
   mScrollEvent.Revoke();
 
   if (!aOn) {
     nsWeakFrame weakFrame(this);
 
-    nsAutoString valueString;
-    GetValue(valueString, PR_TRUE);
-    if (valueString.IsEmpty())
-      ShowPlaceholder();
+    PRInt32 length;
+    nsresult rv = GetTextLength(&length);
+    NS_ENSURE_SUCCESS(rv, );
+    if (!length)
+      txtCtrl->SetPlaceholderClass(PR_TRUE, PR_TRUE);
 
     if (!weakFrame.IsAlive())
     {
       return;
     }
 
     MaybeEndSecureKeyboardInput();
     return;
   }
 
-  if (!mSelCon)
+  nsISelectionController* selCon = txtCtrl->GetSelectionController();
+  if (!selCon)
     return;
 
   nsWeakFrame weakFrame(this);
 
-  HidePlaceholder();
+  txtCtrl->SetPlaceholderClass(PR_FALSE, PR_TRUE);
 
   if (!weakFrame.IsAlive())
   {
     return;
   }
 
   if (NS_SUCCEEDED(InitFocusedValue()))
     MaybeBeginSecureKeyboardInput();
 
   nsCOMPtr<nsISelection> ourSel;
-  mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, 
+  selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, 
     getter_AddRefs(ourSel));
   if (!ourSel) return;
 
   nsIPresShell* presShell = PresContext()->GetPresShell();
   nsRefPtr<nsCaret> caret = presShell->GetCaret();
   if (!caret) return;
 
   // Scroll the current selection into view
@@ -1963,106 +698,84 @@ void nsTextControlFrame::SetFocus(PRBool
 
   // tell the caret to use our selection
   caret->SetCaretDOMSelection(ourSel);
 
   // mutual-exclusion: the selection is either controlled by the
   // document or by the text input/area. Clear any selection in the
   // document since the focus is now on our independent selection.
 
-  nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(presShell));
+  nsCOMPtr<nsISelectionController> selcon = do_QueryInterface(presShell);
   nsCOMPtr<nsISelection> docSel;
-  selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
+  selcon->GetSelection(nsISelectionController::SELECTION_NORMAL,
     getter_AddRefs(docSel));
   if (!docSel) return;
 
   PRBool isCollapsed = PR_FALSE;
   docSel->GetIsCollapsed(&isCollapsed);
   if (!isCollapsed)
     docSel->RemoveAllRanges();
 }
 
 nsresult nsTextControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue)
 {
   if (!mIsProcessing)//some kind of lock.
   {
     mIsProcessing = PR_TRUE;
-    PRBool isUserInput = (nsGkAtoms::userInput == aName);
-    if (nsGkAtoms::value == aName || isUserInput) 
-    {
-      PRBool fireChangeEvent = GetFireChangeEventState();
-      if (isUserInput) {
-        SetFireChangeEventState(PR_TRUE);
-      }
-      SetValueChanged(PR_TRUE);
-      nsresult rv = SetValue(aValue); // set new text value
-      if (isUserInput) {
-        SetFireChangeEventState(fireChangeEvent);
-      }
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-    else if (nsGkAtoms::select == aName)
+    if (nsGkAtoms::select == aName)
     {
       // Select all the text.
       //
-      // XXX: This is lame, we can't call mEditor->SelectAll()
+      // XXX: This is lame, we can't call editor's SelectAll method
       //      because that triggers AutoCopies in unix builds.
       //      Instead, we have to call our own homegrown version
       //      of select all which merely builds a range that selects
       //      all of the content and adds that to the selection.
 
       SelectAllOrCollapseToEndOfText(PR_TRUE);
     }
     mIsProcessing = PR_FALSE;
   }
   return NS_OK;
 }
 
 nsresult
 nsTextControlFrame::GetFormProperty(nsIAtom* aName, nsAString& aValue) const
 {
-  // Return the value of the property from the widget it is not null.
-  // If widget is null, assume the widget is GFX-rendered and return a member variable instead.
-
-  if (nsGkAtoms::value == aName) {
-    GetValue(aValue, PR_FALSE);
-  }
+  NS_ASSERTION(nsGkAtoms::value != aName,
+               "Should get the value from the content node instead");
   return NS_OK;
 }
 
 
 
 NS_IMETHODIMP
 nsTextControlFrame::GetEditor(nsIEditor **aEditor)
 {
   NS_ENSURE_ARG_POINTER(aEditor);
 
   nsresult rv = EnsureEditorInitialized();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  *aEditor = mEditor;
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
+  *aEditor = txtCtrl->GetTextEditor();
   NS_IF_ADDREF(*aEditor);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsTextControlFrame::OwnsValue(PRBool* aOwnsValue)
-{
-  NS_PRECONDITION(aOwnsValue, "aOwnsValue must be non-null");
-  *aOwnsValue = mUseEditor;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsTextControlFrame::GetTextLength(PRInt32* aTextLength)
 {
   NS_ENSURE_ARG_POINTER(aTextLength);
 
   nsAutoString   textContents;
-  GetValue(textContents, PR_FALSE);   // this is expensive!
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
+  txtCtrl->GetTextEditorValue(textContents, PR_FALSE);   // this is expensive!
   *aTextLength = textContents.Length();
   return NS_OK;
 }
 
 nsresult
 nsTextControlFrame::SetSelectionInternal(nsIDOMNode *aStartNode,
                                          PRInt32 aStartOffset,
                                          nsIDOMNode *aEndNode,
@@ -2077,42 +790,57 @@ nsTextControlFrame::SetSelectionInternal
 
   nsresult rv = range->SetStart(aStartNode, aStartOffset);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = range->SetEnd(aEndNode, aEndOffset);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Get the selection, clear it and add the new range to it!
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
+  nsISelectionController* selCon = txtCtrl->GetSelectionController();
+  NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsISelection> selection;
-  mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(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);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Scroll the selection into view (see bug 231389)
-  return mSelCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
-                                          nsISelectionController::SELECTION_FOCUS_REGION,
-                                          PR_FALSE);
+  return selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
+                                         nsISelectionController::SELECTION_FOCUS_REGION,
+                                         PR_FALSE);
+}
+
+nsresult
+nsTextControlFrame::GetRootNodeAndInitializeEditor(nsIDOMElement **aRootElement)
+{
+  NS_ENSURE_ARG_POINTER(aRootElement);
+
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
+  nsIEditor* editor = txtCtrl->GetTextEditor();
+  if (!editor)
+    return NS_OK;
+
+  return editor->GetRootElement(aRootElement);
 }
 
 nsresult
 nsTextControlFrame::SelectAllOrCollapseToEndOfText(PRBool aSelect)
 {
-  if (!mEditor)
-    return NS_OK;
-
   nsCOMPtr<nsIDOMElement> rootElement;
-  nsresult rv = mEditor->GetRootElement(getter_AddRefs(rootElement));
+  nsresult rv = GetRootNodeAndInitializeEditor(getter_AddRefs(rootElement));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIContent> rootContent = do_QueryInterface(rootElement);
   nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
   PRInt32 numChildren = rootContent->GetChildCount();
 
   if (numChildren > 0) {
     // We never want to place the selection after the last
@@ -2233,21 +961,18 @@ nsresult
 nsTextControlFrame::DOMPointToOffset(nsIDOMNode* aNode,
                                      PRInt32 aNodeOffset,
                                      PRInt32* aResult)
 {
   NS_ENSURE_ARG_POINTER(aNode && aResult);
 
   *aResult = 0;
 
-  nsresult rv = EnsureEditorInitialized();
-  NS_ENSURE_SUCCESS(rv, rv);
-
   nsCOMPtr<nsIDOMElement> rootElement;
-  mEditor->GetRootElement(getter_AddRefs(rootElement));
+  nsresult rv = GetRootNodeAndInitializeEditor(getter_AddRefs(rootElement));
   nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
 
   NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIDOMNodeList> nodeList;
 
   rv = rootNode->GetChildNodes(getter_AddRefs(nodeList));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -2313,21 +1038,19 @@ nsTextControlFrame::OffsetToDOMPoint(PRI
                                      nsIDOMNode** aResult,
                                      PRInt32* aPosition)
 {
   NS_ENSURE_ARG_POINTER(aResult && aPosition);
 
   *aResult = nsnull;
   *aPosition = 0;
 
-  nsresult rv = EnsureEditorInitialized();
+  nsCOMPtr<nsIDOMElement> rootElement;
+  nsresult rv = GetRootNodeAndInitializeEditor(getter_AddRefs(rootElement));
   NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIDOMElement> rootElement;
-  mEditor->GetRootElement(getter_AddRefs(rootElement));
   nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
 
   NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIDOMNodeList> nodeList;
 
   rv = rootNode->GetChildNodes(getter_AddRefs(nodeList));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -2410,18 +1133,22 @@ nsTextControlFrame::GetSelectionRange(PR
 {
   // make sure we have an editor
   nsresult rv = EnsureEditorInitialized();
   NS_ENSURE_SUCCESS(rv, rv);
 
   *aSelectionStart = 0;
   *aSelectionEnd = 0;
 
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
+  nsISelectionController* selCon = txtCtrl->GetSelectionController();
+  NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
   nsCOMPtr<nsISelection> selection;
-  rv = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));  
+  rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));  
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
 
   PRInt32 numRanges = 0;
   selection->GetRangeCount(&numRanges);
 
   if (numRanges < 1)
     return NS_OK;
@@ -2459,99 +1186,105 @@ nsTextControlFrame::GetSelectionRange(PR
   rv = DOMPointToOffset(startNode, startOffset, aSelectionStart);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Convert the end point to a selection offset.
 
   return DOMPointToOffset(endNode, endOffset, aSelectionEnd);
 }
 
-nsISelectionController*
-nsTextControlFrame::GetOwnedSelectionController()
-{
-  return mSelCon;
-}
-
 /////END INTERFACE IMPLEMENTATIONS
 
 ////NSIFRAME
 NS_IMETHODIMP
 nsTextControlFrame::AttributeChanged(PRInt32         aNameSpaceID,
                                      nsIAtom*        aAttribute,
                                      PRInt32         aModType)
 {
   // First, check for the placeholder attribute, because it doesn't
   // depend on the editor being present.
   if (nsGkAtoms::placeholder == aAttribute)
   {
     nsWeakFrame weakFrame(this);
-    UpdatePlaceholderText(PR_TRUE);
+    nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+    NS_ASSERTION(txtCtrl, "Content not a text control element");
+    txtCtrl->UpdatePlaceholderText(PR_TRUE);
     NS_ENSURE_STATE(weakFrame.IsAlive());
   }
 
-  if (!mEditor || !mSelCon) 
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
+  nsISelectionController* selCon = txtCtrl->GetSelectionController();
+  const PRBool needEditor = nsGkAtoms::maxlength == aAttribute ||
+                            nsGkAtoms::readonly == aAttribute ||
+                            nsGkAtoms::disabled == aAttribute;
+  nsIEditor *editor = nsnull;
+  if (needEditor) {
+    editor = txtCtrl->GetTextEditor();
+  }
+  if ((needEditor && !editor) || !selCon)
     return nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);;
 
   nsresult rv = NS_OK;
 
   if (nsGkAtoms::maxlength == aAttribute) 
   {
     PRInt32 maxLength;
     PRBool maxDefined = GetMaxLength(&maxLength);
     
-    nsCOMPtr<nsIPlaintextEditor> textEditor = do_QueryInterface(mEditor);
+    nsCOMPtr<nsIPlaintextEditor> textEditor = do_QueryInterface(editor);
     if (textEditor)
     {
       if (maxDefined) 
       {  // set the maxLength attribute
           textEditor->SetMaxTextLength(maxLength);
         // if maxLength>docLength, we need to truncate the doc content
       }
       else { // unset the maxLength attribute
           textEditor->SetMaxTextLength(-1);
       }
     }
     rv = NS_OK; // don't propagate the error
   } 
   else if (nsGkAtoms::readonly == aAttribute) 
   {
     PRUint32 flags;
-    mEditor->GetFlags(&flags);
+    editor->GetFlags(&flags);
     if (AttributeExists(nsGkAtoms::readonly))
     { // set readonly
       flags |= nsIPlaintextEditor::eEditorReadonlyMask;
-      if (IsFocusedContent(mContent))
-        mSelCon->SetCaretEnabled(PR_FALSE);
+      if (nsContentUtils::IsFocusedContent(mContent))
+        selCon->SetCaretEnabled(PR_FALSE);
     }
     else 
     { // unset readonly
       flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
       if (!(flags & nsIPlaintextEditor::eEditorDisabledMask) &&
-          IsFocusedContent(mContent))
-        mSelCon->SetCaretEnabled(PR_TRUE);
+          nsContentUtils::IsFocusedContent(mContent))
+        selCon->SetCaretEnabled(PR_TRUE);
     }
-    mEditor->SetFlags(flags);
+    editor->SetFlags(flags);
   }
   else if (nsGkAtoms::disabled == aAttribute) 
   {
     PRUint32 flags;
-    mEditor->GetFlags(&flags);
+    editor->GetFlags(&flags);
     if (AttributeExists(nsGkAtoms::disabled))
     { // set disabled
       flags |= nsIPlaintextEditor::eEditorDisabledMask;
-      mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
-      if (IsFocusedContent(mContent))
-        mSelCon->SetCaretEnabled(PR_FALSE);
+      selCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
+      if (nsContentUtils::IsFocusedContent(mContent))
+        selCon->SetCaretEnabled(PR_FALSE);
     }
     else 
     { // unset disabled
       flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
-      mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
+      selCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
     }
-    mEditor->SetFlags(flags);
+    editor->SetFlags(flags);
   }
   else if (!mUseEditor && nsGkAtoms::value == aAttribute) {
     UpdateValueDisplay(PR_TRUE);
   }
   // Allow the base class to handle common attributes supported
   // by all form elements... 
   else {
     rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
@@ -2560,39 +1293,42 @@ nsTextControlFrame::AttributeChanged(PRI
   return rv;
 }
 
 
 nsresult
 nsTextControlFrame::GetText(nsString& aText)
 {
   nsresult rv = NS_OK;
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
   if (IsSingleLineTextControl()) {
     // If we're going to remove newlines anyway, ignore the wrap property
-    GetValue(aText, PR_TRUE);
+    txtCtrl->GetTextEditorValue(aText, PR_TRUE);
     nsContentUtils::RemoveNewlines(aText);
   } else {
     nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(mContent);
     if (textArea) {
       rv = textArea->GetValue(aText);
     }
   }
   return rv;
 }
 
 
 nsresult
 nsTextControlFrame::GetPhonetic(nsAString& aPhonetic)
 {
   aPhonetic.Truncate(0); 
 
-  nsresult rv = EnsureEditorInitialized();
+  nsCOMPtr<nsIEditor> editor;
+  nsresult rv = GetEditor(getter_AddRefs(editor));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIEditorIMESupport> imeSupport = do_QueryInterface(mEditor);
+  nsCOMPtr<nsIEditorIMESupport> imeSupport = do_QueryInterface(editor);
   if (imeSupport) {
     nsCOMPtr<nsIPhonetic> phonetic = do_QueryInterface(imeSupport);
     if (phonetic)
       phonetic->GetPhonetic(aPhonetic);
   }
   return NS_OK;
 }
 
@@ -2651,498 +1387,130 @@ nsTextControlFrame::CheckFireOnChange()
     nsEventStatus status = nsEventStatus_eIgnore;
     nsInputEvent event(PR_TRUE, NS_FORM_CHANGE, nsnull);
     nsCOMPtr<nsIPresShell> shell = PresContext()->PresShell();
     shell->HandleEventWithTarget(&event, nsnull, mContent, &status);
   }
   return NS_OK;
 }
 
-//======
-//privates
-
-NS_IMETHODIMP
-nsTextControlFrame::GetValue(nsAString& aValue, PRBool aIgnoreWrap) const
-{
-  aValue.Truncate();  // initialize out param
-  nsresult rv = NS_OK;
-  
-  if (mEditor && mUseEditor) 
-  {
-    PRBool canCache = aIgnoreWrap && !IsSingleLineTextControl();
-    if (canCache && !mCachedValue.IsEmpty()) {
-      aValue = mCachedValue;
-      return NS_OK;
-    }
-
-    PRUint32 flags = (nsIDocumentEncoder::OutputLFLineBreak |
-                      nsIDocumentEncoder::OutputPreformatted |
-                      nsIDocumentEncoder::OutputPersistNBSP);
-
-    if (PR_TRUE==IsPlainTextControl())
-    {
-      flags |= nsIDocumentEncoder::OutputBodyOnly;
-    }
-
-    if (!aIgnoreWrap) {
-      nsHTMLTextWrap wrapProp;
-      if (::GetWrapPropertyEnum(mContent, wrapProp) &&
-          wrapProp == eHTMLTextWrap_Hard) {
-        flags |= nsIDocumentEncoder::OutputWrap;
-      }
-    }
-
-    // What follows is a bit of a hack.  The problem is that we could be in
-    // this method because we're being destroyed for whatever reason while
-    // script is executing.  If that happens, editor will run with the
-    // privileges of the executing script, which means it may not be able to
-    // access its own DOM nodes!  Let's try to deal with that by pushing a null
-    // JSContext on the JSContext stack to make it clear that we're native
-    // code.  Note that any script that's directly trying to access our value
-    // has to be going through some scriptable object to do that and that
-    // already does the relevant security checks.
-    // XXXbz if we could just get the textContent of our anonymous content (eg
-    // if plaintext editor didn't create <br> nodes all over), we wouldn't need
-    // this.
-    { /* Scope for context pusher */
-      nsCxPusher pusher;
-      pusher.PushNull();
-      
-      rv = mEditor->OutputToString(NS_LITERAL_STRING("text/plain"), flags,
-                                   aValue);
-    }
-    if (canCache) {
-      const_cast<nsTextControlFrame*>(this)->mCachedValue = aValue;
-    } else {
-      const_cast<nsTextControlFrame*>(this)->mCachedValue.Truncate();
-    }
-  }
-  else
-  {
-    // Otherwise get the value from content.
-    nsCOMPtr<nsIDOMHTMLInputElement> inputControl = do_QueryInterface(mContent);
-    if (inputControl)
-    {
-      rv = inputControl->GetValue(aValue);
-    }
-    else
-    {
-      nsCOMPtr<nsIDOMHTMLTextAreaElement> textareaControl
-          = do_QueryInterface(mContent);
-      if (textareaControl)
-      {
-        rv = textareaControl->GetValue(aValue);
-      }
-    }
-  }
-
-  return rv;
-}
-
-
 // END IMPLEMENTING NS_IFORMCONTROLFRAME
 
-nsresult
-nsTextControlFrame::SetValue(const nsAString& aValue)
-{
-  // XXX this method should actually propagate errors!  It'd make debugging it
-  // so much easier...
-  if (mEditor && mUseEditor) 
-  {
-    // This method isn't used for user-generated changes, except for calls
-    // from nsFileControlFrame which sets mFireChangeEventState==true and
-    // restores it afterwards (ie. we want 'change' events for those changes).
-    // Focused value must be updated to prevent incorrect 'change' events,
-    // but only if user hasn't changed the value.
-
-    // GetText removes newlines from single line control.
-    nsString currentValue;
-    GetText(currentValue);
-    PRBool focusValueInit = !mFireChangeEventState &&
-      mFocusedValue.Equals(currentValue);
-
-    nsCOMPtr<nsIEditor> editor = mEditor;
-    nsWeakFrame weakFrame(this);
-
-    // this is necessary to avoid infinite recursion
-    if (!currentValue.Equals(aValue))
-    {
-      // \r is an illegal character in the dom, but people use them,
-      // so convert windows and mac platform linebreaks to \n:
-      // Unfortunately aValue is declared const, so we have to copy
-      // in order to do this substitution.
-      nsString newValue(aValue);
-      nsContentUtils::PlatformToDOMLineBreaks(newValue);
-
-      nsCOMPtr<nsIDOMDocument> domDoc;
-      editor->GetDocument(getter_AddRefs(domDoc));
-      NS_ENSURE_STATE(domDoc);
-
-      PRBool outerTransaction;
-      // Time to mess with our security context... See comments in GetValue()
-      // for why this is needed.  Note that we have to do this up here, because
-      // otherwise SelectAll() will fail.
-      { /* Scope for context pusher */
-        nsCxPusher pusher;
-        pusher.PushNull();
-
-        nsCOMPtr<nsISelection> domSel;
-        nsCOMPtr<nsISelectionPrivate> selPriv;
-        mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
-                              getter_AddRefs(domSel));
-        if (domSel)
-        {
-          selPriv = do_QueryInterface(domSel);
-          if (selPriv)
-            selPriv->StartBatchChanges();
-        }
-
-        nsCOMPtr<nsISelectionController> kungFuDeathGrip = mSelCon.get();
-        PRUint32 currentLength = currentValue.Length();
-        PRUint32 newlength = newValue.Length();
-        if (!currentLength ||
-            !StringBeginsWith(newValue, currentValue)) {
-          // Replace the whole text.
-          currentLength = 0;
-          mSelCon->SelectAll();
-        } else {
-          // Collapse selection to the end so that we can append data.
-          SelectAllOrCollapseToEndOfText(PR_FALSE);
-        }
-        const nsAString& insertValue =
-          StringTail(newValue, newlength - currentLength);
-        nsCOMPtr<nsIPlaintextEditor> plaintextEditor = do_QueryInterface(editor);
-        if (!plaintextEditor || !weakFrame.IsAlive()) {
-          NS_WARNING("Somehow not a plaintext editor?");
-          return NS_ERROR_FAILURE;
-        }
-
-        // Since this code does not handle user-generated changes to the text,
-        // make sure we don't fire oninput when the editor notifies us.
-        // (mNotifyOnInput must be reset before we return).
-
-        // To protect against a reentrant call to SetValue, we check whether
-        // another SetValue is already happening for this frame.  If it is,
-        // we must wait until we unwind to re-enable oninput events.
-        outerTransaction = mNotifyOnInput;
-        if (outerTransaction)
-          mNotifyOnInput = PR_FALSE;
-
-        // get the flags, remove readonly and disabled, set the value,
-        // restore flags
-        PRUint32 flags, savedFlags;
-        editor->GetFlags(&savedFlags);
-        flags = savedFlags;
-        flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
-        flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
-        flags |= nsIPlaintextEditor::eEditorUseAsyncUpdatesMask;
-        flags |= nsIPlaintextEditor::eEditorDontEchoPassword;
-        editor->SetFlags(flags);
-
-        // Also don't enforce max-length here
-        PRInt32 savedMaxLength;
-        plaintextEditor->GetMaxTextLength(&savedMaxLength);
-        plaintextEditor->SetMaxTextLength(-1);
-
-        if (insertValue.IsEmpty()) {
-          editor->DeleteSelection(nsIEditor::eNone);
-        } else {
-          plaintextEditor->InsertText(insertValue);
-        }
-        NS_ENSURE_STATE(weakFrame.IsAlive());
-
-        if (!IsSingleLineTextControl()) {
-          mCachedValue = newValue;
-        }
-
-        plaintextEditor->SetMaxTextLength(savedMaxLength);
-        editor->SetFlags(savedFlags);
-        if (selPriv)
-          selPriv->EndBatchChanges();
-      }
-
-      // This second check _shouldn't_ be necessary, but let's be safe.
-      NS_ENSURE_STATE(weakFrame.IsAlive());
-      if (outerTransaction)
-        mNotifyOnInput = PR_TRUE;
-
-      if (focusValueInit) {
-        // Reset mFocusedValue so the onchange event doesn't fire incorrectly.
-        InitFocusedValue();
-      }
-    }
-
-    NS_ENSURE_STATE(weakFrame.IsAlive());
-    nsIScrollableFrame* scrollableFrame = do_QueryFrame(GetFirstChild(nsnull));
-    if (scrollableFrame)
-    {
-      // Scroll the upper left corner of the text control's
-      // content area back into view.
-      scrollableFrame->ScrollTo(nsPoint(0, 0), nsIScrollableFrame::INSTANT);
-    }
-  }
-  else
-  {
-    // Otherwise set the value in content.
-    nsCOMPtr<nsITextControlElement> textControl = do_QueryInterface(mContent);
-    if (textControl)
-    {
-      textControl->TakeTextFrameValue(aValue);
-    }
-    // The only time mEditor is non-null but mUseEditor is false is when the
-    // frame is being torn down.  If that's what's going on, don't bother with
-    // updating the display.
-    if (!mEditor) {
-      UpdateValueDisplay(PR_TRUE, PR_FALSE, &aValue);
-    }
-  }
-  return NS_OK;
-}
-
-
 NS_IMETHODIMP
 nsTextControlFrame::SetInitialChildList(nsIAtom*        aListName,
                                         nsFrameList&    aChildList)
 {
   nsresult rv = nsBoxFrame::SetInitialChildList(aListName, aChildList);
 
   nsIFrame* first = GetFirstChild(nsnull);
 
   // Mark the scroll frame as being a reflow root. This will allow
   // incremental reflows to be initiated at the scroll frame, rather
   // than descending from the root frame of the frame hierarchy.
   first->AddStateBits(NS_FRAME_REFLOW_ROOT);
 
-  //register key listeners
-  nsCOMPtr<nsIDOMEventGroup> systemGroup;
-  mContent->GetSystemEventGroup(getter_AddRefs(systemGroup));
-  nsCOMPtr<nsIDOM3EventTarget> dom3Targ = do_QueryInterface(mContent);
-  if (dom3Targ) {
-    // cast because of ambiguous base
-    nsIDOMEventListener *listener = static_cast<nsIDOMKeyListener*>
-                                               (mTextListener);
-
-    dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keydown"),
-                                      listener, PR_FALSE, systemGroup);
-    dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keypress"),
-                                      listener, PR_FALSE, systemGroup);
-    dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keyup"),
-                                      listener, PR_FALSE, systemGroup);
-  }
-
-  mSelCon->SetScrollableFrame(do_QueryFrame(first));
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
+  txtCtrl->InitializeKeyboardEventListeners();
   return rv;
 }
 
 PRBool
 nsTextControlFrame::IsScrollable() const
 {
   return !IsSingleLineTextControl();
 }
 
 void
 nsTextControlFrame::SetValueChanged(PRBool aValueChanged)
 {
-  // placeholder management
-  if (!IsFocusedContent(mContent)) {
-    // If the content is focused, we don't care about the changes because
-    // the placeholder is going to be hide/show on blur.
-    nsAutoString valueString;
-    GetValue(valueString, PR_TRUE);
-    if (valueString.IsEmpty())
-      ShowPlaceholder();
-    else
-      HidePlaceholder();
-  }
-
-  nsCOMPtr<nsITextControlElement> elem = do_QueryInterface(mContent);
-  if (elem) {
-    elem->SetValueChanged(aValueChanged);
-  }
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
+  txtCtrl->SetValueChanged(aValueChanged);
 }
 
 
 nsresult
 nsTextControlFrame::UpdateValueDisplay(PRBool aNotify,
                                        PRBool aBeforeEditorInit,
                                        const nsAString *aValue)
 {
   if (!IsSingleLineTextControl()) // textareas don't use this
     return NS_OK;
 
-  NS_PRECONDITION(mValueDiv, "Must have a div content\n");
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
+  nsIContent* rootNode = txtCtrl->GetRootEditorNode();
+
+  NS_PRECONDITION(rootNode, "Must have a div content\n");
   NS_PRECONDITION(!mUseEditor,
                   "Do not call this after editor has been initialized");
-  NS_ASSERTION(mPlaceholderDiv, "A placeholder div must exist");
+  NS_ASSERTION(txtCtrl->GetPlaceholderNode(), "A placeholder div must exist");
 
-  nsIContent *textContent = mValueDiv->GetChildAt(0);
+  nsIContent *textContent = rootNode->GetChildAt(0);
   if (!textContent) {
     // Set up a textnode with our value
     nsCOMPtr<nsIContent> textNode;
     nsresult rv = NS_NewTextNode(getter_AddRefs(textNode),
                                  mContent->NodeInfo()->NodeInfoManager());
     NS_ENSURE_SUCCESS(rv, rv);
 
     NS_ASSERTION(textNode, "Must have textcontent!\n");
 
-    mValueDiv->AppendChildTo(textNode, aNotify);
+    rootNode->AppendChildTo(textNode, aNotify);
     textContent = textNode;
   }
 
   NS_ENSURE_TRUE(textContent, NS_ERROR_UNEXPECTED);
 
   // Get the current value of the textfield from the content.
   nsAutoString value;
   if (aValue) {
     value = *aValue;
   } else {
-    GetValue(value, PR_TRUE);
+    txtCtrl->GetTextEditorValue(value, PR_TRUE);
   }
 
   // Update the display of the placeholder value if needed.
   // We don't need to do this if we're about to initialize the
   // editor, since EnsureEditorInitialized takes care of this.
   if (!aBeforeEditorInit)
   {
     nsWeakFrame weakFrame(this);
-    SetPlaceholderClass(value.IsEmpty(), aNotify);
+    txtCtrl->SetPlaceholderClass(value.IsEmpty(), aNotify);
     NS_ENSURE_STATE(weakFrame.IsAlive());
   }
 
   if (aBeforeEditorInit && value.IsEmpty()) {
-    mValueDiv->RemoveChildAt(0, PR_TRUE, PR_FALSE);
+    rootNode->RemoveChildAt(0, PR_TRUE, PR_FALSE);
     return NS_OK;
   }
 
   nsTextEditRules::HandleNewLines(value, -1);
   if (!value.IsEmpty() && IsPasswordTextControl()) {
     nsTextEditRules::FillBufWithPWChars(&value, value.Length());
   }
   return textContent->SetText(value, aNotify);
 }
 
-
-/* static */ void
-nsTextControlFrame::ShutDown()
+NS_IMETHODIMP
+nsTextControlFrame::GetOwnedSelectionController(nsISelectionController** aSelCon)
 {
-  NS_IF_RELEASE(sNativeTextAreaBindings);
-  NS_IF_RELEASE(sNativeInputBindings);
-}
-
-nsresult
-nsTextControlFrame::CreatePlaceholderDiv(nsTArray<nsIContent*>& aElements,
-                                         nsNodeInfoManager* pNodeInfoManager)
-{
-  nsresult rv;
-  nsCOMPtr<nsIContent> placeholderText;
+  NS_ENSURE_ARG_POINTER(aSelCon);
 
-  // Create a DIV for the placeholder
-  // and add it to the anonymous content child list
-  nsCOMPtr<nsINodeInfo> nodeInfo;
-  nodeInfo = pNodeInfoManager->GetNodeInfo(nsGkAtoms::div, nsnull,
-                                           kNameSpaceID_XHTML);
-  NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
-
-  rv = NS_NewHTMLElement(getter_AddRefs(mPlaceholderDiv), nodeInfo, PR_FALSE);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Create the text node for the placeholder text before doing anything else
-  rv = NS_NewTextNode(getter_AddRefs(placeholderText), pNodeInfoManager);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
 
-  rv = mPlaceholderDiv->AppendChildTo(placeholderText, PR_FALSE);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Set the necessary classes on the text control. We use class values
-  // instead of a 'style' attribute so that the style comes from a user-agent
-  // style sheet and is still applied even if author styles are disabled.
-  SetPlaceholderClass(PR_TRUE, PR_FALSE);
-
-  if (!aElements.AppendElement(mPlaceholderDiv))
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  // initialise the text
-  UpdatePlaceholderText(PR_FALSE);
+  *aSelCon = txtCtrl->GetSelectionController();
+  NS_IF_ADDREF(*aSelCon);
 
   return NS_OK;
 }
 
-nsresult
-nsTextControlFrame::ShowPlaceholder()
+nsFrameSelection*
+nsTextControlFrame::GetOwnedFrameSelection()
 {
-  return SetPlaceholderClass(PR_TRUE, PR_TRUE);
-}
-
-nsresult
-nsTextControlFrame::HidePlaceholder()
-{
-  return SetPlaceholderClass(PR_FALSE, PR_TRUE);
-}
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  NS_ASSERTION(txtCtrl, "Content not a text control element");
 
-nsresult
-nsTextControlFrame::SetPlaceholderClass(PRBool aVisible,
-                                        PRBool aNotify)
-{
-  nsresult rv;
-  nsAutoString classValue;
-
-  classValue.Assign(NS_LITERAL_STRING("anonymous-div placeholder"));
-
-  if (!aVisible)
-    classValue.AppendLiteral(" hidden");
-
-  rv = mPlaceholderDiv->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
-                                classValue, aNotify);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
+  return txtCtrl->GetConstFrameSelection();
 }
 
-nsresult
-nsTextControlFrame::UpdatePlaceholderText(PRBool aNotify)
-{
-  nsAutoString placeholderValue;
-
-  mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, placeholderValue);
-  nsContentUtils::RemoveNewlines(placeholderValue);
-  NS_ASSERTION(mPlaceholderDiv->GetChildAt(0), "placeholder div has no child");
-  mPlaceholderDiv->GetChildAt(0)->SetText(placeholderValue, aNotify);
-
-  return NS_OK;
-}
-
-NS_IMPL_ISUPPORTS1(nsAnonDivObserver, nsIMutationObserver)
-
-void
-nsAnonDivObserver::CharacterDataChanged(nsIDocument*             aDocument,
-                                        nsIContent*              aContent,
-                                        CharacterDataChangeInfo* aInfo)
-{
-  mTextControl->ClearValueCache();
-}
-
-void
-nsAnonDivObserver::ContentAppended(nsIDocument* aDocument,
-                                   nsIContent*  aContainer,
-                                   nsIContent*  aFirstNewContent,
-                                   PRInt32      /* unused */)
-{
-  mTextControl->ClearValueCache();
-}
-
-void
-nsAnonDivObserver::ContentInserted(nsIDocument* aDocument,
-                                   nsIContent*  aContainer,
-                                   nsIContent*  aChild,
-                                   PRInt32      /* unused */)
-{
-  mTextControl->ClearValueCache();
-}
-
-void
-nsAnonDivObserver::ContentRemoved(nsIDocument* aDocument,
-                                  nsIContent*  aContainer,
-                                  nsIContent*  aChild,
-                                  PRInt32      aIndexInContainer)
-{
-  mTextControl->ClearValueCache();
-}
-
--- a/layout/forms/nsTextControlFrame.h
+++ b/layout/forms/nsTextControlFrame.h
@@ -36,54 +36,31 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsTextControlFrame_h___
 #define nsTextControlFrame_h___
 
 #include "nsStackFrame.h"
 #include "nsBlockFrame.h"
 #include "nsIFormControlFrame.h"
-#include "nsIDOMMouseListener.h"
 #include "nsIAnonymousContentCreator.h"
-#include "nsIEditor.h"
 #include "nsITextControlFrame.h"
-#include "nsIFontMetrics.h"
-#include "nsWeakReference.h" //for service and presshell pointers
-#include "nsContentUtils.h"
 #include "nsDisplayList.h"
 #include "nsIScrollableFrame.h"
 #include "nsStubMutationObserver.h"
-#include "nsThreadUtils.h"
+#include "nsITextControlElement.h"
 
 class nsIEditor;
 class nsISelectionController;
-class nsTextInputSelectionImpl;
-class nsTextInputListener;
 class nsIDOMCharacterData;
 #ifdef ACCESSIBILITY
 class nsIAccessible;
 #endif
-class nsTextInputSelectionImpl;
-class nsTextControlFrame;
 class EditorInitializerEntryTracker;
-
-class nsAnonDivObserver : public nsStubMutationObserver
-{
-public:
-  nsAnonDivObserver(nsTextControlFrame* aTextControl)
-  : mTextControl(aTextControl) {}
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
-  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
-  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
-  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
-
-private:
-  nsTextControlFrame* mTextControl;
-};
+class nsTextEditorState;
 
 class nsTextControlFrame : public nsStackFrame,
                            public nsIAnonymousContentCreator,
                            public nsITextControlFrame
 {
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
@@ -140,45 +117,38 @@ public:
   }
 
   // nsIAnonymousContentCreator
   virtual nsresult CreateAnonymousContent(nsTArray<nsIContent*>& aElements);
   virtual void AppendAnonymousContentTo(nsBaseContentList& aElements);
 
   // Utility methods to set current widget state
 
-  // Be careful when using this method.
-  // Calling it may cause |this| to be deleted.
-  // In that case the method returns an error value.
-  nsresult SetValue(const nsAString& aValue);
   NS_IMETHOD SetInitialChildList(nsIAtom*        aListName,
                                  nsFrameList&    aChildList);
 
 //==== BEGIN NSIFORMCONTROLFRAME
   virtual void SetFocus(PRBool aOn , PRBool aRepaint); 
   virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue);
   virtual nsresult GetFormProperty(nsIAtom* aName, nsAString& aValue) const; 
 
 
 //==== END NSIFORMCONTROLFRAME
 
 //==== NSITEXTCONTROLFRAME
 
   NS_IMETHOD    GetEditor(nsIEditor **aEditor);
-  NS_IMETHOD    OwnsValue(PRBool* aOwnsValue);
-  NS_IMETHOD    GetValue(nsAString& aValue, PRBool aIgnoreWrap) const;
   NS_IMETHOD    GetTextLength(PRInt32* aTextLength);
   NS_IMETHOD    CheckFireOnChange();
   NS_IMETHOD    SetSelectionStart(PRInt32 aSelectionStart);
   NS_IMETHOD    SetSelectionEnd(PRInt32 aSelectionEnd);
   NS_IMETHOD    SetSelectionRange(PRInt32 aSelectionStart, PRInt32 aSelectionEnd);
   NS_IMETHOD    GetSelectionRange(PRInt32* aSelectionStart, PRInt32* aSelectionEnd);
-  virtual nsISelectionController* GetOwnedSelectionController();
-  virtual nsFrameSelection* GetOwnedFrameSelection()
-    { return mFrameSel; }
+  NS_IMETHOD    GetOwnedSelectionController(nsISelectionController** aSelCon);
+  virtual nsFrameSelection* GetOwnedFrameSelection();
 
   nsresult GetPhonetic(nsAString& aPhonetic);
 
   /**
    * Ensure mEditor 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
    */
@@ -193,76 +163,131 @@ public:
                               nsIAtom*        aAttribute,
                               PRInt32         aModType);
 
   nsresult GetText(nsString& aText);
 
   NS_DECL_QUERYFRAME
 
 public: //for methods who access nsTextControlFrame directly
-  /**
-   * Find out whether this is a single line text control.  (text or password)
-   * @return whether this is a single line text control
-   */
-  PRBool IsSingleLineTextControl() const;
-  /**
-   * Find out whether this control is a textarea.
-   * @return whether this is a textarea text control
-   */
-  PRBool IsTextArea() const;
-  /**
-   * Find out whether this control edits plain text.  (Currently always true.)
-   * @return whether this is a plain text control
-   */
-  PRBool IsPlainTextControl() const;
-  /**
-   * Find out whether this is a password control (input type=password)
-   * @return whether this is a password ontrol
-   */
-  PRBool IsPasswordTextControl() const;
   void FireOnInput();
   void SetValueChanged(PRBool aValueChanged);
   /** Called when the frame is focused, to remember the value for onChange. */
   nsresult InitFocusedValue();
 
   void SetFireChangeEventState(PRBool aNewState)
   {
     mFireChangeEventState = aNewState;
   }
 
   PRBool GetFireChangeEventState() const
   {
     return mFireChangeEventState;
   }    
 
-  /* called to free up native keybinding services */
-  static NS_HIDDEN_(void) ShutDown();
-
   // called by the focus listener
   nsresult MaybeBeginSecureKeyboardInput();
   void MaybeEndSecureKeyboardInput();
 
-  void ClearValueCache() { mCachedValue.Truncate(); }
+  class ValueSetter {
+  public:
+    ValueSetter(nsTextControlFrame* aFrame,
+                PRBool aHasFocusValue)
+      : mFrame(aFrame)
+      , mInited(PR_FALSE)
+    {
+      NS_ASSERTION(aFrame, "Should pass a valid frame");
+
+      // This method isn't used for user-generated changes, except for calls
+      // from nsFileControlFrame which sets mFireChangeEventState==true and
+      // restores it afterwards (ie. we want 'change' events for those changes).
+      // Focused value must be updated to prevent incorrect 'change' events,
+      // but only if user hasn't changed the value.
+      mFocusValueInit = !mFrame->mFireChangeEventState && aHasFocusValue;
+    }
+    void Cancel() {
+      mInited = PR_FALSE;
+    }
+    void Init() {
+      // Since this code does not handle user-generated changes to the text,
+      // make sure we don't fire oninput when the editor notifies us.
+      // (mNotifyOnInput must be reset before we return).
+
+      // To protect against a reentrant call to SetValue, we check whether
+      // another SetValue is already happening for this frame.  If it is,
+      // we must wait until we unwind to re-enable oninput events.
+      mOuterTransaction = mFrame->mNotifyOnInput;
+      if (mOuterTransaction)
+        mFrame->mNotifyOnInput = PR_FALSE;
+
+      mInited = PR_TRUE;
+    }
+    ~ValueSetter() {
+      if (!mInited)
+        return;
+
+      if (mOuterTransaction)
+        mFrame->mNotifyOnInput = PR_TRUE;
+
+      if (mFocusValueInit) {
+        // Reset mFocusedValue so the onchange event doesn't fire incorrectly.
+        mFrame->InitFocusedValue();
+      }
+    }
+
+  private:
+    nsTextControlFrame* mFrame;
+    PRPackedBool mFocusValueInit;
+    PRPackedBool mOuterTransaction;
+    PRPackedBool mInited;
+  };
+  friend class ValueSetter;
+
+#define DEFINE_TEXTCTRL_FORWARDER(type, name)                                  \
+  type name() {                                                                \
+    nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); \
+    NS_ASSERTION(txtCtrl, "Content not a text control element");               \
+    return txtCtrl->name();                                                    \
+  }
+#define DEFINE_TEXTCTRL_CONST_FORWARDER(type, name)                            \
+  type name() const {                                                          \
+    nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent()); \
+    NS_ASSERTION(txtCtrl, "Content not a text control element");               \
+    return txtCtrl->name();                                                    \
+  }
+
+  DEFINE_TEXTCTRL_CONST_FORWARDER(PRBool, IsSingleLineTextControl)
+  DEFINE_TEXTCTRL_CONST_FORWARDER(PRBool, IsTextArea)
+  DEFINE_TEXTCTRL_CONST_FORWARDER(PRBool, IsPlainTextControl)
+  DEFINE_TEXTCTRL_CONST_FORWARDER(PRBool, IsPasswordTextControl)
+  DEFINE_TEXTCTRL_FORWARDER(PRInt32, GetCols)
+  DEFINE_TEXTCTRL_FORWARDER(PRInt32, GetWrapCols)
+  DEFINE_TEXTCTRL_FORWARDER(PRInt32, GetRows)
+
+#undef DEFINE_TEXTCTRL_CONST_FORWARDER
+#undef DEFINE_TEXTCTRL_FORWARDER
+
 protected:
   class EditorInitializer;
   friend class EditorInitializer;
+  friend class nsTextEditorState; // needs access to UpdateValueDisplay
 
   class EditorInitializer : public nsRunnable {
   public:
     EditorInitializer(nsTextControlFrame* aFrame) :
       mWeakFrame(aFrame),
       mFrame(aFrame) {}
 
     NS_IMETHOD Run() {
       if (mWeakFrame) {
         nsCOMPtr<nsIPresShell> shell =
           mWeakFrame.GetFrame()->PresContext()->GetPresShell();
         PRBool observes = shell->ObservesNativeAnonMutationsForPrint();
         shell->ObserveNativeAnonMutationsForPrint(PR_TRUE);
-        mFrame->EnsureEditorInitializedInternal();
+        mFrame->EnsureEditorInitialized();
         shell->ObserveNativeAnonMutationsForPrint(observes);
       }
       return NS_OK;
     }
 
   private:
     nsWeakFrame mWeakFrame;
     nsTextControlFrame* mFrame;
@@ -321,84 +346,52 @@ protected:
   { return mContent && mContent->HasAttr(kNameSpaceID_None, aAtt); }
 
   /**
    * We call this when we are being destroyed or removed from the PFM.
    * @param aPresContext the current pres context
    */
   void PreDestroy();
 
-  // Helper methods
-  /**
-   * Get the cols attribute (if textarea) or a default
-   * @return the number of columns to use
-   */
-  PRInt32 GetCols();
-
-  /**
-   * Get the column index to wrap at, or -1 if we shouldn't wrap
-   */
-  PRInt32 GetWrapCols();
-
-  /**
-   * Get the rows attribute (if textarea) or a default
-   * @return the number of rows to use
-   */
-  PRInt32 GetRows();
-
   // 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);
 
 private:
   //helper methods
   nsresult SetSelectionInternal(nsIDOMNode *aStartNode, PRInt32 aStartOffset,
                                 nsIDOMNode *aEndNode, PRInt32 aEndOffset);
   nsresult SelectAllOrCollapseToEndOfText(PRBool aSelect);
   nsresult SetSelectionEndPoints(PRInt32 aSelStart, PRInt32 aSelEnd);
 
-  // placeholder methods
-  nsresult CreatePlaceholderDiv(nsTArray<nsIContent*>& aElements, nsNodeInfoManager* pNodeInfoManager);
-  nsresult ShowPlaceholder();
-  nsresult HidePlaceholder();
-  nsresult SetPlaceholderClass(PRBool aVisible, PRBool aNotify);
-  nsresult UpdatePlaceholderText(PRBool aNotify); 
+  // accessors for the notify on input flag
+  PRBool GetNotifyOnInput() const { return mNotifyOnInput; }
+  void SetNotifyOnInput(PRBool val) { mNotifyOnInput = val; }
 
-  // This method performs the actual tasks of initializing the editor.
-  // EnsureEditorInitialized is a wrapper of this method which wraps it with
-  // a weak frame check.
-  virtual nsresult EnsureEditorInitializedInternal();
+  /**
+   * Return the root DOM element, and implicitly initialize the editor if needed.
+   */
+  nsresult GetRootNodeAndInitializeEditor(nsIDOMElement **aRootElement);
 
 private:
-  nsCOMPtr<nsIContent> mValueDiv;
-  nsCOMPtr<nsIContent> mPlaceholderDiv;
-
-  nsCOMPtr<nsIEditor> mEditor;
-
   // these packed bools could instead use the high order bits on mState, saving 4 bytes 
   PRPackedBool mUseEditor;
   PRPackedBool mIsProcessing;
   PRPackedBool mNotifyOnInput;//default this to off to stop any notifications until setup is complete
-  PRPackedBool mDidPreDestroy; // has PreDestroy been called
   // Calls to SetValue will be treated as user values (i.e. trigger onChange
   // eventually) when mFireChangeEventState==true, this is used by nsFileControlFrame.
   PRPackedBool mFireChangeEventState;
   PRPackedBool mInSecureKeyboardInputMode;
 
 #ifdef DEBUG
   PRPackedBool mInEditorInitialization;
   friend class EditorInitializerEntryTracker;
 #endif
 
-  nsRefPtr<nsTextInputSelectionImpl> mSelCon;
-  nsCOMPtr<nsFrameSelection> mFrameSel;
-  nsTextInputListener* mTextListener;
   nsString mFocusedValue;
-  nsString mCachedValue; // Caches non-hard-wrapped value on a multiline control.
-  nsRefPtr<nsAnonDivObserver> mMutationObserver;
   nsRevocableEventPtr<ScrollOnFocusEvent> mScrollEvent;
 };
 
 #endif
 
 
--- a/layout/forms/test/Makefile.in
+++ b/layout/forms/test/Makefile.in
@@ -57,16 +57,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug446663.html \
 		test_bug476308.html \
 		test_bug477531.html \
 		test_bug477700.html \
 		     bug477700_subframe.html \
 		test_textarea_resize.html \
 		test_bug478219.xhtml \
 		test_bug542914.html \
+		test_bug534785.html \
 		test_bug536567.html \
 		     bug536567_subframe.html \
 		test_bug549170.html \
 		test_bug562447.html \
 		test_bug563642.html \
 		test_bug564115.html \
 		bug564115_window.html \
 		test_bug377624.html \
new file mode 100644
--- /dev/null
+++ b/layout/forms/test/test_bug534785.html
@@ -0,0 +1,89 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=534785
+-->
+<head>
+  <title>Test for Bug 534785</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>
+  <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=534785">Mozilla Bug 534785</a>
+<p id="display"></p>
+<input type="text" value="test">
+<div id="reframe">
+<textarea></textarea>
+<textarea>test</textarea>
+<input type="text">
+<input type="text" value="test">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 534785 **/
+
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(function() {
+  var i = document.querySelector("input");
+  i.addEventListener("focus", function() {
+    is(i.value, "test", "Sanity check");
+
+    is(document.activeElement, i, "Should be focused before frame reconstruction");
+    synthesizeKey("1", {});
+    is(i.value, "test1", "Can accept keyboard events before frame reconstruction");
+
+    // force frame reconstruction
+    i.style.display = "none";
+    document.offsetHeight;
+    i.style.display = "";
+    document.offsetHeight;
+
+    is(document.activeElement, i, "Should be focused after frame reconstruction");
+    synthesizeKey("2", {});
+    is(i.value, "test12", "Can accept keyboard events after frame reconstruction");
+
+    // Make sure reframing happens gracefully
+    var reframeDiv = document.getElementById("reframe");
+    var textAreaWithoutValue = reframeDiv.querySelectorAll("textarea")[0];
+    var textAreaWithValue = reframeDiv.querySelectorAll("textarea")[1];
+    var inputWithoutValue = reframeDiv.querySelectorAll("input")[0];
+    var inputWithValue = reframeDiv.querySelectorAll("input")[1];
+    reframeDiv.style.display = "none";
+    document.body.offsetWidth;
+    reframeDiv.style.display = "";
+    document.body.offsetWidth;
+    [textAreaWithoutValue, inputWithoutValue].forEach(function (elem) {
+      is(elem.value, "", "Value should persist correctly");
+    });
+    [textAreaWithValue, inputWithValue].forEach(function (elem) {
+      is(elem.value, "test", "Value should persist correctly");
+    });
+    [inputWithoutValue, inputWithValue].forEach(function (elem) elem.type = "submit");
+    document.body.offsetWidth;
+    is(inputWithoutValue.value, "", "Value should persist correctly");
+    is(inputWithValue.value, "test", "Value should persist correctly");
+    [inputWithoutValue, inputWithValue].forEach(function (elem) elem.type = "text");
+    document.body.offsetWidth;
+    is(inputWithoutValue.value, "", "Value should persist correctly");
+    is(inputWithValue.value, "test", "Value should persist correctly");
+    [inputWithoutValue, inputWithValue].forEach(function (elem) elem.focus()); // initialze the editor
+    reframeDiv.style.display = "none";
+    document.body.offsetWidth;
+    reframeDiv.style.display = "";
+    document.body.offsetWidth;
+    is(inputWithoutValue.value, "", "Value should persist correctly with editor");
+    is(inputWithValue.value, "test", "Value should persist correctly with editor");
+
+    SimpleTest.finish();
+  }, false);
+  i.focus();
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -4310,18 +4310,17 @@ nsFrame::GetSelectionController(nsPresCo
 {
   if (!aPresContext || !aSelCon)
     return NS_ERROR_INVALID_ARG;
 
   nsIFrame *frame = this;
   while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) {
     nsITextControlFrame *tcf = do_QueryFrame(frame);
     if (tcf) {
-      NS_IF_ADDREF(*aSelCon = tcf->GetOwnedSelectionController());
-      return NS_OK;
+      return tcf->GetOwnedSelectionController(aSelCon);
     }
     frame = frame->GetParent();
   }
 
   return CallQueryInterface(aPresContext->GetPresShell(), aSelCon);
 }
 
 already_AddRefed<nsFrameSelection>
--- a/layout/generic/test/test_bug527306.html
+++ b/layout/generic/test/test_bug527306.html
@@ -5,28 +5,45 @@ https://bugzilla.mozilla.org/show_bug.cg
 -->
 <head>
   <title>Test for Bug 527306</title>
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
 </head>
-<body onload="showText()">
+<body onload="runTests()">
 <p><a target="_blank" href="https://bugzilla.mozilla.org/show_bug?id=527306">Mozilla Bug 527306</a>
 
-<p><input type="text" id="currenttext" value="FAIL">
+<p><input type="text" id="t1" value="FAIL"></p>
+<p><input type="text" id="t2" value="FAIL"></p>
+<p><input type="text" id="t3" value="FAIL"></p>
+<p><textarea id="t4">FAIL</textarea></p>
 
 <pre id="test">
 <script>
-function showText() {
-  var t = document.getElementById("currenttext");
+
+function testElement(t) {
   t.style.display = "none";
   t.value = "PASS";
   is(t.value, "PASS", "Value should be set correctly");
+}
+
+function runTests() {
+  var t = document.getElementById("t1");
+  testElement(t);
+  t = document.getElementById("t2");
+  t.focus();
+  testElement(t);
+  t = document.getElementById("t3");
+  t.focus();
+  t.blur();
+  testElement(t);
+  t = document.getElementById("t4");
+  testElement(t);
   SimpleTest.finish();
 }
 
 SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
 
 </body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/editor/dynamic-type-1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<body>
+    <input type="checkbox">
+    <script>
+      var i = document.getElementsByTagName("input")[0];
+      i.type = "text";
+      i.value = "abcdef";
+    </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/editor/dynamic-type-2.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<body>
+    <input type="checkbox">
+    <script>
+      var i = document.getElementsByTagName("input")[0];
+      i.value = "abcdef";
+      i.type = "text";
+    </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/editor/dynamic-type-3.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<body>
+    <input type="checkbox" value="foo">
+    <script>
+      var i = document.getElementsByTagName("input")[0];
+      i.type = "text";
+      i.value = "abcdef";
+    </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/editor/dynamic-type-4.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<body>
+    <input type="checkbox" value="foo">
+    <script>
+      var i = document.getElementsByTagName("input")[0];
+      i.type = "text";
+      i.value = "abcdef";
+    </script>
+</body>
+</html>
--- a/layout/reftests/editor/reftest.list
+++ b/layout/reftests/editor/reftest.list
@@ -1,14 +1,18 @@
 # include the XUL reftests
 include xul/reftest.list
 
 == newline-1.html newline-ref.html
 == newline-2.html newline-ref.html
 == newline-3.html newline-ref.html
 == dynamic-1.html dynamic-ref.html
+== dynamic-type-1.html dynamic-ref.html
+== dynamic-type-2.html dynamic-ref.html
+== dynamic-type-3.html dynamic-ref.html
+== dynamic-type-4.html dynamic-ref.html
 == passwd-1.html passwd-ref.html
 != passwd-2.html passwd-ref.html
 == passwd-3.html passwd-ref.html
 == passwd-4.html passwd-ref.html
 == emptypasswd-1.html emptypasswd-ref.html
 == emptypasswd-2.html emptypasswd-ref.html
 == caret_on_positioned.html caret_on_positioned-ref.html
copy from layout/reftests/forms/placeholder/placeholder-14.html
copy to layout/reftests/forms/placeholder/placeholder-18.html
--- a/layout/reftests/forms/placeholder/placeholder-14.html
+++ b/layout/reftests/forms/placeholder/placeholder-18.html
@@ -1,17 +1,22 @@
 <!DOCTYPE html>
 <html class="reftest-wait">
-  <!-- Test: placeholder shouldn't show new lines (\n version) -->
+  <!-- Test: placeholder should appear with dynamic DOM modifications -->
   <script type="text/javascript">
     function setPlaceholder()
     {
-      document.getElementById('p1').placeholder = 'my\n placeholder';
+      var i = document.getElementById('p1');
+      i.focus();
+      i.blur();
+      i.value = "not empty";
+      i.value = "";
+      i.value = "my value";
     }
     function disableReftestWait()
     {
       document.documentElement.className = '';
     }
   </script>
   <body onload="setPlaceholder(); disableReftestWait();">
-    <input type="text" id="p1" value="" placeholder="">
+    <input type="text" id="p1" value="" placeholder="my placeholder">
   </body>
 </html>
--- a/layout/reftests/forms/placeholder/reftest.list
+++ b/layout/reftests/forms/placeholder/reftest.list
@@ -13,8 +13,9 @@
 == placeholder-10.html placeholder-visible-ref.html
 == placeholder-11.html placeholder-visible-ref.html
 == placeholder-12.html placeholder-visible-ref.html
 == placeholder-13.html placeholder-visible-ref.html
 == placeholder-14.html placeholder-visible-ref.html
 == placeholder-15.html placeholder-focus-ref.html
 == placeholder-16.html placeholder-focus-ref.html
 == placeholder-17.html placeholder-focus-ref.html
+== placeholder-18.html placeholder-overridden-ref.html
--- a/layout/reftests/forms/reftest.list
+++ b/layout/reftests/forms/reftest.list
@@ -13,16 +13,17 @@ HTTP(..) == text-control-baseline-1.html
 == textbox-align-baseline-1.xul textbox-align-baseline-1-ref.xul # test for bug 494901
 == textbox-setsize.xul textbox-setsize-ref.xul
 == textarea-resize.html textarea-resize-ref.html
 != textarea-ltr.html textarea-rtl.html
 != textarea-ltr-scrollbar.html textarea-rtl-scrollbar.html
 != textarea-in-ltr-doc-scrollbar.html textarea-in-rtl-doc-scrollbar.html
 != textarea-ltr.html textarea-no-resize.html
 random-if(MOZ_WIDGET_TOOLKIT=="gtk2") != textarea-rtl.html textarea-no-resize.html # bug 558201
+== textarea-setvalue-framereconstruction-1.html textarea-setvalue-framereconstruction-ref.html
 
 == radio-label-dynamic.html radio-label-dynamic-ref.html
 == out-of-bounds-selectedindex.html out-of-bounds-selectedindex-ref.html # test for bug 471741
 != indeterminate-checked.html indeterminate-checked-notref.html
 != indeterminate-unchecked.html indeterminate-unchecked-notref.html
 != indeterminate-native-checked.html indeterminate-native-checked-notref.html
 != indeterminate-native-unchecked.html indeterminate-native-unchecked-notref.html
 == indeterminate-selector.html indeterminate-selector-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/textarea-setvalue-framereconstruction-1.html
@@ -0,0 +1,48 @@
+<!--
+
+This test is mostly a copy of layout/forms/crashtests/373586-1.xhtml,
+and it makes sure that the value setter works correctly when setting
+the value causes the frame to be reconstructed.
+
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
+<head>
+
+<bindings xmlns="http://www.mozilla.org/xbl">
+  <binding id="foo">
+    <content>
+      <children xmlns="http://www.mozilla.org/xbl" />
+    </content>
+  </binding>
+</bindings>
+
+<script>
+function boom()
+{
+  document.getElementById("div").style.MozBinding = "url('#foo')";
+
+  var opt1 = document.getElementById("opt1");
+  opt1.removeChild(opt1.firstChild);
+
+  document.getElementById("textarea").value += " y";
+
+  document.getElementById("div").style.MozBinding = "";
+  document.documentElement.removeAttribute("class")
+}
+</script>
+
+</head>
+
+<body onload="setTimeout(boom, 30);">
+
+<div id="div">
+  <textarea rows="3" cols="5" id="textarea">x</textarea>
+</div>
+
+<select>
+  <option id="opt1">opt1</option>
+</select>
+
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/textarea-setvalue-framereconstruction-ref.html
@@ -0,0 +1,17 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+
+<body>
+
+<div id="div">
+  <textarea rows="3" cols="5" id="textarea">x y</textarea>
+</div>
+
+<select>
+  <option id="opt1"></option>
+</select>
+
+</body>
+</html>
+