diff --git a/layout/forms/nsITextControlFrame.h b/layout/forms/nsITextControlFrame.h index 223b7d898111..751a1e3d2082 100644 --- a/layout/forms/nsITextControlFrame.h +++ b/layout/forms/nsITextControlFrame.h @@ -87,6 +87,13 @@ public: 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 diff --git a/layout/forms/nsTextControlFrame.cpp b/layout/forms/nsTextControlFrame.cpp index 9e747145342e..0aa7a983b728 100644 --- a/layout/forms/nsTextControlFrame.cpp +++ b/layout/forms/nsTextControlFrame.cpp @@ -1299,38 +1299,6 @@ nsTextControlFrame::CalcIntrinsicSize(nsIRenderingContext* aRenderingContext, 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() { @@ -1352,23 +1320,58 @@ nsTextControlFrame::GetWrapCols() } 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; @@ -1499,48 +1502,45 @@ nsTextControlFrame::InitEditor() // 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 transMgr; @@ -1571,7 +1571,7 @@ nsTextControlFrame::CreateAnonymousContent(nsTArray& aElements) { mState |= NS_FRAME_INDEPENDENT_SELECTION; - nsIPresShell* shell = PresContext()->GetPresShell(); + nsIPresShell *shell = PresContext()->GetPresShell(); if (!shell) return NS_ERROR_FAILURE; @@ -2424,7 +2424,7 @@ nsTextControlFrame::AttributeChanged(PRInt32 aNameSpaceID, if (!(flags & nsIPlaintextEditor::eEditorDisabledMask) && IsFocusedContent(mContent)) mSelCon->SetCaretEnabled(PR_TRUE); - } + } mEditor->SetFlags(flags); } else if (nsGkAtoms::disabled == aAttribute) diff --git a/layout/forms/nsTextControlFrame.h b/layout/forms/nsTextControlFrame.h index c6427c96035c..3f9bb8c4e5a6 100644 --- a/layout/forms/nsTextControlFrame.h +++ b/layout/forms/nsTextControlFrame.h @@ -175,6 +175,13 @@ public: 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; @@ -248,7 +255,7 @@ protected: mWeakFrame.GetFrame()->PresContext()->GetPresShell(); PRBool observes = shell->ObservesNativeAnonMutationsForPrint(); shell->ObserveNativeAnonMutationsForPrint(PR_TRUE); - mFrame->DelayedEditorInit(); + mFrame->EnsureEditorInitialized(); shell->ObserveNativeAnonMutationsForPrint(observes); } return NS_OK; @@ -259,10 +266,6 @@ protected: 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); @@ -272,12 +275,6 @@ protected: * @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]