diff --git a/image/IDecodingTask.cpp b/image/IDecodingTask.cpp index d92aca7cb778..8c598bf33a62 100644 --- a/image/IDecodingTask.cpp +++ b/image/IDecodingTask.cpp @@ -13,6 +13,8 @@ #include "RasterImage.h" #include "SurfaceCache.h" +#include "mozilla/SystemGroup.h" + namespace mozilla { using gfx::IntRect; @@ -23,11 +25,47 @@ namespace image { // Helpers for sending notifications to the image associated with a decoder. /////////////////////////////////////////////////////////////////////////////// -/* static */ void +void +IDecodingTask::EnsureHasEventTarget(NotNull aImage) +{ + if (!mEventTarget) { + // We determine the event target as late as possible, at the first dispatch + // time, because the observers bound to an imgRequest will affect it. + // We cache it rather than query for the event target each time because the + // event target can change. We don't want to risk events being executed in + // a different order than they are dispatched, which can happen if we + // selected scheduler groups which have no ordering guarantees relative to + // each other (e.g. it moves from scheduler group A for doc group DA to + // scheduler group B for doc group DB due to changing observers -- if we + // dispatched the first event on A, and the second on B, we don't know which + // will execute first.) + RefPtr tracker = aImage->GetProgressTracker(); + if (tracker) { + mEventTarget = tracker->GetEventTarget(); + } else { + mEventTarget = SystemGroup::EventTargetFor(TaskCategory::Other); + } + } +} + +bool +IDecodingTask::IsOnEventTarget() const +{ + // This is essentially equivalent to NS_IsOnMainThread() because all of the + // event targets are for the main thread (although perhaps with a different + // label / scheduler group). The observers in ProgressTracker may have + // different event targets from this, so this is just a best effort guess. + bool current = false; + mEventTarget->IsOnCurrentThread(¤t); + return current; +} + +void IDecodingTask::NotifyProgress(NotNull aImage, NotNull aDecoder) { MOZ_ASSERT(aDecoder->HasProgress() && !aDecoder->IsMetadataDecode()); + EnsureHasEventTarget(aImage); // Capture the decoder's state. If we need to notify asynchronously, it's // important that we don't wait until the lambda actually runs to capture the @@ -42,7 +80,7 @@ IDecodingTask::NotifyProgress(NotNull aImage, SurfaceFlags surfaceFlags = aDecoder->GetSurfaceFlags(); // Synchronously notify if we can. - if (NS_IsMainThread() && !(decoderFlags & DecoderFlags::ASYNC_NOTIFY)) { + if (IsOnEventTarget() && !(decoderFlags & DecoderFlags::ASYNC_NOTIFY)) { aImage->NotifyProgress(progress, invalidRect, frameCount, decoderFlags, surfaceFlags); return; @@ -50,20 +88,21 @@ IDecodingTask::NotifyProgress(NotNull aImage, // We're forced to notify asynchronously. NotNull> image = aImage; - NS_DispatchToMainThread(NS_NewRunnableFunction( - "IDecodingTask::NotifyProgress", - [=]() -> void { + mEventTarget->Dispatch(NS_NewRunnableFunction( + "IDecodingTask::NotifyProgress", + [=]() -> void { image->NotifyProgress(progress, invalidRect, frameCount, decoderFlags, surfaceFlags); - })); + }), NS_DISPATCH_NORMAL); } -/* static */ void +void IDecodingTask::NotifyDecodeComplete(NotNull aImage, NotNull aDecoder) { MOZ_ASSERT(aDecoder->HasError() || !aDecoder->InFrame(), "Decode complete in the middle of a frame?"); + EnsureHasEventTarget(aImage); // Capture the decoder's state. DecoderFinalStatus finalStatus = aDecoder->FinalStatus(); @@ -76,7 +115,7 @@ IDecodingTask::NotifyDecodeComplete(NotNull aImage, SurfaceFlags surfaceFlags = aDecoder->GetSurfaceFlags(); // Synchronously notify if we can. - if (NS_IsMainThread() && !(decoderFlags & DecoderFlags::ASYNC_NOTIFY)) { + if (IsOnEventTarget() && !(decoderFlags & DecoderFlags::ASYNC_NOTIFY)) { aImage->NotifyDecodeComplete(finalStatus, metadata, telemetry, progress, invalidRect, frameCount, decoderFlags, surfaceFlags); @@ -85,13 +124,13 @@ IDecodingTask::NotifyDecodeComplete(NotNull aImage, // We're forced to notify asynchronously. NotNull> image = aImage; - NS_DispatchToMainThread(NS_NewRunnableFunction( - "IDecodingTask::NotifyDecodeComplete", - [=]() -> void { + mEventTarget->Dispatch(NS_NewRunnableFunction( + "IDecodingTask::NotifyDecodeComplete", + [=]() -> void { image->NotifyDecodeComplete(finalStatus, metadata, telemetry, progress, invalidRect, frameCount, decoderFlags, surfaceFlags); - })); + }), NS_DISPATCH_NORMAL); } diff --git a/image/IDecodingTask.h b/image/IDecodingTask.h index 196ce5fdc44a..cf8daa462cad 100644 --- a/image/IDecodingTask.h +++ b/image/IDecodingTask.h @@ -50,15 +50,22 @@ public: void Resume() override; protected: + virtual ~IDecodingTask() { } + /// Notify @aImage of @aDecoder's progress. - static void NotifyProgress(NotNull aImage, - NotNull aDecoder); + void NotifyProgress(NotNull aImage, + NotNull aDecoder); /// Notify @aImage that @aDecoder has finished. - static void NotifyDecodeComplete(NotNull aImage, - NotNull aDecoder); + void NotifyDecodeComplete(NotNull aImage, + NotNull aDecoder); - virtual ~IDecodingTask() { } +private: + void EnsureHasEventTarget(NotNull aImage); + + bool IsOnEventTarget() const; + + nsCOMPtr mEventTarget; };