Bug 1503012 - Part 2: Shim some web-platform-tests for streams into the jit-tests. r=arai,chmanchester
☠☠ backed out by 7eb42458e2d8 ☠ ☠
authorJason Orendorff <jorendorff@mozilla.com>
Fri, 07 Dec 2018 20:02:53 +0000
changeset 508848 b2a0ae5e511537db7d316ce5a8de1b739290595b
parent 508847 834d7428bc8d134ccbfafc67246a101a30e478e9
child 508849 345ad3e746e8e50d2365f6dac8ef5be6383d1afd
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai, chmanchester
bugs1503012
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1503012 - Part 2: Shim some web-platform-tests for streams into the jit-tests. r=arai,chmanchester This is an egregious hack. The web-platform-tests were not meant to run in the shell. The eight tests that are included are the ones that we pass with flying colors. In most of the others, we still have a failure or two. Differential Revision: https://phabricator.services.mozilla.com/D11533
build/gen_test_packages_manifest.py
js/src/jit-test/lib/w3c-testharness.js
js/src/jit-test/lib/web-platform-testharness.js
js/src/jit-test/tests/web-platform/streams/readable-streams/bad-strategies.js
js/src/jit-test/tests/web-platform/streams/readable-streams/brand-checks.js
js/src/jit-test/tests/web-platform/streams/readable-streams/cancel.js
js/src/jit-test/tests/web-platform/streams/readable-streams/count-queuing-strategy-integration.js
js/src/jit-test/tests/web-platform/streams/readable-streams/default-reader.js
js/src/jit-test/tests/web-platform/streams/readable-streams/floating-point-total-queue-size.js
js/src/jit-test/tests/web-platform/streams/readable-streams/patched-global.js
js/src/jit-test/tests/web-platform/streams/readable-streams/tee.js
--- a/build/gen_test_packages_manifest.py
+++ b/build/gen_test_packages_manifest.py
@@ -71,19 +71,22 @@ def generate_package_data(args):
     # Generate a dictionary mapping test harness names (exactly as they're known to
     # mozharness and testsuite-targets.mk, ideally) to the set of archive names that
     # harness depends on to run.
     # mozharness will use this file to determine what test zips to download,
     # which will be an optimization once parts of the main zip are split to harness
     # specific zips.
     tests_common = args.tests_common
     jsshell = args.jsshell
+    web_platform = getattr(args, 'web-platform')
 
     harness_requirements = dict([(k, [tests_common]) for k in ALL_HARNESSES])
     harness_requirements['jittest'].append(jsshell)
+    harness_requirements['jittest'].append(web_platform)
+
     for harness in PACKAGE_SPECIFIED_HARNESSES + OPTIONAL_PACKAGES:
         pkg_name = getattr(args, harness, None)
         if pkg_name is None:
             continue
         harness_requirements[harness].append(pkg_name)
     return harness_requirements
 
 
copy from dom/imptests/testharness.js
copy to js/src/jit-test/lib/w3c-testharness.js
--- a/dom/imptests/testharness.js
+++ b/js/src/jit-test/lib/w3c-testharness.js
@@ -9,676 +9,26 @@ policies and contribution forms [3].
 [2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license
 [3] http://www.w3.org/2004/10/27-testcases
 */
 
 /* Documentation is in docs/api.md */
 
 (function ()
 {
-    var debug = false;
-    // default timeout is 10 seconds, test can override if needed
-    var settings = {
-        output:true,
-        harness_timeout:{
-            "normal":10000,
-            "long":60000
-        },
-        test_timeout:null,
-        message_events: ["start", "test_state", "result", "completion"]
-    };
-
-    var xhtml_ns = "http://www.w3.org/1999/xhtml";
-
-    /*
-     * TestEnvironment is an abstraction for the environment in which the test
-     * harness is used. Each implementation of a test environment has to provide
-     * the following interface:
-     *
-     * interface TestEnvironment {
-     *   // Invoked after the global 'tests' object has been created and it's
-     *   // safe to call add_*_callback() to register event handlers.
-     *   void on_tests_ready();
-     *
-     *   // Invoked after setup() has been called to notify the test environment
-     *   // of changes to the test harness properties.
-     *   void on_new_harness_properties(object properties);
-     *
-     *   // Should return a new unique default test name.
-     *   DOMString next_default_test_name();
-     *
-     *   // Should return the test harness timeout duration in milliseconds.
-     *   float test_timeout();
-     *
-     *   // Should return the global scope object.
-     *   object global_scope();
-     * };
-     */
-
-    /*
-     * A test environment with a DOM. The global object is 'window'. By default
-     * test results are displayed in a table. Any parent windows receive
-     * callbacks or messages via postMessage() when test events occur. See
-     * apisample11.html and apisample12.html.
-     */
-    function WindowTestEnvironment() {
-        this.name_counter = 0;
-        this.window_cache = null;
-        this.output_handler = null;
-        this.all_loaded = false;
-        var this_obj = this;
-        this.message_events = [];
-
-        this.message_functions = {
-            start: [add_start_callback, remove_start_callback,
-                    function (properties) {
-                        this_obj._dispatch("start_callback", [properties],
-                                           {type: "start", properties: properties});
-                    }],
-
-            test_state: [add_test_state_callback, remove_test_state_callback,
-                         function(test) {
-                             this_obj._dispatch("test_state_callback", [test],
-                                                {type: "test_state",
-                                                 test: test.structured_clone()});
-                         }],
-            result: [add_result_callback, remove_result_callback,
-                     function (test) {
-                         this_obj.output_handler.show_status();
-                         this_obj._dispatch("result_callback", [test],
-                                            {type: "result",
-                                             test: test.structured_clone()});
-                     }],
-            completion: [add_completion_callback, remove_completion_callback,
-                         function (tests, harness_status) {
-                             var cloned_tests = map(tests, function(test) {
-                                 return test.structured_clone();
-                             });
-                             this_obj._dispatch("completion_callback", [tests, harness_status],
-                                                {type: "complete",
-                                                 tests: cloned_tests,
-                                                 status: harness_status.structured_clone()});
-                         }]
-        }
-
-        on_event(window, 'load', function() {
-            this_obj.all_loaded = true;
-        });
-    }
-
-    WindowTestEnvironment.prototype._dispatch = function(selector, callback_args, message_arg) {
-        this._forEach_windows(
-                function(w, same_origin) {
-                    if (same_origin) {
-                        try {
-                            var has_selector = selector in w;
-                        } catch(e) {
-                            // If document.domain was set at some point same_origin can be
-                            // wrong and the above will fail.
-                            has_selector = false;
-                        }
-                        if (has_selector) {
-                            try {
-                                w[selector].apply(undefined, callback_args);
-                            } catch (e) {
-                                if (debug) {
-                                    throw e;
-                                }
-                            }
-                        }
-                    }
-                    if (supports_post_message(w) && w !== self) {
-                        w.postMessage(message_arg, "*");
-                    }
-                });
-    };
-
-    WindowTestEnvironment.prototype._forEach_windows = function(callback) {
-        // Iterate of the the windows [self ... top, opener]. The callback is passed
-        // two objects, the first one is the windows object itself, the second one
-        // is a boolean indicating whether or not its on the same origin as the
-        // current window.
-        var cache = this.window_cache;
-        if (!cache) {
-            cache = [[self, true]];
-            var w = self;
-            var i = 0;
-            var so;
-            var origins = location.ancestorOrigins;
-            while (w != w.parent) {
-                w = w.parent;
-                // In WebKit, calls to parent windows' properties that aren't on the same
-                // origin cause an error message to be displayed in the error console but
-                // don't throw an exception. This is a deviation from the current HTML5
-                // spec. See: https://bugs.webkit.org/show_bug.cgi?id=43504
-                // The problem with WebKit's behavior is that it pollutes the error console
-                // with error messages that can't be caught.
-                //
-                // This issue can be mitigated by relying on the (for now) proprietary
-                // `location.ancestorOrigins` property which returns an ordered list of
-                // the origins of enclosing windows. See:
-                // http://trac.webkit.org/changeset/113945.
-                if (origins) {
-                    so = (location.origin == origins[i]);
-                } else {
-                    so = is_same_origin(w);
-                }
-                cache.push([w, so]);
-                i++;
-            }
-            w = window.opener;
-            if (w) {
-                // window.opener isn't included in the `location.ancestorOrigins` prop.
-                // We'll just have to deal with a simple check and an error msg on WebKit
-                // browsers in this case.
-                cache.push([w, is_same_origin(w)]);
-            }
-            this.window_cache = cache;
-        }
-
-        forEach(cache,
-                function(a) {
-                    callback.apply(null, a);
-                });
-    };
-
-    WindowTestEnvironment.prototype.on_tests_ready = function() {
-        var output = new Output();
-        this.output_handler = output;
-
-        var this_obj = this;
-
-        add_start_callback(function (properties) {
-            this_obj.output_handler.init(properties);
-        });
-
-        add_test_state_callback(function(test) {
-            this_obj.output_handler.show_status();
-        });
-
-        add_result_callback(function (test) {
-            this_obj.output_handler.show_status();
-        });
-
-        add_completion_callback(function (tests, harness_status) {
-            this_obj.output_handler.show_results(tests, harness_status);
-        });
-        this.setup_messages(settings.message_events);
-    };
-
-    WindowTestEnvironment.prototype.setup_messages = function(new_events) {
-        var this_obj = this;
-        forEach(settings.message_events, function(x) {
-            var current_dispatch = this_obj.message_events.includes(x);
-            var new_dispatch = new_events.includes(x);
-            if (!current_dispatch && new_dispatch) {
-                this_obj.message_functions[x][0](this_obj.message_functions[x][2]);
-            } else if (current_dispatch && !new_dispatch) {
-                this_obj.message_functions[x][1](this_obj.message_functions[x][2]);
-            }
-        });
-        this.message_events = new_events;
-    }
-
-    WindowTestEnvironment.prototype.next_default_test_name = function() {
-        //Don't use document.title to work around an Opera bug in XHTML documents
-        var title = document.getElementsByTagName("title")[0];
-        var prefix = (title && title.firstChild && title.firstChild.data) || "Untitled";
-        var suffix = this.name_counter > 0 ? " " + this.name_counter : "";
-        this.name_counter++;
-        return prefix + suffix;
-    };
-
-    WindowTestEnvironment.prototype.on_new_harness_properties = function(properties) {
-        this.output_handler.setup(properties);
-        if (properties.hasOwnProperty("message_events")) {
-            this.setup_messages(properties.message_events);
-        }
-    };
-
-    WindowTestEnvironment.prototype.add_on_loaded_callback = function(callback) {
-        on_event(window, 'load', callback);
-    };
-
-    WindowTestEnvironment.prototype.test_timeout = function() {
-        var metas = document.getElementsByTagName("meta");
-        for (var i = 0; i < metas.length; i++) {
-            if (metas[i].name == "timeout") {
-                if (metas[i].content == "long") {
-                    return settings.harness_timeout.long;
-                }
-                break;
-            }
-        }
-        return settings.harness_timeout.normal;
-    };
-
-    WindowTestEnvironment.prototype.global_scope = function() {
-        return window;
-    };
-
-    /*
-     * Base TestEnvironment implementation for a generic web worker.
-     *
-     * Workers accumulate test results. One or more clients can connect and
-     * retrieve results from a worker at any time.
-     *
-     * WorkerTestEnvironment supports communicating with a client via a
-     * MessagePort.  The mechanism for determining the appropriate MessagePort
-     * for communicating with a client depends on the type of worker and is
-     * implemented by the various specializations of WorkerTestEnvironment
-     * below.
-     *
-     * A client document using testharness can use fetch_tests_from_worker() to
-     * retrieve results from a worker. See apisample16.html.
-     */
-    function WorkerTestEnvironment() {
-        this.name_counter = 0;
-        this.all_loaded = true;
-        this.message_list = [];
-        this.message_ports = [];
-    }
-
-    WorkerTestEnvironment.prototype._dispatch = function(message) {
-        this.message_list.push(message);
-        for (var i = 0; i < this.message_ports.length; ++i)
-        {
-            this.message_ports[i].postMessage(message);
-        }
-    };
-
-    // The only requirement is that port has a postMessage() method. It doesn't
-    // have to be an instance of a MessagePort, and often isn't.
-    WorkerTestEnvironment.prototype._add_message_port = function(port) {
-        this.message_ports.push(port);
-        for (var i = 0; i < this.message_list.length; ++i)
-        {
-            port.postMessage(this.message_list[i]);
-        }
-    };
-
-    WorkerTestEnvironment.prototype.next_default_test_name = function() {
-        var suffix = this.name_counter > 0 ? " " + this.name_counter : "";
-        this.name_counter++;
-        return "Untitled" + suffix;
-    };
-
-    WorkerTestEnvironment.prototype.on_new_harness_properties = function() {};
-
-    WorkerTestEnvironment.prototype.on_tests_ready = function() {
-        var this_obj = this;
-        add_start_callback(
-                function(properties) {
-                    this_obj._dispatch({
-                        type: "start",
-                        properties: properties,
-                    });
-                });
-        add_test_state_callback(
-                function(test) {
-                    this_obj._dispatch({
-                        type: "test_state",
-                        test: test.structured_clone()
-                    });
-                });
-        add_result_callback(
-                function(test) {
-                    this_obj._dispatch({
-                        type: "result",
-                        test: test.structured_clone()
-                    });
-                });
-        add_completion_callback(
-                function(tests, harness_status) {
-                    this_obj._dispatch({
-                        type: "complete",
-                        tests: map(tests,
-                            function(test) {
-                                return test.structured_clone();
-                            }),
-                        status: harness_status.structured_clone()
-                    });
-                });
-    };
-
-    WorkerTestEnvironment.prototype.add_on_loaded_callback = function() {};
-
-    WorkerTestEnvironment.prototype.test_timeout = function() {
-        // Tests running in a worker don't have a default timeout. I.e. all
-        // worker tests behave as if settings.explicit_timeout is true.
-        return null;
-    };
-
-    WorkerTestEnvironment.prototype.global_scope = function() {
-        return self;
-    };
-
-    /*
-     * Dedicated web workers.
-     * https://html.spec.whatwg.org/multipage/workers.html#dedicatedworkerglobalscope
-     *
-     * This class is used as the test_environment when testharness is running
-     * inside a dedicated worker.
-     */
-    function DedicatedWorkerTestEnvironment() {
-        WorkerTestEnvironment.call(this);
-        // self is an instance of DedicatedWorkerGlobalScope which exposes
-        // a postMessage() method for communicating via the message channel
-        // established when the worker is created.
-        this._add_message_port(self);
-    }
-    DedicatedWorkerTestEnvironment.prototype = Object.create(WorkerTestEnvironment.prototype);
-
-    DedicatedWorkerTestEnvironment.prototype.on_tests_ready = function() {
-        WorkerTestEnvironment.prototype.on_tests_ready.call(this);
-        // In the absence of an onload notification, we a require dedicated
-        // workers to explicitly signal when the tests are done.
-        tests.wait_for_finish = true;
-    };
-
-    /*
-     * Shared web workers.
-     * https://html.spec.whatwg.org/multipage/workers.html#sharedworkerglobalscope
-     *
-     * This class is used as the test_environment when testharness is running
-     * inside a shared web worker.
-     */
-    function SharedWorkerTestEnvironment() {
-        WorkerTestEnvironment.call(this);
-        var this_obj = this;
-        // Shared workers receive message ports via the 'onconnect' event for
-        // each connection.
-        self.addEventListener("connect",
-                function(message_event) {
-                    this_obj._add_message_port(message_event.source);
-                });
-    }
-    SharedWorkerTestEnvironment.prototype = Object.create(WorkerTestEnvironment.prototype);
-
-    SharedWorkerTestEnvironment.prototype.on_tests_ready = function() {
-        WorkerTestEnvironment.prototype.on_tests_ready.call(this);
-        // In the absence of an onload notification, we a require shared
-        // workers to explicitly signal when the tests are done.
-        tests.wait_for_finish = true;
-    };
-
-    /*
-     * Service workers.
-     * http://www.w3.org/TR/service-workers/
-     *
-     * This class is used as the test_environment when testharness is running
-     * inside a service worker.
-     */
-    function ServiceWorkerTestEnvironment() {
-        WorkerTestEnvironment.call(this);
-        this.all_loaded = false;
-        this.on_loaded_callback = null;
-        var this_obj = this;
-        self.addEventListener("message",
-                function(event) {
-                    if (event.data.type && event.data.type === "connect") {
-                        if (event.ports && event.ports[0]) {
-                            // If a MessageChannel was passed, then use it to
-                            // send results back to the main window.  This
-                            // allows the tests to work even if the browser
-                            // does not fully support MessageEvent.source in
-                            // ServiceWorkers yet.
-                            this_obj._add_message_port(event.ports[0]);
-                            event.ports[0].start();
-                        } else {
-                            // If there is no MessageChannel, then attempt to
-                            // use the MessageEvent.source to send results
-                            // back to the main window.
-                            this_obj._add_message_port(event.source);
-                        }
-                    }
-                });
-
-        // The oninstall event is received after the service worker script and
-        // all imported scripts have been fetched and executed. It's the
-        // equivalent of an onload event for a document. All tests should have
-        // been added by the time this event is received, thus it's not
-        // necessary to wait until the onactivate event.
-        on_event(self, "install",
-                function(event) {
-                    this_obj.all_loaded = true;
-                    if (this_obj.on_loaded_callback) {
-                        this_obj.on_loaded_callback();
-                    }
-                });
-    }
-    ServiceWorkerTestEnvironment.prototype = Object.create(WorkerTestEnvironment.prototype);
-
-    ServiceWorkerTestEnvironment.prototype.add_on_loaded_callback = function(callback) {
-        if (this.all_loaded) {
-            callback();
-        } else {
-            this.on_loaded_callback = callback;
-        }
-    };
-
-    function create_test_environment() {
-        if ('document' in self) {
-            return new WindowTestEnvironment();
-        }
-        if ('DedicatedWorkerGlobalScope' in self &&
-            self instanceof DedicatedWorkerGlobalScope) {
-            return new DedicatedWorkerTestEnvironment();
-        }
-        if ('SharedWorkerGlobalScope' in self &&
-            self instanceof SharedWorkerGlobalScope) {
-            return new SharedWorkerTestEnvironment();
-        }
-        if ('ServiceWorkerGlobalScope' in self &&
-            self instanceof ServiceWorkerGlobalScope) {
-            return new ServiceWorkerTestEnvironment();
-        }
-        throw new Error("Unsupported test environment");
-    }
-
-    var test_environment = create_test_environment();
-
-    function is_shared_worker(worker) {
-        return 'SharedWorker' in self && worker instanceof SharedWorker;
-    }
-
-    function is_service_worker(worker) {
-        return 'ServiceWorker' in self && worker instanceof ServiceWorker;
-    }
-
-    /*
-     * API functions
-     */
-
-    function test(func, name, properties)
-    {
-        var test_name = name ? name : test_environment.next_default_test_name();
-        properties = properties ? properties : {};
-        var test_obj = new Test(test_name, properties);
-        test_obj.step(func, test_obj, test_obj);
-        if (test_obj.phase === test_obj.phases.STARTED) {
-            test_obj.done();
-        }
-    }
-
-    function async_test(func, name, properties)
-    {
-        if (typeof func !== "function") {
-            properties = name;
-            name = func;
-            func = null;
-        }
-        var test_name = name ? name : test_environment.next_default_test_name();
-        properties = properties ? properties : {};
-        var test_obj = new Test(test_name, properties);
-        if (func) {
-            test_obj.step(func, test_obj, test_obj);
-        }
-        return test_obj;
-    }
-
-    function promise_test(func, name, properties) {
-        var test = async_test(name, properties);
-        // If there is no promise tests queue make one.
-        test.step(function() {
-            if (!tests.promise_tests) {
-                tests.promise_tests = Promise.resolve();
-            }
-        });
-        tests.promise_tests = tests.promise_tests.then(function() {
-            return Promise.resolve(test.step(func, test, test))
-                .then(
-                    function() {
-                        test.done();
-                    })
-                .catch(test.step_func(
-                    function(value) {
-                        if (value instanceof AssertionError) {
-                            throw value;
-                        }
-                        assert(false, "promise_test", null,
-                               "Unhandled rejection with value: ${value}", {value:value});
-                    }));
-        });
-    }
-
-    function promise_rejects(test, expected, promise) {
-        return promise.then(test.unreached_func("Should have rejected.")).catch(function(e) {
-            assert_throws(expected, function() { throw e });
-        });
-    }
-
-    /**
-     * This constructor helper allows DOM events to be handled using Promises,
-     * which can make it a lot easier to test a very specific series of events,
-     * including ensuring that unexpected events are not fired at any point.
-     */
-    function EventWatcher(test, watchedNode, eventTypes)
-    {
-        if (typeof eventTypes == 'string') {
-            eventTypes = [eventTypes];
-        }
-
-        var waitingFor = null;
-
-        var eventHandler = test.step_func(function(evt) {
-            assert_true(!!waitingFor,
-                        'Not expecting event, but got ' + evt.type + ' event');
-            assert_equals(evt.type, waitingFor.types[0],
-                          'Expected ' + waitingFor.types[0] + ' event, but got ' +
-                          evt.type + ' event instead');
-            if (waitingFor.types.length > 1) {
-                // Pop first event from array
-                waitingFor.types.shift();
-                return;
-            }
-            // We need to null out waitingFor before calling the resolve function
-            // since the Promise's resolve handlers may call wait_for() which will
-            // need to set waitingFor.
-            var resolveFunc = waitingFor.resolve;
-            waitingFor = null;
-            resolveFunc(evt);
-        });
-
-        for (var i = 0; i < eventTypes.length; i++) {
-            watchedNode.addEventListener(eventTypes[i], eventHandler);
-        }
-
-        /**
-         * Returns a Promise that will resolve after the specified event or
-         * series of events has occured.
-         */
-        this.wait_for = function(types) {
-            if (waitingFor) {
-                return Promise.reject('Already waiting for an event or events');
-            }
-            if (typeof types == 'string') {
-                types = [types];
-            }
-            return new Promise(function(resolve, reject) {
-                waitingFor = {
-                    types: types,
-                    resolve: resolve,
-                    reject: reject
-                };
-            });
-        };
-
-        function stop_watching() {
-            for (var i = 0; i < eventTypes.length; i++) {
-                watchedNode.removeEventListener(eventTypes[i], eventHandler);
-            }
-        };
-
-        test.add_cleanup(stop_watching);
-
-        return this;
-    }
-    expose(EventWatcher, 'EventWatcher');
-
-    function setup(func_or_properties, maybe_properties)
-    {
-        var func = null;
-        var properties = {};
-        if (arguments.length === 2) {
-            func = func_or_properties;
-            properties = maybe_properties;
-        } else if (func_or_properties instanceof Function) {
-            func = func_or_properties;
-        } else {
-            properties = func_or_properties;
-        }
-        tests.setup(func, properties);
-        test_environment.on_new_harness_properties(properties);
-    }
-
-    function done() {
-        if (tests.tests.length === 0) {
-            tests.set_file_is_test();
-        }
-        if (tests.file_is_test) {
-            tests.tests[0].done();
-        }
-        tests.end_wait();
-    }
-
-    function generate_tests(func, args, properties) {
-        forEach(args, function(x, i)
-                {
-                    var name = x[0];
-                    test(function()
-                         {
-                             func.apply(this, x.slice(1));
-                         },
-                         name,
-                         Array.isArray(properties) ? properties[i] : properties);
-                });
-    }
-
-    function on_event(object, event, callback)
-    {
-        object.addEventListener(event, callback);
-    }
+    let tests = {timeout_multiplier: 1};  // ADDED
 
     function step_timeout(f, t) {
         var outer_this = this;
         var args = Array.prototype.slice.call(arguments, 2);
         return setTimeout(function() {
             f.apply(outer_this, args);
         }, t * tests.timeout_multiplier);
     }
 
-    expose(test, 'test');
-    expose(async_test, 'async_test');
-    expose(promise_test, 'promise_test');
-    expose(promise_rejects, 'promise_rejects');
-    expose(generate_tests, 'generate_tests');
-    expose(setup, 'setup');
-    expose(done, 'done');
-    expose(on_event, 'on_event');
     expose(step_timeout, 'step_timeout');
 
     /*
      * Return a string truncated to the given length, with ... added at the end
      * if it was longer.
      */
     function truncate(s, len)
     {
@@ -1285,970 +635,16 @@ policies and contribution forms [3].
                     }
                 });
         if (!passed) {
             throw new AssertionError(errors.join("\n\n"));
         }
     }
     expose(assert_any, "assert_any");
 
-    function Test(name, properties)
-    {
-        if (tests.file_is_test && tests.tests.length) {
-            throw new Error("Tried to create a test with file_is_test");
-        }
-        this.name = name;
-
-        this.phase = this.phases.INITIAL;
-
-        this.status = this.NOTRUN;
-        this.timeout_id = null;
-        this.index = null;
-
-        this.properties = properties;
-        var timeout = properties.timeout ? properties.timeout : settings.test_timeout;
-        if (timeout !== null) {
-            this.timeout_length = timeout * tests.timeout_multiplier;
-        } else {
-            this.timeout_length = null;
-        }
-
-        this.message = null;
-        this.stack = null;
-
-        this.steps = [];
-
-        this.cleanup_callbacks = [];
-
-        tests.push(this);
-    }
-
-    Test.statuses = {
-        PASS:0,
-        FAIL:1,
-        TIMEOUT:2,
-        NOTRUN:3
-    };
-
-    Test.prototype = merge({}, Test.statuses);
-
-    Test.prototype.phases = {
-        INITIAL:0,
-        STARTED:1,
-        HAS_RESULT:2,
-        COMPLETE:3
-    };
-
-    Test.prototype.structured_clone = function()
-    {
-        if (!this._structured_clone) {
-            var msg = this.message;
-            msg = msg ? String(msg) : msg;
-            this._structured_clone = merge({
-                name:String(this.name),
-                properties:merge({}, this.properties),
-            }, Test.statuses);
-        }
-        this._structured_clone.status = this.status;
-        this._structured_clone.message = this.message;
-        this._structured_clone.stack = this.stack;
-        this._structured_clone.index = this.index;
-        return this._structured_clone;
-    };
-
-    Test.prototype.step = function(func, this_obj)
-    {
-        if (this.phase > this.phases.STARTED) {
-            return;
-        }
-        this.phase = this.phases.STARTED;
-        //If we don't get a result before the harness times out that will be a test timout
-        this.set_status(this.TIMEOUT, "Test timed out");
-
-        tests.started = true;
-        tests.notify_test_state(this);
-
-        if (this.timeout_id === null) {
-            this.set_timeout();
-        }
-
-        this.steps.push(func);
-
-        if (arguments.length === 1) {
-            this_obj = this;
-        }
-
-        try {
-            return func.apply(this_obj, Array.prototype.slice.call(arguments, 2));
-        } catch (e) {
-            if (this.phase >= this.phases.HAS_RESULT) {
-                return;
-            }
-            var message = String((typeof e === "object" && e !== null) ? e.message : e);
-            var stack = e.stack ? e.stack : null;
-
-            this.set_status(this.FAIL, message, stack);
-            this.phase = this.phases.HAS_RESULT;
-            this.done();
-        }
-    };
-
-    Test.prototype.step_func = function(func, this_obj)
-    {
-        var test_this = this;
-
-        if (arguments.length === 1) {
-            this_obj = test_this;
-        }
-
-        return function()
-        {
-            return test_this.step.apply(test_this, [func, this_obj].concat(
-                Array.prototype.slice.call(arguments)));
-        };
-    };
-
-    Test.prototype.step_func_done = function(func, this_obj)
-    {
-        var test_this = this;
-
-        if (arguments.length === 1) {
-            this_obj = test_this;
-        }
-
-        return function()
-        {
-            if (func) {
-                test_this.step.apply(test_this, [func, this_obj].concat(
-                    Array.prototype.slice.call(arguments)));
-            }
-            test_this.done();
-        };
-    };
-
-    Test.prototype.unreached_func = function(description)
-    {
-        return this.step_func(function() {
-            assert_unreached(description);
-        });
-    };
-
-    Test.prototype.step_timeout = function(f, timeout) {
-        var test_this = this;
-        var args = Array.prototype.slice.call(arguments, 2);
-        return setTimeout(this.step_func(function() {
-            return f.apply(test_this, args);
-        }, timeout * tests.timeout_multiplier));
-    }
-
-    Test.prototype.add_cleanup = function(callback) {
-        this.cleanup_callbacks.push(callback);
-    };
-
-    Test.prototype.force_timeout = function() {
-        this.set_status(this.TIMEOUT);
-        this.phase = this.phases.HAS_RESULT;
-    };
-
-    Test.prototype.set_timeout = function()
-    {
-        if (this.timeout_length !== null) {
-            var this_obj = this;
-            this.timeout_id = setTimeout(function()
-                                         {
-                                             this_obj.timeout();
-                                         }, this.timeout_length);
-        }
-    };
-
-    Test.prototype.set_status = function(status, message, stack)
-    {
-        this.status = status;
-        this.message = message;
-        this.stack = stack ? stack : null;
-    };
-
-    Test.prototype.timeout = function()
-    {
-        this.timeout_id = null;
-        this.set_status(this.TIMEOUT, "Test timed out");
-        this.phase = this.phases.HAS_RESULT;
-        this.done();
-    };
-
-    Test.prototype.done = function()
-    {
-        if (this.phase == this.phases.COMPLETE) {
-            return;
-        }
-
-        if (this.phase <= this.phases.STARTED) {
-            this.set_status(this.PASS, null);
-        }
-
-        this.phase = this.phases.COMPLETE;
-
-        clearTimeout(this.timeout_id);
-        tests.result(this);
-        this.cleanup();
-    };
-
-    Test.prototype.cleanup = function() {
-        forEach(this.cleanup_callbacks,
-                function(cleanup_callback) {
-                    cleanup_callback();
-                });
-    };
-
-    /*
-     * A RemoteTest object mirrors a Test object on a remote worker. The
-     * associated RemoteWorker updates the RemoteTest object in response to
-     * received events. In turn, the RemoteTest object replicates these events
-     * on the local document. This allows listeners (test result reporting
-     * etc..) to transparently handle local and remote events.
-     */
-    function RemoteTest(clone) {
-        var this_obj = this;
-        Object.keys(clone).forEach(
-                function(key) {
-                    this_obj[key] = clone[key];
-                });
-        this.index = null;
-        this.phase = this.phases.INITIAL;
-        this.update_state_from(clone);
-        tests.push(this);
-    }
-
-    RemoteTest.prototype.structured_clone = function() {
-        var clone = {};
-        Object.keys(this).forEach(
-                key => {
-                    if (typeof(this[key]) === "object") {
-                        clone[key] = merge({}, this[key]);
-                    } else {
-                        clone[key] = this[key];
-                    }
-                });
-        clone.phases = merge({}, this.phases);
-        return clone;
-    };
-
-    RemoteTest.prototype.cleanup = function() {};
-    RemoteTest.prototype.phases = Test.prototype.phases;
-    RemoteTest.prototype.update_state_from = function(clone) {
-        this.status = clone.status;
-        this.message = clone.message;
-        this.stack = clone.stack;
-        if (this.phase === this.phases.INITIAL) {
-            this.phase = this.phases.STARTED;
-        }
-    };
-    RemoteTest.prototype.done = function() {
-        this.phase = this.phases.COMPLETE;
-    }
-
-    /*
-     * A RemoteWorker listens for test events from a worker. These events are
-     * then used to construct and maintain RemoteTest objects that mirror the
-     * tests running on the remote worker.
-     */
-    function RemoteWorker(worker) {
-        this.running = true;
-        this.tests = new Array();
-
-        var this_obj = this;
-        worker.onerror = function(error) { this_obj.worker_error(error); };
-
-        var message_port;
-
-        if (is_service_worker(worker)) {
-            if (window.MessageChannel) {
-                // The ServiceWorker's implicit MessagePort is currently not
-                // reliably accessible from the ServiceWorkerGlobalScope due to
-                // Blink setting MessageEvent.source to null for messages sent
-                // via ServiceWorker.postMessage(). Until that's resolved,
-                // create an explicit MessageChannel and pass one end to the
-                // worker.
-                var message_channel = new MessageChannel();
-                message_port = message_channel.port1;
-                message_port.start();
-                worker.postMessage({type: "connect"}, [message_channel.port2]);
-            } else {
-                // If MessageChannel is not available, then try the
-                // ServiceWorker.postMessage() approach using MessageEvent.source
-                // on the other end.
-                message_port = navigator.serviceWorker;
-                worker.postMessage({type: "connect"});
-            }
-        } else if (is_shared_worker(worker)) {
-            message_port = worker.port;
-        } else {
-            message_port = worker;
-        }
-
-        // Keeping a reference to the worker until worker_done() is seen
-        // prevents the Worker object and its MessageChannel from going away
-        // before all the messages are dispatched.
-        this.worker = worker;
-
-        message_port.onmessage =
-            function(message) {
-                if (this_obj.running && (message.data.type in this_obj.message_handlers)) {
-                    this_obj.message_handlers[message.data.type].call(this_obj, message.data);
-                }
-            };
-    }
-
-    RemoteWorker.prototype.worker_error = function(error) {
-        var message = error.message || String(error);
-        var filename = (error.filename ? " " + error.filename: "");
-        // FIXME: Display worker error states separately from main document
-        // error state.
-        this.worker_done({
-            status: {
-                status: tests.status.ERROR,
-                message: "Error in worker" + filename + ": " + message,
-                stack: error.stack
-            }
-        });
-        error.preventDefault();
-    };
-
-    RemoteWorker.prototype.test_state = function(data) {
-        var remote_test = this.tests[data.test.index];
-        if (!remote_test) {
-            remote_test = new RemoteTest(data.test);
-            this.tests[data.test.index] = remote_test;
-        }
-        remote_test.update_state_from(data.test);
-        tests.notify_test_state(remote_test);
-    };
-
-    RemoteWorker.prototype.test_done = function(data) {
-        var remote_test = this.tests[data.test.index];
-        remote_test.update_state_from(data.test);
-        remote_test.done();
-        tests.result(remote_test);
-    };
-
-    RemoteWorker.prototype.worker_done = function(data) {
-        if (tests.status.status === null &&
-            data.status.status !== data.status.OK) {
-            tests.status.status = data.status.status;
-            tests.status.message = data.status.message;
-            tests.status.stack = data.status.stack;
-        }
-        this.running = false;
-        this.worker = null;
-        if (tests.all_done()) {
-            tests.complete();
-        }
-    };
-
-    RemoteWorker.prototype.message_handlers = {
-        test_state: RemoteWorker.prototype.test_state,
-        result: RemoteWorker.prototype.test_done,
-        complete: RemoteWorker.prototype.worker_done
-    };
-
-    /*
-     * Harness
-     */
-
-    function TestsStatus()
-    {
-        this.status = null;
-        this.message = null;
-        this.stack = null;
-    }
-
-    TestsStatus.statuses = {
-        OK:0,
-        ERROR:1,
-        TIMEOUT:2
-    };
-
-    TestsStatus.prototype = merge({}, TestsStatus.statuses);
-
-    TestsStatus.prototype.structured_clone = function()
-    {
-        if (!this._structured_clone) {
-            var msg = this.message;
-            msg = msg ? String(msg) : msg;
-            this._structured_clone = merge({
-                status:this.status,
-                message:msg,
-                stack:this.stack
-            }, TestsStatus.statuses);
-        }
-        return this._structured_clone;
-    };
-
-    function Tests()
-    {
-        this.tests = [];
-        this.num_pending = 0;
-
-        this.phases = {
-            INITIAL:0,
-            SETUP:1,
-            HAVE_TESTS:2,
-            HAVE_RESULTS:3,
-            COMPLETE:4
-        };
-        this.phase = this.phases.INITIAL;
-
-        this.properties = {};
-
-        this.wait_for_finish = false;
-        this.processing_callbacks = false;
-
-        this.allow_uncaught_exception = false;
-
-        this.file_is_test = false;
-
-        this.timeout_multiplier = 1;
-        this.timeout_length = test_environment.test_timeout();
-        this.timeout_id = null;
-
-        this.start_callbacks = [];
-        this.test_state_callbacks = [];
-        this.test_done_callbacks = [];
-        this.all_done_callbacks = [];
-
-        this.pending_workers = [];
-
-        this.status = new TestsStatus();
-
-        var this_obj = this;
-
-        test_environment.add_on_loaded_callback(function() {
-            if (this_obj.all_done()) {
-                this_obj.complete();
-            }
-        });
-
-        this.set_timeout();
-    }
-
-    Tests.prototype.setup = function(func, properties)
-    {
-        if (this.phase >= this.phases.HAVE_RESULTS) {
-            return;
-        }
-
-        if (this.phase < this.phases.SETUP) {
-            this.phase = this.phases.SETUP;
-        }
-
-        this.properties = properties;
-
-        for (var p in properties) {
-            if (properties.hasOwnProperty(p)) {
-                var value = properties[p];
-                if (p == "allow_uncaught_exception") {
-                    this.allow_uncaught_exception = value;
-                } else if (p == "explicit_done" && value) {
-                    this.wait_for_finish = true;
-                } else if (p == "explicit_timeout" && value) {
-                    this.timeout_length = null;
-                    if (this.timeout_id)
-                    {
-                        clearTimeout(this.timeout_id);
-                    }
-                } else if (p == "timeout_multiplier") {
-                    this.timeout_multiplier = value;
-                }
-            }
-        }
-
-        if (func) {
-            try {
-                func();
-            } catch (e) {
-                this.status.status = this.status.ERROR;
-                this.status.message = String(e);
-                this.status.stack = e.stack ? e.stack : null;
-            }
-        }
-        this.set_timeout();
-    };
-
-    Tests.prototype.set_file_is_test = function() {
-        if (this.tests.length > 0) {
-            throw new Error("Tried to set file as test after creating a test");
-        }
-        this.wait_for_finish = true;
-        this.file_is_test = true;
-        // Create the test, which will add it to the list of tests
-        async_test();
-    };
-
-    Tests.prototype.set_timeout = function() {
-        var this_obj = this;
-        clearTimeout(this.timeout_id);
-        if (this.timeout_length !== null) {
-            this.timeout_id = setTimeout(function() {
-                                             this_obj.timeout();
-                                         }, this.timeout_length);
-        }
-    };
-
-    Tests.prototype.timeout = function() {
-        if (this.status.status === null) {
-            this.status.status = this.status.TIMEOUT;
-        }
-        this.complete();
-    };
-
-    Tests.prototype.end_wait = function()
-    {
-        this.wait_for_finish = false;
-        if (this.all_done()) {
-            this.complete();
-        }
-    };
-
-    Tests.prototype.push = function(test)
-    {
-        if (this.phase < this.phases.HAVE_TESTS) {
-            this.start();
-        }
-        this.num_pending++;
-        test.index = this.tests.push(test);
-        this.notify_test_state(test);
-    };
-
-    Tests.prototype.notify_test_state = function(test) {
-        var this_obj = this;
-        forEach(this.test_state_callbacks,
-                function(callback) {
-                    callback(test, this_obj);
-                });
-    };
-
-    Tests.prototype.all_done = function() {
-        return (this.tests.length > 0 && test_environment.all_loaded &&
-                this.num_pending === 0 && !this.wait_for_finish &&
-                !this.processing_callbacks &&
-                !this.pending_workers.some(function(w) { return w.running; }));
-    };
-
-    Tests.prototype.start = function() {
-        this.phase = this.phases.HAVE_TESTS;
-        this.notify_start();
-    };
-
-    Tests.prototype.notify_start = function() {
-        var this_obj = this;
-        forEach (this.start_callbacks,
-                 function(callback)
-                 {
-                     callback(this_obj.properties);
-                 });
-    };
-
-    Tests.prototype.result = function(test)
-    {
-        if (this.phase > this.phases.HAVE_RESULTS) {
-            return;
-        }
-        this.phase = this.phases.HAVE_RESULTS;
-        this.num_pending--;
-        this.notify_result(test);
-    };
-
-    Tests.prototype.notify_result = function(test) {
-        var this_obj = this;
-        this.processing_callbacks = true;
-        forEach(this.test_done_callbacks,
-                function(callback)
-                {
-                    callback(test, this_obj);
-                });
-        this.processing_callbacks = false;
-        if (this_obj.all_done()) {
-            this_obj.complete();
-        }
-    };
-
-    Tests.prototype.complete = function() {
-        if (this.phase === this.phases.COMPLETE) {
-            return;
-        }
-        this.phase = this.phases.COMPLETE;
-        var this_obj = this;
-        this.tests.forEach(
-            function(x)
-            {
-                if (x.phase < x.phases.COMPLETE) {
-                    this_obj.notify_result(x);
-                    x.cleanup();
-                    x.phase = x.phases.COMPLETE;
-                }
-            }
-        );
-        this.notify_complete();
-    };
-
-    Tests.prototype.notify_complete = function() {
-        var this_obj = this;
-        if (this.status.status === null) {
-            this.status.status = this.status.OK;
-        }
-
-        forEach (this.all_done_callbacks,
-                 function(callback)
-                 {
-                     callback(this_obj.tests, this_obj.status);
-                 });
-    };
-
-    Tests.prototype.fetch_tests_from_worker = function(worker) {
-        if (this.phase >= this.phases.COMPLETE) {
-            return;
-        }
-
-        this.pending_workers.push(new RemoteWorker(worker));
-    };
-
-    function fetch_tests_from_worker(port) {
-        tests.fetch_tests_from_worker(port);
-    }
-    expose(fetch_tests_from_worker, 'fetch_tests_from_worker');
-
-    function timeout() {
-        if (tests.timeout_length === null) {
-            tests.timeout();
-        }
-    }
-    expose(timeout, 'timeout');
-
-    function add_start_callback(callback) {
-        tests.start_callbacks.push(callback);
-    }
-
-    function add_test_state_callback(callback) {
-        tests.test_state_callbacks.push(callback);
-    }
-
-    function add_result_callback(callback) {
-        tests.test_done_callbacks.push(callback);
-    }
-
-    function add_completion_callback(callback) {
-        tests.all_done_callbacks.push(callback);
-    }
-
-    expose(add_start_callback, 'add_start_callback');
-    expose(add_test_state_callback, 'add_test_state_callback');
-    expose(add_result_callback, 'add_result_callback');
-    expose(add_completion_callback, 'add_completion_callback');
-
-    function remove(array, item) {
-        var index = array.indexOf(item);
-        if (index > -1) {
-            array.splice(index, 1);
-        }
-    }
-
-    function remove_start_callback(callback) {
-        remove(tests.start_callbacks, callback);
-    }
-
-    function remove_test_state_callback(callback) {
-        remove(tests.test_state_callbacks, callback);
-    }
-
-    function remove_result_callback(callback) {
-        remove(tests.test_done_callbacks, callback);
-    }
-
-    function remove_completion_callback(callback) {
-       remove(tests.all_done_callbacks, callback);
-    }
-
-    /*
-     * Output listener
-    */
-
-    function Output() {
-        this.output_document = document;
-        this.output_node = null;
-        this.enabled = settings.output;
-        this.phase = this.INITIAL;
-    }
-
-    Output.prototype.INITIAL = 0;
-    Output.prototype.STARTED = 1;
-    Output.prototype.HAVE_RESULTS = 2;
-    Output.prototype.COMPLETE = 3;
-
-    Output.prototype.setup = function(properties) {
-        if (this.phase > this.INITIAL) {
-            return;
-        }
-
-        //If output is disabled in testharnessreport.js the test shouldn't be
-        //able to override that
-        this.enabled = this.enabled && (properties.hasOwnProperty("output") ?
-                                        properties.output : settings.output);
-    };
-
-    Output.prototype.init = function(properties) {
-        if (this.phase >= this.STARTED) {
-            return;
-        }
-        if (properties.output_document) {
-            this.output_document = properties.output_document;
-        } else {
-            this.output_document = document;
-        }
-        this.phase = this.STARTED;
-    };
-
-    Output.prototype.resolve_log = function() {
-        var output_document;
-        if (typeof this.output_document === "function") {
-            output_document = this.output_document.apply(undefined);
-        } else {
-            output_document = this.output_document;
-        }
-        if (!output_document) {
-            return;
-        }
-        var node = output_document.getElementById("log");
-        if (!node) {
-            if (!document.body || document.readyState == "loading") {
-                return;
-            }
-            node = output_document.createElement("div");
-            node.id = "log";
-            output_document.body.appendChild(node);
-        }
-        this.output_document = output_document;
-        this.output_node = node;
-    };
-
-    Output.prototype.show_status = function() {
-        if (this.phase < this.STARTED) {
-            this.init();
-        }
-        if (!this.enabled) {
-            return;
-        }
-        if (this.phase < this.HAVE_RESULTS) {
-            this.resolve_log();
-            this.phase = this.HAVE_RESULTS;
-        }
-        var done_count = tests.tests.length - tests.num_pending;
-        if (this.output_node) {
-            if (done_count < 100 ||
-                (done_count < 1000 && done_count % 100 === 0) ||
-                done_count % 1000 === 0) {
-                this.output_node.textContent = "Running, " +
-                    done_count + " complete, " +
-                    tests.num_pending + " remain";
-            }
-        }
-    };
-
-    Output.prototype.show_results = function (tests, harness_status) {
-        if (this.phase >= this.COMPLETE) {
-            return;
-        }
-        if (!this.enabled) {
-            return;
-        }
-        if (!this.output_node) {
-            this.resolve_log();
-        }
-        this.phase = this.COMPLETE;
-
-        var log = this.output_node;
-        if (!log) {
-            return;
-        }
-        var output_document = this.output_document;
-
-        while (log.lastChild) {
-            log.removeChild(log.lastChild);
-        }
-
-        var harness_url = get_harness_url();
-        if (harness_url !== null) {
-            var stylesheet = output_document.createElementNS(xhtml_ns, "link");
-            stylesheet.setAttribute("rel", "stylesheet");
-            stylesheet.setAttribute("href", harness_url + "testharness.css");
-            var heads = output_document.getElementsByTagName("head");
-            if (heads.length) {
-                heads[0].appendChild(stylesheet);
-            }
-        }
-
-        var status_text_harness = {};
-        status_text_harness[harness_status.OK] = "OK";
-        status_text_harness[harness_status.ERROR] = "Error";
-        status_text_harness[harness_status.TIMEOUT] = "Timeout";
-
-        var status_text = {};
-        status_text[Test.prototype.PASS] = "Pass";
-        status_text[Test.prototype.FAIL] = "Fail";
-        status_text[Test.prototype.TIMEOUT] = "Timeout";
-        status_text[Test.prototype.NOTRUN] = "Not Run";
-
-        var status_number = {};
-        forEach(tests,
-                function(test) {
-                    var status = status_text[test.status];
-                    if (status_number.hasOwnProperty(status)) {
-                        status_number[status] += 1;
-                    } else {
-                        status_number[status] = 1;
-                    }
-                });
-
-        function status_class(status)
-        {
-            return status.replace(/\s/g, '').toLowerCase();
-        }
-
-        var summary_template = ["section", {"id":"summary"},
-                                ["h2", {}, "Summary"],
-                                function()
-                                {
-
-                                    var status = status_text_harness[harness_status.status];
-                                    var rv = [["section", {},
-                                               ["p", {},
-                                                "Harness status: ",
-                                                ["span", {"class":status_class(status)},
-                                                 status
-                                                ],
-                                               ]
-                                              ]];
-
-                                    if (harness_status.status === harness_status.ERROR) {
-                                        rv[0].push(["pre", {}, harness_status.message]);
-                                        if (harness_status.stack) {
-                                            rv[0].push(["pre", {}, harness_status.stack]);
-                                        }
-                                    }
-                                    return rv;
-                                },
-                                ["p", {}, "Found ${num_tests} tests"],
-                                function() {
-                                    var rv = [["div", {}]];
-                                    var i = 0;
-                                    while (status_text.hasOwnProperty(i)) {
-                                        if (status_number.hasOwnProperty(status_text[i])) {
-                                            var status = status_text[i];
-                                            rv[0].push(["div", {"class":status_class(status)},
-                                                        ["label", {},
-                                                         ["input", {type:"checkbox", checked:"checked"}],
-                                                         status_number[status] + " " + status]]);
-                                        }
-                                        i++;
-                                    }
-                                    return rv;
-                                },
-                               ];
-
-        log.appendChild(render(summary_template, {num_tests:tests.length}, output_document));
-
-        forEach(output_document.querySelectorAll("section#summary label"),
-                function(element)
-                {
-                    on_event(element, "click",
-                             function(e)
-                             {
-                                 if (output_document.getElementById("results") === null) {
-                                     e.preventDefault();
-                                     return;
-                                 }
-                                 var result_class = element.parentNode.getAttribute("class");
-                                 var style_element = output_document.querySelector("style#hide-" + result_class);
-                                 var input_element = element.querySelector("input");
-                                 if (!style_element && !input_element.checked) {
-                                     style_element = output_document.createElementNS(xhtml_ns, "style");
-                                     style_element.id = "hide-" + result_class;
-                                     style_element.textContent = "table#results > tbody > tr."+result_class+"{display:none}";
-                                     output_document.body.appendChild(style_element);
-                                 } else if (style_element && input_element.checked) {
-                                     style_element.remove();
-                                 }
-                             });
-                });
-
-        // This use of innerHTML plus manual escaping is not recommended in
-        // general, but is necessary here for performance.  Using textContent
-        // on each individual <td> adds tens of seconds of execution time for
-        // large test suites (tens of thousands of tests).
-        function escape_html(s)
-        {
-            return s.replace(/\&/g, "&amp;")
-                .replace(/</g, "&lt;")
-                .replace(/"/g, "&quot;")
-                .replace(/'/g, "&#39;");
-        }
-
-        function has_assertions()
-        {
-            for (var i = 0; i < tests.length; i++) {
-                if (tests[i].properties.hasOwnProperty("assert")) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        function get_assertion(test)
-        {
-            if (test.properties.hasOwnProperty("assert")) {
-                if (Array.isArray(test.properties.assert)) {
-                    return test.properties.assert.join(' ');
-                }
-                return test.properties.assert;
-            }
-            return '';
-        }
-
-        log.appendChild(document.createElementNS(xhtml_ns, "section"));
-        var assertions = has_assertions();
-        var html = "<h2>Details</h2><table id='results' " + (assertions ? "class='assertions'" : "" ) + ">" +
-            "<thead><tr><th>Result</th><th>Test Name</th>" +
-            (assertions ? "<th>Assertion</th>" : "") +
-            "<th>Message</th></tr></thead>" +
-            "<tbody>";
-        for (var i = 0; i < tests.length; i++) {
-            html += '<tr class="' +
-                escape_html(status_class(status_text[tests[i].status])) +
-                '"><td>' +
-                escape_html(status_text[tests[i].status]) +
-                "</td><td>" +
-                escape_html(tests[i].name) +
-                "</td><td>" +
-                (assertions ? escape_html(get_assertion(tests[i])) + "</td><td>" : "") +
-                escape_html(tests[i].message ? tests[i].message : " ") +
-                (tests[i].stack ? "<pre>" +
-                 escape_html(tests[i].stack) +
-                 "</pre>": "") +
-                "</td></tr>";
-        }
-        html += "</tbody></table>";
-        try {
-            log.lastChild.innerHTML = html;
-        } catch (e) {
-            log.appendChild(document.createElementNS(xhtml_ns, "p"))
-               .textContent = "Setting innerHTML for the log threw an exception.";
-            log.appendChild(document.createElementNS(xhtml_ns, "pre"))
-               .textContent = html;
-        }
-    };
-
     /*
      * Template code
      *
      * A template is just a javascript structure. An element is represented as:
      *
      * [tag_name, {attr_name:attr_value}, child1, child2]
      *
      * the children can either be strings (which act like text nodes), other templates or
@@ -2357,70 +753,21 @@ policies and contribution forms [3].
         } else {
             substitute_attrs(template[1], rv);
             substitute_children(template.slice(2), rv);
         }
 
         return rv;
     }
 
-    function make_dom_single(template, doc)
-    {
-        var output_document = doc || document;
-        var element;
-        if (template[0] === "{text}") {
-            element = output_document.createTextNode("");
-            for (var i = 1; i < template.length; i++) {
-                element.data += template[i];
-            }
-        } else {
-            element = output_document.createElementNS(xhtml_ns, template[0]);
-            for (var name in template[1]) {
-                if (template[1].hasOwnProperty(name)) {
-                    element.setAttribute(name, template[1][name]);
-                }
-            }
-            for (var i = 2; i < template.length; i++) {
-                if (template[i] instanceof Object) {
-                    var sub_element = make_dom(template[i]);
-                    element.appendChild(sub_element);
-                } else {
-                    var text_node = output_document.createTextNode(template[i]);
-                    element.appendChild(text_node);
-                }
-            }
-        }
-
-        return element;
-    }
-
-    function make_dom(template, substitutions, output_document)
-    {
-        if (is_single_node(template)) {
-            return make_dom_single(template, output_document);
-        }
-
-        return map(template, function(x) {
-                       return make_dom_single(x, output_document);
-                   });
-    }
-
-    function render(template, substitutions, output_document)
-    {
-        return make_dom(substitute(template, substitutions), output_document);
-    }
-
     /*
      * Utility funcions
      */
     function assert(expected_true, function_name, description, error, substitutions)
     {
-        if (tests.tests.length === 0) {
-            tests.set_file_is_test();
-        }
         if (expected_true !== true) {
             var msg = make_message(function_name, description,
                                    error, substitutions);
             throw new AssertionError(msg);
         }
     }
 
     function AssertionError(message)
@@ -2441,17 +788,17 @@ policies and contribution forms [3].
                 stack = e.stack;
             }
         }
 
         var lines = stack.split("\n");
 
         // Create a pattern to match stack frames originating within testharness.js.  These include the
         // script URL, followed by the line/col (e.g., '/resources/testharness.js:120:21').
-        var re = new RegExp((get_script_url() || "\\btestharness.js") + ":\\d+:\\d+");
+        var re = new RegExp(("-testharness.js") + ":\\d+:\\d+");  // EDITED
 
         // Some browsers include a preamble that specifies the type of the error object.  Skip this by
         // advancing until we find the first stack frame originating from testharness.js.
         var i = 0;
         while (!re.test(lines[i]) && i < lines.length) {
             i++;
         }
 
@@ -2532,126 +879,19 @@ policies and contribution forms [3].
             rv[p] = b[p];
         }
         return rv;
     }
 
     function expose(object, name)
     {
         var components = name.split(".");
-        var target = test_environment.global_scope();
+        var target = self;  // EDITED
         for (var i = 0; i < components.length - 1; i++) {
             if (!(components[i] in target)) {
                 target[components[i]] = {};
             }
             target = target[components[i]];
         }
         target[components[components.length - 1]] = object;
     }
-
-    function is_same_origin(w) {
-        try {
-            'random_prop' in w;
-            return true;
-        } catch (e) {
-            return false;
-        }
-    }
-
-    /** Returns the 'src' URL of the first <script> tag in the page to include the file 'testharness.js'. */
-    function get_script_url()
-    {
-        if (!('document' in self)) {
-            return undefined;
-        }
-
-        var scripts = document.getElementsByTagName("script");
-        for (var i = 0; i < scripts.length; i++) {
-            var src;
-            if (scripts[i].src) {
-                src = scripts[i].src;
-            } else if (scripts[i].href) {
-                //SVG case
-                src = scripts[i].href.baseVal;
-            }
-
-            var matches = src && src.match(/^(.*\/|)testharness\.js$/);
-            if (matches) {
-                return src;
-            }
-        }
-        return undefined;
-    }
-
-    /** Returns the URL path at which the files for testharness.js are assumed to reside (e.g., '/resources/').
-        The path is derived from inspecting the 'src' of the <script> tag that included 'testharness.js'. */
-    function get_harness_url()
-    {
-        var script_url = get_script_url();
-
-        // Exclude the 'testharness.js' file from the returned path, but '+ 1' to include the trailing slash.
-        return script_url ? script_url.slice(0, script_url.lastIndexOf('/') + 1) : undefined;
-    }
-
-    function supports_post_message(w)
-    {
-        var supports;
-        var type;
-        // Given IE implements postMessage across nested iframes but not across
-        // windows or tabs, you can't infer cross-origin communication from the presence
-        // of postMessage on the current window object only.
-        //
-        // Touching the postMessage prop on a window can throw if the window is
-        // not from the same origin AND post message is not supported in that
-        // browser. So just doing an existence test here won't do, you also need
-        // to wrap it in a try..cacth block.
-        try {
-            type = typeof w.postMessage;
-            if (type === "function") {
-                supports = true;
-            }
-
-            // IE8 supports postMessage, but implements it as a host object which
-            // returns "object" as its `typeof`.
-            else if (type === "object") {
-                supports = true;
-            }
-
-            // This is the case where postMessage isn't supported AND accessing a
-            // window property across origins does NOT throw (e.g. old Safari browser).
-            else {
-                supports = false;
-            }
-        } catch (e) {
-            // This is the case where postMessage isn't supported AND accessing a
-            // window property across origins throws (e.g. old Firefox browser).
-            supports = false;
-        }
-        return supports;
-    }
-
-    /**
-     * Setup globals
-     */
-
-    var tests = new Tests();
-
-    addEventListener("error", function(e) {
-        if (tests.file_is_test) {
-            var test = tests.tests[0];
-            if (test.phase >= test.phases.HAS_RESULT) {
-                return;
-            }
-            test.set_status(test.FAIL, e.message, e.stack);
-            test.phase = test.phases.HAS_RESULT;
-            test.done();
-            done();
-        } else if (!tests.allow_uncaught_exception) {
-            tests.status.status = tests.status.ERROR;
-            tests.status.message = e.message;
-            tests.status.stack = e.stack;
-        }
-    });
-
-    test_environment.on_tests_ready();
-
 })();
 // vim: set expandtab shiftwidth=4 tabstop=4:
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/lib/web-platform-testharness.js
@@ -0,0 +1,198 @@
+// Emulate W3C testharness.js in the JS shell.
+//
+// This file loads ./w3c-testharness.js, which consists of code copied from the
+// actual W3C testharness.js file (/dom/imptests/testharness.js).  But that
+// code won't work in the shell without considerable help provided by this
+// file.
+
+
+// *** Hacky versions of DOM builtins *****************************************
+
+(function (self) {
+    "use strict";
+
+    self.self = self;
+    self.window = self;
+
+    // Our stub version of addEventListener never actually fires events.
+    // This is sufficient for the tests we've copied so far.
+    self.addEventListener = function (event, callback) {};
+
+    let nextId = 1;
+    let now = 0;  // bogus clock used by our stub version of setTimeout
+    let timeouts = [];
+
+    function enqueue(id, action, dt) {
+        let t = now + dt;
+        timeouts.push({id, action, t});
+        timeouts.sort((a, b) => a.t - b.t);
+    }
+
+    self.setTimeout = function (action, dt) {
+        let id = nextId++;
+        enqueue(id, action, dt);
+        return id;
+    }
+
+    self.clearTimeout = function (id) {
+        timeouts = timeouts.filter(timeout => timeout.id !== id);
+    }
+
+    self.setInterval = function (action, dt) {
+        let id = nextId++;
+        function scheduleNext() {
+            enqueue(id, () => { scheduleNext(); action(); }, dt);
+        }
+        scheduleNext();
+        return id;
+    }
+
+    self.clearInterval = self.clearTimeout;
+
+    self.drainTimeoutQueue = function drainTimeoutQueue() {
+        drainJobQueue();
+        while (timeouts.length > 0) {
+            let target = timeouts.shift();
+            let f = target.action;
+            let dt = target.t - now;
+            now = target.t;
+            f();
+            drainJobQueue();
+        }
+    };
+
+    // *** importScripts ******************************************************
+    // Many wpt tests use this; for sanity's sake we allow loading only a few
+    // files used by the particular tests we run as jit-tests.
+
+    let allowed_scripts = [
+        "../resources/test-utils.js",
+        "../resources/rs-utils.js",
+        "../resources/constructor-ordering.js",
+        "../resources/recording-streams.js",
+        "../resources/rs-test-templates.js",
+    ];
+
+    self.importScripts = function (url) {
+        // Don't load the real test harness, as this would clobber our
+        // carefully constructed fake test harness that doesn't require the DOM.
+        if (url === "/resources/testharness.js") {
+            return;  // this is fine
+        }
+
+        if (!allowed_scripts.includes(url)) {
+            throw new Error("can't import script: " + uneval(url));
+        }
+
+        // Load the file relative to the caller's filename.
+        let stack = new Error().stack.split("\n");
+        let match = /^\s*[^@]*@(.*):\d+:\d+$/.exec(stack[1]);
+        let callerFile = match[1];
+        let dir = callerFile.replace(/[^/\\]*$/, "");
+
+        load(dir + url);
+    };
+
+}(this));
+
+
+loadRelativeToScript("w3c-testharness.js");
+
+
+// *** Hand-coded reimplementation of some parts of testharness.js ************
+
+let all_tests = Promise.resolve();
+
+function fail() {
+    print("fail() was called");
+    quit(1);
+}
+
+function promise_rejects(test, expected, promise) {
+    return promise.then(fail).catch(function(e) {
+        assert_throws(expected, function() { throw e });
+    });
+}
+
+function test(fn, description) {
+    promise_test(fn, description);
+}
+
+function promise_test(fn, description) {
+    let test = {
+        unreached_func(message) {
+            return () => { assert_unreached(description); };
+        },
+
+        cleanups: [],
+        add_cleanup(fn) {
+            this.cleanups.push(fn);
+        },
+
+        step_func(func, this_obj = this) {
+            return (...args) => this.step(func, this_obj, ...args);
+        },
+
+        step(func, this_obj, ...args) {
+            return func.apply(this_obj, args);
+        },
+    };
+
+    all_tests = all_tests.then(async () => {
+        print("*   " + description);
+        try {
+            await fn(test);
+            print("    passed\n");
+        } catch (exc) {
+            print("    failed: " + exc + "\n");
+            throw exc;
+        } finally {
+            for (const cleanup of test.cleanups) {
+                cleanup();
+            }
+        }
+    });
+}
+
+function done() {
+    let passed;
+    let problem;
+    all_tests
+        .then(() => { passed = true; })
+        .catch(exc => { passed = false; problem = exc; });
+    drainTimeoutQueue();
+    if (passed === undefined) {
+        throw new Error("test didn't finish");
+    }
+    if (!passed) {
+        throw problem;
+    }
+    print("all tests passed");
+}
+
+
+// *** Test runner ********************************************************************************
+
+function load_web_platform_test(path) {
+  let stabs = [
+    libdir + "../../../../testing/web-platform/tests/" + path,
+    libdir + "../../../web-platform/tests/" + path,
+  ];
+
+  for (let filename of stabs) {
+    let code;
+    try {
+      code = read(filename);
+    } catch (exc) {
+      if (!(exc instanceof Error && /^can't open/.exec(exc.message))) {
+        throw exc;
+      }
+      continue;
+    }
+
+    // Successfully read the file! Now evaluate the code.
+    return evaluate(code, {fileName: filename, lineNumber: 1});
+  }
+
+  throw new Error(`can't open ${uneval(path)}: tried ${uneval(stabs)}`);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/web-platform/streams/readable-streams/bad-strategies.js
@@ -0,0 +1,2 @@
+load(libdir + "web-platform-testharness.js");
+load_web_platform_test("streams/readable-streams/bad-strategies.js");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/web-platform/streams/readable-streams/brand-checks.js
@@ -0,0 +1,2 @@
+load(libdir + "web-platform-testharness.js");
+load_web_platform_test("streams/readable-streams/brand-checks.js");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/web-platform/streams/readable-streams/cancel.js
@@ -0,0 +1,2 @@
+load(libdir + "web-platform-testharness.js");
+load_web_platform_test("streams/readable-streams/cancel.js");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/web-platform/streams/readable-streams/count-queuing-strategy-integration.js
@@ -0,0 +1,2 @@
+load(libdir + "web-platform-testharness.js");
+load_web_platform_test("streams/readable-streams/count-queuing-strategy-integration.js");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/web-platform/streams/readable-streams/default-reader.js
@@ -0,0 +1,2 @@
+load(libdir + "web-platform-testharness.js");
+load_web_platform_test("streams/readable-streams/default-reader.js");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/web-platform/streams/readable-streams/floating-point-total-queue-size.js
@@ -0,0 +1,2 @@
+load(libdir + "web-platform-testharness.js");
+load_web_platform_test("streams/readable-streams/floating-point-total-queue-size.js");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/web-platform/streams/readable-streams/patched-global.js
@@ -0,0 +1,2 @@
+load(libdir + "web-platform-testharness.js");
+load_web_platform_test("streams/readable-streams/patched-global.js");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/web-platform/streams/readable-streams/tee.js
@@ -0,0 +1,2 @@
+load(libdir + "web-platform-testharness.js");
+load_web_platform_test("streams/readable-streams/tee.js");