1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/test/test_bug507902.html Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,382 @@ 1.4 +<!DOCTYPE HTML> 1.5 +<html> 1.6 +<!-- 1.7 +https://bugzilla.mozilla.org/show_bug.cgi?id=507902 1.8 +--> 1.9 +<head> 1.10 + <title>Test for Bug 507902</title> 1.11 + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> 1.12 + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> 1.13 +</head> 1.14 +<body> 1.15 +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=507902">Mozilla Bug 507902</a> 1.16 + 1.17 +<iframe id="testFrameElem"></iframe> 1.18 + 1.19 +<pre id="test"> 1.20 +<script class="testbody" type="text/javascript"> 1.21 + 1.22 +// 1.23 +// Mochitest to test nsImageFrame icons 1.24 +// 1.25 +// The 'loading' icon should be displayed up until we have enough image 1.26 +// data to determine the frame size. 1.27 +// 1.28 +// The 'broken' icon should be displayed when the URL is invalid (either 1.29 +// a bad server or a file that fails to be sniffed to an appropriate 1.30 +// mimetype). 1.31 +// 1.32 + 1.33 +// Boilerplate 1.34 +gWindowUtils = SpecialPowers.getDOMWindowUtils(window); 1.35 + 1.36 +// URL + paths 1.37 +// 1.38 +// We have a separate copy of the icons in the test directory to 1.39 +// avoid any firefox caching mechanisms that might affect the 1.40 +// behavior of the load. 1.41 +var us = window.location.href; 1.42 +var baseURL = us.substring(0, us.lastIndexOf('/') + 1); 1.43 +var loadIconFilename = "file_LoadingImageReference.png"; 1.44 +var imageFilename = "file_Dolske.png"; 1.45 +var brokenIconFilename = "file_BrokenImageReference.png"; 1.46 +var serverFilename = "file_IconTestServer.sjs"; 1.47 +var serverContinueFlag = "?continue=true"; 1.48 +var bogusFilename = "oneuponatimewhendolskewasyoung.png"; 1.49 + 1.50 +// Our test image element, inside a div, inside an iframe 1.51 +var testFrameElem = document.getElementById("testFrameElem"); 1.52 +var innerDoc = testFrameElem.contentWindow.document; 1.53 +var divContainer = innerDoc.createElement("div"); 1.54 +divContainer.style.cssFloat = "left"; 1.55 +innerDoc.body.appendChild(divContainer); 1.56 +var testImageElem = new Image(); 1.57 +divContainer.appendChild(testImageElem); 1.58 +var pingImage = new Image(); 1.59 + 1.60 +// Set up the canvases 1.61 +var canvases = {}; 1.62 +var canvasNames = [ "brokenIconTest", "brokenIconReference", 1.63 + "loadingIconTest", "loadingIconReference", 1.64 + "loadedTest", "loadedReference" ]; 1.65 +var windowElem = document.documentElement; 1.66 +for (var i in canvasNames) { 1.67 + var can = document.createElement("canvas"); 1.68 + can.setAttribute("width", windowElem.getAttribute("width")); 1.69 + can.setAttribute("height", windowElem.getAttribute("height")); 1.70 + canvases[canvasNames[i]] = can; 1.71 + 1.72 + // When the image frame has no idea how to size itself, it sizes itself 1.73 + // to dimensions capable of displaying the alt feedback icons. However, if 1.74 + // the image has been loaded before, something (I don't know what) seems to 1.75 + // remember the size of the last successful image for that URL. So when we 1.76 + // create a new image frame for that URL, it uses that size until it hears 1.77 + // something different. This happens through a refresh (not sure if this is 1.78 + // desired behavior). This means that if you refresh the test, the "loading" 1.79 + // icon for the test image will appear with a border that stretches further 1.80 + // right and down, because that URL previously displayed an image with larger 1.81 + // dimensions. This causes the verify stage to fail. To allow for 1.82 + // successful test refreshes (only useful for people, not automated tests), 1.83 + // we add a clipping region so that we see the left and top borders, along 1.84 + // with the image, but not the bottom and right borders. 1.85 + 1.86 + if ((i > 1) && (i < 4)) { 1.87 + var ctx = can.getContext("2d"); 1.88 + ctx.beginPath(); 1.89 + ctx.rect(0,0, 30, 30); 1.90 + ctx.clip(); 1.91 + } 1.92 + 1.93 +} 1.94 + 1.95 +// Stage 1 - Load the reference image for the broken icon 1.96 +function loadBrokenIconReference() { 1.97 + 1.98 + // Debugging - Let's see if setting onload after src is a problem 1.99 + testImageElem.onload = function(event) { dump("test_bug507902.html DEBUG - uh oh, placeholder onload 1 called\n");}; 1.100 + 1.101 + // Debug - Figure out if we're getting an onerror instead of onload 1.102 + testImageElem.onerror = function(event) {dump("test_bug507902.html DEBUG - Got onerror for testImageElem!\n");}; 1.103 + 1.104 + testImageElem.src = baseURL + brokenIconFilename; 1.105 + stageTransition(); 1.106 +} 1.107 + 1.108 +// Stage 2 - Draw the reference image for the broken icon to a canvas 1.109 +function drawBrokenIconReference() { 1.110 + 1.111 + enableBorderAndPad(); 1.112 + drawWindowToCanvas("brokenIconReference"); 1.113 + disableBorderAndPad(); 1.114 + 1.115 + stageTransition(); 1.116 +} 1.117 + 1.118 +// Stage 3 - Load the reference image for the loading icon 1.119 +function loadLoadingIconReference() { 1.120 + 1.121 + // Debugging - Let's see if setting onload after src is a problem 1.122 + testImageElem.onload = function(event) { dump("test_bug507902.html DEBUG - uh oh, placeholder onload 3 called\n");}; 1.123 + 1.124 + testImageElem.src = baseURL + loadIconFilename; 1.125 + stageTransition(); 1.126 +} 1.127 + 1.128 +// Stage 4 - Draw the reference image for the loading icon to a canvas 1.129 +function drawLoadingIconReference() { 1.130 + 1.131 + enableBorderAndPad(); 1.132 + drawWindowToCanvas("loadingIconReference"); 1.133 + disableBorderAndPad(); 1.134 + 1.135 + stageTransition(); 1.136 +} 1.137 + 1.138 +// Stage 5 - Try to load a broken image 1.139 +function loadBrokenImage() { 1.140 + resetImage(); 1.141 + testImageElem.src = baseURL + bogusFilename; 1.142 + stageTransition(); 1.143 +} 1.144 + 1.145 +// Stage 6 - Draw the screen to a canvas. This should hopefully 1.146 +// be the broken icon. 1.147 +function drawBrokenIcon() { 1.148 + drawWindowToCanvas("brokenIconTest"); 1.149 + stageTransition(); 1.150 +} 1.151 + 1.152 +// Stage 7 - Load the reference image for the test image 1.153 +function loadImageReference() { 1.154 + resetImage(); 1.155 + 1.156 + // Debugging - Let's see if setting onload after src is a problem 1.157 + testImageElem.onload = function(event) { dump("test_bug507902.html DEBUG - uh oh, placeholder onload 7 called\n");}; 1.158 + 1.159 + testImageElem.src = baseURL + imageFilename; 1.160 + stageTransition(); 1.161 +} 1.162 + 1.163 +// Stage 8 - Draw the reference image for the test image to a canvas 1.164 +function drawImageReference() { 1.165 + drawWindowToCanvas("loadedReference"); 1.166 + stageTransition(); 1.167 +} 1.168 + 1.169 +// Stage 9 - Start a load of the test image from the delay-generating server 1.170 +function startServerLoad() { 1.171 + 1.172 + // Reset the image 1.173 + resetImage(); 1.174 + 1.175 + // Debugging info so we can figure out the hang 1.176 + dump("test_bug507902.html DEBUG - starting server load\n"); 1.177 + 1.178 + // Load the image 1.179 + testImageElem.src = baseURL + serverFilename; 1.180 + stageTransition(); 1.181 +} 1.182 + 1.183 +// Stage 10 - Draw the screen to a canvas. This should hopefully be the loading 1.184 +// icon. 1.185 +function drawLoadingIcon() { 1.186 + 1.187 + // Debugging info so we can figure out the hang 1.188 + dump("test_bug507902.html DEBUG - drawing loading icon\n"); 1.189 + 1.190 + drawWindowToCanvas("loadingIconTest"); 1.191 + stageTransition(); 1.192 +} 1.193 + 1.194 +// Stage 11 - Tell the server to continue. 1.195 +function signalServerContinue() { 1.196 + 1.197 + // Debugging info so we can figure out the hang 1.198 + dump("test_bug507902.html DEBUG - signaling server to continue\n"); 1.199 + 1.200 + pingImage.src = baseURL + serverFilename + serverContinueFlag; 1.201 + stageTransition(); 1.202 +} 1.203 + 1.204 +// Stage 12 - Draw the screen to a canvas. This should hopefully be the loaded 1.205 +// test image. 1.206 +function drawLoadedImage() { 1.207 + drawWindowToCanvas("loadedTest"); 1.208 + stageTransition(); 1.209 +} 1.210 + 1.211 + 1.212 +// Stage 13 - Verify That the appropriate canvases match 1.213 +function verifyCanvases() { 1.214 + 1.215 + // Verify the broken icon 1.216 + ok(canvasesAreEqual("brokenIconTest", "brokenIconReference"), 1.217 + "Window drawn on broken load should match broken icon reference"); 1.218 + 1.219 + // Verify the loading icon 1.220 + ok(canvasesAreEqual("loadingIconTest", "loadingIconReference"), 1.221 + "Window drawn mid-load should match loading icon reference"); 1.222 + 1.223 + // Verify the loaded image 1.224 + ok(canvasesAreEqual("loadedTest", "loadedReference"), 1.225 + "Window drawn post-load should match reference image"); 1.226 + 1.227 + stageTransition(); 1.228 +} 1.229 + 1.230 +// We have a bunch of different things that need to happen in order 1.231 +// with different transitions. We make a "stage table" here where 1.232 +// each entry contains the stage function ('fn') and a transition 1.233 +// ('trans'), which can be one of the following: 1.234 +// "instant" - Just calls the next stage directly 1.235 +// "onload" - Sets the next stage as an onload event for the image element 1.236 +// "onerror" - Sets the next stage as an onerror event for the image element 1.237 +// integer - Sets the next stage to be called after the given timeout duration 1.238 +// "finish" - Finish the test 1.239 +var testStages = [ 1.240 + { "fn" : loadBrokenIconReference, "trans" : "onload"}, 1.241 + { "fn" : drawBrokenIconReference, "trans" : "instant"}, 1.242 + { "fn" : loadLoadingIconReference, "trans" : "onload" }, 1.243 + { "fn" : drawLoadingIconReference, "trans" : "instant" }, 1.244 + { "fn" : loadBrokenImage, "trans" : "onerror" }, 1.245 + { "fn" : drawBrokenIcon, "trans" : "instant" }, 1.246 + { "fn" : loadImageReference, "trans" : "onload" }, 1.247 + { "fn" : drawImageReference, "trans" : "instant" }, 1.248 + // XXXbholley - We use a timeout here because resetting the 1.249 + // image doesn't seem to be quite synchronous. If we make 1.250 + // this transition "instant", then the drawImage call draws 1.251 + // an empty (0,0,0,0) rect to the canvas and we're left with 1.252 + // whatever was there before. I don't know of any good event 1.253 + // mechanism to figure out when the image frame is bootstrapped 1.254 + // enough to display the loading image, so I did trial-and-error 1.255 + // with timeouts. 50ms seems to be enough time for things to work 1.256 + // reliably, so *= 6 for good measure. 1.257 + { "fn" : startServerLoad, "trans" : 300 }, 1.258 + { "fn" : drawLoadingIcon, "trans" : "instant" }, 1.259 + { "fn" : signalServerContinue, "trans" : "onload" }, 1.260 + { "fn" : drawLoadedImage, "trans" : "instant" }, 1.261 + { "fn" : verifyCanvases, "trans" : "finish" } ]; 1.262 +var currentStage = 0; 1.263 + 1.264 +// Transition function called at the end of each stage 1.265 +function stageTransition() { 1.266 + 1.267 + // Debugging info so we can figure out the hang 1.268 + dump("test_bug507902.html DEBUG - Current Stage: " + currentStage + "\n"); 1.269 + 1.270 + // What's our transition? 1.271 + var trans = testStages[currentStage++].trans; 1.272 + 1.273 + // If the transition is finish, stop now before we access out of bounds 1.274 + if (trans == "finish") { 1.275 + makeCanvasesVisible(); // Useful for debugging 1.276 + SimpleTest.finish(); 1.277 + return; 1.278 + } 1.279 + 1.280 + // Otherwise, get the next function 1.281 + var nextfn = testStages[currentStage].fn; 1.282 + 1.283 + // Switch based on transition 1.284 + switch (trans) { 1.285 + 1.286 + // Continue right away 1.287 + case "instant": 1.288 + nextfn(); 1.289 + break; 1.290 + 1.291 + // Continue after we get an onload event on the test image 1.292 + case "onload": 1.293 + testImageElem.onload = function(event) {testImageElem.onload = undefined; nextfn();}; 1.294 + break; 1.295 + 1.296 + // Continue after we get an onerror event on the test image 1.297 + case "onerror": 1.298 + testImageElem.onerror = function(event) {testImageElem.onerror = undefined; nextfn();}; 1.299 + break; 1.300 + 1.301 + // Timeout 1.302 + default: 1.303 + setTimeout(nextfn, trans); 1.304 + break 1.305 + } 1.306 +} 1.307 + 1.308 +// Lots if asynchronous behavior here 1.309 +SimpleTest.waitForExplicitFinish(); 1.310 + 1.311 +// Catch somebody's eye 1.312 +dump("This test is failing intermittently, see bug 510001 - If you see orange here, please paste the following debugging output on the bug!\n"); 1.313 + 1.314 +// Kick off the test by invoking the first stage. The stages call each other 1.315 +testStages[0].fn(); 1.316 + 1.317 + 1.318 +// We need to get rid of the old image element and make a new one. If we 1.319 +// don't, the "current/pending" machinery will display the old image until 1.320 +// the new one is loaded, so we won't see the loading icon. 1.321 +function resetImage() { 1.322 + divContainer.removeChild(testImageElem); 1.323 + testImageElem = null; 1.324 + testImageElem = new Image(); 1.325 + divContainer.appendChild(testImageElem); 1.326 +} 1.327 + 1.328 +// 1.329 +// Makes the canvases visible. Called before the tests finish. This is useful for 1.330 +// debugging. 1.331 +// 1.332 +function makeCanvasesVisible() { 1.333 + for (var i = 0; i < canvasNames.length - 1; i += 2) { 1.334 + var title = document.createElement("h3"); 1.335 + title.innerHTML = canvasNames[i] + ", " + canvasNames[i+1] + ":"; 1.336 + document.body.appendChild(title); 1.337 + var myDiv = document.createElement("div"); 1.338 + myDiv.appendChild(canvases[canvasNames[i]]); 1.339 + myDiv.appendChild(canvases[canvasNames[i+1]]); 1.340 + document.body.appendChild(myDiv); 1.341 + } 1.342 +} 1.343 + 1.344 +// 1.345 +// Enables and disables bordering/padding to mimic the look of alt feedback icons 1.346 +// 1.347 +function enableBorderAndPad() { 1.348 + divContainer.style.border = "1px"; 1.349 + divContainer.style.borderStyle = "inset"; 1.350 + testImageElem.style.padding = "3px"; 1.351 +} 1.352 + 1.353 +function disableBorderAndPad() { 1.354 + testImageElem.style.padding = 0; 1.355 + divContainer.style.border = "0px"; 1.356 + divContainer.style.borderStyle = ""; 1.357 +} 1.358 + 1.359 +// 1.360 +// Helper canvas methods. This is mostly copped directly from the reftest framework 1.361 +// 1.362 + 1.363 +function drawWindowToCanvas(canvasName) { 1.364 + var win = testFrameElem.contentWindow; 1.365 + var ctx = canvases[canvasName].getContext("2d"); 1.366 + // drawWindow always draws one canvas pixel for each CSS pixel in the source 1.367 + // window, so scale the drawing to show the zoom (making each canvas pixel be one 1.368 + // device pixel instead) 1.369 + ctx.drawWindow(win, win.scrollX, win.scrollY, 1.370 + Math.ceil(canvases[canvasName].width), 1.371 + Math.ceil(canvases[canvasName].height), 1.372 + "rgb(255,255,255)"); 1.373 +} 1.374 + 1.375 +function canvasesAreEqual(canvas1Name, canvas2Name) { 1.376 + var c1 = canvases[canvas1Name]; 1.377 + var c2 = canvases[canvas2Name]; 1.378 + var differences = gWindowUtils.compareCanvases(c1, c2, {}); 1.379 + return (differences == 0); 1.380 +} 1.381 + 1.382 +</script> 1.383 +</pre> 1.384 +</body> 1.385 +</html>