Bug 1343341. Change GetTimeoutForFrame to return a Maybe, and make all callers deal with a lack of a return value. r=aosmond

Do this to allow GetTimeoutForFrame to be called for frames that haven't been decoded yet. Propagate a Maybe result where it makes sense. The remaining callers just bail if they get no return value. Many of them can just assert that they get a return value because they already got the same frame, so the timeout has to be available.

The logic is a little tricky because we have "Forever" timeouts that were sort of treated as error cases.
This commit is contained in:
Timothy Nikkel 2017-03-23 00:02:54 -05:00
parent f088324ed2
commit 3d98a47ed8
2 changed files with 50 additions and 24 deletions

View File

@ -141,28 +141,33 @@ AnimationState::LoopLength() const
// FrameAnimator implementation.
///////////////////////////////////////////////////////////////////////////////
TimeStamp
Maybe<TimeStamp>
FrameAnimator::GetCurrentImgFrameEndTime(AnimationState& aState) const
{
TimeStamp currentFrameTime = aState.mCurrentAnimationFrameTime;
FrameTimeout timeout = GetTimeoutForFrame(aState.mCurrentAnimationFrameIndex);
Maybe<FrameTimeout> timeout = GetTimeoutForFrame(aState, aState.mCurrentAnimationFrameIndex);
if (timeout == FrameTimeout::Forever()) {
if (timeout.isNothing()) {
MOZ_ASSERT(aState.GetHasBeenDecoded() && !aState.GetIsCurrentlyDecoded());
return Nothing();
}
if (*timeout == FrameTimeout::Forever()) {
// We need to return a sentinel value in this case, because our logic
// doesn't work correctly if we have an infinitely long timeout. We use one
// year in the future as the sentinel because it works with the loop in
// RequestRefresh() below.
// XXX(seth): It'd be preferable to make our logic work correctly with
// infinitely long timeouts.
return TimeStamp::NowLoRes() +
TimeDuration::FromMilliseconds(31536000.0);
return Some(TimeStamp::NowLoRes() +
TimeDuration::FromMilliseconds(31536000.0));
}
TimeDuration durationOfTimeout =
TimeDuration::FromMilliseconds(double(timeout.AsMilliseconds()));
TimeDuration::FromMilliseconds(double(timeout->AsMilliseconds()));
TimeStamp currentFrameEndTime = currentFrameTime + durationOfTimeout;
return currentFrameEndTime;
return Some(currentFrameEndTime);
}
RefreshResult
@ -238,7 +243,11 @@ FrameAnimator::AdvanceFrame(AnimationState& aState, TimeStamp aTime)
return ret;
}
if (GetTimeoutForFrame(nextFrameIndex) == FrameTimeout::Forever()) {
Maybe<FrameTimeout> nextFrameTimeout = GetTimeoutForFrame(aState, nextFrameIndex);
// GetTimeoutForFrame can only return none if frame doesn't exist,
// but we just got it above.
MOZ_ASSERT(nextFrameTimeout.isSome());
if (*nextFrameTimeout == FrameTimeout::Forever()) {
ret.mAnimationFinished = true;
}
@ -252,7 +261,9 @@ FrameAnimator::AdvanceFrame(AnimationState& aState, TimeStamp aTime)
// something went wrong, move on to next
NS_WARNING("FrameAnimator::AdvanceFrame(): Compositing of frame failed");
nextFrame->SetCompositingFailed(true);
aState.mCurrentAnimationFrameTime = GetCurrentImgFrameEndTime(aState);
Maybe<TimeStamp> currentFrameEndTime = GetCurrentImgFrameEndTime(aState);
MOZ_ASSERT(currentFrameEndTime.isSome());
aState.mCurrentAnimationFrameTime = *currentFrameEndTime;
aState.mCurrentAnimationFrameIndex = nextFrameIndex;
return ret;
@ -261,7 +272,9 @@ FrameAnimator::AdvanceFrame(AnimationState& aState, TimeStamp aTime)
nextFrame->SetCompositingFailed(false);
}
aState.mCurrentAnimationFrameTime = GetCurrentImgFrameEndTime(aState);
Maybe<TimeStamp> currentFrameEndTime = GetCurrentImgFrameEndTime(aState);
MOZ_ASSERT(currentFrameEndTime.isSome());
aState.mCurrentAnimationFrameTime = *currentFrameEndTime;
// If we can get closer to the current time by a multiple of the image's loop
// time, we should. We can only do this if we're done decoding; otherwise, we
@ -301,10 +314,18 @@ FrameAnimator::RequestRefresh(AnimationState& aState, const TimeStamp& aTime)
// only advance the frame if the current time is greater than or
// equal to the current frame's end time.
TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime(aState);
Maybe<TimeStamp> currentFrameEndTime = GetCurrentImgFrameEndTime(aState);
if (currentFrameEndTime.isNothing()) {
MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
MOZ_ASSERT(aState.GetHasBeenDecoded() && !aState.GetIsCurrentlyDecoded());
MOZ_ASSERT(aState.mCompositedFrameInvalid);
// Nothing we can do but wait for our previous current frame to be decoded
// again so we can determine what to do next.
return ret;
}
while (currentFrameEndTime <= aTime) {
TimeStamp oldFrameEndTime = currentFrameEndTime;
while (*currentFrameEndTime <= aTime) {
TimeStamp oldFrameEndTime = *currentFrameEndTime;
RefreshResult frameRes = AdvanceFrame(aState, aTime);
@ -312,17 +333,19 @@ FrameAnimator::RequestRefresh(AnimationState& aState, const TimeStamp& aTime)
ret.Accumulate(frameRes);
currentFrameEndTime = GetCurrentImgFrameEndTime(aState);
// AdvanceFrame can't advance to a frame that doesn't exist yet.
MOZ_ASSERT(currentFrameEndTime.isSome());
// If we didn't advance a frame, and our frame end time didn't change,
// then we need to break out of this loop & wait for the frame(s)
// to finish downloading.
if (!frameRes.mFrameAdvanced && (currentFrameEndTime == oldFrameEndTime)) {
if (!frameRes.mFrameAdvanced && (*currentFrameEndTime == oldFrameEndTime)) {
break;
}
}
// Advanced to the correct frame, the composited frame is now valid to be drawn.
if (currentFrameEndTime > aTime) {
if (*currentFrameEndTime > aTime) {
aState.mCompositedFrameInvalid = false;
}
@ -371,17 +394,18 @@ FrameAnimator::GetCompositedFrame(AnimationState& aState)
return result;
}
FrameTimeout
FrameAnimator::GetTimeoutForFrame(uint32_t aFrameNum) const
Maybe<FrameTimeout>
FrameAnimator::GetTimeoutForFrame(AnimationState& aState,
uint32_t aFrameNum) const
{
RawAccessFrameRef frame = GetRawFrame(aFrameNum);
if (frame) {
AnimationData data = frame->GetAnimationData();
return data.mTimeout;
return Some(data.mTimeout);
}
NS_WARNING("No frame; called GetTimeoutForFrame too early?");
return FrameTimeout::FromRawMilliseconds(100);
MOZ_ASSERT(aState.mHasBeenDecoded && !aState.mIsCurrentlyDecoded);
return Nothing();
}
static void

View File

@ -312,15 +312,17 @@ private: // methods
*/
RawAccessFrameRef GetRawFrame(uint32_t aFrameNum) const;
/// @return the given frame's timeout.
FrameTimeout GetTimeoutForFrame(uint32_t aFrameNum) const;
/// @return the given frame's timeout if it is available
Maybe<FrameTimeout> GetTimeoutForFrame(AnimationState& aState,
uint32_t aFrameNum) const;
/**
* Get the time the frame we're currently displaying is supposed to end.
*
* In the error case, returns an "infinity" timestamp.
* In the error case (like if the requested frame is not currently
* decoded), returns None().
*/
TimeStamp GetCurrentImgFrameEndTime(AnimationState& aState) const;
Maybe<TimeStamp> GetCurrentImgFrameEndTime(AnimationState& aState) const;
bool DoBlend(gfx::IntRect* aDirtyRect,
uint32_t aPrevFrameIndex,