tor-browser

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

test_picture_mutations.html (10948B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <head>
      4  <meta charset="utf-8">
      5  <title>Image srcset mutations</title>
      6  <script src="/tests/SimpleTest/SimpleTest.js"></script>
      7  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
      8 </head>
      9 <body>
     10  <script type="application/javascript">
     11    "use strict";
     12 
     13    // Tests the relevant mutations part of the spec for <img> inside <picture> tags
     14    // https://html.spec.whatwg.org/#relevant-mutations
     15    SimpleTest.waitForExplicitFinish();
     16 
     17    // 50x50 png
     18    var testPNG50 = new URL("image_50.png", location).href;
     19    // 100x100 png
     20    var testPNG100 = new URL("image_100.png", location).href;
     21    // 200x200 png
     22    var testPNG200 = new URL("image_200.png", location).href;
     23 
     24    var tests = [];
     25    var img;
     26    var picture;
     27    var source1;
     28    var source2;
     29    var source3;
     30    var expectingErrors = 0;
     31    var expectingLoads = 0;
     32    var afterExpectCallback;
     33 
     34    function onImgLoad() {
     35      ok(expectingLoads > 0, "expected load");
     36      if (expectingLoads > 0) {
     37        expectingLoads--;
     38      }
     39      if (!expectingLoads && !expectingErrors) {
     40        setTimeout(afterExpectCallback, 0);
     41      }
     42    }
     43    function onImgError() {
     44      ok(expectingErrors > 0, "expected error");
     45      if (expectingErrors > 0) {
     46        expectingErrors--;
     47      }
     48      if (!expectingLoads && !expectingErrors) {
     49        setTimeout(afterExpectCallback, 0);
     50      }
     51    }
     52    function expectEvents(loads, errors, callback) {
     53      if (!loads && !errors) {
     54        setTimeout(callback, 0);
     55      } else {
     56        expectingLoads += loads;
     57        expectingErrors += errors;
     58        info("Waiting for " + expectingLoads + " load and " + expectingErrors + " error events");
     59        afterExpectCallback = callback;
     60      }
     61    }
     62 
     63    // Setup image outside the tree dom, make sure it loads
     64    tests.push(function() {
     65      info("test 1");
     66      img.srcset = testPNG100;
     67      img.src = testPNG50;
     68      is(img.currentSrc, '', "Should not have synchronously selected source");
     69 
     70      // No events should have fired synchronously, now we should get just one load (and no 404 error)
     71      expectEvents(1, 0, nextTest);
     72    });
     73 
     74    // Binding to an empty picture should trigger an event, even if source doesn't change
     75    tests.push(function() {
     76      info("test 2");
     77      is(img.currentSrc, testPNG100, "Should have loaded testPNG100");
     78      document.body.appendChild(picture);
     79      picture.appendChild(img);
     80      is(img.currentSrc, testPNG100, "Should still have testPNG100");
     81 
     82      expectEvents(1, 0, nextTest);
     83    });
     84 
     85    // inserting and removing an empty source before the image should both trigger a no-op reload
     86    tests.push(function() {
     87      info("test 3");
     88      is(img.currentSrc, testPNG100, "Should still have testPNG100");
     89      picture.insertBefore(source1, img);
     90      is(img.currentSrc, testPNG100, "Should still have testPNG100");
     91 
     92      // should fire one event, not change source
     93      expectEvents(1, 0, function() {
     94        is(img.currentSrc, testPNG100, "Should still have testPNG100");
     95        picture.removeChild(source1);
     96        is(img.currentSrc, testPNG100, "Should still have testPNG100");
     97 
     98        // Should also no-op fire
     99        expectEvents(1, 0, nextTest);
    100      });
    101    });
    102 
    103    // insert and remove valid source before
    104    tests.push(function() {
    105      info("test 4");
    106      is(img.currentSrc, testPNG100, "Should still have testPNG100");
    107 
    108      // Insert source1 before img with valid candidate
    109      source1.srcset = testPNG50;
    110      picture.insertBefore(source1, img);
    111      is(img.currentSrc, testPNG100, "Should still have testPNG100");
    112 
    113      // should fire one event, change to the source
    114      expectEvents(1, 0, function() {
    115        is(img.currentSrc, testPNG50, "Should have switched to testPNG50");
    116        picture.removeChild(source1);
    117        is(img.currentSrc, testPNG50, "Should still have testPNG50");
    118 
    119        // Should also no-op fire
    120        expectEvents(1, 0, function() {
    121          is(img.currentSrc, testPNG100, "Should have returned to testPNG100");
    122          nextTest();
    123        });
    124      });
    125    });
    126 
    127    // insert and remove valid source after
    128    tests.push(function() {
    129      info("test 5");
    130      is(img.currentSrc, testPNG100, "Should still have testPNG100");
    131 
    132      // Insert source1 before img with valid candidate
    133      source1.srcset = testPNG50;
    134      picture.appendChild(source1);
    135      is(img.currentSrc, testPNG100, "Should still have testPNG100");
    136 
    137      // should fire nothing, no action
    138      expectEvents(0, 0, function() {
    139        is(img.currentSrc, testPNG100, "Should still have testPNG100");
    140 
    141        // Same with removing
    142        picture.removeChild(source1);
    143        expectEvents(0, 0, function() {
    144          is(img.currentSrc, testPNG100, "Should still have testPNG100");
    145          nextTest();
    146        });
    147      });
    148    });
    149 
    150    // Should re-consider earlier sources when a load event occurs.
    151    tests.push(function() {
    152      info("test 6");
    153 
    154      // Insert two valid sources, with MQ causing us to select the second
    155      source1.srcset = testPNG50 + " 1x";
    156      source1.media = "(min-resolution: 2dppx)"; // Wont match, test starts at 1x
    157      source2.srcset = testPNG200;
    158      picture.insertBefore(source1, img);
    159      picture.insertBefore(source2, img);
    160      is(img.currentSrc, testPNG100, "Should still have testPNG100");
    161 
    162      // should get one load, selecting source2
    163      expectEvents(1, 0, function() {
    164        is(img.currentSrc, testPNG200, "Should have selected testPNG200");
    165 
    166        expectEvents(1, 0, function() {
    167          is(img.currentSrc, testPNG50, "Should have switched to testPNG50");
    168 
    169          // Now add a source *also* wanting that DPI *just before* the
    170          // selected source. Properly re-running the algorithm should
    171          // re-consider all sources and thus go back to the first
    172          // source, not just the valid source just inserted before us.
    173          source3.media = source1.media;
    174          source3.srcset = testPNG100;
    175          picture.insertBefore(source3, source2);
    176          // This should trigger a reload, but we should re-consider
    177          // source1 and remain with that, not just the newly added source2
    178          expectEvents(1, 0, function() {
    179            is(img.currentSrc, testPNG50, "Should have remained on testPNG50");
    180            expectEvents(0, 0, nextTest);
    181          });
    182        });
    183 
    184        // Switch DPI to match the first source.
    185        SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "2.0"] ] });
    186      });
    187    });
    188 
    189    // insert and remove valid source after our current source should
    190    // trigger a reload, but not switch source
    191    tests.push(function() {
    192      info("test 7");
    193      // Should be using source1 from last test
    194      is(img.currentSrc, testPNG50, "Should still have testPNG50");
    195 
    196      // Remove source2, should trigger an event even though we would
    197      // not switch
    198 
    199      picture.removeChild(source2);
    200      expectEvents(1, 0, function() {
    201        is(img.currentSrc, testPNG50, "Should still have testPNG50");
    202 
    203        // Same with re-adding
    204        picture.insertBefore(source2, img);
    205        expectEvents(1, 0, function() {
    206          is(img.currentSrc, testPNG50, "Should still have testPNG50");
    207          expectEvents(0, 0, nextTest);
    208        });
    209      });
    210    });
    211 
    212    // Changing source attributes should trigger updates
    213    tests.push(function() {
    214      info("test 8");
    215      // Should be using source1 from last test
    216      is(img.currentSrc, testPNG50, "Should still have testPNG50");
    217 
    218      // Reconfigure source1 to have empty srcset. Should switch to
    219      // next source due to becoming invalid.
    220      source1.srcset = "";
    221      is(img.currentSrc, testPNG50, "Should still have testPNG50");
    222 
    223      expectEvents(1, 0, function() {
    224        is(img.currentSrc, testPNG100, "Should have switched to testPNG100");
    225 
    226        // Give source1 valid sizes. Should trigger an event but not switch anywhere.
    227        source1.sizes = "100vw";
    228 
    229        expectEvents(1, 0, function() {
    230          is(img.currentSrc, testPNG100, "Should still have testPNG100");
    231 
    232          // And a valid MQ
    233          source1.media = "(min-resolution: 1dppx)";
    234          expectEvents(1, 0, function() {
    235            // And a valid type...
    236            source1.type = "image/png";
    237            expectEvents(1, 0, function() {
    238              // Finally restore srcset, should trigger load and re-consider source1 which is valid again
    239              source1.srcset = testPNG50;
    240              expectEvents(1, 0, function() {
    241                is(img.currentSrc, testPNG50, "Should have selected testPNG50");
    242                expectEvents(0, 0, nextTest);
    243              });
    244            });
    245          });
    246        });
    247      });
    248    });
    249 
    250    // Inserting a source after <img> and touching its attributes should all be no-ops
    251    tests.push(function() {
    252      info("test 9");
    253      // Setup: source2
    254      picture.removeChild(source2);
    255 
    256      expectEvents(1, 0, function() {
    257        is(img.currentSrc, testPNG50, "Should still have testPNG50");
    258 
    259        source2.srcset = testPNG200;
    260        source2.media = "";
    261        source2.sizes = "100vw";
    262        source2.type = "image/png";
    263        // Append valid source
    264        picture.appendChild(source2);
    265        // Touch all the things (but keep it valid)
    266        source2.srcset = testPNG100;
    267        source2.media = "(min-resolution: 2dppx)";
    268        source2.sizes = "50vw";
    269        source2.type = "image/png";
    270 
    271        // No event should fire. Source should not change.
    272        expectEvents(0, 0, function() {
    273          is(img.currentSrc, testPNG50, "Should still have testPNG50");
    274          expectEvents(0, 0, nextTest);
    275        });
    276      });
    277    });
    278 
    279    function nextTest() {
    280      if (tests.length) {
    281        // Spin event loop to make sure no unexpected image events are
    282        // pending (unexpected events will assert in the handlers)
    283        setTimeout(function() {
    284          (tests.shift())();
    285        }, 0);
    286      } else {
    287        // We'll get a flood of load events due to prefs being popped while cleaning up.
    288        // Ignore it all.
    289        img.removeEventListener("load", onImgLoad);
    290        img.removeEventListener("error", onImgError);
    291        SimpleTest.finish();
    292      }
    293    }
    294 
    295    addEventListener("load", function() {
    296      SpecialPowers.pushPrefEnv({'set': [["layout.css.devPixelsPerPx", "1.0" ]] },
    297                                function() {
    298        // Create these after the pref is set, as it is guarding webIDL attributes
    299        img = document.createElement("img");
    300        img.addEventListener("load", onImgLoad);
    301        img.addEventListener("error", onImgError);
    302        picture = document.createElement("picture");
    303        source1 = document.createElement("source");
    304        source2 = document.createElement("source");
    305        source3 = document.createElement("source");
    306        setTimeout(nextTest, 0);
    307      });
    308    });
    309  </script>
    310 </body>
    311 </html>