|
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 type="text/javascript" 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 (var 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 var 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 (var 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 var 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> |