mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 716140 - Create a clone imgStatusTracker on every asynchronous decoding event, and replay the difference between that imgStatusTracker and the image's current status tracker when decoding completes. r=seth
* * * [mq]: test-fixes * * * [mq]: undefer-on-remove * * * imported patch notify-in-sequence --HG-- extra : rebase_source : 01a495a64cb4b6a8076a3550c254ddd804f53e80
This commit is contained in:
parent
fcda375530
commit
b2712b400e
@ -135,6 +135,13 @@ Decoder::Finish(RasterImage::eShutdownIntent aShutdownIntent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set image metadata before calling DecodingComplete, because DecodingComplete calls Optimize().
|
||||
mImageMetadata.SetOnImage(&mImage);
|
||||
|
||||
if (mDecodeDone) {
|
||||
mImage.DecodingComplete();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -155,9 +162,6 @@ Decoder::FlushInvalidations()
|
||||
if (mInvalidRect.IsEmpty())
|
||||
return;
|
||||
|
||||
// Tell the image that it's been updated
|
||||
mImage.FrameUpdated(mFrameCount - 1, mInvalidRect);
|
||||
|
||||
if (mObserver) {
|
||||
#ifdef XP_MACOSX
|
||||
// Bug 703231
|
||||
@ -285,15 +289,9 @@ Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */)
|
||||
NS_ABORT_IF_FALSE(!mDecodeDone, "Decode already done!");
|
||||
mDecodeDone = true;
|
||||
|
||||
// Set metadata before DecodingComplete(), since DecodingComplete() calls Optimize()
|
||||
mImageMetadata.SetLoopCount(aLoopCount);
|
||||
mImageMetadata.SetIsNonPremultiplied(GetDecodeFlags() & DECODER_NO_PREMULTIPLY_ALPHA);
|
||||
|
||||
// Sync metadata to image
|
||||
mImageMetadata.SetOnImage(&mImage);
|
||||
|
||||
// Notify
|
||||
mImage.DecodingComplete();
|
||||
if (mObserver) {
|
||||
mObserver->OnStopDecode(NS_OK);
|
||||
}
|
||||
|
@ -132,6 +132,8 @@ public:
|
||||
// Use HistogramCount as an invalid Histogram ID
|
||||
virtual Telemetry::ID SpeedHistogram() { return Telemetry::HistogramCount; }
|
||||
|
||||
ImageMetadata& GetImageMetadata() { return mImageMetadata; }
|
||||
|
||||
protected:
|
||||
|
||||
/*
|
||||
|
@ -648,8 +648,11 @@ RasterImage::RequestRefresh(const mozilla::TimeStamp& aTime)
|
||||
#endif
|
||||
|
||||
UpdateImageContainer();
|
||||
|
||||
// Explicitly call this on mStatusTracker so we're sure to not interfere
|
||||
// with the decoding process
|
||||
if (mStatusTracker)
|
||||
mStatusTracker->GetDecoderObserver()->FrameChanged(&dirtyRect);
|
||||
mStatusTracker->FrameChanged(&dirtyRect);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1683,8 +1686,9 @@ RasterImage::ResetAnimation()
|
||||
// we fix bug 500402.
|
||||
|
||||
// Update display if we were animating before
|
||||
if (mAnimating && mStatusTracker)
|
||||
mStatusTracker->GetDecoderObserver()->FrameChanged(&(mAnim->firstFrameRefreshArea));
|
||||
if (mAnimating && mStatusTracker) {
|
||||
mStatusTracker->FrameChanged(&(mAnim->firstFrameRefreshArea));
|
||||
}
|
||||
|
||||
if (ShouldAnimate()) {
|
||||
StartAnimation();
|
||||
@ -1844,8 +1848,7 @@ RasterImage::DoImageDataComplete()
|
||||
// directly to the decoder in the AddSourceData() calls. This means we're
|
||||
// done, so we can shut down the decoder.
|
||||
if (!StoringSourceData()) {
|
||||
nsresult rv = ShutdownDecoder(eShutdownIntent_Done);
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
RasterImage::FinishedSomeDecoding(this);
|
||||
}
|
||||
|
||||
// If there's a decoder open, synchronously decode the beginning of the image
|
||||
@ -2575,8 +2578,6 @@ RasterImage::InitDecoder(bool aDoSizeDecode)
|
||||
CONTAINER_ENSURE_TRUE(type != eDecoderType_unknown, NS_IMAGELIB_ERROR_NO_DECODER);
|
||||
|
||||
// Instantiate the appropriate decoder
|
||||
imgDecoderObserver* observer = mStatusTracker ? mStatusTracker->GetDecoderObserver()
|
||||
: nullptr;
|
||||
switch (type) {
|
||||
case eDecoderType_png:
|
||||
mDecoder = new nsPNGDecoder(*this);
|
||||
@ -2608,7 +2609,10 @@ RasterImage::InitDecoder(bool aDoSizeDecode)
|
||||
}
|
||||
|
||||
// Initialize the decoder
|
||||
mDecoder->SetObserver(observer);
|
||||
if (!mDecodeRequest) {
|
||||
mDecodeRequest = new DecodeRequest(this);
|
||||
}
|
||||
mDecoder->SetObserver(mDecodeRequest->mStatusTracker->GetDecoderObserver());
|
||||
mDecoder->SetSizeDecode(aDoSizeDecode);
|
||||
mDecoder->SetDecodeFlags(mFrameDecodeFlags);
|
||||
mDecoder->Init();
|
||||
@ -2644,6 +2648,8 @@ RasterImage::InitDecoder(bool aDoSizeDecode)
|
||||
nsresult
|
||||
RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Ensure that our intent is valid
|
||||
NS_ABORT_IF_FALSE((aIntent >= 0) && (aIntent < eShutdownIntent_AllCount),
|
||||
"Invalid shutdown intent");
|
||||
@ -2820,19 +2826,19 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType)
|
||||
if (mDecoder &&
|
||||
(mDecoder->IsSizeDecode() || mDecoder->GetDecodeFlags() != mFrameDecodeFlags))
|
||||
{
|
||||
rv = ShutdownDecoder(eShutdownIntent_NotNeeded);
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
RasterImage::FinishedSomeDecoding(this, eShutdownIntent_NotNeeded);
|
||||
}
|
||||
|
||||
// If we don't have a decoder, create one
|
||||
if (!mDecoder) {
|
||||
NS_ABORT_IF_FALSE(mFrames.IsEmpty(), "Trying to decode to non-empty frame-array");
|
||||
rv = InitDecoder(/* aDoSizeDecode = */ false);
|
||||
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
}
|
||||
|
||||
// If we've read all the data we have, we're done
|
||||
if (mBytesDecoded == mSourceData.Length())
|
||||
if (mHasSourceData && mBytesDecoded == mSourceData.Length())
|
||||
return NS_OK;
|
||||
|
||||
// If we can do decoding now, do so. Small images will decode completely,
|
||||
@ -2879,8 +2885,7 @@ RasterImage::SyncDecode()
|
||||
if (mDecoder &&
|
||||
(mDecoder->IsSizeDecode() || mDecoder->GetDecodeFlags() != mFrameDecodeFlags))
|
||||
{
|
||||
rv = ShutdownDecoder(eShutdownIntent_NotNeeded);
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
RasterImage::FinishedSomeDecoding(this, eShutdownIntent_NotNeeded);
|
||||
}
|
||||
|
||||
// If we don't have a decoder, create one
|
||||
@ -2906,8 +2911,12 @@ RasterImage::SyncDecode()
|
||||
|
||||
// If we finished the decode, shutdown the decoder
|
||||
if (mDecoder && IsDecodeFinished()) {
|
||||
rv = ShutdownDecoder(eShutdownIntent_Done);
|
||||
// We have to shut down the decoder *now*, so we explicitly shut down the
|
||||
// decoder, and let FinishedSomeDecoding handle the rest for us.
|
||||
nsRefPtr<DecodeRequest> request = mDecodeRequest;
|
||||
nsresult rv = ShutdownDecoder(eShutdownIntent_Done);
|
||||
CONTAINER_ENSURE_SUCCESS(rv);
|
||||
RasterImage::FinishedSomeDecoding(this, eShutdownIntent_Done, request);
|
||||
}
|
||||
|
||||
// All good if no errors!
|
||||
@ -2966,7 +2975,7 @@ RasterImage::ScalingDone(ScaleRequest* request, ScaleStatus status)
|
||||
if (mStatusTracker) {
|
||||
imgFrame *scaledFrame = request->dstFrame.get();
|
||||
scaledFrame->ImageUpdated(scaledFrame->GetRect());
|
||||
mStatusTracker->GetDecoderObserver()->FrameChanged(&request->srcRect);
|
||||
mStatusTracker->FrameChanged(&request->srcRect);
|
||||
}
|
||||
|
||||
mScaleResult.status = SCALE_DONE;
|
||||
@ -3113,7 +3122,7 @@ RasterImage::Draw(gfxContext *aContext,
|
||||
// that case we use our animation consumers count as a proxy for lock count.
|
||||
if (mLockCount == 0 || (mAnim && mAnimationConsumers == 0)) {
|
||||
if (mStatusTracker)
|
||||
mStatusTracker->GetDecoderObserver()->OnUnlockedDraw();
|
||||
mStatusTracker->OnUnlockedDraw();
|
||||
}
|
||||
|
||||
// We use !mDecoded && mHasSourceData to mean discarded.
|
||||
@ -3196,7 +3205,7 @@ RasterImage::UnlockImage()
|
||||
PR_LOG(GetCompressedImageAccountingLog(), PR_LOG_DEBUG,
|
||||
("RasterImage[0x%p] canceling decode because image "
|
||||
"is now unlocked.", this));
|
||||
ShutdownDecoder(eShutdownIntent_NotNeeded);
|
||||
RasterImage::FinishedSomeDecoding(this, eShutdownIntent_NotNeeded);
|
||||
ForceDiscard();
|
||||
return NS_OK;
|
||||
}
|
||||
@ -3254,14 +3263,15 @@ RasterImage::IsDecodeFinished()
|
||||
// Precondition
|
||||
NS_ABORT_IF_FALSE(mDecoder, "Can't call IsDecodeFinished() without decoder!");
|
||||
|
||||
// If we aren't storing source data, just presume that it's OK to shut down
|
||||
// the decoder as we always write all the data we get.
|
||||
if (!StoringSourceData()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Assume it's not finished
|
||||
bool decodeFinished = false;
|
||||
|
||||
// There shouldn't be any reason to call this if we're not storing
|
||||
// source data
|
||||
NS_ABORT_IF_FALSE(StoringSourceData(),
|
||||
"just shut down on SourceDataComplete!");
|
||||
|
||||
// The decode is complete if we got what we wanted...
|
||||
if (mDecoder->IsSizeDecode()) {
|
||||
if (mHasSize)
|
||||
@ -3292,8 +3302,9 @@ RasterImage::DoError()
|
||||
return;
|
||||
|
||||
// If we're mid-decode, shut down the decoder.
|
||||
if (mDecoder)
|
||||
ShutdownDecoder(eShutdownIntent_Error);
|
||||
if (mDecoder) {
|
||||
FinishedSomeDecoding(this, eShutdownIntent_Error);
|
||||
}
|
||||
|
||||
// Put the container in an error state
|
||||
mError = true;
|
||||
@ -3352,6 +3363,86 @@ RasterImage::GetFramesNotified(uint32_t *aFramesNotified)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* static */ void
|
||||
RasterImage::FinishedSomeDecoding(RasterImage* aImage,
|
||||
eShutdownIntent aIntent /* = eShutdownIntent_Done */,
|
||||
DecodeRequest* aRequest /* = nullptr */)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsRefPtr<DecodeRequest> request;
|
||||
if (aRequest) {
|
||||
request = aRequest;
|
||||
} else {
|
||||
request = aImage->mDecodeRequest;
|
||||
}
|
||||
|
||||
// Ensure that, if the decoder is the last reference to the image, we don't
|
||||
// destroy it by destroying the decoder.
|
||||
nsRefPtr<RasterImage> image = aImage;
|
||||
|
||||
bool done = false;
|
||||
|
||||
if (image->mDecoder) {
|
||||
if (request && request->mChunkCount && !image->mDecoder->IsSizeDecode()) {
|
||||
Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, request->mChunkCount);
|
||||
}
|
||||
|
||||
// If the decode finished, tell the image and shut down the decoder.
|
||||
if (image->mDecoder->GetDecodeDone() || image->IsDecodeFinished() ||
|
||||
aIntent != eShutdownIntent_Done) {
|
||||
done = true;
|
||||
|
||||
// Hold on to a reference to the decoder until we're done with it
|
||||
nsRefPtr<Decoder> decoder = image->mDecoder;
|
||||
|
||||
// We need to shut down the decoder first, in order to ensure all
|
||||
// decoding routines have been finished.
|
||||
nsresult rv = image->ShutdownDecoder(aIntent);
|
||||
if (NS_FAILED(rv)) {
|
||||
image->DoError();
|
||||
}
|
||||
|
||||
// Do some telemetry if this isn't a size decode.
|
||||
if (request && !decoder->IsSizeDecode()) {
|
||||
Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
|
||||
int32_t(request->mDecodeTime.ToMicroseconds()));
|
||||
|
||||
// We record the speed for only some decoders. The rest have
|
||||
// SpeedHistogram return HistogramCount.
|
||||
Telemetry::ID id = decoder->SpeedHistogram();
|
||||
if (id < Telemetry::HistogramCount) {
|
||||
int32_t KBps = int32_t(request->mImage->mBytesDecoded /
|
||||
(1024 * request->mDecodeTime.ToSeconds()));
|
||||
Telemetry::Accumulate(id, KBps);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If it have any frames, tell the image what happened to the most recent
|
||||
// one. This will be better when frames are published to decoders instead of
|
||||
// decoders requesting them.
|
||||
if (image->GetNumFrames()) {
|
||||
nsIntRect rect;
|
||||
if (request) {
|
||||
rect = request->mStatusTracker->GetInvalidRect();
|
||||
} else {
|
||||
rect = image->CurrentStatusTracker().GetInvalidRect();
|
||||
}
|
||||
aImage->FrameUpdated(image->GetNumFrames() - 1, rect);
|
||||
}
|
||||
|
||||
// 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->SyncAndSyncNotifyDifference(request->mStatusTracker);
|
||||
} else {
|
||||
image->mStatusTracker->SyncNotifyDecodeState();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ RasterImage::DecodeWorker*
|
||||
RasterImage::DecodeWorker::Singleton()
|
||||
{
|
||||
@ -3363,13 +3454,33 @@ RasterImage::DecodeWorker::Singleton()
|
||||
return sSingleton;
|
||||
}
|
||||
|
||||
RasterImage::DecodeWorker::~DecodeWorker()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodeWorker on main thread!");
|
||||
|
||||
// Shut down all the decoders since we're going away.
|
||||
DecodeRequest* request = mASAPDecodeRequests.getFirst();
|
||||
while (request) {
|
||||
RasterImage::FinishedSomeDecoding(request->mImage, eShutdownIntent_NotNeeded);
|
||||
|
||||
request = request->getNext();
|
||||
}
|
||||
|
||||
request = mNormalDecodeRequests.getFirst();
|
||||
while (request) {
|
||||
RasterImage::FinishedSomeDecoding(request->mImage, eShutdownIntent_NotNeeded);
|
||||
|
||||
request = request->getNext();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::DecodeWorker::MarkAsASAP(RasterImage* aImg)
|
||||
{
|
||||
// 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) {
|
||||
CreateRequestForImage(aImg);
|
||||
if (!aImg->mDecodeRequest) {
|
||||
aImg->mDecodeRequest = new DecodeRequest(aImg);
|
||||
}
|
||||
|
||||
DecodeRequest* request = aImg->mDecodeRequest;
|
||||
@ -3401,7 +3512,7 @@ RasterImage::DecodeWorker::MarkAsASAP(RasterImage* aImg)
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::DecodeWorker::AddDecodeRequest(DecodeRequest* aRequest)
|
||||
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
|
||||
@ -3409,6 +3520,8 @@ RasterImage::DecodeWorker::AddDecodeRequest(DecodeRequest* aRequest)
|
||||
return;
|
||||
}
|
||||
|
||||
aRequest->mBytesToDecode = bytesToDecode;
|
||||
|
||||
if (aRequest->mIsASAP) {
|
||||
mASAPDecodeRequests.insertBack(aRequest);
|
||||
} else {
|
||||
@ -3416,27 +3529,22 @@ RasterImage::DecodeWorker::AddDecodeRequest(DecodeRequest* aRequest)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::DecodeWorker::CreateRequestForImage(RasterImage* aImg)
|
||||
{
|
||||
aImg->mDecodeRequest = new DecodeRequest(aImg);
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::DecodeWorker::RequestDecode(RasterImage* aImg)
|
||||
{
|
||||
if (!aImg->mDecodeRequest) {
|
||||
CreateRequestForImage(aImg);
|
||||
}
|
||||
MOZ_ASSERT(aImg->mDecoder);
|
||||
|
||||
AddDecodeRequest(aImg->mDecodeRequest);
|
||||
AddDecodeRequest(aImg->mDecodeRequest, aImg->mSourceData.Length() - aImg->mBytesDecoded);
|
||||
EnsurePendingInEventLoop();
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::DecodeWorker::DecodeABitOf(RasterImage* aImg)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
DecodeSomeOfImage(aImg);
|
||||
RasterImage::FinishedSomeDecoding(aImg);
|
||||
|
||||
// If we aren't yet finished decoding and we have more data in hand, add
|
||||
// this request to the back of the priority list.
|
||||
@ -3466,7 +3574,6 @@ RasterImage::DecodeWorker::StopDecoding(RasterImage* aImg)
|
||||
if (aImg->mDecodeRequest->isInList()) {
|
||||
aImg->mDecodeRequest->remove();
|
||||
}
|
||||
aImg->mDecodeRequest = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3492,16 +3599,29 @@ RasterImage::DecodeWorker::Run()
|
||||
|
||||
// This has to be a strong pointer, because DecodeSomeOfImage may destroy
|
||||
// image->mDecoder, which may be holding the only other reference to image.
|
||||
RefPtr<RasterImage> image = request->mImage;
|
||||
DecodeSomeOfImage(image);
|
||||
nsRefPtr<RasterImage> image = request->mImage;
|
||||
uint32_t oldCount = image->mDecoder->GetFrameCount();
|
||||
uint32_t oldByteCount = image->mBytesDecoded;
|
||||
|
||||
DecodeSomeOfImage(image, DECODE_TYPE_NORMAL, request->mBytesToDecode);
|
||||
|
||||
uint32_t bytesDecoded = image->mBytesDecoded - oldByteCount;
|
||||
|
||||
// If we aren't yet finished decoding and we have more data in hand, add
|
||||
// this request to the back of the list.
|
||||
if (image->mDecoder &&
|
||||
!image->mError &&
|
||||
!image->IsDecodeFinished() &&
|
||||
image->mSourceData.Length() > image->mBytesDecoded) {
|
||||
AddDecodeRequest(request);
|
||||
bytesDecoded < request->mBytesToDecode) {
|
||||
AddDecodeRequest(request, request->mBytesToDecode - bytesDecoded);
|
||||
|
||||
// If we have a new frame, let everybody know about it.
|
||||
if (image->mDecoder->GetFrameCount() != oldCount) {
|
||||
DecodeDoneWorker::DidSomeDecoding(image, request);
|
||||
}
|
||||
} else {
|
||||
// Nothing more for us to do - let everyone know what happened.
|
||||
DecodeDoneWorker::DidSomeDecoding(image, request);
|
||||
}
|
||||
|
||||
} while ((TimeStamp::Now() - eventStart).ToMilliseconds() <= gMaxMSBeforeYield);
|
||||
@ -3520,13 +3640,19 @@ RasterImage::DecodeWorker::Run()
|
||||
nsresult
|
||||
RasterImage::DecodeWorker::DecodeUntilSizeAvailable(RasterImage* aImg)
|
||||
{
|
||||
return DecodeSomeOfImage(aImg, DECODE_TYPE_UNTIL_SIZE);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsresult rv = DecodeSomeOfImage(aImg, DECODE_TYPE_UNTIL_SIZE);
|
||||
|
||||
RasterImage::FinishedSomeDecoding(aImg);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
RasterImage::DecodeWorker::DecodeSomeOfImage(
|
||||
RasterImage* aImg,
|
||||
DecodeType aDecodeType /* = DECODE_TYPE_NORMAL */)
|
||||
RasterImage::DecodeWorker::DecodeSomeOfImage(RasterImage* aImg,
|
||||
DecodeType aDecodeType /* = DECODE_TYPE_NORMAL */,
|
||||
uint32_t bytesToDecode /* = 0 */)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aImg->mInitialized,
|
||||
"Worker active for uninitialized container!");
|
||||
@ -3555,6 +3681,10 @@ RasterImage::DecodeWorker::DecodeSomeOfImage(
|
||||
maxBytes = gDecodeBytesAtATime;
|
||||
}
|
||||
|
||||
if (bytesToDecode == 0) {
|
||||
bytesToDecode = aImg->mSourceData.Length() - aImg->mBytesDecoded;
|
||||
}
|
||||
|
||||
int32_t chunkCount = 0;
|
||||
TimeStamp start = TimeStamp::Now();
|
||||
TimeStamp deadline = start + TimeDuration::FromMilliseconds(gMaxMSBeforeYield);
|
||||
@ -3565,31 +3695,28 @@ RasterImage::DecodeWorker::DecodeSomeOfImage(
|
||||
// * we're an UNTIL_SIZE decode and we get the size, or
|
||||
// * we run out of time.
|
||||
while (aImg->mSourceData.Length() > aImg->mBytesDecoded &&
|
||||
bytesToDecode > 0 &&
|
||||
!aImg->IsDecodeFinished() &&
|
||||
!(aDecodeType == DECODE_TYPE_UNTIL_SIZE && aImg->mHasSize)) {
|
||||
chunkCount++;
|
||||
nsresult rv = aImg->DecodeSomeData(maxBytes);
|
||||
uint32_t chunkSize = std::min(bytesToDecode, maxBytes);
|
||||
nsresult rv = aImg->DecodeSomeData(chunkSize);
|
||||
if (NS_FAILED(rv)) {
|
||||
aImg->DoError();
|
||||
return rv;
|
||||
}
|
||||
|
||||
bytesToDecode -= chunkSize;
|
||||
|
||||
// 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)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!aImg->mDecodeRequest) {
|
||||
MOZ_ASSERT(aDecodeType == DECODE_TYPE_UNTIL_SIZE);
|
||||
}
|
||||
|
||||
if (aImg->mDecodeRequest) {
|
||||
aImg->mDecodeRequest->mDecodeTime += (TimeStamp::Now() - start);
|
||||
}
|
||||
|
||||
if (chunkCount && !aImg->mDecoder->IsSizeDecode()) {
|
||||
Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, chunkCount);
|
||||
aImg->mDecodeRequest->mChunkCount += chunkCount;
|
||||
}
|
||||
|
||||
// Flush invalidations (and therefore paint) now that we've decoded all the
|
||||
@ -3617,30 +3744,30 @@ RasterImage::DecodeWorker::DecodeSomeOfImage(
|
||||
aImg->mInDecoder = false;
|
||||
}
|
||||
|
||||
// If the decode finished, shut down the decoder.
|
||||
if (aImg->mDecoder && aImg->IsDecodeFinished()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Do some telemetry if this isn't a size decode.
|
||||
DecodeRequest* request = aImg->mDecodeRequest;
|
||||
if (request && !aImg->mDecoder->IsSizeDecode()) {
|
||||
Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
|
||||
int32_t(request->mDecodeTime.ToMicroseconds()));
|
||||
RasterImage::DecodeDoneWorker::DecodeDoneWorker(RasterImage* image, DecodeRequest* request)
|
||||
: mImage(image)
|
||||
, mRequest(request)
|
||||
{}
|
||||
|
||||
// We record the speed for only some decoders. The rest have
|
||||
// SpeedHistogram return HistogramCount.
|
||||
Telemetry::ID id = aImg->mDecoder->SpeedHistogram();
|
||||
if (id < Telemetry::HistogramCount) {
|
||||
int32_t KBps = int32_t(request->mImage->mBytesDecoded /
|
||||
(1024 * request->mDecodeTime.ToSeconds()));
|
||||
Telemetry::Accumulate(id, KBps);
|
||||
}
|
||||
}
|
||||
void
|
||||
RasterImage::DecodeDoneWorker::DidSomeDecoding(RasterImage* image, DecodeRequest* request)
|
||||
{
|
||||
nsCOMPtr<DecodeDoneWorker> worker = new DecodeDoneWorker(image, request);
|
||||
NS_DispatchToMainThread(worker);
|
||||
}
|
||||
|
||||
nsresult rv = aImg->ShutdownDecoder(RasterImage::eShutdownIntent_Done);
|
||||
if (NS_FAILED(rv)) {
|
||||
aImg->DoError();
|
||||
return rv;
|
||||
}
|
||||
NS_IMETHODIMP
|
||||
RasterImage::DecodeDoneWorker::Run()
|
||||
{
|
||||
RasterImage::FinishedSomeDecoding(mImage, 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);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -334,6 +334,15 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
imgStatusTracker& CurrentStatusTracker()
|
||||
{
|
||||
if (mDecodeRequest) {
|
||||
return *mDecodeRequest->mStatusTracker;
|
||||
} else {
|
||||
return *mStatusTracker;
|
||||
}
|
||||
}
|
||||
|
||||
struct Anim
|
||||
{
|
||||
//! Area of the first frame that needs to be redrawn on subsequent loops.
|
||||
@ -381,16 +390,28 @@ private:
|
||||
{
|
||||
DecodeRequest(RasterImage* aImage)
|
||||
: mImage(aImage)
|
||||
, mBytesToDecode(0)
|
||||
, mChunkCount(0)
|
||||
, mIsASAP(false)
|
||||
{
|
||||
mStatusTracker = aImage->mStatusTracker->CloneForRecording();
|
||||
}
|
||||
|
||||
// The status tracker that is associated with a given decode request, to
|
||||
// ensure their lifetimes are linked.
|
||||
nsRefPtr<imgStatusTracker> mStatusTracker;
|
||||
|
||||
RasterImage* mImage;
|
||||
|
||||
uint32_t mBytesToDecode;
|
||||
|
||||
/* Keeps track of how much time we've burned decoding this particular decode
|
||||
* request. */
|
||||
TimeDuration mDecodeTime;
|
||||
|
||||
/* The number of chunks it took to decode this image. */
|
||||
int32_t mChunkCount;
|
||||
|
||||
/* True if we need to handle this decode as soon as possible. */
|
||||
bool mIsASAP;
|
||||
};
|
||||
@ -459,6 +480,8 @@ private:
|
||||
|
||||
NS_IMETHOD Run();
|
||||
|
||||
virtual ~DecodeWorker();
|
||||
|
||||
private: /* statics */
|
||||
static StaticRefPtr<DecodeWorker> sSingleton;
|
||||
|
||||
@ -472,7 +495,7 @@ private:
|
||||
|
||||
/* 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);
|
||||
void AddDecodeRequest(DecodeRequest* aRequest, uint32_t bytesToDecode);
|
||||
|
||||
enum DecodeType {
|
||||
DECODE_TYPE_NORMAL,
|
||||
@ -480,9 +503,11 @@ private:
|
||||
};
|
||||
|
||||
/* Decode some chunks of the given image. If aDecodeType is UNTIL_SIZE,
|
||||
* decode until we have the image's size, then stop. */
|
||||
* decode until we have the image's size, then stop. If bytesToDecode is
|
||||
* non-0, at most bytesToDecode bytes will be decoded. */
|
||||
nsresult DecodeSomeOfImage(RasterImage* aImg,
|
||||
DecodeType aDecodeType = DECODE_TYPE_NORMAL);
|
||||
DecodeType aDecodeType = DECODE_TYPE_NORMAL,
|
||||
uint32_t bytesToDecode = 0);
|
||||
|
||||
/* Create a new DecodeRequest suitable for doing some decoding and set it
|
||||
* as aImg's mDecodeRequest. */
|
||||
@ -498,6 +523,33 @@ private:
|
||||
bool mPendingInEventLoop;
|
||||
};
|
||||
|
||||
class DecodeDoneWorker : public nsRunnable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Called by the DecodeWorker 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
|
||||
* applied to the image.
|
||||
*/
|
||||
static void DidSomeDecoding(RasterImage* image, DecodeRequest* request);
|
||||
|
||||
NS_IMETHOD Run();
|
||||
|
||||
private: /* methods */
|
||||
DecodeDoneWorker(RasterImage* image, DecodeRequest* request);
|
||||
|
||||
private: /* members */
|
||||
|
||||
nsRefPtr<RasterImage> mImage;
|
||||
nsRefPtr<DecodeRequest> mRequest;
|
||||
};
|
||||
|
||||
static void FinishedSomeDecoding(RasterImage* image,
|
||||
eShutdownIntent intent = eShutdownIntent_Done,
|
||||
DecodeRequest* request = nullptr);
|
||||
|
||||
void DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
|
||||
gfxContext *aContext,
|
||||
gfxPattern::GraphicsFilter aFilter,
|
||||
@ -567,7 +619,7 @@ private:
|
||||
LockImage();
|
||||
|
||||
// Notify our observers that we are starting animation.
|
||||
mStatusTracker->RecordImageIsAnimated();
|
||||
CurrentStatusTracker().RecordImageIsAnimated();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -285,17 +285,17 @@ private:
|
||||
|
||||
imgStatusTracker::imgStatusTracker(Image* aImage)
|
||||
: mImage(aImage),
|
||||
mTrackerObserver(new imgStatusTrackerNotifyingObserver(this)),
|
||||
mState(0),
|
||||
mImageStatus(imgIRequest::STATUS_NONE),
|
||||
mIsMultipart(false),
|
||||
mHadLastPart(false)
|
||||
{}
|
||||
{
|
||||
mTrackerObserver = new imgStatusTrackerObserver(this);
|
||||
}
|
||||
|
||||
// Private, used only by CloneForRecording.
|
||||
imgStatusTracker::imgStatusTracker(const imgStatusTracker& aOther)
|
||||
: mImage(aOther.mImage),
|
||||
mTrackerObserver(new imgStatusTrackerNotifyingObserver(this)),
|
||||
mState(aOther.mState),
|
||||
mImageStatus(aOther.mImageStatus),
|
||||
mIsMultipart(aOther.mIsMultipart),
|
||||
@ -307,7 +307,9 @@ imgStatusTracker::imgStatusTracker(const imgStatusTracker& aOther)
|
||||
// object
|
||||
// - mConsumers, because we don't need to talk to consumers
|
||||
// - mInvalidRect, because the point of it is to be fired off and reset
|
||||
{}
|
||||
{
|
||||
mTrackerObserver = new imgStatusTrackerObserver(this);
|
||||
}
|
||||
|
||||
imgStatusTracker::~imgStatusTracker()
|
||||
{}
|
||||
@ -361,6 +363,11 @@ class imgRequestNotifyRunnable : public nsRunnable
|
||||
mProxies.AppendElement(aRequestProxy);
|
||||
}
|
||||
|
||||
void RemoveProxy(imgRequestProxy* aRequestProxy)
|
||||
{
|
||||
mProxies.RemoveElement(aRequestProxy);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class imgStatusTracker;
|
||||
|
||||
@ -440,26 +447,37 @@ imgStatusTracker::NotifyCurrentState(imgRequestProxy* proxy)
|
||||
NS_DispatchToCurrentThread(ev);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
imgStatusTracker::SyncNotifyState(imgRequestProxy* proxy, bool hasImage, uint32_t state, nsIntRect& dirtyRect, bool hadLastPart)
|
||||
{
|
||||
nsCOMPtr<imgIRequest> kungFuDeathGrip(proxy);
|
||||
#define NOTIFY_IMAGE_OBSERVERS(func) \
|
||||
do { \
|
||||
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(proxies); \
|
||||
while (iter.HasMore()) { \
|
||||
nsRefPtr<imgRequestProxy> proxy = iter.GetNext(); \
|
||||
if (!proxy->NotificationsDeferred()) { \
|
||||
proxy->func; \
|
||||
} \
|
||||
} \
|
||||
} while (false);
|
||||
|
||||
/* static */ void
|
||||
imgStatusTracker::SyncNotifyState(nsTObserverArray<imgRequestProxy*>& proxies,
|
||||
bool hasImage, uint32_t state,
|
||||
nsIntRect& dirtyRect, bool hadLastPart)
|
||||
{
|
||||
// OnStartRequest
|
||||
if (state & stateRequestStarted)
|
||||
proxy->OnStartRequest();
|
||||
NOTIFY_IMAGE_OBSERVERS(OnStartRequest());
|
||||
|
||||
// OnStartContainer
|
||||
if (state & stateHasSize)
|
||||
proxy->OnStartContainer();
|
||||
NOTIFY_IMAGE_OBSERVERS(OnStartContainer());
|
||||
|
||||
// OnStartDecode
|
||||
if (state & stateDecodeStarted)
|
||||
proxy->OnStartDecode();
|
||||
NOTIFY_IMAGE_OBSERVERS(OnStartDecode());
|
||||
|
||||
// BlockOnload
|
||||
if (state & stateBlockingOnload)
|
||||
proxy->BlockOnload();
|
||||
NOTIFY_IMAGE_OBSERVERS(BlockOnload());
|
||||
|
||||
if (hasImage) {
|
||||
// OnFrameUpdate
|
||||
@ -467,60 +485,79 @@ imgStatusTracker::SyncNotifyState(imgRequestProxy* proxy, bool hasImage, uint32_
|
||||
// vector images, true for raster images that have decoded at
|
||||
// least one frame) then send OnFrameUpdate.
|
||||
if (!dirtyRect.IsEmpty())
|
||||
proxy->OnFrameUpdate(&dirtyRect);
|
||||
NOTIFY_IMAGE_OBSERVERS(OnFrameUpdate(&dirtyRect));
|
||||
|
||||
if (state & stateFrameStopped)
|
||||
proxy->OnStopFrame();
|
||||
NOTIFY_IMAGE_OBSERVERS(OnStopFrame());
|
||||
|
||||
// OnImageIsAnimated
|
||||
if (state & stateImageIsAnimated)
|
||||
proxy->OnImageIsAnimated();
|
||||
NOTIFY_IMAGE_OBSERVERS(OnImageIsAnimated());
|
||||
}
|
||||
|
||||
if (state & stateDecodeStopped) {
|
||||
NS_ABORT_IF_FALSE(hasImage, "stopped decoding without ever having an image?");
|
||||
proxy->OnStopDecode();
|
||||
NOTIFY_IMAGE_OBSERVERS(OnStopDecode());
|
||||
}
|
||||
|
||||
if (state & stateRequestStopped) {
|
||||
proxy->OnStopRequest(hadLastPart);
|
||||
NOTIFY_IMAGE_OBSERVERS(OnStopRequest(hadLastPart));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
imgStatusTracker::SyncAndSyncNotifyDifference(imgStatusTracker* other)
|
||||
{
|
||||
uint32_t diffState = ~mState & other->mState;
|
||||
// We must not modify or notify for the begin-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;
|
||||
bool foundError = (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 |= other->mState;
|
||||
mState |= diffState | loadState;
|
||||
if (unblockedOnload) {
|
||||
mState &= ~stateBlockingOnload;
|
||||
}
|
||||
mImageStatus = other->mImageStatus;
|
||||
mIsMultipart = other->mIsMultipart;
|
||||
mHadLastPart = other->mHadLastPart;
|
||||
|
||||
// Now that we've updated our state, notify all the consumers about the state
|
||||
// that's changed.
|
||||
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
|
||||
while (iter.HasMore()) {
|
||||
imgRequestProxy* proxy = iter.GetNext();
|
||||
// The error state is sticky and overrides all other bits.
|
||||
if (mImageStatus == imgIRequest::STATUS_ERROR ||
|
||||
other->mImageStatus == imgIRequest::STATUS_ERROR) {
|
||||
mImageStatus = imgIRequest::STATUS_ERROR;
|
||||
} else {
|
||||
mImageStatus |= other->mImageStatus;
|
||||
|
||||
if (!proxy->NotificationsDeferred()) {
|
||||
SyncNotifyState(proxy, mImage, diffState, mInvalidRect, other->mHadLastPart);
|
||||
// Unset the bits that can get unset as part of the decoding process.
|
||||
if (!(other->mImageStatus & imgIRequest::STATUS_DECODE_STARTED)) {
|
||||
mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED;
|
||||
}
|
||||
}
|
||||
|
||||
SyncNotifyState(mConsumers, !!mImage, diffState, mInvalidRect, mHadLastPart);
|
||||
|
||||
if (unblockedOnload) {
|
||||
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
|
||||
while (iter.HasMore()) {
|
||||
// Hold on to a reference to this proxy, since notifying the state can
|
||||
// cause it to disappear.
|
||||
nsRefPtr<imgRequestProxy> proxy = iter.GetNext();
|
||||
|
||||
if (!proxy->NotificationsDeferred()) {
|
||||
SendUnblockOnload(proxy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the other rectangle for another go, if it's going to have one.
|
||||
// Reset the invalid rectangles for another go.
|
||||
other->mInvalidRect.SetEmpty();
|
||||
mInvalidRect.SetEmpty();
|
||||
|
||||
if (foundError) {
|
||||
FireFailureNotification();
|
||||
@ -552,7 +589,17 @@ imgStatusTracker::SyncNotify(imgRequestProxy* proxy)
|
||||
r = mImage->FrameRect(imgIContainer::FRAME_CURRENT);
|
||||
}
|
||||
|
||||
SyncNotifyState(proxy, !!mImage, mState, r, mHadLastPart);
|
||||
nsTObserverArray<imgRequestProxy*> array;
|
||||
array.AppendElement(proxy);
|
||||
SyncNotifyState(array, !!mImage, mState, r, mHadLastPart);
|
||||
}
|
||||
|
||||
void
|
||||
imgStatusTracker::SyncNotifyDecodeState()
|
||||
{
|
||||
SyncNotifyState(mConsumers, !!mImage, mState & ~stateRequestStarted, mInvalidRect, mHadLastPart);
|
||||
|
||||
mInvalidRect.SetEmpty();
|
||||
}
|
||||
|
||||
void
|
||||
@ -591,8 +638,18 @@ imgStatusTracker::RemoveConsumer(imgRequestProxy* aConsumer, nsresult aStatus)
|
||||
|
||||
// Consumers can get confused if they don't get all the proper teardown
|
||||
// notifications. Part ways on good terms.
|
||||
if (removed)
|
||||
if (removed && !aConsumer->NotificationsDeferred()) {
|
||||
EmulateRequestFinished(aConsumer, aStatus);
|
||||
}
|
||||
|
||||
// Make sure we don't give callbacks to a consumer that isn't interested in
|
||||
// them any more.
|
||||
imgRequestNotifyRunnable* runnable = static_cast<imgRequestNotifyRunnable*>(mRequestRunnable.get());
|
||||
if (aConsumer->NotificationsDeferred() && runnable) {
|
||||
runnable->RemoveProxy(aConsumer);
|
||||
aConsumer->SetNotificationsDeferred(false);
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
@ -737,7 +794,7 @@ imgStatusTracker::RecordImageIsAnimated()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mImage,
|
||||
"RecordImageIsAnimated called before we have an Image");
|
||||
mImageStatus |= stateImageIsAnimated;
|
||||
mState |= stateImageIsAnimated;
|
||||
}
|
||||
|
||||
void
|
||||
@ -754,6 +811,16 @@ imgStatusTracker::SendUnlockedDraw(imgRequestProxy* aProxy)
|
||||
aProxy->OnUnlockedDraw();
|
||||
}
|
||||
|
||||
void
|
||||
imgStatusTracker::OnUnlockedDraw()
|
||||
{
|
||||
RecordUnlockedDraw();
|
||||
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
|
||||
while (iter.HasMore()) {
|
||||
SendUnlockedDraw(iter.GetNext());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
imgStatusTracker::RecordFrameChanged(const nsIntRect* aDirtyRect)
|
||||
{
|
||||
@ -861,6 +928,18 @@ imgStatusTracker::OnDiscard()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
imgStatusTracker::FrameChanged(const nsIntRect* aDirtyRect)
|
||||
{
|
||||
RecordFrameChanged(aDirtyRect);
|
||||
|
||||
/* notify the kids */
|
||||
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
|
||||
while (iter.HasMore()) {
|
||||
SendFrameChanged(iter.GetNext(), aDirtyRect);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
imgStatusTracker::OnDataAvailable()
|
||||
{
|
||||
@ -889,7 +968,6 @@ imgStatusTracker::SendBlockOnload(imgRequestProxy* aProxy)
|
||||
void
|
||||
imgStatusTracker::RecordUnblockOnload()
|
||||
{
|
||||
MOZ_ASSERT(mState & stateBlockingOnload);
|
||||
mState &= ~stateBlockingOnload;
|
||||
}
|
||||
|
||||
@ -919,6 +997,8 @@ imgStatusTracker::MaybeUnblockOnload()
|
||||
void
|
||||
imgStatusTracker::FireFailureNotification()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Some kind of problem has happened with image decoding.
|
||||
// Report the URI to net:failed-to-process-uri-conent observers.
|
||||
nsCOMPtr<nsIURI> uri = GetImage()->GetURI();
|
||||
|
@ -89,6 +89,11 @@ public:
|
||||
// OnStartRequest).
|
||||
void SyncNotify(imgRequestProxy* proxy);
|
||||
|
||||
// "Replays" all of the decode notifications (i.e., not
|
||||
// OnStartRequest/OnStopRequest) that have happened to us to all of our
|
||||
// non-deferred proxies.
|
||||
void SyncNotifyDecodeState();
|
||||
|
||||
// Send some notifications that would be necessary to make |proxy| believe
|
||||
// the request is finished downloading and decoding. We only send
|
||||
// OnStopRequest and UnblockOnload, and only if necessary.
|
||||
@ -161,6 +166,8 @@ public:
|
||||
void OnDataAvailable();
|
||||
void OnStopRequest(bool aLastPart, nsresult aStatus);
|
||||
void OnDiscard();
|
||||
void FrameChanged(const nsIntRect* aDirtyRect);
|
||||
void OnUnlockedDraw();
|
||||
|
||||
/* non-virtual imgIOnloadBlocker methods */
|
||||
// NB: If UnblockOnload is sent, and then we are asked to replay the
|
||||
@ -181,6 +188,9 @@ public:
|
||||
inline imgDecoderObserver* GetDecoderObserver() { return mTrackerObserver.get(); }
|
||||
|
||||
imgStatusTracker* CloneForRecording();
|
||||
void SyncAndSyncNotifyDifference(imgStatusTracker* other);
|
||||
|
||||
nsIntRect GetInvalidRect() const { return mInvalidRect; }
|
||||
|
||||
private:
|
||||
friend class imgStatusNotifyRunnable;
|
||||
@ -191,11 +201,9 @@ private:
|
||||
|
||||
void FireFailureNotification();
|
||||
|
||||
static void SyncNotifyState(imgRequestProxy* proxy, bool hasImage,
|
||||
uint32_t state, nsIntRect& dirtyRect,
|
||||
bool hadLastPart);
|
||||
|
||||
void SyncAndSyncNotifyDifference(imgStatusTracker* other);
|
||||
static void SyncNotifyState(nsTObserverArray<imgRequestProxy*>& proxies,
|
||||
bool hasImage, uint32_t state,
|
||||
nsIntRect& dirtyRect, bool hadLastPart);
|
||||
|
||||
nsCOMPtr<nsIRunnable> mRequestRunnable;
|
||||
|
||||
|
@ -48,6 +48,7 @@ function checkClone(other_listener, aRequest)
|
||||
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
|
||||
.createScriptedObserver(listener);
|
||||
var clone = aRequest.clone(outer);
|
||||
requests.push(clone);
|
||||
}
|
||||
|
||||
// Ensure that all the callbacks were called on aRequest.
|
||||
@ -73,6 +74,7 @@ function secondLoadDone(oldlistener, aRequest)
|
||||
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
|
||||
.createScriptedObserver(listener);
|
||||
var staticrequestclone = staticrequest.clone(outer);
|
||||
requests.push(staticrequestclone);
|
||||
} catch(e) {
|
||||
// We can't create a static request. Most likely the request we started
|
||||
// with didn't load successfully.
|
||||
@ -205,8 +207,17 @@ function startImageCallback(otherCb)
|
||||
|
||||
var gCurrentLoader;
|
||||
|
||||
function cleanup()
|
||||
{
|
||||
for (var i = 0; i < requests.length; ++i) {
|
||||
requests[i].cancelAndForgetObserver(0);
|
||||
}
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
do_register_cleanup(cleanup);
|
||||
|
||||
gCurrentLoader = Cc["@mozilla.org/image/loader;1"].createInstance(Ci.imgILoader);
|
||||
|
||||
do_test_pending();
|
||||
|
@ -45,9 +45,11 @@ function ImageListener(start_callback, stop_callback)
|
||||
{
|
||||
do_check_false(this.synchronous);
|
||||
|
||||
// We have to cancel the request when we're done with it to break any
|
||||
// reference loops!
|
||||
aRequest.cancelAndForgetObserver(0);
|
||||
try {
|
||||
aRequest.requestDecode();
|
||||
} catch (e) {
|
||||
do_print("requestDecode threw " + e);
|
||||
}
|
||||
|
||||
this.state |= LOAD_COMPLETE;
|
||||
|
||||
|
@ -94,7 +94,16 @@ function run_loadImage_tests() {
|
||||
});
|
||||
}
|
||||
|
||||
function cleanup()
|
||||
{
|
||||
for (var i = 0; i < requests.length; ++i) {
|
||||
requests[i].cancelAndForgetObserver(0);
|
||||
}
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
do_register_cleanup(cleanup);
|
||||
|
||||
do_test_pending();
|
||||
|
||||
// We create a public channel that loads an image, then an identical
|
||||
|
Loading…
Reference in New Issue
Block a user