Bug 716140 - Implement multithreaded decoding using a thread pool. r=seth

This commit is contained in:
Joe Drew 2013-03-01 18:17:24 -05:00
Родитель 120d6e205c
Коммит 87a1590a0d
6 изменённых файлов: 412 добавлений и 304 удалений

Просмотреть файл

@ -76,7 +76,7 @@ public:
void FlushInvalidations();
// We're not COM-y, so we don't get refcounts by default
NS_INLINE_DECL_REFCOUNTING(Decoder)
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Decoder)
/*
* State.
@ -97,6 +97,11 @@ public:
mSynchronous = aSynchronous;
}
bool IsSynchronous() const
{
return mSynchronous;
}
void SetObserver(imgDecoderObserver* aObserver)
{
MOZ_ASSERT(aObserver);

Просмотреть файл

@ -16,9 +16,14 @@
#include "nsAutoPtr.h"
#include "nsStringStream.h"
#include "prenv.h"
#include "prsystem.h"
#include "ImageContainer.h"
#include "Layers.h"
#include "nsPresContext.h"
#include "nsThread.h"
#include "nsIThreadPool.h"
#include "nsXPCOMCIDInternal.h"
#include "nsIObserverService.h"
#include "nsPNGDecoder.h"
#include "nsGIFDecoder2.h"
@ -30,6 +35,7 @@
#include "gfxContext.h"
#include "mozilla/Services.h"
#include "mozilla/Preferences.h"
#include "mozilla/StandardInteger.h"
#include "mozilla/Telemetry.h"
@ -349,14 +355,14 @@ private:
namespace mozilla {
namespace image {
/* static */ StaticRefPtr<RasterImage::DecodeWorker> RasterImage::DecodeWorker::sSingleton;
/* static */ StaticRefPtr<RasterImage::DecodePool> RasterImage::DecodePool::sSingleton;
static nsCOMPtr<nsIThread> sScaleWorkerThread = nullptr;
#ifndef DEBUG
NS_IMPL_ISUPPORTS2(RasterImage, imgIContainer, nsIProperties)
NS_IMPL_THREADSAFE_ISUPPORTS2(RasterImage, imgIContainer, nsIProperties)
#else
NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
imgIContainerDebug)
NS_IMPL_THREADSAFE_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
imgIContainerDebug)
#endif
//******************************************************************************
@ -368,12 +374,14 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker,
mAnim(nullptr),
mLoopCount(-1),
mLockCount(0),
mDecoder(nullptr),
mBytesDecoded(0),
mDecodeCount(0),
#ifdef DEBUG
mFramesNotified(0),
#endif
mDecodingMutex("RasterImage"),
mDecoder(nullptr),
mBytesDecoded(0),
mInDecoder(false),
mHasSize(false),
mDecodeOnDraw(false),
mMultipart(false),
@ -381,7 +389,6 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker,
mHasSourceData(false),
mDecoded(false),
mHasBeenDecoded(false),
mInDecoder(false),
mAnimationFinished(false),
mFinishing(false),
mInUpdateImageContainer(false),
@ -418,7 +425,8 @@ RasterImage::~RasterImage()
if (mDecoder) {
// Kill off our decode request, if it's pending. (If not, this call is
// harmless.)
DecodeWorker::Singleton()->StopDecoding(this);
MutexAutoLock lock(mDecodingMutex);
DecodePool::StopDecoding(this);
mDecoder = nullptr;
// Unlock the last frame (if we have any). Our invariant is that, while we
@ -452,7 +460,7 @@ RasterImage::Initialize()
// Create our singletons now, so we don't have to worry about what thread
// they're created on.
DecodeWorker::Singleton();
DecodePool::Singleton();
}
nsresult
@ -779,7 +787,7 @@ RasterImage::GetHeight(int32_t *aHeight)
*aHeight = mSize.height;
return NS_OK;
}
//******************************************************************************
/* [noscript] readonly attribute nsSize intrinsicSize; */
NS_IMETHODIMP
@ -1377,6 +1385,8 @@ RasterImage::ApplyDecodeFlags(uint32_t aNewFlags)
nsresult
RasterImage::SetSize(int32_t aWidth, int32_t aHeight)
{
MOZ_ASSERT(NS_IsMainThread());
if (mError)
return NS_ERROR_FAILURE;
@ -1532,6 +1542,8 @@ RasterImage::SetFrameAsNonPremult(uint32_t aFrameNum, bool aIsNonPremult)
nsresult
RasterImage::DecodingComplete()
{
MOZ_ASSERT(NS_IsMainThread());
if (mError)
return NS_ERROR_FAILURE;
@ -1660,6 +1672,8 @@ RasterImage::SetLoopCount(int32_t aLoopCount)
nsresult
RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount)
{
MutexAutoLock lock(mDecodingMutex);
if (mError)
return NS_ERROR_FAILURE;
@ -1721,6 +1735,9 @@ RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount)
mInDecoder = true;
mDecoder->FlushInvalidations();
mInDecoder = false;
rv = FinishedSomeDecoding();
CONTAINER_ENSURE_SUCCESS(rv);
}
// Otherwise, we're storing data in the source buffer
@ -1731,10 +1748,8 @@ RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount)
if (!newElem)
return NS_ERROR_OUT_OF_MEMORY;
// If there's a decoder open, that means we want to do more decoding.
// Wake up the worker.
if (mDecoder) {
DecodeWorker::Singleton()->RequestDecode(this);
DecodePool::Singleton()->RequestDecode(this);
}
}
@ -1778,6 +1793,8 @@ get_header_str (char *buf, char *data, size_t data_len)
nsresult
RasterImage::DoImageDataComplete()
{
MOZ_ASSERT(NS_IsMainThread());
if (mError)
return NS_ERROR_FAILURE;
@ -1786,26 +1803,27 @@ RasterImage::DoImageDataComplete()
return NS_OK;
mHasSourceData = true;
// This call should come straight from necko - no reentrancy allowed
NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");
// If there's a decoder open, synchronously decode the beginning of the image
// to check for errors and get the image's size. (If we already have the
// image's size, this does nothing.) Then kick off an async decode of the
// rest of the image.
if (mDecoder) {
nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this);
nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this);
CONTAINER_ENSURE_SUCCESS(rv);
}
// If DecodeUntilSizeAvailable didn't finish the decode, let the decode worker
// finish decoding this image.
if (mDecoder) {
DecodeWorker::Singleton()->RequestDecode(this);
}
{
MutexAutoLock lock(mDecodingMutex);
// Free up any extra space in the backing buffer
mSourceData.Compact();
// If DecodeUntilSizeAvailable didn't finish the decode, let the decode worker
// finish decoding this image.
if (mDecoder) {
DecodePool::Singleton()->RequestDecode(this);
}
// Free up any extra space in the backing buffer
mSourceData.Compact();
}
// Log header information
if (PR_LOG_TEST(GetCompressedImageAccountingLog(), PR_LOG_DEBUG)) {
@ -1845,7 +1863,10 @@ RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*, nsresult aStatus, bo
}
// We just recorded OnStopRequest; we need to inform our listeners.
FinishedSomeDecoding();
{
MutexAutoLock lock(mDecodingMutex);
FinishedSomeDecoding();
}
return finalStatus;
}
@ -1873,6 +1894,8 @@ RasterImage::OnImageDataAvailable(nsIRequest*,
nsresult
RasterImage::OnNewSourceData()
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv;
if (mError)
@ -2428,6 +2451,8 @@ RasterImage::GetKeys(uint32_t *count, char ***keys)
void
RasterImage::Discard(bool force)
{
MOZ_ASSERT(NS_IsMainThread());
// We should be ok for discard
NS_ABORT_IF_FALSE(force ? CanForciblyDiscard() : CanDiscard(), "Asked to discard but can't!");
@ -2653,7 +2678,7 @@ RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
// Kill off our decode request, if it's pending. (If not, this call is
// harmless.)
DecodeWorker::Singleton()->StopDecoding(this);
DecodePool::StopDecoding(this);
nsresult decoderStatus = decoder->GetDecoderError();
if (NS_FAILED(decoderStatus)) {
@ -2688,6 +2713,8 @@ RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
nsresult
RasterImage::WriteToDecoder(const char *aBuffer, uint32_t aCount)
{
mDecodingMutex.AssertCurrentThreadOwns();
// We should have a decoder
NS_ABORT_IF_FALSE(mDecoder, "Trying to write to null decoder!");
@ -2751,6 +2778,8 @@ RasterImage::StartDecoding()
NS_IMETHODIMP
RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv;
if (mError)
@ -2781,7 +2810,7 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
// If we have a size decoder open, make sure we get the size
if (mDecoder && mDecoder->IsSizeDecode()) {
nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this);
nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this);
CONTAINER_ENSURE_SUCCESS(rv);
// If we didn't get the size out of the image, we won't until we get more
@ -2798,6 +2827,8 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
if (mDecoded)
return NS_OK;
MutexAutoLock lock(mDecodingMutex);
// If we have a size decode open, interrupt it and shut it down; or if
// the decoder has different flags than what we need
if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) {
@ -2805,7 +2836,7 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
CONTAINER_ENSURE_SUCCESS(rv);
}
// If we don't have a decoder, create one
// If we don't have a decoder, create one
if (!mDecoder) {
rv = InitDecoder(/* aDoSizeDecode = */ false);
CONTAINER_ENSURE_SUCCESS(rv);
@ -2816,6 +2847,13 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
MOZ_ASSERT(mDecoder);
}
// If we're waiting for decode work to be notified, go ahead and do that.
if (mDecodeRequest &&
mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) {
nsresult rv = FinishedSomeDecoding();
CONTAINER_ENSURE_SUCCESS(rv);
}
// If we've read all the data we have, we're done
if (mHasSourceData && mBytesDecoded == mSourceData.Length())
return NS_OK;
@ -2827,7 +2865,7 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
SAMPLE_LABEL_PRINTF("RasterImage", "DecodeABitOf", "%s", GetURIString().get());
mDecoder->SetSynchronous(true);
DecodeWorker::Singleton()->DecodeABitOf(this);
DecodePool::Singleton()->DecodeABitOf(this);
// DecodeABitOf can destroy mDecoder.
if (mDecoder) {
@ -2836,10 +2874,12 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
return NS_OK;
}
// If we get this far, dispatch the worker. We do this instead of starting
// any immediate decoding to guarantee that all our decode notifications are
// dispatched asynchronously, and to ensure we stay responsive.
DecodeWorker::Singleton()->RequestDecode(this);
if (!mDecoded) {
// If we get this far, dispatch the worker. We do this instead of starting
// any immediate decoding to guarantee that all our decode notifications are
// dispatched asynchronously, and to ensure we stay responsive.
DecodePool::Singleton()->RequestDecode(this);
}
return NS_OK;
}
@ -2848,6 +2888,16 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
nsresult
RasterImage::SyncDecode()
{
MutexAutoLock imgLock(mDecodingMutex);
if (mDecodeRequest) {
// If the image is waiting for decode work to be notified, go ahead and do that.
if (mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) {
nsresult rv = FinishedSomeDecoding();
CONTAINER_ENSURE_SUCCESS(rv);
}
}
nsresult rv;
SAMPLE_LABEL_PRINTF("RasterImage", "SyncDecode", "%s", GetURIString().get());;
@ -2860,7 +2910,7 @@ RasterImage::SyncDecode()
// If we have a size decoder open, make sure we get the size
if (mDecoder && mDecoder->IsSizeDecode()) {
nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this);
nsresult rv = DecodePool::Singleton()->DecodeUntilSizeAvailable(this);
CONTAINER_ENSURE_SUCCESS(rv);
// If we didn't get the size out of the image, we won't until we get more
@ -2916,6 +2966,9 @@ RasterImage::SyncDecode()
if (mDecoder) {
mDecoder->SetSynchronous(false);
// If our decoder's still open, there's still work to be done.
DecodePool::Singleton()->RequestDecode(this);
}
// All good if no errors!
@ -3126,11 +3179,7 @@ RasterImage::Draw(gfxContext *aContext,
// We use !mDecoded && mHasSourceData to mean discarded.
if (!mDecoded && mHasSourceData) {
mDrawStartTime = TimeStamp::Now();
// We're drawing this image, so indicate that we should decode it as soon
// as possible.
DecodeWorker::Singleton()->MarkAsASAP(this);
mDrawStartTime = TimeStamp::Now();
}
// If a synchronous draw is requested, flush anything that might be sitting around
@ -3204,6 +3253,7 @@ RasterImage::UnlockImage()
PR_LOG(GetCompressedImageAccountingLog(), PR_LOG_DEBUG,
("RasterImage[0x%p] canceling decode because image "
"is now unlocked.", this));
MutexAutoLock lock(mDecodingMutex);
FinishedSomeDecoding(eShutdownIntent_NotNeeded);
ForceDiscard();
return NS_OK;
@ -3238,6 +3288,8 @@ RasterImage::DecodeSomeData(uint32_t aMaxBytes)
// We should have a decoder if we get here
NS_ABORT_IF_FALSE(mDecoder, "trying to decode without decoder!");
mDecodingMutex.AssertCurrentThreadOwns();
// First, if we've just been called because we allocated a frame on the main
// thread, let the decoder deal with the data it set aside at that time by
// passing it a null buffer.
@ -3255,7 +3307,7 @@ RasterImage::DecodeSomeData(uint32_t aMaxBytes)
// write the proper amount of data
uint32_t bytesToDecode = std::min(aMaxBytes,
mSourceData.Length() - mBytesDecoded);
mSourceData.Length() - mBytesDecoded);
nsresult rv = WriteToDecoder(mSourceData.Elements() + mBytesDecoded,
bytesToDecode);
@ -3311,6 +3363,7 @@ RasterImage::DoError()
// If we're mid-decode, shut down the decoder.
if (mDecoder) {
MutexAutoLock lock(mDecodingMutex);
FinishedSomeDecoding(eShutdownIntent_Error);
}
@ -3383,6 +3436,8 @@ RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_D
{
MOZ_ASSERT(NS_IsMainThread());
mDecodingMutex.AssertCurrentThreadOwns();
nsRefPtr<DecodeRequest> request;
if (aRequest) {
request = aRequest;
@ -3444,135 +3499,134 @@ RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_D
}
}
// Then, tell the observers what happened in the decoder.
// If we have no request, we have not yet created a decoder, but we still
// need to send out notifications.
imgStatusTracker::StatusDiff diff;
if (request) {
image->mStatusTracker->SyncAndSyncNotifyDifference(request->mStatusTracker);
} else {
image->mStatusTracker->SyncNotifyDecodeState();
diff = image->mStatusTracker->CalculateAndApplyDifference(request->mStatusTracker);
}
// If we were a size decode and a full decode was requested, now's the time.
if (NS_SUCCEEDED(rv) && done && wasSize && image->mWantFullDecode) {
image->mWantFullDecode = false;
{
// Notifications can't go out with the decoding lock held.
MutexAutoUnlock unlock(mDecodingMutex);
// If we're not meant to be storing source data and we just got the size,
// we need to synchronously flush all the data we got to a full decoder.
// When that decoder is shut down, we'll also clear our source data.
if (!image->StoringSourceData()) {
rv = image->SyncDecode();
// Then, tell the observers what happened in the decoder.
// If we have no request, we have not yet created a decoder, but we still
// need to send out notifications.
if (request) {
image->mStatusTracker->SyncNotifyDifference(diff);
} else {
rv = image->RequestDecode();
image->mStatusTracker->SyncNotifyDecodeState();
}
// If we were a size decode and a full decode was requested, now's the time.
if (NS_SUCCEEDED(rv) && done && wasSize && image->mWantFullDecode) {
image->mWantFullDecode = false;
// If we're not meant to be storing source data and we just got the size,
// we need to synchronously flush all the data we got to a full decoder.
// When that decoder is shut down, we'll also clear our source data.
if (!image->StoringSourceData()) {
rv = image->SyncDecode();
} else {
rv = image->RequestDecode();
}
}
}
return rv;
}
/* static */ RasterImage::DecodeWorker*
RasterImage::DecodeWorker::Singleton()
NS_IMPL_THREADSAFE_ISUPPORTS1(RasterImage::DecodePool,
nsIObserver)
/* static */ RasterImage::DecodePool*
RasterImage::DecodePool::Singleton()
{
if (!sSingleton) {
sSingleton = new DecodeWorker();
MOZ_ASSERT(NS_IsMainThread());
sSingleton = new DecodePool();
ClearOnShutdown(&sSingleton);
}
return sSingleton;
}
RasterImage::DecodeWorker::~DecodeWorker()
RasterImage::DecodePool::DecodePool()
{
MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodeWorker on main thread!");
mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
if (mThreadPool) {
mThreadPool->SetName(NS_LITERAL_CSTRING("ImageDecoder"));
mThreadPool->SetThreadLimit(std::max(PR_GetNumberOfProcessors() - 1, 1));
// Shut down all the decoders since we're going away.
DecodeRequest* request = mASAPDecodeRequests.getFirst();
while (request) {
request->mImage->FinishedSomeDecoding(eShutdownIntent_NotNeeded);
request = request->getNext();
}
request = mNormalDecodeRequests.getFirst();
while (request) {
request->mImage->FinishedSomeDecoding(eShutdownIntent_NotNeeded);
request = request->getNext();
nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
if (obsSvc) {
obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
}
}
}
void
RasterImage::DecodeWorker::MarkAsASAP(RasterImage* aImg)
RasterImage::DecodePool::~DecodePool()
{
// We can be marked as ASAP before we've been asked to decode. If we are,
// create the request so we have somewhere to write down our status.
if (!aImg->mDecodeRequest) {
aImg->mDecodeRequest = new DecodeRequest(aImg);
MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodePool on main thread!");
}
NS_IMETHODIMP
RasterImage::DecodePool::Observe(nsISupports *subject, const char *topic,
const PRUnichar *data)
{
NS_ASSERTION(strcmp(topic, "xpcom-shutdown-threads") == 0, "oops");
if (mThreadPool) {
mThreadPool->Shutdown();
mThreadPool = nullptr;
}
DecodeRequest* request = aImg->mDecodeRequest;
// If we're already an ASAP request, there's nothing to do here.
if (request->mIsASAP) {
return;
}
request->mIsASAP = true;
if (request->isInList()) {
// If the decode request is in a list, it must be in the normal decode
// requests list -- if it had been in the ASAP list, then mIsASAP would
// have been true above. Move the request to the ASAP list.
request->removeFrom(mNormalDecodeRequests);
mASAPDecodeRequests.insertBack(request);
// Since request is in a list, one of the decode worker's lists is
// non-empty, so the worker should be pending in the event loop.
//
// (Note that this invariant only holds while we are not in Run(), because
// DecodeSomeOfImage adds requests to the decode worker using
// AddDecodeRequest, not RequestDecode, and AddDecodeRequest does not call
// EnsurePendingInEventLoop. Therefore, it is an error to call MarkAsASAP
// from within DecodeWorker::Run.)
MOZ_ASSERT(mPendingInEventLoop);
}
return NS_OK;
}
void
RasterImage::DecodeWorker::AddDecodeRequest(DecodeRequest* aRequest, uint32_t bytesToDecode)
{
if (aRequest->isInList()) {
// The image is already in our list of images to decode, so we don't have
// to do anything here.
return;
}
aRequest->mBytesToDecode = bytesToDecode;
if (aRequest->mIsASAP) {
mASAPDecodeRequests.insertBack(aRequest);
} else {
mNormalDecodeRequests.insertBack(aRequest);
}
}
void
RasterImage::DecodeWorker::RequestDecode(RasterImage* aImg)
RasterImage::DecodePool::RequestDecode(RasterImage* aImg)
{
MOZ_ASSERT(aImg->mDecoder);
MOZ_ASSERT(NS_IsMainThread());
aImg->mDecodingMutex.AssertCurrentThreadOwns();
// If we're currently waiting on a new frame for this image, we can't do any
// decoding.
if (!aImg->mDecoder->NeedsNewFrame()) {
AddDecodeRequest(aImg->mDecodeRequest, aImg->mSourceData.Length() - aImg->mBytesDecoded);
EnsurePendingInEventLoop();
// No matter whether this is currently being decoded, we need to update the
// number of bytes we want it to decode.
aImg->mDecodeRequest->mBytesToDecode = aImg->mSourceData.Length() - aImg->mBytesDecoded;
if (aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_PENDING ||
aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_ACTIVE) {
// The image is already in our list of images to decode, or currently being
// decoded, so we don't have to do anything else.
return;
}
aImg->mDecodeRequest->mRequestStatus = DecodeRequest::REQUEST_PENDING;
nsRefPtr<DecodeJob> job = new DecodeJob(aImg->mDecodeRequest, aImg);
if (!mThreadPool) {
NS_DispatchToMainThread(job);
} else {
mThreadPool->Dispatch(job, nsIEventTarget::DISPATCH_NORMAL);
}
}
}
void
RasterImage::DecodeWorker::DecodeABitOf(RasterImage* aImg)
RasterImage::DecodePool::DecodeABitOf(RasterImage* aImg)
{
MOZ_ASSERT(NS_IsMainThread());
aImg->mDecodingMutex.AssertCurrentThreadOwns();
if (aImg->mDecodeRequest) {
// If the image is waiting for decode work to be notified, go ahead and do that.
if (aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) {
aImg->FinishedSomeDecoding();
}
}
DecodeSomeOfImage(aImg);
@ -3594,98 +3648,98 @@ RasterImage::DecodeWorker::DecodeABitOf(RasterImage* aImg)
}
}
void
RasterImage::DecodeWorker::EnsurePendingInEventLoop()
/* static */ void
RasterImage::DecodePool::StopDecoding(RasterImage* aImg)
{
if (!mPendingInEventLoop) {
mPendingInEventLoop = true;
NS_DispatchToCurrentThread(this);
}
}
aImg->mDecodingMutex.AssertCurrentThreadOwns();
void
RasterImage::DecodeWorker::StopDecoding(RasterImage* aImg)
{
// If we haven't got a decode request, we're not currently decoding. (Having
// a decode request doesn't imply we *are* decoding, though.)
if (aImg->mDecodeRequest) {
if (aImg->mDecodeRequest->isInList()) {
aImg->mDecodeRequest->remove();
}
aImg->mDecodeRequest->mRequestStatus = DecodeRequest::REQUEST_STOPPED;
}
}
NS_IMETHODIMP
RasterImage::DecodeWorker::Run()
RasterImage::DecodePool::DecodeJob::Run()
{
// We just got called back by the event loop; therefore, we're no longer
// pending.
mPendingInEventLoop = false;
MutexAutoLock imglock(mImage->mDecodingMutex);
TimeStamp eventStart = TimeStamp::Now();
// Now decode until we either run out of time or run out of images.
do {
// Try to get an ASAP request to handle. If there isn't one, try to get a
// normal request. If no normal request is pending either, then we're done
// here.
DecodeRequest* request = mASAPDecodeRequests.popFirst();
if (!request)
request = mNormalDecodeRequests.popFirst();
if (!request)
break;
// This has to be a strong pointer, because DecodeSomeOfImage may destroy
// image->mDecoder, which may be holding the only other reference to image.
nsRefPtr<RasterImage> image = request->mImage;
uint32_t oldFrameCount = image->mDecoder->GetFrameCount();
uint32_t oldByteCount = image->mBytesDecoded;
DecodeSomeOfImage(image, DECODE_TYPE_NORMAL, request->mBytesToDecode);
uint32_t bytesDecoded = image->mBytesDecoded - oldByteCount;
// If the decoder needs a new frame, enqueue an event to get it; that event
// will enqueue another decode request when it's done.
if (image->mDecoder && image->mDecoder->NeedsNewFrame()) {
FrameNeededWorker::GetNewFrame(image);
}
// If we aren't yet finished decoding and we have more data in hand, add
// this request to the back of the list.
else if (image->mDecoder &&
!image->mError &&
!image->IsDecodeFinished() &&
bytesDecoded < request->mBytesToDecode) {
AddDecodeRequest(request, request->mBytesToDecode - bytesDecoded);
// If we have a new frame, let everybody know about it.
if (image->mDecoder->GetFrameCount() != oldFrameCount) {
DecodeDoneWorker::NotifyFinishedSomeDecoding(image, request);
}
} else {
// Nothing more for us to do - let everyone know what happened.
DecodeDoneWorker::NotifyFinishedSomeDecoding(image, request);
}
} while ((TimeStamp::Now() - eventStart).ToMilliseconds() <= gMaxMSBeforeYield);
// If decode requests are pending, re-post ourself to the event loop.
if (!mASAPDecodeRequests.isEmpty() || !mNormalDecodeRequests.isEmpty()) {
EnsurePendingInEventLoop();
// If we were interrupted, we shouldn't do any work.
if (mRequest->mRequestStatus == DecodeRequest::REQUEST_STOPPED) {
DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest);
return NS_OK;
}
Telemetry::Accumulate(Telemetry::IMAGE_DECODE_LATENCY_US,
uint32_t((TimeStamp::Now() - eventStart).ToMicroseconds()));
// If someone came along and synchronously decoded us, there's nothing for us to do.
if (!mRequest->mAllocatedNewFrame && (!mImage->mDecoder || mImage->IsDecodeFinished())) {
DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest);
return NS_OK;
}
// If we're a decode job that's been enqueued since a previous decode that
// still needs a new frame, we can't do anything. Wait until the
// FrameNeededWorker enqueues another frame.
if (mImage->mDecoder->NeedsNewFrame()) {
return NS_OK;
}
mRequest->mRequestStatus = DecodeRequest::REQUEST_ACTIVE;
uint32_t oldByteCount = mImage->mBytesDecoded;
DecodeType type = DECODE_TYPE_UNTIL_DONE_BYTES;
// Multithreaded decoding can be disabled. If we've done so, we don't want to
// monopolize the main thread, and will allow a timeout in DecodeSomeOfImage.
if (NS_IsMainThread()) {
type = DECODE_TYPE_UNTIL_TIME;
}
DecodePool::Singleton()->DecodeSomeOfImage(mImage, type, mRequest->mBytesToDecode);
uint32_t bytesDecoded = mImage->mBytesDecoded - oldByteCount;
mRequest->mRequestStatus = DecodeRequest::REQUEST_WORK_DONE;
// If the decoder needs a new frame, enqueue an event to get it; that event
// will enqueue another decode request when it's done.
if (mImage->mDecoder && mImage->mDecoder->NeedsNewFrame()) {
FrameNeededWorker::GetNewFrame(mImage);
}
// If we aren't yet finished decoding and we have more data in hand, add
// this request to the back of the list.
else if (mImage->mDecoder &&
!mImage->mError &&
!mImage->IsDecodeFinished() &&
bytesDecoded < mRequest->mBytesToDecode) {
DecodePool::Singleton()->RequestDecode(mImage);
} else {
// Nothing more for us to do - let everyone know what happened.
DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest);
}
return NS_OK;
}
nsresult
RasterImage::DecodeWorker::DecodeUntilSizeAvailable(RasterImage* aImg)
RasterImage::DecodePool::DecodeUntilSizeAvailable(RasterImage* aImg)
{
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock imgLock(aImg->mDecodingMutex);
if (aImg->mDecodeRequest) {
// If the image is waiting for decode work to be notified, go ahead and do that.
if (aImg->mDecodeRequest->mRequestStatus == DecodeRequest::REQUEST_WORK_DONE) {
nsresult rv = aImg->FinishedSomeDecoding();
if (NS_FAILED(rv)) {
aImg->DoError();
return rv;
}
}
}
nsresult rv = DecodeSomeOfImage(aImg, DECODE_TYPE_UNTIL_SIZE);
if (NS_FAILED(rv)) {
return rv;
@ -3703,12 +3757,13 @@ RasterImage::DecodeWorker::DecodeUntilSizeAvailable(RasterImage* aImg)
}
nsresult
RasterImage::DecodeWorker::DecodeSomeOfImage(RasterImage* aImg,
DecodeType aDecodeType /* = DECODE_TYPE_NORMAL */,
uint32_t bytesToDecode /* = 0 */)
RasterImage::DecodePool::DecodeSomeOfImage(RasterImage* aImg,
DecodeType aDecodeType /* = DECODE_TYPE_UNTIL_TIME */,
uint32_t bytesToDecode /* = 0 */)
{
NS_ABORT_IF_FALSE(aImg->mInitialized,
"Worker active for uninitialized container!");
aImg->mDecodingMutex.AssertCurrentThreadOwns();
// If an error is flagged, it probably happened while we were waiting
// in the event queue.
@ -3720,9 +3775,17 @@ RasterImage::DecodeWorker::DecodeSomeOfImage(RasterImage* aImg,
if (!aImg->mDecoder || aImg->mDecoded)
return NS_OK;
// If we're currently waiting on a new frame for this image, we can't do any
// decoding right now.
if (aImg->mDecoder->NeedsNewFrame()) {
// If we're doing synchronous decodes, and we're waiting on a new frame for
// this image, get it now.
if (aImg->mDecoder->IsSynchronous() && aImg->mDecoder->NeedsNewFrame()) {
MOZ_ASSERT(NS_IsMainThread());
aImg->mDecoder->AllocateFrame();
aImg->mDecodeRequest->mAllocatedNewFrame = true;
}
// If we're not synchronous, we can't allocate a frame right now.
else if (aImg->mDecoder->NeedsNewFrame()) {
return NS_OK;
}
@ -3773,7 +3836,7 @@ RasterImage::DecodeWorker::DecodeSomeOfImage(RasterImage* aImg,
// Yield if we've been decoding for too long. We check this _after_ decoding
// a chunk to ensure that we don't yield without doing any decoding.
if (TimeStamp::Now() >= deadline)
if (aDecodeType == DECODE_TYPE_UNTIL_TIME && TimeStamp::Now() >= deadline)
break;
}
@ -3818,6 +3881,8 @@ RasterImage::DecodeDoneWorker::DecodeDoneWorker(RasterImage* image, DecodeReques
void
RasterImage::DecodeDoneWorker::NotifyFinishedSomeDecoding(RasterImage* image, DecodeRequest* request)
{
image->mDecodingMutex.AssertCurrentThreadOwns();
nsCOMPtr<DecodeDoneWorker> worker = new DecodeDoneWorker(image, request);
NS_DispatchToMainThread(worker);
}
@ -3825,12 +3890,14 @@ RasterImage::DecodeDoneWorker::NotifyFinishedSomeDecoding(RasterImage* image, De
NS_IMETHODIMP
RasterImage::DecodeDoneWorker::Run()
{
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mImage->mDecodingMutex);
mImage->FinishedSomeDecoding(eShutdownIntent_Done, mRequest);
// If we didn't finish decoding yet, try again
if (mImage->mDecoder && !mImage->IsDecodeFinished() &&
mImage->mSourceData.Length() > mImage->mBytesDecoded) {
DecodeWorker::Singleton()->RequestDecode(mImage);
if (mImage->mDecoder) {
DecodePool::Singleton()->RequestDecode(mImage);
}
return NS_OK;
@ -3851,6 +3918,7 @@ RasterImage::FrameNeededWorker::GetNewFrame(RasterImage* image)
NS_IMETHODIMP
RasterImage::FrameNeededWorker::Run()
{
MutexAutoLock lock(mImage->mDecodingMutex);
nsresult rv = NS_OK;
// If we got a synchronous decode in the mean time, we don't need to do
@ -3861,7 +3929,8 @@ RasterImage::FrameNeededWorker::Run()
}
if (NS_SUCCEEDED(rv) && mImage->mDecoder) {
DecodeWorker::Singleton()->RequestDecode(mImage);
// By definition, we're not done decoding, so enqueue us for more decoding.
DecodePool::Singleton()->RequestDecode(mImage);
}
return NS_OK;

Просмотреть файл

@ -18,7 +18,6 @@
#define mozilla_imagelib_RasterImage_h_
#include "Image.h"
#include "nsCOMArray.h"
#include "nsCOMPtr.h"
#include "imgIContainer.h"
#include "nsIProperties.h"
@ -28,18 +27,20 @@
#include "imgFrame.h"
#include "nsThreadUtils.h"
#include "DiscardTracker.h"
#include "nsISupportsImpl.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Telemetry.h"
#include "mozilla/LinkedList.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/WeakPtr.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Mutex.h"
#include "gfx2DGlue.h"
#ifdef DEBUG
#include "imgIContainerDebug.h"
#endif
class nsIInputStream;
class nsIThreadPool;
#define NS_RASTERIMAGE_CID \
{ /* 376ff2c1-9bf6-418a-b143-3340c00112f7 */ \
@ -387,25 +388,23 @@ private:
};
/**
* DecodeWorker keeps a linked list of DecodeRequests to keep track of the
* images it needs to decode.
*
* Each RasterImage has a pointer to one or zero heap-allocated
* DecodeRequests.
*/
struct DecodeRequest : public LinkedListElement<DecodeRequest>,
public RefCounted<DecodeRequest>
struct DecodeRequest
{
DecodeRequest(RasterImage* aImage)
: mImage(aImage)
, mBytesToDecode(0)
, mRequestStatus(REQUEST_INACTIVE)
, mChunkCount(0)
, mAllocatedNewFrame(false)
, mIsASAP(false)
{
mStatusTracker = aImage->mStatusTracker->CloneForRecording();
}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodeRequest)
// The status tracker that is associated with a given decode request, to
// ensure their lifetimes are linked.
nsRefPtr<imgStatusTracker> mStatusTracker;
@ -414,6 +413,15 @@ private:
uint32_t mBytesToDecode;
enum DecodeRequestStatus
{
REQUEST_INACTIVE,
REQUEST_PENDING,
REQUEST_ACTIVE,
REQUEST_WORK_DONE,
REQUEST_STOPPED
} mRequestStatus;
/* Keeps track of how much time we've burned decoding this particular decode
* request. */
TimeDuration mDecodeTime;
@ -424,35 +432,31 @@ private:
/* True if a new frame has been allocated, but DecodeSomeData hasn't yet
* been called to flush data to it */
bool mAllocatedNewFrame;
/* True if we need to handle this decode as soon as possible. */
bool mIsASAP;
};
/*
* DecodeWorker is a singleton class we use when decoding large images.
* DecodePool is a singleton class we use when decoding large images.
*
* When we wish to decode an image larger than
* image.mem.max_bytes_for_sync_decode, we call DecodeWorker::RequestDecode()
* image.mem.max_bytes_for_sync_decode, we call DecodePool::RequestDecode()
* for the image. This adds the image to a queue of pending requests and posts
* the DecodeWorker singleton to the event queue, if it's not already pending
* the DecodePool singleton to the event queue, if it's not already pending
* there.
*
* When the DecodeWorker is run from the event queue, it decodes the image (and
* When the DecodePool is run from the event queue, it decodes the image (and
* all others it's managing) in chunks, periodically yielding control back to
* the event loop.
*
* An image being decoded may have one of two priorities: normal or ASAP. ASAP
* images are always decoded before normal images. (We currently give ASAP
* priority to images which appear onscreen but are not yet decoded.)
*/
class DecodeWorker : public nsRunnable
class DecodePool : public nsIObserver
{
public:
static DecodeWorker* Singleton();
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
static DecodePool* Singleton();
/**
* Ask the DecodeWorker to asynchronously decode this image.
* Ask the DecodePool to asynchronously decode this image.
*/
void RequestDecode(RasterImage* aImg);
@ -463,23 +467,13 @@ private:
void DecodeABitOf(RasterImage* aImg);
/**
* Give this image ASAP priority; it will be decoded before all non-ASAP
* images. You can call MarkAsASAP before or after you call RequestDecode
* for the image, but if you MarkAsASAP before you call RequestDecode, you
* still need to call RequestDecode.
*
* StopDecoding() resets the image's ASAP flag.
*/
void MarkAsASAP(RasterImage* aImg);
/**
* Ask the DecodeWorker to stop decoding this image. Internally, we also
* Ask the DecodePool to stop decoding this image. Internally, we also
* call this function when we finish decoding an image.
*
* Since the DecodeWorker keeps raw pointers to RasterImages, make sure you
* Since the DecodePool keeps raw pointers to RasterImages, make sure you
* call this before a RasterImage is destroyed!
*/
void StopDecoding(RasterImage* aImg);
static void StopDecoding(RasterImage* aImg);
/**
* Synchronously decode the beginning of the image until we run out of
@ -491,56 +485,56 @@ private:
*/
nsresult DecodeUntilSizeAvailable(RasterImage* aImg);
NS_IMETHOD Run();
virtual ~DecodeWorker();
virtual ~DecodePool();
private: /* statics */
static StaticRefPtr<DecodeWorker> sSingleton;
static StaticRefPtr<DecodePool> sSingleton;
private: /* methods */
DecodeWorker()
: mPendingInEventLoop(false)
{}
/* Post ourselves to the event loop if we're not currently pending. */
void EnsurePendingInEventLoop();
/* Add the given request to the appropriate list of decode requests, but
* don't ensure that we're pending in the event loop. */
void AddDecodeRequest(DecodeRequest* aRequest, uint32_t bytesToDecode);
DecodePool();
enum DecodeType {
DECODE_TYPE_NORMAL,
DECODE_TYPE_UNTIL_SIZE
DECODE_TYPE_UNTIL_TIME,
DECODE_TYPE_UNTIL_SIZE,
DECODE_TYPE_UNTIL_DONE_BYTES
};
/* Decode some chunks of the given image. If aDecodeType is UNTIL_SIZE,
* decode until we have the image's size, then stop. If bytesToDecode is
* non-0, at most bytesToDecode bytes will be decoded. */
* non-0, at most bytesToDecode bytes will be decoded. if aDecodeType is
* UNTIL_DONE_BYTES, decode until all bytesToDecode bytes are decoded.
*/
nsresult DecodeSomeOfImage(RasterImage* aImg,
DecodeType aDecodeType = DECODE_TYPE_NORMAL,
DecodeType aDecodeType = DECODE_TYPE_UNTIL_TIME,
uint32_t bytesToDecode = 0);
/* Create a new DecodeRequest suitable for doing some decoding and set it
* as aImg's mDecodeRequest. */
void CreateRequestForImage(RasterImage* aImg);
/* A decode job dispatched to a thread pool by DecodePool.
*/
class DecodeJob : public nsRunnable
{
public:
DecodeJob(DecodeRequest* aRequest, RasterImage* aImg)
: mRequest(aRequest)
, mImage(aImg)
{}
NS_IMETHOD Run();
private:
nsRefPtr<DecodeRequest> mRequest;
nsRefPtr<RasterImage> mImage;
};
private: /* members */
LinkedList<DecodeRequest> mASAPDecodeRequests;
LinkedList<DecodeRequest> mNormalDecodeRequests;
/* True if we've posted ourselves to the event loop and expect Run() to
* be called sometime in the future. */
bool mPendingInEventLoop;
nsCOMPtr<nsIThreadPool> mThreadPool;
};
class DecodeDoneWorker : public nsRunnable
{
public:
/**
* Called by the DecodeWorker with an image when it's done some significant
* Called by the DecodePool with an image when it's done some significant
* portion of decoding that needs to be notified about.
*
* Ensures the decode state accumulated by the decoding process gets
@ -563,7 +557,7 @@ private:
{
public:
/**
* Called by the DecodeWorker with an image when it's been told by the
* Called by the DecodeJob with an image when it's been told by the
* decoder that it needs a new frame to be allocated on the main thread.
*
* Dispatches an event to do so, which will further dispatch a
@ -754,16 +748,10 @@ private: // data
DiscardTracker::Node mDiscardTrackerNode;
// Source data members
FallibleTArray<char> mSourceData;
nsCString mSourceDataMimeType;
friend class DiscardTracker;
// Decoder and friends
nsRefPtr<Decoder> mDecoder;
nsRefPtr<DecodeRequest> mDecodeRequest;
uint32_t mBytesDecoded;
// How many times we've decoded this image.
// This is currently only used for statistics
int32_t mDecodeCount;
@ -778,6 +766,22 @@ private: // data
uint32_t mFramesNotified;
#endif
// Below are the pieces of data that can be accessed on more than one thread
// at once, and hence need to be locked by mDecodingMutex.
// BEGIN LOCKED MEMBER VARIABLES
mozilla::Mutex mDecodingMutex;
FallibleTArray<char> mSourceData;
// Decoder and friends
nsRefPtr<Decoder> mDecoder;
nsRefPtr<DecodeRequest> mDecodeRequest;
uint32_t mBytesDecoded;
// END LOCKED MEMBER VARIABLES
bool mInDecoder;
// Boolean flags (clustered together to conserve space):
bool mHasSize:1; // Has SetSize() been called?
bool mDecodeOnDraw:1; // Decoding on draw?
@ -789,7 +793,6 @@ private: // data
bool mDecoded:1;
bool mHasBeenDecoded:1;
bool mInDecoder:1;
// Whether the animation can stop, due to running out
// of frames, or no more owning request

Просмотреть файл

@ -388,7 +388,8 @@ VectorImage::OnImageDataComplete(nsIRequest* aRequest,
nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
imgDecoderObserver* observer = clone->GetDecoderObserver();
observer->OnStopRequest(aLastPart, finalStatus);
mStatusTracker->SyncAndSyncNotifyDifference(clone);
imgStatusTracker::StatusDiff diff = mStatusTracker->CalculateAndApplyDifference(clone);
mStatusTracker->SyncNotifyDifference(diff);
}
return finalStatus;
}
@ -846,7 +847,8 @@ VectorImage::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt)
nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
imgDecoderObserver* observer = clone->GetDecoderObserver();
observer->OnStartDecode();
mStatusTracker->SyncAndSyncNotifyDifference(clone);
imgStatusTracker::StatusDiff diff = mStatusTracker->CalculateAndApplyDifference(clone);
mStatusTracker->SyncNotifyDifference(diff);
}
// Create a listener to wait until the SVG document is fully loaded, which
@ -933,7 +935,8 @@ VectorImage::OnSVGDocumentLoaded()
observer->OnStopFrame();
observer->OnStopDecode(NS_OK); // Unblock page load.
mStatusTracker->SyncAndSyncNotifyDifference(clone);
imgStatusTracker::StatusDiff diff = mStatusTracker->CalculateAndApplyDifference(clone);
mStatusTracker->SyncNotifyDifference(diff);
}
EvaluateAnimation();
@ -955,7 +958,8 @@ VectorImage::OnSVGDocumentError()
// Unblock page load.
observer->OnStopDecode(NS_ERROR_FAILURE);
mStatusTracker->SyncAndSyncNotifyDifference(clone);
imgStatusTracker::StatusDiff diff = mStatusTracker->CalculateAndApplyDifference(clone);
mStatusTracker->SyncNotifyDifference(diff);
}
}

Просмотреть файл

@ -517,24 +517,26 @@ imgStatusTracker::SyncNotifyState(nsTObserverArray<imgRequestProxy*>& proxies,
}
}
void
imgStatusTracker::SyncAndSyncNotifyDifference(imgStatusTracker* other)
imgStatusTracker::StatusDiff
imgStatusTracker::CalculateAndApplyDifference(imgStatusTracker* other)
{
LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncAndSyncNotifyDifference");
LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncAndCalculateDifference");
// We must not modify or notify for the start-load state, which happens from Necko callbacks.
uint32_t loadState = mState & stateRequestStarted;
uint32_t diffState = ~mState & other->mState & ~stateRequestStarted;
bool unblockedOnload = mState & stateBlockingOnload && !(other->mState & stateBlockingOnload);
bool foundError = (mImageStatus != imgIRequest::STATUS_ERROR) && (other->mImageStatus == imgIRequest::STATUS_ERROR);
StatusDiff diff;
diff.mDiffState = ~mState & other->mState & ~stateRequestStarted;
diff.mUnblockedOnload = mState & stateBlockingOnload && !(other->mState & stateBlockingOnload);
diff.mFoundError = (mImageStatus != imgIRequest::STATUS_ERROR) && (other->mImageStatus == imgIRequest::STATUS_ERROR);
// Now that we've calculated the difference in state, synchronize our state
// with the other tracker.
// First, actually synchronize our state.
mInvalidRect = mInvalidRect.Union(other->mInvalidRect);
mState |= diffState | loadState;
if (unblockedOnload) {
diff.mInvalidRect = mInvalidRect.Union(other->mInvalidRect);
mState |= diff.mDiffState | loadState;
if (diff.mUnblockedOnload) {
mState &= ~stateBlockingOnload;
}
mImageStatus = other->mImageStatus;
@ -552,9 +554,21 @@ imgStatusTracker::SyncAndSyncNotifyDifference(imgStatusTracker* other)
}
}
SyncNotifyState(mConsumers, !!mImage, diffState, mInvalidRect, mHadLastPart);
// Reset the invalid rectangles for another go.
other->mInvalidRect.SetEmpty();
mInvalidRect.SetEmpty();
if (unblockedOnload) {
return diff;
}
void
imgStatusTracker::SyncNotifyDifference(imgStatusTracker::StatusDiff diff)
{
LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncNotifyDifference");
SyncNotifyState(mConsumers, !!mImage, diff.mDiffState, diff.mInvalidRect, mHadLastPart);
if (diff.mUnblockedOnload) {
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
while (iter.HasMore()) {
// Hold on to a reference to this proxy, since notifying the state can
@ -567,11 +581,7 @@ imgStatusTracker::SyncAndSyncNotifyDifference(imgStatusTracker* other)
}
}
// Reset the invalid rectangles for another go.
other->mInvalidRect.SetEmpty();
mInvalidRect.SetEmpty();
if (foundError) {
if (diff.mFoundError) {
FireFailureNotification();
}
}

Просмотреть файл

@ -23,11 +23,11 @@ class Image;
#include "mozilla/RefPtr.h"
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nsTObserverArray.h"
#include "nsIRunnable.h"
#include "nscore.h"
#include "imgDecoderObserver.h"
#include "nsISupportsImpl.h"
enum {
stateRequestStarted = 1u << 0,
@ -51,9 +51,11 @@ enum {
* and the notifications will be replayed to the proxy asynchronously.
*/
class imgStatusTracker : public mozilla::RefCounted<imgStatusTracker>
class imgStatusTracker
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(imgStatusTracker)
// aImage is the image that this status tracker will pass to the
// imgRequestProxys in SyncNotify() and EmulateRequestFinished(), and must be
// alive as long as this instance is, because we hold a weak reference to it.
@ -193,7 +195,22 @@ public:
inline imgDecoderObserver* GetDecoderObserver() { return mTrackerObserver.get(); }
imgStatusTracker* CloneForRecording();
void SyncAndSyncNotifyDifference(imgStatusTracker* other);
struct StatusDiff
{
uint32_t mDiffState;
bool mUnblockedOnload;
bool mFoundError;
nsIntRect mInvalidRect;
};
// Calculate the difference between this and other, apply that difference to
// ourselves, and return it for passing to SyncNotifyDifference.
StatusDiff CalculateAndApplyDifference(imgStatusTracker* other);
// Notify for the difference found in CalculateAndApplyDifference. No
// decoding locks may be held.
void SyncNotifyDifference(StatusDiff diff);
nsIntRect GetInvalidRect() const { return mInvalidRect; }