Revert "Bug 1977019 - re-enable hw video decoding testing. r=media-playback-reviewers,jolin" for causing win mda failures on test_hw_video_decoding.html
This reverts commit 158474bdc0cf585b701bc47921f0a7d84f7bb84d.
<!DOCTYPE html><metacharset=utf-8><title>Test chrome-only MutationObserver animation notifications (sync tests)</title><!-- This file contains synchronous tests for animation mutation observers. In general we prefer to write synchronous tests since they are less likely to timeout when run on automation. Tests that require asynchronous steps (e.g. waiting on events) should be added to test_animations_observers_async.html instead.--><scripttype="application/javascript"src="../testharness.js"></script><scripttype="application/javascript"src="../testharnessreport.js"></script><scripttype="application/javascript"src="../testcommon.js"></script><divid="log"></div><style>@keyframesanim{to{transform:translate(100px);}}@keyframesanotherAnim{to{transform:translate(0px);}}</style><script>/** * Return a new MutationObserver which observing |target| element * with { animations: true, subtree: |subtree| } option. * * NOTE: This observer should be used only with takeRecords(). If any of * MutationRecords are observed in the callback of the MutationObserver, * it will raise an assertion. */functionsetupSynchronousObserver(t,target,subtree){varobserver=newMutationObserver(records=>{assert_unreached("Any MutationRecords should not be observed in this "+"callback");});t.add_cleanup(()=>{observer.disconnect();});observer.observe(target,{animations:true,subtree});returnobserver;}functionassert_record_list(actual,expected,desc,index,listName){assert_equals(actual.length,expected.length,`${desc} - record[${index}].${listName} length`);if(actual.length!=expected.length){return;}for(vari=0;i<actual.length;i++){assert_not_equals(actual.indexOf(expected[i]),-1,`${desc} - record[${index}].${listName} contains expected Animation`);}}functionassert_equals_records(actual,expected,desc){assert_equals(actual.length,expected.length,`${desc} - number of records`);if(actual.length!=expected.length){return;}for(vari=0;i<actual.length;i++){assert_record_list(actual[i].addedAnimations,expected[i].added,desc,i,"addedAnimations");assert_record_list(actual[i].changedAnimations,expected[i].changed,desc,i,"changedAnimations");assert_record_list(actual[i].removedAnimations,expected[i].removed,desc,i,"removedAnimations");}}functionrunTest(){[{subtree:false},{subtree:true}].forEach(aOptions=>{test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanim=div.animate({opacity:[0,1]},200*MS_PER_SEC);assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation is added");anim.effect.updateTiming({duration:100*MS_PER_SEC});assert_equals_records(observer.takeRecords(),[{added:[],changed:[anim],removed:[]}],"records after duration is changed");anim.effect.updateTiming({duration:100*MS_PER_SEC});assert_equals_records(observer.takeRecords(),[],"records after assigning same value");anim.currentTime=anim.effect.getComputedTiming().duration*2;anim.finish();assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:[anim]}],"records after animation end");anim.effect.updateTiming({duration:anim.effect.getComputedTiming().duration*3});assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation restarted");anim.effect.updateTiming({duration:'auto'});assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:[anim]}],"records after duration set \"auto\"");anim.effect.updateTiming({duration:'auto'});assert_equals_records(observer.takeRecords(),[],"records after assigning same value \"auto\"");},"change_duration_and_currenttime");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanim=div.animate({opacity:[0,1]},100*MS_PER_SEC);assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation is added");anim.effect.updateTiming({endDelay:10*MS_PER_SEC});assert_equals_records(observer.takeRecords(),[{added:[],changed:[anim],removed:[]}],"records after endDelay is changed");anim.effect.updateTiming({endDelay:10*MS_PER_SEC});assert_equals_records(observer.takeRecords(),[],"records after assigning same value");anim.currentTime=109*MS_PER_SEC;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:[anim]}],"records after currentTime during endDelay");anim.effect.updateTiming({endDelay:-110*MS_PER_SEC});assert_equals_records(observer.takeRecords(),[],"records after assigning negative value");},"change_enddelay_and_currenttime");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanim=div.animate({opacity:[0,1]},{duration:100*MS_PER_SEC,endDelay:-100*MS_PER_SEC});assert_equals_records(observer.takeRecords(),[],"records after animation is added");},"zero_end_time");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanim=div.animate({opacity:[0,1]},100*MS_PER_SEC);assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation is added");anim.effect.updateTiming({iterations:2});assert_equals_records(observer.takeRecords(),[{added:[],changed:[anim],removed:[]}],"records after iterations is changed");anim.effect.updateTiming({iterations:2});assert_equals_records(observer.takeRecords(),[],"records after assigning same value");anim.effect.updateTiming({iterations:0});assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:[anim]}],"records after animation end");anim.effect.updateTiming({iterations:Infinity});assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation restarted");},"change_iterations");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanim=div.animate({opacity:[0,1]},100*MS_PER_SEC);assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation is added");anim.effect.updateTiming({delay:100});assert_equals_records(observer.takeRecords(),[{added:[],changed:[anim],removed:[]}],"records after delay is changed");anim.effect.updateTiming({delay:100});assert_equals_records(observer.takeRecords(),[],"records after assigning same value");anim.effect.updateTiming({delay:-100*MS_PER_SEC});assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:[anim]}],"records after animation end");anim.effect.updateTiming({delay:0});assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation restarted");},"change_delay");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanim=div.animate({opacity:[0,1]},{duration:100*MS_PER_SEC,easing:"steps(2, start)"});assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation is added");anim.effect.updateTiming({easing:"steps(2, end)"});assert_equals_records(observer.takeRecords(),[{added:[],changed:[anim],removed:[]}],"records after easing is changed");anim.effect.updateTiming({easing:"steps(2, end)"});assert_equals_records(observer.takeRecords(),[],"records after assigning same value");},"change_easing");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanim=div.animate({opacity:[0,1]},{duration:100,delay:-100});assert_equals_records(observer.takeRecords(),[],"records after assigning negative value");},"negative_delay_in_constructor");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);vareffect=newKeyframeEffect(null,{opacity:[0,1]},{duration:100*MS_PER_SEC});varanim=newAnimation(effect,document.timeline);anim.play();assert_equals_records(observer.takeRecords(),[],"no records after animation is added");},"create_animation_without_target");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanim=div.animate({opacity:[0,1]},{duration:100*MS_PER_SEC});assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation is added");anim.effect.target=div;assert_equals_records(observer.takeRecords(),[],"no records after setting the same target");anim.effect.target=null;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:[anim]}],"records after setting null");anim.effect.target=null;assert_equals_records(observer.takeRecords(),[],"records after setting redundant null");},"set_redundant_animation_target");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanim=div.animate({opacity:[0,1]},{duration:100*MS_PER_SEC});assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation is added");anim.effect=null;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:[anim]}],"records after animation is removed");},"set_null_animation_effect");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanim=newAnimation();anim.play();anim.effect=newKeyframeEffect(div,{opacity:[0,1]},100*MS_PER_SEC);assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation is added");},"set_effect_on_null_effect_animation");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanim=div.animate({marginLeft:["0px","100px"]},100*MS_PER_SEC);assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation is added");anim.effect=newKeyframeEffect(div,{opacity:[0,1]},100*MS_PER_SEC);assert_equals_records(observer.takeRecords(),[{added:[],changed:[anim],removed:[]}],"records after replace effects");},"replace_effect_targeting_on_the_same_element");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanim=div.animate({marginLeft:["0px","100px"]},100*MS_PER_SEC);assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation is added");anim.currentTime=60*MS_PER_SEC;assert_equals_records(observer.takeRecords(),[{added:[],changed:[anim],removed:[]}],"records after animation is changed");anim.effect=newKeyframeEffect(div,{opacity:[0,1]},50*MS_PER_SEC);assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:[anim]}],"records after replacing effects");},"replace_effect_targeting_on_the_same_element_not_in_effect");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanim=div.animate({},100*MS_PER_SEC);assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation is added");anim.effect.composite="add";assert_equals_records(observer.takeRecords(),[{added:[],changed:[anim],removed:[]}],"records after composite is changed");anim.effect.composite="add";assert_equals_records(observer.takeRecords(),[],"no record after setting the same composite");},"set_composite");// Test that starting a single animation that is cancelled by calling// cancel() dispatches an added notification and then a removed// notification.test(t=>{vardiv=addDiv(t,{style:"animation: anim 100s forwards"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");animations[0].cancel();assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:animations}],"records after animation end");// Re-trigger the animation.animations[0].play();// Single MutationRecord for the Animation (re-)addition.assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");},"single_animation_cancelled_api");// Test that updating a property on the Animation object dispatches a changed// notification.[{prop:"playbackRate",val:0.5},{prop:"startTime",val:50*MS_PER_SEC},{prop:"currentTime",val:50*MS_PER_SEC},].forEach(aChangeTest=>{test(t=>{// We use a forwards fill mode so that even if the change we make causes// the animation to become finished, it will still be "relevant" so we// won't mark it as removed.vardiv=addDiv(t,{style:"animation: anim 100s forwards"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");// Update the property.animations[0][aChangeTest.prop]=aChangeTest.val;// Make a redundant change.// eslint-disable-next-line no-self-assignanimations[0][aChangeTest.prop]=animations[0][aChangeTest.prop];assert_equals_records(observer.takeRecords(),[{added:[],changed:animations,removed:[]}],"records after animation property change");},`single_animation_api_change_${aChangeTest.prop}`);});// Test that making a redundant change to currentTime while an Animation// is pause-pending still generates a change MutationRecord since setting// the currentTime to any value in this state aborts the pending pause.test(t=>{vardiv=addDiv(t,{style:"animation: anim 100s"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");animations[0].pause();// We are now pause-pending. Even if we make a redundant change to the// currentTime, we should still get a change record because setting the// currentTime while pause-pending has the effect of cancelling a pause.// eslint-disable-next-line no-self-assignanimations[0].currentTime=animations[0].currentTime;// Two MutationRecords for the Animation changes: one for pausing, one// for aborting the pause.assert_equals_records(observer.takeRecords(),[{added:[],changed:animations,removed:[]},{added:[],changed:animations,removed:[]}],"records after pausing then seeking");},"change_currentTime_while_pause_pending");// Test that calling finish() on a forwards-filling Animation dispatches// a changed notification.test(t=>{vardiv=addDiv(t,{style:"animation: anim 100s forwards"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");animations[0].finish();assert_equals_records(observer.takeRecords(),[{added:[],changed:animations,removed:[]}],"records after finish()");// Redundant finish.animations[0].finish();// Ensure no change records.assert_equals_records(observer.takeRecords(),[],"records after redundant finish()");},"finish_with_forwards_fill");// Test that calling finish() on an Animation that does not fill forwards,// dispatches a removal notification.test(t=>{vardiv=addDiv(t,{style:"animation: anim 100s"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");animations[0].finish();// Single MutationRecord for the Animation removal.assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:animations}],"records after finishing");},"finish_without_fill");// Test that calling finish() on a forwards-filling Animation dispatchestest(t=>{vardiv=addDiv(t,{style:"animation: anim 100s"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanimation=div.getAnimations()[0];assert_equals_records(observer.takeRecords(),[{added:[animation],changed:[],removed:[]}],"records after creation");animation.id="new id";assert_equals_records(observer.takeRecords(),[{added:[],changed:[animation],removed:[]}],"records after id is changed");animation.id="new id";assert_equals_records(observer.takeRecords(),[],"records after assigning same value with id");},"change_id");// Test that calling reverse() dispatches a changed notification.test(t=>{vardiv=addDiv(t,{style:"animation: anim 100s both"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");animations[0].reverse();assert_equals_records(observer.takeRecords(),[{added:[],changed:animations,removed:[]}],"records after calling reverse()");},"reverse");// Test that calling reverse() does *not* dispatch a changed notification// when playbackRate == 0.test(t=>{vardiv=addDiv(t,{style:"animation: anim 100s both"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");// Seek to the middle and set playbackRate to zero.animations[0].currentTime=50*MS_PER_SEC;animations[0].playbackRate=0;// Two MutationRecords, one for each change.assert_equals_records(observer.takeRecords(),[{added:[],changed:animations,removed:[]},{added:[],changed:animations,removed:[]}],"records after seeking and setting playbackRate");animations[0].reverse();// We should get no notifications.assert_equals_records(observer.takeRecords(),[],"records after calling reverse()");},"reverse_with_zero_playbackRate");// Test that reverse() on an Animation does *not* dispatch a changed// notification when it throws an exception.test(t=>{// Start an infinite animationvardiv=addDiv(t,{style:"animation: anim 10s infinite"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");// Shift the animation into the future such that when we call reverse// it will try to seek to the (infinite) end.animations[0].startTime=100*MS_PER_SEC;assert_equals_records(observer.takeRecords(),[{added:[],changed:animations,removed:[]}],"records after adjusting startTime");// Reverse: should throwassert_throws('InvalidStateError',()=>{animations[0].reverse();},'reverse() on future infinite animation throws an exception');// We should get no notifications.assert_equals_records(observer.takeRecords(),[],"records after calling reverse()");},"reverse_with_exception");// Test that attempting to start an animation that should already be finished// does not send any notifications.test(t=>{// Start an animation that should already be finished.vardiv=addDiv(t,{style:"animation: anim 1s -2s;"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);// The animation should cause no Animations to be created.varanimations=div.getAnimations();assert_equals(animations.length,0,"getAnimations().length after animation start");// And we should get no notifications.assert_equals_records(observer.takeRecords(),[],"records after attempted animation start");},"already_finished");test(t=>{vardiv=addDiv(t,{style:"animation: anim 100s, anotherAnim 100s"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanimations=div.getAnimations();assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after creation");div.style.animation="anotherAnim 100s, anim 100s";animations=div.getAnimations();assert_equals_records(observer.takeRecords(),[{added:[],changed:animations,removed:[]}],"records after the order is changed");div.style.animation="anotherAnim 100s, anim 100s";assert_equals_records(observer.takeRecords(),[],"no records after applying the same order");},"animation_order_change");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanim=div.animate({opacity:[0,1]},{duration:100*MS_PER_SEC,iterationComposite:'replace'});assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation is added");anim.effect.iterationComposite='accumulate';assert_equals_records(observer.takeRecords(),[{added:[],changed:[anim],removed:[]}],"records after iterationComposite is changed");anim.effect.iterationComposite='accumulate';assert_equals_records(observer.takeRecords(),[],"no record after setting the same iterationComposite");},"set_iterationComposite");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);varanim=div.animate({opacity:[0,1]},{duration:100*MS_PER_SEC});assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation is added");anim.effect.setKeyframes({opacity:0.1});assert_equals_records(observer.takeRecords(),[{added:[],changed:[anim],removed:[]}],"records after keyframes are changed");anim.effect.setKeyframes({opacity:0.1});assert_equals_records(observer.takeRecords(),[],"no record after setting the same keyframes");anim.effect.setKeyframes(null);assert_equals_records(observer.takeRecords(),[{added:[],changed:[anim],removed:[]}],"records after keyframes are set to empty");},"set_keyframes");// Test that starting a single transition that is cancelled by resetting// the transition-property property dispatches an added notification and// then a removed notification.test(t=>{vardiv=addDiv(t,{style:"transition: background-color 100s; "+"background-color: yellow;"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);getComputedStyle(div).transitionProperty;div.style.backgroundColor="lime";// The transition should cause the creation of a single Animation.varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after transition start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after transition start");// Cancel the transition by setting transition-property.div.style.transitionProperty="none";getComputedStyle(div).transitionProperty;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:animations}],"records after transition end");},"single_transition_cancelled_property");// Test that starting a single transition that is cancelled by setting// style to the currently animated value dispatches an added// notification and then a removed notification.test(t=>{// A long transition with a predictable value.vardiv=addDiv(t,{style:"transition: z-index 100s -51s; "+"z-index: 10;"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);getComputedStyle(div).transitionProperty;div.style.zIndex="100";// The transition should cause the creation of a single Animation.varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after transition start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after transition start");// Cancel the transition by setting the current animation value.letvalue="83";assert_equals(getComputedStyle(div).zIndex,value,"half-way transition value");div.style.zIndex=value;getComputedStyle(div).transitionProperty;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:animations}],"records after transition end");},"single_transition_cancelled_value");// Test that starting a single transition that is cancelled by setting// style to a non-interpolable value dispatches an added notification// and then a removed notification.test(t=>{vardiv=addDiv(t,{style:"transition: line-height 100s; "+"line-height: 16px;"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);getComputedStyle(div).transitionProperty;div.style.lineHeight="100px";// The transition should cause the creation of a single Animation.varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after transition start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after transition start");// Cancel the transition by setting line-height to a non-interpolable value.div.style.lineHeight="normal";getComputedStyle(div).transitionProperty;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:animations}],"records after transition end");},"single_transition_cancelled_noninterpolable");// Test that starting a single transition and then reversing it// dispatches an added notification, then a simultaneous removed and// added notification, then a removed notification once finished.test(t=>{vardiv=addDiv(t,{style:"transition: background-color 100s step-start; "+"background-color: yellow;"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);getComputedStyle(div).transitionProperty;div.style.backgroundColor="lime";varanimations=div.getAnimations();// The transition should cause the creation of a single Animation.assert_equals(animations.length,1,"getAnimations().length after transition start");varfirstAnimation=animations[0];assert_equals_records(observer.takeRecords(),[{added:[firstAnimation],changed:[],removed:[]}],"records after transition start");firstAnimation.currentTime=50*MS_PER_SEC;// Reverse the transition by setting the background-color back to its// original value.div.style.backgroundColor="yellow";// The reversal should cause the creation of a new Animation.animations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after transition reversal");varsecondAnimation=animations[0];assert_true(firstAnimation!=secondAnimation,"second Animation should be different from the first");assert_equals_records(observer.takeRecords(),[{added:[],changed:[firstAnimation],removed:[]},{added:[secondAnimation],changed:[],removed:[firstAnimation]}],"records after transition reversal");// Cancel the transition.div.style.transitionProperty="none";getComputedStyle(div).transitionProperty;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:[secondAnimation]}],"records after transition end");},"single_transition_reversed");// Test that multiple transitions starting and ending on an element// at the same time get batched up into a single MutationRecord.test(t=>{vardiv=addDiv(t,{style:"transition-duration: 100s; "+"transition-property: color, background-color, line-height"+"background-color: yellow; line-height: 16px"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);getComputedStyle(div).transitionProperty;div.style.backgroundColor="lime";div.style.color="blue";div.style.lineHeight="24px";// The transitions should cause the creation of three Animations.varanimations=div.getAnimations();assert_equals(animations.length,3,"getAnimations().length after transition starts");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after transition starts");assert_equals(animations.filter(p=>p.playState=="running").length,3,"number of running Animations");// Seek well into each animation.animations.forEach(p=>p.currentTime=50*MS_PER_SEC);// Prepare the set of expected change MutationRecords, one for each// animation that was seeked.varseekRecords=animations.map(p=>({added:[],changed:[p],removed:[]}));// Cancel one of the transitions by setting transition-property.div.style.transitionProperty="background-color, line-height";varcolorAnimation=animations.filter(p=>p.playState!="running");varotherAnimations=animations.filter(p=>p.playState=="running");assert_equals(colorAnimation.length,1,"number of non-running Animations after cancelling one");assert_equals(otherAnimations.length,2,"number of running Animations after cancelling one");assert_equals_records(observer.takeRecords(),seekRecords.concat({added:[],changed:[],removed:colorAnimation}),"records after color transition end");// Cancel the remaining transitions.div.style.transitionProperty="none";getComputedStyle(div).transitionProperty;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:otherAnimations}],"records after other transition ends");},"multiple_transitions");// Test that starting a single animation that is cancelled by resetting// the animation-name property dispatches an added notification and// then a removed notification.test(t=>{vardiv=addDiv(t,{style:"animation: anim 100s"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);// The animation should cause the creation of a single Animation.varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");// Cancel the animation by setting animation-name.div.style.animationName="none";getComputedStyle(div).animationName;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:animations}],"records after animation end");},"single_animation_cancelled_name");// Test that starting a single animation that is cancelled by updating// the animation-duration property dispatches an added notification and// then a removed notification.test(t=>{vardiv=addDiv(t,{style:"animation: anim 100s"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);// The animation should cause the creation of a single Animation.varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");// Advance the animation by a second.animations[0].currentTime+=1*MS_PER_SEC;// Cancel the animation by setting animation-duration to a value less// than a second.div.style.animationDuration="0.1s";getComputedStyle(div).animationName;assert_equals_records(observer.takeRecords(),[{added:[],changed:animations,removed:[]},{added:[],changed:[],removed:animations}],"records after animation end");},"single_animation_cancelled_duration");// Test that starting a single animation that is cancelled by updating// the animation-delay property dispatches an added notification and// then a removed notification.test(t=>{vardiv=addDiv(t,{style:"animation: anim 100s"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);// The animation should cause the creation of a single Animation.varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");// Cancel the animation by setting animation-delay.div.style.animationDelay="-200s";getComputedStyle(div).animationName;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:animations}],"records after animation end");},"single_animation_cancelled_delay");// Test that starting a single animation that is cancelled by updating// the animation-iteration-count property dispatches an added notification// and then a removed notification.test(t=>{// A short, repeated animation.vardiv=addDiv(t,{style:"animation: anim 0.5s infinite;"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);// The animation should cause the creation of a single Animation.varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");// Advance the animation until we are past the first iteration.animations[0].currentTime+=1*MS_PER_SEC;assert_equals_records(observer.takeRecords(),[{added:[],changed:animations,removed:[]}],"records after seeking animations");// Cancel the animation by setting animation-iteration-count.div.style.animationIterationCount="1";getComputedStyle(div).animationName;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:animations}],"records after animation end");},"single_animation_cancelled_iteration_count");// Test that updating an animation property dispatches a changed notification.[{name:"duration",prop:"animationDuration",val:"200s"},{name:"timing",prop:"animationTimingFunction",val:"linear"},{name:"iteration",prop:"animationIterationCount",val:"2"},{name:"direction",prop:"animationDirection",val:"reverse"},{name:"state",prop:"animationPlayState",val:"paused"},{name:"delay",prop:"animationDelay",val:"-1s"},{name:"fill",prop:"animationFillMode",val:"both"},].forEach(aChangeTest=>{test(t=>{// Start a long animation.vardiv=addDiv(t,{style:"animation: anim 100s;"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);// The animation should cause the creation of a single Animation.varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");// Change a property of the animation such that it keeps running.div.style[aChangeTest.prop]=aChangeTest.val;getComputedStyle(div).animationName;assert_equals_records(observer.takeRecords(),[{added:[],changed:animations,removed:[]}],"records after animation change");// Cancel the animation.div.style.animationName="none";getComputedStyle(div).animationName;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:animations}],"records after animation end");},`single_animation_change_${aChangeTest.name}`);});// Test that calling finish() on a pause-pending (but otherwise finished)// animation dispatches a changed notification.test(t=>{vardiv=addDiv(t,{style:"animation: anim 100s forwards"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);// The animation should cause the creation of a single Animation.varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");// Finish and pause.animations[0].finish();animations[0].pause();assert_true(animations[0].pending&&animations[0].playState==="paused","playState after finishing and calling pause()");// Call finish() again to abort the pauseanimations[0].finish();assert_equals(animations[0].playState,"finished","playState after finishing again");// Wait for three MutationRecords for the Animation changes to// be delivered: one for each finish(), pause(), finish() operation.assert_equals_records(observer.takeRecords(),[{added:[],changed:animations,removed:[]},{added:[],changed:animations,removed:[]},{added:[],changed:animations,removed:[]}],"records after finish(), pause(), finish()");// Cancel the animation.div.style="";getComputedStyle(div).animationName;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:animations}],"records after animation end");},"finish_from_pause_pending");// Test that calling play() on a finished Animation that fills forwards// dispatches a changed notification.test(t=>{// Animation with a forwards fillvardiv=addDiv(t,{style:"animation: anim 100s forwards"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);// The animation should cause the creation of a single Animation.varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");// Seek to the endanimations[0].finish();assert_equals_records(observer.takeRecords(),[{added:[],changed:animations,removed:[]}],"records after finish()");// Since we are filling forwards, calling play() should produce a// change record since the animation remains relevant.animations[0].play();assert_equals_records(observer.takeRecords(),[{added:[],changed:animations,removed:[]}],"records after play()");// Cancel the animation.div.style="";getComputedStyle(div).animationName;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:animations}],"records after animation end");},"play_filling_forwards");// Test that calling pause() on an Animation dispatches a changed// notification.test(t=>{vardiv=addDiv(t,{style:"animation: anim 100s"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);// The animation should cause the creation of a single Animation.varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");// Pauseanimations[0].pause();assert_equals_records(observer.takeRecords(),[{added:[],changed:animations,removed:[]}],"records after pause()");// Redundant pauseanimations[0].pause();assert_equals_records(observer.takeRecords(),[],"records after redundant pause()");// Cancel the animation.div.style="";getComputedStyle(div).animationName;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:animations}],"records after animation end");},"pause");// Test that calling pause() on an Animation that is pause-pending// does not dispatch an additional changed notification.test(t=>{vardiv=addDiv(t,{style:"animation: anim 100s"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);// The animation should cause the creation of a single Animation.varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");// Pauseanimations[0].pause();// We are now pause-pending, but pause againanimations[0].pause();assert_equals_records(observer.takeRecords(),[{added:[],changed:animations,removed:[]}],"records after pause()");// Cancel the animation.div.style="";getComputedStyle(div).animationName;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:animations}],"records after animation end");},"pause_while_pause_pending");// Test that calling play() on an Animation that is pause-pending// dispatches a changed notification.test(t=>{vardiv=addDiv(t,{style:"animation: anim 100s"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);// The animation should cause the creation of a single Animation.varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");// Pauseanimations[0].pause();// We are now pause-pending. If we play() now, we will abort the pauseanimations[0].play();assert_equals_records(observer.takeRecords(),[{added:[],changed:animations,removed:[]},{added:[],changed:animations,removed:[]}],"records after aborting a pause()");// Cancel the animation.div.style="";getComputedStyle(div).animationName;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:animations}],"records after animation end");},"aborted_pause");// Test that calling play() on a finished Animation that does *not* fill// forwards dispatches an addition notification.test(t=>{vardiv=addDiv(t,{style:"animation: anim 100s"});varobserver=setupSynchronousObserver(t,aOptions.subtree?div.parentNode:div,aOptions.subtree);// The animation should cause the creation of a single Animation.varanimations=div.getAnimations();assert_equals(animations.length,1,"getAnimations().length after animation start");assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after animation start");// Seek to the endanimations[0].finish();assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:animations}],"records after finish()");// Since we are *not* filling forwards, calling play() is equivalent// to creating a new animation since it becomes relevant again.animations[0].play();assert_equals_records(observer.takeRecords(),[{added:animations,changed:[],removed:[]}],"records after play()");// Cancel the animation.div.style="";getComputedStyle(div).animationName;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:animations}],"records after animation end");},"play_after_finish");});test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,div,true);varchild=document.createElement("div");div.appendChild(child);varanim1=div.animate({marginLeft:["0px","50px"]},100*MS_PER_SEC);varanim2=child.animate({marginLeft:["0px","100px"]},50*MS_PER_SEC);assert_equals_records(observer.takeRecords(),[{added:[anim1],changed:[],removed:[]},{added:[anim2],changed:[],removed:[]}],"records after animation is added");// After setting a new effect, we remove the current animation, anim1,// because it is no longer attached to |div|, and then remove the previous// animation, anim2. Finally, add back the anim1 which is in effect on// |child| now. In addition, we sort them by tree order and they are// batched.anim1.effect=anim2.effect;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:[anim1]},// div{added:[anim1],changed:[],removed:[anim2]}],// child"records after animation effects are changed");},"set_effect_with_previous_animation");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,document,true);varanim=div.animate({opacity:[0,1]},{duration:100*MS_PER_SEC});varnewTarget=document.createElement("div");assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation is added");anim.effect.target=null;assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:[anim]}],"records after setting null");anim.effect.target=div;assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after setting a target");anim.effect.target=addDiv(t);assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:[anim]},{added:[anim],changed:[],removed:[]}],"records after setting a different target");},"set_animation_target");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,div,true);varanim=div.animate({opacity:[0,1]},{duration:200*MS_PER_SEC,pseudoElement:'::before'});assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation is added");anim.effect.updateTiming({duration:100*MS_PER_SEC});assert_equals_records(observer.takeRecords(),[{added:[],changed:[anim],removed:[]}],"records after duration is changed");anim.effect.updateTiming({duration:100*MS_PER_SEC});assert_equals_records(observer.takeRecords(),[],"records after assigning same value");anim.currentTime=anim.effect.getComputedTiming().duration*2;anim.finish();assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:[anim]}],"records after animation end");anim.effect.updateTiming({duration:anim.effect.getComputedTiming().duration*3});assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation restarted");anim.effect.updateTiming({duration:"auto"});assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:[anim]}],"records after duration set \"auto\"");anim.effect.updateTiming({duration:"auto"});assert_equals_records(observer.takeRecords(),[],"records after assigning same value \"auto\"");},"change_duration_and_currenttime_on_pseudo_elements");test(t=>{vardiv=addDiv(t);varobserver=setupSynchronousObserver(t,div,false);varanim=div.animate({opacity:[0,1]},{duration:100*MS_PER_SEC});varpAnim=div.animate({opacity:[0,1]},{duration:100*MS_PER_SEC,pseudoElement:"::before"});assert_equals_records(observer.takeRecords(),[{added:[anim],changed:[],removed:[]}],"records after animation is added");anim.finish();pAnim.finish();assert_equals_records(observer.takeRecords(),[{added:[],changed:[],removed:[anim]}],"records after animation is finished");},"exclude_animations_targeting_pseudo_elements");}W3CTest.runner.expectAssertions(0,12);// bug 1189015runTest();</script>