Bug 335615. Don't do editor init inside frame construction. r=mats, sr=roc

This commit is contained in:
Boris Zbarsky 2009-01-29 14:46:17 -05:00
parent 16fff652b0
commit ff8ff59c94
7 changed files with 131 additions and 166 deletions

View File

@ -1302,6 +1302,7 @@ public:
* scripts. Passing null is allowed and results in nothing
* happening. It is also allowed to pass an object that
* has not yet been AddRefed.
* @return false on out of memory, true otherwise.
*/
static PRBool AddScriptRunner(nsIRunnable* aRunnable);

View File

@ -1005,41 +1005,26 @@ nsHTMLInputElement::SetValueInternal(const nsAString& aValue,
if (mType == NS_FORM_INPUT_TEXT || mType == NS_FORM_INPUT_PASSWORD) {
nsITextControlFrame* textControlFrame = aFrame;
nsIFormControlFrame* formControlFrame = textControlFrame;
if (!textControlFrame) {
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) {
textControlFrame = do_QueryFrame(formControlFrame);
}
}
// File frames always own the value (if the frame is there).
// Text frames have a bit that says whether they own the value.
PRBool frameOwnsValue = PR_FALSE;
if (textControlFrame) {
textControlFrame->OwnsValue(&frameOwnsValue);
}
// If the frame owns the value, set the value in the frame
if (frameOwnsValue) {
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.
formControlFrame->SetFormProperty(
aUserInput ? nsGkAtoms::userInput : nsGkAtoms::value, aValue);
return NS_OK;
}
// If the frame does not own the value, set mValue
if (mValue) {
nsMemory::Free(mValue);
}
mValue = ToNewUTF8String(aValue);
SetValueChanged(PR_TRUE);
return mValue ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
return TakeTextFrameValue(aValue);
}
if (mType == NS_FORM_INPUT_FILE) {

View File

@ -5406,8 +5406,6 @@ nsCSSFrameConstructor::CreateAnonymousFrames(nsFrameConstructorState& aState,
}
}
creator->PostCreateFrames();
// process the current pseudo frame state
if (!aState.mPseudoFrames.IsEmpty()) {
ProcessPseudoFrames(aState, aChildItems);

View File

@ -1020,9 +1020,7 @@ struct nsAutoLayoutPhase {
"constructing frames in the middle of a paint");
NS_ASSERTION(mPresContext->mLayoutPhaseCount[eLayoutPhase_Reflow] == 0,
"constructing frames in the middle of reflow");
// The nsXBLService::LoadBindings call in ConstructFrameInternal
// makes us hit this one too often to be an NS_ASSERTION,
// despite how scary it is.
// Once bug 337957 is fixed this should become an NS_ASSERTION
NS_WARN_IF_FALSE(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0,
"recurring into frame construction");
break;

View File

@ -1034,9 +1034,6 @@ nsTextControlFrame::nsTextControlFrame(nsIPresShell* aShell, nsStyleContext* aCo
, mFireChangeEventState(PR_FALSE)
, mInSecureKeyboardInputMode(PR_FALSE)
, mTextListener(nsnull)
#ifdef DEBUG
, mCreateFrameForCalled(PR_FALSE)
#endif
{
}
@ -1133,7 +1130,6 @@ nsTextControlFrame::PreDestroy()
mFrameSel = nsnull;
}
//unregister self from content
nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), PR_FALSE);
if (mTextListener)
{
@ -1388,7 +1384,7 @@ nsTextControlFrame::CalcIntrinsicSize(nsIRenderingContext* aRenderingContext,
}
void
nsTextControlFrame::PostCreateFrames()
nsTextControlFrame::DelayedEditorInit()
{
InitEditor();
// Notify the text listener we have focus and setup the caret etc (bug 446663).
@ -1398,53 +1394,30 @@ nsTextControlFrame::PostCreateFrames()
}
}
nsIFrame*
nsTextControlFrame::CreateFrameFor(nsIContent* aContent)
nsresult
nsTextControlFrame::InitEditor()
{
#ifdef DEBUG
NS_ASSERTION(!mCreateFrameForCalled, "CreateFrameFor called more than once!");
mCreateFrameForCalled = PR_TRUE;
#endif
// This method initializes our editor, if needed.
nsPresContext *presContext = PresContext();
nsIPresShell *shell = presContext->GetPresShell();
if (!shell)
return nsnull;
nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(shell->GetDocument());
if (!domdoc)
return nsnull;
// 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.
// Don't create any frames here, but just setup the editor.
// This way DOM Ranges (which editor uses) work properly since the anonymous
// content is bound to tree after CreateAnonymousContent but before this
// method.
nsresult rv = NS_OK;
// Check if this method has been called already.
// If so, just return early.
if (mUseEditor)
return NS_OK;
// Create an editor
nsresult rv;
mEditor = do_CreateInstance(kTextEditorCID, &rv);
if (NS_FAILED(rv) || !mEditor)
return nsnull;
// Create selection
mFrameSel = do_CreateInstance(kFrameSelectionCID, &rv);
if (NS_FAILED(rv))
return nsnull;
mFrameSel->SetScrollableViewProvider(this);
// Create a SelectionController
mSelCon = static_cast<nsISelectionController*>
(new nsTextInputSelectionImpl(mFrameSel, shell, aContent));
if (!mSelCon)
return nsnull;
mTextListener = new nsTextInputListener();
if (!mTextListener)
return nsnull;
NS_ADDREF(mTextListener);
mTextListener->SetFrame(this);
mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
NS_ENSURE_SUCCESS(rv, rv);
// Setup the editor flags
PRUint32 editorFlags = 0;
@ -1455,7 +1428,7 @@ nsTextControlFrame::CreateFrameFor(nsIContent* aContent)
if (IsPasswordTextControl())
editorFlags |= nsIPlaintextEditor::eEditorPasswordMask;
// All gfxtextcontrolframe2's are widgets
// All nsTextControlFrames are widgets
editorFlags |= nsIPlaintextEditor::eEditorWidgetMask;
// Use async reflow and painting for text widgets to improve
@ -1471,10 +1444,16 @@ nsTextControlFrame::CreateFrameFor(nsIContent* aContent)
// NOTE: Conversion of '\n' to <BR> happens inside the
// editor's Init() call.
rv = mEditor->Init(domdoc, shell, aContent, mSelCon, editorFlags);
nsPresContext *presContext = PresContext();
nsIPresShell *shell = presContext->GetPresShell();
if (NS_FAILED(rv))
return nsnull;
// Get the DOM document
nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(shell->GetDocument());
if (!domdoc)
return NS_ERROR_FAILURE;
rv = mEditor->Init(domdoc, shell, mAnonymousDiv, mSelCon, editorFlags);
NS_ENSURE_SUCCESS(rv, rv);
// Initialize the controller for the editor
@ -1489,13 +1468,13 @@ nsTextControlFrame::CreateFrameFor(nsIContent* aContent)
do_QueryInterface(mContent);
if (!textAreaElement)
return nsnull;
return NS_ERROR_FAILURE;
rv = textAreaElement->GetControllers(getter_AddRefs(controllers));
}
if (NS_FAILED(rv))
return nsnull;
return rv;
if (controllers) {
PRUint32 numControllers;
@ -1545,26 +1524,6 @@ nsTextControlFrame::CreateFrameFor(nsIContent* aContent)
textEditor->SetMaxTextLength(maxLength);
}
}
// 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;
nsCOMPtr<nsISelectionListener> listener;
if (NS_SUCCEEDED(shell->GetCaret(getter_AddRefs(caret))) && caret) {
listener = do_QueryInterface(caret);
if (listener) {
selPriv->AddSelectionListener(listener);
}
}
selPriv->AddSelectionListener(static_cast<nsISelectionListener*>
(mTextListener));
}
if (mContent) {
rv = mEditor->GetFlags(&editorFlags);
@ -1589,33 +1548,6 @@ nsTextControlFrame::CreateFrameFor(nsIContent* aContent)
mEditor->SetFlags(editorFlags);
}
return nsnull;
}
nsresult
nsTextControlFrame::InitEditor()
{
// This method must be called during/after the text
// control frame's initial reflow to avoid any unintened
// forced reflows that might result when the editor
// calls into DOM/layout code while trying to set the
// initial string.
//
// 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.
// Check if this method has been called already.
// If so, just return early.
if (mUseEditor)
return NS_OK;
// If the editor is not here, then we can't use it, now can we?
if (!mEditor)
return NS_ERROR_NOT_INITIALIZED;
// Get the current value of the textfield from the content.
nsAutoString defaultValue;
@ -1631,13 +1563,6 @@ nsTextControlFrame::InitEditor()
// editor for us.
if (!defaultValue.IsEmpty()) {
PRUint32 editorFlags = 0;
nsresult rv = mEditor->GetFlags(&editorFlags);
if (NS_FAILED(rv))
return rv;
// 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.
@ -1662,8 +1587,8 @@ nsTextControlFrame::InitEditor()
rv = mEditor->EnableUndo(PR_TRUE);
NS_ASSERTION(NS_SUCCEEDED(rv),"Transaction Manager must have failed");
// Now restore the original editor flags.
// Now restore the original editor flags.
rv = mEditor->SetFlags(editorFlags);
if (NS_FAILED(rv))
@ -1685,6 +1610,8 @@ nsTextControlFrame::InitEditor()
mEditor->EnableUndo(PR_FALSE);
}
mEditor->PostCreate();
return NS_OK;
}
@ -1738,7 +1665,55 @@ nsTextControlFrame::CreateAnonymousContent(nsTArray<nsIContent*>& aElements)
if (!aElements.AppendElement(mAnonymousDiv))
return NS_ERROR_OUT_OF_MEMORY;
// rv = divContent->SetAttr(kNameSpaceID_None,nsGkAtoms::debug, NS_LITERAL_STRING("true"), PR_FALSE);
// Create selection
mFrameSel = do_CreateInstance(kFrameSelectionCID, &rv);
if (NS_FAILED(rv))
return rv;
mFrameSel->SetScrollableViewProvider(this);
// Create a SelectionController
mSelCon = static_cast<nsISelectionController*>
(new nsTextInputSelectionImpl(mFrameSel, shell,
mAnonymousDiv));
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;
nsCOMPtr<nsISelectionListener> listener;
if (NS_SUCCEEDED(shell->GetCaret(getter_AddRefs(caret))) && caret) {
listener = do_QueryInterface(caret);
if (listener) {
selPriv->AddSelectionListener(listener);
}
}
selPriv->AddSelectionListener(static_cast<nsISelectionListener*>
(mTextListener));
}
NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
"Someone forgot a script blocker?");
if (!nsContentUtils::AddScriptRunner(new EditorInitializer(this))) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
@ -1930,11 +1905,7 @@ nsresult nsTextControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aV
if (isUserInput) {
SetFireChangeEventState(PR_TRUE);
}
if (mEditor && mUseEditor) {
// If the editor exists, the control needs to be informed that the value
// has changed.
SetValueChanged(PR_TRUE);
}
SetValueChanged(PR_TRUE);
nsresult rv = SetValue(aValue); // set new text value
if (isUserInput) {
SetFireChangeEventState(fireChangeEvent);
@ -2387,7 +2358,8 @@ nsTextControlFrame::AttributeChanged(PRInt32 aNameSpaceID,
PRInt32 aModType)
{
if (!mEditor || !mSelCon)
return NS_ERROR_NOT_INITIALIZED;
return nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);;
nsresult rv = NS_OK;
if (nsGkAtoms::maxlength == aAttribute)
@ -2428,7 +2400,7 @@ nsTextControlFrame::AttributeChanged(PRInt32 aNameSpaceID,
}
mEditor->SetFlags(flags);
}
else if (mEditor && nsGkAtoms::disabled == aAttribute)
else if (nsGkAtoms::disabled == aAttribute)
{
PRUint32 flags;
mEditor->GetFlags(&flags);
@ -2786,8 +2758,7 @@ nsTextControlFrame::SetInitialChildList(nsIAtom* aListName,
nsIFrame* aChildList)
{
nsresult rv = nsBoxFrame::SetInitialChildList(aListName, aChildList);
if (mEditor)
mEditor->PostCreate();
//look for scroll view below this frame go along first child list
nsIFrame* first = GetFirstChild(nsnull);

View File

@ -119,8 +119,6 @@ public:
// nsIAnonymousContentCreator
virtual nsresult CreateAnonymousContent(nsTArray<nsIContent*>& aElements);
virtual nsIFrame* CreateFrameFor(nsIContent* aContent);
virtual void PostCreateFrames();
// Utility methods to set current widget state
@ -139,7 +137,7 @@ public:
//==== END NSIFORMCONTROLFRAME
//==== NSIGFXTEXTCONTROLFRAME2
//==== NSITEXTCONTROLFRAME
NS_IMETHOD GetEditor(nsIEditor **aEditor);
NS_IMETHOD OwnsValue(PRBool* aOwnsValue);
@ -157,7 +155,7 @@ public:
nsresult GetPhonetic(nsAString& aPhonetic);
//==== END NSIGFXTEXTCONTROLFRAME2
//==== END NSITEXTCONTROLFRAME
//==== OVERLOAD of nsIFrame
virtual nsIAtom* GetType() const;
@ -195,8 +193,6 @@ public: //for methods who access nsTextControlFrame directly
void SetValueChanged(PRBool aValueChanged);
/** Called when the frame is focused, to remember the value for onChange. */
nsresult InitFocusedValue();
nsresult DOMPointToOffset(nsIDOMNode* aNode, PRInt32 aNodeOffset, PRInt32 *aResult);
nsresult OffsetToDOMPoint(PRInt32 aOffset, nsIDOMNode** aResult, PRInt32* aPosition);
void SetFireChangeEventState(PRBool aNewState)
{
@ -216,6 +212,34 @@ public: //for methods who access nsTextControlFrame directly
void MaybeEndSecureKeyboardInput();
protected:
class EditorInitializer;
friend class EditorInitializer;
class EditorInitializer : public nsRunnable {
public:
EditorInitializer(nsTextControlFrame* aFrame) :
mWeakFrame(aFrame),
mFrame(aFrame) {}
NS_IMETHOD Run() {
if (mWeakFrame) {
mFrame->DelayedEditorInit();
}
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)
@ -303,10 +327,6 @@ private:
nsCOMPtr<nsFrameSelection> mFrameSel;
nsTextInputListener* mTextListener;
nsString mFocusedValue;
#ifdef DEBUG
PRBool mCreateFrameForCalled;
#endif
};
#endif

View File

@ -46,14 +46,12 @@
#include "nsQueryFrame.h"
#include "nsIContent.h"
class nsPresContext;
class nsIFrame;
template <class T> class nsTArray;
/**
* Any source for anonymous content can implement this interface to provide it.
* HTML frames like nsFileControlFrame currently use this as well as XUL frames
* like nsScrollbarFrame and nsSliderFrame.
* HTML frames like nsFileControlFrame currently use this.
*
* @see nsCSSFrameConstructor
*/
@ -80,12 +78,6 @@ public:
* is created.
*/
virtual nsIFrame* CreateFrameFor(nsIContent* aContent) { return nsnull; }
/**
* This gets called after the frames for the anonymous content have been
* created and added to the frame tree. By default it does nothing.
*/
virtual void PostCreateFrames() {}
};
#endif