From 9554ef498f874a62ed263f07ee20cba8c91258fd Mon Sep 17 00:00:00 2001 From: Ben Turner Date: Thu, 11 Mar 2010 12:36:44 -0800 Subject: [PATCH] Bug 547399 - 'Workers: Don't let worker messages run if the worker is suspended'. r+sr=jst. --- dom/src/threads/nsDOMWorker.cpp | 29 ++++++ dom/src/threads/nsDOMWorker.h | 6 ++ dom/src/threads/test/Makefile.in | 3 + dom/src/threads/test/suspend_iframe.html | 40 ++++++++ dom/src/threads/test/suspend_worker.js | 5 + dom/src/threads/test/test_suspend.html | 113 +++++++++++++++++++++++ 6 files changed, 196 insertions(+) create mode 100644 dom/src/threads/test/suspend_iframe.html create mode 100644 dom/src/threads/test/suspend_worker.js create mode 100644 dom/src/threads/test/test_suspend.html diff --git a/dom/src/threads/nsDOMWorker.cpp b/dom/src/threads/nsDOMWorker.cpp index 3f7f614b3ec7..48f75d9b9c8d 100644 --- a/dom/src/threads/nsDOMWorker.cpp +++ b/dom/src/threads/nsDOMWorker.cpp @@ -933,6 +933,17 @@ public: return NS_ERROR_ABORT; } + // If the worker is suspended and we're running on the main thread then we + // can't actually dispatch the event yet. Instead we queue it for whenever + // we resume. + if (mWorker->IsSuspended() && NS_IsMainThread()) { + if (!mWorker->QueueSuspendedRunnable(this)) { + NS_ERROR("Out of memory?!"); + return NS_ERROR_ABORT; + } + return NS_OK; + } + nsCOMPtr target = mToInner ? static_cast(mWorker->GetInnerScope()) : static_cast(mWorker); @@ -1044,6 +1055,7 @@ nsDOMWorker::~nsDOMWorker() } NS_ASSERTION(!mFeatures.Length(), "Live features!"); + NS_ASSERTION(!mQueuedRunnables.Length(), "Events that never ran!"); nsCOMPtr mainThread; NS_GetMainThread(getter_AddRefs(mainThread)); @@ -1349,6 +1361,9 @@ nsDOMWorker::Kill() features[index]->Cancel(); } + // Make sure we kill any queued runnables that we never had a chance to run. + mQueuedRunnables.Clear(); + // We no longer need to keep our inner scope. mInnerScope = nsnull; mScopeWN = nsnull; @@ -1400,6 +1415,13 @@ nsDOMWorker::Resume() if (shouldResumeFeatures) { ResumeFeatures(); } + + // Repost any events that were queued for the main thread while suspended. + PRUint32 count = mQueuedRunnables.Length(); + for (PRUint32 index = 0; index < count; index++) { + NS_DispatchToCurrentThread(mQueuedRunnables[index]); + } + mQueuedRunnables.Clear(); } PRBool @@ -1934,6 +1956,13 @@ nsDOMWorker::GetExpirationTime() } #endif +PRBool +nsDOMWorker::QueueSuspendedRunnable(nsIRunnable* aRunnable) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + return mQueuedRunnables.AppendElement(aRunnable) ? PR_TRUE : PR_FALSE; +} + NS_IMETHODIMP nsDOMWorker::AddEventListener(const nsAString& aType, nsIDOMEventListener* aListener, diff --git a/dom/src/threads/nsDOMWorker.h b/dom/src/threads/nsDOMWorker.h index 76e4875e107e..fbe7c9d8b0dc 100644 --- a/dom/src/threads/nsDOMWorker.h +++ b/dom/src/threads/nsDOMWorker.h @@ -64,6 +64,7 @@ class nsDOMWorkerTimeout; class nsICancelable; class nsIDOMEventListener; class nsIEventTarget; +class nsIRunnable; class nsIScriptGlobalObject; class nsIXPConnectWrappedNative; @@ -118,6 +119,7 @@ class nsDOMWorker : public nsDOMWorkerMessageHandler, friend class nsDOMWorkerXHR; friend class nsDOMWorkerXHRProxy; friend class nsReportErrorRunnable; + friend class nsDOMFireEventRunnable; friend JSBool DOMWorkerOperationCallback(JSContext* aCx); friend void DOMWorkerErrorReporter(JSContext* aCx, @@ -288,6 +290,8 @@ private: return mLocation; } + PRBool QueueSuspendedRunnable(nsIRunnable* aRunnable); + private: // mParent will live as long as mParentWN but only mParentWN will keep the JS @@ -327,6 +331,8 @@ private: nsCOMPtr mLocation; + nsTArray > mQueuedRunnables; + PRPackedBool mSuspended; PRPackedBool mCompileAttempted; }; diff --git a/dom/src/threads/test/Makefile.in b/dom/src/threads/test/Makefile.in index 4771fac12540..77b0bca17e87 100644 --- a/dom/src/threads/test/Makefile.in +++ b/dom/src/threads/test/Makefile.in @@ -82,6 +82,9 @@ _TEST_FILES = \ relativeLoad_import.js \ test_scopeOnerror.html \ scopeOnerror_worker.js \ + test_suspend.html \ + suspend_iframe.html \ + suspend_worker.js \ test_simpleThread.html \ simpleThread_worker.js \ test_terminate.html \ diff --git a/dom/src/threads/test/suspend_iframe.html b/dom/src/threads/test/suspend_iframe.html new file mode 100644 index 000000000000..0fbbb91c8d28 --- /dev/null +++ b/dom/src/threads/test/suspend_iframe.html @@ -0,0 +1,40 @@ + + + + Test for DOM Worker Threads Suspending + + + +
+
+ +
+ + diff --git a/dom/src/threads/test/suspend_worker.js b/dom/src/threads/test/suspend_worker.js new file mode 100644 index 000000000000..04569bfbd1a4 --- /dev/null +++ b/dom/src/threads/test/suspend_worker.js @@ -0,0 +1,5 @@ +var counter = 0; + +setInterval(function() { + postMessage(++counter); +}, 100); diff --git a/dom/src/threads/test/test_suspend.html b/dom/src/threads/test/test_suspend.html new file mode 100644 index 000000000000..08285671d856 --- /dev/null +++ b/dom/src/threads/test/test_suspend.html @@ -0,0 +1,113 @@ + + + + Test for DOM Worker Threads + + + + + +

+ +
+
+
+
+ +