!import
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1 /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- /
2 /* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is Mozilla's layout acceptance tests.
17 *
18 * The Initial Developer of the Original Code is the Mozilla Foundation.
19 * Portions created by the Initial Developer are Copyright (C) 2006
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation (original author)
24 *
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
36 *
37 * ***** END LICENSE BLOCK ***** */
38
39 const CC = Components.classes;
40 const CI = Components.interfaces;
41 const CR = Components.results;
42
43 const XHTML_NS = "http://www.w3.org/1999/xhtml";
44
45 const NS_LOCAL_FILE_CONTRACTID = "@mozilla.org/file/local;1";
46 const IO_SERVICE_CONTRACTID = "@mozilla.org/network/io-service;1";
47 const NS_LOCALFILEINPUTSTREAM_CONTRACTID =
48 "@mozilla.org/network/file-input-stream;1";
49 const NS_SCRIPTSECURITYMANAGER_CONTRACTID =
50 "@mozilla.org/scriptsecuritymanager;1";
51 const NS_REFTESTHELPER_CONTRACTID =
52 "@mozilla.org/reftest-helper;1";
53
54 const LOAD_FAILURE_TIMEOUT = 90000; // ms
55
56 var gBrowser;
57 var gCanvas1, gCanvas2;
58 var gURLs;
59 var gState;
60 var gFailureTimeout;
61 var gServer;
62 var gCount = 0;
63
64 var gIOService;
65 var gReftestHelper;
66
67 const EXPECTED_PASS = 0;
68 const EXPECTED_FAIL = 1;
69 const EXPECTED_RANDOM = 2;
70 const EXPECTED_DEATH = 3; // test must be skipped to avoid e.g. crash/hang
71 const EXPECTED_LOAD = 4; // test without a reference (just test that it does
72 // not assert, crash, hang, or leak)
73
74 const HTTP_SERVER_PORT = 4444;
75
OnRefTestLoad
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
76 function OnRefTestLoad()
77 {
78 gBrowser = document.getElementById("browser");
79
80 gBrowser.addEventListener("load", OnDocumentLoad, true);
81
82 try {
83 gReftestHelper = CC[NS_REFTESTHELPER_CONTRACTID].getService(CI.nsIReftestHelper);
84 } catch (e) {
85 gReftestHelper = null;
86 }
87
88 var windowElem = document.documentElement;
89
90 gCanvas1 = document.createElementNS(XHTML_NS, "canvas");
91 gCanvas1.setAttribute("width", windowElem.getAttribute("width"));
92 gCanvas1.setAttribute("height", windowElem.getAttribute("height"));
93
94 gCanvas2 = document.createElementNS(XHTML_NS, "canvas");
95 gCanvas2.setAttribute("width", windowElem.getAttribute("width"));
96 gCanvas2.setAttribute("height", windowElem.getAttribute("height"));
97
98 gIOService = CC[IO_SERVICE_CONTRACTID].getService(CI.nsIIOService);
99
100 try {
101 ReadTopManifest(window.arguments[0]);
102 if (gServer) {
103 gServer.registerContentType("sjs", "sjs");
104 gServer.start(HTTP_SERVER_PORT);
105 }
106 StartCurrentTest();
107 } catch (ex) {
108 //gBrowser.loadURI('data:text/plain,' + ex);
109 dump("REFTEST EXCEPTION: " + ex + "\n");
110 DoneTests();
111 }
112 }
113
OnRefTestUnload
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
114 function OnRefTestUnload()
115 {
116 gBrowser.removeEventListener("load", OnDocumentLoad, true);
117 }
118
ReadTopManifest
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
119 function ReadTopManifest(aFileURL)
120 {
121 gURLs = new Array();
122 var url = gIOService.newURI(aFileURL, null, null);
123 if (!url || !url.schemeIs("file"))
124 throw "Expected a file URL for the manifest.";
125 ReadManifest(url);
126 }
127
ReadManifest
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
128 function ReadManifest(aURL)
129 {
130 var listURL = aURL.QueryInterface(CI.nsIFileURL);
131
132 var secMan = CC[NS_SCRIPTSECURITYMANAGER_CONTRACTID]
133 .getService(CI.nsIScriptSecurityManager);
134
135 var fis = CC[NS_LOCALFILEINPUTSTREAM_CONTRACTID].
136 createInstance(CI.nsIFileInputStream);
137 fis.init(listURL.file, -1, -1, false);
138 var lis = fis.QueryInterface(CI.nsILineInputStream);
139
140 var sandbox = new Components.utils.Sandbox(aURL.spec);
141 for (var prop in gAutoconfVars)
142 sandbox[prop] = gAutoconfVars[prop];
143
144 var line = {value:null};
145 var lineNo = 0;
146 do {
147 var more = lis.readLine(line);
148 ++lineNo;
149 var str = line.value;
150 if (str.charAt(0) == "#")
151 continue; // entire line was a comment
152 var i = str.search(/\s+#/);
153 if (i >= 0)
154 str = str.substring(0, i);
155 // strip leading and trailing whitespace
156 str = str.replace(/^\s*/, '').replace(/\s*$/, '');
157 if (!str || str == "")
158 continue;
159 var items = str.split(/\s+/); // split on whitespace
160
161 var expected_status = EXPECTED_PASS;
162 while (items[0].match(/^(fails|random|skip)/)) {
163 var item = items.shift();
164 var stat;
165 var cond;
166 var m = item.match(/^(fails|random|skip)-if(\(.*\))$/);
167 if (m) {
168 stat = m[1];
169 // Note: m[2] contains the parentheses, and we want them.
170 cond = Components.utils.evalInSandbox(m[2], sandbox);
171 } else if (item.match(/^(fails|random|skip)$/)) {
172 stat = item;
173 cond = true;
174 } else {
175 throw "Error in manifest file " + aURL.spec + " line " + lineNo;
176 }
177
178 if (cond) {
179 if (stat == "fails") {
180 expected_status = EXPECTED_FAIL;
181 } else if (stat == "random") {
182 expected_status = EXPECTED_RANDOM;
183 } else if (stat == "skip") {
184 expected_status = EXPECTED_DEATH;
185 }
186 }
187 }
188
189 var runHttp = items[0] == "HTTP";
190 if (runHttp)
191 items.shift();
192
193 if (items[0] == "include") {
194 if (items.length != 2 || runHttp)
195 throw "Error in manifest file " + aURL.spec + " line " + lineNo;
196 var incURI = gIOService.newURI(items[1], null, listURL);
197 secMan.checkLoadURI(aURL, incURI,
198 CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
199 ReadManifest(incURI);
200 } else if (items[0] == "load") {
201 if (expected_status == EXPECTED_PASS)
202 expected_status = EXPECTED_LOAD;
203 if (items.length != 2 ||
204 (expected_status != EXPECTED_LOAD &&
205 expected_status != EXPECTED_DEATH))
206 throw "Error in manifest file " + aURL.spec + " line " + lineNo;
207 var [testURI] = runHttp
208 ? ServeFiles(aURL,
209 listURL.file.parent, [items[1]])
210 : [gIOService.newURI(items[1], null, listURL)];
211 var prettyPath = runHttp
212 ? gIOService.newURI(items[1], null, listURL).spec
213 : testURI.spec;
214 secMan.checkLoadURI(aURL, testURI,
215 CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
216 gURLs.push( { equal: true /* meaningless */,
217 expected: expected_status,
218 prettyPath: prettyPath,
219 url1: testURI,
220 url2: null } );
221 } else if (items[0] == "==" || items[0] == "!=") {
222 if (items.length != 3)
223 throw "Error in manifest file " + aURL.spec + " line " + lineNo;
224 var [testURI, refURI] = runHttp
225 ? ServeFiles(aURL,
226 listURL.file.parent, [items[1], items[2]])
227 : [gIOService.newURI(items[1], null, listURL),
228 gIOService.newURI(items[2], null, listURL)];
229 var prettyPath = runHttp
230 ? gIOService.newURI(items[1], null, listURL).spec
231 : testURI.spec;
232 secMan.checkLoadURI(aURL, testURI,
233 CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
234 secMan.checkLoadURI(aURL, refURI,
235 CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
236 gURLs.push( { equal: (items[0] == "=="),
237 expected: expected_status,
238 prettyPath: prettyPath,
239 url1: testURI,
240 url2: refURI } );
241 } else {
242 throw "Error in manifest file " + aURL.spec + " line " + lineNo;
243 }
244 } while (more);
245 }
246
ServeFiles
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
247 function ServeFiles(manifestURL, directory, files)
248 {
249 if (!gServer)
250 gServer = CC["@mozilla.org/server/jshttp;1"].
251 createInstance(CI.nsIHttpServer);
252
253 gCount++;
254 var path = "/" + gCount + "/";
255 gServer.registerDirectory(path, directory);
256
257 var secMan = CC[NS_SCRIPTSECURITYMANAGER_CONTRACTID]
258 .getService(CI.nsIScriptSecurityManager);
259
FileToURI
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
260 function FileToURI(file)
261 {
262 var testURI = gIOService.newURI("http://localhost:" + HTTP_SERVER_PORT +
263 path + file,
264 null, null);
265
266 // XXX necessary? manifestURL guaranteed to be file, others always HTTP
267 secMan.checkLoadURI(manifestURL, testURI,
268 CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
269
270 return testURI;
271 }
272
273 return files.map(FileToURI);
274 }
275
StartCurrentTest
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
276 function StartCurrentTest()
277 {
278 // make sure we don't run tests that are expected to kill the browser
279 while (gURLs.length > 0 && gURLs[0].expected == EXPECTED_DEATH) {
280 dump("REFTEST KNOWN FAIL (SKIP): " + gURLs[0].url1.spec + "\n");
281 gURLs.shift();
282 }
283
284 if (gURLs.length == 0)
285 DoneTests();
286 else
287 StartCurrentURI(1);
288 }
289
StartCurrentURI
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
290 function StartCurrentURI(aState)
291 {
292 gFailureTimeout = setTimeout(LoadFailed, LOAD_FAILURE_TIMEOUT);
293
294 gState = aState;
295 gBrowser.loadURI(gURLs[0]["url" + aState].spec);
296 }
297
DoneTests
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
298 function DoneTests()
299 {
300 if (gServer)
301 gServer.stop();
302 goQuitApplication();
303 }
304
CanvasToURL
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
305 function CanvasToURL(canvas)
306 {
307 var ctx = whichCanvas.getContext("2d");
308 return canvas.toDataURL();
309 }
310
OnDocumentLoad
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
311 function OnDocumentLoad(event)
312 {
313 if (event.target != gBrowser.contentDocument)
314 // Ignore load events for subframes.
315 return;
316
317 var contentRootElement = gBrowser.contentDocument.documentElement;
318
shouldWait
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
319 function shouldWait() {
320 // use getAttribute because className works differently in HTML and SVG
321 return contentRootElement.hasAttribute('class') &&
322 contentRootElement.getAttribute('class').split(/\s+/)
323 .indexOf("reftest-wait") != -1;
324 }
325
doPrintMode
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
326 function doPrintMode() {
327 // use getAttribute because className works differently in HTML and SVG
328 return contentRootElement.hasAttribute('class') &&
329 contentRootElement.getAttribute('class').split(/\s+/)
330 .indexOf("reftest-print") != -1;
331 }
332
333 if (shouldWait()) {
334 // The testcase will let us know when the test snapshot should be made.
335 // Register a mutation listener to know when the 'reftest-wait' class
336 // gets removed.
337 contentRootElement.addEventListener(
338 "DOMAttrModified",
anon:339:12
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
339 function(event) {
340 if (!shouldWait()) {
341 contentRootElement.removeEventListener(
342 "DOMAttrModified",
343 arguments.callee,
344 false);
345 setTimeout(DocumentLoaded, 0);
346 }
347 }, false);
348 } else {
349 if (doPrintMode()) {
350 var PSSVC = Components.classes["@mozilla.org/gfx/printsettings-service;1"]
351 .getService(Components.interfaces.nsIPrintSettingsService);
352 var ps = PSSVC.newPrintSettings;
353 ps.paperWidth = 5;
354 ps.paperHeight = 3;
355
356 // Override any os-specific unwriteable margins
357 ps.unwriteableMarginTop = 0;
358 ps.unwriteableMarginLeft = 0;
359 ps.unwriteableMarginBottom = 0;
360 ps.unwriteableMarginRight = 0;
361
362 ps.headerStrLeft = "";
363 ps.headerStrCenter = "";
364 ps.headerStrRight = "";
365 ps.footerStrLeft = "";
366 ps.footerStrCenter = "";
367 ps.footerStrRight = "";
368 gBrowser.docShell.contentViewer.setPageMode(true, ps);
369 }
370
371 // Since we can't use a bubbling-phase load listener from chrome,
372 // this is a capturing phase listener. So do setTimeout twice, the
373 // first to get us after the onload has fired in the content, and
374 // the second to get us after any setTimeout(foo, 0) in the content.
375 setTimeout(setTimeout, 0, DocumentLoaded, 0);
376 }
377 }
378
DocumentLoaded
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
379 function DocumentLoaded()
380 {
381 clearTimeout(gFailureTimeout);
382
383 if (gURLs[0].expected == EXPECTED_LOAD) {
384 dump("REFTEST PASS (LOAD ONLY): " + gURLs[0].prettyPath + "\n");
385 gURLs.shift();
386 StartCurrentTest();
387 return;
388 }
389
390 var canvas;
391
392 if (gState == 1)
393 canvas = gCanvas1;
394 else
395 canvas = gCanvas2;
396
397 /* XXX This needs to be rgb(255,255,255) because otherwise we get
398 * black bars at the bottom of every test that are different size
399 * for the first test and the rest (scrollbar-related??) */
400 var win = gBrowser.contentWindow;
401 canvas.getContext("2d").drawWindow(win, win.scrollX, win.scrollY,
402 canvas.width, canvas.height, "rgb(255,255,255)");
403
404 switch (gState) {
405 case 1:
406 // First document has been loaded.
407 // Proceed to load the second document.
408
409 StartCurrentURI(2);
410 break;
411 case 2:
412 // Both documents have been loaded. Compare the renderings and see
413 // if the comparison result matches the expected result specified
414 // in the manifest.
415
416 // number of different pixels
417 var differences;
418 // whether the two renderings match:
419 var equal;
420
421 if (gReftestHelper) {
422 differences = gReftestHelper.compareCanvas(gCanvas1, gCanvas2);
423 equal = (differences == 0);
424 } else {
425 differences = -1;
426 var k1 = gCanvas1.toDataURL();
427 var k2 = gCanvas2.toDataURL();
428 equal = (k1 == k2);
429 }
430
431 // whether the comparison result matches what is in the manifest
432 var test_passed = (equal == gURLs[0].equal);
433 // what is expected on this platform (PASS, FAIL, or RANDOM)
434 var expected = gURLs[0].expected;
435
436 var outputs = {};
437 const randomMsg = " (RESULT EXPECTED TO BE RANDOM)";
438 outputs[EXPECTED_PASS] = {true: "PASS",
439 false: "UNEXPECTED FAIL"};
440 outputs[EXPECTED_FAIL] = {true: "UNEXPECTED PASS",
441 false: "KNOWN FAIL"};
442 outputs[EXPECTED_RANDOM] = {true: "PASS" + randomMsg,
443 false: "KNOWN FAIL" + randomMsg};
444
445 var result = "REFTEST " + outputs[expected][test_passed] + ": ";
446 if (!gURLs[0].equal) {
447 result += "(!=) ";
448 }
449 result += gURLs[0].prettyPath; // the URL being tested
450 dump(result + "\n");
451 if (!test_passed && expected == EXPECTED_PASS ||
452 test_passed && expected == EXPECTED_FAIL) {
453 if (!equal) {
454 dump("REFTEST IMAGE 1 (TEST): " + gCanvas1.toDataURL() + "\n");
455 dump("REFTEST IMAGE 2 (REFERENCE): " + gCanvas2.toDataURL() + "\n");
456 dump("REFTEST number of differing pixels: " + differences + "\n");
457 } else {
458 dump("REFTEST IMAGE: " + gCanvas1.toDataURL() + "\n");
459 }
460 }
461
462 gURLs.shift();
463 StartCurrentTest();
464 break;
465 default:
466 throw "Unexpected state.";
467 }
468 }
469
LoadFailed
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
470 function LoadFailed()
471 {
472 dump("REFTEST UNEXPECTED FAIL (LOADING): " +
473 gURLs[0]["url" + gState].spec + "\n");
474 gURLs.shift();
475 StartCurrentTest();
476 }