diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 934ba10a73c..667db0b560d 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -3164,6 +3164,8 @@ WorkerPrivate::NotifyFeatures(JSContext* aCx, Status aStatus) void WorkerPrivate::CancelAllTimeouts(JSContext* aCx) { + AssertIsOnWorkerThread(); + if (mTimerRunning) { NS_ASSERTION(mTimer, "Huh?!"); NS_ASSERTION(!mTimeouts.IsEmpty(), "Huh?!"); @@ -3176,13 +3178,19 @@ WorkerPrivate::CancelAllTimeouts(JSContext* aCx) mTimeouts[index]->mCanceled = true; } - RunExpiredTimeouts(aCx); + if (!RunExpiredTimeouts(aCx)) { + JS_ReportPendingException(aCx); + } - mTimer = nsnull; + mTimerRunning = false; } - else { +#ifdef DEBUG + else if (!mRunningExpiredTimeouts) { NS_ASSERTION(mTimeouts.IsEmpty(), "Huh?!"); } +#endif + + mTimer = nsnull; } PRUint32 @@ -3511,8 +3519,15 @@ WorkerPrivate::SetTimeout(JSContext* aCx, unsigned aArgc, jsval* aVp, currentStatus = mStatus; } - if (currentStatus > Running) { + // It's a script bug if setTimeout/setInterval are called from a close handler + // so throw an exception. + if (currentStatus == Closing) { JS_ReportError(aCx, "Cannot schedule timeouts from the close handler!"); + } + + // If the worker is trying to call setTimeout/setInterval and the parent + // thread has initiated the close process then just silently fail. + if (currentStatus >= Closing) { return false; } @@ -3655,6 +3670,7 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx) return true; } + NS_ASSERTION(mTimer, "Must have a timer!"); NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some work to do!"); bool retval = true; @@ -3719,12 +3735,16 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx) } } + NS_ASSERTION(mRunningExpiredTimeouts, "Someone changed this!"); + // Reschedule intervals. - if (info->mIsInterval) { + if (info->mIsInterval && !info->mCanceled) { PRUint32 timeoutIndex = mTimeouts.IndexOf(info); NS_ASSERTION(timeoutIndex != PRUint32(-1), "Should still be in the main list!"); + // This is nasty but we have to keep the old nsAutoPtr from deleting the + // info we're about to re-add. mTimeouts[timeoutIndex].forget(); mTimeouts.RemoveElementAt(timeoutIndex); @@ -3755,8 +3775,8 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx) } } - // Signal the parent that we're no longer using timeouts or reschedule the - // timer. + // Either signal the parent that we're no longer using timeouts or reschedule + // the timer. if (mTimeouts.IsEmpty()) { if (!ModifyBusyCountFromWorker(aCx, false)) { retval = false; diff --git a/dom/workers/test/Makefile.in b/dom/workers/test/Makefile.in index efe31ede1d3..f577afd6bae 100644 --- a/dom/workers/test/Makefile.in +++ b/dom/workers/test/Makefile.in @@ -56,6 +56,8 @@ _TEST_FILES = \ test_atob.html \ atob_worker.js \ test_blobWorkers.html \ + test_clearTimeouts.html \ + clearTimeouts_worker.js \ test_close.html \ close_worker.js \ test_closeOnGC.html \ diff --git a/dom/workers/test/clearTimeouts_worker.js b/dom/workers/test/clearTimeouts_worker.js new file mode 100644 index 00000000000..b471515b3ec --- /dev/null +++ b/dom/workers/test/clearTimeouts_worker.js @@ -0,0 +1,12 @@ +var count = 0; +function timerFunction() { + if (++count == 30) { + close(); + postMessage("ready"); + while (true) { } + } +} + +for (var i = 0; i < 10; i++) { + setInterval(timerFunction, 500); +} diff --git a/dom/workers/test/test_clearTimeouts.html b/dom/workers/test/test_clearTimeouts.html new file mode 100644 index 00000000000..5a6e83e5205 --- /dev/null +++ b/dom/workers/test/test_clearTimeouts.html @@ -0,0 +1,30 @@ + + + +
++ ++ +