From 37e8c3a3920e41eaab46cefd32325fb58cefe299 Mon Sep 17 00:00:00 2001 From: "troy%netscape.com" Date: Fri, 16 Jul 1999 23:27:46 +0000 Subject: [PATCH] Added code to make sure that posted events are removed when the frame they refer to is destroyed --- layout/base/nsPresShell.cpp | 180 +++++++++++++++++++++------ layout/html/base/src/nsPresShell.cpp | 180 +++++++++++++++++++++------ 2 files changed, 290 insertions(+), 70 deletions(-) diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 2179a402f6b8..dcff4c8eac7d 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -241,6 +241,8 @@ static NS_DEFINE_IID(kIScrollableViewIID, NS_ISCROLLABLEVIEW_IID); static NS_DEFINE_IID(kViewCID, NS_VIEW_CID); static NS_DEFINE_IID(kIWebShellIID, NS_IWEB_SHELL_IID); +struct CantRenderReplacedElementEvent; + class PresShell : public nsIPresShell, public nsIViewObserver, private nsIDocumentObserver, public nsIFocusTracker, public nsIDOMSelectionListener @@ -305,6 +307,8 @@ public: PRInt32 aHOffsetPercent, PRUint32 aHFlags) const; + NS_IMETHOD NotifyDestroyingFrame(nsIFrame* aFrame); + NS_IMETHOD DoCopy(); //nsIViewObserver interface @@ -383,7 +387,7 @@ public: NS_IMETHOD DocumentWillBeDestroyed(nsIDocument *aDocument); // implementation - void HandleCantRenderReplacedElementEvent(nsIFrame* aFrame); + void HandleCantRenderReplacedElementEvent(CantRenderReplacedElementEvent* aEvent); protected: virtual ~PresShell(); @@ -430,7 +434,13 @@ protected: PRBool mScrollingEnabled; //used to disable programmable scrolling from outside nsDST* mPrimaryFrameMap; FrameHashTable* mPlaceholderMap; + CantRenderReplacedElementEvent* mPostedEvents; + private: + void RevokePostedEvents(); + CantRenderReplacedElementEvent** FindPostedEventFor(nsIFrame* aFrame); + void DequeuePostedEventFor(nsIFrame* aFrame); + //helper funcs for disabing autoscrolling void DisableScrolling(){mScrollingEnabled = PR_FALSE;} void EnableScrolling(){mScrollingEnabled = PR_TRUE;} @@ -590,6 +600,8 @@ PresShell::~PresShell() // Disable paints during tear down of the frame tree mViewManager->DisableRefresh(); } + // Revoke any events posted to the event queue that we haven't processed yet + RevokePostedEvents(); if (mRootFrame) mRootFrame->DeleteFrame(*mPresContext); if (mDocument) @@ -684,6 +696,32 @@ PresShell::Init(nsIDocument* aDocument, return NS_OK; } +void +PresShell::RevokePostedEvents() +{ + if (mPostedEvents) { + mPostedEvents = nsnull; + + // Revoke any events in the event queue that are owned by us + nsIEventQueueService* eventService; + nsresult rv; + + rv = nsServiceManager::GetService(kEventQueueServiceCID, + kIEventQueueServiceIID, + (nsISupports **)&eventService); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr eventQueue; + rv = eventService->GetThreadEventQueue(PR_GetCurrentThread(), + getter_AddRefs(eventQueue)); + nsServiceManager::ReleaseService(kEventQueueServiceCID, eventService); + + if (NS_SUCCEEDED(rv) && eventQueue) { + eventQueue->RevokeEvents(this); + } + } + } +} + NS_IMETHODIMP PresShell::EnterReflowLock() { @@ -1010,7 +1048,8 @@ PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight) return NS_OK; //XXX this needs to be real. MMP } -NS_IMETHODIMP PresShell::ScrollFrameIntoView(nsIFrame *aFrame){ +NS_IMETHODIMP +PresShell::ScrollFrameIntoView(nsIFrame *aFrame){ if (!aFrame) return NS_ERROR_NULL_POINTER; if (IsScrollingEnabled()) @@ -1020,6 +1059,16 @@ NS_IMETHODIMP PresShell::ScrollFrameIntoView(nsIFrame *aFrame){ return NS_OK; } +NS_IMETHODIMP +PresShell::NotifyDestroyingFrame(nsIFrame* aFrame) +{ + // Cancel any pending reflow commands targeted at this frame + CancelReflowCommand(aFrame); + + // Dequeue and destroy and posted events for this frame + DequeuePostedEventFor(aFrame); + return NS_OK; +} NS_IMETHODIMP PresShell::GetCaret(nsICaret **outCaret) { @@ -1388,33 +1437,90 @@ PresShell::CreateRenderingContext(nsIFrame *aFrame, return rv; } -void -PresShell::HandleCantRenderReplacedElementEvent(nsIFrame* aFrame) +struct CantRenderReplacedElementEvent : public PLEvent { + CantRenderReplacedElementEvent(PresShell* aShell, nsIFrame* aFrame); + + nsIFrame* mFrame; // the frame that can't be rendered + CantRenderReplacedElementEvent* mNext; // next event in the list +}; + +CantRenderReplacedElementEvent** +PresShell::FindPostedEventFor(nsIFrame* aFrame) { - // Double-check that we haven't deleted the frame hierarchy - // XXX If we stay with this model we approach, then we need to observe - // aFrame and if it's deleted null out the pointer in the PL event struct - if (nsnull != mRootFrame) { - mStyleSet->CantRenderReplacedElement(mPresContext, aFrame); - ProcessReflowCommands(); + CantRenderReplacedElementEvent** event = &mPostedEvents; + + while (*event) { + if ((*event)->mFrame == aFrame) { + return event; + } + event = &(*event)->mNext; + } + + return event; +} + +void +PresShell::DequeuePostedEventFor(nsIFrame* aFrame) +{ + // If there's a posted event for this frame, then remove it + CantRenderReplacedElementEvent** event = FindPostedEventFor(aFrame); + if (*event) { + CantRenderReplacedElementEvent* tmp = *event; + + // Remove it from our linked list of posted events + *event = (*event)->mNext; + + // Dequeue it from the event queue + nsIEventQueueService* eventService; + nsresult rv; + + rv = nsServiceManager::GetService(kEventQueueServiceCID, + kIEventQueueServiceIID, + (nsISupports **)&eventService); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr eventQueue; + rv = eventService->GetThreadEventQueue(PR_GetCurrentThread(), + getter_AddRefs(eventQueue)); + nsServiceManager::ReleaseService(kEventQueueServiceCID, eventService); + + if (NS_SUCCEEDED(rv) && eventQueue) { + PLEventQueue* plqueue; + + eventQueue->GetPLEventQueue(&plqueue); + if (plqueue) { + // Removes the event and destroys it + PL_DequeueEvent(tmp, plqueue); + } + } + } } } -struct CantRenderReplacedElementEvent : public PLEvent { - CantRenderReplacedElementEvent(PresShell* aShell, nsIFrame* aFrame); - ~CantRenderReplacedElementEvent(); +void +PresShell::HandleCantRenderReplacedElementEvent(CantRenderReplacedElementEvent* aEvent) +{ + // Remove the posted event from our linked list + CantRenderReplacedElementEvent** events = &mPostedEvents; + while (*events) { + if (*events == aEvent) { + *events = (*events)->mNext; + break; + } else { + events = &(*events)->mNext; + } + } - PresShell* mShell; - nsIContent* mContent; // using content rather than frame, see bug #3605 -}; + // Notify the style system and then process any reflow commands that + // are generated + mStyleSet->CantRenderReplacedElement(mPresContext, aEvent->mFrame); + ProcessReflowCommands(); +} static void PR_CALLBACK HandlePLEvent(CantRenderReplacedElementEvent* aEvent) { - nsIFrame* frame; - if (NS_SUCCEEDED(aEvent->mShell->GetPrimaryFrameFor(aEvent->mContent, &frame))) { - aEvent->mShell->HandleCantRenderReplacedElementEvent(frame); - } + PresShell* shell = (PresShell*)aEvent->owner; + shell->HandleCantRenderReplacedElementEvent(aEvent); } static void PR_CALLBACK @@ -1426,19 +1532,11 @@ DestroyPLEvent(CantRenderReplacedElementEvent* aEvent) CantRenderReplacedElementEvent::CantRenderReplacedElementEvent(PresShell* aShell, nsIFrame* aFrame) { - nsIContent* content; - mShell = aShell; - NS_ADDREF(mShell); - aFrame->GetContent(&content); - mContent = content; - PL_InitEvent(this, nsnull, (PLHandleEventProc)::HandlePLEvent, + // Note: because the pres shell owns us we don't hold a reference to the + // pres shell + PL_InitEvent(this, aShell, (PLHandleEventProc)::HandlePLEvent, (PLDestroyEventProc)::DestroyPLEvent); -} - -CantRenderReplacedElementEvent::~CantRenderReplacedElementEvent() -{ - NS_RELEASE(mShell); - NS_RELEASE(mContent); + mFrame = aFrame; } NS_IMETHODIMP @@ -1447,9 +1545,9 @@ PresShell::CantRenderReplacedElement(nsIPresContext* aPresContext, { nsIEventQueueService* eventService; nsresult rv; - - // Notify the style set, but post the notification so it doesn't happen - // now + + // We need to notify the style stystem, but post the notification so it + // doesn't happen now rv = nsServiceManager::GetService(kEventQueueServiceCID, kIEventQueueServiceIID, (nsISupports **)&eventService); @@ -1460,9 +1558,21 @@ PresShell::CantRenderReplacedElement(nsIPresContext* aPresContext, nsServiceManager::ReleaseService(kEventQueueServiceCID, eventService); if (NS_SUCCEEDED(rv) && eventQueue) { +#ifdef NS_DEBUG + // Verify that there isn't already a posted event associated with + // this frame + NS_ASSERTION(!*FindPostedEventFor(aFrame), "frame already has posted event"); +#endif CantRenderReplacedElementEvent* ev; + // Create a new event ev = new CantRenderReplacedElementEvent(this, aFrame); + + // Add the event to our linked list of posted events + ev->mNext = mPostedEvents; + mPostedEvents = ev; + + // Post the event eventQueue->PostEvent(ev); } } diff --git a/layout/html/base/src/nsPresShell.cpp b/layout/html/base/src/nsPresShell.cpp index 2179a402f6b8..dcff4c8eac7d 100644 --- a/layout/html/base/src/nsPresShell.cpp +++ b/layout/html/base/src/nsPresShell.cpp @@ -241,6 +241,8 @@ static NS_DEFINE_IID(kIScrollableViewIID, NS_ISCROLLABLEVIEW_IID); static NS_DEFINE_IID(kViewCID, NS_VIEW_CID); static NS_DEFINE_IID(kIWebShellIID, NS_IWEB_SHELL_IID); +struct CantRenderReplacedElementEvent; + class PresShell : public nsIPresShell, public nsIViewObserver, private nsIDocumentObserver, public nsIFocusTracker, public nsIDOMSelectionListener @@ -305,6 +307,8 @@ public: PRInt32 aHOffsetPercent, PRUint32 aHFlags) const; + NS_IMETHOD NotifyDestroyingFrame(nsIFrame* aFrame); + NS_IMETHOD DoCopy(); //nsIViewObserver interface @@ -383,7 +387,7 @@ public: NS_IMETHOD DocumentWillBeDestroyed(nsIDocument *aDocument); // implementation - void HandleCantRenderReplacedElementEvent(nsIFrame* aFrame); + void HandleCantRenderReplacedElementEvent(CantRenderReplacedElementEvent* aEvent); protected: virtual ~PresShell(); @@ -430,7 +434,13 @@ protected: PRBool mScrollingEnabled; //used to disable programmable scrolling from outside nsDST* mPrimaryFrameMap; FrameHashTable* mPlaceholderMap; + CantRenderReplacedElementEvent* mPostedEvents; + private: + void RevokePostedEvents(); + CantRenderReplacedElementEvent** FindPostedEventFor(nsIFrame* aFrame); + void DequeuePostedEventFor(nsIFrame* aFrame); + //helper funcs for disabing autoscrolling void DisableScrolling(){mScrollingEnabled = PR_FALSE;} void EnableScrolling(){mScrollingEnabled = PR_TRUE;} @@ -590,6 +600,8 @@ PresShell::~PresShell() // Disable paints during tear down of the frame tree mViewManager->DisableRefresh(); } + // Revoke any events posted to the event queue that we haven't processed yet + RevokePostedEvents(); if (mRootFrame) mRootFrame->DeleteFrame(*mPresContext); if (mDocument) @@ -684,6 +696,32 @@ PresShell::Init(nsIDocument* aDocument, return NS_OK; } +void +PresShell::RevokePostedEvents() +{ + if (mPostedEvents) { + mPostedEvents = nsnull; + + // Revoke any events in the event queue that are owned by us + nsIEventQueueService* eventService; + nsresult rv; + + rv = nsServiceManager::GetService(kEventQueueServiceCID, + kIEventQueueServiceIID, + (nsISupports **)&eventService); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr eventQueue; + rv = eventService->GetThreadEventQueue(PR_GetCurrentThread(), + getter_AddRefs(eventQueue)); + nsServiceManager::ReleaseService(kEventQueueServiceCID, eventService); + + if (NS_SUCCEEDED(rv) && eventQueue) { + eventQueue->RevokeEvents(this); + } + } + } +} + NS_IMETHODIMP PresShell::EnterReflowLock() { @@ -1010,7 +1048,8 @@ PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight) return NS_OK; //XXX this needs to be real. MMP } -NS_IMETHODIMP PresShell::ScrollFrameIntoView(nsIFrame *aFrame){ +NS_IMETHODIMP +PresShell::ScrollFrameIntoView(nsIFrame *aFrame){ if (!aFrame) return NS_ERROR_NULL_POINTER; if (IsScrollingEnabled()) @@ -1020,6 +1059,16 @@ NS_IMETHODIMP PresShell::ScrollFrameIntoView(nsIFrame *aFrame){ return NS_OK; } +NS_IMETHODIMP +PresShell::NotifyDestroyingFrame(nsIFrame* aFrame) +{ + // Cancel any pending reflow commands targeted at this frame + CancelReflowCommand(aFrame); + + // Dequeue and destroy and posted events for this frame + DequeuePostedEventFor(aFrame); + return NS_OK; +} NS_IMETHODIMP PresShell::GetCaret(nsICaret **outCaret) { @@ -1388,33 +1437,90 @@ PresShell::CreateRenderingContext(nsIFrame *aFrame, return rv; } -void -PresShell::HandleCantRenderReplacedElementEvent(nsIFrame* aFrame) +struct CantRenderReplacedElementEvent : public PLEvent { + CantRenderReplacedElementEvent(PresShell* aShell, nsIFrame* aFrame); + + nsIFrame* mFrame; // the frame that can't be rendered + CantRenderReplacedElementEvent* mNext; // next event in the list +}; + +CantRenderReplacedElementEvent** +PresShell::FindPostedEventFor(nsIFrame* aFrame) { - // Double-check that we haven't deleted the frame hierarchy - // XXX If we stay with this model we approach, then we need to observe - // aFrame and if it's deleted null out the pointer in the PL event struct - if (nsnull != mRootFrame) { - mStyleSet->CantRenderReplacedElement(mPresContext, aFrame); - ProcessReflowCommands(); + CantRenderReplacedElementEvent** event = &mPostedEvents; + + while (*event) { + if ((*event)->mFrame == aFrame) { + return event; + } + event = &(*event)->mNext; + } + + return event; +} + +void +PresShell::DequeuePostedEventFor(nsIFrame* aFrame) +{ + // If there's a posted event for this frame, then remove it + CantRenderReplacedElementEvent** event = FindPostedEventFor(aFrame); + if (*event) { + CantRenderReplacedElementEvent* tmp = *event; + + // Remove it from our linked list of posted events + *event = (*event)->mNext; + + // Dequeue it from the event queue + nsIEventQueueService* eventService; + nsresult rv; + + rv = nsServiceManager::GetService(kEventQueueServiceCID, + kIEventQueueServiceIID, + (nsISupports **)&eventService); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr eventQueue; + rv = eventService->GetThreadEventQueue(PR_GetCurrentThread(), + getter_AddRefs(eventQueue)); + nsServiceManager::ReleaseService(kEventQueueServiceCID, eventService); + + if (NS_SUCCEEDED(rv) && eventQueue) { + PLEventQueue* plqueue; + + eventQueue->GetPLEventQueue(&plqueue); + if (plqueue) { + // Removes the event and destroys it + PL_DequeueEvent(tmp, plqueue); + } + } + } } } -struct CantRenderReplacedElementEvent : public PLEvent { - CantRenderReplacedElementEvent(PresShell* aShell, nsIFrame* aFrame); - ~CantRenderReplacedElementEvent(); +void +PresShell::HandleCantRenderReplacedElementEvent(CantRenderReplacedElementEvent* aEvent) +{ + // Remove the posted event from our linked list + CantRenderReplacedElementEvent** events = &mPostedEvents; + while (*events) { + if (*events == aEvent) { + *events = (*events)->mNext; + break; + } else { + events = &(*events)->mNext; + } + } - PresShell* mShell; - nsIContent* mContent; // using content rather than frame, see bug #3605 -}; + // Notify the style system and then process any reflow commands that + // are generated + mStyleSet->CantRenderReplacedElement(mPresContext, aEvent->mFrame); + ProcessReflowCommands(); +} static void PR_CALLBACK HandlePLEvent(CantRenderReplacedElementEvent* aEvent) { - nsIFrame* frame; - if (NS_SUCCEEDED(aEvent->mShell->GetPrimaryFrameFor(aEvent->mContent, &frame))) { - aEvent->mShell->HandleCantRenderReplacedElementEvent(frame); - } + PresShell* shell = (PresShell*)aEvent->owner; + shell->HandleCantRenderReplacedElementEvent(aEvent); } static void PR_CALLBACK @@ -1426,19 +1532,11 @@ DestroyPLEvent(CantRenderReplacedElementEvent* aEvent) CantRenderReplacedElementEvent::CantRenderReplacedElementEvent(PresShell* aShell, nsIFrame* aFrame) { - nsIContent* content; - mShell = aShell; - NS_ADDREF(mShell); - aFrame->GetContent(&content); - mContent = content; - PL_InitEvent(this, nsnull, (PLHandleEventProc)::HandlePLEvent, + // Note: because the pres shell owns us we don't hold a reference to the + // pres shell + PL_InitEvent(this, aShell, (PLHandleEventProc)::HandlePLEvent, (PLDestroyEventProc)::DestroyPLEvent); -} - -CantRenderReplacedElementEvent::~CantRenderReplacedElementEvent() -{ - NS_RELEASE(mShell); - NS_RELEASE(mContent); + mFrame = aFrame; } NS_IMETHODIMP @@ -1447,9 +1545,9 @@ PresShell::CantRenderReplacedElement(nsIPresContext* aPresContext, { nsIEventQueueService* eventService; nsresult rv; - - // Notify the style set, but post the notification so it doesn't happen - // now + + // We need to notify the style stystem, but post the notification so it + // doesn't happen now rv = nsServiceManager::GetService(kEventQueueServiceCID, kIEventQueueServiceIID, (nsISupports **)&eventService); @@ -1460,9 +1558,21 @@ PresShell::CantRenderReplacedElement(nsIPresContext* aPresContext, nsServiceManager::ReleaseService(kEventQueueServiceCID, eventService); if (NS_SUCCEEDED(rv) && eventQueue) { +#ifdef NS_DEBUG + // Verify that there isn't already a posted event associated with + // this frame + NS_ASSERTION(!*FindPostedEventFor(aFrame), "frame already has posted event"); +#endif CantRenderReplacedElementEvent* ev; + // Create a new event ev = new CantRenderReplacedElementEvent(this, aFrame); + + // Add the event to our linked list of posted events + ev->mNext = mPostedEvents; + mPostedEvents = ev; + + // Post the event eventQueue->PostEvent(ev); } }