<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>Image srcset mutations</title> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> </head> <body> <script type="application/javascript"> "use strict"; // Tests the relevant mutations part of the spec for img src and srcset // and that img.src still behaves by the older spec. (Bug 1076583) // https://html.spec.whatwg.org/#relevant-mutations SimpleTest.waitForExplicitFinish(); // 50x50 png var testPNG50 = new URL("image_50.png", location).href; // 100x100 png var testPNG100 = new URL("image_100.png", location).href; // 200x200 png var testPNG200 = new URL("image_200.png", location).href; var tests = []; var img; var expectingErrors = 0; var expectingLoads = 0; var afterExpectCallback; function onImgLoad() { ok(expectingLoads > 0, "expected load"); if (expectingLoads > 0) { expectingLoads--; } if (!expectingLoads && !expectingErrors && afterExpectCallback) { setTimeout(afterExpectCallback, 0); afterExpectCallback = null; } } function onImgError() { ok(expectingErrors > 0, "expected error"); if (expectingErrors > 0) { expectingErrors--; } if (!expectingLoads && !expectingErrors && afterExpectCallback) { setTimeout(afterExpectCallback, 0); afterExpectCallback = null; } } function expectEvents(loads, errors, callback) { if (!loads && !errors) { setTimeout(callback, 0); } else { expectingLoads += loads; expectingErrors += errors; info("Waiting for " + expectingLoads + " load and " + expectingErrors + " error events"); afterExpectCallback = callback; } } // // Test that img.src still does some work synchronously per the older spec (bug 1076583) // tests.push(function test1() { info("test 1"); img.src = testPNG50; is(img.currentSrc, testPNG50, "Should have synchronously selected source"); // Assigning a wrong URL should not trigger error event (bug 1321300). img.src = '//:0'; // Wrong URL img.src = "non_existent_image.404"; ok(img.currentSrc.endsWith("non_existent_image.404"), "Should have synchronously selected source"); img.removeAttribute("src"); is(img.currentSrc, '', "Should have dropped currentSrc"); // Load another image while previous load is still pending img.src = testPNG200; is(img.currentSrc, testPNG200, "Should have synchronously selected source"); // No events should have fired synchronously, now we should get just one load (and no 404 error) expectEvents(1, 0, nextTest); }); // Setting srcset should be async tests.push(function () { info("test 2"); img.srcset = testPNG100; is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request"); expectEvents(1, 0, function() { is(img.currentSrc, testPNG100, "Should now have testPNG100 as current request"); nextTest(); }); }); // Setting srcset, even to no ultimate effect, should trigger a reload tests.push(function () { info("test 3"); img.srcset = testPNG100 + " 1x, " + testPNG200 + " 2x"; is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); expectEvents(1, 0, function() { is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); nextTest(); }); }); // Should switch to src as 1x source tests.push(function () { info("test 4"); img.srcset = testPNG50 + " 2x"; is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); expectEvents(1, 0, function() { is(img.currentSrc, testPNG200, "Should now have testPNG200 as current request"); nextTest(); }); }); // Changing src while we have responsive attributes should not be sync tests.push(function () { info("test 5"); img.src = testPNG100; is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request"); expectEvents(1, 0, function() { is(img.currentSrc, testPNG100, "Should now have testPNG100 as current request"); // Switch to using srcset again for next test img.srcset = testPNG100; expectEvents(1, 0, nextTest); }); }); // img.src = img.src should trigger an async event even in responsive mode tests.push(function () { info("test 6"); is(img.currentSrc, testPNG100, "Should now have testPNG100 as current request"); img.src = img.src; is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); expectEvents(1, 0, nextTest); }); // img.srcset = img.srcset should be a no-op tests.push(function () { info("test 7"); img.srcset = img.srcset; is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); expectEvents(0, 0, nextTest); }); // re-binding the image to the document should be a no-op tests.push(function () { info("test 8"); document.body.appendChild(img); is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); expectEvents(0, 0, nextTest); }); // We should re-run our selection algorithm when any load event occurs tests.push(function () { info("test 9"); img.srcset = testPNG50 + " 1x, " + testPNG200 + " 2x"; is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); expectEvents(1, 0, function() { is(img.currentSrc, testPNG50, "Should now have testPNG50 as current request"); // The preference change will trigger a load, as the image will change SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "2.0"] ] }); expectEvents(1, 0, function() { is(img.currentSrc, testPNG200, "Should now have testPNG200 as current request"); img.src = img.src; is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request"); // img.src = img.src is special-cased by the spec. It should always // trigger an load event expectEvents(1, 0, function() { is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request"); expectEvents(0, 0, nextTest); }); }) }); }); // Removing srcset attr should async switch back to src tests.push(function () { info("test 10"); is(img.currentSrc, testPNG200, "Should have testPNG200 as current request"); img.removeAttribute("srcset"); is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request"); expectEvents(1, 0, function() { is(img.currentSrc, testPNG100, "Should now have testPNG100 as current request"); expectEvents(0, 0, nextTest); }); }); function nextTest() { if (tests.length) { // Spin event loop to make sure no unexpected image events are // pending (unexpected events will assert in the handlers) setTimeout(function() { (tests.shift())(); }, 0); } else { // Remove the event listeners to prevent the prefenv being popped from // causing test failures. img.removeEventListener("load", onImgLoad); img.removeEventListener("error", onImgError); SimpleTest.finish(); } } addEventListener("load", function() { SpecialPowers.pushPrefEnv({'set': [["layout.css.devPixelsPerPx", "1.0"]] }, function() { // Create this after the pref is set, as it is guarding webIDL attributes img = document.createElement("img"); img.addEventListener("load", onImgLoad); img.addEventListener("error", onImgError); document.body.appendChild(img); setTimeout(nextTest, 0); }); }); </script> </body> </html>