From 65520ba9285dd2b89f01eccef9fd5f876629e718 Mon Sep 17 00:00:00 2001 From: "joki%netscape.com" Date: Thu, 19 Nov 1998 00:43:36 +0000 Subject: [PATCH] Modify event flow to avoid walking into trashed frames. --- content/events/public/nsIEventStateManager.h | 1 + content/events/src/nsEventStateManager.cpp | 33 ++++++++++++++++++-- content/events/src/nsEventStateManager.h | 2 ++ layout/base/nsIPresShell.h | 1 + layout/base/nsPresShell.cpp | 13 +++++++- layout/base/public/nsIFrame.h | 5 +++ layout/base/public/nsIPresShell.h | 1 + layout/events/public/nsIEventStateManager.h | 1 + layout/events/src/nsEventStateManager.cpp | 33 ++++++++++++++++++-- layout/events/src/nsEventStateManager.h | 2 ++ layout/generic/nsFrame.cpp | 8 +++++ layout/generic/nsIFrame.h | 5 +++ layout/html/base/src/nsFrame.cpp | 8 +++++ layout/html/base/src/nsPresShell.cpp | 13 +++++++- 14 files changed, 120 insertions(+), 6 deletions(-) diff --git a/content/events/public/nsIEventStateManager.h b/content/events/public/nsIEventStateManager.h index e8f47322031c..6d75caa2ad6e 100644 --- a/content/events/public/nsIEventStateManager.h +++ b/content/events/public/nsIEventStateManager.h @@ -46,6 +46,7 @@ public: nsEventStatus& aStatus) = 0; NS_IMETHOD SetPresContext(nsIPresContext* aPresContext) = 0; + NS_IMETHOD ClearFrameRefs(nsIFrame* aFrame) = 0; NS_IMETHOD GetEventTarget(nsIFrame **aFrame) = 0; diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index bdd69e9f2a5a..1329d070609a 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -67,6 +67,12 @@ nsEventStateManager::HandleEvent(nsIPresContext& aPresContext, nsEventStatus& aStatus) { mCurrentTarget = aTargetFrame; + + nsFrameState state; + mCurrentTarget->GetFrameState(state); + state |= NS_FRAME_EXTERNAL_REFERENCE; + mCurrentTarget->SetFrameState(state); + aStatus = nsEventStatus_eIgnore; switch (aEvent->message) { @@ -100,6 +106,18 @@ nsEventStateManager::SetPresContext(nsIPresContext* aPresContext) return NS_OK; } +NS_IMETHODIMP +nsEventStateManager::ClearFrameRefs(nsIFrame* aFrame) +{ + if (aFrame == mLastMouseOverFrame) { + mLastMouseOverFrame = nsnull; + } + else if (aFrame == mCurrentTarget) { + mCurrentTarget = nsnull; + } + return NS_OK; +} + void nsEventStateManager::UpdateCursor(nsIPresContext& aPresContext, nsPoint& aPoint, nsIFrame* aTargetFrame) { @@ -171,7 +189,10 @@ nsEventStateManager::GenerateMouseEnterExit(nsIPresContext& aPresContext, nsGUIE mLastMouseOverFrame->GetContent(lastContent); if (lastContent != targetContent) { - lastContent->HandleDOMEvent(aPresContext, &event, nsnull, DOM_EVENT_INIT, status); + //XXX This event should still go somewhere!! + if (nsnull != lastContent) { + lastContent->HandleDOMEvent(aPresContext, &event, nsnull, DOM_EVENT_INIT, status); + } } //Now dispatch to the frame @@ -186,7 +207,10 @@ nsEventStateManager::GenerateMouseEnterExit(nsIPresContext& aPresContext, nsGUIE //The frame has change but the content may not have. Check before dispatching to content if (lastContent != targetContent) { - targetContent->HandleDOMEvent(aPresContext, &event, nsnull, DOM_EVENT_INIT, status); + //XXX This event should still go somewhere!! + if (nsnull != targetContent) { + targetContent->HandleDOMEvent(aPresContext, &event, nsnull, DOM_EVENT_INIT, status); + } } //Now dispatch to the frame @@ -196,6 +220,11 @@ nsEventStateManager::GenerateMouseEnterExit(nsIPresContext& aPresContext, nsGUIE NS_IF_RELEASE(targetContent); mLastMouseOverFrame = aTargetFrame; + + nsFrameState state; + mLastMouseOverFrame->GetFrameState(state); + state |= NS_FRAME_EXTERNAL_REFERENCE; + mLastMouseOverFrame->SetFrameState(state); } } break; diff --git a/content/events/src/nsEventStateManager.h b/content/events/src/nsEventStateManager.h index b43bd3a2e538..f64961ac4beb 100644 --- a/content/events/src/nsEventStateManager.h +++ b/content/events/src/nsEventStateManager.h @@ -41,12 +41,14 @@ public: nsEventStatus& aStatus); NS_IMETHOD SetPresContext(nsIPresContext* aPresContext); + NS_IMETHOD ClearFrameRefs(nsIFrame* aFrame); NS_IMETHOD GetEventTarget(nsIFrame **aFrame); NS_IMETHOD GetActiveLink(nsIContent **aLink); NS_IMETHOD SetActiveLink(nsIContent *aLink); + protected: void UpdateCursor(nsIPresContext& aPresContext, nsPoint& aPoint, nsIFrame* aTargetFrame); void GenerateMouseEnterExit(nsIPresContext& aPresContext, nsGUIEvent* aEvent, nsIFrame* aTargetFrame); diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index 460d98a1e644..50e8615b3e2a 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -100,6 +100,7 @@ public: virtual void ProcessReflowCommands() = 0; + virtual void ClearFrameRefs(nsIFrame* aFrame) = 0; /** * Given a frame, cough up a rendering context suitable for use with * the frame. diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index f2669bb05cdf..93db40425edb 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -215,6 +215,7 @@ public: virtual nsIFrame* FindFrameWithContent(nsIContent* aContent); virtual void AppendReflowCommand(nsIReflowCommand* aReflowCommand); virtual void ProcessReflowCommands(); + virtual void ClearFrameRefs(nsIFrame*); NS_IMETHOD CreateRenderingContext(nsIFrame *aFrame, nsIRenderingContext *&aContext); //nsIViewObserver interface @@ -739,6 +740,16 @@ PresShell::ProcessReflowCommands() } } +void +PresShell::ClearFrameRefs(nsIFrame* aFrame) +{ + nsIEventStateManager *manager; + if (NS_OK == mPresContext->GetEventStateManager(&manager)) { + manager->ClearFrameRefs(aFrame); + NS_RELEASE(manager); + } +} + NS_IMETHODIMP PresShell :: CreateRenderingContext(nsIFrame *aFrame, nsIRenderingContext *&aContext) { @@ -1013,7 +1024,7 @@ NS_IMETHODIMP PresShell :: HandleEvent(nsIView *aView, NS_ASSERTION(!(nsnull == aView), "null view"); - if (mIsDestroying) { + if (mIsDestroying || mReflowLockCount > 0) { return NS_OK; } diff --git a/layout/base/public/nsIFrame.h b/layout/base/public/nsIFrame.h index 8bbbc6661944..dd579de1c443 100644 --- a/layout/base/public/nsIFrame.h +++ b/layout/base/public/nsIFrame.h @@ -105,6 +105,11 @@ typedef PRUint32 nsFrameState; // must operate differently. #define NS_FRAME_OUTSIDE_CHILDREN 0x00000008 +// If this bit is set then a reference to the frame is being held +// elsewhere. The frame may want to send a notification when it is +// destroyed to allow these references to be cleared. +#define NS_FRAME_EXTERNAL_REFERENCE 0x00000010 + //---------------------------------------------------------------------- /** diff --git a/layout/base/public/nsIPresShell.h b/layout/base/public/nsIPresShell.h index 460d98a1e644..50e8615b3e2a 100644 --- a/layout/base/public/nsIPresShell.h +++ b/layout/base/public/nsIPresShell.h @@ -100,6 +100,7 @@ public: virtual void ProcessReflowCommands() = 0; + virtual void ClearFrameRefs(nsIFrame* aFrame) = 0; /** * Given a frame, cough up a rendering context suitable for use with * the frame. diff --git a/layout/events/public/nsIEventStateManager.h b/layout/events/public/nsIEventStateManager.h index e8f47322031c..6d75caa2ad6e 100644 --- a/layout/events/public/nsIEventStateManager.h +++ b/layout/events/public/nsIEventStateManager.h @@ -46,6 +46,7 @@ public: nsEventStatus& aStatus) = 0; NS_IMETHOD SetPresContext(nsIPresContext* aPresContext) = 0; + NS_IMETHOD ClearFrameRefs(nsIFrame* aFrame) = 0; NS_IMETHOD GetEventTarget(nsIFrame **aFrame) = 0; diff --git a/layout/events/src/nsEventStateManager.cpp b/layout/events/src/nsEventStateManager.cpp index bdd69e9f2a5a..1329d070609a 100644 --- a/layout/events/src/nsEventStateManager.cpp +++ b/layout/events/src/nsEventStateManager.cpp @@ -67,6 +67,12 @@ nsEventStateManager::HandleEvent(nsIPresContext& aPresContext, nsEventStatus& aStatus) { mCurrentTarget = aTargetFrame; + + nsFrameState state; + mCurrentTarget->GetFrameState(state); + state |= NS_FRAME_EXTERNAL_REFERENCE; + mCurrentTarget->SetFrameState(state); + aStatus = nsEventStatus_eIgnore; switch (aEvent->message) { @@ -100,6 +106,18 @@ nsEventStateManager::SetPresContext(nsIPresContext* aPresContext) return NS_OK; } +NS_IMETHODIMP +nsEventStateManager::ClearFrameRefs(nsIFrame* aFrame) +{ + if (aFrame == mLastMouseOverFrame) { + mLastMouseOverFrame = nsnull; + } + else if (aFrame == mCurrentTarget) { + mCurrentTarget = nsnull; + } + return NS_OK; +} + void nsEventStateManager::UpdateCursor(nsIPresContext& aPresContext, nsPoint& aPoint, nsIFrame* aTargetFrame) { @@ -171,7 +189,10 @@ nsEventStateManager::GenerateMouseEnterExit(nsIPresContext& aPresContext, nsGUIE mLastMouseOverFrame->GetContent(lastContent); if (lastContent != targetContent) { - lastContent->HandleDOMEvent(aPresContext, &event, nsnull, DOM_EVENT_INIT, status); + //XXX This event should still go somewhere!! + if (nsnull != lastContent) { + lastContent->HandleDOMEvent(aPresContext, &event, nsnull, DOM_EVENT_INIT, status); + } } //Now dispatch to the frame @@ -186,7 +207,10 @@ nsEventStateManager::GenerateMouseEnterExit(nsIPresContext& aPresContext, nsGUIE //The frame has change but the content may not have. Check before dispatching to content if (lastContent != targetContent) { - targetContent->HandleDOMEvent(aPresContext, &event, nsnull, DOM_EVENT_INIT, status); + //XXX This event should still go somewhere!! + if (nsnull != targetContent) { + targetContent->HandleDOMEvent(aPresContext, &event, nsnull, DOM_EVENT_INIT, status); + } } //Now dispatch to the frame @@ -196,6 +220,11 @@ nsEventStateManager::GenerateMouseEnterExit(nsIPresContext& aPresContext, nsGUIE NS_IF_RELEASE(targetContent); mLastMouseOverFrame = aTargetFrame; + + nsFrameState state; + mLastMouseOverFrame->GetFrameState(state); + state |= NS_FRAME_EXTERNAL_REFERENCE; + mLastMouseOverFrame->SetFrameState(state); } } break; diff --git a/layout/events/src/nsEventStateManager.h b/layout/events/src/nsEventStateManager.h index b43bd3a2e538..f64961ac4beb 100644 --- a/layout/events/src/nsEventStateManager.h +++ b/layout/events/src/nsEventStateManager.h @@ -41,12 +41,14 @@ public: nsEventStatus& aStatus); NS_IMETHOD SetPresContext(nsIPresContext* aPresContext); + NS_IMETHOD ClearFrameRefs(nsIFrame* aFrame); NS_IMETHOD GetEventTarget(nsIFrame **aFrame); NS_IMETHOD GetActiveLink(nsIContent **aLink); NS_IMETHOD SetActiveLink(nsIContent *aLink); + protected: void UpdateCursor(nsIPresContext& aPresContext, nsPoint& aPoint, nsIFrame* aTargetFrame); void GenerateMouseEnterExit(nsIPresContext& aPresContext, nsGUIEvent* aEvent, nsIFrame* aTargetFrame); diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 7fc1ff80fb65..8819d7830148 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -312,6 +312,14 @@ NS_IMETHODIMP nsFrame::SetInitialChildList(nsIPresContext& aPresContext, NS_IMETHODIMP nsFrame::DeleteFrame(nsIPresContext& aPresContext) { + if (mState & NS_FRAME_EXTERNAL_REFERENCE) { + nsIPresShell *shell = aPresContext.GetShell(); + if (nsnull != shell) { + shell->ClearFrameRefs(this); + NS_RELEASE(shell); + } + } + //XXX Why is this done in nsFrame instead of some frame class // that actually loads images? aPresContext.StopLoadImage(this); diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 8bbbc6661944..dd579de1c443 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -105,6 +105,11 @@ typedef PRUint32 nsFrameState; // must operate differently. #define NS_FRAME_OUTSIDE_CHILDREN 0x00000008 +// If this bit is set then a reference to the frame is being held +// elsewhere. The frame may want to send a notification when it is +// destroyed to allow these references to be cleared. +#define NS_FRAME_EXTERNAL_REFERENCE 0x00000010 + //---------------------------------------------------------------------- /** diff --git a/layout/html/base/src/nsFrame.cpp b/layout/html/base/src/nsFrame.cpp index 7fc1ff80fb65..8819d7830148 100644 --- a/layout/html/base/src/nsFrame.cpp +++ b/layout/html/base/src/nsFrame.cpp @@ -312,6 +312,14 @@ NS_IMETHODIMP nsFrame::SetInitialChildList(nsIPresContext& aPresContext, NS_IMETHODIMP nsFrame::DeleteFrame(nsIPresContext& aPresContext) { + if (mState & NS_FRAME_EXTERNAL_REFERENCE) { + nsIPresShell *shell = aPresContext.GetShell(); + if (nsnull != shell) { + shell->ClearFrameRefs(this); + NS_RELEASE(shell); + } + } + //XXX Why is this done in nsFrame instead of some frame class // that actually loads images? aPresContext.StopLoadImage(this); diff --git a/layout/html/base/src/nsPresShell.cpp b/layout/html/base/src/nsPresShell.cpp index f2669bb05cdf..93db40425edb 100644 --- a/layout/html/base/src/nsPresShell.cpp +++ b/layout/html/base/src/nsPresShell.cpp @@ -215,6 +215,7 @@ public: virtual nsIFrame* FindFrameWithContent(nsIContent* aContent); virtual void AppendReflowCommand(nsIReflowCommand* aReflowCommand); virtual void ProcessReflowCommands(); + virtual void ClearFrameRefs(nsIFrame*); NS_IMETHOD CreateRenderingContext(nsIFrame *aFrame, nsIRenderingContext *&aContext); //nsIViewObserver interface @@ -739,6 +740,16 @@ PresShell::ProcessReflowCommands() } } +void +PresShell::ClearFrameRefs(nsIFrame* aFrame) +{ + nsIEventStateManager *manager; + if (NS_OK == mPresContext->GetEventStateManager(&manager)) { + manager->ClearFrameRefs(aFrame); + NS_RELEASE(manager); + } +} + NS_IMETHODIMP PresShell :: CreateRenderingContext(nsIFrame *aFrame, nsIRenderingContext *&aContext) { @@ -1013,7 +1024,7 @@ NS_IMETHODIMP PresShell :: HandleEvent(nsIView *aView, NS_ASSERTION(!(nsnull == aView), "null view"); - if (mIsDestroying) { + if (mIsDestroying || mReflowLockCount > 0) { return NS_OK; }