2 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "mozilla/gfx/2D.h"
10 #include "DecodePool.h"
11 #include "GeckoProfiler.h"
12 #include "imgIContainer.h"
13 #include "nsIConsoleService.h"
14 #include "nsIScriptError.h"
15 #include "nsProxyRelease.h"
16 #include "nsServiceManagerUtils.h"
17 #include "nsComponentManagerUtils.h"
19 using mozilla::gfx::IntSize
;
20 using mozilla::gfx::SurfaceFormat
;
25 Decoder::Decoder(RasterImage
* aImage
)
27 , mProgress(NoProgress
)
33 , mSendPartialInvalidations(false)
37 , mDecodeAborted(false)
38 , mShouldReportError(false)
39 , mImageIsTransient(false)
40 , mImageIsLocked(false)
43 , mNeedsNewFrame(false)
44 , mNeedsToFlushData(false)
53 MOZ_ASSERT(mProgress
== NoProgress
,
54 "Destroying Decoder without taking all its progress changes");
55 MOZ_ASSERT(mInvalidRect
.IsEmpty(),
56 "Destroying Decoder without taking all its invalidations");
59 if (!NS_IsMainThread()) {
60 // Dispatch mImage to main thread to prevent it from being destructed by the
62 nsCOMPtr
<nsIThread
> mainThread
= do_GetMainThread();
63 NS_WARN_IF_FALSE(mainThread
, "Couldn't get the main thread!");
65 // Handle ambiguous nsISupports inheritance.
66 RasterImage
* rawImg
= nullptr;
68 DebugOnly
<nsresult
> rv
=
69 NS_ProxyRelease(mainThread
, NS_ISUPPORTS_CAST(ImageResource
*, rawImg
));
70 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Failed to proxy release to main thread");
76 * Common implementation of the decoder interface.
83 MOZ_ASSERT(!mInitialized
, "Can't re-initialize a decoder!");
85 // Fire OnStartDecode at init time to support bug 512435.
86 if (!IsSizeDecode()) {
87 mProgress
|= FLAG_DECODE_STARTED
| FLAG_ONLOAD_BLOCKED
;
90 // Implementation-specific initialization
96 // Initializes a decoder whose image and observer is already being used by a
99 Decoder::InitSharedDecoder(uint8_t* aImageData
, uint32_t aImageDataLength
,
100 uint32_t* aColormap
, uint32_t aColormapSize
,
101 RawAccessFrameRef
&& aFrameRef
)
103 // No re-initializing
104 NS_ABORT_IF_FALSE(!mInitialized
, "Can't re-initialize a decoder!");
106 mImageData
= aImageData
;
107 mImageDataLength
= aImageDataLength
;
108 mColormap
= aColormap
;
109 mColormapSize
= aColormapSize
;
110 mCurrentFrame
= Move(aFrameRef
);
112 // We have all the frame data, so we've started the frame.
113 if (!IsSizeDecode()) {
118 // Implementation-specific initialization
126 MOZ_ASSERT(mInitialized
, "Should be initialized here");
127 MOZ_ASSERT(mIterator
, "Should have a SourceBufferIterator");
129 // We keep decoding chunks until the decode completes or there are no more
131 while (!GetDecodeDone() && !HasError()) {
132 auto newState
= mIterator
->AdvanceOrScheduleResume(this);
134 if (newState
== SourceBufferIterator::WAITING
) {
135 // We can't continue because the rest of the data hasn't arrived from the
136 // network yet. We don't have to do anything special; the
137 // SourceBufferIterator will ensure that Decode() gets called again on a
138 // DecodePool thread when more data is available.
142 if (newState
== SourceBufferIterator::COMPLETE
) {
145 nsresult finalStatus
= mIterator
->CompletionStatus();
146 if (NS_FAILED(finalStatus
)) {
154 MOZ_ASSERT(newState
== SourceBufferIterator::READY
);
156 Write(mIterator
->Data(), mIterator
->Length());
160 return HasError() ? NS_ERROR_FAILURE
: NS_OK
;
166 DecodePool
* decodePool
= DecodePool::Singleton();
167 MOZ_ASSERT(decodePool
);
169 nsCOMPtr
<nsIEventTarget
> target
= decodePool
->GetEventTarget();
170 if (MOZ_UNLIKELY(!target
)) {
171 // We're shutting down and the DecodePool's thread pool has been destroyed.
175 nsCOMPtr
<nsIRunnable
> worker
= decodePool
->CreateDecodeWorker(this);
176 target
->Dispatch(worker
, nsIEventTarget::DISPATCH_NORMAL
);
180 Decoder::ShouldSyncDecode(size_t aByteLimit
)
182 MOZ_ASSERT(aByteLimit
> 0);
183 MOZ_ASSERT(mIterator
, "Should have a SourceBufferIterator");
185 return mIterator
->RemainingBytesIsNoMoreThan(aByteLimit
);
189 Decoder::Write(const char* aBuffer
, uint32_t aCount
)
191 PROFILER_LABEL("ImageDecoder", "Write",
192 js::ProfileEntry::Category::GRAPHICS
);
194 // We're strict about decoder errors
195 MOZ_ASSERT(!HasDecoderError(),
196 "Not allowed to make more decoder calls after error!");
198 // Begin recording telemetry data.
199 TimeStamp start
= TimeStamp::Now();
202 // Keep track of the total number of bytes written.
203 mBytesDecoded
+= aCount
;
205 // If we're flushing data, clear the flag.
206 if (aBuffer
== nullptr && aCount
== 0) {
207 MOZ_ASSERT(mNeedsToFlushData
, "Flushing when we don't need to");
208 mNeedsToFlushData
= false;
211 // If a data error occured, just ignore future data.
215 if (IsSizeDecode() && HasSize()) {
216 // More data came in since we found the size. We have nothing to do here.
220 MOZ_ASSERT(!NeedsNewFrame() || HasDataError(),
221 "Should not need a new frame before writing anything");
222 MOZ_ASSERT(!NeedsToFlushData() || HasDataError(),
223 "Should not need to flush data before writing anything");
225 // Pass the data along to the implementation.
226 WriteInternal(aBuffer
, aCount
);
228 // If we need a new frame to proceed, let's create one and call it again.
229 while (NeedsNewFrame() && !HasDataError()) {
230 MOZ_ASSERT(!IsSizeDecode(), "Shouldn't need new frame for size decode");
232 nsresult rv
= AllocateFrame();
234 if (NS_SUCCEEDED(rv
)) {
235 // Use the data we saved when we asked for a new frame.
236 WriteInternal(nullptr, 0);
239 mNeedsToFlushData
= false;
243 mDecodeTime
+= (TimeStamp::Now() - start
);
247 Decoder::CompleteDecode()
249 // Implementation-specific finalization
253 // If the implementation left us mid-frame, finish that up.
254 if (mInFrame
&& !HasError())
257 // If PostDecodeDone() has not been called, and this decoder wasn't aborted
258 // early because of low-memory conditions or losing a race with another
259 // decoder, we need to send teardown notifications (and report an error to the
261 if (!IsSizeDecode() && !mDecodeDone
&& !WasAborted()) {
262 mShouldReportError
= true;
264 // If we only have a data error, we're usable if we have at least one
266 if (!HasDecoderError() && GetCompleteFrameCount() > 0) {
267 // We're usable, so do exactly what we should have when the decoder
270 // Not writing to the entire frame may have left us transparent.
271 PostHasTransparency();
278 // We're not usable. Record some final progress indicating the error.
279 if (!IsSizeDecode()) {
280 mProgress
|= FLAG_DECODE_COMPLETE
| FLAG_ONLOAD_UNBLOCKED
;
282 mProgress
|= FLAG_HAS_ERROR
;
290 MOZ_ASSERT(NS_IsMainThread());
292 MOZ_ASSERT(HasError() || !mInFrame
, "Finishing while we're still in a frame");
294 // If we detected an error in CompleteDecode(), log it to the error console.
295 if (mShouldReportError
&& !WasAborted()) {
296 nsCOMPtr
<nsIConsoleService
> consoleService
=
297 do_GetService(NS_CONSOLESERVICE_CONTRACTID
);
298 nsCOMPtr
<nsIScriptError
> errorObject
=
299 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
);
301 if (consoleService
&& errorObject
&& !HasDecoderError()) {
302 nsAutoString
msg(NS_LITERAL_STRING("Image corrupt or truncated: ") +
303 NS_ConvertUTF8toUTF16(mImage
->GetURIString()));
305 if (NS_SUCCEEDED(errorObject
->InitWithWindowID(
307 NS_ConvertUTF8toUTF16(mImage
->GetURIString()),
308 EmptyString(), 0, 0, nsIScriptError::errorFlag
,
309 "Image", mImage
->InnerWindowID()
311 consoleService
->LogMessage(errorObject
);
316 // Set image metadata before calling DecodingComplete, because
317 // DecodingComplete calls Optimize().
318 mImageMetadata
.SetOnImage(mImage
);
324 if (mDecodeDone
&& !IsSizeDecode()) {
325 MOZ_ASSERT(HasError() || mCurrentFrame
, "Should have an error or a frame");
327 // If this image wasn't animated and isn't a transient image, mark its frame
328 // as optimizable. We don't support optimizing animated images and
329 // optimizing transient images isn't worth it.
330 if (!mIsAnimated
&& !mImageIsTransient
&& mCurrentFrame
) {
331 mCurrentFrame
->SetOptimizable();
334 mImage
->OnDecodingComplete();
339 Decoder::FinishSharedDecoder()
347 Decoder::AllocateFrame(const nsIntSize
& aTargetSize
/* = nsIntSize() */)
349 MOZ_ASSERT(mNeedsNewFrame
);
351 nsIntSize targetSize
= aTargetSize
;
352 if (targetSize
== nsIntSize()) {
353 MOZ_ASSERT(HasSize());
354 targetSize
= mImageMetadata
.GetSize();
357 mCurrentFrame
= EnsureFrame(mNewFrameData
.mFrameNum
,
359 mNewFrameData
.mFrameRect
,
361 mNewFrameData
.mFormat
,
362 mNewFrameData
.mPaletteDepth
,
363 mCurrentFrame
.get());
366 // Gather the raw pointers the decoders will use.
367 mCurrentFrame
->GetImageData(&mImageData
, &mImageDataLength
);
368 mCurrentFrame
->GetPaletteData(&mColormap
, &mColormapSize
);
370 if (mNewFrameData
.mFrameNum
+ 1 == mFrameCount
) {
377 // Mark ourselves as not needing another frame before talking to anyone else
378 // so they can tell us if they need yet another.
379 mNeedsNewFrame
= false;
381 // If we've received any data at all, we may have pending data that needs to
382 // be flushed now that we have a frame to decode into.
383 if (mBytesDecoded
> 0) {
384 mNeedsToFlushData
= true;
387 return mCurrentFrame
? NS_OK
: NS_ERROR_FAILURE
;
391 Decoder::EnsureFrame(uint32_t aFrameNum
,
392 const nsIntSize
& aTargetSize
,
393 const nsIntRect
& aFrameRect
,
394 uint32_t aDecodeFlags
,
395 SurfaceFormat aFormat
,
396 uint8_t aPaletteDepth
,
397 imgFrame
* aPreviousFrame
)
399 if (mDataError
|| NS_FAILED(mFailCode
)) {
400 return RawAccessFrameRef();
403 MOZ_ASSERT(aFrameNum
<= mFrameCount
, "Invalid frame index!");
404 if (aFrameNum
> mFrameCount
) {
405 return RawAccessFrameRef();
408 // Adding a frame that doesn't already exist. This is the normal case.
409 if (aFrameNum
== mFrameCount
) {
410 return InternalAddFrame(aFrameNum
, aTargetSize
, aFrameRect
, aDecodeFlags
,
411 aFormat
, aPaletteDepth
, aPreviousFrame
);
414 // We're replacing a frame. It must be the first frame; there's no reason to
415 // ever replace any other frame, since the first frame is the only one we
416 // speculatively allocate without knowing what the decoder really needs.
417 // XXX(seth): I'm not convinced there's any reason to support this at all. We
418 // should figure out how to avoid triggering this and rip it out.
419 MOZ_ASSERT(aFrameNum
== 0, "Replacing a frame other than the first?");
420 MOZ_ASSERT(mFrameCount
== 1, "Should have only one frame");
421 MOZ_ASSERT(aPreviousFrame
, "Need the previous frame to replace");
422 if (aFrameNum
!= 0 || !aPreviousFrame
|| mFrameCount
!= 1) {
423 return RawAccessFrameRef();
426 MOZ_ASSERT(!aPreviousFrame
->GetRect().IsEqualEdges(aFrameRect
) ||
427 aPreviousFrame
->GetFormat() != aFormat
||
428 aPreviousFrame
->GetPaletteDepth() != aPaletteDepth
,
429 "Replacing first frame with the same kind of frame?");
433 RawAccessFrameRef ref
= Move(mCurrentFrame
);
435 MOZ_ASSERT(ref
, "No ref to current frame?");
437 // Reinitialize the old frame.
438 nsIntSize oldSize
= ThebesIntSize(aPreviousFrame
->GetImageSize());
440 aDecodeFlags
& imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA
;
441 if (NS_FAILED(aPreviousFrame
->ReinitForDecoder(oldSize
, aFrameRect
, aFormat
,
442 aPaletteDepth
, nonPremult
))) {
443 NS_WARNING("imgFrame::ReinitForDecoder should succeed");
445 aPreviousFrame
->Abort();
446 return RawAccessFrameRef();
453 Decoder::InternalAddFrame(uint32_t aFrameNum
,
454 const nsIntSize
& aTargetSize
,
455 const nsIntRect
& aFrameRect
,
456 uint32_t aDecodeFlags
,
457 SurfaceFormat aFormat
,
458 uint8_t aPaletteDepth
,
459 imgFrame
* aPreviousFrame
)
461 MOZ_ASSERT(aFrameNum
<= mFrameCount
, "Invalid frame index!");
462 if (aFrameNum
> mFrameCount
) {
463 return RawAccessFrameRef();
466 if (aTargetSize
.width
<= 0 || aTargetSize
.height
<= 0 ||
467 aFrameRect
.width
<= 0 || aFrameRect
.height
<= 0) {
468 NS_WARNING("Trying to add frame with zero or negative size");
469 return RawAccessFrameRef();
472 if (!SurfaceCache::CanHold(aTargetSize
.ToIntSize())) {
473 NS_WARNING("Trying to add frame that's too large for the SurfaceCache");
474 return RawAccessFrameRef();
477 nsRefPtr
<imgFrame
> frame
= new imgFrame();
479 aDecodeFlags
& imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA
;
480 if (NS_FAILED(frame
->InitForDecoder(aTargetSize
, aFrameRect
, aFormat
,
481 aPaletteDepth
, nonPremult
))) {
482 NS_WARNING("imgFrame::Init should succeed");
483 return RawAccessFrameRef();
486 RawAccessFrameRef ref
= frame
->RawAccessRef();
489 return RawAccessFrameRef();
492 InsertOutcome outcome
=
493 SurfaceCache::Insert(frame
, ImageKey(mImage
.get()),
494 RasterSurfaceKey(aTargetSize
.ToIntSize(),
497 Lifetime::Persistent
);
498 if (outcome
!= InsertOutcome::SUCCESS
) {
499 // We either hit InsertOutcome::FAILURE, which is a temporary failure due to
500 // low memory (we know it's not permanent because we checked CanHold()
501 // above), or InsertOutcome::FAILURE_ALREADY_PRESENT, which means that
502 // another decoder beat us to decoding this frame. Either way, we should
503 // abort this decoder rather than treat this as a real error.
504 mDecodeAborted
= true;
506 return RawAccessFrameRef();
509 nsIntRect refreshArea
;
511 if (aFrameNum
== 1) {
512 MOZ_ASSERT(aPreviousFrame
, "Must provide a previous frame when animated");
513 aPreviousFrame
->SetRawAccessOnly();
515 // If we dispose of the first frame by clearing it, then the first frame's
516 // refresh area is all of itself.
517 // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR).
518 AnimationData previousFrameData
= aPreviousFrame
->GetAnimationData();
519 if (previousFrameData
.mDisposalMethod
== DisposalMethod::CLEAR
||
520 previousFrameData
.mDisposalMethod
== DisposalMethod::CLEAR_ALL
||
521 previousFrameData
.mDisposalMethod
== DisposalMethod::RESTORE_PREVIOUS
) {
522 refreshArea
= previousFrameData
.mRect
;
527 ref
->SetRawAccessOnly();
529 // Some GIFs are huge but only have a small area that they animate. We only
530 // need to refresh that small area when frame 0 comes around again.
531 refreshArea
.UnionRect(refreshArea
, frame
->GetRect());
535 mImage
->OnAddedFrame(mFrameCount
, refreshArea
);
541 Decoder::SetSizeOnImage()
543 MOZ_ASSERT(mImageMetadata
.HasSize(), "Should have size");
544 MOZ_ASSERT(mImageMetadata
.HasOrientation(), "Should have orientation");
546 nsresult rv
= mImage
->SetSize(mImageMetadata
.GetWidth(),
547 mImageMetadata
.GetHeight(),
548 mImageMetadata
.GetOrientation());
555 * Hook stubs. Override these as necessary in decoder implementations.
558 void Decoder::InitInternal() { }
559 void Decoder::WriteInternal(const char* aBuffer
, uint32_t aCount
) { }
560 void Decoder::FinishInternal() { }
563 * Progress Notifications
567 Decoder::PostSize(int32_t aWidth
,
569 Orientation aOrientation
/* = Orientation()*/)
572 NS_ABORT_IF_FALSE(aWidth
>= 0, "Width can't be negative!");
573 NS_ABORT_IF_FALSE(aHeight
>= 0, "Height can't be negative!");
576 mImageMetadata
.SetSize(aWidth
, aHeight
, aOrientation
);
578 // Record this notification.
579 mProgress
|= FLAG_SIZE_AVAILABLE
;
583 Decoder::PostHasTransparency()
585 mProgress
|= FLAG_HAS_TRANSPARENCY
;
589 Decoder::PostFrameStart()
591 // We shouldn't already be mid-frame
592 NS_ABORT_IF_FALSE(!mInFrame
, "Starting new frame but not done with old one!");
594 // Update our state to reflect the new frame
597 // If we just became animated, record that fact.
598 if (mFrameCount
> 1) {
600 mProgress
|= FLAG_IS_ANIMATED
;
605 Decoder::PostFrameStop(Opacity aFrameOpacity
/* = Opacity::TRANSPARENT */,
606 DisposalMethod aDisposalMethod
/* = DisposalMethod::KEEP */,
607 int32_t aTimeout
/* = 0 */,
608 BlendMethod aBlendMethod
/* = BlendMethod::OVER */)
610 // We should be mid-frame
611 MOZ_ASSERT(!IsSizeDecode(), "Stopping frame during a size decode");
612 MOZ_ASSERT(mInFrame
, "Stopping frame when we didn't start one");
613 MOZ_ASSERT(mCurrentFrame
, "Stopping frame when we don't have one");
618 mCurrentFrame
->Finish(aFrameOpacity
, aDisposalMethod
, aTimeout
, aBlendMethod
);
620 mProgress
|= FLAG_FRAME_COMPLETE
| FLAG_ONLOAD_UNBLOCKED
;
622 // If we're not sending partial invalidations, then we send an invalidation
623 // here when the first frame is complete.
624 if (!mSendPartialInvalidations
&& !mIsAnimated
) {
625 mInvalidRect
.UnionRect(mInvalidRect
,
626 nsIntRect(nsIntPoint(0, 0), GetSize()));
631 Decoder::PostInvalidation(const nsIntRect
& aRect
,
632 const Maybe
<nsIntRect
>& aRectAtTargetSize
635 // We should be mid-frame
636 NS_ABORT_IF_FALSE(mInFrame
, "Can't invalidate when not mid-frame!");
637 NS_ABORT_IF_FALSE(mCurrentFrame
, "Can't invalidate when not mid-frame!");
639 // Record this invalidation, unless we're not sending partial invalidations
640 // or we're past the first frame.
641 if (mSendPartialInvalidations
&& !mIsAnimated
) {
642 mInvalidRect
.UnionRect(mInvalidRect
, aRect
);
643 mCurrentFrame
->ImageUpdated(aRectAtTargetSize
.valueOr(aRect
));
648 Decoder::PostDecodeDone(int32_t aLoopCount
/* = 0 */)
650 NS_ABORT_IF_FALSE(!IsSizeDecode(), "Can't be done with decoding with size decode!");
651 NS_ABORT_IF_FALSE(!mInFrame
, "Can't be done decoding if we're mid-frame!");
652 NS_ABORT_IF_FALSE(!mDecodeDone
, "Decode already done!");
655 mImageMetadata
.SetLoopCount(aLoopCount
);
657 mProgress
|= FLAG_DECODE_COMPLETE
;
661 Decoder::PostDataError()
665 if (mInFrame
&& mCurrentFrame
) {
666 mCurrentFrame
->Abort();
671 Decoder::PostDecoderError(nsresult aFailureCode
)
673 NS_ABORT_IF_FALSE(NS_FAILED(aFailureCode
), "Not a failure code!");
675 mFailCode
= aFailureCode
;
677 // XXXbholley - we should report the image URI here, but imgContainer
678 // needs to know its URI first
679 NS_WARNING("Image decoding error - This is probably a bug!");
681 if (mInFrame
&& mCurrentFrame
) {
682 mCurrentFrame
->Abort();
687 Decoder::NeedNewFrame(uint32_t framenum
, uint32_t x_offset
, uint32_t y_offset
,
688 uint32_t width
, uint32_t height
,
689 gfx::SurfaceFormat format
,
690 uint8_t palette_depth
/* = 0 */)
692 // Decoders should never call NeedNewFrame without yielding back to Write().
693 MOZ_ASSERT(!mNeedsNewFrame
);
695 // We don't want images going back in time or skipping frames.
696 MOZ_ASSERT(framenum
== mFrameCount
|| framenum
== (mFrameCount
- 1));
698 mNewFrameData
= NewFrameData(framenum
,
699 nsIntRect(x_offset
, y_offset
, width
, height
),
700 format
, palette_depth
);
701 mNeedsNewFrame
= true;
705 } // namespace mozilla