tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_discardAnimatedImage.html (8452B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <!--
      4 https://bugzilla.mozilla.org/show_bug.cgi?id=686905
      5 -->
      6 <head>
      7  <title>Test that animated images can be discarded</title>
      8  <script src="/tests/SimpleTest/SimpleTest.js"></script>
      9  <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
     10  <script type="text/javascript" src="imgutils.js"></script>
     11  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
     12 </head>
     13 <body>
     14 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=686905">Mozilla Bug 686905</a>
     15 <p id="display"></p>
     16 <div id="content">
     17  <div id="container">
     18    <canvas id="canvas" width="100" height="100"></canvas>
     19 
     20    <!-- NOTE if adding a new image here you need to adjust four other places:
     21      * add it to the gImgs array below
     22      * add it to the 4 arrays below that
     23      * add it to the condition in checkIfFinished if it's infinite
     24      * potentially update the starting index of finite imgs in
     25        addCallbacks observer.discard.
     26    -->
     27    <img id="infinitepng" src="infinite-apng.png">
     28    <img id="infinitegif" src="animated1.gif">
     29    <img id="infinitewebp" src="infinite.webp">
     30    <img id="infiniteavif" src="infinite.avif">
     31 
     32    <img id="corruptinfinitegif" src="1835509.gif">
     33 
     34    <img id="finitepng" src="restore-previous.png">
     35    <img id="finitegif" src="animated-gif.gif">
     36    <img id="finitewebp" src="keep.webp">
     37    <img id="finiteavif" src="animated-avif.avif">
     38    <!-- NOTE see above the steps you need to do if adding an img here -->
     39 
     40  </div>
     41 </div>
     42 <pre id="test">
     43 <script class="testbody" type="text/javascript">
     44 
     45 /** Test for Bug 686905. */
     46 SimpleTest.waitForExplicitFinish();
     47 
     48 var gFinished = false;
     49 
     50 var gNumDiscards = 0;
     51 
     52 window.onload = function() {
     53  // Enable discarding for the test.
     54  SpecialPowers.pushPrefEnv({
     55    'set':[['image.mem.discardable',true],
     56           ['image.avif.sequence.enabled',true]]
     57  }, runTest);
     58 }
     59 
     60 var gImgs = ['infinitepng', 'infinitegif', 'infinitewebp', 'infiniteavif',
     61             'corruptinfinitegif',
     62             'finitepng',   'finitegif',   'finitewebp', 'finiteavif'];
     63 // If we are currently counting frame updates.
     64 var gCountingFrameUpdates = false;
     65 // The number of frame update notifications for the images in gImgs that happen
     66 // after discarding. (The last two images are finite looping so we don't expect
     67 // them to get incremented but it's possible if they don't finish their
     68 // animation before we discard them.)
     69 var gNumFrameUpdates = [0, 0, 0, 0, 0, 0, 0, 0, 0];
     70 // The last snapshot of the image. Used to check that the image actually changes.
     71 var gLastSnapShot = [null, null, null, null, null, null, null, null, null];
     72 // Number of observed changes in the snapshot.
     73 var gNumSnapShotChanges = [0, 0, 0, 0, 0, 0, 0, 0, 0];
     74 // If we've removed the observer.
     75 var gRemovedObserver = [false, false, false, false, false, false, false, false, false];
     76 
     77 // 2 would probably be a good enough test, we arbitrarily choose 4.
     78 var kNumFrameUpdatesToExpect = 5;
     79 
     80 function runTest() {
     81  let numImgsInDoc = document.getElementsByTagName("img").length;
     82  ok(gImgs.length == numImgsInDoc, "gImgs missing img");
     83  ok(gNumFrameUpdates.length == numImgsInDoc, "gNumFrameUpdates missing img");
     84  ok(gLastSnapShot.length == numImgsInDoc, "gLastSnapShot missing img");
     85  ok(gNumSnapShotChanges.length == numImgsInDoc, "gNumSnapShotChanges missing img");
     86  ok(gRemovedObserver.length == numImgsInDoc, "gRemovedObserver missing img");
     87 
     88  var animatedDiscardable =
     89    SpecialPowers.getBoolPref('image.mem.animated.discardable');
     90  if (!animatedDiscardable) {
     91    ok(true, "discarding of animated images is disabled, nothing to test");
     92    SimpleTest.finish();
     93    return;
     94  }
     95 
     96  setTimeout(step2, 0);
     97 }
     98 
     99 function step2() {
    100  // Draw the images to canvas to force them to be decoded.
    101  for (let i = 0; i < gImgs.length; i++) {
    102    drawCanvas(document.getElementById(gImgs[i]));
    103  }
    104 
    105  for (let i = 0; i < gImgs.length; i++) {
    106    addCallbacks(document.getElementById(gImgs[i]), i);
    107  }
    108 
    109  setTimeout(step3, 0);
    110 }
    111 
    112 function step3() {
    113  document.getElementById("container").style.display = "none";
    114  document.documentElement.offsetLeft; // force that style to take effect
    115 
    116  for (var i = 0; i < gImgs.length; i++) {
    117    requestDiscard(document.getElementById(gImgs[i]));
    118  }
    119 
    120  // the discard observers will call step4
    121 }
    122 
    123 function step4() {
    124  gCountingFrameUpdates = true;
    125  document.getElementById("container").style.display = "";
    126 
    127  // Draw the images to canvas to force them to be decoded again.
    128  for (var i = 0; i < gImgs.length; i++) {
    129    drawCanvas(document.getElementById(gImgs[i]));
    130  }
    131 }
    132 
    133 function checkIfFinished() {
    134  if (gFinished) {
    135    return;
    136  }
    137 
    138  if ((gNumFrameUpdates[0] >= kNumFrameUpdatesToExpect) &&
    139      (gNumFrameUpdates[1] >= kNumFrameUpdatesToExpect) &&
    140      (gNumFrameUpdates[2] >= kNumFrameUpdatesToExpect) &&
    141      (gNumFrameUpdates[3] >= kNumFrameUpdatesToExpect) &&
    142      (gNumFrameUpdates[4] >= kNumFrameUpdatesToExpect) &&
    143      (gNumSnapShotChanges[0] >= kNumFrameUpdatesToExpect) &&
    144      (gNumSnapShotChanges[1] >= kNumFrameUpdatesToExpect) &&
    145      (gNumSnapShotChanges[2] >= kNumFrameUpdatesToExpect) &&
    146      (gNumSnapShotChanges[3] >= kNumFrameUpdatesToExpect) &&
    147      (gNumSnapShotChanges[4] >= kNumFrameUpdatesToExpect)) {
    148    ok(true, "got expected frame updates");
    149    gFinished = true;
    150    SimpleTest.finish();
    151  }
    152 }
    153 
    154 // arrayIndex is the index into the arrays gNumFrameUpdates and gNumDecodes
    155 // to increment when a frame update notification is received.
    156 function addCallbacks(anImage, arrayIndex) {
    157  var observer = new ImageDecoderObserverStub();
    158  observer.discard = function () {
    159    gNumDiscards++;
    160    ok(true, "got image discard");
    161    if (arrayIndex >= 5) {
    162      // The last four images are finite, so we don't expect any frame updates,
    163      // this image is done the test, so remove the observer.
    164      if (!gRemovedObserver[arrayIndex]) {
    165        gRemovedObserver[arrayIndex] = true;
    166        imgLoadingContent.removeObserver(scriptedObserver);
    167      }
    168    }
    169    if (gNumDiscards == gImgs.length) {
    170      step4();
    171    }
    172  };
    173  observer.frameUpdate = function () {
    174    if (!gCountingFrameUpdates) {
    175      return;
    176    }
    177 
    178    // Do this off a setTimeout since nsImageLoadingContent uses a scriptblocker
    179    // when it notifies us and calling drawWindow can call will paint observers
    180    // which can dispatch a scrollport event, and events assert if dispatched
    181    // when there is a scriptblocker.
    182    setTimeout(function () {
    183      gNumFrameUpdates[arrayIndex]++;
    184 
    185      var r = document.getElementById(gImgs[arrayIndex]).getBoundingClientRect();
    186      var snapshot = snapshotRect(window, r, "rgba(0,0,0,0)");
    187      if (gLastSnapShot[arrayIndex] != null) {
    188        if (snapshot.toDataURL() != gLastSnapShot[arrayIndex].toDataURL()) {
    189          gNumSnapShotChanges[arrayIndex]++;
    190        }
    191      }
    192      gLastSnapShot[arrayIndex] = snapshot;
    193 
    194      if (gNumFrameUpdates[arrayIndex] >= kNumFrameUpdatesToExpect &&
    195          gNumSnapShotChanges[arrayIndex] >= kNumFrameUpdatesToExpect) {
    196        if (!gRemovedObserver[arrayIndex]) {
    197          gRemovedObserver[arrayIndex] = true;
    198          imgLoadingContent.removeObserver(scriptedObserver);
    199        }
    200      }
    201      if (!gFinished) {
    202        // because we do this in a setTimeout we can have several in flight
    203        // so don't call ok if we've already finished.
    204        ok(true, "got frame update");
    205      }
    206      checkIfFinished();
    207    }, 0);
    208  };
    209  observer = SpecialPowers.wrapCallbackObject(observer);
    210 
    211  var scriptedObserver = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
    212                           .getService(SpecialPowers.Ci.imgITools)
    213                           .createScriptedObserver(observer);
    214 
    215  var imgLoadingContent = SpecialPowers.wrap(anImage);
    216  imgLoadingContent.addObserver(scriptedObserver);
    217 }
    218 
    219 function requestDiscard(anImage) {
    220  var request = SpecialPowers.wrap(anImage)
    221      .getRequest(SpecialPowers.Ci.nsIImageLoadingContent.CURRENT_REQUEST);
    222  setTimeout(() => request.requestDiscard(), 0);
    223 }
    224 
    225 function drawCanvas(anImage) {
    226  var canvas = document.getElementById('canvas');
    227  var context = canvas.getContext('2d');
    228 
    229  context.clearRect(0,0,100,100);
    230  var cleared = canvas.toDataURL();
    231 
    232  context.drawImage(anImage, 0, 0);
    233  ok(true, "we got through the drawImage call without an exception being thrown");
    234 
    235  ok(cleared != canvas.toDataURL(), "drawImage drew something");
    236 }
    237 
    238 </script>
    239 </pre>
    240 </body>
    241 </html>