testharness.js 75 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233
  1. /*
  2. Distributed under both the W3C Test Suite License [1] and the W3C
  3. 3-clause BSD License [2]. To contribute to a W3C Test Suite, see the
  4. policies and contribution forms [3].
  5. [1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license
  6. [2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license
  7. [3] http://www.w3.org/2004/10/27-testcases
  8. */
  9. /*
  10. * == Introduction ==
  11. *
  12. * This file provides a framework for writing testcases. It is intended to
  13. * provide a convenient API for making common assertions, and to work both
  14. * for testing synchronous and asynchronous DOM features in a way that
  15. * promotes clear, robust, tests.
  16. *
  17. * == Basic Usage ==
  18. *
  19. * To use this file, import the script and the testharnessreport script into
  20. * the test document:
  21. * <script src="/resources/testharness.js"></script>
  22. * <script src="/resources/testharnessreport.js"></script>
  23. *
  24. * Within each file one may define one or more tests. Each test is atomic
  25. * in the sense that a single test has a single result (pass/fail/timeout).
  26. * Within each test one may have a number of asserts. The test fails at the
  27. * first failing assert, and the remainder of the test is (typically) not run.
  28. *
  29. * If the file containing the tests is a HTML file with an element of id "log"
  30. * this will be populated with a table containing the test results after all
  31. * the tests have run.
  32. *
  33. * NOTE: By default tests must be created before the load event fires. For ways
  34. * to create tests after the load event, see "Determining when all tests
  35. * are complete", below
  36. *
  37. * == Synchronous Tests ==
  38. *
  39. * To create a synchronous test use the test() function:
  40. *
  41. * test(test_function, name, properties)
  42. *
  43. * test_function is a function that contains the code to test. For example a
  44. * trivial passing test would be:
  45. *
  46. * test(function() {assert_true(true)}, "assert_true with true")
  47. *
  48. * The function passed in is run in the test() call.
  49. *
  50. * properties is an object that overrides default test properties. The
  51. * recognised properties are:
  52. * timeout - the test timeout in ms
  53. *
  54. * e.g.
  55. * test(test_function, "Sample test", {timeout:1000})
  56. *
  57. * would run test_function with a timeout of 1s.
  58. *
  59. * Additionally, test-specific metadata can be passed in the properties. These
  60. * are used when the individual test has different metadata from that stored
  61. * in the <head>.
  62. * The recognized metadata properties are:
  63. *
  64. * help - The url of the part of the specification being tested
  65. *
  66. * assert - A human readable description of what the test is attempting
  67. * to prove
  68. *
  69. * author - Name and contact information for the author of the test in the
  70. * format: "Name <email_addr>" or "Name http://contact/url"
  71. *
  72. * == Asynchronous Tests ==
  73. *
  74. * Testing asynchronous features is somewhat more complex since the result of
  75. * a test may depend on one or more events or other callbacks. The API provided
  76. * for testing these features is indended to be rather low-level but hopefully
  77. * applicable to many situations.
  78. *
  79. * To create a test, one starts by getting a Test object using async_test:
  80. *
  81. * async_test(name, properties)
  82. *
  83. * e.g.
  84. * var t = async_test("Simple async test")
  85. *
  86. * Assertions can be added to the test by calling the step method of the test
  87. * object with a function containing the test assertions:
  88. *
  89. * t.step(function() {assert_true(true)});
  90. *
  91. * When all the steps are complete, the done() method must be called:
  92. *
  93. * t.done();
  94. *
  95. * As a convenience, async_test can also takes a function as first argument.
  96. * This function is called with the test object as both its `this` object and
  97. * first argument. The above example can be rewritten as:
  98. *
  99. * async_test(function(t) {
  100. * object.some_event = function() {
  101. * t.step(function (){assert_true(true); t.done();});
  102. * };
  103. * }, "Simple async test");
  104. *
  105. * which avoids cluttering the global scope with references to async
  106. * tests instances.
  107. *
  108. * The properties argument is identical to that for test().
  109. *
  110. * In many cases it is convenient to run a step in response to an event or a
  111. * callback. A convenient method of doing this is through the step_func method
  112. * which returns a function that, when called runs a test step. For example
  113. *
  114. * object.some_event = t.step_func(function(e) {assert_true(e.a)});
  115. *
  116. * == Making assertions ==
  117. *
  118. * Functions for making assertions start assert_
  119. * The best way to get a list is to look in this file for functions names
  120. * matching that pattern. The general signature is
  121. *
  122. * assert_something(actual, expected, description)
  123. *
  124. * although not all assertions precisely match this pattern e.g. assert_true
  125. * only takes actual and description as arguments.
  126. *
  127. * The description parameter is used to present more useful error messages when
  128. * a test fails
  129. *
  130. * NOTE: All asserts must be located in a test() or a step of an async_test().
  131. * asserts outside these places won't be detected correctly by the harness
  132. * and may cause a file to stop testing.
  133. *
  134. * == Setup ==
  135. *
  136. * Sometimes tests require non-trivial setup that may fail. For this purpose
  137. * there is a setup() function, that may be called with one or two arguments.
  138. * The two argument version is:
  139. *
  140. * setup(func, properties)
  141. *
  142. * The one argument versions may omit either argument.
  143. * func is a function to be run synchronously. setup() becomes a no-op once
  144. * any tests have returned results. Properties are global properties of the test
  145. * harness. Currently recognised properties are:
  146. *
  147. * timeout - The time in ms after which the harness should stop waiting for
  148. * tests to complete (this is different to the per-test timeout
  149. * because async tests do not start their timer until .step is called)
  150. *
  151. * explicit_done - Wait for an explicit call to done() before declaring all
  152. * tests complete (see below)
  153. *
  154. * output_document - The document to which results should be logged. By default
  155. * this is the current document but could be an ancestor
  156. * document in some cases e.g. a SVG test loaded in an HTML
  157. * wrapper
  158. *
  159. * explicit_timeout - disable file timeout; only stop waiting for results
  160. * when the timeout() function is called (typically for
  161. * use when integrating with some existing test framework
  162. * that has its own timeout mechanism).
  163. *
  164. * == Determining when all tests are complete ==
  165. *
  166. * By default the test harness will assume there are no more results to come
  167. * when:
  168. * 1) There are no Test objects that have been created but not completed
  169. * 2) The load event on the document has fired
  170. *
  171. * This behaviour can be overridden by setting the explicit_done property to
  172. * true in a call to setup(). If explicit_done is true, the test harness will
  173. * not assume it is done until the global done() function is called. Once done()
  174. * is called, the two conditions above apply like normal.
  175. *
  176. * == Generating tests ==
  177. *
  178. * NOTE: this functionality may be removed
  179. *
  180. * There are scenarios in which is is desirable to create a large number of
  181. * (synchronous) tests that are internally similar but vary in the parameters
  182. * used. To make this easier, the generate_tests function allows a single
  183. * function to be called with each set of parameters in a list:
  184. *
  185. * generate_tests(test_function, parameter_lists, properties)
  186. *
  187. * For example:
  188. *
  189. * generate_tests(assert_equals, [
  190. * ["Sum one and one", 1+1, 2],
  191. * ["Sum one and zero", 1+0, 1]
  192. * ])
  193. *
  194. * Is equivalent to:
  195. *
  196. * test(function() {assert_equals(1+1, 2)}, "Sum one and one")
  197. * test(function() {assert_equals(1+0, 1)}, "Sum one and zero")
  198. *
  199. * Note that the first item in each parameter list corresponds to the name of
  200. * the test.
  201. *
  202. * The properties argument is identical to that for test(). This may be a
  203. * single object (used for all generated tests) or an array.
  204. *
  205. * == Callback API ==
  206. *
  207. * The framework provides callbacks corresponding to 3 events:
  208. *
  209. * start - happens when the first Test is created
  210. * result - happens when a test result is recieved
  211. * complete - happens when all results are recieved
  212. *
  213. * The page defining the tests may add callbacks for these events by calling
  214. * the following methods:
  215. *
  216. * add_start_callback(callback) - callback called with no arguments
  217. * add_result_callback(callback) - callback called with a test argument
  218. * add_completion_callback(callback) - callback called with an array of tests
  219. * and an status object
  220. *
  221. * tests have the following properties:
  222. * status: A status code. This can be compared to the PASS, FAIL, TIMEOUT and
  223. * NOTRUN properties on the test object
  224. * message: A message indicating the reason for failure. In the future this
  225. * will always be a string
  226. *
  227. * The status object gives the overall status of the harness. It has the
  228. * following properties:
  229. * status: Can be compared to the OK, ERROR and TIMEOUT properties
  230. * message: An error message set when the status is ERROR
  231. *
  232. * == External API ==
  233. *
  234. * In order to collect the results of multiple pages containing tests, the test
  235. * harness will, when loaded in a nested browsing context, attempt to call
  236. * certain functions in each ancestor and opener browsing context:
  237. *
  238. * start - start_callback
  239. * result - result_callback
  240. * complete - completion_callback
  241. *
  242. * These are given the same arguments as the corresponding internal callbacks
  243. * described above.
  244. *
  245. * == External API through cross-document messaging ==
  246. *
  247. * Where supported, the test harness will also send messages using
  248. * cross-document messaging to each ancestor and opener browsing context. Since
  249. * it uses the wildcard keyword (*), cross-origin communication is enabled and
  250. * script on different origins can collect the results.
  251. *
  252. * This API follows similar conventions as those described above only slightly
  253. * modified to accommodate message event API. Each message is sent by the harness
  254. * is passed a single vanilla object, available as the `data` property of the
  255. * event object. These objects are structures as follows:
  256. *
  257. * start - { type: "start" }
  258. * result - { type: "result", test: Test }
  259. * complete - { type: "complete", tests: [Test, ...], status: TestsStatus }
  260. *
  261. * == List of assertions ==
  262. *
  263. * assert_true(actual, description)
  264. * asserts that /actual/ is strictly true
  265. *
  266. * assert_false(actual, description)
  267. * asserts that /actual/ is strictly false
  268. *
  269. * assert_equals(actual, expected, description)
  270. * asserts that /actual/ is the same value as /expected/
  271. *
  272. * assert_not_equals(actual, expected, description)
  273. * asserts that /actual/ is a different value to /expected/. Yes, this means
  274. * that "expected" is a misnomer
  275. *
  276. * assert_in_array(actual, expected, description)
  277. * asserts that /expected/ is an Array, and /actual/ is equal to one of the
  278. * members -- expected.indexOf(actual) != -1
  279. *
  280. * assert_array_equals(actual, expected, description)
  281. * asserts that /actual/ and /expected/ have the same length and the value of
  282. * each indexed property in /actual/ is the strictly equal to the corresponding
  283. * property value in /expected/
  284. *
  285. * assert_approx_equals(actual, expected, epsilon, description)
  286. * asserts that /actual/ is a number within +/- /epsilon/ of /expected/
  287. *
  288. * assert_less_than(actual, expected, description)
  289. * asserts that /actual/ is a number less than /expected/
  290. *
  291. * assert_greater_than(actual, expected, description)
  292. * asserts that /actual/ is a number greater than /expected/
  293. *
  294. * assert_less_than_equal(actual, expected, description)
  295. * asserts that /actual/ is a number less than or equal to /expected/
  296. *
  297. * assert_greater_than_equal(actual, expected, description)
  298. * asserts that /actual/ is a number greater than or equal to /expected/
  299. *
  300. * assert_regexp_match(actual, expected, description)
  301. * asserts that /actual/ matches the regexp /expected/
  302. *
  303. * assert_class_string(object, class_name, description)
  304. * asserts that the class string of /object/ as returned in
  305. * Object.prototype.toString is equal to /class_name/.
  306. *
  307. * assert_own_property(object, property_name, description)
  308. * assert that object has own property property_name
  309. *
  310. * assert_inherits(object, property_name, description)
  311. * assert that object does not have an own property named property_name
  312. * but that property_name is present in the prototype chain for object
  313. *
  314. * assert_idl_attribute(object, attribute_name, description)
  315. * assert that an object that is an instance of some interface has the
  316. * attribute attribute_name following the conditions specified by WebIDL
  317. *
  318. * assert_readonly(object, property_name, description)
  319. * assert that property property_name on object is readonly
  320. *
  321. * assert_throws(code, func, description)
  322. * code - the expected exception:
  323. * o string: the thrown exception must be a DOMException with the given
  324. * name, e.g., "TimeoutError" (for compatibility with existing
  325. * tests, a constant is also supported, e.g., "TIMEOUT_ERR")
  326. * o object: the thrown exception must have a property called "name" that
  327. * matches code.name
  328. * o null: allow any exception (in general, one of the options above
  329. * should be used)
  330. * func - a function that should throw
  331. *
  332. * assert_unreached(description)
  333. * asserts if called. Used to ensure that some codepath is *not* taken e.g.
  334. * an event does not fire.
  335. *
  336. * assert_any(assert_func, actual, expected_array, extra_arg_1, ... extra_arg_N)
  337. * asserts that one assert_func(actual, expected_array_N, extra_arg1, ..., extra_arg_N)
  338. * is true for some expected_array_N in expected_array. This only works for assert_func
  339. * with signature assert_func(actual, expected, args_1, ..., args_N). Note that tests
  340. * with multiple allowed pass conditions are bad practice unless the spec specifically
  341. * allows multiple behaviours. Test authors should not use this method simply to hide
  342. * UA bugs.
  343. *
  344. * assert_exists(object, property_name, description)
  345. * *** deprecated ***
  346. * asserts that object has an own property property_name
  347. *
  348. * assert_not_exists(object, property_name, description)
  349. * *** deprecated ***
  350. * assert that object does not have own property property_name
  351. */
  352. (function ()
  353. {
  354. var debug = false;
  355. // default timeout is 5 seconds, test can override if needed
  356. var settings = {
  357. output:true,
  358. timeout:5000,
  359. test_timeout:2000
  360. };
  361. var xhtml_ns = "http://www.w3.org/1999/xhtml";
  362. // script_prefix is used by Output.prototype.show_results() to figure out
  363. // where to get testharness.css from. It's enclosed in an extra closure to
  364. // not pollute the library's namespace with variables like "src".
  365. var script_prefix = null;
  366. (function ()
  367. {
  368. var scripts = document.getElementsByTagName("script");
  369. for (var i = 0; i < scripts.length; i++)
  370. {
  371. if (scripts[i].src)
  372. {
  373. var src = scripts[i].src;
  374. }
  375. else if (scripts[i].href)
  376. {
  377. //SVG case
  378. var src = scripts[i].href.baseVal;
  379. }
  380. if (src && src.slice(src.length - "testharness.js".length) === "testharness.js")
  381. {
  382. script_prefix = src.slice(0, src.length - "testharness.js".length);
  383. break;
  384. }
  385. }
  386. })();
  387. /*
  388. * API functions
  389. */
  390. var name_counter = 0;
  391. function next_default_name()
  392. {
  393. //Don't use document.title to work around an Opera bug in XHTML documents
  394. var prefix = document.getElementsByTagName("title").length > 0 ?
  395. document.getElementsByTagName("title")[0].firstChild.data :
  396. "Untitled";
  397. var suffix = name_counter > 0 ? " " + name_counter : "";
  398. name_counter++;
  399. return prefix + suffix;
  400. }
  401. function test(func, name, properties)
  402. {
  403. var test_name = name ? name : next_default_name();
  404. properties = properties ? properties : {};
  405. var test_obj = new Test(test_name, properties);
  406. test_obj.step(func);
  407. if (test_obj.status === test_obj.NOTRUN) {
  408. test_obj.done();
  409. }
  410. }
  411. function async_test(func, name, properties)
  412. {
  413. if (typeof func !== "function") {
  414. properties = name;
  415. name = func;
  416. func = null;
  417. }
  418. var test_name = name ? name : next_default_name();
  419. properties = properties ? properties : {};
  420. var test_obj = new Test(test_name, properties);
  421. if (func) {
  422. test_obj.step(func, test_obj, test_obj);
  423. }
  424. return test_obj;
  425. }
  426. function setup(func_or_properties, maybe_properties)
  427. {
  428. var func = null;
  429. var properties = {};
  430. if (arguments.length === 2) {
  431. func = func_or_properties;
  432. properties = maybe_properties;
  433. } else if (func_or_properties instanceof Function){
  434. func = func_or_properties;
  435. } else {
  436. properties = func_or_properties;
  437. }
  438. tests.setup(func, properties);
  439. output.setup(properties);
  440. }
  441. function done() {
  442. tests.end_wait();
  443. }
  444. function generate_tests(func, args, properties) {
  445. forEach(args, function(x, i)
  446. {
  447. var name = x[0];
  448. test(function()
  449. {
  450. func.apply(this, x.slice(1));
  451. },
  452. name,
  453. Array.isArray(properties) ? properties[i] : properties);
  454. });
  455. }
  456. function on_event(object, event, callback)
  457. {
  458. object.addEventListener(event, callback, false);
  459. }
  460. expose(test, 'test');
  461. expose(async_test, 'async_test');
  462. expose(generate_tests, 'generate_tests');
  463. expose(setup, 'setup');
  464. expose(done, 'done');
  465. expose(on_event, 'on_event');
  466. /*
  467. * Return a string truncated to the given length, with ... added at the end
  468. * if it was longer.
  469. */
  470. function truncate(s, len)
  471. {
  472. if (s.length > len) {
  473. return s.substring(0, len - 3) + "...";
  474. }
  475. return s;
  476. }
  477. /*
  478. * Convert a value to a nice, human-readable string
  479. */
  480. function format_value(val, seen)
  481. {
  482. if (!seen) {
  483. seen = [];
  484. }
  485. if (typeof val === "object" && val !== null)
  486. {
  487. if (seen.indexOf(val) >= 0)
  488. {
  489. return "[...]";
  490. }
  491. seen.push(val);
  492. }
  493. if (Array.isArray(val))
  494. {
  495. return "[" + val.map(function(x) {return format_value(x, seen)}).join(", ") + "]";
  496. }
  497. switch (typeof val)
  498. {
  499. case "string":
  500. val = val.replace("\\", "\\\\");
  501. for (var i = 0; i < 32; i++)
  502. {
  503. var replace = "\\";
  504. switch (i) {
  505. case 0: replace += "0"; break;
  506. case 1: replace += "x01"; break;
  507. case 2: replace += "x02"; break;
  508. case 3: replace += "x03"; break;
  509. case 4: replace += "x04"; break;
  510. case 5: replace += "x05"; break;
  511. case 6: replace += "x06"; break;
  512. case 7: replace += "x07"; break;
  513. case 8: replace += "b"; break;
  514. case 9: replace += "t"; break;
  515. case 10: replace += "n"; break;
  516. case 11: replace += "v"; break;
  517. case 12: replace += "f"; break;
  518. case 13: replace += "r"; break;
  519. case 14: replace += "x0e"; break;
  520. case 15: replace += "x0f"; break;
  521. case 16: replace += "x10"; break;
  522. case 17: replace += "x11"; break;
  523. case 18: replace += "x12"; break;
  524. case 19: replace += "x13"; break;
  525. case 20: replace += "x14"; break;
  526. case 21: replace += "x15"; break;
  527. case 22: replace += "x16"; break;
  528. case 23: replace += "x17"; break;
  529. case 24: replace += "x18"; break;
  530. case 25: replace += "x19"; break;
  531. case 26: replace += "x1a"; break;
  532. case 27: replace += "x1b"; break;
  533. case 28: replace += "x1c"; break;
  534. case 29: replace += "x1d"; break;
  535. case 30: replace += "x1e"; break;
  536. case 31: replace += "x1f"; break;
  537. }
  538. val = val.replace(RegExp(String.fromCharCode(i), "g"), replace);
  539. }
  540. return '"' + val.replace(/"/g, '\\"') + '"';
  541. case "boolean":
  542. case "undefined":
  543. return String(val);
  544. case "number":
  545. // In JavaScript, -0 === 0 and String(-0) == "0", so we have to
  546. // special-case.
  547. if (val === -0 && 1/val === -Infinity)
  548. {
  549. return "-0";
  550. }
  551. return String(val);
  552. case "object":
  553. if (val === null)
  554. {
  555. return "null";
  556. }
  557. // Special-case Node objects, since those come up a lot in my tests. I
  558. // ignore namespaces. I use duck-typing instead of instanceof, because
  559. // instanceof doesn't work if the node is from another window (like an
  560. // iframe's contentWindow):
  561. // http://www.w3.org/Bugs/Public/show_bug.cgi?id=12295
  562. if ("nodeType" in val
  563. && "nodeName" in val
  564. && "nodeValue" in val
  565. && "childNodes" in val)
  566. {
  567. switch (val.nodeType)
  568. {
  569. case Node.ELEMENT_NODE:
  570. var ret = "<" + val.tagName.toLowerCase();
  571. for (var i = 0; i < val.attributes.length; i++)
  572. {
  573. ret += " " + val.attributes[i].name + '="' + val.attributes[i].value + '"';
  574. }
  575. ret += ">" + val.innerHTML + "</" + val.tagName.toLowerCase() + ">";
  576. return "Element node " + truncate(ret, 60);
  577. case Node.TEXT_NODE:
  578. return 'Text node "' + truncate(val.data, 60) + '"';
  579. case Node.PROCESSING_INSTRUCTION_NODE:
  580. return "ProcessingInstruction node with target " + format_value(truncate(val.target, 60)) + " and data " + format_value(truncate(val.data, 60));
  581. case Node.COMMENT_NODE:
  582. return "Comment node <!--" + truncate(val.data, 60) + "-->";
  583. case Node.DOCUMENT_NODE:
  584. return "Document node with " + val.childNodes.length + (val.childNodes.length == 1 ? " child" : " children");
  585. case Node.DOCUMENT_TYPE_NODE:
  586. return "DocumentType node";
  587. case Node.DOCUMENT_FRAGMENT_NODE:
  588. return "DocumentFragment node with " + val.childNodes.length + (val.childNodes.length == 1 ? " child" : " children");
  589. default:
  590. return "Node object of unknown type";
  591. }
  592. }
  593. // Fall through to default
  594. default:
  595. return typeof val + ' "' + truncate(String(val), 60) + '"';
  596. }
  597. }
  598. expose(format_value, "format_value");
  599. /*
  600. * Assertions
  601. */
  602. function assert_true(actual, description)
  603. {
  604. assert(actual === true, "assert_true", description,
  605. "expected true got ${actual}", {actual:actual});
  606. };
  607. expose(assert_true, "assert_true");
  608. function assert_false(actual, description)
  609. {
  610. assert(actual === false, "assert_false", description,
  611. "expected false got ${actual}", {actual:actual});
  612. };
  613. expose(assert_false, "assert_false");
  614. function same_value(x, y) {
  615. if (y !== y)
  616. {
  617. //NaN case
  618. return x !== x;
  619. }
  620. else if (x === 0 && y === 0) {
  621. //Distinguish +0 and -0
  622. return 1/x === 1/y;
  623. }
  624. else
  625. {
  626. //typical case
  627. return x === y;
  628. }
  629. }
  630. function assert_equals(actual, expected, description)
  631. {
  632. /*
  633. * Test if two primitives are equal or two objects
  634. * are the same object
  635. */
  636. if (typeof actual != typeof expected)
  637. {
  638. assert(false, "assert_equals", description,
  639. "expected (" + typeof expected + ") ${expected} but got (" + typeof actual + ") ${actual}",
  640. {expected:expected, actual:actual});
  641. return;
  642. }
  643. assert(same_value(actual, expected), "assert_equals", description,
  644. "expected ${expected} but got ${actual}",
  645. {expected:expected, actual:actual});
  646. };
  647. expose(assert_equals, "assert_equals");
  648. function assert_not_equals(actual, expected, description)
  649. {
  650. /*
  651. * Test if two primitives are unequal or two objects
  652. * are different objects
  653. */
  654. assert(!same_value(actual, expected), "assert_not_equals", description,
  655. "got disallowed value ${actual}",
  656. {actual:actual});
  657. };
  658. expose(assert_not_equals, "assert_not_equals");
  659. function assert_in_array(actual, expected, description)
  660. {
  661. assert(expected.indexOf(actual) != -1, "assert_in_array", description,
  662. "value ${actual} not in array ${expected}",
  663. {actual:actual, expected:expected});
  664. }
  665. expose(assert_in_array, "assert_in_array");
  666. function assert_object_equals(actual, expected, description)
  667. {
  668. //This needs to be improved a great deal
  669. function check_equal(actual, expected, stack)
  670. {
  671. stack.push(actual);
  672. var p;
  673. for (p in actual)
  674. {
  675. assert(expected.hasOwnProperty(p), "assert_object_equals", description,
  676. "unexpected property ${p}", {p:p});
  677. if (typeof actual[p] === "object" && actual[p] !== null)
  678. {
  679. if (stack.indexOf(actual[p]) === -1)
  680. {
  681. check_equal(actual[p], expected[p], stack);
  682. }
  683. }
  684. else
  685. {
  686. assert(same_value(actual[p], expected[p]), "assert_object_equals", description,
  687. "property ${p} expected ${expected} got ${actual}",
  688. {p:p, expected:expected, actual:actual});
  689. }
  690. }
  691. for (p in expected)
  692. {
  693. assert(actual.hasOwnProperty(p),
  694. "assert_object_equals", description,
  695. "expected property ${p} missing", {p:p});
  696. }
  697. stack.pop();
  698. }
  699. check_equal(actual, expected, []);
  700. };
  701. expose(assert_object_equals, "assert_object_equals");
  702. function assert_array_equals(actual, expected, description)
  703. {
  704. assert(actual.length === expected.length,
  705. "assert_array_equals", description,
  706. "lengths differ, expected ${expected} got ${actual}",
  707. {expected:expected.length, actual:actual.length});
  708. for (var i=0; i < actual.length; i++)
  709. {
  710. assert(actual.hasOwnProperty(i) === expected.hasOwnProperty(i),
  711. "assert_array_equals", description,
  712. "property ${i}, property expected to be $expected but was $actual",
  713. {i:i, expected:expected.hasOwnProperty(i) ? "present" : "missing",
  714. actual:actual.hasOwnProperty(i) ? "present" : "missing"});
  715. assert(same_value(expected[i], actual[i]),
  716. "assert_array_equals", description,
  717. "property ${i}, expected ${expected} but got ${actual}",
  718. {i:i, expected:expected[i], actual:actual[i]});
  719. }
  720. }
  721. expose(assert_array_equals, "assert_array_equals");
  722. function assert_approx_equals(actual, expected, epsilon, description)
  723. {
  724. /*
  725. * Test if two primitive numbers are equal withing +/- epsilon
  726. */
  727. assert(typeof actual === "number",
  728. "assert_approx_equals", description,
  729. "expected a number but got a ${type_actual}",
  730. {type_actual:typeof actual});
  731. assert(Math.abs(actual - expected) <= epsilon,
  732. "assert_approx_equals", description,
  733. "expected ${expected} +/- ${epsilon} but got ${actual}",
  734. {expected:expected, actual:actual, epsilon:epsilon});
  735. };
  736. expose(assert_approx_equals, "assert_approx_equals");
  737. function assert_less_than(actual, expected, description)
  738. {
  739. /*
  740. * Test if a primitive number is less than another
  741. */
  742. assert(typeof actual === "number",
  743. "assert_less_than", description,
  744. "expected a number but got a ${type_actual}",
  745. {type_actual:typeof actual});
  746. assert(actual < expected,
  747. "assert_less_than", description,
  748. "expected a number less than ${expected} but got ${actual}",
  749. {expected:expected, actual:actual});
  750. };
  751. expose(assert_less_than, "assert_less_than");
  752. function assert_greater_than(actual, expected, description)
  753. {
  754. /*
  755. * Test if a primitive number is greater than another
  756. */
  757. assert(typeof actual === "number",
  758. "assert_greater_than", description,
  759. "expected a number but got a ${type_actual}",
  760. {type_actual:typeof actual});
  761. assert(actual > expected,
  762. "assert_greater_than", description,
  763. "expected a number greater than ${expected} but got ${actual}",
  764. {expected:expected, actual:actual});
  765. };
  766. expose(assert_greater_than, "assert_greater_than");
  767. function assert_less_than_equal(actual, expected, description)
  768. {
  769. /*
  770. * Test if a primitive number is less than or equal to another
  771. */
  772. assert(typeof actual === "number",
  773. "assert_less_than_equal", description,
  774. "expected a number but got a ${type_actual}",
  775. {type_actual:typeof actual});
  776. assert(actual <= expected,
  777. "assert_less_than", description,
  778. "expected a number less than or equal to ${expected} but got ${actual}",
  779. {expected:expected, actual:actual});
  780. };
  781. expose(assert_less_than_equal, "assert_less_than_equal");
  782. function assert_greater_than_equal(actual, expected, description)
  783. {
  784. /*
  785. * Test if a primitive number is greater than or equal to another
  786. */
  787. assert(typeof actual === "number",
  788. "assert_greater_than_equal", description,
  789. "expected a number but got a ${type_actual}",
  790. {type_actual:typeof actual});
  791. assert(actual >= expected,
  792. "assert_greater_than_equal", description,
  793. "expected a number greater than or equal to ${expected} but got ${actual}",
  794. {expected:expected, actual:actual});
  795. };
  796. expose(assert_greater_than_equal, "assert_greater_than_equal");
  797. function assert_regexp_match(actual, expected, description) {
  798. /*
  799. * Test if a string (actual) matches a regexp (expected)
  800. */
  801. assert(expected.test(actual),
  802. "assert_regexp_match", description,
  803. "expected ${expected} but got ${actual}",
  804. {expected:expected, actual:actual});
  805. }
  806. expose(assert_regexp_match, "assert_regexp_match");
  807. function assert_class_string(object, class_string, description) {
  808. assert_equals({}.toString.call(object), "[object " + class_string + "]",
  809. description);
  810. }
  811. expose(assert_class_string, "assert_class_string");
  812. function _assert_own_property(name) {
  813. return function(object, property_name, description)
  814. {
  815. assert(object.hasOwnProperty(property_name),
  816. name, description,
  817. "expected property ${p} missing", {p:property_name});
  818. };
  819. }
  820. expose(_assert_own_property("assert_exists"), "assert_exists");
  821. expose(_assert_own_property("assert_own_property"), "assert_own_property");
  822. function assert_not_exists(object, property_name, description)
  823. {
  824. assert(!object.hasOwnProperty(property_name),
  825. "assert_not_exists", description,
  826. "unexpected property ${p} found", {p:property_name});
  827. };
  828. expose(assert_not_exists, "assert_not_exists");
  829. function _assert_inherits(name) {
  830. return function (object, property_name, description)
  831. {
  832. assert(typeof object === "object",
  833. name, description,
  834. "provided value is not an object");
  835. assert("hasOwnProperty" in object,
  836. name, description,
  837. "provided value is an object but has no hasOwnProperty method");
  838. assert(!object.hasOwnProperty(property_name),
  839. name, description,
  840. "property ${p} found on object expected in prototype chain",
  841. {p:property_name});
  842. assert(property_name in object,
  843. name, description,
  844. "property ${p} not found in prototype chain",
  845. {p:property_name});
  846. };
  847. }
  848. expose(_assert_inherits("assert_inherits"), "assert_inherits");
  849. expose(_assert_inherits("assert_idl_attribute"), "assert_idl_attribute");
  850. function assert_readonly(object, property_name, description)
  851. {
  852. var initial_value = object[property_name];
  853. try {
  854. //Note that this can have side effects in the case where
  855. //the property has PutForwards
  856. object[property_name] = initial_value + "a"; //XXX use some other value here?
  857. assert(same_value(object[property_name], initial_value),
  858. "assert_readonly", description,
  859. "changing property ${p} succeeded",
  860. {p:property_name});
  861. }
  862. finally
  863. {
  864. object[property_name] = initial_value;
  865. }
  866. };
  867. expose(assert_readonly, "assert_readonly");
  868. function assert_throws(code, func, description)
  869. {
  870. try
  871. {
  872. func.call(this);
  873. assert(false, "assert_throws", description,
  874. "${func} did not throw", {func:func});
  875. }
  876. catch(e)
  877. {
  878. if (e instanceof AssertionError) {
  879. throw(e);
  880. }
  881. if (code === null)
  882. {
  883. return;
  884. }
  885. if (typeof code === "object")
  886. {
  887. assert(typeof e == "object" && "name" in e && e.name == code.name,
  888. "assert_throws", description,
  889. "${func} threw ${actual} (${actual_name}) expected ${expected} (${expected_name})",
  890. {func:func, actual:e, actual_name:e.name,
  891. expected:code,
  892. expected_name:code.name});
  893. return;
  894. }
  895. var code_name_map = {
  896. INDEX_SIZE_ERR: 'IndexSizeError',
  897. HIERARCHY_REQUEST_ERR: 'HierarchyRequestError',
  898. WRONG_DOCUMENT_ERR: 'WrongDocumentError',
  899. INVALID_CHARACTER_ERR: 'InvalidCharacterError',
  900. NO_MODIFICATION_ALLOWED_ERR: 'NoModificationAllowedError',
  901. NOT_FOUND_ERR: 'NotFoundError',
  902. NOT_SUPPORTED_ERR: 'NotSupportedError',
  903. INVALID_STATE_ERR: 'InvalidStateError',
  904. SYNTAX_ERR: 'SyntaxError',
  905. INVALID_MODIFICATION_ERR: 'InvalidModificationError',
  906. NAMESPACE_ERR: 'NamespaceError',
  907. INVALID_ACCESS_ERR: 'InvalidAccessError',
  908. TYPE_MISMATCH_ERR: 'TypeMismatchError',
  909. SECURITY_ERR: 'SecurityError',
  910. NETWORK_ERR: 'NetworkError',
  911. ABORT_ERR: 'AbortError',
  912. URL_MISMATCH_ERR: 'URLMismatchError',
  913. QUOTA_EXCEEDED_ERR: 'QuotaExceededError',
  914. TIMEOUT_ERR: 'TimeoutError',
  915. INVALID_NODE_TYPE_ERR: 'InvalidNodeTypeError',
  916. DATA_CLONE_ERR: 'DataCloneError'
  917. };
  918. var name = code in code_name_map ? code_name_map[code] : code;
  919. var name_code_map = {
  920. IndexSizeError: 1,
  921. HierarchyRequestError: 3,
  922. WrongDocumentError: 4,
  923. InvalidCharacterError: 5,
  924. NoModificationAllowedError: 7,
  925. NotFoundError: 8,
  926. NotSupportedError: 9,
  927. InvalidStateError: 11,
  928. SyntaxError: 12,
  929. InvalidModificationError: 13,
  930. NamespaceError: 14,
  931. InvalidAccessError: 15,
  932. TypeMismatchError: 17,
  933. SecurityError: 18,
  934. NetworkError: 19,
  935. AbortError: 20,
  936. URLMismatchError: 21,
  937. QuotaExceededError: 22,
  938. TimeoutError: 23,
  939. InvalidNodeTypeError: 24,
  940. DataCloneError: 25,
  941. UnknownError: 0,
  942. ConstraintError: 0,
  943. DataError: 0,
  944. TransactionInactiveError: 0,
  945. ReadOnlyError: 0,
  946. VersionError: 0
  947. };
  948. if (!(name in name_code_map))
  949. {
  950. throw new AssertionError('Test bug: unrecognized DOMException code "' + code + '" passed to assert_throws()');
  951. }
  952. var required_props = { code: name_code_map[name] };
  953. if (required_props.code === 0
  954. || ("name" in e && e.name !== e.name.toUpperCase() && e.name !== "DOMException"))
  955. {
  956. // New style exception: also test the name property.
  957. required_props.name = name;
  958. }
  959. //We'd like to test that e instanceof the appropriate interface,
  960. //but we can't, because we don't know what window it was created
  961. //in. It might be an instanceof the appropriate interface on some
  962. //unknown other window. TODO: Work around this somehow?
  963. assert(typeof e == "object",
  964. "assert_throws", description,
  965. "${func} threw ${e} with type ${type}, not an object",
  966. {func:func, e:e, type:typeof e});
  967. for (var prop in required_props)
  968. {
  969. assert(typeof e == "object" && prop in e && e[prop] == required_props[prop],
  970. "assert_throws", description,
  971. "${func} threw ${e} that is not a DOMException " + code + ": property ${prop} is equal to ${actual}, expected ${expected}",
  972. {func:func, e:e, prop:prop, actual:e[prop], expected:required_props[prop]});
  973. }
  974. }
  975. }
  976. expose(assert_throws, "assert_throws");
  977. function assert_unreached(description) {
  978. assert(false, "assert_unreached", description,
  979. "Reached unreachable code");
  980. }
  981. expose(assert_unreached, "assert_unreached");
  982. function assert_any(assert_func, actual, expected_array)
  983. {
  984. var args = [].slice.call(arguments, 3)
  985. var errors = []
  986. var passed = false;
  987. forEach(expected_array,
  988. function(expected)
  989. {
  990. try {
  991. assert_func.apply(this, [actual, expected].concat(args))
  992. passed = true;
  993. } catch(e) {
  994. errors.push(e.message);
  995. }
  996. });
  997. if (!passed) {
  998. throw new AssertionError(errors.join("\n\n"));
  999. }
  1000. }
  1001. expose(assert_any, "assert_any");
  1002. function Test(name, properties)
  1003. {
  1004. this.name = name;
  1005. this.status = this.NOTRUN;
  1006. this.timeout_id = null;
  1007. this.is_done = false;
  1008. this.properties = properties;
  1009. this.timeout_length = properties.timeout ? properties.timeout : settings.test_timeout;
  1010. this.message = null;
  1011. var this_obj = this;
  1012. this.steps = [];
  1013. tests.push(this);
  1014. }
  1015. Test.statuses = {
  1016. PASS:0,
  1017. FAIL:1,
  1018. TIMEOUT:2,
  1019. NOTRUN:3
  1020. };
  1021. Test.prototype = merge({}, Test.statuses);
  1022. Test.prototype.structured_clone = function()
  1023. {
  1024. if(!this._structured_clone)
  1025. {
  1026. var msg = this.message;
  1027. msg = msg ? String(msg) : msg;
  1028. this._structured_clone = merge({
  1029. name:String(this.name),
  1030. status:this.status,
  1031. message:msg
  1032. }, Test.statuses);
  1033. }
  1034. return this._structured_clone;
  1035. };
  1036. Test.prototype.step = function(func, this_obj)
  1037. {
  1038. //In case the test has already failed
  1039. if (this.status !== this.NOTRUN)
  1040. {
  1041. return;
  1042. }
  1043. tests.started = true;
  1044. if (this.timeout_id === null) {
  1045. this.set_timeout();
  1046. }
  1047. this.steps.push(func);
  1048. if (arguments.length === 1)
  1049. {
  1050. this_obj = this;
  1051. }
  1052. try
  1053. {
  1054. return func.apply(this_obj, Array.prototype.slice.call(arguments, 2));
  1055. }
  1056. catch(e)
  1057. {
  1058. //This can happen if something called synchronously invoked another
  1059. //step
  1060. if (this.status !== this.NOTRUN)
  1061. {
  1062. return;
  1063. }
  1064. this.status = this.FAIL;
  1065. this.message = (typeof e === "object" && e !== null) ? e.message : e;
  1066. if (typeof e.stack != "undefined" && typeof e.message == "string") {
  1067. //Try to make it more informative for some exceptions, at least
  1068. //in Gecko and WebKit. This results in a stack dump instead of
  1069. //just errors like "Cannot read property 'parentNode' of null"
  1070. //or "root is null". Makes it a lot longer, of course.
  1071. this.message += "(stack: " + e.stack + ")";
  1072. }
  1073. this.done();
  1074. if (debug && e.constructor !== AssertionError) {
  1075. throw e;
  1076. }
  1077. }
  1078. };
  1079. Test.prototype.step_func = function(func, this_obj)
  1080. {
  1081. var test_this = this;
  1082. if (arguments.length === 1)
  1083. {
  1084. this_obj = test_this;
  1085. }
  1086. return function()
  1087. {
  1088. test_this.step.apply(test_this, [func, this_obj].concat(
  1089. Array.prototype.slice.call(arguments)));
  1090. };
  1091. };
  1092. Test.prototype.step_func_done = function(func, this_obj)
  1093. {
  1094. var test_this = this;
  1095. if (arguments.length === 1)
  1096. {
  1097. this_obj = test_this;
  1098. }
  1099. return function()
  1100. {
  1101. test_this.step.apply(test_this, [func, this_obj].concat(
  1102. Array.prototype.slice.call(arguments)));
  1103. test_this.done();
  1104. };
  1105. };
  1106. Test.prototype.set_timeout = function()
  1107. {
  1108. var this_obj = this;
  1109. this.timeout_id = setTimeout(function()
  1110. {
  1111. this_obj.timeout();
  1112. }, this.timeout_length);
  1113. };
  1114. Test.prototype.timeout = function()
  1115. {
  1116. this.status = this.TIMEOUT;
  1117. this.timeout_id = null;
  1118. this.message = "Test timed out";
  1119. this.done();
  1120. };
  1121. Test.prototype.done = function()
  1122. {
  1123. if (this.is_done) {
  1124. return;
  1125. }
  1126. clearTimeout(this.timeout_id);
  1127. if (this.status === this.NOTRUN)
  1128. {
  1129. this.status = this.PASS;
  1130. }
  1131. this.is_done = true;
  1132. tests.result(this);
  1133. };
  1134. /*
  1135. * Harness
  1136. */
  1137. function TestsStatus()
  1138. {
  1139. this.status = null;
  1140. this.message = null;
  1141. }
  1142. TestsStatus.statuses = {
  1143. OK:0,
  1144. ERROR:1,
  1145. TIMEOUT:2
  1146. };
  1147. TestsStatus.prototype = merge({}, TestsStatus.statuses);
  1148. TestsStatus.prototype.structured_clone = function()
  1149. {
  1150. if(!this._structured_clone)
  1151. {
  1152. var msg = this.message;
  1153. msg = msg ? String(msg) : msg;
  1154. this._structured_clone = merge({
  1155. status:this.status,
  1156. message:msg
  1157. }, TestsStatus.statuses);
  1158. }
  1159. return this._structured_clone;
  1160. };
  1161. function Tests()
  1162. {
  1163. this.tests = [];
  1164. this.num_pending = 0;
  1165. this.phases = {
  1166. INITIAL:0,
  1167. SETUP:1,
  1168. HAVE_TESTS:2,
  1169. HAVE_RESULTS:3,
  1170. COMPLETE:4
  1171. };
  1172. this.phase = this.phases.INITIAL;
  1173. this.properties = {};
  1174. //All tests can't be done until the load event fires
  1175. this.all_loaded = false;
  1176. this.wait_for_finish = false;
  1177. this.processing_callbacks = false;
  1178. this.timeout_length = settings.timeout;
  1179. this.timeout_id = null;
  1180. this.start_callbacks = [];
  1181. this.test_done_callbacks = [];
  1182. this.all_done_callbacks = [];
  1183. this.status = new TestsStatus();
  1184. var this_obj = this;
  1185. on_event(window, "load",
  1186. function()
  1187. {
  1188. this_obj.all_loaded = true;
  1189. if (this_obj.all_done())
  1190. {
  1191. this_obj.complete();
  1192. }
  1193. });
  1194. this.set_timeout();
  1195. }
  1196. Tests.prototype.setup = function(func, properties)
  1197. {
  1198. if (this.phase >= this.phases.HAVE_RESULTS)
  1199. {
  1200. return;
  1201. }
  1202. if (this.phase < this.phases.SETUP)
  1203. {
  1204. this.phase = this.phases.SETUP;
  1205. }
  1206. for (var p in properties)
  1207. {
  1208. if (properties.hasOwnProperty(p))
  1209. {
  1210. this.properties[p] = properties[p];
  1211. }
  1212. }
  1213. if (properties.timeout)
  1214. {
  1215. this.timeout_length = properties.timeout;
  1216. }
  1217. if (properties.explicit_done)
  1218. {
  1219. this.wait_for_finish = true;
  1220. }
  1221. if (properties.explicit_timeout) {
  1222. this.timeout_length = null;
  1223. }
  1224. if (func)
  1225. {
  1226. try
  1227. {
  1228. func();
  1229. } catch(e)
  1230. {
  1231. this.status.status = this.status.ERROR;
  1232. this.status.message = e;
  1233. };
  1234. }
  1235. this.set_timeout();
  1236. };
  1237. Tests.prototype.set_timeout = function()
  1238. {
  1239. var this_obj = this;
  1240. clearTimeout(this.timeout_id);
  1241. if (this.timeout_length !== null)
  1242. {
  1243. this.timeout_id = setTimeout(function() {
  1244. this_obj.timeout();
  1245. }, this.timeout_length);
  1246. }
  1247. };
  1248. Tests.prototype.timeout = function() {
  1249. this.status.status = this.status.TIMEOUT;
  1250. this.complete();
  1251. };
  1252. Tests.prototype.end_wait = function()
  1253. {
  1254. this.wait_for_finish = false;
  1255. if (this.all_done()) {
  1256. this.complete();
  1257. }
  1258. };
  1259. Tests.prototype.push = function(test)
  1260. {
  1261. if (this.phase < this.phases.HAVE_TESTS) {
  1262. this.start();
  1263. }
  1264. this.num_pending++;
  1265. this.tests.push(test);
  1266. };
  1267. Tests.prototype.all_done = function() {
  1268. return (this.all_loaded && this.num_pending === 0 &&
  1269. !this.wait_for_finish && !this.processing_callbacks);
  1270. };
  1271. Tests.prototype.start = function() {
  1272. this.phase = this.phases.HAVE_TESTS;
  1273. this.notify_start();
  1274. };
  1275. Tests.prototype.notify_start = function() {
  1276. var this_obj = this;
  1277. forEach (this.start_callbacks,
  1278. function(callback)
  1279. {
  1280. callback(this_obj.properties);
  1281. });
  1282. forEach_windows(
  1283. function(w, is_same_origin)
  1284. {
  1285. if(is_same_origin && w.start_callback)
  1286. {
  1287. try
  1288. {
  1289. w.start_callback(this_obj.properties);
  1290. }
  1291. catch(e)
  1292. {
  1293. if (debug)
  1294. {
  1295. throw(e);
  1296. }
  1297. }
  1298. }
  1299. if (supports_post_message(w) && w !== self)
  1300. {
  1301. w.postMessage({
  1302. type: "start",
  1303. properties: this_obj.properties
  1304. }, "*");
  1305. }
  1306. });
  1307. };
  1308. Tests.prototype.result = function(test)
  1309. {
  1310. if (this.phase > this.phases.HAVE_RESULTS)
  1311. {
  1312. return;
  1313. }
  1314. this.phase = this.phases.HAVE_RESULTS;
  1315. this.num_pending--;
  1316. this.notify_result(test);
  1317. };
  1318. Tests.prototype.notify_result = function(test) {
  1319. var this_obj = this;
  1320. this.processing_callbacks = true;
  1321. forEach(this.test_done_callbacks,
  1322. function(callback)
  1323. {
  1324. callback(test, this_obj);
  1325. });
  1326. forEach_windows(
  1327. function(w, is_same_origin)
  1328. {
  1329. if(is_same_origin && w.result_callback)
  1330. {
  1331. try
  1332. {
  1333. w.result_callback(test);
  1334. }
  1335. catch(e)
  1336. {
  1337. if(debug) {
  1338. throw e;
  1339. }
  1340. }
  1341. }
  1342. if (supports_post_message(w) && w !== self)
  1343. {
  1344. w.postMessage({
  1345. type: "result",
  1346. test: test.structured_clone()
  1347. }, "*");
  1348. }
  1349. });
  1350. this.processing_callbacks = false;
  1351. if (this_obj.all_done())
  1352. {
  1353. this_obj.complete();
  1354. }
  1355. };
  1356. Tests.prototype.complete = function() {
  1357. if (this.phase === this.phases.COMPLETE) {
  1358. return;
  1359. }
  1360. this.phase = this.phases.COMPLETE;
  1361. var this_obj = this;
  1362. this.tests.forEach(
  1363. function(x)
  1364. {
  1365. if(x.status === x.NOTRUN)
  1366. {
  1367. this_obj.notify_result(x);
  1368. }
  1369. }
  1370. );
  1371. this.notify_complete();
  1372. };
  1373. Tests.prototype.notify_complete = function()
  1374. {
  1375. clearTimeout(this.timeout_id);
  1376. var this_obj = this;
  1377. var tests = map(this_obj.tests,
  1378. function(test)
  1379. {
  1380. return test.structured_clone();
  1381. });
  1382. if (this.status.status === null)
  1383. {
  1384. this.status.status = this.status.OK;
  1385. }
  1386. forEach (this.all_done_callbacks,
  1387. function(callback)
  1388. {
  1389. callback(this_obj.tests, this_obj.status);
  1390. });
  1391. forEach_windows(
  1392. function(w, is_same_origin)
  1393. {
  1394. if(is_same_origin && w.completion_callback)
  1395. {
  1396. try
  1397. {
  1398. w.completion_callback(this_obj.tests, this_obj.status);
  1399. }
  1400. catch(e)
  1401. {
  1402. if (debug)
  1403. {
  1404. throw e;
  1405. }
  1406. }
  1407. }
  1408. if (supports_post_message(w) && w !== self)
  1409. {
  1410. w.postMessage({
  1411. type: "complete",
  1412. tests: tests,
  1413. status: this_obj.status.structured_clone()
  1414. }, "*");
  1415. }
  1416. });
  1417. };
  1418. var tests = new Tests();
  1419. function timeout() {
  1420. if (tests.timeout_length === null)
  1421. {
  1422. tests.timeout();
  1423. }
  1424. }
  1425. expose(timeout, 'timeout');
  1426. function add_start_callback(callback) {
  1427. tests.start_callbacks.push(callback);
  1428. }
  1429. function add_result_callback(callback)
  1430. {
  1431. tests.test_done_callbacks.push(callback);
  1432. }
  1433. function add_completion_callback(callback)
  1434. {
  1435. tests.all_done_callbacks.push(callback);
  1436. }
  1437. expose(add_start_callback, 'add_start_callback');
  1438. expose(add_result_callback, 'add_result_callback');
  1439. expose(add_completion_callback, 'add_completion_callback');
  1440. /*
  1441. * Output listener
  1442. */
  1443. function Output() {
  1444. this.output_document = document;
  1445. this.output_node = null;
  1446. this.done_count = 0;
  1447. this.enabled = settings.output;
  1448. this.phase = this.INITIAL;
  1449. }
  1450. Output.prototype.INITIAL = 0;
  1451. Output.prototype.STARTED = 1;
  1452. Output.prototype.HAVE_RESULTS = 2;
  1453. Output.prototype.COMPLETE = 3;
  1454. Output.prototype.setup = function(properties) {
  1455. if (this.phase > this.INITIAL) {
  1456. return;
  1457. }
  1458. //If output is disabled in testharnessreport.js the test shouldn't be
  1459. //able to override that
  1460. this.enabled = this.enabled && (properties.hasOwnProperty("output") ?
  1461. properties.output : settings.output);
  1462. };
  1463. Output.prototype.init = function(properties)
  1464. {
  1465. if (this.phase >= this.STARTED) {
  1466. return;
  1467. }
  1468. if (properties.output_document) {
  1469. this.output_document = properties.output_document;
  1470. } else {
  1471. this.output_document = document;
  1472. }
  1473. this.phase = this.STARTED;
  1474. };
  1475. Output.prototype.resolve_log = function()
  1476. {
  1477. var output_document;
  1478. if (typeof this.output_document === "function")
  1479. {
  1480. output_document = this.output_document.apply(undefined);
  1481. } else
  1482. {
  1483. output_document = this.output_document;
  1484. }
  1485. if (!output_document)
  1486. {
  1487. return;
  1488. }
  1489. var node = output_document.getElementById("log");
  1490. if (node)
  1491. {
  1492. this.output_document = output_document;
  1493. this.output_node = node;
  1494. }
  1495. };
  1496. Output.prototype.show_status = function(test)
  1497. {
  1498. if (this.phase < this.STARTED)
  1499. {
  1500. this.init();
  1501. }
  1502. if (!this.enabled)
  1503. {
  1504. return;
  1505. }
  1506. if (this.phase < this.HAVE_RESULTS)
  1507. {
  1508. this.resolve_log();
  1509. this.phase = this.HAVE_RESULTS;
  1510. }
  1511. this.done_count++;
  1512. if (this.output_node)
  1513. {
  1514. if (this.done_count < 100
  1515. || (this.done_count < 1000 && this.done_count % 100 == 0)
  1516. || this.done_count % 1000 == 0) {
  1517. this.output_node.textContent = "Running, "
  1518. + this.done_count + " complete, "
  1519. + tests.num_pending + " remain";
  1520. }
  1521. }
  1522. };
  1523. Output.prototype.show_results = function (tests, harness_status)
  1524. {
  1525. if (this.phase >= this.COMPLETE) {
  1526. return;
  1527. }
  1528. if (!this.enabled)
  1529. {
  1530. return;
  1531. }
  1532. if (!this.output_node) {
  1533. this.resolve_log();
  1534. }
  1535. this.phase = this.COMPLETE;
  1536. var log = this.output_node;
  1537. if (!log)
  1538. {
  1539. return;
  1540. }
  1541. var output_document = this.output_document;
  1542. while (log.lastChild)
  1543. {
  1544. log.removeChild(log.lastChild);
  1545. }
  1546. if (script_prefix != null) {
  1547. var stylesheet = output_document.createElementNS(xhtml_ns, "link");
  1548. stylesheet.setAttribute("rel", "stylesheet");
  1549. stylesheet.setAttribute("href", script_prefix + "testharness.css");
  1550. var heads = output_document.getElementsByTagName("head");
  1551. if (heads.length) {
  1552. heads[0].appendChild(stylesheet);
  1553. }
  1554. }
  1555. var status_text = {};
  1556. status_text[Test.prototype.PASS] = "Pass";
  1557. status_text[Test.prototype.FAIL] = "Fail";
  1558. status_text[Test.prototype.TIMEOUT] = "Timeout";
  1559. status_text[Test.prototype.NOTRUN] = "Not Run";
  1560. var status_number = {};
  1561. forEach(tests, function(test) {
  1562. var status = status_text[test.status];
  1563. if (status_number.hasOwnProperty(status))
  1564. {
  1565. status_number[status] += 1;
  1566. } else {
  1567. status_number[status] = 1;
  1568. }
  1569. });
  1570. function status_class(status)
  1571. {
  1572. return status.replace(/\s/g, '').toLowerCase();
  1573. }
  1574. var summary_template = ["section", {"id":"summary"},
  1575. ["h2", {}, "Summary"],
  1576. ["p", {}, "Found ${num_tests} tests"],
  1577. function(vars) {
  1578. var rv = [["div", {}]];
  1579. var i=0;
  1580. while (status_text.hasOwnProperty(i)) {
  1581. if (status_number.hasOwnProperty(status_text[i])) {
  1582. var status = status_text[i];
  1583. rv[0].push(["div", {"class":status_class(status)},
  1584. ["label", {},
  1585. ["input", {type:"checkbox", checked:"checked"}],
  1586. status_number[status] + " " + status]]);
  1587. }
  1588. i++;
  1589. }
  1590. return rv;
  1591. }];
  1592. log.appendChild(render(summary_template, {num_tests:tests.length}, output_document));
  1593. forEach(output_document.querySelectorAll("section#summary label"),
  1594. function(element)
  1595. {
  1596. on_event(element, "click",
  1597. function(e)
  1598. {
  1599. if (output_document.getElementById("results") === null)
  1600. {
  1601. e.preventDefault();
  1602. return;
  1603. }
  1604. var result_class = element.parentNode.getAttribute("class");
  1605. var style_element = output_document.querySelector("style#hide-" + result_class);
  1606. var input_element = element.querySelector("input");
  1607. if (!style_element && !input_element.checked) {
  1608. style_element = output_document.createElementNS(xhtml_ns, "style");
  1609. style_element.id = "hide-" + result_class;
  1610. style_element.textContent = "table#results > tbody > tr."+result_class+"{display:none}";
  1611. output_document.body.appendChild(style_element);
  1612. } else if (style_element && input_element.checked) {
  1613. style_element.parentNode.removeChild(style_element);
  1614. }
  1615. });
  1616. });
  1617. // This use of innerHTML plus manual escaping is not recommended in
  1618. // general, but is necessary here for performance. Using textContent
  1619. // on each individual <td> adds tens of seconds of execution time for
  1620. // large test suites (tens of thousands of tests).
  1621. function escape_html(s)
  1622. {
  1623. return s.replace(/\&/g, "&amp;")
  1624. .replace(/</g, "&lt;")
  1625. .replace(/"/g, "&quot;")
  1626. .replace(/'/g, "&#39;");
  1627. }
  1628. function has_assertions()
  1629. {
  1630. for (var i = 0; i < tests.length; i++) {
  1631. if (tests[i].properties.hasOwnProperty("assert")) {
  1632. return true;
  1633. }
  1634. }
  1635. return false;
  1636. }
  1637. function get_assertion(test)
  1638. {
  1639. if (test.properties.hasOwnProperty("assert")) {
  1640. if (Array.isArray(test.properties.assert)) {
  1641. return test.properties.assert.join(' ');
  1642. }
  1643. return test.properties.assert;
  1644. }
  1645. return '';
  1646. }
  1647. log.appendChild(document.createElementNS(xhtml_ns, "section"));
  1648. var assertions = has_assertions();
  1649. var html = "<h2>Details</h2><table id='results' " + (assertions ? "class='assertions'" : "" ) + ">"
  1650. + "<thead><tr><th>Result</th><th>Test Name</th>"
  1651. + (assertions ? "<th>Assertion</th>" : "")
  1652. + "<th>Message</th></tr></thead>"
  1653. + "<tbody>";
  1654. for (var i = 0; i < tests.length; i++) {
  1655. html += '<tr class="'
  1656. + escape_html(status_class(status_text[tests[i].status]))
  1657. + '"><td>'
  1658. + escape_html(status_text[tests[i].status])
  1659. + "</td><td>"
  1660. + escape_html(tests[i].name)
  1661. + "</td><td>"
  1662. + (assertions ? escape_html(get_assertion(tests[i])) + "</td><td>" : "")
  1663. + escape_html(tests[i].message ? tests[i].message : " ")
  1664. + "</td></tr>";
  1665. }
  1666. html += "</tbody></table>";
  1667. try {
  1668. log.lastChild.innerHTML = html;
  1669. } catch (e) {
  1670. log.appendChild(document.createElementNS(xhtml_ns, "p"))
  1671. .textContent = "Setting innerHTML for the log threw an exception.";
  1672. log.appendChild(document.createElementNS(xhtml_ns, "pre"))
  1673. .textContent = html;
  1674. }
  1675. };
  1676. var output = new Output();
  1677. add_start_callback(function (properties) {output.init(properties);});
  1678. add_result_callback(function (test) {output.show_status(tests);});
  1679. add_completion_callback(function (tests, harness_status) {output.show_results(tests, harness_status);});
  1680. /*
  1681. * Template code
  1682. *
  1683. * A template is just a javascript structure. An element is represented as:
  1684. *
  1685. * [tag_name, {attr_name:attr_value}, child1, child2]
  1686. *
  1687. * the children can either be strings (which act like text nodes), other templates or
  1688. * functions (see below)
  1689. *
  1690. * A text node is represented as
  1691. *
  1692. * ["{text}", value]
  1693. *
  1694. * String values have a simple substitution syntax; ${foo} represents a variable foo.
  1695. *
  1696. * It is possible to embed logic in templates by using a function in a place where a
  1697. * node would usually go. The function must either return part of a template or null.
  1698. *
  1699. * In cases where a set of nodes are required as output rather than a single node
  1700. * with children it is possible to just use a list
  1701. * [node1, node2, node3]
  1702. *
  1703. * Usage:
  1704. *
  1705. * render(template, substitutions) - take a template and an object mapping
  1706. * variable names to parameters and return either a DOM node or a list of DOM nodes
  1707. *
  1708. * substitute(template, substitutions) - take a template and variable mapping object,
  1709. * make the variable substitutions and return the substituted template
  1710. *
  1711. */
  1712. function is_single_node(template)
  1713. {
  1714. return typeof template[0] === "string";
  1715. }
  1716. function substitute(template, substitutions)
  1717. {
  1718. if (typeof template === "function") {
  1719. var replacement = template(substitutions);
  1720. if (replacement)
  1721. {
  1722. var rv = substitute(replacement, substitutions);
  1723. return rv;
  1724. }
  1725. else
  1726. {
  1727. return null;
  1728. }
  1729. }
  1730. else if (is_single_node(template))
  1731. {
  1732. return substitute_single(template, substitutions);
  1733. }
  1734. else
  1735. {
  1736. return filter(map(template, function(x) {
  1737. return substitute(x, substitutions);
  1738. }), function(x) {return x !== null;});
  1739. }
  1740. }
  1741. function substitute_single(template, substitutions)
  1742. {
  1743. var substitution_re = /\${([^ }]*)}/g;
  1744. function do_substitution(input) {
  1745. var components = input.split(substitution_re);
  1746. var rv = [];
  1747. for (var i=0; i<components.length; i+=2)
  1748. {
  1749. rv.push(components[i]);
  1750. if (components[i+1])
  1751. {
  1752. rv.push(String(substitutions[components[i+1]]));
  1753. }
  1754. }
  1755. return rv;
  1756. }
  1757. var rv = [];
  1758. rv.push(do_substitution(String(template[0])).join(""));
  1759. if (template[0] === "{text}") {
  1760. substitute_children(template.slice(1), rv);
  1761. } else {
  1762. substitute_attrs(template[1], rv);
  1763. substitute_children(template.slice(2), rv);
  1764. }
  1765. function substitute_attrs(attrs, rv)
  1766. {
  1767. rv[1] = {};
  1768. for (var name in template[1])
  1769. {
  1770. if (attrs.hasOwnProperty(name))
  1771. {
  1772. var new_name = do_substitution(name).join("");
  1773. var new_value = do_substitution(attrs[name]).join("");
  1774. rv[1][new_name] = new_value;
  1775. };
  1776. }
  1777. }
  1778. function substitute_children(children, rv)
  1779. {
  1780. for (var i=0; i<children.length; i++)
  1781. {
  1782. if (children[i] instanceof Object) {
  1783. var replacement = substitute(children[i], substitutions);
  1784. if (replacement !== null)
  1785. {
  1786. if (is_single_node(replacement))
  1787. {
  1788. rv.push(replacement);
  1789. }
  1790. else
  1791. {
  1792. extend(rv, replacement);
  1793. }
  1794. }
  1795. }
  1796. else
  1797. {
  1798. extend(rv, do_substitution(String(children[i])));
  1799. }
  1800. }
  1801. return rv;
  1802. }
  1803. return rv;
  1804. }
  1805. function make_dom_single(template, doc)
  1806. {
  1807. var output_document = doc || document;
  1808. if (template[0] === "{text}")
  1809. {
  1810. var element = output_document.createTextNode("");
  1811. for (var i=1; i<template.length; i++)
  1812. {
  1813. element.data += template[i];
  1814. }
  1815. }
  1816. else
  1817. {
  1818. var element = output_document.createElementNS(xhtml_ns, template[0]);
  1819. for (var name in template[1]) {
  1820. if (template[1].hasOwnProperty(name))
  1821. {
  1822. element.setAttribute(name, template[1][name]);
  1823. }
  1824. }
  1825. for (var i=2; i<template.length; i++)
  1826. {
  1827. if (template[i] instanceof Object)
  1828. {
  1829. var sub_element = make_dom(template[i]);
  1830. element.appendChild(sub_element);
  1831. }
  1832. else
  1833. {
  1834. var text_node = output_document.createTextNode(template[i]);
  1835. element.appendChild(text_node);
  1836. }
  1837. }
  1838. }
  1839. return element;
  1840. }
  1841. function make_dom(template, substitutions, output_document)
  1842. {
  1843. if (is_single_node(template))
  1844. {
  1845. return make_dom_single(template, output_document);
  1846. }
  1847. else
  1848. {
  1849. return map(template, function(x) {
  1850. return make_dom_single(x, output_document);
  1851. });
  1852. }
  1853. }
  1854. function render(template, substitutions, output_document)
  1855. {
  1856. return make_dom(substitute(template, substitutions), output_document);
  1857. }
  1858. /*
  1859. * Utility funcions
  1860. */
  1861. function assert(expected_true, function_name, description, error, substitutions)
  1862. {
  1863. if (expected_true !== true)
  1864. {
  1865. throw new AssertionError(make_message(function_name, description,
  1866. error, substitutions));
  1867. }
  1868. }
  1869. function AssertionError(message)
  1870. {
  1871. this.message = message;
  1872. }
  1873. function make_message(function_name, description, error, substitutions)
  1874. {
  1875. for (var p in substitutions) {
  1876. if (substitutions.hasOwnProperty(p)) {
  1877. substitutions[p] = format_value(substitutions[p]);
  1878. }
  1879. }
  1880. var node_form = substitute(["{text}", "${function_name}: ${description}" + error],
  1881. merge({function_name:function_name,
  1882. description:(description?description + " ":"")},
  1883. substitutions));
  1884. return node_form.slice(1).join("");
  1885. }
  1886. function filter(array, callable, thisObj) {
  1887. var rv = [];
  1888. for (var i=0; i<array.length; i++)
  1889. {
  1890. if (array.hasOwnProperty(i))
  1891. {
  1892. var pass = callable.call(thisObj, array[i], i, array);
  1893. if (pass) {
  1894. rv.push(array[i]);
  1895. }
  1896. }
  1897. }
  1898. return rv;
  1899. }
  1900. function map(array, callable, thisObj)
  1901. {
  1902. var rv = [];
  1903. rv.length = array.length;
  1904. for (var i=0; i<array.length; i++)
  1905. {
  1906. if (array.hasOwnProperty(i))
  1907. {
  1908. rv[i] = callable.call(thisObj, array[i], i, array);
  1909. }
  1910. }
  1911. return rv;
  1912. }
  1913. function extend(array, items)
  1914. {
  1915. Array.prototype.push.apply(array, items);
  1916. }
  1917. function forEach (array, callback, thisObj)
  1918. {
  1919. for (var i=0; i<array.length; i++)
  1920. {
  1921. if (array.hasOwnProperty(i))
  1922. {
  1923. callback.call(thisObj, array[i], i, array);
  1924. }
  1925. }
  1926. }
  1927. function merge(a,b)
  1928. {
  1929. var rv = {};
  1930. var p;
  1931. for (p in a)
  1932. {
  1933. rv[p] = a[p];
  1934. }
  1935. for (p in b) {
  1936. rv[p] = b[p];
  1937. }
  1938. return rv;
  1939. }
  1940. function expose(object, name)
  1941. {
  1942. var components = name.split(".");
  1943. var target = window;
  1944. for (var i=0; i<components.length - 1; i++)
  1945. {
  1946. if (!(components[i] in target))
  1947. {
  1948. target[components[i]] = {};
  1949. }
  1950. target = target[components[i]];
  1951. }
  1952. target[components[components.length - 1]] = object;
  1953. }
  1954. function forEach_windows(callback) {
  1955. // Iterate of the the windows [self ... top, opener]. The callback is passed
  1956. // two objects, the first one is the windows object itself, the second one
  1957. // is a boolean indicating whether or not its on the same origin as the
  1958. // current window.
  1959. var cache = forEach_windows.result_cache;
  1960. if (!cache) {
  1961. cache = [[self, true]];
  1962. var w = self;
  1963. var i = 0;
  1964. var so;
  1965. var origins = location.ancestorOrigins;
  1966. while (w != w.parent)
  1967. {
  1968. w = w.parent;
  1969. // In WebKit, calls to parent windows' properties that aren't on the same
  1970. // origin cause an error message to be displayed in the error console but
  1971. // don't throw an exception. This is a deviation from the current HTML5
  1972. // spec. See: https://bugs.webkit.org/show_bug.cgi?id=43504
  1973. // The problem with WebKit's behavior is that it pollutes the error console
  1974. // with error messages that can't be caught.
  1975. //
  1976. // This issue can be mitigated by relying on the (for now) proprietary
  1977. // `location.ancestorOrigins` property which returns an ordered list of
  1978. // the origins of enclosing windows. See:
  1979. // http://trac.webkit.org/changeset/113945.
  1980. if(origins) {
  1981. so = (location.origin == origins[i]);
  1982. }
  1983. else
  1984. {
  1985. so = is_same_origin(w);
  1986. }
  1987. cache.push([w, so]);
  1988. i++;
  1989. }
  1990. w = window.opener;
  1991. if(w)
  1992. {
  1993. // window.opener isn't included in the `location.ancestorOrigins` prop.
  1994. // We'll just have to deal with a simple check and an error msg on WebKit
  1995. // browsers in this case.
  1996. cache.push([w, is_same_origin(w)]);
  1997. }
  1998. forEach_windows.result_cache = cache;
  1999. }
  2000. forEach(cache,
  2001. function(a)
  2002. {
  2003. callback.apply(null, a);
  2004. });
  2005. }
  2006. function is_same_origin(w) {
  2007. try {
  2008. 'random_prop' in w;
  2009. return true;
  2010. } catch(e) {
  2011. return false;
  2012. }
  2013. }
  2014. function supports_post_message(w)
  2015. {
  2016. var supports;
  2017. var type;
  2018. // Given IE implements postMessage across nested iframes but not across
  2019. // windows or tabs, you can't infer cross-origin communication from the presence
  2020. // of postMessage on the current window object only.
  2021. //
  2022. // Touching the postMessage prop on a window can throw if the window is
  2023. // not from the same origin AND post message is not supported in that
  2024. // browser. So just doing an existence test here won't do, you also need
  2025. // to wrap it in a try..cacth block.
  2026. try
  2027. {
  2028. type = typeof w.postMessage;
  2029. if (type === "function")
  2030. {
  2031. supports = true;
  2032. }
  2033. // IE8 supports postMessage, but implements it as a host object which
  2034. // returns "object" as its `typeof`.
  2035. else if (type === "object")
  2036. {
  2037. supports = true;
  2038. }
  2039. // This is the case where postMessage isn't supported AND accessing a
  2040. // window property across origins does NOT throw (e.g. old Safari browser).
  2041. else
  2042. {
  2043. supports = false;
  2044. }
  2045. }
  2046. catch(e) {
  2047. // This is the case where postMessage isn't supported AND accessing a
  2048. // window property across origins throws (e.g. old Firefox browser).
  2049. supports = false;
  2050. }
  2051. return supports;
  2052. }
  2053. })();
  2054. // vim: set expandtab shiftwidth=4 tabstop=4: