tor-browser

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

animationPolling.js (13625B)


      1 // This file expects imgutils.js to be loaded as well.
      2 /* import-globals-from imgutils.js */
      3 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
      4 var currentTest;
      5 var gIsRefImageLoaded = false;
      6 const gShouldOutputDebugInfo = false;
      7 
      8 function pollForSuccess() {
      9  if (!currentTest.isTestFinished) {
     10    if (
     11      !currentTest.reusingReferenceImage ||
     12      (currentTest.reusingReferenceImage && gIsRefImageLoaded)
     13    ) {
     14      currentTest.checkImage();
     15    }
     16 
     17    setTimeout(pollForSuccess, currentTest.pollFreq);
     18  }
     19 }
     20 
     21 function reuseImageCallback() {
     22  gIsRefImageLoaded = true;
     23 }
     24 
     25 function failTest() {
     26  if (currentTest.isTestFinished || currentTest.closeFunc) {
     27    return;
     28  }
     29 
     30  ok(
     31    false,
     32    "timing out after " +
     33      currentTest.timeout +
     34      "ms.  " +
     35      "Animated image still doesn't look correct, after poll #" +
     36      currentTest.pollCounter
     37  );
     38  currentTest.wereFailures = true;
     39 
     40  if (currentTest.currentSnapshotDataURI) {
     41    currentTest.outputDebugInfo(
     42      "Snapshot #" + currentTest.pollCounter,
     43      "snapNum" + currentTest.pollCounter,
     44      currentTest.currentSnapshotDataURI
     45    );
     46  }
     47 
     48  currentTest.enableDisplay(
     49    document.getElementById(currentTest.debugElementId)
     50  );
     51 
     52  currentTest.cleanUpAndFinish();
     53 }
     54 
     55 /**
     56 * Create a new AnimationTest object.
     57 *
     58 * @param pollFreq The amount of time (in ms) to wait between consecutive
     59 *        snapshots if the reference image and the test image don't match.
     60 * @param timeout The total amount of time (in ms) to wait before declaring the
     61 *        test as failed.
     62 * @param referenceElementId The id attribute of the reference image element, or
     63 *        the source of the image to change to, once the reference snapshot has
     64 *        been successfully taken. This latter option could be used if you don't
     65 *        want the image to become invisible at any time during the test.
     66 * @param imageElementId The id attribute of the test image element.
     67 * @param debugElementId The id attribute of the div where links should be
     68 *        appended if the test fails.
     69 * @param cleanId The id attribute of the div or element to use as the 'clean'
     70 *        test. This element is only enabled when we are testing to verify that
     71 *        the reference image has been loaded. It can be undefined.
     72 * @param srcAttr The location of the source of the image, for preloading. This
     73 *        is usually not required, but it useful for preloading reference
     74 *        images.
     75 * @param xulTest A boolean value indicating whether or not this is a XUL test
     76 *        (uses hidden=true/false rather than display: none to hide/show
     77 *        elements).
     78 * @param closeFunc A function that should be called when this test is finished.
     79 *        If null, then cleanUpAndFinish() will be called. This can be used to
     80 *        chain tests together, so they are all finished exactly once.
     81 * @returns {AnimationTest}
     82 */
     83 function AnimationTest(
     84  pollFreq,
     85  timeout,
     86  referenceElementId,
     87  imageElementId,
     88  debugElementId,
     89  cleanId,
     90  srcAttr,
     91  xulTest,
     92  closeFunc
     93 ) {
     94  // We want to test the cold loading behavior, so clear cache in case an
     95  // earlier test got our image in there already.
     96  clearAllImageCaches();
     97 
     98  this.wereFailures = false;
     99  this.pollFreq = pollFreq;
    100  this.timeout = timeout;
    101  this.imageElementId = imageElementId;
    102  this.referenceElementId = referenceElementId;
    103 
    104  if (!document.getElementById(referenceElementId)) {
    105    // In this case, we're assuming the user passed in a string that
    106    // indicates the source of the image they want to change to,
    107    // after the reference image has been taken.
    108    this.reusingImageAsReference = true;
    109  }
    110 
    111  this.srcAttr = srcAttr;
    112  this.debugElementId = debugElementId;
    113  this.referenceSnapshot = ""; // value will be set in takeReferenceSnapshot()
    114  this.pollCounter = 0;
    115  this.isTestFinished = false;
    116  this.numRefsTaken = 0;
    117  this.blankWaitTime = 0;
    118 
    119  this.cleanId = cleanId ? cleanId : "";
    120  this.xulTest = xulTest ? xulTest : "";
    121  this.closeFunc = closeFunc ? closeFunc : "";
    122 }
    123 
    124 AnimationTest.prototype.preloadImage = function () {
    125  if (this.srcAttr) {
    126    this.myImage = new Image();
    127    this.myImage.onload = function () {
    128      currentTest.continueTest();
    129    };
    130    this.myImage.src = this.srcAttr;
    131  } else {
    132    this.continueTest();
    133  }
    134 };
    135 
    136 AnimationTest.prototype.outputDebugInfo = function (message, id, dataUri) {
    137  if (!gShouldOutputDebugInfo) {
    138    return;
    139  }
    140  var debugElement = document.getElementById(this.debugElementId);
    141  var newDataUriElement = document.createElement("a");
    142  newDataUriElement.setAttribute("id", id);
    143  newDataUriElement.setAttribute("href", dataUri);
    144  newDataUriElement.appendChild(document.createTextNode(message));
    145  debugElement.appendChild(newDataUriElement);
    146  var brElement = document.createElement("br");
    147  debugElement.appendChild(brElement);
    148  todo(false, "Debug (" + id + "): " + message + " " + dataUri);
    149 };
    150 
    151 AnimationTest.prototype.isFinished = function () {
    152  return this.isTestFinished;
    153 };
    154 
    155 AnimationTest.prototype.takeCleanSnapshot = function () {
    156  var cleanElement;
    157  if (this.cleanId) {
    158    cleanElement = document.getElementById(this.cleanId);
    159  }
    160 
    161  // Enable clean page comparison element
    162  if (cleanElement) {
    163    this.enableDisplay(cleanElement);
    164  }
    165 
    166  // Take a snapshot of the initial (clean) page
    167  this.cleanSnapshot = snapshotWindow(window, false);
    168 
    169  // Disable the clean page comparison element
    170  if (cleanElement) {
    171    this.disableDisplay(cleanElement);
    172  }
    173 
    174  var dataString1 = "Clean Snapshot";
    175  this.outputDebugInfo(
    176    dataString1,
    177    "cleanSnap",
    178    this.cleanSnapshot.toDataURL()
    179  );
    180 };
    181 
    182 AnimationTest.prototype.takeBlankSnapshot = function () {
    183  // Take a snapshot of the initial (essentially blank) page
    184  this.blankSnapshot = snapshotWindow(window, false);
    185 
    186  var dataString1 = "Initial Blank Snapshot";
    187  this.outputDebugInfo(
    188    dataString1,
    189    "blank1Snap",
    190    this.blankSnapshot.toDataURL()
    191  );
    192 };
    193 
    194 /**
    195 * Begin the AnimationTest. This will utilize the information provided in the
    196 * constructor to invoke a mochitest on animated images. It will automatically
    197 * fail if allowed to run past the timeout. This will attempt to preload an
    198 * image, if applicable, and then asynchronously call continueTest(), or if not
    199 * applicable, synchronously trigger a call to continueTest().
    200 */
    201 AnimationTest.prototype.beginTest = function () {
    202  SimpleTest.waitForExplicitFinish();
    203  SimpleTest.requestFlakyTimeout("untriaged");
    204 
    205  currentTest = this;
    206  this.preloadImage();
    207 };
    208 
    209 /**
    210 * This is the second part of the test. It is triggered (eventually) from
    211 * beginTest() either synchronously or asynchronously, as an image load
    212 * callback.
    213 */
    214 AnimationTest.prototype.continueTest = async function () {
    215  // In case something goes wrong, fail earlier than mochitest timeout,
    216  // and with more information.
    217  setTimeout(failTest, this.timeout);
    218 
    219  if (!this.reusingImageAsReference) {
    220    this.disableDisplay(document.getElementById(this.imageElementId));
    221  }
    222 
    223  let tookReference = new Promise(resolve => {
    224    this.takeReferenceSnapshot(resolve);
    225  });
    226 
    227  tookReference.then(() => {
    228    this.setupPolledImage();
    229    SimpleTest.executeSoon(pollForSuccess);
    230  });
    231 };
    232 
    233 AnimationTest.prototype.setupPolledImage = function () {
    234  // Make sure the image is visible
    235  if (!this.reusingImageAsReference) {
    236    this.enableDisplay(document.getElementById(this.imageElementId));
    237    var currentSnapshot = snapshotWindow(window, false);
    238    var result = compareSnapshots(
    239      currentSnapshot,
    240      this.referenceSnapshot,
    241      true
    242    );
    243 
    244    this.currentSnapshotDataURI = currentSnapshot.toDataURL();
    245 
    246    if (result[0]) {
    247      // SUCCESS!
    248      ok(true, "Animated image looks correct, at poll #" + this.pollCounter);
    249 
    250      this.outputDebugInfo(
    251        "Animated image",
    252        "animImage",
    253        this.currentSnapshotDataURI
    254      );
    255 
    256      this.outputDebugInfo(
    257        "Reference image",
    258        "refImage",
    259        this.referenceSnapshot.toDataURL()
    260      );
    261 
    262      this.cleanUpAndFinish();
    263    }
    264  } else if (!gIsRefImageLoaded) {
    265    this.myImage = new Image();
    266    this.myImage.onload = reuseImageCallback;
    267    document
    268      .getElementById(this.imageElementId)
    269      .setAttribute("src", this.referenceElementId);
    270  }
    271 };
    272 
    273 AnimationTest.prototype.checkImage = function () {
    274  if (this.isTestFinished) {
    275    return;
    276  }
    277 
    278  this.pollCounter++;
    279 
    280  // We need this for some tests, because we need to force the
    281  // test image to be visible.
    282  if (!this.reusingImageAsReference) {
    283    this.enableDisplay(document.getElementById(this.imageElementId));
    284  }
    285 
    286  var currentSnapshot = snapshotWindow(window, false);
    287  var result = compareSnapshots(currentSnapshot, this.referenceSnapshot, true);
    288 
    289  this.currentSnapshotDataURI = currentSnapshot.toDataURL();
    290 
    291  if (result[0]) {
    292    // SUCCESS!
    293    ok(true, "Animated image looks correct, at poll #" + this.pollCounter);
    294 
    295    this.outputDebugInfo("Animated image", "animImage", result[1]);
    296 
    297    this.outputDebugInfo("Reference image", "refImage", result[2]);
    298 
    299    this.cleanUpAndFinish();
    300  }
    301 };
    302 
    303 AnimationTest.prototype.takeReferenceSnapshot = function (resolve) {
    304  this.numRefsTaken++;
    305 
    306  // Test to make sure the reference image doesn't match a clean snapshot
    307  if (!this.cleanSnapshot) {
    308    this.takeCleanSnapshot();
    309  }
    310 
    311  // Used later to verify that the reference div disappeared
    312  if (!this.blankSnapshot) {
    313    this.takeBlankSnapshot();
    314  }
    315 
    316  if (this.reusingImageAsReference) {
    317    // Show reference elem (which is actually our image), & take a snapshot
    318    var referenceElem = document.getElementById(this.imageElementId);
    319    this.enableDisplay(referenceElem);
    320 
    321    this.referenceSnapshot = snapshotWindow(window, false);
    322 
    323    let snapResult = compareSnapshots(
    324      this.cleanSnapshot,
    325      this.referenceSnapshot,
    326      false
    327    );
    328    if (!snapResult[0]) {
    329      if (this.blankWaitTime > 2000) {
    330        // if it took longer than two seconds to load the image, we probably
    331        // have a problem.
    332        this.wereFailures = true;
    333        ok(
    334          snapResult[0],
    335          "Reference snapshot shouldn't match clean (non-image) snapshot"
    336        );
    337      } else {
    338        this.blankWaitTime += currentTest.pollFreq;
    339        // let's wait a bit and see if it clears up
    340        setTimeout(
    341          () => this.takeReferenceSnapshot(resolve),
    342          currentTest.pollFreq
    343        );
    344        return;
    345      }
    346    }
    347 
    348    ok(
    349      snapResult[0],
    350      "Reference snapshot shouldn't match clean (non-image) snapshot"
    351    );
    352 
    353    let dataString = "Reference Snapshot #" + this.numRefsTaken;
    354    this.outputDebugInfo(
    355      dataString,
    356      "refSnapId",
    357      this.referenceSnapshot.toDataURL()
    358    );
    359  } else {
    360    // Make sure the animation section is hidden
    361    this.disableDisplay(document.getElementById(this.imageElementId));
    362 
    363    // Show reference div, & take a snapshot
    364    var referenceDiv = document.getElementById(this.referenceElementId);
    365    this.enableDisplay(referenceDiv);
    366 
    367    this.referenceSnapshot = snapshotWindow(window, false);
    368    let snapResult = compareSnapshots(
    369      this.cleanSnapshot,
    370      this.referenceSnapshot,
    371      false
    372    );
    373    if (!snapResult[0]) {
    374      if (this.blankWaitTime > 2000) {
    375        // if it took longer than two seconds to load the image, we probably
    376        // have a problem.
    377        this.wereFailures = true;
    378        ok(
    379          snapResult[0],
    380          "Reference snapshot shouldn't match clean (non-image) snapshot"
    381        );
    382      } else {
    383        this.blankWaitTime += 20;
    384        // let's wait a bit and see if it clears up
    385        setTimeout(() => this.takeReferenceSnapshot(resolve), 20);
    386        return;
    387      }
    388    }
    389 
    390    ok(
    391      snapResult[0],
    392      "Reference snapshot shouldn't match clean (non-image) snapshot"
    393    );
    394 
    395    let dataString = "Reference Snapshot #" + this.numRefsTaken;
    396    this.outputDebugInfo(
    397      dataString,
    398      "refSnapId",
    399      this.referenceSnapshot.toDataURL()
    400    );
    401 
    402    // Re-hide reference div, and take another snapshot to be sure it's gone
    403    this.disableDisplay(referenceDiv);
    404    this.testBlankCameBack();
    405  }
    406  resolve();
    407 };
    408 
    409 AnimationTest.prototype.enableDisplay = function (element) {
    410  if (!element) {
    411    return;
    412  }
    413 
    414  if (!this.xulTest) {
    415    element.style.display = "";
    416  } else {
    417    element.setAttribute("hidden", "false");
    418  }
    419 };
    420 
    421 AnimationTest.prototype.disableDisplay = function (element) {
    422  if (!element) {
    423    return;
    424  }
    425 
    426  if (!this.xulTest) {
    427    element.style.display = "none";
    428  } else {
    429    element.setAttribute("hidden", "true");
    430  }
    431 };
    432 
    433 AnimationTest.prototype.testBlankCameBack = function () {
    434  var blankSnapshot2 = snapshotWindow(window, false);
    435  var result = compareSnapshots(this.blankSnapshot, blankSnapshot2, true);
    436  ok(
    437    result[0],
    438    "Reference image should disappear when it becomes display:none"
    439  );
    440 
    441  if (!result[0]) {
    442    this.wereFailures = true;
    443    var dataString = "Second Blank Snapshot";
    444    this.outputDebugInfo(dataString, "blank2SnapId", result[2]);
    445  }
    446 };
    447 
    448 AnimationTest.prototype.cleanUpAndFinish = function () {
    449  // On the off chance that failTest and checkImage are triggered
    450  // back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
    451  if (this.isTestFinished) {
    452    return;
    453  }
    454 
    455  this.isTestFinished = true;
    456 
    457  // Call our closing function, if one exists
    458  if (this.closeFunc) {
    459    this.closeFunc();
    460    return;
    461  }
    462 
    463  if (this.wereFailures) {
    464    document.getElementById(this.debugElementId).style.display = "block";
    465  }
    466 
    467  SimpleTest.finish();
    468  document.getElementById(this.debugElementId).style.display = "";
    469 };