[testharness.js] Tolerate late tests (#15634)

diff --git a/resources/test/tests/unit/late-test.html b/resources/test/tests/unit/late-test.html
new file mode 100644
index 0000000..b1b74b2
--- /dev/null
+++ b/resources/test/tests/unit/late-test.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Test declared after harness completion</title>
+</head>
+<body>
+<div id="log"></div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p>This test simulates an automated test running scenario, where the test
+results emitted by testharness.js may be interpreted after some delay. It is
+intended to demonstrate that in such cases, any additional tests which are
+executed during that delay are <em>not</em> included in the dataset.</p>
+
+<p>Although these "late" tests are likely an indication of a mistake in test
+design, they cannot be detected deterministically, so in the interest of
+stability, they should be silently tolerated.</p>
+<script>
+async_test(function(t) {
+    var source = [
+        "<div id='log'></div>",
+        "<script src='/resources/testharness.js'></" + "script>",
+        "<script src='/resources/testharnessreport.js'></" + "script>",
+        "<script>",
+        "parent.childReady(window);",
+        "setup({ explicit_done: true });",
+        "test(function() {}, 'acceptable test');",
+        "onload = function() {",
+        "  done();",
+        "  test(function() {}, 'this test is late and should be ignored');",
+        "};",
+        "</" + "script>"
+    ].join("\n");
+    var iframe = document.createElement("iframe");
+
+    document.body.appendChild(iframe);
+    window.childReady = t.step_func(function(childWindow) {
+        childWindow.add_completion_callback(t.step_func(function(tests, status) {
+            setTimeout(t.step_func(function() {
+                assert_equals(tests.length, 1);
+                assert_equals(tests[0].name, "acceptable test");
+                assert_equals(status.status, status.OK);
+                t.done();
+            }), 0);
+        }));
+    });
+
+    iframe.contentDocument.open();
+    iframe.contentDocument.write(source);
+    iframe.contentDocument.close();
+});
+</script>
+</body>
+</html>
diff --git a/resources/testharness.js b/resources/testharness.js
index e57c06d..bffdf02 100644
--- a/resources/testharness.js
+++ b/resources/testharness.js
@@ -1499,7 +1499,7 @@
         }
         this.name = name;
 
-        this.phase = tests.is_aborted ?
+        this.phase = (tests.is_aborted || tests.phase === tests.phases.COMPLETE) ?
             this.phases.COMPLETE : this.phases.INITIAL;
 
         this.status = this.NOTRUN;
@@ -1522,6 +1522,13 @@
         this._user_defined_cleanup_count = 0;
         this._done_callbacks = [];
 
+        // Tests declared following harness completion are likely an indication
+        // of a programming error, but they cannot be reported
+        // deterministically.
+        if (tests.phase === tests.phases.COMPLETE) {
+            return;
+        }
+
         tests.push(this);
     }
 
@@ -2672,7 +2679,7 @@
         if (this.phase < this.STARTED) {
             this.init();
         }
-        if (!this.enabled) {
+        if (!this.enabled || this.phase === this.COMPLETE) {
             return;
         }
         this.resolve_log();