From b9cfdbdbdc219dd3ae6ce0a59347631b015c4f68 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Thu, 22 Jul 2021 10:47:48 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1159409 - (Part 1-) - Remove Init() from the Image interface. r=tn (0b663ee45) - Bug 1159409 - (Part 2) - Remove ProgressTrackerInit and register Images with a ProgressTracker in ImageFactory. r=tn (a24d4e849) - Bug 1179909: Refactor stable state handling. r=smaug This is motivated by three separate but related problems: (0ead73dbd) - remove mPreemptingRunnableInfos of PM which I couldn't trace in FF (96474c90a) - Bug 1179909: Build fix. r=me CLOSED TREE (5d35a65d5) - Bug 1144418 - target events for text nodes in shadow dom to the nearest element in the flattened tree. r=wchen (26c0eb8b2) - Bug 853889 - Check single-box orientaton in _cairo_bentley_ottmann_tessellate_rectangular_traps and _cairo_bentley_ottmann_tessellate_boxes. r=jmuizelaar (a13abee2f) - Bug 1143303 - extend D2D circle workaround to work for small circles. r=bas (1ccb1c0c1) --- dom/base/nsContentUtils.cpp | 20 +-- dom/base/nsContentUtils.h | 22 +-- dom/base/nsDOMWindowUtils.cpp | 29 +--- dom/events/EventStateManager.cpp | 2 +- dom/indexedDB/ActorsParent.cpp | 4 +- dom/indexedDB/IDBFileHandle.cpp | 20 +-- dom/indexedDB/IDBTransaction.cpp | 49 +------ dom/interfaces/base/nsIDOMWindowUtils.idl | 28 +--- dom/media/webaudio/AudioDestinationNode.cpp | 4 +- dom/promise/Promise.cpp | 1 + dom/promise/tests/test_promise.html | 21 +++ dom/storage/DOMStorageDBThread.cpp | 4 +- dom/workers/RuntimeService.cpp | 9 ++ dom/workers/WorkerPrivate.cpp | 95 ++----------- dom/workers/WorkerPrivate.h | 12 +- dom/workers/WorkerThread.cpp | 10 +- dom/workers/test/promise_worker.js | 9 +- gfx/2d/PathD2D.cpp | 73 +++++++--- .../src/cairo-bentley-ottmann-rectangular.c | 44 +++++- gfx/tests/reftest/1143303-1.svg | 27 ++++ gfx/tests/reftest/853889-1-ref.html | 12 ++ gfx/tests/reftest/853889-1.html | 19 +++ gfx/tests/reftest/pass.svg | 8 ++ gfx/tests/reftest/reftest.list | 2 + image/DynamicImage.cpp | 6 - image/DynamicImage.h | 2 - image/Image.cpp | 6 + image/Image.h | 10 +- image/ImageFactory.cpp | 33 ++++- image/ImageFactory.h | 13 ++ image/ImageWrapper.cpp | 6 - image/ImageWrapper.h | 2 - image/MultipartImage.cpp | 19 ++- image/MultipartImage.h | 7 +- image/ProgressTracker.cpp | 20 --- image/ProgressTracker.h | 22 +-- image/RasterImage.cpp | 5 +- image/RasterImage.h | 11 +- image/VectorImage.cpp | 7 +- image/VectorImage.h | 11 +- image/imgRequest.cpp | 3 +- .../test_synchronized_animation.html | 3 +- ipc/glue/MessagePump.cpp | 4 +- js/xpconnect/src/XPCJSRuntime.cpp | 53 ++++++++ js/xpconnect/src/nsXPConnect.cpp | 90 +------------ js/xpconnect/src/xpcprivate.h | 25 +--- layout/base/nsPresShell.cpp | 2 +- layout/style/Loader.cpp | 6 +- netwerk/base/nsSocketTransportService2.cpp | 3 +- netwerk/cache2/CacheIOThread.cpp | 4 +- widget/ScreenProxy.cpp | 13 +- widget/cocoa/nsAppShell.h | 4 +- widget/cocoa/nsAppShell.mm | 9 +- widget/nsBaseAppShell.cpp | 121 +---------------- widget/nsBaseAppShell.h | 37 +---- widget/nsIAppShell.idl | 24 +--- widget/nsScreenManagerProxy.cpp | 13 +- widget/tests/moz.build | 4 - xpcom/base/CycleCollectedJSRuntime.cpp | 127 ++++++++++++++++++ xpcom/base/CycleCollectedJSRuntime.h | 33 ++++- xpcom/threads/LazyIdleThread.cpp | 4 +- xpcom/threads/nsIThreadInternal.idl | 25 +--- xpcom/threads/nsThread.cpp | 64 ++++----- xpcom/threads/nsThread.h | 14 +- 64 files changed, 626 insertions(+), 763 deletions(-) create mode 100644 gfx/tests/reftest/1143303-1.svg create mode 100644 gfx/tests/reftest/853889-1-ref.html create mode 100644 gfx/tests/reftest/853889-1.html create mode 100644 gfx/tests/reftest/pass.svg diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 93e9bb8e2d..fd9bcea213 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -5195,16 +5195,18 @@ nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) /* static */ void -nsContentUtils::RunInStableState(already_AddRefed aRunnable, - DispatchFailureHandling aHandling) +nsContentUtils::RunInStableState(already_AddRefed aRunnable) { - nsCOMPtr runnable = aRunnable; - nsCOMPtr 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 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 ac78f2cab3..bf6cc096a1 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -1649,12 +1649,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 @@ -1666,9 +1660,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 aRunnable, - DispatchFailureHandling aHandling = - DispatchFailureHandling::AssertSuccess); + static void RunInStableState(already_AddRefed 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 aRunnable); /** * Retrieve information about the viewport as a data structure. diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 551e10a71d..6fa59eb8f6 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" @@ -118,8 +117,6 @@ using namespace mozilla::gfx; class gfxContext; -static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); - NS_INTERFACE_MAP_BEGIN(nsDOMWindowUtils) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMWindowUtils) NS_INTERFACE_MAP_ENTRY(nsIDOMWindowUtils) @@ -3489,30 +3486,6 @@ nsDOMWindowUtils::DispatchEventToChromeOnly(nsIDOMEventTarget* aTarget, return NS_OK; } -NS_IMETHODIMP -nsDOMWindowUtils::RunInStableState(nsIRunnable *aRunnable) -{ - MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); - - nsCOMPtr runnable = aRunnable; - nsContentUtils::RunInStableState(runnable.forget()); - - return NS_OK; -} - -NS_IMETHODIMP -nsDOMWindowUtils::RunBeforeNextEvent(nsIRunnable *runnable) -{ - MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); - - nsCOMPtr 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/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 20f0f1a396..5f851051d9 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -487,7 +487,7 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext, NS_WARN_IF_FALSE(!aTargetFrame || !aTargetFrame->GetContent() || aTargetFrame->GetContent() == aTargetContent || - aTargetFrame->GetContent()->GetParent() == aTargetContent, + aTargetFrame->GetContent()->GetFlattenedTreeParent() == aTargetContent, "aTargetFrame should be related with aTargetContent"); #endif diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index 059df36e09..dcf0142b93 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -25449,15 +25449,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 702c70e252..36564214ff 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 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 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 d8d5aa3fd6..9aba23ebee 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 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 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 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 0e66237561..dfc83e539d 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 { /** @@ -1715,32 +1715,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 4b450b4590..a82d2660cf 100644 --- a/dom/media/webaudio/AudioDestinationNode.cpp +++ b/dom/media/webaudio/AudioDestinationNode.cpp @@ -600,9 +600,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 5343613956..117fb01775 100644 --- a/dom/promise/Promise.cpp +++ b/dom/promise/Promise.cpp @@ -508,6 +508,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 4dc81e4233..af185efcda 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 f6a40a7bb4..ca95a1e010 100644 --- a/dom/storage/DOMStorageDBThread.cpp +++ b/dom/storage/DOMStorageDBThread.cpp @@ -373,15 +373,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 fa856bd476..e657d2a3e3 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -975,6 +975,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 f45bd82ba6..8e9ab0665c 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -2750,22 +2750,6 @@ WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget) { } -struct WorkerPrivate::PreemptingRunnableInfo final -{ - nsCOMPtr mRunnable; - uint32_t mRecursionDepth; - - PreemptingRunnableInfo() - { - MOZ_COUNT_CTOR(WorkerPrivate::PreemptingRunnableInfo); - } - - ~PreemptingRunnableInfo() - { - MOZ_COUNT_DTOR(WorkerPrivate::PreemptingRunnableInfo); - } -}; - template nsIDocument* WorkerPrivateParent::GetDocument() const @@ -5303,8 +5287,6 @@ WorkerPrivate::DoRunLoop(JSContext* aCx) } } - mPreemptingRunnableInfos.Clear(); - // Clear away our MessagePorts. mWorkerPorts.Clear(); @@ -5349,10 +5331,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. @@ -5372,85 +5350,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 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 = 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 4a057c518d..877bcc8529 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -948,9 +948,6 @@ class WorkerPrivate : public WorkerPrivateParent // modifications are done with mMutex held *only* in DEBUG builds. nsTArray> mSyncLoopStack; - struct PreemptingRunnableInfo; - nsTArray mPreemptingRunnableInfos; - nsCOMPtr mTimer; nsCOMPtr mGCTimer; @@ -1340,10 +1337,10 @@ public: ClearMainEventQueue(WorkerRanOrNot aRanOrNot); void - OnProcessNextEvent(uint32_t aRecursionDepth); + OnProcessNextEvent(); void - AfterProcessNextEvent(uint32_t aRecursionDepth); + AfterProcessNextEvent(); void AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget) @@ -1370,11 +1367,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 29fa26556d..89ee469942 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 b4e6d7b23c..329c54f06b 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/gfx/2d/PathD2D.cpp b/gfx/2d/PathD2D.cpp index 8cb802b391..e442d113a0 100644 --- a/gfx/2d/PathD2D.cpp +++ b/gfx/2d/PathD2D.cpp @@ -224,18 +224,36 @@ PathBuilderD2D::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle, // beginning and an end point. This means the circle will be the wrong way // around if the start angle is smaller than the end angle. It might seem // tempting to invert aAntiClockwise but that would change the sweeping - // direction of the arc to instead we exchange start/begin. + // direction of the arc so instead we exchange start/begin. Float oldStart = aStartAngle; aStartAngle = aEndAngle; aEndAngle = oldStart; } + const Float kSmallRadius = 0.007f; + Float midAngle = 0; + bool smallFullCircle = false; + // XXX - Workaround for now, D2D does not appear to do the desired thing when // the angle sweeps a complete circle. if (aEndAngle - aStartAngle >= 2 * M_PI) { - aEndAngle = Float(aStartAngle + M_PI * 1.9999); + if (aRadius > kSmallRadius) { + aEndAngle = Float(aStartAngle + M_PI * 1.9999); + } + else { + smallFullCircle = true; + midAngle = Float(aStartAngle + M_PI); + aEndAngle = Float(aStartAngle + 2 * M_PI); + } } else if (aStartAngle - aEndAngle >= 2 * M_PI) { - aStartAngle = Float(aEndAngle + M_PI * 1.9999); + if (aRadius > kSmallRadius) { + aStartAngle = Float(aEndAngle + M_PI * 1.9999); + } + else { + smallFullCircle = true; + midAngle = Float(aEndAngle + M_PI); + aStartAngle = Float(aEndAngle + 2 * M_PI); + } } Point startPoint; @@ -253,23 +271,46 @@ PathBuilderD2D::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle, endPoint.y = aOrigin.y + aRadius * sin(aEndAngle); D2D1_ARC_SIZE arcSize = D2D1_ARC_SIZE_SMALL; + D2D1_SWEEP_DIRECTION direction = + aAntiClockwise ? D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE : + D2D1_SWEEP_DIRECTION_CLOCKWISE; - if (aAntiClockwise) { - if (aStartAngle - aEndAngle > M_PI) { - arcSize = D2D1_ARC_SIZE_LARGE; - } - } else { - if (aEndAngle - aStartAngle > M_PI) { - arcSize = D2D1_ARC_SIZE_LARGE; + if (!smallFullCircle) { + + if (aAntiClockwise) { + if (aStartAngle - aEndAngle > M_PI) { + arcSize = D2D1_ARC_SIZE_LARGE; + } + } else { + if (aEndAngle - aStartAngle > M_PI) { + arcSize = D2D1_ARC_SIZE_LARGE; + } } + + mSink->AddArc(D2D1::ArcSegment(D2DPoint(endPoint), + D2D1::SizeF(aRadius, aRadius), + 0.0f, + direction, + arcSize)); } + else { + // draw small circles as two half-circles + Point midPoint; + midPoint.x = aOrigin.x + aRadius * cos(midAngle); + midPoint.y = aOrigin.y + aRadius * sin(midAngle); - mSink->AddArc(D2D1::ArcSegment(D2DPoint(endPoint), - D2D1::SizeF(aRadius, aRadius), - 0.0f, - aAntiClockwise ? D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE : - D2D1_SWEEP_DIRECTION_CLOCKWISE, - arcSize)); + mSink->AddArc(D2D1::ArcSegment(D2DPoint(midPoint), + D2D1::SizeF(aRadius, aRadius), + 0.0f, + direction, + arcSize)); + + mSink->AddArc(D2D1::ArcSegment(D2DPoint(endPoint), + D2D1::SizeF(aRadius, aRadius), + 0.0f, + direction, + arcSize)); + } mCurrentPoint = endPoint; } diff --git a/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c b/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c index 38933cc34b..8c1d54f0c7 100644 --- a/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c +++ b/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c @@ -674,11 +674,20 @@ _cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps, cairo_status_t status; int i; - if (unlikely (traps->num_traps <= 1)) - return CAIRO_STATUS_SUCCESS; - assert (traps->is_rectangular); + if (unlikely (traps->num_traps <= 1)) { + if (traps->num_traps == 1) { + cairo_trapezoid_t *trap = traps->traps; + if (trap->left.p1.x > trap->right.p1.x) { + cairo_line_t tmp = trap->left; + trap->left = trap->right; + trap->right = tmp; + } + } + return CAIRO_STATUS_SUCCESS; + } + dump_traps (traps, "bo-rects-traps-in.txt"); rectangles = stack_rectangles; @@ -746,8 +755,35 @@ _cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, cairo_status_t status; int i, j; - if (unlikely (in->num_boxes <= 1)) + if (unlikely (in->num_boxes == 0)) { + _cairo_boxes_clear (out); return CAIRO_STATUS_SUCCESS; + } + + if (in->num_boxes == 1) { + if (in == out) { + cairo_box_t *box = &in->chunks.base[0]; + + if (box->p1.x > box->p2.x) { + cairo_fixed_t tmp = box->p1.x; + box->p1.x = box->p2.x; + box->p2.x = tmp; + } + } else { + cairo_box_t box = in->chunks.base[0]; + + if (box.p1.x > box.p2.x) { + cairo_fixed_t tmp = box.p1.x; + box.p1.x = box.p2.x; + box.p2.x = tmp; + } + + _cairo_boxes_clear (out); + status = _cairo_boxes_add (out, &box); + assert (status == CAIRO_STATUS_SUCCESS); + } + return CAIRO_STATUS_SUCCESS; + } rectangles = stack_rectangles; rectangles_ptrs = stack_rectangles_ptrs; diff --git a/gfx/tests/reftest/1143303-1.svg b/gfx/tests/reftest/1143303-1.svg new file mode 100644 index 0000000000..0ef50874cf --- /dev/null +++ b/gfx/tests/reftest/1143303-1.svg @@ -0,0 +1,27 @@ + + + + Testcase for small circles + + + + + + + + + + + + + + + + + + + diff --git a/gfx/tests/reftest/853889-1-ref.html b/gfx/tests/reftest/853889-1-ref.html new file mode 100644 index 0000000000..1a8513dedf --- /dev/null +++ b/gfx/tests/reftest/853889-1-ref.html @@ -0,0 +1,12 @@ + + + Testcase for bug 853889 + + + + + + diff --git a/gfx/tests/reftest/853889-1.html b/gfx/tests/reftest/853889-1.html new file mode 100644 index 0000000000..2b728c2975 --- /dev/null +++ b/gfx/tests/reftest/853889-1.html @@ -0,0 +1,19 @@ + + + Testcase for bug 853889 + + + + + + + + + + diff --git a/gfx/tests/reftest/pass.svg b/gfx/tests/reftest/pass.svg new file mode 100644 index 0000000000..c09c6601e8 --- /dev/null +++ b/gfx/tests/reftest/pass.svg @@ -0,0 +1,8 @@ + + + Testcase reference file for generic pass condition + + diff --git a/gfx/tests/reftest/reftest.list b/gfx/tests/reftest/reftest.list index 1be25e6434..2f53804ce6 100644 --- a/gfx/tests/reftest/reftest.list +++ b/gfx/tests/reftest/reftest.list @@ -3,3 +3,5 @@ fuzzy-if(winWidget,175,443) == 611498-1.html 611498-ref.html skip-if(B2G) fuzzy-if(Android&&AndroidVersion>=15,8,1000) == 709477-1.html 709477-1-ref.html # bug 773482 skip-if(!asyncPanZoom) == 1086723.html 1086723-ref.html +== 853889-1.html 853889-1-ref.html +== 1143303-1.svg pass.svg diff --git a/image/DynamicImage.cpp b/image/DynamicImage.cpp index 1515dfd88a..5053dce1d6 100644 --- a/image/DynamicImage.cpp +++ b/image/DynamicImage.cpp @@ -24,12 +24,6 @@ namespace image { // Inherited methods from Image. -nsresult -DynamicImage::Init(const char* aMimeType, uint32_t aFlags) -{ - return NS_OK; -} - already_AddRefed DynamicImage::GetProgressTracker() { diff --git a/image/DynamicImage.h b/image/DynamicImage.h index 98ca88c922..ac1f1796d5 100644 --- a/image/DynamicImage.h +++ b/image/DynamicImage.h @@ -31,8 +31,6 @@ public: } // Inherited methods from Image. - virtual nsresult Init(const char* aMimeType, uint32_t aFlags) override; - virtual already_AddRefed GetProgressTracker() override; virtual size_t SizeOfSourceWithComputedFallback( MallocSizeOf aMallocSizeOf) const override; diff --git a/image/Image.cpp b/image/Image.cpp index 5431aed63d..e023926d68 100644 --- a/image/Image.cpp +++ b/image/Image.cpp @@ -23,6 +23,12 @@ ImageResource::ImageResource(ImageURL* aURI) : mError(false) { } +ImageResource::~ImageResource() +{ + // Ask our ProgressTracker to drop its weak reference to us. + mProgressTracker->ResetImage(); +} + // Translates a mimetype into a concrete decoder Image::eDecoderType Image::GetDecoderType(const char* aMimeType) diff --git a/image/Image.h b/image/Image.h index fbc6b19b72..2a01d16d9d 100644 --- a/image/Image.h +++ b/image/Image.h @@ -74,15 +74,6 @@ public: static const uint32_t INIT_FLAG_DOWNSCALE_DURING_DECODE = 0x10; static const uint32_t INIT_FLAG_SYNC_LOAD = 0x20; - /** - * Creates a new image container. - * - * @param aMimeType The mimetype of the image. - * @param aFlags Initialization flags of the INIT_FLAG_* variety. - */ - virtual nsresult Init(const char* aMimeType, - uint32_t aFlags) = 0; - virtual already_AddRefed GetProgressTracker() = 0; virtual void SetProgressTracker(ProgressTracker* aProgressTracker) {} @@ -197,6 +188,7 @@ public: protected: explicit ImageResource(ImageURL* aURI); + ~ImageResource(); // Shared functionality for implementors of imgIContainer. Every // implementation of attribute animationMode should forward here. diff --git a/image/ImageFactory.cpp b/image/ImageFactory.cpp index 6e6b2365ef..5002fceb5b 100644 --- a/image/ImageFactory.cpp +++ b/image/ImageFactory.cpp @@ -14,6 +14,7 @@ #include "nsMimeTypes.h" #include "nsIRequest.h" +#include "MultipartImage.h" #include "RasterImage.h" #include "VectorImage.h" #include "Image.h" @@ -154,12 +155,32 @@ ImageFactory::CreateAnonymousImage(const nsCString& aMimeType) nsRefPtr newImage = new RasterImage(); + nsRefPtr newTracker = new ProgressTracker(); + newTracker->SetImage(newImage); + newImage->SetProgressTracker(newTracker); + rv = newImage->Init(aMimeType.get(), Image::INIT_FLAG_SYNC_LOAD); NS_ENSURE_SUCCESS(rv, BadImage(newImage)); return newImage.forget(); } +/* static */ already_AddRefed +ImageFactory::CreateMultipartImage(Image* aFirstPart, + ProgressTracker* aProgressTracker) +{ + MOZ_ASSERT(aFirstPart); + MOZ_ASSERT(aProgressTracker); + + nsRefPtr newImage = new MultipartImage(aFirstPart); + aProgressTracker->SetImage(newImage); + newImage->SetProgressTracker(aProgressTracker); + + newImage->Init(); + + return newImage.forget(); +} + int32_t SaturateToInt32(int64_t val) { @@ -211,9 +232,13 @@ ImageFactory::CreateRasterImage(nsIRequest* aRequest, uint32_t aImageFlags, uint32_t aInnerWindowId) { + MOZ_ASSERT(aProgressTracker); + nsresult rv; - nsRefPtr newImage = new RasterImage(aProgressTracker, aURI); + nsRefPtr newImage = new RasterImage(aURI); + aProgressTracker->SetImage(newImage); + newImage->SetProgressTracker(aProgressTracker); rv = newImage->Init(aMimeType.get(), aImageFlags); NS_ENSURE_SUCCESS(rv, BadImage(newImage)); @@ -273,9 +298,13 @@ ImageFactory::CreateVectorImage(nsIRequest* aRequest, uint32_t aImageFlags, uint32_t aInnerWindowId) { + MOZ_ASSERT(aProgressTracker); + nsresult rv; - nsRefPtr newImage = new VectorImage(aProgressTracker, aURI); + nsRefPtr newImage = new VectorImage(aURI); + aProgressTracker->SetImage(newImage); + newImage->SetProgressTracker(aProgressTracker); rv = newImage->Init(aMimeType.get(), aImageFlags); NS_ENSURE_SUCCESS(rv, BadImage(newImage)); diff --git a/image/ImageFactory.h b/image/ImageFactory.h index 25e15c29e6..6c2e0f5045 100644 --- a/image/ImageFactory.h +++ b/image/ImageFactory.h @@ -18,6 +18,7 @@ namespace image { class Image; class ImageURL; +class MultipartImage; class ProgressTracker; class ImageFactory @@ -54,6 +55,18 @@ public: static already_AddRefed CreateAnonymousImage(const nsCString& aMimeType); + /** + * Creates a new multipart/x-mixed-replace image wrapper, and initializes it + * with the first part. Subsequent parts should be passed to the existing + * MultipartImage via MultipartImage::BeginTransitionToPart(). + * + * @param aFirstPart An image containing the first part of the multipart + * stream. + * @param aProgressTracker A progress tracker for the multipart image. + */ + static already_AddRefed + CreateMultipartImage(Image* aFirstPart, ProgressTracker* aProgressTracker); + private: // Factory functions that create specific types of image containers. static already_AddRefed diff --git a/image/ImageWrapper.cpp b/image/ImageWrapper.cpp index 7056e6f9e7..c14e931e9b 100644 --- a/image/ImageWrapper.cpp +++ b/image/ImageWrapper.cpp @@ -21,12 +21,6 @@ namespace image { // Inherited methods from Image. -nsresult -ImageWrapper::Init(const char* aMimeType, uint32_t aFlags) -{ - return mInnerImage->Init(aMimeType, aFlags); -} - already_AddRefed ImageWrapper::GetProgressTracker() { diff --git a/image/ImageWrapper.h b/image/ImageWrapper.h index 5a5cf84fd6..22abf4d52d 100644 --- a/image/ImageWrapper.h +++ b/image/ImageWrapper.h @@ -22,8 +22,6 @@ public: NS_DECL_IMGICONTAINER // Inherited methods from Image. - virtual nsresult Init(const char* aMimeType, uint32_t aFlags) override; - virtual already_AddRefed GetProgressTracker() override; virtual size_t diff --git a/image/MultipartImage.cpp b/image/MultipartImage.cpp index ddfb6b7d5a..0a6b89d3d9 100644 --- a/image/MultipartImage.cpp +++ b/image/MultipartImage.cpp @@ -103,13 +103,18 @@ private: // Implementation /////////////////////////////////////////////////////////////////////////////// -MultipartImage::MultipartImage(Image* aImage, ProgressTracker* aTracker) - : ImageWrapper(aImage) +MultipartImage::MultipartImage(Image* aFirstPart) + : ImageWrapper(aFirstPart) , mDeferNotifications(false) { - MOZ_ASSERT(aTracker); - mProgressTrackerInit = new ProgressTrackerInit(this, aTracker); mNextPartObserver = new NextPartObserver(this); +} + +void +MultipartImage::Init() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mTracker, "Should've called SetProgressTracker() by now"); // Start observing the first part. nsRefPtr firstPartTracker = @@ -119,7 +124,11 @@ MultipartImage::MultipartImage(Image* aImage, ProgressTracker* aTracker) InnerImage()->IncrementAnimationConsumers(); } -MultipartImage::~MultipartImage() { } +MultipartImage::~MultipartImage() +{ + // Ask our ProgressTracker to drop its weak reference to us. + mTracker->ResetImage(); +} NS_IMPL_QUERY_INTERFACE_INHERITED0(MultipartImage, ImageWrapper) NS_IMPL_ADDREF(MultipartImage) diff --git a/image/MultipartImage.h b/image/MultipartImage.h index 6e8bbc347b..6c45c10755 100644 --- a/image/MultipartImage.h +++ b/image/MultipartImage.h @@ -27,8 +27,6 @@ public: MOZ_DECLARE_REFCOUNTED_TYPENAME(MultipartImage) NS_DECL_ISUPPORTS - MultipartImage(Image* aImage, ProgressTracker* aTracker); - void BeginTransitionToPart(Image* aNextPart); // Overridden ImageWrapper methods: @@ -73,12 +71,15 @@ protected: virtual ~MultipartImage(); private: + friend class ImageFactory; friend class NextPartObserver; + explicit MultipartImage(Image* aFirstPart); + void Init(); + void FinishTransition(); nsRefPtr mTracker; - nsAutoPtr mProgressTrackerInit; nsRefPtr mNextPartObserver; nsRefPtr mNextPart; bool mDeferNotifications : 1; diff --git a/image/ProgressTracker.cpp b/image/ProgressTracker.cpp index aa4fc9adfa..360761a1e7 100644 --- a/image/ProgressTracker.cpp +++ b/image/ProgressTracker.cpp @@ -22,26 +22,6 @@ using mozilla::WeakPtr; namespace mozilla { namespace image { -ProgressTrackerInit::ProgressTrackerInit(Image* aImage, - ProgressTracker* aTracker) -{ - MOZ_ASSERT(aImage); - - if (aTracker) { - mTracker = aTracker; - } else { - mTracker = new ProgressTracker(); - } - mTracker->SetImage(aImage); - aImage->SetProgressTracker(mTracker); - MOZ_ASSERT(mTracker); -} - -ProgressTrackerInit::~ProgressTrackerInit() -{ - mTracker->ResetImage(); -} - static void CheckProgressConsistency(Progress aProgress) { diff --git a/image/ProgressTracker.h b/image/ProgressTracker.h index d3b0713578..e65fdfa605 100644 --- a/image/ProgressTracker.h +++ b/image/ProgressTracker.h @@ -161,22 +161,21 @@ public: // probably be improved, but it's too scary to mess with at the moment. bool FirstObserverIs(IProgressObserver* aObserver); + // Resets our weak reference to our image. Image subclasses should call this + // in their destructor. + void ResetImage(); + private: typedef nsTObserverArray> ObserverArray; friend class AsyncNotifyRunnable; friend class AsyncNotifyCurrentStateRunnable; - friend class ProgressTrackerInit; + friend class ImageFactory; ProgressTracker(const ProgressTracker& aOther) = delete; - // This method should only be called once, and only on an ProgressTracker - // that was initialized without an image. ProgressTrackerInit automates this. + // Sets our weak reference to our image. Only ImageFactory should call this. void SetImage(Image* aImage); - // Resets our weak reference to our image, for when mImage is about to go out - // of scope. ProgressTrackerInit automates this. - void ResetImage(); - // Send some notifications that would be necessary to make |aObserver| believe // the request is finished downloading and decoding. We only send // FLAG_LOAD_COMPLETE and FLAG_ONLOAD_UNBLOCKED, and only if necessary. @@ -206,15 +205,6 @@ private: Progress mProgress; }; -class ProgressTrackerInit -{ -public: - ProgressTrackerInit(Image* aImage, ProgressTracker* aTracker); - ~ProgressTrackerInit(); -private: - ProgressTracker* mTracker; -}; - } // namespace image } // namespace mozilla diff --git a/image/RasterImage.cpp b/image/RasterImage.cpp index e08319835d..8190e02e1b 100644 --- a/image/RasterImage.cpp +++ b/image/RasterImage.cpp @@ -254,8 +254,7 @@ NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties, #endif //****************************************************************************** -RasterImage::RasterImage(ProgressTracker* aProgressTracker, - ImageURL* aURI /* = nullptr */) : +RasterImage::RasterImage(ImageURL* aURI /* = nullptr */) : ImageResource(aURI), // invoke superclass's constructor mSize(0,0), mLockCount(0), @@ -279,8 +278,6 @@ RasterImage::RasterImage(ProgressTracker* aProgressTracker, mAnimationFinished(false), mWantFullDecode(false) { - mProgressTrackerInit = new ProgressTrackerInit(this, aProgressTracker); - Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(0); } diff --git a/image/RasterImage.h b/image/RasterImage.h index 7e7c7481c0..fc675250b9 100644 --- a/image/RasterImage.h +++ b/image/RasterImage.h @@ -166,9 +166,6 @@ public: virtual nsresult StopAnimation() override; // Methods inherited from Image - nsresult Init(const char* aMimeType, - uint32_t aFlags) override; - virtual void OnSurfaceDiscarded() override; // Raster-specific methods @@ -289,6 +286,8 @@ public: } private: + nsresult Init(const char* aMimeType, uint32_t aFlags); + DrawResult DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef, gfxContext* aContext, const nsIntSize& aSize, @@ -430,9 +429,6 @@ private: // data TimeStamp mDrawStartTime; - // Initializes ProgressTracker and resets it on RasterImage destruction. - nsAutoPtr mProgressTrackerInit; - ////////////////////////////////////////////////////////////////////////////// // Scaling. @@ -480,8 +476,7 @@ private: // data bool CanDiscard(); protected: - explicit RasterImage(ProgressTracker* aProgressTracker = nullptr, - ImageURL* aURI = nullptr); + explicit RasterImage(ImageURL* aURI = nullptr); bool ShouldAnimate() override; diff --git a/image/VectorImage.cpp b/image/VectorImage.cpp index 5aa26b603c..f599ddb1b9 100644 --- a/image/VectorImage.cpp +++ b/image/VectorImage.cpp @@ -328,8 +328,7 @@ NS_IMPL_ISUPPORTS(VectorImage, //------------------------------------------------------------------------------ // Constructor / Destructor -VectorImage::VectorImage(ProgressTracker* aProgressTracker, - ImageURL* aURI /* = nullptr */) : +VectorImage::VectorImage(ImageURL* aURI /* = nullptr */) : ImageResource(aURI), // invoke superclass's constructor mLockCount(0), mIsInitialized(false), @@ -337,9 +336,7 @@ VectorImage::VectorImage(ProgressTracker* aProgressTracker, mIsDrawing(false), mHaveAnimations(false), mHasPendingInvalidation(false) -{ - mProgressTrackerInit = new ProgressTrackerInit(this, aProgressTracker); -} +{ } VectorImage::~VectorImage() { diff --git a/image/VectorImage.h b/image/VectorImage.h index 053e5d7678..891989b51f 100644 --- a/image/VectorImage.h +++ b/image/VectorImage.h @@ -34,9 +34,6 @@ public: // (no public constructor - use ImageFactory) // Methods inherited from Image - nsresult Init(const char* aMimeType, - uint32_t aFlags) override; - virtual size_t SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const override; virtual size_t SizeOfDecoded(gfxMemoryLocation aLocation, @@ -72,8 +69,7 @@ public: void OnSVGDocumentError(); protected: - explicit VectorImage(ProgressTracker* aProgressTracker = nullptr, - ImageURL* aURI = nullptr); + explicit VectorImage(ImageURL* aURI = nullptr); virtual ~VectorImage(); virtual nsresult StartAnimation() override; @@ -84,6 +80,8 @@ protected: void Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams); private: + nsresult Init(const char* aMimeType, uint32_t aFlags); + /** * In catastrophic circumstances like a GPU driver crash, we may lose our * surfaces even if they're locked. RecoverFromLossOfSurfaces discards all @@ -112,9 +110,6 @@ private: bool mHasPendingInvalidation; // Invalidate observers next refresh // driver tick. - // Initializes ProgressTracker and resets it on RasterImage destruction. - nsAutoPtr mProgressTrackerInit; - friend class ImageFactory; }; diff --git a/image/imgRequest.cpp b/image/imgRequest.cpp index 481e12b9a9..e6f12a97ba 100644 --- a/image/imgRequest.cpp +++ b/image/imgRequest.cpp @@ -977,7 +977,8 @@ PrepareForNewPart(nsIRequest* aRequest, nsIInputStream* aInStr, uint32_t aCount, if (result.mIsFirstPart) { // First part for a multipart channel. Create the MultipartImage wrapper. MOZ_ASSERT(aProgressTracker, "Shouldn't have given away tracker yet"); - result.mImage = new MultipartImage(partImage, aProgressTracker); + result.mImage = + ImageFactory::CreateMultipartImage(partImage, aProgressTracker); } else { // Transition to the new part. auto multipartImage = static_cast(aExistingImage); diff --git a/image/test/mochitest/test_synchronized_animation.html b/image/test/mochitest/test_synchronized_animation.html index f537d490ef..53e709fb95 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 45fe96d81b..0ff08881d0 100644 --- a/ipc/glue/MessagePump.cpp +++ b/ipc/glue/MessagePump.cpp @@ -439,15 +439,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 fc1b54624b..3549bfb825 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -3456,6 +3456,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 9e7a2ef803..a56e30587c 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); } @@ -949,81 +938,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 b0f3bac75a..b162b16919 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" @@ -246,14 +245,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: @@ -330,13 +327,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: @@ -495,6 +485,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 @@ -621,16 +614,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; - } - private: XPCJSRuntime(); // no implementation explicit XPCJSRuntime(nsXPConnect* aXPConnect); diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index d4681ed4f4..29d8807cef 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -7729,7 +7729,7 @@ PresShell::HandlePositionedEvent(nsIFrame* aTargetFrame, // We use weak pointers because during this tight loop, the node // will *not* go away. And this happens on every mousemove. while (targetElement && !targetElement->IsElement()) { - targetElement = targetElement->GetParent(); + targetElement = targetElement->GetFlattenedTreeParent(); } // If we found an element, target it. Otherwise, target *nothing*. diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index ba8e1ecb7d..303a164dc2 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 f379d96c2c..95e6475253 100644 --- a/netwerk/base/nsSocketTransportService2.cpp +++ b/netwerk/base/nsSocketTransportService2.cpp @@ -709,14 +709,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 72ecbc0a50..856ce2dd1b 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 936841431f..6481e85d9f 100644 --- a/widget/ScreenProxy.cpp +++ b/widget/ScreenProxy.cpp @@ -169,16 +169,9 @@ ScreenProxy::InvalidateCacheOnNextTick() mCacheWillInvalidate = true; - nsCOMPtr appShell = do_GetService(kAppShellCID); - if (appShell) { - nsCOMPtr 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 r = + NS_NewRunnableMethod(this, &ScreenProxy::InvalidateCache); + nsContentUtils::RunInStableState(r.forget()); } void diff --git a/widget/cocoa/nsAppShell.h b/widget/cocoa/nsAppShell.h index bf5b06a494..b7836b6391 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 c89cee92b0..71f5d28b0c 100644 --- a/widget/cocoa/nsAppShell.mm +++ b/widget/cocoa/nsAppShell.mm @@ -719,8 +719,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; @@ -730,7 +729,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; } @@ -744,7 +743,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; @@ -759,8 +757,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 2bd7b92e1f..c74ada402a 100644 --- a/widget/nsBaseAppShell.cpp +++ b/widget/nsBaseAppShell.cpp @@ -28,7 +28,6 @@ nsBaseAppShell::nsBaseAppShell() , mSwitchTime(0) , mLastNativeEventTime(0) , mEventloopNestingState(eEventloopNone) - , mRunningSyncSections(false) , mRunning(false) , mExiting(false) , mBlockNativeEvent(false) @@ -37,7 +36,6 @@ nsBaseAppShell::nsBaseAppShell() nsBaseAppShell::~nsBaseAppShell() { - NS_ASSERTION(mSyncSections.IsEmpty(), "Must have run all sync sections"); } nsresult @@ -117,7 +115,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 @@ -134,14 +132,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; @@ -236,8 +227,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) @@ -275,13 +265,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); } } @@ -293,7 +283,7 @@ nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait, mayWait = false; mLastNativeEventTime = PR_IntervalNow(); - if (!DoProcessNextNativeEvent(mayWait, recursionDepth) || !mayWait) + if (!DoProcessNextNativeEvent(mayWait) || !mayWait) break; } @@ -306,9 +296,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; } @@ -335,95 +322,11 @@ nsBaseAppShell::DecrementEventloopNestingLevel() --mEventloopNestingLevel; } -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 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 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 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; } @@ -435,17 +338,3 @@ nsBaseAppShell::Observe(nsISupports *subject, const char *topic, Exit(); return NS_OK; } - -void -nsBaseAppShell::RunInStableState(already_AddRefed aRunnable) -{ - ScheduleSyncSection(mozilla::Move(aRunnable), true); -} - -NS_IMETHODIMP -nsBaseAppShell::RunBeforeNextEvent(nsIRunnable* aRunnable) -{ - nsCOMPtr runnable = aRunnable; - ScheduleSyncSection(runnable.forget(), false); - return NS_OK; -} diff --git a/widget/nsBaseAppShell.h b/widget/nsBaseAppShell.h index 09f8e47b71..4ee9e3d2e7 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 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 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 mRunnable; - }; - nsCOMPtr 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 mSyncSections; - bool mRunningSyncSections; bool mRunning; bool mExiting; /** diff --git a/widget/nsIAppShell.idl b/widget/nsIAppShell.idl index bb089d20ae..c21c4d107b 100644 --- a/widget/nsIAppShell.idl +++ b/widget/nsIAppShell.idl @@ -15,7 +15,7 @@ template 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 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 b768a53091..956529971a 100644 --- a/widget/nsScreenManagerProxy.cpp +++ b/widget/nsScreenManagerProxy.cpp @@ -198,16 +198,9 @@ nsScreenManagerProxy::InvalidateCacheOnNextTick() mCacheWillInvalidate = true; - nsCOMPtr appShell = do_GetService(kAppShellCID); - if (appShell) { - nsCOMPtr 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 r = + NS_NewRunnableMethod(this, &nsScreenManagerProxy::InvalidateCache); + nsContentUtils::RunInStableState(r.forget()); } void diff --git a/widget/tests/moz.build b/widget/tests/moz.build index 8fd2991857..698f455f8b 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 ed76827481..42629af075 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -62,6 +62,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" @@ -76,6 +77,7 @@ #endif #include "nsIException.h" +#include "nsThread.h" #include "nsThreadUtils.h" #include "xpcpublic.h" @@ -401,9 +403,18 @@ CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSRuntime* aParentRuntime, , mJSRuntime(nullptr) , mPrevGCSliceCallback(nullptr) , mJSHolders(256) + , mDoingStableStates(false) , mOutOfMemoryState(OOMState::OK) , mLargeAllocationFailureState(OOMState::OK) { + nsCOMPtr thread = do_GetCurrentThread(); + mOwningThread = thread.forget().downcast().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); @@ -439,6 +450,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; @@ -447,6 +465,9 @@ CycleCollectedJSRuntime::~CycleCollectedJSRuntime() nsCycleCollector_forgetJSRuntime(); mozilla::dom::DestroyScriptSettings(); + + mOwningThread->SetScriptObserver(nullptr); + NS_RELEASE(mOwningThread); } size_t @@ -1010,6 +1031,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 event = mStableStateEvents[i].forget(); + event->Run(); + } + + mStableStateEvents.Clear(); + mDoingStableStates = false; +} + +void +CycleCollectedJSRuntime::ProcessMetastableStateQueue(uint32_t aRecursionDepth) +{ + MOZ_RELEASE_ASSERT(!mDoingStableStates); + mDoingStableStates = true; + + nsTArray localQueue = Move(mMetastableStateEvents); + + for (uint32_t i = 0; i < localQueue.Length(); ++i) + { + RunInMetastableStateData& data = localQueue[i]; + if (data.mRecursionDepth != aRecursionDepth) { + continue; + } + + { + nsCOMPtr 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&& aRunnable) +{ + MOZ_ASSERT(mJSRuntime); + mStableStateEvents.AppendElement(Move(aRunnable)); +} + +void +CycleCollectedJSRuntime::RunInMetastableState(already_AddRefed&& 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 dfe8997619..d48ae4e8b5 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&& 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&& aRunnable); + // Get the current thread's CycleCollectedJSRuntime. Returns null if there // isn't one. static CycleCollectedJSRuntime* Get(); @@ -325,9 +344,21 @@ private: nsRefPtr mFinalizeRunnable; nsCOMPtr mPendingException; + nsThread* mOwningThread; // Manual refcounting to avoid include hell. std::queue> mPromiseMicroTaskQueue; + struct RunInMetastableStateData + { + nsCOMPtr mRunnable; + uint32_t mRecursionDepth; + }; + + nsTArray> mStableStateEvents; + nsTArray mMetastableStateEvents; + uint32_t mBaseRecursionDepth; + bool mDoingStableStates; + OOMState mOutOfMemoryState; OOMState mLargeAllocationFailureState; }; diff --git a/xpcom/threads/LazyIdleThread.cpp b/xpcom/threads/LazyIdleThread.cpp index d7e1c8d347..1ed33ef036 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 1c2782e4ce..9287bf5d79 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 34266647bc..d0d6a4b599 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) @@ -89,8 +90,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. @@ -407,6 +406,7 @@ int sCanaryOutputFD = -1; nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize) : mLock("nsThread.mLock") + , mScriptObserver(nullptr) , mEvents(&mEventsRoot) , mPriority(PRIORITY_NORMAL) , mThread(nullptr) @@ -791,22 +791,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 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; @@ -839,20 +836,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; } @@ -929,15 +924,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 @@ -1036,19 +1027,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 7b972f0cf9..b42a939c07 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 mObserver; + mozilla::CycleCollectedJSRuntime* mScriptObserver; // Only accessed on the target thread. nsAutoTObserverArray, 2> mEventObservers;