mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 359608 - Animated GIFs are animated even when user navigates to another page.r=bholley,bz;sr=bz;a=blocker
This commit is contained in:
parent
dbade7f8ac
commit
4606f548ac
@ -1527,6 +1527,7 @@ nsDOMImplementation::CreateHTMLDocument(const nsAString& aTitle,
|
||||
|
||||
nsDocument::nsDocument(const char* aContentType)
|
||||
: nsIDocument()
|
||||
, mAnimatingImages(PR_TRUE)
|
||||
{
|
||||
SetContentTypeInternal(nsDependentCString(aContentType));
|
||||
|
||||
@ -7379,6 +7380,11 @@ nsDocument::OnPageShow(PRBool aPersisted,
|
||||
mAnimationController->OnPageShow();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (aPersisted) {
|
||||
SetImagesNeedAnimating(PR_TRUE);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMEventTarget> target =
|
||||
aDispatchStartTarget ? do_QueryInterface(aDispatchStartTarget) :
|
||||
do_QueryInterface(GetWindow());
|
||||
@ -7429,6 +7435,10 @@ nsDocument::OnPageHide(PRBool aPersisted,
|
||||
}
|
||||
#endif
|
||||
|
||||
if (aPersisted) {
|
||||
SetImagesNeedAnimating(PR_FALSE);
|
||||
}
|
||||
|
||||
// Now send out a PageHide event.
|
||||
nsCOMPtr<nsPIDOMEventTarget> target =
|
||||
aDispatchStartTarget ? do_QueryInterface(aDispatchStartTarget) :
|
||||
@ -8093,7 +8103,14 @@ nsDocument::AddImage(imgIRequest* aImage)
|
||||
if ((oldCount == 0) && mLockingImages) {
|
||||
nsresult rv = aImage->LockImage();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return aImage->RequestDecode();
|
||||
rv = aImage->RequestDecode();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// If this is the first insertion and we're animating images, request
|
||||
// that this image be animated too.
|
||||
if (oldCount == 0 && mAnimatingImages) {
|
||||
return aImage->IncrementAnimationConsumers();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -8126,6 +8143,11 @@ nsDocument::RemoveImage(imgIRequest* aImage)
|
||||
if ((count == 0) && mLockingImages)
|
||||
return aImage->UnlockImage();
|
||||
|
||||
// If we removed the image from the tracker and we're animating images,
|
||||
// remove our request to animate this image.
|
||||
if (count == 0 && mAnimatingImages)
|
||||
return aImage->DecrementAnimationConsumers();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -8164,3 +8186,35 @@ nsDocument::SetImageLockingState(PRBool aLocked)
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PLDHashOperator IncrementAnimationEnumerator(imgIRequest* aKey,
|
||||
PRUint32 aData,
|
||||
void* userArg)
|
||||
{
|
||||
aKey->IncrementAnimationConsumers();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
PLDHashOperator DecrementAnimationEnumerator(imgIRequest* aKey,
|
||||
PRUint32 aData,
|
||||
void* userArg)
|
||||
{
|
||||
aKey->DecrementAnimationConsumers();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::SetImagesNeedAnimating(PRBool aAnimating)
|
||||
{
|
||||
// If there's no change, there's nothing to do.
|
||||
if (mAnimatingImages == aAnimating)
|
||||
return;
|
||||
|
||||
// Otherwise, iterate over our images and perform the appropriate action.
|
||||
mImageTracker.EnumerateRead(aAnimating ? IncrementAnimationEnumerator
|
||||
: DecrementAnimationEnumerator,
|
||||
nsnull);
|
||||
|
||||
// Update state.
|
||||
mAnimatingImages = aAnimating;
|
||||
}
|
||||
|
@ -1144,6 +1144,9 @@ protected:
|
||||
// Whether we're currently holding a lock on all of our images.
|
||||
PRPackedBool mLockingImages:1;
|
||||
|
||||
// Whether we currently require our images to animate
|
||||
PRPackedBool mAnimatingImages:1;
|
||||
|
||||
PRUint8 mXMLDeclarationBits;
|
||||
|
||||
PRUint8 mDefaultElementType;
|
||||
@ -1250,6 +1253,12 @@ private:
|
||||
protected:
|
||||
PRBool mWillReparent;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// Makes the images on this document capable of having their animation
|
||||
// active or suspended. An Image will animate as long as at least one of its
|
||||
// owning Documents needs it to animate; otherwise it can suspend.
|
||||
void SetImagesNeedAnimating(PRBool aAnimating);
|
||||
};
|
||||
|
||||
#define NS_DOCUMENT_INTERFACE_TABLE_BEGIN(_class) \
|
||||
|
@ -147,8 +147,6 @@ NS_IMETHODIMP nsImageLoader::OnStartContainer(imgIRequest *aRequest,
|
||||
* one loop = 2
|
||||
*/
|
||||
aImage->SetAnimationMode(mFrame->PresContext()->ImageAnimationMode());
|
||||
// Ensure the animation (if any) is started.
|
||||
aImage->StartAnimation();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1468,9 +1468,10 @@ NS_IMETHODIMP nsBulletFrame::OnStartContainer(imgIRequest *aRequest,
|
||||
|
||||
// Handle animations
|
||||
aImage->SetAnimationMode(presContext->ImageAnimationMode());
|
||||
// Ensure the animation (if any) is started.
|
||||
aImage->StartAnimation();
|
||||
|
||||
// Ensure the animation (if any) is started. Note: There is no
|
||||
// corresponding call to Decrement for this. This Increment will be
|
||||
// 'cleaned up' by the Request when it is destroyed, but only then.
|
||||
aRequest->IncrementAnimationConsumers();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -261,8 +261,6 @@ nsImageFrame::Init(nsIContent* aContent,
|
||||
currentRequest->GetImage(getter_AddRefs(image));
|
||||
if (image) {
|
||||
image->SetAnimationMode(aPresContext->ImageAnimationMode());
|
||||
// Ensure the animation (if any) is started.
|
||||
image->StartAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
@ -480,8 +478,6 @@ nsImageFrame::OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage)
|
||||
*/
|
||||
nsPresContext *presContext = PresContext();
|
||||
aImage->SetAnimationMode(presContext->ImageAnimationMode());
|
||||
// Ensure the animation (if any) is started.
|
||||
aImage->StartAnimation();
|
||||
|
||||
if (IsPendingLoad(aRequest)) {
|
||||
// We don't care
|
||||
|
@ -509,8 +509,10 @@ NS_IMETHODIMP nsImageBoxFrame::OnStartContainer(imgIRequest *request,
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(image);
|
||||
|
||||
// Ensure the animation (if any) is started
|
||||
image->StartAnimation();
|
||||
// Ensure the animation (if any) is started. Note: There is no
|
||||
// corresponding call to Decrement for this. This Increment will be
|
||||
// 'cleaned up' by the Request when it is destroyed, but only then.
|
||||
request->IncrementAnimationConsumers();
|
||||
|
||||
nscoord w, h;
|
||||
image->GetWidth(&w);
|
||||
|
@ -39,6 +39,7 @@
|
||||
|
||||
#include "nsTreeImageListener.h"
|
||||
#include "nsITreeBoxObject.h"
|
||||
#include "imgIRequest.h"
|
||||
#include "imgIContainer.h"
|
||||
|
||||
NS_IMPL_ISUPPORTS3(nsTreeImageListener, imgIDecoderObserver, imgIContainerObserver, nsITreeImageListener)
|
||||
@ -58,8 +59,10 @@ nsTreeImageListener::~nsTreeImageListener()
|
||||
NS_IMETHODIMP nsTreeImageListener::OnStartContainer(imgIRequest *aRequest,
|
||||
imgIContainer *aImage)
|
||||
{
|
||||
// Ensure the animation (if any) is started
|
||||
aImage->StartAnimation();
|
||||
// Ensure the animation (if any) is started. Note: There is no
|
||||
// corresponding call to Decrement for this. This Increment will be
|
||||
// 'cleaned up' by the Request when it is destroyed, but only then.
|
||||
aRequest->IncrementAnimationConsumers();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ native gfxGraphicsFilter(gfxPattern::GraphicsFilter);
|
||||
*
|
||||
* Internally, imgIContainer also manages animation of images.
|
||||
*/
|
||||
[scriptable, uuid(362e5b5f-f677-49e0-9a3c-03249c794624)]
|
||||
[scriptable, uuid(8bb94fa2-f57a-482c-bef8-e0b0424b0b3c)]
|
||||
interface imgIContainer : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -242,7 +242,5 @@ interface imgIContainer : nsISupports
|
||||
attribute unsigned short animationMode;
|
||||
|
||||
/* Methods to control animation */
|
||||
void startAnimation();
|
||||
void stopAnimation();
|
||||
void resetAnimation();
|
||||
};
|
||||
|
@ -52,7 +52,7 @@ interface nsIPrincipal;
|
||||
* @version 0.1
|
||||
* @see imagelib2
|
||||
*/
|
||||
[scriptable, uuid(ebde51c9-cc11-4b6a-99c3-d7f568c7481b)]
|
||||
[scriptable, uuid(62f58f12-3076-44fc-a742-5b648dac21bc)]
|
||||
interface imgIRequest : nsIRequest
|
||||
{
|
||||
/**
|
||||
@ -179,5 +179,19 @@ interface imgIRequest : nsIRequest
|
||||
* Otherwise returns the same request.
|
||||
*/
|
||||
imgIRequest getStaticRequest();
|
||||
|
||||
/**
|
||||
* Requests that the image animate (if it has an animation).
|
||||
*
|
||||
* @see Image::IncrementAnimationConsumers for documentation of the underlying call.
|
||||
*/
|
||||
void incrementAnimationConsumers();
|
||||
|
||||
/**
|
||||
* Tell the image it can forget about a request that the image animate.
|
||||
*
|
||||
* @see Image::DecrementAnimationConsumers for documentation of the underlying call.
|
||||
*/
|
||||
void decrementAnimationConsumers();
|
||||
};
|
||||
|
||||
|
@ -42,7 +42,9 @@ namespace imagelib {
|
||||
|
||||
// Constructor
|
||||
Image::Image(imgStatusTracker* aStatusTracker) :
|
||||
mInitialized(PR_FALSE)
|
||||
mAnimationConsumers(0),
|
||||
mInitialized(PR_FALSE),
|
||||
mAnimating(PR_FALSE)
|
||||
{
|
||||
if (aStatusTracker) {
|
||||
mStatusTracker = aStatusTracker;
|
||||
@ -98,6 +100,32 @@ Image::GetDecoderType(const char *aMimeType)
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
Image::IncrementAnimationConsumers()
|
||||
{
|
||||
mAnimationConsumers++;
|
||||
EvaluateAnimation();
|
||||
}
|
||||
|
||||
void
|
||||
Image::DecrementAnimationConsumers()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mAnimationConsumers >= 1, "Invalid no. of animation consumers!");
|
||||
mAnimationConsumers--;
|
||||
EvaluateAnimation();
|
||||
}
|
||||
|
||||
void
|
||||
Image::EvaluateAnimation()
|
||||
{
|
||||
if (!mAnimating && ShouldAnimate()) {
|
||||
nsresult rv = StartAnimation();
|
||||
mAnimating = NS_SUCCEEDED(rv);
|
||||
} else if (mAnimating && !ShouldAnimate()) {
|
||||
StopAnimation();
|
||||
mAnimating = PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace imagelib
|
||||
} // namespace mozilla
|
||||
|
@ -106,12 +106,35 @@ public:
|
||||
};
|
||||
static eDecoderType GetDecoderType(const char *aMimeType);
|
||||
|
||||
void IncrementAnimationConsumers();
|
||||
void DecrementAnimationConsumers();
|
||||
#ifdef DEBUG
|
||||
PRUint32 GetAnimationConsumers() { return mAnimationConsumers; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
Image(imgStatusTracker* aStatusTracker);
|
||||
|
||||
/**
|
||||
* Decides whether animation should or should not be happening,
|
||||
* and makes sure the right thing is being done.
|
||||
*/
|
||||
virtual void EvaluateAnimation();
|
||||
|
||||
virtual nsresult StartAnimation() = 0;
|
||||
virtual nsresult StopAnimation() = 0;
|
||||
|
||||
// Member data shared by all implementations of this abstract class
|
||||
nsAutoPtr<imgStatusTracker> mStatusTracker;
|
||||
PRUint32 mAnimationConsumers;
|
||||
PRPackedBool mInitialized; // Have we been initalized?
|
||||
PRPackedBool mAnimating;
|
||||
|
||||
/**
|
||||
* Extended by child classes, if they have additional
|
||||
* conditions for being able to animate
|
||||
*/
|
||||
virtual PRBool ShouldAnimate() { return mAnimationConsumers > 0; }
|
||||
};
|
||||
|
||||
} // namespace imagelib
|
||||
|
@ -190,7 +190,8 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker) :
|
||||
mHasBeenDecoded(PR_FALSE),
|
||||
mWorkerPending(PR_FALSE),
|
||||
mInDecoder(PR_FALSE),
|
||||
mError(PR_FALSE)
|
||||
mError(PR_FALSE),
|
||||
mAnimationFinished(PR_FALSE)
|
||||
{
|
||||
// Set up the discard tracker node.
|
||||
mDiscardTrackerNode.curr = this;
|
||||
@ -802,11 +803,8 @@ RasterImage::InternalAddFrame(PRUint32 framenum,
|
||||
rv = InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength,
|
||||
paletteData, paletteLength);
|
||||
|
||||
// If this is our second frame (We've just added our second frame above),
|
||||
// count should now be 2. This must be called after we AppendObject
|
||||
// because StartAnimation checks for > 1 frames
|
||||
if (mFrames.Length() == 2)
|
||||
StartAnimation();
|
||||
// We may be able to start animating, if we now have enough frames
|
||||
EvaluateAnimation();
|
||||
|
||||
return rv;
|
||||
}
|
||||
@ -1099,76 +1097,59 @@ RasterImage::SetAnimationMode(PRUint16 aAnimationMode)
|
||||
aAnimationMode == kLoopOnceAnimMode,
|
||||
"Wrong Animation Mode is being set!");
|
||||
|
||||
switch (mAnimationMode = aAnimationMode) {
|
||||
case kDontAnimMode:
|
||||
StopAnimation();
|
||||
break;
|
||||
case kNormalAnimMode:
|
||||
if (mLoopCount != 0 ||
|
||||
(mAnim && (mAnim->currentAnimationFrameIndex + 1 < mFrames.Length())))
|
||||
StartAnimation();
|
||||
break;
|
||||
case kLoopOnceAnimMode:
|
||||
if (mAnim && (mAnim->currentAnimationFrameIndex + 1 < mFrames.Length()))
|
||||
StartAnimation();
|
||||
break;
|
||||
}
|
||||
|
||||
mAnimationMode = aAnimationMode;
|
||||
|
||||
EvaluateAnimation();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
/* void startAnimation () */
|
||||
NS_IMETHODIMP
|
||||
/* void StartAnimation () */
|
||||
nsresult
|
||||
RasterImage::StartAnimation()
|
||||
{
|
||||
if (mError)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (mAnimationMode == kDontAnimMode ||
|
||||
(mAnim && (mAnim->timer || mAnim->animating)))
|
||||
return NS_OK;
|
||||
NS_ABORT_IF_FALSE(ShouldAnimate(), "Should not animate!");
|
||||
|
||||
if (!ensureAnimExists())
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
NS_ABORT_IF_FALSE(mAnim && !mAnim->timer, "Anim must exist and not have a timer yet");
|
||||
|
||||
if (mFrames.Length() > 1) {
|
||||
if (!ensureAnimExists())
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
// Default timeout to 100: the timer notify code will do the right
|
||||
// thing, so just get that started.
|
||||
PRInt32 timeout = 100;
|
||||
imgFrame *currentFrame = GetCurrentImgFrame();
|
||||
if (currentFrame) {
|
||||
timeout = currentFrame->GetTimeout();
|
||||
if (timeout <= 0) // -1 means display this frame forever
|
||||
return NS_OK;
|
||||
// Default timeout to 100: the timer notify code will do the right
|
||||
// thing, so just get that started.
|
||||
PRInt32 timeout = 100;
|
||||
imgFrame *currentFrame = GetCurrentImgFrame();
|
||||
if (currentFrame) {
|
||||
timeout = currentFrame->GetTimeout();
|
||||
if (timeout < 0) { // -1 means display this frame forever
|
||||
mAnimationFinished = PR_TRUE;
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
mAnim->timer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
NS_ENSURE_TRUE(mAnim->timer, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
// The only way animating becomes true is if the timer is created
|
||||
mAnim->animating = PR_TRUE;
|
||||
mAnim->timer->InitWithCallback(static_cast<nsITimerCallback*>(this),
|
||||
timeout, nsITimer::TYPE_REPEATING_SLACK);
|
||||
}
|
||||
|
||||
mAnim->timer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
NS_ENSURE_TRUE(mAnim->timer, NS_ERROR_OUT_OF_MEMORY);
|
||||
mAnim->timer->InitWithCallback(static_cast<nsITimerCallback*>(this),
|
||||
timeout, nsITimer::TYPE_REPEATING_SLACK);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
/* void stopAnimation (); */
|
||||
NS_IMETHODIMP
|
||||
nsresult
|
||||
RasterImage::StopAnimation()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mAnimating, "Should be animating!");
|
||||
|
||||
if (mError)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (mAnim) {
|
||||
mAnim->animating = PR_FALSE;
|
||||
|
||||
if (!mAnim->timer)
|
||||
return NS_OK;
|
||||
|
||||
if (mAnim->timer) {
|
||||
mAnim->timer->Cancel();
|
||||
mAnim->timer = nsnull;
|
||||
}
|
||||
@ -1188,12 +1169,8 @@ RasterImage::ResetAnimation()
|
||||
!mAnim || mAnim->currentAnimationFrameIndex == 0)
|
||||
return NS_OK;
|
||||
|
||||
PRBool oldAnimating = mAnim->animating;
|
||||
|
||||
if (mAnim->animating) {
|
||||
nsresult rv = StopAnimation();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
if (mAnimating)
|
||||
StopAnimation();
|
||||
|
||||
mAnim->lastCompositedFrameIndex = -1;
|
||||
mAnim->currentAnimationFrameIndex = 0;
|
||||
@ -1203,11 +1180,12 @@ RasterImage::ResetAnimation()
|
||||
|
||||
// Update display if we were animating before
|
||||
nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
|
||||
if (oldAnimating && observer)
|
||||
if (mAnimating && observer)
|
||||
observer->FrameChanged(this, &(mAnim->firstFrameRefreshArea));
|
||||
|
||||
if (oldAnimating)
|
||||
return StartAnimation();
|
||||
if (ShouldAnimate())
|
||||
StartAnimation();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1430,17 +1408,21 @@ RasterImage::Notify(nsITimer *timer)
|
||||
{
|
||||
// This should never happen since the timer is only set up in StartAnimation()
|
||||
// after mAnim is checked to exist.
|
||||
NS_ENSURE_TRUE(mAnim, NS_ERROR_UNEXPECTED);
|
||||
NS_ASSERTION(mAnim->timer == timer,
|
||||
"RasterImage::Notify() called with incorrect timer");
|
||||
NS_ABORT_IF_FALSE(mAnim, "Need anim for Notify()");
|
||||
NS_ABORT_IF_FALSE(timer, "Need timer for Notify()");
|
||||
NS_ABORT_IF_FALSE(mAnim->timer == timer,
|
||||
"RasterImage::Notify() called with incorrect timer");
|
||||
|
||||
if (!mAnim->animating || !mAnim->timer)
|
||||
if (!mAnimating || !ShouldAnimate())
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
|
||||
if (!observer) {
|
||||
// the imgRequest that owns us is dead, we should die now too.
|
||||
StopAnimation();
|
||||
NS_ABORT_IF_FALSE(mAnimationConsumers == 0,
|
||||
"If no observer, should have no consumers");
|
||||
if (mAnimating)
|
||||
StopAnimation();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1463,7 +1445,8 @@ RasterImage::Notify(nsITimer *timer)
|
||||
|
||||
// If animation mode is "loop once", it's time to stop animating
|
||||
if (mAnimationMode == kLoopOnceAnimMode || mLoopCount == 0) {
|
||||
StopAnimation();
|
||||
mAnimationFinished = PR_TRUE;
|
||||
EvaluateAnimation();
|
||||
return NS_OK;
|
||||
} else {
|
||||
// We may have used compositingFrame to build a frame, and then copied
|
||||
@ -1506,8 +1489,10 @@ RasterImage::Notify(nsITimer *timer)
|
||||
|
||||
if (timeout > 0)
|
||||
mAnim->timer->SetDelay(timeout);
|
||||
else
|
||||
StopAnimation();
|
||||
else {
|
||||
mAnimationFinished = PR_TRUE;
|
||||
EvaluateAnimation();
|
||||
}
|
||||
|
||||
nsIntRect dirtyRect;
|
||||
imgFrame *frameToUse = nsnull;
|
||||
@ -2740,5 +2725,12 @@ RasterImage::WriteToRasterImage(nsIInputStream* /* unused */,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool
|
||||
RasterImage::ShouldAnimate()
|
||||
{
|
||||
return Image::ShouldAnimate() && mFrames.Length() >= 2 &&
|
||||
mAnimationMode != kDontAnimMode && !mAnimationFinished;
|
||||
}
|
||||
|
||||
} // namespace imagelib
|
||||
} // namespace mozilla
|
||||
|
@ -86,8 +86,8 @@ class nsIInputStream;
|
||||
* with StartAnimation().
|
||||
*
|
||||
* @par
|
||||
* StartAnimation() checks if animating is allowed, and creates a timer. The
|
||||
* timer calls Notify when the specified frame delay time is up.
|
||||
* StartAnimation() creates a timer. The timer calls Notify when the
|
||||
* specified frame delay time is up.
|
||||
*
|
||||
* @par
|
||||
* Notify() moves on to the next frame, sets up the new timer delay, destroys
|
||||
@ -159,6 +159,9 @@ public:
|
||||
RasterImage(imgStatusTracker* aStatusTracker = nsnull);
|
||||
virtual ~RasterImage();
|
||||
|
||||
virtual nsresult StartAnimation();
|
||||
virtual nsresult StopAnimation();
|
||||
|
||||
// C++-only version of imgIContainer::GetType, for convenience
|
||||
virtual PRUint16 GetType() { return imgIContainer::TYPE_RASTER; }
|
||||
|
||||
@ -326,16 +329,13 @@ private:
|
||||
//! Whether we can assume there will be no more frames
|
||||
//! (and thus loop the animation)
|
||||
PRPackedBool doneDecoding;
|
||||
//! Are we currently animating the image?
|
||||
PRPackedBool animating;
|
||||
|
||||
Anim() :
|
||||
firstFrameRefreshArea(),
|
||||
currentDecodingFrameIndex(0),
|
||||
currentAnimationFrameIndex(0),
|
||||
lastCompositedFrameIndex(-1),
|
||||
doneDecoding(PR_FALSE),
|
||||
animating(PR_FALSE)
|
||||
doneDecoding(PR_FALSE)
|
||||
{
|
||||
;
|
||||
}
|
||||
@ -490,6 +490,10 @@ private: // data
|
||||
|
||||
PRPackedBool mError:1; // Error handling
|
||||
|
||||
// Whether the animation can stop, due to running out
|
||||
// of frames, or no more owning request
|
||||
PRPackedBool mAnimationFinished:1;
|
||||
|
||||
// Decoding
|
||||
nsresult WantDecodedFrames();
|
||||
nsresult SyncDecode();
|
||||
@ -513,6 +517,8 @@ private: // data
|
||||
PRBool DiscardingActive();
|
||||
PRBool StoringSourceData();
|
||||
|
||||
protected:
|
||||
PRBool ShouldAnimate();
|
||||
};
|
||||
|
||||
// XXXdholbert These helper classes should move to be inside the
|
||||
|
@ -311,8 +311,22 @@ nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, PRBoo
|
||||
{
|
||||
LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::RemoveProxy", "proxy", proxy);
|
||||
|
||||
NS_ABORT_IF_FALSE(!mImage || HaveProxyWithObserver(nsnull) ||
|
||||
mImage->GetAnimationConsumers() == 0,
|
||||
"How can we have an image with animation consumers, but no observer?");
|
||||
|
||||
// This will remove our animation consumers, so after removing
|
||||
// this proxy, we don't end up without proxies with observers, but still
|
||||
// have animation consumers.
|
||||
proxy->ClearAnimationConsumers();
|
||||
|
||||
mObservers.RemoveElement(proxy);
|
||||
|
||||
// Check that after our changes, we are still in a consistent state
|
||||
NS_ABORT_IF_FALSE(!mImage || HaveProxyWithObserver(nsnull) ||
|
||||
mImage->GetAnimationConsumers() == 0,
|
||||
"How can we have an image with animation consumers, but no observer?");
|
||||
|
||||
// Let the status tracker do its thing before we potentially call Cancel()
|
||||
// below, because Cancel() may result in OnStopRequest being called back
|
||||
// before Cancel() returns, leaving the image in a different state then the
|
||||
@ -321,12 +335,6 @@ nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, PRBoo
|
||||
imgStatusTracker& statusTracker = GetStatusTracker();
|
||||
statusTracker.EmulateRequestFinished(proxy, aStatus, !aNotify);
|
||||
|
||||
if (mImage && !HaveProxyWithObserver(nsnull)) {
|
||||
LOG_MSG(gImgLog, "imgRequest::RemoveProxy", "stopping animation");
|
||||
|
||||
mImage->StopAnimation();
|
||||
}
|
||||
|
||||
if (mObservers.IsEmpty()) {
|
||||
// If we have no observers, there's nothing holding us alive. If we haven't
|
||||
// been cancelled and thus removed from the cache, tell the image loader so
|
||||
@ -394,11 +402,6 @@ void imgRequest::Cancel(nsresult aStatus)
|
||||
|
||||
LOG_SCOPE(gImgLog, "imgRequest::Cancel");
|
||||
|
||||
LOG_MSG(gImgLog, "imgRequest::Cancel", "stopping animation");
|
||||
if (mImage) {
|
||||
mImage->StopAnimation();
|
||||
}
|
||||
|
||||
imgStatusTracker& statusTracker = GetStatusTracker();
|
||||
statusTracker.RecordCancel();
|
||||
|
||||
|
@ -68,6 +68,7 @@ imgRequestProxy::imgRequestProxy() :
|
||||
mListener(nsnull),
|
||||
mLoadFlags(nsIRequest::LOAD_NORMAL),
|
||||
mLockCount(0),
|
||||
mAnimationConsumers(0),
|
||||
mCanceled(PR_FALSE),
|
||||
mIsInLoadGroup(PR_FALSE),
|
||||
mListenerIsStrongRef(PR_FALSE),
|
||||
@ -89,6 +90,8 @@ imgRequestProxy::~imgRequestProxy()
|
||||
while (mLockCount)
|
||||
UnlockImage();
|
||||
|
||||
ClearAnimationConsumers();
|
||||
|
||||
// Explicitly set mListener to null to ensure that the RemoveProxy
|
||||
// call below can't send |this| to an arbitrary listener while |this|
|
||||
// is being destroyed. This is all belt-and-suspenders in view of the
|
||||
@ -119,6 +122,8 @@ nsresult imgRequestProxy::Init(imgRequest* request, nsILoadGroup* aLoadGroup, Im
|
||||
|
||||
LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequestProxy::Init", "request", request);
|
||||
|
||||
NS_ABORT_IF_FALSE(mAnimationConsumers == 0, "Cannot have animation before Init");
|
||||
|
||||
mOwner = request;
|
||||
mListener = aObserver;
|
||||
// Make sure to addref mListener before the AddProxy call below, since
|
||||
@ -149,6 +154,10 @@ nsresult imgRequestProxy::ChangeOwner(imgRequest *aNewOwner)
|
||||
while (mLockCount)
|
||||
UnlockImage();
|
||||
|
||||
// If we're holding animation requests, undo them.
|
||||
PRUint32 oldAnimationConsumers = mAnimationConsumers;
|
||||
ClearAnimationConsumers();
|
||||
|
||||
// Even if we are cancelled, we MUST change our image, because the image
|
||||
// holds our status, and the status must always be correct.
|
||||
mImage = aNewOwner->mImage;
|
||||
@ -157,6 +166,10 @@ nsresult imgRequestProxy::ChangeOwner(imgRequest *aNewOwner)
|
||||
for (PRUint32 i = 0; i < oldLockCount; i++)
|
||||
LockImage();
|
||||
|
||||
// If we had animation requests, apply them here
|
||||
for (PRUint32 i = 0; i < oldAnimationConsumers; i++)
|
||||
IncrementAnimationConsumers();
|
||||
|
||||
if (mCanceled)
|
||||
return NS_OK;
|
||||
|
||||
@ -334,6 +347,54 @@ imgRequestProxy::UnlockImage()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
imgRequestProxy::IncrementAnimationConsumers()
|
||||
{
|
||||
// Without an observer, we should not animate
|
||||
if (!HasObserver())
|
||||
return NS_OK;
|
||||
|
||||
mAnimationConsumers++;
|
||||
if (mImage)
|
||||
mImage->IncrementAnimationConsumers();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
imgRequestProxy::DecrementAnimationConsumers()
|
||||
{
|
||||
if (!HasObserver()) {
|
||||
NS_ABORT_IF_FALSE(mAnimationConsumers == 0,
|
||||
"How can we have animation consumers without an observer?");
|
||||
|
||||
// Without an observer, we have no consumers anyhow
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We may get here if some responsible code called Increment,
|
||||
// then called us, but we have meanwhile called ClearAnimationConsumers
|
||||
// because we needed to get rid of them earlier (see
|
||||
// imgRequest::RemoveProxy), and hence have nothing left to
|
||||
// decrement. (In such a case we got rid of the animation consumers
|
||||
// early, but not the observer.)
|
||||
if (mAnimationConsumers > 0) {
|
||||
mAnimationConsumers--;
|
||||
if (mImage)
|
||||
mImage->DecrementAnimationConsumers();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
imgRequestProxy::ClearAnimationConsumers()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(HasObserver() || mAnimationConsumers == 0,
|
||||
"How can we have animation consumers without an observer?");
|
||||
|
||||
while (mAnimationConsumers > 0)
|
||||
DecrementAnimationConsumers();
|
||||
}
|
||||
|
||||
/* void suspend (); */
|
||||
NS_IMETHODIMP imgRequestProxy::Suspend()
|
||||
{
|
||||
@ -691,6 +752,10 @@ void imgRequestProxy::OnStopRequest(PRBool lastPart)
|
||||
|
||||
void imgRequestProxy::NullOutListener()
|
||||
{
|
||||
// If we have animation consumers, then they don't matter anymore
|
||||
if (mListener)
|
||||
ClearAnimationConsumers();
|
||||
|
||||
if (mListenerIsStrongRef) {
|
||||
// Releasing could do weird reentery stuff, so just play it super-safe
|
||||
nsCOMPtr<imgIDecoderObserver> obs;
|
||||
@ -783,6 +848,10 @@ imgRequestProxy::SetImage(Image* aImage)
|
||||
// Apply any locks we have
|
||||
for (PRUint32 i = 0; i < mLockCount; ++i)
|
||||
mImage->LockImage();
|
||||
|
||||
// Apply any animation consumers we have
|
||||
for (PRUint32 i = 0; i < mAnimationConsumers; i++)
|
||||
mImage->IncrementAnimationConsumers();
|
||||
}
|
||||
|
||||
imgStatusTracker&
|
||||
|
@ -127,6 +127,12 @@ public:
|
||||
// instantiates an Image.
|
||||
void SetImage(mozilla::imagelib::Image* aImage);
|
||||
|
||||
// Removes all animation consumers that were created with
|
||||
// IncrementAnimationConsumers. This is necessary since we need
|
||||
// to do it before the proxy itself is destroyed. See
|
||||
// imgRequest::RemoveProxy
|
||||
void ClearAnimationConsumers();
|
||||
|
||||
protected:
|
||||
friend class imgStatusTracker;
|
||||
friend class imgStatusNotifyRunnable;
|
||||
@ -220,6 +226,7 @@ private:
|
||||
|
||||
nsLoadFlags mLoadFlags;
|
||||
PRUint32 mLockCount;
|
||||
PRUint32 mAnimationConsumers;
|
||||
PRPackedBool mCanceled;
|
||||
PRPackedBool mIsInLoadGroup;
|
||||
PRPackedBool mListenerIsStrongRef;
|
||||
|
Loading…
Reference in New Issue
Block a user