diff --git a/content/html/content/src/nsTextEditorState.cpp b/content/html/content/src/nsTextEditorState.cpp index ddb7968d6ba..75440592411 100644 --- a/content/html/content/src/nsTextEditorState.cpp +++ b/content/html/content/src/nsTextEditorState.cpp @@ -84,7 +84,6 @@ public: RestoreSelectionState(nsTextEditorState *aState, nsTextControlFrame *aFrame, PRInt32 aStart, PRInt32 aEnd) : mFrame(aFrame), - mWeakFrame(aFrame), mStart(aStart), mEnd(aEnd), mTextEditorState(aState) @@ -92,19 +91,24 @@ public: } NS_IMETHOD Run() { - if (mWeakFrame.IsAlive()) { + if (mFrame) { // SetSelectionRange leads to Selection::AddRange which flushes Layout - // need to block script to avoid nested PrepareEditor calls (bug 642800). nsAutoScriptBlocker scriptBlocker; mFrame->SetSelectionRange(mStart, mEnd); mTextEditorState->HideSelectionIfBlurred(); } + mTextEditorState->FinishedRestoringSelection(); return NS_OK; } + // Let the text editor tell us we're no longer relevant - avoids use of nsWeakFrame + void Revoke() { + mFrame = nsnull; + } + private: nsTextControlFrame* mFrame; - nsWeakFrame mWeakFrame; PRInt32 mStart; PRInt32 mEnd; nsTextEditorState* mTextEditorState; @@ -645,7 +649,7 @@ protected: protected: - nsWeakFrame mFrame; + nsIFrame* mFrame; nsITextControlElement* const mTxtCtrlElement; @@ -673,7 +677,8 @@ protected: */ nsTextInputListener::nsTextInputListener(nsITextControlElement* aTxtCtrlElement) -: mTxtCtrlElement(aTxtCtrlElement) +: mFrame(nsnull) +, mTxtCtrlElement(aTxtCtrlElement) , mSelectionWasCollapsed(PR_TRUE) , mHadUndoItems(PR_FALSE) , mHadRedoItems(PR_FALSE) @@ -703,7 +708,9 @@ NS_IMETHODIMP nsTextInputListener::NotifySelectionChanged(nsIDOMDocument* aDoc, nsISelection* aSel, PRInt16 aReason) { PRBool collapsed; - if (!mFrame.IsAlive() || !aDoc || !aSel || NS_FAILED(aSel->GetIsCollapsed(&collapsed))) + nsWeakFrame weakFrame = mFrame; + + if (!aDoc || !aSel || NS_FAILED(aSel->GetIsCollapsed(&collapsed))) return NS_OK; // Fire the select event @@ -748,7 +755,7 @@ nsTextInputListener::NotifySelectionChanged(nsIDOMDocument* aDoc, nsISelection* mSelectionWasCollapsed = collapsed; - if (!mFrame.IsAlive() || !nsContentUtils::IsFocusedContent(mFrame->GetContent())) + if (!weakFrame.IsAlive() || !nsContentUtils::IsFocusedContent(mFrame->GetContent())) return NS_OK; return UpdateTextInputCommands(NS_LITERAL_STRING("select")); @@ -799,7 +806,6 @@ DoCommandCallback(const char *aCommand, void *aData) NS_IMETHODIMP nsTextInputListener::KeyDown(nsIDOMEvent *aDOMEvent) { - NS_ENSURE_STATE(mFrame.IsAlive()); nsCOMPtr keyEvent(do_QueryInterface(aDOMEvent)); NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG); @@ -818,7 +824,6 @@ nsTextInputListener::KeyDown(nsIDOMEvent *aDOMEvent) NS_IMETHODIMP nsTextInputListener::KeyPress(nsIDOMEvent *aDOMEvent) { - NS_ENSURE_STATE(mFrame.IsAlive()); nsCOMPtr keyEvent(do_QueryInterface(aDOMEvent)); NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG); @@ -837,7 +842,6 @@ nsTextInputListener::KeyPress(nsIDOMEvent *aDOMEvent) NS_IMETHODIMP nsTextInputListener::KeyUp(nsIDOMEvent *aDOMEvent) { - NS_ENSURE_STATE(mFrame.IsAlive()); nsCOMPtr keyEvent(do_QueryInterface(aDOMEvent)); NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG); @@ -859,8 +863,9 @@ nsTextInputListener::KeyUp(nsIDOMEvent *aDOMEvent) NS_IMETHODIMP nsTextInputListener::EditAction() { - NS_ENSURE_STATE(mFrame.IsAlive()); - nsITextControlFrame* frameBase = do_QueryFrame(mFrame.GetFrame()); + nsWeakFrame weakFrame = mFrame; + + nsITextControlFrame* frameBase = do_QueryFrame(mFrame); nsTextControlFrame* frame = static_cast (frameBase); NS_ASSERTION(frame, "Where is our frame?"); // @@ -887,7 +892,7 @@ nsTextInputListener::EditAction() mHadRedoItems = numRedoItems != 0; } - if (!mFrame.IsAlive()) { + if (!weakFrame.IsAlive()) { return NS_OK; } @@ -916,8 +921,6 @@ nsTextInputListener::EditAction() nsresult nsTextInputListener::UpdateTextInputCommands(const nsAString& commandsToUpdate) { - NS_ENSURE_STATE(mFrame.IsAlive()); - nsIContent* content = mFrame->GetContent(); NS_ENSURE_TRUE(content, NS_ERROR_FAILURE); @@ -967,6 +970,7 @@ nsTextInputListener::GetKeyBindings() nsTextEditorState::nsTextEditorState(nsITextControlElement* aOwningElement) : mTextCtrlElement(aOwningElement), + mRestoringSelection(nsnull), mBoundFrame(nsnull), mTextListener(nsnull), mEditorInitialized(PR_FALSE), @@ -1385,8 +1389,13 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue) // Restore our selection after being bound to a new frame if (mSelState) { - nsContentUtils::AddScriptRunner(new RestoreSelectionState(this, mBoundFrame, mSelState->mStart, mSelState->mEnd)); - mSelState = nsnull; + if (mRestoringSelection) // paranoia + mRestoringSelection->Revoke(); + mRestoringSelection = new RestoreSelectionState(this, mBoundFrame, mSelState->mStart, mSelState->mEnd); + if (mRestoringSelection) { + nsContentUtils::AddScriptRunner(mRestoringSelection); + mSelState = nsnull; + } } return rv; @@ -1419,6 +1428,11 @@ nsTextEditorState::UnbindFromFrame(nsTextControlFrame* aFrame) nsAutoString value; GetValue(value, PR_TRUE); + if (mRestoringSelection) { + mRestoringSelection->Revoke(); + mRestoringSelection = nsnull; + } + // Save our selection state if needed. // Note that nsTextControlFrame::GetSelectionRange attempts to initialize the // editor before grabbing the range, and because this is not an acceptable diff --git a/content/html/content/src/nsTextEditorState.h b/content/html/content/src/nsTextEditorState.h index ec707dae03e..b0cf011103c 100644 --- a/content/html/content/src/nsTextEditorState.h +++ b/content/html/content/src/nsTextEditorState.h @@ -140,6 +140,8 @@ struct SelectionState; * frame is bound to the text editor state object. */ +class RestoreSelectionState; + class nsTextEditorState { public: explicit nsTextEditorState(nsITextControlElement* aOwningElement); @@ -213,6 +215,8 @@ public: void HideSelectionIfBlurred(); private: + friend class RestoreSelectionState; + // not copy constructible nsTextEditorState(const nsTextEditorState&); // not assignable @@ -225,6 +229,8 @@ private: void DestroyEditor(); void Clear(); + void FinishedRestoringSelection() { mRestoringSelection = nsnull; } + class InitializationGuard { public: explicit InitializationGuard(nsTextEditorState& aState) : @@ -253,6 +259,7 @@ private: nsITextControlElement* const mTextCtrlElement; nsRefPtr mSelCon; nsAutoPtr mSelState; + RestoreSelectionState* mRestoringSelection; nsCOMPtr mEditor; nsCOMPtr mRootNode; nsCOMPtr mPlaceholderDiv; diff --git a/layout/forms/nsTextControlFrame.cpp b/layout/forms/nsTextControlFrame.cpp index e6ad1347f49..e777abdc3f0 100644 --- a/layout/forms/nsTextControlFrame.cpp +++ b/layout/forms/nsTextControlFrame.cpp @@ -201,6 +201,12 @@ nsTextControlFrame::DestroyFrom(nsIFrame* aDestructRoot) { mScrollEvent.Revoke(); + EditorInitializer* initializer = (EditorInitializer*) Properties().Get(TextControlInitializer()); + if (initializer) { + initializer->Revoke(); + Properties().Delete(TextControlInitializer()); + } + // Unbind the text editor state object from the frame. The editor will live // on, but things like controllers will be released. nsCOMPtr txtCtrl = do_QueryInterface(GetContent()); @@ -445,10 +451,17 @@ nsTextControlFrame::CreateAnonymousContent(nsTArray& aElements) if (initEagerly) { NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), "Someone forgot a script blocker?"); - - if (!nsContentUtils::AddScriptRunner(new EditorInitializer(this))) { + EditorInitializer* initializer = (EditorInitializer*) Properties().Get(TextControlInitializer()); + if (initializer) { + initializer->Revoke(); + } + initializer = new EditorInitializer(this); + if (!nsContentUtils::AddScriptRunner(initializer)) { + initializer->Revoke(); // paranoia + delete initializer; return NS_ERROR_OUT_OF_MEMORY; } + Properties().Set(TextControlInitializer(),initializer); } return NS_OK; diff --git a/layout/forms/nsTextControlFrame.h b/layout/forms/nsTextControlFrame.h index f6e87c6f20f..638c6981204 100644 --- a/layout/forms/nsTextControlFrame.h +++ b/layout/forms/nsTextControlFrame.h @@ -180,6 +180,11 @@ public: NS_DECL_QUERYFRAME + // Temp reference to scriptrunner + // We could make these auto-Revoking via the "delete" entry for safety + NS_DECLARE_FRAME_PROPERTY(TextControlInitializer, nsnull) + + public: //for methods who access nsTextControlFrame directly void FireOnInput(PRBool aTrusted); void SetValueChanged(PRBool aValueChanged); @@ -286,23 +291,27 @@ protected: class EditorInitializer : public nsRunnable { public: EditorInitializer(nsTextControlFrame* aFrame) : - mWeakFrame(aFrame), mFrame(aFrame) {} NS_IMETHOD Run() { - if (mWeakFrame) { + if (mFrame) { nsCOMPtr shell = - mWeakFrame.GetFrame()->PresContext()->GetPresShell(); + mFrame->PresContext()->GetPresShell(); PRBool observes = shell->ObservesNativeAnonMutationsForPrint(); shell->ObserveNativeAnonMutationsForPrint(PR_TRUE); mFrame->EnsureEditorInitialized(); shell->ObserveNativeAnonMutationsForPrint(observes); + mFrame->FinishedInitializer(); } return NS_OK; } + // avoids use of nsWeakFrame + void Revoke() { + mFrame = nsnull; + } + private: - nsWeakFrame mWeakFrame; nsTextControlFrame* mFrame; }; @@ -388,6 +397,10 @@ private: */ nsresult GetRootNodeAndInitializeEditor(nsIDOMElement **aRootElement); + void FinishedInitializer() { + Properties().Delete(TextControlInitializer()); + } + private: // these packed bools could instead use the high order bits on mState, saving 4 bytes PRPackedBool mUseEditor;