Added code to make sure that posted events are removed when the frame they

refer to is destroyed
This commit is contained in:
troy%netscape.com 1999-07-16 23:27:46 +00:00
parent f9d48b07a9
commit 37e8c3a392
2 changed files with 290 additions and 70 deletions

View File

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

View File

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