Bug 1842773 - Part 5: Add ArrayBuffer.prototype.{maxByteLength,resizable} getters...
[gecko.git] / testing / mochitest / tests / SimpleTest / SimpleTest.js
bloba237c25103d222857a50a3be26281cc8a5fd7186
1 /* -*- js-indent-level: 2; tab-width: 2; indent-tabs-mode: nil -*- */
2 /* vim:set ts=2 sw=2 sts=2 et: */
4 // Generally gTestPath should be set by the harness.
5 /* global gTestPath */
7 /**
8  * SimpleTest framework object.
9  * @class
10  */
11 var SimpleTest = {};
12 var parentRunner = null;
14 // Using a try/catch rather than SpecialPowers.Cu.isRemoteProxy() because
15 // it doesn't cover the case where an iframe is xorigin but fission is
16 // not enabled.
17 let isSameOrigin = function (w) {
18   try {
19     w.top.TestRunner;
20   } catch (e) {
21     if (e instanceof DOMException) {
22       return false;
23     }
24   }
25   return true;
27 let isXOrigin = !isSameOrigin(window);
29 // Note: duplicated in browser-test.js . See also bug 1820150.
30 function isErrorOrException(err) {
31   // It'd be nice if we had either `Error.isError(err)` or `Error.isInstance(err)`
32   // but we don't, so do it ourselves:
33   if (!err) {
34     return false;
35   }
36   if (err instanceof SpecialPowers.Ci.nsIException) {
37     return true;
38   }
39   try {
40     let glob = SpecialPowers.Cu.getGlobalForObject(err);
41     return err instanceof glob.Error;
42   } catch {
43     // getGlobalForObject can be upset if it doesn't get passed an object.
44     // Just do a standard instanceof check using this global and cross fingers:
45   }
46   return err instanceof Error;
49 // In normal test runs, the window that has a TestRunner in its parent is
50 // the primary window.  In single test runs, if there is no parent and there
51 // is no opener then it is the primary window.
52 var isSingleTestRun =
53   parent == window &&
54   !(opener || (window.arguments && window.arguments[0].SimpleTest));
55 try {
56   var isPrimaryTestWindow =
57     (isXOrigin && parent != window && parent == top) ||
58     (!isXOrigin && (!!parent.TestRunner || isSingleTestRun));
59 } catch (e) {
60   dump(
61     "TEST-UNEXPECTED-FAIL, Exception caught: " +
62       e.message +
63       ", at: " +
64       e.fileName +
65       " (" +
66       e.lineNumber +
67       "), location: " +
68       window.location.href +
69       "\n"
70   );
73 let xOriginRunner = {
74   init(harnessWindow) {
75     this.harnessWindow = harnessWindow;
76     let url = new URL(document.URL);
77     this.testFile = url.pathname;
78     this.showTestReport = url.searchParams.get("showTestReport") == "true";
79     this.expected = url.searchParams.get("expected");
80   },
81   callHarnessMethod(applyOn, command, ...params) {
82     // Message handled by xOriginTestRunnerHandler in TestRunner.js
83     this.harnessWindow.postMessage(
84       {
85         harnessType: "SimpleTest",
86         applyOn,
87         command,
88         params,
89       },
90       "*"
91     );
92   },
93   getParameterInfo() {
94     let url = new URL(document.URL);
95     return {
96       currentTestURL: url.searchParams.get("currentTestURL"),
97       testRoot: url.searchParams.get("testRoot"),
98     };
99   },
100   addFailedTest(test) {
101     this.callHarnessMethod("runner", "addFailedTest", test);
102   },
103   expectAssertions(min, max) {
104     this.callHarnessMethod("runner", "expectAssertions", min, max);
105   },
106   expectChildProcessCrash() {
107     this.callHarnessMethod("runner", "expectChildProcessCrash");
108   },
109   requestLongerTimeout(factor) {
110     this.callHarnessMethod("runner", "requestLongerTimeout", factor);
111   },
112   _lastAssertionCount: 0,
113   testFinished(tests) {
114     var newAssertionCount = SpecialPowers.assertionCount();
115     var numAsserts = newAssertionCount - this._lastAssertionCount;
116     this._lastAssertionCount = newAssertionCount;
117     this.callHarnessMethod("runner", "addAssertionCount", numAsserts);
118     this.callHarnessMethod("runner", "testFinished", tests);
119   },
120   structuredLogger: {
121     info(msg) {
122       xOriginRunner.callHarnessMethod("logger", "structuredLogger.info", msg);
123     },
124     warning(msg) {
125       xOriginRunner.callHarnessMethod(
126         "logger",
127         "structuredLogger.warning",
128         msg
129       );
130     },
131     error(msg) {
132       xOriginRunner.callHarnessMethod("logger", "structuredLogger.error", msg);
133     },
134     activateBuffering() {
135       xOriginRunner.callHarnessMethod(
136         "logger",
137         "structuredLogger.activateBuffering"
138       );
139     },
140     deactivateBuffering() {
141       xOriginRunner.callHarnessMethod(
142         "logger",
143         "structuredLogger.deactivateBuffering"
144       );
145     },
146     testStatus(url, subtest, status, expected, diagnostic, stack) {
147       xOriginRunner.callHarnessMethod(
148         "logger",
149         "structuredLogger.testStatus",
150         url,
151         subtest,
152         status,
153         expected,
154         diagnostic,
155         stack
156       );
157     },
158   },
161 // Finds the TestRunner for this test run and the SpecialPowers object (in
162 // case it is not defined) from a parent/opener window.
164 // Finding the SpecialPowers object is needed when we have ChromePowers in
165 // harness.xhtml and we need SpecialPowers in the iframe, and also for tests
166 // like test_focus.xhtml where we open a window which opens another window which
167 // includes SimpleTest.js.
168 (function () {
169   function ancestor(w) {
170     return w.parent != w
171       ? w.parent
172       : w.opener ||
173           (!isXOrigin &&
174             w.arguments &&
175             SpecialPowers.wrap(Window).isInstance(w.arguments[0]) &&
176             w.arguments[0]);
177   }
179   var w = ancestor(window);
180   while (w && !parentRunner) {
181     isXOrigin = !isSameOrigin(w);
183     if (isXOrigin) {
184       if (w.parent != w) {
185         w = w.top;
186       }
187       xOriginRunner.init(w);
188       parentRunner = xOriginRunner;
189     }
191     if (!parentRunner) {
192       parentRunner = w.TestRunner;
193       if (!parentRunner && w.wrappedJSObject) {
194         parentRunner = w.wrappedJSObject.TestRunner;
195       }
196     }
197     w = ancestor(w);
198   }
200   if (parentRunner) {
201     SimpleTest.harnessParameters = parentRunner.getParameterInfo();
202   }
203 })();
205 /* Helper functions pulled out of various MochiKit modules */
206 if (typeof repr == "undefined") {
207   this.repr = function repr(o) {
208     if (typeof o == "undefined") {
209       return "undefined";
210     } else if (o === null) {
211       return "null";
212     }
213     try {
214       if (typeof o.__repr__ == "function") {
215         return o.__repr__();
216       } else if (typeof o.repr == "function" && o.repr != repr) {
217         return o.repr();
218       }
219     } catch (e) {}
220     try {
221       if (
222         typeof o.NAME == "string" &&
223         (o.toString == Function.prototype.toString ||
224           o.toString == Object.prototype.toString)
225       ) {
226         return o.NAME;
227       }
228     } catch (e) {}
229     var ostring;
230     try {
231       if (o === 0) {
232         ostring = 1 / o > 0 ? "+0" : "-0";
233       } else if (typeof o === "string") {
234         ostring = JSON.stringify(o);
235       } else if (Array.isArray(o)) {
236         ostring = "[" + o.map(val => repr(val)).join(", ") + "]";
237       } else {
238         ostring = o + "";
239       }
240     } catch (e) {
241       return "[" + typeof o + "]";
242     }
243     if (typeof o == "function") {
244       o = ostring.replace(/^\s+/, "");
245       var idx = o.indexOf("{");
246       if (idx != -1) {
247         o = o.substr(0, idx) + "{...}";
248       }
249     }
250     return ostring;
251   };
254 /* This returns a function that applies the previously given parameters.
255  * This is used by SimpleTest.showReport
256  */
257 if (typeof partial == "undefined") {
258   this.partial = function (func) {
259     var args = [];
260     for (let i = 1; i < arguments.length; i++) {
261       args.push(arguments[i]);
262     }
263     return function () {
264       if (arguments.length) {
265         for (let i = 1; i < arguments.length; i++) {
266           args.push(arguments[i]);
267         }
268       }
269       func(args);
270     };
271   };
274 if (typeof getElement == "undefined") {
275   this.getElement = function (id) {
276     return typeof id == "string" ? document.getElementById(id) : id;
277   };
278   this.$ = this.getElement;
281 SimpleTest._newCallStack = function (path) {
282   var rval = function callStackHandler() {
283     var callStack = callStackHandler.callStack;
284     for (var i = 0; i < callStack.length; i++) {
285       if (callStack[i].apply(this, arguments) === false) {
286         break;
287       }
288     }
289     try {
290       this[path] = null;
291     } catch (e) {
292       // pass
293     }
294   };
295   rval.callStack = [];
296   return rval;
299 if (typeof addLoadEvent == "undefined") {
300   this.addLoadEvent = function (func) {
301     var existing = window.onload;
302     var regfunc = existing;
303     if (
304       !(
305         typeof existing == "function" &&
306         typeof existing.callStack == "object" &&
307         existing.callStack !== null
308       )
309     ) {
310       regfunc = SimpleTest._newCallStack("onload");
311       if (typeof existing == "function") {
312         regfunc.callStack.push(existing);
313       }
314       window.onload = regfunc;
315     }
316     regfunc.callStack.push(func);
317   };
320 function createEl(type, attrs, html) {
321   //use createElementNS so the xul/xhtml tests have no issues
322   var el;
323   if (!document.body) {
324     el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
325   } else {
326     el = document.createElement(type);
327   }
328   if (attrs !== null && attrs !== undefined) {
329     for (var k in attrs) {
330       el.setAttribute(k, attrs[k]);
331     }
332   }
333   if (html !== null && html !== undefined) {
334     el.appendChild(document.createTextNode(html));
335   }
336   return el;
339 /* lots of tests use this as a helper to get css properties */
340 if (typeof computedStyle == "undefined") {
341   this.computedStyle = function (elem, cssProperty) {
342     elem = getElement(elem);
343     if (elem.currentStyle) {
344       return elem.currentStyle[cssProperty];
345     }
346     if (typeof document.defaultView == "undefined" || document === null) {
347       return undefined;
348     }
349     var style = document.defaultView.getComputedStyle(elem);
350     if (typeof style == "undefined" || style === null) {
351       return undefined;
352     }
354     var selectorCase = cssProperty.replace(/([A-Z])/g, "-$1").toLowerCase();
356     return style.getPropertyValue(selectorCase);
357   };
360 SimpleTest._tests = [];
361 SimpleTest._stopOnLoad = true;
362 SimpleTest._cleanupFunctions = [];
363 SimpleTest._taskCleanupFunctions = [];
364 SimpleTest._currentTask = null;
365 SimpleTest._timeoutFunctions = [];
366 SimpleTest._inChaosMode = false;
367 // When using failure pattern file to filter unexpected issues,
368 // SimpleTest.expected would be an array of [pattern, expected count],
369 // and SimpleTest.num_failed would be an array of actual counts which
370 // has the same length as SimpleTest.expected.
371 SimpleTest.expected = "pass";
372 SimpleTest.num_failed = 0;
374 SpecialPowers.setAsDefaultAssertHandler();
376 function usesFailurePatterns() {
377   return Array.isArray(SimpleTest.expected);
381  * Checks whether there is any failure pattern matches the given error
382  * message, and if found, bumps the counter of the failure pattern.
384  * @return {boolean} Whether a matched failure pattern is found.
385  */
386 function recordIfMatchesFailurePattern(name, diag) {
387   let index = SimpleTest.expected.findIndex(([pat, count]) => {
388     return (
389       pat == null ||
390       (typeof name == "string" && name.includes(pat)) ||
391       (typeof diag == "string" && diag.includes(pat))
392     );
393   });
394   if (index >= 0) {
395     SimpleTest.num_failed[index]++;
396     return true;
397   }
398   return false;
401 SimpleTest.setExpected = function () {
402   if (!parentRunner) {
403     return;
404   }
405   if (!Array.isArray(parentRunner.expected)) {
406     SimpleTest.expected = parentRunner.expected;
407   } else {
408     // Assertions are checked by the runner.
409     SimpleTest.expected = parentRunner.expected.filter(
410       ([pat]) => pat != "ASSERTION"
411     );
412     SimpleTest.num_failed = new Array(SimpleTest.expected.length);
413     SimpleTest.num_failed.fill(0);
414   }
416 SimpleTest.setExpected();
419  * Something like assert.
420  **/
421 SimpleTest.ok = function (condition, name) {
422   if (arguments.length > 2) {
423     const diag = "Too many arguments passed to `ok(condition, name)`";
424     SimpleTest.record(false, name, diag);
425   } else {
426     SimpleTest.record(condition, name);
427   }
430 SimpleTest.record = function (condition, name, diag, stack, expected) {
431   var test = { result: !!condition, name, diag };
432   let successInfo;
433   let failureInfo;
434   if (SimpleTest.expected == "fail") {
435     if (!test.result) {
436       SimpleTest.num_failed++;
437       test.result = true;
438     }
439     successInfo = {
440       status: "PASS",
441       expected: "PASS",
442       message: "TEST-PASS",
443     };
444     failureInfo = {
445       status: "FAIL",
446       expected: "FAIL",
447       message: "TEST-KNOWN-FAIL",
448     };
449   } else if (!test.result && usesFailurePatterns()) {
450     if (recordIfMatchesFailurePattern(name, diag)) {
451       test.result = true;
452       // Add a mark for unexpected failures suppressed by failure pattern.
453       name = "[suppressed] " + name;
454     }
455     successInfo = {
456       status: "FAIL",
457       expected: "FAIL",
458       message: "TEST-KNOWN-FAIL",
459     };
460     failureInfo = {
461       status: "FAIL",
462       expected: "PASS",
463       message: "TEST-UNEXPECTED-FAIL",
464     };
465   } else if (expected == "fail") {
466     successInfo = {
467       status: "PASS",
468       expected: "FAIL",
469       message: "TEST-UNEXPECTED-PASS",
470     };
471     failureInfo = {
472       status: "FAIL",
473       expected: "FAIL",
474       message: "TEST-KNOWN-FAIL",
475     };
476   } else {
477     successInfo = {
478       status: "PASS",
479       expected: "PASS",
480       message: "TEST-PASS",
481     };
482     failureInfo = {
483       status: "FAIL",
484       expected: "PASS",
485       message: "TEST-UNEXPECTED-FAIL",
486     };
487   }
489   if (condition) {
490     stack = null;
491   } else if (!stack) {
492     stack = new Error().stack
493       .replace(/^(.*@)http:\/\/mochi.test:8888\/tests\//gm, "    $1")
494       .split("\n");
495     stack.splice(0, 1);
496     stack = stack.join("\n");
497   }
498   SimpleTest._logResult(test, successInfo, failureInfo, stack);
499   SimpleTest._tests.push(test);
503  * Roughly equivalent to ok(Object.is(a, b), name)
504  **/
505 SimpleTest.is = function (a, b, name) {
506   // Be lazy and use Object.is til we want to test a browser without it.
507   var pass = Object.is(a, b);
508   var diag = pass ? "" : "got " + repr(a) + ", expected " + repr(b);
509   SimpleTest.record(pass, name, diag);
512 SimpleTest.isfuzzy = function (a, b, epsilon, name) {
513   var pass = a >= b - epsilon && a <= b + epsilon;
514   var diag = pass
515     ? ""
516     : "got " +
517       repr(a) +
518       ", expected " +
519       repr(b) +
520       " epsilon: +/- " +
521       repr(epsilon);
522   SimpleTest.record(pass, name, diag);
525 SimpleTest.isnot = function (a, b, name) {
526   var pass = !Object.is(a, b);
527   var diag = pass ? "" : "didn't expect " + repr(a) + ", but got it";
528   SimpleTest.record(pass, name, diag);
532  * Check that the function call throws an exception.
533  */
534 SimpleTest.doesThrow = function (fn, name) {
535   var gotException = false;
536   try {
537     fn();
538   } catch (ex) {
539     gotException = true;
540   }
541   ok(gotException, name);
544 //  --------------- Test.Builder/Test.More todo() -----------------
546 SimpleTest.todo = function (condition, name, diag) {
547   var test = { result: !!condition, name, diag, todo: true };
548   if (
549     test.result &&
550     usesFailurePatterns() &&
551     recordIfMatchesFailurePattern(name, diag)
552   ) {
553     // Flipping the result to false so we don't get unexpected result. There
554     // is no perfect way here. A known failure can trigger unexpected pass,
555     // in which case, tagging it as KNOWN-FAIL probably makes more sense than
556     // marking it PASS.
557     test.result = false;
558     // Add a mark for unexpected failures suppressed by failure pattern.
559     name = "[suppressed] " + name;
560   }
561   var successInfo = {
562     status: "PASS",
563     expected: "FAIL",
564     message: "TEST-UNEXPECTED-PASS",
565   };
566   var failureInfo = {
567     status: "FAIL",
568     expected: "FAIL",
569     message: "TEST-KNOWN-FAIL",
570   };
571   SimpleTest._logResult(test, successInfo, failureInfo);
572   SimpleTest._tests.push(test);
576  * Returns the absolute URL to a test data file from where tests
577  * are served. i.e. the file doesn't necessarely exists where tests
578  * are executed.
580  * (For android, mochitest are executed on the device, while
581  * all mochitest html (and others) files are served from the test runner
582  * slave)
583  */
584 SimpleTest.getTestFileURL = function (path) {
585   var location = window.location;
586   // Remove mochitest html file name from the path
587   var remotePath = location.pathname.replace(/\/[^\/]+?$/, "");
588   var url = location.origin + remotePath + "/" + path;
589   return url;
592 SimpleTest._getCurrentTestURL = function () {
593   return (
594     (SimpleTest.harnessParameters &&
595       SimpleTest.harnessParameters.currentTestURL) ||
596     (parentRunner && parentRunner.currentTestURL) ||
597     (typeof gTestPath == "string" && gTestPath) ||
598     "unknown test url"
599   );
602 SimpleTest._forceLogMessageOutput = false;
605  * Force all test messages to be displayed.  Only applies for the current test.
606  */
607 SimpleTest.requestCompleteLog = function () {
608   if (!parentRunner || SimpleTest._forceLogMessageOutput) {
609     return;
610   }
612   parentRunner.structuredLogger.deactivateBuffering();
613   SimpleTest._forceLogMessageOutput = true;
615   SimpleTest.registerCleanupFunction(function () {
616     parentRunner.structuredLogger.activateBuffering();
617     SimpleTest._forceLogMessageOutput = false;
618   });
621 SimpleTest._logResult = function (test, passInfo, failInfo, stack) {
622   var url = SimpleTest._getCurrentTestURL();
623   var result = test.result ? passInfo : failInfo;
624   var diagnostic = test.diag || null;
625   // BUGFIX : coercing test.name to a string, because some a11y tests pass an xpconnect object
626   var subtest = test.name ? String(test.name) : null;
627   var isError = !test.result == !test.todo;
629   if (parentRunner) {
630     if (!result.status || !result.expected) {
631       if (diagnostic) {
632         parentRunner.structuredLogger.info(diagnostic);
633       }
634       return;
635     }
637     if (isError) {
638       parentRunner.addFailedTest(url);
639     }
641     parentRunner.structuredLogger.testStatus(
642       url,
643       subtest,
644       result.status,
645       result.expected,
646       diagnostic,
647       stack
648     );
649   } else if (typeof dump === "function") {
650     var diagMessage = test.name + (test.diag ? " - " + test.diag : "");
651     var debugMsg = [result.message, url, diagMessage].join(" | ");
652     dump(debugMsg + "\n");
653   } else {
654     // Non-Mozilla browser?  Just do nothing.
655   }
658 SimpleTest.info = function (name, message) {
659   var log = message ? name + " | " + message : name;
660   if (parentRunner) {
661     parentRunner.structuredLogger.info(log);
662   } else {
663     dump(log + "\n");
664   }
668  * Copies of is and isnot with the call to ok replaced by a call to todo.
669  **/
671 SimpleTest.todo_is = function (a, b, name) {
672   var pass = Object.is(a, b);
673   var diag = pass
674     ? repr(a) + " should equal " + repr(b)
675     : "got " + repr(a) + ", expected " + repr(b);
676   SimpleTest.todo(pass, name, diag);
679 SimpleTest.todo_isnot = function (a, b, name) {
680   var pass = !Object.is(a, b);
681   var diag = pass
682     ? repr(a) + " should not equal " + repr(b)
683     : "didn't expect " + repr(a) + ", but got it";
684   SimpleTest.todo(pass, name, diag);
688  * Makes a test report, returns it as a DIV element.
689  **/
690 SimpleTest.report = function () {
691   var passed = 0;
692   var failed = 0;
693   var todo = 0;
695   var tallyAndCreateDiv = function (test) {
696     var cls, msg, div;
697     var diag = test.diag ? " - " + test.diag : "";
698     if (test.todo && !test.result) {
699       todo++;
700       cls = "test_todo";
701       msg = "todo | " + test.name + diag;
702     } else if (test.result && !test.todo) {
703       passed++;
704       cls = "test_ok";
705       msg = "passed | " + test.name + diag;
706     } else {
707       failed++;
708       cls = "test_not_ok";
709       msg = "failed | " + test.name + diag;
710     }
711     div = createEl("div", { class: cls }, msg);
712     return div;
713   };
714   var results = [];
715   for (var d = 0; d < SimpleTest._tests.length; d++) {
716     results.push(tallyAndCreateDiv(SimpleTest._tests[d]));
717   }
719   var summary_class =
720     // eslint-disable-next-line no-nested-ternary
721     failed != 0 ? "some_fail" : passed == 0 ? "todo_only" : "all_pass";
723   var div1 = createEl("div", { class: "tests_report" });
724   var div2 = createEl("div", { class: "tests_summary " + summary_class });
725   var div3 = createEl("div", { class: "tests_passed" }, "Passed: " + passed);
726   var div4 = createEl("div", { class: "tests_failed" }, "Failed: " + failed);
727   var div5 = createEl("div", { class: "tests_todo" }, "Todo: " + todo);
728   div2.appendChild(div3);
729   div2.appendChild(div4);
730   div2.appendChild(div5);
731   div1.appendChild(div2);
732   for (var t = 0; t < results.length; t++) {
733     //iterate in order
734     div1.appendChild(results[t]);
735   }
736   return div1;
740  * Toggle element visibility
741  **/
742 SimpleTest.toggle = function (el) {
743   if (computedStyle(el, "display") == "block") {
744     el.style.display = "none";
745   } else {
746     el.style.display = "block";
747   }
751  * Toggle visibility for divs with a specific class.
752  **/
753 SimpleTest.toggleByClass = function (cls, evt) {
754   var children = document.getElementsByTagName("div");
755   var elements = [];
756   for (var i = 0; i < children.length; i++) {
757     var child = children[i];
758     var clsName = child.className;
759     if (!clsName) {
760       continue;
761     }
762     var classNames = clsName.split(" ");
763     for (var j = 0; j < classNames.length; j++) {
764       if (classNames[j] == cls) {
765         elements.push(child);
766         break;
767       }
768     }
769   }
770   for (var t = 0; t < elements.length; t++) {
771     //TODO: again, for-in loop over elems seems to break this
772     SimpleTest.toggle(elements[t]);
773   }
774   if (evt) {
775     evt.preventDefault();
776   }
780  * Shows the report in the browser
781  **/
782 SimpleTest.showReport = function () {
783   var togglePassed = createEl("a", { href: "#" }, "Toggle passed checks");
784   var toggleFailed = createEl("a", { href: "#" }, "Toggle failed checks");
785   var toggleTodo = createEl("a", { href: "#" }, "Toggle todo checks");
786   togglePassed.onclick = partial(SimpleTest.toggleByClass, "test_ok");
787   toggleFailed.onclick = partial(SimpleTest.toggleByClass, "test_not_ok");
788   toggleTodo.onclick = partial(SimpleTest.toggleByClass, "test_todo");
789   var body = document.body; // Handles HTML documents
790   if (!body) {
791     // Do the XML thing.
792     body = document.getElementsByTagNameNS(
793       "http://www.w3.org/1999/xhtml",
794       "body"
795     )[0];
796   }
797   var firstChild = body.childNodes[0];
798   var addNode;
799   if (firstChild) {
800     addNode = function (el) {
801       body.insertBefore(el, firstChild);
802     };
803   } else {
804     addNode = function (el) {
805       body.appendChild(el);
806     };
807   }
808   addNode(togglePassed);
809   addNode(createEl("span", null, " "));
810   addNode(toggleFailed);
811   addNode(createEl("span", null, " "));
812   addNode(toggleTodo);
813   addNode(SimpleTest.report());
814   // Add a separator from the test content.
815   addNode(createEl("hr"));
819  * Tells SimpleTest to don't finish the test when the document is loaded,
820  * useful for asynchronous tests.
822  * When SimpleTest.waitForExplicitFinish is called,
823  * explicit SimpleTest.finish() is required.
824  **/
825 SimpleTest.waitForExplicitFinish = function () {
826   SimpleTest._stopOnLoad = false;
830  * Multiply the timeout the parent runner uses for this test by the
831  * given factor.
833  * For example, in a test that may take a long time to complete, using
834  * "SimpleTest.requestLongerTimeout(5)" will give it 5 times as long to
835  * finish.
837  * @param {Number} factor
838  *        The multiplication factor to use on the timeout for this test.
839  */
840 SimpleTest.requestLongerTimeout = function (factor) {
841   if (parentRunner) {
842     parentRunner.requestLongerTimeout(factor);
843   } else {
844     dump(
845       "[SimpleTest.requestLongerTimeout()] ignoring request, maybe you meant to call the global `requestLongerTimeout` instead?\n"
846     );
847   }
851  * Note that the given range of assertions is to be expected.  When
852  * this function is not called, 0 assertions are expected.  When only
853  * one argument is given, that number of assertions are expected.
855  * A test where we expect to have assertions (which should largely be a
856  * transitional mechanism to get assertion counts down from our current
857  * situation) can call the SimpleTest.expectAssertions() function, with
858  * either one or two arguments:  one argument gives an exact number
859  * expected, and two arguments give a range.  For example, a test might do
860  * one of the following:
862  * @example
864  *   // Currently triggers two assertions (bug NNNNNN).
865  *   SimpleTest.expectAssertions(2);
867  *   // Currently triggers one assertion on Mac (bug NNNNNN).
868  *   if (navigator.platform.indexOf("Mac") == 0) {
869  *     SimpleTest.expectAssertions(1);
870  *   }
872  *   // Currently triggers two assertions on all platforms (bug NNNNNN),
873  *   // but intermittently triggers two additional assertions (bug NNNNNN)
874  *   // on Windows.
875  *   if (navigator.platform.indexOf("Win") == 0) {
876  *     SimpleTest.expectAssertions(2, 4);
877  *   } else {
878  *     SimpleTest.expectAssertions(2);
879  *   }
881  *   // Intermittently triggers up to three assertions (bug NNNNNN).
882  *   SimpleTest.expectAssertions(0, 3);
883  */
884 SimpleTest.expectAssertions = function (min, max) {
885   if (parentRunner) {
886     parentRunner.expectAssertions(min, max);
887   }
890 SimpleTest._flakyTimeoutIsOK = false;
891 SimpleTest._originalSetTimeout = window.setTimeout;
892 window.setTimeout = function SimpleTest_setTimeoutShim() {
893   // Don't break tests that are loaded without a parent runner.
894   if (parentRunner) {
895     // Right now, we only enable these checks for mochitest-plain.
896     switch (SimpleTest.harnessParameters.testRoot) {
897       case "browser":
898       case "chrome":
899       case "a11y":
900         break;
901       default:
902         if (
903           !SimpleTest._alreadyFinished &&
904           arguments.length > 1 &&
905           arguments[1] > 0
906         ) {
907           if (SimpleTest._flakyTimeoutIsOK) {
908             SimpleTest.todo(
909               false,
910               "The author of the test has indicated that flaky timeouts are expected.  Reason: " +
911                 SimpleTest._flakyTimeoutReason
912             );
913           } else {
914             SimpleTest.ok(
915               false,
916               "Test attempted to use a flaky timeout value " + arguments[1]
917             );
918           }
919         }
920     }
921   }
922   return SimpleTest._originalSetTimeout.apply(window, arguments);
926  * Request the framework to allow usage of setTimeout(func, timeout)
927  * where ``timeout > 0``.  This is required to note that the author of
928  * the test is aware of the inherent flakiness in the test caused by
929  * that, and asserts that there is no way around using the magic timeout
930  * value number for some reason.
932  * Use of this function is **STRONGLY** discouraged.  Think twice before
933  * using it.  Such magic timeout values could result in intermittent
934  * failures in your test, and are almost never necessary!
936  * @param {String} reason
937  *        A string representation of the reason why the test needs timeouts.
939  */
940 SimpleTest.requestFlakyTimeout = function (reason) {
941   SimpleTest.is(typeof reason, "string", "A valid string reason is expected");
942   SimpleTest.isnot(reason, "", "Reason cannot be empty");
943   SimpleTest._flakyTimeoutIsOK = true;
944   SimpleTest._flakyTimeoutReason = reason;
948  * If the page is not yet loaded, waits for the load event. If the page is
949  * not yet focused, focuses and waits for the window to be focused.
950  * If the current page is 'about:blank', then the page is assumed to not
951  * yet be loaded. Pass true for expectBlankPage to not make this assumption
952  * if you expect a blank page to be present.
954  * The target object should be specified if it is different than 'window'. The
955  * actual focused window may be a descendant window of aObject.
957  * @param {Window|browser|BrowsingContext} [aObject]
958  *        Optional object to be focused, and may be any of:
959  *          window - a window object to focus
960  *          browser - a <browser>/<iframe> element. The top-level window
961  *                    within the frame will be focused.
962  *          browsing context - a browsing context containing a window to focus
963  *        If not specified, defaults to the global 'window'.
964  * @param {boolean} [expectBlankPage=false]
965  *        True if targetWindow.location is 'about:blank'.
966  * @param {boolean} [aBlurSubframe=false]
967  *        If true, and a subframe within the window to focus is focused, blur
968  *        it so that the specified window or browsing context will receive
969  *        focus events.
971  * @returns The browsing context that was focused.
972  */
973 SimpleTest.promiseFocus = async function (
974   aObject,
975   aExpectBlankPage = false,
976   aBlurSubframe = false
977 ) {
978   let browser;
979   let browsingContext;
980   let windowToFocus;
982   if (!aObject) {
983     aObject = window;
984   }
986   async function waitForEvent(aTarget, aEventName) {
987     return new Promise(resolve => {
988       aTarget.addEventListener(aEventName, resolve, {
989         capture: true,
990         once: true,
991       });
992     });
993   }
995   if (SpecialPowers.wrap(Window).isInstance(aObject)) {
996     windowToFocus = aObject;
998     let isBlank = windowToFocus.location.href == "about:blank";
999     if (
1000       aExpectBlankPage != isBlank ||
1001       windowToFocus.document.readyState != "complete"
1002     ) {
1003       info("must wait for load");
1004       await waitForEvent(windowToFocus, "load");
1005     }
1006   } else {
1007     if (SpecialPowers.wrap(Element).isInstance(aObject)) {
1008       // assume this is a browser/iframe element
1009       browsingContext = aObject.browsingContext;
1010     } else {
1011       browsingContext = aObject;
1012     }
1014     browser =
1015       browsingContext == aObject ? aObject.top.embedderElement : aObject;
1016     windowToFocus = browser.ownerGlobal;
1017   }
1019   if (!windowToFocus.document.hasFocus()) {
1020     info("must wait for focus");
1021     let focusPromise = waitForEvent(windowToFocus.document, "focus");
1022     SpecialPowers.focus(windowToFocus);
1023     await focusPromise;
1024   }
1026   if (browser) {
1027     if (windowToFocus.document.activeElement != browser) {
1028       browser.focus();
1029     }
1031     info("must wait for focus in content");
1033     // Make sure that the child process thinks it is focused as well.
1034     await SpecialPowers.ensureFocus(browsingContext, aBlurSubframe);
1035   } else {
1036     if (aBlurSubframe) {
1037       SpecialPowers.clearFocus(windowToFocus);
1038     }
1040     browsingContext = windowToFocus.browsingContext;
1041   }
1043   // Some tests rely on this delay, likely expecting layout or paint to occur.
1044   await new Promise(resolve => {
1045     SimpleTest.executeSoon(resolve);
1046   });
1048   return browsingContext;
1052  * Version of promiseFocus that uses a callback. For compatibility,
1053  * the callback is passed one argument, the window that was focused.
1054  * If the focused window is not in the same process, null is supplied.
1055  */
1056 SimpleTest.waitForFocus = function (callback, aObject, expectBlankPage) {
1057   SimpleTest.promiseFocus(aObject, expectBlankPage).then(focusedBC => {
1058     callback(focusedBC?.window);
1059   });
1061 /* eslint-enable mozilla/use-services */
1063 SimpleTest.stripLinebreaksAndWhitespaceAfterTags = function (aString) {
1064   return aString.replace(/(>\s*(\r\n|\n|\r)*\s*)/gm, ">");
1068  * `navigator.platform` should include this, when the platform is Windows.
1069  */
1070 const kPlatformWindows = "Win";
1073  * See `SimpleTest.waitForClipboard`.
1074  */
1075 const kTextHtmlPrefixClipboardDataWindows =
1076   "<html><body>\n<!--StartFragment-->";
1079  * See `SimpleTest.waitForClipboard`.
1080  */
1081 const kTextHtmlSuffixClipboardDataWindows =
1082   "<!--EndFragment-->\n</body>\n</html>";
1085  * Polls the clipboard waiting for the expected value. A known value different than
1086  * the expected value is put on the clipboard first (and also polled for) so we
1087  * can be sure the value we get isn't just the expected value because it was already
1088  * on the clipboard. This only uses the global clipboard and only for text/plain
1089  * values.
1091  * @param {String|Function} aExpectedStringOrValidatorFn
1092  *        The string value that is expected to be on the clipboard, or a
1093  *        validator function getting expected clipboard data and returning a bool.
1094  *        If you specify string value, line breakers in clipboard are treated
1095  *        as LineFeed.  Therefore, you cannot include CarriageReturn to the
1096  *        string.
1097  *        If you specify string value and expect "text/html" data, this wraps
1098  *        the expected value with `kTextHtmlPrefixClipboardDataWindows` and
1099  *        `kTextHtmlSuffixClipboardDataWindows` only when it runs on Windows
1100  *        because they are appended only by nsDataObj.cpp for Windows.
1101  *        https://searchfox.org/mozilla-central/rev/8f7b017a31326515cb467e69eef1f6c965b4f00e/widget/windows/nsDataObj.cpp#1798-1805,1839-1840,1842
1102  *        Therefore, you can specify selected (copied) HTML data simply on any
1103  *        platforms.
1104  * @param {Function} aSetupFn
1105  *        A function responsible for setting the clipboard to the expected value,
1106  *        called after the known value setting succeeds.
1107  * @param {Function} aSuccessFn
1108  *        A function called when the expected value is found on the clipboard.
1109  * @param {Function} aFailureFn
1110  *        A function called if the expected value isn't found on the clipboard
1111  *        within 5s. It can also be called if the known value can't be found.
1112  * @param {String} [aFlavor="text/plain"]
1113  *        The flavor to look for.
1114  * @param {Number} [aTimeout=5000]
1115  *        The timeout (in milliseconds) to wait for a clipboard change.
1116  * @param {boolean} [aExpectFailure=false]
1117  *        If true, fail if the clipboard contents are modified within the timeout
1118  *        interval defined by aTimeout.  When aExpectFailure is true, the argument
1119  *        aExpectedStringOrValidatorFn must be null, as it won't be used.
1120  * @param {boolean} [aDontInitializeClipboardIfExpectFailure=false]
1121  *        If aExpectFailure and this is set to true, this does NOT initialize
1122  *        clipboard with random data before running aSetupFn.
1123  */
1124 SimpleTest.waitForClipboard = function (
1125   aExpectedStringOrValidatorFn,
1126   aSetupFn,
1127   aSuccessFn,
1128   aFailureFn,
1129   aFlavor,
1130   aTimeout,
1131   aExpectFailure,
1132   aDontInitializeClipboardIfExpectFailure
1133 ) {
1134   let promise = SimpleTest.promiseClipboardChange(
1135     aExpectedStringOrValidatorFn,
1136     aSetupFn,
1137     aFlavor,
1138     aTimeout,
1139     aExpectFailure,
1140     aDontInitializeClipboardIfExpectFailure
1141   );
1142   promise.then(aSuccessFn).catch(aFailureFn);
1146  * Promise-oriented version of waitForClipboard.
1147  */
1148 SimpleTest.promiseClipboardChange = async function (
1149   aExpectedStringOrValidatorFn,
1150   aSetupFn,
1151   aFlavor,
1152   aTimeout,
1153   aExpectFailure,
1154   aDontInitializeClipboardIfExpectFailure
1155 ) {
1156   let requestedFlavor = aFlavor || "text/plain";
1158   // The known value we put on the clipboard before running aSetupFn
1159   let initialVal = "waitForClipboard-known-value-" + Math.random();
1160   let preExpectedVal = initialVal;
1162   let inputValidatorFn;
1163   if (aExpectFailure) {
1164     // If we expect failure, the aExpectedStringOrValidatorFn should be null
1165     if (aExpectedStringOrValidatorFn !== null) {
1166       SimpleTest.ok(
1167         false,
1168         "When expecting failure, aExpectedStringOrValidatorFn must be null"
1169       );
1170     }
1172     inputValidatorFn = function (aData) {
1173       return aData != initialVal;
1174     };
1175     // Build a default validator function for common string input.
1176   } else if (typeof aExpectedStringOrValidatorFn == "string") {
1177     if (aExpectedStringOrValidatorFn.includes("\r")) {
1178       throw new Error(
1179         "Use function instead of string to compare raw line breakers in clipboard"
1180       );
1181     }
1182     if (requestedFlavor === "text/html" && navigator.platform.includes("Win")) {
1183       inputValidatorFn = function (aData) {
1184         return (
1185           aData.replace(/\r\n?/g, "\n") ===
1186           kTextHtmlPrefixClipboardDataWindows +
1187             aExpectedStringOrValidatorFn +
1188             kTextHtmlSuffixClipboardDataWindows
1189         );
1190       };
1191     } else {
1192       inputValidatorFn = function (aData) {
1193         return aData.replace(/\r\n?/g, "\n") === aExpectedStringOrValidatorFn;
1194       };
1195     }
1196   } else {
1197     inputValidatorFn = aExpectedStringOrValidatorFn;
1198   }
1200   let maxPolls = aTimeout ? aTimeout / 100 : 50;
1202   async function putAndVerify(operationFn, validatorFn, flavor, expectFailure) {
1203     await operationFn();
1205     let data;
1206     for (let i = 0; i < maxPolls; i++) {
1207       data = SpecialPowers.getClipboardData(flavor);
1208       if (validatorFn(data)) {
1209         // Don't show the success message when waiting for preExpectedVal
1210         if (preExpectedVal) {
1211           preExpectedVal = null;
1212         } else {
1213           SimpleTest.ok(
1214             !expectFailure,
1215             "Clipboard has the given value: '" + data + "'"
1216           );
1217         }
1219         return data;
1220       }
1222       // Wait 100ms and check again.
1223       await new Promise(resolve => {
1224         SimpleTest._originalSetTimeout.apply(window, [resolve, 100]);
1225       });
1226     }
1228     let errorMsg = `Timed out while polling clipboard for ${
1229       preExpectedVal ? "initialized" : "requested"
1230     } data, got: ${data}`;
1231     SimpleTest.ok(expectFailure, errorMsg);
1232     if (!expectFailure) {
1233       throw new Error(errorMsg);
1234     }
1235     return data;
1236   }
1238   if (!aExpectFailure || !aDontInitializeClipboardIfExpectFailure) {
1239     // First we wait for a known value different from the expected one.
1240     SimpleTest.info(`Initializing clipboard with "${preExpectedVal}"...`);
1241     await putAndVerify(
1242       function () {
1243         SpecialPowers.clipboardCopyString(preExpectedVal);
1244       },
1245       function (aData) {
1246         return aData == preExpectedVal;
1247       },
1248       "text/plain",
1249       false
1250     );
1252     SimpleTest.info(
1253       "Succeeded initializing clipboard, start requested things..."
1254     );
1255   } else {
1256     preExpectedVal = null;
1257   }
1259   return putAndVerify(
1260     aSetupFn,
1261     inputValidatorFn,
1262     requestedFlavor,
1263     aExpectFailure
1264   );
1268  * Wait for a condition for a while (actually up to 3s here).
1270  * @param {Function} aCond
1271  *        A function returns the result of the condition
1272  * @param {Function} aCallback
1273  *        A function called after the condition is passed or timeout.
1274  * @param {String} aErrorMsg
1275  *        The message displayed when the condition failed to pass
1276  *        before timeout.
1277  */
1278 SimpleTest.waitForCondition = function (aCond, aCallback, aErrorMsg) {
1279   this.promiseWaitForCondition(aCond, aErrorMsg).then(() => aCallback());
1281 SimpleTest.promiseWaitForCondition = async function (aCond, aErrorMsg) {
1282   for (let tries = 0; tries < 30; ++tries) {
1283     // Wait 100ms between checks.
1284     await new Promise(resolve => {
1285       SimpleTest._originalSetTimeout.apply(window, [resolve, 100]);
1286     });
1288     let conditionPassed;
1289     try {
1290       conditionPassed = await aCond();
1291     } catch (e) {
1292       ok(false, `${e}\n${e.stack}`);
1293       conditionPassed = false;
1294     }
1295     if (conditionPassed) {
1296       return;
1297     }
1298   }
1299   ok(false, aErrorMsg);
1303  * Executes a function shortly after the call, but lets the caller continue
1304  * working (or finish).
1306  * @param {Function} aFunc
1307  *        Function to execute soon.
1308  */
1309 SimpleTest.executeSoon = function (aFunc) {
1310   if ("SpecialPowers" in window) {
1311     return SpecialPowers.executeSoon(aFunc, window);
1312   }
1313   setTimeout(aFunc, 0);
1314   return null; // Avoid warning.
1318  * Register a cleanup/teardown function (which may be async) to run after all
1319  * tasks have finished, before running the next test. If async (or the function
1320  * returns a promise), the framework will wait for the promise/async function
1321  * to resolve.
1323  * @param {Function} aFunc
1324  *        The cleanup/teardown function to run.
1325  */
1326 SimpleTest.registerCleanupFunction = function (aFunc) {
1327   SimpleTest._cleanupFunctions.push(aFunc);
1331  * Register a cleanup/teardown function (which may be async) to run after the
1332  * current task has finished, before running the next task. If async (or the
1333  * function returns a promise), the framework will wait for the promise/async
1334  * function to resolve.
1336  * @param {Function} aFunc
1337  *        The cleanup/teardown function to run.
1338  */
1339 SimpleTest.registerCurrentTaskCleanupFunction = function (aFunc) {
1340   if (!SimpleTest._currentTask) {
1341     return;
1342   }
1343   SimpleTest._currentTask._cleanupFunctions ||= [];
1344   SimpleTest._currentTask._cleanupFunctions.push(aFunc);
1348  * Register a cleanup/teardown function (which may be async) to run after each
1349  * task has finished, before running the next task. If async (or the
1350  * function returns a promise), the framework will wait for the promise/async
1351  * function to resolve.
1353  * @param {Function} aFunc
1354  *        The cleanup/teardown function to run.
1355  */
1356 SimpleTest.registerTaskCleanupFunction = function (aFunc) {
1357   SimpleTest._taskCleanupFunctions.push(aFunc);
1360 SimpleTest.registerTimeoutFunction = function (aFunc) {
1361   SimpleTest._timeoutFunctions.push(aFunc);
1364 SimpleTest.testInChaosMode = function () {
1365   if (SimpleTest._inChaosMode) {
1366     // It's already enabled for this test, don't enter twice
1367     return;
1368   }
1369   SpecialPowers.DOMWindowUtils.enterChaosMode();
1370   SimpleTest._inChaosMode = true;
1371   // increase timeout here as chaosmode is very slow (i.e. 10x)
1372   // doing 20x as this overwrites anything the tests set
1373   SimpleTest.requestLongerTimeout(20);
1376 SimpleTest.timeout = async function () {
1377   for (const func of SimpleTest._timeoutFunctions) {
1378     await func();
1379   }
1380   SimpleTest._timeoutFunctions = [];
1383 SimpleTest.finishWithFailure = function (msg) {
1384   SimpleTest.ok(false, msg);
1385   SimpleTest.finish();
1389  * Finishes the tests. This is automatically called, except when
1390  * SimpleTest.waitForExplicitFinish() has been invoked.
1391  **/
1392 SimpleTest.finish = function () {
1393   if (SimpleTest._alreadyFinished) {
1394     var err =
1395       "TEST-UNEXPECTED-FAIL | SimpleTest | this test already called finish!";
1396     if (parentRunner) {
1397       parentRunner.structuredLogger.error(err);
1398     } else {
1399       dump(err + "\n");
1400     }
1401   }
1403   if (SimpleTest.expected == "fail" && SimpleTest.num_failed <= 0) {
1404     let msg = "We expected at least one failure";
1405     let test = {
1406       result: false,
1407       name: "fail-if condition in manifest",
1408       diag: msg,
1409     };
1410     let successInfo = {
1411       status: "FAIL",
1412       expected: "FAIL",
1413       message: "TEST-KNOWN-FAIL",
1414     };
1415     let failureInfo = {
1416       status: "PASS",
1417       expected: "FAIL",
1418       message: "TEST-UNEXPECTED-PASS",
1419     };
1420     SimpleTest._logResult(test, successInfo, failureInfo);
1421     SimpleTest._tests.push(test);
1422   } else if (usesFailurePatterns()) {
1423     SimpleTest.expected.forEach(([pat, expected_count], i) => {
1424       let count = SimpleTest.num_failed[i];
1425       let diag;
1426       if (expected_count === null && count == 0) {
1427         diag = "expected some failures but got none";
1428       } else if (expected_count !== null && expected_count != count) {
1429         diag = `expected ${expected_count} failures but got ${count}`;
1430       } else {
1431         return;
1432       }
1433       let name = pat
1434         ? `failure pattern \`${pat}\` in this test`
1435         : "failures in this test";
1436       let test = { result: false, name, diag };
1437       let successInfo = {
1438         status: "PASS",
1439         expected: "PASS",
1440         message: "TEST-PASS",
1441       };
1442       let failureInfo = {
1443         status: "FAIL",
1444         expected: "PASS",
1445         message: "TEST-UNEXPECTED-FAIL",
1446       };
1447       SimpleTest._logResult(test, successInfo, failureInfo);
1448       SimpleTest._tests.push(test);
1449     });
1450   }
1452   SimpleTest._timeoutFunctions = [];
1454   SimpleTest.testsLength = SimpleTest._tests.length;
1456   SimpleTest._alreadyFinished = true;
1458   if (SimpleTest._inChaosMode) {
1459     SpecialPowers.DOMWindowUtils.leaveChaosMode();
1460     SimpleTest._inChaosMode = false;
1461   }
1463   var afterCleanup = async function () {
1464     SpecialPowers.removeFiles();
1466     if (SpecialPowers.DOMWindowUtils.isTestControllingRefreshes) {
1467       SimpleTest.ok(false, "test left refresh driver under test control");
1468       SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
1469     }
1470     if (SimpleTest._expectingUncaughtException) {
1471       SimpleTest.ok(
1472         false,
1473         "expectUncaughtException was called but no uncaught exception was detected!"
1474       );
1475     }
1476     if (!SimpleTest._tests.length) {
1477       SimpleTest.ok(
1478         false,
1479         "[SimpleTest.finish()] No checks actually run. " +
1480           "(You need to call ok(), is(), or similar " +
1481           "functions at least once.  Make sure you use " +
1482           "SimpleTest.waitForExplicitFinish() if you need " +
1483           "it.)"
1484       );
1485     }
1487     let workers = await SpecialPowers.registeredServiceWorkers();
1488     let promise = null;
1489     if (SimpleTest._expectingRegisteredServiceWorker) {
1490       if (workers.length === 0) {
1491         SimpleTest.ok(
1492           false,
1493           "This test is expected to leave a service worker registered"
1494         );
1495       }
1496     } else if (workers.length) {
1497       let FULL_PROFILE_WORKERS_TO_IGNORE = [];
1498       if (parentRunner.conditionedProfile) {
1499         // Full profile has service workers in the profile, without clearing the
1500         // profile service workers will be leftover.  We perform a startsWith
1501         // check below because some origins (s.0cf.io) use a cache-busting query
1502         // parameter.
1503         FULL_PROFILE_WORKERS_TO_IGNORE = [
1504           "https://www.youtube.com/sw.js",
1505           "https://s.0cf.io/sw.js",
1506         ];
1507       } else {
1508         SimpleTest.ok(
1509           false,
1510           "This test left a service worker registered without cleaning it up"
1511         );
1512       }
1514       for (let worker of workers) {
1515         if (
1516           FULL_PROFILE_WORKERS_TO_IGNORE.some(ignoreBase =>
1517             worker.scriptSpec.startsWith(ignoreBase)
1518           )
1519         ) {
1520           continue;
1521         }
1522         SimpleTest.ok(
1523           false,
1524           `Left over worker: ${worker.scriptSpec} (scope: ${worker.scope})`
1525         );
1526       }
1527       promise = SpecialPowers.removeAllServiceWorkerData();
1528     }
1530     // If we want to wait for removeAllServiceWorkerData to finish, above,
1531     // there's a small chance that spinning the event loop could cause
1532     // SpecialPowers and SimpleTest to go away (e.g. if the test did
1533     // document.open). promise being non-null should be rare (a test would
1534     // have had to already fail by leaving a service worker around), so
1535     // limit the chances of the async wait happening to that case.
1536     function finish() {
1537       if (parentRunner) {
1538         /* We're running in an iframe, and the parent has a TestRunner */
1539         parentRunner.testFinished(SimpleTest._tests);
1540       }
1542       if (!parentRunner || parentRunner.showTestReport) {
1543         SpecialPowers.flushPermissions(function () {
1544           SpecialPowers.flushPrefEnv(function () {
1545             SimpleTest.showReport();
1546           });
1547         });
1548       }
1549     }
1551     if (promise) {
1552       promise.then(finish);
1553     } else {
1554       finish();
1555     }
1556   };
1558   var executeCleanupFunction = function () {
1559     var func = SimpleTest._cleanupFunctions.pop();
1561     if (!func) {
1562       afterCleanup();
1563       return;
1564     }
1566     var ret;
1567     try {
1568       ret = func();
1569     } catch (ex) {
1570       SimpleTest.ok(false, "Cleanup function threw exception: " + ex);
1571     }
1573     if (ret && ret.constructor.name == "Promise") {
1574       ret.then(executeCleanupFunction, ex =>
1575         SimpleTest.ok(false, "Cleanup promise rejected: " + ex)
1576       );
1577     } else {
1578       executeCleanupFunction();
1579     }
1580   };
1582   executeCleanupFunction();
1584   SpecialPowers.notifyObservers(null, "test-complete");
1588  * Monitor console output from now until endMonitorConsole is called.
1590  * Expect to receive all console messages described by the elements of
1591  * ``msgs``, an array, in the order listed in ``msgs``; each element is an
1592  * object which may have any number of the following properties:
1594  *   message, errorMessage, sourceName, sourceLine, category: string or regexp
1595  *   lineNumber, columnNumber: number
1596  *   isScriptError, isWarning: boolean
1598  * Strings, numbers, and booleans must compare equal to the named
1599  * property of the Nth console message.  Regexps must match.  Any
1600  * fields present in the message but not in the pattern object are ignored.
1602  * In addition to the above properties, elements in ``msgs`` may have a ``forbid``
1603  * boolean property.  When ``forbid`` is true, a failure is logged each time a
1604  * matching message is received.
1606  * If ``forbidUnexpectedMsgs`` is true, then the messages received in the console
1607  * must exactly match the non-forbidden messages in ``msgs``; for each received
1608  * message not described by the next element in ``msgs``, a failure is logged.  If
1609  * false, then other non-forbidden messages are ignored, but all expected
1610  * messages must still be received.
1612  * After endMonitorConsole is called, ``continuation`` will be called
1613  * asynchronously.  (Normally, you will want to pass ``SimpleTest.finish`` here.)
1615  * It is incorrect to use this function in a test which has not called
1616  * SimpleTest.waitForExplicitFinish.
1617  */
1618 SimpleTest.monitorConsole = function (
1619   continuation,
1620   msgs,
1621   forbidUnexpectedMsgs
1622 ) {
1623   if (SimpleTest._stopOnLoad) {
1624     ok(false, "Console monitoring requires use of waitForExplicitFinish.");
1625   }
1627   function msgMatches(msg, pat) {
1628     for (var k in pat) {
1629       if (!(k in msg)) {
1630         return false;
1631       }
1632       if (pat[k] instanceof RegExp && typeof msg[k] === "string") {
1633         if (!pat[k].test(msg[k])) {
1634           return false;
1635         }
1636       } else if (msg[k] !== pat[k]) {
1637         return false;
1638       }
1639     }
1640     return true;
1641   }
1643   var forbiddenMsgs = [];
1644   var i = 0;
1645   while (i < msgs.length) {
1646     let pat = msgs[i];
1647     if ("forbid" in pat) {
1648       var forbid = pat.forbid;
1649       delete pat.forbid;
1650       if (forbid) {
1651         forbiddenMsgs.push(pat);
1652         msgs.splice(i, 1);
1653         continue;
1654       }
1655     }
1656     i++;
1657   }
1659   var counter = 0;
1660   var assertionLabel = JSON.stringify(msgs);
1661   function listener(msg) {
1662     if (msg.message === "SENTINEL" && !msg.isScriptError) {
1663       is(
1664         counter,
1665         msgs.length,
1666         "monitorConsole | number of messages " + assertionLabel
1667       );
1668       SimpleTest.executeSoon(continuation);
1669       return;
1670     }
1671     for (let pat of forbiddenMsgs) {
1672       if (msgMatches(msg, pat)) {
1673         ok(
1674           false,
1675           "monitorConsole | observed forbidden message " + JSON.stringify(msg)
1676         );
1677         return;
1678       }
1679     }
1680     if (counter >= msgs.length) {
1681       var str = "monitorConsole | extra message | " + JSON.stringify(msg);
1682       if (forbidUnexpectedMsgs) {
1683         ok(false, str);
1684       } else {
1685         info(str);
1686       }
1687       return;
1688     }
1689     var matches = msgMatches(msg, msgs[counter]);
1690     if (forbidUnexpectedMsgs) {
1691       ok(
1692         matches,
1693         "monitorConsole | [" + counter + "] must match " + JSON.stringify(msg)
1694       );
1695     } else {
1696       info(
1697         "monitorConsole | [" +
1698           counter +
1699           "] " +
1700           (matches ? "matched " : "did not match ") +
1701           JSON.stringify(msg)
1702       );
1703     }
1704     if (matches) {
1705       counter++;
1706     }
1707   }
1708   SpecialPowers.registerConsoleListener(listener);
1712  * Stop monitoring console output.
1713  */
1714 SimpleTest.endMonitorConsole = function () {
1715   SpecialPowers.postConsoleSentinel();
1719  * Run ``testfn`` synchronously, and monitor its console output.
1721  * ``msgs`` is handled as described above for monitorConsole.
1723  * After ``testfn`` returns, console monitoring will stop, and ``continuation``
1724  * will be called asynchronously.
1726  */
1727 SimpleTest.expectConsoleMessages = function (testfn, msgs, continuation) {
1728   SimpleTest.monitorConsole(continuation, msgs);
1729   testfn();
1730   SimpleTest.executeSoon(SimpleTest.endMonitorConsole);
1734  * Wrapper around ``expectConsoleMessages`` for the case where the test has
1735  * only one ``testfn`` to run.
1736  */
1737 SimpleTest.runTestExpectingConsoleMessages = function (testfn, msgs) {
1738   SimpleTest.waitForExplicitFinish();
1739   SimpleTest.expectConsoleMessages(testfn, msgs, SimpleTest.finish);
1743  * Indicates to the test framework that the current test expects one or
1744  * more crashes (from plugins or IPC documents), and that the minidumps from
1745  * those crashes should be removed.
1746  */
1747 SimpleTest.expectChildProcessCrash = function () {
1748   if (parentRunner) {
1749     parentRunner.expectChildProcessCrash();
1750   }
1754  * Indicates to the test framework that the next uncaught exception during
1755  * the test is expected, and should not cause a test failure.
1756  */
1757 SimpleTest.expectUncaughtException = function (aExpecting) {
1758   SimpleTest._expectingUncaughtException =
1759     aExpecting === void 0 || !!aExpecting;
1763  * Returns whether the test has indicated that it expects an uncaught exception
1764  * to occur.
1765  */
1766 SimpleTest.isExpectingUncaughtException = function () {
1767   return SimpleTest._expectingUncaughtException;
1771  * Indicates to the test framework that all of the uncaught exceptions
1772  * during the test are known problems that should be fixed in the future,
1773  * but which should not cause the test to fail currently.
1774  */
1775 SimpleTest.ignoreAllUncaughtExceptions = function (aIgnoring) {
1776   SimpleTest._ignoringAllUncaughtExceptions =
1777     aIgnoring === void 0 || !!aIgnoring;
1781  * Returns whether the test has indicated that all uncaught exceptions should be
1782  * ignored.
1783  */
1784 SimpleTest.isIgnoringAllUncaughtExceptions = function () {
1785   return SimpleTest._ignoringAllUncaughtExceptions;
1789  * Indicates to the test framework that this test is expected to leave a
1790  * service worker registered when it finishes.
1791  */
1792 SimpleTest.expectRegisteredServiceWorker = function () {
1793   SimpleTest._expectingRegisteredServiceWorker = true;
1797  * Resets any state this SimpleTest object has.  This is important for
1798  * browser chrome mochitests, which reuse the same SimpleTest object
1799  * across a run.
1800  */
1801 SimpleTest.reset = function () {
1802   SimpleTest._ignoringAllUncaughtExceptions = false;
1803   SimpleTest._expectingUncaughtException = false;
1804   SimpleTest._expectingRegisteredServiceWorker = false;
1805   SimpleTest._bufferedMessages = [];
1808 if (isPrimaryTestWindow) {
1809   addLoadEvent(function () {
1810     if (SimpleTest._stopOnLoad) {
1811       SimpleTest.finish();
1812     }
1813   });
1816 //  --------------- Test.Builder/Test.More isDeeply() -----------------
1818 SimpleTest.DNE = { dne: "Does not exist" };
1819 SimpleTest.LF = "\r\n";
1821 SimpleTest._deepCheck = function (e1, e2, stack, seen) {
1822   var ok = false;
1823   if (Object.is(e1, e2)) {
1824     // Handles identical primitives and references.
1825     ok = true;
1826   } else if (
1827     typeof e1 != "object" ||
1828     typeof e2 != "object" ||
1829     e1 === null ||
1830     e2 === null
1831   ) {
1832     // If either argument is a primitive or function, don't consider the arguments the same.
1833     ok = false;
1834   } else if (e1 == SimpleTest.DNE || e2 == SimpleTest.DNE) {
1835     ok = false;
1836   } else if (SimpleTest.isa(e1, "Array") && SimpleTest.isa(e2, "Array")) {
1837     ok = SimpleTest._eqArray(e1, e2, stack, seen);
1838   } else {
1839     ok = SimpleTest._eqAssoc(e1, e2, stack, seen);
1840   }
1841   return ok;
1844 SimpleTest._eqArray = function (a1, a2, stack, seen) {
1845   // Return if they're the same object.
1846   if (a1 == a2) {
1847     return true;
1848   }
1850   // JavaScript objects have no unique identifiers, so we have to store
1851   // references to them all in an array, and then compare the references
1852   // directly. It's slow, but probably won't be much of an issue in
1853   // practice. Start by making a local copy of the array to as to avoid
1854   // confusing a reference seen more than once (such as [a, a]) for a
1855   // circular reference.
1856   for (var j = 0; j < seen.length; j++) {
1857     if (seen[j][0] == a1) {
1858       return seen[j][1] == a2;
1859     }
1860   }
1862   // If we get here, we haven't seen a1 before, so store it with reference
1863   // to a2.
1864   seen.push([a1, a2]);
1866   var ok = true;
1867   // Only examines enumerable attributes. Only works for numeric arrays!
1868   // Associative arrays return 0. So call _eqAssoc() for them, instead.
1869   var max = Math.max(a1.length, a2.length);
1870   if (max == 0) {
1871     return SimpleTest._eqAssoc(a1, a2, stack, seen);
1872   }
1873   for (var i = 0; i < max; i++) {
1874     var e1 = i < a1.length ? a1[i] : SimpleTest.DNE;
1875     var e2 = i < a2.length ? a2[i] : SimpleTest.DNE;
1876     stack.push({ type: "Array", idx: i, vals: [e1, e2] });
1877     ok = SimpleTest._deepCheck(e1, e2, stack, seen);
1878     if (ok) {
1879       stack.pop();
1880     } else {
1881       break;
1882     }
1883   }
1884   return ok;
1887 SimpleTest._eqAssoc = function (o1, o2, stack, seen) {
1888   // Return if they're the same object.
1889   if (o1 == o2) {
1890     return true;
1891   }
1893   // JavaScript objects have no unique identifiers, so we have to store
1894   // references to them all in an array, and then compare the references
1895   // directly. It's slow, but probably won't be much of an issue in
1896   // practice. Start by making a local copy of the array to as to avoid
1897   // confusing a reference seen more than once (such as [a, a]) for a
1898   // circular reference.
1899   seen = seen.slice(0);
1900   for (let j = 0; j < seen.length; j++) {
1901     if (seen[j][0] == o1) {
1902       return seen[j][1] == o2;
1903     }
1904   }
1906   // If we get here, we haven't seen o1 before, so store it with reference
1907   // to o2.
1908   seen.push([o1, o2]);
1910   // They should be of the same class.
1912   var ok = true;
1913   // Only examines enumerable attributes.
1914   var o1Size = 0;
1915   // eslint-disable-next-line no-unused-vars
1916   for (let i in o1) {
1917     o1Size++;
1918   }
1919   var o2Size = 0;
1920   // eslint-disable-next-line no-unused-vars
1921   for (let i in o2) {
1922     o2Size++;
1923   }
1924   var bigger = o1Size > o2Size ? o1 : o2;
1925   for (let i in bigger) {
1926     var e1 = i in o1 ? o1[i] : SimpleTest.DNE;
1927     var e2 = i in o2 ? o2[i] : SimpleTest.DNE;
1928     stack.push({ type: "Object", idx: i, vals: [e1, e2] });
1929     ok = SimpleTest._deepCheck(e1, e2, stack, seen);
1930     if (ok) {
1931       stack.pop();
1932     } else {
1933       break;
1934     }
1935   }
1936   return ok;
1939 SimpleTest._formatStack = function (stack) {
1940   var variable = "$Foo";
1941   for (let i = 0; i < stack.length; i++) {
1942     var entry = stack[i];
1943     var type = entry.type;
1944     var idx = entry.idx;
1945     if (idx != null) {
1946       if (type == "Array") {
1947         // Numeric array index.
1948         variable += "[" + idx + "]";
1949       } else {
1950         // Associative array index.
1951         idx = idx.replace("'", "\\'");
1952         variable += "['" + idx + "']";
1953       }
1954     }
1955   }
1957   var vals = stack[stack.length - 1].vals.slice(0, 2);
1958   var vars = [
1959     variable.replace("$Foo", "got"),
1960     variable.replace("$Foo", "expected"),
1961   ];
1963   var out = "Structures begin differing at:" + SimpleTest.LF;
1964   for (let i = 0; i < vals.length; i++) {
1965     var val = vals[i];
1966     if (val === SimpleTest.DNE) {
1967       val = "Does not exist";
1968     } else {
1969       val = repr(val);
1970     }
1971     out += vars[i] + " = " + val + SimpleTest.LF;
1972   }
1974   return "    " + out;
1977 SimpleTest.isDeeply = function (it, as, name) {
1978   var stack = [{ vals: [it, as] }];
1979   var seen = [];
1980   if (SimpleTest._deepCheck(it, as, stack, seen)) {
1981     SimpleTest.record(true, name);
1982   } else {
1983     SimpleTest.record(false, name, SimpleTest._formatStack(stack));
1984   }
1987 SimpleTest.typeOf = function (object) {
1988   var c = Object.prototype.toString.apply(object);
1989   var name = c.substring(8, c.length - 1);
1990   if (name != "Object") {
1991     return name;
1992   }
1993   // It may be a non-core class. Try to extract the class name from
1994   // the constructor function. This may not work in all implementations.
1995   if (/function ([^(\s]+)/.test(Function.toString.call(object.constructor))) {
1996     return RegExp.$1;
1997   }
1998   // No idea. :-(
1999   return name;
2002 SimpleTest.isa = function (object, clas) {
2003   return SimpleTest.typeOf(object) == clas;
2006 // Global symbols:
2007 var ok = SimpleTest.ok;
2008 var record = SimpleTest.record;
2009 var is = SimpleTest.is;
2010 var isfuzzy = SimpleTest.isfuzzy;
2011 var isnot = SimpleTest.isnot;
2012 var todo = SimpleTest.todo;
2013 var todo_is = SimpleTest.todo_is;
2014 var todo_isnot = SimpleTest.todo_isnot;
2015 var isDeeply = SimpleTest.isDeeply;
2016 var info = SimpleTest.info;
2018 var gOldOnError = window.onerror;
2019 window.onerror = function simpletestOnerror(
2020   errorMsg,
2021   url,
2022   lineNumber,
2023   columnNumber,
2024   originalException
2025 ) {
2026   // Log the message.
2027   // XXX Chrome mochitests sometimes trigger this window.onerror handler,
2028   // but there are a number of uncaught JS exceptions from those tests.
2029   // For now, for tests that self identify as having unintentional uncaught
2030   // exceptions, just dump it so that the error is visible but doesn't cause
2031   // a test failure.  See bug 652494.
2032   var isExpected = !!SimpleTest._expectingUncaughtException;
2033   var message = (isExpected ? "expected " : "") + "uncaught exception";
2034   var error = errorMsg + " at ";
2035   try {
2036     error += originalException.stack;
2037   } catch (e) {
2038     // At least use the url+line+column we were given
2039     error += url + ":" + lineNumber + ":" + columnNumber;
2040   }
2041   if (!SimpleTest._ignoringAllUncaughtExceptions) {
2042     // Don't log if SimpleTest.finish() is already called, it would cause failures
2043     if (!SimpleTest._alreadyFinished) {
2044       SimpleTest.record(isExpected, message, error);
2045     }
2046     SimpleTest._expectingUncaughtException = false;
2047   } else {
2048     SimpleTest.todo(false, message + ": " + error);
2049   }
2050   // There is no Components.stack.caller to log. (See bug 511888.)
2052   // Call previous handler.
2053   if (gOldOnError) {
2054     try {
2055       // Ignore return value: always run default handler.
2056       gOldOnError(errorMsg, url, lineNumber);
2057     } catch (e) {
2058       // Log the error.
2059       SimpleTest.info("Exception thrown by gOldOnError(): " + e);
2060       // Log its stack.
2061       if (e.stack) {
2062         SimpleTest.info("JavaScript error stack:\n" + e.stack);
2063       }
2064     }
2065   }
2067   if (!SimpleTest._stopOnLoad && !isExpected && !SimpleTest._alreadyFinished) {
2068     // Need to finish() manually here, yet let the test actually end first.
2069     SimpleTest.executeSoon(SimpleTest.finish);
2070   }
2073 // Lifted from dom/media/test/manifest.js
2074 // Make sure to not touch navigator in here, since we want to push prefs that
2075 // will affect the APIs it exposes, but the set of exposed APIs is determined
2076 // when Navigator.prototype is created.  So if we touch navigator before pushing
2077 // the prefs, the APIs it exposes will not take those prefs into account.  We
2078 // work around this by using a navigator object from a different global for our
2079 // UA string testing.
2080 var gAndroidSdk = null;
2081 function getAndroidSdk() {
2082   if (gAndroidSdk === null) {
2083     var iframe = document.documentElement.appendChild(
2084       document.createElement("iframe")
2085     );
2086     iframe.style.display = "none";
2087     var nav = iframe.contentWindow.navigator;
2088     if (
2089       !nav.userAgent.includes("Mobile") &&
2090       !nav.userAgent.includes("Tablet")
2091     ) {
2092       gAndroidSdk = -1;
2093     } else {
2094       // See nsSystemInfo.cpp, the getProperty('version') returns different value
2095       // on each platforms, so we need to distinguish the android platform.
2096       var versionString = nav.userAgent.includes("Android")
2097         ? "version"
2098         : "sdk_version";
2099       gAndroidSdk = SpecialPowers.Services.sysinfo.getProperty(versionString);
2100     }
2101     document.documentElement.removeChild(iframe);
2102   }
2103   return gAndroidSdk;
2106 // add_task(generatorFunction):
2107 // Call `add_task(generatorFunction)` for each separate
2108 // asynchronous task in a mochitest. Tasks are run consecutively.
2109 // Before the first task, `SimpleTest.waitForExplicitFinish()`
2110 // will be called automatically, and after the last task,
2111 // `SimpleTest.finish()` will be called.
2112 var add_task = (function () {
2113   // The list of tasks to run.
2114   var task_list = [];
2115   var run_only_this_task = null;
2117   function isGenerator(value) {
2118     return (
2119       value && typeof value === "object" && typeof value.next === "function"
2120     );
2121   }
2123   // The "add_task" function
2124   return function (generatorFunction, options = { isSetup: false }) {
2125     if (task_list.length === 0) {
2126       // This is the first time add_task has been called.
2127       // First, confirm that SimpleTest is available.
2128       if (!SimpleTest) {
2129         throw new Error("SimpleTest not available.");
2130       }
2131       // Don't stop tests until asynchronous tasks are finished.
2132       SimpleTest.waitForExplicitFinish();
2133       // Because the client is using add_task for this set of tests,
2134       // we need to spawn a "master task" that calls each task in succesion.
2135       // Use setTimeout to ensure the master task runs after the client
2136       // script finishes.
2137       setTimeout(function nextTick() {
2138         // If we are in a HTML document, we should wait for the document
2139         // to be fully loaded.
2140         // These checks ensure that we are in an HTML document without
2141         // throwing TypeError; also I am told that readyState in XUL documents
2142         // are totally bogus so we don't try to do this there.
2143         if (
2144           typeof window !== "undefined" &&
2145           typeof HTMLDocument !== "undefined" &&
2146           window.document instanceof HTMLDocument &&
2147           window.document.readyState !== "complete"
2148         ) {
2149           setTimeout(nextTick);
2150           return;
2151         }
2153         (async () => {
2154           // Allow for a task to be skipped; we need only use the structured logger
2155           // for this, whilst deactivating log buffering to ensure that messages
2156           // are always printed to stdout.
2157           function skipTask(name) {
2158             let logger = parentRunner && parentRunner.structuredLogger;
2159             if (!logger) {
2160               info("add_task | Skipping test " + name);
2161               return;
2162             }
2163             logger.deactivateBuffering();
2164             logger.testStatus(SimpleTest._getCurrentTestURL(), name, "SKIP");
2165             logger.warning("add_task | Skipping test " + name);
2166             logger.activateBuffering();
2167           }
2169           // We stop the entire test file at the first exception because this
2170           // may mean that the state of subsequent tests may be corrupt.
2171           try {
2172             for (var task of task_list) {
2173               SimpleTest._currentTask = task;
2174               var name = task.name || "";
2175               if (
2176                 task.__skipMe ||
2177                 (run_only_this_task && task != run_only_this_task)
2178               ) {
2179                 skipTask(name);
2180                 continue;
2181               }
2182               const taskInfo = action =>
2183                 info(
2184                   `${
2185                     task.isSetup ? "add_setup" : "add_task"
2186                   } | ${action} ${name}`
2187                 );
2188               taskInfo("Entering");
2189               let result = await task();
2190               if (isGenerator(result)) {
2191                 ok(false, "Task returned a generator");
2192               }
2193               if (task._cleanupFunctions) {
2194                 for (const fn of task._cleanupFunctions) {
2195                   await fn();
2196                 }
2197               }
2198               for (const fn of SimpleTest._taskCleanupFunctions) {
2199                 await fn();
2200               }
2201               taskInfo("Leaving");
2202               delete SimpleTest._currentTask;
2203             }
2204           } catch (ex) {
2205             try {
2206               let serializedEx;
2207               if (
2208                 typeof ex == "string" ||
2209                 (typeof ex == "object" && isErrorOrException(ex))
2210               ) {
2211                 serializedEx = `${ex}`;
2212               } else {
2213                 serializedEx = JSON.stringify(ex);
2214               }
2216               SimpleTest.record(
2217                 false,
2218                 serializedEx,
2219                 "Should not throw any errors",
2220                 ex.stack
2221               );
2222             } catch (ex2) {
2223               SimpleTest.record(
2224                 false,
2225                 "(The exception cannot be converted to string.)",
2226                 "Should not throw any errors",
2227                 ex.stack
2228               );
2229             }
2230           }
2231           // All tasks are finished.
2232           SimpleTest.finish();
2233         })();
2234       });
2235     }
2236     generatorFunction.skip = () => (generatorFunction.__skipMe = true);
2237     generatorFunction.only = () => (run_only_this_task = generatorFunction);
2238     // Add the task to the list of tasks to run after
2239     // the main thread is finished.
2240     if (options.isSetup) {
2241       generatorFunction.isSetup = true;
2242       let lastSetupIndex = task_list.findLastIndex(t => t.isSetup) + 1;
2243       task_list.splice(lastSetupIndex, 0, generatorFunction);
2244     } else {
2245       task_list.push(generatorFunction);
2246     }
2247     return generatorFunction;
2248   };
2249 })();
2251 // Like add_task, but setup tasks are executed first.
2252 function add_setup(generatorFunction) {
2253   return add_task(generatorFunction, { isSetup: true });
2256 // Request complete log when using failure patterns so that failure info
2257 // from infra can be useful.
2258 if (usesFailurePatterns()) {
2259   SimpleTest.requestCompleteLog();
2262 addEventListener("message", async event => {
2263   if (event.data == "SimpleTest:timeout") {
2264     await SimpleTest.timeout();
2265     SimpleTest.finish();
2266   }