summaryrefslogtreecommitdiffstats
path: root/dom/tests/mochitest/general/test_picture_mutations.html
diff options
context:
space:
mode:
Diffstat (limited to 'dom/tests/mochitest/general/test_picture_mutations.html')
-rw-r--r--dom/tests/mochitest/general/test_picture_mutations.html311
1 files changed, 311 insertions, 0 deletions
diff --git a/dom/tests/mochitest/general/test_picture_mutations.html b/dom/tests/mochitest/general/test_picture_mutations.html
new file mode 100644
index 000000000..491504aba
--- /dev/null
+++ b/dom/tests/mochitest/general/test_picture_mutations.html
@@ -0,0 +1,311 @@
+<!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> inside <picture> tags
+ // 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 picture;
+ var source1;
+ var source2;
+ var source3;
+ var expectingErrors = 0;
+ var expectingLoads = 0;
+ var afterExpectCallback;
+
+ function onImgLoad() {
+ ok(expectingLoads > 0, "expected load");
+ if (expectingLoads > 0) {
+ expectingLoads--;
+ }
+ if (!expectingLoads && !expectingErrors) {
+ setTimeout(afterExpectCallback, 0);
+ }
+ }
+ function onImgError() {
+ ok(expectingErrors > 0, "expected error");
+ if (expectingErrors > 0) {
+ expectingErrors--;
+ }
+ if (!expectingLoads && !expectingErrors) {
+ setTimeout(afterExpectCallback, 0);
+ }
+ }
+ 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;
+ }
+ }
+
+ // Setup image outside the tree dom, make sure it loads
+ tests.push(function() {
+ info("test 1");
+ img.srcset = testPNG100;
+ img.src = testPNG50;
+ is(img.currentSrc, '', "Should not 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);
+ });
+
+ // Binding to an empty picture should trigger an event, even if source doesn't change
+ tests.push(function() {
+ info("test 2");
+ is(img.currentSrc, testPNG100, "Should have loaded testPNG100");
+ document.body.appendChild(picture);
+ picture.appendChild(img);
+ is(img.currentSrc, testPNG100, "Should still have testPNG100");
+
+ expectEvents(1, 0, nextTest);
+ });
+
+ // inserting and removing an empty source before the image should both trigger a no-op reload
+ tests.push(function() {
+ info("test 3");
+ is(img.currentSrc, testPNG100, "Should still have testPNG100");
+ picture.insertBefore(source1, img);
+ is(img.currentSrc, testPNG100, "Should still have testPNG100");
+
+ // should fire one event, not change source
+ expectEvents(1, 0, function() {
+ is(img.currentSrc, testPNG100, "Should still have testPNG100");
+ picture.removeChild(source1);
+ is(img.currentSrc, testPNG100, "Should still have testPNG100");
+
+ // Should also no-op fire
+ expectEvents(1, 0, nextTest);
+ });
+ });
+
+ // insert and remove valid source before
+ tests.push(function() {
+ info("test 4");
+ is(img.currentSrc, testPNG100, "Should still have testPNG100");
+
+ // Insert source1 before img with valid candidate
+ source1.srcset = testPNG50;
+ picture.insertBefore(source1, img);
+ is(img.currentSrc, testPNG100, "Should still have testPNG100");
+
+ // should fire one event, change to the source
+ expectEvents(1, 0, function() {
+ is(img.currentSrc, testPNG50, "Should have switched to testPNG50");
+ picture.removeChild(source1);
+ is(img.currentSrc, testPNG50, "Should still have testPNG50");
+
+ // Should also no-op fire
+ expectEvents(1, 0, function() {
+ is(img.currentSrc, testPNG100, "Should have returned to testPNG100");
+ nextTest();
+ });
+ });
+ });
+
+ // insert and remove valid source after
+ tests.push(function() {
+ info("test 5");
+ is(img.currentSrc, testPNG100, "Should still have testPNG100");
+
+ // Insert source1 before img with valid candidate
+ source1.srcset = testPNG50;
+ picture.appendChild(source1);
+ is(img.currentSrc, testPNG100, "Should still have testPNG100");
+
+ // should fire nothing, no action
+ expectEvents(0, 0, function() {
+ is(img.currentSrc, testPNG100, "Should still have testPNG100");
+
+ // Same with removing
+ picture.removeChild(source1);
+ expectEvents(0, 0, function() {
+ is(img.currentSrc, testPNG100, "Should still have testPNG100");
+ nextTest();
+ });
+ });
+ });
+
+ // Should re-consider earlier sources when a load event occurs.
+ tests.push(function() {
+ info("test 6");
+
+ // Insert two valid sources, with MQ causing us to select the second
+ source1.srcset = testPNG50 + " 1x";
+ source1.media = "(min-resolution: 2dppx)"; // Wont match, test starts at 1x
+ source2.srcset = testPNG200;
+ picture.insertBefore(source1, img);
+ picture.insertBefore(source2, img);
+ is(img.currentSrc, testPNG100, "Should still have testPNG100");
+
+ // should get one load, selecting source2
+ expectEvents(1, 0, function() {
+ is(img.currentSrc, testPNG200, "Should have selected testPNG200");
+
+ expectEvents(1, 0, function() {
+ is(img.currentSrc, testPNG50, "Should have switched to testPNG50");
+
+ // Now add a source *also* wanting that DPI *just before* the
+ // selected source. Properly re-running the algorithm should
+ // re-consider all sources and thus go back to the first
+ // source, not just the valid source just inserted before us.
+ source3.media = source1.media;
+ source3.srcset = testPNG100;
+ picture.insertBefore(source3, source2);
+ // This should trigger a reload, but we should re-consider
+ // source1 and remain with that, not just the newly added source2
+ expectEvents(1, 0, function() {
+ is(img.currentSrc, testPNG50, "Should have remained on testPNG50");
+ expectEvents(0, 0, nextTest);
+ });
+ });
+
+ // Switch DPI to match the first source.
+ SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "2.0"] ] });
+ });
+ });
+
+ // insert and remove valid source after our current source should
+ // trigger a reload, but not switch source
+ tests.push(function() {
+ info("test 7");
+ // Should be using source1 from last test
+ is(img.currentSrc, testPNG50, "Should still have testPNG50");
+
+ // Remove source2, should trigger an event even though we would
+ // not switch
+
+ picture.removeChild(source2);
+ expectEvents(1, 0, function() {
+ is(img.currentSrc, testPNG50, "Should still have testPNG50");
+
+ // Same with re-adding
+ picture.insertBefore(source2, img);
+ expectEvents(1, 0, function() {
+ is(img.currentSrc, testPNG50, "Should still have testPNG50");
+ expectEvents(0, 0, nextTest);
+ });
+ });
+ });
+
+ // Changing source attributes should trigger updates
+ tests.push(function() {
+ info("test 8");
+ // Should be using source1 from last test
+ is(img.currentSrc, testPNG50, "Should still have testPNG50");
+
+ // Reconfigure source1 to have empty srcset. Should switch to
+ // next source due to becoming invalid.
+ source1.srcset = "";
+ is(img.currentSrc, testPNG50, "Should still have testPNG50");
+
+ expectEvents(1, 0, function() {
+ is(img.currentSrc, testPNG100, "Should have switched to testPNG100");
+
+ // Give source1 valid sizes. Should trigger an event but not switch anywhere.
+ source1.sizes = "100vw";
+
+ expectEvents(1, 0, function() {
+ is(img.currentSrc, testPNG100, "Should still have testPNG100");
+
+ // And a valid MQ
+ source1.media = "(min-resolution: 1dppx)";
+ expectEvents(1, 0, function() {
+ // And a valid type...
+ source1.type = "image/png";
+ expectEvents(1, 0, function() {
+ // Finally restore srcset, should trigger load and re-consider source1 which is valid again
+ source1.srcset = testPNG50;
+ expectEvents(1, 0, function() {
+ is(img.currentSrc, testPNG50, "Should have selected testPNG50");
+ expectEvents(0, 0, nextTest);
+ });
+ });
+ });
+ });
+ });
+ });
+
+ // Inserting a source after <img> and touching its attributes should all be no-ops
+ tests.push(function() {
+ info("test 9");
+ // Setup: source2
+ picture.removeChild(source2);
+
+ expectEvents(1, 0, function() {
+ is(img.currentSrc, testPNG50, "Should still have testPNG50");
+
+ source2.srcset = testPNG200;
+ source2.media = "";
+ source2.sizes = "100vw";
+ source2.type = "image/png";
+ // Append valid source
+ picture.appendChild(source2);
+ // Touch all the things (but keep it valid)
+ source2.srcset = testPNG100;
+ source2.media = "(min-resolution: 2dppx)";
+ source2.sizes = "50vw";
+ source2.type = "image/png";
+
+ // No event should fire. Source should not change.
+ expectEvents(0, 0, function() {
+ is(img.currentSrc, testPNG50, "Should still have testPNG50");
+ 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 {
+ // We'll get a flood of load events due to prefs being popped while cleaning up.
+ // Ignore it all.
+ img.removeEventListener("load", onImgLoad);
+ img.removeEventListener("error", onImgError);
+ SimpleTest.finish();
+ }
+ }
+
+ addEventListener("load", function() {
+ SpecialPowers.pushPrefEnv({'set': [["layout.css.devPixelsPerPx", "1.0" ]] },
+ function() {
+ // Create these after the pref is set, as it is guarding webIDL attributes
+ img = document.createElement("img");
+ img.addEventListener("load", onImgLoad);
+ img.addEventListener("error", onImgError);
+ picture = document.createElement("picture");
+ source1 = document.createElement("source");
+ source2 = document.createElement("source");
+ source3 = document.createElement("source");
+ setTimeout(nextTest, 0);
+ });
+ });
+ </script>
+</body>
+</html>