diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index e628b274188a..257100d0ed18 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -5207,16 +5207,18 @@ nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) /* static */ void -nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable, - DispatchFailureHandling aHandling) +nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable) { - nsCOMPtr<nsIRunnable> runnable = aRunnable; - nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID)); - if (!appShell) { - MOZ_ASSERT(aHandling == DispatchFailureHandling::IgnoreFailure); - return; - } - appShell->RunInStableState(runnable.forget()); + MOZ_ASSERT(CycleCollectedJSRuntime::Get(), "Must be on a script thread!"); + CycleCollectedJSRuntime::Get()->RunInStableState(Move(aRunnable)); +} + +/* static */ +void +nsContentUtils::RunInMetastableState(already_AddRefed<nsIRunnable> aRunnable) +{ + MOZ_ASSERT(CycleCollectedJSRuntime::Get(), "Must be on a script thread!"); + CycleCollectedJSRuntime::Get()->RunInMetastableState(Move(aRunnable)); } void diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 089b67bebde9..9c541a75716c 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -1597,12 +1597,6 @@ public: */ static void WarnScriptWasIgnored(nsIDocument* aDocument); - /** - * Whether to assert that RunInStableState() succeeds, or ignore failure, - * which may happen late in shutdown. - */ - enum class DispatchFailureHandling { AssertSuccess, IgnoreFailure }; - /** * Add a "synchronous section", in the form of an nsIRunnable run once the * event loop has reached a "stable state". |aRunnable| must not cause any @@ -1614,9 +1608,19 @@ public: * finishes. If called multiple times per task/event, all the runnables will * be executed, in the order in which RunInStableState() was called. */ - static void RunInStableState(already_AddRefed<nsIRunnable> aRunnable, - DispatchFailureHandling aHandling = - DispatchFailureHandling::AssertSuccess); + static void RunInStableState(already_AddRefed<nsIRunnable> aRunnable); + + /* Add a "synchronous section", in the form of an nsIRunnable run once the + * event loop has reached a "metastable state". |aRunnable| must not cause any + * queued events to be processed (i.e. must not spin the event loop). + * We've reached a metastable state when the currently executing task or + * microtask has finished. This is not specced at this time. + * In practice this runs aRunnable once the currently executing task or + * microtask finishes. If called multiple times per microtask, all the + * runnables will be executed, in the order in which RunInMetastableState() + * was called + */ + static void RunInMetastableState(already_AddRefed<nsIRunnable> aRunnable); /** * Retrieve information about the viewport as a data structure. diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index ae3ffc3fa07e..d870ba24bb55 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -88,8 +88,7 @@ #include "nsViewportInfo.h" #include "nsIFormControl.h" #include "nsIScriptError.h" -#include "nsIAppShell.h" -#include "nsWidgetsCID.h" +//#include "nsWidgetsCID.h" #include "FrameLayerBuilder.h" #include "nsDisplayList.h" #include "nsROCSSPrimitiveValue.h" @@ -3528,30 +3527,6 @@ nsDOMWindowUtils::DispatchEventToChromeOnly(nsIDOMEventTarget* aTarget, return NS_OK; } -NS_IMETHODIMP -nsDOMWindowUtils::RunInStableState(nsIRunnable *aRunnable) -{ - MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); - - nsCOMPtr<nsIRunnable> runnable = aRunnable; - nsContentUtils::RunInStableState(runnable.forget()); - - return NS_OK; -} - -NS_IMETHODIMP -nsDOMWindowUtils::RunBeforeNextEvent(nsIRunnable *runnable) -{ - MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); - - nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID)); - if (!appShell) { - return NS_ERROR_NOT_AVAILABLE; - } - - return appShell->RunBeforeNextEvent(runnable); -} - NS_IMETHODIMP nsDOMWindowUtils::RequestCompositorProperty(const nsAString& property, float* aResult) diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index ec2eb0b2c9a4..aad6d8800327 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -25417,15 +25417,13 @@ DEBUGThreadSlower::OnDispatchedEvent(nsIThreadInternal* /* aThread */) NS_IMETHODIMP DEBUGThreadSlower::OnProcessNextEvent(nsIThreadInternal* /* aThread */, - bool /* aMayWait */, - uint32_t /* aRecursionDepth */) + bool /* aMayWait */) { return NS_OK; } NS_IMETHODIMP DEBUGThreadSlower::AfterProcessNextEvent(nsIThreadInternal* /* aThread */, - uint32_t /* aRecursionDepth */, bool /* aEventWasProcessed */) { MOZ_ASSERT(kDEBUGThreadSleepMS); diff --git a/dom/indexedDB/IDBFileHandle.cpp b/dom/indexedDB/IDBFileHandle.cpp index 702c70e25200..36564214ff0d 100644 --- a/dom/indexedDB/IDBFileHandle.cpp +++ b/dom/indexedDB/IDBFileHandle.cpp @@ -12,7 +12,6 @@ #include "mozilla/dom/IDBFileHandleBinding.h" #include "mozilla/dom/MetadataHelper.h" #include "mozilla/EventDispatcher.h" -#include "nsIAppShell.h" #include "nsServiceManagerUtils.h" #include "nsWidgetsCID.h" @@ -20,12 +19,6 @@ namespace mozilla { namespace dom { namespace indexedDB { -namespace { - -NS_DEFINE_CID(kAppShellCID2, NS_APPSHELL_CID); - -} // namespace - IDBFileHandle::IDBFileHandle(FileMode aMode, RequestMode aRequestMode, IDBMutableFile* aMutableFile) @@ -51,15 +44,8 @@ IDBFileHandle::Create(FileMode aMode, fileHandle->BindToOwner(aMutableFile); - nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID2); - if (NS_WARN_IF(!appShell)) { - return nullptr; - } - - nsresult rv = appShell->RunBeforeNextEvent(fileHandle); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } + nsCOMPtr<nsIRunnable> runnable = do_QueryObject(fileHandle); + nsContentUtils::RunInMetastableState(runnable.forget()); fileHandle->SetCreating(); @@ -68,7 +54,7 @@ IDBFileHandle::Create(FileMode aMode, return nullptr; } - rv = service->Enqueue(fileHandle, nullptr); + nsresult rv = service->Enqueue(fileHandle, nullptr); if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } diff --git a/dom/indexedDB/IDBTransaction.cpp b/dom/indexedDB/IDBTransaction.cpp index d8d5aa3fd6ab..9aba23ebee20 100644 --- a/dom/indexedDB/IDBTransaction.cpp +++ b/dom/indexedDB/IDBTransaction.cpp @@ -16,11 +16,9 @@ #include "mozilla/dom/DOMError.h" #include "mozilla/dom/DOMStringList.h" #include "mozilla/ipc/BackgroundChild.h" -#include "nsIAppShell.h" #include "nsPIDOMWindow.h" #include "nsServiceManagerUtils.h" #include "nsTHashtable.h" -#include "nsWidgetsCID.h" #include "ProfilerHelpers.h" #include "ReportInternalError.h" #include "WorkerFeature.h" @@ -36,36 +34,6 @@ namespace indexedDB { using namespace mozilla::dom::workers; using namespace mozilla::ipc; -namespace { - -NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); - -bool -RunBeforeNextEvent(IDBTransaction* aTransaction) -{ - MOZ_ASSERT(aTransaction); - - if (NS_IsMainThread()) { - nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID); - MOZ_ASSERT(appShell); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(appShell->RunBeforeNextEvent(aTransaction))); - - return true; - } - - WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); - MOZ_ASSERT(workerPrivate); - - if (NS_WARN_IF(!workerPrivate->RunBeforeNextEvent(aTransaction))) { - return false; - } - - return true; -} - -} // namespace - class IDBTransaction::WorkerFeature final : public mozilla::dom::workers::WorkerFeature { @@ -222,15 +190,8 @@ IDBTransaction::CreateVersionChange( transaction->SetScriptOwner(aDatabase->GetScriptOwner()); - if (NS_WARN_IF(!RunBeforeNextEvent(transaction))) { - MOZ_ASSERT(!NS_IsMainThread()); -#ifdef DEBUG - // Silence assertions. - transaction->mSentCommitOrAbort = true; -#endif - aActor->SendDeleteMeInternal(/* aFailedConstructor */ true); - return nullptr; - } + nsCOMPtr<nsIRunnable> runnable = do_QueryObject(transaction); + nsContentUtils::RunInMetastableState(runnable.forget()); transaction->mBackgroundActor.mVersionChangeBackgroundActor = aActor; transaction->mNextObjectStoreId = aNextObjectStoreId; @@ -262,10 +223,8 @@ IDBTransaction::Create(IDBDatabase* aDatabase, transaction->SetScriptOwner(aDatabase->GetScriptOwner()); - if (NS_WARN_IF(!RunBeforeNextEvent(transaction))) { - MOZ_ASSERT(!NS_IsMainThread()); - return nullptr; - } + nsCOMPtr<nsIRunnable> runnable = do_QueryObject(transaction); + nsContentUtils::RunInMetastableState(runnable.forget()); transaction->mCreating = true; diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 5df7f9ac8993..1181a3d0f4b8 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -49,7 +49,7 @@ interface nsIJSRAIIHelper; interface nsIContentPermissionRequest; interface nsIObserver; -[scriptable, uuid(7a37e173-ea6e-495e-8702-013f8063352a)] +[scriptable, uuid(6064615a-a782-4d08-86db-26ef3851208a)] interface nsIDOMWindowUtils : nsISupports { /** @@ -1722,32 +1722,6 @@ interface nsIDOMWindowUtils : nsISupports { */ attribute boolean paintFlashing; - /** - * Add a "synchronous section", in the form of an nsIRunnable run once the - * event loop has reached a "stable state". |runnable| must not cause any - * queued events to be processed (i.e. must not spin the event loop). - * We've reached a stable state when the currently executing task/event has - * finished, see: - * http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#synchronous-section - * In practice this runs aRunnable once the currently executing event - * finishes. If called multiple times per task/event, all the runnables will - * be executed, in the order in which runInStableState() was called. - * - * XXX - This can wreak havoc if you're not using this for very simple - * purposes, eg testing or setting a flag. - */ - void runInStableState(in nsIRunnable runnable); - - /** - * Run the given runnable before the next iteration of the event loop (this - * includes native events too). If a nested loop is spawned within the current - * event then the runnable will not be run until that loop has terminated. - * - * XXX - This can wreak havoc if you're not using this for very simple - * purposes, eg testing or setting a flag. - */ - void runBeforeNextEvent(in nsIRunnable runnable); - /* * Returns the value of a given property animated on the compositor thread. * If the property is NOT currently being animated on the compositor thread, diff --git a/dom/media/webaudio/AudioDestinationNode.cpp b/dom/media/webaudio/AudioDestinationNode.cpp index 302503c63508..b13f3bcd8b06 100644 --- a/dom/media/webaudio/AudioDestinationNode.cpp +++ b/dom/media/webaudio/AudioDestinationNode.cpp @@ -633,9 +633,7 @@ AudioDestinationNode::ScheduleStableStateNotification() NS_NewRunnableMethod(this, &AudioDestinationNode::NotifyStableState); // Dispatch will fail if this is called on AudioNode destruction during // shutdown, in which case failure can be ignored. - nsContentUtils::RunInStableState(event.forget(), - nsContentUtils:: - DispatchFailureHandling::IgnoreFailure); + nsContentUtils::RunInStableState(event.forget()); } double diff --git a/dom/promise/Promise.cpp b/dom/promise/Promise.cpp index 8d7a73a17ca9..a1a5e3635d2b 100644 --- a/dom/promise/Promise.cpp +++ b/dom/promise/Promise.cpp @@ -510,6 +510,7 @@ Promise::PerformMicroTaskCheckpoint() if (cx.isSome()) { JS_CheckForInterrupt(cx.ref()); } + runtime->AfterProcessMicrotask(); } while (!microtaskQueue.empty()); return true; diff --git a/dom/promise/tests/test_promise.html b/dom/promise/tests/test_promise.html index 4dc81e4233ce..af185efcdaf8 100644 --- a/dom/promise/tests/test_promise.html +++ b/dom/promise/tests/test_promise.html @@ -169,6 +169,26 @@ function promiseAsync_ResolveThenTimeout() { ok(!handlerExecuted, "Handlers are not called before 'then' returns."); } +function promiseAsync_SyncXHR() +{ + var handlerExecuted = false; + + Promise.resolve().then(function() { + handlerExecuted = true; + + // Allow other assertions to run so the test could fail before the next one. + setTimeout(runTest, 0); + }); + + ok(!handlerExecuted, "Handlers are not called until the next microtask."); + + var xhr = new XMLHttpRequest(); + xhr.open("GET", "testXHR.txt", false); + xhr.send(null); + + todo(!handlerExecuted, "Sync XHR should not trigger microtask execution."); +} + function promiseDoubleThen() { var steps = 0; var promise = new Promise(function(r1, r2) { @@ -756,6 +776,7 @@ var tests = [ promiseResolve, promiseReject, promiseAsync_TimeoutResolveThen, promiseAsync_ResolveTimeoutThen, promiseAsync_ResolveThenTimeout, + promiseAsync_SyncXHR, promiseDoubleThen, promiseThenException, promiseThenCatchThen, promiseRejectThenCatchThen, promiseRejectThenCatchThen2, diff --git a/dom/storage/DOMStorageDBThread.cpp b/dom/storage/DOMStorageDBThread.cpp index 5ac98661220d..27ac7a2ecc14 100644 --- a/dom/storage/DOMStorageDBThread.cpp +++ b/dom/storage/DOMStorageDBThread.cpp @@ -362,15 +362,13 @@ DOMStorageDBThread::ThreadObserver::OnDispatchedEvent(nsIThreadInternal *thread) NS_IMETHODIMP DOMStorageDBThread::ThreadObserver::OnProcessNextEvent(nsIThreadInternal *thread, - bool mayWait, - uint32_t recursionDepth) + bool mayWait) { return NS_OK; } NS_IMETHODIMP DOMStorageDBThread::ThreadObserver::AfterProcessNextEvent(nsIThreadInternal *thread, - uint32_t recursionDepth, bool eventWasProcessed) { return NS_OK; diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 0fc26d9efc59..ca126f9c9ca7 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -996,6 +996,15 @@ public: } } + virtual void AfterProcessTask(uint32_t aRecursionDepth) override + { + // Only perform the Promise microtask checkpoint on the outermost event + // loop. Don't run it, for example, during sync XHR or importScripts. + if (aRecursionDepth == 2) { + CycleCollectedJSRuntime::AfterProcessTask(aRecursionDepth); + } + } + private: WorkerPrivate* mWorkerPrivate; }; diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index a0b74a3ec8e3..50c33056d99f 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -2588,22 +2588,6 @@ WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget) { } -struct WorkerPrivate::PreemptingRunnableInfo final -{ - nsCOMPtr<nsIRunnable> mRunnable; - uint32_t mRecursionDepth; - - PreemptingRunnableInfo() - { - MOZ_COUNT_CTOR(WorkerPrivate::PreemptingRunnableInfo); - } - - ~PreemptingRunnableInfo() - { - MOZ_COUNT_DTOR(WorkerPrivate::PreemptingRunnableInfo); - } -}; - template <class Derived> nsIDocument* WorkerPrivateParent<Derived>::GetDocument() const @@ -5187,10 +5171,6 @@ WorkerPrivate::DoRunLoop(JSContext* aCx) // Process a single runnable from the main queue. MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread, false)); - // Only perform the Promise microtask checkpoint on the outermost event - // loop. Don't run it, for example, during sync XHR or importScripts. - (void)Promise::PerformMicroTaskCheckpoint(); - normalRunnablesPending = NS_HasPendingEvents(mThread); if (normalRunnablesPending && GlobalScope()) { // Now *might* be a good time to GC. Let the JS engine make the decision. @@ -5210,85 +5190,28 @@ WorkerPrivate::DoRunLoop(JSContext* aCx) } void -WorkerPrivate::OnProcessNextEvent(uint32_t aRecursionDepth) +WorkerPrivate::OnProcessNextEvent() { AssertIsOnWorkerThread(); - MOZ_ASSERT(aRecursionDepth); + + uint32_t recursionDepth = CycleCollectedJSRuntime::Get()->RecursionDepth(); + MOZ_ASSERT(recursionDepth); // Normally we process control runnables in DoRunLoop or RunCurrentSyncLoop. // However, it's possible that non-worker C++ could spin its own nested event // loop, and in that case we must ensure that we continue to process control // runnables here. - if (aRecursionDepth > 1 && - mSyncLoopStack.Length() < aRecursionDepth - 1) { + if (recursionDepth > 1 && + mSyncLoopStack.Length() < recursionDepth - 1) { ProcessAllControlRunnables(); } - - // Run any preempting runnables that match this depth. - if (!mPreemptingRunnableInfos.IsEmpty()) { - nsTArray<PreemptingRunnableInfo> pendingRunnableInfos; - - for (uint32_t index = 0; - index < mPreemptingRunnableInfos.Length(); - index++) { - PreemptingRunnableInfo& preemptingRunnableInfo = - mPreemptingRunnableInfos[index]; - - if (preemptingRunnableInfo.mRecursionDepth == aRecursionDepth) { - preemptingRunnableInfo.mRunnable->Run(); - preemptingRunnableInfo.mRunnable = nullptr; - } else { - PreemptingRunnableInfo* pending = pendingRunnableInfos.AppendElement(); - pending->mRunnable.swap(preemptingRunnableInfo.mRunnable); - pending->mRecursionDepth = preemptingRunnableInfo.mRecursionDepth; - } - } - - mPreemptingRunnableInfos.SwapElements(pendingRunnableInfos); - } } void -WorkerPrivate::AfterProcessNextEvent(uint32_t aRecursionDepth) +WorkerPrivate::AfterProcessNextEvent() { AssertIsOnWorkerThread(); - MOZ_ASSERT(aRecursionDepth); -} - -bool -WorkerPrivate::RunBeforeNextEvent(nsIRunnable* aRunnable) -{ - AssertIsOnWorkerThread(); - MOZ_ASSERT(aRunnable); - MOZ_ASSERT_IF(!mPreemptingRunnableInfos.IsEmpty(), - NS_HasPendingEvents(mThread)); - - const uint32_t recursionDepth = - mThread->RecursionDepth(WorkerThreadFriendKey()); - - PreemptingRunnableInfo* preemptingRunnableInfo = - mPreemptingRunnableInfos.AppendElement(); - - preemptingRunnableInfo->mRunnable = aRunnable; - - // Due to the weird way that the thread recursion counter is implemented we - // subtract one from the recursion level if we have one. - preemptingRunnableInfo->mRecursionDepth = - recursionDepth ? recursionDepth - 1 : 0; - - // Ensure that we have a pending event so that the runnable will be guaranteed - // to run. - if (mPreemptingRunnableInfos.Length() == 1 && !NS_HasPendingEvents(mThread)) { - nsRefPtr<DummyRunnable> dummyRunnable = new DummyRunnable(this); - if (NS_FAILED(Dispatch(dummyRunnable.forget()))) { - NS_WARNING("RunBeforeNextEvent called after the thread is shutting " - "down!"); - mPreemptingRunnableInfos.Clear(); - return false; - } - } - - return true; + MOZ_ASSERT(CycleCollectedJSRuntime::Get()->RecursionDepth()); } void diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index d536c171160f..0b2dfc966d6b 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -949,9 +949,6 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate> // modifications are done with mMutex held *only* in DEBUG builds. nsTArray<nsAutoPtr<SyncLoopInfo>> mSyncLoopStack; - struct PreemptingRunnableInfo; - nsTArray<PreemptingRunnableInfo> mPreemptingRunnableInfos; - nsCOMPtr<nsITimer> mTimer; nsCOMPtr<nsITimer> mGCTimer; @@ -1362,10 +1359,10 @@ public: ClearMainEventQueue(WorkerRanOrNot aRanOrNot); void - OnProcessNextEvent(uint32_t aRecursionDepth); + OnProcessNextEvent(); void - AfterProcessNextEvent(uint32_t aRecursionDepth); + AfterProcessNextEvent(); void AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget) @@ -1392,11 +1389,6 @@ public: return mWorkerScriptExecutedSuccessfully; } - // Just like nsIAppShell::RunBeforeNextEvent. May only be called on the worker - // thread. - bool - RunBeforeNextEvent(nsIRunnable* aRunnable); - void MaybeDispatchLoadFailedRunnable(); diff --git a/dom/workers/WorkerThread.cpp b/dom/workers/WorkerThread.cpp index 29fa26556de4..89ee469942da 100644 --- a/dom/workers/WorkerThread.cpp +++ b/dom/workers/WorkerThread.cpp @@ -310,8 +310,7 @@ WorkerThread::Observer::OnDispatchedEvent(nsIThreadInternal* /* aThread */) NS_IMETHODIMP WorkerThread::Observer::OnProcessNextEvent(nsIThreadInternal* /* aThread */, - bool aMayWait, - uint32_t aRecursionDepth) + bool aMayWait) { mWorkerPrivate->AssertIsOnWorkerThread(); @@ -321,23 +320,22 @@ WorkerThread::Observer::OnProcessNextEvent(nsIThreadInternal* /* aThread */, // PrimaryWorkerRunnable::Run() and don't want to process the event in // mWorkerPrivate yet. if (aMayWait) { - MOZ_ASSERT(aRecursionDepth == 2); + MOZ_ASSERT(CycleCollectedJSRuntime::Get()->RecursionDepth() == 2); MOZ_ASSERT(!BackgroundChild::GetForCurrentThread()); return NS_OK; } - mWorkerPrivate->OnProcessNextEvent(aRecursionDepth); + mWorkerPrivate->OnProcessNextEvent(); return NS_OK; } NS_IMETHODIMP WorkerThread::Observer::AfterProcessNextEvent(nsIThreadInternal* /* aThread */, - uint32_t aRecursionDepth, bool /* aEventWasProcessed */) { mWorkerPrivate->AssertIsOnWorkerThread(); - mWorkerPrivate->AfterProcessNextEvent(aRecursionDepth); + mWorkerPrivate->AfterProcessNextEvent(); return NS_OK; } diff --git a/dom/workers/test/promise_worker.js b/dom/workers/test/promise_worker.js index b4e6d7b23c9e..329c54f06b95 100644 --- a/dom/workers/test/promise_worker.js +++ b/dom/workers/test/promise_worker.js @@ -3,6 +3,11 @@ function ok(a, msg) { postMessage({type: 'status', status: !!a, msg: a + ": " + msg }); } +function todo(a, msg) { + dump("TODO: " + !a + " => " + a + " " + msg + "\n"); + postMessage({type: 'status', status: !a, msg: a + ": " + msg }); +} + function is(a, b, msg) { dump("IS: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n"); postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg }); @@ -148,7 +153,7 @@ function promiseAsync_ResolveThenTimeout() { ok(!handlerExecuted, "Handlers are not called before 'then' returns."); } -function promiseAsync_SyncHXRAndImportScripts() +function promiseAsync_SyncXHRAndImportScripts() { var handlerExecuted = false; @@ -790,7 +795,7 @@ var tests = [ promiseAsync_TimeoutResolveThen, promiseAsync_ResolveTimeoutThen, promiseAsync_ResolveThenTimeout, - promiseAsync_SyncHXRAndImportScripts, + promiseAsync_SyncXHRAndImportScripts, promiseDoubleThen, promiseThenException, promiseThenCatchThen, diff --git a/image/test/mochitest/test_synchronized_animation.html b/image/test/mochitest/test_synchronized_animation.html index f537d490ef86..53e709fb95ef 100644 --- a/image/test/mochitest/test_synchronized_animation.html +++ b/image/test/mochitest/test_synchronized_animation.html @@ -55,8 +55,7 @@ function cleanUpAndFinish() { function frameUpdate(aRequest) { if (!gDispatched) { - var util = window.getInterface(Ci.nsIDOMWindowUtils); - util.runBeforeNextEvent(function() { + Promise.resolve().then(function() { gRanEvent = true; }); gDispatched = true; diff --git a/ipc/glue/MessagePump.cpp b/ipc/glue/MessagePump.cpp index 62c0f1cde2df..01fb71483779 100644 --- a/ipc/glue/MessagePump.cpp +++ b/ipc/glue/MessagePump.cpp @@ -433,15 +433,13 @@ MessagePumpForNonMainUIThreads::OnDispatchedEvent(nsIThreadInternal *thread) NS_IMETHODIMP MessagePumpForNonMainUIThreads::OnProcessNextEvent(nsIThreadInternal *thread, - bool mayWait, - uint32_t recursionDepth) + bool mayWait) { return NS_OK; } NS_IMETHODIMP MessagePumpForNonMainUIThreads::AfterProcessNextEvent(nsIThreadInternal *thread, - uint32_t recursionDepth, bool eventWasProcessed) { return NS_OK; diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 233f7af19c0b..ae0b702bfe2f 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -3574,6 +3574,59 @@ XPCJSRuntime::NoteCustomGCThingXPCOMChildren(const js::Class* clasp, JSObject* o return true; } +void +XPCJSRuntime::BeforeProcessTask(bool aMightBlock) +{ + MOZ_ASSERT(NS_IsMainThread()); + + // If ProcessNextEvent was called during a Promise "then" callback, we + // must process any pending microtasks before blocking in the event loop, + // otherwise we may deadlock until an event enters the queue later. + if (aMightBlock) { + if (Promise::PerformMicroTaskCheckpoint()) { + // If any microtask was processed, we post a dummy event in order to + // force the ProcessNextEvent call not to block. This is required + // to support nested event loops implemented using a pattern like + // "while (condition) thread.processNextEvent(true)", in case the + // condition is triggered here by a Promise "then" callback. + + class DummyRunnable : public nsRunnable { + public: + NS_IMETHOD Run() { return NS_OK; } + }; + + NS_DispatchToMainThread(new DummyRunnable()); + } + } + + // Start the slow script timer. + mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes(); + mSlowScriptSecondHalf = false; + js::ResetStopwatches(Get()->Runtime()); + + // Push a null JSContext so that we don't see any script during + // event processing. + PushNullJSContext(); + + CycleCollectedJSRuntime::BeforeProcessTask(aMightBlock); +} + +void +XPCJSRuntime::AfterProcessTask(uint32_t aNewRecursionDepth) +{ + // Now that we're back to the event loop, reset the slow script checkpoint. + mSlowScriptCheckpoint = mozilla::TimeStamp(); + mSlowScriptSecondHalf = false; + + // Call cycle collector occasionally. + MOZ_ASSERT(NS_IsMainThread()); + nsJSContext::MaybePokeCC(); + + CycleCollectedJSRuntime::AfterProcessTask(aNewRecursionDepth); + + PopNullJSContext(); +} + /***************************************************************************/ void diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 043929d4c2c6..3d7e56242871 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -26,7 +26,6 @@ #include "nsDOMMutationObserver.h" #include "nsICycleCollectorListener.h" -#include "nsThread.h" #include "mozilla/XPTInterfaceInfoManager.h" #include "nsIObjectInputStream.h" #include "nsIObjectOutputStream.h" @@ -37,9 +36,7 @@ using namespace mozilla::dom; using namespace xpc; using namespace JS; -NS_IMPL_ISUPPORTS(nsXPConnect, - nsIXPConnect, - nsIThreadObserver) +NS_IMPL_ISUPPORTS(nsXPConnect, nsIXPConnect) nsXPConnect* nsXPConnect::gSelf = nullptr; bool nsXPConnect::gOnceAliveNowDead = false; @@ -61,8 +58,7 @@ const char XPC_XPCONNECT_CONTRACTID[] = "@mozilla.org/js/xpc/XPConnect;1"; nsXPConnect::nsXPConnect() : mRuntime(nullptr), - mShuttingDown(false), - mEventDepth(0) + mShuttingDown(false) { mRuntime = XPCJSRuntime::newXPCJSRuntime(this); @@ -120,11 +116,6 @@ nsXPConnect::InitStatics() // balanced by explicit call to ReleaseXPConnectSingleton() NS_ADDREF(gSelf); - // Set XPConnect as the main thread observer. - if (NS_FAILED(nsThread::SetMainThreadObserver(gSelf))) { - MOZ_CRASH(); - } - // Fire up the SSM. nsScriptSecurityManager::InitStatics(); gScriptSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager(); @@ -152,8 +143,6 @@ nsXPConnect::ReleaseXPConnectSingleton() { nsXPConnect* xpc = gSelf; if (xpc) { - nsThread::SetMainThreadObserver(nullptr); - nsrefcnt cnt; NS_RELEASE2(xpc, cnt); } @@ -933,81 +922,6 @@ nsXPConnect::JSToVariant(JSContext* ctx, HandleValue value, nsIVariant** _retval return NS_OK; } -namespace { - -class DummyRunnable : public nsRunnable { -public: - NS_IMETHOD Run() { return NS_OK; } -}; - -} // namespace - -NS_IMETHODIMP -nsXPConnect::OnProcessNextEvent(nsIThreadInternal* aThread, bool aMayWait, - uint32_t aRecursionDepth) -{ - MOZ_ASSERT(NS_IsMainThread()); - - // If ProcessNextEvent was called during a Promise "then" callback, we - // must process any pending microtasks before blocking in the event loop, - // otherwise we may deadlock until an event enters the queue later. - if (aMayWait) { - if (Promise::PerformMicroTaskCheckpoint()) { - // If any microtask was processed, we post a dummy event in order to - // force the ProcessNextEvent call not to block. This is required - // to support nested event loops implemented using a pattern like - // "while (condition) thread.processNextEvent(true)", in case the - // condition is triggered here by a Promise "then" callback. - NS_DispatchToMainThread(new DummyRunnable()); - } - } - - // Record this event. - mEventDepth++; - - // Start the slow script timer. - mRuntime->OnProcessNextEvent(); - - // Push a null JSContext so that we don't see any script during - // event processing. - bool ok = PushNullJSContext(); - NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); - return NS_OK; -} - -NS_IMETHODIMP -nsXPConnect::AfterProcessNextEvent(nsIThreadInternal* aThread, - uint32_t aRecursionDepth, - bool aEventWasProcessed) -{ - // Watch out for unpaired events during observer registration. - if (MOZ_UNLIKELY(mEventDepth == 0)) - return NS_OK; - mEventDepth--; - - // Now that we're back to the event loop, reset the slow script checkpoint. - mRuntime->OnAfterProcessNextEvent(); - - // Call cycle collector occasionally. - MOZ_ASSERT(NS_IsMainThread()); - nsJSContext::MaybePokeCC(); - - nsContentUtils::PerformMainThreadMicroTaskCheckpoint(); - - Promise::PerformMicroTaskCheckpoint(); - - PopNullJSContext(); - - return NS_OK; -} - -NS_IMETHODIMP -nsXPConnect::OnDispatchedEvent(nsIThreadInternal* aThread) -{ - NS_NOTREACHED("Why tell us?"); - return NS_ERROR_UNEXPECTED; -} - NS_IMETHODIMP nsXPConnect::SetReportAllJSExceptions(bool newval) { diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index fe5bc257c07e..9f09a8fa4490 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -153,7 +153,6 @@ #include "nsJSPrincipals.h" #include "nsIScriptObjectPrincipal.h" #include "xpcObjectHelper.h" -#include "nsIThreadInternal.h" #include "SandboxPrivate.h" #include "BackstagePass.h" @@ -240,14 +239,12 @@ static inline bool IS_WN_REFLECTOR(JSObject* obj) // returned as function call result values they are not addref'd. Exceptions // to this rule are noted explicitly. -class nsXPConnect final : public nsIXPConnect, - public nsIThreadObserver +class nsXPConnect final : public nsIXPConnect { public: // all the interface method declarations... NS_DECL_ISUPPORTS NS_DECL_NSIXPCONNECT - NS_DECL_NSITHREADOBSERVER // non-interface implementation public: @@ -324,13 +321,6 @@ private: XPCJSRuntime* mRuntime; bool mShuttingDown; - // nsIThreadInternal doesn't remember which observers it called - // OnProcessNextEvent on when it gets around to calling AfterProcessNextEvent. - // So if XPConnect gets initialized mid-event (which can happen), we'll get - // an 'after' notification without getting an 'on' notification. If we don't - // watch out for this, we'll do an unmatched |pop| on the context stack. - uint16_t mEventDepth; - static uint32_t gReportAllJSExceptions; public: @@ -489,6 +479,9 @@ public: NoteCustomGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj, nsCycleCollectionTraversalCallback& aCb) const override; + virtual void BeforeProcessTask(bool aMightBlock) override; + virtual void AfterProcessTask(uint32_t aNewRecursionDepth) override; + /** * Infrastructure for classes that need to defer part of the finalization * until after the GC has run, for example for objects that we don't want to @@ -615,16 +608,6 @@ public: PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory); - void OnProcessNextEvent() { - mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes(); - mSlowScriptSecondHalf = false; - js::ResetStopwatches(Get()->Runtime()); - } - void OnAfterProcessNextEvent() { - mSlowScriptCheckpoint = mozilla::TimeStamp(); - mSlowScriptSecondHalf = false; - } - nsTArray<nsXPCWrappedJS*>& WrappedJSToReleaseArray() { return mWrappedJSToReleaseArray; } private: diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index 16fd8495680a..8b7d7de4f8f2 100644 --- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -431,9 +431,9 @@ SheetLoadData::OnDispatchedEvent(nsIThreadInternal* aThread) NS_IMETHODIMP SheetLoadData::OnProcessNextEvent(nsIThreadInternal* aThread, - bool aMayWait, - uint32_t aRecursionDepth) + bool aMayWait) { + // XXXkhuey this is insane! // We want to fire our load even before or after event processing, // whichever comes first. FireLoadEvent(aThread); @@ -442,9 +442,9 @@ SheetLoadData::OnProcessNextEvent(nsIThreadInternal* aThread, NS_IMETHODIMP SheetLoadData::AfterProcessNextEvent(nsIThreadInternal* aThread, - uint32_t aRecursionDepth, bool aEventWasProcessed) { + // XXXkhuey this too! // We want to fire our load even before or after event processing, // whichever comes first. FireLoadEvent(aThread); diff --git a/netwerk/base/nsSocketTransportService2.cpp b/netwerk/base/nsSocketTransportService2.cpp index 5c9c09b57de6..fec1d0a93a4d 100644 --- a/netwerk/base/nsSocketTransportService2.cpp +++ b/netwerk/base/nsSocketTransportService2.cpp @@ -763,14 +763,13 @@ nsSocketTransportService::OnDispatchedEvent(nsIThreadInternal *thread) NS_IMETHODIMP nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal *thread, - bool mayWait, uint32_t depth) + bool mayWait) { return NS_OK; } NS_IMETHODIMP nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread, - uint32_t depth, bool eventWasProcessed) { return NS_OK; diff --git a/netwerk/cache2/CacheIOThread.cpp b/netwerk/cache2/CacheIOThread.cpp index 72ecbc0a5057..856ce2dd1b92 100644 --- a/netwerk/cache2/CacheIOThread.cpp +++ b/netwerk/cache2/CacheIOThread.cpp @@ -315,12 +315,12 @@ NS_IMETHODIMP CacheIOThread::OnDispatchedEvent(nsIThreadInternal *thread) return NS_OK; } -NS_IMETHODIMP CacheIOThread::OnProcessNextEvent(nsIThreadInternal *thread, bool mayWait, uint32_t recursionDepth) +NS_IMETHODIMP CacheIOThread::OnProcessNextEvent(nsIThreadInternal *thread, bool mayWait) { return NS_OK; } -NS_IMETHODIMP CacheIOThread::AfterProcessNextEvent(nsIThreadInternal *thread, uint32_t recursionDepth, +NS_IMETHODIMP CacheIOThread::AfterProcessNextEvent(nsIThreadInternal *thread, bool eventWasProcessed) { return NS_OK; diff --git a/widget/ScreenProxy.cpp b/widget/ScreenProxy.cpp index 936841431f8e..6481e85d9f3e 100644 --- a/widget/ScreenProxy.cpp +++ b/widget/ScreenProxy.cpp @@ -169,16 +169,9 @@ ScreenProxy::InvalidateCacheOnNextTick() mCacheWillInvalidate = true; - nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID); - if (appShell) { - nsCOMPtr<nsIRunnable> r = - NS_NewRunnableMethod(this, &ScreenProxy::InvalidateCache); - appShell->RunInStableState(r.forget()); - } else { - // It's pretty bad news if we can't get the appshell. In that case, - // let's just invalidate the cache right away. - InvalidateCache(); - } + nsCOMPtr<nsIRunnable> r = + NS_NewRunnableMethod(this, &ScreenProxy::InvalidateCache); + nsContentUtils::RunInStableState(r.forget()); } void diff --git a/widget/cocoa/nsAppShell.h b/widget/cocoa/nsAppShell.h index bf5b06a49437..b7836b63918b 100644 --- a/widget/cocoa/nsAppShell.h +++ b/widget/cocoa/nsAppShell.h @@ -35,10 +35,8 @@ public: NS_IMETHOD Run(void); NS_IMETHOD Exit(void); - NS_IMETHOD OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait, - uint32_t aRecursionDepth); + NS_IMETHOD OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait); NS_IMETHOD AfterProcessNextEvent(nsIThreadInternal *aThread, - uint32_t aRecursionDepth, bool aEventWasProcessed); // public only to be visible to Objective-C code that must call it diff --git a/widget/cocoa/nsAppShell.mm b/widget/cocoa/nsAppShell.mm index c94245f63295..0f753530ca19 100644 --- a/widget/cocoa/nsAppShell.mm +++ b/widget/cocoa/nsAppShell.mm @@ -735,8 +735,7 @@ nsAppShell::Exit(void) // // public NS_IMETHODIMP -nsAppShell::OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait, - uint32_t aRecursionDepth) +nsAppShell::OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; @@ -746,7 +745,7 @@ nsAppShell::OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait, NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; ::CFArrayAppendValue(mAutoreleasePools, pool); - return nsBaseAppShell::OnProcessNextEvent(aThread, aMayWait, aRecursionDepth); + return nsBaseAppShell::OnProcessNextEvent(aThread, aMayWait); NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } @@ -760,7 +759,6 @@ nsAppShell::OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait, // public NS_IMETHODIMP nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread, - uint32_t aRecursionDepth, bool aEventWasProcessed) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; @@ -775,8 +773,7 @@ nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread, ::CFArrayRemoveValueAtIndex(mAutoreleasePools, count - 1); [pool release]; - return nsBaseAppShell::AfterProcessNextEvent(aThread, aRecursionDepth, - aEventWasProcessed); + return nsBaseAppShell::AfterProcessNextEvent(aThread, aEventWasProcessed); NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } diff --git a/widget/nsBaseAppShell.cpp b/widget/nsBaseAppShell.cpp index db59bf80dac6..eee3eaf3d073 100644 --- a/widget/nsBaseAppShell.cpp +++ b/widget/nsBaseAppShell.cpp @@ -31,7 +31,6 @@ nsBaseAppShell::nsBaseAppShell() , mSwitchTime(0) , mLastNativeEventTime(0) , mEventloopNestingState(eEventloopNone) - , mRunningSyncSections(false) , mRunning(false) , mExiting(false) , mBlockNativeEvent(false) @@ -40,7 +39,6 @@ nsBaseAppShell::nsBaseAppShell() nsBaseAppShell::~nsBaseAppShell() { - NS_ASSERTION(mSyncSections.IsEmpty(), "Must have run all sync sections"); } nsresult @@ -120,7 +118,7 @@ nsBaseAppShell::DoProcessMoreGeckoEvents() // Main thread via OnProcessNextEvent below bool -nsBaseAppShell::DoProcessNextNativeEvent(bool mayWait, uint32_t recursionDepth) +nsBaseAppShell::DoProcessNextNativeEvent(bool mayWait) { // The next native event to be processed may trigger our NativeEventCallback, // in which case we do not want it to process any thread events since we'll @@ -137,14 +135,7 @@ nsBaseAppShell::DoProcessNextNativeEvent(bool mayWait, uint32_t recursionDepth) mEventloopNestingState = eEventloopXPCOM; IncrementEventloopNestingLevel(); - bool result = ProcessNextNativeEvent(mayWait); - - // Make sure that any sync sections registered during this most recent event - // are run now. This is not considered a stable state because we're not back - // to the event loop yet. - RunSyncSections(false, recursionDepth); - DecrementEventloopNestingLevel(); mEventloopNestingState = prevVal; @@ -239,8 +230,7 @@ nsBaseAppShell::OnDispatchedEvent(nsIThreadInternal *thr) // Called from the main thread NS_IMETHODIMP -nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait, - uint32_t recursionDepth) +nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait) { if (mBlockNativeEvent) { if (!mayWait) @@ -278,13 +268,13 @@ nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait, bool keepGoing; do { mLastNativeEventTime = now; - keepGoing = DoProcessNextNativeEvent(false, recursionDepth); + keepGoing = DoProcessNextNativeEvent(false); } while (keepGoing && ((now = PR_IntervalNow()) - start) < limit); } else { // Avoid starving native events completely when in performance mode if (start - mLastNativeEventTime > limit) { mLastNativeEventTime = start; - DoProcessNextNativeEvent(false, recursionDepth); + DoProcessNextNativeEvent(false); } } @@ -296,7 +286,7 @@ nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait, mayWait = false; mLastNativeEventTime = PR_IntervalNow(); - if (!DoProcessNextNativeEvent(mayWait, recursionDepth) || !mayWait) + if (!DoProcessNextNativeEvent(mayWait) || !mayWait) break; } @@ -309,9 +299,6 @@ nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait, DispatchDummyEvent(thr); } - // We're about to run an event, so we're in a stable state. - RunSyncSections(true, recursionDepth); - return NS_OK; } @@ -344,95 +331,11 @@ nsBaseAppShell::DecrementEventloopNestingLevel() #endif } -void -nsBaseAppShell::RunSyncSectionsInternal(bool aStable, - uint32_t aThreadRecursionLevel) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!mSyncSections.IsEmpty(), "Nothing to do!"); - - // We don't support re-entering sync sections. This effectively means that - // sync sections may not spin the event loop. - MOZ_RELEASE_ASSERT(!mRunningSyncSections); - mRunningSyncSections = true; - - // We've got synchronous sections. Run all of them that are are awaiting a - // stable state if aStable is true (i.e. we really are in a stable state). - // Also run the synchronous sections that are simply waiting for the right - // combination of event loop nesting level and thread recursion level. - // Note that a synchronous section could add another synchronous section, so - // we don't remove elements from mSyncSections until all sections have been - // run, or else we'll screw up our iteration. Any sync sections that are not - // ready to be run are saved for later. - - nsTArray<SyncSection> pendingSyncSections; - - for (uint32_t i = 0; i < mSyncSections.Length(); i++) { - SyncSection& section = mSyncSections[i]; - if ((aStable && section.mStable) || - (!section.mStable && - section.mEventloopNestingLevel == mEventloopNestingLevel && - section.mThreadRecursionLevel == aThreadRecursionLevel)) { - section.mRunnable->Run(); - } - else { - // Add to pending list. - SyncSection* pending = pendingSyncSections.AppendElement(); - section.Forget(pending); - } - } - - mSyncSections.SwapElements(pendingSyncSections); - mRunningSyncSections = false; -} - -void -nsBaseAppShell::ScheduleSyncSection(already_AddRefed<nsIRunnable> aRunnable, - bool aStable) -{ - NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); - - nsIThread* thread = NS_GetCurrentThread(); - - // Add this runnable to our list of synchronous sections. - SyncSection* section = mSyncSections.AppendElement(); - section->mStable = aStable; - section->mRunnable = aRunnable; - - // If aStable is false then this synchronous section is supposed to run before - // the next event at the current nesting level. Record the event loop nesting - // level and the thread recursion level so that the synchronous section will - // run at the proper time. - if (!aStable) { - section->mEventloopNestingLevel = mEventloopNestingLevel; - - nsCOMPtr<nsIThreadInternal> threadInternal = do_QueryInterface(thread); - NS_ASSERTION(threadInternal, "This should never fail!"); - - uint32_t recursionLevel; - if (NS_FAILED(threadInternal->GetRecursionDepth(&recursionLevel))) { - NS_ERROR("This should never fail!"); - } - - // Due to the weird way that the thread recursion counter is implemented we - // subtract one from the recursion level if we have one. - section->mThreadRecursionLevel = recursionLevel ? recursionLevel - 1 : 0; - } - - // Ensure we've got a pending event, else the callbacks will never run. - if (!NS_HasPendingEvents(thread) && !DispatchDummyEvent(thread)) { - RunSyncSections(true, 0); - } -} - // Called from the main thread NS_IMETHODIMP nsBaseAppShell::AfterProcessNextEvent(nsIThreadInternal *thr, - uint32_t recursionDepth, bool eventWasProcessed) { - // We've just finished running an event, so we're in a stable state. - RunSyncSections(true, recursionDepth); return NS_OK; } @@ -444,17 +347,3 @@ nsBaseAppShell::Observe(nsISupports *subject, const char *topic, Exit(); return NS_OK; } - -void -nsBaseAppShell::RunInStableState(already_AddRefed<nsIRunnable> aRunnable) -{ - ScheduleSyncSection(mozilla::Move(aRunnable), true); -} - -NS_IMETHODIMP -nsBaseAppShell::RunBeforeNextEvent(nsIRunnable* aRunnable) -{ - nsCOMPtr<nsIRunnable> runnable = aRunnable; - ScheduleSyncSection(runnable.forget(), false); - return NS_OK; -} diff --git a/widget/nsBaseAppShell.h b/widget/nsBaseAppShell.h index 09f8e47b71ca..4ee9e3d2e79d 100644 --- a/widget/nsBaseAppShell.h +++ b/widget/nsBaseAppShell.h @@ -25,7 +25,6 @@ class nsBaseAppShell : public nsIAppShell, public nsIThreadObserver, public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIAPPSHELL - void RunInStableState(already_AddRefed<nsIRunnable> runnable) override; NS_DECL_NSITHREADOBSERVER NS_DECL_NSIOBSERVER @@ -77,45 +76,13 @@ protected: uint32_t mEventloopNestingLevel; private: - bool DoProcessNextNativeEvent(bool mayWait, uint32_t recursionDepth); + bool DoProcessNextNativeEvent(bool mayWait); bool DispatchDummyEvent(nsIThread* target); void IncrementEventloopNestingLevel(); void DecrementEventloopNestingLevel(); - /** - * Runs all synchronous sections which are queued up in mSyncSections. - */ - void RunSyncSectionsInternal(bool stable, uint32_t threadRecursionLevel); - - void RunSyncSections(bool stable, uint32_t threadRecursionLevel) - { - if (!mSyncSections.IsEmpty()) { - RunSyncSectionsInternal(stable, threadRecursionLevel); - } - } - - void ScheduleSyncSection(already_AddRefed<nsIRunnable> runnable, bool stable); - - struct SyncSection { - SyncSection() - : mStable(false), mEventloopNestingLevel(0), mThreadRecursionLevel(0) - { } - - void Forget(SyncSection* other) { - other->mStable = mStable; - other->mEventloopNestingLevel = mEventloopNestingLevel; - other->mThreadRecursionLevel = mThreadRecursionLevel; - other->mRunnable = mRunnable.forget(); - } - - bool mStable; - uint32_t mEventloopNestingLevel; - uint32_t mThreadRecursionLevel; - nsCOMPtr<nsIRunnable> mRunnable; - }; - nsCOMPtr<nsIRunnable> mDummyEvent; /** * mBlockedWait points back to a slot that controls the wait loop in @@ -135,8 +102,6 @@ private: eEventloopOther // innermost native event loop is a native library/plugin etc }; EventloopNestingState mEventloopNestingState; - nsTArray<SyncSection> mSyncSections; - bool mRunningSyncSections; bool mRunning; bool mExiting; /** diff --git a/widget/nsIAppShell.idl b/widget/nsIAppShell.idl index bb089d20ae86..c21c4d107b9e 100644 --- a/widget/nsIAppShell.idl +++ b/widget/nsIAppShell.idl @@ -15,7 +15,7 @@ template <class T> struct already_AddRefed; * Interface for the native event system layer. This interface is designed * to be used on the main application thread only. */ -[uuid(3d09973e-3975-4fd4-b103-276300cc8437)] +[uuid(7cd5c71d-223b-4afe-931d-5eedb1f2b01f)] interface nsIAppShell : nsISupports { /** @@ -73,26 +73,4 @@ interface nsIAppShell : nsISupports * The current event loop nesting level. */ readonly attribute unsigned long eventloopNestingLevel; - -%{ C++ - /** - * Add a "synchronous section", in the form of an nsIRunnable run once the - * event loop has reached a "stable state". |runnable| must not cause any - * queued events to be processed (i.e. must not spin the event loop). We've - * reached a stable state when the currently executing task/event has - * finished, see: - * http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#synchronous-section - * In practice this runs aRunnable once the currently executing event - * finishes. If called multiple times per task/event, all the runnables will - * be executed, in the order in which runInStableState() was called. - */ - virtual void RunInStableState(already_AddRefed<nsIRunnable> runnable) = 0; -%} - - /** - * Run the given runnable before the next iteration of the event loop (this - * includes native events too). If a nested loop is spawned within the current - * event then the runnable will not be run until that loop has terminated. - */ - void runBeforeNextEvent(in nsIRunnable runnable); }; diff --git a/widget/nsScreenManagerProxy.cpp b/widget/nsScreenManagerProxy.cpp index b768a53091ae..956529971ab4 100644 --- a/widget/nsScreenManagerProxy.cpp +++ b/widget/nsScreenManagerProxy.cpp @@ -198,16 +198,9 @@ nsScreenManagerProxy::InvalidateCacheOnNextTick() mCacheWillInvalidate = true; - nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID); - if (appShell) { - nsCOMPtr<nsIRunnable> r = - NS_NewRunnableMethod(this, &nsScreenManagerProxy::InvalidateCache); - appShell->RunInStableState(r.forget()); - } else { - // It's pretty bad news if we can't get the appshell. In that case, - // let's just invalidate the cache right away. - InvalidateCache(); - } + nsCOMPtr<nsIRunnable> r = + NS_NewRunnableMethod(this, &nsScreenManagerProxy::InvalidateCache); + nsContentUtils::RunInStableState(r.forget()); } void diff --git a/widget/tests/moz.build b/widget/tests/moz.build index 8fd299185720..698f455f8b3d 100644 --- a/widget/tests/moz.build +++ b/widget/tests/moz.build @@ -8,10 +8,6 @@ XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini'] MOCHITEST_MANIFESTS += ['mochitest.ini'] MOCHITEST_CHROME_MANIFESTS += ['chrome.ini'] -GeckoCppUnitTests([ - 'TestAppShellSteadyState', -]) - FAIL_ON_WARNINGS = True # if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index 492c7483c530..109821ad2286 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -63,6 +63,7 @@ #include "mozilla/dom/BindingUtils.h" #include "mozilla/DebuggerOnGCRunnable.h" #include "mozilla/dom/DOMJSClass.h" +#include "mozilla/dom/Promise.h" #include "mozilla/dom/ScriptSettings.h" #include "jsprf.h" #include "js/Debug.h" @@ -77,6 +78,7 @@ #endif #include "nsIException.h" +#include "nsThread.h" #include "nsThreadUtils.h" #include "xpcpublic.h" @@ -402,9 +404,18 @@ CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSRuntime* aParentRuntime, , mJSRuntime(nullptr) , mPrevGCSliceCallback(nullptr) , mJSHolders(256) + , mDoingStableStates(false) , mOutOfMemoryState(OOMState::OK) , mLargeAllocationFailureState(OOMState::OK) { + nsCOMPtr<nsIThread> thread = do_GetCurrentThread(); + mOwningThread = thread.forget().downcast<nsThread>().take(); + MOZ_RELEASE_ASSERT(mOwningThread); + + mOwningThread->SetScriptObserver(this); + // The main thread has a base recursion depth of 0, workers of 1. + mBaseRecursionDepth = RecursionDepth(); + mozilla::dom::InitScriptSettings(); mJSRuntime = JS_NewRuntime(aMaxBytes, aMaxNurseryBytes, aParentRuntime); @@ -440,6 +451,13 @@ CycleCollectedJSRuntime::~CycleCollectedJSRuntime() MOZ_ASSERT(mJSRuntime); MOZ_ASSERT(!mDeferredFinalizerTable.Count()); + // Last chance to process any events. + ProcessMetastableStateQueue(mBaseRecursionDepth); + MOZ_ASSERT(mMetastableStateEvents.IsEmpty()); + + ProcessStableStateQueue(); + MOZ_ASSERT(mStableStateEvents.IsEmpty()); + // Clear mPendingException first, since it might be cycle collected. mPendingException = nullptr; @@ -448,6 +466,9 @@ CycleCollectedJSRuntime::~CycleCollectedJSRuntime() nsCycleCollector_forgetJSRuntime(); mozilla::dom::DestroyScriptSettings(); + + mOwningThread->SetScriptObserver(nullptr); + NS_RELEASE(mOwningThread); } size_t @@ -1015,6 +1036,112 @@ CycleCollectedJSRuntime::DumpJSHeap(FILE* aFile) js::DumpHeap(Runtime(), aFile, js::CollectNurseryBeforeDump); } +void +CycleCollectedJSRuntime::ProcessStableStateQueue() +{ + MOZ_RELEASE_ASSERT(!mDoingStableStates); + mDoingStableStates = true; + + for (uint32_t i = 0; i < mStableStateEvents.Length(); ++i) { + nsCOMPtr<nsIRunnable> event = mStableStateEvents[i].forget(); + event->Run(); + } + + mStableStateEvents.Clear(); + mDoingStableStates = false; +} + +void +CycleCollectedJSRuntime::ProcessMetastableStateQueue(uint32_t aRecursionDepth) +{ + MOZ_RELEASE_ASSERT(!mDoingStableStates); + mDoingStableStates = true; + + nsTArray<RunInMetastableStateData> localQueue = Move(mMetastableStateEvents); + + for (uint32_t i = 0; i < localQueue.Length(); ++i) + { + RunInMetastableStateData& data = localQueue[i]; + if (data.mRecursionDepth != aRecursionDepth) { + continue; + } + + { + nsCOMPtr<nsIRunnable> runnable = data.mRunnable.forget(); + runnable->Run(); + } + + localQueue.RemoveElementAt(i--); + } + + // If the queue has events in it now, they were added from something we called, + // so they belong at the end of the queue. + localQueue.AppendElements(mMetastableStateEvents); + localQueue.SwapElements(mMetastableStateEvents); + mDoingStableStates = false; +} + +void +CycleCollectedJSRuntime::AfterProcessTask(uint32_t aRecursionDepth) +{ + // See HTML 6.1.4.2 Processing model + + // Execute any events that were waiting for a microtask to complete. + // This is not (yet) in the spec. + ProcessMetastableStateQueue(aRecursionDepth); + + // Step 4.1: Execute microtasks. + if (NS_IsMainThread()) { + nsContentUtils::PerformMainThreadMicroTaskCheckpoint(); + } + + Promise::PerformMicroTaskCheckpoint(); + + // Step 4.2 Execute any events that were waiting for a stable state. + ProcessStableStateQueue(); +} + +void +CycleCollectedJSRuntime::AfterProcessMicrotask() +{ + AfterProcessMicrotask(RecursionDepth()); +} + +void +CycleCollectedJSRuntime::AfterProcessMicrotask(uint32_t aRecursionDepth) +{ + // Between microtasks, execute any events that were waiting for a microtask + // to complete. + ProcessMetastableStateQueue(aRecursionDepth); +} + +uint32_t +CycleCollectedJSRuntime::RecursionDepth() +{ + return mOwningThread->RecursionDepth(); +} + +void +CycleCollectedJSRuntime::RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable) +{ + MOZ_ASSERT(mJSRuntime); + mStableStateEvents.AppendElement(Move(aRunnable)); +} + +void +CycleCollectedJSRuntime::RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable) +{ + RunInMetastableStateData data; + data.mRunnable = aRunnable; + + MOZ_ASSERT(mOwningThread); + data.mRecursionDepth = RecursionDepth(); + + // There must be an event running to get here. + MOZ_ASSERT(data.mRecursionDepth > mBaseRecursionDepth); + + mMetastableStateEvents.AppendElement(Move(data)); +} IncrementalFinalizeRunnable::IncrementalFinalizeRunnable(CycleCollectedJSRuntime* aRt, DeferredFinalizerTable& aFinalizers) diff --git a/xpcom/base/CycleCollectedJSRuntime.h b/xpcom/base/CycleCollectedJSRuntime.h index dfe8997619d3..d48ae4e8b5e2 100644 --- a/xpcom/base/CycleCollectedJSRuntime.h +++ b/xpcom/base/CycleCollectedJSRuntime.h @@ -21,6 +21,7 @@ class nsCycleCollectionNoteRootCallback; class nsIException; class nsIRunnable; +class nsThread; namespace js { struct Class; @@ -151,7 +152,6 @@ protected: } private: - void DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing, nsCycleCollectionTraversalCallback& aCb) const; @@ -208,6 +208,10 @@ private: virtual void TraceNativeBlackRoots(JSTracer* aTracer) { }; void TraceNativeGrayRoots(JSTracer* aTracer); + void AfterProcessMicrotask(uint32_t aRecursionDepth); + void ProcessStableStateQueue(); + void ProcessMetastableStateQueue(uint32_t aRecursionDepth); + public: enum DeferredFinalizeType { FinalizeIncrementally, @@ -294,6 +298,21 @@ public: return mJSRuntime; } + // nsThread entrypoints + virtual void BeforeProcessTask(bool aMightBlock) { }; + virtual void AfterProcessTask(uint32_t aRecursionDepth); + + // microtask processor entry point + void AfterProcessMicrotask(); + + uint32_t RecursionDepth(); + + // Run in stable state (call through nsContentUtils) + void RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable); + // This isn't in the spec at all yet, but this gets the behavior we want for IDB. + // Runs after the current microtask completes. + void RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable); + // Get the current thread's CycleCollectedJSRuntime. Returns null if there // isn't one. static CycleCollectedJSRuntime* Get(); @@ -325,9 +344,21 @@ private: nsRefPtr<IncrementalFinalizeRunnable> mFinalizeRunnable; nsCOMPtr<nsIException> mPendingException; + nsThread* mOwningThread; // Manual refcounting to avoid include hell. std::queue<nsCOMPtr<nsIRunnable>> mPromiseMicroTaskQueue; + struct RunInMetastableStateData + { + nsCOMPtr<nsIRunnable> mRunnable; + uint32_t mRecursionDepth; + }; + + nsTArray<nsCOMPtr<nsIRunnable>> mStableStateEvents; + nsTArray<RunInMetastableStateData> mMetastableStateEvents; + uint32_t mBaseRecursionDepth; + bool mDoingStableStates; + OOMState mOutOfMemoryState; OOMState mLargeAllocationFailureState; }; diff --git a/xpcom/threads/LazyIdleThread.cpp b/xpcom/threads/LazyIdleThread.cpp index d7e1c8d34740..1ed33ef03673 100644 --- a/xpcom/threads/LazyIdleThread.cpp +++ b/xpcom/threads/LazyIdleThread.cpp @@ -526,15 +526,13 @@ LazyIdleThread::OnDispatchedEvent(nsIThreadInternal* /*aThread */) NS_IMETHODIMP LazyIdleThread::OnProcessNextEvent(nsIThreadInternal* /* aThread */, - bool /* aMayWait */, - uint32_t /* aRecursionDepth */) + bool /* aMayWait */) { return NS_OK; } NS_IMETHODIMP LazyIdleThread::AfterProcessNextEvent(nsIThreadInternal* /* aThread */, - uint32_t /* aRecursionDepth */, bool aEventWasProcessed) { bool shouldNotifyIdle; diff --git a/xpcom/threads/nsIThreadInternal.idl b/xpcom/threads/nsIThreadInternal.idl index 1c2782e4cedf..9287bf5d79f5 100644 --- a/xpcom/threads/nsIThreadInternal.idl +++ b/xpcom/threads/nsIThreadInternal.idl @@ -13,7 +13,7 @@ interface nsIThreadObserver; * The XPCOM thread object implements this interface, which allows a consumer * to observe dispatch activity on the thread. */ -[scriptable, uuid(b24c5af3-43c2-4d17-be14-94d6648a305f)] +[scriptable, uuid(9cc51754-2eb3-4b46-ae99-38a61881c622)] interface nsIThreadInternal : nsIThread { /** @@ -25,13 +25,6 @@ interface nsIThreadInternal : nsIThread */ attribute nsIThreadObserver observer; - /** - * The current recursion depth, 0 when no events are running, 1 when a single - * event is running, and higher when nested events are running. Must only be - * called on the target thread. - */ - readonly attribute unsigned long recursionDepth; - /** * Add an observer that will *only* receive onProcessNextEvent, * beforeProcessNextEvent. and afterProcessNextEvent callbacks. Always called @@ -81,7 +74,7 @@ interface nsIThreadInternal : nsIThread * onDispatchedEvent(thread) { * NativeQueue.signal(); * } - * onProcessNextEvent(thread, mayWait, recursionDepth) { + * onProcessNextEvent(thread, mayWait) { * if (NativeQueue.hasNextEvent()) * NativeQueue.processNextEvent(); * while (mayWait && !thread.hasPendingEvent()) { @@ -100,7 +93,7 @@ interface nsIThreadInternal : nsIThread * afterProcessNextEvent, then another that inherits the first and adds * onDispatchedEvent. */ -[scriptable, uuid(09b424c3-26b0-4128-9039-d66f85b02c63)] +[uuid(cc8da053-1776-44c2-9199-b5a629d0a19d)] interface nsIThreadObserver : nsISupports { /** @@ -122,29 +115,21 @@ interface nsIThreadObserver : nsISupports * @param mayWait * Indicates whether or not the method is allowed to block the calling * thread. For example, this parameter is false during thread shutdown. - * @param recursionDepth - * Indicates the number of calls to ProcessNextEvent on the call stack in - * addition to the current call. */ - void onProcessNextEvent(in nsIThreadInternal thread, in boolean mayWait, - in unsigned long recursionDepth); + void onProcessNextEvent(in nsIThreadInternal thread, in boolean mayWait); /** * This method is called (from nsIThread::ProcessNextEvent) after an event * is processed. It does not guarantee that an event was actually processed * (depends on the value of |eventWasProcessed|. This method is only called - * on the target thread. + * on the target thread. DO NOT EVER RUN SCRIPT FROM THIS CALLBACK!!! * * @param thread * The thread that processed another event. - * @param recursionDepth - * Indicates the number of calls to ProcessNextEvent on the call stack in - * addition to the current call. * @param eventWasProcessed * Indicates whether an event was actually processed. May be false if the * |mayWait| flag was false when calling nsIThread::ProcessNextEvent(). */ void afterProcessNextEvent(in nsIThreadInternal thread, - in unsigned long recursionDepth, in bool eventWasProcessed); }; diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 62b82a36f3cc..eaf569f6f240 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -22,6 +22,7 @@ #include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "pratom.h" +#include "mozilla/CycleCollectedJSRuntime.h" #include "mozilla/Logging.h" #include "nsIObserverService.h" #if !defined(MOZILLA_XPCOMRT_API) @@ -94,8 +95,6 @@ GetThreadLog() NS_DECL_CI_INTERFACE_GETTER(nsThread) -nsIThreadObserver* nsThread::sMainThreadObserver = nullptr; - //----------------------------------------------------------------------------- // Because we do not have our own nsIFactory, we have to implement nsIClassInfo // somewhat manually. @@ -440,6 +439,7 @@ int sCanaryOutputFD = -1; nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize) : mLock("nsThread.mLock") + , mScriptObserver(nullptr) , mEvents(&mEventsRoot) , mPriority(PRIORITY_NORMAL) , mThread(nullptr) @@ -824,22 +824,19 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult) } #endif - bool notifyMainThreadObserver = - (MAIN_THREAD == mIsMainThread) && sMainThreadObserver; - if (notifyMainThreadObserver) { - sMainThreadObserver->OnProcessNextEvent(this, reallyWait, - mNestedEventLoopDepth); + ++mNestedEventLoopDepth; + + bool callScriptObserver = !!mScriptObserver; + if (callScriptObserver) { + mScriptObserver->BeforeProcessTask(reallyWait); } nsCOMPtr<nsIThreadObserver> obs = mObserver; if (obs) { - obs->OnProcessNextEvent(this, reallyWait, mNestedEventLoopDepth); + obs->OnProcessNextEvent(this, reallyWait); } - NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent, - (this, reallyWait, mNestedEventLoopDepth)); - - ++mNestedEventLoopDepth; + NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent, (this, reallyWait)); #ifdef MOZ_CANARY Canary canary; @@ -872,20 +869,18 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult) } } - --mNestedEventLoopDepth; - - NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent, - (this, mNestedEventLoopDepth, *aResult)); + NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent, (this, *aResult)); if (obs) { - obs->AfterProcessNextEvent(this, mNestedEventLoopDepth, *aResult); + obs->AfterProcessNextEvent(this, *aResult); } - if (notifyMainThreadObserver && sMainThreadObserver) { - sMainThreadObserver->AfterProcessNextEvent(this, mNestedEventLoopDepth, - *aResult); + if (callScriptObserver && mScriptObserver) { + mScriptObserver->AfterProcessTask(mNestedEventLoopDepth); } + --mNestedEventLoopDepth; + return rv; } @@ -962,15 +957,11 @@ nsThread::SetObserver(nsIThreadObserver* aObs) return NS_OK; } -NS_IMETHODIMP -nsThread::GetRecursionDepth(uint32_t* aDepth) +uint32_t +nsThread::RecursionDepth() const { - if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) { - return NS_ERROR_NOT_SAME_THREAD; - } - - *aDepth = mNestedEventLoopDepth; - return NS_OK; + MOZ_ASSERT(PR_GetCurrentThread() == mThread); + return mNestedEventLoopDepth; } NS_IMETHODIMP @@ -1069,19 +1060,16 @@ nsThread::PopEventQueue(nsIEventTarget* aInnermostTarget) return NS_OK; } -nsresult -nsThread::SetMainThreadObserver(nsIThreadObserver* aObserver) +void +nsThread::SetScriptObserver(mozilla::CycleCollectedJSRuntime* aScriptObserver) { - if (aObserver && nsThread::sMainThreadObserver) { - return NS_ERROR_NOT_AVAILABLE; + if (!aScriptObserver) { + mScriptObserver = nullptr; + return; } - if (!NS_IsMainThread()) { - return NS_ERROR_UNEXPECTED; - } - - nsThread::sMainThreadObserver = aObserver; - return NS_OK; + MOZ_ASSERT(!mScriptObserver); + mScriptObserver = aScriptObserver; } //----------------------------------------------------------------------------- diff --git a/xpcom/threads/nsThread.h b/xpcom/threads/nsThread.h index 7b972f0cf959..b42a939c0707 100644 --- a/xpcom/threads/nsThread.h +++ b/xpcom/threads/nsThread.h @@ -18,6 +18,10 @@ #include "nsAutoPtr.h" #include "mozilla/AlreadyAddRefed.h" +namespace mozilla { +class CycleCollectedJSRuntime; +} + // A native thread class nsThread : public nsIThreadInternal @@ -67,12 +71,13 @@ public: mEventObservers.Clear(); } - static nsresult - SetMainThreadObserver(nsIThreadObserver* aObserver); + void + SetScriptObserver(mozilla::CycleCollectedJSRuntime* aScriptObserver); + + uint32_t + RecursionDepth() const; protected: - static nsIThreadObserver* sMainThreadObserver; - class nsChainedEventQueue; class nsNestedEventTarget; @@ -175,6 +180,7 @@ protected: mozilla::Mutex mLock; nsCOMPtr<nsIThreadObserver> mObserver; + mozilla::CycleCollectedJSRuntime* mScriptObserver; // Only accessed on the target thread. nsAutoTObserverArray<nsCOMPtr<nsIThreadObserver>, 2> mEventObservers;