Bug 542919 - Refactor the plain text editor initialization to facilitate lazy initialization; r=bzbarsky sr=roc
authorEhsan Akhgari <ehsan@mozilla.com>
Mon, 01 Feb 2010 23:00:06 -0500
changeset 40507 32471a45b39b2d81cc608ec9aea39f45b51fbef9
parent 40506 64ebf70ed4a201c96db2e65b6fda6ccd31b6e91d
child 40508 0a2f12f042446fa718dda3ebab4061872650f2ef
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersbzbarsky, roc
bugs542919
milestone1.9.3a4pre
Bug 542919 - Refactor the plain text editor initialization to facilitate lazy initialization; r=bzbarsky sr=roc
layout/forms/nsITextControlFrame.h
layout/forms/nsTextControlFrame.cpp
layout/forms/nsTextControlFrame.h
--- a/layout/forms/nsITextControlFrame.h
+++ b/layout/forms/nsITextControlFrame.h
@@ -82,11 +82,18 @@ public:
   
   NS_IMETHOD    SetSelectionRange(PRInt32 aSelectionStart, PRInt32 aSelectionEnd) = 0;
   NS_IMETHOD    GetSelectionRange(PRInt32* aSelectionStart, PRInt32* aSelectionEnd) = 0;
 
   virtual nsISelectionController* GetOwnedSelectionController() = 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
+   */
+  virtual nsresult EnsureEditorInitialized() = 0;
 };
 
 #endif
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -1294,48 +1294,16 @@ nsTextControlFrame::CalcIntrinsicSize(ns
     aIntrinsicSize.width  += scrollbarSizes.LeftRight();
     
     aIntrinsicSize.height += scrollbarSizes.TopBottom();;
   }
 
   return NS_OK;
 }
 
-void
-nsTextControlFrame::DelayedEditorInit()
-{
-  nsIDocument* doc = mContent->GetCurrentDoc();
-  if (!doc) {
-    return;
-  }
-  
-  nsWeakFrame weakFrame(this);
-
-  // Flush out content on our document.  Have to do this, because script
-  // blockers don't prevent the sink flushing out content and notifying in the
-  // process, which can destroy frames.
-  doc->FlushPendingNotifications(Flush_ContentAndNotify);
-  if (!weakFrame.IsAlive()) {
-    return;
-  }
-  
-  // Make sure that editor init doesn't do things that would kill us off
-  // (especially off the script blockers it'll create for its DOM mutations).
-  nsAutoScriptBlocker scriptBlocker;
-
-  // Time to mess with our security context... See comments in GetValue()
-  // for why this is needed.
-  nsCxPusher pusher;
-  pusher.PushNull();
-
-  InitEditor();
-  if (IsFocusedContent(GetContent()))
-    SetFocus(PR_TRUE, PR_FALSE);
-}
-
 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) {
@@ -1347,33 +1315,68 @@ nsTextControlFrame::GetWrapCols()
     return GetCols();
   }
 
   // Never wrap non-textareas
   return -1;
 }
 
 nsresult
-nsTextControlFrame::InitEditor()
+nsTextControlFrame::EnsureEditorInitialized()
 {
   // 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.  So we call this from a script runner
-  // now.
+  // to display wrong values.  Additionally, calling this every time
+  // a text frame control is instantiated means that we're effectively
+  // instantiating the editor for all text fields, even if they
+  // never get used.
 
   // Check if this method has been called already.
   // If so, just return early.
-
   if (mUseEditor)
     return NS_OK;
 
+  nsIDocument* doc = mContent->GetCurrentDoc();
+  NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+
+  nsWeakFrame weakFrame(this);
+
+  // Flush out content on our document.  Have to do this, because script
+  // blockers don't prevent the sink flushing out content and notifying in the
+  // process, which can destroy frames.
+  doc->FlushPendingNotifications(Flush_ContentAndNotify);
+  NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_ERROR_FAILURE);
+
+  // Make sure that editor init doesn't do things that would kill us off
+  // (especially off the script blockers it'll create for its DOM mutations).
+  nsAutoScriptBlocker scriptBlocker;
+
+  // Time to mess with our security context... See comments in GetValue()
+  // for why this is needed.
+  nsCxPusher pusher;
+  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()))
+        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
 
@@ -1494,58 +1497,55 @@ nsTextControlFrame::InitEditor()
   // 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;
 
-  // 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.
+  // Set the editor's contents to our default value. We have to be
+  // sure to use the editor so that '*' characters get displayed for
+  // password fields, etc. SetValue() will call the editor for us.  We
+  // want to call this even if defaultValue is empty, since empty text
+  // inputs have a single non-breaking space in the textnode under
+  // mAnonymousDiv, and this space needs to go away as we init the
+  // editor.
+
+  // 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);
+  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 = mEditor->EnableUndo(PR_FALSE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  SetValue(defaultValue);
+
+  rv = mEditor->EnableUndo(PR_TRUE);
+  NS_ASSERTION(NS_SUCCEEDED(rv),"Transaction Manager must have failed");
+
+  // Now restore the original editor flags.
+  rv = mEditor->SetFlags(editorFlags);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   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");
-
-    // Now restore the original editor flags.
-    rv = mEditor->SetFlags(editorFlags);
-
     // 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());
-
-    if (NS_FAILED(rv))
-      return rv;
   }
 
   nsCOMPtr<nsITransactionManager> transMgr;
   mEditor->GetTransactionManager(getter_AddRefs(transMgr));
   NS_ENSURE_TRUE(transMgr, NS_ERROR_FAILURE);
 
   transMgr->SetMaxTransactionCount(DEFAULT_UNDO_CAP);
 
@@ -1566,17 +1566,17 @@ nsTextControlFrame::InitEditor()
   return NS_OK;
 }
 
 nsresult
 nsTextControlFrame::CreateAnonymousContent(nsTArray<nsIContent*>& aElements)
 {
   mState |= NS_FRAME_INDEPENDENT_SELECTION;
 
-  nsIPresShell* shell = PresContext()->GetPresShell();
+  nsIPresShell *shell = PresContext()->GetPresShell();
   if (!shell)
     return NS_ERROR_FAILURE;
 
   nsIDocument *doc = shell->GetDocument();
   if (!doc)
     return NS_ERROR_FAILURE;
 
   // Now create a DIV and add it to the anonymous content child list.
@@ -2419,17 +2419,17 @@ nsTextControlFrame::AttributeChanged(PRI
         mSelCon->SetCaretEnabled(PR_FALSE);
     }
     else 
     { // unset readonly
       flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
       if (!(flags & nsIPlaintextEditor::eEditorDisabledMask) &&
           IsFocusedContent(mContent))
         mSelCon->SetCaretEnabled(PR_TRUE);
-    }    
+    }
     mEditor->SetFlags(flags);
   }
   else if (nsGkAtoms::disabled == aAttribute) 
   {
     PRUint32 flags;
     mEditor->GetFlags(&flags);
     if (AttributeExists(nsGkAtoms::disabled))
     { // set disabled
--- a/layout/forms/nsTextControlFrame.h
+++ b/layout/forms/nsTextControlFrame.h
@@ -170,16 +170,23 @@ public:
   NS_IMETHOD    SetSelectionRange(PRInt32 aSelectionStart, PRInt32 aSelectionEnd);
   NS_IMETHOD    GetSelectionRange(PRInt32* aSelectionStart, PRInt32* aSelectionEnd);
   virtual nsISelectionController* GetOwnedSelectionController();
   virtual nsFrameSelection* GetOwnedFrameSelection()
     { return mFrameSel; }
 
   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
+   */
+  virtual nsresult EnsureEditorInitialized();
+
 //==== END NSITEXTCONTROLFRAME
 //==== OVERLOAD of nsIFrame
   virtual nsIAtom* GetType() const;
 
   /** handler for attribute changes to mContent */
   NS_IMETHOD AttributeChanged(PRInt32         aNameSpaceID,
                               nsIAtom*        aAttribute,
                               PRInt32         aModType);
@@ -243,47 +250,37 @@ protected:
       mFrame(aFrame) {}
 
     NS_IMETHOD Run() {
       if (mWeakFrame) {
         nsCOMPtr<nsIPresShell> shell =
           mWeakFrame.GetFrame()->PresContext()->GetPresShell();
         PRBool observes = shell->ObservesNativeAnonMutationsForPrint();
         shell->ObserveNativeAnonMutationsForPrint(PR_TRUE);
-        mFrame->DelayedEditorInit();
+        mFrame->EnsureEditorInitialized();
         shell->ObserveNativeAnonMutationsForPrint(observes);
       }
       return NS_OK;
     }
 
   private:
     nsWeakFrame mWeakFrame;
     nsTextControlFrame* mFrame;
   };
 
-  // Init our editor and then make sure to focus our text input
-  // listener if our content node has focus.
-  void DelayedEditorInit();
-
   nsresult DOMPointToOffset(nsIDOMNode* aNode, PRInt32 aNodeOffset, PRInt32 *aResult);
   nsresult OffsetToDOMPoint(PRInt32 aOffset, nsIDOMNode** aResult, PRInt32* aPosition);
 
   /**
    * Find out whether this control is scrollable (i.e. if it is not a single
    * line text control)
    * @return whether this control is scrollable
    */
   PRBool IsScrollable() const;
   /**
-   * Initialize mEditor 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
-   */
-  nsresult InitEditor();
-  /**
    * Strip all \n, \r and nulls from the given string
    * @param aString the string to remove newlines from [in/out]
    */
   void RemoveNewlines(nsString &aString);
   /**
    * Get the maxlength attribute
    * @param aMaxLength the value of the max length attr
    * @returns PR_FALSE if attr not defined