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

" + string + "

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