michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: var gPageCompleted; michael@0: var GLOBAL = this + ''; michael@0: michael@0: // Variables local to jstests harness. michael@0: var jstestsTestPassesUnlessItThrows = false; michael@0: var jstestsRestoreFunction; michael@0: var jstestsOptions; michael@0: michael@0: /* michael@0: * Signals to this script that the current test case should be considered to michael@0: * have passed if it doesn't throw an exception. michael@0: * michael@0: * Overrides the same-named function in shell.js. michael@0: */ michael@0: function testPassesUnlessItThrows() { michael@0: jstestsTestPassesUnlessItThrows = true; michael@0: } michael@0: michael@0: /* michael@0: * Requests to load the given JavaScript file before the file containing the michael@0: * test case. michael@0: */ michael@0: function include(file) { michael@0: outputscripttag(file, {language: "type", mimetype: "text/javascript"}); michael@0: } michael@0: michael@0: /* michael@0: * Sets a restore function which restores the standard built-in ECMAScript michael@0: * properties after a destructive test case, and which will be called after michael@0: * the test case terminates. michael@0: */ michael@0: function setRestoreFunction(restore) { michael@0: jstestsRestoreFunction = restore; michael@0: } michael@0: michael@0: function htmlesc(str) { michael@0: if (str == '<') michael@0: return '<'; michael@0: if (str == '>') michael@0: return '>'; michael@0: if (str == '&') michael@0: return '&'; michael@0: return str; michael@0: } michael@0: michael@0: function DocumentWrite(s) michael@0: { michael@0: try michael@0: { michael@0: var msgDiv = document.createElement('div'); michael@0: msgDiv.innerHTML = s; michael@0: document.body.appendChild(msgDiv); michael@0: msgDiv = null; michael@0: } michael@0: catch(excp) michael@0: { michael@0: document.write(s + '
\n'); michael@0: } michael@0: } michael@0: michael@0: function print() { michael@0: var s = ''; michael@0: var a; michael@0: for (var i = 0; i < arguments.length; i++) michael@0: { michael@0: a = arguments[i]; michael@0: s += String(a) + ' '; michael@0: } michael@0: michael@0: if (typeof dump == 'function') michael@0: { michael@0: dump( s + '\n'); michael@0: } michael@0: michael@0: s = s.replace(/[<>&]/g, htmlesc); michael@0: michael@0: DocumentWrite(s); michael@0: } michael@0: michael@0: function writeHeaderToLog( string ) { michael@0: string = String(string); michael@0: michael@0: if (typeof dump == 'function') michael@0: { michael@0: dump( string + '\n'); michael@0: } michael@0: michael@0: string = string.replace(/[<>&]/g, htmlesc); michael@0: michael@0: DocumentWrite( "

" + string + "

" ); michael@0: } michael@0: michael@0: function writeFormattedResult( expect, actual, string, passed ) { michael@0: string = String(string); michael@0: michael@0: if (typeof dump == 'function') michael@0: { michael@0: dump( string + '\n'); michael@0: } michael@0: michael@0: string = string.replace(/[<>&]/g, htmlesc); michael@0: michael@0: var s = ""+ string ; michael@0: s += "" ; michael@0: s += ( passed ) ? "  " + PASSED michael@0: : " " + FAILED + expect; michael@0: michael@0: DocumentWrite( s + "
" ); michael@0: return passed; michael@0: } michael@0: michael@0: window.onerror = function (msg, page, line) michael@0: { michael@0: jstestsTestPassesUnlessItThrows = false; michael@0: michael@0: // Restore options in case a test case used this common variable name. michael@0: options = jstestsOptions; michael@0: michael@0: // Restore the ECMAScript environment after potentially destructive tests. michael@0: if (typeof jstestsRestoreFunction === "function") { michael@0: jstestsRestoreFunction(); michael@0: } michael@0: michael@0: optionsPush(); michael@0: michael@0: if (typeof DESCRIPTION == 'undefined') michael@0: { michael@0: DESCRIPTION = 'Unknown'; michael@0: } michael@0: if (typeof EXPECTED == 'undefined') michael@0: { michael@0: EXPECTED = 'Unknown'; michael@0: } michael@0: michael@0: var testcase = new TestCase("unknown-test-name", DESCRIPTION, EXPECTED, "error"); michael@0: michael@0: if (document.location.href.indexOf('-n.js') != -1) michael@0: { michael@0: // negative test michael@0: testcase.passed = true; michael@0: } michael@0: michael@0: testcase.reason = page + ':' + line + ': ' + msg; michael@0: michael@0: reportFailure(msg); michael@0: michael@0: optionsReset(); michael@0: }; michael@0: michael@0: function gc() michael@0: { michael@0: try michael@0: { michael@0: SpecialPowers.forceGC(); michael@0: } michael@0: catch(ex) michael@0: { michael@0: print('gc: ' + ex); michael@0: } michael@0: } michael@0: michael@0: function jsdgc() michael@0: { michael@0: try michael@0: { michael@0: var jsdIDebuggerService = SpecialPowers.Ci.jsdIDebuggerService; michael@0: var service = SpecialPowers.Cc['@mozilla.org/js/jsd/debugger-service;1']. michael@0: getService(jsdIDebuggerService); michael@0: service.GC(); michael@0: } michael@0: catch(ex) michael@0: { michael@0: print('jsdgc: ' + ex); michael@0: } michael@0: } michael@0: michael@0: function quit() michael@0: { michael@0: } michael@0: michael@0: function options(aOptionName) michael@0: { michael@0: // return value of options() is a comma delimited list michael@0: // of the previously set values michael@0: michael@0: var value = ''; michael@0: for (var optionName in options.currvalues) michael@0: { michael@0: value += optionName + ','; michael@0: } michael@0: if (value) michael@0: { michael@0: value = value.substring(0, value.length-1); michael@0: } michael@0: michael@0: if (aOptionName) { michael@0: if (!(aOptionName in SpecialPowers.Cu)) { michael@0: // This test is trying to flip an unsupported option, so it's michael@0: // likely no longer testing what it was supposed to. Fail it michael@0: // hard. michael@0: throw "Unsupported JSContext option '"+ aOptionName +"'"; michael@0: } michael@0: michael@0: if (options.currvalues.hasOwnProperty(aOptionName)) michael@0: // option is set, toggle it to unset michael@0: delete options.currvalues[aOptionName]; michael@0: else michael@0: // option is not set, toggle it to set michael@0: options.currvalues[aOptionName] = true; michael@0: michael@0: SpecialPowers.Cu[aOptionName] = michael@0: options.currvalues.hasOwnProperty(aOptionName); michael@0: } michael@0: michael@0: return value; michael@0: } michael@0: michael@0: // Keep a reference to options around so that we can restore it after running michael@0: // a test case, which may have used this common name for one of its own michael@0: // variables. michael@0: jstestsOptions = options; michael@0: michael@0: function optionsInit() { michael@0: michael@0: // hash containing the set options. michael@0: options.currvalues = { michael@0: strict: true, michael@0: werror: true, michael@0: strict_mode: true michael@0: }; michael@0: michael@0: // record initial values to support resetting michael@0: // options to their initial values michael@0: options.initvalues = {}; michael@0: michael@0: // record values in a stack to support pushing michael@0: // and popping options michael@0: options.stackvalues = []; michael@0: michael@0: for (var optionName in options.currvalues) michael@0: { michael@0: var propName = optionName; michael@0: michael@0: if (!(propName in SpecialPowers.Cu)) michael@0: { michael@0: throw "options.currvalues is out of sync with Components.utils"; michael@0: } michael@0: if (!SpecialPowers.Cu[propName]) michael@0: { michael@0: delete options.currvalues[optionName]; michael@0: } michael@0: else michael@0: { michael@0: options.initvalues[optionName] = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: function gczeal(z) michael@0: { michael@0: SpecialPowers.setGCZeal(z); michael@0: } michael@0: michael@0: function jit(on) michael@0: { michael@0: } michael@0: michael@0: function jsTestDriverBrowserInit() michael@0: { michael@0: michael@0: if (typeof dump != 'function') michael@0: { michael@0: dump = print; michael@0: } michael@0: michael@0: optionsInit(); michael@0: optionsClear(); michael@0: michael@0: if (document.location.search.indexOf('?') != 0) michael@0: { michael@0: // not called with a query string michael@0: return; michael@0: } michael@0: michael@0: var properties = {}; michael@0: var fields = document.location.search.slice(1).split(';'); michael@0: for (var ifield = 0; ifield < fields.length; ifield++) michael@0: { michael@0: var propertycaptures = /^([^=]+)=(.*)$/.exec(fields[ifield]); michael@0: if (!propertycaptures) michael@0: { michael@0: properties[fields[ifield]] = true; michael@0: } michael@0: else michael@0: { michael@0: properties[propertycaptures[1]] = decodeURIComponent(propertycaptures[2]); michael@0: if (propertycaptures[1] == 'language') michael@0: { michael@0: // language=(type|language);mimetype michael@0: properties.mimetype = fields[ifield+1]; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (properties.language != 'type') michael@0: { michael@0: try michael@0: { michael@0: properties.version = /javascript([.0-9]+)/.exec(properties.mimetype)[1]; michael@0: } michael@0: catch(ex) michael@0: { michael@0: } michael@0: } michael@0: michael@0: if (!properties.version && navigator.userAgent.indexOf('Gecko/') != -1) michael@0: { michael@0: // If the version is not specified, and the browser is Gecko, michael@0: // use the default version corresponding to the shell's version(0). michael@0: // See https://bugzilla.mozilla.org/show_bug.cgi?id=522760#c11 michael@0: // Otherwise adjust the version to match the suite version for 1.6, michael@0: // and later due to the use of for-each, let, yield, etc. michael@0: // michael@0: // Note that js1_8, js1_8_1, and js1_8_5 are treated identically in michael@0: // the browser. michael@0: if (properties.test.match(/^js1_6/)) michael@0: { michael@0: properties.version = '1.6'; michael@0: } michael@0: else if (properties.test.match(/^js1_7/)) michael@0: { michael@0: properties.version = '1.7'; michael@0: } michael@0: else if (properties.test.match(/^js1_8/)) michael@0: { michael@0: properties.version = '1.8'; michael@0: } michael@0: } michael@0: michael@0: // default to language=type;text/javascript. required for michael@0: // reftest style manifests. michael@0: if (!properties.language) michael@0: { michael@0: properties.language = 'type'; michael@0: properties.mimetype = 'text/javascript'; michael@0: } michael@0: michael@0: gTestPath = properties.test; michael@0: michael@0: if (properties.gczeal) michael@0: { michael@0: gczeal(Number(properties.gczeal)); michael@0: } michael@0: michael@0: /* michael@0: * since the default setting of jit changed from false to true michael@0: * in http://hg.mozilla.org/tracemonkey/rev/685e00e68be9 michael@0: * bisections which depend upon jit settings can be thrown off. michael@0: * default jit(false) when not running jsreftests to make bisections michael@0: * depending upon jit settings consistent over time. This is not needed michael@0: * in shell tests as the default jit setting has not changed there. michael@0: */ michael@0: michael@0: if (properties.jit || !document.location.href.match(/jsreftest.html/)) michael@0: jit(properties.jit); michael@0: michael@0: var testpathparts = properties.test.split(/\//); michael@0: michael@0: if (testpathparts.length < 3) michael@0: { michael@0: // must have at least suitepath/subsuite/testcase.js michael@0: return; michael@0: } michael@0: michael@0: document.write('' + properties.test + '<\/title>'); michael@0: michael@0: // XXX bc - the first document.written script is ignored if the protocol michael@0: // is file:. insert an empty script tag, to work around it. michael@0: document.write('<script></script>'); michael@0: michael@0: // Output script tags for shell.js, then browser.js, at each level of the michael@0: // test path hierarchy. michael@0: var prepath = ""; michael@0: var i = 0; michael@0: for (end = testpathparts.length - 1; i < end; i++) { michael@0: prepath += testpathparts[i] + "/"; michael@0: outputscripttag(prepath + "shell.js", properties); michael@0: outputscripttag(prepath + "browser.js", properties); michael@0: } michael@0: michael@0: // Output the test script itself. michael@0: outputscripttag(prepath + testpathparts[i], properties); michael@0: michael@0: // Finally output the driver-end script to advance to the next test. michael@0: outputscripttag('js-test-driver-end.js', properties); michael@0: return; michael@0: } michael@0: michael@0: function outputscripttag(src, properties) michael@0: { michael@0: if (!src) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: var s = '<script src="' + src + '" charset="utf-8" '; michael@0: michael@0: if (properties.language != 'type') michael@0: { michael@0: s += 'language="javascript'; michael@0: if (properties.version) michael@0: { michael@0: s += properties.version; michael@0: } michael@0: } michael@0: else michael@0: { michael@0: s += 'type="' + properties.mimetype; michael@0: if (properties.version) michael@0: { michael@0: s += ';version=' + properties.version; michael@0: } michael@0: } michael@0: s += '"><\/script>'; michael@0: michael@0: document.write(s); michael@0: } michael@0: michael@0: function jsTestDriverEnd() michael@0: { michael@0: // gDelayTestDriverEnd is used to michael@0: // delay collection of the test result and michael@0: // signal to Spider so that tests can continue michael@0: // to run after page load has fired. They are michael@0: // responsible for setting gDelayTestDriverEnd = true michael@0: // then when completed, setting gDelayTestDriverEnd = false michael@0: // then calling jsTestDriverEnd() michael@0: michael@0: if (gDelayTestDriverEnd) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: window.onerror = null; michael@0: michael@0: // Restore options in case a test case used this common variable name. michael@0: options = jstestsOptions; michael@0: michael@0: // Restore the ECMAScript environment after potentially destructive tests. michael@0: if (typeof jstestsRestoreFunction === "function") { michael@0: jstestsRestoreFunction(); michael@0: } michael@0: michael@0: if (jstestsTestPassesUnlessItThrows) { michael@0: var testcase = new TestCase("unknown-test-name", "", true, true); michael@0: print(PASSED); michael@0: jstestsTestPassesUnlessItThrows = false; michael@0: } michael@0: michael@0: try michael@0: { michael@0: optionsReset(); michael@0: } michael@0: catch(ex) michael@0: { michael@0: dump('jsTestDriverEnd ' + ex); michael@0: } michael@0: michael@0: if (window.opener && window.opener.runNextTest) michael@0: { michael@0: if (window.opener.reportCallBack) michael@0: { michael@0: window.opener.reportCallBack(window.opener.gWindow); michael@0: } michael@0: setTimeout('window.opener.runNextTest()', 250); michael@0: } michael@0: else michael@0: { michael@0: for (var i = 0; i < gTestcases.length; i++) michael@0: { michael@0: gTestcases[i].dump(); michael@0: } michael@0: michael@0: // tell reftest the test is complete. michael@0: document.documentElement.className = ''; michael@0: // tell Spider page is complete michael@0: gPageCompleted = true; michael@0: } michael@0: } michael@0: michael@0: //var dlog = (function (s) { print('debug: ' + s); }); michael@0: var dlog = (function (s) {}); michael@0: michael@0: // dialog closer from http://bclary.com/projects/spider/spider/chrome/content/spider/dialog-closer.js michael@0: michael@0: var gDialogCloser; michael@0: var gDialogCloserObserver; michael@0: michael@0: function registerDialogCloser() michael@0: { michael@0: gDialogCloser = SpecialPowers. michael@0: Cc['@mozilla.org/embedcomp/window-watcher;1']. michael@0: getService(SpecialPowers.Ci.nsIWindowWatcher); michael@0: michael@0: gDialogCloserObserver = {observe: dialogCloser_observe}; michael@0: michael@0: gDialogCloser.registerNotification(gDialogCloserObserver); michael@0: } michael@0: michael@0: function unregisterDialogCloser() michael@0: { michael@0: gczeal(0); michael@0: michael@0: if (!gDialogCloserObserver || !gDialogCloser) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: gDialogCloser.unregisterNotification(gDialogCloserObserver); michael@0: michael@0: gDialogCloserObserver = null; michael@0: gDialogCloser = null; michael@0: } michael@0: michael@0: // use an array to handle the case where multiple dialogs michael@0: // appear at one time michael@0: var gDialogCloserSubjects = []; michael@0: michael@0: function dialogCloser_observe(subject, topic, data) michael@0: { michael@0: if (subject instanceof ChromeWindow && topic == 'domwindowopened' ) michael@0: { michael@0: gDialogCloserSubjects.push(subject); michael@0: // timeout of 0 needed when running under reftest framework. michael@0: subject.setTimeout(closeDialog, 0); michael@0: } michael@0: } michael@0: michael@0: function closeDialog() michael@0: { michael@0: var subject; michael@0: michael@0: while ( (subject = gDialogCloserSubjects.pop()) != null) michael@0: { michael@0: if (subject.document instanceof XULDocument && michael@0: subject.document.documentURI == 'chrome://global/content/commonDialog.xul') michael@0: { michael@0: subject.close(); michael@0: } michael@0: else michael@0: { michael@0: // alerts inside of reftest framework are not XULDocument dialogs. michael@0: subject.close(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: registerDialogCloser(); michael@0: window.addEventListener('unload', unregisterDialogCloser, true); michael@0: michael@0: jsTestDriverBrowserInit();