mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 948245 part 2 - Allow the min attribute to extend the active duration; r=dholbert
The min attribute on an animation element can extend the active duration making it longer than the "repeat duration" (the amount of the time the animation runs including all repeats). For the remaining time after the repeat duration has completed until the end of the active duration the animation should apply its fill behavior. Previously this was not implemented and min could not extend the active duration. Allowing this effectively introduces an additional kind of state where we are both within the active interval but not animating. In this case we set the animation function (referred to as the "client" for historical reasons) to inactive so that effectively the timing model is active but the animation model is inactive. (In the future we will come up with something a little easier to understand when we rework this in terms of Web Animations components.)
This commit is contained in:
parent
096d486b89
commit
dc0d4cd8d9
@ -164,6 +164,15 @@ public:
|
||||
return (mIsActive || mIsFrozen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the animation is active.
|
||||
*
|
||||
* @return true if the animation is active, false otherwise.
|
||||
*/
|
||||
bool IsActive() const {
|
||||
return mIsActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this animation will replace the passed in result rather than
|
||||
* adding to it. Animations that replace the underlying value may be called
|
||||
|
@ -667,19 +667,32 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, bool aEndOnly)
|
||||
NS_ASSERTION(aContainerTime >= beginTime,
|
||||
"Sample time should not precede current interval");
|
||||
nsSMILTime activeTime = aContainerTime - beginTime;
|
||||
SampleSimpleTime(activeTime);
|
||||
// We register our repeat times as milestones (except when we're
|
||||
// seeking) so we should get a sample at exactly the time we repeat.
|
||||
// (And even when we are seeking we want to update
|
||||
// mCurrentRepeatIteration so we do that first before testing the seek
|
||||
// state.)
|
||||
uint32_t prevRepeatIteration = mCurrentRepeatIteration;
|
||||
if (ActiveTimeToSimpleTime(activeTime, mCurrentRepeatIteration)==0 &&
|
||||
|
||||
// The 'min' attribute can cause the active interval to be longer than
|
||||
// the 'repeating interval'.
|
||||
// In that extended period we apply the fill mode.
|
||||
if (GetRepeatDuration() <= nsSMILTimeValue(activeTime)) {
|
||||
if (mClient && mClient->IsActive()) {
|
||||
mClient->Inactivate(mFillMode == FILL_FREEZE);
|
||||
}
|
||||
SampleFillValue();
|
||||
} else {
|
||||
SampleSimpleTime(activeTime);
|
||||
|
||||
// We register our repeat times as milestones (except when we're
|
||||
// seeking) so we should get a sample at exactly the time we repeat.
|
||||
// (And even when we are seeking we want to update
|
||||
// mCurrentRepeatIteration so we do that first before testing the
|
||||
// seek state.)
|
||||
uint32_t prevRepeatIteration = mCurrentRepeatIteration;
|
||||
if (
|
||||
ActiveTimeToSimpleTime(activeTime, mCurrentRepeatIteration)==0 &&
|
||||
mCurrentRepeatIteration != prevRepeatIteration &&
|
||||
mCurrentRepeatIteration &&
|
||||
mSeekState == SEEK_NOT_SEEKING) {
|
||||
FireTimeEventAsync(NS_SMIL_REPEAT,
|
||||
static_cast<int32_t>(mCurrentRepeatIteration));
|
||||
FireTimeEventAsync(NS_SMIL_REPEAT,
|
||||
static_cast<int32_t>(mCurrentRepeatIteration));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1084,12 +1097,8 @@ nsSMILTimedElement::SetFillMode(const nsAString& aFillModeSpec)
|
||||
? nsSMILFillMode(temp.GetEnumValue())
|
||||
: FILL_REMOVE;
|
||||
|
||||
// Check if we're in a fill-able state: i.e. we've played at least one
|
||||
// interval and are now between intervals or at the end of all intervals
|
||||
bool isFillable = HasPlayed() &&
|
||||
(mElementState == STATE_WAITING || mElementState == STATE_POSTACTIVE);
|
||||
|
||||
if (mClient && mFillMode != previousFillMode && isFillable) {
|
||||
// Update fill mode of client
|
||||
if (mFillMode != previousFillMode && HasClientInFillRange()) {
|
||||
mClient->Inactivate(mFillMode == FILL_FREEZE);
|
||||
SampleFillValue();
|
||||
}
|
||||
@ -1102,9 +1111,9 @@ nsSMILTimedElement::UnsetFillMode()
|
||||
{
|
||||
uint16_t previousFillMode = mFillMode;
|
||||
mFillMode = FILL_REMOVE;
|
||||
if ((mElementState == STATE_WAITING || mElementState == STATE_POSTACTIVE) &&
|
||||
previousFillMode == FILL_FREEZE && mClient && HasPlayed())
|
||||
if (previousFillMode == FILL_FREEZE && HasClientInFillRange()) {
|
||||
mClient->Inactivate(false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -1865,8 +1874,7 @@ nsSMILTimedElement::ApplyMinAndMax(const nsSMILTimeValue& aDuration) const
|
||||
if (aDuration > mMax) {
|
||||
result = mMax;
|
||||
} else if (aDuration < mMin) {
|
||||
nsSMILTimeValue repeatDur = GetRepeatDuration();
|
||||
result = mMin > repeatDur ? repeatDur : mMin;
|
||||
result = mMin;
|
||||
} else {
|
||||
result = aDuration;
|
||||
}
|
||||
@ -2060,16 +2068,35 @@ nsSMILTimedElement::SampleFillValue()
|
||||
if (mFillMode != FILL_FREEZE || !mClient)
|
||||
return;
|
||||
|
||||
const nsSMILInterval* prevInterval = GetPreviousInterval();
|
||||
NS_ABORT_IF_FALSE(prevInterval,
|
||||
"Attempting to sample fill value but there is no previous interval");
|
||||
NS_ABORT_IF_FALSE(prevInterval->End()->Time().IsDefinite() &&
|
||||
prevInterval->End()->IsFixedTime(),
|
||||
"Attempting to sample fill value but the endpoint of the previous "
|
||||
"interval is not resolved and fixed");
|
||||
nsSMILTime activeTime;
|
||||
|
||||
nsSMILTime activeTime = prevInterval->End()->Time().GetMillis() -
|
||||
prevInterval->Begin()->Time().GetMillis();
|
||||
if (mElementState == STATE_WAITING || mElementState == STATE_POSTACTIVE) {
|
||||
const nsSMILInterval* prevInterval = GetPreviousInterval();
|
||||
NS_ABORT_IF_FALSE(prevInterval,
|
||||
"Attempting to sample fill value but there is no previous interval");
|
||||
NS_ABORT_IF_FALSE(prevInterval->End()->Time().IsDefinite() &&
|
||||
prevInterval->End()->IsFixedTime(),
|
||||
"Attempting to sample fill value but the endpoint of the previous "
|
||||
"interval is not resolved and fixed");
|
||||
|
||||
activeTime = prevInterval->End()->Time().GetMillis() -
|
||||
prevInterval->Begin()->Time().GetMillis();
|
||||
|
||||
// If the interval's repeat duration was shorter than its active duration,
|
||||
// use the end of the repeat duration to determine the frozen animation's
|
||||
// state.
|
||||
nsSMILTimeValue repeatDuration = GetRepeatDuration();
|
||||
if (repeatDuration.IsDefinite()) {
|
||||
activeTime = std::min(repeatDuration.GetMillis(), activeTime);
|
||||
}
|
||||
} else if (mElementState == STATE_ACTIVE) {
|
||||
// If we are being asked to sample the fill value while active we *must*
|
||||
// have a repeat duration shorter than the active duration so use that.
|
||||
MOZ_ASSERT(GetRepeatDuration().IsDefinite(),
|
||||
"Attempting to sample fill value of an active animation with "
|
||||
"an indefinite repeat duration");
|
||||
activeTime = GetRepeatDuration().GetMillis();
|
||||
}
|
||||
|
||||
uint32_t repeatIteration;
|
||||
nsSMILTime simpleTime =
|
||||
@ -2165,8 +2192,13 @@ nsSMILTimedElement::GetNextMilestone(nsSMILMilestone& aNextMilestone) const
|
||||
// Work out what comes next: the interval end or the next repeat iteration
|
||||
nsSMILTimeValue nextRepeat;
|
||||
if (mSeekState == SEEK_NOT_SEEKING && mSimpleDur.IsDefinite()) {
|
||||
nextRepeat.SetMillis(mCurrentInterval->Begin()->Time().GetMillis() +
|
||||
(mCurrentRepeatIteration + 1) * mSimpleDur.GetMillis());
|
||||
nsSMILTime nextRepeatActiveTime =
|
||||
(mCurrentRepeatIteration + 1) * mSimpleDur.GetMillis();
|
||||
// Check that the repeat fits within the repeat duration
|
||||
if (nsSMILTimeValue(nextRepeatActiveTime) < GetRepeatDuration()) {
|
||||
nextRepeat.SetMillis(mCurrentInterval->Begin()->Time().GetMillis() +
|
||||
nextRepeatActiveTime);
|
||||
}
|
||||
}
|
||||
nsSMILTimeValue nextMilestone =
|
||||
std::min(mCurrentInterval->End()->Time(), nextRepeat);
|
||||
@ -2275,6 +2307,15 @@ nsSMILTimedElement::GetPreviousInterval() const
|
||||
: mOldIntervals[mOldIntervals.Length()-1].get();
|
||||
}
|
||||
|
||||
bool
|
||||
nsSMILTimedElement::HasClientInFillRange() const
|
||||
{
|
||||
// Returns true if we have a client that is in the range where it will fill
|
||||
return mClient &&
|
||||
((mElementState != STATE_ACTIVE && HasPlayed()) ||
|
||||
(mElementState == STATE_ACTIVE && !mClient->IsActive()));
|
||||
}
|
||||
|
||||
bool
|
||||
nsSMILTimedElement::EndHasEventConditions() const
|
||||
{
|
||||
|
@ -512,6 +512,7 @@ protected:
|
||||
const nsSMILInstanceTime* GetEffectiveBeginInstance() const;
|
||||
const nsSMILInterval* GetPreviousInterval() const;
|
||||
bool HasPlayed() const { return !mOldIntervals.IsEmpty(); }
|
||||
bool HasClientInFillRange() const;
|
||||
bool EndHasEventConditions() const;
|
||||
bool AreEndTimesDependentOn(
|
||||
const nsSMILInstanceTime* aBase) const;
|
||||
|
@ -38,6 +38,7 @@ support-files =
|
||||
[test_smilMappedAttrFromBy.xhtml]
|
||||
[test_smilMappedAttrFromTo.xhtml]
|
||||
[test_smilMappedAttrPaced.xhtml]
|
||||
[test_smilMinTiming.html]
|
||||
[test_smilRepeatDuration.html]
|
||||
[test_smilRepeatTiming.xhtml]
|
||||
[test_smilReset.xhtml]
|
||||
|
93
content/smil/test/test_smilMinTiming.html
Normal file
93
content/smil/test/test_smilMinTiming.html
Normal file
@ -0,0 +1,93 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=948245
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 948245</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=948245">Mozilla Bug 948245</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<svg id="svg" onload="this.pauseAnimations()">
|
||||
<rect fill="red" id="rect" x="0">
|
||||
<animate attributeName="x" to="100" id="animation" dur="100s" min="200s"/>
|
||||
</rect>
|
||||
</svg>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
// The 'min' attribute introduces a kind of additional state into the SMIL
|
||||
// model. If the 'min' attribute extends the active duration, the additional
|
||||
// time between the amount of time the animation normally runs for (called the
|
||||
// 'repeat duration') and the extended active duration is filled using the
|
||||
// fill mode.
|
||||
//
|
||||
// Below we refer to this period of time between the end of the repeat
|
||||
// duration and the end of the active duration as the 'extended period'.
|
||||
//
|
||||
// This test verifies that as we jump in and out of these states we produce
|
||||
// the correct values.
|
||||
//
|
||||
// The test animation above produces an active interval that is longer than
|
||||
// the 'repeating duration' of the animation.
|
||||
var rect = $('rect'),
|
||||
animation = $('animation');
|
||||
|
||||
// Animation doesn't start until onload
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
window.addEventListener("load", runTests, false);
|
||||
|
||||
function runTests() {
|
||||
ok($('svg').animationsPaused(), "should be paused by <svg> load handler");
|
||||
|
||||
// In the extended period (t=150s) we should not be animating or filling
|
||||
// since the default fill mode is "none".
|
||||
animation.ownerSVGElement.setCurrentTime(150);
|
||||
ise(rect.x.animVal.value, 0,
|
||||
"Shouldn't fill in extended period with fill='none'");
|
||||
|
||||
// If we set the fill mode we should start filling.
|
||||
animation.setAttribute("fill", "freeze");
|
||||
ise(rect.x.animVal.value, 100,
|
||||
"Should fill in extended period with fill='freeze'");
|
||||
|
||||
// If we unset the fill attribute we should stop filling.
|
||||
animation.removeAttribute("fill");
|
||||
ise(rect.x.animVal.value, 0, "Shouldn't fill after unsetting fill");
|
||||
|
||||
// If we jump back into the repeated interval (at t=50s) we should be
|
||||
// animating.
|
||||
animation.ownerSVGElement.setCurrentTime(50);
|
||||
ise(rect.x.animVal.value, 50, "Should be active in repeating interval");
|
||||
|
||||
// If we jump to the boundary at the start of the extended period we should
|
||||
// not be filling (since we removed the fill attribute above).
|
||||
animation.ownerSVGElement.setCurrentTime(100);
|
||||
ise(rect.x.animVal.value, 0,
|
||||
"Shouldn't fill after seeking to boundary of extended period");
|
||||
|
||||
// If we apply a fill mode at this boundary point we should do regular fill
|
||||
// behavior of using the last value in the interpolation range.
|
||||
animation.setAttribute("fill", "freeze");
|
||||
ise(rect.x.animVal.value, 100,
|
||||
"Should fill at boundary to extended period");
|
||||
|
||||
// Check that if we seek past the interval we fill with the value at the end
|
||||
// of the _repeat_duration_ not the value at the end of the
|
||||
// _active_duration_.
|
||||
animation.setAttribute("repeatCount", "1.5");
|
||||
animation.ownerSVGElement.setCurrentTime(225);
|
||||
ise(rect.x.animVal.value, 50,
|
||||
"Should fill with the end of the repeat duration value");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
24
layout/reftests/svg/smil/min-1.svg
Normal file
24
layout/reftests/svg/smil/min-1.svg
Normal file
@ -0,0 +1,24 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
class="reftest-wait"
|
||||
onload="setTimeAndSnapshot(115, true)">
|
||||
<script xlink:href="smil-util.js" type="text/javascript"/>
|
||||
<!-- Test min behavior
|
||||
|
||||
Set up is as follows:
|
||||
1. There is a rectangle with a lime fill.
|
||||
2. A <set> animation (with default fill="none") sets the fill to
|
||||
orange at t=100s.
|
||||
It has a simple duration of 10s and a min duration of 20s.
|
||||
3. A further <set> animation sets the fill to red when the first
|
||||
animation finishes (using syncbase timing).
|
||||
|
||||
At time t=115s we should still be in the first animation's active
|
||||
interval with its fill mode of 'none' applied which should mean the
|
||||
original lime fill is used. -->
|
||||
<rect width="100%" height="100%" fill="lime">
|
||||
<set attributeName="fill" to="orange" dur="10s" min="20s" begin="100s"
|
||||
id="a"/>
|
||||
<set attributeName="fill" to="red" begin="a.end"/>
|
||||
</rect>
|
||||
</svg>
|
After Width: | Height: | Size: 1017 B |
@ -251,6 +251,8 @@ fuzzy-if(cocoaWidget&&layersGPUAccelerated,1,2) == anim-gradient-attr-presence-0
|
||||
# interaction between xml mapped attributes and their css equivalents
|
||||
== mapped-attr-vs-css-prop-1.svg lime.svg
|
||||
|
||||
== min-1.svg lime.svg
|
||||
|
||||
== smil-transitions-interaction-1a.svg lime.svg
|
||||
== smil-transitions-interaction-1b.svg lime.svg
|
||||
== smil-transitions-interaction-2a.svg lime.svg
|
||||
|
Loading…
Reference in New Issue
Block a user