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
This commit is contained in:
bzbarsky%mit.edu 2005-03-28 23:20:54 +00:00
parent b46e318e6a
commit 9b4aaa3fb3
4 changed files with 222 additions and 274 deletions

View File

@ -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<nsIEventQueueService> eventService =
do_GetService(kEventQueueServiceCID, &rv);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIEventQueue> 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<nsIEventQueueService> 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<nsIEventQueue> 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<nsILoadGroup> 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<nsIRequest> request = mDummyLayoutRequest;
mDummyLayoutRequest = nsnull;
nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
if (!presShell) return NS_ERROR_FAILURE;
nsIDocument *doc = presShell->GetDocument();
if (!doc) return NS_ERROR_FAILURE;;
nsCOMPtr<nsILoadGroup> 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<nsIEventQueueService> eventService =
do_GetService(kEventQueueServiceCID, &rv);
if (eventService) {
nsCOMPtr<nsIEventQueue> 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)

View File

@ -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

View File

@ -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;
};

View File

@ -1448,6 +1448,8 @@ protected:
// reflow commands around and we revoke our events.
nsCOMPtr<nsIRequest> 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<nsIEventQueue> 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<nsIRequest> 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<nsIEventQueue> 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<nsILoadGroup> 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<nsIRequest> request = mDummyLayoutRequest;
mDummyLayoutRequest = nsnull;
nsIDocument *doc = OurPresShell()->GetDocument();
if (!doc) {
return;
}
nsCOMPtr<nsILoadGroup> 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<nsIEventQueue> 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() {