test_bug507902.html (12993B)
1 <!DOCTYPE HTML> 2 <html> 3 <!-- 4 https://bugzilla.mozilla.org/show_bug.cgi?id=507902 5 --> 6 <head> 7 <title>Test for Bug 507902</title> 8 <script src="/tests/SimpleTest/SimpleTest.js"></script> 9 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> 10 </head> 11 <body> 12 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=507902">Mozilla Bug 507902</a> 13 14 <iframe id="testFrameElem"></iframe> 15 16 <pre id="test"> 17 <script class="testbody" type="text/javascript"> 18 19 // 20 // Mochitest to test nsImageFrame icons 21 // 22 // The 'loading' icon should be displayed up until we have enough image 23 // data to determine the frame size. 24 // 25 // The 'broken' icon should be displayed when the URL is invalid (either 26 // a bad server or a file that fails to be sniffed to an appropriate 27 // mimetype). 28 // 29 30 // Boilerplate 31 gWindowUtils = SpecialPowers.getDOMWindowUtils(window); 32 33 // URL + paths 34 // 35 // We have a separate copy of the icons in the test directory to 36 // avoid any firefox caching mechanisms that might affect the 37 // behavior of the load. 38 var us = window.location.href; 39 var baseURL = us.substring(0, us.lastIndexOf('/') + 1); 40 var loadIconFilename = "file_LoadingImageReference.png"; 41 var imageFilename = "file_Dolske.png"; 42 var brokenIconFilename = "file_BrokenImageReference.png"; 43 var serverFilename = "file_IconTestServer.sjs"; 44 var serverContinueFlag = "?continue=true"; 45 var bogusFilename = "oneuponatimewhendolskewasyoung.png"; 46 47 // Our test image element, inside a div, inside an iframe 48 var testFrameElem = document.getElementById("testFrameElem"); 49 var innerDoc = testFrameElem.contentWindow.document; 50 var divContainer = innerDoc.createElement("div"); 51 divContainer.style.cssFloat = "left"; 52 innerDoc.body.appendChild(divContainer); 53 var testImageElem = new Image(); 54 divContainer.appendChild(testImageElem); 55 var pingImage = new Image(); 56 57 // Set up the canvases 58 var canvases = {}; 59 var canvasNames = [ "brokenIconTest", "brokenIconReference", 60 "loadingIconTest", "loadingIconReference", 61 "loadedTest", "loadedReference" ]; 62 var windowElem = document.documentElement; 63 for (let i in canvasNames) { 64 var can = document.createElement("canvas"); 65 can.setAttribute("width", windowElem.getAttribute("width")); 66 can.setAttribute("height", windowElem.getAttribute("height")); 67 canvases[canvasNames[i]] = can; 68 69 // When the image frame has no idea how to size itself, it sizes itself 70 // to dimensions capable of displaying the alt feedback icons. However, if 71 // the image has been loaded before, something (I don't know what) seems to 72 // remember the size of the last successful image for that URL. So when we 73 // create a new image frame for that URL, it uses that size until it hears 74 // something different. This happens through a refresh (not sure if this is 75 // desired behavior). This means that if you refresh the test, the "loading" 76 // icon for the test image will appear with a border that stretches further 77 // right and down, because that URL previously displayed an image with larger 78 // dimensions. This causes the verify stage to fail. To allow for 79 // successful test refreshes (only useful for people, not automated tests), 80 // we add a clipping region so that we see the left and top borders, along 81 // with the image, but not the bottom and right borders. 82 83 if ((i > 1) && (i < 4)) { 84 let ctx = can.getContext("2d"); 85 ctx.beginPath(); 86 ctx.rect(0,0, 30, 30); 87 ctx.clip(); 88 } 89 90 } 91 92 // Stage 1 - Load the reference image for the broken icon 93 function loadBrokenIconReference() { 94 95 // Debugging - Let's see if setting onload after src is a problem 96 testImageElem.onload = function(event) { dump("test_bug507902.html DEBUG - uh oh, placeholder onload 1 called\n");}; 97 98 // Debug - Figure out if we're getting an onerror instead of onload 99 testImageElem.onerror = function(event) {dump("test_bug507902.html DEBUG - Got onerror for testImageElem!\n");}; 100 101 testImageElem.src = baseURL + brokenIconFilename; 102 stageTransition(); 103 } 104 105 // Stage 2 - Draw the reference image for the broken icon to a canvas 106 function drawBrokenIconReference() { 107 108 enableBorderAndPad(); 109 drawWindowToCanvas("brokenIconReference"); 110 disableBorderAndPad(); 111 112 stageTransition(); 113 } 114 115 // Stage 3 - Load the reference image for the loading icon 116 function loadLoadingIconReference() { 117 118 // Debugging - Let's see if setting onload after src is a problem 119 testImageElem.onload = function(event) { dump("test_bug507902.html DEBUG - uh oh, placeholder onload 3 called\n");}; 120 121 testImageElem.src = baseURL + loadIconFilename; 122 stageTransition(); 123 } 124 125 // Stage 4 - Draw the reference image for the loading icon to a canvas 126 function drawLoadingIconReference() { 127 128 enableBorderAndPad(); 129 drawWindowToCanvas("loadingIconReference"); 130 disableBorderAndPad(); 131 132 stageTransition(); 133 } 134 135 // Stage 5 - Try to load a broken image 136 function loadBrokenImage() { 137 resetImage(); 138 testImageElem.src = baseURL + bogusFilename; 139 stageTransition(); 140 } 141 142 // Stage 6 - Draw the screen to a canvas. This should hopefully 143 // be the broken icon. 144 function drawBrokenIcon() { 145 drawWindowToCanvas("brokenIconTest"); 146 stageTransition(); 147 } 148 149 // Stage 7 - Load the reference image for the test image 150 function loadImageReference() { 151 resetImage(); 152 153 // Debugging - Let's see if setting onload after src is a problem 154 testImageElem.onload = function(event) { dump("test_bug507902.html DEBUG - uh oh, placeholder onload 7 called\n");}; 155 156 testImageElem.src = baseURL + imageFilename; 157 stageTransition(); 158 } 159 160 // Stage 8 - Draw the reference image for the test image to a canvas 161 function drawImageReference() { 162 drawWindowToCanvas("loadedReference"); 163 stageTransition(); 164 } 165 166 // Stage 9 - Start a load of the test image from the delay-generating server 167 function startServerLoad() { 168 169 // Reset the image 170 resetImage(); 171 172 // Debugging info so we can figure out the hang 173 dump("test_bug507902.html DEBUG - starting server load\n"); 174 175 // Load the image 176 testImageElem.src = baseURL + serverFilename; 177 stageTransition(); 178 } 179 180 // Stage 10 - Draw the screen to a canvas. This should hopefully be the loading 181 // icon. 182 function drawLoadingIcon() { 183 184 // Debugging info so we can figure out the hang 185 dump("test_bug507902.html DEBUG - drawing loading icon\n"); 186 187 drawWindowToCanvas("loadingIconTest"); 188 stageTransition(); 189 } 190 191 // Stage 11 - Tell the server to continue. 192 function signalServerContinue() { 193 194 // Debugging info so we can figure out the hang 195 dump("test_bug507902.html DEBUG - signaling server to continue\n"); 196 197 pingImage.src = baseURL + serverFilename + serverContinueFlag; 198 stageTransition(); 199 } 200 201 // Stage 12 - Draw the screen to a canvas. This should hopefully be the loaded 202 // test image. 203 function drawLoadedImage() { 204 drawWindowToCanvas("loadedTest"); 205 stageTransition(); 206 } 207 208 209 // Stage 13 - Verify That the appropriate canvases match 210 function verifyCanvases() { 211 212 // Verify the broken icon 213 ok(canvasesAreEqual("brokenIconTest", "brokenIconReference"), 214 "Window drawn on broken load should match broken icon reference"); 215 216 // Verify the loading icon 217 ok(canvasesAreEqual("loadingIconTest", "loadingIconReference"), 218 "Window drawn mid-load should match loading icon reference"); 219 220 // Verify the loaded image 221 ok(canvasesAreEqual("loadedTest", "loadedReference"), 222 "Window drawn post-load should match reference image"); 223 224 stageTransition(); 225 } 226 227 // We have a bunch of different things that need to happen in order 228 // with different transitions. We make a "stage table" here where 229 // each entry contains the stage function ('fn') and a transition 230 // ('trans'), which can be one of the following: 231 // "instant" - Just calls the next stage directly 232 // "onload" - Sets the next stage as an onload event for the image element 233 // "onerror" - Sets the next stage as an onerror event for the image element 234 // integer - Sets the next stage to be called after the given timeout duration 235 // "finish" - Finish the test 236 var testStages = [ 237 { "fn" : loadBrokenIconReference, "trans" : "onload"}, 238 { "fn" : drawBrokenIconReference, "trans" : "instant"}, 239 { "fn" : loadLoadingIconReference, "trans" : "onload" }, 240 { "fn" : drawLoadingIconReference, "trans" : "instant" }, 241 { "fn" : loadBrokenImage, "trans" : "onerror" }, 242 { "fn" : drawBrokenIcon, "trans" : "instant" }, 243 { "fn" : loadImageReference, "trans" : "onload" }, 244 { "fn" : drawImageReference, "trans" : "instant" }, 245 // XXXbholley - We use a timeout here because resetting the 246 // image doesn't seem to be quite synchronous. If we make 247 // this transition "instant", then the drawImage call draws 248 // an empty (0,0,0,0) rect to the canvas and we're left with 249 // whatever was there before. I don't know of any good event 250 // mechanism to figure out when the image frame is bootstrapped 251 // enough to display the loading image, so I did trial-and-error 252 // with timeouts. 50ms seems to be enough time for things to work 253 // reliably, so *= 6 for good measure. 254 { "fn" : startServerLoad, "trans" : 300 }, 255 { "fn" : drawLoadingIcon, "trans" : "instant" }, 256 { "fn" : signalServerContinue, "trans" : "onload" }, 257 { "fn" : drawLoadedImage, "trans" : "instant" }, 258 { "fn" : verifyCanvases, "trans" : "finish" } ]; 259 var currentStage = 0; 260 261 // Transition function called at the end of each stage 262 function stageTransition() { 263 264 // Debugging info so we can figure out the hang 265 dump("test_bug507902.html DEBUG - Current Stage: " + currentStage + "\n"); 266 267 // What's our transition? 268 var trans = testStages[currentStage++].trans; 269 270 // If the transition is finish, stop now before we access out of bounds 271 if (trans == "finish") { 272 makeCanvasesVisible(); // Useful for debugging 273 SimpleTest.finish(); 274 return; 275 } 276 277 // Otherwise, get the next function 278 var nextfn = testStages[currentStage].fn; 279 280 // Switch based on transition 281 switch (trans) { 282 283 // Continue right away 284 case "instant": 285 nextfn(); 286 break; 287 288 // Continue after we get an onload event on the test image 289 case "onload": 290 testImageElem.onload = function(event) {testImageElem.onload = undefined; nextfn();}; 291 break; 292 293 // Continue after we get an onerror event on the test image 294 case "onerror": 295 testImageElem.onerror = function(event) {testImageElem.onerror = undefined; nextfn();}; 296 break; 297 298 // Timeout 299 default: 300 setTimeout(nextfn, trans); 301 break 302 } 303 } 304 305 // Lots if asynchronous behavior here 306 SimpleTest.waitForExplicitFinish(); 307 308 // Catch somebody's eye 309 dump("This test is failing intermittently, see bug 510001 - If you see orange here, please paste the following debugging output on the bug!\n"); 310 311 // Kick off the test by invoking the first stage. The stages call each other 312 testStages[0].fn(); 313 314 315 // We need to get rid of the old image element and make a new one. If we 316 // don't, the "current/pending" machinery will display the old image until 317 // the new one is loaded, so we won't see the loading icon. 318 function resetImage() { 319 divContainer.removeChild(testImageElem); 320 testImageElem = null; 321 testImageElem = new Image(); 322 divContainer.appendChild(testImageElem); 323 } 324 325 // 326 // Makes the canvases visible. Called before the tests finish. This is useful for 327 // debugging. 328 // 329 function makeCanvasesVisible() { 330 for (let i = 0; i < canvasNames.length - 1; i += 2) { 331 var title = document.createElement("h3"); 332 title.innerHTML = canvasNames[i] + ", " + canvasNames[i+1] + ":"; 333 document.body.appendChild(title); 334 var myDiv = document.createElement("div"); 335 myDiv.appendChild(canvases[canvasNames[i]]); 336 myDiv.appendChild(canvases[canvasNames[i+1]]); 337 document.body.appendChild(myDiv); 338 } 339 } 340 341 // 342 // Enables and disables bordering/padding to mimic the look of alt feedback icons 343 // 344 function enableBorderAndPad() { 345 divContainer.style.border = "1px"; 346 divContainer.style.borderStyle = "inset"; 347 testImageElem.style.padding = "3px"; 348 } 349 350 function disableBorderAndPad() { 351 testImageElem.style.padding = 0; 352 divContainer.style.border = "0px"; 353 divContainer.style.borderStyle = ""; 354 } 355 356 // 357 // Helper canvas methods. This is mostly copped directly from the reftest framework 358 // 359 360 function drawWindowToCanvas(canvasName) { 361 var win = testFrameElem.contentWindow; 362 let ctx = canvases[canvasName].getContext("2d"); 363 // drawWindow always draws one canvas pixel for each CSS pixel in the source 364 // window, so scale the drawing to show the zoom (making each canvas pixel be one 365 // device pixel instead) 366 ctx.drawWindow(win, win.scrollX, win.scrollY, 367 Math.ceil(canvases[canvasName].width), 368 Math.ceil(canvases[canvasName].height), 369 "rgb(255,255,255)"); 370 } 371 372 function canvasesAreEqual(canvas1Name, canvas2Name) { 373 var c1 = canvases[canvas1Name]; 374 var c2 = canvases[canvas2Name]; 375 var differences = gWindowUtils.compareCanvases(c1, c2, {}); 376 return (differences == 0); 377 } 378 379 </script> 380 </pre> 381 </body> 382 </html>