test_img_mutations.html (8878B)
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 src and srcset 14 // and that img.src still behaves by the older spec. (Bug 1076583) 15 // https://html.spec.whatwg.org/#relevant-mutations 16 SimpleTest.waitForExplicitFinish(); 17 18 // 50x50 png 19 var testPNG50 = new URL("image_50.png?noCache=" + Math.random(), location).href; 20 // 100x100 png 21 var testPNG100 = new URL("image_100.png?noCache=" + Math.random(), location).href; 22 // 200x200 png 23 var testPNG200 = new URL("image_200.png?noCache=" + Math.random(), location).href; 24 25 var tests = []; 26 var img; 27 var expectingErrors = 0; 28 var expectingLoads = 0; 29 var afterExpectCallback; 30 31 function onImgLoad() { 32 ok(expectingLoads > 0, "expected load"); 33 if (expectingLoads > 0) { 34 expectingLoads--; 35 } 36 if (!expectingLoads && !expectingErrors && afterExpectCallback) { 37 setTimeout(afterExpectCallback, 0); 38 afterExpectCallback = null; 39 } 40 } 41 function onImgError() { 42 ok(expectingErrors > 0, "expected error"); 43 if (expectingErrors > 0) { 44 expectingErrors--; 45 } 46 if (!expectingLoads && !expectingErrors && afterExpectCallback) { 47 setTimeout(afterExpectCallback, 0); 48 afterExpectCallback = null; 49 } 50 } 51 function expectEvents(loads, errors, callback) { 52 let p = new Promise(resolve => { 53 if (!loads && !errors) { 54 setTimeout(resolve, 0); 55 } else { 56 expectingLoads += loads; 57 expectingErrors += errors; 58 info("Waiting for " + expectingLoads + " load and " + expectingErrors + " error events"); 59 afterExpectCallback = resolve; 60 } 61 }); 62 return p.then(() => callback && callback()); 63 } 64 65 // 66 // Test that img.src still does some work synchronously per the older spec (bug 1076583) 67 // 68 tests.push(async function test1() { 69 info("test 1"); 70 img.src = testPNG50; 71 is(img.currentSrc, "", "Should not have synchronously selected source"); 72 73 await expectEvents(1, 0); 74 is(img.currentSrc, testPNG50, "Should now have testPNG50 as current request"); 75 76 // Assigning a wrong URL should not trigger error event (bug 1321300). 77 img.src = '//:0'; // Wrong URL 78 is(img.currentSrc, "", "Should have dropped current request sync"); 79 80 img.src = "non_existent_image.404"; 81 is(img.currentSrc, "", "Should still have empty current request"); 82 83 await expectEvents(0, 1); 84 ok(img.currentSrc.endsWith("non_existent_image.404"), "Should have asynchronously selected source"); 85 86 img.removeAttribute("src"); 87 is(img.currentSrc, "", "Should have dropped currentSrc sync"); 88 89 // Load another image while previous load is still pending 90 img.src = testPNG200; 91 is(img.currentSrc, "", "Should asynchronously load selected source"); 92 93 await expectEvents(1, 0); 94 is(img.currentSrc, testPNG200, "Should have asynchronously loaded selected source"); 95 nextTest(); 96 }); 97 98 99 // Setting srcset should be async 100 tests.push(function () { 101 info("test 2"); 102 img.srcset = testPNG100; 103 is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request"); 104 105 expectEvents(1, 0, function() { 106 is(img.currentSrc, testPNG100, "Should now have testPNG100 as current request"); 107 nextTest(); 108 }); 109 }); 110 111 // Setting srcset, even to no ultimate effect, should trigger a reload 112 tests.push(function () { 113 info("test 3"); 114 img.srcset = testPNG100 + " 1x, " + testPNG200 + " 2x"; 115 is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); 116 117 expectEvents(1, 0, function() { 118 is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); 119 nextTest(); 120 }); 121 }); 122 123 // Should switch to src as 1x source 124 tests.push(function () { 125 info("test 4"); 126 img.srcset = testPNG50 + " 2x"; 127 is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); 128 129 expectEvents(1, 0, function() { 130 is(img.currentSrc, testPNG200, "Should now have testPNG200 as current request"); 131 nextTest(); 132 }); 133 }); 134 135 // Changing src while we have responsive attributes should not be sync 136 tests.push(function () { 137 info("test 5"); 138 img.src = testPNG100; 139 is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request"); 140 141 expectEvents(1, 0, function() { 142 is(img.currentSrc, testPNG100, "Should now have testPNG100 as current request"); 143 144 // Switch to using srcset again for next test 145 img.srcset = testPNG100; 146 expectEvents(1, 0, nextTest); 147 }); 148 }); 149 150 // img.src = img.src should trigger an async event even in responsive mode 151 tests.push(function () { 152 info("test 6"); 153 is(img.currentSrc, testPNG100, "Should now have testPNG100 as current request"); 154 // eslint-disable-next-line no-self-assign 155 img.src = img.src; 156 is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); 157 158 expectEvents(1, 0, nextTest); 159 }); 160 161 // img.srcset = img.srcset should be a no-op 162 tests.push(function () { 163 info("test 7"); 164 // eslint-disable-next-line no-self-assign 165 img.srcset = img.srcset; 166 is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); 167 168 expectEvents(0, 0, nextTest); 169 }); 170 171 // re-binding the image to the document should be a no-op 172 tests.push(function () { 173 info("test 8"); 174 document.body.appendChild(img); 175 is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); 176 177 expectEvents(0, 0, nextTest); 178 }); 179 180 // We should re-run our selection algorithm when any load event occurs 181 tests.push(function () { 182 info("test 9"); 183 img.srcset = testPNG50 + " 1x, " + testPNG200 + " 2x"; 184 is(img.currentSrc, testPNG100, "Should still have testPNG100 as current request"); 185 186 expectEvents(1, 0, function() { 187 is(img.currentSrc, testPNG50, "Should now have testPNG50 as current request"); 188 189 // The preference change will trigger a load, as the image will change 190 SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "2.0"] ] }); 191 expectEvents(1, 0, function() { 192 is(img.currentSrc, testPNG200, "Should now have testPNG200 as current request"); 193 // eslint-disable-next-line no-self-assign 194 img.src = img.src; 195 is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request"); 196 // img.src = img.src is special-cased by the spec. It should always 197 // trigger an load event 198 expectEvents(1, 0, function() { 199 is(img.currentSrc, testPNG200, "Should still have testPNG200 as current request"); 200 expectEvents(0, 0, nextTest); 201 }); 202 }) 203 }); 204 }); 205 206 // Removing srcset attr should async switch back to src 207 tests.push(async function () { 208 info("test 10"); 209 is(img.currentSrc, testPNG200, "Should have testPNG200 as current request"); 210 211 img.removeAttribute("srcset"); 212 is(img.currentSrc, testPNG100, "Should testPNG100 as current request (hits sync load case for src)"); 213 214 await expectEvents(1, 0); 215 is(img.currentSrc, testPNG100, "Should now have testPNG100 as current request"); 216 217 expectEvents(0, 0, nextTest); 218 }); 219 220 function nextTest() { 221 if (tests.length) { 222 // Spin event loop to make sure no unexpected image events are 223 // pending (unexpected events will assert in the handlers) 224 setTimeout(async function() { 225 await (tests.shift())(); 226 }, 0); 227 } else { 228 // Remove the event listeners to prevent the prefenv being popped from 229 // causing test failures. 230 img.removeEventListener("load", onImgLoad); 231 img.removeEventListener("error", onImgError); 232 SimpleTest.finish(); 233 } 234 } 235 236 addEventListener("load", function() { 237 SpecialPowers.pushPrefEnv({'set': [["layout.css.devPixelsPerPx", "1.0"]] }, 238 function() { 239 // Create this after the pref is set, as it is guarding webIDL attributes 240 img = document.createElement("img"); 241 img.addEventListener("load", onImgLoad); 242 img.addEventListener("error", onImgError); 243 document.body.appendChild(img); 244 setTimeout(nextTest, 0); 245 }); 246 }); 247 </script> 248 </body> 249 </html>