Bug 479490 - 'ASSERTION: Mismatched calls to ResumeTimeouts'. r=bent, sr=jst, a=blocking1.9.1+

This commit is contained in:
Olli Pettay 2009-02-26 14:00:32 -08:00
parent e6f01244a1
commit 1380d66c68
6 changed files with 156 additions and 101 deletions

View File

@ -6892,6 +6892,11 @@ nsDocument::CanSavePresentation(nsIRequest *aNewRequest)
return PR_FALSE;
}
nsPIDOMWindow* win = GetInnerWindow();
if (win && win->TimeoutSuspendCount()) {
return PR_FALSE;
}
// Check our event listener manager for unload/beforeunload listeners.
nsCOMPtr<nsPIDOMEventTarget> piTarget = do_QueryInterface(mScriptGlobalObject);
if (piTarget) {

View File

@ -145,6 +145,21 @@
#define NS_PROGRESS_EVENT_INTERVAL 50
class nsResumeTimeoutsEvent : public nsRunnable
{
public:
nsResumeTimeoutsEvent(nsPIDOMWindow* aWindow) : mWindow(aWindow) {}
NS_IMETHOD Run()
{
mWindow->ResumeTimeouts(PR_FALSE);
return NS_OK;
}
private:
nsCOMPtr<nsPIDOMWindow> mWindow;
};
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMEventListenerWrapper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMEventListenerWrapper)
@ -2793,15 +2808,14 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
nsCOMPtr<nsIDOMWindow> topWindow;
if (NS_SUCCEEDED(mOwner->GetTop(getter_AddRefs(topWindow)))) {
nsCOMPtr<nsPIDOMWindow> suspendedWindow(do_QueryInterface(topWindow));
if (suspendedWindow) {
suspendedDoc = do_QueryInterface(suspendedWindow->GetExtantDocument());
if (suspendedDoc) {
suspendedDoc->SuppressEventHandling();
}
suspendedWindow->SuspendTimeouts();
resumeTimeoutRunnable = NS_NEW_RUNNABLE_METHOD(nsPIDOMWindow,
suspendedWindow.get(),
ResumeTimeouts);
if (suspendedWindow &&
(suspendedWindow = suspendedWindow->GetCurrentInnerWindow())) {
suspendedDoc = do_QueryInterface(suspendedWindow->GetExtantDocument());
if (suspendedDoc) {
suspendedDoc->SuppressEventHandling();
}
suspendedWindow->SuspendTimeouts(1, PR_FALSE);
resumeTimeoutRunnable = new nsResumeTimeoutsEvent(suspendedWindow);
}
}
}

View File

@ -6020,13 +6020,20 @@ nsDocShell::RestoreFromHistory()
}
nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
PRUint32 parentSuspendCount = 0;
if (document) {
nsCOMPtr<nsIDocShellTreeItem> parent;
GetParent(getter_AddRefs(parent));
nsCOMPtr<nsIDOMDocument> parentDoc = do_GetInterface(parent);
nsCOMPtr<nsIDocument> d = do_QueryInterface(parentDoc);
if (d && d->EventHandlingSuppressed()) {
document->SuppressEventHandling(d->EventHandlingSuppressed());
if (d) {
if (d->EventHandlingSuppressed()) {
document->SuppressEventHandling(d->EventHandlingSuppressed());
}
nsCOMPtr<nsPIDOMWindow> parentWindow = d->GetWindow();
if (parentWindow) {
parentSuspendCount = parentWindow->TimeoutSuspendCount();
}
}
// Use the uri from the mLSHE we had when we entered this function
@ -6116,6 +6123,13 @@ nsDocShell::RestoreFromHistory()
}
}
// If parent is suspended, increase suspension count.
// This can't be done as early as event suppression since this
// depends on docshell tree.
if (parentSuspendCount) {
privWin->SuspendTimeouts(parentSuspendCount, PR_FALSE);
}
// Now that all of the child docshells have been put into place, we can
// restart the timers for the window and all of the child frames.
privWin->ResumeTimeouts();

View File

@ -76,8 +76,8 @@ class nsScriptObjectHolder;
class nsXBLPrototypeHandler;
#define NS_PIDOMWINDOW_IID \
{ 0x3d2b6b38, 0x810d, 0x4ac5, \
{ 0x81, 0x7c, 0xb9, 0x70, 0x81, 0x80, 0x4d, 0x9f } }
{ 0x80dd53b6, 0x8c61, 0x4dd6, \
{ 0xb4, 0x51, 0xf7, 0xd7, 0x5c, 0xfc, 0x51, 0x96 } }
class nsPIDOMWindow : public nsIDOMWindowInternal
{
@ -272,11 +272,14 @@ public:
virtual nsresult RestoreWindowState(nsISupports *aState) = 0;
// Suspend timeouts in this window and in child windows.
virtual void SuspendTimeouts() = 0;
virtual void SuspendTimeouts(PRUint32 aIncrease = 1,
PRBool aFreezeChildren = PR_TRUE) = 0;
// Resume suspended timeouts in this window and in child windows.
virtual nsresult ResumeTimeouts() = 0;
virtual nsresult ResumeTimeouts(PRBool aThawChildren = PR_TRUE) = 0;
virtual PRUint32 TimeoutSuspendCount() = 0;
// Fire any DOM notification events related to things that happened while
// the window was frozen.
virtual nsresult FireDelayedDOMEvents() = 0;

View File

@ -1885,6 +1885,14 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
if (st_id == nsIProgrammingLanguage::JAVASCRIPT)
JS_EndRequest((JSContext *)this_ctx->GetNativeContext());
}
nsCOMPtr<nsIContent> frame = do_QueryInterface(GetFrameElementInternal());
if (frame && frame->GetOwnerDoc()) {
nsPIDOMWindow* parentWindow = frame->GetOwnerDoc()->GetWindow();
if (parentWindow && parentWindow->TimeoutSuspendCount()) {
SuspendTimeouts(parentWindow->TimeoutSuspendCount());
}
}
}
// Tell the contexts we have completed setting up the doc.
NS_STID_FOR_ID(st_id) {
@ -2054,7 +2062,7 @@ nsGlobalWindow::SetDocShell(nsIDocShell* aDocShell)
// Call FreeInnerObjects on all inner windows, not just the current
// one, since some could be held by WindowStateHolder objects that
// are GC-owned.
for (nsGlobalWindow *inner = (nsGlobalWindow *)PR_LIST_HEAD(this);
for (nsRefPtr<nsGlobalWindow> inner = (nsGlobalWindow *)PR_LIST_HEAD(this);
inner != this;
inner = (nsGlobalWindow*)PR_NEXT_LINK(inner)) {
NS_ASSERTION(inner->mOuterWindow == this, "bad outer window pointer");
@ -8389,37 +8397,39 @@ nsGlobalWindow::RestoreWindowState(nsISupports *aState)
}
void
nsGlobalWindow::SuspendTimeouts()
nsGlobalWindow::SuspendTimeouts(PRUint32 aIncrease,
PRBool aFreezeChildren)
{
FORWARD_TO_INNER_VOID(SuspendTimeouts, ());
FORWARD_TO_INNER_VOID(SuspendTimeouts, (aIncrease, aFreezeChildren));
if (++mTimeoutsSuspendDepth != 1) {
return;
}
PRBool suspended = (mTimeoutsSuspendDepth != 0);
mTimeoutsSuspendDepth += aIncrease;
nsDOMThreadService* dts = nsDOMThreadService::get();
if (dts) {
dts->SuspendWorkersForGlobal(static_cast<nsIScriptGlobalObject*>(this));
}
PRTime now = PR_Now();
for (nsTimeout *t = FirstTimeout(); IsTimeout(t); t = t->Next()) {
// Change mWhen to be the time remaining for this timer.
if (t->mWhen > now)
t->mWhen -= now;
else
t->mWhen = 0;
// Drop the XPCOM timer; we'll reschedule when restoring the state.
if (t->mTimer) {
t->mTimer->Cancel();
t->mTimer = nsnull;
// Drop the reference that the timer's closure had on this timeout, we'll
// add it back in ResumeTimeouts. Note that it shouldn't matter that we're
// passing null for the context, since this shouldn't actually release this
// timeout.
t->Release();
if (!suspended) {
nsDOMThreadService* dts = nsDOMThreadService::get();
if (dts) {
dts->SuspendWorkersForGlobal(static_cast<nsIScriptGlobalObject*>(this));
}
PRTime now = PR_Now();
for (nsTimeout *t = FirstTimeout(); IsTimeout(t); t = t->Next()) {
// Change mWhen to be the time remaining for this timer.
if (t->mWhen > now)
t->mWhen -= now;
else
t->mWhen = 0;
// Drop the XPCOM timer; we'll reschedule when restoring the state.
if (t->mTimer) {
t->mTimer->Cancel();
t->mTimer = nsnull;
// Drop the reference that the timer's closure had on this timeout, we'll
// add it back in ResumeTimeouts. Note that it shouldn't matter that we're
// passing null for the context, since this shouldn't actually release this
// timeout.
t->Release();
}
}
}
@ -8439,12 +8449,11 @@ nsGlobalWindow::SuspendTimeouts()
nsGlobalWindow *win =
static_cast<nsGlobalWindow*>
(static_cast<nsPIDOMWindow*>(pWin));
win->SuspendTimeouts();
win->SuspendTimeouts(aIncrease, aFreezeChildren);
NS_ASSERTION(win->IsOuterWindow(), "Expected outer window");
nsGlobalWindow* inner = win->GetCurrentInnerWindowInternal();
if (inner) {
if (inner && aFreezeChildren) {
inner->Freeze();
}
}
@ -8453,67 +8462,68 @@ nsGlobalWindow::SuspendTimeouts()
}
nsresult
nsGlobalWindow::ResumeTimeouts()
nsGlobalWindow::ResumeTimeouts(PRBool aThawChildren)
{
FORWARD_TO_INNER(ResumeTimeouts, (), NS_ERROR_NOT_INITIALIZED);
NS_ASSERTION(mTimeoutsSuspendDepth, "Mismatched calls to ResumeTimeouts!");
if (--mTimeoutsSuspendDepth != 0) {
return NS_OK;
}
nsDOMThreadService* dts = nsDOMThreadService::get();
if (dts) {
dts->ResumeWorkersForGlobal(static_cast<nsIScriptGlobalObject*>(this));
}
// Restore all of the timeouts, using the stored time remaining
// (stored in timeout->mWhen).
PRTime now = PR_Now();
--mTimeoutsSuspendDepth;
PRBool shouldResume = (mTimeoutsSuspendDepth == 0);
nsresult rv;
#ifdef DEBUG
PRBool _seenDummyTimeout = PR_FALSE;
#endif
for (nsTimeout *t = FirstTimeout(); IsTimeout(t); t = t->Next()) {
// There's a chance we're being called with RunTimeout on the stack in which
// case we have a dummy timeout in the list that *must not* be resumed. It
// can be identified by a null mWindow.
if (!t->mWindow) {
#ifdef DEBUG
NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
_seenDummyTimeout = PR_TRUE;
#endif
continue;
if (shouldResume) {
nsDOMThreadService* dts = nsDOMThreadService::get();
if (dts) {
dts->ResumeWorkersForGlobal(static_cast<nsIScriptGlobalObject*>(this));
}
// Make sure to cast the unsigned PR_USEC_PER_MSEC to signed
// PRTime to make the division do the right thing on 64-bit
// platforms whether t->mWhen is positive or negative (which is
// likely to always be positive here, but cast anyways for
// consistency).
PRUint32 delay =
PR_MAX(((PRUint32)(t->mWhen / (PRTime)PR_USEC_PER_MSEC)),
DOM_MIN_TIMEOUT_VALUE);
// Restore all of the timeouts, using the stored time remaining
// (stored in timeout->mWhen).
// Set mWhen back to the time when the timer is supposed to
// fire.
t->mWhen += now;
PRTime now = PR_Now();
t->mTimer = do_CreateInstance("@mozilla.org/timer;1");
NS_ENSURE_TRUE(t->mTimer, NS_ERROR_OUT_OF_MEMORY);
#ifdef DEBUG
PRBool _seenDummyTimeout = PR_FALSE;
#endif
rv = t->mTimer->InitWithFuncCallback(TimerCallback, t, delay,
nsITimer::TYPE_ONE_SHOT);
if (NS_FAILED(rv)) {
t->mTimer = nsnull;
return rv;
for (nsTimeout *t = FirstTimeout(); IsTimeout(t); t = t->Next()) {
// There's a chance we're being called with RunTimeout on the stack in which
// case we have a dummy timeout in the list that *must not* be resumed. It
// can be identified by a null mWindow.
if (!t->mWindow) {
#ifdef DEBUG
NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
_seenDummyTimeout = PR_TRUE;
#endif
continue;
}
// Make sure to cast the unsigned PR_USEC_PER_MSEC to signed
// PRTime to make the division do the right thing on 64-bit
// platforms whether t->mWhen is positive or negative (which is
// likely to always be positive here, but cast anyways for
// consistency).
PRUint32 delay =
PR_MAX(((PRUint32)(t->mWhen / (PRTime)PR_USEC_PER_MSEC)),
DOM_MIN_TIMEOUT_VALUE);
// Set mWhen back to the time when the timer is supposed to
// fire.
t->mWhen += now;
t->mTimer = do_CreateInstance("@mozilla.org/timer;1");
NS_ENSURE_TRUE(t->mTimer, NS_ERROR_OUT_OF_MEMORY);
rv = t->mTimer->InitWithFuncCallback(TimerCallback, t, delay,
nsITimer::TYPE_ONE_SHOT);
if (NS_FAILED(rv)) {
t->mTimer = nsnull;
return rv;
}
// Add a reference for the new timer's closure.
t->AddRef();
}
// Add a reference for the new timer's closure.
t->AddRef();
}
// Resume our children as well.
@ -8536,11 +8546,11 @@ nsGlobalWindow::ResumeTimeouts()
NS_ASSERTION(win->IsOuterWindow(), "Expected outer window");
nsGlobalWindow* inner = win->GetCurrentInnerWindowInternal();
if (inner) {
if (inner && aThawChildren) {
inner->Thaw();
}
rv = win->ResumeTimeouts();
rv = win->ResumeTimeouts(aThawChildren);
NS_ENSURE_SUCCESS(rv, rv);
}
}
@ -8549,6 +8559,13 @@ nsGlobalWindow::ResumeTimeouts()
return NS_OK;
}
PRUint32
nsGlobalWindow::TimeoutSuspendCount()
{
FORWARD_TO_INNER(TimeoutSuspendCount, (), 0);
return mTimeoutsSuspendDepth;
}
NS_IMETHODIMP
nsGlobalWindow::GetScriptTypeID(PRUint32 *aScriptType)
{

View File

@ -303,8 +303,10 @@ public:
virtual NS_HIDDEN_(nsresult) SaveWindowState(nsISupports **aState);
virtual NS_HIDDEN_(nsresult) RestoreWindowState(nsISupports *aState);
virtual NS_HIDDEN_(void) SuspendTimeouts();
virtual NS_HIDDEN_(nsresult) ResumeTimeouts();
virtual NS_HIDDEN_(void) SuspendTimeouts(PRUint32 aIncrease = 1,
PRBool aFreezeChildren = PR_TRUE);
virtual NS_HIDDEN_(nsresult) ResumeTimeouts(PRBool aThawChildren = PR_TRUE);
virtual NS_HIDDEN_(PRUint32) TimeoutSuspendCount();
virtual NS_HIDDEN_(nsresult) FireDelayedDOMEvents();
virtual NS_HIDDEN_(PRBool) IsFrozen() const
{