mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
e7535dc94b
- Bug 1187401 (Part 1) - Simplify the condition that determines whether we set RasterImage::mHasBeenDecoded. r=tn (69be36e7ef) - Bug 1187401 (Part 2) - Eliminate the nsresult return value from RasterImage::SetMetadata, since it's not used anymore. r=tn (dc521c4b9f) - Bug 1187401 (Part 3) - For consistency, call DoError if SetMetadata sees a negative size. r=tn (d9ca8ec91b) - Bug 1207183 - micro-optimize removing work items from DecodePool's queues; r=seth (6bd2717e3a) - No bug - Fix out-of-date comment in Decoder.cpp. r=me (54fdbbd444) - Bug 1181324 - Eliminate the duplicate mRefCnt member in MultipartImage. r=seth (c4512a443e) - Bug 1180105 - Do not leak the SourceSurface returned from imgIContainer::GetFrame in BlockUntilDecodedAndFinishObserving; r=seth (1cadbffc53) - Bug 1181909 - Fix potential null dereference in NextPartObserver. r=tn (cfd8ad0119) - Bug 108603 - Remove NS_IMPL_QUERY_INTERFACE_INHERITED0. r=mccr8, r=froydnj (4bfa6771fc) - Bug 1159502 - Don't block onload for multipart images. r=tn (8b50eadf39) - Bug 1200413 - Part 1: Re-write RefCountedInsideLambdaChecker to use captures instead of checking for DeclRef instances, r=ehsan (80ef99efe2) - Bug 1200413 - Part 2: Make lambdas in ProgressTracker.cpp capture strong references, r=seth (9e4d96dffa) - Bug 1194557 - Ensure that if the image was locked before RecoverFromLossOfFrames() was called, it's still locked afterwards. r=tn (ea4dc6ea9f) - code style (ad3773ba42) - Bug 1167590 - Mark imgRequestProxy::mListener as MOZ_UNSAFE_REF. r=seth (946ffaed8a) - Bug 1148397 - Fix data race on imgRequest::mHadInsecureRedirect. r=tanvi (e73d0664f3) - No bug - Tweak formatting of logging statement in imgRequest. r=me (cab2bcb014) - Bug 1180126 - Read content disposition regardless of content type in imgRequest::PrepareForNewPart. r=tn (2934597743) - Bug 1139225 - Followup - Remove duplicate multiPartChannel variable. (7f7f555a0b) - Bug 1141398 - Do not always revalidate image cache entries for file URIs. r=tn (31d73cb508) - Bug 1183563 - Fix incorrect mixed content warning after internal redirects. r=tanvi, r=seth (12a6c8a15f) - Bug 1150127 - Stop leaking windows via imgCacheValidator. r=baku (a7809c5fa7) - bits of Bug 1102048 (Part 20, imgLoader) (b2098c8a5a) - (No bug) - Correct blatantly lying comment in imgLoader.cpp. r=me DONTBUILD (64c42a5b09) - Bug 1160592 - Report image source size again in about:memory. r=dholbert (4e04cf3c3e) - Add an assertion for the first argument of NewImageChannel, no bug (4c8f087a8f) - Bug 1127534 - Remove assertion before creating a channel (r=sicking) (988692dc91) - Bug 1175371 - Make VectorImage wait to deliver LOAD_COMPLETE until its size is available. r=dholbert (3c81e0daff) - Bug 1181323 - Move nsSVGRenderingObserver's isupports/refcounting decl to subclasses, since one subclass (nsSVGFilterReference) already has its own redundant copy of the decl. r=dholbert (6171171c2c) - Bug 1161722 - If we're shutting down, don't warn about untracked unlocked surfaces. r=dholbert (f7e18ce481) - Bug 1170877 - Track how many times the SurfaceCache has overflowed and report it in about:memory. r=dholbert (884176cb1d) - Bug 1161743 - Upgrade 'WARNING: Not expiration-tracking an unlocked surface' to an assertion. r=dholbert (9900169e7b) - Bug 1167557 - Crash when a null surface is passed to SurfaceCache::Insert. r=dholbert (b3c4cf60aa) - remove bypass cache not fonud either in FF nor TF (3ed4056a27) - Missing bit Bug 1102048 (Part 25, header guards) - Make image/src files comply (cb8ed2428f) - No bug - Remove obsolete comment in SourceBuffer.h. r=me (6e9c233448) - coding style (94b7269690) - Bug 1157065 - GFX: 2D: Add Loongson3 MMI helpers. r=jrmuizel (ebce946c91) - reverto to FF52 and TFF settings (e147a8c7b5) - Bug 1134599 - Fix rpi build target. r=jrmuizel, r=shuang (b9722f860c) - Bug 1129147 - Part 1. Take CanvasPath into a separate file, to avoid circular dependency. r=roc (859bcad807) - Bug 1129147 - Part 2. Path option to addHitRegion. r=ehsan r=gw280 (b2ab08a8a1) - Bug 1206076: Use a specialized PersistentBufferProvider for Canvas2D when using a SkiaGL DrawTarget. r=jrmuizel (859589caf8) - Bug 1188752 - Addendum: Make PersistentBufferProviderBasic constructor explicit. r=bustage on a CLOSED TREE (a27a4dc974) - style (72a65dcb26) - Bug 1198574 - Remove unnecessary argument for PersistentBufferProvider. r=bas (dca718bba8) - Bug 1163124 - The initial value of the canvas filter property should be "none". r=roc (59df6a01d8)
526 lines
15 KiB
C++
526 lines
15 KiB
C++
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "Decoder.h"
|
|
|
|
#include "mozilla/gfx/2D.h"
|
|
#include "DecodePool.h"
|
|
#include "GeckoProfiler.h"
|
|
#include "imgIContainer.h"
|
|
#include "nsProxyRelease.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "nsComponentManagerUtils.h"
|
|
#include "mozilla/Telemetry.h"
|
|
|
|
using mozilla::gfx::IntSize;
|
|
using mozilla::gfx::SurfaceFormat;
|
|
|
|
namespace mozilla {
|
|
namespace image {
|
|
|
|
Decoder::Decoder(RasterImage* aImage)
|
|
: mImageData(nullptr)
|
|
, mImageDataLength(0)
|
|
, mColormap(nullptr)
|
|
, mColormapSize(0)
|
|
, mImage(aImage)
|
|
, mProgress(NoProgress)
|
|
, mFrameCount(0)
|
|
, mFailCode(NS_OK)
|
|
, mChunkCount(0)
|
|
, mDecoderFlags(DefaultDecoderFlags())
|
|
, mSurfaceFlags(DefaultSurfaceFlags())
|
|
, mBytesDecoded(0)
|
|
, mInitialized(false)
|
|
, mMetadataDecode(false)
|
|
, mInFrame(false)
|
|
, mDataDone(false)
|
|
, mDecodeDone(false)
|
|
, mDataError(false)
|
|
, mDecodeAborted(false)
|
|
, mShouldReportError(false)
|
|
{ }
|
|
|
|
Decoder::~Decoder()
|
|
{
|
|
MOZ_ASSERT(mProgress == NoProgress || !mImage,
|
|
"Destroying Decoder without taking all its progress changes");
|
|
MOZ_ASSERT(mInvalidRect.IsEmpty() || !mImage,
|
|
"Destroying Decoder without taking all its invalidations");
|
|
mInitialized = false;
|
|
|
|
if (mImage && !NS_IsMainThread()) {
|
|
// Dispatch mImage to main thread to prevent it from being destructed by the
|
|
// decode thread.
|
|
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
|
|
NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
|
|
if (mainThread) {
|
|
// Handle ambiguous nsISupports inheritance.
|
|
RasterImage* rawImg = nullptr;
|
|
mImage.swap(rawImg);
|
|
DebugOnly<nsresult> rv =
|
|
NS_ProxyRelease(mainThread, NS_ISUPPORTS_CAST(ImageResource*, rawImg));
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to proxy release to main thread");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Common implementation of the decoder interface.
|
|
*/
|
|
|
|
void
|
|
Decoder::Init()
|
|
{
|
|
// No re-initializing
|
|
MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!");
|
|
|
|
// It doesn't make sense to decode anything but the first frame if we can't
|
|
// store anything in the SurfaceCache, since only the last frame we decode
|
|
// will be retrievable.
|
|
MOZ_ASSERT(ShouldUseSurfaceCache() || IsFirstFrameDecode());
|
|
|
|
// Implementation-specific initialization
|
|
InitInternal();
|
|
|
|
mInitialized = true;
|
|
}
|
|
|
|
nsresult
|
|
Decoder::Decode(IResumable* aOnResume)
|
|
{
|
|
MOZ_ASSERT(mInitialized, "Should be initialized here");
|
|
MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
|
|
|
|
// If no IResumable was provided, default to |this|.
|
|
IResumable* onResume = aOnResume ? aOnResume : this;
|
|
|
|
// We keep decoding chunks until the decode completes or there are no more
|
|
// chunks available.
|
|
while (!GetDecodeDone() && !HasError()) {
|
|
auto newState = mIterator->AdvanceOrScheduleResume(onResume);
|
|
|
|
if (newState == SourceBufferIterator::WAITING) {
|
|
// We can't continue because the rest of the data hasn't arrived from the
|
|
// network yet. We don't have to do anything special; the
|
|
// SourceBufferIterator will ensure that Decode() gets called again on a
|
|
// DecodePool thread when more data is available.
|
|
return NS_OK;
|
|
}
|
|
|
|
if (newState == SourceBufferIterator::COMPLETE) {
|
|
mDataDone = true;
|
|
|
|
nsresult finalStatus = mIterator->CompletionStatus();
|
|
if (NS_FAILED(finalStatus)) {
|
|
PostDataError();
|
|
}
|
|
|
|
CompleteDecode();
|
|
return finalStatus;
|
|
}
|
|
|
|
MOZ_ASSERT(newState == SourceBufferIterator::READY);
|
|
|
|
Write(mIterator->Data(), mIterator->Length());
|
|
}
|
|
|
|
CompleteDecode();
|
|
return HasError() ? NS_ERROR_FAILURE : NS_OK;
|
|
}
|
|
|
|
void
|
|
Decoder::Resume()
|
|
{
|
|
DecodePool* decodePool = DecodePool::Singleton();
|
|
MOZ_ASSERT(decodePool);
|
|
decodePool->AsyncDecode(this);
|
|
}
|
|
|
|
bool
|
|
Decoder::ShouldSyncDecode(size_t aByteLimit)
|
|
{
|
|
MOZ_ASSERT(aByteLimit > 0);
|
|
MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
|
|
|
|
return mIterator->RemainingBytesIsNoMoreThan(aByteLimit);
|
|
}
|
|
|
|
void
|
|
Decoder::Write(const char* aBuffer, uint32_t aCount)
|
|
{
|
|
PROFILER_LABEL("ImageDecoder", "Write",
|
|
js::ProfileEntry::Category::GRAPHICS);
|
|
|
|
MOZ_ASSERT(aBuffer);
|
|
MOZ_ASSERT(aCount > 0);
|
|
|
|
// We're strict about decoder errors
|
|
MOZ_ASSERT(!HasDecoderError(),
|
|
"Not allowed to make more decoder calls after error!");
|
|
|
|
// Begin recording telemetry data.
|
|
TimeStamp start = TimeStamp::Now();
|
|
mChunkCount++;
|
|
|
|
// Keep track of the total number of bytes written.
|
|
mBytesDecoded += aCount;
|
|
|
|
// If a data error occured, just ignore future data.
|
|
if (HasDataError()) {
|
|
return;
|
|
}
|
|
|
|
if (IsMetadataDecode() && HasSize()) {
|
|
// More data came in since we found the size. We have nothing to do here.
|
|
return;
|
|
}
|
|
|
|
// Pass the data along to the implementation.
|
|
WriteInternal(aBuffer, aCount);
|
|
|
|
// Finish telemetry.
|
|
mDecodeTime += (TimeStamp::Now() - start);
|
|
}
|
|
|
|
void
|
|
Decoder::CompleteDecode()
|
|
{
|
|
// Implementation-specific finalization
|
|
if (!HasError()) {
|
|
FinishInternal();
|
|
} else {
|
|
FinishWithErrorInternal();
|
|
}
|
|
|
|
// If the implementation left us mid-frame, finish that up.
|
|
if (mInFrame && !HasError()) {
|
|
PostFrameStop();
|
|
}
|
|
|
|
// If PostDecodeDone() has not been called, and this decoder wasn't aborted
|
|
// early because of low-memory conditions or losing a race with another
|
|
// decoder, we need to send teardown notifications (and report an error to the
|
|
// console later).
|
|
if (!IsMetadataDecode() && !mDecodeDone && !WasAborted()) {
|
|
mShouldReportError = true;
|
|
|
|
// If we only have a data error, we're usable if we have at least one
|
|
// complete frame.
|
|
if (!HasDecoderError() && GetCompleteFrameCount() > 0) {
|
|
// We're usable, so do exactly what we should have when the decoder
|
|
// completed.
|
|
|
|
// Not writing to the entire frame may have left us transparent.
|
|
PostHasTransparency();
|
|
|
|
if (mInFrame) {
|
|
PostFrameStop();
|
|
}
|
|
PostDecodeDone();
|
|
} else {
|
|
// We're not usable. Record some final progress indicating the error.
|
|
if (!IsMetadataDecode()) {
|
|
mProgress |= FLAG_DECODE_COMPLETE;
|
|
}
|
|
mProgress |= FLAG_HAS_ERROR;
|
|
}
|
|
}
|
|
|
|
if (mDecodeDone && !IsMetadataDecode()) {
|
|
MOZ_ASSERT(HasError() || mCurrentFrame, "Should have an error or a frame");
|
|
|
|
// If this image wasn't animated and isn't a transient image, mark its frame
|
|
// as optimizable. We don't support optimizing animated images and
|
|
// optimizing transient images isn't worth it.
|
|
if (!HasAnimation() &&
|
|
!(mDecoderFlags & DecoderFlags::IMAGE_IS_TRANSIENT) &&
|
|
mCurrentFrame) {
|
|
mCurrentFrame->SetOptimizable();
|
|
}
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
Decoder::SetTargetSize(const nsIntSize& aSize)
|
|
{
|
|
// Make sure the size is reasonable.
|
|
if (MOZ_UNLIKELY(aSize.width <= 0 || aSize.height <= 0)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// Create a downscaler that we'll filter our output through.
|
|
mDownscaler.emplace(aSize);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
Decoder::AllocateFrame(uint32_t aFrameNum,
|
|
const nsIntSize& aTargetSize,
|
|
const nsIntRect& aFrameRect,
|
|
gfx::SurfaceFormat aFormat,
|
|
uint8_t aPaletteDepth)
|
|
{
|
|
mCurrentFrame = AllocateFrameInternal(aFrameNum, aTargetSize, aFrameRect,
|
|
aFormat, aPaletteDepth,
|
|
mCurrentFrame.get());
|
|
|
|
if (mCurrentFrame) {
|
|
// Gather the raw pointers the decoders will use.
|
|
mCurrentFrame->GetImageData(&mImageData, &mImageDataLength);
|
|
mCurrentFrame->GetPaletteData(&mColormap, &mColormapSize);
|
|
|
|
if (aFrameNum + 1 == mFrameCount) {
|
|
// If we're past the first frame, PostIsAnimated() should've been called.
|
|
MOZ_ASSERT_IF(mFrameCount > 1, HasAnimation());
|
|
|
|
// Update our state to reflect the new frame
|
|
MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
|
|
mInFrame = true;
|
|
}
|
|
} else {
|
|
PostDataError();
|
|
}
|
|
|
|
return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE;
|
|
}
|
|
|
|
RawAccessFrameRef
|
|
Decoder::AllocateFrameInternal(uint32_t aFrameNum,
|
|
const nsIntSize& aTargetSize,
|
|
const nsIntRect& aFrameRect,
|
|
SurfaceFormat aFormat,
|
|
uint8_t aPaletteDepth,
|
|
imgFrame* aPreviousFrame)
|
|
{
|
|
if (mDataError || NS_FAILED(mFailCode)) {
|
|
return RawAccessFrameRef();
|
|
}
|
|
|
|
if (aFrameNum != mFrameCount) {
|
|
MOZ_ASSERT_UNREACHABLE("Allocating frames out of order");
|
|
return RawAccessFrameRef();
|
|
}
|
|
|
|
if (aTargetSize.width <= 0 || aTargetSize.height <= 0 ||
|
|
aFrameRect.width <= 0 || aFrameRect.height <= 0) {
|
|
NS_WARNING("Trying to add frame with zero or negative size");
|
|
return RawAccessFrameRef();
|
|
}
|
|
|
|
const uint32_t bytesPerPixel = aPaletteDepth == 0 ? 4 : 1;
|
|
if (ShouldUseSurfaceCache() &&
|
|
!SurfaceCache::CanHold(aFrameRect.Size(), bytesPerPixel)) {
|
|
NS_WARNING("Trying to add frame that's too large for the SurfaceCache");
|
|
return RawAccessFrameRef();
|
|
}
|
|
|
|
nsRefPtr<imgFrame> frame = new imgFrame();
|
|
bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
|
|
if (NS_FAILED(frame->InitForDecoder(aTargetSize, aFrameRect, aFormat,
|
|
aPaletteDepth, nonPremult))) {
|
|
NS_WARNING("imgFrame::Init should succeed");
|
|
return RawAccessFrameRef();
|
|
}
|
|
|
|
RawAccessFrameRef ref = frame->RawAccessRef();
|
|
if (!ref) {
|
|
frame->Abort();
|
|
return RawAccessFrameRef();
|
|
}
|
|
|
|
if (ShouldUseSurfaceCache()) {
|
|
InsertOutcome outcome =
|
|
SurfaceCache::Insert(frame, ImageKey(mImage.get()),
|
|
RasterSurfaceKey(aTargetSize,
|
|
mSurfaceFlags,
|
|
aFrameNum));
|
|
if (outcome == InsertOutcome::FAILURE) {
|
|
// We couldn't insert the surface, almost certainly due to low memory. We
|
|
// treat this as a permanent error to help the system recover; otherwise,
|
|
// we might just end up attempting to decode this image again immediately.
|
|
ref->Abort();
|
|
return RawAccessFrameRef();
|
|
} else if (outcome == InsertOutcome::FAILURE_ALREADY_PRESENT) {
|
|
// Another decoder beat us to decoding this frame. We abort this decoder
|
|
// rather than treat this as a real error.
|
|
mDecodeAborted = true;
|
|
ref->Abort();
|
|
return RawAccessFrameRef();
|
|
}
|
|
}
|
|
|
|
nsIntRect refreshArea;
|
|
|
|
if (aFrameNum == 1) {
|
|
MOZ_ASSERT(aPreviousFrame, "Must provide a previous frame when animated");
|
|
aPreviousFrame->SetRawAccessOnly();
|
|
|
|
// If we dispose of the first frame by clearing it, then the first frame's
|
|
// refresh area is all of itself.
|
|
// RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR).
|
|
AnimationData previousFrameData = aPreviousFrame->GetAnimationData();
|
|
if (previousFrameData.mDisposalMethod == DisposalMethod::CLEAR ||
|
|
previousFrameData.mDisposalMethod == DisposalMethod::CLEAR_ALL ||
|
|
previousFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) {
|
|
refreshArea = previousFrameData.mRect;
|
|
}
|
|
}
|
|
|
|
if (aFrameNum > 0) {
|
|
ref->SetRawAccessOnly();
|
|
|
|
// Some GIFs are huge but only have a small area that they animate. We only
|
|
// need to refresh that small area when frame 0 comes around again.
|
|
refreshArea.UnionRect(refreshArea, frame->GetRect());
|
|
}
|
|
|
|
mFrameCount++;
|
|
|
|
if (mImage) {
|
|
mImage->OnAddedFrame(mFrameCount, refreshArea);
|
|
}
|
|
|
|
return ref;
|
|
}
|
|
|
|
/*
|
|
* Hook stubs. Override these as necessary in decoder implementations.
|
|
*/
|
|
|
|
void Decoder::InitInternal() { }
|
|
void Decoder::FinishInternal() { }
|
|
void Decoder::FinishWithErrorInternal() { }
|
|
|
|
/*
|
|
* Progress Notifications
|
|
*/
|
|
|
|
void
|
|
Decoder::PostSize(int32_t aWidth,
|
|
int32_t aHeight,
|
|
Orientation aOrientation /* = Orientation()*/)
|
|
{
|
|
// Validate
|
|
MOZ_ASSERT(aWidth >= 0, "Width can't be negative!");
|
|
MOZ_ASSERT(aHeight >= 0, "Height can't be negative!");
|
|
|
|
// Tell the image
|
|
mImageMetadata.SetSize(aWidth, aHeight, aOrientation);
|
|
|
|
// Record this notification.
|
|
mProgress |= FLAG_SIZE_AVAILABLE;
|
|
}
|
|
|
|
void
|
|
Decoder::PostHasTransparency()
|
|
{
|
|
mProgress |= FLAG_HAS_TRANSPARENCY;
|
|
}
|
|
|
|
void
|
|
Decoder::PostIsAnimated(int32_t aFirstFrameTimeout)
|
|
{
|
|
mProgress |= FLAG_IS_ANIMATED;
|
|
mImageMetadata.SetHasAnimation();
|
|
mImageMetadata.SetFirstFrameTimeout(aFirstFrameTimeout);
|
|
}
|
|
|
|
void
|
|
Decoder::PostFrameStop(Opacity aFrameOpacity
|
|
/* = Opacity::SOME_TRANSPARENCY */,
|
|
DisposalMethod aDisposalMethod
|
|
/* = DisposalMethod::KEEP */,
|
|
int32_t aTimeout /* = 0 */,
|
|
BlendMethod aBlendMethod /* = BlendMethod::OVER */)
|
|
{
|
|
// We should be mid-frame
|
|
MOZ_ASSERT(!IsMetadataDecode(), "Stopping frame during metadata decode");
|
|
MOZ_ASSERT(mInFrame, "Stopping frame when we didn't start one");
|
|
MOZ_ASSERT(mCurrentFrame, "Stopping frame when we don't have one");
|
|
|
|
// Update our state
|
|
mInFrame = false;
|
|
|
|
mCurrentFrame->Finish(aFrameOpacity, aDisposalMethod, aTimeout, aBlendMethod);
|
|
|
|
mProgress |= FLAG_FRAME_COMPLETE;
|
|
|
|
// If we're not sending partial invalidations, then we send an invalidation
|
|
// here when the first frame is complete.
|
|
if (!ShouldSendPartialInvalidations() && !HasAnimation()) {
|
|
mInvalidRect.UnionRect(mInvalidRect,
|
|
gfx::IntRect(gfx::IntPoint(0, 0), GetSize()));
|
|
}
|
|
}
|
|
|
|
void
|
|
Decoder::PostInvalidation(const nsIntRect& aRect,
|
|
const Maybe<nsIntRect>& aRectAtTargetSize
|
|
/* = Nothing() */)
|
|
{
|
|
// We should be mid-frame
|
|
MOZ_ASSERT(mInFrame, "Can't invalidate when not mid-frame!");
|
|
MOZ_ASSERT(mCurrentFrame, "Can't invalidate when not mid-frame!");
|
|
|
|
// Record this invalidation, unless we're not sending partial invalidations
|
|
// or we're past the first frame.
|
|
if (ShouldSendPartialInvalidations() && !HasAnimation()) {
|
|
mInvalidRect.UnionRect(mInvalidRect, aRect);
|
|
mCurrentFrame->ImageUpdated(aRectAtTargetSize.valueOr(aRect));
|
|
}
|
|
}
|
|
|
|
void
|
|
Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */)
|
|
{
|
|
MOZ_ASSERT(!IsMetadataDecode(), "Done with decoding in metadata decode");
|
|
MOZ_ASSERT(!mInFrame, "Can't be done decoding if we're mid-frame!");
|
|
MOZ_ASSERT(!mDecodeDone, "Decode already done!");
|
|
mDecodeDone = true;
|
|
|
|
mImageMetadata.SetLoopCount(aLoopCount);
|
|
|
|
mProgress |= FLAG_DECODE_COMPLETE;
|
|
}
|
|
|
|
void
|
|
Decoder::PostDataError()
|
|
{
|
|
mDataError = true;
|
|
|
|
if (mInFrame && mCurrentFrame) {
|
|
mCurrentFrame->Abort();
|
|
}
|
|
}
|
|
|
|
void
|
|
Decoder::PostDecoderError(nsresult aFailureCode)
|
|
{
|
|
MOZ_ASSERT(NS_FAILED(aFailureCode), "Not a failure code!");
|
|
|
|
mFailCode = aFailureCode;
|
|
|
|
// XXXbholley - we should report the image URI here, but imgContainer
|
|
// needs to know its URI first
|
|
NS_WARNING("Image decoding error - This is probably a bug!");
|
|
|
|
if (mInFrame && mCurrentFrame) {
|
|
mCurrentFrame->Abort();
|
|
}
|
|
}
|
|
|
|
Telemetry::ID
|
|
Decoder::SpeedHistogram()
|
|
{
|
|
// Use HistogramCount as an invalid Histogram ID.
|
|
return Telemetry::HistogramCount;
|
|
}
|
|
|
|
} // namespace image
|
|
} // namespace mozilla
|