From 9b4aaa3fb3105928f86a90de4f75dff187e7e864 Mon Sep 17 00:00:00 2001 From: "bzbarsky%mit.edu" Date: Mon, 28 Mar 2005 23:20:54 +0000 Subject: [PATCH] Move handling of replaced element events from frame manager to presshell, make sure we don't allow reflow flushing while we're constructing the new frames. Bug 286813, r+sr=dbaron --- layout/base/nsFrameManager.cpp | 252 ------------------------------- layout/base/nsFrameManager.h | 17 --- layout/base/nsFrameManagerBase.h | 3 - layout/base/nsPresShell.cpp | 224 ++++++++++++++++++++++++++- 4 files changed, 222 insertions(+), 274 deletions(-) diff --git a/layout/base/nsFrameManager.cpp b/layout/base/nsFrameManager.cpp index 64a68722fd4c..5554d18a7603 100644 --- a/layout/base/nsFrameManager.cpp +++ b/layout/base/nsFrameManager.cpp @@ -326,14 +326,6 @@ nsFrameManager::Destroy() } delete mUndisplayedMap; - // If we're not going to be used anymore, we should revoke the - // pending |CantRenderReplacedElementEvent|s being sent to us. -#ifdef DEBUG - nsresult rv = -#endif - RevokePostedEvents(); - NS_ASSERTION(NS_SUCCEEDED(rv), "RevokePostedEvents failed: might crash"); - mPresShell = nsnull; } @@ -742,9 +734,6 @@ nsFrameManager::RemoveFrame(nsIFrame* aParentFrame, void nsFrameManager::NotifyDestroyingFrame(nsIFrame* aFrame) { - // Dequeue and destroy and posted events for this frame - DequeuePostedEventFor(aFrame); - // We've already removed from the primary frame map once, but we're // going to try to do it again here to fix callers of GetPrimaryFrameFor // during frame destruction, since this problem keeps coming back to @@ -760,247 +749,6 @@ nsFrameManager::NotifyDestroyingFrame(nsIFrame* aFrame) } } -nsresult -nsFrameManager::RevokePostedEvents() -{ - nsresult rv = NS_OK; -#ifdef NOISY_EVENTS - printf("%p ~RevokePostedEvents() start\n", this); -#endif - if (mPostedEvents) { - mPostedEvents = nsnull; - - // Revoke any events in the event queue that are owned by us - nsCOMPtr eventService = - do_GetService(kEventQueueServiceCID, &rv); - - if (NS_SUCCEEDED(rv)) { - nsCOMPtr eventQueue; - rv = eventService->GetThreadEventQueue(NS_CURRENT_THREAD, - getter_AddRefs(eventQueue)); - - if (NS_SUCCEEDED(rv) && eventQueue) { - rv = eventQueue->RevokeEvents(this); - } - } - } -#ifdef NOISY_EVENTS - printf("%p ~RevokePostedEvents() end\n", this); -#endif - return rv; -} - -CantRenderReplacedElementEvent** -nsFrameManager::FindPostedEventFor(nsIFrame* aFrame) -{ - CantRenderReplacedElementEvent** event = &mPostedEvents; - - while (*event) { - if ((*event)->mFrame == aFrame) { - return event; - } - event = &(*event)->mNext; - } - - return event; -} - -void -nsFrameManager::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 - nsresult rv; - - nsCOMPtr eventService = - do_GetService(kEventQueueServiceCID, &rv); - - NS_ASSERTION(NS_SUCCEEDED(rv), - "will crash soon due to event holding dangling pointer to frame"); - if (NS_SUCCEEDED(rv)) { - nsCOMPtr eventQueue; - rv = eventService->GetThreadEventQueue(NS_CURRENT_THREAD, - getter_AddRefs(eventQueue)); - - NS_ASSERTION(NS_SUCCEEDED(rv) && eventQueue, - "will crash soon due to event holding dangling pointer to frame"); - if (NS_SUCCEEDED(rv) && eventQueue) { - PLEventQueue* plqueue; - - eventQueue->GetPLEventQueue(&plqueue); - NS_ASSERTION(plqueue, - "will crash soon due to event holding dangling pointer to frame"); - if (plqueue) { - // Remove the event and then destroy it - PL_DequeueEvent(tmp, plqueue); - PL_DestroyEvent(tmp); - } - } - } - } -} - -void -nsFrameManager::HandlePLEvent(CantRenderReplacedElementEvent* aEvent) -{ -#ifdef NOISY_EVENTS - printf("nsFrameManager::HandlePLEvent() start for FM %p\n", aEvent->owner); -#endif - nsFrameManager* frameManager = (nsFrameManager*)aEvent->owner; - NS_ASSERTION(frameManager, "null frame manager"); - - if (!frameManager->mPresShell) { - NS_ASSERTION(frameManager->mPresShell, - "event not removed from queue on shutdown"); - return; - } - - // Remove the posted event from the linked list - CantRenderReplacedElementEvent** events = &frameManager->mPostedEvents; - while (*events) { - if (*events == aEvent) { - *events = (*events)->mNext; - break; - } - events = &(*events)->mNext; - NS_ASSERTION(*events, "event not in queue"); - } - - // Notify the style system and then process any reflow commands that - // are generated - nsIPresShell *shell = frameManager->mPresShell; - shell->FrameConstructor()->CantRenderReplacedElement(aEvent->mFrame); - -#ifdef NOISY_EVENTS - printf("nsFrameManager::HandlePLEvent() end for FM %p\n", aEvent->owner); -#endif -} - -void -nsFrameManager::DestroyPLEvent(CantRenderReplacedElementEvent* aEvent) -{ - delete aEvent; -} - -CantRenderReplacedElementEvent::CantRenderReplacedElementEvent(nsFrameManager* aFrameManager, - nsIFrame* aFrame, - nsIPresShell* aPresShell) -{ - PL_InitEvent(this, aFrameManager, - (PLHandleEventProc)&nsFrameManager::HandlePLEvent, - (PLDestroyEventProc)&nsFrameManager::DestroyPLEvent); - mFrame = aFrame; - - if (nsLayoutAtoms::objectFrame == aFrame->GetType()) { - AddLoadGroupRequest(aPresShell); - } -} - -CantRenderReplacedElementEvent::~CantRenderReplacedElementEvent() -{ - RemoveLoadGroupRequest(); -} - -// Add a load group request in order to delay the onLoad handler when we have -// pending replacements -nsresult -CantRenderReplacedElementEvent::AddLoadGroupRequest(nsIPresShell* aPresShell) -{ - nsIDocument *doc = aPresShell->GetDocument(); - if (!doc) return NS_ERROR_FAILURE; - - nsresult rv = nsDummyLayoutRequest::Create(getter_AddRefs(mDummyLayoutRequest), aPresShell); - if (NS_FAILED(rv)) return rv; - if (!mDummyLayoutRequest) return NS_ERROR_FAILURE; - - nsCOMPtr loadGroup = doc->GetDocumentLoadGroup(); - if (!loadGroup) return NS_ERROR_FAILURE; - - rv = mDummyLayoutRequest->SetLoadGroup(loadGroup); - if (NS_FAILED(rv)) return rv; - - mPresShell = do_GetWeakReference(aPresShell); - - return loadGroup->AddRequest(mDummyLayoutRequest, nsnull); -} - -// Remove the load group request added above -nsresult -CantRenderReplacedElementEvent::RemoveLoadGroupRequest() -{ - nsresult rv = NS_OK; - - if (mDummyLayoutRequest) { - nsCOMPtr request = mDummyLayoutRequest; - mDummyLayoutRequest = nsnull; - - nsCOMPtr presShell = do_QueryReferent(mPresShell); - if (!presShell) return NS_ERROR_FAILURE; - - nsIDocument *doc = presShell->GetDocument(); - if (!doc) return NS_ERROR_FAILURE;; - - nsCOMPtr loadGroup = doc->GetDocumentLoadGroup(); - if (!loadGroup) return NS_ERROR_FAILURE; - - rv = loadGroup->RemoveRequest(request, nsnull, NS_OK); - } - return rv; -} - -nsresult -nsFrameManager::CantRenderReplacedElement(nsIFrame* aFrame) -{ -#ifdef NOISY_EVENTS - printf("%p nsFrameManager::CantRenderReplacedElement called\n", this); -#endif - - // We need to notify the style stystem, but post the notification so it - // doesn't happen now - nsresult rv; - nsCOMPtr eventService = - do_GetService(kEventQueueServiceCID, &rv); - - if (eventService) { - nsCOMPtr eventQueue; - rv = eventService->GetThreadEventQueue(NS_CURRENT_THREAD, - getter_AddRefs(eventQueue)); - - if (NS_SUCCEEDED(rv) && eventQueue) { - // Verify that there isn't already a posted event associated with - // this frame. - if (*FindPostedEventFor(aFrame)) - return NS_OK; - - CantRenderReplacedElementEvent* ev; - - // Create a new event - ev = new CantRenderReplacedElementEvent(this, aFrame, mPresShell); - - // Post the event - rv = eventQueue->PostEvent(ev); - if (NS_FAILED(rv)) { - NS_ERROR("failed to post event"); - PL_DestroyEvent(ev); - } - else { - // Add the event to our linked list of posted events - ev->mNext = mPostedEvents; - mPostedEvents = ev; - } - } - } - - return rv; -} - #ifdef NS_DEBUG static void DumpContext(nsIFrame* aFrame, nsStyleContext* aContext) diff --git a/layout/base/nsFrameManager.h b/layout/base/nsFrameManager.h index f70a0b03b5b2..da11a5930845 100644 --- a/layout/base/nsFrameManager.h +++ b/layout/base/nsFrameManager.h @@ -159,9 +159,6 @@ public: return aParentFrame->ReplaceFrame(aListName, aOldFrame, aNewFrame); } - // Notification that we were unable to render a replaced element - NS_HIDDEN_(nsresult) CantRenderReplacedElement(nsIFrame* aFrame); - /* * Notification that a frame is about to be destroyed. This allows any * outstanding references to the frame to be cleaned up. @@ -226,8 +223,6 @@ public: private: - friend struct CantRenderReplacedElementEvent; - NS_HIDDEN_(nsIPresShell*) GetPresShell() const { return mPresShell; } NS_HIDDEN_(nsPresContext*) GetPresContext() const { return mPresShell->GetPresContext(); @@ -239,18 +234,6 @@ private: nsIContent *aParentContent, nsStyleChangeList *aChangeList, nsChangeHint aMinChange); - - NS_HIDDEN_(nsresult) RevokePostedEvents(); - NS_HIDDEN_(CantRenderReplacedElementEvent**) - FindPostedEventFor(nsIFrame* aFrame); - - NS_HIDDEN_(void) DequeuePostedEventFor(nsIFrame* aFrame); - - static NS_HIDDEN_(void) - HandlePLEvent(CantRenderReplacedElementEvent* aEvent); - - static NS_HIDDEN_(void) - DestroyPLEvent(CantRenderReplacedElementEvent* aEvent); }; #endif diff --git a/layout/base/nsFrameManagerBase.h b/layout/base/nsFrameManagerBase.h index 834ac770f4c5..8806b61b8ac9 100644 --- a/layout/base/nsFrameManagerBase.h +++ b/layout/base/nsFrameManagerBase.h @@ -63,8 +63,6 @@ class nsIAtom; class nsStyleChangeList; class nsILayoutHistoryState; -struct CantRenderReplacedElementEvent; - class nsFrameManagerBase { protected: @@ -78,7 +76,6 @@ protected: PLDHashTable mPrimaryFrameMap; PLDHashTable mPlaceholderMap; UndisplayedMap* mUndisplayedMap; - CantRenderReplacedElementEvent* mPostedEvents; PRBool mIsDestroyingFrames; }; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index bc28def6160b..ebde783cf69a 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -1448,6 +1448,8 @@ protected: // reflow commands around and we revoke our events. nsCOMPtr mDummyLayoutRequest; + CantRenderReplacedElementEvent* mPostedReplaces; + // used for list of posted events and attribute changes. To be done // after reflow. nsDOMEventRequest* mFirstDOMEventRequest; @@ -1957,6 +1959,7 @@ PresShell::Destroy() } // Revoke pending events + mPostedReplaces = nsnull; mReflowEventQueue = nsnull; nsCOMPtr eventQueue; mEventQueueService->GetSpecialEventQueue(nsIEventQueueService::UI_THREAD_EVENT_QUEUE, @@ -3090,6 +3093,8 @@ PresShell::NotifyDestroyingFrame(nsIFrame* aFrame) // Cancel any pending reflow commands targeted at this frame CancelReflowCommandInternal(aFrame, nsnull); + DequeuePostedEventFor(aFrame); + // Notify the frame manager FrameManager()->NotifyDestroyingFrame(aFrame); @@ -3897,10 +3902,225 @@ PresShell::CreateRenderingContext(nsIFrame *aFrame, return rv; } +// A CantRenderReplacedElementEvent has a weak pointer to the presshell and the +// presshell has a weak pointer to the event. The event queue owns the event +// and the presshell will delete the event if it's going to go away. +struct CantRenderReplacedElementEvent : public PLEvent { + CantRenderReplacedElementEvent(PresShell* aPresShell, + nsIFrame* aFrame) NS_HIDDEN; + ~CantRenderReplacedElementEvent() { + RemoveLoadGroupRequest(); + } + + // XXXldb Should the pres shell maintain a reference count on a single + // dummy layout request instead of doing creation of a separate one + // here (and per-event!)? + // XXXbz absolutely! Should be a per-document counter, actually.... + NS_HIDDEN_(void) AddLoadGroupRequest(); + NS_HIDDEN_(void) RemoveLoadGroupRequest(); + NS_HIDDEN_(PresShell*) OurPresShell() { + return NS_STATIC_CAST(PresShell*, owner); + } + + void HandleEvent(); + + nsIFrame* mFrame; // the frame that can't be rendered + CantRenderReplacedElementEvent* mNext; // next event in the list + nsCOMPtr mDummyLayoutRequest; // load group request +}; + +PR_STATIC_CALLBACK(void*) +HandleCantRenderReplacedElementEvent(PLEvent* aEvent) +{ + CantRenderReplacedElementEvent* evt = + NS_STATIC_CAST(CantRenderReplacedElementEvent*, aEvent); + evt->HandleEvent(); + return nsnull; +} + +PR_STATIC_CALLBACK(void) +DestroyCantRenderReplacedElementEvent(PLEvent* aEvent) +{ + CantRenderReplacedElementEvent* evt = + NS_STATIC_CAST(CantRenderReplacedElementEvent*, aEvent); + + delete evt; +} + +CantRenderReplacedElementEvent::CantRenderReplacedElementEvent(PresShell* aPresShell, + nsIFrame* aFrame) : + mFrame(aFrame) +{ + PL_InitEvent(this, aPresShell, + ::HandleCantRenderReplacedElementEvent, + ::DestroyCantRenderReplacedElementEvent); + + // XXXbz why only for object frames? + if (nsLayoutAtoms::objectFrame == aFrame->GetType()) { + AddLoadGroupRequest(); + } +} + +void +PresShell::DequeuePostedEventFor(nsIFrame* aFrame) +{ + // If there's a posted event for this frame, then remove it + CantRenderReplacedElementEvent** event = FindPostedEventFor(aFrame); + if (!*event) { + return; + } + + CantRenderReplacedElementEvent* tmp = *event; + + // Remove it from our linked list of posted events + *event = (*event)->mNext; + + // Dequeue it from the event queue + nsCOMPtr eventQueue; + mEventQueueService-> + GetSpecialEventQueue(nsIEventQueueService::UI_THREAD_EVENT_QUEUE, + getter_AddRefs(eventQueue)); + + NS_ASSERTION(eventQueue, + "will crash soon due to event holding dangling pointer to " + "frame"); + if (eventQueue) { + PLEventQueue* plqueue; + + eventQueue->GetPLEventQueue(&plqueue); + NS_ASSERTION(plqueue, + "will crash soon due to event holding dangling pointer to " + "frame"); + if (plqueue) { + // Remove the event and then destroy it + PL_DequeueEvent(tmp, plqueue); + PL_DestroyEvent(tmp); + } + } +} + +// Add a load group request in order to delay the onLoad handler when we have +// pending replacements +void +CantRenderReplacedElementEvent::AddLoadGroupRequest() +{ + PresShell* presShell = OurPresShell(); + nsIDocument *doc = presShell->GetDocument(); + if (!doc) { + return; + } + + nsDummyLayoutRequest::Create(getter_AddRefs(mDummyLayoutRequest), presShell); + if (!mDummyLayoutRequest) { + return; + } + + nsCOMPtr loadGroup = doc->GetDocumentLoadGroup(); + if (!loadGroup) { + return; + } + + nsresult rv = mDummyLayoutRequest->SetLoadGroup(loadGroup); + if (NS_FAILED(rv)) { + return; + } + + loadGroup->AddRequest(mDummyLayoutRequest, nsnull); +} + +// Remove the load group request added above +void +CantRenderReplacedElementEvent::RemoveLoadGroupRequest() +{ + if (mDummyLayoutRequest) { + nsCOMPtr request = mDummyLayoutRequest; + mDummyLayoutRequest = nsnull; + + nsIDocument *doc = OurPresShell()->GetDocument(); + if (!doc) { + return; + } + + nsCOMPtr loadGroup = doc->GetDocumentLoadGroup(); + if (!loadGroup) { + return; + } + + loadGroup->RemoveRequest(request, nsnull, NS_OK); + } +} + +void +CantRenderReplacedElementEvent::HandleEvent() +{ + // Remove ourselves from the linked list + PresShell* presShell = OurPresShell(); + CantRenderReplacedElementEvent** events = &presShell->mPostedReplaces; + while (*events) { + if (*events == this) { + *events = (*events)->mNext; + break; + } + events = &(*events)->mNext; + NS_ASSERTION(*events, "event not in queue"); + } + + // Make sure to prevent reflow while we're messing with frames + ++presShell->mChangeNestCount; + presShell->FrameConstructor()->CantRenderReplacedElement(mFrame); + --presShell->mChangeNestCount; +} + +CantRenderReplacedElementEvent** +PresShell::FindPostedEventFor(nsIFrame* aFrame) +{ + CantRenderReplacedElementEvent** event = &mPostedReplaces; + + while (*event) { + if ((*event)->mFrame == aFrame) { + return event; + } + event = &(*event)->mNext; + } + + return event; +} + NS_IMETHODIMP PresShell::CantRenderReplacedElement(nsIFrame* aFrame) { - return FrameManager()->CantRenderReplacedElement(aFrame); + if (*FindPostedEventFor(aFrame)) + return NS_OK; + + // Handle this asynchronously + nsCOMPtr eventQueue; + nsresult rv = mEventQueueService-> + GetSpecialEventQueue(nsIEventQueueService::UI_THREAD_EVENT_QUEUE, + getter_AddRefs(eventQueue)); + + NS_ENSURE_SUCCESS(rv, rv); + // Verify that there isn't already a posted event associated with + // this frame. + CantRenderReplacedElementEvent* ev; + + // Create a new event + ev = new CantRenderReplacedElementEvent(this, aFrame); + if (!ev) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // Post the event + rv = eventQueue->PostEvent(ev); + if (NS_FAILED(rv)) { + PL_DestroyEvent(ev); + } + else { + // Add the event to our linked list of posted events + ev->mNext = mPostedReplaces; + mPostedReplaces = ev; + } + + return rv; } NS_IMETHODIMP @@ -6573,7 +6793,7 @@ PresShell::ReflowCommandRemoved(nsHTMLReflowCommand* aRC) } struct DummyLayoutRequestEvent : public PLEvent { - DummyLayoutRequestEvent(PresShell* aPresShell); + DummyLayoutRequestEvent(PresShell* aPresShell) NS_HIDDEN; ~DummyLayoutRequestEvent() { } void HandleEvent() {