<!DOCTYPE HTML><html><!--https://bugzilla.mozilla.org/show_bug.cgi?id=686905--><head><title>Test that animated images can be discarded</title><scriptsrc="/tests/SimpleTest/SimpleTest.js"></script><scriptsrc="/tests/SimpleTest/WindowSnapshot.js"></script><scripttype="text/javascript"src="imgutils.js"></script><linkrel="stylesheet"type="text/css"href="/tests/SimpleTest/test.css"/></head><body><atarget="_blank"href="https://bugzilla.mozilla.org/show_bug.cgi?id=686905">Mozilla Bug 686905</a><pid="display"></p><divid="content"><divid="container"><canvasid="canvas"width="100"height="100"></canvas><!-- NOTE if adding a new image here you need to adjust four other places: * add it to the gImgs array below * add it to the 4 arrays below that * add it to the condition in checkIfFinished if it's infinite * potentially update the starting index of finite imgs in addCallbacks observer.discard. --><imgid="infinitepng"src="infinite-apng.png"><imgid="infinitegif"src="animated1.gif"><imgid="infinitewebp"src="infinite.webp"><imgid="infiniteavif"src="infinite.avif"><imgid="corruptinfinitegif"src="1835509.gif"><imgid="finitepng"src="restore-previous.png"><imgid="finitegif"src="animated-gif.gif"><imgid="finitewebp"src="keep.webp"><imgid="finiteavif"src="animated-avif.avif"><!-- NOTE see above the steps you need to do if adding an img here --></div></div><preid="test"><scriptclass="testbody"type="text/javascript">/** Test for Bug 686905. **/SimpleTest.waitForExplicitFinish();vargFinished=false;vargNumDiscards=0;window.onload=function(){// Enable discarding for the test.SpecialPowers.pushPrefEnv({'set':[['image.mem.discardable',true],['image.avif.sequence.enabled',true]]},runTest);}vargImgs=['infinitepng','infinitegif','infinitewebp','infiniteavif','corruptinfinitegif','finitepng','finitegif','finitewebp','finiteavif'];// If we are currently counting frame updates.vargCountingFrameUpdates=false;// The number of frame update notifications for the images in gImgs that happen// after discarding. (The last two images are finite looping so we don't expect// them to get incremented but it's possible if they don't finish their// animation before we discard them.)vargNumFrameUpdates=[0,0,0,0,0,0,0,0,0];// The last snapshot of the image. Used to check that the image actually changes.vargLastSnapShot=[null,null,null,null,null,null,null,null,null];// Number of observed changes in the snapshot.vargNumSnapShotChanges=[0,0,0,0,0,0,0,0,0];// If we've removed the observer.vargRemovedObserver=[false,false,false,false,false,false,false,false,false];// 2 would probably be a good enough test, we arbitrarily choose 4.varkNumFrameUpdatesToExpect=5;functionrunTest(){letnumImgsInDoc=document.getElementsByTagName("img").length;ok(gImgs.length==numImgsInDoc,"gImgs missing img");ok(gNumFrameUpdates.length==numImgsInDoc,"gNumFrameUpdates missing img");ok(gLastSnapShot.length==numImgsInDoc,"gLastSnapShot missing img");ok(gNumSnapShotChanges.length==numImgsInDoc,"gNumSnapShotChanges missing img");ok(gRemovedObserver.length==numImgsInDoc,"gRemovedObserver missing img");varanimatedDiscardable=SpecialPowers.getBoolPref('image.mem.animated.discardable');if(!animatedDiscardable){ok(true,"discarding of animated images is disabled, nothing to test");SimpleTest.finish();return;}setTimeout(step2,0);}functionstep2(){// Draw the images to canvas to force them to be decoded.for(leti=0;i<gImgs.length;i++){drawCanvas(document.getElementById(gImgs[i]));}for(leti=0;i<gImgs.length;i++){addCallbacks(document.getElementById(gImgs[i]),i);}setTimeout(step3,0);}functionstep3(){document.getElementById("container").style.display="none";document.documentElement.offsetLeft;// force that style to take effectfor(vari=0;i<gImgs.length;i++){requestDiscard(document.getElementById(gImgs[i]));}// the discard observers will call step4}functionstep4(){gCountingFrameUpdates=true;document.getElementById("container").style.display="";// Draw the images to canvas to force them to be decoded again.for(vari=0;i<gImgs.length;i++){drawCanvas(document.getElementById(gImgs[i]));}}functioncheckIfFinished(){if(gFinished){return;}if((gNumFrameUpdates[0]>=kNumFrameUpdatesToExpect)&&(gNumFrameUpdates[1]>=kNumFrameUpdatesToExpect)&&(gNumFrameUpdates[2]>=kNumFrameUpdatesToExpect)&&(gNumFrameUpdates[3]>=kNumFrameUpdatesToExpect)&&(gNumFrameUpdates[4]>=kNumFrameUpdatesToExpect)&&(gNumSnapShotChanges[0]>=kNumFrameUpdatesToExpect)&&(gNumSnapShotChanges[1]>=kNumFrameUpdatesToExpect)&&(gNumSnapShotChanges[2]>=kNumFrameUpdatesToExpect)&&(gNumSnapShotChanges[3]>=kNumFrameUpdatesToExpect)&&(gNumSnapShotChanges[4]>=kNumFrameUpdatesToExpect)){ok(true,"got expected frame updates");gFinished=true;SimpleTest.finish();}}// arrayIndex is the index into the arrays gNumFrameUpdates and gNumDecodes// to increment when a frame update notification is received.functionaddCallbacks(anImage,arrayIndex){varobserver=newImageDecoderObserverStub();observer.discard=function(){gNumDiscards++;ok(true,"got image discard");if(arrayIndex>=5){// The last four images are finite, so we don't expect any frame updates,// this image is done the test, so remove the observer.if(!gRemovedObserver[arrayIndex]){gRemovedObserver[arrayIndex]=true;imgLoadingContent.removeObserver(scriptedObserver);}}if(gNumDiscards==gImgs.length){step4();}};observer.frameUpdate=function(){if(!gCountingFrameUpdates){return;}// Do this off a setTimeout since nsImageLoadingContent uses a scriptblocker// when it notifies us and calling drawWindow can call will paint observers// which can dispatch a scrollport event, and events assert if dispatched// when there is a scriptblocker.setTimeout(function(){gNumFrameUpdates[arrayIndex]++;varr=document.getElementById(gImgs[arrayIndex]).getBoundingClientRect();varsnapshot=snapshotRect(window,r,"rgba(0,0,0,0)");if(gLastSnapShot[arrayIndex]!=null){if(snapshot.toDataURL()!=gLastSnapShot[arrayIndex].toDataURL()){gNumSnapShotChanges[arrayIndex]++;}}gLastSnapShot[arrayIndex]=snapshot;if(gNumFrameUpdates[arrayIndex]>=kNumFrameUpdatesToExpect&&gNumSnapShotChanges[arrayIndex]>=kNumFrameUpdatesToExpect){if(!gRemovedObserver[arrayIndex]){gRemovedObserver[arrayIndex]=true;imgLoadingContent.removeObserver(scriptedObserver);}}if(!gFinished){// because we do this in a setTimeout we can have several in flight// so don't call ok if we've already finished.ok(true,"got frame update");}checkIfFinished();},0);};observer=SpecialPowers.wrapCallbackObject(observer);varscriptedObserver=SpecialPowers.Cc["@mozilla.org/image/tools;1"].getService(SpecialPowers.Ci.imgITools).createScriptedObserver(observer);varimgLoadingContent=SpecialPowers.wrap(anImage);imgLoadingContent.addObserver(scriptedObserver);}functionrequestDiscard(anImage){varrequest=SpecialPowers.wrap(anImage).getRequest(SpecialPowers.Ci.nsIImageLoadingContent.CURRENT_REQUEST);setTimeout(()=>request.requestDiscard(),0);}functiondrawCanvas(anImage){varcanvas=document.getElementById('canvas');varcontext=canvas.getContext('2d');context.clearRect(0,0,100,100);varcleared=canvas.toDataURL();context.drawImage(anImage,0,0);ok(true,"we got through the drawImage call without an exception being thrown");ok(cleared!=canvas.toDataURL(),"drawImage drew something");}</script></pre></body></html>